summaryrefslogtreecommitdiff
path: root/src/main/print-olympus.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/print-olympus.c')
-rw-r--r--src/main/print-olympus.c786
1 files changed, 629 insertions, 157 deletions
diff --git a/src/main/print-olympus.c b/src/main/print-olympus.c
index 59c8d0c..95eb64c 100644
--- a/src/main/print-olympus.c
+++ b/src/main/print-olympus.c
@@ -1,9 +1,10 @@
/*
*
- * Print plug-in DyeSub driver (formerly Olympus driver) for the GIMP.
+ * Print plug-in DyeSub driver (formerly Olympus driver) for Gutenprint
*
- * Copyright 2003 - 2006
- * Michael Mraka (Michael.Mraka@linux.cz)
+ * Copyright 2003-2006 Michael Mraka (Michael.Mraka@linux.cz)
+ *
+ * Copyright 2007-2017 Solomon Peachy (pizza@shaftnet.org)
*
* The plug-in is based on the code of the RAW plugin for the GIMP of
* Michael Sweet (mike@easysw.com) and Robert Krawitz (rlk@alum.mit.edu)
@@ -41,6 +42,10 @@
#define inline __inline__
#endif
+#define MITSU70X_8BPP
+//#define S6145_YMC
+//#define CANONSELPHYNEO_CMY
+
#define DYESUB_FEATURE_NONE 0x00000000
#define DYESUB_FEATURE_FULL_WIDTH 0x00000001
#define DYESUB_FEATURE_FULL_HEIGHT 0x00000002
@@ -53,15 +58,15 @@
#define DYESUB_FEATURE_12BPP 0x00000100
#define DYESUB_FEATURE_16BPP 0x00000200
#define DYESUB_FEATURE_BIGENDIAN 0x00000400
-#define DYESUB_FEATURE_RGBtoYCBCR 0x00000800
-#define DYESUB_FEATURE_DUPLEX 0x00001000
-#define DYESUB_FEATURE_MONOCHROME 0x00002000 /* Monochrome only..? */
+#define DYESUB_FEATURE_DUPLEX 0x00000800
+#define DYESUB_FEATURE_MONOCHROME 0x00001000
+#ifndef CANONSELPHYNEO_CMY
+#define DYESUB_FEATURE_RGBtoYCBCR 0x00002000
+#endif
#define DYESUB_PORTRAIT 0
#define DYESUB_LANDSCAPE 1
-#define MITSU70X_8BPP
-
#ifndef MIN
# define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif /* !MIN */
@@ -221,7 +226,10 @@ typedef struct
int gamma;
int flags;
int comment;
- char usercomment[34];
+ int contrast;
+ int sharpen;
+ int brightness;
+ char usercomment[40];
char commentbuf[19]; /* With one extra byte for null termination */
} mitsu_p95d_privdata_t;
@@ -279,7 +287,7 @@ typedef struct /* printer specific parameters */
const dyesub_resolution_list_t *resolution;
const dyesub_pagesize_list_t *pages;
const dyesub_printsize_list_t *printsize;
- int block_size;
+ int block_size; /* Really # of rows in a block */
int features;
void (*printer_init_func)(stp_vars_t *);
void (*printer_end_func)(stp_vars_t *);
@@ -1149,7 +1157,7 @@ static const dyesub_pagesize_t cp910_page[] =
{
{ "Postcard", "Postcard 100x148mm", PT(1248,300)+1, PT(1872,300)+1, 13, 13, 16, 19, DYESUB_PORTRAIT},
{ "w253h337", "CP_L 89x119mm", PT(1152,300)+1, PT(1472,300)+1, 13, 13, 15, 15, DYESUB_PORTRAIT},
- { "w155h244", "Card 54x86mm", PT(1088,300)+1, PT(668,300)+1, 13, 13, 15, 15, DYESUB_LANDSCAPE},
+ { "w155h244", "Card 54x86mm", PT(668,300)+1, PT(1088,300)+1, 13, 13, 15, 15, DYESUB_LANDSCAPE},
};
LIST(dyesub_pagesize_list_t, cp910_page_list, dyesub_pagesize_t, cp910_page);
@@ -1180,7 +1188,12 @@ static void cp910_printer_init_func(stp_vars_t *v)
0x50 )));
stp_putc(pg, v);
- dyesub_nputc(v, '\0', 5);
+ dyesub_nputc(v, '\0', 4);
+#ifdef CANONSELPHYNEO_CMY
+ stp_putc(0x01, v);
+#else
+ stp_putc(0x00, v);
+#endif
stp_put32_le(pd->w_size, v);
stp_put32_le(pd->h_size, v);
@@ -1981,7 +1994,7 @@ static const dyesub_pagesize_t kodak_1400_page[] =
*/
{ "w612h864", "8.5x12", PT(2560,301)+1, PT(3010,301)+72*2, PT(76,301)+1, PT(76,301), 72, 72, DYESUB_PORTRAIT}, /* 8x12 */
{ "Legal", "8.5x14", PT(2560,301)+1, PT(3612,301)+72*2, PT(35,301)+1, PT(35,301)+1, 72, 72, DYESUB_PORTRAIT}, /* 8x14 */
- { "A4", "A4", PT(2560,301)+1, PT(3010,301)+72*2, PT(76,301)+1, PT(76,301), 0, 0, DYESUB_PORTRAIT}, /* A4, indentical to 8x12 */
+ { "A4", "A4", PT(2560,301)+1, PT(3010,301)+72*2, PT(76,301)+1, PT(76,301), 0, 0, DYESUB_PORTRAIT}, /* A4, identical to 8x12 */
};
LIST(dyesub_pagesize_list_t, kodak_1400_page_list, dyesub_pagesize_t, kodak_1400_page);
@@ -2481,7 +2494,7 @@ static const stp_parameter_t kodak_8500_parameters[] =
},
{
"MatteIntensity", N_("Matte Intensity"), "Color=No,Category=Advanced Printer Setup",
- N_("Strengh of matte lamination pattern (-5 through +5)"),
+ N_("Strength of matte lamination pattern (-5 through +5)"),
STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
},
@@ -2893,7 +2906,10 @@ static int mitsu_p95d_parse_parameters(stp_vars_t *v)
if (pd->copies > 200)
pd->copies = 200;
-
+
+ pd->privdata.m95d.brightness = stp_get_int_parameter(v, "P95Brightness");
+ pd->privdata.m95d.contrast = stp_get_int_parameter(v, "P95Contrast");
+
if (!strcmp(gamma, "Printer")) {
pd->privdata.m95d.gamma = 0x00;
} else if (!strcmp(gamma, "T1")) {
@@ -3051,7 +3067,10 @@ static void mitsu_p95d_printer_init(stp_vars_t *v)
stp_putc(0x00, v);
dyesub_nputc(v, 0x00, 5);
stp_putc(pd->privdata.m95d.gamma, v);
- dyesub_nputc(v, 0x00, 3);
+ stp_putc(pd->privdata.m95d.brightness, v);
+ stp_putc(pd->privdata.m95d.contrast, v);
+ stp_putc(0x00, v);
+
if (pd->privdata.m95d.gamma == 0x10) {
stp_zfwrite(p95d_lut, 1, sizeof(p95d_lut), v); /* XXX only for K95HG? */
} else {
@@ -3086,6 +3105,363 @@ static void mitsu_p95d_printer_end(stp_vars_t *v)
stp_putc(0x50, v);
}
+/* Mitsubishi P93D/DW */
+
+static const dyesub_media_t mitsu_p93d_medias[] =
+{
+ {"Standard", N_("Standard (KP61B)"), {1, "\x02"}},
+ {"HighDensity", N_("High Density (KP65HM)"), {1, "\x00"}},
+ {"HighGlossy", N_("High Glossy (KP91HG)"), {1, "\x01"}},
+};
+
+LIST(dyesub_media_list_t, mitsu_p93d_media_list, dyesub_media_t, mitsu_p93d_medias);
+
+static const dyesub_stringitem_t mitsu_p93d_gammas[] =
+{
+ { "T1", N_ ("Table 1") },
+ { "T2", N_ ("Table 2") },
+ { "T3", N_ ("Table 3") },
+ { "T4", N_ ("Table 4") },
+ { "T5", N_ ("Table 5") },
+};
+LIST(dyesub_stringlist_t, mitsu_p93d_gamma_list, dyesub_stringitem_t, mitsu_p93d_gammas);
+
+static const stp_parameter_t mitsu_p93d_parameters[] =
+{
+ {
+ "P93Gamma", N_("Printer Gamma Correction"), "Color=No,Category=Advanced Printer Setup",
+ N_("Printer Gamma Correction"),
+ STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
+ STP_PARAMETER_LEVEL_ADVANCED, 1, 1, STP_CHANNEL_NONE, 1, 0
+ },
+ {
+ "Buzzer", N_("Printer Buzzer"), "Color=No,Category=Advanced Printer Setup",
+ N_("Printer Buzzer"),
+ STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
+ STP_PARAMETER_LEVEL_ADVANCED, 1, 1, STP_CHANNEL_NONE, 1, 0
+ },
+ {
+ "PaperSaving", N_("Paper Saving Mode"), "Color=Yes,Category=Advanced Printer Setup",
+ N_("Paper Saving Mode"),
+ STP_PARAMETER_TYPE_BOOLEAN, STP_PARAMETER_CLASS_FEATURE,
+ STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
+ },
+ {
+ "Comment", N_("Generate Comment"), "Color=No,Category=Advanced Printer Setup",
+ N_("Generate Comment"),
+ STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
+ STP_PARAMETER_LEVEL_ADVANCED, 1, 1, STP_CHANNEL_NONE, 1, 0
+ },
+ {
+ "ClearMemory", N_("Clear Memory"), "Color=No,Category=Advanced Printer Setup",
+ N_("Clear Memory"),
+ STP_PARAMETER_TYPE_BOOLEAN, STP_PARAMETER_CLASS_FEATURE,
+ STP_PARAMETER_LEVEL_ADVANCED, 1, 1, STP_CHANNEL_NONE, 1, 0
+ },
+ {
+ "ContinuousPrint", N_("Continuous Printing"), "Color=No,Category=Advanced Printer Setup",
+ N_("Continuous Printing"),
+ STP_PARAMETER_TYPE_BOOLEAN, STP_PARAMETER_CLASS_FEATURE,
+ STP_PARAMETER_LEVEL_ADVANCED, 1, 1, STP_CHANNEL_NONE, 1, 0
+ },
+ {
+ "P93Brightness", N_("Brightness"), "Color=No,Category=Advanced Printer Setup",
+ N_("Printer Brightness Adjustment"),
+ STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
+ STP_PARAMETER_LEVEL_ADVANCED, 1, 1, STP_CHANNEL_NONE, 1, 0
+ },
+ {
+ "P93Contrast", N_("Contrast"), "Color=No,Category=Advanced Printer Setup",
+ N_("Printer Contrast Adjustment"),
+ STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
+ STP_PARAMETER_LEVEL_ADVANCED, 1, 1, STP_CHANNEL_NONE, 1, 0
+ },
+ {
+ "Sharpen", N_("Image Sharpening"), "Color=No,Category=Advanced Printer Setup",
+ N_("Sharpening to apply to image (1 is soft, 1 is normal, 2 is hard"),
+ STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
+ STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
+ },
+ {
+ "UserComment", N_("User Comment"), "Color=No,Category=Advanced Printer Setup",
+ N_("User-specified comment (0-40 characters from 0x20->0x7E), null terminated if under 40 characters long"),
+ STP_PARAMETER_TYPE_RAW, STP_PARAMETER_CLASS_FEATURE,
+ STP_PARAMETER_LEVEL_ADVANCED, 0, 1, STP_CHANNEL_NONE, 1, 0
+ },
+};
+#define mitsu_p93d_parameter_count (sizeof(mitsu_p93d_parameters) / sizeof(const stp_parameter_t))
+
+static int
+mitsu_p93d_load_parameters(const stp_vars_t *v, const char *name,
+ stp_parameter_t *description)
+{
+ int i;
+ const dyesub_cap_t *caps = dyesub_get_model_capabilities(
+ stp_get_model_id(v));
+
+ if (caps->parameter_count && caps->parameters)
+ {
+ for (i = 0; i < caps->parameter_count; i++)
+ if (strcmp(name, caps->parameters[i].name) == 0)
+ {
+ stp_fill_parameter_settings(description, &(caps->parameters[i]));
+ break;
+ }
+ }
+
+ if (strcmp(name, "P93Gamma") == 0)
+ {
+ description->bounds.str = stp_string_list_create();
+
+ const dyesub_stringlist_t *mlist = &mitsu_p93d_gamma_list;
+ for (i = 0; i < mlist->n_items; i++)
+ {
+ const dyesub_stringitem_t *m = &(mlist->item[i]);
+ stp_string_list_add_string(description->bounds.str,
+ m->name, m->text); /* Do *not* want this translated, otherwise use gettext(m->text) */
+ }
+ description->deflt.str = stp_string_list_param(description->bounds.str, 0)->name;
+ description->is_active = 1;
+ } else if (strcmp(name, "Buzzer") == 0) {
+ description->bounds.str = stp_string_list_create();
+
+ const dyesub_stringlist_t *mlist = &mitsu_p95d_buzzer_list;
+ for (i = 0; i < mlist->n_items; i++)
+ {
+ const dyesub_stringitem_t *m = &(mlist->item[i]);
+ stp_string_list_add_string(description->bounds.str,
+ m->name, m->text); /* Do *not* want this translated, otherwise use gettext(m->text) */
+ }
+ description->deflt.str = stp_string_list_param(description->bounds.str, 2)->name;
+ description->is_active = 1;
+ } else if (strcmp(name, "PaperSaving") == 0) {
+ description->deflt.boolean = 0;
+ description->is_active = 1;
+ } else if (strcmp(name, "Comment") == 0) {
+ description->bounds.str = stp_string_list_create();
+
+ const dyesub_stringlist_t *mlist = &mitsu_p95d_comment_list;
+ for (i = 0; i < mlist->n_items; i++)
+ {
+ const dyesub_stringitem_t *m = &(mlist->item[i]);
+ stp_string_list_add_string(description->bounds.str,
+ m->name, m->text); /* Do *not* want this translated, otherwise use gettext(m->text) */
+ }
+ description->deflt.str = stp_string_list_param(description->bounds.str, 0)->name;
+ description->is_active = 1;
+ } else if (strcmp(name, "ClearMemory") == 0) {
+ description->is_active = 1;
+ description->deflt.boolean = 0;
+ } else if (strcmp(name, "ContinuousPrint") == 0) {
+ description->is_active = 1;
+ description->deflt.boolean = 0;
+ } else if (strcmp(name, "P93Brightness") == 0) {
+ description->deflt.integer = 0;
+ description->bounds.integer.lower = -127;
+ description->bounds.integer.upper = 127;
+ description->is_active = 1;
+ } else if (strcmp(name, "P93Contrast") == 0) {
+ description->deflt.integer = 0;
+ description->bounds.integer.lower = -127;
+ description->bounds.integer.upper = 127;
+ description->is_active = 1;
+ } else if (strcmp(name, "Sharpen") == 0) {
+ description->deflt.integer = 1;
+ description->bounds.integer.lower = 0;
+ description->bounds.integer.upper = 2;
+ description->is_active = 1;
+ } else if (strcmp(name, "UserComment") == 0) {
+ description->is_active = 1;
+ }
+ else
+ {
+ return 0;
+ }
+ return 1;
+}
+
+static int mitsu_p93d_parse_parameters(stp_vars_t *v)
+{
+ dyesub_privdata_t *pd = get_privdata(v);
+ const char *gamma = stp_get_string_parameter(v, "P93Gamma");
+ const char *buzzer = stp_get_string_parameter(v, "Buzzer");
+ const char *comment = stp_get_string_parameter(v, "Comment");
+ const stp_raw_t *usercomment = NULL;
+
+ /* Sanity check */
+ if (stp_check_raw_parameter(v, "UserComment", STP_PARAMETER_ACTIVE)) {
+ usercomment = stp_get_raw_parameter(v, "UserComment");
+ if (usercomment->bytes > 40) {
+ stp_eprintf(v, _("StpUserComment must be between 0 and 40 bytes!\n"));
+ return 0;
+ }
+ }
+
+ /* No need to set global params if there's no privdata yet */
+ if (!pd)
+ return 1;
+
+ /* Parse options */
+ pd->privdata.m95d.clear_mem = stp_get_boolean_parameter(v, "ClearMemory");
+ pd->privdata.m95d.cont_print = stp_get_boolean_parameter(v, "ContinuousPrint");
+
+ if (pd->copies > 200)
+ pd->copies = 200;
+
+ if (!strcmp(gamma, "T1")) {
+ pd->privdata.m95d.gamma = 0x00;
+ } else if (!strcmp(gamma, "T2")) {
+ pd->privdata.m95d.gamma = 0x01;
+ } else if (!strcmp(gamma, "T3")) {
+ pd->privdata.m95d.gamma = 0x02;
+ } else if (!strcmp(gamma, "T4")) {
+ pd->privdata.m95d.gamma = 0x03;
+ } else if (!strcmp(gamma, "T5")) {
+ pd->privdata.m95d.gamma = 0x04;
+ }
+
+ if (!strcmp(buzzer, "Off")) {
+ pd->privdata.m95d.flags |= 0x00;
+ } else if (!strcmp(buzzer, "Low")) {
+ pd->privdata.m95d.flags |= 0x02;
+ } else if (!strcmp(buzzer, "High")) {
+ pd->privdata.m95d.flags |= 0x03;
+ }
+
+ pd->privdata.m95d.brightness = stp_get_int_parameter(v, "P93Brightness");
+ pd->privdata.m95d.contrast = stp_get_int_parameter(v, "P93Contrast");
+ pd->privdata.m95d.sharpen = stp_get_int_parameter(v, "Sharpen");
+
+ if (stp_get_boolean_parameter(v, "PaperSaving")) {
+ pd->privdata.m95d.flags |= 0x04;
+ }
+
+ if (!strcmp(comment, "Off")) {
+ memset(pd->privdata.m95d.commentbuf, 0, sizeof(pd->privdata.m95d.commentbuf));
+ pd->privdata.m95d.comment = 0;
+ } else if (!strcmp(comment, "Settings")) {
+ memset(pd->privdata.m95d.commentbuf, 0, sizeof(pd->privdata.m95d.commentbuf));
+ pd->privdata.m95d.comment = 1;
+ } else if (!strcmp(comment, "Date")) {
+ struct tm tmp;
+ time_t t;
+ t = time(NULL);
+ localtime_r(&t, &tmp);
+ strftime(pd->privdata.m95d.commentbuf, sizeof(pd->privdata.m95d.commentbuf), " %F", &tmp);
+ pd->privdata.m95d.comment = 2;
+ } else if (!strcmp(comment, "DateTime")) {
+ struct tm tmp;
+ time_t t;
+ t = time(NULL);
+ localtime_r(&t, &tmp);
+ strftime(pd->privdata.m95d.commentbuf, sizeof(pd->privdata.m95d.commentbuf), " %F %R", &tmp);
+ pd->privdata.m95d.comment = 3;
+ }
+
+ if (usercomment) {
+ if (strncmp("None", usercomment->data, usercomment->bytes)) {
+ int i;
+ memcpy(pd->privdata.m95d.usercomment, usercomment->data, usercomment->bytes);
+ if (usercomment->bytes < 40)
+ pd->privdata.m95d.usercomment[usercomment->bytes] = 0;
+ for (i = 0 ; i < usercomment->bytes ; i++) {
+ if (pd->privdata.m95d.usercomment[i] < 0x20 ||
+ pd->privdata.m95d.usercomment[i] > 0x7F)
+ pd->privdata.m95d.usercomment[i] = 0x20;
+ }
+ }
+ } else {
+ memset(pd->privdata.m95d.usercomment, 0x20, sizeof(pd->privdata.m95d.usercomment));
+ }
+
+ return 1;
+}
+
+static void mitsu_p93d_printer_init(stp_vars_t *v)
+{
+ dyesub_privdata_t *pd = get_privdata(v);
+
+ /* Header */
+ stp_putc(0x1b, v);
+ stp_putc(0x51, v);
+
+ /* Clear memory */
+ if (pd->privdata.m95d.clear_mem) {
+ stp_putc(0x1b, v);
+ stp_putc(0x5a, v);
+ stp_putc(0x43, v);
+ stp_putc(0x00, v);
+ }
+
+ /* Page Setup */
+ stp_putc(0x1b, v);
+ stp_putc(0x57, v);
+ stp_putc(0x20, v);
+ stp_putc(0x2e, v);
+ stp_putc(0x00, v);
+ stp_putc(0x0a, v);
+ dyesub_nputc(v, 0x00, 8);
+ stp_put16_be(pd->w_size, v); /* Columns */
+ stp_put16_be(pd->h_size, v); /* Rows */
+
+ /* This is only set under Windows if a "custom" size is selected,
+ but the USB comms always show it set to 1... */
+ if (!strcmp(pd->pagesize,"Custom"))
+ stp_putc(0x01, v);
+ else
+ stp_putc(0x00, v);
+ dyesub_nputc(v, 0x00, 31);
+
+ /* Print Options */
+ stp_putc(0x1b, v);
+ stp_putc(0x57, v);
+ stp_putc(0x21, v);
+ stp_putc(0x2e, v);
+ stp_putc(0x00, v);
+ stp_putc(0x4a, v);
+ stp_putc(0xaa, v);
+ stp_putc(0x00, v);
+ stp_putc(0x00, v);
+ stp_zfwrite((pd->media->seq).data, 1, 1, v); /* Media Type */
+ stp_putc(0x00, v);
+ stp_putc(0x00, v);
+ stp_putc(0x00, v);
+ if (pd->privdata.m95d.cont_print)
+ stp_putc(0xff, v);
+ else
+ stp_putc(pd->copies, v);
+ stp_putc(0x00, v);
+ stp_putc(pd->privdata.m95d.comment, v);
+ stp_zfwrite(pd->privdata.m95d.commentbuf, 1, sizeof(pd->privdata.m95d.commentbuf) -1, v);
+ dyesub_nputc(v, 0x00, 3);
+ stp_putc(0x02, v);
+ dyesub_nputc(v, 0x00, 11);
+ stp_putc(pd->privdata.m95d.flags, v);
+
+ /* Gamma */
+ stp_putc(0x1b, v);
+ stp_putc(0x57, v);
+ stp_putc(0x22, v);
+ stp_putc(0x2e, v);
+ stp_putc(0x00, v);
+ stp_putc(0xd5, v);
+ dyesub_nputc(v, 0x00, 6);
+
+ stp_putc(pd->privdata.m95d.sharpen, v); // XXX
+ stp_putc(0x00, v);
+ stp_putc(pd->privdata.m95d.gamma, v);
+ stp_putc(0x00, v);
+ stp_putc(pd->privdata.m95d.brightness, v);
+ stp_putc(0x00, v);
+ stp_putc(pd->privdata.m95d.contrast, v);
+ dyesub_nputc(v, 0x00, 31);
+
+ /* User Comment */
+ stp_putc(0x1b, v);
+ stp_putc(0x58, v);
+ stp_zfwrite(pd->privdata.m95d.usercomment, 1, sizeof(pd->privdata.m95d.usercomment), v);
+}
+
/* Mitsubishi CP3020D/DU/DE */
static const dyesub_pagesize_t mitsu_cp3020d_page[] =
{
@@ -4243,10 +4619,11 @@ static void mitsu_cpd70k60_printer_init(stp_vars_t *v, unsigned char model)
stp_putc(pd->privdata.m70x.sharpen, v);
stp_putc(0x01, v); /* Mark as 8bpp BGR rather than 16bpp YMC cooked */
stp_putc(pd->privdata.m70x.use_lut, v); /* Use LUT? */
+ stp_putc(0x01, v); /* Tell the backend the data's in the proper order */
#else
- dyesub_nputc(v, 0x00, 15);
+ dyesub_nputc(v, 0x00, 16);
#endif
- dyesub_nputc(v, 0x00, 448); /* Pad to 512-byte block */
+ dyesub_nputc(v, 0x00, 447); /* Pad to 512-byte block */
}
static void mitsu_cpd70x_printer_init(stp_vars_t *v)
@@ -4994,7 +5371,7 @@ static const stp_parameter_t shinko_chcs1245_parameters[] =
},
{
"MatteIntensity", N_("Matte Intensity"), "Color=No,Category=Advanced Printer Setup",
- N_("Strengh of matte lamination pattern (-25 through +25)"),
+ N_("Strength of matte lamination pattern (-25 through +25)"),
STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
},
@@ -5381,7 +5758,11 @@ static void shinko_chcs6145_printer_init(stp_vars_t *v)
stp_put32_le(0x00, v);
stp_put32_le(0x00, v);
+#ifdef S6145_YMC
+ stp_put32_le(0x01, v);
+#else
stp_put32_le(0x00, v);
+#endif
}
/* Ciaat Brava 21 */
@@ -6691,20 +7072,34 @@ static const dyesub_cap_t dyesub_model_capabilities[] =
NULL, NULL,
NULL, 0, NULL, NULL,
},
- { /* Canon CP820, CP910 */
+ { /* Canon CP820, CP910, CP1000, CP1200 */
1011,
+#ifdef CANONSELPHYNEO_CMY
+ &cmy_ink_list,
+#else
&rgb_ink_list,
+#endif
&res_300dpi_list,
&cp910_page_list,
&cp910_printsize_list,
SHRT_MAX,
+#ifdef CANONSELPHYNEO_CMY
+ DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT
+ | DYESUB_FEATURE_BORDERLESS | DYESUB_FEATURE_WHITE_BORDER
+ | DYESUB_FEATURE_PLANE_INTERLACE,
+#else
DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT
| DYESUB_FEATURE_BORDERLESS | DYESUB_FEATURE_WHITE_BORDER
| DYESUB_FEATURE_PLANE_INTERLACE | DYESUB_FEATURE_RGBtoYCBCR,
+#endif
&cp910_printer_init_func, NULL,
NULL, NULL,
NULL, NULL,
+#ifdef CANONSELPHYNEO_CMY
+ cpx00_adjust_curves,
+#else
NULL,
+#endif
NULL, NULL,
NULL, NULL,
NULL, 0, NULL, NULL,
@@ -7135,7 +7530,7 @@ static const dyesub_cap_t dyesub_model_capabilities[] =
&mitsu_cpd70x_printsize_list,
SHRT_MAX,
#ifdef MITSU70X_8BPP
- DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT,
+ DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT | DYESUB_FEATURE_PLANE_LEFTTORIGHT,
&mitsu_cpd70x_printer_init, NULL,
#else
DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT
@@ -7165,7 +7560,7 @@ static const dyesub_cap_t dyesub_model_capabilities[] =
&mitsu_cpk60_printsize_list,
SHRT_MAX,
#ifdef MITSU70X_8BPP
- DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT,
+ DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT | DYESUB_FEATURE_PLANE_LEFTTORIGHT,
&mitsu_cpk60_printer_init, NULL,
#else
DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT
@@ -7195,7 +7590,7 @@ static const dyesub_cap_t dyesub_model_capabilities[] =
&mitsu_cpd80_printsize_list,
SHRT_MAX,
#ifdef MITSU70X_8BPP
- DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT,
+ DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT | DYESUB_FEATURE_PLANE_LEFTTORIGHT,
&mitsu_cpd70x_printer_init, NULL,
#else
DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT
@@ -7225,7 +7620,7 @@ static const dyesub_cap_t dyesub_model_capabilities[] =
&kodak305_printsize_list,
SHRT_MAX,
#ifdef MITSU70X_8BPP
- DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT,
+ DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT | DYESUB_FEATURE_PLANE_LEFTTORIGHT,
&kodak305_printer_init, NULL,
#else
DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT
@@ -7250,7 +7645,7 @@ static const dyesub_cap_t dyesub_model_capabilities[] =
&mitsu_cpd90_page_list,
&mitsu_cpd90_printsize_list,
SHRT_MAX,
- DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT,
+ DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT | DYESUB_FEATURE_PLANE_LEFTTORIGHT,
&mitsu_cpd90_printer_init, &mitsu_cpd90_printer_end,
NULL, NULL,
NULL, NULL, /* No block funcs */
@@ -7311,7 +7706,7 @@ static const dyesub_cap_t dyesub_model_capabilities[] =
&fuji_ask300_printsize_list,
SHRT_MAX,
#ifdef MITSU70X_8BPP
- DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT,
+ DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT | DYESUB_FEATURE_PLANE_LEFTTORIGHT,
&mitsu_cpd70x_printer_init, NULL,
#else
DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT
@@ -7390,6 +7785,26 @@ static const dyesub_cap_t dyesub_model_capabilities[] =
mitsu9500_load_parameters,
mitsu9500_parse_parameters,
},
+ { /* Mitsubishi P93D/DW */
+ 4116,
+ &w_ink_list,
+ &res_325dpi_list,
+ &mitsu_p95d_page_list,
+ &mitsu_p95d_printsize_list,
+ SHRT_MAX,
+ DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT
+ | DYESUB_FEATURE_MONOCHROME,
+ &mitsu_p93d_printer_init, &mitsu_p95d_printer_end,
+ &mitsu_p95d_plane_start, NULL,
+ NULL, NULL, /* No block funcs */
+ NULL,
+ NULL, &mitsu_p93d_media_list,
+ NULL, NULL,
+ mitsu_p93d_parameters,
+ mitsu_p93d_parameter_count,
+ mitsu_p93d_load_parameters,
+ mitsu_p93d_parse_parameters,
+ },
{ /* Shinko CHC-S9045 (experimental) */
5000,
&rgb_ink_list,
@@ -7459,11 +7874,18 @@ static const dyesub_cap_t dyesub_model_capabilities[] =
},
{ /* Shinko/Sinfonia CHC-S6145 */
5004,
+#ifdef S6145_YMC
+ &ymc_ink_list,
+#else
&rgb_ink_list,
+#endif
&res_300dpi_list,
&shinko_chcs6145_page_list,
&shinko_chcs6145_printsize_list,
SHRT_MAX,
+#ifdef S6145_YMC
+ DYESUB_FEATURE_PLANE_INTERLACE |
+#endif
DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT,
&shinko_chcs6145_printer_init, &shinko_chcs2145_printer_end,
NULL, NULL, /* No planes */
@@ -7475,11 +7897,18 @@ static const dyesub_cap_t dyesub_model_capabilities[] =
},
{ /* CIAAT Brava 21 (aka CHC-S6145D) */
5005,
+#ifdef S6145_YMC
+ &ymc_ink_list,
+#else
&rgb_ink_list,
+#endif
&res_300dpi_list,
&ciaat_brava21_page_list,
&ciaat_brava21_printsize_list,
SHRT_MAX,
+#ifdef S6145_YMC
+ DYESUB_FEATURE_PLANE_INTERLACE |
+#endif
DYESUB_FEATURE_FULL_WIDTH | DYESUB_FEATURE_FULL_HEIGHT,
&shinko_chcs6145_printer_init, &shinko_chcs2145_printer_end,
NULL, NULL, /* No planes */
@@ -8232,7 +8661,15 @@ static void
dyesub_swap_ints(int *a, int *b)
{
int t = *a;
- *a = *b;
+ *a = *b;
+ *b = t;
+}
+
+static void
+dyesub_swap_doubles(double *a, double *b)
+{
+ double t = *a;
+ *a = *b;
*b = t;
}
@@ -8278,15 +8715,12 @@ dyesub_exec_check(stp_vars_t *v,
return 1;
}
-
-static int
+/* FIXME: This function is badly named. It actually picks the best single
+ "point" on the original image to use for the desired output pixel. */
+static double
dyesub_interpolate(int oldval, int oldsize, int newsize)
{
- /*
- * This is simple linear interpolation algorithm.
- * When imagesize <> printsize I need rescale image somehow... :-/
- */
- return (int)(oldval * newsize / oldsize);
+ return ((double)oldval * (double)newsize / (double)oldsize);
}
static void
@@ -8345,121 +8779,127 @@ dyesub_read_image(stp_vars_t *v,
return image_data;
}
-static int
-dyesub_print_pixel(stp_vars_t *v,
- dyesub_print_vars_t *pv,
- const dyesub_cap_t *caps,
- int row,
- int col,
- int plane)
-{
- unsigned short ink[MAX_INK_CHANNELS], *out;
- int i, j, b;
-
- if (pv->print_mode == DYESUB_LANDSCAPE)
- { /* "rotate" image */
- dyesub_swap_ints(&col, &row);
- row = (pv->imgw_px - 1) - row;
- }
+static void
+dyesub_render_pixel(unsigned short *src, char *dest,
+ dyesub_print_vars_t *pv,
+ const dyesub_cap_t *caps,
+ int plane)
+{
+ unsigned short ink[MAX_INK_CHANNELS]; /* What is sent to printer */
- out = &(pv->image_data[row][col * pv->out_channels]);
+ int i;
+ int start, end;
- for (i = 0; i < pv->ink_channels; i++)
+ /* Only compute one color at a time */
+ if (pv->plane_interlacing || pv->row_interlacing)
{
- if (pv->out_channels == pv->ink_channels)
- { /* copy out_channel (image) to equiv ink_channel (printer) */
- if (dyesub_feature(caps, DYESUB_FEATURE_RGBtoYCBCR)) {
- /* Convert RGB -> YCbCr (JPEG YCbCr444 coefficients) */
- double R, G, B;
- R = out[0];
- G = out[1];
- B = out[2];
-
- if (i == 0) /* Y */
- ink[i] = R * 0.299 + G * 0.587 + B * 0.114;
- else if (i == 1) /* Cb */
- ink[i] = R * -0.168736 + G * -0.331264 + B * 0.5 + 32768;
- else if (i == 2) /* Cr */
- ink[i] = R * 0.5 + G * -0.418688 + B * -0.081312 + 32768;
-
- /* FIXME: Natively support YCbCr "inks" in the
- Gutenprint core and allow that as an input
- into the dyesub driver. */
- } else {
- ink[i] = out[i];
- }
- }
- else if (pv->out_channels < pv->ink_channels)
- { /* several ink_channels (printer) "share" same out_channel (image) */
- ink[i] = out[i * pv->out_channels / pv->ink_channels];
- }
- else /* (pv->out_channels > pv->ink_channels) */
- { /* merge several out_channels (image) into ink_channel (printer) */
- int avg = 0;
- for (j = 0; j < pv->out_channels / pv->ink_channels; j++)
- avg += out[j + i * pv->out_channels / pv->ink_channels];
- ink[i] = avg * pv->ink_channels / pv->out_channels;
- }
+ start = plane;
+ end = plane + 1;
+ }
+ else
+ {
+ start = 0;
+ end = pv->ink_channels;
}
-
- /* Downscale 16bpp to output bpp */
- /* FIXME: Do we want to round? */
- if (pv->bytes_per_ink_channel == 1)
+
+ /* copy out_channel (image) to equiv ink_channel (printer) */
+ for (i = start; i < end; i++)
{
- unsigned char *ink_u8 = (unsigned char *) ink;
- for (i = 0; i < pv->ink_channels; i++) {
+#ifndef CANONSELPHYNEO_CMY
+ if (dyesub_feature(caps, DYESUB_FEATURE_RGBtoYCBCR))
+ {
+ /* Convert RGB -> YCbCr (JPEG YCbCr444 coefficients) */
+ double R, G, B;
+ R = src[0];
+ G = src[1];
+ B = src[2];
+
+ if (i == 0) /* Y */
+ ink[i] = R * 0.299 + G * 0.587 + B * 0.114;
+ else if (i == 1) /* Cb */
+ ink[i] = R * -0.168736 + G * -0.331264 + B * 0.5 + (1 << (16 -1)); // Math is 16bpp here.
+ else if (i == 2) /* Cr */
+ ink[i] = R * 0.5 + G * -0.418688 + B * -0.081312 + (1 << (16 -1)); // Math is 16bpp here.
+ /* FIXME: Natively support YCbCr "inks" in the
+ Gutenprint core and allow that as an input
+ into the dyesub driver. */
+ }
+ else
+#endif
+ {
+ ink[i] = src[i];
+ }
+
+ /* Downscale 16bpp to output bpp */
+ if (pv->bytes_per_ink_channel == 1)
+ {
+ unsigned char *ink_u8 = (unsigned char *) ink;
+#ifndef CANONSELPHYNEO_CMY
#if 0
- if (dyesub_feature(caps, DYESUB_FEATURE_RGBtoYCBCR))
- ink_u8[i] = ink[i] >> 8;
- else
+ /* FIXME: Do we want to round? */
+ if (dyesub_feature(caps, DYESUB_FEATURE_RGBtoYCBCR))
+ ink_u8[i] = ink[i] >> 8;
+ else
#endif
- ink_u8[i] = ink[i] / 257;
- }
- }
- else if (pv->bits_per_ink_channel != 16)
- {
- for (i = 0; i < pv->ink_channels; i++)
- ink[i] = ink[i] >> (16 - pv->bits_per_ink_channel);
- }
+#endif
+ ink_u8[i] = ink[i] / 257;
+ }
+ else /* ie 2 bytes per channel */
+ {
+ /* Scale down to output bits */
+ if (pv->bits_per_ink_channel != 16)
+ ink[i] = ink[i] >> (16 - pv->bits_per_ink_channel);
- /* Byteswap as needed */
- if (pv->bytes_per_ink_channel == 2 && pv->byteswap)
- for (i = 0; i < pv->ink_channels; i++)
- ink[i] = ((ink[i] >> 8) & 0xff) | ((ink[i] & 0xff) << 8);
+ /* Byteswap if needed */
+ if (pv->byteswap)
+ ink[i] = ((ink[i] >> 8) & 0xff) | ((ink[i] & 0xff) << 8);
+ }
+ }
+ /* If we use plane or row interlacing, only write the plane's channel */
if (pv->plane_interlacing || pv->row_interlacing)
- stp_zfwrite((char *) ink + (plane * pv->bytes_per_ink_channel),
- pv->bytes_per_ink_channel, 1, v);
- else
- /* print inks in correct order, eg. RGB BGR */
- for (b = 0; b < pv->ink_channels; b++)
- stp_zfwrite((char *) ink + (pv->bytes_per_ink_channel * (pv->ink_order[b]-1)),
- pv->bytes_per_ink_channel, 1, v);
-
- return 1;
+ memcpy(dest, (char *) ink + (plane * pv->bytes_per_ink_channel),
+ pv->bytes_per_ink_channel);
+ else /* Otherwise, print the full set of inks, in order (eg RGB or BGR) */
+ for (i = 0; i < pv->ink_channels; i++)
+ memcpy(dest + i*pv->bytes_per_ink_channel,
+ (char *) ink + (pv->bytes_per_ink_channel * (pv->ink_order[i]-1)),
+ pv->bytes_per_ink_channel);
}
-static int
-dyesub_print_row(stp_vars_t *v,
- dyesub_print_vars_t *pv,
- const dyesub_cap_t *caps,
- int row,
- int plane)
-{
- int ret = 0;
- int w, col;
+static void
+dyesub_render_row(stp_vars_t *v,
+ dyesub_print_vars_t *pv,
+ const dyesub_cap_t *caps,
+ double in_row,
+ char *dest,
+ int bytes_per_pixel,
+ int plane)
+{
+ int w;
+ unsigned short *src;
for (w = 0; w < pv->outw_px; w++)
{
- col = dyesub_interpolate(w, pv->outw_px, pv->imgw_px);
+ double row = in_row;
+ double col = dyesub_interpolate(w, pv->outw_px, pv->imgw_px);
if (pv->plane_lefttoright)
- ret = dyesub_print_pixel(v, pv, caps, row, pv->imgw_px - col - 1, plane);
- else
- ret = dyesub_print_pixel(v, pv, caps, row, col, plane);
- if (ret > 1)
- break;
+ col = pv->imgw_px - col - 1;
+ if (pv->print_mode == DYESUB_LANDSCAPE)
+ { /* "rotate" image */
+ dyesub_swap_doubles(&col, &row);
+ row = (pv->imgw_px - 1) - row;
+ }
+ // XXX FIXME: This is "point" interpolation. Be smarter!
+ // eg: Average (average all pixels that touch this one)
+ // BiLinear (scale based on linear interpolation)
+ // BiCubic (scale based on weighted average, based on proximity)
+ // Lanczos (awesome!! but slow)
+ src = &(pv->image_data[(int)row][(int)col * pv->out_channels]);
+
+ dyesub_render_pixel(src, dest + w*bytes_per_pixel,
+ pv, caps, plane);
}
- return ret;
}
static int
@@ -8469,16 +8909,36 @@ dyesub_print_plane(stp_vars_t *v,
const dyesub_cap_t *caps,
int plane)
{
-
-
- int ret = 0;
- int h, row, p;
- int out_bytes = ((pv->plane_interlacing || pv->row_interlacing) ? 1 : pv->ink_channels)
+ int h;
+ int bpp = ((pv->plane_interlacing || pv->row_interlacing) ? 1 : pv->ink_channels)
* pv->bytes_per_ink_channel;
+ size_t rowlen = pv->prnw_px * bpp;
+ char *destrow = stp_malloc(rowlen); /* Allocate a buffer for the rendered rows */
+ if (!destrow)
+ return 0; /* ? out of memory ? */
+
+ /* Pre-Fill in the blank bits of the row. */
+ if (dyesub_feature(caps, DYESUB_FEATURE_FULL_WIDTH))
+ {
+ /* FIXME: This is broken for bpp != 1 and packed data -- but no such models exist. */
+ /* empty part left of image area */
+ if (pv->outl_px > 0)
+ {
+ memset(destrow, pv->empty_byte[plane], bpp * pv->outl_px);
+ }
+ /* empty part right of image area */
+ if (pv->outr_px < pv->prnw_px)
+ {
+ memset(destrow + rowlen - bpp * (pv->prnw_px - pv->outr_px),
+ pv->empty_byte[plane],
+ bpp * (pv->prnw_px - pv->outr_px));
+ }
+ }
for (h = 0; h <= pv->prnb_px - pv->prnt_px; h++)
{
- p = pv->row_interlacing ? 0 : plane;
+ int p = pv->row_interlacing ? 0 : plane;
+ double row;
do {
@@ -8493,31 +8953,25 @@ dyesub_print_plane(stp_vars_t *v,
dyesub_exec(v, caps->block_init_func, "caps->block_init");
}
+ /* Generate a single row */
if (h + pv->prnt_px < pv->outt_px || h + pv->prnt_px >= pv->outb_px)
{ /* empty part above or below image area */
- dyesub_nputc(v, pv->empty_byte[plane], out_bytes * pv->prnw_px);
+ memset(destrow, pv->empty_byte[plane], rowlen);
+ /* FIXME: This is also broken for bpp != 1 and packed data */
+ /* FIXME: Also this is inefficient; it won't change once generated.. */
}
else
{
- if (dyesub_feature(caps, DYESUB_FEATURE_FULL_WIDTH)
- && pv->outl_px > 0)
- { /* empty part left of image area */
- dyesub_nputc(v, pv->empty_byte[plane], out_bytes * pv->outl_px);
- }
-
row = dyesub_interpolate(h + pv->prnt_px - pv->outt_px,
- pv->outh_px, pv->imgh_px);
+ pv->outh_px, pv->imgh_px);
+
stp_deprintf(STP_DBG_DYESUB,
- "dyesub_print_plane: h = %d, row = %d\n", h, row);
- ret = dyesub_print_row(v, pv, caps, row, p);
-
- if (dyesub_feature(caps, DYESUB_FEATURE_FULL_WIDTH)
- && pv->outr_px < pv->prnw_px)
- { /* empty part right of image area */
- dyesub_nputc(v, pv->empty_byte[plane], out_bytes
- * (pv->prnw_px - pv->outr_px));
- }
+ "dyesub_print_plane: h = %d, row = %f\n", h, row);
+
+ dyesub_render_row(v, pv, caps, row, destrow + bpp * pv->outl_px, bpp, p);
}
+ /* And send it out */
+ stp_zfwrite(destrow, rowlen, 1, v);
if (h + pv->prnt_px == pd->block_max_h)
{ /* block end */
@@ -8526,7 +8980,9 @@ dyesub_print_plane(stp_vars_t *v,
} while (pv->row_interlacing && ++p < pv->ink_channels);
}
- return ret;
+
+ stp_free(destrow);
+ return 1;
}
/*
@@ -8676,6 +9132,16 @@ dyesub_do_print(stp_vars_t *v, stp_image_t *image)
stp_channel_add(v, i, 0, 1.0);
pv.out_channels = stp_color_init(v, image, 65536);
+ /* If there's a mismatch in channels, that is ALWAYS a problem */
+ if (pv.out_channels != pv.ink_channels)
+ {
+ stp_deprintf(STP_DBG_DYESUB,
+ "Input and output channel count mismatch! (%d vs %d)\n", pv.out_channels, pv.ink_channels);
+ stp_image_conclude(image);
+ stp_free(pd);
+ return 2;
+ }
+
if (dyesub_feature(caps, DYESUB_FEATURE_12BPP)) {
pv.bytes_per_ink_channel = 2;
pv.bits_per_ink_channel = 12;
@@ -8699,11 +9165,16 @@ dyesub_do_print(stp_vars_t *v, stp_image_t *image)
pv.image_data = dyesub_read_image(v, &pv, image);
if (ink_type) {
+#ifndef CANONSELPHYNEO_CMY
if (dyesub_feature(caps, DYESUB_FEATURE_RGBtoYCBCR)) {
pv.empty_byte[0] = 0xff; /* Y */
pv.empty_byte[1] = 0x80; /* Cb */
pv.empty_byte[2] = 0x80; /* Cr */
- } else if (strcmp(ink_type, "RGB") == 0 || strcmp(ink_type, "BGR") == 0 || strcmp(ink_type, "Whitescale") == 0) {
+ } else
+#endif
+ if (strcmp(ink_type, "RGB") == 0 ||
+ strcmp(ink_type, "BGR") == 0 ||
+ strcmp(ink_type, "Whitescale") == 0) {
pv.empty_byte[0] = 0xff;
pv.empty_byte[1] = 0xff;
pv.empty_byte[2] = 0xff;
@@ -8725,6 +9196,7 @@ dyesub_do_print(stp_vars_t *v, stp_image_t *image)
if (!pv.image_data)
{
stp_image_conclude(image);
+ stp_free(pd);
return 2;
}
/* /FIXME */