001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.mobility.svgcore.export;
042:
043: import java.awt.*;
044: import java.awt.image.BufferedImage;
045: import java.awt.image.IndexColorModel;
046: import java.awt.image.MemoryImageSource;
047: import java.awt.image.WritableRaster;
048:
049: public final class Quantizer {
050: private static final int MAXCOLORS = 255;
051: private static final int TRANSPARENT_COLOR = 255;
052: private static final int HSIZE = 32768;
053:
054: private static final class ColorCube {
055: private int lower;
056: private int upper;
057: private int count;
058: private int level;
059: private int rmin;
060: private int rmax;
061: private int gmin;
062: private int gmax;
063: private int bmin;
064: private int bmax;
065:
066: ColorCube() {
067: count = 0;
068: }
069: }
070:
071: private final int[] m_pixels32;
072: private final int m_width;
073: private final int m_height;
074: private final int[] m_hist;
075: private int[] m_histPtr;
076: private ColorCube[] m_list;
077: private IndexColorModel m_cm;
078:
079: public Quantizer(int[] pixels, int width, int height) {
080: m_pixels32 = pixels;
081: m_width = width;
082: m_height = height;
083:
084: m_hist = new int[HSIZE];
085: for (int i = 0; i < width * height; i++) {
086: m_hist[rgb(m_pixels32[i])]++;
087: }
088: }
089:
090: public int getColorCount() {
091: int count = 0;
092:
093: for (int i = 0; i < HSIZE; i++) {
094: if (m_hist[i] > 0) {
095: count++;
096: }
097: }
098:
099: return count;
100: }
101:
102: public Color getModalColor() {
103: int max = 0;
104: int c = 0;
105:
106: for (int i = 0; i < HSIZE; i++) {
107: if (m_hist[i] > max) {
108: max = m_hist[i];
109: c = i;
110: }
111: }
112: return new Color(red(c), green(c), blue(c));
113: }
114:
115: public BufferedImage toImage() {
116: int lr, lg, lb;
117: int i, median, color;
118: int count;
119: int k, level, ncubes, splitpos;
120: int longdim = 0;
121: ColorCube cube, cubeA, cubeB;
122:
123: m_list = new ColorCube[MAXCOLORS];
124: m_histPtr = new int[HSIZE];
125: ncubes = 0;
126: cube = new ColorCube();
127: for (i = 0, color = 0; i <= HSIZE - 1; i++) {
128: if (m_hist[i] != 0) {
129: m_histPtr[color++] = i;
130: cube.count = cube.count + m_hist[i];
131: }
132: }
133: cube.lower = 0;
134: cube.upper = color - 1;
135: cube.level = 0;
136: reduce(cube);
137: m_list[ncubes++] = cube;
138:
139: while (ncubes < MAXCOLORS) {
140: level = 255;
141: splitpos = -1;
142: for (k = 0; k <= ncubes - 1; k++) {
143: if (m_list[k].lower == m_list[k].upper)
144: ; // single color; cannot be split
145: else if (m_list[k].level < level) {
146: level = m_list[k].level;
147: splitpos = k;
148: }
149: }
150: if (splitpos == -1)
151: break;
152:
153: cube = m_list[splitpos];
154: lr = cube.rmax - cube.rmin;
155: lg = cube.gmax - cube.gmin;
156: lb = cube.bmax - cube.bmin;
157: if (lr >= lg && lr >= lb)
158: longdim = 0;
159: if (lg >= lr && lg >= lb)
160: longdim = 1;
161: if (lb >= lr && lb >= lg)
162: longdim = 2;
163:
164: changeColorOrder(m_histPtr, cube.lower, cube.upper, longdim);
165: quickSort(m_histPtr, cube.lower, cube.upper);
166: resetColorOrder(m_histPtr, cube.lower, cube.upper, longdim);
167:
168: count = 0;
169: for (i = cube.lower; i <= cube.upper - 1; i++) {
170: if (count >= cube.count / 2)
171: break;
172: color = m_histPtr[i];
173: count = count + m_hist[color];
174: }
175: median = i;
176:
177: cubeA = new ColorCube();
178: cubeA.lower = cube.lower;
179: cubeA.upper = median - 1;
180: cubeA.count = count;
181: cubeA.level = cube.level + 1;
182: reduce(cubeA);
183: m_list[splitpos] = cubeA;
184:
185: cubeB = new ColorCube();
186: cubeB.lower = median;
187: cubeB.upper = cube.upper;
188: cubeB.count = cube.count - count;
189: cubeB.level = cube.level + 1;
190: reduce(cubeB);
191: m_list[ncubes++] = cubeB;
192: }
193:
194: invertMap(m_hist, ncubes);
195: return makeBufferedImage();
196: }
197:
198: private void reduce(ColorCube cube) {
199: int r, g, b;
200: int color;
201: int rmin, rmax, gmin, gmax, bmin, bmax;
202:
203: rmin = 255;
204: rmax = 0;
205: gmin = 255;
206: gmax = 0;
207: bmin = 255;
208: bmax = 0;
209: for (int i = cube.lower; i <= cube.upper; i++) {
210: color = m_histPtr[i];
211: r = red(color);
212: g = green(color);
213: b = blue(color);
214: if (r > rmax)
215: rmax = r;
216: if (r < rmin)
217: rmin = r;
218: if (g > gmax)
219: gmax = g;
220: if (g < gmin)
221: gmin = g;
222: if (b > bmax)
223: bmax = b;
224: if (b < bmin)
225: bmin = b;
226: }
227: cube.rmin = rmin;
228: cube.rmax = rmax;
229: cube.gmin = gmin;
230: cube.gmax = gmax;
231: cube.gmin = gmin;
232: cube.gmax = gmax;
233: }
234:
235: private void invertMap(int[] hist, int ncubes) {
236: ColorCube cube;
237: int r, g, b;
238: float rsum, gsum, bsum;
239: int color;
240: byte[] rLUT = new byte[MAXCOLORS + 1];
241: byte[] gLUT = new byte[MAXCOLORS + 1];
242: byte[] bLUT = new byte[MAXCOLORS + 1];
243:
244: for (int k = 0; k <= ncubes - 1; k++) {
245: cube = m_list[k];
246: rsum = gsum = bsum = (float) 0.0;
247: for (int i = cube.lower; i <= cube.upper; i++) {
248: color = m_histPtr[i];
249: r = red(color);
250: rsum += (float) r * (float) hist[color];
251: g = green(color);
252: gsum += (float) g * (float) hist[color];
253: b = blue(color);
254: bsum += (float) b * (float) hist[color];
255: }
256:
257: r = (int) (rsum / (float) cube.count);
258: g = (int) (gsum / (float) cube.count);
259: b = (int) (bsum / (float) cube.count);
260: if (r == 248 && g == 248 && b == 248)
261: r = g = b = 255;
262: rLUT[k] = (byte) r;
263: gLUT[k] = (byte) g;
264: bLUT[k] = (byte) b;
265: }
266: m_cm = new IndexColorModel(8, MAXCOLORS + 1, rLUT, gLUT, bLUT,
267: TRANSPARENT_COLOR);
268:
269: for (int k = 0; k <= ncubes - 1; k++) {
270: cube = m_list[k];
271: for (int i = cube.lower; i <= cube.upper; i++) {
272: color = m_histPtr[i];
273: hist[color] = k;
274: }
275: }
276: }
277:
278: private void changeColorOrder(int[] a, int lo, int hi, int longDim) {
279: int c, r, g, b;
280: switch (longDim) {
281: case 0:
282: for (int i = lo; i <= hi; i++) {
283: c = a[i];
284: r = c & 31;
285: a[i] = (r << 10) | (c >> 5);
286: }
287: break;
288: case 1:
289: for (int i = lo; i <= hi; i++) {
290: c = a[i];
291: r = c & 31;
292: g = (c >> 5) & 31;
293: b = c >> 10;
294: a[i] = (g << 10) | (b << 5) | r;
295: }
296: break;
297: case 2:
298: break;
299: }
300: }
301:
302: void resetColorOrder(int[] a, int lo, int hi, int longDim) {
303: int c, r, g, b;
304: switch (longDim) {
305: case 0:
306: for (int i = lo; i <= hi; i++) {
307: c = a[i];
308: r = c >> 10;
309: a[i] = ((c & 1023) << 5) | r;
310: }
311: break;
312: case 1:
313: for (int i = lo; i <= hi; i++) {
314: c = a[i];
315: r = c & 31;
316: g = c >> 10;
317: b = (c >> 5) & 31;
318: a[i] = (b << 10) | (g << 5) | r;
319: }
320: break;
321: case 2:
322: break;
323: }
324: }
325:
326: void quickSort(int a[], int lo0, int hi0) {
327: int lo = lo0;
328: int hi = hi0;
329: int mid, t;
330:
331: if (hi0 > lo0) {
332: mid = a[(lo0 + hi0) / 2];
333: while (lo <= hi) {
334: while ((lo < hi0) && (a[lo] < mid))
335: ++lo;
336: while ((hi > lo0) && (a[hi] > mid))
337: --hi;
338: if (lo <= hi) {
339: t = a[lo];
340: a[lo] = a[hi];
341: a[hi] = t;
342: ++lo;
343: --hi;
344: }
345: }
346: if (lo0 < hi)
347: quickSort(a, lo0, hi);
348: if (lo < hi0)
349: quickSort(a, lo, hi0);
350:
351: }
352: }
353:
354: byte[] toByteArray() {
355: byte[] pixels8;
356: int color16;
357: pixels8 = new byte[m_width * m_height];
358: for (int i = 0; i < m_width * m_height; i++) {
359: color16 = rgb(m_pixels32[i]);
360: pixels8[i] = (byte) m_hist[color16];
361: }
362: return pixels8;
363: }
364:
365: BufferedImage makeBufferedImage() {
366: byte[] pixels8 = toByteArray();
367: BufferedImage bufferedImage = new BufferedImage(m_cm, m_cm
368: .createCompatibleWritableRaster(m_width, m_height),
369: false, null);
370:
371: WritableRaster raster = bufferedImage.getRaster();
372: int[] pixelArray = new int[1];
373: int i = 0;
374: for (int x = 0; x < m_width; x++) {
375: for (int y = 0; y < m_height; y++) {
376: int pixel = m_pixels32[i];
377: if ((pixel >>> 24) > 127) {
378: pixelArray[0] = pixels8[i++];
379: } else {
380: pixelArray[0] = 255;
381: i++;
382: }
383: raster.setPixel(x, y, pixelArray);
384: }
385: }
386: return bufferedImage;
387: }
388:
389: Image makeImage() {
390: byte[] pixels8 = toByteArray();
391: Image img8 = Toolkit.getDefaultToolkit().createImage(
392: new MemoryImageSource(m_width, m_height, m_cm, pixels8,
393: 0, m_width));
394: return img8;
395: }
396:
397: private final int rgb(int c) {
398: int r = (c & 0xf80000) >> 19;
399: int g = (c & 0xf800) >> 6;
400: int b = (c & 0xf8) << 7;
401: return b | g | r;
402: }
403:
404: private final int red(int x) {
405: return (x & 31) << 3;
406: }
407:
408: private final int green(int x) {
409: return (x >> 2) & 0xf8;
410: }
411:
412: private final int blue(int x) {
413: return (x >> 7) & 0xf8;
414: }
415: }
|