KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > ext > awt > image > codec > PNGEncodeParam


1 /*
2
3    Copyright 2001,2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.ext.awt.image.codec;
19
20 import java.awt.image.ColorModel JavaDoc;
21 import java.awt.image.IndexColorModel JavaDoc;
22 import java.awt.image.RenderedImage JavaDoc;
23 import java.awt.image.SampleModel JavaDoc;
24 import java.util.Date JavaDoc;
25 import java.util.Vector JavaDoc;
26
27 /**
28  * An instance of <code>ImageEncodeParam</code> for encoding images in
29  * the PNG format.
30  *
31  * <p><b> This class is not a committed part of the JAI API. It may
32  * be removed or changed in future releases of JAI.</b>
33  */

34 public abstract class PNGEncodeParam implements ImageEncodeParam {
35
36     /** Constant for use with the sRGB chunk. */
37     public static final int INTENT_PERCEPTUAL = 0;
38
39     /** Constant for use with the sRGB chunk. */
40     public static final int INTENT_RELATIVE = 1;
41
42     /** Constant for use with the sRGB chunk. */
43     public static final int INTENT_SATURATION = 2;
44
45     /** Constant for use with the sRGB chunk. */
46     public static final int INTENT_ABSOLUTE = 3;
47
48     /** Constant for use in filtering. */
49     public static final int PNG_FILTER_NONE = 0;
50
51     /** Constant for use in filtering. */
52     public static final int PNG_FILTER_SUB = 1;
53
54     /** Constant for use in filtering. */
55     public static final int PNG_FILTER_UP = 2;
56
57     /** Constant for use in filtering. */
58     public static final int PNG_FILTER_AVERAGE = 3;
59
60     /** Constant for use in filtering. */
61     public static final int PNG_FILTER_PAETH = 4;
62
63
64     /**
65      * Returns an instance of <code>PNGEncodeParam.Palette</code>,
66      * <code>PNGEncodeParam.Gray</code>, or
67      * <code>PNGEncodeParam.RGB</code> appropriate for encoding
68      * the given image.
69      *
70      * <p> If the image has an <code>IndexColorModel</code>, an
71      * instance of <code>PNGEncodeParam.Palette</code> is returned.
72      * Otherwise, if the image has 1 or 2 bands an instance of
73      * <code>PNGEncodeParam.Gray</code> is returned. In all other
74      * cases an instance of <code>PNGEncodeParam.RGB</code> is
75      * returned.
76      *
77      * <p> Note that this method does not provide any guarantee that
78      * the given image will be successfully encoded by the PNG
79      * encoder, as it only performs a very superficial analysis of
80      * the image structure.
81      */

82     public static PNGEncodeParam getDefaultEncodeParam(RenderedImage JavaDoc im) {
83         ColorModel JavaDoc colorModel = im.getColorModel();
84         if (colorModel instanceof IndexColorModel JavaDoc) {
85             return new PNGEncodeParam.Palette();
86         }
87
88         SampleModel JavaDoc sampleModel = im.getSampleModel();
89         int numBands = sampleModel.getNumBands();
90
91         if (numBands == 1 || numBands == 2) {
92             return new PNGEncodeParam.Gray();
93         } else {
94             return new PNGEncodeParam.RGB();
95         }
96     }
97
98     public static class Palette extends PNGEncodeParam {
99
100         /** Constructs an instance of <code>PNGEncodeParam.Palette</code>. */
101         public Palette() {}
102
103         // bKGD chunk
104

105         private boolean backgroundSet = false;
106         
107         /**
108          * Suppresses the 'bKGD' chunk from being output.
109          */

110         public void unsetBackground() {
111             backgroundSet = false;
112         }
113         
114         /**
115          * Returns true if a 'bKGD' chunk will be output.
116          */

117         public boolean isBackgroundSet() {
118             return backgroundSet;
119         }
120
121         /**
122          * Sets the desired bit depth for a palette image. The bit
123          * depth must be one of 1, 2, 4, or 8, or else an
124          * <code>IllegalArgumentException</code> will be thrown.
125          */

126         public void setBitDepth(int bitDepth) {
127             if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 &&
128                 bitDepth != 8) {
129                 throw new IllegalArgumentException JavaDoc(PropertyUtil.getString("PNGEncodeParam2"));
130             }
131             this.bitDepth = bitDepth;
132             bitDepthSet = true;
133         }
134     
135         // PLTE chunk
136

137         private int[] palette = null;
138         private boolean paletteSet = false;
139
140         /**
141          * Sets the RGB palette of the image to be encoded.
142          * The <code>rgb</code> parameter contains alternating
143          * R, G, B values for each color index used in the image.
144          * The number of elements must be a multiple of 3 between
145          * 3 and 3*256.
146          *
147          * <p> The 'PLTE' chunk will encode this information.
148          *
149          * @param rgb An array of <code>int</code>s.
150          */

151         public void setPalette(int[] rgb) {
152             if (rgb.length < 1*3 || rgb.length > 256*3) {
153         throw new
154           IllegalArgumentException JavaDoc(PropertyUtil.getString("PNGEncodeParam0"));
155             }
156             if ((rgb.length % 3) != 0) {
157                 throw new
158               IllegalArgumentException JavaDoc(PropertyUtil.getString("PNGEncodeParam1"));
159             }
160             
161             palette = (int[])(rgb.clone());
162             paletteSet = true;
163         }
164         
165         /**
166          * Returns the current RGB palette.
167          *
168          * <p> If the palette has not previously been set, or has been
169          * unset, an <code>IllegalStateException</code> will be thrown.
170          *
171          * @throws IllegalStateException if the palette is not set.
172          *
173          * @return An array of <code>int</code>s.
174          */

175         public int[] getPalette() {
176             if (!paletteSet) {
177                 throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam3"));
178             }
179             return (int[])(palette.clone());
180         }
181
182         /**
183          * Suppresses the 'PLTE' chunk from being output.
184          */

185         public void unsetPalette() {
186             palette = null;
187             paletteSet = false;
188         }
189         
190         /**
191          * Returns true if a 'PLTE' chunk will be output.
192          */

193         public boolean isPaletteSet() {
194             return paletteSet;
195         }
196
197         // bKGD chunk
198

199         private int backgroundPaletteIndex;
200         
201         /**
202          * Sets the palette index of the suggested background color.
203          *
204          * <p> The 'bKGD' chunk will encode this information.
205          */

206         public void setBackgroundPaletteIndex(int index) {
207             backgroundPaletteIndex = index;
208             backgroundSet = true;
209         }
210         
211         /**
212          * Returns the palette index of the suggested background color.
213          *
214          * <p> If the background palette index has not previously been
215          * set, or has been unset, an
216          * <code>IllegalStateException</code> will be thrown.
217          *
218          * @throws IllegalStateException if the palette index is not set.
219          */

220         public int getBackgroundPaletteIndex() {
221             if (!backgroundSet) {
222                 throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam4"));
223             }
224             return backgroundPaletteIndex;
225         }
226
227         // tRNS chunk
228

229         private int[] transparency;
230
231         /**
232          * Sets the alpha values associated with each palette entry.
233          * The <code>alpha</code> parameter should have as many entries
234          * as there are RGB triples in the palette.
235          *
236          * <p> The 'tRNS' chunk will encode this information.
237          */

238         public void setPaletteTransparency(byte[] alpha) {
239             transparency = new int[alpha.length];
240             for (int i = 0; i < alpha.length; i++) {
241                 transparency[i] = alpha[i] & 0xff;
242             }
243             transparencySet = true;
244         }
245         
246         /**
247          * Returns the alpha values associated with each palette entry.
248          *
249          * <p> If the palette transparency has not previously been
250          * set, or has been unset, an
251          * <code>IllegalStateException</code> will be thrown.
252          *
253          * @throws IllegalStateException if the palette transparency is
254          * not set.
255          */

256         public byte[] getPaletteTransparency() {
257             if (!transparencySet) {
258                 throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam5"));
259             }
260             byte[] alpha = new byte[transparency.length];
261             for (int i = 0; i < alpha.length; i++) {
262                 alpha[i] = (byte)transparency[i];
263             }
264             return alpha;
265         }
266     }
267
268     public static class Gray extends PNGEncodeParam {
269
270         /** Constructs an instance of <code>PNGEncodeParam.Gray</code>. */
271         public Gray() {}
272
273         // bKGD chunk
274

275         private boolean backgroundSet = false;
276         
277         /**
278          * Suppresses the 'bKGD' chunk from being output.
279          */

280         public void unsetBackground() {
281             backgroundSet = false;
282         }
283         
284         /**
285          * Returns true if a 'bKGD' chunk will be output.
286          */

287         public boolean isBackgroundSet() {
288             return backgroundSet;
289         }
290
291         /**
292          * Sets the desired bit depth for a grayscale image. The bit
293          * depth must be one of 1, 2, 4, 8, or 16.
294          *
295          * <p> When encoding a source image of a greater bit depth,
296          * pixel values will be clamped to the smaller range after
297          * shifting by the value given by <code>getBitShift()</code>.
298          * When encoding a source image of a smaller bit depth, pixel
299          * values will be shifted and left-filled with zeroes.
300          */

301         public void setBitDepth(int bitDepth) {
302             if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 &&
303                 bitDepth != 8 && bitDepth != 16) {
304                 throw new IllegalArgumentException JavaDoc();
305             }
306             this.bitDepth = bitDepth;
307             bitDepthSet = true;
308         }
309     
310         // bKGD chunk
311

