1 /* sane - Scanner Access Now Easy.
2 Copyright (C) Marian Eichholz 2001
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 /* ======================================================================
41
42 Userspace scan tool for the Microtek 3600 scanner
43
44 grayscale scan routine
45
46 (C) Marian Eichholz 2001
47
48 ====================================================================== */
49
50 #include "sm3600-scantool.h"
51
52 /* **********************************************************************
53
54 DoScanGray()
55
56 ********************************************************************** */
57
58 #define LINE_THRESHOLD 0x800
59
60 static unsigned char uchRegs075[]={
61 /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x20,
62 /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
63 /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x3F,
64 /*R_LEN*/ 0x28, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
65 /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
66 /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x40,
67 /*0x13*/ 0x15, /*0x14*/ 0x80, /*0x15*/ 0x2A,
68 /*0x16*/ 0xC0, /*0x17*/ 0x40, /*0x18*/ 0xC0,
69 /*0x19*/ 0x40, /*0x1A*/ 0xFF, /*0x1B*/ 0x01,
70 /*0x1C*/ 0x88, /*0x1D*/ 0x40, /*0x1E*/ 0x4C,
71 /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
72 /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
73 /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
74 /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
75 /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
76 /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
77 /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
78 /*0x34*/ 0x83, /*0x35*/ 0x29, /*0x36*/ 0x00,
79 /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
80 /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
81 /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
82 /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x00,
83 /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
84 /*R_CTL*/ 0x39, /*0x47*/ 0xC0, /*0x48*/ 0x40,
85 /*0x49*/ 0x9E, /*0x4A*/ 0x8C };
86
87 static unsigned char uchRegs100[]={
88 /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x20,
89 /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
90 /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x3F,
91 /*R_LEN*/ 0x34, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
92 /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
93 /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x42,
94 /*0x13*/ 0x15, /*0x14*/ 0x84, /*0x15*/ 0x2A,
95 /*0x16*/ 0xC2, /*0x17*/ 0x40, /*0x18*/ 0xC2,
96 /*0x19*/ 0x40, /*0x1A*/ 0xFF, /*0x1B*/ 0x01,
97 /*0x1C*/ 0x88, /*0x1D*/ 0x40, /*0x1E*/ 0x4C,
98 /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
99 /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
100 /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
101 /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
102 /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
103 /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
104 /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
105 /*0x34*/ 0x63, /*0x35*/ 0x29, /*0x36*/ 0x00,
106 /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
107 /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
108 /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
109 /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x80,
110 /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
111 /*R_CTL*/ 0x39, /*0x47*/ 0xC2, /*0x48*/ 0x40,
112 /*0x49*/ 0x9E, /*0x4A*/ 0x8C };
113
114 static unsigned char uchRegs200[]={
115 /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x24,
116 /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
117 /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x3F,
118 /*R_LEN*/ 0x22, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
119 /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
120 /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x42,
121 /*0x13*/ 0x15, /*0x14*/ 0x42, /*0x15*/ 0x15,
122 /*0x16*/ 0x42, /*0x17*/ 0x15, /*0x18*/ 0x42,
123 /*0x19*/ 0x15, /*0x1A*/ 0x07, /*0x1B*/ 0x00,
124 /*0x1C*/ 0x08, /*0x1D*/ 0x12, /*0x1E*/ 0x4C,
125 /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
126 /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
127 /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
128 /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
129 /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
130 /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
131 /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
132 /*0x34*/ 0x43, /*0x35*/ 0x29, /*0x36*/ 0x00,
133 /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
134 /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
135 /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
136 /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x80,
137 /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
138 /*R_CTL*/ 0x39, /*0x47*/ 0x42, /*0x48*/ 0x15,
139 /*0x49*/ 0x9E, /*0x4A*/ 0x8C };
140
141 static unsigned char uchRegs300[]={
142 /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x2A,
143 /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
144 /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x2A,
145 /*R_LEN*/ 0x16, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
146 /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
147 /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x40,
148 /*0x13*/ 0x15, /*0x14*/ 0x40, /*0x15*/ 0x15,
149 /*0x16*/ 0x40, /*0x17*/ 0x15, /*0x18*/ 0x40,
150 /*0x19*/ 0x15, /*0x1A*/ 0x07, /*0x1B*/ 0x00,
151 /*0x1C*/ 0x08, /*0x1D*/ 0x12, /*0x1E*/ 0x4C,
152 /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
153 /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
154 /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
155 /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
156 /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
157 /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
158 /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
159 /*0x34*/ 0x03, /*0x35*/ 0x29, /*0x36*/ 0x00,
160 /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
161 /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
162 /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
163 /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x80,
164 /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
165 /*R_CTL*/ 0x39, /*0x47*/ 0x40, /*0x48*/ 0x15,
166 /*0x49*/ 0x96, /*0x4A*/ 0x8C };
167
168 static unsigned char uchRegs600[]={
169 /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x3F,
170 /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
171 /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x3F,
172 /*R_LEN*/ 0x16, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
173 /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
174 /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x42,
175 /*0x13*/ 0x15, /*0x14*/ 0x42, /*0x15*/ 0x15,
176 /*0x16*/ 0x42, /*0x17*/ 0x15, /*0x18*/ 0x42,
177 /*0x19*/ 0x15, /*0x1A*/ 0x07, /*0x1B*/ 0x00,
178 /*0x1C*/ 0x08, /*0x1D*/ 0x12, /*0x1E*/ 0x4C,
179 /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
180 /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
181 /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
182 /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
183 /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
184 /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
185 /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
186 /*0x34*/ 0x03, /*0x35*/ 0x29, /*0x36*/ 0x00,
187 /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
188 /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
189 /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
190 /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x80,
191 /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
192 /*R_CTL*/ 0x39, /*0x47*/ 0x42, /*0x48*/ 0x15,
193 /*0x49*/ 0x96, /*0x4A*/ 0x8C };
194
195 /* ======================================================================
196
197 ReadNextGrayLine()
198
199 ====================================================================== */
200
ReadNextGrayLine(PTInstance this)201 static TState ReadNextGrayLine(PTInstance this)
202 {
203 int iWrite;
204 int iDot;
205 unsigned char chBits;
206 int iRead; /* read position in raw line */
207 int nInterpolator;
208
209 iWrite=0;
210
211 while (iWrite<this->state.cxMax) /* max 1 time in reality */
212 {
213 while (iWrite<this->state.cxMax &&
214 this->state.iBulkReadPos<this->state.cchBulk)
215 this->state.ppchLines[0][iWrite++] += /* add! */
216 this->state.pchBuf[this->state.iBulkReadPos++]<<4;
217 if (iWrite<this->state.cxMax) /* we need an additional chunk */
218 {
219 if (this->state.bLastBulk)
220 return SANE_STATUS_EOF;
221 this->state.cchBulk=BulkReadBuffer(this,this->state.pchBuf,
222 USB_CHUNK_SIZE);
223 dprintf(DEBUG_SCAN,"bulk read: %d byte(s), line #%d\n",
224 this->state.cchBulk, this->state.iLine);
225 if (this->bWriteRaw)
226 fwrite(this->state.pchBuf,1,this->state.cchBulk,this->fhScan);
227 INST_ASSERT();
228 if (this->state.cchBulk!=USB_CHUNK_SIZE)
229 this->state.bLastBulk=true;
230 this->state.iBulkReadPos=0;
231 }
232 } /* while raw line buffer acquiring */
233 this->state.iLine++;
234 iDot=0; chBits=0; /* init pixelbuffer */
235 for (nInterpolator=50, iWrite=0, iRead=0;
236 iRead<this->state.cxMax;
237 iRead++)
238 {
239 nInterpolator+=this->state.nFixAspect;
240 if (nInterpolator<100) continue; /* res. reduction */
241 nInterpolator-=100;
242 if (iWrite>=this->state.cchLineOut) continue;
243 /* dprintf(DEBUG_SCAN," i=%d",iTo); */
244 if (this->mode==gray)
245 this->state.pchLineOut[iWrite++]=
246 this->state.ppchLines[0][iRead]>>4;
247 else
248 {
249 unsigned char chBit; /* 1=white */
250 if (this->mode==line)
251 chBit=(this->state.ppchLines[0][iRead]<LINE_THRESHOLD);
252 else
253 {
254 short nError=this->state.ppchLines[0][iRead];
255 /* printf("(%d)",nError); */
256 if (nError>=0xFF0)
257 {
258 nError-=0xFF0;
259 chBit=0;
260 }
261 else
262 chBit=1;
263 /* since I sketched the Floyd-Steinberg
264 algorithm from heart, I have no idea, if
265 there is room for improvement in the
266 coefficients. If You know, please drop
267 me a line (eichholz@computer.org, 1.4.2001) */
268 #define FASTDITHER
269 #ifdef FASTDITHER
270 this->state.ppchLines[0][iRead+1]+=(nError>>2); /* 8/16 */
271 this->state.ppchLines[1][iRead+1]+=(nError>>1);
272 this->state.ppchLines[1][iRead] +=(nError>>2); /* 8/16 */
273 #else
274 this->state.ppchLines[0][iRead+1]+=(nError*5)>>4;
275 this->state.ppchLines[1][iRead+1]+=(nError*8)>>4;
276 this->state.ppchLines[1][iRead] +=(nError*3)>>4;
277 #endif
278 }
279 chBits=(chBits<<1)|chBit;
280 iDot++;
281 if (iDot==8 && iWrite<this->state.cchLineOut)
282 {
283 this->state.pchLineOut[iWrite++]=chBits;
284 iDot=0; chBits=0;
285 }
286 } /* gray pixel postprocessing */
287 } /* line postprocessing */
288 if (iDot && iWrite<this->state.cchLineOut)
289 this->state.pchLineOut[iWrite++]=chBits;
290 /* cycle the history lines and clear the preread buffer*/
291 {
292 short *p=this->state.ppchLines[0];
293 this->state.ppchLines[0]=this->state.ppchLines[1];
294 this->state.ppchLines[1]=p;
295 memset(this->state.ppchLines[1],0,(this->state.cxMax+1)*sizeof(short));
296 }
297 return SANE_STATUS_GOOD;
298 }
299
300 /* ======================================================================
301
302 StartScanGray()
303
304 ====================================================================== */
305
306 __SM3600EXPORT__
StartScanGray(TInstance *this)307 TState StartScanGray(TInstance *this)
308 {
309 unsigned char *puchRegs;
310 int i;
311 if (this->state.bScanning)
312 return SetError(this,SANE_STATUS_DEVICE_BUSY,"scan active");
313 memset(&(this->state),0,sizeof(TScanState));
314 this->state.ReadProc =ReadNextGrayLine;
315 puchRegs=NULL;
316 switch (this->param.res)
317 {
318 case 75: puchRegs=uchRegs075; break;
319 case 100: puchRegs=uchRegs100; break;
320 case 200: puchRegs=uchRegs200; break;
321 case 300: puchRegs=uchRegs300; break;
322 case 600: puchRegs=uchRegs600; break;
323 }
324 GetAreaSize(this);
325 this->state.cyTotalPath = this->param.y/2;
326 DoJog(this,this->state.cyTotalPath);
327 INST_ASSERT();
328 this->state.cyTotalPath += this->param.cy/2; /* for jogging back */
329
330 /*
331 regular scan is asynchronously, that is,
332 the scanning is issued, and the driver does bulk reads,
333 until there are no data left.
334 */
335 RegWriteArray(this,R_ALL, NUM_SCANREGS, puchRegs); INST_ASSERT();
336 RegWrite(this,R_SPOS, 2,
337 this->param.x/2+this->calibration.xMargin); INST_ASSERT();
338 RegWrite(this,R_SLEN, 2, this->state.cyWindow); INST_ASSERT();
339 RegWrite(this,R_SWID, 2, this->state.cxWindow); INST_ASSERT();
340 RegWrite(this,R_STPS, 2, 0); INST_ASSERT();
341
342 /* upload gamma table */
343 RegWrite(this,0x41,1,0x01); /* gamma, gray */
344 RegWrite(this,0x40,1,0x20); /* FIFO at 0x08000 */
345 UploadGammaTable(this,0,this->agammaY); INST_ASSERT();
346
347 UploadGainCorrection(this, 0x2000);
348 INST_ASSERT();
349
350 /* for halftone dithering we need one history line */
351 this->state.pchBuf=malloc(USB_CHUNK_SIZE);
352 this->state.cBacklog=2;
353 this->state.ppchLines=calloc(this->state.cBacklog,sizeof(short *));
354 if (!this->state.pchBuf || !this->state.ppchLines)
355 return FreeState(this,SetError(this,
356 SANE_STATUS_NO_MEM,"no buffers available"));
357 for (i=0; i<this->state.cBacklog; i++)
358 {
359 this->state.ppchLines[i]=calloc(this->state.cxMax+1,
360 sizeof(short)); /* 1 dummy at right edge */
361 if (!this->state.ppchLines[i])
362 return FreeState(this,SetError(this,
363 SANE_STATUS_NO_MEM,"no buffers available"));
364 }
365
366 /* calculate and prepare intermediate line transfer buffer */
367
368 this->state.cchLineOut=(this->mode==gray)
369 ? this->state.cxPixel
370 : (this->state.cxPixel+7)/8;
371
372 this->state.pchLineOut = malloc(this->state.cchLineOut);
373 if (!this->state.pchLineOut)
374 return FreeState(this,SetError(this,
375 SANE_STATUS_NO_MEM,
376 "no buffers available"));
377
378 /* start the unit, when all buffers are available */
379
380 RegWrite(this,R_CTL, 1, 0x39); INST_ASSERT();
381 RegWrite(this,R_CTL, 1, 0x79); INST_ASSERT();
382 RegWrite(this,R_CTL, 1, 0xF9); INST_ASSERT();
383
384 this->state.bScanning = true;
385 return SANE_STATUS_GOOD;
386 }
387