Porównaj commity

...

31 Commity

Autor SHA1 Wiadomość Data
Ralph Little b31b82c8bd Merge branch 'brother_mfp_backend' into 'master'
Draft: Brother MFP backend

See merge request sane-project/backends!751
2024-04-21 06:52:43 +00:00
Ralph Little 0f472aa205 Merge branch 'editorconfig_inline_comment' into 'master'
.editorconfig: inline comments are forbidden

See merge request sane-project/backends!832
2024-04-19 19:53:22 +00:00
Guillaume Girol a6d63a72ec .editorconfig: inline comments are forbidden
source: https://spec.editorconfig.org/#no-inline-comments

this makes neovim unhappy, at least.
2024-02-26 12:00:00 +00:00
Ralph Little c6b0b1871c brother_mfp: Fix for 1-sided ADF selection
Also added definition for new family 4 machine MFC 8910DW with can do
duplex ADF.
2023-12-11 13:06:26 -08:00
Ralph Little 32be74e1f6 brother_mfp: Added first draft of brother5 family decoder. 2023-12-11 13:06:26 -08:00
Ralph Little 45416ffe03 brother_mfp: start of network driver.
We start with SNMP experiments for button registration and general
network driver framework.
2023-12-11 13:06:26 -08:00
Ralph Little 311a38c76f brother_mfp: added first draft of man page for backend. 2023-12-11 13:05:54 -08:00
Ralph Little ceb7342877 brother_mfp: corrections for ADF cap flags and model definitions. 2023-12-11 13:02:32 -08:00
Ralph Little cc4ad6be99 brother_mfp: Added ADF duplex function for supported devices. 2023-12-11 13:02:32 -08:00
Ralph Little 4173926d3b brother_mfp: Added additional test suites for brother2 and brother3
Also some encoder tests and general code tidy up.
2023-12-11 13:02:32 -08:00
Ralph Little b64b8c13bb brother_mfp: Added support for source option.
Can now select Flatbed or ADF for supported machines.
2023-12-11 13:02:32 -08:00
Ralph Little ca760d54b3 brother_mfp: General tidy up ready for next stage. 2023-12-11 13:02:32 -08:00
Ralph Little a8a9e895b9 brother_mfp: added query block and error conditions
Now detects cover open and paper jam.
Also formalised the query packet which occurs during initialization.
Hopefully, we will be able to decode this for useful information.
2023-12-11 13:02:32 -08:00
Ralph Little 9bbf24cf97 brother_mfp: updated unit tests for changes to decoder API 2023-12-11 13:02:32 -08:00
Ralph Little 40de9f87b8 brother_mfp: added preliminary ADF multisheet support.
I don't have a brother4 machine with an ADF so could not test this.
Quite a bit of refactoring to the encoder infrastructure to
accommodate this.
2023-12-11 13:02:32 -08:00
Ralph Little d95166777f brother_mfp: added split resolution option
By detail, user can now select a single resolution value or split x
and y resolutions with an additional "split resolution" option.

Default is to enable combine resolution.
2023-12-11 13:02:32 -08:00
Ralph Little 76481920c3 brother_mfp: Added preview option.
Also fixed some warnings that affected the pipeline.
2023-12-11 13:02:32 -08:00
Ralph Little a484c6ad93 brother_mfp: added device cancel and compression option
Some devices can compress colour (JPEG) and also provide raw data
in uncompressed form. The two devices that I have supply raw data
not in RBG but in YCbCr form.
Now applications have the option to choose compressed or uncompressed
colour scans if supported by the device.

Cancel from the device by pressing STOP is now handled properly.
2023-12-11 13:02:32 -08:00
Ralph Little a6f4f816b1 brother_mfp: Added draft for brother3 driver.
Test machine MFC-290C seems to support JPEG and RAW colour data.
However, the raw colour data has what appears to be a strong green
component leading to very green images. Maybe some post processing
is required to generate a proper image. Dunno. JPEG looks perfectly fine.
2023-12-11 13:02:32 -08:00
Ralph Little fbe872ae86 brother_mfp: brother2 driver requires uncompressed gray decoder also
Noticed in lineart output.
2023-12-11 13:02:32 -08:00
Ralph Little 0055214614 brother_mfp: Added brother2 decoder + button support
Also:
- Fixed major issue in gray decoder that caused data loss.
- Button support added for Brother2 decoder.
2023-12-11 13:02:32 -08:00
Ralph Little 437bfd27f3 brother_mfp: colour scan now fully functional.
Also:
- Init recovery for when previous crash left a session open.
- Small corrections for gray scans
2023-12-11 13:02:32 -08:00
Ralph Little 3fae2e561d brother_mfp: corrected some decoding bugs.
Still one rare illusive issue that causes loss of a bit of a line's
data, resulting in a sliding of the image to the left. It doesn't
happen very often, very difficult to reproduce.
2023-12-11 13:02:32 -08:00
Ralph Little 22d8ab2b49 brother_mfp: first set of unit tests for decoder.
Looks like we found the rare lost data issue in gray decoding.
Yayy! for unit tests.
2023-12-11 13:02:32 -08:00
Ralph Little 6eb9e1cc43 brother_mfp: Added basic test framework and moved source into brother_mfp directory 2023-12-11 13:02:32 -08:00
Ralph Little 8280db1eef brother_mfp: compile out test scan dump to file. Also fix up some bugs.
Hopefully, the fussy pipeline likes this version.
2023-12-11 13:02:31 -08:00
Ralph Little 79a9b6ac44 brother_mfp: fixes to hopefully satisfy the pipeline. 2023-12-11 13:02:31 -08:00
Ralph Little 7f763c0763 brother_mfp: silence temp. parameter warnings upsetting the pipeline. 2023-12-11 13:02:31 -08:00
Ralph Little 4f060d6cea brother_mfp: fix for makefile and other small enhancements. 2023-12-11 13:02:31 -08:00
Ralph Little a7d6a553ee brother_mfp: First C++ draft.
This version is a refactor using bits and pieces from the prototype.
Converted from C to C++ to make code reuse easier.
Will do gray/errdif/text scanning on the test MFC-J4320DW machine.
Cancel function added.
2023-12-11 13:02:31 -08:00
Ralph Little a9853d1bb8 brother_mfp: first draft prototype
Supports only grey/dithered_gray and text on MFC-4320DW.
Supports only USB.
2023-12-11 13:02:31 -08:00
25 zmienionych plików z 10270 dodań i 8 usunięć

Wyświetl plik

@ -6,7 +6,8 @@
# Your editor may need a plugin for this configuration to take effect.
# See http://editorconfig.org/#download for details.
root = true ; look no further
; look no further
root = true
[*]
charset = utf-8

Wyświetl plik

@ -577,6 +577,13 @@ for be in ${BACKENDS}; do
fi
;;
brother_mfp)
if test "${HAVE_CXX11}" != "1"; then
echo "*** $be backend requires C++11 support - $DISABLE_MSG"
backend_supported="no"
fi
;;
mustek_pp)
if test "${sane_cv_use_libieee1284}" != "yes" && test "${enable_parport_directio}" != "yes"; then
echo "*** $be backend requires libieee1284 or parport-directio libraries - $DISABLE_MSG"

Wyświetl plik

