kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			2750 wiersze
		
	
	
		
			76 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			2750 wiersze
		
	
	
		
			76 KiB
		
	
	
	
		
			C
		
	
	
| /* sane - Scanner Access Now Easy.
 | |
| 
 | |
|    Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
 | |
|    AFE offset/gain setting by David Stevenson <david.stevenson@zoom.co.uk>
 | |
|    Copyright (C) 2002 - 2007 Henning Geinitz <sane@geinitz.org>
 | |
|    Copyright (C) 2009 Stéphane Voltz <stef.dev@free.fr> for sheetfed
 | |
|                       calibration code.
 | |
| 
 | |
|    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.
 | |
| */
 | |
| 
 | |
| #include "gt68xx_high.h"
 | |
| #include "gt68xx_mid.c"
 | |
| 
 | |
| #include <unistd.h>
 | |
| #include <math.h>
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_afe_ccd_auto (GT68xx_Scanner * scanner, GT68xx_Scan_Request * request);
 | |
| static SANE_Status gt68xx_afe_cis_auto (GT68xx_Scanner * scanner);
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_calibrator_new (SANE_Int width,
 | |
| 		       SANE_Int white_level, GT68xx_Calibrator ** cal_return)
 | |
| {
 | |
|   GT68xx_Calibrator *cal;
 | |
|   SANE_Int i;
 | |
| 
 | |
|   DBG (4, "gt68xx_calibrator_new: enter: width=%d, white_level=%d\n",
 | |
|        width, white_level);
 | |
| 
 | |
|   *cal_return = 0;
 | |
| 
 | |
|   if (width <= 0)
 | |
|     {
 | |
|       DBG (5, "gt68xx_calibrator_new: invalid width=%d\n", width);
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   cal = (GT68xx_Calibrator *) malloc (sizeof (GT68xx_Calibrator));
 | |
|   if (!cal)
 | |
|     {
 | |
|       DBG (5, "gt68xx_calibrator_new: no memory for GT68xx_Calibrator\n");
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
| 
 | |
|   cal->k_white = NULL;
 | |
|   cal->k_black = NULL;
 | |
|   cal->white_line = NULL;
 | |
|   cal->black_line = NULL;
 | |
|   cal->width = width;
 | |
|   cal->white_level = white_level;
 | |
|   cal->white_count = 0;
 | |
|   cal->black_count = 0;
 | |
| #ifdef TUNE_CALIBRATOR
 | |
|   cal->min_clip_count = cal->max_clip_count = 0;
 | |
| #endif /* TUNE_CALIBRATOR */
 | |
| 
 | |
|   cal->k_white = (unsigned int *) malloc (width * sizeof (unsigned int));
 | |
|   cal->k_black = (unsigned int *) malloc (width * sizeof (unsigned int));
 | |
|   cal->white_line = (double *) malloc (width * sizeof (double));
 | |
|   cal->black_line = (double *) malloc (width * sizeof (double));
 | |
| 
 | |
|   if (!cal->k_white || !cal->k_black | !cal->white_line || !cal->black_line)
 | |
|     {
 | |
|       DBG (5, "gt68xx_calibrator_new: no memory for calibration data\n");
 | |
|       gt68xx_calibrator_free (cal);
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
| 
 | |
|   for (i = 0; i < width; ++i)
 | |
|     {
 | |
|       cal->k_white[i] = 0;
 | |
|       cal->k_black[i] = 0;
 | |
|       cal->white_line[i] = 0.0;
 | |
|       cal->black_line[i] = 0.0;
 | |
|     }
 | |
| 
 | |
|   *cal_return = cal;
 | |
|   DBG (5, "gt68xx_calibrator_new: leave: ok\n");
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_calibrator_free (GT68xx_Calibrator * cal)
 | |
| {
 | |
|   DBG (5, "gt68xx_calibrator_free: enter\n");
 | |
| 
 | |
|   if (!cal)
 | |
|     {
 | |
|       DBG (5, "gt68xx_calibrator_free: cal==NULL\n");
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
| #ifdef TUNE_CALIBRATOR
 | |
|   DBG (4, "gt68xx_calibrator_free: min_clip_count=%d, max_clip_count=%d\n",
 | |
|        cal->min_clip_count, cal->max_clip_count);
 | |
| #endif /* TUNE_CALIBRATOR */
 | |
| 
 | |
|   if (cal->k_white)
 | |
|     {
 | |
|       free (cal->k_white);
 | |
|       cal->k_white = NULL;
 | |
|     }
 | |
| 
 | |
|   if (cal->k_black)
 | |
|     {
 | |
|       free (cal->k_black);
 | |
|       cal->k_black = NULL;
 | |
|     }
 | |
| 
 | |
|   if (cal->white_line)
 | |
|     {
 | |
|       free (cal->white_line);
 | |
|       cal->white_line = NULL;
 | |
|     }
 | |
| 
 | |
|   if (cal->black_line)
 | |
|     {
 | |
|       free (cal->black_line);
 | |
|       cal->black_line = NULL;
 | |
|     }
 | |
| 
 | |
|   free (cal);
 | |
| 
 | |
|   DBG (5, "gt68xx_calibrator_free: leave: ok\n");
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_calibrator_add_white_line (GT68xx_Calibrator * cal, unsigned int *line)
 | |
| {
 | |
|   SANE_Int i;
 | |
|   SANE_Int width = cal->width;
 | |
| 
 | |
|   SANE_Int sum = 0;
 | |
| 
 | |
|   cal->white_count++;
 | |
| 
 | |
|   for (i = 0; i < width; ++i)
 | |
|     {
 | |
|       cal->white_line[i] += line[i];
 | |
|       sum += line[i];
 | |
| #ifdef SAVE_WHITE_CALIBRATION
 | |
|       printf ("%c", line[i] >> 8);
 | |
| #endif
 | |
|     }
 | |
|   if (sum / width / 256 < 0x50)
 | |
|     DBG (1,
 | |
| 	 "gt68xx_calibrator_add_white_line: WARNING: dark calibration line: "
 | |
| 	 "%2d medium white: 0x%02x\n", cal->white_count - 1,
 | |
| 	 sum / width / 256);
 | |
|   else
 | |
|     DBG (5,
 | |
| 	 "gt68xx_calibrator_add_white_line: line: %2d medium white: 0x%02x\n",
 | |
| 	 cal->white_count - 1, sum / width / 256);
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_calibrator_eval_white (GT68xx_Calibrator * cal, double factor)
 | |
| {
 | |
|   SANE_Int i;
 | |
|   SANE_Int width = cal->width;
 | |
| 
 | |
|   for (i = 0; i < width; ++i)
 | |
|     {
 | |
|       cal->white_line[i] = cal->white_line[i] / cal->white_count * factor;
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_calibrator_add_black_line (GT68xx_Calibrator * cal, unsigned int *line)
 | |
| {
 | |
|   SANE_Int i;
 | |
|   SANE_Int width = cal->width;
 | |
| 
 | |
|   SANE_Int sum = 0;
 | |
| 
 | |
|   cal->black_count++;
 | |
| 
 | |
|   for (i = 0; i < width; ++i)
 | |
|     {
 | |
|       cal->black_line[i] += line[i];
 | |
|       sum += line[i];
 | |
| #ifdef SAVE_BLACK_CALIBRATION
 | |
|       printf ("%c", line[i] >> 8);
 | |
| #endif
 | |
|     }
 | |
| 
 | |
|   DBG (5,
 | |
|        "gt68xx_calibrator_add_black_line: line: %2d medium black: 0x%02x\n",
 | |
|        cal->black_count - 1, sum / width / 256);
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_calibrator_eval_black (GT68xx_Calibrator * cal, double factor)
 | |
| {
 | |
|   SANE_Int i;
 | |
|   SANE_Int width = cal->width;
 | |
| 
 | |
|   for (i = 0; i < width; ++i)
 | |
|     {
 | |
|       cal->black_line[i] = cal->black_line[i] / cal->black_count - factor;
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_calibrator_finish_setup (GT68xx_Calibrator * cal)
 | |
| {
 | |
| #ifdef TUNE_CALIBRATOR
 | |
|   double ave_black = 0.0;
 | |
|   double ave_diff = 0.0;
 | |
| #endif /* TUNE_CALIBRATOR */
 | |
|   int i;
 | |
|   int width = cal->width;
 | |
|   unsigned int max_value = 65535;
 | |
| 
 | |
|   for (i = 0; i < width; ++i)
 | |
|     {
 | |
|       unsigned int white = cal->white_line[i];
 | |
|       unsigned int black = cal->black_line[i];
 | |
|       unsigned int diff = (white > black) ? white - black : 1;
 | |
|       if (diff > max_value)
 | |
| 	diff = max_value;
 | |
|       cal->k_white[i] = diff;
 | |
|       cal->k_black[i] = black;
 | |
| #ifdef TUNE_CALIBRATOR
 | |
|       ave_black += black;
 | |
|       ave_diff += diff;
 | |
| #endif /* TUNE_CALIBRATOR */
 | |
|     }
 | |
| 
 | |
| #ifdef TUNE_CALIBRATOR
 | |
|   ave_black /= width;
 | |
|   ave_diff /= width;
 | |
|   DBG (4, "gt68xx_calibrator_finish_setup: ave_black=%f, ave_diff=%f\n",
 | |
|        ave_black, ave_diff);
 | |
| #endif /* TUNE_CALIBRATOR */
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_calibrator_process_line (GT68xx_Calibrator * cal, unsigned int *line)
 | |
| {
 | |
|   int i;
 | |
|   int width = cal->width;
 | |
|   unsigned int white_level = cal->white_level;
 | |
| 
 | |
|   for (i = 0; i < width; ++i)
 | |
|     {
 | |
|       unsigned int src_value = line[i];
 | |
|       unsigned int black = cal->k_black[i];
 | |
|       unsigned int value;
 | |
| 
 | |
|       if (src_value > black)
 | |
| 	{
 | |
| 	  value = (src_value - black) * white_level / cal->k_white[i];
 | |
| 	  if (value > 0xffff)
 | |
| 	    {
 | |
| 	      value = 0xffff;
 | |
| #ifdef TUNE_CALIBRATOR
 | |
| 	      cal->max_clip_count++;
 | |
| #endif /* TUNE_CALIBRATOR */
 | |
| 	    }
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  value = 0;
 | |
| #ifdef TUNE_CALIBRATOR
 | |
| 	  if (src_value < black)
 | |
| 	    cal->min_clip_count++;
 | |
| #endif /* TUNE_CALIBRATOR */
 | |
| 	}
 | |
| 
 | |
|       line[i] = value;
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_scanner_new (GT68xx_Device * dev, GT68xx_Scanner ** scanner_return)
 | |
| {
 | |
|   GT68xx_Scanner *scanner;
 | |
|   int i;
 | |
| 
 | |
|   *scanner_return = NULL;
 | |
| 
 | |
|   scanner = (GT68xx_Scanner *) malloc (sizeof (GT68xx_Scanner));
 | |
|   if (!scanner)
 | |
|     {
 | |
|       DBG (5, "gt68xx_scanner_new: no memory for GT68xx_Scanner\n");
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
| 
 | |
|   scanner->dev = dev;
 | |
|   scanner->reader = NULL;
 | |
|   scanner->cal_gray = NULL;
 | |
|   scanner->cal_r = NULL;
 | |
|   scanner->cal_g = NULL;
 | |
|   scanner->cal_b = NULL;
 | |
| 
 | |
|   for(i=0;i<MAX_RESOLUTIONS;i++)
 | |
|     {
 | |
|       scanner->calibrations[i].dpi = 0;
 | |
|       scanner->calibrations[i].red = NULL;
 | |
|       scanner->calibrations[i].green = NULL;
 | |
|       scanner->calibrations[i].blue = NULL;
 | |
|       scanner->calibrations[i].gray = NULL;
 | |
|     }
 | |
| 
 | |
|   *scanner_return = scanner;
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static void
 | |
| gt68xx_scanner_free_calibrators (GT68xx_Scanner * scanner)
 | |
| {
 | |
|   if (scanner->cal_gray)
 | |
|     {
 | |
|       gt68xx_calibrator_free (scanner->cal_gray);
 | |
|       scanner->cal_gray = NULL;
 | |
|     }
 | |
| 
 | |
|   if (scanner->cal_r)
 | |
|     {
 | |
|       gt68xx_calibrator_free (scanner->cal_r);
 | |
|       scanner->cal_r = NULL;
 | |
|     }
 | |
| 
 | |
|   if (scanner->cal_g)
 | |
|     {
 | |
|       gt68xx_calibrator_free (scanner->cal_g);
 | |
|       scanner->cal_g = NULL;
 | |
|     }
 | |
| 
 | |
|   if (scanner->cal_b)
 | |
|     {
 | |
|       gt68xx_calibrator_free (scanner->cal_b);
 | |
|       scanner->cal_b = NULL;
 | |
|     }
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_scanner_free (GT68xx_Scanner * scanner)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   if (!scanner)
 | |
|     {
 | |
|       DBG (5, "gt68xx_scanner_free: scanner==NULL\n");
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   if (scanner->reader)
 | |
|     {
 | |
|       gt68xx_line_reader_free (scanner->reader);
 | |
|       scanner->reader = NULL;
 | |
|     }
 | |
| 
 | |
|   gt68xx_scanner_free_calibrators (scanner);
 | |
| 
 | |
|   /* free in memory calibration data */
 | |
|   for (i = 0; i < MAX_RESOLUTIONS; i++)
 | |
|     {
 | |
|       scanner->calibrations[i].dpi = 0;
 | |
|       if (scanner->calibrations[i].red != NULL)
 | |
| 	{
 | |
| 	  gt68xx_calibrator_free (scanner->calibrations[i].red);
 | |
| 	}
 | |
|       if (scanner->calibrations[i].green != NULL)
 | |
| 	{
 | |
| 	  gt68xx_calibrator_free (scanner->calibrations[i].green);
 | |
| 	}
 | |
|       if (scanner->calibrations[i].blue != NULL)
 | |
| 	{
 | |
| 	  gt68xx_calibrator_free (scanner->calibrations[i].blue);
 | |
| 	}
 | |
|       if (scanner->calibrations[i].gray != NULL)
 | |
| 	{
 | |
| 	  gt68xx_calibrator_free (scanner->calibrations[i].gray);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   free (scanner);
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_scanner_wait_for_positioning (GT68xx_Scanner * scanner)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   SANE_Bool moving;
 | |
|   SANE_Int status_count = 0;
 | |
| 
 | |
|   usleep (100000);		/* needed by the BP 2400 CU Plus? */
 | |
|   while (SANE_TRUE)
 | |
|     {
 | |
|       status = gt68xx_device_is_moving (scanner->dev, &moving);
 | |
|       if (status == SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  if (!moving)
 | |
| 	    break;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  if (status_count > 9)
 | |
| 	    {
 | |
| 	      DBG (1, "gt68xx_scanner_wait_for_positioning: error count too high!\n");
 | |
| 	      return status;
 | |
| 	    }
 | |
| 	  status_count++;
 | |
| 	  DBG(3, "gt68xx_scanner_wait_for_positioning: ignored error\n");
 | |
| 	}
 | |
|       usleep (100000);
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_scanner_internal_start_scan (GT68xx_Scanner * scanner)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   SANE_Bool ready;
 | |
|   SANE_Int repeat_count;
 | |
| 
 | |
|   status = gt68xx_scanner_wait_for_positioning (scanner);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_internal_start_scan: gt68xx_scanner_wait_for_positioning error: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   status = gt68xx_device_start_scan (scanner->dev);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_internal_start_scan: gt68xx_device_start_scan error: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   for (repeat_count = 0; repeat_count < 30 * 100; ++repeat_count)
 | |
|     {
 | |
|       status = gt68xx_device_read_scanned_data (scanner->dev, &ready);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (5,
 | |
| 	       "gt68xx_scanner_internal_start_scan: gt68xx_device_read_scanned_data error: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
|       if (ready)
 | |
| 	break;
 | |
|       usleep (10000);
 | |
|     }
 | |
|   if (!ready)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_internal_start_scan: scanner still not ready - giving up\n");
 | |
|       return SANE_STATUS_DEVICE_BUSY;
 | |
|     }
 | |
| 
 | |
|   status = gt68xx_device_read_start (scanner->dev);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_internal_start_scan: gt68xx_device_read_start error: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_scanner_start_scan_extended (GT68xx_Scanner * scanner,
 | |
| 				    GT68xx_Scan_Request * request,
 | |
| 				    GT68xx_Scan_Action action,
 | |
| 				    GT68xx_Scan_Parameters * params)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   GT68xx_AFE_Parameters afe = *scanner->dev->afe;
 | |
| 
 | |
|   status = gt68xx_scanner_wait_for_positioning (scanner);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_start_scan_extended: gt68xx_scanner_wait_for_positioning error: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   status = gt68xx_device_setup_scan (scanner->dev, request, action, params);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_start_scan_extended: gt68xx_device_setup_scan failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   status = gt68xx_line_reader_new (scanner->dev, params,
 | |
| 				   action == SA_SCAN ? SANE_TRUE : SANE_FALSE,
 | |
| 				   &scanner->reader);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_start_scan_extended: gt68xx_line_reader_new failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   if (scanner->dev->model->is_cis
 | |
|       && !((scanner->dev->model->flags & GT68XX_FLAG_SHEET_FED) && scanner->calibrated == SANE_FALSE))
 | |
|     {
 | |
|       status =
 | |
| 	gt68xx_device_set_exposure_time (scanner->dev,
 | |
| 					 scanner->dev->exposure);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (5,
 | |
| 	       "gt68xx_scanner_start_scan_extended: gt68xx_device_set_exposure_time failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   status = gt68xx_device_set_afe (scanner->dev, &afe);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_start_scan_extended: gt68xx_device_set_afe failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   status = gt68xx_scanner_internal_start_scan (scanner);
 | |
| 
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_start_scan_extended: gt68xx_scanner_internal_start_scan failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_scanner_create_calibrator (GT68xx_Scan_Parameters * params,
 | |
| 				  GT68xx_Calibrator ** cal_return)
 | |
| {
 | |
|   return gt68xx_calibrator_new (params->pixel_xs, 65535, cal_return);
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_scanner_create_color_calibrators (GT68xx_Scanner * scanner,
 | |
| 					 GT68xx_Scan_Parameters * params)
 | |
| {
 | |
|   SANE_Status status;
 | |
| 
 | |
|   if (!scanner->cal_r)
 | |
|     {
 | |
|       status = gt68xx_scanner_create_calibrator (params, &scanner->cal_r);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	return status;
 | |
|     }
 | |
|   if (!scanner->cal_g)
 | |
|     {
 | |
|       status = gt68xx_scanner_create_calibrator (params, &scanner->cal_g);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	return status;
 | |
|     }
 | |
|   if (!scanner->cal_b)
 | |
|     {
 | |
|       status = gt68xx_scanner_create_calibrator (params, &scanner->cal_b);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	return status;
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_scanner_create_gray_calibrators (GT68xx_Scanner * scanner,
 | |
| 					GT68xx_Scan_Parameters * params)
 | |
| {
 | |
|   SANE_Status status;
 | |
| 
 | |
|   if (!scanner->cal_gray)
 | |
|     {
 | |
|       status = gt68xx_scanner_create_calibrator (params, &scanner->cal_gray);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	return status;
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_scanner_calibrate_color_white_line (GT68xx_Scanner * scanner,
 | |
| 					   unsigned int **buffer_pointers)
 | |
| {
 | |
| 
 | |
|   gt68xx_calibrator_add_white_line (scanner->cal_r, buffer_pointers[0]);
 | |
|   gt68xx_calibrator_add_white_line (scanner->cal_g, buffer_pointers[1]);
 | |
|   gt68xx_calibrator_add_white_line (scanner->cal_b, buffer_pointers[2]);
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_scanner_calibrate_gray_white_line (GT68xx_Scanner * scanner,
 | |
| 					  unsigned int **buffer_pointers)
 | |
| {
 | |
|   gt68xx_calibrator_add_white_line (scanner->cal_gray, buffer_pointers[0]);
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_scanner_calibrate_color_black_line (GT68xx_Scanner * scanner,
 | |
| 					   unsigned int **buffer_pointers)
 | |
| {
 | |
|   gt68xx_calibrator_add_black_line (scanner->cal_r, buffer_pointers[0]);
 | |
|   gt68xx_calibrator_add_black_line (scanner->cal_g, buffer_pointers[1]);
 | |
|   gt68xx_calibrator_add_black_line (scanner->cal_b, buffer_pointers[2]);
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_scanner_calibrate_gray_black_line (GT68xx_Scanner * scanner,
 | |
| 					  unsigned int **buffer_pointers)
 | |
| {
 | |
|   gt68xx_calibrator_add_black_line (scanner->cal_gray, buffer_pointers[0]);
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_scanner_calibrate (GT68xx_Scanner * scanner,
 | |
| 			  GT68xx_Scan_Request * request)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   GT68xx_Scan_Parameters params;
 | |
|   GT68xx_Scan_Request req;
 | |
|   SANE_Int i;
 | |
|   unsigned int *buffer_pointers[3];
 | |
|   GT68xx_AFE_Parameters *afe = scanner->dev->afe;
 | |
|   GT68xx_Exposure_Parameters *exposure = scanner->dev->exposure;
 | |
| 
 | |
|   memcpy (&req, request, sizeof (req));
 | |
| 
 | |
|   gt68xx_scanner_free_calibrators (scanner);
 | |
| 
 | |
|   if (scanner->auto_afe)
 | |
|     {
 | |
|       if (scanner->dev->model->is_cis)
 | |
| 	status = gt68xx_afe_cis_auto (scanner);
 | |
|       else
 | |
| 	status = gt68xx_afe_ccd_auto (scanner, request);
 | |
| 
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (5, "gt68xx_scanner_calibrate: gt68xx_afe_*_auto failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
|       req.mbs = SANE_FALSE;
 | |
|     }
 | |
|   else
 | |
|     req.mbs = SANE_TRUE;
 | |
| 
 | |
|   DBG (3, "afe 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", afe->r_offset,
 | |
|        afe->r_pga, afe->g_offset, afe->g_pga, afe->b_offset, afe->b_pga);
 | |
|   DBG (3, "exposure 0x%02x 0x%02x 0x%02x\n", exposure->r_time,
 | |
|        exposure->g_time, exposure->b_time);
 | |
| 
 | |
|   if (!scanner->calib)
 | |
|     return SANE_STATUS_GOOD;
 | |
| 
 | |
|   req.mds = SANE_TRUE;
 | |
|   req.mas = SANE_FALSE;
 | |
| 
 | |
|   if (scanner->dev->model->is_cis && !(scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP))
 | |
|     req.color = SANE_TRUE;
 | |
| 
 | |
|   if (req.use_ta)
 | |
|     {
 | |
|       req.lamp = SANE_FALSE;
 | |
|       status =
 | |
| 	gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       req.lamp = SANE_TRUE;
 | |
|       status =
 | |
| 	gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE);
 | |
|     }
 | |
| 
 | |
|   status = gt68xx_scanner_start_scan_extended (scanner, &req, SA_CALIBRATE,
 | |
| 					       ¶ms);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_calibrate: gt68xx_scanner_start_scan_extended failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   if (params.color)
 | |
|     {
 | |
|       status = gt68xx_scanner_create_color_calibrators (scanner, ¶ms);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       status = gt68xx_scanner_create_gray_calibrators (scanner, ¶ms);
 | |
|     }
 | |
| 
 | |
| #if defined(SAVE_WHITE_CALIBRATION) || defined(SAVE_BLACK_CALIBRATION)
 | |
|   printf ("P5\n%d %d\n255\n", params.pixel_xs, params.pixel_ys * 3);
 | |
| #endif
 | |
|   for (i = 0; i < params.pixel_ys; ++i)
 | |
|     {
 | |
|       status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (5,
 | |
| 	       "gt68xx_scanner_calibrate: gt68xx_line_reader_read failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       if (params.color)
 | |
| 	status = gt68xx_scanner_calibrate_color_white_line (scanner,
 | |
| 							    buffer_pointers);
 | |
|       else
 | |
| 	status = gt68xx_scanner_calibrate_gray_white_line (scanner,
 | |
| 							   buffer_pointers);
 | |
| 
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (5, "gt68xx_scanner_calibrate: calibration failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
|     }
 | |
|   gt68xx_scanner_stop_scan (scanner);
 | |
| 
 | |
|   if (params.color)
 | |
|     {
 | |
|       gt68xx_calibrator_eval_white (scanner->cal_r, 1);
 | |
|       gt68xx_calibrator_eval_white (scanner->cal_g, 1);
 | |
|       gt68xx_calibrator_eval_white (scanner->cal_b, 1);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       gt68xx_calibrator_eval_white (scanner->cal_gray, 1);
 | |
|     }
 | |
| 
 | |
|   req.mbs = SANE_FALSE;
 | |
|   req.mds = SANE_FALSE;
 | |
|   req.mas = SANE_FALSE;
 | |
|   req.lamp = SANE_FALSE;
 | |
| 
 | |
|   status = gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_FALSE);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_calibrate: gt68xx_device_lamp_control failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   if (!scanner->dev->model->is_cis
 | |
|       || (scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP))
 | |
|     usleep (500000);
 | |
|   status = gt68xx_scanner_start_scan_extended (scanner, &req, SA_CALIBRATE,
 | |
| 					       ¶ms);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_calibrate: gt68xx_scanner_start_scan_extended failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   for (i = 0; i < params.pixel_ys; ++i)
 | |
|     {
 | |
|       status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (5,
 | |
| 	       "gt68xx_scanner_calibrate: gt68xx_line_reader_read failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       if (params.color)
 | |
| 	status = gt68xx_scanner_calibrate_color_black_line (scanner,
 | |
| 							    buffer_pointers);
 | |
|       else
 | |
| 	status = gt68xx_scanner_calibrate_gray_black_line (scanner,
 | |
| 							   buffer_pointers);
 | |
| 
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (5, "gt68xx_scanner_calibrate: calibration failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
|     }
 | |
|   gt68xx_scanner_stop_scan (scanner);
 | |
| 
 | |
|   if (req.use_ta)
 | |
|     status = gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE);
 | |
|   else
 | |
|     status = gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE);
 | |
| 
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_calibrate: gt68xx_device_lamp_control failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   if (!scanner->dev->model->is_cis)
 | |
|     usleep (500000);
 | |
| 
 | |
|   if (params.color)
 | |
|     {
 | |
|       gt68xx_calibrator_eval_black (scanner->cal_r, 0.0);
 | |
|       gt68xx_calibrator_eval_black (scanner->cal_g, 0.0);
 | |
|       gt68xx_calibrator_eval_black (scanner->cal_b, 0.0);
 | |
| 
 | |
|       gt68xx_calibrator_finish_setup (scanner->cal_r);
 | |
|       gt68xx_calibrator_finish_setup (scanner->cal_g);
 | |
|       gt68xx_calibrator_finish_setup (scanner->cal_b);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       gt68xx_calibrator_eval_black (scanner->cal_gray, 0.0);
 | |
|       gt68xx_calibrator_finish_setup (scanner->cal_gray);
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_scanner_start_scan (GT68xx_Scanner * scanner,
 | |
| 			   GT68xx_Scan_Request * request,
 | |
| 			   GT68xx_Scan_Parameters * params)
 | |
| {
 | |
|   request->mbs = SANE_FALSE;	/* don't go home before real scan */
 | |
|   request->mds = SANE_TRUE;
 | |
|   request->mas = SANE_FALSE;
 | |
|   if (request->use_ta)
 | |
|     {
 | |
|       gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE);
 | |
|       request->lamp = SANE_FALSE;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE);
 | |
|       request->lamp = SANE_TRUE;
 | |
|     }
 | |
|   if (!scanner->dev->model->is_cis)
 | |
|     sleep (2);
 | |
| 
 | |
|   return gt68xx_scanner_start_scan_extended (scanner, request, SA_SCAN,
 | |
| 					     params);
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_scanner_read_line (GT68xx_Scanner * scanner,
 | |
| 			  unsigned int **buffer_pointers)
 | |
| {
 | |
|   SANE_Status status;
 | |
| 
 | |
|   status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
 | |
| 
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_scanner_read_line: gt68xx_line_reader_read failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   if (scanner->calib)
 | |
|     {
 | |
|       if (scanner->reader->params.color)
 | |
| 	{
 | |
| 	  gt68xx_calibrator_process_line (scanner->cal_r, buffer_pointers[0]);
 | |
| 	  gt68xx_calibrator_process_line (scanner->cal_g, buffer_pointers[1]);
 | |
| 	  gt68xx_calibrator_process_line (scanner->cal_b, buffer_pointers[2]);
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  if (scanner->dev->model->is_cis && !(scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP))
 | |
| 	    {
 | |
| 	      if (strcmp
 | |
| 		  (scanner->val[OPT_GRAY_MODE_COLOR].s,
 | |
| 		   GT68XX_COLOR_BLUE) == 0)
 | |
| 		gt68xx_calibrator_process_line (scanner->cal_b,
 | |
| 						buffer_pointers[0]);
 | |
| 	      else
 | |
| 		if (strcmp
 | |
| 		    (scanner->val[OPT_GRAY_MODE_COLOR].s,
 | |
| 		     GT68XX_COLOR_GREEN) == 0)
 | |
| 		gt68xx_calibrator_process_line (scanner->cal_g,
 | |
| 						buffer_pointers[0]);
 | |
| 	      else
 | |
| 		gt68xx_calibrator_process_line (scanner->cal_r,
 | |
| 						buffer_pointers[0]);
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      gt68xx_calibrator_process_line (scanner->cal_gray,
 | |
| 					      buffer_pointers[0]);
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| gt68xx_scanner_stop_scan (GT68xx_Scanner * scanner)
 | |
| {
 | |
|   gt68xx_line_reader_free (scanner->reader);
 | |
|   scanner->reader = NULL;
 | |
| 
 | |
|   return gt68xx_device_stop_scan (scanner->dev);
 | |
| }
 | |
| 
 | |
| /************************************************************************/
 | |
| /*                                                                      */
 | |
| /* AFE offset/gain automatic configuration                              */
 | |
| /*                                                                      */
 | |
| /************************************************************************/
 | |
| 
 | |
| typedef struct GT68xx_Afe_Values GT68xx_Afe_Values;
 | |
| 
 | |
| struct GT68xx_Afe_Values
 | |
| {
 | |
|   SANE_Int black;		/* minimum black (0-255) */
 | |
|   SANE_Int white;		/* maximum white (0-255) */
 | |
|   SANE_Int total_white;		/* average white of the complete line (0-65536) */
 | |
|   SANE_Int calwidth;
 | |
|   SANE_Int callines;
 | |
|   SANE_Int max_width;
 | |
|   SANE_Int scan_dpi;
 | |
|   SANE_Fixed start_black;
 | |
|   SANE_Int offset_direction;
 | |
|   SANE_Int coarse_black;
 | |
|   SANE_Int coarse_white;
 | |
| };
 | |
| 
 | |
| 
 | |
| /************************************************************************/
 | |
| /* CCD scanners                                                         */
 | |
| /************************************************************************/
 | |
| 
 | |
| /** Calculate average black and maximum white
 | |
|  *
 | |
|  * This function is used for CCD scanners. The black mark to the left ist used
 | |
|  * for the calculation of average black. The remaining calibration strip
 | |
|  * is used for searching the segment whose white average is the highest.
 | |
|  *
 | |
|  * @param values AFE values
 | |
|  * @param buffer scanned line
 | |
|  */
 | |
| static void
 | |
| gt68xx_afe_ccd_calc (GT68xx_Afe_Values * values, unsigned int *buffer)
 | |
| {
 | |
|   SANE_Int start_black;
 | |
|   SANE_Int end_black;
 | |
|   SANE_Int start_white;
 | |
|   SANE_Int end_white;
 | |
|   SANE_Int i;
 | |
|   SANE_Int max_black = 0;
 | |
|   SANE_Int min_black = 255;
 | |
|   SANE_Int max_white = 0;
 | |
|   SANE_Int total_white = 0;
 | |
| 
 | |
|   /* set size of black mark and white segments */
 | |
|   start_black =
 | |
|     SANE_UNFIX (values->start_black) * values->scan_dpi / MM_PER_INCH;
 | |
|   end_black = start_black + 1.0 * values->scan_dpi / MM_PER_INCH;	/* 1 mm */
 | |
| 
 | |
|   /* 5mm after mark */
 | |
|   start_white = end_black + 5.0 * values->scan_dpi / MM_PER_INCH;
 | |
|   end_white = values->calwidth;
 | |
| 
 | |
|   DBG (5,
 | |
|        "gt68xx_afe_ccd_calc: dpi=%d, start_black=%d, end_black=%d, start_white=%d, end_white=%d\n",
 | |
|        values->scan_dpi, start_black, end_black, start_white, end_white);
 | |
| 
 | |
|   /* calc min and max black value */
 | |
|   for (i = start_black; i < end_black; i++)
 | |
|     {
 | |
|       if ((SANE_Int) (buffer[i] >> 8) < min_black)
 | |
| 	min_black = (buffer[i] >> 8);
 | |
|       if ((SANE_Int) (buffer[i] >> 8) > max_black)
 | |
| 	max_black = (buffer[i] >> 8);
 | |
| #ifdef DEBUG_BLACK
 | |
|       if ((buffer[i] >> 8) > 15)
 | |
| 	fprintf (stderr, "+");
 | |
|       else if ((buffer[i] >> 8) < 5)
 | |
| 	fprintf (stderr, "-");
 | |
|       else
 | |
| 	fprintf (stderr, "_");
 | |
| #endif
 | |
|     }
 | |
| #ifdef DEBUG_BLACK
 | |
|   fprintf (stderr, "\n");
 | |
| #endif
 | |
| 
 | |
|   for (i = start_white; i < end_white; ++i)
 | |
|     {
 | |
|       if ((SANE_Int) (buffer[i] >> 8) > max_white)
 | |
| 	max_white = (buffer[i] >> 8);
 | |
|       total_white += buffer[i];
 | |
|     }
 | |
|   values->total_white = total_white / (end_white - start_white);
 | |
| 
 | |
|   values->black = min_black;
 | |
|   values->white = max_white;
 | |
|   if (values->white < 50 || values->black > 150
 | |
|       || values->white - values->black < 30 || max_black - min_black > 15)
 | |
|     DBG (1,
 | |
| 	 "gt68xx_afe_ccd_calc: WARNING: max_white %3d   min_black %3d  max_black %3d\n",
 | |
| 	 values->white, values->black, max_black);
 | |
|   else
 | |
|     DBG (5,
 | |
| 	 "gt68xx_afe_ccd_calc: max_white %3d   min_black %3d  max_black %3d\n",
 | |
| 	 values->white, values->black, max_black);
 | |
| }
 | |
| 
 | |
| static SANE_Bool
 | |
| gt68xx_afe_ccd_adjust_offset_gain (SANE_String_Const color_name,
 | |
| 				   GT68xx_Afe_Values * values,
 | |
| 				   unsigned int *buffer, SANE_Byte * offset,
 | |
| 				   SANE_Byte * pga, SANE_Byte * old_offset,
 | |
| 				   SANE_Byte * old_pga)
 | |
| {
 | |
|   SANE_Int black_low = values->coarse_black, black_high = black_low + 10;
 | |
|   SANE_Int white_high = values->coarse_white, white_low = white_high - 10;
 | |
|   SANE_Bool done = SANE_TRUE;
 | |
|   SANE_Byte local_pga = *pga;
 | |
|   SANE_Byte local_offset = *offset;
 | |
| 
 | |
|   gt68xx_afe_ccd_calc (values, buffer);
 | |
| 
 | |
| #if 0
 | |
|   /* test all offset values */
 | |
|   local_offset++;
 | |
|   done = SANE_FALSE;
 | |
|   goto finish;
 | |
| #endif
 | |
| 
 | |
|   if (values->white > white_high)
 | |
|     {
 | |
|       if (values->black > black_high)
 | |
| 	local_offset += values->offset_direction;
 | |
|       else if (values->black < black_low)
 | |
| 	local_pga--;
 | |
|       else
 | |
| 	{
 | |
| 	  local_offset += values->offset_direction;
 | |
| 	  local_pga--;
 | |
| 	}
 | |
|       done = SANE_FALSE;
 | |
|       goto finish;
 | |
|     }
 | |
|   else if (values->white < white_low)
 | |
|     {
 | |
|       if (values->black < black_low)
 | |
| 	local_offset -= values->offset_direction;
 | |
|       else if (values->black > black_high)
 | |
| 	local_pga++;
 | |
|       else
 | |
| 	{
 | |
| 	  local_offset -= values->offset_direction;
 | |
| 	  local_pga++;
 | |
| 	}
 | |
|       done = SANE_FALSE;
 | |
|       goto finish;
 | |
|     }
 | |
|   if (values->black > black_high)
 | |
|     {
 | |
|       if (values->white > white_high)
 | |
| 	local_offset += values->offset_direction;
 | |
|       else if (values->white < white_low)
 | |
| 	local_pga++;
 | |
|       else
 | |
| 	{
 | |
| 	  local_offset += values->offset_direction;
 | |
| 	  local_pga++;
 | |
| 	}
 | |
|       done = SANE_FALSE;
 | |
|       goto finish;
 | |
|     }
 | |
|   else if (values->black < black_low)
 | |
|     {
 | |
|       if (values->white < white_low)
 | |
| 	local_offset -= values->offset_direction;
 | |
|       else if (values->white > white_high)
 | |
| 	local_pga--;
 | |
|       else
 | |
| 	{
 | |
| 	  local_offset -= values->offset_direction;
 | |
| 	  local_pga--;
 | |
| 	}
 | |
|       done = SANE_FALSE;
 | |
|       goto finish;
 | |
|     }
 | |
| finish:
 | |
|   if ((local_pga == *pga) && (local_offset == *offset))
 | |
|     done = SANE_TRUE;
 | |
|   if ((local_pga == *old_pga) && (local_offset == *old_offset))
 | |
|     done = SANE_TRUE;
 | |
| 
 | |
|   *old_pga = *pga;
 | |
|   *old_offset = *offset;
 | |
| 
 | |
|   DBG (4, "%5s white=%3d, black=%3d, offset=%2d, gain=%2d, old offs=%2d, "
 | |
|        "old gain=%2d, total_white=%5d %s\n", color_name, values->white,
 | |
|        values->black, local_offset, local_pga, *offset, *pga,
 | |
|        values->total_white, done ? "DONE " : "");
 | |
| 
 | |
|   *pga = local_pga;
 | |
|   *offset = local_offset;
 | |
| 
 | |
|   return done;
 | |
| }
 | |
| 
 | |
| /* Wait for lamp to give stable brightness */
 | |
| static SANE_Status
 | |
| gt68xx_wait_lamp_stable (GT68xx_Scanner * scanner,
 | |
| 			 GT68xx_Scan_Parameters * params,
 | |
| 			 GT68xx_Scan_Request *request,
 | |
| 			 unsigned int *buffer_pointers[3],
 | |
| 			 GT68xx_Afe_Values *values,
 | |
| 			 SANE_Bool dont_move)
 | |
| {
 | |
|   SANE_Status status = SANE_STATUS_GOOD;
 | |
|   SANE_Int last_white = 0;
 | |
|   SANE_Bool first = SANE_TRUE;
 | |
|   SANE_Bool message_printed = SANE_FALSE;
 | |
|   struct timeval now, start_time;
 | |
|   int secs_lamp_on, secs_start;
 | |
|   int increase = -5;
 | |
| 
 | |
|   gettimeofday (&start_time, 0);
 | |
|   do
 | |
|     {
 | |
|       usleep (200000);
 | |
| 
 | |
|       if (!first && dont_move)
 | |
| 	{
 | |
| 	  request->mbs = SANE_FALSE;
 | |
| 	  request->mds = SANE_FALSE;
 | |
| 	}
 | |
|       first = SANE_FALSE;
 | |
| 
 | |
|       /* read line */
 | |
|       status = gt68xx_scanner_start_scan_extended (scanner, request,
 | |
| 						   SA_CALIBRATE_ONE_LINE,
 | |
| 						   params);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (3,
 | |
| 	       "gt68xx_wait_lamp_stable: gt68xx_scanner_start_scan_extended "
 | |
| 	       "failed: %s\n", sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
|       status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (3, "gt68xx_wait_lamp_stable: gt68xx_line_reader_read failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
|       gt68xx_scanner_stop_scan (scanner);
 | |
| 
 | |
|       gt68xx_afe_ccd_calc (values, buffer_pointers[0]);
 | |
| 
 | |
|       DBG (4,
 | |
| 	   "gt68xx_wait_lamp_stable: this white = %d, last white = %d\n",
 | |
| 	   values->total_white, last_white);
 | |
| 
 | |
|       gettimeofday (&now, 0);
 | |
|       secs_lamp_on = now.tv_sec - scanner->lamp_on_time.tv_sec;
 | |
|       secs_start = now.tv_sec - start_time.tv_sec;
 | |
| 
 | |
|       if (!message_printed && secs_start > 5)
 | |
| 	{
 | |
| 	  DBG (0, "Please wait for lamp warm-up\n");
 | |
| 	  message_printed = SANE_TRUE;
 | |
| 	}
 | |
| 
 | |
|       if (scanner->val[OPT_AUTO_WARMUP].w == SANE_TRUE)
 | |
| 	{
 | |
| 	  if (scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP)
 | |
| 	    {
 | |
| 	      if (values->total_white <= (last_white - 20))
 | |
| 		  increase--;
 | |
| 	      if (values->total_white >= last_white)
 | |
| 		  increase++;
 | |
| 	      if (increase > 0 && (values->total_white <= (last_white + 20))
 | |
| 		  && values->total_white != 0)
 | |
| 		break;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      if ((values->total_white <= (last_white + 20))
 | |
| 		  && values->total_white != 0)
 | |
| 		break;		/* lamp is warmed up */
 | |
| 	    }
 | |
| 	}
 | |
|       last_white = values->total_white;
 | |
|     }
 | |
|   while (secs_lamp_on <= WARMUP_TIME);
 | |
| 
 | |
|   DBG (3, "gt68xx_wait_lamp_stable: Lamp is stable after %d secs (%d secs total)\n",
 | |
|        secs_start, secs_lamp_on);
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| /** Select best AFE gain and offset parameters.
 | |
|  *
 | |
|  * This function must be called before the main scan to choose the best values
 | |
|  * for the AFE gains and offsets.  It performs several one-line scans of the
 | |
|  * calibration strip.
 | |
|  *
 | |
|  * @param scanner Scanner object.
 | |
|  * @param orig_request Scan parameters.
 | |
|  *
 | |
|  * @returns
 | |
|  * - #SANE_STATUS_GOOD - gain and offset setting completed successfully
 | |
|  * - other error value - failure of some internal function
 | |
|  */
 | |
| static SANE_Status
 | |
| gt68xx_afe_ccd_auto (GT68xx_Scanner * scanner,
 | |
| 		     GT68xx_Scan_Request * orig_request)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   GT68xx_Scan_Parameters params;
 | |
|   GT68xx_Scan_Request request;
 | |
|   int i;
 | |
|   GT68xx_Afe_Values values;
 | |
|   unsigned int *buffer_pointers[3];
 | |
|   GT68xx_AFE_Parameters *afe = scanner->dev->afe, old_afe;
 | |
|   SANE_Bool gray_done = SANE_FALSE;
 | |
|   SANE_Bool red_done = SANE_FALSE, green_done = SANE_FALSE, blue_done =
 | |
|     SANE_FALSE;
 | |
| 
 | |
|   values.offset_direction = 1;
 | |
|   if (scanner->dev->model->flags & GT68XX_FLAG_OFFSET_INV)
 | |
|     values.offset_direction = -1;
 | |
| 
 | |
|   memset (&old_afe, 255, sizeof (old_afe));
 | |
| 
 | |
|   request.x0 = SANE_FIX (0.0);
 | |
|   request.xs = scanner->dev->model->x_size;
 | |
|   request.xdpi = 300;
 | |
|   request.ydpi = 300;
 | |
|   request.depth = 8;
 | |
|   request.color = orig_request->color;
 | |
|   /*  request.color = SANE_TRUE; */
 | |
|   request.mas = SANE_FALSE;
 | |
|   request.mbs = SANE_FALSE;
 | |
|   request.mds = SANE_TRUE;
 | |
|   request.calculate = SANE_FALSE;
 | |
|   request.use_ta = orig_request->use_ta;
 | |
| 
 | |
|   if (orig_request->use_ta)
 | |
|     {
 | |
|       gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE);
 | |
|       request.lamp = SANE_FALSE;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE);
 | |
|       request.lamp = SANE_TRUE;
 | |
|     }
 | |
| 
 | |
|   /* read line */
 | |
|   status = gt68xx_scanner_start_scan_extended (scanner, &request,
 | |
| 					       SA_CALIBRATE_ONE_LINE,
 | |
| 					       ¶ms);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (3,
 | |
| 	   "gt68xx_afe_ccd_auto: gt68xx_scanner_start_scan_extended failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
|   values.scan_dpi = params.xdpi;
 | |
|   values.calwidth = params.pixel_xs;
 | |
|   values.max_width =
 | |
|     (params.pixel_xs * scanner->dev->model->optical_xdpi) / params.xdpi;
 | |
|   if (orig_request->use_ta)
 | |
|     values.start_black = SANE_FIX (20.0);
 | |
|   else
 | |
|     values.start_black = scanner->dev->model->x_offset_mark;
 | |
|   values.coarse_black = 1;
 | |
|   values.coarse_white = 254;
 | |
| 
 | |
|   request.mds = SANE_FALSE;
 | |
|   DBG (5, "gt68xx_afe_ccd_auto: scan_dpi=%d, calwidth=%d, max_width=%d, "
 | |
|        "start_black=%.1f mm\n", values.scan_dpi,
 | |
|        values.calwidth, values.max_width, SANE_UNFIX (values.start_black));
 | |
| 
 | |
|   status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (3, "gt68xx_afe_ccd_auto: gt68xx_line_reader_read failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
|   gt68xx_scanner_stop_scan (scanner);
 | |
| 
 | |
|   status = gt68xx_wait_lamp_stable (scanner, ¶ms, &request, buffer_pointers,
 | |
| 				    &values, SANE_FALSE);
 | |
| 
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "gt68xx_afe_ccd_auto: gt68xx_wait_lamp_stable failed %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   i = 0;
 | |
|   do
 | |
|     {
 | |
|       i++;
 | |
|       /* read line */
 | |
|       status = gt68xx_scanner_start_scan_extended (scanner, &request,
 | |
| 						   SA_CALIBRATE_ONE_LINE,
 | |
| 						   ¶ms);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (3,
 | |
| 	       "gt68xx_afe_ccd_auto: gt68xx_scanner_start_scan_extended failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (3, "gt68xx_afe_ccd_auto: gt68xx_line_reader_read failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       if (params.color)
 | |
| 	{
 | |
| 	  /* red */
 | |
| 	  if (!red_done)
 | |
| 	    red_done =
 | |
| 	      gt68xx_afe_ccd_adjust_offset_gain ("red", &values,
 | |
| 						 buffer_pointers[0],
 | |
| 						 &afe->r_offset, &afe->r_pga,
 | |
| 						 &old_afe.r_offset,
 | |
| 						 &old_afe.r_pga);
 | |
| 	  /* green */
 | |
| 	  if (!green_done)
 | |
| 	    green_done =
 | |
| 	      gt68xx_afe_ccd_adjust_offset_gain ("green", &values,
 | |
| 						 buffer_pointers[1],
 | |
| 						 &afe->g_offset, &afe->g_pga,
 | |
| 						 &old_afe.g_offset,
 | |
| 						 &old_afe.g_pga);
 | |
| 
 | |
| 	  /* blue */
 | |
| 	  if (!blue_done)
 | |
| 	    blue_done =
 | |
| 	      gt68xx_afe_ccd_adjust_offset_gain ("blue", &values,
 | |
| 						 buffer_pointers[2],
 | |
| 						 &afe->b_offset, &afe->b_pga,
 | |
| 						 &old_afe.b_offset,
 | |
| 						 &old_afe.b_pga);
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  if (strcmp (scanner->val[OPT_GRAY_MODE_COLOR].s, GT68XX_COLOR_BLUE)
 | |
| 	      == 0)
 | |
| 	    {
 | |
| 	      gray_done =
 | |
| 		gt68xx_afe_ccd_adjust_offset_gain ("gray", &values,
 | |
| 						   buffer_pointers[0],
 | |
| 						   &afe->b_offset,
 | |
| 						   &afe->b_pga,
 | |
| 						   &old_afe.b_offset,
 | |
| 						   &old_afe.b_pga);
 | |
| 	    }
 | |
| 	  else
 | |
| 	    if (strcmp
 | |
| 		(scanner->val[OPT_GRAY_MODE_COLOR].s,
 | |
| 		 GT68XX_COLOR_GREEN) == 0)
 | |
| 	    {
 | |
| 	      gray_done =
 | |
| 		gt68xx_afe_ccd_adjust_offset_gain ("gray", &values,
 | |
| 						   buffer_pointers[0],
 | |
| 						   &afe->g_offset,
 | |
| 						   &afe->g_pga,
 | |
| 						   &old_afe.g_offset,
 | |
| 						   &old_afe.g_pga);
 | |
| 	      afe->r_offset = afe->b_offset = 0x20;
 | |
| 	      afe->r_pga = afe->b_pga = 0x18;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      gray_done =
 | |
| 		gt68xx_afe_ccd_adjust_offset_gain ("gray", &values,
 | |
| 						   buffer_pointers[0],
 | |
| 						   &afe->r_offset,
 | |
| 						   &afe->r_pga,
 | |
| 						   &old_afe.r_offset,
 | |
| 						   &old_afe.r_pga);
 | |
| 	    }
 | |
| 	}
 | |
|       gt68xx_scanner_stop_scan (scanner);
 | |
|     }
 | |
|   while (((params.color && (!red_done || !green_done || !blue_done))
 | |
| 	  || (!params.color && !gray_done)) && i < 100);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| /************************************************************************/
 | |
| /* CIS scanners                                                         */
 | |
| /************************************************************************/
 | |
| 
 | |
| 
 | |
| static void
 | |
| gt68xx_afe_cis_calc_black (GT68xx_Afe_Values * values,
 | |
| 			   unsigned int *black_buffer)
 | |
| {
 | |
|   SANE_Int start_black;
 | |
|   SANE_Int end_black;
 | |
|   SANE_Int i, j;
 | |
|   SANE_Int min_black = 255;
 | |
|   SANE_Int average = 0;
 | |
| 
 | |
|   start_black = 0;
 | |
|   end_black = values->calwidth;
 | |
| 
 | |
|   /* find min average black value */
 | |
|   for (i = start_black; i < end_black; ++i)
 | |
|     {
 | |
|       SANE_Int avg_black = 0;
 | |
|       for (j = 0; j < values->callines; j++)
 | |
| 	avg_black += (*(black_buffer + i + j * values->calwidth) >> 8);
 | |
|       avg_black /= values->callines;
 | |
|       average += avg_black;
 | |
|       if (avg_black < min_black)
 | |
| 	min_black = avg_black;
 | |
|     }
 | |
|   values->black = min_black;
 | |
|   average /= (end_black - start_black);
 | |
|   DBG (5,
 | |
|        "gt68xx_afe_cis_calc_black: min_black=0x%02x, average_black=0x%02x\n",
 | |
|        values->black, average);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gt68xx_afe_cis_calc_white (GT68xx_Afe_Values * values,
 | |
| 			   unsigned int *white_buffer)
 | |
| {
 | |
|   SANE_Int start_white;
 | |
|   SANE_Int end_white;
 | |
|   SANE_Int i, j;
 | |
|   SANE_Int max_white = 0;
 | |
| 
 | |
|   start_white = 0;
 | |
|   end_white = values->calwidth;
 | |
|   values->total_white = 0;
 | |
| 
 | |
|   /* find max average white value */
 | |
|   for (i = start_white; i < end_white; ++i)
 | |
|     {
 | |
|       SANE_Int avg_white = 0;
 | |
|       for (j = 0; j < values->callines; j++)
 | |
| 	{
 | |
| 	  avg_white += (*(white_buffer + i + j * values->calwidth) >> 8);
 | |
| 	  values->total_white += (*(white_buffer + i + j * values->calwidth));
 | |
| 	}
 | |
|       avg_white /= values->callines;
 | |
|       if (avg_white > max_white)
 | |
| 	max_white = avg_white;
 | |
|     }
 | |
|   values->white = max_white;
 | |
|   values->total_white /= (values->callines * (end_white - start_white));
 | |
|   DBG (5,
 | |
|        "gt68xx_afe_cis_calc_white: max_white=0x%02x, average_white=0x%02x\n",
 | |
|        values->white, values->total_white >> 8);
 | |
| }
 | |
| 
 | |
| static SANE_Bool
 | |
| gt68xx_afe_cis_adjust_gain_offset (SANE_String_Const color_name,
 | |
| 				   GT68xx_Afe_Values * values,
 | |
| 				   unsigned int *black_buffer,
 | |
| 				   unsigned int *white_buffer,
 | |
| 				   GT68xx_AFE_Parameters * afe,
 | |
| 				   GT68xx_AFE_Parameters * old_afe)
 | |
| {
 | |
|   SANE_Byte *offset, *old_offset, *gain, *old_gain;
 | |
|   SANE_Int o, g;
 | |
|   SANE_Int black_low = values->coarse_black, black_high = black_low + 10;
 | |
|   SANE_Int white_high = values->coarse_white, white_low = white_high - 10;
 | |
|   SANE_Bool done = SANE_TRUE;
 | |
| 
 | |
|   gt68xx_afe_cis_calc_black (values, black_buffer);
 | |
|   gt68xx_afe_cis_calc_white (values, white_buffer);
 | |
| 
 | |
|   if (strcmp (color_name, "red") == 0)
 | |
|     {
 | |
|       offset = &(afe->r_offset);
 | |
|       old_offset = &old_afe->r_offset;
 | |
|       gain = &afe->r_pga;
 | |
|       old_gain = &old_afe->r_pga;
 | |
|     }
 | |
|   else if (strcmp (color_name, "green") == 0)
 | |
|     {
 | |
|       offset = &afe->g_offset;
 | |
|       old_offset = &old_afe->g_offset;
 | |
|       gain = &afe->g_pga;
 | |
|       old_gain = &old_afe->g_pga;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       offset = &afe->b_offset;
 | |
|       old_offset = &old_afe->b_offset;
 | |
|       gain = &afe->b_pga;
 | |
|       old_gain = &old_afe->b_pga;
 | |
|     }
 | |
| 
 | |
|   o = *offset;
 | |
|   g = *gain;
 | |
| 
 | |
|   if (values->white > white_high)
 | |
|     {
 | |
|       if (values->black > black_high)
 | |
| 	o -= values->offset_direction;
 | |
|       else if (values->black < black_low)
 | |
| 	g--;
 | |
|       else
 | |
| 	{
 | |
| 	  o -= values->offset_direction;
 | |
| 	  g--;
 | |
| 	}
 | |
|       done = SANE_FALSE;
 | |
|       goto finish;
 | |
|     }
 | |
|   else if (values->white < white_low)
 | |
|     {
 | |
|       if (values->black < black_low)
 | |
| 	o += values->offset_direction;
 | |
|       else if (values->black > black_high)
 | |
| 	g++;
 | |
|       else
 | |
| 	{
 | |
| 	  o += values->offset_direction;
 | |
| 	  g++;
 | |
| 	}
 | |
|       done = SANE_FALSE;
 | |
|       goto finish;
 | |
|     }
 | |
|   if (values->black > black_high)
 | |
|     {
 | |
|       if (values->white > white_high)
 | |
| 	o -= values->offset_direction;
 | |
|       else if (values->white < white_low)
 | |
| 	g++;
 | |
|       else
 | |
| 	{
 | |
| 	  o -= values->offset_direction;
 | |
| 	  g++;
 | |
| 	}
 | |
|       done = SANE_FALSE;
 | |
|       goto finish;
 | |
|     }
 | |
|   else if (values->black < black_low)
 | |
|     {
 | |
|       if (values->white < white_low)
 | |
| 	o += values->offset_direction;
 | |
|       else if (values->white > white_high)
 | |
| 	g--;
 | |
|       else
 | |
| 	{
 | |
| 	  o += values->offset_direction;
 | |
| 	  g--;
 | |
| 	}
 | |
|       done = SANE_FALSE;
 | |
|       goto finish;
 | |
|     }
 | |
| finish:
 | |
|   if (g < 0)
 | |
|     g = 0;
 | |
|   if (g > 48)
 | |
|     g = 48;
 | |
|   if (o < 0)
 | |
|     o = 0;
 | |
|   if (o > 64)
 | |
|     o = 64;
 | |
| 
 | |
|   if ((g == *gain) && (o == *offset))
 | |
|     done = SANE_TRUE;
 | |
|   if ((g == *old_gain) && (o == *old_offset))
 | |
|     done = SANE_TRUE;
 | |
| 
 | |
|   *old_gain = *gain;
 | |
|   *old_offset = *offset;
 | |
| 
 | |
|   DBG (4, "%5s white=%3d, black=%3d, offset=0x%02X, gain=0x%02X, old offs=0x%02X, "
 | |
|        "old gain=0x%02X, total_white=%5d %s\n", color_name, values->white,
 | |
|        values->black, o, g, *offset, *gain, values->total_white,
 | |
|        done ? "DONE " : "");
 | |
| 
 | |
|   *gain = g;
 | |
|   *offset = o;
 | |
| 
 | |
|   return done;
 | |
| }
 | |
| 
 | |
| 
 | |
| static SANE_Bool
 | |
| gt68xx_afe_cis_adjust_exposure (SANE_String_Const color_name,
 | |
| 				GT68xx_Afe_Values * values,
 | |
| 				unsigned int *white_buffer, SANE_Int border,
 | |
| 				SANE_Int * exposure_time)
 | |
| {
 | |
|   SANE_Int exposure_change = 0;
 | |
| 
 | |
|   gt68xx_afe_cis_calc_white (values, white_buffer);
 | |
| 
 | |
|   if (values->white < border)
 | |
|     {
 | |
|       exposure_change = ((border - values->white) * 1);
 | |
|       (*exposure_time) += exposure_change;
 | |
|       DBG (4,
 | |
| 	   "%5s: white = %3d, total_white=%5d (exposure too low) --> exposure += %d (=0x%03x)\n",
 | |
| 	   color_name, values->white, values->total_white, exposure_change, *exposure_time);
 | |
|       return SANE_FALSE;
 | |
|     }
 | |
|   else if (values->white > border + 5)
 | |
|     {
 | |
|       exposure_change = -((values->white - (border + 5)) * 1);
 | |
|       (*exposure_time) += exposure_change;
 | |
|       DBG (4,
 | |
| 	   "%5s: white = %3d, total_white=%5d (exposure too high) --> exposure -= %d (=0x%03x)\n",
 | |
| 	   color_name, values->white, values->total_white, exposure_change, *exposure_time);
 | |
|       return SANE_FALSE;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       DBG (4, "%5s: white = %3d, total_white=%5d (exposure ok=0x%03x)\n",
 | |
| 	   color_name, values->white, values->total_white, *exposure_time);
 | |
|     }
 | |
|   return SANE_TRUE;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_afe_cis_read_lines (GT68xx_Afe_Values * values,
 | |
| 			   GT68xx_Scanner * scanner, SANE_Bool lamp,
 | |
| 			   SANE_Bool first, unsigned int *r_buffer,
 | |
| 			   unsigned int *g_buffer, unsigned int *b_buffer)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   int line;
 | |
|   unsigned int *buffer_pointers[3];
 | |
|   GT68xx_Scan_Request request;
 | |
|   GT68xx_Scan_Parameters params;
 | |
| 
 | |
|   request.x0 = SANE_FIX (0.0);
 | |
|   request.xs = scanner->dev->model->x_size;
 | |
|   request.xdpi = 300;
 | |
|   request.ydpi = 300;
 | |
|   request.depth = 8;
 | |
|   request.color = SANE_TRUE;
 | |
|   request.mas = SANE_FALSE;
 | |
|   request.calculate = SANE_FALSE;
 | |
|   request.use_ta = SANE_FALSE;
 | |
| 
 | |
|   if (first)			/* go to start position */
 | |
|     {
 | |
|       request.mbs = SANE_TRUE;
 | |
|       request.mds = SANE_TRUE;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       request.mbs = SANE_FALSE;
 | |
|       request.mds = SANE_FALSE;
 | |
|     }
 | |
|   request.lamp = lamp;
 | |
| 
 | |
|   if (!r_buffer)		/* First, set the size parameters */
 | |
|     {
 | |
|       request.calculate = SANE_TRUE;
 | |
|       RIE (gt68xx_device_setup_scan
 | |
| 	   (scanner->dev, &request, SA_CALIBRATE_ONE_LINE, ¶ms));
 | |
|       values->scan_dpi = params.xdpi;
 | |
|       values->calwidth = params.pixel_xs;
 | |
|       values->callines = params.pixel_ys;
 | |
|       values->start_black = scanner->dev->model->x_offset_mark;
 | |
|       return SANE_STATUS_GOOD;
 | |
|     }
 | |
| 
 | |
|   if (first && (scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP))
 | |
|     {
 | |
|       if (request.use_ta)
 | |
| 	{
 | |
| 	  gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE);
 | |
| 	  request.lamp = SANE_FALSE;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE);
 | |
| 	  request.lamp = SANE_TRUE;
 | |
| 	}
 | |
|       status = gt68xx_wait_lamp_stable (scanner, ¶ms, &request,
 | |
| 					buffer_pointers, values, SANE_TRUE);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1, "gt68xx_afe_cis_read_lines: gt68xx_wait_lamp_stable failed %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
|       request.mbs = SANE_FALSE;
 | |
|       request.mds = SANE_FALSE;
 | |
|     }
 | |
| 
 | |
|   status =
 | |
|     gt68xx_scanner_start_scan_extended (scanner, &request,
 | |
| 					SA_CALIBRATE_ONE_LINE, ¶ms);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_afe_cis_read_lines: gt68xx_scanner_start_scan_extended failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
|   values->scan_dpi = params.xdpi;
 | |
|   values->calwidth = params.pixel_xs;
 | |
|   values->callines = params.pixel_ys;
 | |
|   values->coarse_black = 2;
 | |
|   values->coarse_white = 253;
 | |
| 
 | |
|   if (r_buffer && g_buffer && b_buffer)
 | |
|     for (line = 0; line < values->callines; line++)
 | |
|       {
 | |
| 	status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
 | |
| 	if (status != SANE_STATUS_GOOD)
 | |
| 	  {
 | |
| 	    DBG (5,
 | |
| 		 "gt68xx_afe_cis_read_lines: gt68xx_line_reader_read failed: %s\n",
 | |
| 		 sane_strstatus (status));
 | |
| 	    return status;
 | |
| 	  }
 | |
| 	memcpy (r_buffer + values->calwidth * line, buffer_pointers[0],
 | |
| 		values->calwidth * sizeof (unsigned int));
 | |
| 	memcpy (g_buffer + values->calwidth * line, buffer_pointers[1],
 | |
| 		values->calwidth * sizeof (unsigned int));
 | |
| 	memcpy (b_buffer + values->calwidth * line, buffer_pointers[2],
 | |
| 		values->calwidth * sizeof (unsigned int));
 | |
|       }
 | |
| 
 | |
|   status = gt68xx_scanner_stop_scan (scanner);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5,
 | |
| 	   "gt68xx_afe_cis_read_lines: gt68xx_scanner_stop_scan failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_afe_cis_auto (GT68xx_Scanner * scanner)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   int total_count, exposure_count;
 | |
|   GT68xx_Afe_Values values;
 | |
|   GT68xx_AFE_Parameters *afe = scanner->dev->afe, old_afe;
 | |
|   GT68xx_Exposure_Parameters *exposure = scanner->dev->exposure;
 | |
|   SANE_Int red_done, green_done, blue_done;
 | |
|   SANE_Bool first = SANE_TRUE;
 | |
|   unsigned int *r_gbuffer = 0, *g_gbuffer = 0, *b_gbuffer = 0;
 | |
|   unsigned int *r_obuffer = 0, *g_obuffer = 0, *b_obuffer = 0;
 | |
| 
 | |
|   DBG (5, "gt68xx_afe_cis_auto: start\n");
 | |
| 
 | |
|   if (scanner->dev->model->flags & GT68XX_FLAG_NO_CALIBRATE)
 | |
|   {
 | |
|     return SANE_STATUS_GOOD;
 | |
|   }
 | |
| 
 | |
|   memset (&old_afe, 255, sizeof (old_afe));
 | |
| 
 | |
|   /* Start with the preset exposure settings */
 | |
|   memcpy (scanner->dev->exposure, &scanner->dev->model->exposure,
 | |
| 	  sizeof (*scanner->dev->exposure));
 | |
| 
 | |
|   RIE (gt68xx_afe_cis_read_lines (&values, scanner, SANE_FALSE, SANE_FALSE,
 | |
| 				  r_gbuffer, g_gbuffer, b_gbuffer));
 | |
| 
 | |
|   r_gbuffer =
 | |
|     malloc (values.calwidth * values.callines * sizeof (unsigned int));
 | |
|   g_gbuffer =
 | |
|     malloc (values.calwidth * values.callines * sizeof (unsigned int));
 | |
|   b_gbuffer =
 | |
|     malloc (values.calwidth * values.callines * sizeof (unsigned int));
 | |
|   r_obuffer =
 | |
|     malloc (values.calwidth * values.callines * sizeof (unsigned int));
 | |
|   g_obuffer =
 | |
|     malloc (values.calwidth * values.callines * sizeof (unsigned int));
 | |
|   b_obuffer =
 | |
|     malloc (values.calwidth * values.callines * sizeof (unsigned int));
 | |
|   if (!r_gbuffer || !g_gbuffer || !b_gbuffer || !r_obuffer || !g_obuffer
 | |
|       || !b_obuffer)
 | |
|     return SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   total_count = 0;
 | |
|   red_done = green_done = blue_done = SANE_FALSE;
 | |
|   old_afe.r_offset = old_afe.g_offset = old_afe.b_offset = 255;
 | |
|   old_afe.r_pga = old_afe.g_pga = old_afe.b_pga = 255;
 | |
|   do
 | |
|     {
 | |
|       values.offset_direction = 1;
 | |
|       if (scanner->dev->model->flags & GT68XX_FLAG_OFFSET_INV)
 | |
| 	values.offset_direction = -1;
 | |
| 
 | |
|       RIE (gt68xx_afe_cis_read_lines (&values, scanner, SANE_FALSE, first,
 | |
| 				      r_obuffer, g_obuffer, b_obuffer));
 | |
|       RIE (gt68xx_afe_cis_read_lines (&values, scanner, SANE_TRUE, SANE_FALSE,
 | |
| 				      r_gbuffer, g_gbuffer, b_gbuffer));
 | |
| 
 | |
|       if (!red_done)
 | |
| 	red_done =
 | |
| 	  gt68xx_afe_cis_adjust_gain_offset ("red", &values, r_obuffer,
 | |
| 					     r_gbuffer, afe, &old_afe);
 | |
|       if (!green_done)
 | |
| 	green_done =
 | |
| 	  gt68xx_afe_cis_adjust_gain_offset ("green", &values, g_obuffer,
 | |
| 					     g_gbuffer, afe, &old_afe);
 | |
|       if (!blue_done)
 | |
| 	blue_done =
 | |
| 	  gt68xx_afe_cis_adjust_gain_offset ("blue", &values, b_obuffer,
 | |
| 					     b_gbuffer, afe, &old_afe);
 | |
|       total_count++;
 | |
|       first = SANE_FALSE;
 | |
| 
 | |
|     }
 | |
|   while (total_count < 100 && (!red_done || !green_done || !blue_done));
 | |
| 
 | |
|   if (!red_done || !green_done || !blue_done)
 | |
|     DBG (0, "gt68xx_afe_cis_auto: setting AFE reached limit\n");
 | |
| 
 | |
|   /* Exposure time */
 | |
|   exposure_count = 0;
 | |
|   red_done = green_done = blue_done = SANE_FALSE;
 | |
|   do
 | |
|     {
 | |
|       /* read white line */
 | |
|       RIE (gt68xx_afe_cis_read_lines (&values, scanner, SANE_TRUE, SANE_FALSE,
 | |
| 				      r_gbuffer, g_gbuffer, b_gbuffer));
 | |
|       if (!red_done)
 | |
| 	red_done =
 | |
| 	  gt68xx_afe_cis_adjust_exposure ("red", &values, r_gbuffer, 245,
 | |
| 					  &exposure->r_time);
 | |
|       if (!green_done)
 | |
| 	green_done =
 | |
| 	  gt68xx_afe_cis_adjust_exposure ("green", &values, g_gbuffer, 245,
 | |
| 					  &exposure->g_time);
 | |
|       if (!blue_done)
 | |
| 	blue_done =
 | |
| 	  gt68xx_afe_cis_adjust_exposure ("blue", &values, b_gbuffer, 245,
 | |
| 					  &exposure->b_time);
 | |
|       exposure_count++;
 | |
|       total_count++;
 | |
|     }
 | |
|   while ((!red_done || !green_done || !blue_done) && exposure_count < 50);
 | |
| 
 | |
|   if (!red_done || !green_done || !blue_done)
 | |
|     DBG (0, "gt68xx_afe_cis_auto: setting exposure reached limit\n");
 | |
| 
 | |
|   /* store afe calibration when needed */
 | |
|   if(scanner->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE)
 | |
|     {
 | |
|       memcpy(&(scanner->afe_params), afe, sizeof(GT68xx_AFE_Parameters));
 | |
|       scanner->exposure_params.r_time=exposure->r_time;
 | |
|       scanner->exposure_params.g_time=exposure->g_time;
 | |
|       scanner->exposure_params.b_time=exposure->b_time;
 | |
|     }
 | |
| 
 | |
|   free (r_gbuffer);
 | |
|   free (g_gbuffer);
 | |
|   free (b_gbuffer);
 | |
|   free (r_obuffer);
 | |
|   free (g_obuffer);
 | |
|   free (b_obuffer);
 | |
|   DBG (4, "gt68xx_afe_cis_auto: total_count: %d\n", total_count);
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| /** @brief create and copy calibrator
 | |
|  * Creates a calibrator of the given width and copy data from reference
 | |
|  * to initialize it
 | |
|  * @param calibator pointer to the calibrator to create
 | |
|  * @param reference calibrator with reference data to copy
 | |
|  * @param width the width in pixels of the calibrator
 | |
|  * @param offset offset in pixels when copying data from reference
 | |
|  * @return SANE_STATUS_GOOD and a filled calibrator if enough memory
 | |
|  */
 | |
| static SANE_Status
 | |
| gt68xx_calibrator_create_copy (GT68xx_Calibrator ** calibrator,
 | |
| 			       GT68xx_Calibrator * reference, int width,
 | |
| 			       int offset)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   int i;
 | |
| 
 | |
|   if (reference == NULL)
 | |
|     {
 | |
|       DBG (1, "gt68xx_calibrator_create_copy: NULL reference, skipping...\n");
 | |
|       *calibrator = NULL;
 | |
|       return SANE_STATUS_GOOD;
 | |
|     }
 | |
|   /* check for reference overflow */
 | |
|   if(width+offset>reference->width)
 | |
|     {
 | |
|       DBG (1, "gt68xx_calibrator_create_copy: required with and offset exceed reference width\n");
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   status = gt68xx_calibrator_new (width, 65535, calibrator);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1,
 | |
| 	   "gt68xx_calibrator_create_copy: failed to create calibrator: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   for(i=0;i<width;i++)
 | |
|     {
 | |
|       (*calibrator)->k_white[i]=reference->k_white[i+offset];
 | |
|       (*calibrator)->k_black[i]=reference->k_black[i+offset];
 | |
|       (*calibrator)->white_line[i]=reference->white_line[i+offset];
 | |
|       (*calibrator)->black_line[i]=reference->black_line[i+offset];
 | |
|     }
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_sheetfed_move_to_scan_area (GT68xx_Scanner * scanner,
 | |
| 				  GT68xx_Scan_Request * request)
 | |
| {
 | |
|   SANE_Status status;
 | |
| 
 | |
|   if (!(scanner->dev->model->flags & GT68XX_FLAG_SHEET_FED)
 | |
|       || scanner->dev->model->command_set->move_paper == NULL)
 | |
|     return SANE_STATUS_GOOD;
 | |
| 
 | |
|   /* send move paper command */
 | |
|   RIE (scanner->dev->model->command_set->move_paper (scanner->dev, request));
 | |
| 
 | |
|   /* wait until paper is set to the desired position */
 | |
|   return gt68xx_scanner_wait_for_positioning (scanner);
 | |
| }
 | |
| 
 | |
| /**< number of consecutive white line to detect a white area */
 | |
| #define WHITE_LINES 2
 | |
| 
 | |
| /** @brief calibrate sheet fed scanner
 | |
|  * This function calibrates sheet fed scanner by scanning a calibration
 | |
|  * target (which may be a blank page). It first move to a white area then
 | |
|  * does afe and exposure calibration. Then it scans white lines to get data
 | |
|  * for shading correction.
 | |
|  * @param scanner structure describing the frontend session and the device
 | |
|  * @return SANE_STATUS_GOOD is everything goes right, SANE_STATUS_INVAL
 | |
|  * otherwise.
 | |
|  */
 | |
| static SANE_Status
 | |
| gt68xx_sheetfed_scanner_calibrate (GT68xx_Scanner * scanner)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   GT68xx_Scan_Request request;
 | |
|   GT68xx_Scan_Parameters params;
 | |
|   int count, i, x, y, white;
 | |
|   unsigned int *buffer_pointers[3];
 | |
| #ifdef DEBUG_CALIBRATION
 | |
|   FILE *fcal;
 | |
|   char title[50];
 | |
| #endif
 | |
| 
 | |
|   DBG (3, "gt68xx_sheetfed_scanner_calibrate: start.\n");
 | |
| 
 | |
|   /* clear calibration if needed */
 | |
|   gt68xx_scanner_free_calibrators (scanner);
 | |
|   for (i = 0; i < MAX_RESOLUTIONS; i++)
 | |
|     {
 | |
|       if(scanner->calibrations[i].red!=NULL)
 | |
|         {
 | |
|           gt68xx_calibrator_free (scanner->calibrations[i].red);
 | |
|         }
 | |
|       if(scanner->calibrations[i].green!=NULL)
 | |
|         {
 | |
|           gt68xx_calibrator_free (scanner->calibrations[i].green);
 | |
|         }
 | |
|       if(scanner->calibrations[i].blue!=NULL)
 | |
|         {
 | |
|           gt68xx_calibrator_free (scanner->calibrations[i].blue);
 | |
|         }
 | |
|       if(scanner->calibrations[i].gray!=NULL)
 | |
|         {
 | |
|           gt68xx_calibrator_free (scanner->calibrations[i].gray);
 | |
|         }
 | |
|     }
 | |
|   scanner->calibrated = SANE_FALSE;
 | |
| 
 | |
|   /* find minimum horizontal resolution */
 | |
|   request.xdpi = 9600;
 | |
|   for (i = 0; scanner->dev->model->xdpi_values[i] != 0; i++)
 | |
|     {
 | |
|       if (scanner->dev->model->xdpi_values[i] < request.xdpi)
 | |
| 	{
 | |
| 	  request.xdpi = scanner->dev->model->xdpi_values[i];
 | |
| 	  request.ydpi = scanner->dev->model->xdpi_values[i];
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* move to white area SA_CALIBRATE uses its own y0/ys fixed values */
 | |
|   request.x0 = 0;
 | |
|   request.y0 = scanner->dev->model->y_offset_calib;
 | |
|   request.xs = scanner->dev->model->x_size;
 | |
|   request.depth = 8;
 | |
| 
 | |
|   request.color = SANE_FALSE;
 | |
|   request.mbs = SANE_TRUE;
 | |
|   request.mds = SANE_TRUE;
 | |
|   request.mas = SANE_FALSE;
 | |
|   request.lamp = SANE_TRUE;
 | |
|   request.calculate = SANE_FALSE;
 | |
|   request.use_ta = SANE_FALSE;
 | |
|   request.backtrack = SANE_FALSE;
 | |
|   request.backtrack_lines = 0;
 | |
| 
 | |
|   /* skip start of calibration sheet */
 | |
|   status = gt68xx_sheetfed_move_to_scan_area (scanner, &request);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1,
 | |
| 	   "gt68xx_sheetfed_scanner_calibrate: failed to skip start of calibration sheet %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   status = gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1,
 | |
| 	   "gt68xx_sheetfed_scanner_calibrate: gt68xx_device_lamp_control returned %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   /* loop until we find a white area to calibrate on */
 | |
|   i = 0;
 | |
|   request.y0 = 0;
 | |
|   do
 | |
|     {
 | |
|       /* start scan */
 | |
|       status =
 | |
| 	gt68xx_scanner_start_scan_extended (scanner, &request, SA_CALIBRATE,
 | |
| 					    ¶ms);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1,
 | |
| 	       "gt68xx_sheetfed_scanner_calibrate: gt68xx_scanner_start_scan_extended returned %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       /* loop until we find WHITE_LINES consecutive white lines or we reach and of area */
 | |
|       white = 0;
 | |
|       y = 0;
 | |
|       do
 | |
| 	{
 | |
| 	  status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
 | |
| 	  if (status != SANE_STATUS_GOOD)
 | |
| 	    {
 | |
| 	      DBG (1,
 | |
| 		   "gt68xx_sheetfed_scanner_calibrate: gt68xx_line_reader_read returned %s\n",
 | |
| 		   sane_strstatus (status));
 | |
| 	      gt68xx_scanner_stop_scan (scanner);
 | |
| 	      return status;
 | |
| 	    }
 | |
| 
 | |
| 	  /* check for white line */
 | |
| 	  count = 0;
 | |
| 	  for (x = 0; x < params.pixel_xs; x++)
 | |
| 	    {
 | |
| 	      if (((buffer_pointers[0][x] >> 8) & 0xff) > 50)
 | |
| 		{
 | |
| 		  count++;
 | |
| 		}
 | |
| 	    }
 | |
| 
 | |
| 	  /* line is white if 93% is above black level */
 | |
| 	  if ((100 * count) / params.pixel_xs < 93)
 | |
| 	    {
 | |
| 	      white = 0;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      white++;
 | |
| 	    }
 | |
| 	  y++;
 | |
| 	}
 | |
|       while ((white < WHITE_LINES) && (y < params.pixel_ys));
 | |
| 
 | |
|       /* end scan */
 | |
|       gt68xx_scanner_stop_scan (scanner);
 | |
| 
 | |
|       i++;
 | |
|     }
 | |
|   while (i < 20 && white < WHITE_LINES);
 | |
| 
 | |
|   /* check if we found a white area */
 | |
|   if (white != WHITE_LINES)
 | |
|     {
 | |
|       DBG (1,
 | |
| 	   "gt68xx_sheetfed_scanner_calibrate: didn't find a white area\n");
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   /* now do calibration */
 | |
|   scanner->auto_afe = SANE_TRUE;
 | |
|   scanner->calib = SANE_TRUE;
 | |
| 
 | |
|   /* loop at each possible xdpi to create calibrators */
 | |
|   i = 0;
 | |
|   while (scanner->dev->model->xdpi_values[i] > 0)
 | |
|     {
 | |
|       request.xdpi = scanner->dev->model->xdpi_values[i];
 | |
|       request.ydpi = scanner->dev->model->xdpi_values[i];
 | |
|       request.x0 = 0;
 | |
|       request.y0 = 0;
 | |
|       request.xs = scanner->dev->model->x_size;
 | |
|       request.color = SANE_FALSE;
 | |
|       request.mbs = SANE_FALSE;
 | |
|       request.mds = SANE_TRUE;
 | |
|       request.mas = SANE_FALSE;
 | |
|       request.lamp = SANE_TRUE;
 | |
|       request.calculate = SANE_FALSE;
 | |
|       request.use_ta = SANE_FALSE;
 | |
|       request.backtrack = SANE_FALSE;
 | |
|       request.backtrack_lines = 0;
 | |
| 
 | |
|       /* calibrate in color */
 | |
|       request.color = SANE_TRUE;
 | |
|       status = gt68xx_scanner_calibrate (scanner, &request);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1,
 | |
| 	       "gt68xx_sheetfed_scanner_calibrate: gt68xx_scanner_calibrate returned %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       /* since auto afe is done at a fixed resolution, we don't need to
 | |
|        * do each each time, once is enough */
 | |
|       scanner->auto_afe = SANE_FALSE;
 | |
| 
 | |
|       /* allocate and save per dpi calibrators */
 | |
|       scanner->calibrations[i].dpi = request.xdpi;
 | |
| 
 | |
|       /* recompute params */
 | |
|       request.calculate = SANE_TRUE;
 | |
|       gt68xx_device_setup_scan (scanner->dev, &request, SA_SCAN, ¶ms);
 | |
| 
 | |
|       scanner->calibrations[i].pixel_x0 = params.pixel_x0;
 | |
|       status =
 | |
| 	gt68xx_calibrator_create_copy (&(scanner->calibrations[i].red),
 | |
| 				       scanner->cal_r, scanner->cal_r->width,
 | |
| 				       0);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1,
 | |
| 	       "gt68xx_sheetfed_scanner_calibrate: failed to create red calibrator: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       status =
 | |
| 	gt68xx_calibrator_create_copy (&(scanner->calibrations[i].green),
 | |
| 				       scanner->cal_g, scanner->cal_g->width,
 | |
| 				       0);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1,
 | |
| 	       "gt68xx_sheetfed_scanner_calibrate: failed to create green calibrator: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       status =
 | |
| 	gt68xx_calibrator_create_copy (&(scanner->calibrations[i].blue),
 | |
| 				       scanner->cal_b, scanner->cal_b->width,
 | |
| 				       0);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1,
 | |
| 	       "gt68xx_sheetfed_scanner_calibrate: failed to create blue calibrator: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       /* calibrate in gray */
 | |
|       request.color = SANE_FALSE;
 | |
|       status = gt68xx_scanner_calibrate (scanner, &request);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1,
 | |
| 	       "gt68xx_sheetfed_scanner_calibrate: gt68xx_scanner_calibrate returned %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       if (scanner->cal_gray)
 | |
| 	{
 | |
| 	  status =
 | |
| 	    gt68xx_calibrator_create_copy (&(scanner->calibrations[i].gray),
 | |
| 					   scanner->cal_gray,
 | |
| 					   scanner->cal_gray->width, 0);
 | |
| 	  if (status != SANE_STATUS_GOOD)
 | |
| 	    {
 | |
| 	      DBG (1,
 | |
| 		   "gt68xx_sheetfed_scanner_calibrate: failed to create gray calibrator: %s\n",
 | |
| 		   sane_strstatus (status));
 | |
| 	      return status;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
| #ifdef DEBUG_CALIBRATION
 | |
|       sprintf (title, "cal-%03d-red.pnm", scanner->calibrations[i].dpi);
 | |
|       fcal = fopen (title, "wb");
 | |
|       if (fcal != NULL)
 | |
| 	{
 | |
| 	  fprintf (fcal, "P5\n%d 1\n255\n", params.pixel_xs);
 | |
| 	  for (x = 0; x < params.pixel_xs; x++)
 | |
| 	    fputc ((scanner->calibrations[i].red->k_white[x] >> 8) & 0xff,
 | |
| 		   fcal);
 | |
| 	  fclose (fcal);
 | |
| 	}
 | |
|       sprintf (title, "cal-%03d-green.pnm", scanner->calibrations[i].dpi);
 | |
|       fcal = fopen (title, "wb");
 | |
|       if (fcal != NULL)
 | |
| 	{
 | |
| 	  fprintf (fcal, "P5\n%d 1\n255\n", params.pixel_xs);
 | |
| 	  for (x = 0; x < params.pixel_xs; x++)
 | |
| 	    fputc ((scanner->calibrations[i].green->k_white[x] >> 8) & 0xff,
 | |
| 		   fcal);
 | |
| 	  fclose (fcal);
 | |
| 	}
 | |
|       sprintf (title, "cal-%03d-blue.pnm", scanner->calibrations[i].dpi);
 | |
|       fcal = fopen (title, "wb");
 | |
|       if (fcal != NULL)
 | |
| 	{
 | |
| 	  fprintf (fcal, "P5\n%d 1\n255\n", params.pixel_xs);
 | |
| 	  for (x = 0; x < params.pixel_xs; x++)
 | |
| 	    fputc ((scanner->calibrations[i].blue->k_white[x] >> 8) & 0xff,
 | |
| 		   fcal);
 | |
| 	  fclose (fcal);
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
|       /* next resolution */
 | |
|       i++;
 | |
|     }
 | |
| 
 | |
|   scanner->calibrated = SANE_TRUE;
 | |
| 
 | |
|   /* eject calibration target from feeder */
 | |
|   gt68xx_device_paperfeed (scanner->dev);
 | |
| 
 | |
|   /* save calibration to file */
 | |
|   gt68xx_write_calibration (scanner);
 | |
| 
 | |
|   DBG (3, "gt68xx_sheetfed_scanner_calibrate: end.\n");
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| /** @brief assign calibration for scan
 | |
|  * This function creates the calibrators and set up afe for the requested
 | |
|  * scan. It uses calibration data that has been created by
 | |
|  * gt68xx_sheetfed_scanner_calibrate.
 | |
|  * @param scanner structure describing the frontend session and the device
 | |
|  * @return SANE_STATUS_GOOD is everything goes right, SANE_STATUS_INVAL
 | |
|  * otherwise.
 | |
|  */
 | |
| static SANE_Status
 | |
| gt68xx_assign_calibration (GT68xx_Scanner * scanner,
 | |
| 			   GT68xx_Scan_Parameters params)
 | |
| {
 | |
|   int i, dpi, offset;
 | |
|   SANE_Status status = SANE_STATUS_GOOD;
 | |
| 
 | |
|   DBG (3, "gt68xx_assign_calibration: start.\n");
 | |
| 
 | |
|   dpi = params.xdpi;
 | |
|   DBG (4, "gt68xx_assign_calibration: searching calibration for %d dpi\n",
 | |
|        dpi);
 | |
| 
 | |
|   /* search matching dpi */
 | |
|   i = 0;
 | |
|   while (scanner->calibrations[i].dpi > 0
 | |
| 	 && scanner->calibrations[i].dpi != dpi)
 | |
|     {
 | |
|       i++;
 | |
|     }
 | |
| 
 | |
|   /* check if found a match */
 | |
|   if (scanner->calibrations[i].dpi == 0)
 | |
|     {
 | |
|       DBG (4,
 | |
| 	   "gt68xx_assign_calibration: failed to find calibration for %d dpi\n",
 | |
| 	   dpi);
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
|   DBG (4, "gt68xx_assign_calibration: using entry %d for %d dpi\n", i, dpi);
 | |
| 
 | |
|   DBG (5,
 | |
|        "gt68xx_assign_calibration: using scan_parameters: pixel_x0=%d, pixel_xs=%d \n",
 | |
|        params.pixel_x0, params.pixel_xs);
 | |
| 
 | |
|   /* AFE/exposure data copy */
 | |
|   memcpy (scanner->dev->afe, &(scanner->afe_params),
 | |
| 	  sizeof (GT68xx_AFE_Parameters));
 | |
|   scanner->dev->exposure->r_time = scanner->exposure_params.r_time;
 | |
|   scanner->dev->exposure->g_time = scanner->exposure_params.g_time;
 | |
|   scanner->dev->exposure->b_time = scanner->exposure_params.b_time;
 | |
| 
 | |
|   /* free calibrators if needed */
 | |
|   gt68xx_scanner_free_calibrators (scanner);
 | |
| 
 | |
|   /* TODO compute offset based on the x0 value from scan_request */
 | |
|   offset = params.pixel_x0 - scanner->calibrations[i].pixel_x0;
 | |
| 
 | |
|   /* calibrator allocation and copy */
 | |
|   if (scanner->calibrations[i].red!=NULL)
 | |
|     {
 | |
|       status =
 | |
| 	gt68xx_calibrator_create_copy (&(scanner->cal_r),
 | |
| 				       scanner->calibrations[i].red,
 | |
| 				       params.pixel_xs,
 | |
|                                        offset);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1,
 | |
| 	       "gt68xx_assign_calibration: failed to create calibrator: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (scanner->calibrations[i].green!=NULL)
 | |
|     {
 | |
|       status =
 | |
| 	gt68xx_calibrator_create_copy (&(scanner->cal_g),
 | |
| 				       scanner->calibrations[i].green,
 | |
| 				       params.pixel_xs,
 | |
| 				       offset);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1,
 | |
| 	       "gt68xx_assign_calibration: failed to create calibrator: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (scanner->calibrations[i].blue!=NULL)
 | |
|     {
 | |
|       status =
 | |
| 	gt68xx_calibrator_create_copy (&(scanner->cal_b),
 | |
| 				       scanner->calibrations[i].blue,
 | |
| 				       params.pixel_xs,
 | |
| 				       offset);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1,
 | |
| 	       "gt68xx_assign_calibration: failed to create calibrator: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (scanner->calibrations[i].gray!=NULL)
 | |
|     {
 | |
|       status =
 | |
| 	gt68xx_calibrator_create_copy (&(scanner->cal_gray),
 | |
| 				       scanner->calibrations[i].gray,
 | |
| 				       params.pixel_xs,
 | |
| 				       offset);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1,
 | |
| 	       "gt68xx_assign_calibration: failed to create calibrator: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   DBG (3, "gt68xx_assign_calibration: end.\n");
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static char *gt68xx_calibration_file(GT68xx_Scanner * scanner)
 | |
| {
 | |
|   char *ptr=NULL;
 | |
|   char tmp_str[PATH_MAX];
 | |
| 
 | |
|   ptr=getenv("HOME");
 | |
|   if(ptr!=NULL)
 | |
|     {
 | |
|       sprintf (tmp_str, "%s/.sane/gt68xx-%s.cal", ptr, scanner->dev->model->name);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       ptr=getenv("TMPDIR");
 | |
|       if(ptr!=NULL)
 | |
|         {
 | |
|           sprintf (tmp_str, "%s/gt68xx-%s.cal", ptr, scanner->dev->model->name);
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           sprintf (tmp_str, "/tmp/gt68xx-%s.cal", scanner->dev->model->name);
 | |
|         }
 | |
|     }
 | |
|   DBG(5,"gt68xx_calibration_file: using >%s< for calibration file name\n",tmp_str);
 | |
|   return strdup(tmp_str);
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_clear_calibration (GT68xx_Scanner * scanner)
 | |
| {
 | |
|   char *fname;
 | |
|   int i;
 | |
| 
 | |
|   if (scanner->calibrated == SANE_FALSE)
 | |
|     return SANE_STATUS_GOOD;
 | |
| 
 | |
|   /* clear file */
 | |
|   fname = gt68xx_calibration_file (scanner);
 | |
|   unlink (fname);
 | |
|   free (fname);
 | |
| 
 | |
|   /* free calibrators */
 | |
|   for (i = 0; i < MAX_RESOLUTIONS && scanner->calibrations[i].dpi > 0; i++)
 | |
|     {
 | |
|       scanner->calibrations[i].dpi = 0;
 | |
|       if (scanner->calibrations[i].red)
 | |
| 	gt68xx_calibrator_free (scanner->calibrations[i].red);
 | |
|       if (scanner->calibrations[i].green)
 | |
| 	gt68xx_calibrator_free (scanner->calibrations[i].green);
 | |
|       if (scanner->calibrations[i].blue)
 | |
| 	gt68xx_calibrator_free (scanner->calibrations[i].blue);
 | |
|       if (scanner->calibrations[i].gray)
 | |
| 	gt68xx_calibrator_free (scanner->calibrations[i].gray);
 | |
|     }
 | |
| 
 | |
|   /* reset flags */
 | |
|   scanner->calibrated = SANE_FALSE;
 | |
|   scanner->val[OPT_QUALITY_CAL].w = SANE_FALSE;
 | |
|   scanner->val[OPT_NEED_CALIBRATION_SW].w = SANE_TRUE;
 | |
|   DBG (5, "gt68xx_clear_calibration: done\n");
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_write_calibration (GT68xx_Scanner * scanner)
 | |
| {
 | |
|   char *fname;
 | |
|   FILE *fcal;
 | |
|   int i;
 | |
|   SANE_Int nullwidth = 0;
 | |
| 
 | |
|   if (scanner->calibrated == SANE_FALSE)
 | |
|     {
 | |
|       return SANE_STATUS_GOOD;
 | |
|     }
 | |
| 
 | |
|   /* open file */
 | |
|   fname = gt68xx_calibration_file (scanner);
 | |
|   fcal = fopen (fname, "wb");
 | |
|   free (fname);
 | |
|   if (fcal == NULL)
 | |
|     {
 | |
|       DBG (1,
 | |
| 	   "gt68xx_write_calibration: failed to open calibration file for writing %s\n",
 | |
| 	   strerror (errno));
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
| 
 | |
|   /* TODO we save check endianness and word alignment in case of a home
 | |
|    * directory used trough different archs */
 | |
|   fwrite (&(scanner->afe_params), sizeof (GT68xx_AFE_Parameters), 1, fcal);
 | |
|   fwrite (&(scanner->exposure_params), sizeof (GT68xx_Exposure_Parameters), 1,
 | |
| 	  fcal);
 | |
|   for (i = 0; i < MAX_RESOLUTIONS && scanner->calibrations[i].dpi > 0; i++)
 | |
|     {
 | |
|       DBG (1, "gt68xx_write_calibration: saving %d dpi calibration\n",
 | |
| 	   scanner->calibrations[i].dpi);
 | |
|       fwrite (&(scanner->calibrations[i].dpi), sizeof (SANE_Int), 1, fcal);
 | |
|       fwrite (&(scanner->calibrations[i].pixel_x0), sizeof (SANE_Int), 1,
 | |
| 	      fcal);
 | |
| 
 | |
|       fwrite (&(scanner->calibrations[i].red->width), sizeof (SANE_Int), 1,
 | |
| 	      fcal);
 | |
|       fwrite (&(scanner->calibrations[i].red->white_level), sizeof (SANE_Int),
 | |
| 	      1, fcal);
 | |
|       fwrite (scanner->calibrations[i].red->k_white, sizeof (unsigned int),
 | |
| 	      scanner->calibrations[i].red->width, fcal);
 | |
|       fwrite (scanner->calibrations[i].red->k_black, sizeof (unsigned int),
 | |
| 	      scanner->calibrations[i].red->width, fcal);
 | |
|       fwrite (scanner->calibrations[i].red->white_line, sizeof (double),
 | |
| 	      scanner->calibrations[i].red->width, fcal);
 | |
|       fwrite (scanner->calibrations[i].red->black_line, sizeof (double),
 | |
| 	      scanner->calibrations[i].red->width, fcal);
 | |
| 
 | |
|       fwrite (&(scanner->calibrations[i].green->width), sizeof (SANE_Int), 1,
 | |
| 	      fcal);
 | |
|       fwrite (&(scanner->calibrations[i].green->white_level),
 | |
| 	      sizeof (SANE_Int), 1, fcal);
 | |
|       fwrite (scanner->calibrations[i].green->k_white, sizeof (unsigned int),
 | |
| 	      scanner->calibrations[i].green->width, fcal);
 | |
|       fwrite (scanner->calibrations[i].green->k_black, sizeof (unsigned int),
 | |
| 	      scanner->calibrations[i].green->width, fcal);
 | |
|       fwrite (scanner->calibrations[i].green->white_line, sizeof (double),
 | |
| 	      scanner->calibrations[i].green->width, fcal);
 | |
|       fwrite (scanner->calibrations[i].green->black_line, sizeof (double),
 | |
| 	      scanner->calibrations[i].green->width, fcal);
 | |
| 
 | |
|       fwrite (&(scanner->calibrations[i].blue->width), sizeof (SANE_Int), 1,
 | |
| 	      fcal);
 | |
|       fwrite (&(scanner->calibrations[i].blue->white_level),
 | |
| 	      sizeof (SANE_Int), 1, fcal);
 | |
|       fwrite (scanner->calibrations[i].blue->k_white, sizeof (unsigned int),
 | |
| 	      scanner->calibrations[i].blue->width, fcal);
 | |
|       fwrite (scanner->calibrations[i].blue->k_black, sizeof (unsigned int),
 | |
| 	      scanner->calibrations[i].blue->width, fcal);
 | |
|       fwrite (scanner->calibrations[i].blue->white_line, sizeof (double),
 | |
| 	      scanner->calibrations[i].blue->width, fcal);
 | |
|       fwrite (scanner->calibrations[i].blue->black_line, sizeof (double),
 | |
| 	      scanner->calibrations[i].blue->width, fcal);
 | |
| 
 | |
|       if (scanner->calibrations[i].gray != NULL)
 | |
| 	{
 | |
| 	  fwrite (&(scanner->calibrations[i].gray->width), sizeof (SANE_Int),
 | |
| 		  1, fcal);
 | |
| 	  fwrite (&(scanner->calibrations[i].gray->white_level),
 | |
| 		  sizeof (SANE_Int), 1, fcal);
 | |
| 	  fwrite (scanner->calibrations[i].gray->k_white,
 | |
| 		  sizeof (unsigned int), scanner->calibrations[i].gray->width,
 | |
| 		  fcal);
 | |
| 	  fwrite (scanner->calibrations[i].gray->k_black,
 | |
| 		  sizeof (unsigned int), scanner->calibrations[i].gray->width,
 | |
| 		  fcal);
 | |
| 	  fwrite (scanner->calibrations[i].gray->white_line, sizeof (double),
 | |
| 		  scanner->calibrations[i].gray->width, fcal);
 | |
| 	  fwrite (scanner->calibrations[i].gray->black_line, sizeof (double),
 | |
| 		  scanner->calibrations[i].gray->width, fcal);
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  fwrite (&nullwidth, sizeof (SANE_Int), 1, fcal);
 | |
| 	}
 | |
|     }
 | |
|   DBG (5, "gt68xx_write_calibration: wrote %d calibrations\n", i);
 | |
| 
 | |
|   fclose (fcal);
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| gt68xx_read_calibration (GT68xx_Scanner * scanner)
 | |
| {
 | |
|   char *fname;
 | |
|   FILE *fcal;
 | |
|   int i;
 | |
|   SANE_Int width, level;
 | |
| 
 | |
|   scanner->calibrated = SANE_FALSE;
 | |
|   fname = gt68xx_calibration_file (scanner);
 | |
|   fcal = fopen (fname, "rb");
 | |
|   free (fname);
 | |
|   if (fcal == NULL)
 | |
|     {
 | |
|       DBG (1,
 | |
| 	   "gt68xx_read_calibration: failed to open calibration file for reading %s\n",
 | |
| 	   strerror (errno));
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
| 
 | |
|   /* TODO we should check endiannes and word alignment in case of a home
 | |
|    * directory used trough different archs */
 | |
| 
 | |
|   /* TODO check for errors */
 | |
|   fread (&(scanner->afe_params), sizeof (GT68xx_AFE_Parameters), 1, fcal);
 | |
|   fread (&(scanner->exposure_params), sizeof (GT68xx_Exposure_Parameters), 1,
 | |
| 	 fcal);
 | |
| 
 | |
|   /* loop on calibrators */
 | |
|   i = 0;
 | |
|   fread (&(scanner->calibrations[i].dpi), sizeof (SANE_Int), 1, fcal);
 | |
|   while (!feof (fcal) && scanner->calibrations[i].dpi > 0)
 | |
|     {
 | |
|       fread (&(scanner->calibrations[i].pixel_x0), sizeof (SANE_Int), 1,
 | |
| 	     fcal);
 | |
| 
 | |
|       fread (&width, sizeof (SANE_Int), 1, fcal);
 | |
|       fread (&level, sizeof (SANE_Int), 1, fcal);
 | |
|       gt68xx_calibrator_new (width, level, &(scanner->calibrations[i].red));
 | |
|       fread (scanner->calibrations[i].red->k_white, sizeof (unsigned int),
 | |
| 	     width, fcal);
 | |
|       fread (scanner->calibrations[i].red->k_black, sizeof (unsigned int),
 | |
| 	     width, fcal);
 | |
|       fread (scanner->calibrations[i].red->white_line, sizeof (double), width,
 | |
| 	     fcal);
 | |
|       fread (scanner->calibrations[i].red->black_line, sizeof (double), width,
 | |
| 	     fcal);
 | |
| 
 | |
|       fread (&width, sizeof (SANE_Int), 1, fcal);
 | |
|       fread (&level, sizeof (SANE_Int), 1, fcal);
 | |
|       gt68xx_calibrator_new (width, level, &(scanner->calibrations[i].green));
 | |
|       fread (scanner->calibrations[i].green->k_white, sizeof (unsigned int),
 | |
| 	     width, fcal);
 | |
|       fread (scanner->calibrations[i].green->k_black, sizeof (unsigned int),
 | |
| 	     width, fcal);
 | |
|       fread (scanner->calibrations[i].green->white_line, sizeof (double),
 | |
| 	     width, fcal);
 | |
|       fread (scanner->calibrations[i].green->black_line, sizeof (double),
 | |
| 	     width, fcal);
 | |
| 
 | |
|       fread (&width, sizeof (SANE_Int), 1, fcal);
 | |
|       fread (&level, sizeof (SANE_Int), 1, fcal);
 | |
|       gt68xx_calibrator_new (width, level, &(scanner->calibrations[i].blue));
 | |
|       fread (scanner->calibrations[i].blue->k_white, sizeof (unsigned int),
 | |
| 	     width, fcal);
 | |
|       fread (scanner->calibrations[i].blue->k_black, sizeof (unsigned int),
 | |
| 	     width, fcal);
 | |
|       fread (scanner->calibrations[i].blue->white_line, sizeof (double),
 | |
| 	     width, fcal);
 | |
|       fread (scanner->calibrations[i].blue->black_line, sizeof (double),
 | |
| 	     width, fcal);
 | |
| 
 | |
|       fread (&width, sizeof (SANE_Int), 1, fcal);
 | |
|       if (width > 0)
 | |
| 	{
 | |
| 	  fread (&level, sizeof (SANE_Int), 1, fcal);
 | |
| 	  gt68xx_calibrator_new (width, level,
 | |
| 				 &(scanner->calibrations[i].gray));
 | |
| 	  fread (scanner->calibrations[i].gray->k_white,
 | |
| 		 sizeof (unsigned int), width, fcal);
 | |
| 	  fread (scanner->calibrations[i].gray->k_black,
 | |
| 		 sizeof (unsigned int), width, fcal);
 | |
| 	  fread (scanner->calibrations[i].gray->white_line, sizeof (double),
 | |
| 		 width, fcal);
 | |
| 	  fread (scanner->calibrations[i].gray->black_line, sizeof (double),
 | |
| 		 width, fcal);
 | |
| 	}
 | |
|       /* prepare for nex resolution */
 | |
|       i++;
 | |
|       fread (&(scanner->calibrations[i].dpi), sizeof (SANE_Int), 1, fcal);
 | |
|     }
 | |
| 
 | |
|   DBG (5, "gt68xx_read_calibration: read %d calibrations\n", i);
 | |
|   fclose (fcal);
 | |
| 
 | |
|   scanner->val[OPT_QUALITY_CAL].w = SANE_TRUE;
 | |
|   scanner->val[OPT_NEED_CALIBRATION_SW].w = SANE_FALSE;
 | |
|   scanner->calibrated = SANE_TRUE;
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
 |