brother_mfp: colour scan now fully functional.

Also:
- Init recovery for when previous crash left a session open.
- Small corrections for gray scans
brother_mfp_backend
Ralph Little 2022-10-10 16:00:40 -07:00
rodzic 3fae2e561d
commit 437bfd27f3
5 zmienionych plików z 244 dodań i 75 usunięć

Wyświetl plik

@ -182,6 +182,74 @@ SANE_Status BrotherUSBDriver::StartSession ()
return SANE_STATUS_GOOD;
}
SANE_Status res = ExecStartSession ();
if (res != SANE_STATUS_GOOD)
{
/*
* Perhaps a session is in progress from a prior crash?
* We will try to stop then restart.
*
*/
res = ExecStopSession ();
if (res != SANE_STATUS_GOOD)
{
DBG (DBG_WARN,
"BrotherUSBDriver::StartSession: failed to send stop session sequence for restart: %s.\n",
devicename);
return SANE_STATUS_DEVICE_BUSY;
}
SANE_Status res = ExecStartSession ();
if (res != SANE_STATUS_GOOD)
{
DBG (DBG_WARN,
"BrotherUSBDriver::StartSession: failed to send start session sequence for restart: %s.\n",
devicename);
return SANE_STATUS_DEVICE_BUSY;
}
}
in_session = true;
return SANE_STATUS_GOOD;
}
SANE_Status BrotherUSBDriver::StopSession ()
{
/*
* Initialisation.
*
*/
if (!is_open)
{
DBG (DBG_WARN, "BrotherUSBDriver::StopSession: device not open: %s.\n",
devicename);
return SANE_STATUS_INVAL;
}
if (!in_session)
{
DBG (DBG_WARN, "BrotherUSBDriver::StopSession: not in session: %s.\n",
devicename);
return SANE_STATUS_GOOD;
}
SANE_Status res = ExecStopSession ();
if (res != SANE_STATUS_GOOD)
{
DBG (DBG_WARN, "BrotherUSBDriver::StopSession: failed to send stop sequence.\n");
return res;
}
in_session = false;
return SANE_STATUS_GOOD;
}
SANE_Status BrotherUSBDriver::ExecStartSession ()
{
SANE_Status res = sanei_usb_control_msg (fd, USB_DIR_IN | USB_TYPE_VENDOR,
BROTHER_USB_REQ_STARTSESSION,
2, 0, 5, small_buffer);
@ -217,31 +285,12 @@ SANE_Status BrotherUSBDriver::StartSession ()
return SANE_STATUS_DEVICE_BUSY;
}
in_session = true;
return SANE_STATUS_GOOD;
}
SANE_Status BrotherUSBDriver::StopSession ()
SANE_Status BrotherUSBDriver::ExecStopSession ()
{
/*
* Initialisation.
*
*/
if (!is_open)
{
DBG (DBG_WARN, "BrotherUSBDriver::StopSession: device not open: %s.\n",
devicename);
return SANE_STATUS_INVAL;
}
if (!in_session)
{
DBG (DBG_WARN, "BrotherUSBDriver::StopSession: not in session: %s.\n",
devicename);
return SANE_STATUS_GOOD;
}
SANE_Status res = sanei_usb_control_msg (fd, USB_DIR_IN | USB_TYPE_VENDOR,
BROTHER_USB_REQ_STOPSESSION,
2, 0, 5, small_buffer);
@ -277,8 +326,6 @@ SANE_Status BrotherUSBDriver::StopSession ()
return SANE_STATUS_DEVICE_BUSY;
}
in_session = false;
return SANE_STATUS_GOOD;
}
@ -321,12 +368,14 @@ SANE_Status BrotherUSBDriver::ReadScanData (SANE_Byte *data, size_t max_length,
/*
* First, see if we need to do a read from the device to satisfy the read.
* Only if we have space to read though. Obviously.
*
* I will arbitrarily pick 1024 as our limit. If we have anything near to that, we *really*
* should be able to decode *something*.
* TODO: Perhaps we might put some lower limit to the amount of space available.
* No point in doing a "tiny" read because we don't have much space available.
*
*/
if (data_buffer_bytes < 1024)
size_t bytes_to_read = (BROTHER_READ_BUFFER_LEN - data_buffer_bytes) & ~(1024 - 1);
if (bytes_to_read > 0)
{
/*
@ -338,8 +387,6 @@ SANE_Status BrotherUSBDriver::ReadScanData (SANE_Byte *data, size_t max_length,
* It's OK: as long as we are reading something substantial, that is fine.
*
*/
size_t bytes_to_read = (BROTHER_READ_BUFFER_LEN - data_buffer_bytes) & ~(1024 - 1);
DBG (DBG_IMPORTANT,
"BrotherUSBDriver::ReadScanData: attempting read, space for %zu bytes\n",
bytes_to_read);
@ -364,7 +411,7 @@ SANE_Status BrotherUSBDriver::ReadScanData (SANE_Byte *data, size_t max_length,
* Try to decode some data.
*
*/
size_t data_consumed;
size_t data_consumed = 0;
res = encoder->DecodeScanData (data_buffer,
data_buffer_bytes,

Wyświetl plik

@ -164,6 +164,8 @@ public:
private:
SANE_Status StartSession ();
SANE_Status StopSession ();
SANE_Status ExecStartSession ();
SANE_Status ExecStopSession ();
SANE_Status Init ();
SANE_Status PollForReadFlush (useconds_t max_time);

Wyświetl plik

@ -163,7 +163,7 @@ SANE_Status BrotherEncoderFamily4::DecodeSessionResp (const SANE_Byte *data, siz
* TODO: Check the valid status values are 0x00 or 0x20
*
*/
if ((data[4] != 0x00) && (data[4] != 0x20))
if ((data[4] != 0x00) && (data[4] != 0x80))
{
DBG (DBG_SERIOUS,
@ -408,7 +408,7 @@ SANE_Status BrotherEncoderFamily4::DecodeScanData (const SANE_Byte *src_data, si
}
/*
* Attempt to to decode the data block.
* Attempt to decode the data block.
* We will exit the function if we can only partially
* decode it, hoping to do more next time.
*
@ -454,11 +454,11 @@ SANE_Status BrotherEncoderFamily4::DecodeScanData (const SANE_Byte *src_data, si
}
res = gray_raw_decoder.DecodeScanData (src_data,
in_len,
&bytes_consumed,
dest_data,
dest_data_len,
&bytes_written);
in_len,
&bytes_consumed,
dest_data,
dest_data_len,
&bytes_written);
}
else
{
@ -469,17 +469,6 @@ SANE_Status BrotherEncoderFamily4::DecodeScanData (const SANE_Byte *src_data, si
break;
}
src_data += bytes_consumed;
src_data_len -= bytes_consumed;
current_header.block_len -= bytes_consumed;
DBG (DBG_IMPORTANT,
"BrotherEncoderFamily4::DecodeScanData: bytes remaining after decode=%zu\n",
current_header.block_len);
dest_data += bytes_written;
dest_data_len -= bytes_written;
if (res != DECODE_STATUS_GOOD)
{
current_header.block_type = 0;
@ -487,6 +476,17 @@ SANE_Status BrotherEncoderFamily4::DecodeScanData (const SANE_Byte *src_data, si
break;
}
src_data += bytes_consumed;
src_data_len -= bytes_consumed;
current_header.block_len -= bytes_consumed;
DBG (DBG_IMPORTANT,
"BrotherEncoderFamily4::DecodeScanData: current block bytes remaining after decode=%zu\n",
current_header.block_len);
dest_data += bytes_written;
dest_data_len -= bytes_written;
/*
* Perhaps we have completed the block.
*
@ -511,8 +511,11 @@ SANE_Status BrotherEncoderFamily4::DecodeScanData (const SANE_Byte *src_data, si
}
}
*src_data_consumed = orig_src_data_len - src_data_len;
*dest_data_written = orig_dest_data_len - dest_data_len;
if (ret_status == SANE_STATUS_GOOD)
{
*src_data_consumed = orig_src_data_len - src_data_len;
*dest_data_written = orig_dest_data_len - dest_data_len;
}
return ret_status;
}
@ -745,6 +748,7 @@ void BrotherJFIFDecoder::NewPage ()
*/
jpeg_abort_decompress(&state.cinfo);
state.have_read_header = false;
decompress_bytes = 0;
}
void BrotherJFIFDecoder::InitSource (j_decompress_ptr cinfo)
@ -796,19 +800,104 @@ void BrotherJFIFDecoder::TermSource(j_decompress_ptr cinfo)
}
jmp_buf BrotherJFIFDecoder::my_env;
void BrotherJFIFDecoder::ErrorExitManager(j_common_ptr cinfo)
{
char jpegLastErrorMsg[JMSG_LENGTH_MAX];
( *(cinfo->err->format_message) ) (cinfo, jpegLastErrorMsg);
DBG (DBG_SERIOUS, "BrotherJFIFDecoder::ErrorExitManager: libjpeg error=[%s]\n", jpegLastErrorMsg);
longjmp(my_env, 1);
}
DecodeStatus BrotherJFIFDecoder::DecodeScanData (const SANE_Byte *src_data, size_t src_data_len,
size_t *src_data_consumed, SANE_Byte *dest_data,
size_t dest_data_len, size_t *dest_data_written)
{
DBG (DBG_EVENT, "BrotherJFIFDecoder::DecodeScanData\n");
DBG (DBG_EVENT,
"BrotherJFIFDecoder::DecodeScanData: src_data_len=%zu dest_data_len=%zu\n",
src_data_len,
dest_data_len);
*src_data_consumed = 0;
*dest_data_written = 0;
/*
* Loop around, appending data to our decompress buffer and trying to
* decompress as we go until we exhaust the output buffer or the input.
*
*/
while (src_data_len > 0)
{
/*
* Append what we can to the decompress buffer.
*
*/
size_t bytes_to_copy = MIN(sizeof(decompress_buffer) - decompress_bytes, src_data_len);
(void) memcpy (decompress_buffer + decompress_bytes, src_data, bytes_to_copy);
decompress_bytes += bytes_to_copy;
src_data += bytes_to_copy;
src_data_len -= bytes_to_copy;
*src_data_consumed += bytes_to_copy;
/*
* Now to try to decompress.
*
*/
size_t decompress_consumed = 0;
size_t bytes_written = 0;
DecodeStatus res = DecodeScanData_CompressBuffer (decompress_buffer,
decompress_bytes,
&decompress_consumed,
dest_data,
dest_data_len,
&bytes_written);
if (res != DECODE_STATUS_GOOD)
{
*src_data_consumed = 0;
*dest_data_written = 0;
return res;
}
/*
* Shift out any data that we consumed.
*
*/
(void) memmove (decompress_buffer,
decompress_buffer + decompress_consumed,
decompress_bytes - decompress_consumed);
decompress_bytes -= decompress_consumed;
if (bytes_written == 0)
{
break;
}
dest_data += bytes_written;
dest_data_len -= bytes_written;
*dest_data_written += bytes_written;
}
return DECODE_STATUS_GOOD;
}
DecodeStatus BrotherJFIFDecoder::DecodeScanData_CompressBuffer (const SANE_Byte *src_data,
size_t src_data_len,
size_t *src_data_consumed,
SANE_Byte *dest_data,
size_t dest_data_len,
size_t *dest_data_written)
{
DBG (DBG_EVENT,
"BrotherJFIFDecoder::DecodeScanData_CompressBuffer: src_data_len=%zu dest_data_len=%zu\n",
src_data_len,
dest_data_len);
/*
* Initialise read buffer.
@ -819,7 +908,7 @@ DecodeStatus BrotherJFIFDecoder::DecodeScanData (const SANE_Byte *src_data, size
if (setjmp(my_env) != 0)
{
DBG (DBG_EVENT, "BrotherJFIFDecoder::DecodeScanData: setjmp error return\n");
DBG (DBG_EVENT, "BrotherJFIFDecoder::DecodeScanData_CompressBuffer: setjmp error return\n");
return DECODE_STATUS_ERROR;
}
@ -829,7 +918,7 @@ DecodeStatus BrotherJFIFDecoder::DecodeScanData (const SANE_Byte *src_data, size
*/
if (!state.have_read_header)
{
DBG (DBG_EVENT, "BrotherJFIFDecoder::DecodeScanData: Trying to read header.\n");
DBG (DBG_SERIOUS, "BrotherJFIFDecoder::DecodeScanData_CompressBuffer: Trying to read header.\n");
int read_status = jpeg_read_header(&state.cinfo, TRUE);
if (read_status == JPEG_SUSPENDED)
@ -842,7 +931,7 @@ DecodeStatus BrotherJFIFDecoder::DecodeScanData (const SANE_Byte *src_data, size
}
state.have_read_header = true;
DBG (DBG_EVENT, "BrotherJFIFDecoder::DecodeScanData: Starting decompress.\n");
DBG (DBG_SERIOUS, "BrotherJFIFDecoder::DecodeScanData_CompressBuffer: Starting decompress.\n");
if (!jpeg_start_decompress(&state.cinfo))
{
@ -863,6 +952,11 @@ DecodeStatus BrotherJFIFDecoder::DecodeScanData (const SANE_Byte *src_data, size
return DECODE_STATUS_ERROR;
}
DBG (DBG_SERIOUS,
"BrotherJFIFDecoder::JPEG image parameters: width=%zu height=%zu\n",
(size_t)state.cinfo.image_width, (size_t)state.cinfo.image_height);
/*
* TODO: Perhaps also check the dimensions are what we are expecting.
* Depending on the issues related to width alignment, we might
@ -876,12 +970,17 @@ DecodeStatus BrotherJFIFDecoder::DecodeScanData (const SANE_Byte *src_data, size
* Let's try to decompress some JPEG!
*
*/
DBG (DBG_EVENT, "BrotherJFIFDecoder::DecodeScanData: Converting data.\n");
SANE_Byte *output_ptr = dest_data;
const size_t line_size = sizeof(JSAMPLE) * state.cinfo.output_width * state.cinfo.out_color_components;
DBG (DBG_SERIOUS,
"BrotherJFIFDecoder::DecodeScanData_CompressBuffer: "
"Converting data: line_size=%zu state->output_width=%zu components=%d\n",
line_size,
(size_t)state.cinfo.output_width,
state.cinfo.out_color_components);
size_t output_size = dest_data_len;
while (output_size >= line_size)
@ -890,22 +989,31 @@ DecodeStatus BrotherJFIFDecoder::DecodeScanData (const SANE_Byte *src_data, size
* Estimate the number of lines we can generate from
* the available output space.
*
* We have to ask for single lines because I think that the jpeg_read_scanlines()
* function is expecting an array of line buffer pointers. We don't have that
* but we have a single contiguous buffer, so asking for a number of lines at
* once is *not* going to work, which is a shame because this is less efficient
* potentially. :(
*
*/
// JDIMENSION max_lines = dest_data_len / (state.cinfo.output_width * 3);
JDIMENSION converted = jpeg_read_scanlines (&state.cinfo, &output_ptr, 1);
DBG (DBG_IMPORTANT,
"BrotherJFIFDecoder::DecodeScanData: Convert %u scanlines. Remaining scanlines: %u\n",
(unsigned int) converted, (unsigned int)(state.cinfo.output_height - state.cinfo.output_scanline));
if (converted == 0)
{
DBG (DBG_IMPORTANT, "BrotherJFIFDecoder::DecodeScanData_CompressBuffer: converted none\n");
break;
}
output_size -= line_size;
output_ptr += converted * line_size;
if (converted == 0)
{
break;
}
DBG (DBG_IMPORTANT,
"BrotherJFIFDecoder::DecodeScanData_CompressBuffer: Converted %u scanlines. "
"Remaining scanlines: %u output_remaining=%zu\n",
(unsigned int) converted,
(unsigned int) (state.cinfo.output_height - state.cinfo.output_scanline),
output_size);
}
/*
@ -920,7 +1028,7 @@ DecodeStatus BrotherJFIFDecoder::DecodeScanData (const SANE_Byte *src_data, size
*src_data_consumed = src_data_len - state.src_mgr.bytes_in_buffer;
DBG (DBG_IMPORTANT,
"BrotherJFIFDecoder::DecodeScanData: Finished: consumed %zu bytes, written %zu bytes\n",
"BrotherJFIFDecoder::DecodeScanData_CompressBuffer: Finished: consumed %zu bytes, written %zu bytes\n",
*src_data_consumed,
*dest_data_written);
@ -930,10 +1038,12 @@ DecodeStatus BrotherJFIFDecoder::DecodeScanData (const SANE_Byte *src_data, size
*/
if (state.cinfo.output_scanline >= state.cinfo.output_height)
{
DBG (DBG_EVENT, "BrotherJFIFDecoder::DecodeScanData: Converted all scanlines. Finishing.\n");
DBG (DBG_EVENT, "BrotherJFIFDecoder::DecodeScanData_CompressBuffer: Converted all scanlines. Finishing.\n");
if (!jpeg_finish_decompress(&state.cinfo))
{
DBG (DBG_IMPORTANT,
"BrotherJFIFDecoder::DecodeScanData_CompressBuffer: Failed to finish JPEG decompress.\n");
return DECODE_STATUS_ERROR;
}
}

Wyświetl plik

@ -214,7 +214,7 @@ class BrotherJFIFDecoder
{
public:
BrotherJFIFDecoder():
is_running(false)
decompress_bytes(0)
{
/*
* Not sure if this is a safe way to get the cinfo into
@ -243,11 +243,7 @@ public:
state.cinfo.src = &state.src_mgr;
}
static void ErrorExitManager(j_common_ptr cinfo)
{
(void)cinfo;
longjmp(my_env, 1);
}
static void ErrorExitManager(j_common_ptr cinfo);
~BrotherJFIFDecoder()
{
@ -267,6 +263,11 @@ private:
static void SkipInputData(j_decompress_ptr cinfo, long num_bytes);
static void TermSource(j_decompress_ptr cinfo);
DecodeStatus DecodeScanData_CompressBuffer (const SANE_Byte *src_data, size_t src_data_len,
size_t *src_data_consumed, SANE_Byte *dst_data, size_t dest_data_len,
size_t *dest_data_written);
struct CompressionState
{
struct jpeg_decompress_struct cinfo;
@ -275,7 +276,8 @@ private:
bool have_read_header;
} state;
bool is_running;
SANE_Byte decompress_buffer[1024 * 16];
size_t decompress_bytes;
// TODO: Move me to the state.
static jmp_buf my_env;
@ -339,6 +341,10 @@ public:
void NewPage() override
{
current_header.block_type = 0;
jfif_decoder.NewPage();
gray_decoder.NewPage();
gray_raw_decoder.NewPage();
}
SANE_Status DecodeSessionResp (const SANE_Byte *data, size_t data_len,

Wyświetl plik

@ -967,6 +967,10 @@ sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
params->lines = pixel_y_height;
params->last_frame = SANE_TRUE;
DBG (DBG_IMPORTANT,
"sane_get_parameters: Selected image dimensions: width=%zu height=%zu\n",
(size_t)pixel_x_width, (size_t)pixel_y_height);
rc = device->driver->SetScanDimensions (pixel_x_offset,
pixel_x_width,
pixel_y_offset,