@ -66,7 +66,7 @@ EXTRA_DIST += saned.conf.in
# Backends are not required to have a config file. Any backend
# that wants to install a config file should list it here.
BACKEND_CONFS= abaton.conf agfafocus.conf apple.conf artec.conf \
artec_eplus48u.conf avision.conf bh.conf \
artec_eplus48u.conf avision.conf bh.conf brother_mfp.conf \
canon630u.conf canon.conf canon_dr.conf \
canon_lide70.conf \
canon_pp.conf cardscan.conf coolscan2.conf coolscan3.conf \
@ -160,7 +160,7 @@ clean-local:
# example of working around that issue.
be_convenience_libs = libabaton.la libagfafocus.la \
libapple.la libartec.la libartec_eplus48u.la \
libas6e.la libavision.la libbh.la \
libas6e.la libavision.la libbh.la libbrother_mfp.la \
libcanon.la libcanon630u.la libcanon_dr.la \
libcanon_lide70.la \
libcanon_pp.la libcardscan.la libcoolscan.la \
@ -194,7 +194,7 @@ be_convenience_libs = libabaton.la libagfafocus.la \
# Format is libsane-${backend}.la.
be_dlopen_libs = libsane-abaton.la libsane-agfafocus.la \
libsane-apple.la libsane-artec.la libsane-artec_eplus48u.la \
libsane-as6e.la libsane-avision.la libsane-bh.la \
libsane-as6e.la libsane-avision.la libsane-bh.la libsane-brother_mfp.la \
libsane-canon.la libsane-canon630u.la libsane-canon_dr.la \
libsane-canon_lide70.la \
libsane-canon_pp.la libsane-cardscan.la libsane-coolscan.la \
@ -381,6 +381,34 @@ libsane_bh_la_LIBADD = $(COMMON_LIBS) libbh.la \
$(SCSI_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += bh.conf.in
libbrother_mfp_la_SOURCES = \
brother_mfp/brother_mfp-driver_usb.cpp \
brother_mfp/brother_mfp-driver_network.cpp \
brother_mfp/brother_mfp-driver.h \
brother_mfp/brother_mfp-common.h \
brother_mfp/brother_mfp-encoder.cpp \
brother_mfp/brother_mfp-encoder.h \
brother_mfp/brother_mfp.cpp
libbrother_mfp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=brother_mfp $(SNMP_CFLAGS)
nodist_libsane_brother_mfp_la_SOURCES = brother_mfp-s.cpp
libsane_brother_mfp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=brother_mfp
libsane_brother_mfp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
libsane_brother_mfp_la_LIBADD = $(COMMON_LIBS) libbrother_mfp.la \
../sanei/sanei_init_debug.lo \
../sanei/sanei_constrain_value.lo \
../sanei/sanei_usb.lo \
../sanei/sanei_config.lo \
sane_strstatus.lo \
$(USB_LIBS) $(JPEG_LIBS) $(SNMP_LIBS)
EXTRA_DIST += brother_mfp.conf.in
libcanon_la_SOURCES = canon.c canon.h
libcanon_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon

Wyświetl plik

@ -0,0 +1,11 @@
# Options for the canon_lide70 backend
# Autodetect the Canon CanoScan LiDE 70
usb 0x04a9 0x2225
# Autodetect the Canon CanoScan LiDE 600
usb 0x04a9 0x2224
# device list for non-linux-systems (enable if autodetect fails):
#/dev/scanner
#/dev/usb/scanner0

Wyświetl plik

@ -0,0 +1,66 @@
/* sane - Scanner Access Now Easy.
BACKEND brother_mfp
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
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, see <https://www.gnu.org/licenses/>.
This file implements a SANE backend for Brother Multifunction Devices.
*/
#pragma once
#define BACKEND_NAME brother_mfp
#define BROTHER_MFP_CONFIG_FILE "brother_mfp.conf"
/*
* Model capabilities.
*
* TODO: Some of these capabilities might be redundant if all models possess them.
* E.g. MODE_GRAY
*
*/
#define CAP_MODE_COLOUR (1u << 0)
#define CAP_MODE_GRAY (1u << 1)
#define CAP_MODE_GRAY_DITHER (1u << 2)
#define CAP_MODE_BW (1u << 3)
#define CAP_BUTTON_HAS_SCAN_EMAIL (1u << 4)
#define CAP_BUTTON_HAS_SCAN_OCR (1u << 5)
#define CAP_BUTTON_HAS_SCAN_FILE (1u << 6)
#define CAP_BUTTON_HAS_SCAN_IMAGE (1u << 7)
#define CAP_SOURCE_HAS_FLATBED (1u << 8)
#define CAP_SOURCE_HAS_ADF (1u << 9)
#define CAP_SOURCE_HAS_ADF_DUPLEX (1u << 10)
#define CAP_ENCODING_HAS_RAW (1u << 11)
#define CAP_ENCODING_HAS_JPEG (1u << 12)
// Oddities of particular models.
#define CAP_ENCODING_RAW_IS_CrYCb (1u << 13)
/*
* Diagnostic levels.
*
*/
#define DBG_IMPORTANT 1
#define DBG_SERIOUS 2
#define DBG_WARN 3
#define DBG_EVENT 4
#define DBG_DETAIL 5
#define DBG_DEBUG 6

Wyświetl plik

@ -0,0 +1,242 @@
/* sane - Scanner Access Now Easy.
BACKEND brother_mfp
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
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, see <https://www.gnu.org/licenses/>.
This file implements a SANE backend for Brother Multifunction Devices.
*/
#pragma once
#define BUILD 0
#include "../include/sane/config.h"
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "brother_mfp-common.h"
#include "../include/sane/sane.h"
#include "../include/sane/sanei.h"
#include "../include/sane/saneopts.h"
#include "../include/sane/sanei_config.h"
#include "../include/sane/sanei_usb.h"
#include "brother_mfp-encoder.h"
/*-----------------------------------------------------------------*/
/*
* Handy timeout macros for use with usecond_t-based functions.
*
*/
#define TIMEOUT_SECS(x) ((x) * 1000000)
#define TIMEOUT_MILLISECS(x) ((x) * 1000)
#define TIMEOUT_MICROSECS(x) (x)
/*
* Transport base and implementations.
*
* We have a base which lays the groundwork for our
* scanner communications and the interface for the SANE
* frontend to talk with the device.
*
* We have implementations for both USB and Ethernet.
*
*/
class BrotherDriver
{
public:
explicit BrotherDriver (BrotherFamily family, SANE_Word capabilities);
BrotherDriver(const BrotherDriver &) = delete;
BrotherDriver &operator=(const BrotherDriver &) = delete;
virtual ~BrotherDriver();
/*
* Will include the INIT sequence.
*
*/
virtual SANE_Status Connect () = 0;
/*
* Will ensure that we stop a session if active.
*
*/
virtual SANE_Status Disconnect () = 0;
/*
* Will open a session.
*
*/
virtual SANE_Status StartScan () = 0;
virtual SANE_Status CancelScan () = 0;
virtual SANE_Status CheckSensor(BrotherSensor &status) = 0;
virtual SANE_Status ReadScanData (SANE_Byte *data, size_t data_len,
size_t *bytes_read) = 0;
/*
* Parameter setting.
*
*/
SANE_Status SetSource (BrotherSource source)
{
return encoder->DecodeStatusToSaneStatus (encoder->SetSource (source));
}
SANE_Status SetScanMode (BrotherScanMode scan_mode)
{
return encoder->DecodeStatusToSaneStatus (encoder->SetScanMode (scan_mode));
}
SANE_Status SetRes (SANE_Int x, SANE_Int y)
{
return encoder->DecodeStatusToSaneStatus (encoder->SetRes (x, y));
}
SANE_Status SetContrast (SANE_Int contrast)
{
return encoder->DecodeStatusToSaneStatus (encoder->SetContrast (contrast));
}
SANE_Status SetBrightness (SANE_Int brightness)
{
return encoder->DecodeStatusToSaneStatus (encoder->SetBrightness (brightness));
}
SANE_Status SetCompression (SANE_Bool compression)
{
return encoder->DecodeStatusToSaneStatus (encoder->SetCompression (compression));
}
SANE_Status SetScanDimensions (SANE_Int pixel_x_offset, SANE_Int pixel_x_width,
SANE_Int pixel_y_offset, SANE_Int pixel_y_height)
{
return encoder->DecodeStatusToSaneStatus (encoder->SetScanDimensions (pixel_x_offset,
pixel_x_width,
pixel_y_offset,
pixel_y_height));
}
protected:
BrotherFamily family;
SANE_Word capabilities;
BrotherEncoder *encoder;
};
class BrotherUSBDriver : public BrotherDriver
{
public:
BrotherUSBDriver (const char *devicename, BrotherFamily family, SANE_Word capabilities);
~BrotherUSBDriver ();
SANE_Status Connect () override;
SANE_Status Disconnect () override;
SANE_Status StartScan () override;
SANE_Status CancelScan () override;
SANE_Status CheckSensor(BrotherSensor &status) override;
SANE_Status ReadScanData (SANE_Byte *data, size_t data_len,
size_t *bytes_read) override;
private:
SANE_Status StartSession ();
SANE_Status StopSession ();
SANE_Status ExecStartSession ();
SANE_Status ExecStopSession ();
SANE_Status Init ();
SANE_Status PollForReadFlush (useconds_t max_time);
SANE_Status PollForRead (SANE_Byte *buffer, size_t *buf_len,
useconds_t *max_time);
bool is_open;
bool in_session;
bool is_scanning;
bool was_cancelled;
char *devicename;
SANE_Int next_frame_number;
SANE_Int fd;
SANE_Byte small_buffer[1024];
SANE_Byte *data_buffer;
size_t data_buffer_bytes;
bool out_of_docs;
};
class BrotherNetworkDriver : public BrotherDriver
{
public:
BrotherNetworkDriver (const char *devicename, BrotherFamily family, SANE_Word capabilities);
~BrotherNetworkDriver ();
SANE_Status Connect () override;
SANE_Status Disconnect () override;
SANE_Status StartScan () override;
SANE_Status CancelScan () override;
SANE_Status CheckSensor(BrotherSensor &status) override;
SANE_Status ReadScanData (SANE_Byte *data, size_t data_len,
size_t *bytes_read) override;
private:
SANE_Status Init ();
SANE_Status SNMPRegisterButtons ();
// SANE_Status PollForReadFlush (useconds_t max_time);
// SANE_Status PollForRead (SANE_Byte *buffer, size_t *buf_len,
// useconds_t *max_time);
bool is_open;
bool in_session;
bool is_scanning;
bool was_cancelled;
char *devicename;
SANE_Int next_frame_number;
SANE_Byte small_buffer[1024];
SANE_Byte *data_buffer;
size_t data_buffer_bytes;
bool out_of_docs;
int sockfd;
};

Wyświetl plik

@ -0,0 +1,309 @@
/* sane - Scanner Access Now Easy.
BACKEND brother_mfp
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
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, see <https://www.gnu.org/licenses/>.
This file implements a SANE backend for Brother Multifunction Devices.
*/
#define DEBUG_DECLARE_ONLY
#include "../include/sane/config.h"
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <algorithm>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <netdb.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include "../include/sane/sane.h"
#include "../include/sane/sanei.h"
#include "../include/sane/saneopts.h"
#include "../include/sane/sanei_config.h"
#include "../include/sane/sanei_debug.h"
#include "brother_mfp-common.h"
#include "brother_mfp-driver.h"
#define BROTHER_READ_BUFFER_LEN (16 * 1024)
/*-----------------------------------------------------------------*/
union BrotherSocketAddr
{
struct sockaddr_storage storage;
struct sockaddr addr;
struct sockaddr_in ipv4;
struct sockaddr_in6 ipv6;
};
SANE_Status BrotherNetworkDriver::Connect ()
{
int val;
if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
{
return SANE_STATUS_IO_ERROR;
}
val = 1;
setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
/*
* Using TCP_NODELAY improves responsiveness, especially on systems
* with a slow loopback interface...
*/
val = 1;
setsockopt (sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
/*
* Close this socket when starting another process...
*/
fcntl (sockfd, F_SETFD, FD_CLOEXEC);
BrotherSocketAddr adr_inet;
memset (&adr_inet, 0, sizeof adr_inet);
adr_inet.ipv4.sin_family = AF_INET;
adr_inet.ipv4.sin_port = htons (54921);
if (!inet_aton ("192.168.1.19", &adr_inet.ipv4.sin_addr))
{
return SANE_STATUS_IO_ERROR;
}
if (connect (sockfd, (struct sockaddr*) &adr_inet, sizeof(adr_inet.ipv4)) < 0)
{
return SANE_STATUS_IO_ERROR;
}
SANE_Status init_res = Init ();
if (init_res != SANE_STATUS_GOOD)
{
DBG (DBG_SERIOUS, "BrotherUSBDriver::Connect: failed to send init session: %d\n", init_res);
Disconnect ();
}
/*
* Register buttons.
*
*/
(void)SNMPRegisterButtons();
return SANE_STATUS_NO_MEM;
}
#if HAVE_LIBSNMP
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#endif
SANE_Status BrotherNetworkDriver::SNMPRegisterButtons ()
{
#if HAVE_LIBSNMP
init_snmp ("brother_mfp");
/*
* Initialize a "session" that defines who we're going to talk to
*/
struct snmp_session session;
snmp_sess_init (&session); /* set up defaults */
session.peername = (char*) "192.168.1.19";
session.version = SNMP_VERSION_1;
/* set the SNMPv1 community name used for authentication */
session.community = (u_char*) "internal";
session.community_len = strlen ("internal");
SOCK_STARTUP;
/*
* Open the session
*/
struct snmp_session *ss = snmp_open (&session);
if (!ss)
{
snmp_perror ("ack");
snmp_log (LOG_ERR, "something horrible happened!!!\n");
return SANE_STATUS_IO_ERROR;
}
netsnmp_pdu *pdu;
oid the_oid[MAX_OID_LEN];
size_t oid_len;
const char *oid_str = ".1.3.6.1.4.1.2435.2.3.9.2.11.1.1.0";
pdu = snmp_pdu_create (SNMP_MSG_SET);
oid_len = MAX_OID_LEN;
// Parse the OID
if (snmp_parse_oid (oid_str, the_oid, &oid_len) == 0)
{
snmp_perror (oid_str);
return SANE_STATUS_IO_ERROR;
}
struct ButtonApp
{
const char *function;
unsigned int app_num;
} button_apps[] =
{
{ "IMAGE", 1 },
{ "OCR", 3 },
{ "FILE", 5 },
{ "EMAIL", 2 },
{ nullptr, 0 } };
const char *backend_host_addr = "192.168.1.12";
unsigned int backend_host_port = 54925;
const char *backend_host_name = "BACKEND_HOST";
unsigned int reg_lifetime = 360;
for (const ButtonApp *app = button_apps; app->function; app++)
{
(void) snprintf ((char*) small_buffer,
sizeof(small_buffer),
"TYPE=BR;BUTTON=SCAN;USER=\"%s\";FUNC=%s;HOST=%s:%u;APPNUM=%u;DURATION=%u;",
backend_host_name,
app->function,
backend_host_addr,
backend_host_port,
app->app_num,
reg_lifetime);
if (snmp_pdu_add_variable (pdu, the_oid, oid_len, ASN_OCTET_STR,
(const char*) small_buffer, strlen ((const char*) small_buffer))
== nullptr)
{
snmp_perror ("failed");
return SANE_STATUS_IO_ERROR;
}
}
// Send the request
if (snmp_send (ss, pdu) == 0)
{
snmp_perror ("SNMP: Error while sending!");
snmp_free_pdu (pdu);
return SANE_STATUS_IO_ERROR;
}
// TODO: cleanup and shutdown of SNMP stuff!!!!!
return SANE_STATUS_NO_MEM;
#else
return SANE_STATUS_GOOD;
#endif
}
SANE_Status BrotherNetworkDriver::Disconnect ()
{
DBG (DBG_EVENT, "BrotherNetworkDriver::Disconnect: `%s'\n", devicename);
if (sockfd)
{
close(sockfd);
sockfd = 0;
}
return SANE_STATUS_GOOD;
}
SANE_Status BrotherNetworkDriver::Init ()
{
return SANE_STATUS_UNSUPPORTED;
}
SANE_Status BrotherNetworkDriver::CancelScan ()
{
return SANE_STATUS_UNSUPPORTED;
}
SANE_Status BrotherNetworkDriver::StartScan ()
{
return SANE_STATUS_UNSUPPORTED;
}
SANE_Status BrotherNetworkDriver::CheckSensor (BrotherSensor &status)
{
(void)status;
return SANE_STATUS_UNSUPPORTED;
}
SANE_Status BrotherNetworkDriver::ReadScanData (SANE_Byte *data, size_t max_length, size_t *length)
{
(void)data;
(void)max_length;
(void)length;
return SANE_STATUS_UNSUPPORTED;
}
BrotherNetworkDriver::BrotherNetworkDriver (const char *devicename, BrotherFamily family,
SANE_Word capabilities) :
BrotherDriver (family, capabilities),
is_open (false),
in_session (false),
is_scanning (false),
was_cancelled (false),
devicename (nullptr),
next_frame_number (0),
small_buffer { 0 },
data_buffer (nullptr),
data_buffer_bytes (0),
out_of_docs (false),
sockfd (0)
{
this->devicename = strdup(devicename);
}
BrotherNetworkDriver::~BrotherNetworkDriver ()
{
delete[] data_buffer;
free (devicename);
}

Wyświetl plik

@ -0,0 +1,756 @@
/* sane - Scanner Access Now Easy.
BACKEND brother_mfp
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
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, see <https://www.gnu.org/licenses/>.
This file implements a SANE backend for Brother Multifunction Devices.
*/
#pragma once
#include "../include/sane/config.h"
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <setjmp.h>
#include <jpeglib.h>
#include <jerror.h>
#include "brother_mfp-common.h"
#include "../include/sane/sane.h"
#include "../include/sane/sanei.h"
#include "../include/sane/saneopts.h"
#include "../include/sane/sanei_config.h"
#include "../include/sane/sanei_usb.h"
/*
* Functionality sets.
*
* For the moment, I am going to assume that the various proprietary
* drivers follow the broad protocol versions and I will reflect this
* until I see different.
*
* So these will reflect the brscan2, brscan3, etc that Brother use.
*
* We will have a encoder/decoder for each family.
* From what I have seen, pretty much all the Brother families implement
* the same basic protocol sequence, just with variations in technology.
* So we don't need to abstract out the entire sequence, just the encoding
* and decoding of each packet. The biggest aspect by far is the difference
* in scan data formats.
*
*/
typedef enum
{
BROTHER_FAMILY_NONE,
BROTHER_FAMILY_1,
BROTHER_FAMILY_2,
BROTHER_FAMILY_3,
BROTHER_FAMILY_4,
BROTHER_FAMILY_5
} BrotherFamily;
typedef enum
{
BROTHER_SCAN_MODE_COLOR,
BROTHER_SCAN_MODE_GRAY,
BROTHER_SCAN_MODE_GRAY_DITHERED,
BROTHER_SCAN_MODE_TEXT
} BrotherScanMode;
typedef enum
{
BROTHER_SOURCE_NONE,
BROTHER_SOURCE_AUTO,
BROTHER_SOURCE_FLATBED,
BROTHER_SOURCE_ADF,
BROTHER_SOURCE_ADF_DUPLEX,
} BrotherSource;
typedef SANE_Int BrotherSensor;
#define BROTHER_SENSOR_NONE 0
#define BROTHER_SENSOR_EMAIL (1 << 0)
#define BROTHER_SENSOR_FILE (1 << 1)
#define BROTHER_SENSOR_OCR (1 << 2)
#define BROTHER_SENSOR_IMAGE (1 << 3)
struct BrotherSessionResponse
{
SANE_Bool ready;
};
struct BrotherButtonQueryResponse
{
SANE_Bool has_button_press;
};
struct BrotherButtonStateResponse
{
BrotherSensor button_value; // raw value we get from the packet
};
struct BrotherParameters
{
BrotherParameters():
param_brightness (0),
param_contrast (0),
param_compression (SANE_TRUE),
param_pixel_x_offset (0),
param_pixel_x_width (0),
param_pixel_y_offset (0),
param_pixel_y_height (0),
param_scan_mode (BROTHER_SCAN_MODE_COLOR),
param_source(BROTHER_SOURCE_FLATBED),
param_x_res(100),
param_y_res(100)
{
}
SANE_Int param_brightness;
SANE_Int param_contrast;
SANE_Bool param_compression;
SANE_Int param_pixel_x_offset;
SANE_Int param_pixel_x_width;
SANE_Int param_pixel_y_offset;
SANE_Int param_pixel_y_height;
BrotherScanMode param_scan_mode;
BrotherSource param_source;
SANE_Int param_x_res;
SANE_Int param_y_res;
};
struct BrotherBasicParamResponse
{
int dummy;
// fill me
};
struct BrotherQueryResponse
{
int dummy;
// fill me
};
struct BrotherSourceStatusResponse
{
SANE_Bool source_ready;
};
/*
* DecodeStatus
*
*/
enum DecodeStatus
{
DECODE_STATUS_GOOD,
DECODE_STATUS_TRUNCATED,
DECODE_STATUS_ENDOFDATA,
DECODE_STATUS_ENDOFFRAME_NO_MORE,
DECODE_STATUS_ENDOFFRAME_WITH_MORE,
DECODE_STATUS_CANCEL,
DECODE_STATUS_ERROR,
DECODE_STATUS_MEMORY,
DECODE_STATUS_INVAL,
DECODE_STATUS_UNSUPPORTED,
DECODE_STATUS_PAPER_JAM,
DECODE_STATUS_COVER_OPEN,
DECODE_STATUS_NO_DOCS,
};
struct ScanDataHeader
{
ScanDataHeader():
block_type(0),
block_len(0),
image_number(0)
{
}
SANE_Byte block_type;
size_t block_len;
SANE_Int image_number;
};
class BrotherEncoder
{
public:
BrotherEncoder ()
{
}
virtual ~BrotherEncoder ()
{
}
virtual void NewPage () = 0;
DecodeStatus SetSource (BrotherSource source);
DecodeStatus SetScanMode (BrotherScanMode scan_mode);
DecodeStatus SetRes (SANE_Int x, SANE_Int y);
DecodeStatus SetContrast (SANE_Int contrast);
DecodeStatus SetBrightness (SANE_Int brightness);
DecodeStatus SetCompression (SANE_Bool compression);
DecodeStatus SetScanDimensions (SANE_Int pixel_x_offset, SANE_Int pixel_x_width,
SANE_Int pixel_y_offset, SANE_Int pixel_y_height);
SANE_Status DecodeStatusToSaneStatus(DecodeStatus dec_ret)
{
static SANE_Status status_lookup[] =
{
SANE_STATUS_GOOD,
SANE_STATUS_GOOD,
SANE_STATUS_EOF,
SANE_STATUS_EOF,
SANE_STATUS_EOF,
SANE_STATUS_CANCELLED,
SANE_STATUS_IO_ERROR,
SANE_STATUS_NO_MEM,
SANE_STATUS_INVAL,
SANE_STATUS_UNSUPPORTED,
SANE_STATUS_JAMMED,
SANE_STATUS_COVER_OPEN,
SANE_STATUS_NO_DOCS,
};
return status_lookup[dec_ret];
}
const char *DecodeStatusToString(DecodeStatus dec_ret)
{
static const char * status_lookup[] =
{
"DECODE_STATUS_GOOD",
"DECODE_STATUS_TRUNCATED",
"DECODE_STATUS_ENDOFDATA",
"DECODE_STATUS_ENDOFFRAME_NO_MORE",
"DECODE_STATUS_ENDOFFRAME_WITH_MORE",
"DECODE_STATUS_CANCEL",
"DECODE_STATUS_ERROR",
"DECODE_STATUS_MEMORY",
"DECODE_STATUS_INVAL",
"DECODE_STATUS_UNSUPPORTED",
"DECODE_STATUS_PAPER_JAM",
"DECODE_STATUS_COVER_OPEN",
"DECODE_STATUS_NO_DOCS",
};
return status_lookup[dec_ret];
}
virtual const char* ScanModeToText (BrotherScanMode scan_mode);
// Buttons.
virtual DecodeStatus DecodeSessionResp (const SANE_Byte *data, size_t data_len,
BrotherSessionResponse &response) = 0;
virtual DecodeStatus DecodeButtonQueryResp (const SANE_Byte *data, size_t data_len,
BrotherButtonQueryResponse &response) = 0;
virtual DecodeStatus DecodeButtonStateResp (const SANE_Byte *data, size_t data_len,
BrotherButtonStateResponse &response) = 0;
// Requests and responses.
virtual DecodeStatus EncodeQueryBlock (SANE_Byte *data, size_t data_len, size_t *length) = 0;
virtual DecodeStatus DecodeQueryBlockResp (const SANE_Byte *data, size_t data_len,
BrotherQueryResponse &response) = 0;
virtual DecodeStatus EncodeBasicParameterBlock (SANE_Byte *data, size_t data_len,
size_t *length) = 0;
virtual DecodeStatus DecodeBasicParameterBlockResp (const SANE_Byte *data, size_t data_len,
BrotherBasicParamResponse &response) = 0;
virtual DecodeStatus EncodeSourceStatusBlock (SANE_Byte *data, size_t data_len, size_t *length) = 0;
virtual DecodeStatus DecodeSourceStatusBlockResp (const SANE_Byte *data, size_t data_len,
BrotherSourceStatusResponse &response) = 0;
virtual DecodeStatus EncodeSourceSelectBlock (SANE_Byte *data, size_t data_len, size_t *length) = 0;
virtual DecodeStatus DecodeSourceSelectBlockResp (const SANE_Byte *data, size_t data_len,
BrotherSourceStatusResponse &response) = 0;
virtual DecodeStatus EncodeParameterBlock (SANE_Byte *data, size_t data_len, size_t *length) = 0;
virtual DecodeStatus EncodeParameterBlockBlank (SANE_Byte *data, size_t data_len,
size_t *length) = 0;
// Scan data decoding.
virtual DecodeStatus DecodeScanData (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) = 0;
protected:
BrotherParameters scan_params;
};
class BrotherJFIFDecoder
{
public:
BrotherJFIFDecoder():
decompress_bytes(0)
{
/*
* Not sure if this is a safe way to get the cinfo into
* a consistent state but at least all pointers will be NULL.
*
*/
(void)memset(&state.cinfo, 0, sizeof(state.cinfo));
state.cinfo.err = jpeg_std_error(&state.jerr);
state.cinfo.err->error_exit = ErrorExitManager;
jpeg_create_decompress (&state.cinfo);
/*
* Set up source manager.
*
*/
state.src_mgr.init_source = InitSource;
state.src_mgr.fill_input_buffer = FillInputBuffer;
state.src_mgr.skip_input_data = SkipInputData;
state.src_mgr.resync_to_restart = jpeg_resync_to_restart;
state.src_mgr.term_source = TermSource;
state.cinfo.src = &state.src_mgr;
}
static void ErrorExitManager(j_common_ptr cinfo);
~BrotherJFIFDecoder()
{
jpeg_destroy_decompress(&state.cinfo);
}
void NewBlock();
void NewPage(const BrotherParameters &params);
DecodeStatus DecodeScanData (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);
private:
static void InitSource (j_decompress_ptr cinfo);
static boolean FillInputBuffer(j_decompress_ptr cinfo);
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;
struct jpeg_error_mgr jerr;
struct jpeg_source_mgr src_mgr;
bool have_read_header;
jmp_buf my_env;
} state;
SANE_Byte decompress_buffer[1024 * 16];
size_t decompress_bytes;
BrotherParameters decode_params;
};
class BrotherGrayRLengthDecoder
{
public:
BrotherGrayRLengthDecoder()
{
}
void NewBlock();
void NewPage(const BrotherParameters &params);
DecodeStatus DecodeScanData (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);
private:
BrotherParameters decode_params;
};
class BrotherInterleavedRGBColourDecoder
{
public:
explicit BrotherInterleavedRGBColourDecoder(SANE_Word capabilities):
capabilities(capabilities),
scanline_buffer(nullptr),
scanline_buffer_size(0),
scanline_length(0),
scanline_buffer_data(0)
{
}
~BrotherInterleavedRGBColourDecoder()
{
free(scanline_buffer);
}
void NewBlock();
void NewPage(const BrotherParameters &params);
DecodeStatus DecodeScanData (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);
enum ChannelEncoding
{
CHANNELS_RGB, // RGB
CHANNELS_CrYCb // YCrCb
};
private:
static void ConvertYCbCrToRGB (SANE_Byte y, SANE_Byte cb, SANE_Byte cr, SANE_Byte *red,
SANE_Byte *green, SANE_Byte *blue);
BrotherParameters decode_params;
SANE_Word capabilities;
SANE_Byte *scanline_buffer;
size_t scanline_buffer_size;
size_t scanline_length;
size_t scanline_buffer_data;
};
class BrotherGrayRawDecoder
{
public:
BrotherGrayRawDecoder()
{
}
void NewBlock();
void NewPage(const BrotherParameters &params);
DecodeStatus DecodeScanData (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);
private:
BrotherParameters decode_params;
};
class BrotherEncoderFamily2 : public BrotherEncoder
{
public:
explicit BrotherEncoderFamily2(SANE_Word capabilities):
colour_decoder(capabilities)
{
}
~BrotherEncoderFamily2 ()
{
}
void NewPage () override
{
current_header.block_type = 0;
jfif_decoder.NewPage (scan_params);
gray_decoder.NewPage (scan_params);
gray_raw_decoder.NewPage (scan_params);
colour_decoder.NewPage (scan_params);
}
DecodeStatus EncodeQueryBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus DecodeQueryBlockResp (const SANE_Byte *data, size_t data_len,
BrotherQueryResponse &response) override;
DecodeStatus DecodeSessionResp (const SANE_Byte *data, size_t data_len,
BrotherSessionResponse &response) override;
DecodeStatus EncodeBasicParameterBlock (SANE_Byte *data, size_t data_len, size_t *length)
override;
DecodeStatus DecodeBasicParameterBlockResp (const SANE_Byte *data, size_t data_len,
BrotherBasicParamResponse &response) override;
DecodeStatus EncodeSourceStatusBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus DecodeSourceStatusBlockResp (const SANE_Byte *data, size_t data_len,
BrotherSourceStatusResponse &response) override;
DecodeStatus EncodeSourceSelectBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus DecodeSourceSelectBlockResp (const SANE_Byte *data, size_t data_len,
BrotherSourceStatusResponse &response) override;
DecodeStatus EncodeParameterBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus EncodeParameterBlockBlank (SANE_Byte *data, size_t data_len, size_t *length)
override;
DecodeStatus DecodeScanData (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) override;
DecodeStatus DecodeButtonQueryResp (const SANE_Byte *data, size_t data_len,
BrotherButtonQueryResponse &response) override;
DecodeStatus DecodeButtonStateResp (const SANE_Byte *data, size_t data_len,
BrotherButtonStateResponse &response) override;
private:
DecodeStatus DecodeScanDataHeader (const SANE_Byte *src_data, size_t src_data_len,
size_t *src_data_consumed, ScanDataHeader &header);
ScanDataHeader current_header;
BrotherJFIFDecoder jfif_decoder;
BrotherGrayRLengthDecoder gray_decoder;
BrotherGrayRawDecoder gray_raw_decoder;
BrotherInterleavedRGBColourDecoder colour_decoder;
};
class BrotherEncoderFamily3 : public BrotherEncoder
{
public:
explicit BrotherEncoderFamily3(SANE_Word capabilities):
colour_decoder(capabilities)
{
}
~BrotherEncoderFamily3 ()
{
}
void NewPage() override
{
current_header.block_type = 0;
gray_raw_decoder.NewPage(scan_params);
gray_decoder.NewPage(scan_params);
colour_decoder.NewPage(scan_params);
jfif_decoder.NewPage(scan_params);
}
DecodeStatus EncodeQueryBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus DecodeQueryBlockResp (const SANE_Byte *data, size_t data_len,
BrotherQueryResponse &response) override;
DecodeStatus DecodeSessionResp (const SANE_Byte *data, size_t data_len,
BrotherSessionResponse &response) override;
DecodeStatus EncodeBasicParameterBlock (SANE_Byte *data, size_t data_len, size_t *length)
override;
DecodeStatus DecodeBasicParameterBlockResp (const SANE_Byte *data, size_t data_len,
BrotherBasicParamResponse &response) override;
DecodeStatus EncodeSourceStatusBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus DecodeSourceStatusBlockResp (const SANE_Byte *data, size_t data_len,
BrotherSourceStatusResponse &response) override;
DecodeStatus EncodeSourceSelectBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus DecodeSourceSelectBlockResp (const SANE_Byte *data, size_t data_len,
BrotherSourceStatusResponse &response) override;
DecodeStatus EncodeParameterBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus EncodeParameterBlockBlank (SANE_Byte *data, size_t data_len, size_t *length)
override;
DecodeStatus DecodeScanData (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) override;
DecodeStatus DecodeButtonQueryResp (const SANE_Byte *data, size_t data_len,
BrotherButtonQueryResponse &response) override;
DecodeStatus DecodeButtonStateResp (const SANE_Byte *data, size_t data_len,
BrotherButtonStateResponse &response) override;
private:
DecodeStatus DecodeScanDataHeader (const SANE_Byte *src_data, size_t src_data_len,
size_t *src_data_consumed, ScanDataHeader &header);
ScanDataHeader current_header;
BrotherGrayRawDecoder gray_raw_decoder;
BrotherGrayRLengthDecoder gray_decoder;
BrotherInterleavedRGBColourDecoder colour_decoder;
BrotherJFIFDecoder jfif_decoder;
};
class BrotherEncoderFamily4 : public BrotherEncoder
{
public:
explicit BrotherEncoderFamily4(SANE_Word capabilities)
{
(void)capabilities;
}
~BrotherEncoderFamily4 ()
{
}
void NewPage() override
{
current_header.block_type = 0;
jfif_decoder.NewPage(scan_params);
gray_decoder.NewPage (scan_params);
gray_raw_decoder.NewPage (scan_params);
}
DecodeStatus EncodeQueryBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus DecodeQueryBlockResp (const SANE_Byte *data, size_t data_len,
BrotherQueryResponse &response) override;
DecodeStatus DecodeSessionResp (const SANE_Byte *data, size_t data_len,
BrotherSessionResponse &response) override;
DecodeStatus EncodeBasicParameterBlock (SANE_Byte *data, size_t data_len, size_t *length)
override;
DecodeStatus DecodeBasicParameterBlockResp (const SANE_Byte *data, size_t data_len,
BrotherBasicParamResponse &response) override;
DecodeStatus EncodeSourceStatusBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus DecodeSourceStatusBlockResp (const SANE_Byte *data, size_t data_len,
BrotherSourceStatusResponse &response) override;
DecodeStatus EncodeSourceSelectBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus DecodeSourceSelectBlockResp (const SANE_Byte *data, size_t data_len,
BrotherSourceStatusResponse &response) override;
DecodeStatus EncodeParameterBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus EncodeParameterBlockBlank (SANE_Byte *data, size_t data_len, size_t *length)
override;
DecodeStatus DecodeScanData (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) override;
DecodeStatus DecodeButtonQueryResp (const SANE_Byte *data, size_t data_len,
BrotherButtonQueryResponse &response) override;
DecodeStatus DecodeButtonStateResp (const SANE_Byte *data, size_t data_len,
BrotherButtonStateResponse &response) override;
private:
DecodeStatus DecodeScanDataHeader (const SANE_Byte *src_data, size_t src_data_len,
size_t *src_data_consumed, ScanDataHeader &header);
ScanDataHeader current_header;
BrotherJFIFDecoder jfif_decoder;
BrotherGrayRLengthDecoder gray_decoder;
BrotherGrayRawDecoder gray_raw_decoder;
};
class BrotherEncoderFamily5 : public BrotherEncoder
{
public:
explicit BrotherEncoderFamily5(SANE_Word capabilities)
{
(void)capabilities;
}
~BrotherEncoderFamily5 ()
{
}
void NewPage() override
{
current_header.block_type = 0;
jfif_decoder.NewPage(scan_params);
gray_decoder.NewPage (scan_params);
}
const char* ScanModeToText (BrotherScanMode scan_mode) override;
DecodeStatus EncodeQueryBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus DecodeQueryBlockResp (const SANE_Byte *data, size_t data_len,
BrotherQueryResponse &response) override;
DecodeStatus DecodeSessionResp (const SANE_Byte *data, size_t data_len,
BrotherSessionResponse &response) override;
DecodeStatus EncodeBasicParameterBlock (SANE_Byte *data, size_t data_len, size_t *length)
override;
DecodeStatus DecodeBasicParameterBlockResp (const SANE_Byte *data, size_t data_len,
BrotherBasicParamResponse &response) override;
DecodeStatus EncodeSourceStatusBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus DecodeSourceStatusBlockResp (const SANE_Byte *data, size_t data_len,
BrotherSourceStatusResponse &response) override;
DecodeStatus EncodeSourceSelectBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus DecodeSourceSelectBlockResp (const SANE_Byte *data, size_t data_len,
BrotherSourceStatusResponse &response) override;
DecodeStatus EncodeParameterBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
DecodeStatus EncodeParameterBlockBlank (SANE_Byte *data, size_t data_len, size_t *length)
override;
DecodeStatus DecodeScanData (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) override;
DecodeStatus DecodeButtonQueryResp (const SANE_Byte *data, size_t data_len,
BrotherButtonQueryResponse &response) override;
DecodeStatus DecodeButtonStateResp (const SANE_Byte *data, size_t data_len,
BrotherButtonStateResponse &response) override;
private:
DecodeStatus DecodeScanDataHeader (const SANE_Byte *src_data, size_t src_data_len,
size_t *src_data_consumed, ScanDataHeader &header);
ScanDataHeader current_header;
BrotherJFIFDecoder jfif_decoder;
BrotherGrayRLengthDecoder gray_decoder;
};

Plik diff jest za duży Load Diff

Wyświetl plik

@ -0,0 +1,8 @@
# Options for the brother_mfp backend
# Brother MFC-J4320DW
usb 0x04f9 0x033a
# device list for non-linux-systems (enable if autodetect fails):
#/dev/scanner
#/dev/usb/scanner0

Wyświetl plik

@ -667,7 +667,7 @@ AC_ARG_ENABLE(local-backends,
[turn off compilation of all backends but net]))
ALL_BACKENDS="abaton agfafocus apple artec artec_eplus48u as6e \
avision bh canon canon630u canon_dr canon_lide70 canon_pp cardscan \
avision bh brother_mfp canon canon630u canon_dr canon_lide70 canon_pp cardscan \
coolscan coolscan2 coolscan3 dc25 dc210 dc240 \
dell1600n_net dmc epjitsu epson epson2 epsonds escl fujitsu \
genesys gphoto2 gt68xx hp hp3500 hp3900 hp4200 hp5400 \
@ -723,6 +723,9 @@ for backend in ${BACKENDS} ; do
BACKEND_LIBS_ENABLED="${BACKEND_LIBS_ENABLED} libsane-${backend}.la"
BACKEND_CONFS_ENABLED="${BACKEND_CONFS_ENABLED} ${backend}.conf"
BACKEND_MANS_ENABLED="${BACKEND_MANS_ENABLED} sane-${backend}.5"
if test x$backend = xbrother_mfp; then
with_brother_mfp_tests=yes
fi
if test x$backend = xgenesys; then
with_genesys_tests=yes
fi
@ -732,6 +735,7 @@ for backend in ${BACKENDS} ; do
done
AC_SUBST(BACKEND_LIBS_ENABLED)
AM_CONDITIONAL(WITH_GENESYS_TESTS, test xyes = x$with_genesys_tests)
AM_CONDITIONAL(WITH_BROTHER_MFP_TESTS, test xyes = x$with_brother_mfp_tests)
AM_CONDITIONAL(INSTALL_UMAX_PP_TOOLS, test xyes = x$install_umax_pp_tools)
AC_ARG_VAR(PRELOADABLE_BACKENDS, [list of backends to preload into single DLL])
@ -824,6 +828,7 @@ AC_CONFIG_FILES([Makefile lib/Makefile sanei/Makefile frontend/Makefile \
po/Makefile.in testsuite/Makefile \
testsuite/backend/Makefile \
testsuite/backend/genesys/Makefile \
testsuite/backend/brother_mfp/Makefile \
testsuite/sanei/Makefile testsuite/tools/Makefile \
tools/Makefile doc/doxygen-sanei.conf doc/doxygen-genesys.conf])
AC_CONFIG_FILES([tools/sane-config], [chmod a+x tools/sane-config])

