1 /* sane - Scanner Access Now Easy. 2 Copyright (C) 1997 Jeffrey S. Freedman 3 This file is part of the SANE package. 4 5 This program is free software; you can redistribute it and/or 6 modify it under the terms of the GNU General Public License as 7 published by the Free Software Foundation; either version 2 of the 8 License, or (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <https://www.gnu.org/licenses/>. 17 18 As a special exception, the authors of SANE give permission for 19 additional uses of the libraries contained in this release of SANE. 20 21 The exception is that, if you link a SANE library with other files 22 to produce an executable, this does not by itself cause the 23 resulting executable to be covered by the GNU General Public 24 License. Your use of that executable is in no way restricted on 25 account of linking the SANE library code into it. 26 27 This exception does not, however, invalidate any other reasons why 28 the executable file might be covered by the GNU General Public 29 License. 30 31 If you submit changes to SANE to the maintainers to be included in 32 a subsequent release, you agree by submitting the changes that 33 those changes may be distributed with this exception intact. 34 35 If you write modifications of your own for SANE, it is your choice 36 whether to permit this exception to apply to your modifications. 37 If you do not wish that, delete this exception notice. */ 38 39 /** 40 ** ScanIt.java - Do the actual scanning for SANE. 41 ** 42 ** Written: 11/3/97 - JSF 43 **/ 44 45 import java.util.Vector; 46 import java.util.Enumeration; 47 import java.awt.image.ImageProducer; 48 import java.awt.image.ImageConsumer; 49 import java.awt.image.ColorModel; 50 import java.io.OutputStream; 51 import java.io.PrintWriter; 52 import java.io.BufferedWriter; 53 import java.io.IOException; 54 55 /* 56 * This class uses SANE to scan an image. 57 */ 58 public class ScanIt implements ImageProducer 59 { 60 // # lines we incr. image height. 61 private static final int STRIP_HEIGHT = 256; 62 private Sane sane; 63 private int handle = 0; // SANE device handle. 64 private Vector consumers = new Vector(); 65 // File to write to. 66 private OutputStream outputFile = null; 67 private SaneParameters parms = new SaneParameters(); 68 private ColorModel cm; // RGB color model. 69 private int width, height; // Dimensions. 70 private int x, y; // Position. 71 private int image[] = null; // Image that we build as we scan. 72 private int offset; // Offset within image in pixels if 73 // doing separate frames, bytes 74 // (3/word) if RBG. 75 76 /* 77 * Tell consumers our status. The scan is also terminated de- 78 * pending on the status. 79 */ tellStatus(int s)80 private void tellStatus(int s) 81 { 82 Enumeration next = consumers.elements(); 83 while (next.hasMoreElements()) 84 { 85 ImageConsumer ic = (ImageConsumer) next.nextElement(); 86 ic.imageComplete(s); 87 } 88 // Done? Stop scan. 89 if (s == ImageConsumer.STATICIMAGEDONE || 90 s == ImageConsumer.IMAGEERROR) 91 sane.cancel(handle); 92 } 93 94 /* 95 * Tell consumers the image size. 96 */ tellDimensions(int w, int h)97 private void tellDimensions(int w, int h) 98 { 99 System.out.println("tellDimensions: " + w + ", " + h); 100 Enumeration next = consumers.elements(); 101 while (next.hasMoreElements()) 102 { 103 ImageConsumer ic = (ImageConsumer) next.nextElement(); 104 ic.setDimensions(w, h); 105 } 106 } 107 108 /* 109 * Send pixels to the clients. 110 */ tellPixels(int x, int y, int w, int h)111 private void tellPixels(int x, int y, int w, int h) 112 { 113 /* 114 System.out.println("image length=" + image.length); 115 System.out.println("width=" + width); 116 System.out.println("tellPixels: x="+x +" y="+y + " w="+w 117 + " h="+h); 118 */ 119 Enumeration next = consumers.elements(); 120 while (next.hasMoreElements()) 121 { 122 ImageConsumer ic = (ImageConsumer) next.nextElement(); 123 ic.setPixels(x, y, w, h, cm, image, 0, width); 124 } 125 } 126 127 /* 128 * Construct. 129 */ ScanIt(Sane s, int hndl)130 public ScanIt(Sane s, int hndl) 131 { 132 sane = s; 133 handle = hndl; 134 } 135 136 /* 137 * Add a consumer. 138 */ addConsumer(ImageConsumer ic)139 public synchronized void addConsumer(ImageConsumer ic) 140 { 141 if (consumers.contains(ic)) 142 return; // Already here. 143 consumers.addElement(ic); 144 } 145 146 /* 147 * Is a consumer in the list? 148 */ isConsumer(ImageConsumer ic)149 public synchronized boolean isConsumer(ImageConsumer ic) 150 { return consumers.contains(ic); } 151 152 /* 153 * Remove consumer. 154 */ removeConsumer(ImageConsumer ic)155 public synchronized void removeConsumer(ImageConsumer ic) 156 { consumers.removeElement(ic); } 157 158 /* 159 * Add a consumer and start scanning. 160 */ startProduction(ImageConsumer ic)161 public void startProduction(ImageConsumer ic) 162 { 163 System.out.println("In startProduction()"); 164 addConsumer(ic); 165 scan(); 166 } 167 168 /* 169 * Set file to write to. 170 */ setOutputFile(OutputStream o)171 public void setOutputFile(OutputStream o) 172 { outputFile = o; } 173 174 /* 175 * Ignore this: 176 */ requestTopDownLeftRightResend(ImageConsumer ic)177 public void requestTopDownLeftRightResend(ImageConsumer ic) 178 { } 179 180 /* 181 * Go to next line in image, reallocating if necessary. 182 */ nextLine()183 private void nextLine() 184 { 185 x = 0; 186 ++y; 187 if (y >= height || image == null) 188 { // Got to reallocate. 189 int oldSize = image == null ? 0 : width*height; 190 height += STRIP_HEIGHT; // Add more lines. 191 int newSize = width*height; 192 int[] newImage = new int[newSize]; 193 int i; 194 if (oldSize != 0) // Copy old data. 195 for (i = 0; i < oldSize; i++) 196 newImage[i] = image[i]; 197 // Fill new pixels with 0's, setting 198 // alpha channel. 199 for (i = oldSize; i < newSize; i++) 200 newImage[i] = (255 << 24); 201 image = newImage; 202 System.out.println("nextLine: newSize="+newSize); 203 // Tell clients. 204 tellDimensions(width, height); 205 } 206 } 207 208 /* 209 * Process a buffer of data. 210 */ process(byte[] data, int readLen)211 private boolean process(byte[] data, int readLen) 212 { 213 int prevY = y > 0 ? y : 0; // Save current Y-coord. 214 int i; 215 switch (parms.format) 216 { 217 case SaneParameters.FRAME_RED: 218 case SaneParameters.FRAME_GREEN: 219 case SaneParameters.FRAME_BLUE: 220 System.out.println("Process RED, GREEN or BLUE"); 221 int cindex = 2 - (parms.format - SaneParameters.FRAME_RED); 222 // Single frame. 223 for (i = 0; i < readLen; ++i) 224 { // Doing a single color frame. 225 image[offset + i] |= 226 (((int) data[i]) & 0xff) << (8*cindex); 227 ++x; 228 if (x >= width) 229 nextLine(); 230 } 231 break; 232 case SaneParameters.FRAME_RGB: 233 for (i = 0; i < readLen; ++i) 234 { 235 int b = 2 - (offset + i)%3; 236 image[(offset + i)/3] |= 237 (((int) data[i]) & 0xff) << (8*b); 238 if (b == 0) 239 { 240 ++x; 241 if (x >= width) 242 nextLine(); 243 } 244 } 245 break; 246 case SaneParameters.FRAME_GRAY: 247 System.out.println("Process GREY"); 248 // Single frame. 249 for (i = 0; i < readLen; ++i) 250 { 251 int v = ((int) data[i]) & 0xff; 252 image[offset + i] |= (v<<16) | (v<<8) | (v); 253 ++x; 254 if (x >= width) 255 nextLine(); 256 } 257 break; 258 } 259 offset += readLen; // Update where we are. 260 // Show it. 261 System.out.println("PrevY = " + prevY + ", y = " + y); 262 // tellPixels(0, prevY, width, y - prevY); 263 tellPixels(0, 0, width, height); 264 return true; 265 } 266 267 /* 268 * Start scanning. 269 */ scan()270 public void scan() 271 { 272 int dataLen = 32*1024; 273 byte [] data = new byte[dataLen]; 274 int [] readLen = new int[1]; 275 int frameCnt = 0; 276 // For now, use default RGB model. 277 cm = ColorModel.getRGBdefault(); 278 int status; 279 image = null; 280 do // Do each frame. 281 { 282 frameCnt++; 283 x = 0; // Init. position. 284 y = -1; 285 offset = 0; 286 System.out.println("Reading frame #" + frameCnt); 287 status = sane.start(handle); 288 if (status != Sane.STATUS_GOOD) 289 { 290 System.out.println("start() failed. Status= " 291 + status); 292 tellStatus(ImageConsumer.IMAGEERROR); 293 return; 294 } 295 status = sane.getParameters(handle, parms); 296 if (status != Sane.STATUS_GOOD) 297 { 298 System.out.println("getParameters() failed. Status= " 299 + status); 300 tellStatus(ImageConsumer.IMAGEERROR); 301 return; //++++cleanup. 302 } 303 if (frameCnt == 1) // First time? 304 { 305 width = parms.pixelsPerLine; 306 if (parms.lines >= 0) 307 height = parms.lines - STRIP_HEIGHT + 1; 308 else // Hand-scanner. 309 height = 0; 310 nextLine(); // Allocate image. 311 } 312 while ((status = sane.read(handle, data, dataLen, readLen)) 313 == Sane.STATUS_GOOD) 314 { 315 System.out.println("Read " + readLen[0] + " bytes."); 316 if (!process(data, readLen[0])) 317 { 318 tellStatus(ImageConsumer.IMAGEERROR); 319 return; 320 } 321 } 322 if (status != Sane.STATUS_EOF) 323 { 324 System.out.println("read() failed. Status= " 325 + status); 326 tellStatus(ImageConsumer.IMAGEERROR); 327 return; 328 } 329 } 330 while (!parms.lastFrame); 331 height = y; // For now, send whole image here. 332 tellDimensions(width, height); 333 tellPixels(0, 0, width, height); 334 if (outputFile != null) // Write to file. 335 { 336 try 337 { 338 write(outputFile); 339 } 340 catch (IOException e) 341 { //+++++++++++++++ 342 System.out.println("I/O error writing file."); 343 } 344 outputFile = null; // Clear for next time. 345 } 346 tellStatus(ImageConsumer.STATICIMAGEDONE); 347 image = null; // Allow buffer to be freed. 348 } 349 350 /* 351 * Write ppm/pnm output for last scan to a file. 352 */ write(OutputStream out)353 private void write(OutputStream out) throws IOException 354 { 355 PrintWriter pout = new PrintWriter(out); 356 BufferedWriter bout = new BufferedWriter(pout); 357 int len = width*height; // Get # of pixels. 358 int i; 359 switch (parms.format) 360 { 361 case SaneParameters.FRAME_RED: 362 case SaneParameters.FRAME_GREEN: 363 case SaneParameters.FRAME_BLUE: 364 case SaneParameters.FRAME_RGB: 365 pout.print("P6\n# SANE data follows\n" + 366 width + ' ' + height + "\n255\n"); 367 for (i = 0; i < len; i++) 368 { 369 int pix = image[i]; 370 bout.write((pix >> 16) & 0xff); 371 bout.write((pix >> 8) & 0xff); 372 bout.write(pix & 0xff); 373 } 374 break; 375 case SaneParameters.FRAME_GRAY: 376 pout.print("P5\n# SANE data follows\n" + 377 width + ' ' + height + "\n255\n"); 378 for (i = 0; i < len; i++) 379 { 380 int pix = image[i]; 381 bout.write(pix & 0xff); 382 } 383 break; 384 } 385 386 bout.flush(); // Flush output. 387 pout.flush(); 388 } 389 } 390