001 /* 002 * UniformPaletteQuantizer 003 * 004 * Copyright (c) 2001, 2002, 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.color.quantization; 009 010 import net.sourceforge.jiu.color.quantization.RGBQuantizer; 011 import net.sourceforge.jiu.data.MemoryPaletted8Image; 012 import net.sourceforge.jiu.data.Palette; 013 import net.sourceforge.jiu.data.Paletted8Image; 014 import net.sourceforge.jiu.data.RGB24Image; 015 import net.sourceforge.jiu.data.RGBIndex; 016 import net.sourceforge.jiu.ops.ImageToImageOperation; 017 018 /** 019 * A color quantizer that maps to a palette which is equidistantly distributed 020 * in the RGB color cube. 021 * Equidistantly distributed only within each channel. 022 * @author Marco Schmidt 023 */ 024 public class UniformPaletteQuantizer extends ImageToImageOperation implements 025 RGBIndex, 026 RGBQuantizer 027 { 028 private final int RED_BITS; 029 private final int RED_LEFT_SHIFT; 030 private final int RED_RIGHT_SHIFT; 031 private final int[] RED_VALUES; 032 private final int GREEN_BITS; 033 private final int GREEN_LEFT_SHIFT; 034 private final int GREEN_RIGHT_SHIFT; 035 private final int[] GREEN_VALUES; 036 private final int BLUE_BITS; 037 private final int BLUE_RIGHT_SHIFT; 038 private final int[] BLUE_VALUES; 039 private final int TOTAL_BITS; 040 private int[] PALETTE_RED; 041 private int[] PALETTE_GREEN; 042 private int[] PALETTE_BLUE; 043 044 public UniformPaletteQuantizer(int redBits, int greenBits, int blueBits) 045 { 046 if (redBits < 1) 047 { 048 throw new IllegalArgumentException("Must have at least 1 bit for red."); 049 } 050 if (greenBits < 1) 051 { 052 throw new IllegalArgumentException("Must have at least 1 bit for green."); 053 } 054 if (blueBits < 1) 055 { 056 throw new IllegalArgumentException("Must have at least 1 bit for blue."); 057 } 058 BLUE_BITS = blueBits; 059 BLUE_RIGHT_SHIFT = 8 - BLUE_BITS; 060 BLUE_VALUES = new int[1 << BLUE_BITS]; 061 for (int i = 0; i < BLUE_VALUES.length; i++) 062 BLUE_VALUES[i] = i * 255 / (BLUE_VALUES.length - 1); 063 GREEN_BITS = greenBits; 064 GREEN_RIGHT_SHIFT = 8 - GREEN_BITS; 065 GREEN_LEFT_SHIFT = BLUE_BITS; 066 GREEN_VALUES = new int[1 << GREEN_BITS]; 067 for (int i = 0; i < GREEN_VALUES.length; i++) 068 GREEN_VALUES[i] = i * 255 / (GREEN_VALUES.length - 1); 069 RED_BITS = redBits; 070 RED_RIGHT_SHIFT = 8 - RED_BITS; 071 RED_LEFT_SHIFT = GREEN_BITS + BLUE_BITS; 072 RED_VALUES = new int[1 << RED_BITS]; 073 for (int i = 0; i < RED_VALUES.length; i++) 074 RED_VALUES[i] = i * 255 / (RED_VALUES.length - 1); 075 TOTAL_BITS = RED_BITS + GREEN_BITS + BLUE_BITS; 076 if (TOTAL_BITS > 8) 077 { 078 throw new IllegalArgumentException("Sum of red / green / blue bits must not exceed 8."); 079 } 080 } 081 082 public Palette createPalette() 083 { 084 int numEntries = 1 << TOTAL_BITS; 085 Palette result = new Palette(numEntries, 255); 086 PALETTE_RED = new int[numEntries]; 087 PALETTE_GREEN = new int[numEntries]; 088 PALETTE_BLUE = new int[numEntries]; 089 int index = 0; 090 for (int r = 0; r < (1 << RED_BITS); r++) 091 { 092 for (int g = 0; g < (1 << GREEN_BITS); g++) 093 { 094 for (int b = 0; b < (1 << BLUE_BITS); b++) 095 { 096 //System.out.println(index + ":" + r + ", " + g + ", " + b); 097 result.putSample(INDEX_RED, index, RED_VALUES[r]); 098 PALETTE_RED[index] = RED_VALUES[r]; 099 result.putSample(INDEX_GREEN, index, GREEN_VALUES[g]); 100 PALETTE_GREEN[index] = GREEN_VALUES[g]; 101 result.putSample(INDEX_BLUE, index, BLUE_VALUES[b]); 102 PALETTE_BLUE[index] = BLUE_VALUES[b]; 103 index++; 104 } 105 } 106 } 107 return result; 108 } 109 110 public int map(int[] origRgb, int[] quantizedRgb) 111 { 112 int index = mapToIndex(origRgb[INDEX_RED], origRgb[INDEX_GREEN], origRgb[INDEX_BLUE]); 113 quantizedRgb[INDEX_RED] = PALETTE_RED[index]; 114 quantizedRgb[INDEX_GREEN] = PALETTE_GREEN[index]; 115 quantizedRgb[INDEX_BLUE] = PALETTE_BLUE[index]; 116 return index; 117 } 118 119 public final int mapToIndex(int red, int green, int blue) 120 { 121 return 122 ((red >> RED_RIGHT_SHIFT) << RED_LEFT_SHIFT) | 123 ((green >> GREEN_RIGHT_SHIFT) << GREEN_LEFT_SHIFT) | 124 (blue >> BLUE_RIGHT_SHIFT); 125 } 126 127 private void process(RGB24Image in, Paletted8Image out) 128 { 129 final int WIDTH = in.getWidth(); 130 final int HEIGHT = in.getHeight(); 131 if (out == null) 132 { 133 out = new MemoryPaletted8Image(WIDTH, HEIGHT, createPalette()); 134 } 135 for (int y = 0; y < HEIGHT; y++) 136 { 137 for (int x = 0; x < WIDTH; x++) 138 { 139 int r = in.getSample(INDEX_RED, x, y); 140 int g = in.getSample(INDEX_GREEN, x, y); 141 int b = in.getSample(INDEX_BLUE, x, y); 142 out.putSample(0, x, y, mapToIndex(r, g, b)); 143 } 144 setProgress(y, HEIGHT); 145 } 146 setOutputImage(out); 147 } 148 149 public void process() 150 { 151 process((RGB24Image)getInputImage(), (Paletted8Image)getOutputImage()); 152 } 153 }