312         private int backgroundPaletteGray;
313
314         /**
315          * Sets the suggested gray level of the background.
316          *
317          * <p> The 'bKGD' chunk will encode this information.
318          */

319         public void setBackgroundGray(int gray) {
320             backgroundPaletteGray = gray;
321             backgroundSet = true;
322         }
323         
324         /**
325          * Returns the suggested gray level of the background.
326          *
327          * <p> If the background gray level has not previously been
328          * set, or has been unset, an
329          * <code>IllegalStateException</code> will be thrown.
330          *
331          * @throws IllegalStateException if the background gray level
332          * is not set.
333          */

334         public int getBackgroundGray() {
335             if (!backgroundSet) {
336                 throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam6"));
337             }
338             return backgroundPaletteGray;
339         }
340
341         // tRNS chunk
342

343         private int[] transparency;
344
345         /**
346          * Sets the gray value to be used to denote transparency.
347          *
348          * <p> Setting this attribute will cause the alpha channel
349          * of the input image to be ignored.
350          *
351          * <p> The 'tRNS' chunk will encode this information.
352          */

353         public void setTransparentGray(int transparentGray) {
354             transparency = new int[1];
355             transparency[0] = transparentGray;
356             transparencySet = true;
357         }
358         
359         /**
360          * Returns the gray value to be used to denote transparency.
361          *
362          * <p> If the transparent gray value has not previously been
363          * set, or has been unset, an
364          * <code>IllegalStateException</code> will be thrown.
365          *
366          * @throws IllegalStateException if the transparent gray value
367          * is not set.
368          */

369         public int getTransparentGray() {
370             if (!transparencySet) {
371                 throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam7"));
372             }
373             int gray = transparency[0];
374             return gray;
375         }
376
377         private int bitShift;
378         private boolean bitShiftSet = false;
379
380         /**
381          * Sets the desired bit shift for a grayscale image.
382          * Pixels in the source image will be shifted right by
383          * the given amount prior to being clamped to the maximum
384          * value given by the encoded image's bit depth.
385          */

