View Javadoc
1   /*
2    * SmartSprites Project
3    *
4    * Copyright (C) 2007-2009, Stanisław Osiński.
5    * All rights reserved.
6    *
7    * Redistribution and use in source and binary forms, with or without modification,
8    * are permitted provided that the following conditions are met:
9    *
10   * - Redistributions of  source code must  retain the above  copyright notice, this
11   *   list of conditions and the following disclaimer.
12   *
13   * - Redistributions in binary form must reproduce the above copyright notice, this
14   *   list of conditions and the following  disclaimer in  the documentation  and/or
15   *   other materials provided with the distribution.
16   *
17   * - Neither the name of the SmartSprites Project nor the names of its contributors
18   *   may  be used  to endorse  or  promote  products derived   from  this  software
19   *   without specific prior written permission.
20   *
21   * - We kindly request that you include in the end-user documentation provided with
22   *   the redistribution and/or in the software itself an acknowledgement equivalent
23   *   to  the  following: "This product includes software developed by the SmartSprites
24   *   Project."
25   *
26   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  AND
27   * ANY EXPRESS OR  IMPLIED WARRANTIES, INCLUDING,  BUT NOT LIMITED  TO, THE IMPLIED
28   * WARRANTIES  OF  MERCHANTABILITY  AND  FITNESS  FOR  A  PARTICULAR  PURPOSE   ARE
29   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE  FOR
30   * ANY DIRECT, INDIRECT, INCIDENTAL,  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  DAMAGES
31   * (INCLUDING, BUT  NOT LIMITED  TO, PROCUREMENT  OF SUBSTITUTE  GOODS OR SERVICES;
32   * LOSS OF USE, DATA, OR PROFITS;  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  ON
33   * ANY  THEORY  OF  LIABILITY,  WHETHER  IN  CONTRACT,  STRICT  LIABILITY,  OR TORT
34   * (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY WAY  OUT OF THE USE  OF THIS
35   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36   */
37  package org.carrot2.util;
38  
39  import java.awt.AlphaComposite;
40  import java.awt.Color;
41  import java.awt.CompositeContext;
42  import java.awt.Transparency;
43  import java.awt.image.BufferedImage;
44  import java.awt.image.Raster;
45  import java.util.HashSet;
46  import java.util.Set;
47  
48  /**
49   * Various utility methods for working with {@link BufferedImage}s.
50   */
51  public class BufferedImageUtils {
52  
53      /**
54       * Returns <code>true</code> if the provided image has partially transparent areas (alpha channel).
55       *
56       * @param image
57       *            the image
58       *
59       * @return true, if successful
60       */
61      public static boolean hasPartialTransparency(BufferedImage image) {
62          final Raster alphaRaster = image.getAlphaRaster();
63          if (image.getTransparency() != Transparency.TRANSLUCENT || alphaRaster == null) {
64              return false;
65          }
66  
67          int[] pixels = alphaRaster.getPixels(0, 0, alphaRaster.getWidth(), alphaRaster.getHeight(), (int[]) null);
68          for (int i : pixels) {
69              if (i != 0 && i != 255) {
70                  return true;
71              }
72          }
73  
74          return false;
75      }
76  
77      /**
78       * Returns <code>true</code> if the provided image has any kind of transparent areas.
79       *
80       * @param image
81       *            the image
82       *
83       * @return true, if successful
84       */
85      public static boolean hasTransparency(BufferedImage image) {
86          final Raster alphaRaster = image.getAlphaRaster();
87          if (image.getTransparency() != Transparency.TRANSLUCENT || alphaRaster == null) {
88              return false;
89          }
90  
91          int[] pixels = alphaRaster.getPixels(0, 0, alphaRaster.getWidth(), alphaRaster.getHeight(), (int[]) null);
92          for (int i : pixels) {
93              if (i != 255) {
94                  return true;
95              }
96          }
97  
98          return false;
99      }
100 
101     /**
102      * Returns the number of distinct colors (excluding transparency) in the <code>image</code>.
103      *
104      * @param image
105      *            the image
106      *
107      * @return the int
108      */
109     public static int countDistinctColors(BufferedImage image) {
110         return getDistinctColors(image).length;
111     }
112 
113     /**
114      * Returns the <code>image</code>'s distinct colors in an RGB format, discarding transparency information.
115      *
116      * @param image
117      *            the image
118      *
119      * @return the distinct colors
120      */
121     public static int[] getDistinctColors(BufferedImage image) {
122         return getDistinctColors(image, 0);
123     }
124 
125     /**
126      * Returns the <code>image</code>'s distinct colors in an RGB format, discarding transparency information. Adds
127      * <code>padding</code> empty slots at the beginning of the returned array.
128      *
129      * @param image
130      *            the image
131      * @param padding
132      *            the padding
133      *
134      * @return the distinct colors
135      */
136     public static int[] getDistinctColors(BufferedImage image, int padding) {
137         final int width = image.getWidth();
138         final int height = image.getHeight();
139 
140         final Set<Integer> colors = new HashSet<>();
141 
142         for (int x = 0; x < width; x++) {
143             for (int y = 0; y < height; y++) {
144                 final int pixel = image.getRGB(x, y);
145 
146                 // Count only colors for which alpha is not fully transparent
147                 if ((pixel & 0xff000000) != 0x00000000) {
148                     colors.add(Integer.valueOf(pixel & 0x00ffffff));
149                 }
150             }
151         }
152 
153         final int[] colorMap = new int[colors.size() + padding];
154         int index = padding;
155         for (Integer color : colors) {
156             colorMap[index] = color;
157             index++;
158         }
159 
160         return colorMap;
161     }
162 
163     /**
164      * Returns a two dimensional array of the <code>image</code>'s RGB values, including transparency.
165      *
166      * @param image
167      *            the image
168      *
169      * @return the rgb
170      */
171     public static int[][] getRgb(BufferedImage image) {
172         final int width = image.getWidth();
173         final int height = image.getHeight();
174 
175         final int[][] rgb = new int[width][height];
176 
177         for (int x = 0; x < width; x++) {
178             for (int y = 0; y < height; y++) {
179                 rgb[x][y] = image.getRGB(x, y);
180             }
181         }
182 
183         return rgb;
184     }
185 
186     /**
187      * Performs matting of the <code>source</code> image using <code>matteColor</code>. Matting is rendering partial
188      * transparencies using solid color as if the original image was put on top of a bitmap filled with
189      * <code>matteColor</code>.
190      *
191      * @param source
192      *            the source
193      * @param matteColor
194      *            the matte color
195      *
196      * @return the buffered image
197      */
198     public static BufferedImage matte(BufferedImage source, Color matteColor) {
199         final int width = source.getWidth();
200         final int height = source.getHeight();
201 
202         // A workaround for possibly different custom image types we can get:
203         // draw a copy of the image
204         final BufferedImage sourceConverted = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
205         sourceConverted.getGraphics().drawImage(source, 0, 0, null);
206 
207         final BufferedImage matted = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
208 
209         final BufferedImage matte = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
210         final int matteRgb = matteColor.getRGB();
211         for (int x = 0; x < width; x++) {
212             for (int y = 0; y < height; y++) {
213                 matte.setRGB(x, y, matteRgb);
214             }
215         }
216 
217         CompositeContext context = AlphaComposite.DstOver.createContext(matte.getColorModel(),
218                 sourceConverted.getColorModel(), null);
219         context.compose(matte.getRaster(), sourceConverted.getRaster(), matted.getRaster());
220 
221         return matted;
222     }
223 
224     /**
225      * Draws <code>image</code> on the <code>canvas</code> placing the top left corner of <code>image</code> at
226      * <code>x</code> / <code>y</code> offset from the top left corner of <code>canvas</code>.
227      *
228      * @param image
229      *            the image
230      * @param canvas
231      *            the canvas
232      * @param x
233      *            the x
234      * @param y
235      *            the y
236      */
237     public static void drawImage(BufferedImage image, BufferedImage canvas, int x, int y) {
238         final int[] imgRGB = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
239         canvas.setRGB(x, y, image.getWidth(), image.getHeight(), imgRGB, 0, image.getWidth());
240     }
241 
242     /**
243      * Instantiates a new buffered image utils.
244      */
245     private BufferedImageUtils() {
246     }
247 }