Wyświetl plik

@ -38,7 +38,7 @@ BACKEND_5MANS = sane-abaton.5 sane-agfafocus.5 sane-apple.5 sane-as6e.5 \
sane-hp5590.5 sane-hpljm1005.5 sane-cardscan.5 sane-hp3900.5 \
sane-epjitsu.5 sane-hs2p.5 sane-canon_dr.5 sane-xerox_mfp.5 \
sane-rts8891.5 sane-coolscan3.5 sane-kvs1025.5 sane-kvs20xx.5 \
sane-kvs40xx.5 sane-p5.5 sane-magicolor.5 sane-lexmark_x2600.5
sane-kvs40xx.5 sane-p5.5 sane-magicolor.5 sane-lexmark_x2600.5 sane-brother_mfp.5
EXTRA_DIST += sane-abaton.man sane-agfafocus.man sane-apple.man sane-as6e.man \
sane-canon_lide70.man \
@ -64,7 +64,7 @@ EXTRA_DIST += sane-abaton.man sane-agfafocus.man sane-apple.man sane-as6e.man \
sane-cardscan.man sane-hp3900.man sane-epjitsu.man sane-hs2p.man \
sane-canon_dr.man sane-xerox_mfp.man sane-rts8891.man \
sane-coolscan3.man sane-kvs1025.man sane-kvs20xx.man sane-kvs40xx.man \
sane-p5.man sane-magicolor.man sane-lexmark_x2600.man
sane-p5.man sane-magicolor.man sane-lexmark_x2600.man sane-brother_mfp.man
man7_MANS = sane.7
EXTRA_DIST += sane.man