386         public void setBitShift(int bitShift) {
387             if (bitShift < 0) {
388                 throw new RuntimeException JavaDoc();
389             }
390             this.bitShift = bitShift;
391             bitShiftSet = true;
392         }
393
394         /**
395          * Returns the desired bit shift for a grayscale image.
396          *
397          * <p> If the bit shift has not previously been set, or has been
398          * unset, an <code>IllegalStateException</code> will be thrown.
399          *
400          * @throws IllegalStateException if the bit shift is not set.
401          */

402         public int getBitShift() {
403             if (!bitShiftSet) {
404                 throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam8"));
405             }
406             return bitShift;
407         }
408
409         /**
410          * Suppresses the setting of the bit shift of a grayscale image.
411          * Pixels in the source image will not be shifted prior to encoding.
412          */

413         public void unsetBitShift() {
414             bitShiftSet = false;
415         }
416
417         /**
418          * Returns true if the bit shift has been set.
419          */

420         public boolean isBitShiftSet() {
421             return bitShiftSet;
422         }
423
424         /**
425          * Returns true if the bit depth has been set.
426          */

427         public boolean isBitDepthSet() {
428             return bitDepthSet;
429         }
430     }
431
432     public static class RGB extends PNGEncodeParam {
433
434         /** Constructs an instance of <code>PNGEncodeParam.RGB</code>. */
435         public RGB() {}
436
437         // bKGD chunk
438

439         private boolean backgroundSet = false;
440         
441         /**
442          * Suppresses the 'bKGD' chunk from being output.
443          */

444         public void unsetBackground() {
445             backgroundSet = false;
446         }
447         
448         /**
449          * Returns true if a 'bKGD' chunk will be output.
450          */

451         public boolean isBackgroundSet() {
452             return backgroundSet;
453         }
454
455         /**
456          * Sets the desired bit depth for an RGB image. The bit
457          * depth must be 8 or 16.
458          */

459         public void setBitDepth(int bitDepth) {
460             if (bitDepth != 8 && bitDepth != 16) {
461                 throw new RuntimeException JavaDoc();
462             }
463             this.bitDepth = bitDepth;
464             bitDepthSet = true;
465         }
466     
467         // bKGD chunk
468

469         private int[] backgroundRGB;
470
471         /**
472          * Sets the RGB value of the suggested background color.
473          * The <code>rgb</code> parameter should have 3 entries.
474          *
475          * <p> The 'bKGD' chunk will encode this information.
476          */

477         public void setBackgroundRGB(int[] rgb) {
478             if (rgb.length != 3) {
479                 throw new RuntimeException JavaDoc();
480             }
481             backgroundRGB = rgb;
482             backgroundSet = true;
483         }
484         
485         /**
486          * Returns the RGB value of the suggested background color.
487          *
488          * <p> If the background color has not previously been set, or has been
489          * unset, an <code>IllegalStateException</code> will be thrown.
490          *
491          * @throws IllegalStateException if the background color is not set.
492          */

493         public int[] getBackgroundRGB() {
494             if (!backgroundSet) {
495                 throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam9"));
496             }
497             return backgroundRGB;
498         }
499
500         // tRNS chunk
501

502         private int[] transparency;
503         
504         /**
505          * Sets the RGB value to be used to denote transparency.
506          *
507          * <p> Setting this attribute will cause the alpha channel
508          * of the input image to be ignored.
509          *
510          * <p> The 'tRNS' chunk will encode this information.
511          */

512         public void setTransparentRGB(int[] transparentRGB) {
513             transparency = (int[])(transparentRGB.clone());
514             transparencySet = true;
515         }
516         
517         /**
518          * Returns the RGB value to be used to denote transparency.
519          *
520          * <p> If the transparent color has not previously been set,
521          * or has been unset, an <code>IllegalStateException</code>
522          * will be thrown.
523          *
524          * @throws IllegalStateException if the transparent color is not set.
525          */

526         public int[] getTransparentRGB() {
527             if (!transparencySet) {
528                 throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam10"));
529             }
530             return (int[])(transparency.clone());
531         }
532     }
533
534     protected int bitDepth;
535     protected boolean bitDepthSet = false;
536
537     /**
538      * Sets the desired bit depth of an image.
539      */

540     public abstract void setBitDepth(int bitDepth);
541     
542     /**
543      * Returns the desired bit depth for a grayscale image.
544      *
545      * <p> If the bit depth has not previously been set, or has been
546      * unset, an <code>IllegalStateException</code> will be thrown.
547      *
548      * @throws IllegalStateException if the bit depth is not set.
549      */

550     public int getBitDepth() {
551         if (!bitDepthSet) {
552             throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam11"));
553         }
554         return bitDepth;
555     }
556
557     /**
558      * Suppresses the setting of the bit depth of a grayscale image.
559      * The depth of the encoded image will be inferred from the source
560      * image bit depth, rounded up to the next power of 2 between 1
561      * and 16.
562      */

563     public void unsetBitDepth() {
564         bitDepthSet = false;
565     }
566
567     private boolean useInterlacing = false;
568
569     /**
570      * Turns Adam7 interlacing on or off.
571      */

572     public void setInterlacing(boolean useInterlacing) {
573         this.useInterlacing = useInterlacing;
574     }
575
576     /**
577      * Returns <code>true</code> if Adam7 interlacing will be used.
578      */

579     public boolean getInterlacing() {
580         return useInterlacing;
581     }
582     
583     // bKGD chunk - delegate to subclasses
584

585     // In JAI 1.0, 'backgroundSet' was private. The JDK 1.2 compiler
586
// was lenient and incorrectly allowed this variable to be
587
// accessed from the subclasses. The JDK 1.3 compiler correctly
588
// flags this as a use of a non-static variable in a static
589
// context. Changing 'backgroundSet' to protected would have
590
// solved the problem, but would have introduced a visible API
591
// change. Thus we are forced to adopt the solution of placing a
592
// separate private variable in each subclass and providing
593
// separate implementations of 'unsetBackground' and
594
// 'isBackgroundSet' in each concrete subclass.
595

