diff --git a/Makefile b/Makefile index f84d363..fbff7b0 100644 --- a/Makefile +++ b/Makefile @@ -110,7 +110,8 @@ BACKENDS = canonselphy canonselphyneo dnpds40 hiti kodak605 kodak1400 kodak6800 # List of data files DATAFILES = $(wildcard hiti_data/*bin) $(wildcard lib70x/data/*raw) \ $(wildcard lib70x/data/*cpc) $(wildcard lib70x/data/*lut) \ - $(wildcard lib70x/data/*dat) $(wildcard lib70x/data/*csv) + $(wildcard lib70x/data/*dat) $(wildcard lib70x/data/*csv) \ + $(wildcard dnp_data/*csv) # For the s6145, mitsu70x, mitsud90, and mitsu9550 backends ifneq (,$(findstring mingw,$(CC))) @@ -201,6 +202,10 @@ $(DATAFILES_TMP)/%: hiti_data/% | $(DATAFILES_TMP) @$(E) " LN " $@ $(Q)$(LN) -sf ../$< $@ +$(DATAFILES_TMP)/%: dnp_data/% | $(DATAFILES_TMP) + @$(E) " LN " $@ + $(Q)$(LN) -sf ../$< $@ + $(DATAFILES_TMP)/%: lib70x/data/% | $(DATAFILES_TMP) @$(E) " LN " $@ $(Q)$(LN) -sf ../$< $@ diff --git a/backend_dnpds40.c b/backend_dnpds40.c index 86d8338..87d04d5 100644 --- a/backend_dnpds40.c +++ b/backend_dnpds40.c @@ -60,16 +60,24 @@ struct dnpds40_printjob { int can_rewind; int buffcntrl; + int rows; + int is_pano; + int buf_needed; int cut_paper; }; +#define MAX_PRINTJOB_LEN (((ctx->native_width*ctx->max_height+1024+54+10))*3+1024) /* Worst-case, YMC */ +#define MAX_PANOPRINTJOB_LEN ((((ctx->native_width*ctx->max_height+1024+54+10))*3+1024)*3) /* Worst-case, YMC */ + #define MFG_DNP 0 #define MFG_CITIZEN 1 #define MFG_MITSUBISHI 2 #define MFG_FUJIFILM 3 #define MFG_OTHER 4 +#define USE_PANODATA_FILES + struct dnpds40_ctx { struct dyesub_connection *conn; @@ -230,6 +238,25 @@ struct dnpds40_cmd { #define MULTICUT_S_8x10_75 26 #define MULTICUT_S_8x4X3 28 // different than roll type. +/* Special panorama sizes */ +#define MULTICUT_PANO_6x14 9000 +#define MULTICUT_PANO_6x20 9001 +#define MULTICUT_PANO_8x18 9010 +#define MULTICUT_PANO_8x26 9011 +#define MULTICUT_PANO_8x22 9012 +#define MULTICUT_PANO_8x32 9013 +#define MULTICUT_PANO_A4x21 9014 +#define MULTICUT_PANO_A4x31 9015 + +#define MULTICUT_PANO_6x16 9020 +#define MULTICUT_PANO_6x24 9021 +#define MULTICUT_PANO_8x20 9030 +#define MULTICUT_PANO_8x30 9031 +#define MULTICUT_PANO_8x24 9032 +#define MULTICUT_PANO_8x36 9033 +#define MULTICUT_PANO_A4x2 9034 +#define MULTICUT_PANO_A4x3 9035 + #ifndef min #define min(__x, __y) ((__x) < (__y)) ? __x : __y #endif @@ -244,6 +271,510 @@ static int legacy_qw410_read_parse(struct dnpds40_printjob *job, int data_fd, in 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" + +static struct dnp_panodata *dnp_read_panodata(const char *filename) +{ + struct dnp_panodata *pano = NULL; + + FILE *f; + char buf[4096]; + int line; + char *ptr; + + const char *delim = " ,\t\n"; + + if (!filename) + return NULL; + pano = malloc(sizeof(struct dnp_panodata)); + if (!pano) + return NULL; + + pano->elements = 0; + + snprintf(buf, sizeof(buf), "%s/%s", corrtable_path, filename); + + f = fopen(buf, "r"); + if (!f) + goto done_free; + + /* Skip the first four lines */ + for (line = 0; line < DNP_PANO_MAXROWS ; line++) { + if (fgets(buf, sizeof(buf), f) == NULL) + break; + + ptr = strtok(buf, delim); + if (!ptr) + goto abort; + + if (strcmp("REC", ptr)) + continue; + + ptr = strtok(NULL, delim); + if (!ptr) + goto abort; + pano->rows[pano->elements].start_row = strtol(ptr, NULL, 10); + + ptr = strtok(NULL, delim); + if (!ptr) + goto abort; + pano->rows[pano->elements].rhYMC[0] = strtod(ptr,NULL); + + ptr = strtok(NULL, delim); + if (!ptr) + goto abort; + pano->rows[pano->elements].rhYMC[1] = strtod(ptr,NULL); + + ptr = strtok(NULL, delim); + if (!ptr) + goto abort; + pano->rows[pano->elements].rhYMC[2] = strtod(ptr,NULL); + + ptr = strtok(NULL, delim); + if (!ptr) + goto abort; + pano->rows[pano->elements].lhYMC[0] = strtod(ptr,NULL); + + ptr = strtok(NULL, delim); + if (!ptr) + goto abort; + pano->rows[pano->elements].lhYMC[1] = strtod(ptr,NULL); + + ptr = strtok(NULL, delim); + if (!ptr) + goto abort; + pano->rows[pano->elements].lhYMC[2] = strtod(ptr,NULL); + + pano->elements++; + } + if (pano->elements < 20) + goto abort; + + fclose(f); + +#if 0 + f = fopen("/tmp/panodata.h", "w"); + fprintf(f, "static struct dnp_panodata panodata = {\n"); + fprintf(f, "\t.elements = %d,\n", pano->elements); + for (int i = 0 ; i < pano->elements ; i++) { + fprintf(f, "\t.rows[%2u] = { %4u, { %1.10f, %1.10f, %1.10f }, { %1.10f, %1.10f, %1.10f} },\n", + i, + pano->rows[i].start_row, + pano->rows[i].rhYMC[0], + pano->rows[i].rhYMC[1], + pano->rows[i].rhYMC[2], + pano->rows[i].lhYMC[0], + pano->rows[i].lhYMC[1], + pano->rows[i].lhYMC[2]); + } + fprintf(f, "};\n"); + fclose(f); + exit(1); +#endif + + return pano; + +abort: + fclose(f); + +done_free: + free(pano); + return NULL; +} + +void dnp_free_panodata (struct dnp_panodata *pano) +{ + if (pano) + free(pano); +} +#else /* !USE_PANODATA_FILES */ + +#include "backend_panodata.h" + +#define dnp_read_panodata(__fname) &panodata +#define dnp_free_panodata(__pano) + +#endif + +#define PROCESS_PIXEL(__corr) \ + outdata[r * cols + c] = 255 - ((255 - (double)indata[r * cols + c]) * (__corr)) + +static void dnp_applypano_plane(const struct dnp_panodata *pano, + const uint8_t *indata, uint8_t *outdata, + uint16_t rows, const uint16_t cols, const uint16_t pad_rows, + const uint16_t dpi, int overlap, const int plane, + const int rh, const int lh) +{ + uint16_t r, c; + + /* Fill the start margin with white */ + if (pad_rows) { + memset(outdata, 0xff, pad_rows * cols); + outdata += pad_rows * cols; + } + + for (r = 0 ; r < rows; r++) { + const struct panodata_row *lhc = pano ? &pano->rows[0] : NULL; + const struct panodata_row *rhc = pano ? &pano->rows[pano->elements - 1] : NULL; + + if (rh && r < overlap) { + /* Row is in RH overlap portion of panel */ + int i; + int row = (overlap - r); + if (dpi == 600) + row /= 2; + 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]; + } else if (lh && (rows -r) < overlap) { + /* Row is in LH overlap portion of panel */ + int i; + int row = (rows -r); + if (dpi == 600) + row /= 2; + + 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]; + } else { + /* No processing on row, pass through as-is */ + memcpy(&outdata[r * cols], &indata[r * cols], cols); + continue; + } + + if (rh && rhc && r < overlap) { + /* Fade RH row */ + if (plane == 'Y') + for (c = 0 ; c < cols ; c++) { + PROCESS_PIXEL(rhc->rhYMC[0]); + } + else if (plane == 'M') { + for (c = 0 ; c < cols ; c++) { + PROCESS_PIXEL(rhc->rhYMC[1]); + } + } else { + for (c = 0 ; c < cols ; c++) { + PROCESS_PIXEL(rhc->rhYMC[2]); + } + } + } else if (lh && lhc && (rows -r) < overlap) { + /* Fade LH row */ + if (plane == 'Y') + for (c = 0 ; c < cols ; c++) { + PROCESS_PIXEL(lhc->lhYMC[0]); + } + else if (plane == 'M') { + for (c = 0 ; c < cols ; c++) { + PROCESS_PIXEL(lhc->lhYMC[1]); + } + } else { + for (c = 0 ; c < cols ; c++) { + PROCESS_PIXEL(lhc->lhYMC[2]); + } + } + } + } + + /* Fill the tail margin with white */ + if (pad_rows) + memset(&outdata[rows * cols], 0xff, pad_rows * cols); +} + +static int dnp_panorama_splitjob(struct dnpds40_ctx *ctx, + const struct dnpds40_printjob *injob, + struct dnpds40_printjob **newjobs) +{ + struct dnp_panodata *pano = NULL; + int overlap = injob->dpi * 2; + + uint16_t out_rows = 0; + uint16_t in_rows = 0; + uint8_t num_panels = 0; + uint32_t new_multicut = 0; + uint16_t pad_rows = 0; + + in_rows = injob->dpi == 600 ? injob->rows / 2 : injob->rows; + if (ctx->conn->type == P_DNP_DS620) { + out_rows = 2436; + new_multicut = MULTICUT_6x8; + + if (in_rows == 4236) { + num_panels = 2; + pad_rows = 18; + } else if (in_rows == 6036) { + num_panels = 3; + pad_rows = 18; + } else if (in_rows == 2436*2) { + num_panels = 2; + overlap = 0; + } else if (in_rows == 2436*3) { + num_panels = 3; + overlap = 0; + } else { + ERROR("Invalid panorama size (%d rows)\n", injob->rows); + goto bail; + } + } else if (ctx->conn->type == P_DNP_DS820) { + if (ctx->media == 600) { /* A4 */ + if (in_rows == 5436) { + new_multicut = MULTICUT_A4x10; + out_rows = 3036; + pad_rows = 18; + num_panels = 2; + } else if (in_rows == 7836) { + new_multicut = MULTICUT_A4x10; + out_rows = 3036; + pad_rows = 18; + num_panels = 3; + } else if (in_rows == 3036*2) { + new_multicut = MULTICUT_A4x10; + num_panels = 2; + out_rows = 3036; + overlap = 0; + } else if (in_rows == 3036*3) { + new_multicut = MULTICUT_A4x10; + num_panels = 3; + out_rows = 3036; + overlap = 0; + } else if (in_rows == 6452) { + new_multicut = MULTICUT_A4; + num_panels = 2; + pad_rows = 18; + out_rows = 3544; + } else if (in_rows == 9360) { + new_multicut = MULTICUT_A4; + num_panels = 3; + pad_rows = 18; + out_rows = 3544; + } else if (in_rows == 3544*2) { + new_multicut = MULTICUT_A4; + num_panels = 3; + out_rows = 3544; + overlap = 0; + } else if (in_rows == 3544*3) { + new_multicut = MULTICUT_A4; + num_panels = 3; + out_rows = 3544; + overlap = 0; + } else { + ERROR("Invalid panorama size (%d rows)\n", injob->rows); + goto bail; + } + } else { // 8x10/8x12 + if (in_rows == 5436) { + new_multicut = MULTICUT_8x10; + num_panels = 2; + pad_rows = 18; + out_rows = 3036; + } else if (in_rows == 7836) { + new_multicut = MULTICUT_8x10; + num_panels = 3; + pad_rows = 18; + out_rows = 3036; + } else if (in_rows == 3036*2) { + new_multicut = MULTICUT_8x10; + num_panels = 2; + overlap = 0; + out_rows = 3036; + } else if (in_rows == 3036*3) { + new_multicut = MULTICUT_8x10; + num_panels = 3; + overlap = 0; + out_rows = 3036; + } else if (in_rows == 6636) { + new_multicut = MULTICUT_8x12; + num_panels = 2; + pad_rows = 18; + out_rows = 3636; + } else if (in_rows == 9636) { + new_multicut = MULTICUT_8x12; + num_panels = 3; + pad_rows = 18; + out_rows = 3636; + } else if (in_rows == 3636*2) { + new_multicut = MULTICUT_8x12; + num_panels = 2; + out_rows = 3636; + overlap = 0; + } else if (in_rows == 3636*3) { + new_multicut = MULTICUT_8x12; + num_panels = 3; + out_rows = 3636; + overlap = 0; + } else { + ERROR("Invalid panorama size (%d rows)\n", injob->rows); + goto bail; + } + } + } + + /* Double everything up for >300dpi */ + if (injob->dpi == 600) { + out_rows *= 2; + overlap *= 2; + pad_rows *= 2; + } + + if (overlap) { +#ifdef USE_PANODATA_FILES + const char *filename = NULL; + + if (ctx->conn->type == P_DNP_DS620) + filename = PANODATA_DS620; + else if (ctx->conn->type == P_DNP_DS820) + filename = PANODATA_DS820; + + if (!filename || !(pano = dnp_read_panodata(filename))) { + ERROR("Failure to load panorama LUT file (%s)\n", filename); + goto bail; + } +#else + pano = dnp_read_panodata(NULL); +#endif + + DEBUG("Splitting continuous panorama job... (%d panels of %d+%d rows, %d overlap)\n", num_panels, out_rows - 2*pad_rows, 2*pad_rows, overlap); + } else { + DEBUG("Splitting discrete panel panorama job...(%d panels of %d rows)\n", num_panels, out_rows); + } + + for (int panel = 0 ; panel < num_panels ; panel++) { + int lh = (panel < (num_panels -1)); + int rh = (panel > 0); + + /* Create new job */ + newjobs[panel] = malloc(sizeof(struct dnpds40_printjob)); + if (!newjobs[panel]) { + ERROR("Memory allocation failure"); + return CUPS_BACKEND_RETRY_CURRENT; + } + struct dnpds40_printjob *newjob = newjobs[panel]; + memcpy(newjob, injob, sizeof(struct dnpds40_printjob)); + + newjob->common.copies = 1; + newjob->is_pano = lh; /* All but last */ + newjob->databuf = malloc(MAX_PRINTJOB_LEN); + if (!newjob->databuf) { + dnpds40_cleanup_job(newjob); + newjobs[panel] = NULL; + ERROR("Memory allocation failure!\n"); + return CUPS_BACKEND_RETRY_CURRENT; + } + newjob->datalen = 0; + + newjob->multicut = new_multicut; + newjob->rows = out_rows; + + uint32_t plane_len = out_rows * ctx->native_width + 1088; + + /* Copy data blocks from injob */ + uint8_t *ptr = injob->databuf; + while(ptr && ptr < (injob->databuf + injob->datalen)) { + uint32_t i; + int copy = 1; + if(!memcmp("IMAGE YPLANE", ptr +2, 12) || + !memcmp("IMAGE MPLANE", ptr +2, 12) || + !memcmp("IMAGE CPLANE", ptr +2, 12) ) { + + if (ptr[8] == 'Y') { + /* Insert CONT_PANORAMA _before_ the image data */ + int cont = (panel == num_panels-1) ? 0 : 1; + int lap = overlap; + if (injob->dpi == 600) + lap /= 2; + lap /= 3; /* Convert pixels into inches * 100 */ + + if (overlap) { + /* Continuous panorama */ + newjob->datalen += sprintf((char*)newjob->databuf + newjob->datalen, "\033PCNTRL CONT_PANORAMA 00000008%04u%04u", cont, lap); + } else { + /* Discrete panel */ + newjob->cutter = 1000; + } + } + + uint8_t *bmp_hdr; + DEBUG("Panel %d plane %c\n", panel, ptr[8]); + newjob->datalen += sprintf((char*)newjob->databuf + newjob->datalen, + "\033PIMAGE %cPLANE %08u", ptr[8], plane_len); + + /* Copy over old bitmap header*/ + memcpy(newjob->databuf + newjob->datalen, ptr + 32, 1088); + bmp_hdr = newjob->databuf + newjob->datalen; + + /* Mangle BMP header for new size and row count */ + i = cpu_to_le32(plane_len); + memcpy(bmp_hdr + 2, &i, sizeof(i)); + i = cpu_to_le32(out_rows); + memcpy(bmp_hdr + 22, &i, sizeof(i)); + + /* Split panorama */ + dnp_applypano_plane(pano, + ptr + 32 + 1088 + (pad_rows * ctx->native_width) + (panel * (out_rows - pad_rows*2 - overlap) * ctx->native_width), + newjob->databuf + newjob->datalen + 1088, + out_rows - pad_rows*2, ctx->native_width, pad_rows, injob->dpi, overlap, ptr[8], rh, lh); + newjob->datalen += plane_len; + + /* Don't copy anything else from source */ + copy = 0; + } else if(!memcmp("CNTRL QTY", ptr + 2, 9)) { + /* Ignore copy count */ + copy = 0; + } else if(!memcmp("CNTRL CUTTER", ptr + 2, 12) || + !memcmp("CNTRL FULL_CUTTER_SET", ptr + 2, 21)) { + /* Ignore all cutter commands */ + copy = 0; + } else if (!memcmp("CNTRL START", ptr + 2, 11)) { + } + + /* Work out how much to copy from source, and increment source pointer */ + char buf[10]; + buf[8] = 0; + memcpy(buf, ptr + 24, 8); + i = atoi(buf) + 32; + if (copy) { + memcpy(newjob->databuf + newjob->datalen, ptr, i); + newjob->datalen += i; + } + ptr += i; + } +#if 0 + { + char buf[32]; + sprintf(buf, "/tmp/dump%d.raw", panel); + FILE *f = fopen(buf, "wb"); + fwrite(newjob->databuf, newjob->datalen, 1, f); + fclose(f); + } +#endif + } + + dnp_free_panodata(pano); + return CUPS_BACKEND_OK; + +bail: + dnp_free_panodata(pano); + return CUPS_BACKEND_FAILED; +} + #define JOB_EQUIV(__x) if (job1->__x != job2->__x) goto done /* NOTE: Does _not_ free the input jobs */ @@ -1562,8 +2093,6 @@ static int dnpds40_query_status(struct dnpds40_ctx *ctx) return count; } -#define MAX_PRINTJOB_LEN (((ctx->native_width*ctx->max_height+1024+54+10))*3+1024) /* Worst-case, YMC */ - static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int copies) { struct dnpds40_ctx *ctx = vctx; int run = 1; @@ -1593,7 +2122,7 @@ static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int co the end of the job. */ - job->databuf = malloc(MAX_PRINTJOB_LEN); + job->databuf = malloc(MAX_PANOPRINTJOB_LEN); if (!job->databuf) { dnpds40_cleanup_job(job); ERROR("Memory allocation failure!\n"); @@ -1605,7 +2134,7 @@ static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int co uint32_t remain; /* Make sure we won't overflow... */ - if (job->datalen + sizeof(struct dnpds40_cmd) > MAX_PRINTJOB_LEN) { + if (job->datalen + sizeof(struct dnpds40_cmd) > MAX_PANOPRINTJOB_LEN) { ERROR("Buffer overflow when parsing printjob! (%d+%lu)\n", job->datalen, sizeof(struct dnpds40_cmd)); dnpds40_cleanup_job(job); @@ -1678,7 +2207,7 @@ static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int co remain = j; /* Make sure we won't overflow... */ - if (job->datalen + remain > MAX_PRINTJOB_LEN) { + if (job->datalen + remain > MAX_PANOPRINTJOB_LEN) { ERROR("Buffer overflow when parsing printjob! (%d+%d)\n", job->datalen, remain); dnpds40_cleanup_job(job); @@ -1689,7 +2218,7 @@ static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int co i = read(data_fd, job->databuf + job->datalen + sizeof(struct dnpds40_cmd), remain); if (i < 0) { - ERROR("Data Read Error: %d (%d/%d @%d/%d)\n", i, remain, j, job->datalen,MAX_PRINTJOB_LEN); + ERROR("Data Read Error: %d (%d/%d @%d/%d)\n", i, remain, j, job->datalen,MAX_PANOPRINTJOB_LEN); dnpds40_cleanup_job(job); return i; } @@ -1712,6 +2241,10 @@ static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int co memcpy(buf, job->databuf + job->datalen + 32, 8); job->cutter = atoi(buf); /* We'll insert it ourselves later */ + + if (job->cutter == 1000) + job->is_pano = 1; + continue; } if(!memcmp("CNTRL BUFFCNTRL", job->databuf + job->datalen+2, 15)) { @@ -1740,7 +2273,7 @@ static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int co job->multicut = atoi(buf); /* Backend automatically handles rewind support, so ignore application requests to use it. */ - if (job->multicut > 400) + if (job->multicut > 400 && job->multicut < 1000) job->multicut -= 400; /* We'll insert this ourselves later. */ @@ -1804,6 +2337,10 @@ static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int co dnpds40_cleanup_job(job); return CUPS_BACKEND_CANCEL; } + + /* Capture number of rows */ + memcpy(&y_ppm, job->databuf + job->datalen + 32 + 22, sizeof(y_ppm)); + job->rows = le32_to_cpu(y_ppm); } if(!memcmp("CNTRL PRINTSPEED", job->databuf + job->datalen + 2, 16)) { if (!ctx->supports_printspeed) { @@ -1821,6 +2358,8 @@ static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int co ERROR("Printer does not support Continuous Panorama, aborting!\n"); continue; } + memcpy(buf, job->databuf + job->datalen + 32 + 4, 4); + job->is_pano = atoi(buf); } /* This is the last block.. */ @@ -1830,6 +2369,85 @@ static int dnpds40_read_parse(void *vctx, const void **vjob, int data_fd, int co /* Add in the size of this chunk */ job->datalen += sizeof(struct dnpds40_cmd) + j; } + + /* Figure out the number of buffers we need. */ + job->buf_needed = 1; + + if (job->dpi == 600) { + switch(ctx->conn->type) { + case P_DNP_DS620: + if (job->multicut == MULTICUT_6x9 || + job->multicut == MULTICUT_6x4_5X2) + job->buf_needed = 2; + break; + case P_DNP_DS80: /* DS80/CX-W */ + if (job->matte && (job->multicut == MULTICUT_8xA4LEN || + job->multicut == MULTICUT_8x4X3 || + job->multicut == MULTICUT_8x8_8x4 || + job->multicut == MULTICUT_8x6X2 || + job->multicut == MULTICUT_8x12)) + job->buf_needed = 2; + break; + case P_DNP_DS80D: + if (job->matte) { + int mcut = job->multicut; + + if (mcut > MULTICUT_S_BACK) + mcut -= MULTICUT_S_BACK; + else if (mcut > MULTICUT_S_FRONT) + mcut -= MULTICUT_S_FRONT; + + if (mcut == MULTICUT_8xA4LEN || + mcut == MULTICUT_8x4X3 || + mcut == MULTICUT_8x8_8x4 || + mcut == MULTICUT_8x6X2 || + mcut == MULTICUT_8x12) + job->buf_needed = 2; + + if (mcut == MULTICUT_S_8x12 || + mcut == MULTICUT_S_8x6X2 || + mcut == MULTICUT_S_8x4X3) + job->buf_needed = 2; + } + break; + case P_DNP_DS820: + // Nothing; all sizes only need 1 buffer + break; + case P_CITIZEN_CW01: + job->buf_needed = 2; + break; + default: /* DS40/CX/RX1/CY/everything else */ + if (job->matte) { + if (job->multicut == MULTICUT_6x8 || + job->multicut == MULTICUT_6x9 || + job->multicut == MULTICUT_6x4X2 || + job->multicut == MULTICUT_5x7 || + job->multicut == MULTICUT_5x3_5X2) + job->buf_needed = 2; + + } else { + if (job->multicut == MULTICUT_6x8 || + job->multicut == MULTICUT_6x9 || + job->multicut == MULTICUT_6x4X2) + job->buf_needed = 1; + } + break; + } + } + + if (job->multicut >= MULTICUT_PANO_6x14) + { + int rval = dnp_panorama_splitjob(ctx, job, (struct dnpds40_printjob**)vjob); + + /* Clean up original job regardless */ + dnpds40_cleanup_job(job); + if (rval) + return rval; + + /* And continue validating everything based on the 1st job */ + job = (struct dnpds40_printjob*) *vjob; + } + parsed: /* If we have no data.. don't bother */ if (!job->datalen) { @@ -1838,7 +2456,7 @@ parsed: } /* Use the larger of the copy arguments */ - if (job->common.copies < copies) + if (!job->is_pano && job->common.copies < copies) job->common.copies = copies; /* Make sure advanced matte modes are supported */ @@ -1919,70 +2537,6 @@ parsed: } } - /* Figure out the number of buffers we need. */ - job->buf_needed = 1; - - if (job->dpi == 600) { - switch(ctx->conn->type) { - case P_DNP_DS620: - if (job->multicut == MULTICUT_6x9 || - job->multicut == MULTICUT_6x4_5X2) - job->buf_needed = 2; - break; - case P_DNP_DS80: /* DS80/CX-W */ - if (job->matte && (job->multicut == MULTICUT_8xA4LEN || - job->multicut == MULTICUT_8x4X3 || - job->multicut == MULTICUT_8x8_8x4 || - job->multicut == MULTICUT_8x6X2 || - job->multicut == MULTICUT_8x12)) - job->buf_needed = 2; - break; - case P_DNP_DS80D: - if (job->matte) { - int mcut = job->multicut; - - if (mcut > MULTICUT_S_BACK) - mcut -= MULTICUT_S_BACK; - else if (mcut > MULTICUT_S_FRONT) - mcut -= MULTICUT_S_FRONT; - - if (mcut == MULTICUT_8xA4LEN || - mcut == MULTICUT_8x4X3 || - mcut == MULTICUT_8x8_8x4 || - mcut == MULTICUT_8x6X2 || - mcut == MULTICUT_8x12) - job->buf_needed = 2; - - if (mcut == MULTICUT_S_8x12 || - mcut == MULTICUT_S_8x6X2 || - mcut == MULTICUT_S_8x4X3) - job->buf_needed = 2; - } - break; - case P_DNP_DS820: - // Nothing; all sizes only need 1 buffer - break; - case P_CITIZEN_CW01: - job->buf_needed = 2; - break; - default: /* DS40/CX/RX1/CY/everything else */ - if (job->matte) { - if (job->multicut == MULTICUT_6x8 || - job->multicut == MULTICUT_6x9 || - job->multicut == MULTICUT_6x4X2 || - job->multicut == MULTICUT_5x7 || - job->multicut == MULTICUT_5x3_5X2) - job->buf_needed = 2; - - } else { - if (job->multicut == MULTICUT_6x8 || - job->multicut == MULTICUT_6x9 || - job->multicut == MULTICUT_6x4X2) - job->buf_needed = 1; - } - break; - } - } if (job->dpi == 334 && ctx->conn->type != P_CITIZEN_CW01) { ERROR("Illegal resolution (%u) for printer!\n", job->dpi); @@ -2246,7 +2800,7 @@ skip_multicut: } /* Panorama sanity checking */ - if (job->cutter == 1000 || ctx->pano) { + if (job->is_pano) { if (!ctx->supports_pano) { ERROR("Printer does not support panorama printing, aborting!\n"); dnpds40_cleanup_job(job); @@ -2268,13 +2822,6 @@ skip_multicut: return CUPS_BACKEND_CANCEL; } } - ctx->pano++; - - /* Hard limit of 3 panels */ - if (job->cutter == 1000 && ctx->pano >= 3) { - WARNING("Panoramas limited to three panels!\n"); - job->cutter = 0; - } if (job->fullcut) { ERROR("Cannot use full cutter control in panorama mode!\n"); @@ -2501,6 +3048,33 @@ top: WARNING("Printer does not have sufficient remaining media (%d) to complete job (%d)\n", copies, count); } + /* Ensure we can complete panorama */ + if (ctx->pano && (count < 3)) { + ERROR("Printer does not have sufficent media for panorama job, aborting job!\n"); + return CUPS_BACKEND_CANCEL; + } + + /* If we're printing a panorama, query state before _first_ page */ + if (job->is_pano && ctx->pano == 0) { + int i; + dnpds40_build_cmd(&cmd, "INFO", "PANORAMA_PRINT", 0); + + resp = dnpds40_resp_cmd(ctx, &cmd, &len); + if (!resp) + return CUPS_BACKEND_FAILED; + + dnpds40_cleanup_string((char*)resp, len); + i = atoi((char*)resp); + free(resp); + if (i != 0) { + INFO("Printer Not ready for panorama printing, waiting.. (%d)\n", i); + sleep(1); + goto top; + } + } + if (job->is_pano) + ctx->pano++; + /* Work around a bug in older gutenprint releases. */ if (ctx->last_multicut >= 200 && ctx->last_multicut < 300 && multicut >= 200 && multicut < 300) { @@ -2511,13 +3085,17 @@ top: /* Store our last multicut state */ ctx->last_multicut = multicut; + /* For continuous panorama jobs we only want to send this stuff on the FIRST page */ + if (ctx->pano > 1 && !job->cutter) + goto skip_pageparams; + /* Tell printer how many copies to make */ - snprintf(buf, sizeof(buf), "%07d\r", manual_copies || ctx->pano ? 1 : copies); + snprintf(buf, sizeof(buf), "%07d\r", manual_copies ? 1 : copies); dnpds40_build_cmd(&cmd, "CNTRL", "QTY", 8); if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8))) return CUPS_BACKEND_FAILED; - if (!manual_copies || ctx->pano) + if (!manual_copies) copies = 1; /* Job resumption on correctable errors */ @@ -2573,13 +3151,16 @@ top: } /* Program in the multicut setting, if one exists */ - if (multicut) { + /* Do _NOT_ send for continuous panoramas! */ + if ((!job->is_pano || job->cutter) && multicut) { snprintf(buf, sizeof(buf), "%08d", multicut); dnpds40_build_cmd(&cmd, "IMAGE", "MULTICUT", 8); if ((ret = dnpds40_do_cmd(ctx, &cmd, (uint8_t*)buf, 8))) return CUPS_BACKEND_FAILED; } +skip_pageparams: + /* Finally, send the stream over as individual data chunks */ ptr = job->databuf; while(ptr && ptr < (job->databuf + job->datalen)) { @@ -2597,7 +3178,7 @@ top: sleep(1); /* Give things a moment */ /* Reset panorama state if we've sent the last panel in the set */ - if (job->cutter != 1000 && ctx->pano) + if (!job->is_pano && ctx->pano) ctx->pano = 0; /* Reset partial matte state if necessary */ @@ -4018,7 +4599,7 @@ static const char *dnpds40_prefixes[] = { const struct dyesub_backend dnpds40_backend = { .name = "DNP DS-series / Citizen C-series", - .version = "0.152", + .version = "0.153", .uri_prefixes = dnpds40_prefixes, .cmdline_usage = dnpds40_cmdline, .cmdline_arg = dnpds40_cmdline_arg, diff --git a/backend_panodata.h b/backend_panodata.h new file mode 100644 index 0000000..fac3a3f --- /dev/null +++ b/backend_panodata.h @@ -0,0 +1,24 @@ +static struct dnp_panodata panodata = { + .elements = 21, + .rows[ 0] = { 0, { 1.0000000000, 1.0000000000, 1.0000000000 }, { 0.2000000000, 0.2000000000, 0.2000000000} }, + .rows[ 1] = { 30, { 0.9933580000, 0.9963060000, 0.9946110000 }, { 0.2950650000, 0.2950650000, 0.2950650000} }, + .rows[ 2] = { 60, { 0.9800000000, 0.9850000000, 0.9850000000 }, { 0.3900000000, 0.3900000000, 0.3900000000} }, + .rows[ 3] = { 90, { 0.9540200000, 0.9602540000, 0.9677210000 }, { 0.4879980000, 0.4879980000, 0.4879980000} }, + .rows[ 4] = { 120, { 0.9182812500, 0.9282812500, 0.9460937500 }, { 0.5800000000, 0.5800000000, 0.5800000000} }, + .rows[ 5] = { 150, { 0.8730900000, 0.8920840000, 0.9206270000 }, { 0.6683700000, 0.6683700000, 0.6683700000} }, + .rows[ 6] = { 180, { 0.8276562500, 0.8543750000, 0.8910937500 }, { 0.7400000000, 0.7400000000, 0.7400000000} }, + .rows[ 7] = { 210, { 0.7800400000, 0.8030000000, 0.8550000000 }, { 0.8004440000, 0.8004440000, 0.8004440000} }, + .rows[ 8] = { 240, { 0.7348437500, 0.7550000000, 0.8160000000 }, { 0.8450000000, 0.8450000000, 0.8450000000} }, + .rows[ 9] = { 270, { 0.6750000000, 0.7020000000, 0.7785180000 }, { 0.8855640000, 0.8855640000, 0.8855640000} }, + .rows[10] = { 300, { 0.6120000000, 0.6450000000, 0.7321875000 }, { 0.9150000000, 0.9150000000, 0.9150000000} }, + .rows[11] = { 330, { 0.5617422160, 0.5950000000, 0.6879300400 }, { 0.9359207970, 0.9359207970, 0.9359207970} }, + .rows[12] = { 360, { 0.5112158120, 0.5430000000, 0.6430933460 }, { 0.9533652920, 0.9533652920, 0.9533652920} }, + .rows[13] = { 390, { 0.4626856430, 0.4970173200, 0.5963030420 }, { 0.9663907840, 0.9663907840, 0.9663907840} }, + .rows[14] = { 420, { 0.4104687500, 0.4482812500, 0.5496875000 }, { 0.9770000000, 0.9770000000, 0.9770000000} }, + .rows[15] = { 450, { 0.3518815000, 0.3938775000, 0.4979980000 }, { 0.9847240000, 0.9847240000, 0.9847240000} }, + .rows[16] = { 480, { 0.2965625000, 0.3413281250, 0.4407812500 }, { 0.9900000000, 0.9900000000, 0.9900000000} }, + .rows[17] = { 510, { 0.2393955000, 0.2762305000, 0.3738025000 }, { 0.9956110000, 0.9956110000, 0.9956110000} }, + .rows[18] = { 540, { 0.1855468750, 0.2163281250, 0.3063281250 }, { 1.0000000000, 1.0000000000, 1.0000000000} }, + .rows[19] = { 570, { 0.1243430000, 0.1441130000, 0.2241130000 }, { 1.0000000000, 1.0000000000, 1.0000000000} }, + .rows[20] = { 600, { 0.0800000000, 0.0800000000, 0.1450000000 }, { 1.0000000000, 1.0000000000, 1.0000000000} }, +}; diff --git a/dnp_data/LUTData820_0010.csv b/dnp_data/LUTData820_0010.csv new file mode 100644 index 0000000..c77deb9 --- /dev/null +++ b/dnp_data/LUTData820_0010.csv @@ -0,0 +1,25 @@ +#FORMAT ,Major,Minor,,,,, +FORMAT,1,0,,,,, +RECNUM,64,,,,,, +#Header,,(Rh)Y, (Rh)M, (Rh)C, (Lh)Y, (Lh)M, (Lh)C +REC,0,1,1,1,0.2,0.2,0.2 +REC,30,0.993358,0.996306,0.994611,0.295065,0.295065,0.295065 +REC,60,0.98,0.985,0.985,0.39,0.39,0.39 +REC,90,0.95402,0.960254,0.967721,0.487998,0.487998,0.487998 +REC,120,0.91828125,0.92828125,0.94609375,0.58,0.58,0.58 +REC,150,0.87309,0.892084,0.920627,0.66837,0.66837,0.66837 +REC,180,0.82765625,0.854375,0.89109375,0.74,0.74,0.74 +REC,210,0.78004,0.803,0.855,0.800444,0.800444,0.800444 +REC,240,0.73484375,0.755,0.816,0.845,0.845,0.845 +REC,270,0.675,0.702,0.778518,0.885564,0.885564,0.885564 +REC,300,0.612,0.645,0.7321875,0.915,0.915,0.915 +REC,330,0.561742216,0.595,0.68793004,0.935920797,0.935920797,0.935920797 +REC,360,0.511215812,0.543,0.643093346,0.953365292,0.953365292,0.953365292 +REC,390,0.462685643,0.49701732,0.596303042,0.966390784,0.966390784,0.966390784 +REC,420,0.41046875,0.44828125,0.5496875,0.977,0.977,0.977 +REC,450,0.3518815,0.3938775,0.497998,0.984724,0.984724,0.984724 +REC,480,0.2965625,0.341328125,0.44078125,0.99,0.99,0.99 +REC,510,0.2393955,0.2762305,0.3738025,0.995611,0.995611,0.995611 +REC,540,0.185546875,0.216328125,0.306328125,1,1,1 +REC,570,0.124343,0.144113,0.224113,1,1,1 +REC,600,0.08,0.08,0.145,1,1,1 diff --git a/dnp_data/LUTData_0010.csv b/dnp_data/LUTData_0010.csv new file mode 100644 index 0000000..c77deb9 --- /dev/null +++ b/dnp_data/LUTData_0010.csv @@ -0,0 +1,25 @@ +#FORMAT ,Major,Minor,,,,, +FORMAT,1,0,,,,, +RECNUM,64,,,,,, +#Header,,(Rh)Y, (Rh)M, (Rh)C, (Lh)Y, (Lh)M, (Lh)C +REC,0,1,1,1,0.2,0.2,0.2 +REC,30,0.993358,0.996306,0.994611,0.295065,0.295065,0.295065 +REC,60,0.98,0.985,0.985,0.39,0.39,0.39 +REC,90,0.95402,0.960254,0.967721,0.487998,0.487998,0.487998 +REC,120,0.91828125,0.92828125,0.94609375,0.58,0.58,0.58 +REC,150,0.87309,0.892084,0.920627,0.66837,0.66837,0.66837 +REC,180,0.82765625,0.854375,0.89109375,0.74,0.74,0.74 +REC,210,0.78004,0.803,0.855,0.800444,0.800444,0.800444 +REC,240,0.73484375,0.755,0.816,0.845,0.845,0.845 +REC,270,0.675,0.702,0.778518,0.885564,0.885564,0.885564 +REC,300,0.612,0.645,0.7321875,0.915,0.915,0.915 +REC,330,0.561742216,0.595,0.68793004,0.935920797,0.935920797,0.935920797 +REC,360,0.511215812,0.543,0.643093346,0.953365292,0.953365292,0.953365292 +REC,390,0.462685643,0.49701732,0.596303042,0.966390784,0.966390784,0.966390784 +REC,420,0.41046875,0.44828125,0.5496875,0.977,0.977,0.977 +REC,450,0.3518815,0.3938775,0.497998,0.984724,0.984724,0.984724 +REC,480,0.2965625,0.341328125,0.44078125,0.99,0.99,0.99 +REC,510,0.2393955,0.2762305,0.3738025,0.995611,0.995611,0.995611 +REC,540,0.185546875,0.216328125,0.306328125,1,1,1 +REC,570,0.124343,0.144113,0.224113,1,1,1 +REC,600,0.08,0.08,0.145,1,1,1 diff --git a/pano-split.sh b/pano-split.sh index 8cc389c..c314694 100755 --- a/pano-split.sh +++ b/pano-split.sh @@ -23,7 +23,8 @@ fi # D90 case "${model}" in kodak-6900|kodak-6950) - overlap=636 # 100 + 36 + pad=36 + overlap=600 if [ "${printsize}" eq "6x20" ] ; then cols=1844 inrows=6036 @@ -59,6 +60,7 @@ case "${model}" in ;; kodak-8810) cols=2464 + pad=24 overlap=360 ### XXX probably wrong? if [ "${printsize}" eq "8x14" ] ; then inrows=4224 @@ -85,8 +87,9 @@ case "${model}" in ;; mitsubishi-d90dw) cols=1852 + pad=28 drows=2428 - overlap=628 # 600 + 28 + overlap=600 if [ "${printsize}" eq "6x20" ] ; then inrows=6028 elif [ "${printsize}" eq "6x14" ] ; then @@ -99,11 +102,12 @@ case "${model}" in dnp-ds620) cols=1844 drows=2436 + pad=36 overlap=600 if [ "${printsize}" eq "6x20" ] ; then - inrows=6108 + inrows=6036 elif [ "${printsize}" eq "6x14" ] ; then - inrows=4272 + inrows=4236 else echo "DS620 supportx 6x20 and 6x14 only" exit 1 @@ -111,21 +115,28 @@ case "${model}" in ;; dnp-ds820) overlap=600 + pad=36 cols=2448 if [ "${printsize}" eq "8x18" ] ; then drows=3036 - inrows=5472 + inrows=5436 elif [ "${printsize}" eq "8x26" ] ; then drows=3036 - inrows=7908 + inrows=7836 elif [ "${printsize}" eq "8x22" ] ; then drows=3636 - inrows=6672 + inrows=6636 elif [ "${printsize}" eq "8x32" ] ; then drows=3636 - inrows=9708 + 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, and 8x32 only" + echo "DS820 supportx 8x18, 8x26, 8x22, 8x32, A4x21, A4x31 only" fi ;; *) @@ -135,11 +146,12 @@ case "${model}" in esac # For 600dpi, double everything! -if [ ${dpi} == 600 ] ; then +if [ ${dpi} -gt 300 ] ; then overlap=`expr ${overlap} * 2` cols=`expr ${cols} * 2` drows=`expr ${drows} * 2` inrows=`expr ${inrows} * 2` + pad=`expr ${pad} * 2` fi # Calculate page count XXX is this sane? @@ -153,8 +165,11 @@ fadeoutname=fade_out_${cols}.pgm gm convert -size ${cols}x${overlap} gradient:white-black ${fadeinname} gm convert -size ${cols}x${overlap} gradient:black-white ${fadeoutname} +# Subtract off padding from rows. +drows=`expr ${drows} - ${pad} - ${pad}` + # Generate panels -src_offset=0 +src_offset=`expr $pad / 2` panels='' for panel in `seq 1 ${pages}` ; do echo "generating $panel / $pages" @@ -163,34 +178,26 @@ for panel in `seq 1 ${pages}` ; do panels="${panels} ${panelname}" # Generate panel - gm convert ${infile} -crop ${cols}x${drows}+0+${src_offset} ${panelname} + rows=`expr ${drows} + ${pad} + ${pad}` + gm convert ${infile} -crop ${cols}x${rows}+0+${src_offset} ${panelname} + + ## XXX panel needs to have WHITE margin of ${pad rows} on either end + # perhaps add to fades? + # need a tail too. # Increment offset src_offset=`expr ${src_offset} + ${drows}`; src_offset=`expr ${src_offset} - ${overlap}`; - # First page fades out only - if [ ${panel} == 1 ] ; then + # All but last panel fades out + if [ ${panel} -lt ${pages} ] ; then gm composite -compose screen -gravity south ${fadeoutname} ${panelname} ${panelname} fi - # Middle pages fade in and out + # All but first panel fades in if [ ${panel} -gt 1 ] ; then - if [ ${panel} -lt ${pages} ] ; then - gm composite -compose screen -gravity south ${fadeoutname} ${panelname} ${panelname} - fi - fi - if [ ${panel} -gt 1 ] ; then - if [ ${panel} -lt ${pages} ] ; then - gm composite -compose screen -gravity north ${fadeinname} ${panelname} ${panelname} - fi - fi - - # Last page fades in only - if [ ${panel} == ${pages} ] ; then gm composite -compose screen -gravity north ${fadeinname} ${panelname} ${panelname} fi - done # Clean up templates