001 /* 002 * OnDemandHistogram3D 003 * 004 * Copyright (c) 2000, 2001, 2002 Marco Schmidt <marcoschmidt@users.sourceforge.net> 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.color.data; 009 010 import net.sourceforge.jiu.color.data.Histogram3D; 011 012 /** 013 * A data class for a three-dimensional histogram, creating counters on demand only, 014 * not allocating counters for all possible entries at the beginning. 015 * The creation on demand happens to save space. 016 * A naive implementation can become huge - for eight bits per component, 017 * you'd need 2<sup>(8 + 8 + 8)</sup> = 2<sup>24</sup> = 16,777,216 018 * int values (64 MB), while a typical 24 bit image uses only a fraction of 019 * these possible colors. 020 * 021 * @author Marco Schmidt 022 */ 023 public class OnDemandHistogram3D implements Histogram3D 024 { 025 private int c1; 026 private int c2; 027 private int c3; 028 private int[] compOrder; 029 private int maxValue1; 030 private int maxValue2; 031 private int maxValue3; 032 private int[] maxValue; 033 private int[] vector; 034 private Object[] top; 035 036 /** 037 * Creates a new object of this class with specified maximum values 038 * for all three indexes. 039 * @param maxValue1 the maximum value for the first index 040 * @param maxValue2 the maximum value for the second index 041 * @param maxValue3 the maximum value for the third index 042 * @param c1 the top level component for the internal tree 043 * @param c2 the second-level component for the internal tree 044 * @param c3 the third-level component for the internal tree 045 */ 046 public OnDemandHistogram3D( 047 int maxValue1, int maxValue2, int maxValue3, 048 int c1, int c2, int c3) throws IllegalArgumentException 049 { 050 if (maxValue1 < 1 || maxValue2 < 1 || maxValue3 < 1) 051 { 052 throw new IllegalArgumentException("The three maximum value arguments must all be larger than zero."); 053 } 054 this.maxValue1 = maxValue1; 055 this.maxValue2 = maxValue2; 056 this.maxValue3 = maxValue3; 057 maxValue = new int[3]; 058 maxValue[0] = maxValue1; 059 maxValue[1] = maxValue2; 060 maxValue[2] = maxValue3; 061 if (c1 < 0 || c1 > 2 || c2 < 0 || c2 > 2 || c3 < 0 || c3 > 2) 062 { 063 throw new IllegalArgumentException("Arguments for components must be from 0..2."); 064 } 065 if (c1 == c2 || c1 == c3 || c2 == c3) 066 { 067 throw new IllegalArgumentException("No two arguments for components are allowed to be equal."); 068 } 069 this.c1 = c1; 070 this.c2 = c2; 071 this.c3 = c3; 072 compOrder = new int[3]; 073 compOrder[0] = c1; 074 compOrder[1] = c2; 075 compOrder[2] = c3; 076 vector = new int[3]; 077 clear(); 078 } 079 080 /** 081 * Creates a new object of this class with maximum values as specified 082 * by the arguments and the component values 0, 1, 2. 083 * Simply calls <code>this(maxValue1, maxValue2, maxValue3, 0, 1, 2);</code> 084 * @param maxValue1 the maximum value for the first index 085 * @param maxValue2 the maximum value for the second index 086 * @param maxValue3 the maximum value for the third index 087 */ 088 public OnDemandHistogram3D(int maxValue1, int maxValue2, int maxValue3) 089 { 090 this(maxValue1, maxValue2, maxValue3, 0, 1, 2); 091 } 092 093 /** 094 * Creates a new object of this class with the argument as maximum value for 095 * all three index positions and the component values 0, 1, 2. 096 * Simply calls <code>this(maxValue, maxValue, maxValue);</code> 097 * @param maxValue the maximum value for all indexes 098 */ 099 public OnDemandHistogram3D(int maxValue) 100 { 101 this(maxValue, maxValue, maxValue); 102 } 103 104 /** 105 * Resets all counters to zero. 106 * As the Java VM's garbage collector is responsible for releasing unused 107 * memory, it is unclear when memory allocated for the histogram will be 108 * available again. 109 */ 110 public void clear() 111 { 112 top = createObjectArray(maxValue[c1] + 1); 113 } 114 115 /** 116 * Creates an array of int values, initializes all values to 0 and returns 117 * the newly-allocated array. 118 * @param LENGTH the number of entries in the array to be allocated 119 * @return the resulting array 120 */ 121 private int[] createIntArray(final int LENGTH) 122 { 123 int[] result = new int[LENGTH]; 124 for (int i = 0; i < LENGTH; i++) 125 { 126 result[i] = 0; 127 } 128 return result; 129 } 130 131 /** 132 * Creates an array of objects, initializes all values to null and 133 * returns this new array. 134 * @param LENGTH the number of entries of the new int array to be returned 135 */ 136 private Object[] createObjectArray(final int LENGTH) 137 { 138 Object[] result = new Object[LENGTH]; 139 for (int i = 0; i < LENGTH; i++) 140 { 141 result[i] = null; 142 } 143 return result; 144 } 145 146 /** 147 * Returns counter for the argument color given by its red, green and 148 * blue intensity. 149 * @param index1 the first value of the index 150 * @param index2 151 * @param index3 152 * @throws IllegalArgumentException this exception is thrown if the 153 * argument color is not in the valid interval, i.e., if at least one 154 * of the components is not from 0 .. max 155 */ 156 public int getEntry(int index1, int index2, int index3) throws IllegalArgumentException 157 { 158 if (index1 < 0 && index1 > maxValue1 && 159 index2 < 0 && index2 > maxValue2 && 160 index3 < 0 && index3 > maxValue3) 161 { 162 throw new IllegalArgumentException("Invalid value " + index1 + " " + index2 + " " + index3); 163 } 164 vector[0] = index1; 165 vector[1] = index2; 166 vector[2] = index3; 167 Object[] second = (Object[])top[vector[compOrder[0]]]; 168 if (second == null) 169 { 170 return 0; 171 } 172 int[] counters = (int[])second[vector[compOrder[1]]]; 173 if (counters == null) 174 { 175 return 0; 176 } 177 return counters[vector[compOrder[2]]]; 178 } 179 180 public int getMaxValue(int index) throws IllegalArgumentException 181 { 182 if (index >= 0 && index <= 2) 183 { 184 return maxValue[index]; 185 } 186 else 187 { 188 throw new IllegalArgumentException("The index argument must be " + 189 "from 0 to 2; got " + index); 190 } 191 } 192 193 /** 194 * Returns the number of entries in this histogram with a counter value of one 195 * or higher (in other words: the number of colors that are in use). 196 * @return the number of unique colors 197 */ 198 public int getNumUsedEntries() 199 { 200 int result = 0; 201 for (int i1 = 0; i1 <= maxValue[c1]; i1++) 202 { 203 if (top[i1] != null) 204 { 205 Object[] second = (Object[])top[i1]; 206 if (second != null) 207 { 208 for (int i2 = 0; i2 <= maxValue[c2]; i2++) 209 { 210 if (second[i2] != null) 211 { 212 int[] third = (int[])second[i2]; 213 for (int i3 = 0; i3 <= maxValue[c3]; i3++) 214 { 215 if (third[i3] != 0) 216 { 217 result++; 218 } 219 } 220 } 221 } 222 } 223 } 224 } 225 return result; 226 } 227 228 /** 229 * Increases the counter for the color given by the arguments red, green and blue. 230 * This method could be implemented by the following code snippet: 231 * <pre> 232 * setEntry(red, green, blue, getEntry(red, green, blue) + 1); 233 * </pre>However, this is not done to avoid slow-downs by accessing a counter value twice. 234 * 235 * @param red the red intensity of the color entry whose counter will be increased 236 * @param green the green intensity of the color entry whose counter will be increased 237 * @param blue the blue intensity of the color entry whose counter will be increased 238 * @throws IllegalArgumentException if the argument color is not valid 239 * (minimum is 0, maximum is defined for each component independently) 240 */ 241 public void increaseEntry(int red, int green, int blue) throws IllegalArgumentException 242 { 243 if (red < 0 || red > maxValue1 || 244 green < 0 || green > maxValue2 || 245 blue < 0 || blue > maxValue3) 246 { 247 throw new IllegalArgumentException("Invalid color value in increaseEntry(): " + 248 red + " " + green + " " + blue); 249 } 250 vector[0] = red; 251 vector[1] = green; 252 vector[2] = blue; 253 int index1 = vector[compOrder[0]]; 254 Object[] second = (Object[])(top[index1]); 255 if (second == null) 256 { 257 int num = maxValue[compOrder[1]] + 1; 258 top[index1] = createObjectArray(num); 259 second = (Object[])(top[index1]); 260 } 261 int index2 = vector[compOrder[1]]; 262 int[] counters = (int[])(second[index2]); 263 if (counters == null) 264 { 265 int num = maxValue[compOrder[2]] + 1; 266 second[index2] = createIntArray(num); 267 counters = (int[])(second[index2]); 268 } 269 int index3 = vector[compOrder[2]]; 270 counters[index3]++; 271 } 272 273 /** 274 * Sets one counter to a new value. 275 * @param r red component 276 * @param g green component 277 * @param b blue component 278 * @param newValue the new value for the counter 279 * @throws IllegalArgumentException if the components do not form a valid color 280 */ 281 public void setEntry(int r, int g, int b, int newValue) throws IllegalArgumentException 282 { 283 if (r < 0 || r > maxValue1 || 284 g < 0 || g > maxValue2 || 285 b < 0 || b > maxValue3) 286 { 287 throw new IllegalArgumentException("Invalid index triplet: " + 288 r + " " + g + " " + b); 289 } 290 vector[0] = r; 291 vector[1] = g; 292 vector[2] = b; 293 int index1 = vector[compOrder[0]]; 294 Object[] second = (Object[])(top[index1]); 295 if (second == null) 296 { 297 int num = maxValue[compOrder[1]] + 1; 298 top[index1] = createObjectArray(num); 299 second = (Object[])(top[index1]); 300 } 301 int index2 = vector[compOrder[1]]; 302 int[] counters = (int[])(second[index2]); 303 if (counters == null) 304 { 305 int num = maxValue[compOrder[2]] + 1; 306 second[index2] = createIntArray(num); 307 counters = (int[])(second[index2]); 308 } 309 int index3 = vector[compOrder[2]]; 310 counters[index3] = newValue; 311 } 312 }