3070 lines
80 KiB
C
3070 lines
80 KiB
C
/* LibMitsuD70ImageReProcess -- Re-implemented image processing library for
|
|
the Mitsubishi CP-D70 family of printers
|
|
|
|
Copyright (c) 2016-2024 Solomon Peachy <pizza@shaftnet.org>
|
|
|
|
** ** ** ** Do NOT contact Mitsubishi about this library! ** ** ** **
|
|
|
|
This library is a platform-independent reimplementation of the image
|
|
processing algorithms that are necessary to utilize most newer
|
|
Mitsubishi photo printers.
|
|
|
|
Mitsubishi was *NOT* involved in the creation of this library, and is
|
|
not responsible in any way for the library or any deficiencies in its
|
|
output. They will provide no support if it is used.
|
|
|
|
However, without this library, it is nearly impossible to utilize
|
|
their these printers under Linux and similar operating systems.
|
|
|
|
The following printers are known to function with this library:
|
|
|
|
* Mitsubishi CP-D70DW
|
|
* Mitsubishi CP-D707DW
|
|
* Mitsubishi CP-K60DW-S
|
|
* Mitsubishi CP-D80DW
|
|
* Kodak 305
|
|
* Fujifilm ASK-300
|
|
* Mitsubishi CP-M1
|
|
* Mitsubishi CP-M15
|
|
|
|
More recently, the CP98xx family now uses this library. These
|
|
models are expected to function:
|
|
|
|
* Mitsubishi CP9800DW
|
|
* Mitsubishi CP9810DW
|
|
* Mitsubishi CP9820DW-S
|
|
|
|
Finally, the CP30D family uses this library too:
|
|
|
|
* Mitsubishi CP30D/DW
|
|
|
|
** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 3 of the License, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
|
|
SPDX-License-Identifier: GPL-3.0+
|
|
|
|
*/
|
|
|
|
#define LIB_VERSION "0.10.8"
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "libMitsuD70ImageReProcess.h"
|
|
|
|
#define UNUSED(expr) do { (void)(expr); } while (0)
|
|
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Endian Manipulation macros
|
|
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
|
#define le16_to_cpu(__x) __x
|
|
#define le32_to_cpu(__x) __x
|
|
#define be16_to_cpu(__x) __builtin_bswap16(__x)
|
|
#define be32_to_cpu(__x) __builtin_bswap32(__x)
|
|
|
|
#define le64d_to_cpu(__x) __x
|
|
#define be64d_to_cpu(__x) swab_double(__x)
|
|
#else
|
|
#define le16_to_cpu(__x) __builtin_bswap16(__x)
|
|
#define le32_to_cpu(__x) __builtin_bswap32(__x)
|
|
#define be16_to_cpu(__x) __x
|
|
#define be32_to_cpu(__x) __x
|
|
|
|
#define le64d_to_cpu(__x) swab_double(__x)
|
|
#define be64d_to_cpu(__x) __x
|
|
#endif
|
|
|
|
#define cpu_to_le16 le16_to_cpu
|
|
#define cpu_to_le32 le32_to_cpu
|
|
#define cpu_to_be16 be16_to_cpu
|
|
#define cpu_to_be32 be32_to_cpu
|
|
#define cpu_to_le64d le64d_to_cpu
|
|
#define cpu_to_be64d be64d_to_cpu
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Data declarations
|
|
|
|
#define CPC_DATA_ROWS 2730
|
|
|
|
#define CHUNK_LEN (256*1024)
|
|
|
|
struct CColorConv3D {
|
|
uint8_t lut[17][17][17][3];
|
|
};
|
|
|
|
/* State for image processing algorithm */
|
|
/* Note: pixel data is always ordered YMC! */
|
|
struct CImageEffect70 {
|
|
uint32_t pad; // @0
|
|
double *ttd_htd_scratch; // @4/1 // array [(cols+6) * 3], single row, plus padding. processing buffer from TTD->HTD
|
|
double *ttd_htd_first; // @8/2 // first pixel of ttd_htd_scratch
|
|
double *ttd_htd_last; // @12/3 // last pixel of ttd_htd_scratch
|
|
double *htd_ttd_next; // @16/4 // array [band_pixels], state from HTD->TTDnext.
|
|
double fcc_ymc_scale[3]; // @20/5 // FCC generates, YMC consumes. per-color scaling factor for thermal compensation.
|
|
uint32_t htd_fcc_scratch[3][128]; // @44/11 // per-color state from HTD->FCC
|
|
double fcc_ymc_scratch[3][128]; // @1580/395 // per-color state from FCC->YMC6
|
|
double *fcc_rowcomps; // @4652/1163 // array of [row_count][3], Per-row/color correction factor? Used internally by FCC code.
|
|
uint16_t *linebuf; // @4656/1164 // array of [11 * sizeof(uint16_t) * linebuf_stride], Historical line buffer
|
|
uint16_t *linebuf_row[11]; // @4660/1165 // Pointers into rows in line buffer, minus padding!
|
|
uint16_t *linebuf_line[11]; // @4704/1176 // Pointers to raw rows in line buffer (w/ padding on either side)
|
|
uint16_t *linebuf_shrp[8]; // @4748/1187 // Pointers into line buffer, for pixels used for sharpening
|
|
struct CPCData *cpc; // @4780/1195 // Loaded from disk..
|
|
int32_t sharpen; // @4784/1196 // -1 off, 0-8, "normal" is 4.
|
|
uint32_t columns; // @4788/1197 // columns in image
|
|
uint32_t rows; // @4792/1198 // rows in image
|
|
uint32_t pixel_count; // @4796/1199 // pixels per input band (ie input stride / 2)
|
|
uint32_t cur_row; // @4800/1200 // row index.
|
|
uint32_t band_pixels; // @4804/1201 // pixels per output band (always columns * 3)
|
|
uint32_t linebuf_stride; // @4808/1202 // band_pixels + 6 -- line buffer row stride
|
|
double fhdiv_up; // @4812/1203 // FH[0] // division factor for positive comp
|
|
double fhdiv_dn; // @4820/1205 // FH[1] // divison factor for negative comp
|
|
double fh_cur; // @4828/1207 // FH[2]
|
|
double fh_prev1; // @4836/1209 // FH[3] - FH[2]
|
|
double fh_prev2; // @4844/1211 // FH[4] - FH[3]
|
|
double fh_prev3; // @4852/1213 // FH[4]
|
|
// @4860/1215
|
|
};
|
|
|
|
/* The parsed data out of the CPC files */
|
|
struct CPCData {
|
|
/* One per output row, Used for HTD. */
|
|
uint32_t LINEy[2730]; // @0 // can be uint16?
|
|
uint32_t LINEm[2730]; // @10920 // can be uint16?
|
|
uint32_t LINEc[2730]; // @21840 // can be uint16?
|
|
/* Maps input color to gamma-corrected 16bpp inverse */
|
|
uint16_t GNMby[256]; // @32760 // Gamma map Blue->Yellow
|
|
uint16_t GNMgm[256]; // @33272 // Gamma map Green->Magenta
|
|
uint16_t GNMrc[256]; // @33784 // Gamma map Red->Cyan
|
|
/* Used for FCC */
|
|
double FM[256]; // @34296
|
|
/* Used for TTD */
|
|
double KSP[128]; // @36344
|
|
double KSM[128]; // @37368
|
|
double OSP[128]; // @38392
|
|
double OSM[128]; // @39416
|
|
double KP[11]; // @40440 // weights for line buffer!
|
|
double KM[11]; // @40528 // weights for line buffer!
|
|
/* Used for HTD */
|
|
double HK[4]; // @40616
|
|
|
|
uint32_t Speed[3]; // @40648 -- Unused!
|
|
double FH[5]; // @40660
|
|
/* Used for sharpening */
|
|
double SHK[72]; // @40700 // sharpening coefficients, actually double[9][8]
|
|
/* Used for YMC6 */
|
|
double UH[101]; // @41276
|
|
|
|
/* Used by roller mark correction (K60/D80/EK305) -- Unused! */
|
|
uint32_t ROLK[13]; // @42084
|
|
/* Used by reverse/skip logic (K60/D80/EK305) */
|
|
int32_t REV[190]; // @42136 // Actually int32_t[10][19] EK305, or int32_t[4][19] on D80/K60
|
|
// @42440
|
|
int num_rev; // 0 or 4 or 10.
|
|
};
|
|
|
|
/*** Version ***/
|
|
int lib70x_getapiversion(void)
|
|
{
|
|
return LIB_APIVERSION;
|
|
}
|
|
|
|
/*** 3D color Lookup table ****/
|
|
|
|
/* Load the Lookup table off of disk into *PRE-ALLOCATED* buffer */
|
|
int CColorConv3D_Get3DColorTable(uint8_t *buf, const char *filename)
|
|
{
|
|
FILE *stream;
|
|
int rval;
|
|
|
|
if (!filename)
|
|
return 1;
|
|
if (!*filename)
|
|
return 2;
|
|
if (!buf)
|
|
return 3;
|
|
|
|
stream = fopen(filename, "rb");
|
|
if (!stream)
|
|
return 4;
|
|
|
|
fseek(stream, 0, SEEK_END);
|
|
if (ftell(stream) < LUT_LEN) {
|
|
fclose(stream);
|
|
return 5;
|
|
}
|
|
fseek(stream, 0, SEEK_SET);
|
|
rval = fread(buf, LUT_LEN, 1, stream);
|
|
fclose(stream);
|
|
|
|
return (rval != 1);
|
|
}
|
|
|
|
/* Parse the on-disk LUT data into the structure.... */
|
|
struct CColorConv3D *CColorConv3D_Load3DColorTable(const uint8_t *ptr)
|
|
{
|
|
struct CColorConv3D *this;
|
|
this = malloc(sizeof(*this));
|
|
if (!this)
|
|
return NULL;
|
|
|
|
int i, j, k;
|
|
|
|
for (i = 0 ; i <= 16 ; i++) {
|
|
for (j = 0 ; j <= 16 ; j++) {
|
|
for (k = 0; k <= 16; k++) {
|
|
this->lut[k][j][i][2] = *ptr++;
|
|
this->lut[k][j][i][1] = *ptr++;
|
|
this->lut[k][j][i][0] = *ptr++;
|
|
}
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
void CColorConv3D_Destroy3DColorTable(struct CColorConv3D *this)
|
|
{
|
|
free(this);
|
|
}
|
|
|
|
/* Transform a single pixel. */
|
|
static void CColorConv3D_DoColorConvPixel(struct CColorConv3D *this, uint8_t *redp, uint8_t *grnp, uint8_t *blup)
|
|
{
|
|
int red_h;
|
|
int grn_h;
|
|
int blu_h;
|
|
int grn_li;
|
|
int red_li;
|
|
int blu_li;
|
|
int red_l;
|
|
int grn_l;
|
|
int blu_l;
|
|
|
|
uint8_t *tab0; // @ 14743
|
|
uint8_t *tab1; // @ 14746
|
|
uint8_t *tab2; // @ 14749
|
|
uint8_t *tab3; // @ 14752
|
|
uint8_t *tab4; // @ 14755
|
|
uint8_t *tab5; // @ 14758
|
|
uint8_t *tab6; // @ 14761
|
|
uint8_t *tab7; // @ 14764
|
|
|
|
red_h = *redp >> 4;
|
|
red_l = *redp & 0xF;
|
|
red_li = 16 - red_l;
|
|
|
|
grn_h = *grnp >> 4;
|
|
grn_l = *grnp & 0xF;
|
|
grn_li = 16 - grn_l;
|
|
|
|
blu_h = *blup >> 4;
|
|
blu_l = *blup & 0xF;
|
|
blu_li = 16 - blu_l;
|
|
|
|
tab0 = this->lut[red_h+0][grn_h+0][blu_h+0];
|
|
tab1 = this->lut[red_h+1][grn_h+0][blu_h+0];
|
|
tab2 = this->lut[red_h+0][grn_h+1][blu_h+0];
|
|
tab3 = this->lut[red_h+1][grn_h+1][blu_h+0];
|
|
tab4 = this->lut[red_h+0][grn_h+0][blu_h+1];
|
|
tab5 = this->lut[red_h+1][grn_h+0][blu_h+1];
|
|
tab6 = this->lut[red_h+0][grn_h+1][blu_h+1];
|
|
tab7 = this->lut[red_h+1][grn_h+1][blu_h+1];
|
|
|
|
*redp = (blu_li
|
|
* (grn_li * (red_li * tab0[0] + red_l * tab1[0])
|
|
+ grn_l * (red_li * tab2[0] + red_l * tab3[0]))
|
|
+ blu_l
|
|
* (grn_li * (red_li * tab4[0] + red_l * tab5[0])
|
|
+ grn_l * (red_li * tab6[0] + red_l * tab7[0]))
|
|
+ 2048) >> 12;
|
|
*grnp = (blu_li
|
|
* (grn_li * (red_li * tab0[1] + red_l * tab1[1])
|
|
+ grn_l * (red_li * tab2[1] + red_l * tab3[1]))
|
|
+ blu_l
|
|
* (grn_li * (red_li * tab4[1] + red_l * tab5[1])
|
|
+ grn_l * (red_li * tab6[1] + red_l * tab7[1]))
|
|
+ 2048) >> 12;
|
|
*blup = (blu_li
|
|
* (grn_li * (red_li * tab0[2] + red_l * tab1[2])
|
|
+ grn_l * (red_li * tab2[2] + red_l * tab3[2]))
|
|
+ blu_l
|
|
* (grn_li * (red_li * tab4[2] + red_l * tab5[2])
|
|
+ grn_l * (red_li * tab6[2] + red_l * tab7[2]))
|
|
+ 2048) >> 12;
|
|
}
|
|
|
|
/* Perform a total conversion on an entire image, packed rgb/bgr */
|
|
void CColorConv3D_DoColorConv(struct CColorConv3D *this, uint8_t *data, uint16_t cols, uint16_t rows, uint32_t stride, int rgb_bgr)
|
|
{
|
|
uint16_t i, j;
|
|
|
|
uint8_t *ptr;
|
|
|
|
for ( i = 0; i < rows ; i++ )
|
|
{
|
|
ptr = data;
|
|
for ( j = 0; cols > j; j++ )
|
|
{
|
|
if (rgb_bgr) {
|
|
CColorConv3D_DoColorConvPixel(this, ptr + 2, ptr + 1, ptr);
|
|
} else {
|
|
CColorConv3D_DoColorConvPixel(this, ptr, ptr + 1, ptr + 2);
|
|
}
|
|
ptr += 3;
|
|
}
|
|
data += stride;
|
|
}
|
|
}
|
|
|
|
/* Perform a total conversion on an entire image, planar */
|
|
void CColorConv3D_DoColorConvPlane(struct CColorConv3D *this, uint8_t *data_r, uint8_t *data_g, uint8_t *data_b,
|
|
uint32_t planelen)
|
|
{
|
|
uint32_t i;
|
|
|
|
for ( i = 0; i < planelen ; i++ )
|
|
{
|
|
CColorConv3D_DoColorConvPixel(this, &data_r[i], &data_g[i], &data_b[i]);
|
|
}
|
|
}
|
|
|
|
/*** CPC Data ***/
|
|
|
|
/* Load and parse the CPC data */
|
|
struct CPCData *get_CPCData(const char *filename)
|
|
{
|
|
struct CPCData *data;
|
|
FILE *f;
|
|
char buf[4096];
|
|
int line;
|
|
char *ptr;
|
|
|
|
const char *delim = " ,\t\n";
|
|
|
|
if (!filename)
|
|
return NULL;
|
|
data = malloc(sizeof(struct CPCData));
|
|
if (!data)
|
|
return NULL;
|
|
|
|
f = fopen(filename, "r");
|
|
if (!f)
|
|
goto done_free;
|
|
|
|
/* Skip the first two rows */
|
|
for (line = 0 ; line < 2 ; line++) {
|
|
if (fgets(buf, sizeof(buf), f) == NULL)
|
|
goto abort;
|
|
}
|
|
|
|
/* Init the REV and ROLK first rows */
|
|
data->REV[0] = 0;
|
|
data->ROLK[0] = 0;
|
|
data->num_rev = 0;
|
|
|
|
/* Start reading in data */
|
|
for (line = 0 ; line < CPC_DATA_ROWS ; line++) {
|
|
if (fgets(buf, sizeof(buf), f) == NULL)
|
|
goto abort;
|
|
ptr = strtok(buf, delim); // Always skip first column
|
|
if (!ptr)
|
|
goto abort;
|
|
if (line < CPC_DATA_ROWS) {
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->LINEy[line] = strtol(ptr, NULL, 10);
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->LINEm[line] = strtol(ptr, NULL, 10);
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->LINEc[line] = strtol(ptr, NULL, 10);
|
|
}
|
|
if (line < 256) {
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->GNMby[line] = strtol(ptr, NULL, 10);
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->GNMgm[line] = strtol(ptr, NULL, 10);
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->GNMrc[line] = strtol(ptr, NULL, 10);
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->FM[line] = strtod(ptr, NULL);
|
|
}
|
|
if (line < 128) {
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->KSP[line] = strtod(ptr, NULL);
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->KSM[line] = strtod(ptr, NULL);
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->OSP[line] = strtod(ptr, NULL);
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->OSM[line] = strtod(ptr, NULL);
|
|
}
|
|
if (line < 11) {
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->KP[line] = strtod(ptr, NULL);
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->KM[line] = strtod(ptr, NULL);
|
|
}
|
|
if (line < 4) {
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->HK[line] = strtod(ptr, NULL);
|
|
}
|
|
if (line < 3) {
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->Speed[line] = strtol(ptr, NULL, 10);
|
|
}
|
|
if (line < 5) {
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->FH[line] = strtod(ptr, NULL);
|
|
}
|
|
if (line < 72) {
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->SHK[line] = strtod(ptr, NULL);
|
|
}
|
|
if (line < 101) {
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
goto abort;
|
|
data->UH[line] = strtod(ptr, NULL);
|
|
}
|
|
if (line < 13) { // ROLK, not present on D70
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
continue;
|
|
data->ROLK[line] = strtol(ptr, NULL, 10);
|
|
|
|
}
|
|
if (line < 190) { // REV, not present on D70.
|
|
ptr = strtok(NULL, delim);
|
|
if (!ptr)
|
|
continue;
|
|
data->REV[line] = strtol(ptr, NULL, 10);
|
|
data->num_rev++;
|
|
}
|
|
}
|
|
|
|
fclose(f);
|
|
return data;
|
|
|
|
abort:
|
|
fclose(f);
|
|
done_free:
|
|
free(data);
|
|
return NULL;
|
|
}
|
|
|
|
void destroy_CPCData(struct CPCData *data) {
|
|
free(data);
|
|
}
|
|
|
|
/*** Image Processing ***/
|
|
static struct CImageEffect70 *CImageEffect70_Create(struct CPCData *cpc)
|
|
{
|
|
struct CImageEffect70 *data = malloc(sizeof (struct CImageEffect70));
|
|
if (!data)
|
|
return NULL;
|
|
|
|
memset(data, 0, sizeof(*data));
|
|
data->sharpen = -1;
|
|
data->fhdiv_up = 1.0;
|
|
data->fhdiv_dn = 1.0;
|
|
data->cpc = cpc;
|
|
return data;
|
|
}
|
|
|
|
static void CImageEffect70_Destroy(struct CImageEffect70 *data)
|
|
{
|
|
free(data);
|
|
}
|
|
|
|
static void CImageEffect70_InitMidData(struct CImageEffect70 *data)
|
|
{
|
|
data->ttd_htd_first = NULL;
|
|
data->ttd_htd_last = NULL;
|
|
data->ttd_htd_scratch = NULL;
|
|
data->htd_ttd_next = NULL;
|
|
data->fcc_rowcomps = NULL;
|
|
data->linebuf = NULL;
|
|
|
|
data->fcc_ymc_scale[0] = 1.0;
|
|
data->fcc_ymc_scale[1] = 1.0;
|
|
data->fcc_ymc_scale[2] = 1.0;
|
|
|
|
memset(data->linebuf_row, 0, sizeof(data->linebuf_row));
|
|
memset(data->linebuf_line, 0, sizeof(data->linebuf_line));
|
|
memset(data->htd_fcc_scratch, 0, sizeof(data->htd_fcc_scratch)); // redundant
|
|
memset(data->fcc_ymc_scratch, 0, sizeof(data->fcc_ymc_scratch)); // redundant
|
|
}
|
|
|
|
static void CImageEffect70_CreateMidData(struct CImageEffect70 *data)
|
|
{
|
|
int i;
|
|
|
|
data->ttd_htd_scratch = malloc(sizeof(double) * 3 * (data->columns + 6));
|
|
memset(data->ttd_htd_scratch, 0, (sizeof(double) * 3 * (data->columns + 6)));
|
|
data->ttd_htd_first = data->ttd_htd_scratch + 9;
|
|
data->ttd_htd_last = data->ttd_htd_first + 3 * (data->columns - 1);
|
|
data->htd_ttd_next = malloc(sizeof(double) * data->band_pixels);
|
|
memset(data->htd_ttd_next, 0, (sizeof(double) * data->band_pixels));
|
|
data->fcc_rowcomps = malloc(3 * sizeof(double) * data->rows);
|
|
memset(data->fcc_rowcomps, 0, (3 * sizeof(double) * data->rows));
|
|
data->linebuf_stride = data->band_pixels + 6;
|
|
data->linebuf = malloc(11 * sizeof(uint16_t) * data->linebuf_stride);
|
|
memset(data->linebuf, 0, (11 * sizeof(uint16_t) * data->linebuf_stride));
|
|
data->linebuf_line[0] = data->linebuf;
|
|
data->linebuf_row[0] = data->linebuf_line[0] + 3; // ie 6 bytes.
|
|
|
|
for (i = 1 ; i < 11 ; i++ ) {
|
|
data->linebuf_line[i] = data->linebuf_line[i-1] + data->linebuf_stride;
|
|
data->linebuf_row[i] = data->linebuf_line[i] + 3; // ie 6 bytes
|
|
}
|
|
memset(data->htd_fcc_scratch, 0, sizeof(data->htd_fcc_scratch));
|
|
memset(data->fcc_ymc_scratch, 0, sizeof(data->fcc_ymc_scratch));
|
|
}
|
|
|
|
static void CImageEffect70_DeleteMidData(struct CImageEffect70 *data)
|
|
{
|
|
int i;
|
|
|
|
if (data->ttd_htd_scratch) {
|
|
free(data->ttd_htd_scratch);
|
|
data->ttd_htd_scratch = NULL;
|
|
data->ttd_htd_first = NULL;
|
|
data->ttd_htd_last = NULL;
|
|
}
|
|
if (data->htd_ttd_next) {
|
|
free(data->htd_ttd_next);
|
|
data->htd_ttd_next = NULL;
|
|
}
|
|
if (data->fcc_rowcomps) {
|
|
free(data->fcc_rowcomps);
|
|
data->fcc_rowcomps = NULL;
|
|
}
|
|
if (data->linebuf) {
|
|
free(data->linebuf);
|
|
data->linebuf = NULL;
|
|
}
|
|
|
|
for (i = 0 ; i < 3 ; i++) {
|
|
data->fcc_ymc_scale[i] = 0.0;
|
|
}
|
|
memset(data->linebuf_row, 0, sizeof(data->linebuf_row));
|
|
memset(data->linebuf_line, 0, sizeof(data->linebuf_line));
|
|
memset(data->htd_fcc_scratch, 0, sizeof(data->htd_fcc_scratch));
|
|
memset(data->fcc_ymc_scratch, 0, sizeof(data->fcc_ymc_scratch));
|
|
}
|
|
|
|
static void CImageEffect70_Sharp_CopyLine(struct CImageEffect70 *data,
|
|
int offset, const uint16_t *row, int rownum)
|
|
{
|
|
uint16_t *dst, *end;
|
|
|
|
dst = data->linebuf_row[offset + 5]; /* Points at start of dst row */
|
|
end = dst + 3 * data->columns; /* Point at end of dst row */
|
|
|
|
memcpy(dst, row -(rownum * data->pixel_count), sizeof(uint16_t) * data->band_pixels);
|
|
|
|
memcpy(dst - 3, dst, sizeof(*dst)*3); /* Fill in dst row head */
|
|
memcpy(end, end - 3, sizeof(*end)*3); /* Fill in dst row tail */
|
|
}
|
|
|
|
static void CImageEffect70_Sharp_PrepareLine(struct CImageEffect70 *data,
|
|
const uint16_t *row)
|
|
{
|
|
int i;
|
|
|
|
CImageEffect70_Sharp_CopyLine(data, 0, row, 0);
|
|
for (i = 0 ; i < 5 ; i++) {
|
|
memcpy(data->linebuf_line[i], data->linebuf_line[5], sizeof(uint16_t) * data->linebuf_stride);
|
|
}
|
|
for (i = 1 ; i <= 5 ; i++) {
|
|
int rownum = data->rows -1;
|
|
if (rownum < i)
|
|
rownum = i;
|
|
CImageEffect70_Sharp_CopyLine(data, i, row, rownum);
|
|
}
|
|
}
|
|
|
|
static void CImageEffect70_Sharp_ShiftLine(struct CImageEffect70 *data)
|
|
{
|
|
// XXX was memcpy, but src and dest definitely overlap!
|
|
memmove(data->linebuf_line[0], data->linebuf_line[1], 10 * sizeof(uint16_t) * data->linebuf_stride);
|
|
}
|
|
|
|
/* Sets up reference pointers for the sharpening algorithm */
|
|
static void CImageEffect70_Sharp_SetRefPtr(struct CImageEffect70 *data)
|
|
{
|
|
data->linebuf_shrp[0] = data->linebuf_row[4] - 3; // 6 bytes
|
|
data->linebuf_shrp[1] = data->linebuf_row[4];
|
|
data->linebuf_shrp[2] = data->linebuf_row[4] + 3; // 6 bytes
|
|
data->linebuf_shrp[3] = data->linebuf_row[5] - 3; // 6 bytes
|
|
data->linebuf_shrp[4] = data->linebuf_row[5] + 3; // 6 bytes
|
|
data->linebuf_shrp[5] = data->linebuf_row[6] - 3; // 6 bytes
|
|
data->linebuf_shrp[6] = data->linebuf_row[6];
|
|
data->linebuf_shrp[7] = data->linebuf_row[6] + 3; // 6 bytes
|
|
}
|
|
|
|
/* Applies the final correction factor to a row. */
|
|
static void CImageEffect70_CalcYMC6(struct CImageEffect70 *data,
|
|
const double *in, uint16_t *imgdata)
|
|
{
|
|
uint16_t i, j;
|
|
uint32_t offset;
|
|
double uh_val;
|
|
|
|
/* Work out the UH value we need based on our row count */
|
|
offset = data->rows - 1 - data->cur_row;
|
|
if ( offset > 100 )
|
|
offset = 100;
|
|
uh_val = data->cpc->UH[offset];
|
|
|
|
/* Now apply the final correction to each pixel in the row */
|
|
offset = 0;
|
|
for ( i = 0; i < data->columns; i++ ) {
|
|
for ( j = 0; j < 3; j++ ) {
|
|
/* Processed input pixel * UH factor * per-color scaling * per-bucket scaling */
|
|
double pixel = in[offset] * uh_val * data->fcc_ymc_scale[j] * data->fcc_ymc_scratch[j][((int)in[offset] >> 9)];
|
|
if ( pixel > 65535.0)
|
|
imgdata[offset] = 65535;
|
|
else if ( pixel < 0.0)
|
|
imgdata[offset] = 0;
|
|
else
|
|
imgdata[offset] = (int)pixel;
|
|
++offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void CImageEffect70_CalcFCC(struct CImageEffect70 *data)
|
|
{
|
|
double s[3];
|
|
double *row_comp;
|
|
int i, j;
|
|
double *prev1, *prev2, *prev3;
|
|
|
|
/* Figure out where we need to be */
|
|
row_comp = &data->fcc_rowcomps[3*data->cur_row];
|
|
|
|
/* Initialize correction factors for this row based on the
|
|
buckets that CalcHTD handed us */
|
|
for (j = 0 ; j < 3 ; j++) {
|
|
row_comp[j] = 127 * data->htd_fcc_scratch[j][127];
|
|
}
|
|
for (i = 126 ; i >= 0 ; i--) {
|
|
for (j = 0 ; j < 3 ; j++) {
|
|
row_comp[j] += i * data->htd_fcc_scratch[j][i];
|
|
data->htd_fcc_scratch[j][i] += data->htd_fcc_scratch[j][i+1];
|
|
}
|
|
}
|
|
|
|
/* Set up pointers to previous rows. Or if we're on the first
|
|
three rows, take special action. */
|
|
if (data->cur_row > 2) {
|
|
prev1 = row_comp - 3;
|
|
prev2 = row_comp - 6;
|
|
prev3 = row_comp - 9;
|
|
} else if (data->cur_row == 2) {
|
|
prev1 = row_comp - 3;
|
|
prev2 = row_comp - 6;
|
|
prev3 = row_comp - 6;
|
|
} else if (data->cur_row == 1) {
|
|
prev1 = row_comp - 3;
|
|
prev2 = row_comp - 3;
|
|
prev3 = row_comp - 3;
|
|
} else {
|
|
prev1 = row_comp;
|
|
prev2 = row_comp;
|
|
prev3 = row_comp;
|
|
}
|
|
|
|
/* Work out the global color scaling factor for each color in the row */
|
|
for (i = 0 ; i < 3 ; i++) {
|
|
double val;
|
|
/* Average it out over the number of columns */
|
|
row_comp[i] /= data->columns;
|
|
|
|
val = data->fh_cur * row_comp[i]
|
|
+ data->fh_prev1 * prev1[i]
|
|
+ data->fh_prev2 * prev2[i] // XXX this line is '-' in PPC versions, but + in x86. Investigate WTF is going on.
|
|
- data->fh_prev3 * prev3[i];
|
|
/* Positive vs Negative values require different scaling factors */
|
|
if (val >= 0.0) {
|
|
data->fcc_ymc_scale[i] = val / data->fhdiv_up + 1.0;
|
|
} else {
|
|
data->fcc_ymc_scale[i] = val / data->fhdiv_dn + 1.0;
|
|
}
|
|
}
|
|
|
|
/* Update the output buckets based on the input buckets plus
|
|
the FM correction factor */
|
|
memset(s, 0, sizeof(s));
|
|
for (i = 0 ; i < 128 ; i++) {
|
|
for (j = 0 ; j < 3 ; j++) {
|
|
int val = 255 * data->htd_fcc_scratch[j][i] / 1864;
|
|
if (val > 255)
|
|
val = 255;
|
|
s[j] += data->cpc->FM[val];
|
|
data->fcc_ymc_scratch[j][i] = s[j] / (i + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Heat Transfer compensation (I think)
|
|
|
|
Take the raw data
|
|
add in the scaling factor from the adoining rows, using HK[]
|
|
add in the fixed overhead from LINEy/m/c[]
|
|
cap at 0-65535.
|
|
|
|
Also populates htd_ttd_next, which informs the NEXT CalcTTD run what to do.
|
|
*/
|
|
|
|
static void CImageEffect70_CalcHTD(struct CImageEffect70 *data, const double *in, double *out)
|
|
{
|
|
int32_t cur_row, offset;
|
|
double *hk;
|
|
double *last, *first;
|
|
unsigned int i, k;
|
|
uint32_t line_comp[3];
|
|
|
|
hk = data->cpc->HK;
|
|
first = data->ttd_htd_first;
|
|
last = data->ttd_htd_last;
|
|
|
|
/* Clean out correction buckets */
|
|
memset(data->htd_fcc_scratch, 0, sizeof(data->htd_fcc_scratch));
|
|
|
|
cur_row = data->cur_row;
|
|
if (cur_row > 2729)
|
|
cur_row = 2729;
|
|
|
|
/* Fixed compensation per-line */
|
|
line_comp[0] = data->cpc->LINEy[cur_row];
|
|
line_comp[1] = data->cpc->LINEm[cur_row];
|
|
line_comp[2] = data->cpc->LINEc[cur_row];
|
|
|
|
#if 0
|
|
/* EK305 and K60 have only LINEy in their tables. Copy over to the others! */
|
|
if (!line_comp[1]) line_comp[1] = line_comp[0];
|
|
if (!line_comp[2]) line_comp[2] = line_comp[0];
|
|
#endif
|
|
|
|
/* Fill in shoulders of the row */
|
|
memcpy(first - 9, first, sizeof(*first)*3); // Copy first pixel to pre-buffer
|
|
memcpy(first - 6, first, sizeof(*first)*3);
|
|
memcpy(first - 3, first, sizeof(*first)*3);
|
|
memcpy(last + 3, last, sizeof(*last)*3); // Copy last pixel to post-buffer
|
|
memcpy(last + 6, last, sizeof(*last)*3);
|
|
memcpy(last + 9, last, sizeof(*last)*3);
|
|
|
|
/* Work out the compensation factors for each pixel in the row */
|
|
offset = 0;
|
|
for (i = 0; i < data->columns; i++) {
|
|
for (k = 0; k < 3 ; k++) {
|
|
int v11;
|
|
|
|
/* Compute starting point for next TTD row, weighing the adjacent pixels based on the HK factor.. */
|
|
data->htd_ttd_next[offset] = hk[0] * (first[offset] + first[offset]) +
|
|
hk[1] * (first[offset - 3] + first[offset + 3]) +
|
|
hk[2] * (first[offset - 6] + first[offset + 6]) +
|
|
hk[3] * (first[offset - 9] + first[offset + 9]);
|
|
|
|
/* Add in fixed per-line compensation */
|
|
out[offset] = in[offset] + line_comp[k];
|
|
|
|
/* Cap and Scale */
|
|
v11 = out[offset];
|
|
if ( out[offset] > 65535.0 ) {
|
|
out[offset] = 65535.0;
|
|
v11 = 127;
|
|
} else if (out[offset] < 0.0) {
|
|
out[offset] = 0.0;
|
|
v11 = 0;
|
|
} else {
|
|
v11 >>= 9;
|
|
}
|
|
|
|
/* Increment buckets */
|
|
data->htd_fcc_scratch[k][v11]++;
|
|
offset++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void CImageEffect70_CalcTTD(struct CImageEffect70 *data,
|
|
const uint16_t *in, double *out)
|
|
{
|
|
double *km, *kp, *osm, *osp, *ksm, *ksp;
|
|
double *sharp = NULL;
|
|
uint32_t i;
|
|
ksp = data->cpc->KSP; // KS Plus
|
|
ksm = data->cpc->KSM; // KS Minus
|
|
osp = data->cpc->OSP; // OS Plus
|
|
osm = data->cpc->OSM; // OS Minus
|
|
kp = data->cpc->KP; // K Plus
|
|
km = data->cpc->KM; // K Minus
|
|
|
|
/* If we have sharpening turned on, set up the state */
|
|
if (data->sharpen >= 0)
|
|
sharp = &data->cpc->SHK[8 * data->sharpen];
|
|
|
|
/* For each pixel in the row.. Note this is not color/plane dependent */
|
|
for (i = 0 ; i < data->band_pixels ; i++) {
|
|
double v4, v6, v7, input;
|
|
int v29;
|
|
double ks_comp_f, k_comp, ks_comp, os_comp, sharp_comp;
|
|
int j, k;
|
|
|
|
/* Starting point is the carry-over from the previous row minus
|
|
the new pixel */
|
|
input = in[i];
|
|
v7 = data->htd_ttd_next[i] - input;
|
|
v29 = v7;
|
|
if (v29 >= 0) {
|
|
int v31 = 127;
|
|
if (v29 <= 65535)
|
|
v31 = v29 >> 9;
|
|
ks_comp = ksp[v31];
|
|
} else {
|
|
int v30 = 127;
|
|
if (-v29 <= 65535)
|
|
v30 = -v29 >> 9;
|
|
ks_comp = ksm[v30];
|
|
}
|
|
|
|
v6 = (v7 * ks_comp + input) - input; /* This WTF add/subtract is present in every version I've looked at. Leaving it here for completion's sake. */
|
|
v29 = v6;
|
|
if (v29 >= 0) {
|
|
int v27 = 127;
|
|
if (v29 <= 65535)
|
|
v27 = v29 >> 9;
|
|
os_comp = osp[v27];
|
|
} else {
|
|
int v26 = 127;
|
|
if (-v29 <= 65535)
|
|
v26 = -v29 >> 9;
|
|
os_comp = osm[v26];
|
|
}
|
|
|
|
k_comp = 0.0;
|
|
for ( j = 0 ; j < 11 ; j++) {
|
|
int val;
|
|
if (j == 5)
|
|
continue;
|
|
|
|
val = in[i] - data->linebuf_row[j][i];
|
|
if (val >= 0)
|
|
k_comp += kp[j] * val;
|
|
else
|
|
k_comp += km[j] * val;
|
|
}
|
|
|
|
sharp_comp = 0.0;
|
|
if (sharp) {
|
|
for (k = 0 ; k < 8 ; k++) {
|
|
sharp_comp += sharp[k] * (in[i] - data->linebuf_shrp[k][i]);
|
|
}
|
|
}
|
|
/* Update output state based on input plus the
|
|
various correction factors */
|
|
out[i] = input - v6 * os_comp + k_comp + sharp_comp;
|
|
|
|
/* Work out the state for HTD operation */
|
|
v4 = data->htd_ttd_next[i] - out[i];
|
|
v29 = v4;
|
|
if ( v29 >= 0 ) {
|
|
int v19 = 127;
|
|
if ( v29 <= 65535 )
|
|
v19 = v29 >> 9;
|
|
ks_comp_f = ksp[v19];
|
|
} else {
|
|
int v18 = 127;
|
|
if ( -v29 <= 65535 )
|
|
v18 = -v29 >> 9;
|
|
ks_comp_f = ksm[v18];
|
|
}
|
|
data->ttd_htd_first[i] = out[i] + v4 * ks_comp_f;
|
|
}
|
|
}
|
|
|
|
/* Work out the number of times the density of a given color in
|
|
a given area exceeds a threshold */
|
|
static void CImageEffect70_CalcSA(struct BandImage *img,
|
|
int invert, const int32_t *in,
|
|
int32_t revX, int32_t *out)
|
|
{
|
|
int cols, rows;
|
|
int16_t *buf, *ptr;
|
|
int stride;
|
|
int start_row, row, start_col, col;
|
|
|
|
cols = img->cols - img->origin_cols;
|
|
rows = img->rows - img->origin_rows;
|
|
|
|
if ( img->bytes_per_row >= 0 ) {
|
|
if ( invert ) {
|
|
stride = img->bytes_per_row >> 1;
|
|
buf = (int16_t*)img->imgbuf + stride * (rows - 1);
|
|
} else {
|
|
stride = -img->bytes_per_row >> 1;
|
|
buf = img->imgbuf;
|
|
}
|
|
} else {
|
|
if ( invert ) {
|
|
stride = img->bytes_per_row >> 1;
|
|
buf = img->imgbuf;
|
|
} else {
|
|
stride = -img->bytes_per_row >> 1;
|
|
buf = (int16_t*)img->imgbuf + stride * (rows - 1);
|
|
}
|
|
}
|
|
|
|
start_col = in[0];
|
|
start_row = in[1];
|
|
if ( cols > in[2] )
|
|
cols = in[2];
|
|
if ( rows > in[3] )
|
|
rows = in[3];
|
|
if ( start_row < 0 )
|
|
start_row = 0;
|
|
if ( start_col < 0 )
|
|
start_col = 0;
|
|
|
|
out[2] = 0;
|
|
out[1] = 0;
|
|
out[0] = 0;
|
|
|
|
ptr = buf - start_row * stride;
|
|
for ( row = start_row ; row < rows ; row++ ) {
|
|
int16_t *v18 = ptr + 3 * start_col;
|
|
for ( col = start_col ; col < cols ; col++) {
|
|
out[0] += (revX <= v18[0]);
|
|
out[1] += (revX <= v18[1]);
|
|
out[2] += (revX <= v18[2]);
|
|
v18 += 3;
|
|
}
|
|
ptr -= stride;
|
|
}
|
|
}
|
|
|
|
/* Returns 1 for OK, 0 for do NOT rewind! */
|
|
static int CImageEffect70_JudgeReverseSkipRibbon_int(struct BandImage *img,
|
|
const int32_t *REV,
|
|
int invert)
|
|
{
|
|
int32_t rows, cols;
|
|
|
|
int j;
|
|
|
|
rows = img->rows - img->origin_rows;
|
|
cols = img->cols - img->origin_cols;
|
|
|
|
/* Input rectangles: start_col, start_row, cols, rows */
|
|
int32_t v16[4] = { REV[0], REV[2], REV[1], rows };
|
|
int32_t v20[4] = { REV[1], 0, cols, rows };
|
|
int32_t v24[4] = { 0, 0, REV[0], rows };
|
|
int32_t v28[4] = { REV[0], 0, REV[1], REV[2] };
|
|
|
|
/* Output buffers */
|
|
int32_t v32[3] = { 0, 0, 0 };
|
|
int32_t v35[3] = { 0, 0, 0 };
|
|
int32_t v38[3] = { 0, 0, 0 };
|
|
int32_t v41[3] = { 0, 0, 0 };
|
|
|
|
/* Work out the density inherent in these areas */
|
|
CImageEffect70_CalcSA(img, invert, v24, REV[3], v32);
|
|
CImageEffect70_CalcSA(img, invert, v20, REV[7], v41);
|
|
CImageEffect70_CalcSA(img, invert, v16, REV[11], v38);
|
|
CImageEffect70_CalcSA(img, invert, v28, REV[15], v35);
|
|
|
|
for (j = 0 ; j < 3 ; j++) {
|
|
if ( v32[j] >= REV[4] &&
|
|
(v32[j] >= REV[5] || v38[j] >= REV[14] || v35[j] >= REV[18]) ) {
|
|
return 0;
|
|
}
|
|
|
|
if ( v41[j] >= REV[8] &&
|
|
(v41[j] >= REV[9] || v38[j] >= REV[14] || v35[j] >= REV[18]) ) {
|
|
return 0;
|
|
}
|
|
|
|
if ( v38[j] >= REV[12] &&
|
|
(v38[j] >= REV[13] || v32[j] >= REV[6] || v41[j] >= REV[10] || v35[j] >= REV[18]) ) {
|
|
return 0;
|
|
}
|
|
|
|
if ( v35[j] >= REV[16] &&
|
|
(v35[j] >= REV[17] || v32[j] >= REV[6] || v41[j] >= REV[10] || v38[j] >= REV[14]) ) {
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* called twice, once with param1 == 1, once with param1 == 2.
|
|
Returns 0 for DO NOT REWIND, 1 for REWIND OK */
|
|
static int CImageEffect70_JudgeReverseSkipRibbon(struct CPCData *cpc,
|
|
struct BandImage *img,
|
|
int cols, int rows,
|
|
int param1)
|
|
{
|
|
int offset = -1;
|
|
|
|
if (cpc->num_rev == 10) { /* EK305 only */
|
|
if (cols == 0x748) { // 6"
|
|
if (rows == 0x4c2) { // 6x4"
|
|
if (param1 == 1)
|
|
offset = 0; // REV[0][0] aka 6x4" p1
|
|
else if (param1 == 2)
|
|
offset = 2; // REV[2][0] aka 6x4" p2
|
|
} else if (rows == 0x39e ) { // 6x3"
|
|
if (param1 == 1)
|
|
offset = 5; // REV[5][0] aka 6x3" p1
|
|
else if (param1 == 2)
|
|
offset = 8; // REV[8][0] aka 6x3" p2
|
|
} else if (rows == 0x270 ) { // 6x2"
|
|
if (param1 == 1)
|
|
offset = 4; // REV[4][0] aka 6x2" p1
|
|
else if (param1 == 2)
|
|
offset = 7; // REV[7][0] aka 6x2" p2
|
|
}
|
|
} else if (cols == 0x620) { // 5"
|
|
if (rows == 0x434) {
|
|
if (param1 == 1) // 5x3.5"
|
|
offset = 1; // REV[1][0] aka 5x3.5" p1
|
|
else if (param1 == 2)
|
|
offset = 3; // REV[3][0] aka 5x3.5" p2
|
|
} else if (rows == 0x39e) { // 5x3"
|
|
if (param1 == 1)
|
|
offset = 6; // REV[6][0] aka 5x3" p1
|
|
else if (param1 == 2)
|
|
offset = 9; // REV[9][0] aka 5x3" p2
|
|
}
|
|
}
|
|
} else if (cpc->num_rev == 4) { /* All others */
|
|
// XXX D80 always seems to call this with param1 = 1, and 6" paper type
|
|
if (param1 == 1) {
|
|
if (cols == 0x748) {
|
|
offset = 0; // 6"
|
|
} else {
|
|
offset = 1; // 5"
|
|
}
|
|
} else {
|
|
if (cols == 0x748) {
|
|
offset = 2; // 6"
|
|
} else {
|
|
offset = 3; // 5"
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Make sure we have a table entry; if not, no rewind for you! */
|
|
if (offset == -1)
|
|
return 0;
|
|
|
|
if (19*offset >= cpc->num_rev || !cpc->REV[19*offset])
|
|
return 0;
|
|
|
|
return CImageEffect70_JudgeReverseSkipRibbon_int(img, &cpc->REV[19*offset], 1);
|
|
}
|
|
|
|
static void CImageEffect70_DoConv(struct CImageEffect70 *data,
|
|
struct CPCData *cpc,
|
|
struct BandImage *in,
|
|
struct BandImage *out,
|
|
int sharpen)
|
|
{
|
|
double maxval[3];
|
|
double *v9 = NULL;
|
|
double *v10 = NULL;
|
|
|
|
uint32_t i, j;
|
|
int offset;
|
|
int outstride;
|
|
uint16_t *outptr;
|
|
uint16_t *inptr;
|
|
|
|
CImageEffect70_InitMidData(data);
|
|
|
|
if (sharpen > 8)
|
|
sharpen = 8;
|
|
data->sharpen = sharpen;
|
|
|
|
data->fhdiv_up = cpc->FH[0];
|
|
data->fhdiv_dn = cpc->FH[1];
|
|
data->fh_cur = cpc->FH[2];
|
|
data->fh_prev1 = cpc->FH[3] - cpc->FH[2];
|
|
data->fh_prev2 = cpc->FH[4] - cpc->FH[3];
|
|
data->fh_prev3 = cpc->FH[4];
|
|
|
|
data->columns = in->cols - in->origin_cols;
|
|
data->rows = in->rows - in->origin_rows;
|
|
data->band_pixels = data->columns * 3;
|
|
|
|
if (data->columns <= 0 || data->rows <= 0 ||
|
|
cpc->FH[0] < 1.0 || cpc->FH[1] < 1.0)
|
|
return;
|
|
|
|
if (in->bytes_per_row >= 0) {
|
|
data->pixel_count = in->bytes_per_row / sizeof(uint16_t); // numbers of pixels per input band
|
|
|
|
outstride = out->bytes_per_row / sizeof(uint16_t); // pixels per dest band
|
|
inptr = (uint16_t*) in->imgbuf + data->pixel_count * (data->rows - 1); // ie last row of input buffer
|
|
outptr = (uint16_t*) out->imgbuf + outstride * (data->rows - 1); // last row of output buffer
|
|
} else {
|
|
data->pixel_count = -in->bytes_per_row / sizeof(uint16_t);
|
|
outstride = out->bytes_per_row / sizeof(uint16_t);
|
|
inptr = in->imgbuf;
|
|
outptr = out->imgbuf;
|
|
}
|
|
|
|
CImageEffect70_CreateMidData(data);
|
|
|
|
v10 = malloc(data->band_pixels * sizeof(double));
|
|
memset(v10, 0, (data->band_pixels * sizeof(double)));
|
|
v9 = malloc(data->band_pixels * sizeof(double));
|
|
memset(v9, 0, (data->band_pixels * sizeof(double)));
|
|
maxval[0] = cpc->GNMby[255];
|
|
maxval[1] = cpc->GNMgm[255];
|
|
maxval[2] = cpc->GNMrc[255];
|
|
|
|
/* Initialize ttd_next structures */
|
|
offset = 0;
|
|
for(j = 0; j < data->columns ; j++) {
|
|
for (i = 0 ; i < 3 ; i++) {
|
|
data->htd_ttd_next[offset++] = maxval[i];
|
|
}
|
|
}
|
|
|
|
CImageEffect70_Sharp_PrepareLine(data, inptr);
|
|
|
|
if (data->sharpen >= 0)
|
|
CImageEffect70_Sharp_SetRefPtr(data);
|
|
|
|
for (data->cur_row = 0 ; data->cur_row < data->rows ; data->cur_row++) {
|
|
if (data->cur_row + 5 < data->rows)
|
|
CImageEffect70_Sharp_CopyLine(data, 5, inptr, 5);
|
|
CImageEffect70_CalcTTD(data, inptr, v10);
|
|
CImageEffect70_CalcHTD(data, v10, v9);
|
|
CImageEffect70_CalcFCC(data);
|
|
CImageEffect70_CalcYMC6(data, v9, outptr);
|
|
inptr -= data->pixel_count; // work backwards one input row
|
|
outptr -= outstride; // work backwards one output row
|
|
CImageEffect70_Sharp_ShiftLine(data);
|
|
}
|
|
CImageEffect70_DeleteMidData(data);
|
|
|
|
if (v10)
|
|
free(v10);
|
|
if (v9)
|
|
free(v9);
|
|
}
|
|
|
|
static void CImageEffect70_DoGamma(struct CImageEffect70 *data, struct BandImage *input, struct BandImage *out, int reverse)
|
|
{
|
|
int cols, rows;
|
|
int i, j;
|
|
|
|
uint8_t *outptr, *inptr;
|
|
uint32_t in_stride, out_stride;
|
|
|
|
struct CPCData *cpc = data->cpc;
|
|
|
|
cols = input->cols - input->origin_cols;
|
|
rows = input->rows - input->origin_rows;
|
|
|
|
in_stride = abs(input->bytes_per_row);
|
|
out_stride = abs(out->bytes_per_row);
|
|
|
|
if (cols <= 0 || rows <= 0)
|
|
return;
|
|
|
|
inptr = (uint8_t*) input->imgbuf;
|
|
outptr = out->imgbuf;
|
|
|
|
/* HACK: Reverse the row data when we perform gamma correction,
|
|
because Old Gutenprint sends it in the wrong order. */
|
|
for (i = 0; i < rows; i++) {
|
|
uint8_t *v10 = inptr;
|
|
uint16_t *v9 = (uint16_t*)outptr;
|
|
if (reverse)
|
|
v9 += (cols - 1) * 3;
|
|
for (j = 0 ; j < cols ; j++) {
|
|
v9[0] = cpc->GNMby[v10[0]];
|
|
v9[1] = cpc->GNMgm[v10[1]];
|
|
v9[2] = cpc->GNMrc[v10[2]];
|
|
v10 += 3;
|
|
if (reverse)
|
|
v9 -= 3;
|
|
else
|
|
v9 += 3;
|
|
}
|
|
inptr += in_stride;
|
|
outptr += out_stride;
|
|
}
|
|
}
|
|
|
|
void dump_announce(FILE *fp)
|
|
{
|
|
fprintf(fp, "INFO: libMitsuD70ImageReProcess version '%s' API %d\n", LIB_VERSION, LIB_APIVERSION);
|
|
fprintf(fp, "INFO: Copyright (c) 2016-2023 Solomon Peachy\n");
|
|
fprintf(fp, "INFO: This free software comes with ABSOLUTELY NO WARRANTY!\n");
|
|
fprintf(fp, "INFO: Licensed under the GNU GPLv3.\n");
|
|
fprintf(fp, "INFO: *** This code is NOT supported or endorsed by Mitsubishi! ***\n");
|
|
}
|
|
|
|
int do_image_effect80(struct CPCData *cpc, struct CPCData *ecpc, struct BandImage *input, struct BandImage *output, int sharpen, int reverse, uint8_t rew[3])
|
|
{
|
|
struct CImageEffect70 *data;
|
|
|
|
data = CImageEffect70_Create(cpc);
|
|
if (!data)
|
|
return -1;
|
|
|
|
CImageEffect70_DoGamma(data, input, output, reverse);
|
|
|
|
if (ecpc && (cpc->REV[0])) {
|
|
int result = CImageEffect70_JudgeReverseSkipRibbon(cpc, output, input->cols, input->rows, 1);
|
|
if (!result) {
|
|
/* Switch to ecpc file, and try again */
|
|
CImageEffect70_Destroy(data);
|
|
data = CImageEffect70_Create(ecpc);
|
|
if (!data)
|
|
return -1;
|
|
|
|
CImageEffect70_DoGamma(data, input, output, reverse);
|
|
}
|
|
rew[2] = result;
|
|
}
|
|
|
|
rew[1] = 1;
|
|
// rew[0] = 1;
|
|
// XXX on D80, speed == 0 seems to be the _only_ determination of rewind disable.
|
|
|
|
CImageEffect70_DoConv(data, cpc, output, output, sharpen);
|
|
|
|
CImageEffect70_Destroy(data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int do_image_effect60(struct CPCData *cpc, struct CPCData *ecpc, struct BandImage *input, struct BandImage *output, int sharpen, int reverse, uint8_t rew[3])
|
|
{
|
|
struct CImageEffect70 *data;
|
|
|
|
UNUSED(ecpc);
|
|
|
|
data = CImageEffect70_Create(cpc);
|
|
if (!data)
|
|
return -1;
|
|
|
|
CImageEffect70_DoGamma(data, input, output, reverse);
|
|
CImageEffect70_DoConv(data, cpc, output, output, sharpen);
|
|
|
|
/* Figure out if we can get away with rewinding, or not... */
|
|
if (cpc->REV[0]) {
|
|
rew[0] = CImageEffect70_JudgeReverseSkipRibbon(cpc, output, input->cols, input->rows, 1);
|
|
rew[1] = CImageEffect70_JudgeReverseSkipRibbon(cpc, output, input->cols, input->rows, 2);
|
|
}
|
|
|
|
CImageEffect70_Destroy(data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int do_image_effect70(struct CPCData *cpc, struct CPCData *ecpc, struct BandImage *input, struct BandImage *output, int sharpen, int reverse, uint8_t rew[3])
|
|
{
|
|
struct CImageEffect70 *data;
|
|
|
|
UNUSED(ecpc);
|
|
UNUSED(rew);
|
|
|
|
data = CImageEffect70_Create(cpc);
|
|
if (!data)
|
|
return -1;
|
|
|
|
CImageEffect70_DoGamma(data, input, output, reverse);
|
|
CImageEffect70_DoConv(data, cpc, output, output, sharpen);
|
|
CImageEffect70_Destroy(data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int send_image_data(struct BandImage *out, void *context,
|
|
int (*callback_fn)(void *context, void *buffer, uint32_t len))
|
|
{
|
|
uint32_t rows, cols;
|
|
uint16_t *buf;
|
|
uint32_t i, j, k;
|
|
int ret = 1;
|
|
uint16_t *v15;
|
|
size_t count;
|
|
|
|
cols = out->cols - out->origin_cols;
|
|
rows = out->rows - out->origin_rows;
|
|
buf = malloc(CHUNK_LEN);
|
|
if (!buf)
|
|
goto done;
|
|
if (!callback_fn)
|
|
goto done;
|
|
|
|
if (out->bytes_per_row > 0) {
|
|
v15 = (uint16_t*)((uint8_t*)out->imgbuf + ((rows - 1) * out->bytes_per_row));
|
|
} else {
|
|
v15 = out->imgbuf;
|
|
}
|
|
|
|
for ( i = 0 ; i < 3 ; i++) {
|
|
uint16_t *v13 = &v15[i];
|
|
uint16_t *outptr = buf;
|
|
|
|
count = 0;
|
|
memset(buf, 0, CHUNK_LEN);
|
|
for (j = 0 ; j < rows ; j++) {
|
|
uint16_t *v9 = v13;
|
|
for (k = 0 ; k < cols ; k++) {
|
|
*outptr++ = cpu_to_be16(*v9);
|
|
v9 += 3;
|
|
count += 2;
|
|
if ( count == CHUNK_LEN )
|
|
{
|
|
if (callback_fn(context, buf, count))
|
|
goto done;
|
|
count = 0;
|
|
outptr = buf;
|
|
memset(buf, 0, CHUNK_LEN);
|
|
}
|
|
}
|
|
v13 -= out->bytes_per_row / sizeof(uint16_t);
|
|
}
|
|
if (count) {
|
|
if (callback_fn(context, buf, (count + 511) / 512 * 512))
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
done:
|
|
if (buf)
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
|
|
#if 0
|
|
|
|
struct PrintSetting {
|
|
uint32_t pad; // @0
|
|
uint32_t cols; // @4
|
|
uint32_t rows; // @8
|
|
uint32_t deck; // @12 // Deck selection, 0, 1, 2.
|
|
uint32_t sharp; // @16 // sharpness, -1 is none, 0->8, 4 is normal
|
|
uint32_t matte; // @20 // 1 or 0.
|
|
uint32_t mode; // @24 // 0/1/2 Fine/SuperFine/Ultrafine
|
|
uint32_t cconv; // @28 // -1/0 none/Table1
|
|
uint32_t unk6; // @32 // multicut (0, 4x6*2 = 1, 2x6*2 = 5)
|
|
uint32_t unk7; // @36 // image doubled up, (0 or 1)
|
|
uint16_t unk8; // @40 // actual print cols in image doubled mode
|
|
uint16_t unk9; // @42 // actual print rows in image doubled mode
|
|
uint32_t unk10; // @44 // rows to cut in doubled mode? (38 for 4x6T2)
|
|
uint16_t unk11; // @48 // 4x6 doubled mode, type 3. (0 or 1) ??
|
|
uint16_t gammaR; // @50
|
|
uint16_t gammaG; // @52
|
|
uint16_t gammaB; // @54
|
|
uint16_t brighr; // @56
|
|
uint16_t brighg; // @58
|
|
uint16_t brighb; // @60
|
|
uint16_t contrr; // @62
|
|
uint16_t cpntrg; // @64
|
|
uint16_t contrb; // @66
|
|
};
|
|
|
|
// XXX replace 'args'. buf is >= 512 bytes.
|
|
void create_header(uint8_t *buf, uint8_t *args, uint16_t *dim, uint16_t *dim_lam)
|
|
{
|
|
int laminate;
|
|
int deck;
|
|
|
|
memset((buf + 4), 0, 0x1FCu);
|
|
*(buf + 4) = 0;
|
|
*(buf + 5) = 0;
|
|
*(buf + 16) = dim[0] >> 8;
|
|
*(buf + 17) = dim[0];
|
|
*(buf + 18) = dim[1] >> 8;
|
|
*(buf + 19) = dim[1];
|
|
if ( *(uint32_t *)(args + 12) ) { // laminate dimensions
|
|
*(buf + 20) = dim_lam[0] >> 8;
|
|
*(buf + 21) = dim_lam[0];
|
|
*(buf + 22) = dim_lam[1] >> 8;
|
|
*(buf + 23) = dim_lam[1];
|
|
}
|
|
laminate = *(uint32_t *)(args + 4); // speed/mode
|
|
if ( laminate == 1 ) {
|
|
*(buf + 24) = 3;
|
|
} else if ( laminate == 2 ) {
|
|
*(buf + 24) = 4;
|
|
} else {
|
|
*(buf + 24) = 0;
|
|
}
|
|
deck = *(uint32_t *)(args + 8); // deck
|
|
if ( deck == 1 )
|
|
*(buf + 32) = 2;
|
|
else
|
|
*(buf + 32) = deck == 2;
|
|
*(buf + 40) = 0;
|
|
if ( *(uint32_t *)(args + 12) ) // laminate type
|
|
*(buf + 41) = 2;
|
|
else
|
|
*(buf + 41) = 0;
|
|
|
|
*(buf + 48) = *(uint32_t *)(args + 16); // multicut mode
|
|
}
|
|
|
|
void CImageUtility_CreateBandImage16(struct BandImage *img, uint32_t *a2, int32_t a3)
|
|
{
|
|
img->bytes_per_row = 3 * sizeof(uint16_t) * (a2[2] - a2[0]);
|
|
if (a3 < 0)
|
|
img->bytes_per_row = -img->bytes_per_row;
|
|
img->origin_cols = a2[0]; // origin_cols
|
|
img->origin_rows = a2[1]; // origin_rows
|
|
img->cols = a2[2]; // cols
|
|
img->rows = a2[3]; // rows
|
|
|
|
img->imgbuf = malloc(3 * sizeof(uint16_t) * (a2[3] - a2[1]) * (a2[2] - a2[0]));
|
|
}
|
|
|
|
#endif
|
|
|
|
/* CP98xx family */
|
|
|
|
struct CP98xx_WMAM {
|
|
/* @ 0 */ double unka[256];
|
|
/* @ 2048 */ double unkb[256];
|
|
/* @ 4096 */ double unkc[5]; /* Weight factors */
|
|
/* @ 4136 */ double unkd[256];
|
|
/* @ 6184 */ double unke[256]; // *= sharp->coef[X]
|
|
/* @ 8232 */ double unkf[5]; /* Weight factors */
|
|
/* @ 8272 */ double unkg[256];
|
|
/* @10320 */
|
|
} __attribute__((packed));
|
|
|
|
/* CP98xx Tabular Data, as stored in data file! */
|
|
struct mitsu98xx_data {
|
|
/* @ 0 */ uint16_t GNMby[256]; /* BGR Order uncertain */
|
|
/* @ 512 */ uint16_t GNMgm[256];
|
|
/* @ 1024 */ uint16_t GNMrc[256];
|
|
/* @ 1536 */ int16_t sharp[20]; /* Actual format is: u16, u16[9], u16, u16[9] */
|
|
/* @ 1576 */ double GammaAdj[3];
|
|
/* @ 1600 */ struct CP98xx_WMAM WMAM;
|
|
/* @11920 */ double sharp_coef[11]; /* 0 is off, 1-10 are the levels. Default is 5. [4 in settings] */
|
|
/* @12008 */ int32_t KHStart;
|
|
/* @12012 */ int32_t KHEnd;
|
|
/* @12016 */ int32_t KHStep;
|
|
/* @12020 */ double KH[256];
|
|
/* @14068 */
|
|
} __attribute__((packed));
|
|
|
|
/* Informational purposes only */
|
|
struct mitsu98xx_tables {
|
|
struct mitsu98xx_data superfine;
|
|
struct mitsu98xx_data fine_std;
|
|
struct mitsu98xx_data fine_hg;
|
|
} __attribute__((packed));
|
|
|
|
#define M98XX_DATATABLE_SIZE 42204
|
|
|
|
STATIC_ASSERT(sizeof(struct mitsu98xx_data) *3 == M98XX_DATATABLE_SIZE);
|
|
|
|
/* Cooked versions for local state & manipulation */
|
|
struct CP98xx_KHParams {
|
|
double KH[256];
|
|
int32_t Start;
|
|
int32_t End;
|
|
int32_t Step;
|
|
};
|
|
|
|
struct CP98xx_GammaParams {
|
|
uint16_t GNMby[256];
|
|
uint16_t GNMgm[256];
|
|
uint16_t GNMrc[256];
|
|
double GammaAdj[3];
|
|
};
|
|
|
|
struct CP98xx_AptParams {
|
|
int16_t mask[8][6];
|
|
int unsharp;
|
|
int mpx10;
|
|
};
|
|
|
|
static double swab_double(double d)
|
|
{
|
|
union {
|
|
double d;
|
|
uint64_t n;
|
|
uint8_t c[sizeof(double)];
|
|
} u;
|
|
|
|
u.d = d;
|
|
u.n = __builtin_bswap64(u.n);
|
|
|
|
return u.d;
|
|
}
|
|
|
|
struct mitsu98xx_data *CP98xx_GetData(const char *filename)
|
|
{
|
|
struct mitsu98xx_data *data = NULL;
|
|
FILE *stream;
|
|
int rval;
|
|
|
|
if (!filename || !*filename)
|
|
return NULL;
|
|
|
|
data = malloc(M98XX_DATATABLE_SIZE);
|
|
if (!data)
|
|
return NULL;
|
|
|
|
stream = fopen(filename, "rb");
|
|
if (!stream) {
|
|
free(data);
|
|
return NULL;
|
|
}
|
|
|
|
fseek(stream, 0, SEEK_END);
|
|
if (ftell(stream) < M98XX_DATATABLE_SIZE) {
|
|
fclose(stream);
|
|
free(data);
|
|
return NULL;
|
|
}
|
|
fseek(stream, 0, SEEK_SET);
|
|
rval = fread(data, M98XX_DATATABLE_SIZE, 1, stream);
|
|
fclose(stream);
|
|
|
|
if (rval != 1) {
|
|
free(data);
|
|
return NULL;
|
|
}
|
|
|
|
/* Byteswap data table to native endianness, if necessary */
|
|
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
|
int i, j;
|
|
for (j = 0 ; j < 3 ; j++) {
|
|
data[j].KHStart = be32_to_cpu(data[j].KHStart);
|
|
data[j].KHEnd = be32_to_cpu(data[j].KHEnd);
|
|
data[j].KHStep = be32_to_cpu(data[j].KHStep);
|
|
for (i = 3 ; i < 3 ; i++) {
|
|
data[j].GammaAdj[i] = be64d_to_cpu(data[j].GammaAdj[i]);
|
|
}
|
|
for (i = 0 ; i < 5 ; i++) {
|
|
data[j].WMAM.unkc[i] = be64d_to_cpu(data[j].WMAM.unkc[i]);
|
|
data[j].WMAM.unkf[i] = be64d_to_cpu(data[j].WMAM.unkf[i]);
|
|
}
|
|
for (i = 0 ; i < 11 ; i++) {
|
|
data[j].sharp_coef[i] = be64d_to_cpu(data[j].sharp_coef[i]);
|
|
}
|
|
for (i = 0 ; i < 20 ; i++) {
|
|
data[j].sharp[i] = be16_to_cpu(data[j].sharp[i]);
|
|
}
|
|
for (i = 0 ; i < 256 ; i++) {
|
|
data[j].WMAM.unka[i] = be64d_to_cpu(data[j].WMAM.unka[i]);
|
|
data[j].WMAM.unkb[i] = be64d_to_cpu(data[j].WMAM.unkb[i]);
|
|
data[j].WMAM.unkd[i] = be64d_to_cpu(data[j].WMAM.unkd[i]);
|
|
data[j].WMAM.unke[i] = be64d_to_cpu(data[j].WMAM.unke[i]);
|
|
data[j].WMAM.unkg[i] = be64d_to_cpu(data[j].WMAM.unkg[i]);
|
|
|
|
data[j].GNMby[i] = be16_to_cpu(data[j].GNMby[i]);
|
|
data[j].GNMgm[i] = be16_to_cpu(data[j].GNMgm[i]);
|
|
data[j].GNMrc[i] = be16_to_cpu(data[j].GNMrc[i]);
|
|
|
|
data[j].KH[i] = be64d_to_cpu(data[j].KH[i]);
|
|
}
|
|
}
|
|
#endif
|
|
return data;
|
|
}
|
|
|
|
void CP98xx_DestroyData(const struct mitsu98xx_data *data)
|
|
{
|
|
free((void*)data);
|
|
}
|
|
|
|
/* return 1 if it's ok, 0 if bad */
|
|
static int CP98xx_DoCorrectGammaTbl(struct CP98xx_GammaParams *Gamma,
|
|
const struct CP98xx_KHParams *KH,
|
|
const struct BandImage *img)
|
|
{
|
|
int end, start, cols, rows, step, bytesPerRow;
|
|
int i, j;
|
|
int64_t elements, max;
|
|
uint8_t *rowPtr;
|
|
if (KH->Step < 1 || KH->End < KH->Start)
|
|
return 1;
|
|
|
|
if (KH->Start < 0 ||
|
|
img->cols <= KH->End ||
|
|
img->cols <= KH->Start ||
|
|
img->origin_cols ||
|
|
img->origin_rows)
|
|
return 0;
|
|
|
|
cols = img->cols - img->origin_cols;
|
|
rows = img->rows - img->origin_rows;
|
|
bytesPerRow = img->bytes_per_row;
|
|
|
|
if (bytesPerRow < 0) {
|
|
rowPtr = img->imgbuf;
|
|
} else {
|
|
rowPtr = (uint8_t*)img->imgbuf + (bytesPerRow * (rows - 1));
|
|
}
|
|
|
|
step = KH->Step;
|
|
end = KH->End;
|
|
start = KH->Start;
|
|
elements = (uint64_t) step * ((end - start) + 1);
|
|
max = elements * 0xff;
|
|
|
|
for (j = 0 ; j < rows / step ; j++) {
|
|
int k;
|
|
int64_t sum1, sum2, sum3, sum4, sum5, sum6;
|
|
int startcol = (cols - end) -1;
|
|
|
|
sum6 = sum5 = sum4 = sum3 = sum2 = sum1 = 0;
|
|
|
|
for (k = 0 ; k < step ; k++) {
|
|
int curCol, curRowBufOffset;
|
|
curRowBufOffset = start * 3;
|
|
for (curCol = start ; curCol < (end + 1) ; curCol++) {
|
|
sum3 += rowPtr[curRowBufOffset];
|
|
sum2 += rowPtr[curRowBufOffset + 1];
|
|
sum1 += rowPtr[curRowBufOffset + 2];
|
|
curRowBufOffset += 3;
|
|
}
|
|
curRowBufOffset = startcol * 3;
|
|
for (curCol = startcol ; curCol < (cols - start); curCol++) {
|
|
sum6 += rowPtr[curRowBufOffset];
|
|
sum5 += rowPtr[curRowBufOffset + 1];
|
|
sum4 += rowPtr[curRowBufOffset + 2];
|
|
curRowBufOffset += 3;
|
|
}
|
|
rowPtr -= bytesPerRow;
|
|
}
|
|
if (sum6 < max) {
|
|
max = sum6;
|
|
}
|
|
if (sum5 < max) {
|
|
max = sum5;
|
|
}
|
|
if (sum4 < max) {
|
|
max = sum4;
|
|
}
|
|
if (sum3 < max) {
|
|
max = sum3;
|
|
}
|
|
if (sum2 < max) {
|
|
max = sum2;
|
|
}
|
|
if (sum1 < max) {
|
|
max = sum1;
|
|
}
|
|
}
|
|
|
|
int gammaMaxCalc;
|
|
uint16_t baseRC, baseGM, baseBY;
|
|
double baseKH;
|
|
|
|
gammaMaxCalc = ((double)max / (double)elements) + 0.5;
|
|
|
|
baseRC = Gamma->GNMrc[255];
|
|
baseGM = Gamma->GNMgm[255];
|
|
baseBY = Gamma->GNMby[255];
|
|
baseKH = KH->KH[gammaMaxCalc];
|
|
|
|
for (i = 0; i < 256 ; i++) {
|
|
Gamma->GNMrc[i] = baseRC +
|
|
(baseKH * (Gamma->GNMrc[i] - baseRC)) + 0.5;
|
|
Gamma->GNMgm[i] = baseGM +
|
|
(baseKH * (Gamma->GNMgm[i] - baseGM)) + 0.5;
|
|
Gamma->GNMby[i] = baseBY +
|
|
(baseKH * (Gamma->GNMby[i] - baseBY)) + 0.5;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int CP98xx_DoGammaConv(struct CP98xx_GammaParams *Gamma,
|
|
const struct BandImage *inImage,
|
|
struct BandImage *outImage,
|
|
int reverse)
|
|
{
|
|
int cols, rows, inBytesPerRow, maxTank;
|
|
uint8_t *inRowPtr;
|
|
uint16_t *outRowPtr;
|
|
int pixelsPerRow;
|
|
|
|
cols = inImage->cols - inImage->origin_cols;
|
|
rows = inImage->rows - inImage->origin_rows;
|
|
/* Output always starts at end and works back */
|
|
pixelsPerRow = outImage->bytes_per_row >> 1;
|
|
outRowPtr = (uint16_t*)((uint8_t*)outImage->imgbuf + (pixelsPerRow * (rows-1) * sizeof(uint16_t)));
|
|
|
|
/* Input is another matter.. */
|
|
inBytesPerRow = inImage->bytes_per_row;
|
|
|
|
if ((cols < 1) || (rows < 1) || (inBytesPerRow == 0))
|
|
return 0;
|
|
|
|
if (inBytesPerRow < 0) { /* First row of input is at the beginning */
|
|
if (reverse) {
|
|
/* count backwards from end of buffer */
|
|
inBytesPerRow = -inBytesPerRow;
|
|
inRowPtr = (uint8_t*)inImage->imgbuf + (inBytesPerRow * (rows-1));
|
|
} else {
|
|
/* Count forward from start of buffer */
|
|
inRowPtr = inImage->imgbuf;
|
|
}
|
|
} else { /* First row of input is at the end */
|
|
if (reverse) {
|
|
/* Count forwards from start of buffer */
|
|
inBytesPerRow = -inBytesPerRow;
|
|
inRowPtr = (uint8_t*)inImage->imgbuf;
|
|
} else {
|
|
/* Count backwards from end of buffer */
|
|
inRowPtr = (uint8_t*)inImage->imgbuf + (inBytesPerRow * (rows-1));
|
|
}
|
|
}
|
|
|
|
maxTank = cols * 255;
|
|
|
|
int outVal;
|
|
int row, col;
|
|
int curRowBufOffset;
|
|
|
|
double gammaAdj2 = Gamma->GammaAdj[2];
|
|
double gammaAdj1 = Gamma->GammaAdj[1];
|
|
double gammaAdj0 = Gamma->GammaAdj[0];
|
|
|
|
/* Do gamma mapping with correction/adjustments... */
|
|
for (row = 0 ; row < rows && gammaAdj0 >= 0.5 ; row++) {
|
|
double calc3, calc2, calc1, calc0;
|
|
double gammaAdjX;
|
|
|
|
calc0 = calc1 = calc2 = 0.0;
|
|
|
|
for (col = 0, curRowBufOffset = 0 ; col < cols ; col++) {
|
|
calc2 += inRowPtr[2 + curRowBufOffset];
|
|
calc1 += inRowPtr[1 + curRowBufOffset];
|
|
calc0 += inRowPtr[0 + curRowBufOffset];
|
|
curRowBufOffset += 3;
|
|
}
|
|
|
|
calc3 = ((maxTank - calc0) + (maxTank - calc1) + (maxTank - calc2)) / (cols * 3);
|
|
|
|
gammaAdjX = ((gammaAdj0 + (((calc3 * gammaAdj0) / 255.0) * gammaAdj1) / -4095.0) * gammaAdj2) / 4095.0;
|
|
|
|
/* Input and output order are BGR and YMC! */
|
|
for (col = 0, curRowBufOffset = 0; col < cols ; col++) {
|
|
outVal = Gamma->GNMby[inRowPtr[curRowBufOffset]] + gammaAdjX + 0.5;
|
|
if (outVal < 0x1000) {
|
|
if (outVal < 0) {
|
|
outRowPtr[curRowBufOffset] = 0;
|
|
} else {
|
|
outRowPtr[curRowBufOffset] = outVal;
|
|
}
|
|
} else {
|
|
outRowPtr[curRowBufOffset] = 0xfff;
|
|
}
|
|
|
|
outVal = Gamma->GNMgm[inRowPtr[curRowBufOffset + 1]] + gammaAdjX + 0.5;
|
|
if (outVal < 0x1000) {
|
|
if (outVal < 0) {
|
|
outRowPtr[curRowBufOffset + 1] = 0;
|
|
} else {
|
|
outRowPtr[curRowBufOffset + 1] = outVal;
|
|
}
|
|
} else {
|
|
outRowPtr[curRowBufOffset + 1] = 0xfff;
|
|
}
|
|
|
|
outVal = Gamma->GNMrc[inRowPtr[curRowBufOffset + 2]] + gammaAdjX + 0.5;
|
|
if (outVal < 0x1000) {
|
|
if (outVal < 0) {
|
|
outRowPtr[curRowBufOffset + 2] = 0;
|
|
} else {
|
|
outRowPtr[curRowBufOffset + 2] = outVal;
|
|
}
|
|
} else {
|
|
outRowPtr[curRowBufOffset + 2] = 0xfff;
|
|
}
|
|
curRowBufOffset += 3;
|
|
}
|
|
|
|
inRowPtr -= inBytesPerRow;
|
|
outRowPtr -= pixelsPerRow;
|
|
}
|
|
|
|
/* ...and pick up where the first loop left off, if we don't need adjustments */
|
|
for ( ; row < rows ; row++) {
|
|
for (col = 0, curRowBufOffset = 0 ; col < cols ; col ++, curRowBufOffset += 3) {
|
|
/* Mitsu code treats input as RGB, we always use BGR. */
|
|
outRowPtr[curRowBufOffset] = Gamma->GNMby[inRowPtr[curRowBufOffset]];
|
|
outRowPtr[curRowBufOffset + 1] = Gamma->GNMgm[inRowPtr[curRowBufOffset + 1]];
|
|
outRowPtr[curRowBufOffset + 2] = Gamma->GNMrc[inRowPtr[curRowBufOffset + 2]];
|
|
}
|
|
inRowPtr -= inBytesPerRow;
|
|
outRowPtr -= pixelsPerRow;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void CP98xx_InitAptParams(const struct mitsu98xx_data *table, struct CP98xx_AptParams *APT, int sharpness)
|
|
{
|
|
int i, j;
|
|
double sharpCoef;
|
|
|
|
APT->unsharp = 0;
|
|
APT->mpx10 = 1;
|
|
|
|
sharpCoef = table->sharp_coef[sharpness];
|
|
for (i = 2, j = 0 ; j < 8 ; i++, j++) {
|
|
APT->mask[j][5] = table->sharp[1];
|
|
APT->mask[j][4] = sharpCoef * table->sharp[i] + 0.5;
|
|
APT->mask[j][3] = table->sharp[11];
|
|
APT->mask[j][2] = sharpCoef * table->sharp[i+10] + 0.5;
|
|
}
|
|
|
|
APT->mask[0][0] = -table->sharp[10];
|
|
APT->mask[0][1] = -table->sharp[0];
|
|
APT->mask[1][0] = 0;
|
|
APT->mask[1][1] = -table->sharp[0];
|
|
APT->mask[2][0] = table->sharp[10];
|
|
APT->mask[2][1] = -table->sharp[0];
|
|
APT->mask[3][0] = -table->sharp[10];
|
|
APT->mask[3][1] = 0;
|
|
APT->mask[4][0] = table->sharp[10];
|
|
APT->mask[4][1] = 0;
|
|
APT->mask[5][0] = -table->sharp[10];
|
|
APT->mask[5][1] = table->sharp[0];
|
|
APT->mask[6][0] = table->sharp[10];
|
|
APT->mask[6][1] = table->sharp[0];
|
|
APT->mask[7][0] = 0;
|
|
APT->mask[7][1] = table->sharp[0];
|
|
}
|
|
|
|
#if 0
|
|
static int CP98xx_DoAptMWithParams(const struct CP98xx_AptParams *pAPT,
|
|
const struct BandImage *img,
|
|
int always_0)
|
|
{
|
|
uint8_t *imgBuf;
|
|
int cols, rows;
|
|
int i;
|
|
struct CP98xx_AptParams APT;
|
|
|
|
if (!img || !img->imgbuf || !pAPT)
|
|
return 0;
|
|
|
|
cols = img->cols - img->origin_cols;
|
|
rows = img->rows - img->origin_rows;
|
|
|
|
if (cols <= 0 || rows <= 3 || img->bytes_per_row == 0)
|
|
return 0;
|
|
|
|
memcpy(&APT, pAPT, sizeof(APT));
|
|
|
|
if (always_0) {
|
|
// XXXX revise_aptSet(this,0,always_0);
|
|
}
|
|
|
|
for (i = 0 ; i < 8 ; i++) {
|
|
APT.mask[i][0] = pAPT->mask[i][1];
|
|
APT.mask[i][1] = pAPT->mask[i][0];
|
|
}
|
|
// XXXX set_apttable(this);
|
|
// XXXX if (get_linebuf(this) == 0) {
|
|
// free_linebuf(this);
|
|
// return 0;
|
|
//}
|
|
|
|
#if 0
|
|
pCVar7 = this;
|
|
pCVar5 = this;
|
|
do {
|
|
j = -7;
|
|
pvVar3 = this->LineBuf[3 - pCVar5->mask[0]];
|
|
while (j < pCVar5->mask[1]) {
|
|
lVar9 = 3;
|
|
do {
|
|
lVar9 = lVar9 + -1;
|
|
} while (lVar9 != 0);
|
|
j = j + 1;
|
|
pvVar3 = (void *)((int)pvVar3 + 3);
|
|
}
|
|
pCVar5 = (CImageEffect *)(pCVar5->mask + 1);
|
|
*(void **)pCVar7->field_0x44 = pvVar3;
|
|
pCVar7 = (CImageEffect *)pCVar7->mask;
|
|
} while ((CImageEffect *)(this->mask + 0xf) != pCVar5);
|
|
puVar6 = (undefined4 *)this->LineBuf[3];
|
|
k = 7;
|
|
do {
|
|
lVar9 = 3;
|
|
do {
|
|
lVar9 = lVar9 + -1;
|
|
} while (lVar9 != 0);
|
|
k = k + -1;
|
|
puVar6 = (undefined4 *)((int)puVar6 + 3);
|
|
} while (k != 0);
|
|
this->field_0x64 = puVar6;
|
|
if (BandImage->bytesPerRow < 0) {
|
|
imgRow = (uchar *)BandImage->imgBuf;
|
|
}
|
|
else {
|
|
iVar4 = BandImage->bytesPerRow * (rows + -1);
|
|
imgBuf = (void *)((int)imgBuf + iVar4);
|
|
imgRow = (uchar *)(iVar4 + (int)BandImage->imgBuf);
|
|
}
|
|
mv_line(this,(uchar *)this->LineBuf[0],imgRow);
|
|
imgRow = imgRow + -BandImage->bytesPerRow;
|
|
pCVar7 = this;
|
|
do {
|
|
ppvVar2 = pCVar7->LineBuf;
|
|
pCVar7 = (CImageEffect *)pCVar7->mask;
|
|
_memcpy(ppvVar2[1],this->LineBuf[0],this->colsPadded * 3);
|
|
} while (pCVar7 != (CImageEffect *)(this->mask + 5));
|
|
rowPixels = (dword)cols * 3;
|
|
uVar8 = 1;
|
|
pCVar7 = (CImageEffect *)(this[-1].APT.mask + 0x28);
|
|
while (pCVar5 = this, uVar8 < this->rows) {
|
|
do {
|
|
mpx10 = (CImageEffect *)&pCVar5[-1].APT.mpx10;
|
|
_memcpy(pCVar5->LineBufTmp,pCVar5->LineBuf[5],this->colsPadded * 3);
|
|
pCVar5 = mpx10;
|
|
} while (mpx10 != pCVar7);
|
|
mv_line(this,(uchar *)this->LineBuf[0],imgRow);
|
|
imgRow = imgRow + -BandImage->bytesPerRow;
|
|
if (2 < uVar8) {
|
|
calc_independentM(this);
|
|
_memcpy(imgBuf,this->lineBufRaw,rowPixels);
|
|
imgBuf = (void *)((int)imgBuf - BandImage->bytesPerRow);
|
|
}
|
|
uVar8 = uVar8 + 1;
|
|
}
|
|
plane = 3;
|
|
do {
|
|
do {
|
|
mpx10 = (CImageEffect *)&pCVar5[-1].APT.mpx10;
|
|
_memcpy(pCVar5->LineBufTmp,pCVar5->LineBuf[5],this->colsPadded * 3);
|
|
pCVar5 = mpx10;
|
|
} while (mpx10 != pCVar7);
|
|
calc_independentM(this);
|
|
_memcpy(imgBuf,this->lineBufRaw,rowPixels);
|
|
plane = plane + -1;
|
|
imgBuf = (void *)((int)imgBuf - BandImage->bytesPerRow);
|
|
pCVar5 = this;
|
|
} while (plane != 0);
|
|
free_linebuf(this);
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
static void CP98xx_InitWMAM(struct CP98xx_WMAM *wmam, const struct CP98xx_WMAM *src)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0 ; i < 256 ; i++) {
|
|
wmam->unka[i] = src->unka[i] / 256.0;
|
|
wmam->unkb[i] = src->unkb[i] / 255.0;
|
|
wmam->unkd[i] = src->unkd[i] / 256.0;
|
|
wmam->unke[i] = src->unke[i] / 64.0;
|
|
wmam->unkg[i] = src->unkg[i] / 64.0;
|
|
}
|
|
memcpy(wmam->unkc, src->unkc, sizeof(wmam->unkc));
|
|
memcpy(wmam->unkf, src->unkf, sizeof(wmam->unkf));
|
|
}
|
|
|
|
static int CP98xx_DoWMAM(struct CP98xx_WMAM *wmam, struct BandImage *img, int reverse)
|
|
{
|
|
uint16_t *imgBufPtr, *rowPtr;
|
|
int rows, col, cols, pixelsPerRow, pixelCnt;
|
|
double *rowCalcBuf1, *rowCalcBuf2, *rowCalcBuf3, *rowCalcBuf4, *rowCalcBuf5;
|
|
double *rowCalcBuf3Ptr, *rowCalcBuf4Ptr, *rowCalcBuf4Base, *rowCalcBuf3Base;
|
|
|
|
int row;
|
|
int rowCalcBufLenB, rowCalcBufLen;
|
|
|
|
cols = img->cols - img->origin_cols;
|
|
rows = img->rows - img->origin_rows;
|
|
pixelsPerRow = img->bytes_per_row;
|
|
rowPtr = imgBufPtr = img->imgbuf;
|
|
|
|
if ((cols < 6) || (rows < 1) || (pixelsPerRow == 0))
|
|
return 0;
|
|
|
|
if (pixelsPerRow < 0) {
|
|
if (reverse) {
|
|
pixelsPerRow = pixelsPerRow >> 1;
|
|
} else {
|
|
pixelsPerRow = (-pixelsPerRow) >> 1;
|
|
rowPtr += pixelsPerRow * (rows -1);
|
|
imgBufPtr = rowPtr;
|
|
}
|
|
} else {
|
|
if (reverse) {
|
|
pixelsPerRow = pixelsPerRow >> 1;
|
|
rowPtr += pixelsPerRow * (rows -1);
|
|
imgBufPtr = rowPtr;
|
|
} else {
|
|
pixelsPerRow = (-pixelsPerRow) >> 1;
|
|
}
|
|
}
|
|
|
|
pixelCnt = cols * 3;
|
|
rowCalcBufLen = pixelCnt * sizeof(double);
|
|
rowCalcBufLenB = (pixelCnt + 24) * sizeof(double);
|
|
|
|
rowCalcBuf1 = malloc(rowCalcBufLen);
|
|
if (!rowCalcBuf1) {
|
|
return 0;
|
|
}
|
|
|
|
rowCalcBuf2 = malloc(rowCalcBufLen);
|
|
if (!rowCalcBuf2) {
|
|
free(rowCalcBuf1);
|
|
return 0;
|
|
}
|
|
|
|
rowCalcBuf3 = malloc(rowCalcBufLenB);
|
|
if (!rowCalcBuf3) {
|
|
free(rowCalcBuf2);
|
|
free(rowCalcBuf1);
|
|
return 0;
|
|
}
|
|
rowCalcBuf3Base = rowCalcBuf3 + 12;
|
|
|
|
rowCalcBuf4 = malloc(rowCalcBufLenB);
|
|
if (!rowCalcBuf4) {
|
|
free(rowCalcBuf3);
|
|
free(rowCalcBuf2);
|
|
free(rowCalcBuf1);
|
|
return 0;
|
|
}
|
|
rowCalcBuf4Base = rowCalcBuf4 + 12;
|
|
|
|
rowCalcBuf5 = malloc(rowCalcBufLen);
|
|
if (!rowCalcBuf5) {
|
|
free(rowCalcBuf4);
|
|
free(rowCalcBuf3);
|
|
free(rowCalcBuf2);
|
|
free(rowCalcBuf1);
|
|
return 0;
|
|
}
|
|
|
|
memset(rowCalcBuf1, 0, rowCalcBufLen);
|
|
memset(rowCalcBuf2, 0, rowCalcBufLen);
|
|
rowCalcBuf4Ptr = rowCalcBuf4Base + (cols -1) * 3;
|
|
rowCalcBuf3Ptr = rowCalcBuf3Base + (cols -1) * 3;
|
|
|
|
/* Iterate through all of the rows */
|
|
for (row = 0 ; row < rows ; row++) {
|
|
/* Iterate through each column */
|
|
for (col = 0 ; col < pixelCnt ; col++) {
|
|
double srcVal, calcA, calcB, calcE, calcG, calcD;
|
|
int idx;
|
|
int pixelVal;
|
|
double pixelFloat;
|
|
|
|
srcVal = rowPtr[col];
|
|
calcA = rowCalcBuf1[col] - srcVal;
|
|
|
|
pixelVal = calcA;
|
|
if (pixelVal < 0) {
|
|
if (-0xff0 < pixelVal) {
|
|
idx = -((0x10 - pixelVal) >> 5);
|
|
} else {
|
|
idx = -0x80;
|
|
}
|
|
} else {
|
|
if (pixelVal < 0xfd0) {
|
|
idx = (pixelVal + 0x10) >> 5;
|
|
} else {
|
|
idx = 0x7f;
|
|
}
|
|
}
|
|
calcA *= wmam->unka[128+idx];
|
|
rowCalcBuf3Base[col] = srcVal + calcA;
|
|
|
|
pixelVal = calcA;
|
|
if (pixelVal < 0) {
|
|
if (-0xff0 < pixelVal) {
|
|
idx = -((0x10 - pixelVal) >> 5);
|
|
} else {
|
|
idx = -0x80;
|
|
}
|
|
} else {
|
|
if (pixelVal < 0xfd0) {
|
|
idx = (pixelVal + 0x10) >> 5;
|
|
} else {
|
|
idx = 0x7f;
|
|
}
|
|
}
|
|
calcB = wmam->unkb[128+idx];
|
|
calcD = rowCalcBuf2[col] - srcVal;
|
|
|
|
pixelVal = calcD;
|
|
if (pixelVal < 0) {
|
|
if (-0xff0 < pixelVal) {
|
|
idx = -((0x10 - pixelVal) >> 5);
|
|
} else {
|
|
idx = -0x80;
|
|
}
|
|
} else {
|
|
if (pixelVal < 0xfd0) {
|
|
idx = (pixelVal + 0x10) >> 5;
|
|
} else {
|
|
idx = 0x7f;
|
|
}
|
|
}
|
|
calcD *= wmam->unkd[128+idx];
|
|
rowCalcBuf4Base[col] = srcVal + calcD;
|
|
|
|
pixelVal = calcD;
|
|
if (pixelVal < 0) {
|
|
if (-0xff0 < pixelVal) {
|
|
idx = -((0x10 - pixelVal) >> 5);
|
|
} else {
|
|
idx = -0x80;
|
|
}
|
|
} else {
|
|
if (pixelVal < 0xfd0) {
|
|
idx = (pixelVal + 0x10) >> 5;
|
|
} else {
|
|
idx = 0x7f;
|
|
}
|
|
}
|
|
calcE = (wmam->unke[idx + 128]);
|
|
|
|
/* Work out compensated pixel value */
|
|
pixelFloat = (-(calcA * calcB - srcVal) +
|
|
-(calcD * calcE - srcVal)) * 0.50000000;
|
|
|
|
/* Apply final compensation stage, but has to be
|
|
delayed by one row. We do the final row
|
|
at the end! */
|
|
if (row != 0) {
|
|
if (pixelFloat > 4095.0) {
|
|
pixelVal = (pixelFloat - 4095.00000000);
|
|
if (pixelVal < 0xff0) {
|
|
idx = (pixelVal + 0x10) >> 5;
|
|
} else {
|
|
idx = 0x7f;
|
|
}
|
|
calcG = pixelVal * wmam->unkg[127+idx] + rowCalcBuf5[col];
|
|
} else if (pixelFloat < 0.0) {
|
|
pixelVal = pixelFloat;
|
|
idx = 0x80;
|
|
if (-0xff0 < pixelVal) {
|
|
if (pixelVal < 1) {
|
|
idx = 0xff - ((0x10 - pixelVal) >> 5);
|
|
} else {
|
|
idx = 0xff;
|
|
}
|
|
} else {
|
|
idx = 0x80;
|
|
}
|
|
calcG = pixelVal * wmam->unkg[127+idx] + rowCalcBuf5[col];
|
|
} else {
|
|
calcG = rowCalcBuf5[col];
|
|
}
|
|
|
|
/* Clip & Write */
|
|
pixelVal = calcG + 0.50000000;
|
|
if (pixelVal < 0) {
|
|
imgBufPtr[col] = 0;
|
|
} else if (pixelVal >= 0x1000) {
|
|
imgBufPtr[col] = 0xfff;
|
|
} else {
|
|
imgBufPtr[col] = pixelVal;
|
|
}
|
|
}
|
|
/* Update work buffer */
|
|
rowCalcBuf5[col] = pixelFloat;
|
|
}
|
|
|
|
rowPtr -= pixelsPerRow;
|
|
if (row != 0) {
|
|
imgBufPtr -= pixelsPerRow;
|
|
}
|
|
|
|
/* Iterate the state */
|
|
rowCalcBuf3[9] = rowCalcBuf3[15];
|
|
rowCalcBuf3[10] = rowCalcBuf3[16];
|
|
rowCalcBuf3[11] = rowCalcBuf3[17];
|
|
rowCalcBuf3[6] = rowCalcBuf3[18];
|
|
rowCalcBuf3[7] = rowCalcBuf3[19];
|
|
rowCalcBuf3[8] = rowCalcBuf3[20];
|
|
rowCalcBuf3[3] = rowCalcBuf3[21];
|
|
rowCalcBuf3[4] = rowCalcBuf3[22];
|
|
rowCalcBuf3[5] = rowCalcBuf3[23];
|
|
rowCalcBuf3[0] = rowCalcBuf3[24];
|
|
rowCalcBuf3[1] = rowCalcBuf3[25];
|
|
rowCalcBuf3[2] = rowCalcBuf3[26];
|
|
|
|
rowCalcBuf4[9] = rowCalcBuf4[15];
|
|
rowCalcBuf4[10] = rowCalcBuf4[16];
|
|
rowCalcBuf4[11] = rowCalcBuf4[17];
|
|
rowCalcBuf4[6] = rowCalcBuf4[18];
|
|
rowCalcBuf4[7] = rowCalcBuf4[19];
|
|
rowCalcBuf4[8] = rowCalcBuf4[20];
|
|
rowCalcBuf4[3] = rowCalcBuf4[21];
|
|
rowCalcBuf4[4] = rowCalcBuf4[22];
|
|
rowCalcBuf4[5] = rowCalcBuf4[23];
|
|
rowCalcBuf4[0] = rowCalcBuf4[24];
|
|
rowCalcBuf4[1] = rowCalcBuf4[25];
|
|
rowCalcBuf4[2] = rowCalcBuf4[26];
|
|
|
|
rowCalcBuf3Ptr[3] = rowCalcBuf3Ptr[-3];
|
|
rowCalcBuf3Ptr[4] = rowCalcBuf3Ptr[-2];
|
|
rowCalcBuf3Ptr[5] = rowCalcBuf3Ptr[-1];
|
|
rowCalcBuf3Ptr[6] = rowCalcBuf3Ptr[-6];
|
|
rowCalcBuf3Ptr[7] = rowCalcBuf3Ptr[-5];
|
|
rowCalcBuf3Ptr[8] = rowCalcBuf3Ptr[-4];
|
|
rowCalcBuf3Ptr[9] = rowCalcBuf3Ptr[-9];
|
|
rowCalcBuf3Ptr[10] = rowCalcBuf3Ptr[-8];
|
|
rowCalcBuf3Ptr[11] = rowCalcBuf3Ptr[-7];
|
|
rowCalcBuf3Ptr[12] = rowCalcBuf3Ptr[-12];
|
|
rowCalcBuf3Ptr[13] = rowCalcBuf3Ptr[-11];
|
|
rowCalcBuf3Ptr[14] = rowCalcBuf3Ptr[-10];
|
|
|
|
rowCalcBuf4Ptr[3] = rowCalcBuf4Ptr[-3];
|
|
rowCalcBuf4Ptr[4] = rowCalcBuf4Ptr[-2];
|
|
rowCalcBuf4Ptr[5] = rowCalcBuf4Ptr[-1];
|
|
rowCalcBuf4Ptr[6] = rowCalcBuf4Ptr[-6];
|
|
rowCalcBuf4Ptr[7] = rowCalcBuf4Ptr[-5];
|
|
rowCalcBuf4Ptr[8] = rowCalcBuf4Ptr[-4];
|
|
rowCalcBuf4Ptr[9] = rowCalcBuf4Ptr[-9];
|
|
rowCalcBuf4Ptr[10] = rowCalcBuf4Ptr[-8];
|
|
rowCalcBuf4Ptr[11] = rowCalcBuf4Ptr[-7];
|
|
rowCalcBuf4Ptr[12] = rowCalcBuf4Ptr[-12];
|
|
rowCalcBuf4Ptr[13] = rowCalcBuf4Ptr[-11];
|
|
rowCalcBuf4Ptr[14] = rowCalcBuf4Ptr[-10];
|
|
|
|
double *Buf1Ptr = rowCalcBuf1;
|
|
double *Buf2Ptr = rowCalcBuf2;
|
|
double *Buf3Ptr = rowCalcBuf3Base;
|
|
double *Buf4Ptr = rowCalcBuf4Base;
|
|
|
|
for (col = 0 ; col < pixelCnt ; col += 3) {
|
|
Buf1Ptr[0] = (wmam->unkc[4] * (Buf3Ptr[-12] + Buf3Ptr[12]) +
|
|
wmam->unkc[3] * (Buf3Ptr[-9] + Buf3Ptr[9]) +
|
|
wmam->unkc[2] * (Buf3Ptr[-6] + Buf3Ptr[6]) +
|
|
wmam->unkc[1] * (Buf3Ptr[-3] + Buf3Ptr[3]) +
|
|
wmam->unkc[0] * (Buf3Ptr[0] + Buf3Ptr[0])) / 1000.00000000;
|
|
Buf1Ptr[1] = (wmam->unkc[4] * (Buf3Ptr[-11] + Buf3Ptr[13]) +
|
|
wmam->unkc[3] * (Buf3Ptr[-8] + Buf3Ptr[10]) +
|
|
wmam->unkc[2] * (Buf3Ptr[-5] + Buf3Ptr[7]) +
|
|
wmam->unkc[1] * (Buf3Ptr[-2] + Buf3Ptr[4]) +
|
|
wmam->unkc[0] * (Buf3Ptr[1] + Buf3Ptr[1])) / 1000.00000000;
|
|
Buf1Ptr[2] = (wmam->unkc[4] * (Buf3Ptr[-10] + Buf3Ptr[14]) +
|
|
wmam->unkc[3] * (Buf3Ptr[-7] + Buf3Ptr[11]) +
|
|
wmam->unkc[2] * (Buf3Ptr[-4] + Buf3Ptr[8]) +
|
|
wmam->unkc[1] * (Buf3Ptr[-1] + Buf3Ptr[5]) +
|
|
wmam->unkc[0] * (Buf3Ptr[2] + Buf3Ptr[2])) / 1000.00000000;
|
|
|
|
Buf2Ptr[0] = (wmam->unkf[4] * (Buf4Ptr[-12] + Buf4Ptr[12]) +
|
|
wmam->unkf[3] * (Buf4Ptr[-9] + Buf4Ptr[9]) +
|
|
wmam->unkf[2] * (Buf4Ptr[-6] + Buf4Ptr[6]) +
|
|
wmam->unkf[1] * (Buf4Ptr[-3] + Buf4Ptr[3]) +
|
|
wmam->unkf[0] * (Buf4Ptr[0] + Buf4Ptr[0])) / 1000.00000000;
|
|
Buf2Ptr[1] = (wmam->unkf[4] * (Buf4Ptr[-11] + Buf4Ptr[13]) +
|
|
wmam->unkf[3] * (Buf4Ptr[-8] + Buf4Ptr[10]) +
|
|
wmam->unkf[2] * (Buf4Ptr[-5] + Buf4Ptr[7]) +
|
|
wmam->unkf[1] * (Buf4Ptr[-2] + Buf4Ptr[4]) +
|
|
wmam->unkf[0] * (Buf4Ptr[1] + Buf4Ptr[1])) / 1000.00000000;
|
|
Buf2Ptr[2] = (wmam->unkf[4] * (Buf4Ptr[-10] + Buf4Ptr[14]) +
|
|
wmam->unkf[3] * (Buf4Ptr[-7] + Buf4Ptr[11]) +
|
|
wmam->unkf[2] * (Buf4Ptr[-4] + Buf4Ptr[8]) +
|
|
wmam->unkf[1] * (Buf4Ptr[-1] + Buf4Ptr[5]) +
|
|
wmam->unkf[0] * (Buf4Ptr[2] + Buf4Ptr[2])) / 1000.00000000;
|
|
|
|
Buf1Ptr += 3;
|
|
Buf2Ptr += 3;
|
|
Buf3Ptr += 3;
|
|
Buf4Ptr += 3;
|
|
}
|
|
}
|
|
|
|
/* Clip & Write final row */
|
|
for (col = 0 ; col < pixelCnt ; col++) {
|
|
int16_t val = (rowCalcBuf5[col] + 0.50000000);
|
|
if (val < 0) {
|
|
imgBufPtr[col] = 0;
|
|
} else if (val > 0xfff) {
|
|
imgBufPtr[col] = 0xfff;
|
|
} else {
|
|
imgBufPtr[col] = val;
|
|
}
|
|
}
|
|
|
|
/* Clean up, we're done! */
|
|
free(rowCalcBuf5);
|
|
free(rowCalcBuf4);
|
|
free(rowCalcBuf3);
|
|
free(rowCalcBuf2);
|
|
free(rowCalcBuf1);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int CP98xx_DoConvert(const struct mitsu98xx_data *table,
|
|
const struct BandImage *input,
|
|
struct BandImage *output,
|
|
uint8_t type, int sharpness, int already_reversed)
|
|
{
|
|
int i;
|
|
|
|
/* Figure out which table to use */
|
|
switch (type) {
|
|
case 0x80:
|
|
table = &table[0]; /* Superfine */
|
|
break;
|
|
case 0x11:
|
|
table = &table[2]; /* Fine HG */
|
|
break;
|
|
case 0x10:
|
|
default:
|
|
table = &table[1]; /* Fine STD */
|
|
break;
|
|
}
|
|
|
|
/* We've already gone through 3D LUT */
|
|
|
|
/* Sharpen, as needed */
|
|
if (sharpness > 0) {
|
|
struct CP98xx_AptParams APT;
|
|
CP98xx_InitAptParams(table, &APT, sharpness);
|
|
#if 0
|
|
if (CP98xx_DoAptMWithParams(table, input, 0) != 1) {
|
|
return 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Set up gamma tables */
|
|
struct CP98xx_GammaParams gamma;
|
|
struct CP98xx_KHParams kh;
|
|
|
|
memcpy(gamma.GNMgm, table->GNMgm, sizeof(gamma.GNMgm));
|
|
memcpy(gamma.GNMby, table->GNMby, sizeof(gamma.GNMby));
|
|
memcpy(gamma.GNMrc, table->GNMrc, sizeof(gamma.GNMrc));
|
|
memcpy(gamma.GammaAdj, table->GammaAdj, sizeof(gamma.GammaAdj));
|
|
memcpy(kh.KH, table->KH, sizeof(kh.KH));
|
|
kh.Start = table->KHStart;
|
|
kh.End = table->KHEnd;
|
|
kh.Step = table->KHStep;
|
|
|
|
if (CP98xx_DoCorrectGammaTbl(&gamma, &kh, input) != 1) {
|
|
return 0;
|
|
}
|
|
|
|
/* Run through gamma conversion */
|
|
if (CP98xx_DoGammaConv(&gamma, input, output, already_reversed) != 1) {
|
|
return 0;
|
|
}
|
|
|
|
/* Set up and run through the WMAM flow */
|
|
struct CP98xx_WMAM wmam;
|
|
CP98xx_InitWMAM(&wmam, &table->WMAM);
|
|
if (CP98xx_DoWMAM(&wmam, output, 1) != 1) {
|
|
return 0;
|
|
}
|
|
|
|
/* Convert to printer's native BE16 */
|
|
for (i = 0; i < (output->rows * output->cols * 3) ; i++) {
|
|
((uint16_t*)output->imgbuf)[i] = cpu_to_be16(((uint16_t*)output->imgbuf)[i]);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* Mitsubishi CP-M1 family */
|
|
#define M1CPCDATA_GAMMA_ROWS 256
|
|
#define M1CPCDATA_ROWS 7 /* Correlates to sharpening levels */
|
|
|
|
struct M1CPCData {
|
|
uint16_t GNMaB[M1CPCDATA_GAMMA_ROWS];
|
|
uint16_t GNMaG[M1CPCDATA_GAMMA_ROWS];
|
|
uint16_t GNMaR[M1CPCDATA_GAMMA_ROWS];
|
|
|
|
uint16_t EnHTH[M1CPCDATA_ROWS]; // fixed @96
|
|
uint16_t NoISetH[M1CPCDATA_ROWS]; // fixed @8
|
|
uint16_t NRGain[M1CPCDATA_ROWS]; // fixed @40
|
|
uint16_t NRTH[M1CPCDATA_ROWS]; // fixed @32
|
|
uint8_t NRK[M1CPCDATA_ROWS]; // fixed @1
|
|
uint16_t HDEnhGain[M1CPCDATA_ROWS]; // Varies!
|
|
uint16_t EnhDarkGain[M1CPCDATA_ROWS]; // Fixed @0
|
|
uint8_t DtctArea[M1CPCDATA_ROWS]; // Fixed @1
|
|
uint8_t CorCol[M1CPCDATA_ROWS]; // Fixed @2
|
|
uint8_t HighDownMode[M1CPCDATA_ROWS]; // Fixed @1
|
|
uint16_t HighTH[M1CPCDATA_ROWS]; // Fixed @800
|
|
double HighG[M1CPCDATA_ROWS]; // Fixed @0.1
|
|
};
|
|
|
|
struct SIZE {
|
|
int32_t cx;
|
|
int32_t cy;
|
|
};
|
|
|
|
struct POINT {
|
|
uint32_t x;
|
|
uint32_t y;
|
|
};
|
|
|
|
/* Sharpening stuff */
|
|
static void M1_GetAroundBrightness(const uint16_t *pSrcBrightness,
|
|
const struct SIZE *pSrcSize,
|
|
const struct POINT *pPtCenter,
|
|
uint16_t *pDstBrightness,
|
|
const struct SIZE *pDstSize)
|
|
|
|
{
|
|
uint16_t center;
|
|
int32_t vert, horiz;
|
|
int32_t UVar1;
|
|
int32_t UVar2;
|
|
int bottom, right, top, left;
|
|
int i;
|
|
uint16_t *pBottomRight;
|
|
const uint16_t *pTopLeft;
|
|
int32_t col;
|
|
int32_t row;
|
|
uint16_t *pDstRow;
|
|
|
|
center = pSrcBrightness[pSrcSize->cx * pPtCenter->y + pPtCenter->x];
|
|
pDstRow = pDstBrightness + pDstSize->cx;
|
|
|
|
for (row = 0 ; row < pDstSize->cx ; row++) {
|
|
pDstBrightness[row] = center;
|
|
}
|
|
|
|
for (col = 1; col < pDstSize->cy; col++) {
|
|
memcpy(pDstRow, pDstBrightness, pDstSize->cx * sizeof(uint16_t));
|
|
pDstRow += pDstSize->cx;
|
|
}
|
|
|
|
vert = pPtCenter->x + (pDstSize->cx >> 1);
|
|
horiz = pPtCenter->y + (pDstSize->cy >> 1);
|
|
|
|
top = pPtCenter->x - vert;
|
|
bottom = 0;
|
|
if (top < 0) {
|
|
bottom = -top;
|
|
top = 0;
|
|
}
|
|
|
|
left = pPtCenter->y - horiz;
|
|
right = 0;
|
|
if (left < 0) {
|
|
right = -left;
|
|
left = 0;
|
|
}
|
|
|
|
if (pSrcSize->cx - 1 < vert) {
|
|
UVar1 = pDstSize->cx - ((vert - pSrcSize->cx) + 1);
|
|
} else {
|
|
UVar1 = pDstSize->cx;
|
|
}
|
|
|
|
if (pSrcSize->cy - 1 < horiz) {
|
|
UVar2 = pDstSize->cy - ((horiz - pSrcSize->cy) + 1);
|
|
} else {
|
|
UVar2 = pDstSize->cy;
|
|
}
|
|
|
|
pTopLeft = pSrcBrightness + pSrcSize->cx * left + top;
|
|
pBottomRight = pDstBrightness + pDstSize->cx * right + bottom;
|
|
for (i = right ; i < UVar2 ; i++) {
|
|
memcpy(pBottomRight, pTopLeft, (UVar1 - bottom) * sizeof(uint16_t));
|
|
pTopLeft = pTopLeft + pSrcSize->cx;
|
|
pBottomRight += pDstSize->cx;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static const int16_t aroundMap08[9] = { 1, 1, 1,
|
|
1, 0, 1,
|
|
1, 1, 1};
|
|
static const int16_t aroundMap16[25] = { 0, 0, 1, 1, 0,
|
|
1, 1, 1, 1, 0,
|
|
1, 1, 0, 1, 1,
|
|
0, 1, 1, 1, 1,
|
|
0, 1, 1, 0, 0 };
|
|
static const int16_t aroundMap64[81] = { 0, 0, 0, 1, 1, 1, 1, 0, 0,
|
|
0, 1, 1, 1, 1, 1, 1, 1, 0,
|
|
0, 1, 1, 1, 5, 5, 1, 1, 0,
|
|
1, 1, 5, 5, 5, 5, 1, 1, 1,
|
|
1, 1, 5, 5, 1, 5, 5, 1, 1,
|
|
1, 1, 1, 5, 5, 5, 5, 1, 0,
|
|
0, 1, 1, 5, 5, 1, 1, 1, 0,
|
|
0, 1, 1, 1, 1, 1, 1, 1, 0,
|
|
0, 0, 1, 1, 1, 1, 1, 0, 0 };
|
|
|
|
static double M1_GetBrightnessAverage(const uint16_t *pBitBrightness,
|
|
const struct SIZE *pSize,
|
|
const struct POINT *pPtCenter,
|
|
uint8_t dtctArea,
|
|
int32_t enhTh, int32_t noiseTh)
|
|
|
|
{
|
|