596     /**
597      * Suppresses the 'bKGD' chunk from being output.
598      * For API compatibility with JAI 1.0, the superclass
599      * defines this method to throw a <code>RuntimeException</code>;
600      * accordingly, subclasses must provide their own implementations.
601      */

602     public void unsetBackground() {
603         throw new RuntimeException JavaDoc(PropertyUtil.getString("PNGEncodeParam23"));
604     }
605
606     /**
607      * Returns true if a 'bKGD' chunk will be output.
608      * For API compatibility with JAI 1.0, the superclass
609      * defines this method to throw a <code>RuntimeException</code>;
610      * accordingly, subclasses must provide their own implementations.
611      */

612     public boolean isBackgroundSet() {
613         throw new RuntimeException JavaDoc(PropertyUtil.getString("PNGEncodeParam24"));
614     }
615
616     // cHRM chunk
617

618     private float[] chromaticity = null;
619     private boolean chromaticitySet = false;
620
621     /**
622      * Sets the white point and primary chromaticities in CIE (x, y)
623      * space.
624      *
625      * <p> The <code>chromaticity</code> parameter should be a
626      * <code>float</code> array of length 8 containing the white point
627      * X and Y, red X and Y, green X and Y, and blue X and Y values in
628      * order.
629      *
630      * <p> The 'cHRM' chunk will encode this information.
631      */

632     public void setChromaticity(float[] chromaticity) {
633         if (chromaticity.length != 8) {
634             throw new IllegalArgumentException JavaDoc();
635         }
636         this.chromaticity = (float[])(chromaticity.clone());
637         chromaticitySet = true;
638     }
639
640     /**
641      * A convenience method that calls the array version.
642      */

643     public void setChromaticity(float whitePointX, float whitePointY,
644                                 float redX, float redY,
645                                 float greenX, float greenY,
646                                 float blueX, float blueY) {
647         float[] chroma = new float[8];
648         chroma[0] = whitePointX;
649         chroma[1] = whitePointY;
650         chroma[2] = redX;
651         chroma[3] = redY;
652         chroma[4] = greenX;
653         chroma[5] = greenY;
654         chroma[6] = blueX;
655         chroma[7] = blueY;
656         setChromaticity(chroma);
657     }
658
659     /**
660      * Returns the white point and primary chromaticities in
661      * CIE (x, y) space.
662      *
663      * <p> See the documentation for the <code>setChromaticity</code>
664      * method for the format of the returned data.
665      *
666      * <p> If the chromaticity has not previously been set, or has been
667      * unset, an <code>IllegalStateException</code> will be thrown.
668      *
669      * @throws IllegalStateException if the chromaticity is not set.
670      */

671     public float[] getChromaticity() {
672         if (!chromaticitySet) {
673             throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam12"));
674         }
675         return (float[])(chromaticity.clone());
676     }
677
678     /**
679      * Suppresses the 'cHRM' chunk from being output.
680      */

681     public void unsetChromaticity() {
682         chromaticity = null;
683         chromaticitySet = false;
684     }
685
686     /**
687      * Returns true if a 'cHRM' chunk will be output.
688      */

689     public boolean isChromaticitySet() {
690         return chromaticitySet;
691     }
692
693     // gAMA chunk
694

695     private float gamma;
696     private boolean gammaSet = false;
697
698     /**
699      * Sets the file gamma value for the image.
700      *
701      * <p> The 'gAMA' chunk will encode this information.
702      */

703     public void setGamma(float gamma) {
704         this.gamma = gamma;
705         gammaSet = true;
706     }
707
708     /**
709      * Returns the file gamma value for the image.
710      *
711      * <p> If the file gamma has not previously been set, or has been
712      * unset, an <code>IllegalStateException</code> will be thrown.
713      *
714      * @throws IllegalStateException if the gamma is not set.
715      */

716     public float getGamma() {
717         if (!gammaSet) {
718             throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam13"));
719         }
720         return gamma;
721     }
722
723     /**
724      * Suppresses the 'gAMA' chunk from being output.
725      */

726     public void unsetGamma() {
727         gammaSet = false;
728     }
729
730     /**
731      * Returns true if a 'gAMA' chunk will be output.
732      */

733     public boolean isGammaSet() {
734         return gammaSet;
735     }
736
737     // hIST chunk
738

739     private int[] paletteHistogram = null;
740     private boolean paletteHistogramSet = false;
741
742     /**
743      * Sets the palette histogram to be stored with this image.
744      * The histogram consists of an array of integers, one per
745      * palette entry.
746      *
747      * <p> The 'hIST' chunk will encode this information.
748      */

749     public void setPaletteHistogram(int[] paletteHistogram) {
750         this.paletteHistogram = (int[])(paletteHistogram.clone());
751         paletteHistogramSet = true;
752     }
753
754     /**
755      * Returns the palette histogram to be stored with this image.
756      *
757      * <p> If the histogram has not previously been set, or has been
758      * unset, an <code>IllegalStateException</code> will be thrown.
759      *
760      * @throws IllegalStateException if the histogram is not set.
761      */

762     public int[] getPaletteHistogram() {
763         if (!paletteHistogramSet) {
764             throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam14"));
765         }
766         return paletteHistogram;
767     }
768
769     /**
770      * Suppresses the 'hIST' chunk from being output.
771      */

772     public void unsetPaletteHistogram() {
773         paletteHistogram = null;
774         paletteHistogramSet = false;
775     }
776
777     /**
778      * Returns true if a 'hIST' chunk will be output.
779      */

780     public boolean isPaletteHistogramSet() {
781         return paletteHistogramSet;
782     }
783
784     // iCCP chunk
785

786     private byte[] ICCProfileData = null;
787     private boolean ICCProfileDataSet = false;
788
789     /**
790      * Sets the ICC profile data to be stored with this image.
791      * The profile is represented in raw binary form.
792      *
793      * <p> The 'iCCP' chunk will encode this information.
794      */

