1/*............................................................................. 2 * Project : SANE library for Plustek flatbed scanners; canoscan calibration 3 *............................................................................. 4 */ 5 6/** @file plustek-usbcal.c 7 * @brief Calibration routines for CanoScan CIS devices. 8 * 9 * Based on sources acquired from Plustek Inc.<br> 10 * Copyright (C) 2001-2007 Gerhard Jaeger <gerhard@gjaeger.de><br> 11 * Large parts Copyright (C) 2003 Christopher Montgomery <monty@xiph.org> 12 * 13 * Montys' comment: 14 * The basic premise: The stock Plustek-usbshading.c in the plustek 15 * driver is effectively nonfunctional for Canon CanoScan scanners. 16 * These scanners rely heavily on all calibration steps, especially 17 * fine white, to produce acceptable scan results. However, to make 18 * autocalibration work and make it work well involves some 19 * substantial mucking aobut in code that supports thirty other 20 * scanners with widely varying characteristics... none of which I own 21 * or can test. 22 * 23 * Therefore, I'm splitting out a few calibration functions I need 24 * to modify for the CanoScan which allows me to simplify things 25 * greatly for the CanoScan without worrying about breaking other 26 * scanners, as well as reuse the vast majority of the Plustek 27 * driver infrastructure without forking. 28 * 29 * History: 30 * - 0.45m - birth of the file; tested extensively with the LiDE 20 31 * - 0.46 - renamed to plustek-usbcal.c 32 * - fixed problems with LiDE30, works now with 650, 1220, 670, 1240 33 * - cleanup 34 * - added CCD calibration capability 35 * - added the usage of the swGain and swOffset values, to allow 36 * tweaking the calibration results on a sensor base 37 * - 0.47 - moved usb_HostSwap() to plustek_usbhw.c 38 * - fixed problem in cano_AdjustLightsource(), so that it won't 39 * stop too early. 40 * - 0.48 - cleanup 41 * - 0.49 - a_bRegs is now part of the device structure 42 * - fixed lampsetting in cano_AdjustLightsource() 43 * - 0.50 - tried to use the settings from SANE-1.0.13 44 * - added _TWEAK_GAIN to allow increasing GAIN during 45 * lamp coarse calibration 46 * - added also speedtest 47 * - fixed segfault in fine calibration 48 * - 0.51 - added fine calibration cache 49 * - usb_SwitchLamp() now really switches off the sensor 50 * - 0.52 - fixed setting for frontend values (gain/offset) 51 * - added 0 pixel detection for offset calculation 52 * 53 * This file is part of the SANE package. 54 * 55 * This program is free software; you can redistribute it and/or 56 * modify it under the terms of the GNU General Public License as 57 * published by the Free Software Foundation; either version 2 of the 58 * License, or (at your option) any later version. 59 * 60 * This program is distributed in the hope that it will be useful, but 61 * WITHOUT ANY WARRANTY; without even the implied warranty of 62 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 63 * General Public License for more details. 64 * 65 * You should have received a copy of the GNU General Public License 66 * along with this program. If not, see <https://www.gnu.org/licenses/>. 67 * 68 * As a special exception, the authors of SANE give permission for 69 * additional uses of the libraries contained in this release of SANE. 70 * 71 * The exception is that, if you link a SANE library with other files 72 * to produce an executable, this does not by itself cause the 73 * resulting executable to be covered by the GNU General Public 74 * License. Your use of that executable is in no way restricted on 75 * account of linking the SANE library code into it. 76 * 77 * This exception does not, however, invalidate any other reasons why 78 * the executable file might be covered by the GNU General Public 79 * License. 80 * 81 * If you submit changes to SANE to the maintainers to be included in 82 * a subsequent release, you agree by submitting the changes that 83 * those changes may be distributed with this exception intact. 84 * 85 * If you write modifications of your own for SANE, it is your choice 86 * whether to permit this exception to apply to your modifications. 87 * If you do not wish that, delete this exception notice. 88 * <hr> 89 */ 90 91/* un-/comment the following to en-/disable lamp coarse calibration to tweak 92 * the initial AFE gain settings 93 */ 94#define _TWEAK_GAIN 1 95 96/* set the threshold for 0 pixels (in percent if pixels per line) */ 97#define _DARK_TGT_THRESH 1 98 99/** 0 for not ready, 1 pos white lamp on, 2 lamp off */ 100static int strip_state = 0; 101 102/** depending on the strip state, the sensor is moved to the shading position 103 * and the lamp is switched on 104 */ 105static int 106cano_PrepareToReadWhiteCal( Plustek_Device *dev, SANE_Bool mv2shading_pos ) 107{ 108 SANE_Bool goto_shading_pos = SANE_TRUE; 109 HWDef *hw = &dev->usbDev.HwSetting; 110 111 switch (strip_state) { 112 case 0: 113 if( !usb_IsSheetFedDevice(dev)) { 114 if(!usb_ModuleToHome( dev, SANE_TRUE )) { 115 DBG( _DBG_ERROR, "cano_PrepareToReadWhiteCal() failed\n" ); 116 return _E_LAMP_NOT_IN_POS; 117 } 118 } else { 119 goto_shading_pos = mv2shading_pos; 120 } 121 122 if( goto_shading_pos ) { 123 if( !usb_ModuleMove(dev, MOVE_Forward, 124 (u_long)dev->usbDev.pSource->ShadingOriginY)) { 125 DBG( _DBG_ERROR, "cano_PrepareToReadWhiteCal() failed\n" ); 126 return _E_LAMP_NOT_IN_POS; 127 } 128 } 129 break; 130 case 2: 131 dev->usbDev.a_bRegs[0x29] = hw->bReg_0x29; 132 usb_switchLamp( dev, SANE_TRUE ); 133 if( !usbio_WriteReg( dev->fd, 0x29, dev->usbDev.a_bRegs[0x29])) { 134 DBG( _DBG_ERROR, "cano_PrepareToReadWhiteCal() failed\n" ); 135 return _E_LAMP_NOT_IN_POS; 136 } 137 break; 138 } 139 140 strip_state = 1; 141 return 0; 142} 143 144/** also here, depending on the strip state, the sensor will be moved to 145 * the shading position and the lamp will be switched off 146 */ 147static int 148cano_PrepareToReadBlackCal( Plustek_Device *dev ) 149{ 150 if( strip_state == 0 ) 151 if(cano_PrepareToReadWhiteCal(dev, SANE_FALSE)) 152 return SANE_FALSE; 153 154 if( strip_state != 2 ) { 155 /* 156 * if we have a dark shading strip, there's no need to switch 157 * the lamp off, leave in on a go to that strip 158 */ 159 if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) { 160 161 if( !usb_IsSheetFedDevice(dev)) 162 usb_ModuleToHome( dev, SANE_TRUE ); 163 usb_ModuleMove ( dev, MOVE_Forward, 164 (u_long)dev->usbDev.pSource->DarkShadOrgY ); 165 dev->usbDev.a_bRegs[0x45] &= ~0x10; 166 strip_state = 0; 167 168 } else { 169 /* switch lamp off to read dark data... */ 170 dev->usbDev.a_bRegs[0x29] = 0; 171 usb_switchLamp( dev, SANE_FALSE ); 172 strip_state = 2; 173 } 174 } 175 return 0; 176} 177 178/** according to the strip-state we switch the lamp on 179 */ 180static int 181cano_LampOnAfterCalibration( Plustek_Device *dev ) 182{ 183 HWDef *hw = &dev->usbDev.HwSetting; 184 185 switch (strip_state) { 186 case 2: 187 dev->usbDev.a_bRegs[0x29] = hw->bReg_0x29; 188 usb_switchLamp( dev, SANE_TRUE ); 189 if( !usbio_WriteReg( dev->fd, 0x29, dev->usbDev.a_bRegs[0x29])) { 190 DBG( _DBG_ERROR, "cano_LampOnAfterCalibration() failed\n" ); 191 return _E_LAMP_NOT_IN_POS; 192 } 193 strip_state = 1; 194 break; 195 } 196 return 0; 197} 198 199/** function to adjust the CIS lamp-off setting for a given channel. 200 * @param min - pointer to the min OFF point for the CIS-channel 201 * @param max - pointer to the max OFF point for the CIS-channel 202 * @param off - pointer to the current OFF point of the CIS-channel 203 * @param val - current value to check 204 * @return returns 0 if the value is fine, 1, if we need to adjust 205 */ 206static int 207cano_adjLampSetting( u_short *min, u_short *max, u_short *off, u_short val ) 208{ 209 u_long newoff = *off; 210 211 /* perfect value, no need to adjust 212 * val [53440..61440] is perfect 213 */ 214 if((val < (IDEAL_GainNormal)) && (val > (IDEAL_GainNormal-8000))) 215 return 0; 216 217 if(val >= (IDEAL_GainNormal-4000)) { 218 DBG(_DBG_INFO2, "* TOO BRIGHT --> reduce\n" ); 219 *max = newoff; 220 *off = ((newoff + *min)>>1); 221 222 } else { 223 224 u_short bisect = (newoff + *max)>>1; 225 u_short twice = newoff*2; 226 227 DBG(_DBG_INFO2, "* TOO DARK --> up\n" ); 228 *min = newoff; 229 *off = twice<bisect?twice:bisect; 230 231 /* as we have already set the maximum value, there's no need 232 * for this channel to recalibrate. 233 */ 234 if( *off > 0x3FFF ) { 235 DBG( _DBG_INFO, "* lamp off limited (0x%04x --> 0x3FFF)\n", *off); 236 *off = 0x3FFF; 237 return 10; 238 } 239 } 240 if((*min+1) >= *max ) 241 return 0; 242 243 return 1; 244} 245 246/** cano_AdjustLightsource 247 * coarse calibration step 0 248 * [Monty changes]: On the CanoScan at least, the default lamp 249 * settings are several *hundred* percent too high and vary from 250 * scanner-to-scanner by 20-50%. This is only for CIS devices 251 * where the lamp_off parameter is adjustable; I'd make it more general, 252 * but I only have the CIS hardware to test. 253 */ 254static int 255cano_AdjustLightsource( Plustek_Device *dev ) 256{ 257 char tmp[40]; 258 int i; 259 int res_r, res_g, res_b; 260 u_long dw, dwR, dwG, dwB, dwDiv, dwLoop1, dwLoop2; 261 RGBUShortDef max_rgb, min_rgb, tmp_rgb; 262 u_long *scanbuf = dev->scanning.pScanBuffer; 263 DCapsDef *scaps = &dev->usbDev.Caps; 264 HWDef *hw = &dev->usbDev.HwSetting; 265 266 if( usb_IsEscPressed()) 267 return SANE_FALSE; 268 269 DBG( _DBG_INFO, "cano_AdjustLightsource()\n" ); 270 271 if( !usb_IsCISDevice(dev)) { 272 DBG( _DBG_INFO, "- function skipped, CCD device!\n" ); 273 274 /* HEINER: we might have to tweak the PWM for the lamps */ 275 return SANE_TRUE; 276 } 277 278 /* define the strip to scan for coarse calibration 279 * done at optical resolution. 280 */ 281 m_ScanParam.Size.dwLines = 1; 282 m_ScanParam.Size.dwPixels = scaps->Normal.Size.x * 283 scaps->OpticDpi.x / 300UL; 284 285 m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2; 286 287 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) 288 m_ScanParam.Size.dwBytes *=3; 289 290 m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart * 291 300UL / scaps->OpticDpi.x); 292 m_ScanParam.bCalibration = PARAM_Gain; 293 294 DBG( _DBG_INFO2, "* Coarse Calibration Strip:\n" ); 295 DBG( _DBG_INFO2, "* Lines = %lu\n", m_ScanParam.Size.dwLines ); 296 DBG( _DBG_INFO2, "* Pixels = %lu\n", m_ScanParam.Size.dwPixels ); 297 DBG( _DBG_INFO2, "* Bytes = %lu\n", m_ScanParam.Size.dwBytes ); 298 DBG( _DBG_INFO2, "* Origin.X = %u\n", m_ScanParam.Origin.x ); 299 300 /* init... */ 301 max_rgb.Red = max_rgb.Green = max_rgb.Blue = 0x3fff; 302 min_rgb.Red = hw->red_lamp_on; 303 min_rgb.Green = hw->green_lamp_on; 304 min_rgb.Blue = hw->blue_lamp_on; 305 306 if((dev->adj.rlampoff != -1) && 307 (dev->adj.glampoff != -1) && (dev->adj.blampoff != -1)) { 308 DBG( _DBG_INFO, "- function skipped, using frontend values!\n" ); 309 return SANE_TRUE; 310 } 311 312 /* we probably should preset gain to some reasonably good value 313 * i.e. 0x0a as it's done by Canon within their Windoze driver! 314 */ 315#ifdef _TWEAK_GAIN 316 for( i=0x3b; i<0x3e; i++ ) 317 dev->usbDev.a_bRegs[i] = 0x0a; 318#endif 319 for( i = 0; ; i++ ) { 320 321 m_ScanParam.dMCLK = dMCLK; 322 if( !usb_SetScanParameters( dev, &m_ScanParam )) { 323 return SANE_FALSE; 324 } 325 326 if( !usb_ScanBegin( dev, SANE_FALSE) || 327 !usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwPhyBytes ) || 328 !usb_ScanEnd( dev )) { 329 DBG( _DBG_ERROR, "* cano_AdjustLightsource() failed\n" ); 330 return SANE_FALSE; 331 } 332 333 DBG( _DBG_INFO2, "* PhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes ); 334 DBG( _DBG_INFO2, "* PhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels); 335 336 sprintf( tmp, "coarse-lamp-%u.raw", i ); 337 338 dumpPicInit(&m_ScanParam, tmp); 339 dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0); 340 341 if(usb_HostSwap()) 342 usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes ); 343 344 sprintf( tmp, "coarse-lamp-swap%u.raw", i ); 345 346 dumpPicInit(&m_ScanParam, tmp); 347 dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0); 348 349 dwDiv = 10; 350 dwLoop1 = m_ScanParam.Size.dwPhyPixels/dwDiv; 351 352 tmp_rgb.Red = tmp_rgb.Green = tmp_rgb.Blue = 0; 353 354 /* find out the max pixel value for R, G, B */ 355 for( dw = 0; dwLoop1; dwLoop1-- ) { 356 357 /* do some averaging... */ 358 for (dwLoop2 = dwDiv, dwR = dwG = dwB = 0; dwLoop2; dwLoop2--, dw++) { 359 360 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 361 362 if( usb_IsCISDevice(dev)) { 363 dwR += ((u_short*)scanbuf)[dw]; 364 dwG += ((u_short*)scanbuf) 365 [dw+m_ScanParam.Size.dwPhyPixels+1]; 366 dwB += ((u_short*)scanbuf) 367 [dw+(m_ScanParam.Size.dwPhyPixels+1)*2]; 368 } else { 369 dwR += ((RGBUShortDef*)scanbuf)[dw].Red; 370 dwG += ((RGBUShortDef*)scanbuf)[dw].Green; 371 dwB += ((RGBUShortDef*)scanbuf)[dw].Blue; 372 } 373 } else { 374 dwG += ((u_short*)scanbuf)[dw]; 375 } 376 } 377 378 dwR = dwR / dwDiv; 379 dwG = dwG / dwDiv; 380 dwB = dwB / dwDiv; 381 382 if( tmp_rgb.Red < dwR ) 383 tmp_rgb.Red = dwR; 384 if( tmp_rgb.Green < dwG ) 385 tmp_rgb.Green = dwG; 386 if( tmp_rgb.Blue < dwB ) 387 tmp_rgb.Blue = dwB; 388 } 389 390 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 391 DBG( _DBG_INFO2, "red_lamp_off = %u/%u/%u\n", 392 min_rgb.Red ,hw->red_lamp_off, max_rgb.Red ); 393 } 394 395 DBG( _DBG_INFO2, "green_lamp_off = %u/%u/%u\n", 396 min_rgb.Green, hw->green_lamp_off, max_rgb.Green ); 397 398 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 399 DBG( _DBG_INFO2, "blue_lamp_off = %u/%u/%u\n", 400 min_rgb.Blue, hw->blue_lamp_off, max_rgb.Blue ); 401 } 402 403 DBG(_DBG_INFO2, "CUR(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n", 404 tmp_rgb.Red, tmp_rgb.Red, tmp_rgb.Green, 405 tmp_rgb.Green, tmp_rgb.Blue, tmp_rgb.Blue ); 406 res_r = 0; 407 res_g = 0; 408 res_b = 0; 409 410 /* bisect */ 411 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 412 res_r = cano_adjLampSetting( &min_rgb.Red, &max_rgb.Red, 413 &hw->red_lamp_off, tmp_rgb.Red ); 414 res_b = cano_adjLampSetting( &min_rgb.Blue, &max_rgb.Blue, 415 &hw->blue_lamp_off,tmp_rgb.Blue ); 416 } 417 418 res_g = cano_adjLampSetting( &min_rgb.Green, &max_rgb.Green, 419 &hw->green_lamp_off, tmp_rgb.Green ); 420 421 /* nothing adjusted, so stop here */ 422 if((res_r == 0) && (res_g == 0) && (res_b == 0)) 423 break; 424 425 /* no need to adjust more, we have already reached the limit 426 * without tweaking the gain. 427 */ 428 if((res_r == 10) && (res_g == 10) && (res_b == 10)) 429 break; 430 431 /* we raise the gain for channels, that have been limited */ 432#ifdef _TWEAK_GAIN 433 if( res_r == 10 ) { 434 if( dev->usbDev.a_bRegs[0x3b] < 0xf) 435 dev->usbDev.a_bRegs[0x3b]++; 436 } 437 if( res_g == 10 ) { 438 if( dev->usbDev.a_bRegs[0x3c] < 0x0f) 439 dev->usbDev.a_bRegs[0x3c]++; 440 } 441 if( res_b == 10 ) { 442 if( dev->usbDev.a_bRegs[0x3d] < 0x0f) 443 dev->usbDev.a_bRegs[0x3d]++; 444 } 445#endif 446 447 /* now decide what to do: 448 * if we were too bright, we have to rerun the loop in any 449 * case 450 * if we're too dark, we should rerun it too, but we can 451 * compensate that using higher gain values later 452 */ 453 if( i >= 10 ) { 454 DBG(_DBG_INFO, "* 10 times limit reached, still too dark!!!\n"); 455 break; 456 } 457 usb_AdjustLamps(dev, SANE_TRUE); 458 } 459 460 DBG( _DBG_INFO, "* red_lamp_on = %u\n", hw->red_lamp_on ); 461 DBG( _DBG_INFO, "* red_lamp_off = %u\n", hw->red_lamp_off ); 462 DBG( _DBG_INFO, "* green_lamp_on = %u\n", hw->green_lamp_on ); 463 DBG( _DBG_INFO, "* green_lamp_off = %u\n", hw->green_lamp_off ); 464 DBG( _DBG_INFO, "* blue_lamp_on = %u\n", hw->blue_lamp_on ); 465 DBG( _DBG_INFO, "* blue_lamp_off = %u\n", hw->blue_lamp_off ); 466 467 DBG( _DBG_INFO, "cano_AdjustLightsource() done.\n" ); 468 return SANE_TRUE; 469} 470 471/** 472 */ 473static int 474cano_adjGainSetting( u_char *min, u_char *max, u_char *gain,u_long val ) 475{ 476 u_long newgain = *gain; 477 478 if((val < IDEAL_GainNormal) && (val > (IDEAL_GainNormal-8000))) 479 return 0; 480 481 if(val > (IDEAL_GainNormal-4000)) { 482 *max = newgain; 483 *gain = (newgain + *min)>>1; 484 } else { 485 *min = newgain; 486 *gain = (newgain + *max)>>1; 487 } 488 489 if((*min+1) >= *max) 490 return 0; 491 492 return 1; 493} 494 495/** cano_AdjustGain 496 * function to perform the "coarse calibration step" part 1. 497 * We scan reference image pixels to determine the optimum coarse gain settings 498 * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are 499 * applied at the line rate during normal scanning. 500 * The scanned line should contain a white strip with some black at the 501 * beginning. The function searches for the maximum value which corresponds to 502 * the maximum white value. 503 * Affects register 0x3b, 0x3c and 0x3d 504 * 505 * adjLightsource, above, steals most of this function's thunder. 506 */ 507static SANE_Bool 508cano_AdjustGain( Plustek_Device *dev ) 509{ 510 char tmp[40]; 511 int i = 0, adj = 1; 512 u_long dw; 513 u_long *scanbuf = dev->scanning.pScanBuffer; 514 DCapsDef *scaps = &dev->usbDev.Caps; 515 HWDef *hw = &dev->usbDev.HwSetting; 516 517 unsigned char max[3], min[3]; 518 519 if( usb_IsEscPressed()) 520 return SANE_FALSE; 521 522 bMaxITA = 0xff; 523 524 max[0] = max[1] = max[2] = 0x3f; 525 min[0] = min[1] = min[2] = 1; 526 527 DBG( _DBG_INFO, "cano_AdjustGain()\n" ); 528 if( !usb_InCalibrationMode(dev)) { 529 if((dev->adj.rgain != -1) && 530 (dev->adj.ggain != -1) && (dev->adj.bgain != -1)) { 531 setAdjGain( dev->adj.rgain, &dev->usbDev.a_bRegs[0x3b] ); 532 setAdjGain( dev->adj.ggain, &dev->usbDev.a_bRegs[0x3c] ); 533 setAdjGain( dev->adj.bgain, &dev->usbDev.a_bRegs[0x3d] ); 534 DBG( _DBG_INFO, "- function skipped, using frontend values!\n" ); 535 return SANE_TRUE; 536 } 537 } 538 539 /* define the strip to scan for coarse calibration 540 * done at 300dpi 541 */ 542 m_ScanParam.Size.dwLines = 1; /* for gain */ 543 m_ScanParam.Size.dwPixels = scaps->Normal.Size.x * 544 scaps->OpticDpi.x / 300UL; 545 546 m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2; 547 548 if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color) 549 m_ScanParam.Size.dwBytes *=3; 550 551 m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart * 552 300UL / scaps->OpticDpi.x); 553 m_ScanParam.bCalibration = PARAM_Gain; 554 555 DBG( _DBG_INFO2, "Coarse Calibration Strip:\n" ); 556 DBG( _DBG_INFO2, "Lines = %lu\n", m_ScanParam.Size.dwLines ); 557 DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels ); 558 DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes ); 559 DBG( _DBG_INFO2, "Origin.X = %u\n", m_ScanParam.Origin.x ); 560 561 while( adj ) { 562 563 m_ScanParam.dMCLK = dMCLK; 564 565 if( !usb_SetScanParameters( dev, &m_ScanParam )) 566 return SANE_FALSE; 567 568 if( !usb_ScanBegin( dev, SANE_FALSE) || 569 !usb_ScanReadImage(dev,scanbuf,m_ScanParam.Size.dwPhyBytes) || 570 !usb_ScanEnd( dev )) { 571 DBG( _DBG_ERROR, "cano_AdjustGain() failed\n" ); 572 return SANE_FALSE; 573 } 574 575 DBG( _DBG_INFO2, "PhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes ); 576 DBG( _DBG_INFO2, "PhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels ); 577 578 sprintf( tmp, "coarse-gain-%u.raw", i++ ); 579 580 dumpPicInit(&m_ScanParam, tmp); 581 dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0); 582 583 if(usb_HostSwap()) 584 usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes ); 585 586 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 587 588 RGBUShortDef max_rgb; 589 u_long dwR, dwG, dwB; 590 u_long dwDiv = 10; 591 u_long dwLoop1 = m_ScanParam.Size.dwPhyPixels/dwDiv, dwLoop2; 592 593 max_rgb.Red = max_rgb.Green = max_rgb.Blue = 0; 594 595 /* find out the max pixel value for R, G, B */ 596 for( dw = 0; dwLoop1; dwLoop1-- ) { 597 598 /* do some averaging... */ 599 for (dwLoop2 = dwDiv, dwR=dwG=dwB=0; dwLoop2; dwLoop2--, dw++) { 600 601 if( usb_IsCISDevice(dev)) { 602 dwR += ((u_short*)scanbuf)[dw]; 603 dwG += ((u_short*)scanbuf) 604 [dw+m_ScanParam.Size.dwPhyPixels+1]; 605 dwB += ((u_short*)scanbuf) 606 [dw+(m_ScanParam.Size.dwPhyPixels+1)*2]; 607 } else { 608 dwR += ((RGBUShortDef*)scanbuf)[dw].Red; 609 dwG += ((RGBUShortDef*)scanbuf)[dw].Green; 610 dwB += ((RGBUShortDef*)scanbuf)[dw].Blue; 611 } 612 } 613 dwR = dwR / dwDiv; 614 dwG = dwG / dwDiv; 615 dwB = dwB / dwDiv; 616 617 if(max_rgb.Red < dwR) 618 max_rgb.Red = dwR; 619 if(max_rgb.Green < dwG) 620 max_rgb.Green = dwG; 621 if(max_rgb.Blue < dwB) 622 max_rgb.Blue = dwB; 623 } 624 625 DBG(_DBG_INFO2, "MAX(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n", 626 max_rgb.Red, max_rgb.Red, max_rgb.Green, 627 max_rgb.Green, max_rgb.Blue, max_rgb.Blue ); 628 629 adj = cano_adjGainSetting(min , max ,dev->usbDev.a_bRegs+0x3b,max_rgb.Red ); 630 adj += cano_adjGainSetting(min+1, max+1,dev->usbDev.a_bRegs+0x3c,max_rgb.Green); 631 adj += cano_adjGainSetting(min+2, max+2,dev->usbDev.a_bRegs+0x3d,max_rgb.Blue ); 632 633 } else { 634 635 u_short w_max = 0; 636 637 for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++ ) { 638 if( w_max < ((u_short*)scanbuf)[dw]) 639 w_max = ((u_short*)scanbuf)[dw]; 640 } 641 642 adj = cano_adjGainSetting(min,max,dev->usbDev.a_bRegs+0x3c,w_max); 643 dev->usbDev.a_bRegs[0x3b] = (dev->usbDev.a_bRegs[0x3d] = dev->usbDev.a_bRegs[0x3c]); 644 645 DBG(_DBG_INFO2, "MAX(G)= 0x%04x(%u)\n", w_max, w_max ); 646 647 } 648 DBG( _DBG_INFO2, "REG[0x3b] = %u\n", dev->usbDev.a_bRegs[0x3b] ); 649 DBG( _DBG_INFO2, "REG[0x3c] = %u\n", dev->usbDev.a_bRegs[0x3c] ); 650 DBG( _DBG_INFO2, "REG[0x3d] = %u\n", dev->usbDev.a_bRegs[0x3d] ); 651 } 652 DBG( _DBG_INFO, "cano_AdjustGain() done.\n" ); 653 return SANE_TRUE; 654} 655 656static int tweak_offset[3]; 657 658/** 659 */ 660static int 661cano_GetNewOffset(Plustek_Device *dev, u_long *val, int channel, signed char *low, 662 signed char *now, signed char *high, u_long *zc) 663{ 664 DCapsDef *scaps = &dev->usbDev.Caps; 665 666 if (tweak_offset[channel]) { 667 668 /* if we're too black, we're likely off the low end */ 669 if( val[channel] <= 16 ) { 670 low[channel] = now[channel]; 671 now[channel] = (now[channel]+high[channel])/2; 672 673 dev->usbDev.a_bRegs[0x38+channel]= (now[channel]&0x3f); 674 675 if( low[channel]+1 >= high[channel] ) 676 return 0; 677 return 1; 678 679 } else if ( val[channel]>=2048 ) { 680 high[channel]=now[channel]; 681 now[channel]=(now[channel]+low[channel])/2; 682 683 dev->usbDev.a_bRegs[0x38+channel]= (now[channel]&0x3f); 684 685 if(low[channel]+1>=high[channel]) 686 return 0; 687 return 1; 688 } 689 } 690 691 if (!(scaps->workaroundFlag & _WAF_INC_DARKTGT)) { 692 DBG( _DBG_INFO, "0 Pixel adjustment not active!\n"); 693 return 0; 694 } 695 696 /* reaching this point, our black level should be okay, but 697 * we also should check the percentage of 0 level pixels. 698 * It turned out, that when having a lot of 0 level pixels, 699 * the calibration will be bad and the resulting scans show up 700 * stripes... 701 */ 702 if (zc[channel] > _DARK_TGT_THRESH) { 703 DBG( _DBG_INFO2, "More than %u%% 0 pixels detected, raise offset!\n", 704 _DARK_TGT_THRESH); 705 high[channel]=now[channel]; 706 now[channel]=(now[channel]+low[channel])/2; 707 708 /* no more value checks, the goal to set the black level < 2048 709 * will cause stripes... 710 */ 711 tweak_offset[channel] = 0; 712 713 dev->usbDev.a_bRegs[0x38+channel]= (now[channel]&0x3f); 714 715 if( low[channel]+1 >= high[channel] ) 716 return 0; 717 return 1; 718 719 } 720#if 0 721 else if ( val[channel]>=4096 ) { 722 low[channel] = now[channel]; 723 now[channel] = (now[channel]+high[channel])/2; 724 725 dev->usbDev.a_bRegs[0x38+channel]= (now[channel]&0x3f); 726 727 if( low[channel]+1 >= high[channel] ) 728 return 0; 729 return 1; 730 } 731#endif 732 return 0; 733} 734 735/** cano_AdjustOffset 736 * function to perform the "coarse calibration step" part 2. 737 * We scan reference image pixels to determine the optimum coarse offset settings 738 * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are 739 * applied at the line rate during normal scanning. 740 * On CIS based devices, we switch the light off, on CCD devices, we use the optical 741 * black pixels. 742 * Affects register 0x38, 0x39 and 0x3a 743 */ 744 745/* Move this to a bisection-based algo and correct some fenceposts; 746 Plustek's example code disagrees with NatSemi's docs; going by the 747 docs works better, I will assume the docs are correct. --Monty */ 748 749static int 750cano_AdjustOffset( Plustek_Device *dev ) 751{ 752 char tmp[40]; 753 int i, adj; 754 u_short r, g, b; 755 u_long dw, dwPixels; 756 u_long dwSum[3], zCount[3]; 757 758 signed char low[3] = {-32,-32,-32 }; 759 signed char now[3] = { 0, 0, 0 }; 760 signed char high[3] = { 31, 31, 31 }; 761 762 u_long *scanbuf = dev->scanning.pScanBuffer; 763 HWDef *hw = &dev->usbDev.HwSetting; 764 DCapsDef *scaps = &dev->usbDev.Caps; 765 766 if( usb_IsEscPressed()) 767 return SANE_FALSE; 768 769 DBG( _DBG_INFO, "cano_AdjustOffset()\n" ); 770 if( !usb_InCalibrationMode(dev)) { 771 if((dev->adj.rofs != -1) && 772 (dev->adj.gofs != -1) && (dev->adj.bofs != -1)) { 773 dev->usbDev.a_bRegs[0x38] = (dev->adj.rofs & 0x3f); 774 dev->usbDev.a_bRegs[0x39] = (dev->adj.gofs & 0x3f); 775 dev->usbDev.a_bRegs[0x3a] = (dev->adj.bofs & 0x3f); 776 DBG( _DBG_INFO, "- function skipped, using frontend values!\n" ); 777 return SANE_TRUE; 778 } 779 } 780 781 m_ScanParam.Size.dwLines = 1; 782 m_ScanParam.Size.dwPixels = scaps->Normal.Size.x*scaps->OpticDpi.x/300UL; 783 784 if( usb_IsCISDevice(dev)) 785 dwPixels = m_ScanParam.Size.dwPixels; 786 else 787 dwPixels = (u_long)(hw->bOpticBlackEnd - hw->bOpticBlackStart); 788 789 m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2; 790 791 if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color) 792 m_ScanParam.Size.dwBytes *= 3; 793 794 m_ScanParam.Origin.x = (u_short)((u_long)hw->bOpticBlackStart * 300UL / 795 dev->usbDev.Caps.OpticDpi.x); 796 m_ScanParam.bCalibration = PARAM_Offset; 797 m_ScanParam.dMCLK = dMCLK; 798 799 if( !usb_SetScanParameters( dev, &m_ScanParam )) { 800 DBG( _DBG_ERROR, "cano_AdjustOffset() failed\n" ); 801 return SANE_FALSE; 802 } 803 804 DBG( _DBG_INFO2, "S.dwPixels = %lu\n", m_ScanParam.Size.dwPixels ); 805 DBG( _DBG_INFO2, "dwPixels = %lu\n", dwPixels ); 806 DBG( _DBG_INFO2, "dwPhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes ); 807 DBG( _DBG_INFO2, "dwPhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels ); 808 809 tweak_offset[0] = 810 tweak_offset[1] = 811 tweak_offset[2] = 1; 812 813 for( i = 0, adj = 1; adj != 0; i++ ) { 814 815 if((!usb_ScanBegin(dev, SANE_FALSE)) || 816 (!usb_ScanReadImage(dev,scanbuf,m_ScanParam.Size.dwPhyBytes)) || 817 !usb_ScanEnd( dev )) { 818 DBG( _DBG_ERROR, "cano_AdjustOffset() failed\n" ); 819 return SANE_FALSE; 820 } 821 822 sprintf( tmp, "coarse-off-%u.raw", i ); 823 824 dumpPicInit(&m_ScanParam, tmp); 825 dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0); 826 827 if(usb_HostSwap()) 828 usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes ); 829 830 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 831 832 dwSum[0] = dwSum[1] = dwSum[2] = 0; 833 zCount[0] = zCount[1] = zCount[2] = 0; 834 835 for (dw = 0; dw < dwPixels; dw++) { 836 837 if( usb_IsCISDevice(dev)) { 838 839 r = ((u_short*)scanbuf)[dw]; 840 g = ((u_short*)scanbuf)[dw+m_ScanParam.Size.dwPhyPixels+1]; 841 b = ((u_short*)scanbuf)[dw+(m_ScanParam.Size.dwPhyPixels+1)*2]; 842 843 } else { 844 r = ((RGBUShortDef*)scanbuf)[dw].Red; 845 g = ((RGBUShortDef*)scanbuf)[dw].Green; 846 b = ((RGBUShortDef*)scanbuf)[dw].Blue; 847 } 848 849 dwSum[0] += r; 850 dwSum[1] += g; 851 dwSum[2] += b; 852 853 if (r==0) zCount[0]++; 854 if (g==0) zCount[1]++; 855 if (b==0) zCount[2]++; 856 } 857 858 DBG( _DBG_INFO2, "RedSum = %lu, ave = %lu, ZC=%lu, %lu%%\n", 859 dwSum[0], dwSum[0]/dwPixels, 860 zCount[0], (zCount[0]*100)/dwPixels); 861 DBG( _DBG_INFO2, "GreenSum = %lu, ave = %lu, ZC=%lu, %lu%%\n", 862 dwSum[1], dwSum[1]/dwPixels, 863 zCount[1], (zCount[1]*100)/dwPixels); 864 DBG( _DBG_INFO2, "BlueSum = %lu, ave = %lu, ZC=%lu, %lu%%\n", 865 dwSum[2], dwSum[2]/dwPixels, 866 zCount[2], (zCount[2]*100)/dwPixels); 867 868 /* do averaging for each channel */ 869 dwSum[0] /= dwPixels; 870 dwSum[1] /= dwPixels; 871 dwSum[2] /= dwPixels; 872 873 zCount[0] = (zCount[0] * 100)/ dwPixels; 874 zCount[1] = (zCount[1] * 100)/ dwPixels; 875 zCount[2] = (zCount[2] * 100)/ dwPixels; 876 877 adj = cano_GetNewOffset(dev, dwSum, 0, low, now, high, zCount); 878 adj |= cano_GetNewOffset(dev, dwSum, 1, low, now, high, zCount); 879 adj |= cano_GetNewOffset(dev, dwSum, 2, low, now, high, zCount); 880 881 DBG( _DBG_INFO2, "RedOff = %d/%d/%d\n", 882 (int)low[0],(int)now[0],(int)high[0]); 883 DBG( _DBG_INFO2, "GreenOff = %d/%d/%d\n", 884 (int)low[1],(int)now[1],(int)high[1]); 885 DBG( _DBG_INFO2, "BlueOff = %d/%d/%d\n", 886 (int)low[2],(int)now[2],(int)high[2]); 887 888 } else { 889 dwSum[0] = 0; 890 zCount[0] = 0; 891 892 for( dw = 0; dw < dwPixels; dw++ ) { 893 dwSum[0] += ((u_short*)scanbuf)[dw]; 894 895 if (((u_short*)scanbuf)[dw] == 0) 896 zCount[0]++; 897 } 898 899 DBG( _DBG_INFO2, "Sum=%lu, ave=%lu, ZC=%lu, %lu%%\n", 900 dwSum[0],dwSum[0]/dwPixels, 901 zCount[0], (zCount[0]*100)/dwPixels); 902 903 dwSum[0] /= dwPixels; 904 zCount[0] = (zCount[0] * 100)/ dwPixels; 905 906 adj = cano_GetNewOffset(dev, dwSum, 0, low, now, high, zCount); 907 908 dev->usbDev.a_bRegs[0x3a] = 909 dev->usbDev.a_bRegs[0x39] = dev->usbDev.a_bRegs[0x38]; 910 911 DBG( _DBG_INFO2, "GrayOff = %d/%d/%d\n", 912 (int)low[0],(int)now[0],(int)high[0]); 913 } 914 915 DBG( _DBG_INFO2, "REG[0x38] = %u\n", dev->usbDev.a_bRegs[0x38] ); 916 DBG( _DBG_INFO2, "REG[0x39] = %u\n", dev->usbDev.a_bRegs[0x39] ); 917 DBG( _DBG_INFO2, "REG[0x3a] = %u\n", dev->usbDev.a_bRegs[0x3a] ); 918 919 _UIO(sanei_lm983x_write(dev->fd, 0x38, &dev->usbDev.a_bRegs[0x38], 3, SANE_TRUE)); 920 } 921 922 /* is that really needed?! */ 923 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 924 dev->usbDev.a_bRegs[0x38] = now[0] & 0x3f; 925 dev->usbDev.a_bRegs[0x39] = now[1] & 0x3f; 926 dev->usbDev.a_bRegs[0x3a] = now[2] & 0x3f; 927 } else { 928 dev->usbDev.a_bRegs[0x38] = 929 dev->usbDev.a_bRegs[0x39] = 930 dev->usbDev.a_bRegs[0x3a] = now[0] & 0x3f; 931 } 932 933 DBG( _DBG_INFO, "cano_AdjustOffset() done.\n" ); 934 return SANE_TRUE; 935} 936 937/** usb_AdjustDarkShading 938 * fine calibration part 1 939 */ 940static SANE_Bool 941cano_AdjustDarkShading( Plustek_Device *dev, u_short cal_dpi ) 942{ 943 char tmp[40]; 944 ScanParam *param = &dev->scanning.sParam; 945 ScanDef *scan = &dev->scanning; 946 u_long *scanbuf = scan->pScanBuffer; 947 u_short *bufp; 948 unsigned int i, j; 949 int step, stepW, val; 950 u_long red, green, blue, gray; 951 952 DBG( _DBG_INFO, "cano_AdjustDarkShading()\n" ); 953 if( usb_IsEscPressed()) 954 return SANE_FALSE; 955 956 usb_PrepareFineCal( dev, &m_ScanParam, cal_dpi ); 957 m_ScanParam.bCalibration = PARAM_DarkShading; 958 959 sprintf( tmp, "fine-dark.raw" ); 960 dumpPicInit(&m_ScanParam, tmp); 961 962 usb_SetScanParameters( dev, &m_ScanParam ); 963 if( usb_ScanBegin( dev, SANE_FALSE ) && 964 usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwTotalBytes)) { 965 966 dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwTotalBytes, 0); 967 968 if(usb_HostSwap()) 969 usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwTotalBytes); 970 } 971 if (!usb_ScanEnd( dev )){ 972 DBG( _DBG_ERROR, "cano_AdjustDarkShading() failed\n" ); 973 return SANE_FALSE; 974 } 975 976 /* average the n lines, compute reg values */ 977 if( scan->sParam.bDataType == SCANDATATYPE_Color ) { 978 979 stepW = m_ScanParam.Size.dwPhyPixels; 980 if( usb_IsCISDevice(dev)) 981 step = m_ScanParam.Size.dwPhyPixels + 1; 982 else 983 step = (m_ScanParam.Size.dwPhyPixels*3) + 1; 984 985 for( i=0; i<m_ScanParam.Size.dwPhyPixels; i++ ) { 986 987 red = 0; 988 green = 0; 989 blue = 0; 990 if( usb_IsCISDevice(dev)) 991 bufp = ((u_short *)scanbuf)+i; 992 else 993 bufp = ((u_short *)scanbuf)+(i*3); 994 995 for( j=0; j<m_ScanParam.Size.dwPhyLines; j++ ) { 996 997 if( usb_IsCISDevice(dev)) { 998 red += *bufp; bufp+=step; 999 green += *bufp; bufp+=step; 1000 blue += *bufp; bufp+=step; 1001 } else { 1002 1003 red += bufp[0]; 1004 green += bufp[1]; 1005 blue += bufp[2]; 1006 1007 bufp += step; 1008 } 1009 } 1010 1011 val = ((int)(red/m_ScanParam.Size.dwPhyLines) + param->swOffset[0]); 1012 if( val < 0 ) { 1013 DBG( _DBG_INFO, "val < 0!!!!\n" ); 1014 val = 0; 1015 } 1016 a_wDarkShading[i] = (u_short)val; 1017 1018 val = ((int)(green/m_ScanParam.Size.dwPhyLines) + param->swOffset[1]); 1019 if( val < 0 ) { 1020 DBG( _DBG_INFO, "val < 0!!!!\n" ); 1021 val = 0; 1022 } 1023 a_wDarkShading[i+stepW] = (u_short)val; 1024 1025 val = ((int)(blue/m_ScanParam.Size.dwPhyLines) + param->swOffset[2]); 1026 if( val < 0 ) { 1027 DBG( _DBG_INFO, "val < 0!!!!\n" ); 1028 val = 0; 1029 } 1030 a_wDarkShading[i+stepW*2] = (u_short)val; 1031 } 1032 1033 } else { 1034 1035 step = m_ScanParam.Size.dwPhyPixels + 1; 1036 for( i=0; i<m_ScanParam.Size.dwPhyPixels; i++ ) { 1037 1038 gray = 0; 1039 bufp = ((u_short *)scanbuf)+i; 1040 1041 for( j=0; j < m_ScanParam.Size.dwPhyLines; j++ ) { 1042 gray += *bufp; 1043 bufp += step; 1044 } 1045 a_wDarkShading[i]= gray/j + param->swOffset[0]; 1046 } 1047 1048 memcpy( a_wDarkShading + m_ScanParam.Size.dwPhyPixels, 1049 a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2); 1050 memcpy( a_wDarkShading + m_ScanParam.Size.dwPhyPixels * 2, 1051 a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2); 1052 } 1053 1054 if(usb_HostSwap()) 1055 usb_Swap(a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2 * 3); 1056 1057 usb_line_statistics( "Dark", a_wDarkShading, m_ScanParam.Size.dwPhyPixels, 1058 scan->sParam.bDataType == SCANDATATYPE_Color?1:0); 1059 1060 DBG( _DBG_INFO, "cano_AdjustDarkShading() done\n" ); 1061 return SANE_TRUE; 1062} 1063 1064/** usb_AdjustWhiteShading 1065 * fine calibration part 2 - read the white calibration area and calculate 1066 * the gain coefficient for each pixel 1067 */ 1068static SANE_Bool 1069cano_AdjustWhiteShading( Plustek_Device *dev, u_short cal_dpi ) 1070{ 1071 char tmp[40]; 1072 ScanParam *param = &dev->scanning.sParam; 1073 ScanDef *scan = &dev->scanning; 1074 u_long *scanbuf = scan->pScanBuffer; 1075 u_short *bufp; 1076 unsigned int i, j; 1077 int step, stepW; 1078 u_long red, green, blue, gray; 1079 1080 DBG( _DBG_INFO, "cano_AdjustWhiteShading()\n" ); 1081 if( usb_IsEscPressed()) 1082 return SANE_FALSE; 1083 1084 usb_PrepareFineCal( dev, &m_ScanParam, cal_dpi ); 1085 m_ScanParam.bCalibration = PARAM_WhiteShading; 1086 1087 sprintf( tmp, "fine-white.raw" ); 1088 DBG( _DBG_INFO2, "FINE WHITE Calibration Strip: %s\n", tmp ); 1089 DBG( _DBG_INFO2, "Lines = %lu\n", m_ScanParam.Size.dwLines ); 1090 DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels ); 1091 DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes ); 1092 DBG( _DBG_INFO2, "Origin.X = %u\n", m_ScanParam.Origin.x ); 1093 dumpPicInit(&m_ScanParam, tmp); 1094 1095 if( usb_SetScanParameters( dev, &m_ScanParam ) && 1096 usb_ScanBegin( dev, SANE_FALSE ) && 1097 usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwTotalBytes)) { 1098 1099 dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwTotalBytes, 0); 1100 1101 if(usb_HostSwap()) 1102 usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwTotalBytes); 1103 1104 if (!usb_ScanEnd( dev )) { 1105 DBG( _DBG_ERROR, "cano_AdjustWhiteShading() failed\n" ); 1106 return SANE_FALSE; 1107 } 1108 } else { 1109 DBG( _DBG_ERROR, "cano_AdjustWhiteShading() failed\n" ); 1110 return SANE_FALSE; 1111 } 1112 1113 /* average the n lines, compute reg values */ 1114 if( scan->sParam.bDataType == SCANDATATYPE_Color ) { 1115 1116 stepW = m_ScanParam.Size.dwPhyPixels; 1117 if( usb_IsCISDevice(dev)) 1118 step = m_ScanParam.Size.dwPhyPixels + 1; 1119 else 1120 step = (m_ScanParam.Size.dwPhyPixels*3) + 1; 1121 1122 for( i=0; i < m_ScanParam.Size.dwPhyPixels; i++ ) { 1123 1124 red = 0; 1125 green = 0; 1126 blue = 0; 1127 if( usb_IsCISDevice(dev)) 1128 bufp = ((u_short *)scanbuf)+i; 1129 else 1130 bufp = ((u_short *)scanbuf)+(i*3); 1131 1132 for( j=0; j<m_ScanParam.Size.dwPhyLines; j++ ) { 1133 1134 if( usb_IsCISDevice(dev)) { 1135 red += *bufp; bufp+=step; 1136 green += *bufp; bufp+=step; 1137 blue += *bufp; bufp+=step; 1138 } else { 1139 red += bufp[0]; 1140 green += bufp[1]; 1141 blue += bufp[2]; 1142 bufp += step; 1143 } 1144 } 1145 1146 /* tweaked by the settings in swGain --> 1000/swGain[r,g,b] */ 1147 red = (65535.*1000./(double)param->swGain[0]) * 16384.*j/red; 1148 green = (65535.*1000./(double)param->swGain[1]) * 16384.*j/green; 1149 blue = (65535.*1000./(double)param->swGain[2]) * 16384.*j/blue; 1150 1151 a_wWhiteShading[i] = (red > 65535 ? 65535:red ); 1152 a_wWhiteShading[i+stepW] = (green > 65535 ? 65535:green); 1153 a_wWhiteShading[i+stepW*2] = (blue > 65535 ? 65535:blue ); 1154 } 1155 1156 } else { 1157 1158 step = m_ScanParam.Size.dwPhyPixels + 1; 1159 for( i=0; i<m_ScanParam.Size.dwPhyPixels; i++ ){ 1160 gray = 0; 1161 bufp = ((u_short *)scanbuf)+i; 1162 1163 for( j=0; j<m_ScanParam.Size.dwPhyLines; j++ ) { 1164 gray += *bufp; 1165 bufp += step; 1166 } 1167 1168 gray = (65535.*1000./(double)param->swGain[0]) * 16384.*j/gray; 1169 1170 a_wWhiteShading[i]= (gray > 65535 ? 65535:gray); 1171 } 1172 1173 memcpy( a_wWhiteShading + m_ScanParam.Size.dwPhyPixels, 1174 a_wWhiteShading, m_ScanParam.Size.dwPhyPixels * 2); 1175 memcpy( a_wWhiteShading + m_ScanParam.Size.dwPhyPixels * 2, 1176 a_wWhiteShading, m_ScanParam.Size.dwPhyPixels * 2); 1177 } 1178 1179 if(usb_HostSwap()) 1180 usb_Swap(a_wWhiteShading, m_ScanParam.Size.dwPhyPixels * 2 * 3 ); 1181 1182 usb_SaveCalSetShading( dev, &m_ScanParam ); 1183 1184 usb_line_statistics( "White", a_wWhiteShading, m_ScanParam.Size.dwPhyPixels, 1185 scan->sParam.bDataType == SCANDATATYPE_Color?1:0); 1186 1187 DBG( _DBG_INFO, "cano_AdjustWhiteShading() done\n" ); 1188 return SANE_TRUE; 1189} 1190 1191/** the entry function for the CIS calibration stuff. 1192 */ 1193static int 1194cano_DoCalibration( Plustek_Device *dev ) 1195{ 1196 u_short dpi, idx, idx_end; 1197 u_long save_waf; 1198 SANE_Bool skip_fine; 1199 ScanDef *scan = &dev->scanning; 1200 HWDef *hw = &dev->usbDev.HwSetting; 1201 DCapsDef *scaps = &dev->usbDev.Caps; 1202 1203 if( SANE_TRUE == scan->fCalibrated ) 1204 return SANE_TRUE; 1205 1206 DBG( _DBG_INFO, "cano_DoCalibration()\n" ); 1207 1208 if( _IS_PLUSTEKMOTOR(hw->motorModel)){ 1209 DBG( _DBG_ERROR, "altCalibration can't work with this " 1210 "Plustek motor control setup\n" ); 1211 return SANE_FALSE; /* can't cal this */ 1212 } 1213 1214 /* Don't allow calibration settings from the other driver to confuse our 1215 * use of a few of its functions. 1216 */ 1217 save_waf = scaps->workaroundFlag; 1218 scaps->workaroundFlag &= ~_WAF_SKIP_WHITEFINE; 1219 scaps->workaroundFlag &= ~_WAF_SKIP_FINE; 1220 scaps->workaroundFlag &= ~_WAF_BYPASS_CALIBRATION; 1221 1222 if( !dev->adj.cacheCalData && !usb_IsSheetFedDevice(dev)) 1223 usb_SpeedTest( dev ); 1224 1225 /* here we handle that warmup stuff for CCD devices */ 1226 if( !usb_AutoWarmup( dev )) 1227 return SANE_FALSE; 1228 1229 /* Set the shading position to undefined */ 1230 strip_state = 0; 1231 usb_PrepareCalibration( dev ); 1232 1233 usb_SetMCLK( dev, &scan->sParam ); 1234 1235 if( !scan->skipCoarseCalib ) { 1236 1237 if( !usb_Wait4ScanSample( dev )) 1238 return SANE_FALSE; 1239 1240 DBG( _DBG_INFO2, "###### ADJUST LAMP (COARSE)#######\n" ); 1241 if( cano_PrepareToReadWhiteCal(dev, SANE_TRUE)) 1242 return SANE_FALSE; 1243 1244 dev->usbDev.a_bRegs[0x45] &= ~0x10; 1245 if( !cano_AdjustLightsource(dev)) { 1246 DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" ); 1247 return SANE_FALSE; 1248 } 1249 1250 DBG( _DBG_INFO2, "###### ADJUST OFFSET (COARSE) ####\n" ); 1251 if(cano_PrepareToReadBlackCal(dev)) 1252 return SANE_FALSE; 1253 1254 if( !cano_AdjustOffset(dev)) { 1255 DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" ); 1256 return SANE_FALSE; 1257 } 1258 1259 DBG( _DBG_INFO2, "###### ADJUST GAIN (COARSE)#######\n" ); 1260 if(cano_PrepareToReadWhiteCal(dev, SANE_FALSE)) 1261 return SANE_FALSE; 1262 1263 if( !cano_AdjustGain(dev)) { 1264 DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" ); 1265 return SANE_FALSE; 1266 } 1267 } else { 1268 strip_state = 1; 1269 DBG( _DBG_INFO2, "###### COARSE calibration skipped #######\n" ); 1270 } 1271 1272 skip_fine = SANE_FALSE; 1273 idx_end = 2; 1274 if( dev->adj.cacheCalData || usb_IsSheetFedDevice(dev)) { 1275 1276 skip_fine = usb_FineShadingFromFile(dev); 1277 1278 /* we recalibrate in any case ! */ 1279 if( usb_InCalibrationMode(dev)) { 1280 skip_fine = SANE_FALSE; 1281 idx_end = DIVIDER+1; 1282 1283 /* did I say any case? */ 1284 if (scan->sParam.bBitDepth != 8) { 1285 skip_fine = SANE_TRUE; 1286 DBG( _DBG_INFO2, "No fine calibration for non-8bit modes!\n" ); 1287 } 1288 1289 } else if( usb_IsSheetFedDevice(dev)) { 1290 1291 /* we only do the calibration upon request !*/ 1292 if( !skip_fine ) { 1293 DBG( _DBG_INFO2, "SHEET-FED device, skip fine calibration!\n" ); 1294 skip_fine = SANE_TRUE; 1295 scaps->workaroundFlag |= _WAF_BYPASS_CALIBRATION; 1296 } 1297 } 1298 } 1299 1300 if( !skip_fine ) { 1301 1302 for( idx = 1; idx < idx_end; idx++ ) { 1303 1304 dpi = 0; 1305 if( usb_InCalibrationMode(dev)) { 1306 dpi = usb_get_res( scaps->OpticDpi.x, idx ); 1307 1308 /* we might should check against device specific limit */ 1309 if(dpi < 50) 1310 continue; 1311 } 1312 1313 DBG( _DBG_INFO2, "###### ADJUST DARK (FINE) ########\n" ); 1314 if(cano_PrepareToReadBlackCal(dev)) 1315 return SANE_FALSE; 1316 1317 dev->usbDev.a_bRegs[0x45] |= 0x10; 1318 if( !cano_AdjustDarkShading(dev, dpi)) { 1319 DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" ); 1320 return SANE_FALSE; 1321 } 1322 1323 DBG( _DBG_INFO2, "###### ADJUST WHITE (FINE) #######\n" ); 1324 if(cano_PrepareToReadWhiteCal(dev, SANE_FALSE)) 1325 return SANE_FALSE; 1326 1327 if( !usb_IsSheetFedDevice(dev)) { 1328 if(!usb_ModuleToHome( dev, SANE_TRUE )) 1329 return SANE_FALSE; 1330 1331 if( !usb_ModuleMove(dev, MOVE_Forward, 1332 (u_long)dev->usbDev.pSource->ShadingOriginY)) { 1333 return SANE_FALSE; 1334 } 1335 } 1336 if( !cano_AdjustWhiteShading(dev, dpi)) { 1337 DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" ); 1338 return SANE_FALSE; 1339 } 1340 1341 /* force to go back */ 1342 strip_state = 0; 1343 } 1344 } else { 1345 DBG( _DBG_INFO2, "###### FINE calibration skipped #######\n" ); 1346 1347 dev->usbDev.a_bRegs[0x45] |= 0x10; 1348 strip_state = 2; 1349 1350 m_ScanParam = scan->sParam; 1351 usb_GetPhyPixels( dev, &m_ScanParam ); 1352 1353 usb_line_statistics( "Dark", a_wDarkShading, m_ScanParam.Size.dwPhyPixels, 1354 m_ScanParam.bDataType == SCANDATATYPE_Color?1:0); 1355 usb_line_statistics( "White", a_wWhiteShading, m_ScanParam.Size.dwPhyPixels, 1356 m_ScanParam.bDataType == SCANDATATYPE_Color?1:0); 1357 } 1358 1359 /* Lamp on if it's not */ 1360 cano_LampOnAfterCalibration(dev); 1361 strip_state = 0; 1362 1363 /* home the sensor after calibration 1364 */ 1365 if( !usb_IsSheetFedDevice(dev)) 1366 usb_ModuleToHome( dev, SANE_TRUE ); 1367 scan->fCalibrated = SANE_TRUE; 1368 1369 DBG( _DBG_INFO, "cano_DoCalibration() done\n" ); 1370 DBG( _DBG_INFO, "-------------------------\n" ); 1371 DBG( _DBG_INFO, "Static Gain:\n" ); 1372 DBG( _DBG_INFO, "REG[0x3b] = %u\n", dev->usbDev.a_bRegs[0x3b] ); 1373 DBG( _DBG_INFO, "REG[0x3c] = %u\n", dev->usbDev.a_bRegs[0x3c] ); 1374 DBG( _DBG_INFO, "REG[0x3d] = %u\n", dev->usbDev.a_bRegs[0x3d] ); 1375 DBG( _DBG_INFO, "Static Offset:\n" ); 1376 DBG( _DBG_INFO, "REG[0x38] = %i\n", dev->usbDev.a_bRegs[0x38] ); 1377 DBG( _DBG_INFO, "REG[0x39] = %i\n", dev->usbDev.a_bRegs[0x39] ); 1378 DBG( _DBG_INFO, "REG[0x3a] = %i\n", dev->usbDev.a_bRegs[0x3a] ); 1379 DBG( _DBG_INFO, "-------------------------\n" ); 1380 1381 scaps->workaroundFlag |= save_waf; 1382 1383 return SANE_TRUE; 1384} 1385 1386/* END PLUSTEK-USBCAL.C .....................................................*/ 1387