1 /* sane - Scanner Access Now Easy.
2 Copyright (C) Marian Eichholz 2001
3 This file is part of the SANE package.
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
17
18 As a special exception, the authors of SANE give permission for
19 additional uses of the libraries contained in this release of SANE.
20
21 The exception is that, if you link a SANE library with other files
22 to produce an executable, this does not by itself cause the
23 resulting executable to be covered by the GNU General Public
24 License. Your use of that executable is in no way restricted on
25 account of linking the SANE library code into it.
26
27 This exception does not, however, invalidate any other reasons why
28 the executable file might be covered by the GNU General Public
29 License.
30
31 If you submit changes to SANE to the maintainers to be included in
32 a subsequent release, you agree by submitting the changes that
33 those changes may be distributed with this exception intact.
34
35 If you write modifications of your own for SANE, it is your choice
36 whether to permit this exception to apply to your modifications.
37 If you do not wish that, delete this exception notice.
38 */
39
40 /* ======================================================================
41
42 Userspace scan tool for the Microtek 3600 scanner
43
44 color scan routine
45
46 (C) Marian Eichholz 2001
47
48 ====================================================================== */
49
50 #include "sm3600-scantool.h"
51
52 #define ORDER_RGB "012"
53 #define ORDER_BRG "120"
54
55 /* **********************************************************************
56
57 ReadNextColorLine()
58
59 ********************************************************************** */
60
ReadNextColorLine(PTInstance this)61 static TState ReadNextColorLine(PTInstance this)
62 {
63 int iWrite,i;
64 int iRead; /* read position in raw line */
65 int nInterpolator;
66 int iOffsetR,iOffsetG,iOffsetB;
67 short *pchLineSwap;
68 TBool bVisible;
69
70 bVisible=false;
71 do {
72 iWrite=0;
73 while (iWrite<3*this->state.cxMax) /* max 1 time in reality */
74 {
75 while (iWrite<3*this->state.cxMax &&
76 this->state.iBulkReadPos<this->state.cchBulk)
77 this->state.ppchLines[0][iWrite++] =
78 this->state.pchBuf[this->state.iBulkReadPos++];
79 if (iWrite<3*this->state.cxMax) /* we need an additional chunk */
80 {
81 if (this->state.bLastBulk)
82 return SANE_STATUS_EOF;
83 this->state.cchBulk=BulkReadBuffer(this,this->state.pchBuf,
84 USB_CHUNK_SIZE);
85 dprintf(DEBUG_SCAN,"bulk read: %d byte(s), line #%d\n",
86 this->state.cchBulk, this->state.iLine);
87 if (this->bWriteRaw)
88 fwrite(this->state.pchBuf,1,this->state.cchBulk,this->fhScan);
89 INST_ASSERT();
90 if (this->state.cchBulk!=USB_CHUNK_SIZE)
91 this->state.bLastBulk=true;
92 this->state.iBulkReadPos=0;
93 }
94 } /* while raw line buffer acquiring */
95 this->state.iLine++;
96 if (this->state.iLine>2*this->state.ySensorSkew)
97 {
98 bVisible=true;
99 iOffsetR=(this->state.szOrder[0]-'0')*this->state.cxMax;
100 iOffsetG=(this->state.szOrder[1]-'0')*this->state.cxMax;
101 iOffsetB=(this->state.szOrder[2]-'0')*this->state.cxMax;
102 for (nInterpolator=100, iWrite=0, iRead=0;
103 iRead<3*this->state.cxMax && iWrite<this->state.cchLineOut;
104 iRead++)
105 {
106 nInterpolator+=this->state.nFixAspect;
107 if (nInterpolator<100) continue; /* res. reduction */
108 nInterpolator-=100;
109 /* dprintf(DEBUG_SCAN," i=%d",iTo); */
110 /* the first scan lines only fill the line backlog buffer */
111 {
112 /* dprintf(DEBUG_SCAN,"assembling line %d\n",++this->state.iLine); */
113 this->state.pchLineOut[iWrite++]=
114 this->state.ppchLines[2*this->state.ySensorSkew][iRead+iOffsetR];
115 this->state.pchLineOut[iWrite++]=
116 this->state.ppchLines[1*this->state.ySensorSkew][iRead+iOffsetG];
117 this->state.pchLineOut[iWrite++]=
118 this->state.ppchLines[0*this->state.ySensorSkew][iRead+iOffsetB];
119 }
120 }
121 } /* if visible line */
122 /* cycle backlog buffers */
123 pchLineSwap=this->state.ppchLines[this->state.cBacklog-1];
124 for (i=this->state.cBacklog-2; i>=0; i--)
125 this->state.ppchLines[i+1]=this->state.ppchLines[i];
126 this->state.ppchLines[0]=pchLineSwap;
127 } while (!bVisible);
128 return SANE_STATUS_GOOD;
129 }
130
131 /* ======================================================================
132
133 StartScanColor()
134
135 ====================================================================== */
136
137 /* Parameter are in resolution units! */
138 __SM3600EXPORT__
StartScanColor(TInstance *this)139 TState StartScanColor(TInstance *this)
140 {
141
142 /* live could be easy: Simple calculate a window, start the scan,
143 get the data and get off. It dimply does not work. We have to
144 deal with the fact, that the pixels are reported with color scan
145 line by color scan line, and have to be rearranged to RGB
146 triples. They even ar enot always in RGB order, but mostly in BRG
147 order. And they have a skew of 2/300 inch , given by the slider
148 construction. Thus, we have to deal with several buffers, some
149 interpolation, and related management stuff. */
150 int i;
151 if (this->state.bScanning)
152 return SetError(this,SANE_STATUS_DEVICE_BUSY,"scan active");
153 memset(&(this->state),0,sizeof(this->state));
154 this->state.ReadProc =ReadNextColorLine;
155 this->state.ySensorSkew=0;
156
157 GetAreaSize(this);
158
159 switch (this->param.res)
160 {
161 case 200: this->state.ySensorSkew=1; break;
162 case 300: this->state.ySensorSkew=2; break;
163 case 600: this->state.ySensorSkew=4; break;
164 default: break;
165 }
166 /* since we need 2*this->state.ySensorSkew additional scan lines for de-skewing of
167 the sensor lines, we enlarge the window and shorten the initial movement
168 accordingly */
169 this->state.cyTotalPath =
170 this->param.y/2-(2*this->state.ySensorSkew)*600/this->param.res;
171 DoJog(this,this->state.cyTotalPath); INST_ASSERT();
172 this->state.cyTotalPath +=
173 (this->state.cyPixel+2*this->state.ySensorSkew)
174 *600/this->param.res; /* for jogging back */
175
176 /*
177 regular scan is asynchronously, that is,
178 the scanning is issued, and the driver does bulk reads,
179 until there are no data left.
180 Each line has a full R, G and B subline, 8bit each sample.
181 */
182 {
183 unsigned char uchRegs[]={
184 0xFC /*!!R_SPOS!!*/, 0x00 /*R_SPOSH*/, 0x24 /*!!0x03!!*/,
185 0xB0 /*!!R_SWID!!*/, 0xC4 /*!!R_SWIDH!!*/,
186 1,0,
187 0xFF /*!!0x08!!*/, 0xFF /*!!0x09!!*/,
188 0x22 /*!!R_LEN!!*/, 0x07 /*!!R_LENH!!*/, 0x6D /*0x0C*/,
189 0x70 /*0x0D*/, 0x69 /*0x0E*/, 0xD0 /*0x0F*/,
190 0x00 /*0x10*/, 0x00 /*0x11*/, 0x42 /*!!0x12!!*/,
191 0x15 /*0x13*/, 0x84 /*!!0x14!!*/, 0x2A /*0x15*/,
192 0xC5 /*!!0x16!!*/, 0x40 /*0x17*/, 0xC5 /*!!0x18!!*/,
193 0x40 /*0x19*/, 0xFF /*0x1A*/, 0x01 /*0x1B*/,
194 0x88 /*0x1C*/, 0x40 /*0x1D*/, 0x4C /*0x1E*/,
195 0x50 /*0x1F*/, 0x00 /*0x20*/, 0x0C /*0x21*/,
196 0x21 /*0x22*/, 0xF0 /*0x23*/, 0x40 /*0x24*/,
197 0x00 /*0x25*/, 0x0A /*0x26*/, 0xF0 /*0x27*/,
198 0x00 /*0x28*/, 0x00 /*0x29*/, 0x4E /*0x2A*/,
199 0xF0 /*0x2B*/, 0x00 /*0x2C*/, 0x00 /*0x2D*/,
200 0x4E /*0x2E*/, 0x80 /*R_CCAL*/, 0x80 /*R_CCAL2*/,
201 0x80 /*R_CCAL3*/, 0x0B /*0x32*/, 0x2D /*0x33*/,
202 0x43 /*!!0x34!!*/, 0x29 /*0x35*/, 0x00 /*0x36*/,
203 0x00 /*0x37*/, 0x00 /*0x38*/, 0x00 /*0x39*/,
204 0x00 /*0x3A*/, 0x00 /*0x3B*/, 0xFF /*0x3C*/,
205 0x0F /*0x3D*/, 0x00 /*0x3E*/, 0x00 /*0x3F*/,
206 0x01 /*0x40*/, 0x00 /*0x41*/, 0x80 /*R_CSTAT*/,
207 0x03 /*0x43*/, 0x01 /*R_LMP*/, 0x00 /*0x45*/,
208 0x39 /*R_CTL*/, 0xC5 /*!!0x47!!*/, 0x40 /*0x48*/,
209 0x9E /*0x49*/, 0x8C /*0x4A*/ };
210 RegWriteArray(this,R_ALL, NUM_SCANREGS, uchRegs);
211 RegWrite(this,R_SPOS, 2, this->param.x/2 + this->calibration.xMargin);
212 RegWrite(this,R_SLEN, 2,
213 this->state.cyWindow+
214 (2*this->state.ySensorSkew)*600/this->param.res);
215 this->state.szOrder=ORDER_BRG;
216 RegWrite(this,R_CCAL, 3, this->calibration.rgbBias); INST_ASSERT(); /* 0xBBGGRR */
217 switch (this->param.res)
218 {
219 case 75:
220 RegWrite(this,R_XRES,1, 0x20); /* ups, can do only 100 dpi horizontal */
221 RegWrite(this,R_SWID, 2, 0xC000 | this->state.cxWindow);
222 RegWrite(this,0x34, 1, 0x83); /* halves the vertical resolution */
223 RegWrite(this,0x47,1,0xC0); /* reduces the speed a bit */
224 break;
225 case 100:
226 RegWrite(this,R_XRES,1, 0x20);
227 RegWrite(this,R_SWID, 2, 0xC000 | this->state.cxWindow);
228 RegWrite(this,0x34, 1, 0x63); /* halves the vertical resolution */
229 RegWrite(this,0x47,1,0xC0); /* reduces the speed a bit */
230 /* I have no idea, what these differences are good for. The seem to produce
231 a slight blue presence.
232 RegWrite(this,0x16, 1, 0xC0); RegWrite(this,0x18, 1, 0xC0);
233 RegWrite(this,0x12, 1, 0x40); RegWrite(this,0x10, 2, 0x0728);
234 RegWrite(this,0x14, 1, 0x80); */
235 break;
236 case 200:
237 RegWrite(this,R_XRES,1, 0x24);
238 RegWrite(this,R_SWID, 2, 0xC000 | this->state.cxWindow);
239 break;
240 case 300:
241 RegWrite(this,0x08,2, 0x6A6A);
242 RegWrite(this,R_XRES,1, 0x2A);
243 RegWrite(this,R_SWID, 2, 0x4000 | this->state.cxWindow);
244 RegWrite(this,0x34, 1, 0x03); /* halves the vertical resolution */
245 RegWrite(this,0x47,1,0xC0); /* reduces the speed a bit */
246 this->state.szOrder=ORDER_RGB;
247 break;
248 case 600:
249 RegWrite(this,R_XRES,1, 0x3F);
250 RegWrite(this,R_SWID, 2, 0xC000 | this->state.cxWindow);
251 RegWrite(this,0x34, 1, 0x03); /* halves the vertical resolution */
252 RegWrite(this,0x47,1,0xC2); /* reduces the speed a bit */
253 break;
254 case 1200:
255 /* not supported, since the driver supports only 600 dpi in color */
256 break;
257 }
258 }
259
260 /* setup gamma tables */
261 RegWrite(this,0x41,1,0x03); /* gamma, RGB */
262 RegWrite(this,0x40,1,0x28); /* offset FIFO 8*3 (GAMMA)+16 KB(gain) spared */
263 /*
264 hey, surprise: Although the color lines are sent in a strange order,
265 the gamma tables are mapped constantly to the sensors (i.e. RGB)
266 */
267 UploadGammaTable(this,0x0000,this->agammaR);
268 UploadGammaTable(this,0x2000,this->agammaG);
269 UploadGammaTable(this,0x4000,this->agammaB);
270 INST_ASSERT();
271
272 UploadGainCorrection(this,0x6000);
273 INST_ASSERT();
274
275 /* enough for 1/100 inch sensor distance */
276 this->state.cBacklog=1+2*this->state.ySensorSkew;
277
278 /* allocate raw line buffers */
279 this->state.ppchLines=calloc(this->state.cBacklog,sizeof(short*));
280 this->state.pchBuf=malloc(0x8000);
281 if (!this->state.ppchLines || !this->state.pchBuf)
282 return FreeState(this,SetError(this,
283 SANE_STATUS_NO_MEM,"no buffers available"));
284
285 for (i=0; i<this->state.cBacklog; i++)
286 {
287 this->state.ppchLines[i]=calloc(1,3*this->state.cxMax*sizeof(short)); /* must be less than 0x8000 */
288 if (!this->state.ppchLines[i])
289 return FreeState(this,
290 SetError(this,SANE_STATUS_NO_MEM,
291 "no line buffer available"));
292 }
293
294 /* calculate and prepare intermediate line transfer buffer */
295
296 this->state.cchLineOut=3*this->state.cxPixel;
297 this->state.pchLineOut = malloc(this->state.cchLineOut);
298 if (!this->state.pchLineOut)
299 return FreeState(this,SetError(this,
300 SANE_STATUS_NO_MEM,
301 "no buffers available"));
302
303 RegWrite(this,R_CTL, 1, 0x39); /* #1532[005.0] */
304 RegWrite(this,R_CTL, 1, 0x79); /* #1533[005.0] */
305 RegWrite(this,R_CTL, 1, 0xF9); /* #1534[005.0] */
306 INST_ASSERT();
307
308 this->state.bScanning = true;
309 return SANE_STATUS_GOOD;
310 }
311