795     public void setICCProfileData(byte[] ICCProfileData) {
796         this.ICCProfileData = (byte[])(ICCProfileData.clone());
797         ICCProfileDataSet = true;
798     }
799
800     /**
801      * Returns the ICC profile data to be stored with this image.
802      *
803      * <p> If the ICC profile has not previously been set, or has been
804      * unset, an <code>IllegalStateException</code> will be thrown.
805      *
806      * @throws IllegalStateException if the ICC profile is not set.
807      */

808     public byte[] getICCProfileData() {
809         if (!ICCProfileDataSet) {
810             throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam15"));
811         }
812         return (byte[])(ICCProfileData.clone());
813     }
814
815     /**
816      * Suppresses the 'iCCP' chunk from being output.
817      */

818     public void unsetICCProfileData() {
819         ICCProfileData = null;
820         ICCProfileDataSet = false;
821     }
822
823     /**
824      * Returns true if a 'iCCP' chunk will be output.
825      */

826     public boolean isICCProfileDataSet() {
827         return ICCProfileDataSet;
828     }
829
830     // pHYS chunk
831

832     private int[] physicalDimension = null;
833     private boolean physicalDimensionSet = false;
834
835     /**
836      * Sets the physical dimension information to be stored with this
837      * image. The physicalDimension parameter should be a 3-entry
838      * array containing the number of pixels per unit in the X
839      * direction, the number of pixels per unit in the Y direction,
840      * and the unit specifier (0 = unknown, 1 = meters).
841      *
842      * <p> The 'pHYS' chunk will encode this information.
843      */

844     public void setPhysicalDimension(int[] physicalDimension) {
845         this.physicalDimension = (int[])(physicalDimension.clone());
846         physicalDimensionSet = true;
847     }
848
849     /**
850      * A convenience method that calls the array version.
851      */

852     public void setPhysicalDimension(int xPixelsPerUnit,
853                                      int yPixelsPerUnit,
854                                      int unitSpecifier) {
855         int[] pd = new int[3];
856         pd[0] = xPixelsPerUnit;
857         pd[1] = yPixelsPerUnit;
858         pd[2] = unitSpecifier;
859         
860         setPhysicalDimension(pd);
861     }
862
863     /**
864      * Returns the physical dimension information to be stored
865      * with this image.
866      *
867      * <p> If the physical dimension information has not previously
868      * been set, or has been unset, an
869      * <code>IllegalStateException</code> will be thrown.
870      *
871      * @throws IllegalStateException if the physical dimension information
872      * is not set.
873      */

874     public int[] getPhysicalDimension() {
875         if (!physicalDimensionSet) {
876             throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam16"));
877         }
878         return (int[])(physicalDimension.clone());
879     }
880
881     /**
882      * Suppresses the 'pHYS' chunk from being output.
883      */

884     public void unsetPhysicalDimension() {
885         physicalDimension = null;
886         physicalDimensionSet = false;
887     }
888
889     /**
890      * Returns true if a 'pHYS' chunk will be output.
891      */

892     public boolean isPhysicalDimensionSet() {
893         return physicalDimensionSet;
894     }
895
896     // sPLT chunk
897

898     private PNGSuggestedPaletteEntry[] suggestedPalette = null;
899     private boolean suggestedPaletteSet = false;
900
901     /**
902      * Sets the suggested palette information to be stored with this
903      * image. The information is passed to this method as an array of
904      * <code>PNGSuggestedPaletteEntry</code> objects.
905      *
906      * <p> The 'sPLT' chunk will encode this information.
907      */

908     public void setSuggestedPalette(PNGSuggestedPaletteEntry[] palette) {
909         suggestedPalette = (PNGSuggestedPaletteEntry[])(palette.clone());
910         suggestedPaletteSet = true;
911     }
912
913     /**
914      * Returns the suggested palette information to be stored with this
915      * image.
916      *
917      * <p> If the suggested palette information has not previously
918      * been set, or has been unset, an
919      * <code>IllegalStateException</code> will be thrown.
920      *
921      * @throws IllegalStateException if the suggested palette
922      * information is not set.
923      */

924     public PNGSuggestedPaletteEntry[] getSuggestedPalette() {
925         if (!suggestedPaletteSet) {
926             throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam17"));
927         }
928         return (PNGSuggestedPaletteEntry[])(suggestedPalette.clone());
929     }
930
931     /**
932      * Suppresses the 'sPLT' chunk from being output.
933      */

934     public void unsetSuggestedPalette() {
935         suggestedPalette = null;
936         suggestedPaletteSet = false;
937     }
938
939     /**
940      * Returns true if a 'sPLT' chunk will be output.
941      */

942     public boolean isSuggestedPaletteSet() {
943         return suggestedPaletteSet;
944     }
945
946     // sBIT chunk
947

948     private int[] significantBits = null;
949     private boolean significantBitsSet = false;
950
951     /**
952      * Sets the number of significant bits for each band of the image.
953      *
954      * <p> The number of entries in the <code>significantBits</code>
955      * array must be equal to the number of output bands in the image:
956      * 1 for a gray image, 2 for gray+alpha, 3 for index or truecolor,
957      * and 4 for truecolor+alpha.
958      *
959      * <p> The 'sBIT' chunk will encode this information.
960      */

961     public void setSignificantBits(int[] significantBits) {
962         this.significantBits = (int[])(significantBits.clone());
963         significantBitsSet = true;
964     }
965
966     /**
967      * Returns the number of significant bits for each band of the image.
968      *
969      * <p> If the significant bits values have not previously been
970      * set, or have been unset, an <code>IllegalStateException</code>
971      * will be thrown.
972      *
973      * @throws IllegalStateException if the significant bits values are
974      * not set.
975      */

