001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.imaging; 019 020import java.awt.RenderingHints; 021import java.awt.Transparency; 022import java.awt.color.ColorSpace; 023import java.awt.color.ICC_ColorSpace; 024import java.awt.color.ICC_Profile; 025import java.awt.image.BufferedImage; 026import java.awt.image.ColorConvertOp; 027import java.awt.image.ColorModel; 028import java.awt.image.ComponentColorModel; 029import java.awt.image.DirectColorModel; 030import java.awt.image.ImagingOpException; 031import java.io.File; 032import java.io.IOException; 033 034/** 035 * A selection of tools for evaluating and manipulating color spaces, color values, etc. 036 * <p> 037 * The Javadoc provided in the original code gave the following notation: 038 * </p> 039 * <p> 040 * TODO"This class is a mess and needs to be cleaned up." 041 * </p> 042 */ 043public class ColorTools { 044 045 public BufferedImage convertBetweenColorSpaces(BufferedImage bi, final ColorSpace from, final ColorSpace to) { 046 final RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 047 hints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); 048 hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); 049 050 final ColorConvertOp op = new ColorConvertOp(from, to, hints); 051 052 bi = relabelColorSpace(bi, from); 053 054 final BufferedImage result = op.filter(bi, null); 055 056 return relabelColorSpace(result, to); 057 } 058 059 public BufferedImage convertBetweenColorSpacesX2(BufferedImage bi, final ColorSpace from, final ColorSpace to) { 060 final RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 061 hints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); 062 hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); 063 064 // bi = relabelColorSpace(bi, cs); 065 // dumpColorSpace("\tcs_sRGB", cs_sRGB); 066 // dumpColorSpace("\tColorModel.getRGBdefaultc", 067 // ColorModel.getRGBdefault().getColorSpace()); 068 069 bi = relabelColorSpace(bi, from); 070 final ColorConvertOp op = new ColorConvertOp(from, to, hints); 071 bi = op.filter(bi, null); 072 073 bi = relabelColorSpace(bi, from); 074 075 bi = op.filter(bi, null); 076 077 return relabelColorSpace(bi, to); 078 079 } 080 081 public BufferedImage convertBetweenIccProfiles(final BufferedImage bi, final ICC_Profile from, final ICC_Profile to) { 082 final ICC_ColorSpace csFrom = new ICC_ColorSpace(from); 083 final ICC_ColorSpace csTo = new ICC_ColorSpace(to); 084 085 return convertBetweenColorSpaces(bi, csFrom, csTo); 086 } 087 088 protected BufferedImage convertFromColorSpace(final BufferedImage bi, final ColorSpace from) { 089 final ColorModel srgbCM = ColorModel.getRGBdefault(); 090 return convertBetweenColorSpaces(bi, from, srgbCM.getColorSpace()); 091 } 092 093 public BufferedImage convertToColorSpace(final BufferedImage bi, final ColorSpace to) { 094 final ColorSpace from = bi.getColorModel().getColorSpace(); 095 096 final RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 097 hints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); 098 hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); 099 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}