kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			441 wiersze
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			441 wiersze
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
| /* sane - Scanner Access Now Easy.
 | |
|    Copyright (C) Marian Eichholz 2001
 | |
|    This file is part of the SANE package.
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or
 | |
|    modify it under the terms of the GNU General Public License as
 | |
|    published by the Free Software Foundation; either version 2 of the
 | |
|    License, or (at your option) any later version.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful, but
 | |
|    WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|    General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 | |
|    MA 02111-1307, USA.
 | |
| 
 | |
|    As a special exception, the authors of SANE give permission for
 | |
|    additional uses of the libraries contained in this release of SANE.
 | |
| 
 | |
|    The exception is that, if you link a SANE library with other files
 | |
|    to produce an executable, this does not by itself cause the
 | |
|    resulting executable to be covered by the GNU General Public
 | |
|    License.  Your use of that executable is in no way restricted on
 | |
|    account of linking the SANE library code into it.
 | |
| 
 | |
|    This exception does not, however, invalidate any other reasons why
 | |
|    the executable file might be covered by the GNU General Public
 | |
|    License.
 | |
| 
 | |
|    If you submit changes to SANE to the maintainers to be included in
 | |
|    a subsequent release, you agree by submitting the changes that
 | |
|    those changes may be distributed with this exception intact.
 | |
| 
 | |
|    If you write modifications of your own for SANE, it is your choice
 | |
|    whether to permit this exception to apply to your modifications.
 | |
|    If you do not wish that, delete this exception notice.
 | |
| */
 | |
| 
 | |
| /* ======================================================================
 | |
| 
 | |
| Userspace scan tool for the Microtek 3600 scanner
 | |
| 
 | |
| $Id$
 | |
| 
 | |
| ====================================================================== */
 | |
| 
 | |
| #include <unistd.h>
 | |
| #include "sm3600-scantool.h"
 | |
| 
 | |
| /* **********************************************************************
 | |
| 
 | |
| dprintf(DEBUG_XXXX, format, ...)
 | |
| 
 | |
| Put a debug message on STDERR (or whatever). The message is prefixed with
 | |
| a "debug:" and given, if the current debugging flags contain the given
 | |
| flag "ulType".
 | |
| 
 | |
| ********************************************************************** */
 | |
| 
 | |
| #ifdef INSANE_VERSION
 | |
| void DBG(int nLevel, const char *szFormat, ...)
 | |
