1 7 8 package com.sun.imageio.plugins.common; 9 10 import java.awt.Transparency ; 11 import java.awt.image.BufferedImage ; 12 import java.awt.image.RenderedImage ; 13 import java.awt.image.ColorModel ; 14 import java.awt.image.IndexColorModel ; 15 import java.awt.image.Raster ; 16 import java.awt.image.WritableRaster ; 17 import java.awt.Color ; 18 import javax.imageio.ImageTypeSpecifier ; 19 20 21 26 public class PaletteBuilder { 27 28 31 protected static final int MAXLEVEL = 8; 32 33 protected RenderedImage src; 34 protected ColorModel srcColorModel; 35 protected Raster srcRaster; 36 37 protected int requiredSize; 38 39 protected ColorNode root; 40 41 protected int numNodes; 42 protected int maxNodes; 43 protected int currLevel; 44 protected int currSize; 45 46 protected ColorNode[] reduceList; 47 protected ColorNode[] palette; 48 49 protected int transparency; 50 protected ColorNode transColor; 51 52 53 74 public static RenderedImage createIndexedImage(RenderedImage src) { 75 PaletteBuilder pb = new PaletteBuilder(src); 76 pb.buildPalette(); 77 return pb.getIndexedImage(); 78 } 79 80 97 public static IndexColorModel createIndexColorModel(RenderedImage img) { 98 PaletteBuilder pb = new PaletteBuilder(img); 99 pb.buildPalette(); 100 return pb.getIndexColorModel(); 101 } 102 103 116 public static boolean canCreatePalette(ImageTypeSpecifier type) { 117 if (type == null) { 118 throw new IllegalArgumentException ("type == null"); 119 } 120 return true; 121 } 122 123 136 public static boolean canCreatePalette(RenderedImage image) { 137 if (image == null) { 138 throw new IllegalArgumentException ("image == null"); 139 } 140 ImageTypeSpecifier type = new ImageTypeSpecifier (image); 141 return canCreatePalette(type); 142 } 143 144 protected RenderedImage getIndexedImage() { 145 IndexColorModel icm = getIndexColorModel(); 146 147 BufferedImage dst = 148 new BufferedImage (src.getWidth(), src.getHeight(), 149 BufferedImage.TYPE_BYTE_INDEXED, icm); 150 151 WritableRaster wr = dst.getRaster(); 152 for (int y =0; y < dst.getHeight(); y++) { 153 for (int x = 0; x < dst.getWidth(); x++) { 154 Color aColor = getSrcColor(x,y); 155 wr.setSample(x, y, 0, findColorIndex(root, aColor)); 156 } 157 } 158 159 return dst; 160 } 161 162 163 protected PaletteBuilder(RenderedImage src) { 164 this(src, 256); 165 } 166 167 protected PaletteBuilder(RenderedImage src, int size) { 168 this.src = src; 169 this.srcColorModel = src.getColorModel(); 170 this.srcRaster = src.getData(); 171 172 this.transparency = 173 srcColorModel.getTransparency(); 174 175 if (transparency != Transparency.OPAQUE) { 176 this.requiredSize = size - 1; 177 transColor = new ColorNode(); 178 transColor.isLeaf = true; 179 } else { 180 this.requiredSize = size; 181 } 182 } 183 184 private Color getSrcColor(int x, int y) { 185 int argb = srcColorModel.getRGB(srcRaster.getDataElements(x, y, null)); 186 return new Color (argb, transparency != Transparency.OPAQUE); 187 } 188 189 protected int findColorIndex(ColorNode aNode, Color aColor) { 190 if (transparency != Transparency.OPAQUE && 191 aColor.getAlpha() != 0xff) 192 { 193 return 0; } 195 196 if (aNode.isLeaf) { 197 return aNode.paletteIndex; 198 } else { 199 int childIndex = getBranchIndex(aColor, aNode.level); 200 201 return findColorIndex(aNode.children[childIndex], aColor); 202 } 203 } 204 205 protected void buildPalette() { 206 reduceList = new ColorNode[MAXLEVEL + 1]; 207 for (int i = 0; i < reduceList.length; i++) { 208 reduceList[i] = null; 209 } 210 211 numNodes = 0; 212 maxNodes = 0; 213 root = null; 214 currSize = 0; 215 currLevel = MAXLEVEL; 216 217 221 222 int w = src.getWidth(); 223 int h = src.getHeight(); 224 for (int y = 0; y < h; y++) { 225 for (int x = 0; x < w; x++) { 226 227 Color aColor = getSrcColor(w - x - 1, h - y - 1); 228 232 if (transparency != Transparency.OPAQUE && 233 aColor.getAlpha() != 0xff) 234 { 235 transColor = insertNode(transColor, aColor, 0); 236 } else { 237 root = insertNode(root, aColor, 0); 238 } 239 if (currSize > requiredSize) { 240 reduceTree(); 241 } 242 } 243 } 244 } 245 246 protected ColorNode insertNode(ColorNode aNode, Color aColor, int aLevel) { 247 248 if (aNode == null) { 249 aNode = new ColorNode(); 250 numNodes++; 251 if (numNodes > maxNodes) { 252 maxNodes = numNodes; 253 } 254 aNode.level = aLevel; 255 aNode.isLeaf = (aLevel > MAXLEVEL); 256 if (aNode.isLeaf) { 257 currSize++; 258 } 259 } 260 aNode.colorCount++; 261 aNode.red += aColor.getRed(); 262 aNode.green += aColor.getGreen(); 263 aNode.blue += aColor.getBlue(); 264 265 if (!aNode.isLeaf) { 266 int branchIndex = getBranchIndex(aColor, aLevel); 267 if (aNode.children[branchIndex] == null) { 268 aNode.childCount++; 269 if (aNode.childCount == 2) { 270 aNode.nextReducible = reduceList[aLevel]; 271 reduceList[aLevel] = aNode; 272 } 273 } 274 aNode.children[branchIndex] = 275 insertNode(aNode.children[branchIndex], aColor, aLevel + 1); 276 } 277 return aNode; 278 } 279 280 protected IndexColorModel getIndexColorModel() { 281 int size = currSize; 282 if (transparency == Transparency.BITMASK) { 283 size ++; } 285 286 byte[] red = new byte[size]; 287 byte[] green = new byte[size]; 288 byte[] blue = new byte[size]; 289 290 int index = 0; 291 palette = new ColorNode[size]; 292 if (transparency == Transparency.BITMASK) { 293 index ++; 294 } 295 296 int lastIndex = findPaletteEntry(root, index, red, green, blue); 297 298 IndexColorModel icm = null; 299 if (transparency == Transparency.BITMASK) { 300 icm = new IndexColorModel (8, size, red, green, blue, 0); 301 } else { 302 icm = new IndexColorModel (8, currSize, red, green, blue); 303 } 304 return icm; 305 } 306 307 protected int findPaletteEntry(ColorNode aNode, int index, 308 byte[] red, byte[] green, byte[] blue) 309 { 310 if (aNode.isLeaf) { 311 red[index] = (byte)(aNode.red/aNode.colorCount); 312 green[index] = (byte)(aNode.green/aNode.colorCount); 313 blue[index] = (byte)(aNode.blue/aNode.colorCount); 314 aNode.paletteIndex = index; 315 316 palette[index] = aNode; 317 318 index++; 319 } else { 320 for (int i = 0; i < 8; i++) { 321 if (aNode.children[i] != null) { 322 index = findPaletteEntry(aNode.children[i], index, 323 red, green, blue); 324 } 325 } 326 } 327 return index; 328 } 329 330 protected int getBranchIndex(Color aColor, int aLevel) { 331 if (aLevel > MAXLEVEL || aLevel < 0) { 332 throw new IllegalArgumentException ("Invalid octree node depth: " + 333 aLevel); 334 } 335 336 int shift = MAXLEVEL - aLevel; 337 int red_index = 0x1 & ((0xff & aColor.getRed()) >> shift); 338 int green_index = 0x1 & ((0xff & aColor.getGreen()) >> shift); 339 int blue_index = 0x1 & ((0xff & aColor.getBlue()) >> shift); 340 int index = (red_index << 2) | (green_index << 1) | blue_index; 341 return index; 342 } 343 344 protected void reduceTree() { 345 int level = reduceList.length - 1; 346 while (reduceList[level] == null && level >= 0) { 347 level--; 348 } 349 350 ColorNode thisNode = reduceList[level]; 351 if (thisNode == null) { 352 return; 354 } 355 356 ColorNode pList = thisNode; 358 int minColorCount = pList.colorCount; 359 360 int cnt = 1; 361 while (pList.nextReducible != null) { 362 if (minColorCount > pList.nextReducible.colorCount) { 363 thisNode = pList; 364 minColorCount = pList.colorCount; 365 } 366 pList = pList.nextReducible; 367 cnt++; 368 } 369 370 if (thisNode == reduceList[level]) { 373 reduceList[level] = thisNode.nextReducible; 374 } else { 375 pList = thisNode.nextReducible; thisNode.nextReducible = pList.nextReducible; 377 thisNode = pList; 378 } 379 380 if (thisNode.isLeaf) { 381 return; 382 } 383 384 int leafChildCount = thisNode.getLeafChildCount(); 386 thisNode.isLeaf = true; 387 currSize -= (leafChildCount - 1); 388 int aDepth = thisNode.level; 389 for (int i = 0; i < 8; i++) { 390 thisNode.children[i] = freeTree(thisNode.children[i]); 391 } 392 thisNode.childCount = 0; 393 } 394 395 protected ColorNode freeTree(ColorNode aNode) { 396 if (aNode == null) { 397 return null; 398 } 399 for (int i = 0; i < 8; i++) { 400 aNode.children[i] = freeTree(aNode.children[i]); 401 } 402 403 numNodes--; 404 return null; 405 } 406 407 410 protected class ColorNode { 411 public boolean isLeaf; 412 public int childCount; 413 ColorNode[] children; 414 415 public int colorCount; 416 public long red; 417 public long blue; 418 public long green; 419 420 public int paletteIndex; 421 422 public int level; 423 ColorNode nextReducible; 424 425 public ColorNode() { 426 isLeaf = false; 427 level = 0; 428 childCount = 0; 429 children = new ColorNode[8]; 430 for (int i = 0; i < 8; i++) { 431 children[i] = null; 432 } 433 434 colorCount = 0; 435 red = green = blue = 0; 436 437 paletteIndex = 0; 438 } 439 440 public int getLeafChildCount() { 441 if (isLeaf) { 442 return 0; 443 } 444 int cnt = 0; 445 for (int i = 0; i < children.length; i++) { 446 if (children[i] != null) { 447 if (children[i].isLeaf) { 448 cnt ++; 449 } else { 450 cnt += children[i].getLeafChildCount(); 451 } 452 } 453 } 454 return cnt; 455 } 456 457 public int getRGB() { 458 int r = (int)red/colorCount; 459 int g = (int)green/colorCount; 460 int b = (int)blue/colorCount; 461 462 int c = 0xff << 24 | (0xff&r) << 16 | (0xff&g) << 8 | (0xff&b); 463 return c; 464 } 465 } 466 } 467 | Popular Tags |