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