| {
 | |
|   szFormat++;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| __SM3600EXPORT__
 | |
| void debug_printf(unsigned long ulType, const char *szFormat, ...)
 | |
| {
 | |
|   va_list ap;
 | |
|   if ((ulDebugMask & ulType)!=ulType) return;
 | |
|   if (*szFormat=='~')
 | |
|     szFormat++;
 | |
|   else
 | |
|     fprintf(stderr,"debug:");
 | |
|   va_start(ap,szFormat);
 | |
|   vfprintf(stderr,szFormat,ap);
 | |
|   va_end(ap);
 | |
| }
 | |
| 
 | |
| /* **********************************************************************
 | |
| 
 | |
| SetError(error, format, ...)
 | |
| 
 | |
| The program is aborted, all handles and ressources are freed (this
 | |
| being global) and the user gets a nice panic screen :-)
 | |
| 
 | |
| ********************************************************************** */
 | |
| 
 | |
| __SM3600EXPORT__
 | |
| int SetError(TInstance *this, int nError, const char *szFormat, ...)
 | |
| {
 | |
|   va_list ap;
 | |
|   if (this->nErrorState) return 0; /* do not overwrite error state */
 | |
|   this->nErrorState=nError;
 | |
|   this->szErrorReason=malloc(500);
 | |
|   
 | |
|   if (szFormat!=NULL && this->szErrorReason)
 | |
|     {
 | |
|       va_start(ap,szFormat);
 | |
|       vsnprintf(this->szErrorReason,499,szFormat,ap);
 | |
|       va_end(ap);
 | |
|       this->szErrorReason[499]='\0';
 | |
|     }
 | |
|   return nError;
 | |
| }
 | |
| 
 | |
| #ifdef INSANE_VERSION
 | |
| 
 | |
| /* **********************************************************************
 | |
| 
 | |
| DumpBuffer(fh,pch,cch)
 | |
| 
 | |
| ********************************************************************** */
 | |
| 
 | |
| __SM3600EXPORT__
 | |
| void DumpBuffer(FILE *fh, const char *pch, int cch)
 | |
| {
 | |
|   int i=0;
 | |
|   while (i<cch)
 | |
|     {
 | |
|       if (!(i & 15))
 | |
| 	{
 | |
| 	  if (i) fprintf(fh,"\n");
 | |
| 	  fprintf(fh,"%04X:",i);
 | |
| 	}
 | |
|       fprintf(fh," %02X",(unsigned char)pch[i]);
 | |
|       i++;
 | |
|     }
 | |
|   fprintf(fh,"\n");
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| /* **********************************************************************
 | |
| 
 | |
| FreeState()
 | |
| 
 | |
| Frees all dynamical memory for scan buffering.
 | |
| 
 | |
| ********************************************************************** */
 | |
| 
 | |
| __SM3600EXPORT__
 | |
| TState FreeState(TInstance *this, TState nReturn)
 | |
| {
 | |
|   if (this->state.ppchLines)
 | |
|     {
 | |
|       int i;
 | |
|       for (i=0; i<this->state.cBacklog; i++)
 | |
| 	{
 | |
| 	  if (this->state.ppchLines[i])
 | |
| 	    free(this->state.ppchLines[i]);
 | |
| 	}
 | |
|       free(this->state.ppchLines);
 | |
|     }
 | |
|   if (this->state.pchLineOut) free(this->state.pchLineOut);
 | |
|   if (this->state.pchBuf)     free(this->state.pchBuf);
 | |
|   this->state.pchBuf    =NULL;
 | |
|   this->state.pchLineOut=NULL;
 | |
|   this->state.ppchLines =NULL;
 | |
|   return nReturn;
 | |
| }
 | |
| 
 | |
| /* ======================================================================
 | |
| 
 | |
| EndScan()
 | |
| 
 | |
| ====================================================================== */
 | |
| 
 | |
| __SM3600EXPORT__
 | |
| TState EndScan(TInstance *this)
 | |
| {
 | |
|   if (!this->state.bScanning) return SANE_STATUS_GOOD;
 | |
|   /* move slider back to start */
 | |
|   this->state.bScanning=false;
 | |
|   FreeState(this,0);
 | |
|   INST_ASSERT();
 | |
|   return DoJog(this,-this->state.cyTotalPath);
 | |
| }
 | |
| 
 | |
| /* ======================================================================
 | |
| 
 | |
| TState CancelScan(TInstance *this)
 | |
| 
 | |
| ====================================================================== */
 | |
| 
 | |
| __SM3600EXPORT__
 | |
| TState CancelScan(TInstance *this)
 | |
| {
 | |
|   TBool bCanceled;
 | |
|   DBG(DEBUG_INFO,"CancelScan() called\n");
 | |
| 
 | |
|   this->state.cyTotalPath-=RegRead(this,R_POS,2);
 | |
|   DBG(DEBUG_JUNK,"stepping back %d steps\n",this->state.cyTotalPath);
 | |
|   /* this->state.cyTotalPath=0; */
 | |
| 
 | |
|   usleep(200);
 | |
|   DoReset(this);
 | |
|   EndScan(this); /* and step back! */
 | |
|   
 | |
|   DBG(DEBUG_JUNK,"cs4: %d\n",(int)this->nErrorState);
 | |
|   bCanceled=this->state.bCanceled;
 | |
|   this->state.bCanceled=false; /* re-enable Origination! */
 | |
|   if (!this->bOptSkipOriginate)
 | |
|     DoOriginate(this,false); /* have an error here... */
 | |
|   this->state.bCanceled=bCanceled;
 | |
|   DBG(DEBUG_JUNK,"cs5: %d\n",(int)this->nErrorState);
 | |
|   INST_ASSERT();
 | |
|   DBG(DEBUG_INFO,"cs6: ok.\n");
 | |
|   return SANE_STATUS_CANCELLED; /* or shall be say GOOD? */
 | |
| }
 | |
| 
 | |
| 
 | |
| /* ======================================================================
 | |
| 
 | |
| ReadChunk()
 | |
| 
 | |
| ====================================================================== */
 | |
| 
 | |
| __SM3600EXPORT__
 | |
| TState ReadChunk(TInstance *this, unsigned char *achOut,
 | |
| 		 int cchMax, int *pcchRead)
 | |
| {
 | |
|   /* have we to copy more than we have? */
 | |
|   /* can the current line fill the buffer ? */
 | |
|   int rc;
 | |
|   *pcchRead=0;
 | |
|   INST_ASSERT();
 | |
|   if (!this->state.bScanning)
 | |
|     return SANE_STATUS_CANCELLED; /* deferred cancel? */
 | |
|   if (this->state.bCanceled) /* deferred cancellation? */
 | |
|     return CancelScan(this);
 | |
|   INST_ASSERT();
 | |
|   /* 22.4.2001: This took me hard, harder, hardest:*/
 | |
| 
 | |
|   /*   We need to fill the line buffer with at least a *rest* of a
 | |
|        line. A single line will do. */
 | |
|   /*     Thus, "iLine>0" is a suitable condition. */
 | |
|   /*   Without the preread, there will a dummy line be read, if the
 | |
|        target buffer is large enough.*/
 | |
|   if (this->state.iLine)
 | |
|     rc=SANE_STATUS_GOOD;
 | |
|   else
 | |
|     rc=(*(this->state.ReadProc))(this); /* preread one line */
 | |
|   if (rc!=SANE_STATUS_GOOD) return rc;
 | |
|   dprintf(DEBUG_BUFFER,"Chunk-Init: cchMax = %d\n",cchMax);
 | |
|   while (this->state.iReadPos + cchMax > this->state.cchLineOut)
 | |
|     {
 | |
|       int cch;
 | |
|       /* copy rest of the line into target */
 | |
|       cch = this->state.cchLineOut - this->state.iReadPos;
 | |
|       memcpy(achOut,
 | |
| 	     this->state.pchLineOut+this->state.iReadPos,
 | |
| 	     cch);
 | |
|       cchMax-=cch; /* advance parameters */
 | |
|       achOut+=cch;
 | |
|       (*pcchRead)+=cch;
 | |
|       this->state.iReadPos=0;
 | |
|       rc=(*(this->state.ReadProc))(this);
 | |
|       dprintf(DEBUG_BUFFER,"Chunk-Read: cchMax = %d\n",cchMax);
 | |
|       if (rc!=SANE_STATUS_GOOD)
 | |
| 	return rc; /* should be NOT(!) EOF, but then: good and away! */
 | |
|     }
 | |
|   dprintf(DEBUG_BUFFER,"Chunk-Exit: cchMax = %d\n",cchMax);
 | |
|   if (!cchMax) return SANE_STATUS_GOOD; /* now everything fits! */
 | |
|   (*pcchRead) += cchMax;
 | |
|   memcpy(achOut,
 | |
| 	 this->state.pchLineOut+this->state.iReadPos,
 | |
| 	 cchMax);
 | |
|   this->state.iReadPos += cchMax;
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| /* ======================================================================
 | |
| 
 | |
| GetAreaSize()
 | |
| 
 | |
| ====================================================================== */
 | |
| 
 | |
| __SM3600EXPORT__
 | |
| void GetAreaSize(TInstance *this)
 | |
| {
 | |
|   /* this->state.cxPixel : pixels, we *want* (after interpolation)
 | |
|      this->state.cxMax   : pixels, we *need* (before interpolation) */
 | |
|   int nRefResX,nRefResY;
 | |
|   nRefResX=nRefResY=this->param.res;
 | |
|   switch (this->param.res)
 | |
|     {
 | |
|     case 75:  nRefResX=100; this->state.nFixAspect=75; break;
 | |
|     default: this->state.nFixAspect=100; break;
 | |
|     }
 | |
|   this->state.cxPixel   =this->param.cx*this->param.res/1200;
 | |
|   this->state.cyPixel   =this->param.cy*this->param.res/1200;
 | |
|   this->state.cxMax     =this->state.cxPixel*100/this->state.nFixAspect;
 | |
|   this->state.cxWindow  =this->state.cxMax*600/nRefResX;
 | |
|   this->state.cyWindow  =this->state.cyPixel*600/nRefResY;
 | |
|   dprintf(DEBUG_SCAN,"requesting %d[600] %d[real] %d[raw]\n",
 | |
| 	  this->state.cxWindow,this->state.cxPixel,this->state.cxMax);
 | |
| }
 | |
| 
 | |
| /* ======================================================================
 | |
| 
 | |
| ResetCalibration()
 | |
| 
 | |
| Free calibration data. The Instance can be safely released afterwards.
 | |
| 
 | |
| ====================================================================== */
 | |
| 
 | |
| __SM3600EXPORT__
 | |
| void ResetCalibration(TInstance *this)
 | |
| {
 | |
|   if (this->calibration.achStripeY)
 | |
|     free(this->calibration.achStripeY);
 | |
|   if (this->calibration.achStripeR)
 | |
|     free(this->calibration.achStripeR);
 | |
|   if (this->calibration.achStripeG)
 | |
|     free(this->calibration.achStripeG);
 | |
|   if (this->calibration.achStripeB)
 | |
|     free(this->calibration.achStripeB);
 | |
|   /* reset all handles, pointers, flags */
 | |
|   memset(&(this->calibration),0,sizeof(this->calibration));
 | |
|   /* TODO: type specific margins */
 | |
|   this->calibration.xMargin=200;
 | |
|   this->calibration.yMargin=0x019D;
 | |
|   this->calibration.nHoleGray=10;
 | |
|   this->calibration.rgbBias=0x888884;
 | |
|   this->calibration.nBarGray=0xC0;
 | |
| }
 | |
| 
 | |
| /* ======================================================================
 | |
| 
 | |
| InitGammaTables()
 | |
| 
 | |
| Init gammy tables and gain tables within controller memory.
 | |
| 
 | |
| ====================================================================== */
 | |
| 
 | |
| __SM3600EXPORT__
 | |
| TState InitGammaTables(TInstance *this, int nBrightness, int nContrast)
 | |
| {
 | |
|   long          i;
 | |
|   long          lOffset;
 | |
|   long          lScale;
 | |
|   /* the rescaling is done with temporary zero translation to 2048 */
 | |
|   lOffset=(nBrightness-128)*16; /* signed! */
 | |
|   lScale=(nContrast+128)*100;  /* in percent */
 | |
|   for (i=0; i<4096; i++)
 | |
|     {
 | |
|       int n=(int)((i+lOffset)*lScale/12800L+2048L);
 | |
|       if (n<0) n=0;
 | |
|       else if (n>4095) n=4095;
 | |
|       this->agammaY[i]=n;
 | |
|       this->agammaR[i]=n;
 | |
|       this->agammaG[i]=n;
 | |
|       this->agammaB[i]=n;
 | |
|     }
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| #ifdef INSANE_VERSION
 | |
| 
 | |
| /* ======================================================================
 | |
| 
 | |
| DoScanFile()
 | |
| 
 | |
| Top level caller for scantool.
 | |
| 
 | |
| ====================================================================== */
 | |
| 
 | |
| #define APP_CHUNK_SIZE   0x8000
 | |
| 
 | |
| __SM3600EXPORT__
 | |
| TState DoScanFile(TInstance *this)
 | |
| {
 | |
|   int    cx,cy;
 | |
|   long   lcchRead;
 | |
|   TState rc;
 | |
|   char   *achBuf;
 | |
| 
 | |
|   achBuf=malloc(APP_CHUNK_SIZE);
 | |
|   rc=SANE_STATUS_GOOD; /* make compiler happy */
 | |
|   rc=InitGammaTables(this, this->param.nBrightness, this->param.nContrast);
 | |
|   if (rc!=SANE_STATUS_GOOD) return rc;
 | |
|   if (this->mode==color)
 | |
|     rc=StartScanColor(this);
 | |
|   else
 | |
|     rc=StartScanGray(this);
 | |
|   cx=this->state.cxPixel;
 | |
|   cy=this->state.cyPixel;
 | |
|   if (this->bVerbose)
 | |
|     fprintf(stderr,"scanning %d by %d\n",cx,cy);
 | |
|   if (this->fhScan && !this->bWriteRaw && !this->pchPageBuffer)
 | |
|    {
 | |
|       switch (this->mode)
 | |
| 	{
 | |
| 	case color: fprintf(this->fhScan,"P6\n%d %d\n255\n",cx,cy);
 | |
| 	            break;
 | |
| 	case gray:  fprintf(this->fhScan,"P5\n%d %d\n255\n",cx,cy);
 | |
|                     break;
 | |
| 	default:    fprintf(this->fhScan,"P4\n%d %d\n",cx,cy);
 | |
|                     break;
 | |
| 	}
 | |
|     }
 | |
|   lcchRead=0L;
 | |
|   while (!rc)
 | |
|     {
 | |
|       int cch;
 | |
|       cch=0;
 | |
|       rc=ReadChunk(this,achBuf,APP_CHUNK_SIZE,&cch);
 | |
|       if (cch>0 && this->fhScan && cch<=APP_CHUNK_SIZE)
 | |
| 	{
 | |
| 	  if (this->pchPageBuffer)
 | |
| 	    {
 | |
| #ifdef SM3600_DEBUGPAGEBUFFER
 | |
| 	      if (this->bVerbose)
 | |
| 		fprintf(stderr,"ichPageBuffer:%d, cch:%d, cchPageBuffer:%d\n",
 | |
| 			this->ichPageBuffer,cch,this->cchPageBuffer);
 | |
| #endif
 | |
| 	      CHECK_ASSERTION(this->ichPageBuffer+cch<=this->cchPageBuffer);
 | |
| 	      memcpy(this->pchPageBuffer+this->ichPageBuffer,
 | |
| 		     achBuf,cch);
 | |
| 	      this->ichPageBuffer+=cch;
 | |
| 	    }
 | |
| 	  else if (!this->bWriteRaw)
 | |
| 	    fwrite(achBuf,1,cch,this->fhScan);
 | |
| 	  lcchRead+=cch;
 | |
| 	}
 | |
|      }
 | |
|   free(achBuf);
 | |
|   if (this->bVerbose)
 | |
|     fprintf(stderr,"read %ld image byte(s)\n",lcchRead);
 | |
|   EndScan(this);
 | |
|   INST_ASSERT();
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| #endif
 |