001 /* 002 * Palette 003 * 004 * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.data; 009 010 import net.sourceforge.jiu.data.RGBIndex; 011 012 /** 013 * This class represents a palette, a list of RGB colors. 014 * An RGB color here has three int values for its red, green and blue 015 * intensity. 016 * Each intensity value must be larger than or equal to zero and 017 * smaller than or equal to the maximum intensity value that can be 018 * given to the constructor {@link #Palette(int, int)}. 019 * This maximum value is typically 255. 020 * Note that the number of entries in a palette is restricted only 021 * by the element index type <code>int</code> so that palettes with 022 * more than 256 entries are no problem. 023 * When accessing (reading or writing) samples of this palette, use 024 * the constants {@link #INDEX_RED}, {@link #INDEX_GREEN} and {@link #INDEX_BLUE} of 025 * this class to define a color channel. 026 * @author Marco Schmidt 027 * @see net.sourceforge.jiu.data.PalettedImage 028 */ 029 public class Palette implements RGBIndex 030 { 031 private int[][] data; 032 private int numEntries; 033 private int maxValue; 034 035 /** 036 * Create a palette with the given number of entries and a maximum value 037 * for each sample. 038 * @param numEntries the number of entries to be accessible in this palette 039 * @param maxValue the maximum value to be allowed for each sample 040 */ 041 public Palette(int numEntries, int maxValue) 042 { 043 if (numEntries < 1) 044 { 045 throw new IllegalArgumentException("Error -- numEntries must be larger than 0."); 046 } 047 this.numEntries = numEntries; 048 this.maxValue = maxValue; 049 data = new int[3][]; 050 for (int i = 0; i < 3; i++) 051 { 052 data[i] = new int[numEntries]; 053 } 054 } 055 056 /** 057 * Create a palette with the given number of entries and a maximum value 058 * of <code>255</code>. 059 * @param numEntries the number of entries to be accessible in this palette 060 */ 061 public Palette(int numEntries) 062 { 063 this(numEntries, 255); 064 } 065 066 /** 067 * Creates a copy of this palette, allocating a new Palette object 068 * and copying each RGB triplet to the new palette. 069 * Then returns the new palette. 070 * Thus, a "deep" copy of this Palette object is created, 071 * not a "shallow" one. 072 * 073 * @return newly-created palette 074 */ 075 public Object clone() 076 { 077 Palette result = new Palette(getNumEntries(), getMaxValue()); 078 for (int i = 0; i < getNumEntries(); i++) 079 { 080 result.putSample(INDEX_RED, i, getSample(INDEX_RED, i)); 081 result.putSample(INDEX_GREEN, i, getSample(INDEX_GREEN, i)); 082 result.putSample(INDEX_BLUE, i, getSample(INDEX_BLUE, i)); 083 } 084 return result; 085 } 086 087 /** 088 * Returns the amount of memory in bytes allocated for this palette. 089 * 090 */ 091 public long getAllocatedMemory() 092 { 093 long result = 0; 094 if (data != null) 095 { 096 for (int i = 0; i < data.length; i++) 097 { 098 if (data[i] != null) 099 { 100 result += data[i].length; 101 } 102 } 103 } 104 return result; 105 } 106 107 /** 108 * Returns the maximum value allowed for a sample. 109 * @return the maximum sample value 110 */ 111 public int getMaxValue() 112 { 113 return maxValue; 114 } 115 116 /** 117 * Returns the number of entries in this palette. 118 * @return the number of entries in this palette 119 */ 120 public int getNumEntries() 121 { 122 return numEntries; 123 } 124 125 /** 126 * Returns one of the samples of this palette. 127 * @param entryIndex the index of the color to be addressed, must be from 128 * <code>0</code> to <code>getNumEntries() - 1</code> 129 * @param channelIndex one of the three channels; must be {@link #INDEX_RED}, 130 * {@link #INDEX_GREEN} or {@link #INDEX_BLUE} 131 * @return the requested sample 132 */ 133 public int getSample(int channelIndex, int entryIndex) 134 { 135 try 136 { 137 return data[channelIndex][entryIndex]; 138 } 139 catch (ArrayIndexOutOfBoundsException aioobe) 140 { 141 throw new IllegalArgumentException("Entry must be from 0 to " + (numEntries - 1) + 142 ", channel from 0 to 2."); 143 } 144 } 145 146 /** 147 * Returns all samples of one channel as an int array. 148 * @param channelIndex index of the channel, one of the {@link RGBIndex} constants 149 * @return array with samples 150 */ 151 public int[] getSamples(int channelIndex) 152 { 153 if (channelIndex < 0 || channelIndex > 2) 154 { 155 throw new IllegalArgumentException("Invalid channel index, must be from 0 to 2."); 156 } 157 int[] result = new int[data[channelIndex].length]; 158 System.arraycopy(data[channelIndex], 0, result, 0, result.length); 159 return result; 160 } 161 162 /** 163 * Checks if all entries of this palette are either black or white. 164 * An entry is black if all three intensitites (red, green and blue) are 165 * <code>0</code>, it is white if they are all equal to 166 * {@link #getMaxValue()}. 167 * No particular order of entries (e.g. first color black, second white) 168 * is demanded and no specific number of entries (e.g. 2). 169 * This means that a palette is black and white if it contains ten entries 170 * that are all black. 171 * 172 * @return if the palette contains only the colors black and white 173 */ 174 public boolean isBlackAndWhite() 175 { 176 int i = 0; 177 while (i < numEntries) 178 { 179 if (data[INDEX_RED][i] != data[INDEX_GREEN][i] || 180 data[INDEX_GREEN][i] != data[INDEX_BLUE][i] || 181 (data[INDEX_BLUE][i] != 0 && data[INDEX_BLUE][i] != maxValue)) 182 { 183 return false; 184 } 185 i++; 186 } 187 return true; 188 } 189 190 /** 191 * Checks if this palette is gray, i.e., checks if all entries are 192 * gray. This is the case if for all entries red, green and blue 193 * have the same intensity. 194 * @return if the palette contains only shades of gray 195 */ 196 public boolean isGray() 197 { 198 int i = 0; 199 while (i < numEntries) 200 { 201 if (data[INDEX_RED][i] != data[INDEX_GREEN][i] || 202 data[INDEX_GREEN][i] != data[INDEX_BLUE][i]) 203 { 204 return false; 205 } 206 i++; 207 } 208 return true; 209 } 210 211 public void put(int entryIndex, int red, int green, int blue) 212 { 213 putSample(INDEX_RED, entryIndex, red); 214 putSample(INDEX_GREEN, entryIndex, green); 215 putSample(INDEX_BLUE, entryIndex, blue); 216 } 217 218 /** 219 * Sets one sample of one color entry in the palette to a new value. 220 * @param channelIndex 221 */ 222 public void putSample(int channelIndex, int entryIndex, int newValue) 223 { 224 if (newValue < 0 || newValue > maxValue) 225 { 226 throw new IllegalArgumentException("Value must be from 0 to " + 227 maxValue + "; argument is " + newValue + "."); 228 } 229 try 230 { 231 data[channelIndex][entryIndex] = newValue; 232 } 233 catch (ArrayIndexOutOfBoundsException aioobe) 234 { 235 throw new IllegalArgumentException("Entry must be from 0 to " + (numEntries - 1) + 236 ", channel from 0 to 2."); 237 } 238 } 239 }