mitsud90: Theoretically we support continuous panoramas now

This required substantial rework of the "common" rgb8 pano split code to
add blending support (modeled after the DNP stuff) along with MANY
bugfixes.

The Sinfonia pano code (used by the EK8810 and EK69xx) also needed
to be tweaked, but that isn't expected to work on real printers yet due to
unknown control protocol bits.
This commit is contained in:
Solomon Peachy 2024-01-07 12:00:14 -05:00
parent e4b678ea08
commit 03ca5ce596
7 changed files with 154 additions and 96 deletions

View file

@ -29,7 +29,7 @@
#include <signal.h>
#include <strings.h> /* For strncasecmp */
#define BACKEND_VERSION "0.127"
#define BACKEND_VERSION "0.128"
#ifndef CORRTABLE_PATH
#ifdef PACKAGE_DATA_DIR
@ -1958,31 +1958,91 @@ const void *dyesub_joblist_popjob(struct dyesub_joblist *list)
return NULL;
}
int dyesub_pano_split_rgb8(const uint8_t *src, uint16_t cols,
uint16_t src_rows, uint8_t numpanels,
uint16_t overlap_rows, uint16_t max_rows,
uint8_t *panels[3],
uint16_t panel_rows[3])
#include "backend_panodata.h"
#define PROCESS_PIXEL(__corr, __offset) \
data[(r * cols * 3) + c + __offset] = 255 - ((255 - (double)data[(r * cols *3) + c +__offset]) * (__corr))
static void dyesub_pano_process_rgb8(const struct dnp_panodata *pano,
uint8_t *data, int lh, int rh,
uint16_t cols, uint16_t rows,
uint16_t overlap, uint16_t pad_rows)
{
/* Do nothing if there's no point */
if (numpanels < 2 || src_rows <= max_rows)
return CUPS_BACKEND_OK;
/* Skip over start margin in source */
data += pad_rows * cols * 3;
/* Work out panel sizes if not specified */
if (panel_rows[0] == 0) {
panel_rows[0] = max_rows;
panel_rows[1] = src_rows - panel_rows[0] + overlap_rows;
if (numpanels > 2)
panel_rows[2] = src_rows - panel_rows[0] - panel_rows[1] + overlap_rows*2;
for (int r = 0 ; r < rows ; r++) {
if (rh && r < overlap) {
const struct panodata_row *rhc;
int i, c;
int row = (overlap - r);
for (i = 0 ; i < pano->elements-1 ; i++) {
if (row >= pano->rows[i].start_row && row < pano->rows[i+1].start_row)
break;
}
rhc = &pano->rows[i];
for (c = 0 ; c < cols ; c++) {
PROCESS_PIXEL(rhc->rhYMC[2],0); /* R/C */
PROCESS_PIXEL(rhc->rhYMC[1],1); /* G/M */
PROCESS_PIXEL(rhc->rhYMC[0],2); /* B/Y */
}
} else if (lh && (rows - r) < overlap) {
const struct panodata_row *lhc;
int i, c;
int row = (rows -r);
for (i = 0 ; i < pano->elements-1 ; i++) {
if (row >= pano->rows[i].start_row && row < pano->rows[i+1].start_row)
break;
}
lhc = &pano->rows[i];
for (c = 0 ; c < cols ; c++) {
PROCESS_PIXEL(lhc->rhYMC[2],0); /* R/C */
PROCESS_PIXEL(lhc->rhYMC[1],1); /* G/M */
PROCESS_PIXEL(lhc->rhYMC[0],2); /* B/Y */
}
}
/* Next row */
data += cols * 3;
}
}
/* Copy panel data */
memcpy(panels[0], src, cols * panel_rows[0] * 3);
memcpy(panels[1], src + ((panel_rows[0] - overlap_rows) * cols) * 3, cols * panel_rows[1] * 3);
if (numpanels > 2)
memcpy(panels[2], src + ((panel_rows[0] - overlap_rows + panel_rows[1] - overlap_rows) * cols) * 3, cols * panel_rows[2] * 3);
void dyesub_pano_split_rgb8(const uint8_t *src, uint16_t cols, uint8_t numpanels,
uint16_t overlap_rows, uint16_t pad_rows,
uint16_t *panel_rows, uint8_t **panels)
{
int i = 0;
return CUPS_BACKEND_OK;
INFO("Splitting job into %d panel continuous panorama\n", numpanels);
/* Skip over start margin in source */
src += pad_rows * cols * 3;
for (i = 0 ; i < numpanels ; i++) {
int lh = (i < (numpanels -1));
int rh = (i > 0);
uint8_t *out = panels[i];
/* Fill start margin with white */
if (pad_rows) {
memset(out, 0xff, pad_rows * cols * 3);
out += pad_rows * cols * 3;
}
/* Copy over panel data */
memcpy(out, src, panel_rows[i] * cols * 3);
src += (panel_rows[i] - overlap_rows) * cols * 3; /* Factor in overlap */
out += panel_rows[i] * cols * 3;
/* Fill end margin with white */
if (pad_rows) {
memset(out, 0xff, pad_rows * cols * 3);
out += pad_rows * cols * 3;
}
dyesub_pano_process_rgb8(&panodata, panels[i], lh, rh,
cols, panel_rows[i] - overlap_rows * 2,
overlap_rows, pad_rows);
}
}
int dyesub_joblist_canwait(struct dyesub_joblist *list)

View file

@ -1,7 +1,7 @@
/*
* CUPS Backend common code
*
* (c) 2013-2023 Solomon Peachy <pizza@shaftnet.org>
* (c) 2013-2024 Solomon Peachy <pizza@shaftnet.org>
*
* The latest version of this program can be found at:
*
@ -246,6 +246,19 @@ struct dyesub_job_common {
int can_combine;
};
/* Panorama stuff */
struct panodata_row {
uint16_t start_row;
double rhYMC[3];
double lhYMC[3];
};
#define DNP_PANO_MAXROWS 64
struct dnp_panodata {
uint16_t elements;
struct panodata_row rows[DNP_PANO_MAXROWS];
};
/* Exported functions */
int send_data(struct dyesub_connection *conn, const uint8_t *buf, int len);
int read_data(struct dyesub_connection *conn,
@ -283,11 +296,9 @@ int dyesub_joblist_canwait(struct dyesub_joblist *list);
#define BACKEND_FLAG_BADISERIAL 0x00000001
#define BACKEND_FLAG_DUMMYPRINT 0x00000002
int dyesub_pano_split_rgb8(const uint8_t *src, uint16_t cols,
uint16_t src_rows, uint8_t numpanels,
uint16_t overlap_rows, uint16_t max_rows,
uint8_t *panels[3],
uint16_t panel_rows[3]);
void dyesub_pano_split_rgb8(const uint8_t *src, uint16_t cols, uint8_t numpanels,
uint16_t overlap_rows, uint16_t pad_rows,
uint16_t *panel_rows, uint8_t **panels);
/* Backend Functions */
struct dyesub_backend {

View file

@ -1,7 +1,7 @@
/*
* Citizen / DNP Photo Printer CUPS backend
*
* (c) 2013-2023 Solomon Peachy <pizza@shaftnet.org>
* (c) 2013-2024 Solomon Peachy <pizza@shaftnet.org>
*
* Development of this backend was sponsored by:
*
@ -272,18 +272,6 @@ static void dnpds40_cleanup_job(const void *vjob);
static int dnpds40_query_markers(void *vctx, struct marker **markers, int *count);
/* Panorama crap */
struct panodata_row {
uint16_t start_row;
double rhYMC[3];
double lhYMC[3];
};
#define DNP_PANO_MAXROWS 64
struct dnp_panodata {
uint16_t elements;
struct panodata_row rows[DNP_PANO_MAXROWS];
};
#ifdef USE_PANODATA_FILES
#define PANODATA_DS620 "LUTData_0010.csv"
#define PANODATA_DS820 "LUTData820_0010.csv"

View file

@ -1,7 +1,7 @@
/*
* Mitsubishi CP-D90DW Photo Printer CUPS backend
*
* (c) 2019-2023 Solomon Peachy <pizza@shaftnet.org>
* (c) 2019-2024 Solomon Peachy <pizza@shaftnet.org>
*
* The latest version of this program can be found at:
*
@ -924,11 +924,12 @@ static int mitsud90_panorama_splitjob(struct mitsud90_printjob *injob, struct mi
{
uint8_t *panels[3] = { NULL, NULL, NULL };
uint16_t panel_rows[3] = { 0, 0, 0 };
uint16_t overlap_rows;
uint16_t overlap_rows = 600;
uint16_t pad_rows = 14;
uint8_t numpanels;
uint16_t cols;
uint16_t inrows;
uint16_t max_rows;
uint16_t max_rows = 2428; /* 6x8" only */
int i;
cols = be16_to_cpu(injob->hdr.cols);
@ -937,23 +938,21 @@ static int mitsud90_panorama_splitjob(struct mitsud90_printjob *injob, struct mi
/* Work out parameters */
if (inrows == 6028) {
numpanels = 3;
overlap_rows = 600 + 28;
max_rows = 2428;
} else if (inrows == 4228) {
numpanels = 2;
max_rows = 2428;
overlap_rows = 600 + 28;
} else {
ERROR("Invalid panorama row count (%d)\n", inrows);
return CUPS_BACKEND_CANCEL;
}
/* Work out which number of rows per panel */
if (!panel_rows[0]) {
panel_rows[0] = max_rows;
panel_rows[1] = inrows - panel_rows[0] + overlap_rows;
if (numpanels > 2)
panel_rows[2] = inrows - panel_rows[0] - panel_rows[1] + overlap_rows + overlap_rows;
/* Work out panel sizes... */
for (int src_rows = inrows - 2 * pad_rows, i = 0 ; src_rows > 0; ) {
panel_rows[i] = (src_rows < max_rows) ? src_rows : max_rows - 2*pad_rows;
src_rows -= panel_rows[i];
i++;
if (i < numpanels)
src_rows += overlap_rows;
}
/* Allocate and set up new jobs and buffers */
@ -963,7 +962,8 @@ static int mitsud90_panorama_splitjob(struct mitsud90_printjob *injob, struct mi
ERROR("Memory allocation failure");
return CUPS_BACKEND_RETRY_CURRENT;
}
panels[i] = malloc(cols * panel_rows[i] * 3) + sizeof(struct mitsud90_plane_hdr);
panel_rows[i] += 2 * pad_rows;
panels[i] = malloc((cols * panel_rows[i] * 3) + sizeof(struct mitsud90_plane_hdr));
if (!panels[i]) {
ERROR("Memory allocation failure");
return CUPS_BACKEND_RETRY_CURRENT;
@ -981,6 +981,7 @@ static int mitsud90_panorama_splitjob(struct mitsud90_printjob *injob, struct mi
newjobs[i]->hdr.pano.unk[1] = 0x0c;
newjobs[i]->hdr.pano.unk[3] = 0x06;
newjobs[i]->has_footer = 0;
panel_rows[i] -= 2 * pad_rows;
/* Fill in plane header differences */
memcpy(newjobs[i]->databuf, injob->databuf, sizeof(struct mitsud90_plane_hdr));
@ -993,14 +994,11 @@ static int mitsud90_panorama_splitjob(struct mitsud90_printjob *injob, struct mi
/* Last panel gets the footer, if any */
newjobs[numpanels - 1]->has_footer = injob->has_footer;
dyesub_pano_split_rgb8(injob->databuf, cols, inrows,
numpanels, overlap_rows, max_rows,
panels, panel_rows);
dyesub_pano_split_rgb8(injob->databuf, cols, numpanels,
overlap_rows, pad_rows,
panel_rows, panels);
// XXX process buffers!
// pano_process_rgb8(numpanels, cols, overlap_rows, panels, panel_rows);
return CUPS_BACKEND_OK;
return CUPS_BACKEND_OK;
}
static int mitsud90_read_parse(void *vctx, const void **vjob, int data_fd, int copies) {
@ -1068,8 +1066,12 @@ static int mitsud90_read_parse(void *vctx, const void **vjob, int data_fd, int c
if (job->hdr.zero_b[3] && job->hdr.pano.on == 0x03) {
job->is_pano = 1;
job->hdr.zero_b[3] = 0;
job->hdr.pano.on = 0x01;
job->hdr.pano.on = 0; /* Will get inserted later */
} else if (be16_to_cpu(job->hdr.rows > 2729)) {
job->is_pano = 1;
job->hdr.pano.on = 0;
}
} else if (ctx->conn->type == P_MITSU_W5000) {
struct mitsuw5k_job_hdr *hdr = (struct mitsuw5k_job_hdr*) &job->hdr;
@ -1099,7 +1101,7 @@ static int mitsud90_read_parse(void *vctx, const void **vjob, int data_fd, int c
be16_to_cpu(job->hdr.pano.rows2 != (2428-0x30)) ||
be16_to_cpu(job->hdr.pano.overlap != 600)
) {
ERROR("Invalid panorama parameters");
ERROR("Invalid panorama job parameters\n");
mitsud90_cleanup_job(job);
return CUPS_BACKEND_CANCEL;
}
@ -1266,8 +1268,6 @@ read_data:
/* Clean up original parsed job regardless */
mitsud90_cleanup_job(job);
// XXX process panels!
return rval;
} else {
*vjob = job;
@ -2424,7 +2424,7 @@ static const char *mitsud90_prefixes[] = {
/* Exported */
const struct dyesub_backend mitsud90_backend = {
.name = "Mitsubishi CP-D90/CP-M1/CP-W5000",
.version = "0.46" " (lib " LIBMITSU_VER ")",
.version = "0.47" " (lib " LIBMITSU_VER ")",
.uri_prefixes = mitsud90_prefixes,
.cmdline_arg = mitsud90_cmdline_arg,
.cmdline_usage = mitsud90_cmdline,

View file

@ -212,6 +212,7 @@ int sinfonia_panorama_splitjob(struct sinfonia_printjob *injob,
uint16_t panel_rows[3] = { 0, 0, 0 };
uint8_t numpanels;
uint16_t overlap_rows;
uint16_t pad_rows;
uint16_t inrows;
uint16_t cols;
@ -231,13 +232,14 @@ int sinfonia_panorama_splitjob(struct sinfonia_printjob *injob,
else
numpanels = 2;
overlap_rows = 600 + 36;
pad_rows = 18;
overlap_rows = 600;
if (numpanels == 3) {
if (inrows > 4536) // 5x15
overlap_rows = 100 + 36;
overlap_rows = 100;
else
overlap_rows = 600 + 36;
overlap_rows = 600;
}
break;
case 1844: /* EK6900/6950 */
@ -246,7 +248,8 @@ int sinfonia_panorama_splitjob(struct sinfonia_printjob *injob,
return CUPS_BACKEND_CANCEL;
}
overlap_rows = 600 + 36;
pad_rows = 18;
overlap_rows = 600;
if (inrows > 4236) // ie 6x14
numpanels = 3;
@ -259,7 +262,8 @@ int sinfonia_panorama_splitjob(struct sinfonia_printjob *injob,
ERROR("Bad pano input\n");
return CUPS_BACKEND_CANCEL;
}
overlap_rows = 600 + 24;
pad_rows = 12;
overlap_rows = 600;
if (max_rows == 3024) { /* 8x10 media */
if (inrows > 5424) // 8x18
@ -276,18 +280,18 @@ int sinfonia_panorama_splitjob(struct sinfonia_printjob *injob,
if (numpanels == 3) {
if (max_rows == 3024) {
if (inrows == 6024) // 8x20
overlap_rows = 24;
overlap_rows = 0;
} else {
if (inrows == 10824) // 8x36
overlap_rows = 24;
overlap_rows = 0;
}
} else {
if (max_rows == 3024) {
if (inrows == 9024) // 8x30
overlap_rows = 24;
overlap_rows = 0;
} else {
if (inrows == 7224) // 8x24
overlap_rows = 24;
overlap_rows = 0;
}
}
@ -297,12 +301,14 @@ int sinfonia_panorama_splitjob(struct sinfonia_printjob *injob,
return CUPS_BACKEND_CANCEL;
}
/* Work out which number of rows per panel */
if (!panel_rows[0]) {
panel_rows[0] = max_rows;
panel_rows[1] = inrows - panel_rows[0] + overlap_rows;
if (numpanels > 2)
panel_rows[2] = inrows - panel_rows[0] - panel_rows[1] + overlap_rows + overlap_rows;
/* Work out panel sizes... */
for (int src_rows = inrows - 2 * pad_rows, i = 0 ; src_rows > 0; ) {
panel_rows[i] = (src_rows < max_rows) ? src_rows : max_rows - 2*pad_rows;
src_rows -= panel_rows[i];
i++;
if (i < numpanels)
src_rows += overlap_rows;
}
/* Allocate and set up new jobs and buffers */
@ -312,6 +318,7 @@ int sinfonia_panorama_splitjob(struct sinfonia_printjob *injob,
ERROR("Memory allocation failure");
return CUPS_BACKEND_RETRY_CURRENT;
}
panel_rows[i] += 2 * pad_rows;
panels[i] = malloc(cols * panel_rows[i] * 3);
if (!panels[i]) {
ERROR("Memory allocation failure");
@ -322,15 +329,13 @@ int sinfonia_panorama_splitjob(struct sinfonia_printjob *injob,
newjobs[i]->databuf = panels[i];
newjobs[i]->jp.rows = panel_rows[i];
// XXX what else?
// fill in contents here instead?
panel_rows[i] -= 2 * pad_rows;
}
dyesub_pano_split_rgb8(injob->databuf, cols, inrows,
numpanels, overlap_rows, max_rows,
panels, panel_rows);
// XXX postprocess buffers!
// pano_process_rgb8(numpanels, cols, overlap_rows, panels, panel_rows);
dyesub_pano_split_rgb8(injob->databuf, cols, numpanels,
overlap_rows, pad_rows,
panel_rows, panels);
return CUPS_BACKEND_OK;
}

View file

@ -24,7 +24,7 @@
*
*/
#define LIBSINFONIA_VER "0.19.1"
#define LIBSINFONIA_VER "0.20"
#define SINFONIA_HDR1_LEN 0x10
#define SINFONIA_HDR2_LEN 0x64

View file

@ -129,14 +129,8 @@ case "${model}" in
elif [ "${printsize}" eq "8x32" ] ; then
drows=3636
inrows=9636
elif [ "${printsize}" eq "A4x2" ] ; then
drows=3544
inrows=6452
elif [ "${printsize}" eq "A4x3" ] ; then
drows=3544
inrows=9360
else
echo "DS820 supportx 8x18, 8x26, 8x22, 8x32, A4x21, A4x31 only"
echo "DS820 supportx 8x18, 8x26, 8x22, and 8x32 only"
fi
;;
*)