[sane-devel] New bug introduced for Canon LiDE 30
Monty
xiphmont at xiph.org
Thu Sep 11 17:39:42 BST 2003
On Thu, Sep 11, 2003 at 01:08:37PM +0200, Till Kamppeter wrote:
> Monty wrote:
> >LiDE20 and liDE30 never worked well; the autocal code was broken. I
> >know, I have them. I spent several days debugging the plustek backend
> >for exactly these scanners last month; see CVS (gerhard integrated my
> >patches I believe), or contact me for a Canoscan LiDE patch against
> >vanilla 1.0.12.
>
> Can you send me the patch?
Gerhard has, I believe, integrated it into his latest Plustek backend
download. He may have added additional improvements since I made the
patch; you should probably ask him... specifically, I only enabled the
patch for CanoScan LiDE (as that's all I had to test) but it likely
fixes autocal for a wider array of scanners if you turn it on in the
conf file. Gerhard may have correctly identified a larger default
set.
Regardless, the patch as I originally constructed it is attached. It
applies to vanilla 1.0.12.
The patch has nothing whatsoever to do with USB communication issues;
like I said, I use 1.0.12 happily with USB, but I need a hub to make
most USB devices work properly with my box and Linux. It's not
specific to SANE; most of my USB devices when not on the hub will work
perfectly one week, not at all the next...
Monty
-------------- next part --------------
diff -ruNw sane-backends-1.0.12/backend/canoscan-calibrate.c sane-backends-1.0.12-canoscan/backend/canoscan-calibrate.c
--- sane-backends-1.0.12/backend/canoscan-calibrate.c 1969-12-31 19:00:00.000000000 -0500
+++ sane-backends-1.0.12-canoscan/backend/canoscan-calibrate.c 2003-08-15 01:54:37.000000000 -0400
@@ -0,0 +1,982 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners; canoscan calibration
+ *.............................................................................
+ */
+
+/** @file canoscan-calibrate.c
+ * @brief Calibration routines.
+ *
+ * Based on sources acquired from Plustek Inc. and Gerhard Jaeger
+ * <gerhard at gjaeger.de>
+ *
+ * Current rehash by Monty <monty at xiph.org>
+ *
+ * The basic premise: The stock Plustek-usbshading.c in the plustek
+ * driver is effectively nonfunctional for Canon CanoScan scanners.
+ * These scanners rely heavily on all calibration steps, especially
+ * fine white, to produce acceptible scan results. However, to make
+ * autocalibration work and make it work well involves some
+ * substantial mucking aobut in code that supports thirty other
+ * scanners with widely varying characteristics... none of which I own
+ * or can test.
+ *
+ * Therefore, I'm splitting out a few calibration functions I need
+ * to modify for the CanoScan which allows me to simplify things
+ * greatly for the CanoScan without worrying about breaking other
+ * scanners, as well as reuse the vast majority of the Plustek
+ * driver infrastructure without forking.
+ *
+ * History:
+ * - 0.45m - birth of the file; tested extensively with the LiDE 20
+ *
+ * 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.
+ * <hr> */
+
+static SANE_Bool cano_HostSwap_p(void){
+ /* the NatSemi 983x is a big endian chip, and the line protocol data
+ all arrives big-endian. This determines if we need to swap to
+ host-order */
+ u_short pattern = 0xfeed; /* deadbeef */
+ unsigned char *bytewise = (unsigned char *)&pattern;
+ DBG( _DBG_INFO2, "cano_HostSwap_p()\n" );
+ if (bytewise[0] == 0xfe){
+ DBG( _DBG_INFO2, "We're big-endian! No need to swap!\n" );
+ DBG( _DBG_INFO2, "cano_HostSwap_p() done\n" );
+ return 0;
+ }
+ DBG( _DBG_INFO2, "We're little-endian! NatSemi LM9833 is big! Must swap calibration data!\n" );
+ DBG( _DBG_INFO2, "cano_HostSwap_p() done\n" );
+ return 1;
+}
+
+static int strip_state=0; /* 0 for not ready, 1 pos white lamp on, 2 lamp off */
+
+static int cano_PrepareToReadWhiteCal(pPlustek_Device dev){
+ pHWDef hw = &dev->usbDev.HwSetting;
+ switch (strip_state){
+ case 0: {
+ if(!usb_ModuleToHome( dev, SANE_TRUE )){
+ DBG( _DBG_ERROR, "cano_PrepareToReadWhiteCal() failed\n" );
+ return _E_LAMP_NOT_IN_POS;
+ }
+ if( !usb_ModuleMove(dev, MOVE_Forward,
+ (u_long)dev->usbDev.pSource->ShadingOriginY)) {
+ DBG( _DBG_ERROR, "cano_PrepareToReadWhiteCal() failed\n" );
+ return _E_LAMP_NOT_IN_POS;
+ }
+ break;
+ }
+ case 2:
+
+ a_bRegs[0x29] = hw->bReg_0x29;
+ usb_switchLamp( dev, SANE_TRUE );
+ if( !usbio_WriteReg( dev->fd, 0x29, a_bRegs[0x29])) {
+ DBG( _DBG_ERROR, "cano_PrepareToReadWhiteCal() failed\n" );
+ return _E_LAMP_NOT_IN_POS;
+ }
+ break;
+ }
+
+ strip_state=1;
+ return 0;
+}
+
+static int cano_PrepareToReadBlackCal(pPlustek_Device dev){
+ if(strip_state==0)
+ if(cano_PrepareToReadWhiteCal(dev))return SANE_FALSE;
+ if(strip_state!=2){
+ /* switch lamp off to read dark data... */
+ a_bRegs[0x29] = 0;
+ usb_switchLamp( dev, SANE_FALSE );
+ strip_state=2;
+ }
+ return 0;
+}
+
+static int cano_LampOnAfterCalibration(pPlustek_Device dev){
+ pHWDef hw = &dev->usbDev.HwSetting;
+ switch (strip_state){
+ case 2:
+
+ a_bRegs[0x29] = hw->bReg_0x29;
+ usb_switchLamp( dev, SANE_TRUE );
+ if( !usbio_WriteReg( dev->fd, 0x29, a_bRegs[0x29])) {
+ DBG( _DBG_ERROR, "cano_LampOnAfterCalibration() failed\n" );
+ return _E_LAMP_NOT_IN_POS;
+ }
+ strip_state=1;
+ break;
+ }
+ return 0;
+}
+
+static SANE_Bool cano_adjLampSetting( u_short *min, u_short *max,
+ u_short *off,u_short val){
+
+ u_long newoff = *off;
+ if(val<IDEAL_GainNormal && val>IDEAL_GainNormal-8000) return SANE_FALSE;
+
+ if(val > IDEAL_GainNormal-4000){
+ *max = newoff;
+ *off = ((newoff + *min)>>1);
+ }else{
+ u_short bisect = (newoff + *max)>>1;
+ u_short twice = newoff*2;
+ *min = newoff;
+ *off= twice<bisect?twice:bisect;
+ }
+
+ if(*min+1 >= *max) return SANE_FALSE;
+ return SANE_TRUE;
+}
+
+/** cano_AdjustLightsource
+ * coarse calibration step 0
+ * [Monty changes]: On the CanoScan at least, the default lamp
+ * settings are several *hundred* percent too high and vary from
+ * scanner-to-scanner by 20-50%. This is only for CIS devices
+ * where the lamp_off parameter is adjustable; I'd make it more general,
+ * but I only have the CIS hardware to test.
+ */
+
+static int cano_AdjustLightsource( pPlustek_Device dev)
+{
+ char tmp[40];
+ int i=0;
+ pDCapsDef scaps = &dev->usbDev.Caps;
+ pHWDef hw = &dev->usbDev.HwSetting;
+ u_long dw, bytes2get;
+
+ RGBUShortDef max_rgb, min_rgb;
+
+ if( ! (hw->bReg_0x26 & _ONE_CH_COLOR)) return SANE_FALSE;
+ if( _IS_PLUSTEKMOTOR(hw->motorModel)) return SANE_FALSE;
+ if( usb_IsEscPressed()) return SANE_FALSE;
+
+
+ DBG( _DBG_INFO2, "cano_AdjustLightsource()\n" );
+
+ /* define the strip to scan for coarse calibration; done at 300dpi */
+ m_ScanParam.Size.dwLines = 1;
+ m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
+ scaps->OpticDpi.x / 300UL;
+
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2;
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color )
+ m_ScanParam.Size.dwBytes *=3;
+
+ m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart *
+ 300UL / scaps->OpticDpi.x);
+ m_ScanParam.bCalibration = PARAM_Gain;
+
+ DBG( _DBG_INFO2, "Coarse Calibration Strip:\n" );
+ DBG( _DBG_INFO2, "Lines = %lu\n", m_ScanParam.Size.dwLines );
+ DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels );
+ DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes );
+ DBG( _DBG_INFO2, "Origin.X = %u\n", m_ScanParam.Origin.x );
+
+ max_rgb.Red = max_rgb.Green = max_rgb.Blue = 0xffff;
+ min_rgb.Red = hw->red_lamp_on;
+ min_rgb.Green = hw->green_lamp_on;
+ min_rgb.Blue = hw->blue_lamp_on;
+
+ while(1){
+
+ m_ScanParam.dMCLK = dMCLK;
+ if( !usb_SetScanParameters( dev, &m_ScanParam )){
+ return SANE_FALSE;
+ }
+
+ bytes2get = m_ScanParam.Size.dwPhyBytes;
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color )
+ bytes2get *=3;
+
+ if( !usb_ScanBegin( dev, SANE_FALSE) ||
+ !usb_ScanReadImage( dev, pScanBuffer, bytes2get ) ||
+ !usb_ScanEnd( dev )) {
+ DBG( _DBG_ERROR, "cano_AdjustLightsource() failed\n" );
+ return SANE_FALSE;
+ }
+
+ DBG( _DBG_INFO2, "PhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes );
+ DBG( _DBG_INFO2, "PhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels );
+
+ sprintf( tmp, "/tmp/coarse-lamp-%u.raw", i );
+
+ dumpPic( tmp, NULL, 0 );
+ dumpPic( tmp, pScanBuffer, bytes2get );
+
+ if(cano_HostSwap_p())
+ usb_Swap((u_short *)pScanBuffer, bytes2get );
+
+ sprintf( tmp, "/tmp/coarse-lamp-swap%u.raw", i++ );
+
+ dumpPic( tmp, NULL, 0 );
+ dumpPic( tmp, pScanBuffer, bytes2get );
+
+
+ {
+ RGBUShortDef tmp;
+ u_long dwR, dwG, dwB;
+ u_long dwDiv = 10;
+ u_long dwLoop1 = m_ScanParam.Size.dwPhyPixels / dwDiv, dwLoop2;
+ SANE_Bool adj = SANE_FALSE;
+
+ tmp.Red = tmp.Green = tmp.Blue = 0;
+
+ /* find out the max pixel value for R, G, B */
+ for( dw = 0; dwLoop1; dwLoop1-- ) {
+
+ /* do some averaging... */
+ for (dwLoop2 = dwDiv, dwR = dwG = dwB = 0; dwLoop2; dwLoop2--, dw++)
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color )
+ {
+ dwR += ((u_short*)pScanBuffer)[dw];
+ dwG += ((u_short*)pScanBuffer)[dw+m_ScanParam.Size.dwPhyPixels+1];
+ dwB += ((u_short*)pScanBuffer)[dw+(m_ScanParam.Size.dwPhyPixels+1)*2];
+ }else{
+ dwG += ((u_short*)pScanBuffer)[dw];
+ }
+
+ dwR = dwR / dwDiv;
+ dwG = dwG / dwDiv;
+ dwB = dwB / dwDiv;
+
+ if(tmp.Red < dwR)
+ tmp.Red = dwR;
+ if(tmp.Green < dwG)
+ tmp.Green = dwG;
+ if(tmp.Blue < dwB)
+ tmp.Blue = dwB;
+
+ }
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color )
+ DBG( _DBG_INFO2, "red_lamp_off = %u/%u/%u\n", min_rgb.Red ,hw->red_lamp_off ,max_rgb.Red );
+ DBG( _DBG_INFO2, "green_lamp_on = %u/%u/%u\n", min_rgb.Green,hw->green_lamp_off,max_rgb.Green);
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color )
+ DBG( _DBG_INFO2, "blue_lamp_off = %u/%u/%u\n", min_rgb.Blue ,hw->blue_lamp_off ,max_rgb.Blue );
+
+ DBG(_DBG_INFO2, "CUR(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
+ tmp.Red, tmp.Red, tmp.Green, tmp.Green, tmp.Blue, tmp.Blue);
+
+ /* bisect */
+ adj=0;
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color )
+ adj += cano_adjLampSetting(&min_rgb.Red,&max_rgb.Red,&hw->red_lamp_off,tmp.Red);
+ adj += cano_adjLampSetting(&min_rgb.Green,&max_rgb.Green,&hw->green_lamp_off,tmp.Green);
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color )
+ adj += cano_adjLampSetting(&min_rgb.Blue,&max_rgb.Blue,&hw->blue_lamp_off,tmp.Blue);
+
+ if( !adj ) break;
+ usb_AdjustLamps(dev);
+ }
+ }
+
+ DBG( _DBG_INFO2, "red_lamp_on = %u\n", hw->red_lamp_on );
+ DBG( _DBG_INFO2, "red_lamp_off = %u\n", hw->red_lamp_off );
+ DBG( _DBG_INFO2, "green_lamp_on = %u\n", hw->green_lamp_on );
+ DBG( _DBG_INFO2, "green_lamp_off = %u\n", hw->green_lamp_off );
+ DBG( _DBG_INFO2, "blue_lamp_on = %u\n", hw->blue_lamp_on );
+ DBG( _DBG_INFO2, "blue_lamp_off = %u\n", hw->blue_lamp_off );
+
+ DBG( _DBG_INFO2, "cano_AdjustLightsource() done.\n" );
+
+ return SANE_TRUE;
+}
+
+
+static SANE_Bool cano_adjGainSetting( u_char *min, u_char *max,
+ u_char *gain,u_long val){
+
+ u_long newgain = *gain;
+
+ if(val<IDEAL_GainNormal && val>IDEAL_GainNormal-8000) return SANE_FALSE;
+
+ if(val > IDEAL_GainNormal-4000){
+ *max = newgain;
+ *gain = (newgain + *min)>>1;
+ }else{
+ *min = newgain;
+ *gain = (newgain + *max)>>1;
+ }
+
+ if(*min+1 >= *max) return SANE_FALSE;
+ return SANE_TRUE;
+}
+
+/** cano_AdjustGain
+ * function to perform the "coarse calibration step" part 1.
+ * We scan reference image pixels to determine the optimum coarse gain settings
+ * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are
+ * applied at the line rate during normal scanning.
+ * The scanned line should contain a white strip with some black at the
+ * beginning. The function searches for the maximum value which corresponds to
+ * the maximum white value.
+ * Affects register 0x3b, 0x3c and 0x3d
+ *
+ * adjLightsource, above, steals most of this function's thunder.
+ */
+
+static SANE_Bool cano_AdjustGain( pPlustek_Device dev ) {
+ char tmp[40];
+ int i=0,adj=1;
+ pDCapsDef scaps = &dev->usbDev.Caps;
+ pHWDef hw = &dev->usbDev.HwSetting;
+ u_long dw, bytes2get;
+
+ unsigned char max[3], min[3];
+
+ if( usb_IsEscPressed()) return SANE_FALSE;
+
+ bMaxITA = 0xff;
+
+ max[0] = max[1] = max[2] = 0x3f;
+ min[0] = min[1] = min[2] = 1;
+
+ DBG( _DBG_INFO2, "cano_AdjustGain()\n" );
+
+ /*
+ * define the strip to scan for coarse calibration
+ * done at 300dpi
+ */
+
+ m_ScanParam.Size.dwLines = 1; /* for gain */
+ m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
+ scaps->OpticDpi.x / 300UL;
+
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2;
+ if(m_ScanParam.bDataType == SCANDATATYPE_Color )
+ m_ScanParam.Size.dwBytes *=3;
+
+ m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart *
+ 300UL / scaps->OpticDpi.x);
+ m_ScanParam.bCalibration = PARAM_Gain;
+
+ DBG( _DBG_INFO2, "Coarse Calibration Strip:\n" );
+ DBG( _DBG_INFO2, "Lines = %lu\n", m_ScanParam.Size.dwLines );
+ DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels );
+ DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes );
+ DBG( _DBG_INFO2, "Origin.X = %u\n", m_ScanParam.Origin.x );
+
+ while(adj){
+ m_ScanParam.dMCLK = dMCLK;
+
+ if( !usb_SetScanParameters( dev, &m_ScanParam ))
+ return SANE_FALSE;
+
+ bytes2get = m_ScanParam.Size.dwPhyBytes;
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color )
+ bytes2get *=3;
+
+ if( !usb_ScanBegin( dev, SANE_FALSE) ||
+ !usb_ScanReadImage( dev, pScanBuffer, bytes2get ) ||
+ !usb_ScanEnd( dev )) {
+ DBG( _DBG_ERROR, "cano_AdjustGain() failed\n" );
+ return SANE_FALSE;
+ }
+
+ DBG( _DBG_INFO2, "PhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes );
+ DBG( _DBG_INFO2, "PhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels );
+
+ sprintf( tmp, "/tmp/coarse-gain-%u.raw", i++ );
+
+ dumpPic( tmp, NULL, 0 );
+ dumpPic( tmp, pScanBuffer, bytes2get );
+
+ if(cano_HostSwap_p())
+ usb_Swap((u_short *)pScanBuffer, bytes2get );
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+
+ RGBUShortDef max_rgb;
+ u_long dwR, dwG, dwB;
+ u_long dwDiv = 10;
+ u_long dwLoop1 = m_ScanParam.Size.dwPhyPixels / dwDiv, dwLoop2;
+
+ max_rgb.Red = max_rgb.Green = max_rgb.Blue = 0;
+
+ /* find out the max pixel value for R, G, B */
+ for( dw = 0; dwLoop1; dwLoop1-- ) {
+
+ /* do some averaging... */
+ for (dwLoop2 = dwDiv, dwR = dwG = dwB = 0; dwLoop2; dwLoop2--, dw++) {
+ dwR += ((u_short*)pScanBuffer)[dw];
+ dwG += ((u_short*)pScanBuffer)[dw+m_ScanParam.Size.dwPhyPixels+1];
+ dwB += ((u_short*)pScanBuffer)[dw+(m_ScanParam.Size.dwPhyPixels+1)*2];
+ }
+ dwR = dwR / dwDiv;
+ dwG = dwG / dwDiv;
+ dwB = dwB / dwDiv;
+
+ if(max_rgb.Red < dwR)
+ max_rgb.Red = dwR;
+ if(max_rgb.Green < dwG)
+ max_rgb.Green = dwG;
+ if(max_rgb.Blue < dwB)
+ max_rgb.Blue = dwB;
+ }
+
+ DBG(_DBG_INFO2, "MAX(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
+ max_rgb.Red, max_rgb.Red, max_rgb.Green,
+ max_rgb.Green, max_rgb.Blue, max_rgb.Blue );
+
+ adj = cano_adjGainSetting(min , max , a_bRegs+0x3b,max_rgb.Red );
+ adj |= cano_adjGainSetting(min+1, max+1, a_bRegs+0x3c,max_rgb.Green);
+ adj |= cano_adjGainSetting(min+2, max+2, a_bRegs+0x3d,max_rgb.Blue );
+
+ } else {
+
+ u_short w_max = 0;
+
+ for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++ ) {
+ if( w_max < ((u_short*)pScanBuffer)[dw])
+ w_max = ((u_short*)pScanBuffer)[dw];
+ }
+
+ adj = cano_adjGainSetting(min,max,a_bRegs+0x3c,w_max);
+ a_bRegs[0x3a] = (a_bRegs[0x3d] = a_bRegs[0x3c]);
+
+ DBG(_DBG_INFO2, "MAX(G)= 0x%04x(%u)\n", w_max, w_max );
+
+ }
+ DBG( _DBG_INFO2, "REG[0x3b] = %u\n", a_bRegs[0x3b] );
+ DBG( _DBG_INFO2, "REG[0x3c] = %u\n", a_bRegs[0x3c] );
+ DBG( _DBG_INFO2, "REG[0x3d] = %u\n", a_bRegs[0x3d] );
+
+ }
+
+ DBG( _DBG_INFO2, "cano_AdjustGain() done.\n" );
+
+ return SANE_TRUE;
+}
+
+
+static int cano_GetNewOffset(u_long *val, int channel, signed char *low,
+ signed char *now, signed char *high ){
+
+ /* if we're too black, we're likely off the low end */
+ if(val[channel]<=16){
+ low[channel]=now[channel];
+ now[channel]=(now[channel]+high[channel])/2;
+
+ a_bRegs[0x38+channel]= (now[channel]&0x3f);
+
+ if(low[channel]+1>=high[channel])return 0;
+ return 1;
+ }else if (val[channel]>=2048){
+ high[channel]=now[channel];
+ now[channel]=(now[channel]+low[channel])/2;
+
+ a_bRegs[0x38+channel]= (now[channel]&0x3f);
+
+ if(low[channel]+1>=high[channel])return 0;
+ return 1;
+ }
+ return 0;
+}
+
+/** cano_AdjustOffset
+ * function to perform the "coarse calibration step" part 2.
+ * We scan reference image pixels to determine the optimum coarse offset settings
+ * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are
+ * applied at the line rate during normal scanning.
+ * On CIS based devices, we switch the light off, on CCD devices, we use the optical
+ * black pixels.
+ * Affects register 0x38, 0x39 and 0x3a
+ */
+
+/* Move this to a bisection-based algo and correct some fenceposts;
+ Plustek's example code disagrees with NatSemi's docs; going by the
+ docs works better, I will assume the docs are correct. --Monty */
+
+static int cano_AdjustOffset( pPlustek_Device dev )
+{
+ pDCapsDef scaps = &dev->usbDev.Caps;
+ char tmp[40];
+ int adj=1;
+ int i;
+ u_long dw, dwPixels, bytes2get;
+ u_long dwSum[3];
+
+ signed char low[3]={-32,-32,-32};
+ signed char now[3]={0,0,0};
+ signed char high[3]={31,31,31};
+
+ pHWDef hw = &dev->usbDev.HwSetting;
+ if( usb_IsEscPressed()) return SANE_FALSE;
+
+ DBG( _DBG_INFO2, "cano_AdjustOffset()\n" );
+
+ m_ScanParam.Size.dwLines = 1;
+ m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
+ scaps->OpticDpi.x / 300UL;
+
+ dwPixels = m_ScanParam.Size.dwPixels;
+
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2;
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color )
+ m_ScanParam.Size.dwBytes *= 3;
+
+ m_ScanParam.Origin.x = (u_short)((u_long)hw->bOpticBlackStart * 300UL /
+ dev->usbDev.Caps.OpticDpi.x);
+ m_ScanParam.bCalibration = PARAM_Offset;
+ m_ScanParam.dMCLK = dMCLK;
+
+
+ if( !usb_SetScanParameters( dev, &m_ScanParam )) {
+ DBG( _DBG_ERROR, "cano_AdjustOffset() failed\n" );
+ return SANE_FALSE;
+ }
+
+ i = 0;
+
+ bytes2get = m_ScanParam.Size.dwPhyBytes;
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color )
+ bytes2get *= 3;
+
+ DBG( _DBG_INFO2, "S.dwPixels = %lu\n", m_ScanParam.Size.dwPixels );
+ DBG( _DBG_INFO2, "dwPixels = %lu\n", dwPixels );
+ DBG( _DBG_INFO2, "dwPhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes );
+ DBG( _DBG_INFO2, "dwPhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels );
+ DBG( _DBG_INFO2, "bytes2get = %lu\n", bytes2get );
+
+ while( adj ) {
+
+ if((!usb_ScanBegin(dev, SANE_FALSE)) ||
+ (!usb_ScanReadImage(dev,pScanBuffer,bytes2get)) ||
+ !usb_ScanEnd( dev )) {
+ DBG( _DBG_ERROR, "cano_AdjustOffset() failed\n" );
+ return SANE_FALSE;
+ }
+
+ sprintf( tmp, "/tmp/coarse-off-%u.raw", i++ );
+
+ dumpPic( tmp, NULL, 0 );
+ dumpPic( tmp, pScanBuffer, bytes2get );
+
+ if(cano_HostSwap_p())
+ usb_Swap((u_short *)pScanBuffer, bytes2get );
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+
+ dwSum[0] = dwSum[1] = dwSum[2] = 0;
+
+ for (dw = 0; dw < dwPixels; dw++) {
+
+ dwSum[0] += ((u_short*)pScanBuffer)[dw];
+ dwSum[1] += ((u_short*)pScanBuffer)[dw+m_ScanParam.Size.dwPhyPixels+1];
+ dwSum[2] += ((u_short*)pScanBuffer)[dw+(m_ScanParam.Size.dwPhyPixels+1)*2];
+
+ }
+
+ DBG( _DBG_INFO2, "RedSum = %lu, ave = %lu\n",
+ dwSum[0], dwSum[0] /dwPixels );
+ DBG( _DBG_INFO2, "GreenSum = %lu, ave = %lu\n",
+ dwSum[1], dwSum[1] /dwPixels );
+ DBG( _DBG_INFO2, "BlueSum = %lu, ave = %lu\n",
+ dwSum[2], dwSum[2] /dwPixels );
+
+ /* do averaging for each channel */
+ dwSum[0] /= dwPixels;
+ dwSum[1] /= dwPixels;
+ dwSum[2] /= dwPixels;
+
+ adj = cano_GetNewOffset( dwSum, 0, low, now, high );
+ adj |= cano_GetNewOffset( dwSum, 1, low, now, high );
+ adj |= cano_GetNewOffset( dwSum, 2, low, now, high );
+
+ DBG( _DBG_INFO2, "RedOff = %d/%d/%d\n", (int)low[0],(int)now[0],(int)high[0]);
+ DBG( _DBG_INFO2, "GreenOff = %d/%d/%d\n", (int)low[1],(int)now[1],(int)high[1]);
+ DBG( _DBG_INFO2, "BlueOff = %d/%d/%d\n", (int)low[2],(int)now[2],(int)high[2]);
+
+ } else {
+ dwSum[0] = 0;
+
+ for( dw = 0; dw < dwPixels; dw++ )
+ dwSum[0] += ((u_short*)pScanBuffer)[dw];
+ dwSum [0] /= dwPixels;
+
+ DBG( _DBG_INFO2, "Sum = %lu, ave = %lu\n",
+ dwSum[0], dwSum[0] /dwPixels );
+
+ adj = cano_GetNewOffset( dwSum, 0, low, now, high );
+
+ a_bRegs[0x3a] = a_bRegs[0x39] = a_bRegs[0x38];
+
+ DBG( _DBG_INFO2, "GrayOff = %d/%d/%d\n", (int)low[0],(int)now[0],(int)high[0]);
+
+ }
+
+ DBG( _DBG_INFO2, "REG[0x38] = %u\n", a_bRegs[0x38] );
+ DBG( _DBG_INFO2, "REG[0x39] = %u\n", a_bRegs[0x39] );
+ DBG( _DBG_INFO2, "REG[0x3a] = %u\n", a_bRegs[0x3a] );
+
+ _UIO(sanei_lm983x_write(dev->fd, 0x38, &a_bRegs[0x38], 3, SANE_TRUE));
+
+ }
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+ a_bRegs[0x38] = now[0];
+ a_bRegs[0x39] = now[1];
+ a_bRegs[0x3a] = now[2];
+ } else {
+
+ a_bRegs[0x38] = a_bRegs[0x39] = a_bRegs[0x3a] = now[0];
+ }
+
+ DBG( _DBG_INFO2, "cano_AdjustOffset() done.\n" );
+
+ return SANE_TRUE;
+}
+
+/** usb_AdjustDarkShading
+ * fine calibration part 1
+ *
+ */
+static SANE_Bool cano_AdjustDarkShading( pPlustek_Device dev )
+{
+ char tmp[40];
+ pScanDef scanning = &dev->scanning;
+ pDCapsDef scaps = &dev->usbDev.Caps;
+ unsigned int i,j;
+
+ DBG( _DBG_INFO2, "cano_AdjustDarkShading()\n" );
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+
+ m_ScanParam = scanning->sParam;
+
+ if( m_ScanParam.PhyDpi.x > 75)
+ m_ScanParam.Size.dwLines = 64;
+ else
+ m_ScanParam.Size.dwLines = 32;
+
+ m_ScanParam.Origin.y = 0;
+ m_ScanParam.bBitDepth = 16;
+
+ m_ScanParam.UserDpi.y = scaps->OpticDpi.y;
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2;
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color )
+ m_ScanParam.Size.dwBytes *= 3;
+
+ m_ScanParam.bCalibration = PARAM_DarkShading;
+ m_ScanParam.dMCLK = dMCLK;
+
+ sprintf( tmp, "/tmp/fine-dark.raw" );
+ dumpPic( tmp, NULL, 0 );
+
+ usb_SetScanParameters( dev, &m_ScanParam );
+ if( usb_ScanBegin( dev, SANE_FALSE ) &&
+ usb_ScanReadImage( dev, pScanBuffer, m_ScanParam.Size.dwTotalBytes)) {
+
+ dumpPic( tmp, pScanBuffer, m_ScanParam.Size.dwTotalBytes );
+
+ if(cano_HostSwap_p())
+ usb_Swap((u_short *)pScanBuffer, m_ScanParam.Size.dwTotalBytes);
+ }
+ if (!usb_ScanEnd( dev )){
+ DBG( _DBG_ERROR, "cano_AdjustDarkShading() failed\n" );
+ return SANE_FALSE;
+ }
+
+ /* average the n lines, compute reg values */
+ if( scanning->sParam.bDataType == SCANDATATYPE_Color ) {
+ int step = m_ScanParam.Size.dwPhyPixels + 1;
+ int stepW = m_ScanParam.Size.dwPhyPixels;
+ for(i=0;i<m_ScanParam.Size.dwPhyPixels;i++){
+ u_long red=0,green=0,blue=0;
+ u_short *bufp = ((u_short *)pScanBuffer)+i;
+
+ for(j=0;j<m_ScanParam.Size.dwPhyLines;j++){
+ red += *bufp; bufp+=step;
+ green += *bufp; bufp+=step;
+ blue += *bufp; bufp+=step;
+ }
+
+ a_wDarkShading[i] = red/j +1000;
+ a_wDarkShading[i+stepW] = green/j +1000;
+ a_wDarkShading[i+stepW*2] = blue/j +1000;
+
+ }
+
+ if(cano_HostSwap_p())
+ usb_Swap(a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2 * 3 );
+
+ }else{
+ int step = m_ScanParam.Size.dwPhyPixels + 1;
+ for(i=0;i<m_ScanParam.Size.dwPhyPixels;i++){
+ u_long gray=0;
+ u_short *bufp = ((u_short *)pScanBuffer)+i;
+
+ for(j=0;j<m_ScanParam.Size.dwPhyLines;j++){
+ gray += *bufp; bufp+=step;
+ }
+
+ a_wDarkShading[i]= gray/j;
+
+ }
+ if(cano_HostSwap_p())
+ usb_Swap(a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2 );
+ memcpy(a_wDarkShading+ m_ScanParam.Size.dwPhyPixels * 2,
+ a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2);
+ memcpy(a_wDarkShading+ m_ScanParam.Size.dwPhyPixels * 4,
+ a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2);
+
+ }
+
+ DBG( _DBG_INFO2, "cano_AdjustDarkShading() done\n" );
+ return SANE_TRUE;
+}
+
+/** usb_AdjustWhiteShading
+ * fine calibration part 2 - read the white calibration area and calculate
+ * the gain coefficient for each pixel
+ *
+ */
+static SANE_Bool cano_AdjustWhiteShading( pPlustek_Device dev )
+{
+ char tmp[40];
+ pScanDef scanning = &dev->scanning;
+ pDCapsDef scaps = &dev->usbDev.Caps;
+ pHWDef hw = &dev->usbDev.HwSetting;
+ unsigned int i,j;
+
+ DBG( _DBG_INFO2, "cano_AdjustWhiteShading()\n" );
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+
+ m_ScanParam = scanning->sParam;
+ if( m_ScanParam.PhyDpi.x > 75)
+ m_ScanParam.Size.dwLines = 64;
+ else
+ m_ScanParam.Size.dwLines = 32;
+
+ m_ScanParam.Origin.y = 0;
+ m_ScanParam.bBitDepth = 16;
+
+ m_ScanParam.UserDpi.y = scaps->OpticDpi.y;
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2;
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color )
+ m_ScanParam.Size.dwBytes *= 3;
+
+ m_ScanParam.bCalibration = PARAM_WhiteShading;
+ m_ScanParam.dMCLK = dMCLK;
+
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2;
+ if( hw->bReg_0x26 & _ONE_CH_COLOR &&
+ m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+ m_ScanParam.Size.dwBytes *= 3;
+ }
+
+ sprintf( tmp, "/tmp/fine-white.raw" );
+ dumpPic( tmp, NULL, 0 );
+
+ if( usb_SetScanParameters( dev, &m_ScanParam ) &&
+ usb_ScanBegin( dev, SANE_FALSE ) &&
+ usb_ScanReadImage( dev, pScanBuffer, m_ScanParam.Size.dwTotalBytes)) {
+
+ dumpPic( tmp, pScanBuffer, m_ScanParam.Size.dwTotalBytes );
+
+ if(cano_HostSwap_p())
+ usb_Swap((u_short *)pScanBuffer, m_ScanParam.Size.dwTotalBytes);
+ if (!usb_ScanEnd( dev )){
+ DBG( _DBG_ERROR, "cano_AdjustWhiteShading() failed\n" );
+ return SANE_FALSE;
+ }
+ }else{
+ DBG( _DBG_ERROR, "cano_AdjustWhiteShading() failed\n" );
+ return SANE_FALSE;
+ }
+
+ /* average the n lines, compute reg values */
+ if( scanning->sParam.bDataType == SCANDATATYPE_Color ) {
+ int step = m_ScanParam.Size.dwPhyPixels + 1;
+ int stepW = m_ScanParam.Size.dwPhyPixels;
+ for(i=0;i<m_ScanParam.Size.dwPhyPixels;i++){
+ u_long red=0,green=0,blue=0;
+ u_short *bufp = ((u_short *)pScanBuffer)+i;
+
+ for(j=0;j<m_ScanParam.Size.dwPhyLines;j++){
+ red += *bufp; bufp+=step;
+ green += *bufp; bufp+=step;
+ blue += *bufp; bufp+=step;
+ }
+
+ /* an extra 5% on the top */
+ red = 68811.*16384.*j/red;
+ green = 68811.*16384.*j/green;
+ blue = 68811.*16384.*j/blue;
+
+ a_wWhiteShading[i] = (red>65535?65535:red);
+ a_wWhiteShading[i+stepW] = (green>65535?65535:green);
+ a_wWhiteShading[i+stepW*2] = (blue>65535?65535:blue);
+
+
+ }
+
+ if(cano_HostSwap_p())
+ usb_Swap(a_wWhiteShading, m_ScanParam.Size.dwPhyPixels * 2 * 3 );
+
+ }else{
+ int step = m_ScanParam.Size.dwPhyPixels + 1;
+ for(i=0;i<m_ScanParam.Size.dwPhyPixels;i++){
+ u_long gray=0;
+ u_short *bufp = ((u_short *)pScanBuffer)+i;
+
+ for(j=0;j<m_ScanParam.Size.dwPhyLines;j++){
+ gray += *bufp; bufp+=step;
+ }
+
+ gray = 65535.*16384.*j/gray;
+ a_wWhiteShading[i]= (gray>65535?65535:gray);
+
+ }
+ if(cano_HostSwap_p())
+ usb_Swap(a_wWhiteShading, m_ScanParam.Size.dwPhyPixels * 2 );
+ memcpy(a_wWhiteShading+ m_ScanParam.Size.dwPhyPixels * 2,
+ a_wWhiteShading, m_ScanParam.Size.dwPhyPixels * 2);
+ memcpy(a_wWhiteShading+ m_ScanParam.Size.dwPhyPixels * 4,
+ a_wWhiteShading, m_ScanParam.Size.dwPhyPixels * 2);
+
+ }
+
+ DBG( _DBG_INFO2, "cano_AdjustWhiteShading() done\n" );
+ return SANE_TRUE;
+}
+
+static int cano_DoCalibration( pPlustek_Device dev )
+{
+ pScanDef scanning = &dev->scanning;
+ pHWDef hw = &dev->usbDev.HwSetting;
+ pDCapsDef scaps = &dev->usbDev.Caps;
+
+
+ if( SANE_TRUE == scanning->fCalibrated )
+ return SANE_TRUE;
+
+ DBG( _DBG_INFO2, "cano_DoCalibration()\n" );
+
+ if( !( hw->bReg_0x26 & _ONE_CH_COLOR)){
+ DBG( _DBG_ERROR, "altCalibration can't work with non-line-rate CIS devices\n" );
+ return SANE_FALSE; // can't cal this
+ }
+ if( _IS_PLUSTEKMOTOR(hw->motorModel)){
+ DBG( _DBG_ERROR, "altCalibration can't work with this Plustek motor control setup\n" );
+ return SANE_FALSE; // can't cal this
+ }
+ if( scanning->sParam.bChannels != 1){
+ DBG( _DBG_ERROR, "altCalibration can't work with non-line-rate CIS devices\n" );
+ return SANE_FALSE; // can't cal this
+ }
+
+
+ /* Don't allow calibration settings from the other driver to confuse our use of
+ a few of its functions */
+ scaps->workaroundFlag &= ~_WAF_SKIP_WHITEFINE;
+ scaps->workaroundFlag &= ~_WAF_SKIP_FINE;
+ scaps->workaroundFlag &= ~_WAF_BYPASS_CALIBRATION;
+
+ /* Set the shading position to undefined */
+ strip_state=0;
+ usb_PrepareCalibration( dev );
+
+ /* You *really* don't want to skip calibration for CanoScan devices. I promise. */
+ usb_SetMCLK( dev, &scanning->sParam );
+
+ DBG( _DBG_INFO2, "###### ADJUST LAMP (COARSE)#######\n" );
+ if(cano_PrepareToReadWhiteCal(dev))return SANE_FALSE;
+ a_bRegs[0x45] &= ~0x10;
+ if( !cano_AdjustLightsource(dev)) {
+ DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" );
+ return _E_INTERNAL;
+ }
+
+ DBG( _DBG_INFO2, "###### ADJUST OFFSET (COARSE) ####\n" );
+ if(cano_PrepareToReadBlackCal(dev))return SANE_FALSE;
+ if( !cano_AdjustOffset(dev)) {
+ DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" );
+ return _E_INTERNAL;
+ }
+
+ DBG( _DBG_INFO2, "###### ADJUST GAIN (COARSE)#######\n" );
+ if(cano_PrepareToReadWhiteCal(dev))return SANE_FALSE;
+ if( !cano_AdjustGain(dev)) {
+ DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" );
+ return _E_INTERNAL;
+ }
+
+ DBG( _DBG_INFO2, "###### ADJUST DARK (FINE) ########\n" );
+ if(cano_PrepareToReadBlackCal(dev))return SANE_FALSE;
+ a_bRegs[0x45] |= 0x10;
+ if( !cano_AdjustDarkShading(dev)) {
+ DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" );
+ return _E_INTERNAL;
+ }
+
+ DBG( _DBG_INFO2, "###### ADJUST WHITE (FINE) #######\n" );
+ if(cano_PrepareToReadWhiteCal(dev))return SANE_FALSE;
+
+ if(!usb_ModuleToHome( dev, SANE_TRUE )) return SANE_FALSE;
+ if( !usb_ModuleMove(dev, MOVE_Forward,
+ (u_long)dev->usbDev.pSource->ShadingOriginY)) {
+ return _E_INTERNAL;
+ }
+ if( !cano_AdjustWhiteShading(dev)) {
+ DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" );
+ return _E_INTERNAL;
+ }
+
+ /* Lamp on if it's not */
+ cano_LampOnAfterCalibration(dev);
+ strip_state=0;
+
+ /*
+ * home the sensor after calibration
+ */
+ usb_ModuleToHome( dev, SANE_TRUE );
+ scanning->fCalibrated = SANE_TRUE;
+
+ DBG( _DBG_INFO2, "cano_DoCalibration() done\n" );
+ return SANE_TRUE;
+}
+
diff -ruNw sane-backends-1.0.12/backend/plustek-share.h sane-backends-1.0.12-canoscan/backend/plustek-share.h
--- sane-backends-1.0.12/backend/plustek-share.h 2003-05-16 16:59:43.000000000 -0400
+++ sane-backends-1.0.12-canoscan/backend/plustek-share.h 2003-08-15 00:40:34.000000000 -0400
@@ -258,6 +258,10 @@
* for adjusting the usb stuff
*/
typedef struct {
+ int altCalibrate; /* force use of the alternate canoscan
+ autocal; perhaps other Canon
+ scanners require the alternate
+ autocalibration as well */
int lampOff;
int lampOffOnEnd;
int warmup;
diff -ruNw sane-backends-1.0.12/backend/plustek-usb.c sane-backends-1.0.12-canoscan/backend/plustek-usb.c
--- sane-backends-1.0.12/backend/plustek-usb.c 2003-05-16 16:59:43.000000000 -0400
+++ sane-backends-1.0.12-canoscan/backend/plustek-usb.c 2003-08-15 00:40:34.000000000 -0400
@@ -963,7 +963,19 @@
*/
usb_ModuleStatus( dev );
+ if(dev->usbDev.vendor==0x04A9 && /* canoscan ----------*/
+ (dev->usbDev.product==0x220D || /* LiDE20 and related */
+ dev->usbDev.product==0x220E) /* LiDE30 and related */
+ ){
+ result = cano_DoCalibration( dev );
+ }else{
+ if(dev->adj.altCalibrate)
+ result = cano_DoCalibration( dev );
+ else
result = usb_DoCalibration( dev );
+ }
+
+
if( SANE_TRUE != result ) {
DBG( _DBG_INFO, "calibration failed!!!\n" );
return result;
diff -ruNw sane-backends-1.0.12/backend/plustek.c sane-backends-1.0.12-canoscan/backend/plustek.c
--- sane-backends-1.0.12/backend/plustek.c 2003-05-16 16:59:44.000000000 -0400
+++ sane-backends-1.0.12-canoscan/backend/plustek.c 2003-08-15 02:22:09.000000000 -0400
@@ -175,6 +175,7 @@
# include "plustek-usbscan.c"
# include "plustek-usbimg.c"
# include "plustek-usbshading.c"
+# include "canoscan-calibrate.c"
# include "plustek-usb.c"
#endif
@@ -299,6 +300,7 @@
DBG( _DBG_SANE_INIT, "warmup : %ds\n", cnf->adj.warmup );
DBG( _DBG_SANE_INIT, "lampOff : %d\n", cnf->adj.lampOff );
DBG( _DBG_SANE_INIT, "lampOffOnEnd : %d\n", cnf->adj.lampOffOnEnd );
+ DBG( _DBG_SANE_INIT, "altCalibrate : >%s<\n", cnf->adj.altCalibrate?"yes":"no" );
DBG( _DBG_SANE_INIT, "skipCalibr. : %d\n", cnf->adj.skipCalibration );
DBG( _DBG_SANE_INIT, "skipFine : %d\n", cnf->adj.skipFine );
DBG( _DBG_SANE_INIT, "skipFineWhite: %d\n", cnf->adj.skipFineWhite );
@@ -1434,6 +1436,8 @@
ival = 0;
decodeVal( str, "enableTPA", _INT, &config.adj.enableTpa, &ival);
+ decodeVal( str, "altCalibrate",
+ _INT, &config.adj.altCalibrate,&ival);
decodeVal( str, "skipCalibration",
_INT, &config.adj.skipCalibration,&ival);
decodeVal( str, "skipFine",
More information about the sane-devel
mailing list