Wyświetl plik

@ -0,0 +1,201 @@
.TH "sane\-brother_mfp" "5" "20 Nov 2022" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy"
.IX sane\-brother_mfp
.SH "NAME"
sane\-brother_mfp \- SANE backend for Brother Multifunction Printer/Scanners
.SH "DESCRIPTION"
The
.B sane\-brother_mfp
library implements a SANE (Scanner Access Now Easy) backend that provides
access to a number of machines by Brother. These are mainly in the DCP and MFC line of multi\-function
devices, but also include some dedicated scanners in the AD range.
.PP
The backend should be considered Beta quality at the moment as work is ongoing to decode tha various
options supported by the machine range.
.SH OPTIONS
.TP
.BR \-\-source
Selects the scanning method for the device. Valid options vary depending on the capabilities
of the device. Options available are:
.IR Flatbed ", " "Automatic Document Feeder" ", " "Automatic Document Feeder (one sided)"
and
.IR "Automatic Document Feeder (duplex)" .
.I Flatbed
is the default.
.TP
.BR \-\-mode
Selects the type of scan to perform. Most devices support:
.IR "Color" ", " "Gray" ", " "Gray (dithered)"
and
.IR "Lineart" .
.I Color
is the default.
.TP
.BR \-\-split\-resolution
When selected, activates the
.B \-\-x\-resolution
and
.B \-\-y\-resolution
options. This allows different resolutions to be selected for the horizontal and vertical.
When not selected, then the
.B \-\-resolution
option is activated and the same resolution is selected for both.
.TP
.BR \-\-resolution
Selects the resolution for both the horizontal and vertical directions of the scan.
The parameter is selected from a list of valid values for the device.
.TP
.BR \-\-x\-resolution ", " \-\-y\-resolution
Selects the individual resolutions for the horizontal and vertical directions of the scan.
The parameter is selected from a list of valid values for the device for each direction, and different
ranges for each option may be available.
.TP
.BR \-\-preview
Selects a resolution for both the x and y direction suitable for previewing.
The option will select the lowest available resolutions.
.TP
.BR \-\-tl\-x ", " \-\-tl\-y ", " \-\-br\-x ", " \-\-br\-y
Selects the area to scan. All measurements are in mm and are relative to the top, left hand corner
of the scan area. The
.B \-\-tl\-x
and
.B \-\-tl\-y
options specify the coordinates of the top left corner. The
.B \-\-br\-x
and
.B \-\-br\-y
options specify the coordinates of the bottom right corner.
The default values cover the maximum scan area for the selected source.
.TP
.BR \-\-brightness
Alters the brightness of the generated image when
.I Gray (dithered)
or
.I Lineart
modes are selected. Valid values are between \-50 and +50.
Default value is 0.
.TP
.BR \-\-contrast
Alters the contrast of the generated image when
.I Gray (dithered)
or
.I Lineart
modes are selected. Valid values are between \-50 and +50.
Default value is 0.
.TP
.BR \-\-compression
For devices that offer both raw and compressed (JPEG) scan image delivery, this option
is used to select which option is desired. If set, then JPEG will be used.
If not set, then whatever raw, uncompressed option is used.
The effect of unsetting this option will be to increase the volume of data sent from the
scanner with a small increase in image quality.
Default for this option is for it to be set.
.TP
.BR \-\-email-sensor ", " \-\-file-sensor ", " \-\-image-sensor ", " \-\-ocr-sensor
These options provide a readonly capability to sense when a soft scan button is selected on the
device. Typically, this would be a menu option intended to elicit a specific scanning operation
by the backend. To use this function, a monitoring daemon would be required such as
.BR scanbd (8).
If the corresponding operation has been requested on the device, querying this option would
show that it is set. Once the sensor has been queried, it will be reset on the device so that
the event can only be sensed once.
.SH "CONFIGURATION"
The contents of the
.I brother_mfp.conf
file contains information related to supported devices. Also there is a section to indicate
the location of network devices than cannot be auto detected.
.SH "FILES"
.TP
.I @CONFIGDIR@/brother_mfp.conf
The backend configuration file (see also description of
.B SANE_CONFIG_DIR
below).
.TP
.I @LIBDIR@/libsane\-brother_mfp.a
The static library implementing this backend.
.TP
.I @LIBDIR@/libsane\-brother_mfp.so
The shared library implementing this backend (present on systems that
support dynamic loading).
.SH "ENVIRONMENT"
.TP
.B SANE_CONFIG_DIR
This environment variable specifies the list of directories that may
contain the configuration file. On *NIX systems, the directories are
separated by a colon (`:'), under OS/2, they are separated by a
semi-colon (`;'). If this variable is not set, the configuration file
is searched in two default directories: first, the current working
directory (".") and then in
.IR @CONFIGDIR@ .
If the value of the environment variable ends with the directory separator
character, then the default directories are searched after the explicitly
specified directories. For example, setting
.B SANE_CONFIG_DIR
to "/tmp/config:" would result in directories
.IR tmp/config ,
.IR . ,
and
.I "@CONFIGDIR@"
being searched (in this order).
.TP
.B SANE_DEBUG_BROTHER_MFP
If the library was compiled with debug support enabled, this environment
variable controls the debug level for this backend. Valid values are 1 (IMPORTANT),
2 (SERIOUS), 3 (WARNINGS), 4 (EVENT), 5 (DETAIL) and 6 (DEBUG). Selecting 5 or 6 will generate
a large amount of output.
.SH CREDITS
.TP
.I David R Roberts, GitHub user “davidar”
For his analysis of another Brother machine, which has led to me
getting a good head start on this process and for inspiring me to write this backend.
.TP
.I Various GitLab users
For their help in checking out models of devices that I do not have access to and patiently
making PCAP captures and diag log output for me to look at!
.SH "SEE ALSO"
.BR sane (7),
.BR sane\-usb (5),
.BR scanbd (8)
.br
.SH "AUTHOR"
Ralph Little
.RI < skelband@gmail.com >
.SH "BUGS"
There are no specific bugs to report for this backend.

Wyświetl plik

@ -5,5 +5,11 @@
## included LICENSE file for license information.
if WITH_GENESYS_TESTS
SUBDIRS = genesys
SUBDIR_GENESYS = genesys
endif
if WITH_BROTHER_MFP_TESTS
SUBDIR_BROTHER_MFP = brother_mfp
endif
SUBDIRS = $(SUBDIR_GENESYS) $(SUBDIR_BROTHER_MFP)

Wyświetl plik

@ -0,0 +1,33 @@
## Makefile.am -- an automake template for Makefile.in file
## Copyright (C) 2022 Ralph Little <skelband@gmail.com>
##
## This file is part of the "Sane" build infra-structure. See
## included LICENSE file for license information.
TEST_LDADD = \
../../../sanei/libsanei.la \
../../../sanei/sanei_usb.lo \
../../../sanei/sanei_magic.lo \
../../../lib/liblib.la \
../../../backend/libbrother_mfp.la \
../../../backend/sane_strstatus.lo \
$(USB_LIBS) $(XML_LIBS) $(PTHREAD_LIBS) $(JPEG_LIBS)
check_PROGRAMS = brother_mfp_unit_tests
TESTS = brother_mfp_unit_tests
AM_CPPFLAGS += -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include $(USB_CFLAGS) \
-DBACKEND_NAME=brother_mfp -DTESTSUITE_BACKEND_BROTHER_MFP_SRCDIR=$(srcdir)
brother_mfp_unit_tests_SOURCES = \
brother_mfp_tests.cpp \
brother_mfp_tests.h \
brother_mfp_tests_family4.cpp \
brother_mfp_tests_family3.cpp \
brother_mfp_tests_family2.cpp \
brother_mfp_tests_gray_rlength.cpp \
brother_mfp_tests_colour_int_rgb.cpp \
brother_mfp_tests_colour_jpeg.cpp \
../genesys/minigtest.cpp
brother_mfp_unit_tests_LDADD = $(TEST_LDADD)

Wyświetl plik

@ -0,0 +1,39 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
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, see <https://www.gnu.org/licenses/>.
*/
#define DEBUG_NOT_STATIC
#include "../../include/sane/sanei_debug.h"
#include "../genesys/minigtest.h"
#include "brother_mfp_tests.h"
int main ()
{
DBG_INIT ();
test_family4 ();
test_family3 ();
test_family2 ();
test_gray_rlength ();
test_colour_int_rgb();
test_colour_jpeg();
return finish_tests ();
}

Wyświetl plik

@ -0,0 +1,28 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
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, see <https://www.gnu.org/licenses/>.
*/
#pragma once
extern void test_family4();
extern void test_family3();
extern void test_family2();
extern void test_gray_rlength();
extern void test_colour_int_rgb();
extern void test_colour_jpeg();

Wyświetl plik

@ -0,0 +1,170 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
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, see <https://www.gnu.org/licenses/>.
*/
#define DEBUG_DECLARE_ONLY
#include "../../include/sane/sanei_debug.h"
#include "brother_mfp_tests.h"
#include "../../backend/brother_mfp/brother_mfp-encoder.h"
#include "../genesys/minigtest.h"
/*
* TEST: BrotherInterleavedRGBColourDecoder()
*
*/
static void test_colour_int_rgb_enc_rgb()
{
BrotherInterleavedRGBColourDecoder decoder(CAP_MODE_COLOUR);
const SANE_Byte *src_data;
SANE_Byte dest_data[1024];
DecodeStatus res_code;
size_t src_data_consumed;
size_t dest_data_written;
/*
* Normal decode.
*
*/
BrotherParameters params;
params.param_pixel_x_width = 6;
decoder.NewPage(params);
src_data = (const SANE_Byte *)"123456";
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)6);
ASSERT_EQ(dest_data_written, (size_t)0);
decoder.NewBlock();
src_data = (const SANE_Byte *)"ABCDEF";
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)6);
ASSERT_EQ(dest_data_written, (size_t)0);
decoder.NewBlock();
src_data = (const SANE_Byte *)"MNOPQR";
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)6);
ASSERT_EQ(dest_data_written, (size_t)18);
ASSERT_EQ(memcmp(dest_data, "1AM2BN3CO4DP5EQ6FR", 18), 0);
}
static void test_colour_int_rgb_enc_ycrcb()
{
BrotherInterleavedRGBColourDecoder decoder(CAP_MODE_COLOUR | CAP_ENCODING_RAW_IS_CrYCb);
const SANE_Byte *src_data;
SANE_Byte dest_data[1024];
DecodeStatus res_code;
size_t src_data_consumed;
size_t dest_data_written;
/*
* Normal decode.
*
*/
BrotherParameters params;
params.param_pixel_x_width = 7;
decoder.NewPage(params);
decoder.NewBlock();
src_data = (const SANE_Byte *)"\x5B" "\x63" "\x60" "\x5D" "\x5D" "\x62" "\x6A";
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)7);
ASSERT_EQ(dest_data_written, (size_t)0);
decoder.NewBlock();
src_data = (const SANE_Byte *)"\xDD" "\xE9" "\xEE" "\xEF" "\xED" "\xED" "\xEB";
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)7);
ASSERT_EQ(dest_data_written, (size_t)0);
decoder.NewBlock();
src_data = (const SANE_Byte *)"\x70" "\x6E" "\x6D" "\x6E" "\x6E" "\x6E" "\x6D";
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)7);
ASSERT_EQ(dest_data_written, (size_t)21);
// YCbCr data converted to RGB:
ASSERT_EQ(memcmp (dest_data,
"\xa9" "\xfc" "\xc0" "\xc0" "\xff" "\xc9" "\xc1" "\xff"
"\xcc" "\xbd" "\xff" "\xcf" "\xbb" "\xff" "\xcd" "\xc2"
"\xff" "\xcd" "\xcc" "\xff" "\xc9",
21),
0);
}
/*
* Run all the tests.
*
*/
void test_colour_int_rgb()
{
test_colour_int_rgb_enc_rgb();
test_colour_int_rgb_enc_ycrcb();
}

