1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy. 2141cc406Sopenharmony_ci Copyright (C) 1997 Jeffrey S. Freedman 3141cc406Sopenharmony_ci This file is part of the SANE package. 4141cc406Sopenharmony_ci 5141cc406Sopenharmony_ci This program is free software; you can redistribute it and/or 6141cc406Sopenharmony_ci modify it under the terms of the GNU General Public License as 7141cc406Sopenharmony_ci published by the Free Software Foundation; either version 2 of the 8141cc406Sopenharmony_ci License, or (at your option) any later version. 9141cc406Sopenharmony_ci 10141cc406Sopenharmony_ci This program is distributed in the hope that it will be useful, but 11141cc406Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 12141cc406Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13141cc406Sopenharmony_ci General Public License for more details. 14141cc406Sopenharmony_ci 15141cc406Sopenharmony_ci You should have received a copy of the GNU General Public License 16141cc406Sopenharmony_ci along with this program. If not, see <https://www.gnu.org/licenses/>. 17141cc406Sopenharmony_ci 18141cc406Sopenharmony_ci As a special exception, the authors of SANE give permission for 19141cc406Sopenharmony_ci additional uses of the libraries contained in this release of SANE. 20141cc406Sopenharmony_ci 21141cc406Sopenharmony_ci The exception is that, if you link a SANE library with other files 22141cc406Sopenharmony_ci to produce an executable, this does not by itself cause the 23141cc406Sopenharmony_ci resulting executable to be covered by the GNU General Public 24141cc406Sopenharmony_ci License. Your use of that executable is in no way restricted on 25141cc406Sopenharmony_ci account of linking the SANE library code into it. 26141cc406Sopenharmony_ci 27141cc406Sopenharmony_ci This exception does not, however, invalidate any other reasons why 28141cc406Sopenharmony_ci the executable file might be covered by the GNU General Public 29141cc406Sopenharmony_ci License. 30141cc406Sopenharmony_ci 31141cc406Sopenharmony_ci If you submit changes to SANE to the maintainers to be included in 32141cc406Sopenharmony_ci a subsequent release, you agree by submitting the changes that 33141cc406Sopenharmony_ci those changes may be distributed with this exception intact. 34141cc406Sopenharmony_ci 35141cc406Sopenharmony_ci If you write modifications of your own for SANE, it is your choice 36141cc406Sopenharmony_ci whether to permit this exception to apply to your modifications. 37141cc406Sopenharmony_ci If you do not wish that, delete this exception notice. */ 38141cc406Sopenharmony_ci 39141cc406Sopenharmony_ci/** 40141cc406Sopenharmony_ci ** ScanIt.java - Do the actual scanning for SANE. 41141cc406Sopenharmony_ci ** 42141cc406Sopenharmony_ci ** Written: 11/3/97 - JSF 43141cc406Sopenharmony_ci **/ 44141cc406Sopenharmony_ci 45141cc406Sopenharmony_ciimport java.util.Vector; 46141cc406Sopenharmony_ciimport java.util.Enumeration; 47141cc406Sopenharmony_ciimport java.awt.image.ImageProducer; 48141cc406Sopenharmony_ciimport java.awt.image.ImageConsumer; 49141cc406Sopenharmony_ciimport java.awt.image.ColorModel; 50141cc406Sopenharmony_ciimport java.io.OutputStream; 51141cc406Sopenharmony_ciimport java.io.PrintWriter; 52141cc406Sopenharmony_ciimport java.io.BufferedWriter; 53141cc406Sopenharmony_ciimport java.io.IOException; 54141cc406Sopenharmony_ci 55141cc406Sopenharmony_ci/* 56141cc406Sopenharmony_ci * This class uses SANE to scan an image. 57141cc406Sopenharmony_ci */ 58141cc406Sopenharmony_cipublic class ScanIt implements ImageProducer 59141cc406Sopenharmony_ci { 60141cc406Sopenharmony_ci // # lines we incr. image height. 61141cc406Sopenharmony_ci private static final int STRIP_HEIGHT = 256; 62141cc406Sopenharmony_ci private Sane sane; 63141cc406Sopenharmony_ci private int handle = 0; // SANE device handle. 64141cc406Sopenharmony_ci private Vector consumers = new Vector(); 65141cc406Sopenharmony_ci // File to write to. 66141cc406Sopenharmony_ci private OutputStream outputFile = null; 67141cc406Sopenharmony_ci private SaneParameters parms = new SaneParameters(); 68141cc406Sopenharmony_ci private ColorModel cm; // RGB color model. 69141cc406Sopenharmony_ci private int width, height; // Dimensions. 70141cc406Sopenharmony_ci private int x, y; // Position. 71141cc406Sopenharmony_ci private int image[] = null; // Image that we build as we scan. 72141cc406Sopenharmony_ci private int offset; // Offset within image in pixels if 73141cc406Sopenharmony_ci // doing separate frames, bytes 74141cc406Sopenharmony_ci // (3/word) if RBG. 75141cc406Sopenharmony_ci 76141cc406Sopenharmony_ci /* 77141cc406Sopenharmony_ci * Tell consumers our status. The scan is also terminated de- 78141cc406Sopenharmony_ci * pending on the status. 79141cc406Sopenharmony_ci */ 80141cc406Sopenharmony_ci private void tellStatus(int s) 81141cc406Sopenharmony_ci { 82141cc406Sopenharmony_ci Enumeration next = consumers.elements(); 83141cc406Sopenharmony_ci while (next.hasMoreElements()) 84141cc406Sopenharmony_ci { 85141cc406Sopenharmony_ci ImageConsumer ic = (ImageConsumer) next.nextElement(); 86141cc406Sopenharmony_ci ic.imageComplete(s); 87141cc406Sopenharmony_ci } 88141cc406Sopenharmony_ci // Done? Stop scan. 89141cc406Sopenharmony_ci if (s == ImageConsumer.STATICIMAGEDONE || 90141cc406Sopenharmony_ci s == ImageConsumer.IMAGEERROR) 91141cc406Sopenharmony_ci sane.cancel(handle); 92141cc406Sopenharmony_ci } 93141cc406Sopenharmony_ci 94141cc406Sopenharmony_ci /* 95141cc406Sopenharmony_ci * Tell consumers the image size. 96141cc406Sopenharmony_ci */ 97141cc406Sopenharmony_ci private void tellDimensions(int w, int h) 98141cc406Sopenharmony_ci { 99141cc406Sopenharmony_ci System.out.println("tellDimensions: " + w + ", " + h); 100141cc406Sopenharmony_ci Enumeration next = consumers.elements(); 101141cc406Sopenharmony_ci while (next.hasMoreElements()) 102141cc406Sopenharmony_ci { 103141cc406Sopenharmony_ci ImageConsumer ic = (ImageConsumer) next.nextElement(); 104141cc406Sopenharmony_ci ic.setDimensions(w, h); 105141cc406Sopenharmony_ci } 106141cc406Sopenharmony_ci } 107141cc406Sopenharmony_ci 108141cc406Sopenharmony_ci /* 109141cc406Sopenharmony_ci * Send pixels to the clients. 110141cc406Sopenharmony_ci */ 111141cc406Sopenharmony_ci private void tellPixels(int x, int y, int w, int h) 112141cc406Sopenharmony_ci { 113141cc406Sopenharmony_ci/* 114141cc406Sopenharmony_ci System.out.println("image length=" + image.length); 115141cc406Sopenharmony_ci System.out.println("width=" + width); 116141cc406Sopenharmony_ci System.out.println("tellPixels: x="+x +" y="+y + " w="+w 117141cc406Sopenharmony_ci + " h="+h); 118141cc406Sopenharmony_ci */ 119141cc406Sopenharmony_ci Enumeration next = consumers.elements(); 120141cc406Sopenharmony_ci while (next.hasMoreElements()) 121141cc406Sopenharmony_ci { 122141cc406Sopenharmony_ci ImageConsumer ic = (ImageConsumer) next.nextElement(); 123141cc406Sopenharmony_ci ic.setPixels(x, y, w, h, cm, image, 0, width); 124141cc406Sopenharmony_ci } 125141cc406Sopenharmony_ci } 126141cc406Sopenharmony_ci 127141cc406Sopenharmony_ci /* 128141cc406Sopenharmony_ci * Construct. 129141cc406Sopenharmony_ci */ 130141cc406Sopenharmony_ci public ScanIt(Sane s, int hndl) 131141cc406Sopenharmony_ci { 132141cc406Sopenharmony_ci sane = s; 133141cc406Sopenharmony_ci handle = hndl; 134141cc406Sopenharmony_ci } 135141cc406Sopenharmony_ci 136141cc406Sopenharmony_ci /* 137141cc406Sopenharmony_ci * Add a consumer. 138141cc406Sopenharmony_ci */ 139141cc406Sopenharmony_ci public synchronized void addConsumer(ImageConsumer ic) 140141cc406Sopenharmony_ci { 141141cc406Sopenharmony_ci if (consumers.contains(ic)) 142141cc406Sopenharmony_ci return; // Already here. 143141cc406Sopenharmony_ci consumers.addElement(ic); 144141cc406Sopenharmony_ci } 145141cc406Sopenharmony_ci 146141cc406Sopenharmony_ci /* 147141cc406Sopenharmony_ci * Is a consumer in the list? 148141cc406Sopenharmony_ci */ 149141cc406Sopenharmony_ci public synchronized boolean isConsumer(ImageConsumer ic) 150141cc406Sopenharmony_ci { return consumers.contains(ic); } 151141cc406Sopenharmony_ci 152141cc406Sopenharmony_ci /* 153141cc406Sopenharmony_ci * Remove consumer. 154141cc406Sopenharmony_ci */ 155141cc406Sopenharmony_ci public synchronized void removeConsumer(ImageConsumer ic) 156141cc406Sopenharmony_ci { consumers.removeElement(ic); } 157141cc406Sopenharmony_ci 158141cc406Sopenharmony_ci /* 159141cc406Sopenharmony_ci * Add a consumer and start scanning. 160141cc406Sopenharmony_ci */ 161141cc406Sopenharmony_ci public void startProduction(ImageConsumer ic) 162141cc406Sopenharmony_ci { 163141cc406Sopenharmony_ci System.out.println("In startProduction()"); 164141cc406Sopenharmony_ci addConsumer(ic); 165141cc406Sopenharmony_ci scan(); 166141cc406Sopenharmony_ci } 167141cc406Sopenharmony_ci 168141cc406Sopenharmony_ci /* 169141cc406Sopenharmony_ci * Set file to write to. 170141cc406Sopenharmony_ci */ 171141cc406Sopenharmony_ci public void setOutputFile(OutputStream o) 172141cc406Sopenharmony_ci { outputFile = o; } 173141cc406Sopenharmony_ci 174141cc406Sopenharmony_ci /* 175141cc406Sopenharmony_ci * Ignore this: 176141cc406Sopenharmony_ci */ 177141cc406Sopenharmony_ci public void requestTopDownLeftRightResend(ImageConsumer ic) 178141cc406Sopenharmony_ci { } 179141cc406Sopenharmony_ci 180141cc406Sopenharmony_ci /* 181141cc406Sopenharmony_ci * Go to next line in image, reallocating if necessary. 182141cc406Sopenharmony_ci */ 183141cc406Sopenharmony_ci private void nextLine() 184141cc406Sopenharmony_ci { 185141cc406Sopenharmony_ci x = 0; 186141cc406Sopenharmony_ci ++y; 187141cc406Sopenharmony_ci if (y >= height || image == null) 188141cc406Sopenharmony_ci { // Got to reallocate. 189141cc406Sopenharmony_ci int oldSize = image == null ? 0 : width*height; 190141cc406Sopenharmony_ci height += STRIP_HEIGHT; // Add more lines. 191141cc406Sopenharmony_ci int newSize = width*height; 192141cc406Sopenharmony_ci int[] newImage = new int[newSize]; 193141cc406Sopenharmony_ci int i; 194141cc406Sopenharmony_ci if (oldSize != 0) // Copy old data. 195141cc406Sopenharmony_ci for (i = 0; i < oldSize; i++) 196141cc406Sopenharmony_ci newImage[i] = image[i]; 197141cc406Sopenharmony_ci // Fill new pixels with 0's, setting 198141cc406Sopenharmony_ci // alpha channel. 199141cc406Sopenharmony_ci for (i = oldSize; i < newSize; i++) 200141cc406Sopenharmony_ci newImage[i] = (255 << 24); 201141cc406Sopenharmony_ci image = newImage; 202141cc406Sopenharmony_ci System.out.println("nextLine: newSize="+newSize); 203141cc406Sopenharmony_ci // Tell clients. 204141cc406Sopenharmony_ci tellDimensions(width, height); 205141cc406Sopenharmony_ci } 206141cc406Sopenharmony_ci } 207141cc406Sopenharmony_ci 208141cc406Sopenharmony_ci /* 209141cc406Sopenharmony_ci * Process a buffer of data. 210141cc406Sopenharmony_ci */ 211141cc406Sopenharmony_ci private boolean process(byte[] data, int readLen) 212141cc406Sopenharmony_ci { 213141cc406Sopenharmony_ci int prevY = y > 0 ? y : 0; // Save current Y-coord. 214141cc406Sopenharmony_ci int i; 215141cc406Sopenharmony_ci switch (parms.format) 216141cc406Sopenharmony_ci { 217141cc406Sopenharmony_ci case SaneParameters.FRAME_RED: 218141cc406Sopenharmony_ci case SaneParameters.FRAME_GREEN: 219141cc406Sopenharmony_ci case SaneParameters.FRAME_BLUE: 220141cc406Sopenharmony_ci System.out.println("Process RED, GREEN or BLUE"); 221141cc406Sopenharmony_ci int cindex = 2 - (parms.format - SaneParameters.FRAME_RED); 222141cc406Sopenharmony_ci // Single frame. 223141cc406Sopenharmony_ci for (i = 0; i < readLen; ++i) 224141cc406Sopenharmony_ci { // Doing a single color frame. 225141cc406Sopenharmony_ci image[offset + i] |= 226141cc406Sopenharmony_ci (((int) data[i]) & 0xff) << (8*cindex); 227141cc406Sopenharmony_ci ++x; 228141cc406Sopenharmony_ci if (x >= width) 229141cc406Sopenharmony_ci nextLine(); 230141cc406Sopenharmony_ci } 231141cc406Sopenharmony_ci break; 232141cc406Sopenharmony_ci case SaneParameters.FRAME_RGB: 233141cc406Sopenharmony_ci for (i = 0; i < readLen; ++i) 234141cc406Sopenharmony_ci { 235141cc406Sopenharmony_ci int b = 2 - (offset + i)%3; 236141cc406Sopenharmony_ci image[(offset + i)/3] |= 237141cc406Sopenharmony_ci (((int) data[i]) & 0xff) << (8*b); 238141cc406Sopenharmony_ci if (b == 0) 239141cc406Sopenharmony_ci { 240141cc406Sopenharmony_ci ++x; 241141cc406Sopenharmony_ci if (x >= width) 242141cc406Sopenharmony_ci nextLine(); 243141cc406Sopenharmony_ci } 244141cc406Sopenharmony_ci } 245141cc406Sopenharmony_ci break; 246141cc406Sopenharmony_ci case SaneParameters.FRAME_GRAY: 247141cc406Sopenharmony_ci System.out.println("Process GREY"); 248141cc406Sopenharmony_ci // Single frame. 249141cc406Sopenharmony_ci for (i = 0; i < readLen; ++i) 250141cc406Sopenharmony_ci { 251141cc406Sopenharmony_ci int v = ((int) data[i]) & 0xff; 252141cc406Sopenharmony_ci image[offset + i] |= (v<<16) | (v<<8) | (v); 253141cc406Sopenharmony_ci ++x; 254141cc406Sopenharmony_ci if (x >= width) 255141cc406Sopenharmony_ci nextLine(); 256141cc406Sopenharmony_ci } 257141cc406Sopenharmony_ci break; 258141cc406Sopenharmony_ci } 259141cc406Sopenharmony_ci offset += readLen; // Update where we are. 260141cc406Sopenharmony_ci // Show it. 261141cc406Sopenharmony_ci System.out.println("PrevY = " + prevY + ", y = " + y); 262141cc406Sopenharmony_ci// tellPixels(0, prevY, width, y - prevY); 263141cc406Sopenharmony_ci tellPixels(0, 0, width, height); 264141cc406Sopenharmony_ci return true; 265141cc406Sopenharmony_ci } 266141cc406Sopenharmony_ci 267141cc406Sopenharmony_ci /* 268141cc406Sopenharmony_ci * Start scanning. 269141cc406Sopenharmony_ci */ 270141cc406Sopenharmony_ci public void scan() 271141cc406Sopenharmony_ci { 272141cc406Sopenharmony_ci int dataLen = 32*1024; 273141cc406Sopenharmony_ci byte [] data = new byte[dataLen]; 274141cc406Sopenharmony_ci int [] readLen = new int[1]; 275141cc406Sopenharmony_ci int frameCnt = 0; 276141cc406Sopenharmony_ci // For now, use default RGB model. 277141cc406Sopenharmony_ci cm = ColorModel.getRGBdefault(); 278141cc406Sopenharmony_ci int status; 279141cc406Sopenharmony_ci image = null; 280141cc406Sopenharmony_ci do // Do each frame. 281141cc406Sopenharmony_ci { 282141cc406Sopenharmony_ci frameCnt++; 283141cc406Sopenharmony_ci x = 0; // Init. position. 284141cc406Sopenharmony_ci y = -1; 285141cc406Sopenharmony_ci offset = 0; 286141cc406Sopenharmony_ci System.out.println("Reading frame #" + frameCnt); 287141cc406Sopenharmony_ci status = sane.start(handle); 288141cc406Sopenharmony_ci if (status != Sane.STATUS_GOOD) 289141cc406Sopenharmony_ci { 290141cc406Sopenharmony_ci System.out.println("start() failed. Status= " 291141cc406Sopenharmony_ci + status); 292141cc406Sopenharmony_ci tellStatus(ImageConsumer.IMAGEERROR); 293141cc406Sopenharmony_ci return; 294141cc406Sopenharmony_ci } 295141cc406Sopenharmony_ci status = sane.getParameters(handle, parms); 296141cc406Sopenharmony_ci if (status != Sane.STATUS_GOOD) 297141cc406Sopenharmony_ci { 298141cc406Sopenharmony_ci System.out.println("getParameters() failed. Status= " 299141cc406Sopenharmony_ci + status); 300141cc406Sopenharmony_ci tellStatus(ImageConsumer.IMAGEERROR); 301141cc406Sopenharmony_ci return; //++++cleanup. 302141cc406Sopenharmony_ci } 303141cc406Sopenharmony_ci if (frameCnt == 1) // First time? 304141cc406Sopenharmony_ci { 305141cc406Sopenharmony_ci width = parms.pixelsPerLine; 306141cc406Sopenharmony_ci if (parms.lines >= 0) 307141cc406Sopenharmony_ci height = parms.lines - STRIP_HEIGHT + 1; 308141cc406Sopenharmony_ci else // Hand-scanner. 309141cc406Sopenharmony_ci height = 0; 310141cc406Sopenharmony_ci nextLine(); // Allocate image. 311141cc406Sopenharmony_ci } 312141cc406Sopenharmony_ci while ((status = sane.read(handle, data, dataLen, readLen)) 313141cc406Sopenharmony_ci == Sane.STATUS_GOOD) 314141cc406Sopenharmony_ci { 315141cc406Sopenharmony_ci System.out.println("Read " + readLen[0] + " bytes."); 316141cc406Sopenharmony_ci if (!process(data, readLen[0])) 317141cc406Sopenharmony_ci { 318141cc406Sopenharmony_ci tellStatus(ImageConsumer.IMAGEERROR); 319141cc406Sopenharmony_ci return; 320141cc406Sopenharmony_ci } 321141cc406Sopenharmony_ci } 322141cc406Sopenharmony_ci if (status != Sane.STATUS_EOF) 323141cc406Sopenharmony_ci { 324141cc406Sopenharmony_ci System.out.println("read() failed. Status= " 325141cc406Sopenharmony_ci + status); 326141cc406Sopenharmony_ci tellStatus(ImageConsumer.IMAGEERROR); 327141cc406Sopenharmony_ci return; 328141cc406Sopenharmony_ci } 329141cc406Sopenharmony_ci } 330141cc406Sopenharmony_ci while (!parms.lastFrame); 331141cc406Sopenharmony_ci height = y; // For now, send whole image here. 332141cc406Sopenharmony_ci tellDimensions(width, height); 333141cc406Sopenharmony_ci tellPixels(0, 0, width, height); 334141cc406Sopenharmony_ci if (outputFile != null) // Write to file. 335141cc406Sopenharmony_ci { 336141cc406Sopenharmony_ci try 337141cc406Sopenharmony_ci { 338141cc406Sopenharmony_ci write(outputFile); 339141cc406Sopenharmony_ci } 340141cc406Sopenharmony_ci catch (IOException e) 341141cc406Sopenharmony_ci { //+++++++++++++++ 342141cc406Sopenharmony_ci System.out.println("I/O error writing file."); 343141cc406Sopenharmony_ci } 344141cc406Sopenharmony_ci outputFile = null; // Clear for next time. 345141cc406Sopenharmony_ci } 346141cc406Sopenharmony_ci tellStatus(ImageConsumer.STATICIMAGEDONE); 347141cc406Sopenharmony_ci image = null; // Allow buffer to be freed. 348141cc406Sopenharmony_ci } 349141cc406Sopenharmony_ci 350141cc406Sopenharmony_ci /* 351141cc406Sopenharmony_ci * Write ppm/pnm output for last scan to a file. 352141cc406Sopenharmony_ci */ 353141cc406Sopenharmony_ci private void write(OutputStream out) throws IOException 354141cc406Sopenharmony_ci { 355141cc406Sopenharmony_ci PrintWriter pout = new PrintWriter(out); 356141cc406Sopenharmony_ci BufferedWriter bout = new BufferedWriter(pout); 357141cc406Sopenharmony_ci int len = width*height; // Get # of pixels. 358141cc406Sopenharmony_ci int i; 359141cc406Sopenharmony_ci switch (parms.format) 360141cc406Sopenharmony_ci { 361141cc406Sopenharmony_ci case SaneParameters.FRAME_RED: 362141cc406Sopenharmony_ci case SaneParameters.FRAME_GREEN: 363141cc406Sopenharmony_ci case SaneParameters.FRAME_BLUE: 364141cc406Sopenharmony_ci case SaneParameters.FRAME_RGB: 365141cc406Sopenharmony_ci pout.print("P6\n# SANE data follows\n" + 366141cc406Sopenharmony_ci width + ' ' + height + "\n255\n"); 367141cc406Sopenharmony_ci for (i = 0; i < len; i++) 368141cc406Sopenharmony_ci { 369141cc406Sopenharmony_ci int pix = image[i]; 370141cc406Sopenharmony_ci bout.write((pix >> 16) & 0xff); 371141cc406Sopenharmony_ci bout.write((pix >> 8) & 0xff); 372141cc406Sopenharmony_ci bout.write(pix & 0xff); 373141cc406Sopenharmony_ci } 374141cc406Sopenharmony_ci break; 375141cc406Sopenharmony_ci case SaneParameters.FRAME_GRAY: 376141cc406Sopenharmony_ci pout.print("P5\n# SANE data follows\n" + 377141cc406Sopenharmony_ci width + ' ' + height + "\n255\n"); 378141cc406Sopenharmony_ci for (i = 0; i < len; i++) 379141cc406Sopenharmony_ci { 380141cc406Sopenharmony_ci int pix = image[i]; 381141cc406Sopenharmony_ci bout.write(pix & 0xff); 382141cc406Sopenharmony_ci } 383141cc406Sopenharmony_ci break; 384141cc406Sopenharmony_ci } 385141cc406Sopenharmony_ci 386141cc406Sopenharmony_ci bout.flush(); // Flush output. 387141cc406Sopenharmony_ci pout.flush(); 388141cc406Sopenharmony_ci } 389141cc406Sopenharmony_ci } 390