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 ====================================================================== */
45
46 #include <stdarg.h>
47 #include <unistd.h>
48 #include "sm3600-scantool.h"
49
50 /* **********************************************************************
51
52 dprintf(DEBUG_XXXX, format, ...)
53
54 Put a debug message on STDERR (or whatever). The message is prefixed with
55 a "debug:" and given, if the current debugging flags contain the given
56 flag "ulType".
57
58 ********************************************************************** */
59
60 #ifdef INSANE_VERSION
DBG(int nLevel, const char *szFormat, ...)61 void DBG(int nLevel, const char *szFormat, ...)
62 {
63 szFormat++;
64 }
65 #endif
66
67 __SM3600EXPORT__
debug_printf(unsigned long ulType, const char *szFormat, ...)68 void debug_printf(unsigned long ulType, const char *szFormat, ...)
69 {
70 va_list ap;
71 if ((ulDebugMask & ulType)!=ulType) return;
72 if (*szFormat=='~')
73 szFormat++;
74 else
75 fprintf(stderr,"debug:");
76 va_start(ap,szFormat);
77 vfprintf(stderr,szFormat,ap);
78 va_end(ap);
79 }
80
81 /* **********************************************************************
82
83 SetError(error, format, ...)
84
85 The program is aborted, all handles and resources are freed (this
86 being global) and the user gets a nice panic screen :-)
87
88 ********************************************************************** */
89
90 __SM3600EXPORT__
SetError(TInstance *this, int nError, const char *szFormat, ...)91 int SetError(TInstance *this, int nError, const char *szFormat, ...)
92 {
93 va_list ap;
94 if (this->nErrorState) return 0; /* do not overwrite error state */
95 this->nErrorState=nError;
96 this->szErrorReason=malloc(500);
97
98 if (szFormat!=NULL && this->szErrorReason)
99 {
100 va_start(ap,szFormat);
101 vsnprintf(this->szErrorReason,499,szFormat,ap);
102 va_end(ap);
103 this->szErrorReason[499]='\0';
104 }
105 return nError;
106 }
107
108 #ifdef INSANE_VERSION
109
110 /* **********************************************************************
111
112 DumpBuffer(fh,pch,cch)
113
114 ********************************************************************** */
115
116 __SM3600EXPORT__
DumpBuffer(FILE *fh, const char *pch, int cch)117 void DumpBuffer(FILE *fh, const char *pch, int cch)
118 {
119 int i=0;
120 while (i<cch)
121 {
122 if (!(i & 15))
123 {
124 if (i) fprintf(fh,"\n");
125 fprintf(fh,"%04X:",i);
126 }
127 fprintf(fh," %02X",(unsigned char)pch[i]);
128 i++;
129 }
130 fprintf(fh,"\n");
131 }
132
133 #endif
134
135 /* **********************************************************************
136
137 FreeState()
138
139 Frees all dynamical memory for scan buffering.
140
141 ********************************************************************** */
142
143 __SM3600EXPORT__
FreeState(TInstance *this, TState nReturn)144 TState FreeState(TInstance *this, TState nReturn)
145 {
146 if (this->state.ppchLines)
147 {
148 int i;
149 for (i=0; i<this->state.cBacklog; i++)
150 {
151 if (this->state.ppchLines[i])
152 free(this->state.ppchLines[i]);
153 }
154 free(this->state.ppchLines);
155 }
156 if (this->state.pchLineOut) free(this->state.pchLineOut);
157 if (this->state.pchBuf) free(this->state.pchBuf);
158 this->state.pchBuf =NULL;
159 this->state.pchLineOut=NULL;
160 this->state.ppchLines =NULL;
161 return nReturn;
162 }
163
164 /* ======================================================================
165
166 EndScan()
167
168 ====================================================================== */
169
170 __SM3600EXPORT__
EndScan(TInstance *this)171 TState EndScan(TInstance *this)
172 {
173 if (!this->state.bScanning) return SANE_STATUS_GOOD;
174 /* move slider back to start */
175 this->state.bScanning=false;
176 FreeState(this,0);
177 INST_ASSERT();
178 return DoJog(this,-this->state.cyTotalPath);
179 }
180
181 /* ======================================================================
182
183 TState CancelScan(TInstance *this)
184
185 ====================================================================== */
186
187 __SM3600EXPORT__
CancelScan(TInstance *this)188 TState CancelScan(TInstance *this)
189 {
190 TBool bCanceled;
191 DBG(DEBUG_INFO,"CancelScan() called\n");
192
193 this->state.cyTotalPath-=RegRead(this,R_POS,2);
194 DBG(DEBUG_JUNK,"stepping back %d steps\n",this->state.cyTotalPath);
195 /* this->state.cyTotalPath=0; */
196
197 usleep(200);
198 DoReset(this);
199 EndScan(this); /* and step back! */
200
201 DBG(DEBUG_JUNK,"cs4: %d\n",(int)this->nErrorState);
202 bCanceled=this->state.bCanceled;
203 this->state.bCanceled=false; /* re-enable Origination! */
204 if (!this->bOptSkipOriginate)
205 DoOriginate(this,false); /* have an error here... */
206 this->state.bCanceled=bCanceled;
207 DBG(DEBUG_JUNK,"cs5: %d\n",(int)this->nErrorState);
208 INST_ASSERT();
209 DBG(DEBUG_INFO,"cs6: ok.\n");
210 return SANE_STATUS_CANCELLED; /* or shall be say GOOD? */
211 }
212
213
214 /* ======================================================================
215
216 ReadChunk()
217
218 ====================================================================== */
219
220 __SM3600EXPORT__
ReadChunk(TInstance *this, unsigned char *achOut, int cchMax, int *pcchRead)221 TState ReadChunk(TInstance *this, unsigned char *achOut,
222 int cchMax, int *pcchRead)
223 {
224 /* have we to copy more than we have? */
225 /* can the current line fill the buffer ? */
226 int rc;
227 *pcchRead=0;
228 INST_ASSERT();
229 if (!this->state.bScanning)
230 return SANE_STATUS_CANCELLED; /* deferred cancel? */
231 if (this->state.bCanceled) /* deferred cancellation? */
232 return CancelScan(this);
233 INST_ASSERT();
234 /* 22.4.2001: This took me hard, harder, hardest:*/
235
236 /* We need to fill the line buffer with at least a *rest* of a
237 line. A single line will do. */
238 /* Thus, "iLine>0" is a suitable condition. */
239 /* Without the preread, there will a dummy line be read, if the
240 target buffer is large enough.*/
241 if (this->state.iLine)
242 rc=SANE_STATUS_GOOD;
243 else
244 rc=(*(this->state.ReadProc))(this); /* preread one line */
245 if (rc!=SANE_STATUS_GOOD) return rc;
246 dprintf(DEBUG_BUFFER,"Chunk-Init: cchMax = %d\n",cchMax);
247 while (this->state.iReadPos + cchMax > this->state.cchLineOut)
248 {
249 int cch;
250 /* copy rest of the line into target */
251 cch = this->state.cchLineOut - this->state.iReadPos;
252 memcpy(achOut,
253 this->state.pchLineOut+this->state.iReadPos,
254 cch);
255 cchMax-=cch; /* advance parameters */
256 achOut+=cch;
257 (*pcchRead)+=cch;
258 this->state.iReadPos=0;
259 rc=(*(this->state.ReadProc))(this);
260 dprintf(DEBUG_BUFFER,"Chunk-Read: cchMax = %d\n",cchMax);
261 if (rc!=SANE_STATUS_GOOD)
262 return rc; /* should be NOT(!) EOF, but then: good and away! */
263 }
264 dprintf(DEBUG_BUFFER,"Chunk-Exit: cchMax = %d\n",cchMax);
265 if (!cchMax) return SANE_STATUS_GOOD; /* now everything fits! */
266 (*pcchRead) += cchMax;
267 memcpy(achOut,
268 this->state.pchLineOut+this->state.iReadPos,
269 cchMax);
270 this->state.iReadPos += cchMax;
271 return SANE_STATUS_GOOD;
272 }
273
274 /* ======================================================================
275
276 GetAreaSize()
277
278 ====================================================================== */
279
280 __SM3600EXPORT__
GetAreaSize(TInstance *this)281 void GetAreaSize(TInstance *this)
282 {
283 /* this->state.cxPixel : pixels, we *want* (after interpolation)
284 this->state.cxMax : pixels, we *need* (before interpolation) */
285 int nRefResX,nRefResY;
286 nRefResX=nRefResY=this->param.res;
287 switch (this->param.res)
288 {
289 case 75: nRefResX=100; this->state.nFixAspect=75; break;
290 default: this->state.nFixAspect=100; break;
291 }
292 this->state.cxPixel =this->param.cx*this->param.res/1200;
293 this->state.cyPixel =this->param.cy*this->param.res/1200;
294 this->state.cxMax =this->state.cxPixel*100/this->state.nFixAspect;
295 this->state.cxWindow =this->state.cxMax*600/nRefResX;
296 this->state.cyWindow =this->state.cyPixel*600/nRefResY;
297 dprintf(DEBUG_SCAN,"requesting %d[600] %d[real] %d[raw]\n",
298 this->state.cxWindow,this->state.cxPixel,this->state.cxMax);
299 }
300
301 /* ======================================================================
302
303 ResetCalibration()
304
305 Free calibration data. The Instance can be safely released afterwards.
306
307 ====================================================================== */
308
309 __SM3600EXPORT__
ResetCalibration(TInstance *this)310 void ResetCalibration(TInstance *this)
311 {
312 if (this->calibration.achStripeY)
313 free(this->calibration.achStripeY);
314 if (this->calibration.achStripeR)
315 free(this->calibration.achStripeR);
316 if (this->calibration.achStripeG)
317 free(this->calibration.achStripeG);
318 if (this->calibration.achStripeB)
319 free(this->calibration.achStripeB);
320 /* reset all handles, pointers, flags */
321 memset(&(this->calibration),0,sizeof(this->calibration));
322 /* TODO: type specific margins */
323 this->calibration.xMargin=200;
324 this->calibration.yMargin=0x019D;
325 this->calibration.nHoleGray=10;
326 this->calibration.rgbBias=0x888884;
327 this->calibration.nBarGray=0xC0;
328 }
329
330 /* ======================================================================
331
332 InitGammaTables()
333
334 Init gammy tables and gain tables within controller memory.
335
336 ====================================================================== */
337
338 __SM3600EXPORT__
InitGammaTables(TInstance *this, int nBrightness, int nContrast)339 TState InitGammaTables(TInstance *this, int nBrightness, int nContrast)
340 {
341 long i;
342 long lOffset;
343 long lScale;
344 /* the rescaling is done with temporary zero translation to 2048 */
345 lOffset=(nBrightness-128)*16; /* signed! */
346 lScale=(nContrast+128)*100; /* in percent */
347 for (i=0; i<4096; i++)
348 {
349 int n=(int)((i+lOffset)*lScale/12800L+2048L);
350 if (n<0) n=0;
351 else if (n>4095) n=4095;
352 this->agammaY[i]=n;
353 this->agammaR[i]=n;
354 this->agammaG[i]=n;
355 this->agammaB[i]=n;
356 }
357 return SANE_STATUS_GOOD;
358 }
359
360 #ifdef INSANE_VERSION
361
362 /* ======================================================================
363
364 DoScanFile()
365
366 Top level caller for scantool.
367
368 ====================================================================== */
369
370 #define APP_CHUNK_SIZE 0x8000
371
372 __SM3600EXPORT__
DoScanFile(TInstance *this)373 TState DoScanFile(TInstance *this)
374 {
375 int cx,cy;
376 long lcchRead;
377 TState rc;
378 char *achBuf;
379
380 achBuf=malloc(APP_CHUNK_SIZE);
381 rc=SANE_STATUS_GOOD; /* make compiler happy */
382 rc=InitGammaTables(this, this->param.nBrightness, this->param.nContrast);
383 if (rc!=SANE_STATUS_GOOD) return rc;
384 if (this->mode==color)
385 rc=StartScanColor(this);
386 else
387 rc=StartScanGray(this);
388 cx=this->state.cxPixel;
389 cy=this->state.cyPixel;
390 if (this->bVerbose)
391 fprintf(stderr,"scanning %d by %d\n",cx,cy);
392 if (this->fhScan && !this->bWriteRaw && !this->pchPageBuffer)
393 {
394 switch (this->mode)
395 {
396 case color: fprintf(this->fhScan,"P6\n%d %d\n255\n",cx,cy);
397 break;
398 case gray: fprintf(this->fhScan,"P5\n%d %d\n255\n",cx,cy);
399 break;
400 default: fprintf(this->fhScan,"P4\n%d %d\n",cx,cy);
401 break;
402 }
403 }
404 lcchRead=0L;
405 while (!rc)
406 {
407 int cch;
408 cch=0;
409 rc=ReadChunk(this,achBuf,APP_CHUNK_SIZE,&cch);
410 if (cch>0 && this->fhScan && cch<=APP_CHUNK_SIZE)
411 {
412 if (this->pchPageBuffer)
413 {
414 #ifdef SM3600_DEBUGPAGEBUFFER
415 if (this->bVerbose)
416 fprintf(stderr,"ichPageBuffer:%d, cch:%d, cchPageBuffer:%d\n",
417 this->ichPageBuffer,cch,this->cchPageBuffer);
418 #endif
419 CHECK_ASSERTION(this->ichPageBuffer+cch<=this->cchPageBuffer);
420 memcpy(this->pchPageBuffer+this->ichPageBuffer,
421 achBuf,cch);
422 this->ichPageBuffer+=cch;
423 }
424 else if (!this->bWriteRaw)
425 fwrite(achBuf,1,cch,this->fhScan);
426 lcchRead+=cch;
427 }
428 }
429 free(achBuf);
430 if (this->bVerbose)
431 fprintf(stderr,"read %ld image byte(s)\n",lcchRead);
432 EndScan(this);
433 INST_ASSERT();
434 return SANE_STATUS_GOOD;
435 }
436
437 #endif
438