Wyświetl plik

@ -0,0 +1,225 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
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, see <https://www.gnu.org/licenses/>.
*/
#define DEBUG_DECLARE_ONLY
#include "../../include/sane/sanei_debug.h"
#include "brother_mfp_tests.h"
#include "../../backend/brother_mfp/brother_mfp-encoder.h"
#include "../genesys/minigtest.h"
/*
* TEST: BrotherJFIFDecoder()
*
*/
static void test_colour_jpeg_normal()
{
BrotherJFIFDecoder decoder;
const SANE_Byte *src_data;
SANE_Byte dest_data[1024];
DecodeStatus res_code;
size_t src_data_consumed;
size_t dest_data_written;
/*
* Normal decode.
*
* Test image is 7x14 pixels.
*
*/
#define HEX2DEC(ptr) ((ptr) >= 'a' && (ptr) <= 'f'? (ptr) - 'a' + 10: (ptr) - '0')
src_data = (const SANE_Byte *)"ffd8ffdb008400"
"010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010"
"101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101"
"010101010101010101010101010101010101010101010101ffdd00040000ffc0001108000e000703012200021101031101ffc401a"
"20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d010203000411"
"05122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434"
"445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6"
"a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa010"
"0030101010101010101010000000000000102030405060708090a0b11000201020404030407050404000102770001020311040521"
"31061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a43444"
"5464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6"
"a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000"
"c03010002110311003f00fe929ffe0a19adf807c37f1447ed15fb3378c7e0bfc56f87be1ed37c5ba47c32b3f17e8fe3b87c79e1ed"
"5af534bb2bad03c5ba3585ae991bc7a93ada5f5bcd687ece4131b4ce92431f51a7fede07c33e1cf8a371f1f7e0c6bff03fc77f0d3"
"40d13c5f1f810f88f4ff18cde2ef0b78866b6b5d36f741d4f4fb0b088ddc77575059ea1a6cd6919b59dc0599d63b816ff006df8bf"
"f626fd9abc7fac6b9e20f1b780f57f14eb5e23d134ef0e6a9a8eb7f137e2bdfdc1d0f4abd4d4ac74dd38cfe3765d12de2bf8d6e9f"
"f00b1174e7b89b2d72d36e605fe32fd8aff0066cf887ab6b7aef8e3c03a9f8a356f10f87f4af0b6ab7bac7c47f8a375249a068b73"
"05e69ba6da2b78d045a5c30dddbc57323e951d94d7738796f249de594bfec74f8a3c2ea90a0b11c279861e55ead0a9984b2f963ff"
"d8e341e50a587c8e38de2dc4c6385c5468e70b131cd96618b8bc5615e0f1b8454d470ff009d4b23e35929a867186a0aa29b8db170"
"aff535cb5a30a54e3538720b1897b5a32856adec151960e8a9e1b150ad8a856fffd9";
SANE_Byte *in_buffer = new SANE_Byte[strlen((const char *)src_data) / 2];
SANE_Byte *in_ptr = in_buffer;
for (const SANE_Byte *src_ptr = src_data; *src_ptr; src_ptr += 2)
{
*in_ptr++ = HEX2DEC(src_ptr[0]) << 4 | HEX2DEC(src_ptr[1]);
}
/*
* Test JPEG image has expected width.
*
*/
BrotherParameters params;
params.param_pixel_x_width = 7;
decoder.NewPage(params);
decoder.NewBlock();
res_code = decoder.DecodeScanData (in_buffer,
in_ptr - in_buffer,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)(in_ptr - in_buffer));
ASSERT_EQ(dest_data_written, (size_t)294);
const SANE_Byte expected_dest[] = "\xe4" "\xf0" "\xf0" "\xe8" "\xf2" "\xf4" "\xea" "\xed" "\xf6" "\xeb"
"\xee" "\xf7" "\xeb" "\xee" "\xf7" "\xeb" "\xf0" "\xf6" "\xed" "\xf0" "\xf9" "\xea" "\xf5" "\xf7"
"\xe7" "\xf2" "\xf6" "\xea" "\xee" "\xf7" "\xea" "\xee" "\xf7" "\xe9" "\xf0" "\xf8" "\xe8" "\xef"
"\xf5" "\xea" "\xee" "\xf7" "\xe9" "\xf6" "\xfc" "\xe9" "\xf6" "\xfc" "\xea" "\xf5" "\xfb" "\xea"
"\xf5" "\xfb" "\xe9" "\xf4" "\xfa" "\xe8" "\xf3" "\xf9" "\xe9" "\xf2" "\xf7" "\xdc" "\xe9" "\xff"
"\xd9" "\xe8" "\xff" "\xdd" "\xee" "\xff" "\xe0" "\xf5" "\xff" "\xe3" "\xf7" "\xff" "\xe3" "\xf7"
"\xff" "\xe3" "\xf6" "\xff" "\x4e" "\x59" "\xa9" "\x4c" "\x5e" "\xaa" "\x61" "\x7e" "\xc4" "\xa9"
"\xca" "\xfd" "\xd8" "\xf8" "\xff" "\xd5" "\xf9" "\xff" "\xb2" "\xda" "\xfe" "\x55" "\x5e" "\xad"
"\x4e" "\x5e" "\xac" "\x3f" "\x5c" "\xa8" "\x5b" "\x7f" "\xbd" "\xb2" "\xd6" "\xf6" "\xc2" "\xe9"
"\xff" "\x76" "\xa1" "\xd6" "\xd8" "\xdf" "\xf9" "\xce" "\xdb" "\xfd" "\x90" "\xa4" "\xd6" "\x5e"
"\x77" "\xad" "\x7b" "\x9a" "\xc6" "\xa3" "\xc3" "\xf2" "\x69" "\x88" "\xc8" "\xe9" "\xfe" "\xff"
"\xe7" "\xfd" "\xff" "\xcf" "\xe5" "\xff" "\x76" "\x8e" "\xbe" "\x73" "\x8d" "\xbe" "\x9e" "\xb9"
"\xf0" "\x74" "\x8c" "\xd2" "\xd1" "\xff" "\xff" "\xd5" "\xff" "\xff" "\xbd" "\xe3" "\xff" "\x6c"
"\x8d" "\xba" "\x72" "\x8d" "\xba" "\xa0" "\xb9" "\xef" "\x76" "\x8e" "\xd4" "\xa2" "\xcf" "\xd4"
"\x9c" "\xc6" "\xd2" "\x70" "\x92" "\xad" "\x5a" "\x74" "\x95" "\x8d" "\xa1" "\xc2" "\xb4" "\xc6"
"\xec" "\x80" "\x91" "\xc5" "\xf3" "\xff" "\xff" "\xf5" "\xff" "\xff" "\xf7" "\xff" "\xff" "\xf8"
"\xff" "\xff" "\xf9" "\xff" "\xff" "\xf9" "\xff" "\xff" "\xf9" "\xfe" "\xff" "\xff" "\xff" "\xff"
"\xff" "\xff" "\xff" "\xff" "\xff" "\xfd" "\xff" "\xff" "\xfd" "\xff" "\xff" "\xff" "\xff" "\xff"
"\xff" "\xff" "\xff" "\xff" "\xfe" "\xfe" "\xfc" "\xff" "\xff" "\xfd" "\xff" "\xff" "\xff" "\xff"
"\xff" "\xff" "\xfe" "\xfe" "\xfe" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xfd"
"\xff" "\xff" "\xfd" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff"
"\xff" "\xff" "\xff" "\xff" ;
ASSERT_EQ(memcmp(dest_data, expected_dest, 294), 0);
// /*
// * Test JPEG image has extra width: expecting 5.
// *
// */
// params.param_pixel_x_width = 5;
//
// decoder.NewPage(params);
// decoder.NewBlock();
//
// res_code = decoder.DecodeScanData (in_buffer,
// in_ptr - in_buffer,
// &src_data_consumed,
// dest_data,
// sizeof(dest_data),
// &dest_data_written);
//
// ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
// ASSERT_EQ(src_data_consumed, (size_t)(in_ptr - in_buffer));
// ASSERT_EQ(dest_data_written, (size_t)210);
//
// const SANE_Byte expected_dest2[] = "\xe4" "\xf0" "\xf0" "\xe8" "\xf2" "\xf4" "\xea" "\xed" "\xf6" "\xeb"
// "\xee" "\xf7" "\xeb" "\xee" "\xf7" "\xea" "\xf5" "\xf7" "\xe7" "\xf2" "\xf6" "\xea" "\xee" "\xf7"
// "\xea" "\xee" "\xf7" "\xe9" "\xf0" "\xf8" "\xe9" "\xf6" "\xfc" "\xe9" "\xf6" "\xfc" "\xea" "\xf5"
// "\xfb" "\xea" "\xf5" "\xfb" "\xe9" "\xf4" "\xfa" "\xdc" "\xe9" "\xff" "\xd9" "\xe8" "\xff" "\xdd"
// "\xee" "\xff" "\xe0" "\xf5" "\xff" "\xe3" "\xf7" "\xff" "\x4e" "\x59" "\xa9" "\x4c" "\x5e" "\xaa"
// "\x61" "\x7e" "\xc4" "\xa9" "\xca" "\xfd" "\xd8" "\xf8" "\xff" "\x55" "\x5e" "\xad" "\x4e" "\x5e"
// "\xac" "\x3f" "\x5c" "\xa8" "\x5b" "\x7f" "\xbd" "\xb2" "\xd6" "\xf6" "\xd8" "\xdf" "\xf9" "\xce"
// "\xdb" "\xfd" "\x90" "\xa4" "\xd6" "\x5e" "\x77" "\xad" "\x7b" "\x9a" "\xc6" "\xe9" "\xfe" "\xff"
// "\xe7" "\xfd" "\xff" "\xcf" "\xe5" "\xff" "\x76" "\x8e" "\xbe" "\x73" "\x8d" "\xbe" "\xd1" "\xff"
// "\xff" "\xd5" "\xff" "\xff" "\xbd" "\xe3" "\xff" "\x6c" "\x8d" "\xba" "\x72" "\x8d" "\xba" "\xa2"
// "\xcf" "\xd4" "\x9c" "\xc6" "\xd2" "\x70" "\x92" "\xad" "\x5a" "\x74" "\x95" "\x8d" "\xa1" "\xc2"
// "\xf3" "\xff" "\xff" "\xf5" "\xff" "\xff" "\xf7" "\xff" "\xff" "\xf8" "\xff" "\xff" "\xf9" "\xff"
// "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xfd" "\xff" "\xff" "\xfd" "\xff"
// "\xff" "\xff" "\xfe" "\xfe" "\xfc" "\xff" "\xff" "\xfd" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff"
// "\xfe" "\xfe" "\xfe" "\xff" "\xff" "\xfd" "\xff" "\xff" "\xfd" "\xff" "\xff" "\xff" "\xff" "\xff"
// "\xff" "\xff" "\xff" "\xff";
//
// ASSERT_EQ(memcmp(dest_data, expected_dest2, 210), 0);
//
// /*
// * Test JPEG image has short width: expecting 9.
// * Output will be padded with black.
// *
// */
// params.param_pixel_x_width = 9;
//
// decoder.NewPage(params);
// decoder.NewBlock();
//
// res_code = decoder.DecodeScanData (in_buffer,
// in_ptr - in_buffer,
// &src_data_consumed,
// dest_data,
// sizeof(dest_data),
// &dest_data_written);
//
// ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
// ASSERT_EQ(src_data_consumed, (size_t)(in_ptr - in_buffer));
// ASSERT_EQ(dest_data_written, (size_t)378);
//
// const SANE_Byte expected_dest3[] = "\xe4" "\xf0" "\xf0" "\xe8" "\xf2" "\xf4" "\xea" "\xed" "\xf6" "\xeb"
// "\xee" "\xf7" "\xeb" "\xee" "\xf7" "\xeb" "\xf0" "\xf6" "\xed" "\xf0" "\xf9" "\x00" "\x00" "\x00"
// "\x00" "\x00" "\x00" "\xea" "\xf5" "\xf7" "\xe7" "\xf2" "\xf6" "\xea" "\xee" "\xf7" "\xea" "\xee"
// "\xf7" "\xe9" "\xf0" "\xf8" "\xe8" "\xef" "\xf5" "\xea" "\xee" "\xf7" "\x00" "\x00" "\x00" "\x00"
// "\x00" "\x00" "\xe9" "\xf6" "\xfc" "\xe9" "\xf6" "\xfc" "\xea" "\xf5" "\xfb" "\xea" "\xf5" "\xfb"
// "\xe9" "\xf4" "\xfa" "\xe8" "\xf3" "\xf9" "\xe9" "\xf2" "\xf7" "\x00" "\x00" "\x00" "\x00" "\x00"
// "\x00" "\xdc" "\xe9" "\xff" "\xd9" "\xe8" "\xff" "\xdd" "\xee" "\xff" "\xe0" "\xf5" "\xff" "\xe3"
// "\xf7" "\xff" "\xe3" "\xf7" "\xff" "\xe3" "\xf6" "\xff" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00"
// "\x4e" "\x59" "\xa9" "\x4c" "\x5e" "\xaa" "\x61" "\x7e" "\xc4" "\xa9" "\xca" "\xfd" "\xd8" "\xf8"
// "\xff" "\xd5" "\xf9" "\xff" "\xb2" "\xda" "\xfe" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x55"
// "\x5e" "\xad" "\x4e" "\x5e" "\xac" "\x3f" "\x5c" "\xa8" "\x5b" "\x7f" "\xbd" "\xb2" "\xd6" "\xf6"
// "\xc2" "\xe9" "\xff" "\x76" "\xa1" "\xd6" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\xd8" "\xdf"
// "\xf9" "\xce" "\xdb" "\xfd" "\x90" "\xa4" "\xd6" "\x5e" "\x77" "\xad" "\x7b" "\x9a" "\xc6" "\xa3"
// "\xc3" "\xf2" "\x69" "\x88" "\xc8" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\xe9" "\xfe" "\xff"
// "\xe7" "\xfd" "\xff" "\xcf" "\xe5" "\xff" "\x76" "\x8e" "\xbe" "\x73" "\x8d" "\xbe" "\x9e" "\xb9"
// "\xf0" "\x74" "\x8c" "\xd2" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\xd1" "\xff" "\xff" "\xd5"
// "\xff" "\xff" "\xbd" "\xe3" "\xff" "\x6c" "\x8d" "\xba" "\x72" "\x8d" "\xba" "\xa0" "\xb9" "\xef"
// "\x76" "\x8e" "\xd4" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\xa2" "\xcf" "\xd4" "\x9c" "\xc6"
// "\xd2" "\x70" "\x92" "\xad" "\x5a" "\x74" "\x95" "\x8d" "\xa1" "\xc2" "\xb4" "\xc6" "\xec" "\x80"
// "\x91" "\xc5" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\xf3" "\xff" "\xff" "\xf5" "\xff" "\xff"
// "\xf7" "\xff" "\xff" "\xf8" "\xff" "\xff" "\xf9" "\xff" "\xff" "\xf9" "\xff" "\xff" "\xf9" "\xfe"
// "\xff" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff"
// "\xff" "\xfd" "\xff" "\xff" "\xfd" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff"
// "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\xfe" "\xfe" "\xfc" "\xff" "\xff" "\xfd" "\xff" "\xff"
// "\xff" "\xff" "\xff" "\xff" "\xfe" "\xfe" "\xfe" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\x00"
// "\x00" "\x00" "\x00" "\x00" "\x00" "\xff" "\xff" "\xfd" "\xff" "\xff" "\xfd" "\xff" "\xff" "\xff"
// "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\xff" "\x00" "\x00"
// "\x00" "\x00" "\x00" "\x00";
//
// ASSERT_EQ(memcmp(dest_data, expected_dest3, 378), 0);
}
/*
* Run all the tests.
*
*/
void test_colour_jpeg()
{
test_colour_jpeg_normal();
}