976     public int[] getSignificantBits() {
977         if (!significantBitsSet) {
978             throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam18"));
979         }
980         return (int[])significantBits.clone();
981     }
982
983     /**
984      * Suppresses the 'sBIT' chunk from being output.
985      */

986     public void unsetSignificantBits() {
987         significantBits = null;
988         significantBitsSet = false;
989     }
990
991     /**
992      * Returns true if an 'sBIT' chunk will be output.
993      */

994     public boolean isSignificantBitsSet() {
995         return significantBitsSet;
996     }
997
998     // sRGB chunk
999

1000    private int SRGBIntent;
1001    private boolean SRGBIntentSet = false;
1002
1003    /**
1004     * Sets the sRGB rendering intent to be stored with this image.
1005     * The legal values are 0 = Perceptual, 1 = Relative Colorimetric,
1006     * 2 = Saturation, and 3 = Absolute Colorimetric. Refer to the
1007     * PNG specification for information on these values.
1008     *
1009     * <p> The 'sRGB' chunk will encode this information.
1010     */

1011    public void setSRGBIntent(int SRGBIntent) {
1012        this.SRGBIntent = SRGBIntent;
1013        SRGBIntentSet = true;
1014    }
1015
1016    /**
1017     * Returns the sRGB rendering intent to be stored with this image.
1018     *
1019     * <p> If the sRGB intent has not previously been set, or has been
1020     * unset, an <code>IllegalStateException</code> will be thrown.
1021     *
1022     * @throws IllegalStateException if the sRGB intent is not set.
1023     */

1024    public int getSRGBIntent() {
1025        if (!SRGBIntentSet) {
1026            throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam19"));
1027        }
1028        return SRGBIntent;
1029    }
1030
1031    /**
1032     * Suppresses the 'sRGB' chunk from being output.
1033     */

1034    public void unsetSRGBIntent() {
1035        SRGBIntentSet = false;
1036    }
1037
1038    /**
1039     * Returns true if an 'sRGB' chunk will be output.
1040     */

1041    public boolean isSRGBIntentSet() {
1042        return SRGBIntentSet;
1043    }
1044
1045    // tEXt chunk
1046

1047    private String JavaDoc[] text = null;
1048    private boolean textSet = false;
1049
1050    /**
1051     * Sets the textual data to be stored in uncompressed form with this
1052     * image. The data is passed to this method as an array of
1053     * <code>String</code>s.
1054     *
1055     * <p> The 'tEXt' chunk will encode this information.
1056     */

1057    public void setText(String JavaDoc[] text) {
1058        this.text = text;
1059        textSet = true;
1060    }
1061
1062    /**
1063     * Returns the text strings to be stored in uncompressed form with this
1064     * image as an array of <code>String</code>s.
1065     *
1066     * <p> If the text strings have not previously been set, or have been
1067     * unset, an <code>IllegalStateException</code> will be thrown.
1068     *
1069     * @throws IllegalStateException if the text strings are not set.
1070     */

1071    public String JavaDoc[] getText() {
1072        if (!textSet) {
1073            throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam20"));
1074        }
1075        return text;
1076    }
1077
1078    /**
1079     * Suppresses the 'tEXt' chunk from being output.
1080     */

1081    public void unsetText() {
1082        text = null;
1083        textSet = false;
1084    }
1085
1086    /**
1087     * Returns true if a 'tEXt' chunk will be output.
1088     */

1089    public boolean isTextSet() {
1090        return textSet;
1091    }
1092
1093    // tIME chunk
1094

1095    private Date JavaDoc modificationTime;
1096    private boolean modificationTimeSet = false;
1097
1098    /**
1099     * Sets the modification time, as a <code>Date</code>, to be
1100     * stored with this image. The internal storage format will use
1101     * UTC regardless of how the <code>modificationTime</code>
1102     * parameter was created.
1103     *
1104     * <p> The 'tIME' chunk will encode this information.
1105     */

1106    public void setModificationTime(Date JavaDoc modificationTime) {
1107        this.modificationTime = modificationTime;
1108        modificationTimeSet = true;
1109    }
1110
1111    /**
1112     * Returns the modification time to be stored with this image.
1113     *
1114     * <p> If the bit depth has not previously been set, or has been
1115     * unset, an <code>IllegalStateException</code> will be thrown.
1116     *
1117     * @throws IllegalStateException if the bit depth is not set.
1118     */

1119    public Date JavaDoc getModificationTime() {
1120        if (!modificationTimeSet) {
1121            throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam21"));
1122        }
1123        return modificationTime;
1124    }
1125
1126    /**
1127     * Suppresses the 'tIME' chunk from being output.
1128     */

1129    public void unsetModificationTime() {
1130        modificationTime = null;
1131        modificationTimeSet = false;
1132    }
1133
1134    /**
1135     * Returns true if a 'tIME' chunk will be output.
1136     */

1137    public boolean isModificationTimeSet() {
1138        return modificationTimeSet;
1139    }
1140
1141    // tRNS chunk
1142

1143    boolean transparencySet = false;
1144
1145    /**
1146     * Suppresses the 'tRNS' chunk from being output.
1147     */

1148    public void unsetTransparency() {
1149        transparencySet = false;
1150    }
1151
1152    /**
1153     * Returns true if a 'tRNS' chunk will be output.
1154     */

1155    public boolean isTransparencySet() {
1156        return transparencySet;
1157    }
1158
1159    // zTXT chunk
1160

1161    private String JavaDoc[] zText = null;
1162    private boolean zTextSet = false;
1163
1164    /**
1165     * Sets the text strings to be stored in compressed form with this
1166     * image. The data is passed to this method as an array of
1167     * <code>String</code>s.
1168     *
1169     * <p> The 'zTXt' chunk will encode this information.
1170     */

