From 03ca5ce596a3ec73c3b9b52f5b24340423f9f633 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Sun, 7 Jan 2024 12:00:14 -0500 Subject: [PATCH] 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. --- backend_common.c | 102 +++++++++++++++++++++++++++++++++++---------- backend_common.h | 23 +++++++--- backend_dnpds40.c | 14 +------ backend_mitsud90.c | 52 +++++++++++------------ backend_sinfonia.c | 49 ++++++++++++---------- backend_sinfonia.h | 2 +- pano-split.sh | 8 +--- 7 files changed, 154 insertions(+), 96 deletions(-) diff --git a/backend_common.c b/backend_common.c index c0ba82f..73fbfd0 100644 --- a/backend_common.c +++ b/backend_common.c @@ -29,7 +29,7 @@ #include #include /* 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) diff --git a/backend_common.h b/backend_common.h index 687f6ae..cc7ada0 100644 --- a/backend_common.h +++ b/backend_common.h @@ -1,7 +1,7 @@ /* * CUPS Backend common code * - * (c) 2013-2023 Solomon Peachy + * (c) 2013-2024 Solomon Peachy * * 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 { diff --git a/backend_dnpds40.c b/backend_dnpds40.c index bcfd3ee..44c64dd 100644 --- a/backend_dnpds40.c +++ b/backend_dnpds40.c @@ -1,7 +1,7 @@ /* * Citizen / DNP Photo Printer CUPS backend * - * (c) 2013-2023 Solomon Peachy + * (c) 2013-2024 Solomon Peachy * * 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" diff --git a/backend_mitsud90.c b/backend_mitsud90.c index 1d98a01..e17694f 100644 --- a/backend_mitsud90.c +++ b/backend_mitsud90.c @@ -1,7 +1,7 @@ /* * Mitsubishi CP-D90DW Photo Printer CUPS backend * - * (c) 2019-2023 Solomon Peachy + * (c) 2019-2024 Solomon Peachy * * 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, diff --git a/backend_sinfonia.c b/backend_sinfonia.c index 45583ea..9a2fca1 100644 --- a/backend_sinfonia.c +++ b/backend_sinfonia.c @@ -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; } diff --git a/backend_sinfonia.h b/backend_sinfonia.h index 9bf0514..4d04ce3 100644 --- a/backend_sinfonia.h +++ b/backend_sinfonia.h @@ -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 diff --git a/pano-split.sh b/pano-split.sh index c314694..ec39862 100755 --- a/pano-split.sh +++ b/pano-split.sh @@ -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 ;; *)