001 /* 002 * MemoryBilevelImage 003 * 004 * Copyright (c) 2002 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.data; 009 010 import net.sourceforge.jiu.data.BilevelImage; 011 import net.sourceforge.jiu.util.ArrayConverter; 012 013 /** 014 * An implementation of the {@link BilevelImage} interface that stores image 015 * data in a <code>byte</code> array in memory. 016 * An image of <code>width</code> times <code>height</code> pixels will require 017 * <code>(width + 7) / 8 * height</code> bytes of memory. 018 * @author Marco Schmidt 019 */ 020 public class MemoryBilevelImage implements BilevelImage 021 { 022 private final int BYTES_PER_ROW; 023 private final byte[] data; 024 private final int HEIGHT; 025 private final int WIDTH; 026 027 /** 028 * Create a new MemoryBilevelImage object with the specified resolution. 029 * @param width the horizontal resolution of the new image, must be larger than zero 030 * @param height the vertical resolution of the new image, must be larger than zero 031 * @throws IllegalArgumentException if any of the two parameters is smaller than one 032 */ 033 public MemoryBilevelImage(int width, int height) 034 { 035 if (width < 1) 036 { 037 throw new IllegalArgumentException("Width must be larger than zero; got " + width); 038 } 039 if (height < 1) 040 { 041 throw new IllegalArgumentException("Height must be larger than zero; got " + height); 042 } 043 BYTES_PER_ROW = (width + 7) / 8; 044 WIDTH = width; 045 HEIGHT = height; 046 data = new byte[BYTES_PER_ROW * HEIGHT]; 047 } 048 049 private void checkBitOffset(int bitOffset) 050 { 051 if (bitOffset < 0 || bitOffset > 7) 052 { 053 throw new IllegalArgumentException("A bit offset value must be from the interval 0..7."); 054 } 055 } 056 057 private void checkPositionAndNumber(int x, int y, int w, int h) 058 { 059 if (w < 0) 060 { 061 throw new IllegalArgumentException("Negative number of samples " + 062 "to be copied: " + w); 063 } 064 if (h < 0) 065 { 066 throw new IllegalArgumentException("Negative number of rows " + 067 "to be copied: " + h); 068 } 069 if (x < 0 || x >= getWidth()) 070 { 071 throw new IllegalArgumentException("The value for x is invalid: " + x + "."); 072 } 073 if (y < 0 || y >= getHeight()) 074 { 075 throw new IllegalArgumentException("The value for y is invalid: " + y + "."); 076 } 077 if (x + w > getWidth()) 078 { 079 throw new IllegalArgumentException("Cannot copy " + w + " values starting at " + 080 "offset " + x + " (width is only " + getWidth() + ")."); 081 } 082 if (y + h > getHeight()) 083 { 084 throw new IllegalArgumentException("Cannot copy " + h + " rows starting at " + 085 y + " (height is only " + getHeight() + ")."); 086 } 087 } 088 089 private void checkValue(int value) 090 { 091 if (value != WHITE && value != BLACK) 092 { 093 throw new IllegalArgumentException("Sample value must be either BilevelImage.BLACK or BilevelImage.WHITE."); 094 } 095 } 096 097 public void clear(int newValue) 098 { 099 clear(0, newValue); 100 } 101 102 public void clear(int channelIndex, int newValue) 103 { 104 if (channelIndex != 0) 105 { 106 throw new IllegalArgumentException("Invalid channel index; " + 107 "bilevel images have only one channel, so 0 is the only " + 108 "valid argument; got " + channelIndex); 109 } 110 checkValue(newValue); 111 byte value; 112 if (newValue == BLACK) 113 { 114 value = 0; 115 } 116 else 117 { 118 value = (byte)0xff; 119 } 120 for (int i = 0; i < data.length; i++) 121 { 122 data[i] = value; 123 } 124 } 125 126 public PixelImage createCompatibleImage(int width, int height) 127 { 128 return new MemoryBilevelImage(width, height); 129 } 130 131 public PixelImage createCopy() 132 { 133 PixelImage copy = createCompatibleImage(getWidth(), getHeight()); 134 MemoryBilevelImage result = (MemoryBilevelImage)copy; 135 System.arraycopy(data, 0, result.data, 0, data.length); 136 return result; 137 } 138 139 public long getAllocatedMemory() 140 { 141 return data.length; 142 } 143 144 public int getBitsPerPixel() 145 { 146 return 1; 147 } 148 149 public int getHeight() 150 { 151 return HEIGHT; 152 } 153 154 public Class getImageType() 155 { 156 return BilevelImage.class; 157 } 158 159 public int getMaxSample(int channelIndex) 160 { 161 return 1; 162 } 163 164 public int getNumChannels() 165 { 166 return 1; 167 } 168 169 public void getPackedBytes(int x, int y, int numSamples, byte[] dest, int destOffset, int destBitOffset) 170 { 171 checkPositionAndNumber(x, y, numSamples, 1); 172 checkBitOffset(destBitOffset); 173 int srcOffset = y * BYTES_PER_ROW + (x >> 3); 174 int srcBitOffset = x & 7; 175 ArrayConverter.copyPackedBytes(data, srcOffset, srcBitOffset, dest, destOffset, destBitOffset, numSamples); 176 } 177 178 public int getSample(int x, int y) 179 { 180 if (isBlack(x, y)) 181 { 182 return BLACK; 183 } 184 else 185 { 186 return WHITE; 187 } 188 } 189 190 public int getSample(int channelIndex, int x, int y) 191 { 192 if (channelIndex == 0) 193 { 194 if (isBlack(x, y)) 195 { 196 return BLACK; 197 } 198 else 199 { 200 return WHITE; 201 } 202 } 203 else 204 { 205 throw new IllegalArgumentException("The channelIndex argument must be 0 for bilevel images; got " + channelIndex); 206 } 207 } 208 209 public void getSamples(int channelIndex, int x, int y, int w, int h, int[] dest, int destOffset) 210 { 211 final int INITIAL_MASK = 1 << (7 - (x % 8)); 212 int offset = y * BYTES_PER_ROW + x / 8; 213 while (h-- > 0) 214 { 215 int mask = INITIAL_MASK; 216 int srcOffset = offset; 217 int remainingColumns = w; 218 int srcValue = data[srcOffset++] & 0xff; 219 while (remainingColumns-- != 0) 220 { 221 if ((srcValue & mask) == 0) 222 { 223 dest[destOffset++] = BLACK; 224 } 225 else 226 { 227 dest[destOffset++] = WHITE; 228 } 229 if (mask == 1) 230 { 231 mask = 128; 232 srcValue = data[srcOffset++] & 0xff; 233 } 234 else 235 { 236 mask >>= 1; 237 } 238 } 239 offset += BYTES_PER_ROW; 240 } 241 } 242 243 public int getWidth() 244 { 245 return WIDTH; 246 } 247 248 public boolean isBlack(int x, int y) 249 { 250 try 251 { 252 int offset = y * BYTES_PER_ROW + (x >> 3); 253 return (data[offset] & (byte)(1 << (7 - (x & 7)))) == 0; 254 } 255 catch (ArrayIndexOutOfBoundsException aioobe) 256 { 257 checkPositionAndNumber(x, y, 1, 1); 258 } 259 return true; 260 } 261 262 public boolean isWhite(int x, int y) 263 { 264 try 265 { 266 int offset = y * BYTES_PER_ROW + (x >> 3); 267 return (data[offset] & (byte)(1 << (7 - (x & 7)))) != 0; 268 } 269 catch (ArrayIndexOutOfBoundsException aioobe) 270 { 271 checkPositionAndNumber(x, y, 1, 1); 272 } 273 return true; 274 } 275 276 public void putBlack(int x, int y) 277 { 278 try 279 { 280 int offset = y * BYTES_PER_ROW + (x >> 3); 281 data[offset] &= (byte)(255 - (1 << (7 - (x & 7)))); 282 } 283 catch (ArrayIndexOutOfBoundsException aioobe) 284 { 285 checkPositionAndNumber(x, y, 1, 1); 286 } 287 } 288 289 public void putPackedBytes(int x, int y, int numSamples, byte[] src, int srcOffset, int srcBitOffset) 290 { 291 checkPositionAndNumber(x, y, numSamples, 1); 292 checkBitOffset(srcBitOffset); 293 int destOffset = y * BYTES_PER_ROW + (x >> 3); 294 int destBitOffset = x & 7; 295 ArrayConverter.copyPackedBytes(src, srcOffset, srcBitOffset, data, destOffset, destBitOffset, numSamples); 296 } 297 298 public void putSample(int x, int y, int newValue) 299 { 300 putSample(0, x, y, newValue); 301 } 302 303 public void putSample(int channelIndex, int x, int y, int newValue) 304 { 305 checkValue(newValue); 306 try 307 { 308 int offset = y * BYTES_PER_ROW + (x >> 3); 309 if (newValue == BLACK) 310 { 311 data[offset] &= (byte)(255 - (1 << (7 - (x & 7)))); 312 } 313 else 314 { 315 data[offset] |= (byte)(1 << (7 - (x & 7))); 316 } 317 } 318 catch (ArrayIndexOutOfBoundsException aioobe) 319 { 320 checkPositionAndNumber(x, y, 1, 1); 321 } 322 } 323 324 public void putSamples(int channelIndex, int x, int y, int w, int h, int[] src, int srcOffset) 325 { 326 checkPositionAndNumber(x, y, w, h); 327 int INITIAL_ROW_MASK = 1 << (7 - (x & 7)); 328 int initialDestOffset = y * BYTES_PER_ROW + (x >> 3); 329 while (h-- > 0) 330 { 331 int mask = INITIAL_ROW_MASK; 332 int destOffset = initialDestOffset; 333 int pixelsLeft = w; 334 while (pixelsLeft-- > 0) 335 { 336 if (src[srcOffset++] == BLACK) 337 { 338 data[destOffset] &= (byte)(255 - mask); 339 } 340 else 341 { 342 data[destOffset] |= (byte)mask; 343 } 344 if (mask == 1) 345 { 346 mask = 128; 347 destOffset++; 348 } 349 } 350 initialDestOffset += BYTES_PER_ROW; 351 } 352 } 353 354 public void putWhite(int x, int y) 355 { 356 try 357 { 358 int offset = y * BYTES_PER_ROW + (x >> 3); 359 data[offset] |= (byte)(1 << (7 - (x & 7))); 360 } 361 catch (ArrayIndexOutOfBoundsException aioobe) 362 { 363 checkPositionAndNumber(x, y, 1, 1); 364 } 365 } 366 }