View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  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.commons.imaging;
19  
20  import java.awt.RenderingHints;
21  import java.awt.Transparency;
22  import java.awt.color.ColorSpace;
23  import java.awt.color.ICC_ColorSpace;
24  import java.awt.color.ICC_Profile;
25  import java.awt.image.BufferedImage;
26  import java.awt.image.ColorConvertOp;
27  import java.awt.image.ColorModel;
28  import java.awt.image.ComponentColorModel;
29  import java.awt.image.DirectColorModel;
30  import java.awt.image.ImagingOpException;
31  import java.io.File;
32  import java.io.IOException;
33  
34  /**
35   * A selection of tools for evaluating and manipulating color spaces, color values, etc.
36   * <p>
37   * The Javadoc provided in the original code gave the following notation:
38   * </p>
39   * <p>
40   * TODO"This class is a mess and needs to be cleaned up."
41   * </p>
42   */
43  public class ColorTools {
44  
45      public BufferedImage convertBetweenColorSpaces(BufferedImage bi, final ColorSpace from, final ColorSpace to) {
46          final RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
47          hints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
48          hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
49  
50          final ColorConvertOp op = new ColorConvertOp(from, to, hints);
51  
52          bi = relabelColorSpace(bi, from);
53  
54          final BufferedImage result = op.filter(bi, null);
55  
56          return relabelColorSpace(result, to);
57      }
58  
59      public BufferedImage convertBetweenColorSpacesX2(BufferedImage bi, final ColorSpace from, final ColorSpace to) {
60          final RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
61          hints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
62          hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
63  
64          // bi = relabelColorSpace(bi, cs);
65          // dumpColorSpace("\tcs_sRGB", cs_sRGB);
66          // dumpColorSpace("\tColorModel.getRGBdefaultc",
67          // ColorModel.getRGBdefault().getColorSpace());
68  
69          bi = relabelColorSpace(bi, from);
70          final ColorConvertOp op = new ColorConvertOp(from, to, hints);
71          bi = op.filter(bi, null);
72  
73          bi = relabelColorSpace(bi, from);
74  
75          bi = op.filter(bi, null);
76  
77          return relabelColorSpace(bi, to);
78  
79      }
80  
81      public BufferedImage convertBetweenIccProfiles(final BufferedImage bi, final ICC_Profile from, final ICC_Profile to) {
82          final ICC_ColorSpace csFrom = new ICC_ColorSpace(from);
83          final ICC_ColorSpace csTo = new ICC_ColorSpace(to);
84  
85          return convertBetweenColorSpaces(bi, csFrom, csTo);
86      }
87  
88      protected BufferedImage convertFromColorSpace(final BufferedImage bi, final ColorSpace from) {
89          final ColorModel srgbCM = ColorModel.getRGBdefault();
90          return convertBetweenColorSpaces(bi, from, srgbCM.getColorSpace());
91      }
92  
93      public BufferedImage convertToColorSpace(final BufferedImage bi, final ColorSpace to) {
94          final ColorSpace from = bi.getColorModel().getColorSpace();
95  
96          final RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
97          hints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
98          hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
99  
100         final ColorConvertOp op = new ColorConvertOp(from, to, hints);
101 
102         final BufferedImage result = op.filter(bi, null);
103 
104         return relabelColorSpace(result, to);
105     }
106 
107     public BufferedImage convertToIccProfile(final BufferedImage bi, final ICC_Profile to) {
108         final ICC_ColorSpace csTo = new ICC_ColorSpace(to);
109         return convertToColorSpace(bi, csTo);
110     }
111 
112     public BufferedImage convertTosRgb(final BufferedImage bi) {
113         final ColorModel srgbCM = ColorModel.getRGBdefault();
114         return convertToColorSpace(bi, srgbCM.getColorSpace());
115     }
116 
117     public BufferedImage correctImage(final BufferedImage src, final File file) throws ImagingException, IOException {
118         final ICC_Profile icc = Imaging.getIccProfile(file);
119         if (icc == null) {
120             return src;
121         }
122 
123         final ICC_ColorSpace cs = new ICC_ColorSpace(icc);
124 
125         return convertFromColorSpace(src, cs);
126     }
127 
128     private int countBitsInMask(int i) {
129         int count = 0;
130         while (i != 0) {
131             count += i & 1;
132             // uses the unsigned version of java's right shift operator,
133             // so that left hand bits are zeroed.
134             i >>>= 1;
135         }
136         return count;
137     }
138 
139     public ColorModel deriveColorModel(final BufferedImage bi, final ColorSpace cs) throws ImagingOpException {
140         // boolean hasAlpha = (bi.getAlphaRaster() != null);
141         return deriveColorModel(bi, cs, false);
142     }
143 
144     public ColorModel deriveColorModel(final BufferedImage bi, final ColorSpace cs, final boolean forceNoAlpha) throws ImagingOpException {
145         return deriveColorModel(bi.getColorModel(), cs, forceNoAlpha);
146     }
147 
148     public ColorModel deriveColorModel(final ColorModel colorModel, final ColorSpace cs, final boolean forceNoAlpha) throws ImagingOpException {
149 
150         if (colorModel instanceof ComponentColorModel) {
151             final ComponentColorModel ccm = (ComponentColorModel) colorModel;
152             // ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
153             if (forceNoAlpha) {
154                 return new ComponentColorModel(cs, false, false, Transparency.OPAQUE, ccm.getTransferType());
155             }
156             return new ComponentColorModel(cs, ccm.hasAlpha(), ccm.isAlphaPremultiplied(), ccm.getTransparency(), ccm.getTransferType());
157         }
158         if (colorModel instanceof DirectColorModel) {
159             final DirectColorModel dcm = (DirectColorModel) colorModel;
160 
161             final int oldMask = dcm.getRedMask() | dcm.getGreenMask() | dcm.getBlueMask() | dcm.getAlphaMask();
162 
163             final int oldBits = countBitsInMask(oldMask);
164 
165             return new DirectColorModel(cs, oldBits, dcm.getRedMask(), dcm.getGreenMask(), dcm.getBlueMask(), dcm.getAlphaMask(), dcm.isAlphaPremultiplied(),
166                     dcm.getTransferType());
167         }
168         // else if (old_cm instanceof PackedColorModel)
169         // {
170         // PackedColorModel pcm = (PackedColorModel) old_cm;
171         //
172         // // int old_mask = dcm.getRedMask() | dcm.getGreenMask()
173         // // | dcm.getBlueMask() | dcm.getAlphaMask();
174         //
175         // int[] old_masks = pcm.getMasks();
176         // // System.out.println("old_mask: " + old_mask);
177         // int old_bits = countBitsInMask(old_masks);
178         // // System.out.println("old_bits: " + old_bits);
179         //
180         // // PackedColorModel(ColorSpace space, int bits, int rmask, int gmask,
181         // int bmask, int amask, boolean isAlphaPremultiplied, int trans, int
182         // transferType)
183         // cm = new PackedColorModel(cs, old_bits, pcm.getMasks(),
184         //
185         // pcm.isAlphaPremultiplied(), pcm.getTransparency(), pcm
186         // .getTransferType());
187         // }
188 
189         throw new ImagingOpException("Could not clone unknown ColorModel Type.");
190     }
191 
192     public BufferedImage relabelColorSpace(final BufferedImage bi, final ColorModel cm) throws ImagingOpException {
193         // This does not do the conversion. It tries to relabel the
194         // BufferedImage
195         // with its actual (presumably correct) Colorspace.
196         // use this when the image is mislabeled, presumably having been
197         // wrongly assumed to be sRGB
198 
199         return new BufferedImage(cm, bi.getRaster(), false, null);
200     }
201 
202     public BufferedImage relabelColorSpace(final BufferedImage bi, final ColorSpace cs) throws ImagingOpException {
203         // This does not do the conversion. It tries to relabel the
204         // BufferedImage
205         // with its actual (presumably correct) Colorspace.
206         // use this when the image is mislabeled, presumably having been
207         // wrongly assumed to be sRGB
208 
209         final ColorModel cm = deriveColorModel(bi, cs);
210 
211         return relabelColorSpace(bi, cm);
212 
213     }
214 
215     public BufferedImage relabelColorSpace(final BufferedImage bi, final ICC_Profile profile) throws ImagingOpException {
216         final ICC_ColorSpace cs = new ICC_ColorSpace(profile);
217 
218         return relabelColorSpace(bi, cs);
219     }
220 
221 }