1171    public void setCompressedText(String JavaDoc[] text) {
1172        this.zText = text;
1173        zTextSet = true;
1174    }
1175
1176    /**
1177     * Returns the text strings to be stored in compressed form with
1178     * this image as an array of <code>String</code>s.
1179     *
1180     * <p> If the compressed text strings have not previously been
1181     * set, or have been unset, an <code>IllegalStateException</code>
1182     * will be thrown.
1183     *
1184     * @throws IllegalStateException if the compressed text strings are
1185     * not set.
1186     */

1187    public String JavaDoc[] getCompressedText() {
1188        if (!zTextSet) {
1189            throw new IllegalStateException JavaDoc(PropertyUtil.getString("PNGEncodeParam22"));
1190        }
1191        return zText;
1192    }
1193
1194    /**
1195     * Suppresses the 'zTXt' chunk from being output.
1196     */

1197    public void unsetCompressedText() {
1198        zText = null;
1199        zTextSet = false;
1200    }
1201
1202    /**
1203     * Returns true if a 'zTXT' chunk will be output.
1204     */

1205    public boolean isCompressedTextSet() {
1206        return zTextSet;
1207    }
1208
1209    // Other chunk types
1210

1211    Vector JavaDoc chunkType = new Vector JavaDoc();
1212    Vector JavaDoc chunkData = new Vector JavaDoc();
1213
1214    /**
1215     * Adds a private chunk, in binary form, to the list of chunks to
1216     * be stored with this image.
1217     *
1218     * @param type a 4-character String giving the chunk type name.
1219     * @param data an array of <code>byte</code>s containing the
1220     * chunk data.
1221     */

1222    public synchronized void addPrivateChunk(String JavaDoc type, byte[] data) {
1223        chunkType.add(type);
1224        chunkData.add(data.clone());
1225    }
1226
1227    /**
1228     * Returns the number of private chunks to be written to the
1229     * output file.
1230     */

1231    public synchronized int getNumPrivateChunks() {
1232        return chunkType.size();
1233    }
1234
1235    /**
1236     * Returns the type of the private chunk at a given index, as a
1237     * 4-character <code>String</code>. The index must be smaller
1238     * than the return value of <code>getNumPrivateChunks</code>.
1239     */

1240    public synchronized String JavaDoc getPrivateChunkType(int index) {
1241        return (String JavaDoc)chunkType.elementAt(index);
1242    }
1243
1244    /**
1245     * Returns the data associated of the private chunk at a given
1246     * index, as an array of <code>byte</code>s. The index must be
1247     * smaller than the return value of
1248     * <code>getNumPrivateChunks</code>.
1249     */

1250    public synchronized byte[] getPrivateChunkData(int index) {
1251        return (byte[])chunkData.elementAt(index);
1252    }
1253
1254    /**
1255     * Remove all private chunks associated with this parameter instance
1256     * whose 'safe-to-copy' bit is not set. This may be advisable when
1257     * transcoding PNG images.
1258     */

1259    public synchronized void removeUnsafeToCopyPrivateChunks() {
1260        Vector JavaDoc newChunkType = new Vector JavaDoc();
1261        Vector JavaDoc newChunkData = new Vector JavaDoc();
1262
1263        int len = getNumPrivateChunks();
1264        for (int i = 0; i < len; i++) {
1265            String JavaDoc type = getPrivateChunkType(i);
1266            char lastChar = type.charAt(3);
1267            if (lastChar >= 'a' && lastChar <= 'z') {
1268                newChunkType.add(type);
1269                newChunkData.add(getPrivateChunkData(i));
1270            }
1271        }
1272
1273        chunkType = newChunkType;
1274        chunkData = newChunkData;
1275    }
1276
1277    /**
1278     * Remove all private chunks associated with this parameter instance.
1279     */

1280    public synchronized void removeAllPrivateChunks() {
1281        chunkType = new Vector JavaDoc();
1282        chunkData = new Vector JavaDoc();
1283    }
1284
1285    /**
1286     * An abs() function for use by the Paeth predictor.
1287     */

1288    private static final int abs(int x) {
1289        return (x < 0) ? -x : x;
1290    }
1291
1292    /**
1293     * The Paeth predictor routine used in PNG encoding. This routine
1294     * is included as a convenience to subclasses that override the
1295     * <code>filterRow</code> method.
1296     */

1297    public static final int paethPredictor(int a, int b, int c) {
1298        int p = a + b - c;
1299        int pa = abs(p - a);
1300        int pb = abs(p - b);
1301        int pc = abs(p - c);
1302
1303        if ((pa <= pb) && (pa <= pc)) {
1304            return a;
1305        } else if (pb <= pc) {
1306            return b;
1307        } else {
1308            return c;
1309        }
1310    }
1311
1312    /**
1313     * Performs filtering on a row of an image. This method may be
1314     * overridden in order to provide a custom algorithm for choosing
1315     * the filter type for a given row.
1316     *
1317     * <p> The method is supplied with the current and previous rows
1318     * of the image. For the first row of the image, or of an
1319     * interlacing pass, the previous row array will be filled with
1320     * zeros as required by the PNG specification.
1321     *
1322     * <p> The method is also supplied with five scratch arrays.
1323     * These arrays may be used within the method for any purpose.
1324     * At method exit, the array at the index given by the return
1325     * value of the method should contain the filtered data. The
1326     * return value will also be used as the filter type.
1327     *
1328     * <p> The default implementation of the method performs a trial
1329     * encoding with each of the filter types, and computes the sum of
1330     * absolute values of the differences between the raw bytes of the
1331     * current row and the predicted values. The index of the filter
1332     * producing the smallest result is returned.
1333     *
1334     * <p> As an example, to perform only 'sub' filtering, this method
1335     * could be implemented (non-optimally) as follows:
1336     *
1337     * <pre>
1338     * for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
1339     * int curr = currRow[i] & 0xff;
1340     * int left = currRow[i - bytesPerPixel] & 0xff;
1341     * scratchRow[PNG_FILTER_SUB][i] = (byte)(curr - left);
1342     * }
1343     * return PNG_FILTER_SUB;
1344     * </pre>
1345     *
1346     * @param currRow The current row as an array of <code>byte</code>s
1347     * of length at least <code>bytesPerRow + bytesPerPixel</code>.
1348     * The pixel data starts at index <code>bytesPerPixel</code>;
1349     * the initial <code>bytesPerPixel</code> bytes are zero.
1350     * @param prevRow The current row as an array of <code>byte</code>s
1351     * The pixel data starts at index <code>bytesPerPixel</code>;
1352     * the initial <code>bytesPerPixel</code> bytes are zero.
1353     * @param scratchRows An array of 5 <code>byte</code> arrays of
1354     * length at least <code>bytesPerRow +
1355     * bytesPerPixel</code>, useable to hold temporary results.
1356     * The filtered row will be returned as one of the entries
1357     * of this array. The returned filtered data should start
1358     * at index <code>bytesPerPixel</code>; The initial
1359     * <code>bytesPerPixel</code> bytes are not used.
1360     * @param bytesPerRow The number of bytes in the image row.
1361     * This value will always be greater than 0.
1362     * @param bytesPerPixel The number of bytes representing a single
1363     * pixel, rounded up to an integer. This is the 'bpp' parameter
1364     * described in the PNG specification.
1365     *
1366     * @return The filter type to be used. The entry of
1367     * <code>scratchRows[]</code> at this index holds the
1368     * filtered data. */

