summaryrefslogtreecommitdiff
path: root/backend/hp3500.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2017-07-15 11:25:39 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2017-07-15 11:25:39 +0200
commit1edb02101a9306fc711cd422ed507d18165b1691 (patch)
treebd2d48a139bfbe869f4f49359b63097931a45e7b /backend/hp3500.c
parent2ca8a81bd0d99fe4d75c229d0e988d8ef710285f (diff)
move from support/1.0.27 to feature/1.0.27
Diffstat (limited to 'backend/hp3500.c')
-rw-r--r--backend/hp3500.c836
1 files changed, 603 insertions, 233 deletions
diff --git a/backend/hp3500.c b/backend/hp3500.c
index 48a8035..26fe071 100644
--- a/backend/hp3500.c
+++ b/backend/hp3500.c
@@ -84,6 +84,7 @@
#include <string.h>
#include <ctype.h>
#include <time.h>
+#include <math.h>
#include <sys/types.h>
#include <unistd.h>
@@ -145,6 +146,7 @@ enum hp3500_option
OPT_MODE,
OPT_BRIGHTNESS,
OPT_CONTRAST,
+ OPT_GAMMA,
NUM_OPTIONS
};
@@ -189,6 +191,8 @@ struct hp3500_data
int brightness;
int contrast;
+ double gamma;
+
SANE_Option_Descriptor opt[NUM_OPTIONS];
SANE_Device sane;
};
@@ -218,6 +222,8 @@ static const SANE_Range range_brightness =
{ 0, 255, 0 };
static const SANE_Range range_contrast =
{ 0, 255, 0 };
+static const SANE_Range range_gamma =
+ { SANE_FIX (0.2), SANE_FIX(4.0), SANE_FIX(0.01) };
#define HP3500_COLOR_SCAN 0
@@ -233,6 +239,7 @@ static int reader_process (void *);
static void calculateDerivedValues (struct hp3500_data *scanner);
static void do_reset (struct hp3500_data *scanner);
static void do_cancel (struct hp3500_data *scanner);
+static size_t max_string_size(char const **);
/*
* used by sane_get_devices
@@ -382,6 +389,7 @@ sane_open (SANE_String_Const name, SANE_Handle * handle)
scanner->mode = 0;
scanner->brightness = 128;
scanner->contrast = 64;
+ scanner->gamma = 2.2;
calculateDerivedValues (scanner);
return SANE_STATUS_GOOD;
@@ -536,6 +544,10 @@ sane_control_option (SANE_Handle handle, SANE_Int option,
*(SANE_Word *) val = scanner->contrast;
return SANE_STATUS_GOOD;
+ case OPT_GAMMA:
+ *(SANE_Word *) val = SANE_FIX(scanner->gamma);
+ return SANE_STATUS_GOOD;
+
case OPT_BRIGHTNESS:
*(SANE_Word *) val = scanner->brightness;
return SANE_STATUS_GOOD;
@@ -649,6 +661,10 @@ sane_control_option (SANE_Handle handle, SANE_Int option,
case OPT_CONTRAST:
scanner->contrast = *(SANE_Word *) val;
return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA:
+ scanner->gamma = SANE_UNFIX(*(SANE_Word *) val);
+ return SANE_STATUS_GOOD;
} /* switch */
} /* else */
return SANE_STATUS_INVAL;
@@ -703,7 +719,7 @@ sane_start (SANE_Handle handle)
scanner->reader_pid = sanei_thread_begin (reader_process, scanner);
time (&scanner->last_scan);
- if (scanner->reader_pid == -1)
+ if (!sanei_thread_is_valid (scanner->reader_pid))
{
DBG (MSG_ERR, "cannot fork reader process.\n");
DBG (MSG_ERR, "%s", strerror (errno));
@@ -952,7 +968,7 @@ attachScanner (const char *devicename)
dev->devicename = strdup (devicename);
dev->sfd = -1;
dev->last_scan = 0;
- dev->reader_pid = -1;
+ dev->reader_pid = (SANE_Pid) -1;
dev->pipe_r = dev->pipe_w = -1;
dev->sane.name = dev->devicename;
@@ -1064,8 +1080,9 @@ init_options (struct hp3500_data *scanner)
opt->title = SANE_TITLE_SCAN_MODE;
opt->desc = SANE_DESC_SCAN_MODE;
opt->type = SANE_TYPE_STRING;
+ opt->size = max_string_size(scan_mode_list);
opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
- opt->constraint.string_list = scan_mode_list;
+ opt->constraint.string_list = (SANE_String_Const *) scan_mode_list;
opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
opt = scanner->opt + OPT_BRIGHTNESS;
@@ -1086,6 +1103,16 @@ init_options (struct hp3500_data *scanner)
opt->constraint.range = &range_contrast;
opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opt = scanner->opt + OPT_GAMMA;
+ opt->name = SANE_NAME_ANALOG_GAMMA;
+ opt->title = SANE_TITLE_ANALOG_GAMMA;
+ opt->desc = SANE_DESC_ANALOG_GAMMA;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &range_gamma;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
return SANE_STATUS_GOOD;
}
@@ -1098,7 +1125,7 @@ do_reset (struct hp3500_data *scanner)
static void
do_cancel (struct hp3500_data *scanner)
{
- if (scanner->reader_pid != -1)
+ if (sanei_thread_is_valid (scanner->reader_pid))
{
if (sanei_thread_kill (scanner->reader_pid) == 0)
@@ -1749,14 +1776,14 @@ rt_set_basic_calibration (unsigned char *regs,
int greengain,
int blueoffset1, int blueoffset2, int bluegain)
{
- regs[0x05] = redoffset1;
- regs[0x02] = redoffset2;
+ regs[0x02] = redoffset1;
+ regs[0x05] = redoffset2;
regs[0x08] = redgain;
- regs[0x06] = greenoffset1;
- regs[0x03] = greenoffset2;
+ regs[0x03] = greenoffset1;
+ regs[0x06] = greenoffset2;
regs[0x09] = greengain;
- regs[0x07] = blueoffset1;
- regs[0x04] = blueoffset2;
+ regs[0x04] = blueoffset1;
+ regs[0x07] = blueoffset2;
regs[0x0a] = bluegain;
return 0;
}
@@ -1765,13 +1792,36 @@ static int
rt_set_calibration_addresses (unsigned char *regs,
unsigned redaddr,
unsigned greenaddr,
- unsigned blueaddr, unsigned endaddr)
+ unsigned blueaddr,
+ unsigned endaddr,
+ unsigned width)
{
+ unsigned endpage = (endaddr + 31) / 32;
+ unsigned scanline_pages = ((width + 1) * 3 + 31) / 32;
+
+ /* Red, green and blue detailed calibration addresses */
+
regs[0x84] = redaddr;
regs[0x8e] = (regs[0x8e] & 0x0f) | ((redaddr >> 4) & 0xf0);
rt_set_value_lsbfirst (regs, 0x85, 2, greenaddr);
rt_set_value_lsbfirst (regs, 0x87, 2, blueaddr);
- rt_set_value_lsbfirst (regs, 0x89, 2, (endaddr + 31) / 32);
+
+ /* I don't know what the next three are used for, but each buffer commencing
+ * at 0x80 and 0x82 needs to hold a full scan line.
+ */
+
+ rt_set_value_lsbfirst (regs, 0x80, 2, endpage);
+ rt_set_value_lsbfirst (regs, 0x82, 2, endpage + scanline_pages);
+ rt_set_value_lsbfirst (regs, 0x89, 2, endpage + scanline_pages * 2);
+
+ /* I don't know what this is, but it seems to be a number of pages that can hold
+ * 16 complete scan lines, but not calculated as an offset from any other page
+ */
+
+ rt_set_value_lsbfirst (regs, 0x51, 2, (48 * (width + 1) + 31) / 32);
+
+ /* I don't know what this is either, but this is what the Windows driver does */
+ rt_set_value_lsbfirst (regs, 0x8f, 2, 0x1c00);
return 0;
}
@@ -1798,6 +1848,13 @@ rt_set_data_feed_on (unsigned char *regs)
}
static int
+rt_set_data_feed_off (unsigned char *regs)
+{
+ regs[0xb2] |= 0x04;
+ return 0;
+}
+
+static int
rt_enable_ccd (unsigned char *regs, int enable)
{
if (enable)
@@ -2245,10 +2302,14 @@ rt_nvram_read (int block, int location, unsigned char *data, int bytes)
return 0;
}
+/* This is what we want as the initial registers, not what they
+ * are at power on time. In particular 13 bytes at 0x10 are
+ * different, and the byte at 0x94 is different.
+ */
static unsigned char initial_regs[] = {
/* 0x00 */ 0xf5, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x08 */ 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00,
- /* 0x10 */ 0xe1, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc,
+ /* 0x10 */ 0x81, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
/* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
/* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x19,
@@ -2264,7 +2325,7 @@ static unsigned char initial_regs[] = {
/* 0x78 */ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x80 */ 0x0f, 0x02, 0x4b, 0x02, 0x00, 0xec, 0x19, 0xd8,
/* 0x88 */ 0x2d, 0x87, 0x02, 0xff, 0x3f, 0x78, 0x60, 0x00,
- /* 0x90 */ 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x90 */ 0x1c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
/* 0x98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0xa0 */ 0x00, 0x00, 0x00, 0x0c, 0x27, 0x64, 0x00, 0x00,
/* 0xa8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -2363,14 +2424,14 @@ static struct resolution_parameters resparms[] = {
/* My values - all work */
/*res r39 rC3 rC6 freq cph0s rgo gbo intra mmcm d3 tg stepsz */
{1200, 3, 6, 4, 2, 1, 22, 22, 4, 2, 1, RT_NORMAL_TG, 0x157b},
- {600, 15, 6, 4, 1, 0, 9, 10, 0, 2, 1, RT_NORMAL_TG, 0x055e},
+ {600, 15, 6, 4, 1, 1, 9, 10, 0, 2, 1, RT_NORMAL_TG, 0x055e},
{400, 3, 1, 4, 1, 1, 6, 6, 1, 2, 1, RT_NORMAL_TG, 0x157b},
- {300, 15, 3, 4, 1, 0, 5, 4, 0, 2, 1, RT_NORMAL_TG, 0x02af},
- {200, 7, 1, 4, 1, 0, 3, 3, 0, 2, 1, RT_NORMAL_TG, 0x055e},
- {150, 15, 3, 1, 1, 0, 2, 2, 0, 2, 1, RT_NORMAL_TG, 0x02af},
- {100, 3, 1, 3, 1, 0, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x0abd},
- {75, 15, 3, 3, 1, 0, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x02af},
- {50, 15, 1, 1, 1, 0, 0, 0, 0, 2, 1, RT_NORMAL_TG, 0x055e},
+ {300, 15, 3, 4, 1, 1, 5, 4, 0, 2, 1, RT_NORMAL_TG, 0x02af},
+ {200, 7, 1, 4, 1, 1, 3, 3, 0, 2, 1, RT_NORMAL_TG, 0x055e},
+ {150, 15, 3, 1, 1, 1, 2, 2, 0, 2, 1, RT_NORMAL_TG, 0x02af},
+ {100, 3, 1, 3, 1, 1, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x0abd},
+ {75, 15, 3, 3, 1, 1, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x02af},
+ {50, 15, 1, 1, 1, 1, 0, 0, 0, 2, 1, RT_NORMAL_TG, 0x055e},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
@@ -2597,6 +2658,90 @@ constrain (int val, int min, int max)
return val;
}
+#if 0
+static void
+sram_dump_byte(FILE *fp,
+ unsigned char const *left,
+ unsigned leftstart,
+ unsigned leftlimit,
+ unsigned char const *right,
+ unsigned rightstart,
+ unsigned rightlimit,
+ unsigned idx)
+{
+ unsigned ridx = rightstart + idx;
+ unsigned lidx = leftstart + idx;
+
+ putc(' ', fp);
+ if (rightstart < rightlimit && leftstart < leftlimit && left[lidx] != right[ridx])
+ fputs("<b>", fp);
+ if (leftstart < leftlimit)
+ fprintf(fp, "%02x", left[lidx]);
+ else
+ fputs(" ", fp);
+ if (rightstart < rightlimit && leftstart < leftlimit && left[lidx] != right[ridx])
+ fputs("</b>", fp);
+}
+
+static void
+dump_sram_to_file(char const *fname,
+ unsigned char const *expected,
+ unsigned end_calibration_offset)
+{
+ FILE *fp = fopen(fname, "w");
+ rt_set_sram_page(0);
+
+ if (fp)
+ {
+ unsigned char buf[1024];
+ unsigned loc = 0;
+
+ fprintf(fp, "<html><head></head><body><pre>\n");
+ while (loc < end_calibration_offset)
+ {
+ unsigned byte = 0;
+
+ rt_read_sram(1024, buf);
+
+ while (byte < 1024)
+ {
+ unsigned idx = 0;
+
+ fprintf(fp, "%06x:", loc);
+ do
+ {
+ sram_dump_byte(fp, buf, byte, 1024, expected, loc, end_calibration_offset, idx);
+ } while (++idx & 0x7);
+ fprintf(fp, " -");
+ do
+ {
+ sram_dump_byte(fp, buf, byte, 1024, expected, loc, end_calibration_offset, idx);
+ } while (++idx & 0x7);
+
+ idx = 0;
+ fputs(" ", fp);
+
+ do
+ {
+ sram_dump_byte(fp, expected, loc, end_calibration_offset, buf, byte, 1024, idx);
+ } while (++idx & 0x7);
+ fprintf(fp, " -");
+ do
+ {
+ sram_dump_byte(fp, expected, loc, end_calibration_offset, buf, byte, 1024, idx);
+ } while (++idx & 0x7);
+
+
+ fputs("\n", fp);
+ byte += 16;
+ loc += 16;
+ }
+ }
+ fprintf(fp, "</pre></body></html>");
+ fclose(fp);
+ }
+}
+#endif
static int
rts8801_doscan (unsigned width,
@@ -2610,13 +2755,13 @@ rts8801_doscan (unsigned width,
int oddfirst,
unsigned char const *calib_info,
int merged_channels,
- detailed_calibration_data const *detailed_calib_data)
+ double *postprocess_offsets,
+ double *postprocess_gains)
{
unsigned rowbytes = 0;
unsigned output_rowbytes = 0;
unsigned channels = 0;
unsigned total_rows = 0;
- unsigned bytesperchannel;
unsigned char *row_buffer;
unsigned char *output_buffer;
unsigned buffered_rows;
@@ -2638,7 +2783,7 @@ rts8801_doscan (unsigned width,
channels = 3;
rowbytes = width * 3;
- bytesperchannel = width;
+
switch (colour)
{
case HP3500_GRAY_SCAN:
@@ -2710,32 +2855,27 @@ rts8801_doscan (unsigned width,
if (!rows_to_begin || !--rows_to_begin)
{
unsigned char *outnow = output_buffer;
+ unsigned x;
- for (i = 0;
- i < (merged_channels ? rowbytes : width);
- i += merged_channels ? channels : 1)
+ for (i = x = 0;
+ x < width;
+ ++x, i += merged_channels ? channels : 1)
{
for (j = 0; j < channels; ++j)
{
unsigned pix =
(unsigned char) channel_data[j][i & 1][i];
- if (detailed_calib_data)
- {
- unsigned char const *calib_start =
- detailed_calib_data->channeldata[j] +
- 2 *
- detailed_calib_data->
- resolution_divisor * i /
- (merged_channels ? channels : 1);
- pix =
- constrain ((int) pix -
- (int) calib_start[0], 0,
- 255);
- pix =
- constrain (pix * calib_start[1] /
- 0x40, 0, 255);
- }
+ if (postprocess_gains && postprocess_offsets)
+ {
+ int ppidx = j * width + x;
+
+ pix = constrain ( pix
+ * postprocess_gains[ppidx]
+ - postprocess_offsets[ppidx],
+ 0,
+ 255);
+ }
*outnow++ = pix;
}
}
@@ -2818,6 +2958,9 @@ static unsigned local_sram_size;
static unsigned char r93setting;
#define RTS8801_F_SUPPRESS_MOVEMENT 1
+#define RTS8801_F_LAMP_OFF 2
+#define RTS8801_F_NO_DISPLACEMENTS 4
+#define RTS8801_F_ODDX 8
static int
find_resolution_index (unsigned resolution)
@@ -2848,7 +2991,8 @@ rts8801_fullscan (unsigned x,
int green_calib_offset,
int blue_calib_offset,
int end_calib_offset,
- detailed_calibration_data const *detailed_calib_data)
+ double *postprocess_offsets,
+ double *postprocess_gains)
{
int ires, jres;
int tg_setting;
@@ -2856,6 +3000,10 @@ rts8801_fullscan (unsigned x,
unsigned char offdutytime;
int result;
int scan_frequency;
+ unsigned intra_channel_offset;
+ unsigned red_green_offset;
+ unsigned green_blue_offset;
+ unsigned total_offsets;
ires = find_resolution_index (xresolution);
jres = find_resolution_index (yresolution);
@@ -2889,47 +3037,32 @@ rts8801_fullscan (unsigned x,
rt_set_movement_pattern (regs, 0x800000);
-
- tg_setting = resparms[jres].tg;
- rt_set_ccd_shift_clock_multiplier (regs, tg_info[tg_setting].tg_cph0p);
- rt_set_ccd_clock_reset_interval (regs, tg_info[tg_setting].tg_crsp);
- rt_set_ccd_clamp_clock_multiplier (regs, tg_info[tg_setting].tg_cclpp);
-
-
- rt_set_one_register (0xc6, 0);
- rt_set_one_register (0xc6, 0);
-
- rt_set_step_size (regs, resparms[jres].step_size);
-
rt_set_direction_forwards (regs);
-
rt_set_stop_when_rewound (regs, 0);
- rt_set_data_feed_on (regs);
- rt_set_calibration_addresses (regs, 0, 0, 0, 0);
+ rt_set_calibration_addresses (regs, 0, 0, 0, 0, 0);
rt_set_basic_calibration (regs,
calib_info[0], calib_info[1], calib_info[2],
calib_info[3], calib_info[4], calib_info[5],
calib_info[6], calib_info[7], calib_info[8]);
regs[0x0b] = 0x70; /* If set to 0x71, the alternative, all values are low */
+ regs[0x40] &= 0xc0;
if (red_calib_offset >= 0
&& green_calib_offset >= 0
- && blue_calib_offset >= 0 &&
- yresolution < 400)
+ && blue_calib_offset >= 0)
{
rt_set_calibration_addresses (regs, red_calib_offset,
green_calib_offset, blue_calib_offset,
- end_calib_offset);
+ end_calib_offset,
+ w);
regs[0x40] |= 0x2f;
- detailed_calib_data = 0;
}
else if (end_calib_offset >= 0)
{
rt_set_calibration_addresses (regs, 0x600, 0x600, 0x600,
- end_calib_offset);
- regs[0x40] &= 0xc0;
+ end_calib_offset, w);
}
rt_set_channel (regs, RT_CHANNEL_ALL);
@@ -2937,23 +3070,9 @@ rts8801_fullscan (unsigned x,
rt_set_merge_channels (regs, 1);
rt_set_colour_mode (regs, 1);
- rt_set_motor_movement_clock_multiplier (regs,
- resparms[jres].
- motor_movement_clock_multiplier);
-
- rt_set_cdss (regs, tg_info[tg_setting].tg_cdss1,
- tg_info[tg_setting].tg_cdss2);
- rt_set_cdsc (regs, tg_info[tg_setting].tg_cdsc1,
- tg_info[tg_setting].tg_cdsc2);
- rt_update_after_setting_cdss2 (regs);
-
rt_set_last_sram_page (regs, (local_sram_size - 1) >> 5);
- regs[0x39] = resparms[jres].reg_39_value;
- regs[0xc3] = (regs[0xc3] & 0xf8) | resparms[jres].reg_c3_value;
- regs[0xc6] = (regs[0xc6] & 0xf8) | resparms[jres].reg_c6_value;
scan_frequency = resparms[jres].scan_frequency;
- rt_set_scan_frequency (regs, scan_frequency);
rt_set_cph0s (regs, resparms[ires].cph0s);
if (resparms[ires].d3_bit_3_value)
regs[0xd3] |= 0x08;
@@ -2962,15 +3081,10 @@ rts8801_fullscan (unsigned x,
if (flags & RTS8801_F_SUPPRESS_MOVEMENT)
regs[0xc3] &= 0x7f;
- rt_set_horizontal_resolution (regs, xresolution);
- rt_set_noscan_distance (regs, y * scan_frequency - 1);
- rt_set_total_distance (regs, scan_frequency *
- (y +
- h +
- resparms[jres].red_green_offset +
- resparms[jres].green_blue_offset +
- resparms[jres].intra_channel_offset) - 1);
+ regs[0xb2] &= 0xf7;
+
+ rt_set_horizontal_resolution (regs, xresolution);
rt_set_scanline_start (regs,
x * (1200 / xresolution) /
@@ -2982,6 +3096,118 @@ rts8801_fullscan (unsigned x,
(resparms[ires].cph0s ? 1 : 2) /
(resparms[ires].d3_bit_3_value ? 1 : 2));
+ if (flags & RTS8801_F_NO_DISPLACEMENTS)
+ {
+ red_green_offset = green_blue_offset = intra_channel_offset = 0;
+ }
+ else
+ {
+ red_green_offset = resparms[jres].red_green_offset;
+ green_blue_offset = resparms[jres].green_blue_offset;
+ intra_channel_offset = resparms[jres].intra_channel_offset;
+ }
+ total_offsets = red_green_offset + green_blue_offset + intra_channel_offset;
+ if (y > total_offsets + 2)
+ y -= total_offsets;
+ h += total_offsets;
+
+ if (yresolution > 75 && !(flags & RTS8801_F_SUPPRESS_MOVEMENT))
+ {
+ int rmres = find_resolution_index (50);
+
+ if (rmres >= 0)
+ {
+ int factor = yresolution / 50;
+ int fastres = y / factor;
+ int remainder = y % factor;
+
+ while (remainder < 2)
+ {
+ --fastres;
+ remainder += factor;
+ }
+
+ if (fastres >= 3)
+ {
+ y = remainder;
+
+ rt_set_noscan_distance(regs, fastres * resparms[rmres].scan_frequency - 2);
+ rt_set_total_distance(regs, fastres * resparms[rmres].scan_frequency - 1);
+
+ rt_set_scan_frequency(regs, 1);
+
+ tg_setting = resparms[rmres].tg;
+ rt_set_ccd_shift_clock_multiplier (regs, tg_info[tg_setting].tg_cph0p);
+ rt_set_ccd_clock_reset_interval (regs, tg_info[tg_setting].tg_crsp);
+ rt_set_ccd_clamp_clock_multiplier (regs, tg_info[tg_setting].tg_cclpp);
+
+ rt_set_one_register (0xc6, 0);
+ rt_set_one_register (0xc6, 0);
+
+ rt_set_step_size (regs, resparms[rmres].step_size);
+
+ rt_set_motor_movement_clock_multiplier (regs,
+ resparms[rmres].
+ motor_movement_clock_multiplier);
+
+ rt_set_cdss (regs, tg_info[tg_setting].tg_cdss1,
+ tg_info[tg_setting].tg_cdss2);
+ rt_set_cdsc (regs, tg_info[tg_setting].tg_cdsc1,
+ tg_info[tg_setting].tg_cdsc2);
+ rt_update_after_setting_cdss2 (regs);
+
+ regs[0x39] = resparms[rmres].reg_39_value;
+ regs[0xc3] = (regs[0xc3] & 0xf8) | resparms[rmres].reg_c3_value;
+ regs[0xc6] = (regs[0xc6] & 0xf8) | resparms[rmres].reg_c6_value;
+
+ rt_set_data_feed_off (regs);
+
+ rt_set_all_registers (regs);
+
+ rt_set_one_register (0x2c, regs[0x2c]);
+
+ if (DBG_LEVEL >= 5)
+ dump_registers (regs);
+
+ rt_start_moving ();
+ while (rt_is_moving ());
+ }
+ }
+ }
+
+
+ rt_set_noscan_distance (regs, y * scan_frequency - 1);
+ rt_set_total_distance (regs, scan_frequency * (y + h) - 1);
+
+ rt_set_scan_frequency (regs, scan_frequency);
+
+ tg_setting = resparms[jres].tg;
+
+ rt_set_ccd_shift_clock_multiplier (regs, tg_info[tg_setting].tg_cph0p);
+ rt_set_ccd_clock_reset_interval (regs, tg_info[tg_setting].tg_crsp);
+ rt_set_ccd_clamp_clock_multiplier (regs, tg_info[tg_setting].tg_cclpp);
+
+ rt_set_one_register (0xc6, 0);
+ rt_set_one_register (0xc6, 0);
+
+ rt_set_step_size (regs, resparms[jres].step_size);
+
+ rt_set_motor_movement_clock_multiplier (regs,
+ resparms[jres].
+ motor_movement_clock_multiplier);
+
+ rt_set_cdss (regs, tg_info[tg_setting].tg_cdss1,
+ tg_info[tg_setting].tg_cdss2);
+ rt_set_cdsc (regs, tg_info[tg_setting].tg_cdsc1,
+ tg_info[tg_setting].tg_cdsc2);
+ rt_update_after_setting_cdss2 (regs);
+
+ regs[0x39] = resparms[jres].reg_39_value;
+ regs[0xc3] = (regs[0xc3] & 0xf8) | resparms[jres].reg_c3_value;
+ regs[0xc6] = (regs[0xc6] & 0xf8) | resparms[jres].reg_c6_value;
+
+ rt_set_data_feed_on (regs);
+
rt_set_all_registers (regs);
rt_set_one_register (0x2c, regs[0x2c]);
@@ -2992,12 +3218,13 @@ rts8801_fullscan (unsigned x,
result = rts8801_doscan (w,
h,
colour,
- resparms[jres].red_green_offset,
- resparms[jres].green_blue_offset,
- resparms[jres].intra_channel_offset,
+ red_green_offset,
+ green_blue_offset,
+ intra_channel_offset,
cbfunc, param, (x & 1), calib_info,
- (regs[0x2f] & 0x04) != 0, detailed_calib_data);
-
+ (regs[0x2f] & 0x04) != 0,
+ postprocess_offsets,
+ postprocess_gains);
return result;
}
@@ -3080,6 +3307,11 @@ sum_channel (unsigned char *p, int n, int bytwo)
static int do_warmup = 1;
+#define DETAILED_PASS_COUNT 3
+#define DETAILED_PASS_OFFSETS 0
+#define DETAILED_PASS_GAINS_FIRSTPASS 1
+#define DETAILED_PASS_GAINS_SECONDPASS 2
+
static int
rts8801_scan (unsigned x,
unsigned y,
@@ -3090,26 +3322,22 @@ rts8801_scan (unsigned x,
unsigned brightness,
unsigned contrast,
rts8801_callback cbfunc,
- void *param)
+ void *param,
+ double gamma)
{
unsigned char calib_info[9];
unsigned char calibbuf[2400];
struct dcalibdata dcd;
struct calibdata cd;
unsigned char *detail_buffer = 0;
- int iCalibOffset;
- int iCalibX;
int iCalibY;
- int iCalibWidth;
int iCalibTarget;
- int iCalibPixels;
int iMoveFlags = 0;
- unsigned int aiLow[3] = { 0, 0, 0 };
- unsigned int aiHigh[3] = { 256, 256, 256 };
- unsigned aiBestOffset[3];
+ unsigned aiBestOffset[6];
+ int aiPassed[6];
int i;
unsigned j;
- int anychanged;
+ int k;
int calibration_size;
unsigned char *pDetailedCalib;
int red_calibration_offset;
@@ -3121,7 +3349,13 @@ rts8801_scan (unsigned x,
int resolution_index;
int detailed_calibration_rows = 50;
unsigned char *tdetail_buffer;
- detailed_calibration_data detailed_calib_data;
+ int pass;
+ int onechanged;
+ double *postprocess_gains;
+ double *postprocess_offsets;
+ int needs_postprocessed_calibration = 0;
+ double contrast_adjust = (double) contrast / 64;
+ int brightness_adjust = brightness - 0x80;
/* Initialise and power up */
@@ -3150,99 +3384,101 @@ rts8801_scan (unsigned x,
calib_info[2] = calib_info[5] = calib_info[8] = 1;
- calib_info[0] = calib_info[1] = calib_info[3] = calib_info[4] =
- calib_info[6] = calib_info[7] = 0xb4;
-
- iCalibOffset = 0; /* Note that horizontal resolution is always 600dpi for calibration. 330 is 110 dots in (for R,G,B channels) */
- iCalibX = 1;
- iCalibPixels = 50;
- iCalibY = (resolution == 25) ? 1 : 2; /* Was 1200 / resolution, which would take us past the calibration area for 50dpi */
- iCalibWidth = 100;
+ iCalibY = (resolution == 25) ? 1 : 2;
iCalibTarget = 550;
+
+ rt_turn_off_lamp();
- for (i = 0; i < 3; ++i)
- aiBestOffset[i] = 0xb4;
-
+ for (i = 0; i < 6; ++i)
+ {
+ aiBestOffset[i] = 0xbf;
+ aiPassed[i] = 0;
+ }
+
do
{
DBG (30, "Initial calibration pass commences\n");
- anychanged = 0;
-
- for (i = 0; i < 3; ++i)
- {
- aiBestOffset[i] = (aiHigh[i] + aiLow[i] + 1) / 2;
- }
+ onechanged = 0;
for (i = 0; i < 3; ++i)
- calib_info[i * 3] = calib_info[i * 3 + 1] = aiBestOffset[i];
-
+ {
+ calib_info[i * 3] = aiBestOffset[i];
+ calib_info[i * 3 + 1] = aiBestOffset[i + 3];
+ }
+
cd.buffer = calibbuf;
cd.space = sizeof (calibbuf);
DBG (30, "Commencing scan for initial calibration pass\n");
- rts8801_fullscan (iCalibX, iCalibY, iCalibWidth, 2, 600, resolution,
+ rts8801_fullscan (1401, iCalibY, 100, 2, 400, resolution,
HP3500_COLOR_SCAN, (rts8801_callback) storefunc, &cd,
- calib_info, iMoveFlags, -1, -1, -1, -1, 0);
+ calib_info, iMoveFlags, -1, -1, -1, -1, 0, 0);
DBG (30, "Completed scan for initial calibration pass\n");
- iMoveFlags = RTS8801_F_SUPPRESS_MOVEMENT;
+ iMoveFlags = RTS8801_F_SUPPRESS_MOVEMENT | RTS8801_F_NO_DISPLACEMENTS;
+ iCalibY = 2;
- for (i = 0; i < 3; ++i)
+ for (i = 0; i < 6; ++i)
{
int sum;
- if (aiBestOffset[i] >= 255)
+ if (aiBestOffset[i] >= 255 || aiPassed[i] > 2)
continue;
- sum = sum_channel (calibbuf + iCalibOffset + i, iCalibPixels, 0);
+ sum = sum_channel (calibbuf + i, 50, 1);
DBG (20, "channel[%d] sum = %d (target %d)\n", i, sum,
iCalibTarget);
- if (sum >= iCalibTarget)
- aiHigh[i] = aiBestOffset[i];
- else
- aiLow[i] = aiBestOffset[i];
+ if (sum < iCalibTarget)
+ {
+ onechanged = 1;
+ ++aiBestOffset[i];
+ }
+ else
+ {
+ ++aiPassed[i];
+ }
}
DBG (30, "Initial calibration pass completed\n");
}
- while (aiLow[0] < aiHigh[0] - 1 && aiLow[1] < aiHigh[1] - 1
- && aiLow[1] < aiHigh[1] + 1);
+ while (onechanged);
DBG (20, "Offsets calculated\n");
- cd.buffer = calibbuf;
- cd.space = sizeof (calibbuf);
- DBG (20, "Scanning for part 2 of initial calibration\n");
- rts8801_fullscan (iCalibX + 2100, iCalibY, iCalibWidth, 2, 600, resolution,
- HP3500_COLOR_SCAN, (rts8801_callback) storefunc, &cd,
- calib_info, RTS8801_F_SUPPRESS_MOVEMENT, -1, -1, -1, -1,
- 0);
- DBG (20, "Scan for part 2 of initial calibration completed\n");
- DBG (20, "Initial calibration completed\n");
+ rt_turn_on_lamp();
+ usleep(500000);
tdetail_buffer =
(unsigned char *) malloc (w * 3 * detailed_calibration_rows);
- aiLow[0] = aiLow[1] = aiLow[2] = 1;
- aiHigh[0] = aiHigh[1] = aiHigh[2] = 64;
+ for (i = 0; i < 3; ++i)
+ {
+ calib_info[i * 3 + 2] = 1;
+ aiPassed[i] = 0;
+ }
+
do
{
struct dcalibdata dcdt;
- for (i = 0; i < 3; ++i)
- calib_info[i * 3 + 2] = (aiLow[i] + aiHigh[i]) / 2;
-
dcdt.buffers[0] = tdetail_buffer;
dcdt.buffers[1] = (tdetail_buffer + w * detailed_calibration_rows);
dcdt.buffers[2] = (dcdt.buffers[1] + w * detailed_calibration_rows);
dcdt.pixelsperrow = w;
dcdt.pixelnow = dcdt.channelnow = dcdt.firstrowdone = 0;
+ DBG (20, "Scanning for part 2 of initial calibration\n");
rts8801_fullscan (x, 4, w, detailed_calibration_rows + 1, resolution,
resolution, HP3500_COLOR_SCAN,
(rts8801_callback) accumfunc, &dcdt, calib_info,
- RTS8801_F_SUPPRESS_MOVEMENT, -1, -1, -1, -1, 0);
+ RTS8801_F_SUPPRESS_MOVEMENT | RTS8801_F_NO_DISPLACEMENTS, -1, -1, -1, -1, 0, 0);
+ DBG (20, "Scan for part 2 of initial calibration completed\n");
+
+ onechanged = 0;
for (i = 0; i < 3; ++i)
{
int largest = 1;
- for (j = 0; j < w; ++j)
+ if (aiPassed[i] > 2 || calib_info[i * 3 + 2] >= 63)
+ continue;
+
+ for (j = 0; j < w; ++j)
{
int val =
calcmedian (dcdt.buffers[i], j, w, detailed_calibration_rows);
@@ -3252,16 +3488,17 @@ rts8801_scan (unsigned x,
}
if (largest < 0xe0)
- aiLow[i] = calib_info[i * 3 + 2];
- else
- aiHigh[i] = calib_info[i * 3 + 2];
+ {
+ ++calib_info[i * 3 + 2];
+ onechanged = 1;
+ }
+ else
+ {
+ ++aiPassed[i];
+ }
}
}
- while (aiLow[0] < aiHigh[0] - 1 && aiLow[1] < aiHigh[1] - 1
- && aiLow[1] < aiHigh[1] + 1);
-
- for (i = 0; i < 3; ++i)
- calib_info[i * 3 + 2] = aiLow[i];
+ while (onechanged);
for (i = 0; i < 3; ++i)
{
@@ -3282,15 +3519,7 @@ rts8801_scan (unsigned x,
dcd.buffers[1] = (detail_buffer + w * detailed_calibration_rows);
dcd.buffers[2] = (dcd.buffers[1] + w * detailed_calibration_rows);
dcd.pixelsperrow = w;
- dcd.pixelnow = dcd.channelnow = dcd.firstrowdone = 0;
-
- DBG (10, "Performing detailed calibration scan\n");
- rts8801_fullscan (x, iCalibY, w, detailed_calibration_rows + 1,
- resolution, resolution, HP3500_COLOR_SCAN,
- (rts8801_callback) accumfunc, &dcd, calib_info,
- RTS8801_F_SUPPRESS_MOVEMENT, -1, -1, -1, -1, 0);
- DBG (10, "Detailed calibration scan completed\n");
/* And now for the detailed calibration */
resolution_index = find_resolution_index (resolution);
@@ -3301,101 +3530,221 @@ rts8801_scan (unsigned x,
base_resolution *= 2;
resolution_divisor = base_resolution / resolution;
- calibration_size = w * resolution_divisor * 6 + 1536;
- red_calibration_offset = 1536;
- blue_calibration_offset =
- red_calibration_offset + w * resolution_divisor * 2;
+ calibration_size = w * resolution_divisor * 6 + 1568 + 96;
+ red_calibration_offset = 0x600;
green_calibration_offset =
- blue_calibration_offset + w * resolution_divisor * 2;
- end_calibration_offset =
+ red_calibration_offset + w * resolution_divisor * 2;
+ blue_calibration_offset =
green_calibration_offset + w * resolution_divisor * 2;
+ end_calibration_offset =
+ blue_calibration_offset + w * resolution_divisor * 2;
pDetailedCalib = (unsigned char *) malloc (calibration_size);
memset (pDetailedCalib, 0, calibration_size);
+
for (i = 0; i < 3; ++i)
{
int idx =
- (i == 0) ? red_calibration_offset : (i ==
- 1) ? green_calibration_offset :
- blue_calibration_offset;
- double g = calib_info[i * 3 + 2];
+ (i == 0) ? red_calibration_offset :
+ (i == 1) ? green_calibration_offset :
+ blue_calibration_offset;
for (j = 0; j < 256; j++)
- {
- int val = j;
-
- if (val < 0)
- val = 0;
- if (val > 255)
- val = 255;
- pDetailedCalib[i * 512 + j * 2] = val;
- pDetailedCalib[i * 512 + j * 2 + 1] = val;
- }
+ {
+ /* Gamma table - appears to be 256 byte pairs for each input
+ * range (so the first entry cover inputs in the range 0 to 1,
+ * the second 1 to 2, and so on), mapping that input range
+ * (including the fractional parts within it) to an output
+ * range.
+ */
+ pDetailedCalib[i * 512 + j * 2] = j;
+ pDetailedCalib[i * 512 + j * 2 + 1] = j;
+ }
for (j = 0; j < w; ++j)
- {
- int multnow;
- int offnow;
-
- /* This seems to be the approach for reg 0x40 & 0x3f == 0x27, which allows detailed
- * calibration to return either higher or lower values.
- */
- int k;
-
- {
- double denom1 =
- calcmedian (dcd.buffers[i], j, w, detailed_calibration_rows);
- double f = 0xff / (denom1 - 2 * g);
- double contrast_adjust = (double) (contrast + 1) / 64;
-
- multnow = f * 64 * contrast_adjust;
- offnow = 4 * g + 128 - brightness;
- }
- if (multnow < 0)
- multnow = 0;
- if (multnow > 255)
- multnow = 255;
- if (offnow < 0)
- offnow = 0;
- if (offnow > 255)
- offnow = 255;
-
- for (k = 0; k < resolution_divisor; ++k)
- {
- /*multnow = j * resolution_divisor + k; */
- pDetailedCalib[idx++] = offnow; /* Subtract this value from the result */
- pDetailedCalib[idx++] = multnow; /* Then multiply by this value divided by 0x40 */
- }
- }
+ {
+ for (k = 0; k < resolution_divisor; ++k)
+ {
+ pDetailedCalib[idx++] = 0;
+ pDetailedCalib[idx++] = 0x80;
+ }
+ }
}
- DBG (10, "\n");
-
rt_set_sram_page (0);
rt_set_one_register (0x93, r93setting);
rt_write_sram (calibration_size, pDetailedCalib);
- /* And finally, perform the scan */
+ postprocess_gains = (double *) malloc(sizeof(double) * 3 * w);
+ postprocess_offsets = (double *) malloc(sizeof(double) * 3 * w);
+
+ for (pass = 0; pass < DETAILED_PASS_COUNT; ++pass)
+ {
+ int ppidx = 0;
+
+ DBG (10, "Performing detailed calibration scan %d\n", pass);
+
+ switch (pass)
+ {
+ case DETAILED_PASS_OFFSETS:
+ rt_turn_off_lamp();
+ usleep(500000); /* To be sure it has gone off */
+ break;
+
+ case DETAILED_PASS_GAINS_FIRSTPASS:
+ rt_turn_on_lamp();
+ usleep(500000); /* Give the lamp time to settle */
+ break;
+ }
+
+ dcd.pixelnow = dcd.channelnow = dcd.firstrowdone = 0;
+ rts8801_fullscan (x, iCalibY, w, detailed_calibration_rows + 1,
+ resolution, resolution, HP3500_COLOR_SCAN,
+ (rts8801_callback) accumfunc, &dcd,
+ calib_info,
+ RTS8801_F_SUPPRESS_MOVEMENT | RTS8801_F_NO_DISPLACEMENTS,
+ red_calibration_offset,
+ green_calibration_offset,
+ blue_calibration_offset,
+ end_calibration_offset,
+ 0, 0);
+
+ DBG (10, " Detailed calibration scan %d completed\n", pass);
+
+ for (i = 0; i < 3; ++i)
+ {
+ int idx =
+ (i == 0) ? red_calibration_offset :
+ (i == 1) ? green_calibration_offset :
+ blue_calibration_offset;
+
+ for (j = 0; j < w; ++j)
+ {
+ double multnow = 0x80;
+ int offnow = 0;
+
+ /* This seems to be the approach for reg 0x40 & 0x3f == 0x27, which allows detailed
+ * calibration to return either higher or lower values.
+ */
+
+ {
+ double denom1 =
+ calcmedian (dcd.buffers[i], j, w, detailed_calibration_rows);
+
+ switch (pass)
+ {
+ case DETAILED_PASS_OFFSETS:
+ /* The offset is the number needed to be subtracted from "black" at detailed gain = 0x80,
+ * which is the value we started with. For the next round, pull the gain down to 0x20. Our
+ * next scan is a test scan to confirm the offset works.
+ */
+ multnow = 0x20;
+ offnow = denom1;
+ break;
+
+ case DETAILED_PASS_GAINS_FIRSTPASS:
+ multnow = 128.0 / denom1 * 0x20; /* Then bring it up to whatever we need to hit 192 */
+ if (multnow > 255)
+ multnow = 255;
+ offnow = pDetailedCalib[idx];
+ break;
+
+ case DETAILED_PASS_GAINS_SECONDPASS:
+ multnow = 255.0 / denom1 * contrast_adjust * pDetailedCalib[idx+1]; /* And finally to 255 */
+ offnow = pDetailedCalib[idx] - brightness_adjust * 0x80 / multnow;
+
+ if (offnow < 0)
+ {
+ postprocess_offsets[ppidx] = multnow * offnow / 0x80;
+ offnow = 0;
+ needs_postprocessed_calibration = 1;
+ }
+ else if (offnow > 255)
+ {
+ postprocess_offsets[ppidx] = multnow * (offnow - 255) / 0x80;
+ offnow = 255;
+ needs_postprocessed_calibration = 1;
+ }
+ else
+ {
+ postprocess_offsets[ppidx] = 0;
+ }
+ if (multnow > 255)
+ {
+ postprocess_gains[ppidx] = multnow / 255;
+ multnow = 255;
+ needs_postprocessed_calibration = 1;
+ }
+ else
+ {
+ postprocess_gains[ppidx] = 1.0;
+ }
+ break;
+ }
+ }
+ if (offnow > 255)
+ offnow = 255;
+
+ for (k = 0; k < resolution_divisor; ++k)
+ {
+ pDetailedCalib[idx++] = offnow; /* Subtract this value from the result at gains = 0x80*/
+ pDetailedCalib[idx++] = multnow; /* Then multiply by this value divided by 0x80 */
+ }
+ ++ppidx;
+ }
+ }
+
+ if (pass == DETAILED_PASS_GAINS_SECONDPASS)
+ {
+ /* Build gamma table */
+ unsigned char *redgamma = pDetailedCalib;
+ unsigned char *greengamma = redgamma + 512;
+ unsigned char *bluegamma = greengamma + 512;
+ double val;
+ double invgamma = 1.0l / gamma;
+
+ *redgamma++ = *bluegamma++ = *greengamma++ = 0;
+
+ /* The windows driver does a linear interpolation for the next 19 boundaries */
+ val = pow (20.0l / 255, invgamma) * 255;
+
+ for (j = 1; j <= 20; ++j)
+ {
+ *redgamma++ = *bluegamma++ = *greengamma++ = val * j / 20 + 0.5;
+ *redgamma++ = *bluegamma++ = *greengamma++ = val * j / 20 + 0.5;
+ }
+
+ for (; j <= 255; ++j)
+ {
+ val = pow((double) j / 255, invgamma) * 255;
+
+ *redgamma++ = *bluegamma++ = *greengamma++ = val + 0.5;
+ *redgamma++ = *bluegamma++ = *greengamma++ = val + 0.5;
+ }
+ *redgamma++ = *bluegamma++ = *greengamma++ = 255;
+ }
+
+ DBG (10, "\n");
+
+ rt_set_sram_page (0);
+ rt_set_one_register (0x93, r93setting);
+ rt_write_sram (calibration_size, pDetailedCalib);
+ }
+ /* And finally, perform the scan */
DBG (10, "Scanning\n");
rts8801_rewind ();
- detailed_calib_data.channeldata[0] =
- pDetailedCalib + red_calibration_offset;
- detailed_calib_data.channeldata[1] =
- pDetailedCalib + green_calibration_offset;
- detailed_calib_data.channeldata[2] =
- pDetailedCalib + blue_calibration_offset;
- detailed_calib_data.resolution_divisor = resolution_divisor;
-
rts8801_fullscan (x, y, w, h, resolution, resolution, colour, cbfunc, param,
calib_info, 0,
red_calibration_offset, green_calibration_offset,
blue_calibration_offset, end_calibration_offset,
- &detailed_calib_data);
+ needs_postprocessed_calibration ? postprocess_offsets : 0,
+ needs_postprocessed_calibration ? postprocess_gains : 0);
rt_turn_off_lamp ();
+
rts8801_rewind ();
rt_set_powersave_mode (1);
@@ -3403,6 +3752,12 @@ rts8801_scan (unsigned x,
free (pDetailedCalib);
if (detail_buffer)
free (detail_buffer);
+ if (tdetail_buffer)
+ free(tdetail_buffer);
+ if (postprocess_gains)
+ free(postprocess_gains);
+ if (postprocess_offsets)
+ free(postprocess_offsets);
return 0;
}
@@ -3466,7 +3821,6 @@ reader_process (void *pv)
sigaction (SIGTERM, &act, 0);
}
-
/* Warm up the lamp again if our last scan ended more than 5 minutes ago. */
time (&t);
do_warmup = (t - scanner->last_scan) > 300;
@@ -3498,9 +3852,25 @@ reader_process (void *pv)
scanner->actres_pixels.right - scanner->actres_pixels.left,
scanner->actres_pixels.bottom - scanner->actres_pixels.top,
scanner->resolution, scanner->mode, scanner->brightness,
- scanner->contrast, (rts8801_callback) writefunc, &winfo) >= 0)
+ scanner->contrast, (rts8801_callback) writefunc, &winfo,
+ scanner->gamma) >= 0)
status = SANE_STATUS_GOOD;
status = SANE_STATUS_IO_ERROR;
close (scanner->pipe_w);
return status;
}
+
+static size_t
+max_string_size (char const **strings)
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}