Wyświetl plik

@ -0,0 +1,414 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
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, see <https://www.gnu.org/licenses/>.
*/
#define DEBUG_DECLARE_ONLY
#include "brother_mfp_tests.h"
#include "../../backend/brother_mfp/brother_mfp-encoder.h"
#include "../genesys/minigtest.h"
/*
* TEST: DecodeSessionResp()
*
*/
static void test_family2_decode_session_resp()
{
DecodeStatus decode_resp;
BrotherSessionResponse sess_resp;
BrotherEncoderFamily2 encoder(0);
// SUCCESS status
const SANE_Byte *data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x00";
decode_resp = encoder.DecodeSessionResp (data, 5, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
ASSERT_TRUE(sess_resp.ready);
// BUSY status
data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x80";
decode_resp = encoder.DecodeSessionResp (data, 5, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
ASSERT_FALSE(sess_resp.ready);
// Length issues
data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x20" "\x32";
decode_resp = encoder.DecodeSessionResp (data, 6, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
decode_resp = encoder.DecodeSessionResp (data, 4, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
decode_resp = encoder.DecodeSessionResp (data, 0, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
// Content problems.
const char *content_problems[] = {
"\x32" "\x10" "\x01" "\x02" "\x20",
"\x06" "\x32" "\x01" "\x02" "\x20",
"\x06" "\x10" "\x32" "\x02" "\x20",
"\x06" "\x10" "\x01" "\x32" "\x20",
"\x06" "\x10" "\x01" "\x02" "\x32",
nullptr
};
for (size_t test = 0; test; test++)
{
decode_resp = encoder.DecodeSessionResp ((const SANE_Byte *)content_problems[test], 5, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
}
}
/*
* TEST: DecodeBasicParameterBlockResp()
*
*/
static void test_family2_decode_basic_param_resp()
{
/*
* TODO: Nothing to do here yet.
* We don't decode anything from this block.
* Watch this space.
*
*/
}
/*
* TEST: DecodeADFBlockResp()
*
*/
static void test_family2_decode_source_status_resp()
{
DecodeStatus decode_resp;
BrotherSourceStatusResponse adf_resp;
BrotherEncoderFamily2 encoder(0);
// SUCCESS status
const SANE_Byte *data = (const SANE_Byte *)"\xc2";
decode_resp = encoder.DecodeSourceStatusBlockResp (data, 1, adf_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
ASSERT_FALSE(adf_resp.source_ready);
data = (const SANE_Byte *)"\x80";
decode_resp = encoder.DecodeSourceStatusBlockResp (data, 1, adf_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
ASSERT_TRUE(adf_resp.source_ready);
// Wrong length.
decode_resp = encoder.DecodeSourceStatusBlockResp (data, 0, adf_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
decode_resp = encoder.DecodeSourceStatusBlockResp (data, 20, adf_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
}
/*
* TEST: EncodeBasicParameterBlock()
*
*/
static void test_family2_encode_basic_param()
{
DecodeStatus decode_resp;
SANE_Byte data_buffer[1024];
size_t ret_length;
BrotherEncoderFamily2 encoder(0);
// All defaults.
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=100,100\nM=CGRAY\n" "\x80";
ASSERT_EQ(ret_length, (size_t)22);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Different resolutions.
encoder.SetRes(200,300);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=CGRAY\n" "\x80";
ASSERT_EQ(ret_length, (size_t)22);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// GRAY mode.
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=GRAY64\n" "\x80";
ASSERT_EQ(ret_length, (size_t )23);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// COLOR mode
encoder.SetScanMode (BROTHER_SCAN_MODE_COLOR);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=CGRAY\n" "\x80";
ASSERT_EQ(ret_length, (size_t )22);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// GRAY DITHERED mode
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY_DITHERED);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=ERRDIF\n" "\x80";
ASSERT_EQ(ret_length, (size_t )23);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// TEXT mode
encoder.SetScanMode (BROTHER_SCAN_MODE_TEXT);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=TEXT\n" "\x80";
ASSERT_EQ(ret_length, (size_t )21);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Buffer too short.
encoder.SetScanMode (BROTHER_SCAN_MODE_TEXT);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, 15, &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_INVAL);
}
/*
* TEST: EncodeParameterBlock()
*
*/
static void test_family2_encode_param()
{
DecodeStatus decode_resp;
SANE_Byte data_buffer[1024];
size_t ret_length;
BrotherEncoderFamily2 encoder(0);
// All defaults.
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=100,100\nM=CGRAY\nC=JPEG\nJ=MID\n"
"B=50\nN=50\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Different resolutions.
encoder.SetRes(200,300);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=CGRAY\nC=JPEG\nJ=MID\n"
"B=50\nN=50\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Different modes:
// GRAY mode.
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=GRAY64\nC=RLENGTH\n"
"B=50\nN=50\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// COLOR mode
encoder.SetScanMode (BROTHER_SCAN_MODE_COLOR);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=CGRAY\nC=JPEG\nJ=MID\n"
"B=50\nN=50\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// GRAY DITHERED mode
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY_DITHERED);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=ERRDIF\nC=RLENGTH\n"
"B=50\nN=50\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// TEXT mode
encoder.SetScanMode (BROTHER_SCAN_MODE_TEXT);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=50\nN=50\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Different brightness and contrast, positive and negative.
encoder.SetBrightness (-20);
encoder.SetContrast (-30);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=30\nN=20\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
encoder.SetBrightness (50);
encoder.SetContrast (40);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=100\nN=90\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Different dimensions
encoder.SetScanDimensions (0, 10, 50, 100);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=100\nN=90\nA=0,50,10,150\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// ADF duplex
encoder.SetScanDimensions (0, 10, 50, 100);
encoder.SetSource (BROTHER_SOURCE_ADF_DUPLEX);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=100\nN=90\nA=0,50,10,150\nD=DUP\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Buffer too short.
decode_resp = encoder.EncodeParameterBlock (data_buffer, 15, &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_INVAL);
}
/*
* TEST: EncodeADFBlock()
*
*/
static void test_family2_encode_source_status()
{
DecodeStatus decode_resp;
SANE_Byte data_buffer[1024];
size_t ret_length;
BrotherEncoderFamily2 encoder(0);
// Standard call.
decode_resp = encoder.EncodeSourceStatusBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "D\nADF\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Buffer too short.
decode_resp = encoder.EncodeSourceStatusBlock (data_buffer, 5, &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_INVAL);
}
/*
* Run all the tests.
*
*/
void test_family2 ()
{
// Decodes.
test_family2_decode_session_resp();
test_family2_decode_basic_param_resp();
test_family2_decode_source_status_resp();
// Encodes.
test_family2_encode_basic_param();
test_family2_encode_param();
test_family2_encode_source_status();
}

Wyświetl plik

@ -0,0 +1,414 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
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, see <https://www.gnu.org/licenses/>.
*/
#define DEBUG_DECLARE_ONLY
#include "brother_mfp_tests.h"
#include "../../backend/brother_mfp/brother_mfp-encoder.h"
#include "../genesys/minigtest.h"
/*
* TEST: DecodeSessionResp()
*
*/
static void test_family3_decode_session_resp()
{
DecodeStatus decode_resp;
BrotherSessionResponse sess_resp;
BrotherEncoderFamily3 encoder(0);
// SUCCESS status
const SANE_Byte *data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x00";
decode_resp = encoder.DecodeSessionResp (data, 5, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
ASSERT_TRUE(sess_resp.ready);
// BUSY status
data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x80";
decode_resp = encoder.DecodeSessionResp (data, 5, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
ASSERT_FALSE(sess_resp.ready);
// Length issues
data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x20" "\x32";
decode_resp = encoder.DecodeSessionResp (data, 6, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
decode_resp = encoder.DecodeSessionResp (data, 4, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
decode_resp = encoder.DecodeSessionResp (data, 0, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
// Content problems.
const char *content_problems[] = {
"\x32" "\x10" "\x01" "\x02" "\x20",
"\x06" "\x32" "\x01" "\x02" "\x20",
"\x06" "\x10" "\x32" "\x02" "\x20",
"\x06" "\x10" "\x01" "\x32" "\x20",
"\x06" "\x10" "\x01" "\x02" "\x32",
nullptr
};
for (size_t test = 0; test; test++)
{
decode_resp = encoder.DecodeSessionResp ((const SANE_Byte *)content_problems[test], 5, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
}
}
/*
* TEST: DecodeBasicParameterBlockResp()
*
*/
static void test_family3_decode_basic_param_resp()
{
/*
* TODO: Nothing to do here yet.
* We don't decode anything from this block.
* Watch this space.
*
*/
}
/*
* TEST: DecodeADFBlockResp()
*
*/
static void test_family3_decode_sourcestatus_resp()
{
DecodeStatus decode_resp;
BrotherSourceStatusResponse adf_resp;
BrotherEncoderFamily3 encoder(0);
// SUCCESS status
const SANE_Byte *data = (const SANE_Byte *)"\xc2";
decode_resp = encoder.DecodeSourceStatusBlockResp (data, 1, adf_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
ASSERT_FALSE(adf_resp.source_ready);
data = (const SANE_Byte *)"\x80";
decode_resp = encoder.DecodeSourceStatusBlockResp (data, 1, adf_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
ASSERT_TRUE(adf_resp.source_ready);
// Wrong length.
decode_resp = encoder.DecodeSourceStatusBlockResp (data, 0, adf_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
decode_resp = encoder.DecodeSourceStatusBlockResp (data, 20, adf_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
}
/*
* TEST: EncodeBasicParameterBlock()
*
*/
static void test_family3_encode_basic_param()
{
DecodeStatus decode_resp;
SANE_Byte data_buffer[1024];
size_t ret_length;
BrotherEncoderFamily3 encoder(0);
// All defaults.
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=100,100\nM=CGRAY\n" "\x80";
ASSERT_EQ(ret_length, (size_t)22);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Different resolutions.
encoder.SetRes(200,300);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=CGRAY\n" "\x80";
ASSERT_EQ(ret_length, (size_t)22);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// GRAY mode.
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=GRAY64\n" "\x80";
ASSERT_EQ(ret_length, (size_t )23);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// COLOR mode
encoder.SetScanMode (BROTHER_SCAN_MODE_COLOR);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=CGRAY\n" "\x80";
ASSERT_EQ(ret_length, (size_t )22);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// GRAY DITHERED mode
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY_DITHERED);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=ERRDIF\n" "\x80";
ASSERT_EQ(ret_length, (size_t )23);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// TEXT mode
encoder.SetScanMode (BROTHER_SCAN_MODE_TEXT);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=TEXT\n" "\x80";
ASSERT_EQ(ret_length, (size_t )21);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Buffer too short.
encoder.SetScanMode (BROTHER_SCAN_MODE_TEXT);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, 15, &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_INVAL);
}
/*
* TEST: EncodeParameterBlock()
*
*/
static void test_family3_encode_param()
{
DecodeStatus decode_resp;
SANE_Byte data_buffer[1024];
size_t ret_length;
BrotherEncoderFamily3 encoder(0);
// All defaults.
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=100,100\nM=CGRAY\nC=JPEG\nJ=MID\n"
"B=50\nN=50\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Different resolutions.
encoder.SetRes(200,300);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=CGRAY\nC=JPEG\nJ=MID\n"
"B=50\nN=50\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Different modes:
// GRAY mode.
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=GRAY64\nC=RLENGTH\n"
"B=50\nN=50\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// COLOR mode
encoder.SetScanMode (BROTHER_SCAN_MODE_COLOR);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=CGRAY\nC=JPEG\nJ=MID\n"
"B=50\nN=50\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// GRAY DITHERED mode
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY_DITHERED);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=ERRDIF\nC=RLENGTH\n"
"B=50\nN=50\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// TEXT mode
encoder.SetScanMode (BROTHER_SCAN_MODE_TEXT);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=50\nN=50\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Different brightness and contrast, positive and negative.
encoder.SetBrightness (-20);
encoder.SetContrast (-30);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=30\nN=20\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
encoder.SetBrightness (50);
encoder.SetContrast (40);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=100\nN=90\nA=0,0,0,0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Different dimensions
encoder.SetScanDimensions (0, 10, 50, 100);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=100\nN=90\nA=0,50,10,150\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// ADF duplex
encoder.SetScanDimensions (0, 10, 50, 100);
encoder.SetSource(BROTHER_SOURCE_ADF_DUPLEX);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=100\nN=90\nA=0,50,10,150\nD=DUP\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Buffer too short.
decode_resp = encoder.EncodeParameterBlock (data_buffer, 15, &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_INVAL);
}
/*
* TEST: EncodeADFBlock()
*
*/
static void test_family3_encode_adf()
{
DecodeStatus decode_resp;
SANE_Byte data_buffer[1024];
size_t ret_length;
BrotherEncoderFamily3 encoder(0);
// Standard call.
decode_resp = encoder.EncodeSourceStatusBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "D\nADF\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Buffer too short.
decode_resp = encoder.EncodeSourceStatusBlock (data_buffer, 5, &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_INVAL);
}
/*
* Run all the tests.
*
*/
void test_family3 ()
{
// Decodes.
test_family3_decode_session_resp();
test_family3_decode_basic_param_resp();
test_family3_decode_sourcestatus_resp();
// Encodes.
test_family3_encode_basic_param();
test_family3_encode_param();
test_family3_encode_adf();
}

Wyświetl plik

@ -0,0 +1,424 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
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, see <https://www.gnu.org/licenses/>.
*/
#define DEBUG_DECLARE_ONLY
#include "brother_mfp_tests.h"
#include "../../backend/brother_mfp/brother_mfp-encoder.h"
#include "../genesys/minigtest.h"
/*
* TEST: DecodeSessionResp()
*
*/
static void test_family4_decode_session_resp()
{
DecodeStatus decode_resp;
BrotherSessionResponse sess_resp;
BrotherEncoderFamily4 encoder(0);
// SUCCESS status
const SANE_Byte *data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x00";
decode_resp = encoder.DecodeSessionResp (data, 5, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
ASSERT_TRUE(sess_resp.ready);
// BUSY status
data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x80";
decode_resp = encoder.DecodeSessionResp (data, 5, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
ASSERT_FALSE(sess_resp.ready);
// Length issues
data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x20" "\x32";
decode_resp = encoder.DecodeSessionResp (data, 6, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
decode_resp = encoder.DecodeSessionResp (data, 4, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
decode_resp = encoder.DecodeSessionResp (data, 0, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
// Content problems.
const char *content_problems[] = {
"\x32" "\x10" "\x01" "\x02" "\x20",
"\x06" "\x32" "\x01" "\x02" "\x20",
"\x06" "\x10" "\x32" "\x02" "\x20",
"\x06" "\x10" "\x01" "\x32" "\x20",
"\x06" "\x10" "\x01" "\x02" "\x32",
nullptr
};
for (size_t test = 0; test; test++)
{
decode_resp = encoder.DecodeSessionResp ((const SANE_Byte *)content_problems[test], 5, sess_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
}
}
/*
* TEST: DecodeBasicParameterBlockResp()
*
*/
static void test_family4_decode_basic_param_resp()
{
/*
* TODO: Nothing to do here yet.
* We don't decode anything from this block.
* Watch this space.
*
*/
}
/*
* TEST: DecodeADFBlockResp()
*
*/
static void test_family4_decode_adf_resp()
{
DecodeStatus decode_resp;
BrotherSourceStatusResponse adf_resp;
BrotherEncoderFamily4 encoder(0);
// SUCCESS status
const SANE_Byte *data = (const SANE_Byte *)"\xc2";
decode_resp = encoder.DecodeSourceStatusBlockResp (data, 1, adf_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
ASSERT_FALSE(adf_resp.source_ready);
data = (const SANE_Byte *)"\x80";
decode_resp = encoder.DecodeSourceStatusBlockResp (data, 1, adf_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
ASSERT_TRUE(adf_resp.source_ready);
// Wrong length.
decode_resp = encoder.DecodeSourceStatusBlockResp (data, 0, adf_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
decode_resp = encoder.DecodeSourceStatusBlockResp (data, 20, adf_resp);
ASSERT_EQ(decode_resp, DECODE_STATUS_ERROR);
}
/*
* TEST: EncodeBasicParameterBlock()
*
*/
static void test_family4_encode_basic_param()
{
DecodeStatus decode_resp;
SANE_Byte data_buffer[1024];
size_t ret_length;
BrotherEncoderFamily4 encoder(0);
// All defaults.
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=100,100\nM=CGRAY\n" "\x80";
ASSERT_EQ(ret_length, (size_t)22);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Different resolutions.
encoder.SetRes(200,300);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=CGRAY\n" "\x80";
ASSERT_EQ(ret_length, (size_t)22);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// GRAY mode.
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=GRAY64\n" "\x80";
ASSERT_EQ(ret_length, (size_t )23);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// COLOR mode
encoder.SetScanMode (BROTHER_SCAN_MODE_COLOR);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=CGRAY\n" "\x80";
ASSERT_EQ(ret_length, (size_t )22);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// GRAY DITHERED mode
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY_DITHERED);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=ERRDIF\n" "\x80";
ASSERT_EQ(ret_length, (size_t )23);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// TEXT mode
encoder.SetScanMode (BROTHER_SCAN_MODE_TEXT);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=TEXT\n" "\x80";
ASSERT_EQ(ret_length, (size_t )21);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Buffer too short.
encoder.SetScanMode (BROTHER_SCAN_MODE_TEXT);
decode_resp = encoder.EncodeBasicParameterBlock (data_buffer, 15, &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_INVAL);
}
/*
* TEST: EncodeParameterBlock()
*
*/
static void test_family4_encode_param()
{
DecodeStatus decode_resp;
SANE_Byte data_buffer[1024];
size_t ret_length;
BrotherEncoderFamily4 encoder(0);
// All defaults.
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=100,100\nM=CGRAY\nC=JPEG\nJ=MID\n"
"B=50\nN=50\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Different resolutions.
encoder.SetRes(200,300);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=CGRAY\nC=JPEG\nJ=MID\n"
"B=50\nN=50\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Different modes:
// GRAY mode.
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=GRAY64\nC=RLENGTH\n"
"B=50\nN=50\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// COLOR mode
encoder.SetScanMode (BROTHER_SCAN_MODE_COLOR);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=CGRAY\nC=JPEG\nJ=MID\n"
"B=50\nN=50\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// GRAY DITHERED mode
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY_DITHERED);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=ERRDIF\nC=RLENGTH\n"
"B=50\nN=50\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// TEXT mode
encoder.SetScanMode (BROTHER_SCAN_MODE_TEXT);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=50\nN=50\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Different brightness and contrast, positive and negative.
encoder.SetBrightness (-20);
encoder.SetContrast (-30);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=30\nN=20\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
encoder.SetBrightness (50);
encoder.SetContrast (40);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=100\nN=90\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Different dimensions
encoder.SetScanDimensions (0, 10, 50, 100);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=100\nN=90\nA=0,50,10,150\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\nD=SIN\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Duplex ADF
encoder.SetScanDimensions (0, 10, 50, 100);
encoder.SetSource (BROTHER_SOURCE_ADF_DUPLEX);
decode_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=100\nN=90\nA=0,50,10,150\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\nD=DUP\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Buffer too short.
decode_resp = encoder.EncodeParameterBlock (data_buffer, 15, &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_INVAL);
}
/*
* TEST: EncodeADFBlock()
*
*/
static void test_family4_encode_adf()
{
DecodeStatus decode_resp;
SANE_Byte data_buffer[1024];
size_t ret_length;
BrotherEncoderFamily4 encoder(0);
// Standard call.
decode_resp = encoder.EncodeSourceStatusBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "D\nADF\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Buffer too short.
decode_resp = encoder.EncodeSourceStatusBlock (data_buffer, 5, &ret_length);
ASSERT_EQ(decode_resp, DECODE_STATUS_INVAL);
}
/*
* Run all the tests.
*
*/
void test_family4 ()
{
// Decodes.
test_family4_decode_session_resp();
test_family4_decode_basic_param_resp();
test_family4_decode_adf_resp();
// Encodes.
test_family4_encode_basic_param();
test_family4_encode_param();
test_family4_encode_adf();
}

Wyświetl plik

@ -0,0 +1,251 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
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, see <https://www.gnu.org/licenses/>.
*/
#define DEBUG_DECLARE_ONLY
#include "../../include/sane/sanei_debug.h"
#include "brother_mfp_tests.h"
#include "../../backend/brother_mfp/brother_mfp-encoder.h"
#include "../genesys/minigtest.h"
/*
* TEST: BrotherGrayRLengthDecoder()
*
*/
static void test_gray_rlength_no_compress()
{
BrotherGrayRLengthDecoder decoder;
const SANE_Byte *src_data;
SANE_Byte dest_data[1024];
DecodeStatus res_code;
size_t src_data_consumed;
size_t dest_data_written;
/*
* Normal decode.
*
*/
src_data = (const SANE_Byte *)"\x04" "ABCDE";
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)6);
ASSERT_EQ(dest_data_written, (size_t)5);
ASSERT_EQ(memcmp(dest_data, "ABCDE", 5), 0);
// Boundary condition: 1 character only.
src_data = (const SANE_Byte *)"\x00" "X";
res_code = decoder.DecodeScanData (src_data,
2,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)2);
ASSERT_EQ(dest_data_written, (size_t)1);
ASSERT_EQ(memcmp(dest_data, "X", 1), 0);
/*
* Decodes broken through src exhaustion (fragmentation of input).
*
*/
// Break after length: can't decode: need full miniblock.
src_data = (const SANE_Byte *)"\x04";
res_code = decoder.DecodeScanData (src_data,
1,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)0);
ASSERT_EQ(dest_data_written, (size_t)0);
// BREAK part way through the data portion: can't decode: need full miniblock.
src_data = (const SANE_Byte *)"\x05" "ABC";
res_code = decoder.DecodeScanData (src_data,
4,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)0);
ASSERT_EQ(dest_data_written, (size_t)0);
/*
* Decodes broken by exhausted output buffer.
*
*/
// BREAK part way through the data portion: can't decode: need full miniblock.
src_data = (const SANE_Byte *)"\x05" "MNOPQR";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
7,
&src_data_consumed,
dest_data,
1,
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)0);
ASSERT_EQ(dest_data_written, (size_t)0);
}
static void test_gray_rlength_compression()
{
BrotherGrayRLengthDecoder decoder;
const SANE_Byte *src_data;
SANE_Byte dest_data[1024];
DecodeStatus res_code;
size_t src_data_consumed;
size_t dest_data_written;
/*
* Normal decode.
*
*/
src_data = (const SANE_Byte *)"\xfd" "A";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)2);
ASSERT_EQ(dest_data_written, (size_t)4);
ASSERT_EQ(memcmp(dest_data, "AAAA", 4), 0);
// Boundary check: minimal compression: 2 chars
src_data = (const SANE_Byte *)"\xff" "X";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)2);
ASSERT_EQ(dest_data_written, (size_t)2);
ASSERT_EQ(memcmp(dest_data, "XX", 2), 0);
// Upper limit of compression: 0x80 (gives 129 bytes of output)
src_data = (const SANE_Byte *)"\x80" "Y";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)2);
ASSERT_EQ(dest_data_written, (size_t)129);
ASSERT_EQ(strrchr((const char *)dest_data, 'Y'), (const char *)&dest_data[128]);
/*
* Fragmentation due to dest exhaustion. Can't decode: need full miniblock.
*
*/
src_data = (const SANE_Byte *)"\xfd" "Z";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
1,
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)0);
ASSERT_EQ(dest_data_written, (size_t)0);
/*
* Fragmentation due to src exhaustion.
* The only case here is that we see the length, but not the
* following character so we cannot actually do anything.
*
*/
src_data = (const SANE_Byte *)"\xfd" "A";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
1,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)0);
ASSERT_EQ(dest_data_written, (size_t)0);
res_code = decoder.DecodeScanData (src_data,
2,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)2);
ASSERT_EQ(dest_data_written, (size_t)4);
ASSERT_EQ(memcmp(dest_data, "AAAA", 4), 0);
}
/*
* Run all the tests.
*
*/
void test_gray_rlength()
{
test_gray_rlength_no_compress();
test_gray_rlength_compression();
}