1369    public int filterRow(byte[] currRow,
1370                         byte[] prevRow,
1371                         byte[][] scratchRows,
1372                         int bytesPerRow,
1373                         int bytesPerPixel) {
1374
1375        int [] badness = {0, 0, 0, 0, 0};
1376        int curr, left, up, upleft, diff;
1377        int pa, pb, pc;
1378        for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
1379            curr = currRow[i] & 0xff;
1380            left = currRow[i - bytesPerPixel] & 0xff;
1381            up = prevRow[i] & 0xff;
1382            upleft = prevRow[i - bytesPerPixel] & 0xff;
1383                
1384            // no filter
1385
badness[0] += curr;
1386
1387            // sub filter
1388
diff = curr - left;
1389            scratchRows[1][i] = (byte)diff;
1390            badness [1] += (diff>0)?diff:-diff;
1391
1392            // up filter
1393
diff = curr - up;
1394            scratchRows[2][i] = (byte)diff;
1395            badness [2] += (diff>=0)?diff:-diff;
1396                
1397            // average filter
1398
diff = curr - ((left+up)>>1);
1399            scratchRows[3][i] = (byte)diff;
1400            badness [3] += (diff>=0)?diff:-diff;
1401                
1402            // paeth filter
1403

1404            // Original code much simplier but doesn't take full
1405
// advantage of relationship between pa/b/c and
1406
// information gleaned in abs operations.
1407
/// pa = up -upleft;
1408
/// pb = left-upleft;
1409
/// pc = pa+pb;
1410
/// pa = abs(pa);
1411
/// pb = abs(pb);
1412
/// pc = abs(pc);
1413
/// if ((pa <= pb) && (pa <= pc))
1414
/// diff = curr-left;
1415
/// else if (pb <= pc)
1416
/// diff = curr-up;
1417
/// else
1418
/// diff = curr-upleft;
1419

1420            pa = up -upleft;
1421            pb = left-upleft;
1422            if (pa<0) {
1423              if (pb<0) {
1424                // both pa & pb neg so pc is always greater than or
1425
// equal to pa or pb;
1426
if (pa >= pb) // since pa & pb neg check sense is reversed.
1427
diff = curr-left;
1428                else
1429                  diff = curr-up;
1430              } else {
1431                // pa neg pb pos so we must compute pc...
1432
pc = pa+pb;
1433                pa=-pa;
1434                if (pa <= pb) // pc is positive and less than pb
1435
if (pa <= pc)
1436                    diff = curr-left;
1437                  else
1438                    diff = curr-upleft;
1439                else
1440                  // pc is negative and less than or equal to pa,
1441
// but since pa is greater than pb this isn't an issue...
1442
if (pb <= -pc)
1443                    diff = curr-up;
1444                  else
1445                    diff = curr-upleft;
1446              }
1447            } else {
1448              if (pb<0) {
1449                pb =-pb; // make it positive...
1450
if (pa <= pb) {
1451                  // pc would be negative and less than or equal to pb
1452
pc = pb-pa;
1453                  if (pa <= pc)
1454                    diff = curr-left;
1455                  else if (pb == pc)
1456                    // if pa is zero then pc==pb otherwise
1457
// pc must be less than pb.
1458
diff = curr-up;
1459                  else
1460                    diff = curr-upleft;
1461                } else {
1462                  // pc would be positive and less than pa.
1463
pc = pa-pb;
1464                  if (pb <= pc)
1465                    diff = curr-up;
1466                  else
1467                    diff = curr-upleft;
1468                }
1469              } else {
1470                // both pos so pa+pb is always greater than pa/pb
1471
if (pa <= pb)
1472                  diff = curr-left;
1473                else
1474                  diff = curr-up;
1475              }
1476            }
1477            scratchRows[4][i] = (byte)diff;
1478            badness [4] += (diff>=0)?diff:-diff;
1479        }
1480        int filterType = 0;
1481        int minBadness = badness[0];
1482        
1483        for (int i = 1; i < 5; i++) {
1484            if (badness[i] < minBadness) {
1485                minBadness = badness[i];
1486                filterType = i;
1487            }
1488        }
1489
1490        if (filterType == 0) {
1491            System.arraycopy(currRow, bytesPerPixel,
1492                             scratchRows[0], bytesPerPixel,
1493                             bytesPerRow);
1494        }
1495        
1496        return filterType;
1497    }
1498}
1499
Popular Tags