--- dcraw.c 2016-10-28 13:45:27 +0000 +++ dcraw.cc 2016-10-31 13:35:15 +0000 @@ -1,3 +1,16 @@ +/*RT*/#include +/*RT*/#include +/*RT*/#undef MAX +/*RT*/#undef MIN +/*RT*/#undef ABS +/*RT*/#include "rt_math.h" +/*RT*/#define NO_LCMS +/*RT*/#define NO_JPEG +/*RT*/#define NO_JASPER +/*RT*/#define LOCALTIME +/*RT*/#define DJGPP + +#include "opthelper.h" /* dcraw.c -- Dave Coffin's raw photo decoder Copyright 1997-2016 by Dave Coffin, dcoffin a cybercom o net @@ -29,17 +42,17 @@ #define _GNU_SOURCE #endif #define _USE_MATH_DEFINES -#include -#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include #if defined(DJGPP) || defined(__MINGW32__) @@ -54,7 +67,6 @@ #ifdef WIN32 #include #include -#pragma comment(lib, "ws2_32.lib") #define snprintf _snprintf #define strcasecmp stricmp #define strncasecmp strnicmp @@ -89,89 +101,38 @@ #define _(String) (String) #endif -#if !defined(uchar) -#define uchar unsigned char -#endif -#if !defined(ushort) -#define ushort unsigned short -#endif +#define ushort UshORt +typedef unsigned char uchar; +typedef unsigned short ushort; +#include "dcraw.h" /* - All global variables are defined here, and all functions that + RT All global variables are defined here, and all functions that access them are prefixed with "CLASS". Note that a thread-safe C++ class cannot have non-const static local variables. */ -FILE *ifp, *ofp; -short order; -const char *ifname; -char *meta_data, xtrans[6][6], xtrans_abs[6][6]; -char cdesc[5], desc[512], make[64], model[64], model2[64], artist[64]; -float flash_used, canon_ev, iso_speed, shutter, aperture, focal_len; -time_t timestamp; -off_t strip_offset, data_offset; -off_t thumb_offset, meta_offset, profile_offset; -unsigned shot_order, kodak_cbpp, exif_cfa, unique_id; -unsigned thumb_length, meta_length, profile_length; -unsigned thumb_misc, *oprof, fuji_layout, shot_select=0, multi_out=0; -unsigned tiff_nifds, tiff_samples, tiff_bps, tiff_compress; -unsigned black, maximum, mix_green, raw_color, zero_is_bad; -unsigned zero_after_ff, is_raw, dng_version, is_foveon, data_error; -unsigned tile_width, tile_length, gpsdata[32], load_flags; -unsigned flip, tiff_flip, filters, colors; -ushort raw_height, raw_width, height, width, top_margin, left_margin; -ushort shrink, iheight, iwidth, fuji_width, thumb_width, thumb_height; -ushort *raw_image, (*image)[4], cblack[4102]; -ushort white[8][8], curve[0x10000], cr2_slice[3], sraw_mul[4]; -double pixel_aspect, aber[4]={1,1,1,1}, gamm[6]={ 0.45,4.5,0,0,0,0 }; -float bright=1, user_mul[4]={0,0,0,0}, threshold=0; -int mask[8][4]; -int half_size=0, four_color_rgb=0, document_mode=0, highlight=0; -int verbose=0, use_auto_wb=0, use_camera_wb=0, use_camera_matrix=1; -int output_color=1, output_bps=8, output_tiff=0, med_passes=0; -int no_auto_bright=0; -unsigned greybox[4] = { 0, 0, UINT_MAX, UINT_MAX }; -float cam_mul[4], pre_mul[4], cmatrix[3][4], rgb_cam[3][4]; -const double xyz_rgb[3][3] = { /* XYZ from RGB */ + +const double xyz_rgb[3][3] = { // XYZ from RGB { 0.412453, 0.357580, 0.180423 }, { 0.212671, 0.715160, 0.072169 }, { 0.019334, 0.119193, 0.950227 } }; const float d65_white[3] = { 0.950456, 1, 1.088754 }; -int histogram[4][0x2000]; -void (*write_thumb)(), (*write_fun)(); -void (*load_raw)(), (*thumb_load_raw)(); -jmp_buf failure; - -struct decode { - struct decode *branch[2]; - int leaf; -} first_decode[2048], *second_decode, *free_decode; - -struct tiff_ifd { - int width, height, bps, comp, phint, offset, flip, samples, bytes; - int tile_width, tile_length; - float shutter; -} tiff_ifd[10]; - -struct ph1 { - int format, key_off, tag_21a; - int black, split_col, black_col, split_row, black_row; - float tag_210; -} ph1; -#define CLASS +/* RT: Removed unused structs */ +#define CLASS DCraw:: #define FORC(cnt) for (c=0; c < cnt; c++) #define FORC3 FORC(3) #define FORC4 FORC(4) #define FORCC FORC(colors) -#define SQR(x) ((x)*(x)) +#define SQR(x) rtengine::SQR(x) #define ABS(x) (((int)(x) ^ ((int)(x) >> 31)) - ((int)(x) >> 31)) -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#define LIM(x,min,max) MAX(min,MIN(x,max)) -#define ULIM(x,y,z) ((y) < (z) ? LIM(x,y,z) : LIM(x,z,y)) -#define CLIP(x) LIM((int)(x),0,65535) +#define MIN(a,b) rtengine::min(a,static_cast<__typeof__(a)>(b)) +#define MAX(a,b) rtengine::max(a,static_cast<__typeof__(a)>(b)) +#define LIM(x,min,max) rtengine::LIM(x,static_cast<__typeof__(x)>(min),static_cast<__typeof__(x)>(max)) +#define ULIM(x,y,z) rtengine::median(x,static_cast<__typeof__(x)>(y),static_cast<__typeof__(x)>(z)) +#define CLIP(x) rtengine::CLIP(x) #define SWAP(a,b) { a=a+b; b=a-b; a=a-b; } /* @@ -247,6 +208,7 @@ if (filters == 1) return filter[(row+top_margin)&15][(col+left_margin)&15]; if (filters == 9) return xtrans[(row+6) % 6][(col+6) % 6]; + return FC(row,col); } @@ -289,6 +251,7 @@ fprintf (stderr,_("Corrupt data near 0x%llx\n"), (INT64) ftello(ifp)); } data_error++; +/*RT Issue 2467 longjmp (failure, 1);*/ } ushort CLASS sget2 (uchar *s) @@ -362,7 +325,7 @@ { if (fread (pixel, 2, count, ifp) < count) derror(); if ((order == 0x4949) == (ntohs(0x1234) == 0x1234)) - swab (pixel, pixel, count*2); + swab ((char*)pixel, (char*)pixel, count*2); } void CLASS cubic_spline (const int *x_, const int *y_, const int len) @@ -589,13 +552,13 @@ return 0; } -unsigned CLASS getbithuff (int nbits, ushort *huff) +inline unsigned CLASS getbithuff_t::operator() (int nbits, ushort *huff) { - static unsigned bitbuf=0; - static int vbits=0, reset=0; +/*RT static unsigned bitbuf=0; */ +/*RT static int vbits=0, reset=0; */ unsigned c; - if (nbits > 25) return 0; + if (UNLIKELY(nbits > 25)) return 0; if (nbits < 0) return bitbuf = vbits = reset = 0; if (nbits == 0 || vbits < 0) return 0; @@ -805,9 +768,13 @@ FORC(2) free (huff[c]); } +/* + Not a full implementation of Lossless JPEG, just + enough to decode Canon, Kodak and Adobe DNG images. + */ struct jhead { - int algo, bits, high, wide, clrs, sraw, psv, restart, vpred[6]; - ushort quant[64], idct[64], *huff[20], *free[20], *row; + int bits, high, wide, clrs, sraw, psv, restart, vpred[6]; + ushort *huff[6], *free[4], *row; }; int CLASS ljpeg_start (struct jhead *jh, int info_only) @@ -828,9 +795,9 @@ switch (tag) { case 0xffc3: jh->sraw = ((data[7] >> 4) * (data[7] & 15) - 1) & 3; - case 0xffc1: + case 0xffc1: case 0xffc0: - jh->algo = tag & 0xff; + jh->algo = tag & 0xff; jh->bits = data[0]; jh->high = data[1] << 8 | data[2]; jh->wide = data[3] << 8 | data[4]; @@ -862,7 +829,7 @@ FORC(4) jh->huff[2+c] = jh->huff[1]; FORC(jh->sraw) jh->huff[1+c] = jh->huff[0]; } - jh->row = (ushort *) calloc (jh->wide*jh->clrs, 4); + jh->row = (ushort *) calloc (2 * jh->wide*jh->clrs, 4); merror (jh->row, "ljpeg_start()"); return zero_after_ff = 1; } @@ -874,7 +841,7 @@ free (jh->row); } -int CLASS ljpeg_diff (ushort *huff) +inline int CLASS ljpeg_diff (ushort *huff) { int len, diff; @@ -901,7 +868,7 @@ } getbits(-1); } - FORC3 row[c] = jh->row + jh->wide*jh->clrs*((jrow+c) & 1); + FORC3 row[c] = (jh->row + ((jrow & 1) + 1) * (jh->wide*jh->clrs*((jrow+c) & 1))); for (col=0; col < jh->wide; col++) FORC(jh->clrs) { diff = ljpeg_diff (jh->huff[c]); @@ -909,8 +876,7 @@ pred = spred; else if (col) pred = row[0][-jh->clrs]; else pred = (jh->vpred[c] += diff) - diff; - if (jrow && col) switch (jh->psv) { - case 1: break; + if (jh->psv != 1 && jrow && col) switch (jh->psv) { case 2: pred = row[1][0]; break; case 3: pred = row[1][-jh->clrs]; break; case 4: pred = pred + row[1][0] - row[1][-jh->clrs]; break; @@ -919,7 +885,7 @@ case 7: pred = (pred + row[1][0]) >> 1; break; default: pred = 0; } - if ((**row = pred + diff) >> jh->bits) derror(); + if (UNLIKELY((**row = pred + diff) >> jh->bits)) derror(); if (c <= jh->sraw) spred = **row; row[0]++; row[1]++; } @@ -928,22 +894,38 @@ void CLASS lossless_jpeg_load_raw() { - int jwide, jrow, jcol, val, jidx, i, j, row=0, col=0; struct jhead jh; - ushort *rp; + int row=0, col=0; if (!ljpeg_start (&jh, 0)) return; - jwide = jh.wide * jh.clrs; - - for (jrow=0; jrow < jh.high; jrow++) { - rp = ljpeg_row (jrow, &jh); + int jwide = jh.wide * jh.clrs; + ushort *rp[2]; + rp[0] = ljpeg_row (0, &jh); + + for (int jrow=0; jrow < jh.high; jrow++) { +#ifdef _OPENMP +#pragma omp parallel sections +#endif +{ +#ifdef _OPENMP + #pragma omp section +#endif + { + if(jrow < jh.high - 1) + rp[(jrow + 1)&1] = ljpeg_row (jrow + 1, &jh); + } +#ifdef _OPENMP + #pragma omp section +#endif + { if (load_flags & 1) row = jrow & 1 ? height-1-jrow/2 : jrow/2; - for (jcol=0; jcol < jwide; jcol++) { - val = curve[*rp++]; + for (int jcol=0; jcol < jwide; jcol++) { + int val = curve[*rp[jrow&1]++]; if (cr2_slice[0]) { - jidx = jrow*jwide + jcol; - i = jidx / (cr2_slice[1]*raw_height); + int jidx = jrow*jwide + jcol; + int i = jidx / (cr2_slice[1]*raw_height); + int j; if ((j = i >= cr2_slice[0])) i = cr2_slice[0]; jidx -= i * (cr2_slice[1]*raw_height); @@ -956,6 +938,8 @@ if (++col >= raw_width) col = (row++,0); } + } +} } ljpeg_end (&jh); } @@ -1124,8 +1108,7 @@ if (++col >= tile_width || col >= raw_width) row += 1 + (col = 0); } - } - } + } } fseek (ifp, save+4, SEEK_SET); if ((tcol += tile_width) >= raw_width) trow += tile_length + (tcol = 0); @@ -1332,14 +1315,14 @@ int i, nz; char tail[424]; - fseek (ifp, -sizeof tail, SEEK_END); + fseek (ifp, -(int)sizeof tail, SEEK_END); fread (tail, 1, sizeof tail, ifp); for (nz=i=0; i < sizeof tail; i++) if (tail[i]) nz++; return nz > 20; } -void CLASS jpeg_thumb(); +/*RT void CLASS jpeg_thumb(); */ void CLASS ppm_thumb() { @@ -1701,10 +1684,10 @@ } } -unsigned CLASS ph1_bithuff (int nbits, ushort *huff) +unsigned CLASS ph1_bithuff_t::operator() (int nbits, ushort *huff) { - static UINT64 bitbuf=0; - static int vbits=0; +/*RT static UINT64 bitbuf=0; */ +/*RT static int vbits=0; */ unsigned c; if (nbits == -1) @@ -1779,6 +1762,338 @@ maximum = 0xfffc - ph1.black; } +void CLASS parse_hasselblad_gain() +{ + /* + Reverse-engineer's notes: + + The Hasselblad gain tag (0x19 in makernotes) is only available in the 3FR format and + is applied and removed when Hasselblad Phocus converts it to the FFF format. It's + always 0x300000 bytes large regardless of (tested) model, not all space in it is + used though. + + It contains individual calibration information from the factory to tune the sensor + performance. + + There is more calibration data in the tag than what is applied in conversion to FFF, + I've only cared to figure out the data which is actually used, but have some leads on + remaining info. + + The format is not equal between all models. Due to lack of 3FR files (harder to get + than FFF) only a subset of the models have been reverse-engineered. + + The header space is 512 bytes and is a mix of 16 and 32 bit values. Offset to blocks + are 32 bit values, but all information seems to be encoded in 16 bit values. Many + values in the header can be zeroed with no effect on FFF conversion, and their + meaning, if any, have not been further investigated. + + Formats: + hdr16[22] = raw width + hdr16[23] = raw height + hdr32[24] = offset to level corr block + Data block format. Seems to differ depending on sensor type. For tested H4D-50 + and H3D-31: 10 16 bit signed values per row + value 0: a correction factor (k) used on even columns, where the new pixel value is + calulated as follows: + new_value = old_value + (2 * ((k * (old_value_on_row_above-256)) / 32767) - 2) + note the connection to the value on the row above, seems to be some sort of signal + leakage correction. + value 1: same as value 0 but using old value on row below instead of above + value 2-3: same as value 0-1 but for odd columns + value 4-9: has some effect if non-zero (probably similar to the others) but not + investigated which, as it's seems to be always zero for the tested cameras. + + hdr32[25] = probably offset to "bad/unreliable pixels" info, always 512 as it starts + directly after the header. Not applied in FFF conversion (at least + typically). + Data block format guess: raw_height packets of + + + hdr32[27] = offset to unknown data (bad colulmns?), of the form: + <0> + packet: . + + hdr32[34] = curves offset, seems to be A/D curves (one per channel) on newer models + and some sort of a film curve on older. Not applied in FFF conversion. + + hdr32[36] = flatfield correction, not available in older models. Data format: + <1><11 * 2 pad> + packet: + + The header pad is not zeroed and might seem to contain some sort of + information, but it makes no difference if set to zero. See + hasselblad_correct() how the flatfield is applied. + + Applied in FFF conversion is levels, flatfield correction, and the bad columns(?) + data. A/D curves are surprisingly not applied, maybe pre-applied in hardware and + only available as information? Levels are applied before flatfield, further + ordering has not been investigated. + + Not all combinations/models have been tested so there may be gaps. + + Most clipped pixels in a 3FR is at 65535, but there's also some at 65534. Both + are set to 65535 when calibrated, while 65533 is treated as a normal value. In + the calibration process smaller values can be scaled to 65534 (which should + not be seen as clipped). + */ + + ushort raw_h, count, ch_count, u16; + int i, offset; + off_t base; + + base = ftell(ifp); + fseek(ifp, 2 * 23, SEEK_CUR); + raw_h = get2(); + fseek(ifp, 48, SEEK_CUR); + offset = get4(); + hbd.levels = offset ? base + offset : 0; + fseek(ifp, 8, SEEK_CUR); + offset = get4(); + hbd.unknown1 = offset ? base + offset : 0; + fseek(ifp, 32, SEEK_CUR); + offset = get4(); + hbd.flatfield = offset ? base + offset : 0; +} + +void CLASS hasselblad_correct() +{ + unsigned col, row; + + /* + + This function applies 3FR calibration data. At the time of writing it supports a + subset, so here's a todo list: + + TODO: + - Support all gain tag formats + - The 0x19 tag varies a bit between models. We don't have parsers for all models. + - The reference model used was during inital reverse-engineering was a H4D-50, + we probably support the Hasselblads with Kodak 31, 39, 40 and 50 megapixels + well, but more work is needed for Kodak 16 and 22, Dalsa 60 and Sony 50. + - Apply bad column(?) data (hbd.unknown1) + - It was left out in this initial release since the effect is very small. + - Apply black(?) data, tag 0x1a and 0x1b is not parsed, it has however marginal + effect (at least for shorter exposures) so it's not too important. + + While there are things to do, the current implementation supports the most + important aspects, the faltfield and levels calibrations applied can have strong + visible effects. + + */ + + if (hbd.levels) { + int i; + fseek(ifp, hbd.levels, SEEK_SET); + /* skip the first set (not used as we don't apply on first/last row), we look at it though to see if + the levels format is one that we support (there are other formats on some models which is not + supported here) */ + short test[10]; + for (i = 0; i < 10; i++) test[i] = (short)get2(); + if (test[5] == 0 && test[6] == 0 && test[7] == 0 && test[8] == 0 && test[9] == 0) { + int corr[4]; + ushort *row_above = (ushort *)malloc(sizeof(ushort) * raw_width); // we need to cache row above as we write new values as we go + for (col = 0; col < raw_width; col++) row_above[col] = RAW(0,col); + for (row = 1; row < raw_height-1; row++) { + for (i = 0; i < 4; i++) corr[i] = (short)get2(); + fseek(ifp, 6 * 2, SEEK_CUR); + for (col = 0; col < raw_width; col++) { + unsigned v = RAW(row,col); + if (v >= 65534) { + v = 65535; + } else { + if (corr[((col & 1)<<1)+0] && row_above[col] > black) v += 2 * ((corr[((col & 1)<<1)+0] * (row_above[col]-(int)black)) / 32767) - 2; + if (corr[((col & 1)<<1)+1] && RAW(row+1,col) > black) v += 2 * ((corr[((col & 1)<<1)+1] * (RAW(row+1,col)-(int)black)) / 32767) - 2; + } + row_above[col] = RAW(row,col); + RAW(row,col) = CLIP(v); + } + } + free(row_above); + } + } + + if (hbd.flatfield) { + int bw, bh, ffrows, ffcols, i, c; + ushort ref[4], ref_max; + fseek(ifp, hbd.flatfield, SEEK_SET); + get2(); + bw = get2(); + bh = get2(); + ffcols = get2(); + ffrows = get2(); + fseek(ifp, hbd.flatfield + 16 * 2, SEEK_SET); + + ushort *ffmap = (ushort *)malloc(sizeof(*ffmap) * 4 * ffcols * ffrows); + for (i = 0; i < 4 * ffcols * ffrows; i++) ffmap[i] = get2(); + + /* Get reference values from center of field. This seems to be what Phocus does too, + but haven't cared to figure out exactly at which coordinate */ + i = 4 * (ffcols * ffrows / 2 + ffcols / 2); + ref[0] = ffmap[i+0]; + ref[1] = ffmap[i+1]; + ref[3] = ffmap[i+2]; // G2 = index 3 in dcraw, 2 in 3FR + ref[2] = ffmap[i+3]; + ref_max = 0; + FORC4 if (ref[c] > ref_max) ref_max = ref[c]; + if (ref_max == 0) ref[0] = ref[1] = ref[2] = ref[3] = ref_max = 10000; + + /* Convert measured flatfield values to actual multipliers. The measured flatfield + can have vignetting which should be normalized, only color cast should be corrected. */ + for (i = 0; i < 4 * ffcols * ffrows; i += 4) { + double base, min = 65535.0, max = 0; + double cur[4]; + cur[0] = (double)ffmap[i+0] / ref[0]; + cur[1] = (double)ffmap[i+1] / ref[1]; + cur[3] = (double)ffmap[i+2] / ref[3]; // G2 index differs in dcraw and 3FR + cur[2] = (double)ffmap[i+3] / ref[2]; + FORC4 { + if (cur[c] < min) min = cur[c]; + if (cur[c] > max) max = cur[c]; + } + if (max == 0) max = 1.0; + base = (cur[0]+cur[1]+cur[2]+cur[3])/(max*4); + FORC4 cur[c] = cur[c] == 0 ? 1.0 : (base * max) / cur[c]; + /* convert to integer multiplier and store back to ffmap, we limit + range to 4 (16384*4) which should be fine for flatfield */ + FORC4 { + cur[c] *= 16384.0; + if (cur[c] > 65535.0) cur[c] = 65535.0; + ffmap[i+c] = (ushort)cur[c]; + } + } + + // of the cameras we've tested we know the exact placement of the flatfield map + int row_offset, col_offset; + switch (raw_width) { + case 8282: // 50 megapixel Kodak + row_offset = 21; + col_offset = 71; + break; + default: + /* Default case for camera models we've not tested. We center the map, which may + not be exactly where it should be but close enough for the smooth flatfield */ + row_offset = (raw_height - bh * ffrows) / 2; + col_offset = (raw_width - bw * ffcols) / 2; + break; + } + + /* + Concerning smoothing between blocks in the map Phocus makes it only vertically, + probably because it's simpler and faster. Looking at actual flatfield data it seems + like it's better to smooth in both directions. Possibly flatfield could be used for + correcting tiling on Dalsa sensors (H4D-60) like partly done in Phase One IIQ format, + and then sharp edges may be beneficial at least at the tiling seams, but at the time + of writing I've had no H4D-60 3FR files to test with to verify that. + + Meanwhile we do both vertical and horizontal smoothing/blurring. + */ + + /* pre-calculate constants for blurring. We probably should make a more efficient + blur than this, but this does not need any buffer and makes nice-looking + radial gradients */ + ushort *corners_weight = (ushort *)malloc(bw*bh*9*sizeof(*corners_weight)); + const int corners_mix[9][4][2] = { { {0,0}, {0,1}, {1,0}, {1,1} }, + { {0,1}, {1,1}, {-1,-1}, {-1,-1} }, + { {0,1}, {0,2}, {1,1}, {1,2} }, + { {1,0}, {1,1}, {-1,-1}, {-1,-1} }, + { {1,1}, {-1,-1}, {-1,-1}, {-1,-1} }, + { {1,1}, {1,2}, {-1,-1}, {-1,-1} }, + { {1,0}, {1,1}, {2,0}, {2,1} }, + { {1,1}, {2,1}, {-1,-1}, {-1,-1} }, + { {1,1}, {1,2}, {2,1}, {2,2} } }; + const ushort corners_shift[9] = { 2, 1, 2, 1, 0, 1, 2, 1, 2 }; + for (row = 0; row < bh; row++) { + const ushort maxdist = bw < bh ? bw/2-1 : bh/2-1; + const unsigned bwu = (unsigned)bw; + const unsigned bhu = (unsigned)bh; + const unsigned corners[9][2] = {{0,0}, {0,bwu/2}, {0,bwu-1}, + {bhu/2,0},{bhu/2,bwu/2},{bhu/2,bwu-1}, + {bhu-1,0},{bhu-1,bwu/2},{bhu-1,bwu-1}}; + for (col = 0; col < bw; col++) { + for (i = 0; i < 9; i++) { + ushort dist = (ushort)sqrt(abs(corners[i][0] - row) * abs(corners[i][0] - row) + abs(corners[i][1] - col) * abs(corners[i][1] - col)); + ushort weight = dist > maxdist ? 0 : maxdist - dist; + corners_weight[9*(row*bw+col)+i] = weight; + } + } + } + + // apply flatfield +#pragma omp parallel for + for (int row = 0; row < raw_height; row++) { + int ffs, cur_ffr, i, c; + if (row < row_offset) { + cur_ffr = row_offset; + ffs = 0; + } else if (row >= row_offset + ffrows * bh) { + cur_ffr = row_offset + (ffrows-1) * bh; + ffs = 4 * ffcols * (ffrows-1); + } else { + cur_ffr = row_offset + bh * ((row - row_offset) / bh); + ffs = 4 * ffcols * ((row - row_offset) / bh); + } + int next_ffc = 0, cur_ffc = col_offset; + int ffc = ffs; + ushort *cur[3][3]; // points to local ffmap entries with center at cur[1][1] + for (int col = 0; col < raw_width; col++) { + if (col == next_ffc) { + int rowsub = ffs == 0 ? 0 : ffcols*4; + int rowadd = ffs == 4 * ffcols * (ffrows-1) ? 0 : ffcols * 4; + int colsub = ffc == ffs ? 0 : 4; + int coladd = ffc == ffs + 4 * (ffcols-1) ? 0 : 4; + if (col != 0) cur_ffc = next_ffc; + else next_ffc += col_offset; + next_ffc += bw; + + cur[0][0] = &ffmap[ffc-rowsub-colsub]; + cur[0][1] = &ffmap[ffc-rowsub]; + cur[0][2] = &ffmap[ffc-rowsub+coladd]; + + cur[1][0] = &ffmap[ffc-colsub]; + cur[1][1] = &ffmap[ffc]; + cur[1][2] = &ffmap[ffc+coladd]; + + cur[2][0] = &ffmap[ffc+rowadd-colsub]; + cur[2][1] = &ffmap[ffc+rowadd]; + cur[2][2] = &ffmap[ffc+rowadd+coladd]; + + ffc += 4; + if (ffc == ffs + 4 * ffcols) next_ffc += raw_width; // last col in map, avoid stepping further + } + unsigned v = RAW(row,col); + if (v > black && v < 65535) { + c = FC(row,col); + unsigned x = col < cur_ffc ? 0 : col - cur_ffc; + unsigned y = row < cur_ffr ? 0 : row - cur_ffr; + if (x >= bw) x = bw-1; + if (y >= bh) y = bh-1; + unsigned wsum = 0; + unsigned mul = 0; + for (i = 0; i < 9; i++) { + ushort cw = corners_weight[9*(y*bw+x)+i]; + if (cw) { + unsigned m = 0; + int j; + for (j = 0; j < 1 << corners_shift[i]; j++) { + int cr = corners_mix[i][j][0], cc = corners_mix[i][j][1]; + m += cur[cr][cc][c]; + } + m >>= corners_shift[i]; + mul += m * cw; + wsum += cw; + } + } + mul /= wsum; + v = black + ((v-black) * mul) / 16384; + RAW(row,col) = v > 65535 ? 65535 : v; + } + } + } + free(ffmap); + free(corners_weight); + } +} + void CLASS hasselblad_load_raw() { struct jhead jh; @@ -2002,10 +2317,10 @@ maximum = curve[0x3ff]; } -unsigned CLASS pana_bits (int nbits) +unsigned CLASS pana_bits_t::operator() (int nbits) { - static uchar buf[0x4000]; - static int vbits; +/*RT static uchar buf[0x4000]; */ +/*RT static int vbits;*/ int byte; if (!nbits) return vbits=0; @@ -2188,7 +2503,7 @@ void CLASS kodak_radc_load_raw() { - static const char src[] = { + static const signed char src[] = { 1,1, 2,3, 3,4, 4,2, 5,7, 6,5, 7,6, 7,8, 1,0, 2,1, 3,3, 4,4, 5,2, 6,7, 7,6, 8,5, 8,8, 2,1, 2,3, 3,0, 3,2, 3,4, 4,6, 5,5, 6,7, 6,8, @@ -2294,11 +2609,11 @@ METHODDEF(boolean) fill_input_buffer (j_decompress_ptr cinfo) { - static uchar jpeg_buffer[4096]; +/*RT static uchar jpeg_buffer[4096]; */ size_t nbytes; nbytes = fread (jpeg_buffer, 1, 4096, ifp); - swab (jpeg_buffer, jpeg_buffer, nbytes); + swab ((char*)jpeg_buffer, (char*)jpeg_buffer, nbytes); cinfo->src->next_input_byte = jpeg_buffer; cinfo->src->bytes_in_buffer = nbytes; return TRUE; @@ -2648,10 +2963,9 @@ maximum = (1 << (thumb_misc & 31)) - 1; } -void CLASS sony_decrypt (unsigned *data, int len, int start, int key) +void CLASS sony_decrypt_t::operator()(unsigned *data, int len, int start, int key) { - static unsigned pad[128], p; - +/*RT static unsigned pad[128], p;*/ if (start) { for (p=0; p < 4; p++) pad[p] = key = key * 48828125 + 1; @@ -2736,11 +3050,13 @@ bit += 7; } for (i=0; i < 16; i++, col+=2) - RAW(row,col) = curve[pix[i] << 1] >> 2; + RAW(row,col) = curve[pix[i] << 1]; // >> 2; RT: disabled shifting to avoid precision loss col -= col & 1 ? 1:31; } } free (data); + maximum = curve[0x7ff << 1]; // RT: fix maximum. + maximum = 16300; // RT: conservative white level tested on various ARW2 cameras. This constant was set in 2013-12-17, may need re-evaluation in the future. } void CLASS samsung_load_raw() @@ -2905,7 +3221,10 @@ diff = diff ? -diff : 0x80; if (ftell(ifp) + 12 >= seg[1][1]) diff = 0; - raw_image[pix] = pred[pix & 1] += diff; + if(pix>=raw_width*raw_height) + derror(); + else + raw_image[pix] = pred[pix & 1] += diff; if (!(pix & 1) && HOLE(pix / raw_width)) pix += 2; } maximum = 0xff; @@ -3038,7 +3357,7 @@ void CLASS foveon_decoder (unsigned size, unsigned code) { - static unsigned huff[1024]; +/*RT static unsigned huff[1024];*/ struct decode *cur; int i, len; @@ -3135,7 +3454,7 @@ pred[c] += diff[dindex->leaf]; if (pred[c] >> 16 && ~pred[c] >> 16) derror(); } - FORC3 image[row*width+col][c] = pred[c]; + FORC3 image[row*width+col][c] = pred[c] < 0 ? 0 : pred[c]; } } } @@ -3746,6 +4065,8 @@ if (load_raw == &CLASS phase_one_load_raw || load_raw == &CLASS phase_one_load_raw_c) phase_one_correct(); + if (load_raw == &CLASS hasselblad_load_raw) // RT + hasselblad_correct(); // RT if (fuji_width) { for (row=0; row < raw_height-top_margin*2; row++) { for (col=0; col < fuji_width << !fuji_layout; col++) { @@ -3761,10 +4082,13 @@ } } } else { - for (row=0; row < height; row++) - for (col=0; col < width; col++) + +#pragma omp parallel for + for (int row=0; row < height; row++) + for (int col=0; col < width; col++) BAYER2(row,col) = RAW(row+top_margin,col+left_margin); } + if (mask[0][3] > 0) goto mask_set; if (load_raw == &CLASS canon_load_raw || load_raw == &CLASS lossless_jpeg_load_raw) { @@ -3808,127 +4132,127 @@ } } -void CLASS remove_zeroes() -{ - unsigned row, col, tot, n, r, c; - - for (row=0; row < height; row++) - for (col=0; col < width; col++) - if (BAYER(row,col) == 0) { - tot = n = 0; - for (r = row-2; r <= row+2; r++) - for (c = col-2; c <= col+2; c++) - if (r < height && c < width && - FC(r,c) == FC(row,col) && BAYER(r,c)) - tot += (n++,BAYER(r,c)); - if (n) BAYER(row,col) = tot/n; - } -} +//void CLASS remove_zeroes() +//{ +// unsigned row, col, tot, n, r, c; +// +// for (row=0; row < height; row++) +// for (col=0; col < width; col++) +// if (BAYER(row,col) == 0) { +// tot = n = 0; +// for (r = row-2; r <= row+2; r++) +// for (c = col-2; c <= col+2; c++) +// if (r < height && c < width && +// FC(r,c) == FC(row,col) && BAYER(r,c)) +// tot += (n++,BAYER(r,c)); +// if (n) BAYER(row,col) = tot/n; +// } +//} /* Seach from the current directory up to the root looking for a ".badpixels" file, and fix those pixels now. */ -void CLASS bad_pixels (const char *cfname) -{ - FILE *fp=0; - char *fname, *cp, line[128]; - int len, time, row, col, r, c, rad, tot, n, fixed=0; - - if (!filters) return; - if (cfname) - fp = fopen (cfname, "r"); - else { - for (len=32 ; ; len *= 2) { - fname = (char *) malloc (len); - if (!fname) return; - if (getcwd (fname, len-16)) break; - free (fname); - if (errno != ERANGE) return; - } -#if defined(WIN32) || defined(DJGPP) - if (fname[1] == ':') - memmove (fname, fname+2, len-2); - for (cp=fname; *cp; cp++) - if (*cp == '\\') *cp = '/'; -#endif - cp = fname + strlen(fname); - if (cp[-1] == '/') cp--; - while (*fname == '/') { - strcpy (cp, "/.badpixels"); - if ((fp = fopen (fname, "r"))) break; - if (cp == fname) break; - while (*--cp != '/'); - } - free (fname); - } - if (!fp) return; - while (fgets (line, 128, fp)) { - cp = strchr (line, '#'); - if (cp) *cp = 0; - if (sscanf (line, "%d %d %d", &col, &row, &time) != 3) continue; - if ((unsigned) col >= width || (unsigned) row >= height) continue; - if (time > timestamp) continue; - for (tot=n=0, rad=1; rad < 3 && n==0; rad++) - for (r = row-rad; r <= row+rad; r++) - for (c = col-rad; c <= col+rad; c++) - if ((unsigned) r < height && (unsigned) c < width && - (r != row || c != col) && fcol(r,c) == fcol(row,col)) { - tot += BAYER2(r,c); - n++; - } - BAYER2(row,col) = tot/n; - if (verbose) { - if (!fixed++) - fprintf (stderr,_("Fixed dead pixels at:")); - fprintf (stderr, " %d,%d", col, row); - } - } - if (fixed) fputc ('\n', stderr); - fclose (fp); -} - -void CLASS subtract (const char *fname) -{ - FILE *fp; - int dim[3]={0,0,0}, comment=0, number=0, error=0, nd=0, c, row, col; - ushort *pixel; - - if (!(fp = fopen (fname, "rb"))) { - perror (fname); return; - } - if (fgetc(fp) != 'P' || fgetc(fp) != '5') error = 1; - while (!error && nd < 3 && (c = fgetc(fp)) != EOF) { - if (c == '#') comment = 1; - if (c == '\n') comment = 0; - if (comment) continue; - if (isdigit(c)) number = 1; - if (number) { - if (isdigit(c)) dim[nd] = dim[nd]*10 + c -'0'; - else if (isspace(c)) { - number = 0; nd++; - } else error = 1; - } - } - if (error || nd < 3) { - fprintf (stderr,_("%s is not a valid PGM file!\n"), fname); - fclose (fp); return; - } else if (dim[0] != width || dim[1] != height || dim[2] != 65535) { - fprintf (stderr,_("%s has the wrong dimensions!\n"), fname); - fclose (fp); return; - } - pixel = (ushort *) calloc (width, sizeof *pixel); - merror (pixel, "subtract()"); - for (row=0; row < height; row++) { - fread (pixel, 2, width, fp); - for (col=0; col < width; col++) - BAYER(row,col) = MAX (BAYER(row,col) - ntohs(pixel[col]), 0); - } - free (pixel); - fclose (fp); - memset (cblack, 0, sizeof cblack); - black = 0; -} +//void CLASS bad_pixels (const char *cfname) +//{ +// FILE *fp=0; +// char *fname, *cp, line[128]; +// int len, time, row, col, r, c, rad, tot, n, fixed=0; +// +// if (!filters) return; +// if (cfname) +// fp = fopen (cfname, "r"); +// else { +// for (len=32 ; ; len *= 2) { +// fname = (char *) malloc (len); +// if (!fname) return; +// if (getcwd (fname, len-16)) break; +// free (fname); +// if (errno != ERANGE) return; +// } +//#if defined(WIN32) || defined(DJGPP) +// if (fname[1] == ':') +// memmove (fname, fname+2, len-2); +// for (cp=fname; *cp; cp++) +// if (*cp == '\\') *cp = '/'; +//#endif +// cp = fname + strlen(fname); +// if (cp[-1] == '/') cp--; +// while (*fname == '/') { +// strcpy (cp, "/.badpixels"); +// if ((fp = fopen (fname, "r"))) break; +// if (cp == fname) break; +// while (*--cp != '/'); +// } +// free (fname); +// } +// if (!fp) return; +// while (fgets (line, 128, fp)) { +// cp = strchr (line, '#'); +// if (cp) *cp = 0; +// if (sscanf (line, "%d %d %d", &col, &row, &time) != 3) continue; +// if ((unsigned) col >= width || (unsigned) row >= height) continue; +// if (time > timestamp) continue; +// for (tot=n=0, rad=1; rad < 3 && n==0; rad++) +// for (r = row-rad; r <= row+rad; r++) +// for (c = col-rad; c <= col+rad; c++) +// if ((unsigned) r < height && (unsigned) c < width && +// (r != row || c != col) && fcol(r,c) == fcol(row,col)) { +// tot += BAYER2(r,c); +// n++; +// } +// BAYER2(row,col) = tot/n; +// if (verbose) { +// if (!fixed++) +// fprintf (stderr,_("Fixed dead pixels at:")); +// fprintf (stderr, " %d,%d", col, row); +// } +// } +// if (fixed) fputc ('\n', stderr); +// fclose (fp); +//} + +//void CLASS subtract (const char *fname) +//{ +// FILE *fp; +// int dim[3]={0,0,0}, comment=0, number=0, error=0, nd=0, c, row, col; +// ushort *pixel; +// +// if (!(fp = fopen (fname, "rb"))) { +// perror (fname); return; +// } +// if (fgetc(fp) != 'P' || fgetc(fp) != '5') error = 1; +// while (!error && nd < 3 && (c = fgetc(fp)) != EOF) { +// if (c == '#') comment = 1; +// if (c == '\n') comment = 0; +// if (comment) continue; +// if (isdigit(c)) number = 1; +// if (number) { +// if (isdigit(c)) dim[nd] = dim[nd]*10 + c -'0'; +// else if (isspace(c)) { +// number = 0; nd++; +// } else error = 1; +// } +// } +// if (error || nd < 3) { +// fprintf (stderr,_("%s is not a valid PGM file!\n"), fname); +// fclose (fp); return; +// } else if (dim[0] != width || dim[1] != height || dim[2] != 65535) { +// fprintf (stderr,_("%s has the wrong dimensions!\n"), fname); +// fclose (fp); return; +// } +// pixel = (ushort *) calloc (width, sizeof *pixel); +// merror (pixel, "subtract()"); +// for (row=0; row < height; row++) { +// fread (pixel, 2, width, fp); +// for (col=0; col < width; col++) +// BAYER(row,col) = MAX (BAYER(row,col) - ntohs(pixel[col]), 0); +// } +// free (pixel); +// fclose (fp); +// memset (cblack, 0, sizeof cblack); +// black = 0; +//} void CLASS gamma_curve (double pwr, double ts, int mode, int imax) { @@ -4093,94 +4417,94 @@ } #endif -void CLASS hat_transform (float *temp, float *base, int st, int size, int sc) -{ - int i; - for (i=0; i < sc; i++) - temp[i] = 2*base[st*i] + base[st*(sc-i)] + base[st*(i+sc)]; - for (; i+sc < size; i++) - temp[i] = 2*base[st*i] + base[st*(i-sc)] + base[st*(i+sc)]; - for (; i < size; i++) - temp[i] = 2*base[st*i] + base[st*(i-sc)] + base[st*(2*size-2-(i+sc))]; -} - -void CLASS wavelet_denoise() -{ - float *fimg=0, *temp, thold, mul[2], avg, diff; - int scale=1, size, lev, hpass, lpass, row, col, nc, c, i, wlast, blk[2]; - ushort *window[4]; - static const float noise[] = - { 0.8002,0.2735,0.1202,0.0585,0.0291,0.0152,0.0080,0.0044 }; - - if (verbose) fprintf (stderr,_("Wavelet denoising...\n")); - - while (maximum << scale < 0x10000) scale++; - maximum <<= --scale; - black <<= scale; - FORC4 cblack[c] <<= scale; - if ((size = iheight*iwidth) < 0x15550000) - fimg = (float *) malloc ((size*3 + iheight + iwidth) * sizeof *fimg); - merror (fimg, "wavelet_denoise()"); - temp = fimg + size*3; - if ((nc = colors) == 3 && filters) nc++; - FORC(nc) { /* denoise R,G1,B,G3 individually */ - for (i=0; i < size; i++) - fimg[i] = 256 * sqrt(image[i][c] << scale); - for (hpass=lev=0; lev < 5; lev++) { - lpass = size*((lev & 1)+1); - for (row=0; row < iheight; row++) { - hat_transform (temp, fimg+hpass+row*iwidth, 1, iwidth, 1 << lev); - for (col=0; col < iwidth; col++) - fimg[lpass + row*iwidth + col] = temp[col] * 0.25; - } - for (col=0; col < iwidth; col++) { - hat_transform (temp, fimg+lpass+col, iwidth, iheight, 1 << lev); - for (row=0; row < iheight; row++) - fimg[lpass + row*iwidth + col] = temp[row] * 0.25; - } - thold = threshold * noise[lev]; - for (i=0; i < size; i++) { - fimg[hpass+i] -= fimg[lpass+i]; - if (fimg[hpass+i] < -thold) fimg[hpass+i] += thold; - else if (fimg[hpass+i] > thold) fimg[hpass+i] -= thold; - else fimg[hpass+i] = 0; - if (hpass) fimg[i] += fimg[hpass+i]; - } - hpass = lpass; - } - for (i=0; i < size; i++) - image[i][c] = CLIP(SQR(fimg[i]+fimg[lpass+i])/0x10000); - } - if (filters && colors == 3) { /* pull G1 and G3 closer together */ - for (row=0; row < 2; row++) { - mul[row] = 0.125 * pre_mul[FC(row+1,0) | 1] / pre_mul[FC(row,0) | 1]; - blk[row] = cblack[FC(row,0) | 1]; - } - for (i=0; i < 4; i++) - window[i] = (ushort *) fimg + width*i; - for (wlast=-1, row=1; row < height-1; row++) { - while (wlast < row+1) { - for (wlast++, i=0; i < 4; i++) - window[(i+3) & 3] = window[i]; - for (col = FC(wlast,1) & 1; col < width; col+=2) - window[2][col] = BAYER(wlast,col); - } - thold = threshold/512; - for (col = (FC(row,0) & 1)+1; col < width-1; col+=2) { - avg = ( window[0][col-1] + window[0][col+1] + - window[2][col-1] + window[2][col+1] - blk[~row & 1]*4 ) - * mul[row & 1] + (window[1][col] + blk[row & 1]) * 0.5; - avg = avg < 0 ? 0 : sqrt(avg); - diff = sqrt(BAYER(row,col)) - avg; - if (diff < -thold) diff += thold; - else if (diff > thold) diff -= thold; - else diff = 0; - BAYER(row,col) = CLIP(SQR(avg+diff) + 0.5); - } - } - } - free (fimg); -} +//void CLASS hat_transform (float *temp, float *base, int st, int size, int sc) +//{ +// int i; +// for (i=0; i < sc; i++) +// temp[i] = 2*base[st*i] + base[st*(sc-i)] + base[st*(i+sc)]; +// for (; i+sc < size; i++) +// temp[i] = 2*base[st*i] + base[st*(i-sc)] + base[st*(i+sc)]; +// for (; i < size; i++) +// temp[i] = 2*base[st*i] + base[st*(i-sc)] + base[st*(2*size-2-(i+sc))]; +//} + +//void CLASS wavelet_denoise() +//{ +// float *fimg=0, *temp, thold, mul[2], avg, diff; +// int scale=1, size, lev, hpass, lpass, row, col, nc, c, i, wlast, blk[2]; +// ushort *window[4]; +// static const float noise[] = +// { 0.8002,0.2735,0.1202,0.0585,0.0291,0.0152,0.0080,0.0044 }; +// +// if (verbose) fprintf (stderr,_("Wavelet denoising...\n")); +// +// while (maximum << scale < 0x10000) scale++; +// maximum <<= --scale; +// black <<= scale; +// FORC4 cblack[c] <<= scale; +// if ((size = iheight*iwidth) < 0x15550000) +// fimg = (float *) malloc ((size*3 + iheight + iwidth) * sizeof *fimg); +// merror (fimg, "wavelet_denoise()"); +// temp = fimg + size*3; +// if ((nc = colors) == 3 && filters) nc++; +// FORC(nc) { /* denoise R,G1,B,G3 individually */ +// for (i=0; i < size; i++) +// fimg[i] = 256 * sqrt(image[i][c] << scale); +// for (hpass=lev=0; lev < 5; lev++) { +// lpass = size*((lev & 1)+1); +// for (row=0; row < iheight; row++) { +// hat_transform (temp, fimg+hpass+row*iwidth, 1, iwidth, 1 << lev); +// for (col=0; col < iwidth; col++) +// fimg[lpass + row*iwidth + col] = temp[col] * 0.25; +// } +// for (col=0; col < iwidth; col++) { +// hat_transform (temp, fimg+lpass+col, iwidth, iheight, 1 << lev); +// for (row=0; row < iheight; row++) +// fimg[lpass + row*iwidth + col] = temp[row] * 0.25; +// } +// thold = threshold * noise[lev]; +// for (i=0; i < size; i++) { +// fimg[hpass+i] -= fimg[lpass+i]; +// if (fimg[hpass+i] < -thold) fimg[hpass+i] += thold; +// else if (fimg[hpass+i] > thold) fimg[hpass+i] -= thold; +// else fimg[hpass+i] = 0; +// if (hpass) fimg[i] += fimg[hpass+i]; +// } +// hpass = lpass; +// } +// for (i=0; i < size; i++) +// image[i][c] = CLIP(SQR(fimg[i]+fimg[lpass+i])/0x10000); +// } +// if (filters && colors == 3) { /* pull G1 and G3 closer together */ +// for (row=0; row < 2; row++) { +// mul[row] = 0.125 * pre_mul[FC(row+1,0) | 1] / pre_mul[FC(row,0) | 1]; +// blk[row] = cblack[FC(row,0) | 1]; +// } +// for (i=0; i < 4; i++) +// window[i] = (ushort *) fimg + width*i; +// for (wlast=-1, row=1; row < height-1; row++) { +// while (wlast < row+1) { +// for (wlast++, i=0; i < 4; i++) +// window[(i+3) & 3] = window[i]; +// for (col = FC(wlast,1) & 1; col < width; col+=2) +// window[2][col] = BAYER(wlast,col); +// } +// thold = threshold/512; +// for (col = (FC(row,0) & 1)+1; col < width-1; col+=2) { +// avg = ( window[0][col-1] + window[0][col+1] + +// window[2][col-1] + window[2][col+1] - blk[~row & 1]*4 ) +// * mul[row & 1] + (window[1][col] + blk[row & 1]) * 0.5; +// avg = avg < 0 ? 0 : sqrt(avg); +// diff = sqrt(BAYER(row,col)) - avg; +// if (diff < -thold) diff += thold; +// else if (diff > thold) diff -= thold; +// else diff = 0; +// BAYER(row,col) = CLIP(SQR(avg+diff) + 0.5); +// } +// } +// } +// free (fimg); +//} void CLASS scale_colors() { @@ -4238,7 +4562,7 @@ if (pre_mul[3] == 0) pre_mul[3] = colors < 4 ? pre_mul[1] : 1; dark = black; sat = maximum; - if (threshold) wavelet_denoise(); +// if (threshold) wavelet_denoise(); maximum -= black; for (dmin=DBL_MAX, dmax=c=0; c < 4; c++) { if (dmin > pre_mul[c]) @@ -4344,776 +4668,440 @@ if (half_size) filters = 0; } -void CLASS border_interpolate (int border) -{ - unsigned row, col, y, x, f, c, sum[8]; - - for (row=0; row < height; row++) - for (col=0; col < width; col++) { - if (col==border && row >= border && row < height-border) - col = width-border; - memset (sum, 0, sizeof sum); - for (y=row-1; y != row+2; y++) - for (x=col-1; x != col+2; x++) - if (y < height && x < width) { - f = fcol(y,x); - sum[f] += image[y*width+x][f]; - sum[f+4]++; - } - f = fcol(row,col); - FORCC if (c != f && sum[c+4]) - image[row*width+col][c] = sum[c] / sum[c+4]; - } -} - -void CLASS lin_interpolate() -{ - int code[16][16][32], size=16, *ip, sum[4]; - int f, c, i, x, y, row, col, shift, color; - ushort *pix; - - if (verbose) fprintf (stderr,_("Bilinear interpolation...\n")); - if (filters == 9) size = 6; - border_interpolate(1); - for (row=0; row < size; row++) - for (col=0; col < size; col++) { - ip = code[row][col]+1; - f = fcol(row,col); - memset (sum, 0, sizeof sum); - for (y=-1; y <= 1; y++) - for (x=-1; x <= 1; x++) { - shift = (y==0) + (x==0); - color = fcol(row+y,col+x); - if (color == f) continue; - *ip++ = (width*y + x)*4 + color; - *ip++ = shift; - *ip++ = color; - sum[color] += 1 << shift; - } - code[row][col][0] = (ip - code[row][col]) / 3; - FORCC - if (c != f) { - *ip++ = c; - *ip++ = 256 / sum[c]; - } - } - for (row=1; row < height-1; row++) - for (col=1; col < width-1; col++) { - pix = image[row*width+col]; - ip = code[row % size][col % size]; - memset (sum, 0, sizeof sum); - for (i=*ip++; i--; ip+=3) - sum[ip[2]] += pix[ip[0]] << ip[1]; - for (i=colors; --i; ip+=2) - pix[ip[0]] = sum[ip[0]] * ip[1] >> 8; - } -} - -/* - This algorithm is officially called: - - "Interpolation using a Threshold-based variable number of gradients" - - described in http://scien.stanford.edu/pages/labsite/1999/psych221/projects/99/tingchen/algodep/vargra.html - - I've extended the basic idea to work with non-Bayer filter arrays. - Gradients are numbered clockwise from NW=0 to W=7. - */ -void CLASS vng_interpolate() -{ - static const signed char *cp, terms[] = { - -2,-2,+0,-1,0,0x01, -2,-2,+0,+0,1,0x01, -2,-1,-1,+0,0,0x01, - -2,-1,+0,-1,0,0x02, -2,-1,+0,+0,0,0x03, -2,-1,+0,+1,1,0x01, - -2,+0,+0,-1,0,0x06, -2,+0,+0,+0,1,0x02, -2,+0,+0,+1,0,0x03, - -2,+1,-1,+0,0,0x04, -2,+1,+0,-1,1,0x04, -2,+1,+0,+0,0,0x06, - -2,+1,+0,+1,0,0x02, -2,+2,+0,+0,1,0x04, -2,+2,+0,+1,0,0x04, - -1,-2,-1,+0,0,0x80, -1,-2,+0,-1,0,0x01, -1,-2,+1,-1,0,0x01, - -1,-2,+1,+0,1,0x01, -1,-1,-1,+1,0,0x88, -1,-1,+1,-2,0,0x40, - -1,-1,+1,-1,0,0x22, -1,-1,+1,+0,0,0x33, -1,-1,+1,+1,1,0x11, - -1,+0,-1,+2,0,0x08, -1,+0,+0,-1,0,0x44, -1,+0,+0,+1,0,0x11, - -1,+0,+1,-2,1,0x40, -1,+0,+1,-1,0,0x66, -1,+0,+1,+0,1,0x22, - -1,+0,+1,+1,0,0x33, -1,+0,+1,+2,1,0x10, -1,+1,+1,-1,1,0x44, - -1,+1,+1,+0,0,0x66, -1,+1,+1,+1,0,0x22, -1,+1,+1,+2,0,0x10, - -1,+2,+0,+1,0,0x04, -1,+2,+1,+0,1,0x04, -1,+2,+1,+1,0,0x04, - +0,-2,+0,+0,1,0x80, +0,-1,+0,+1,1,0x88, +0,-1,+1,-2,0,0x40, - +0,-1,+1,+0,0,0x11, +0,-1,+2,-2,0,0x40, +0,-1,+2,-1,0,0x20, - +0,-1,+2,+0,0,0x30, +0,-1,+2,+1,1,0x10, +0,+0,+0,+2,1,0x08, - +0,+0,+2,-2,1,0x40, +0,+0,+2,-1,0,0x60, +0,+0,+2,+0,1,0x20, - +0,+0,+2,+1,0,0x30, +0,+0,+2,+2,1,0x10, +0,+1,+1,+0,0,0x44, - +0,+1,+1,+2,0,0x10, +0,+1,+2,-1,1,0x40, +0,+1,+2,+0,0,0x60, - +0,+1,+2,+1,0,0x20, +0,+1,+2,+2,0,0x10, +1,-2,+1,+0,0,0x80, - +1,-1,+1,+1,0,0x88, +1,+0,+1,+2,0,0x08, +1,+0,+2,-1,0,0x40, - +1,+0,+2,+1,0,0x10 - }, chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 }; - ushort (*brow[5])[4], *pix; - int prow=8, pcol=2, *ip, *code[16][16], gval[8], gmin, gmax, sum[4]; - int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag; - int g, diff, thold, num, c; - - lin_interpolate(); - if (verbose) fprintf (stderr,_("VNG interpolation...\n")); - - if (filters == 1) prow = pcol = 16; - if (filters == 9) prow = pcol = 6; - ip = (int *) calloc (prow*pcol, 1280); - merror (ip, "vng_interpolate()"); - for (row=0; row < prow; row++) /* Precalculate for VNG */ - for (col=0; col < pcol; col++) { - code[row][col] = ip; - for (cp=terms, t=0; t < 64; t++) { - y1 = *cp++; x1 = *cp++; - y2 = *cp++; x2 = *cp++; - weight = *cp++; - grads = *cp++; - color = fcol(row+y1,col+x1); - if (fcol(row+y2,col+x2) != color) continue; - diag = (fcol(row,col+1) == color && fcol(row+1,col) == color) ? 2:1; - if (abs(y1-y2) == diag && abs(x1-x2) == diag) continue; - *ip++ = (y1*width + x1)*4 + color; - *ip++ = (y2*width + x2)*4 + color; - *ip++ = weight; - for (g=0; g < 8; g++) - if (grads & 1< gval[g]) gmin = gval[g]; - if (gmax < gval[g]) gmax = gval[g]; - } - if (gmax == 0) { - memcpy (brow[2][col], pix, sizeof *image); - continue; - } - thold = gmin + (gmax >> 1); - memset (sum, 0, sizeof sum); - color = fcol(row,col); - for (num=g=0; g < 8; g++,ip+=2) { /* Average the neighbors */ - if (gval[g] <= thold) { - FORCC - if (c == color && ip[1]) - sum[c] += (pix[c] + pix[ip[1]]) >> 1; - else - sum[c] += pix[ip[0] + c]; - num++; - } - } - FORCC { /* Save to buffer */ - t = pix[color]; - if (c != color) - t += (sum[c] - sum[color]) / num; - brow[2][col][c] = CLIP(t); - } - } - if (row > 3) /* Write buffer to image */ - memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image); - for (g=0; g < 4; g++) - brow[(g-1) & 3] = brow[g]; - } - memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image); - memcpy (image[(row-1)*width+2], brow[1]+2, (width-4)*sizeof *image); - free (brow[4]); - free (code[0][0]); -} - -/* - Patterned Pixel Grouping Interpolation by Alain Desbiolles -*/ -void CLASS ppg_interpolate() -{ - int dir[5] = { 1, width, -1, -width, 1 }; - int row, col, diff[2], guess[2], c, d, i; - ushort (*pix)[4]; - - border_interpolate(3); - if (verbose) fprintf (stderr,_("PPG interpolation...\n")); - -/* Fill in the green layer with gradients and pattern recognition: */ - for (row=3; row < height-3; row++) - for (col=3+(FC(row,3) & 1), c=FC(row,col); col < width-3; col+=2) { - pix = image + row*width+col; - for (i=0; (d=dir[i]) > 0; i++) { - guess[i] = (pix[-d][1] + pix[0][c] + pix[d][1]) * 2 - - pix[-2*d][c] - pix[2*d][c]; - diff[i] = ( ABS(pix[-2*d][c] - pix[ 0][c]) + - ABS(pix[ 2*d][c] - pix[ 0][c]) + - ABS(pix[ -d][1] - pix[ d][1]) ) * 3 + - ( ABS(pix[ 3*d][1] - pix[ d][1]) + - ABS(pix[-3*d][1] - pix[-d][1]) ) * 2; - } - d = dir[i = diff[0] > diff[1]]; - pix[0][1] = ULIM(guess[i] >> 2, pix[d][1], pix[-d][1]); - } -/* Calculate red and blue for each green pixel: */ - for (row=1; row < height-1; row++) - for (col=1+(FC(row,2) & 1), c=FC(row,col+1); col < width-1; col+=2) { - pix = image + row*width+col; - for (i=0; (d=dir[i]) > 0; c=2-c, i++) - pix[0][c] = CLIP((pix[-d][c] + pix[d][c] + 2*pix[0][1] - - pix[-d][1] - pix[d][1]) >> 1); - } -/* Calculate blue for red pixels and vice versa: */ - for (row=1; row < height-1; row++) - for (col=1+(FC(row,1) & 1), c=2-FC(row,col); col < width-1; col+=2) { - pix = image + row*width+col; - for (i=0; (d=dir[i]+dir[i+1]) > 0; i++) { - diff[i] = ABS(pix[-d][c] - pix[d][c]) + - ABS(pix[-d][1] - pix[0][1]) + - ABS(pix[ d][1] - pix[0][1]); - guess[i] = pix[-d][c] + pix[d][c] + 2*pix[0][1] - - pix[-d][1] - pix[d][1]; - } - if (diff[0] != diff[1]) - pix[0][c] = CLIP(guess[diff[0] > diff[1]] >> 1); - else - pix[0][c] = CLIP((guess[0]+guess[1]) >> 2); - } -} - -void CLASS cielab (ushort rgb[3], short lab[3]) -{ - int c, i, j, k; - float r, xyz[3]; - static float cbrt[0x10000], xyz_cam[3][4]; - - if (!rgb) { - for (i=0; i < 0x10000; i++) { - r = i / 65535.0; - cbrt[i] = r > 0.008856 ? pow(r,1/3.0) : 7.787*r + 16/116.0; - } - for (i=0; i < 3; i++) - for (j=0; j < colors; j++) - for (xyz_cam[i][j] = k=0; k < 3; k++) - xyz_cam[i][j] += xyz_rgb[i][k] * rgb_cam[k][j] / d65_white[i]; - return; - } - xyz[0] = xyz[1] = xyz[2] = 0.5; - FORCC { - xyz[0] += xyz_cam[0][c] * rgb[c]; - xyz[1] += xyz_cam[1][c] * rgb[c]; - xyz[2] += xyz_cam[2][c] * rgb[c]; - } - xyz[0] = cbrt[CLIP((int) xyz[0])]; - xyz[1] = cbrt[CLIP((int) xyz[1])]; - xyz[2] = cbrt[CLIP((int) xyz[2])]; - lab[0] = 64 * (116 * xyz[1] - 16); - lab[1] = 64 * 500 * (xyz[0] - xyz[1]); - lab[2] = 64 * 200 * (xyz[1] - xyz[2]); -} - -#define TS 512 /* Tile Size */ -#define fcol(row,col) xtrans[(row+6) % 6][(col+6) % 6] - -/* - Frank Markesteijn's algorithm for Fuji X-Trans sensors - */ -void CLASS xtrans_interpolate (int passes) -{ - int c, d, f, g, h, i, v, ng, row, col, top, left, mrow, mcol; - int val, ndir, pass, hm[8], avg[4], color[3][8]; - static const short orth[12] = { 1,0,0,1,-1,0,0,-1,1,0,0,1 }, - patt[2][16] = { { 0,1,0,-1,2,0,-1,0,1,1,1,-1,0,0,0,0 }, - { 0,1,0,-2,1,0,-2,0,1,1,-2,-2,1,-1,-1,1 } }, - dir[4] = { 1,TS,TS+1,TS-1 }; - short allhex[3][3][2][8], *hex; - ushort min, max, sgrow, sgcol; - ushort (*rgb)[TS][TS][3], (*rix)[3], (*pix)[4]; - short (*lab) [TS][3], (*lix)[3]; - float (*drv)[TS][TS], diff[6], tr; - char (*homo)[TS][TS], *buffer; - - if (verbose) - fprintf (stderr,_("%d-pass X-Trans interpolation...\n"), passes); - - cielab (0,0); - ndir = 4 << (passes > 1); - buffer = (char *) malloc (TS*TS*(ndir*11+6)); - merror (buffer, "xtrans_interpolate()"); - rgb = (ushort(*)[TS][TS][3]) buffer; - lab = (short (*) [TS][3])(buffer + TS*TS*(ndir*6)); - drv = (float (*)[TS][TS]) (buffer + TS*TS*(ndir*6+6)); - homo = (char (*)[TS][TS]) (buffer + TS*TS*(ndir*10+6)); - -/* Map a green hexagon around each non-green pixel and vice versa: */ - for (row=0; row < 3; row++) - for (col=0; col < 3; col++) - for (ng=d=0; d < 10; d+=2) { - g = fcol(row,col) == 1; - if (fcol(row+orth[d],col+orth[d+2]) == 1) ng=0; else ng++; - if (ng == 4) { sgrow = row; sgcol = col; } - if (ng == g+1) FORC(8) { - v = orth[d ]*patt[g][c*2] + orth[d+1]*patt[g][c*2+1]; - h = orth[d+2]*patt[g][c*2] + orth[d+3]*patt[g][c*2+1]; - allhex[row][col][0][c^(g*2 & d)] = h + v*width; - allhex[row][col][1][c^(g*2 & d)] = h + v*TS; - } - } - -/* Set green1 and green3 to the minimum and maximum allowed values: */ - for (row=2; row < height-2; row++) - for (min=~(max=0), col=2; col < width-2; col++) { - if (fcol(row,col) == 1 && (min=~(max=0))) continue; - pix = image + row*width + col; - hex = allhex[row % 3][col % 3][0]; - if (!max) FORC(6) { - val = pix[hex[c]][1]; - if (min > val) min = val; - if (max < val) max = val; - } - pix[0][1] = min; - pix[0][3] = max; - switch ((row-sgrow) % 3) { - case 1: if (row < height-3) { row++; col--; } break; - case 2: if ((min=~(max=0)) && (col+=2) < width-3 && row > 2) row--; - } - } - - for (top=3; top < height-19; top += TS-16) - for (left=3; left < width-19; left += TS-16) { - mrow = MIN (top+TS, height-3); - mcol = MIN (left+TS, width-3); - for (row=top; row < mrow; row++) - for (col=left; col < mcol; col++) - memcpy (rgb[0][row-top][col-left], image[row*width+col], 6); - FORC3 memcpy (rgb[c+1], rgb[0], sizeof *rgb); - -/* Interpolate green horizontally, vertically, and along both diagonals: */ - for (row=top; row < mrow; row++) - for (col=left; col < mcol; col++) { - if ((f = fcol(row,col)) == 1) continue; - pix = image + row*width + col; - hex = allhex[row % 3][col % 3][0]; - color[1][0] = 174 * (pix[ hex[1]][1] + pix[ hex[0]][1]) - - 46 * (pix[2*hex[1]][1] + pix[2*hex[0]][1]); - color[1][1] = 223 * pix[ hex[3]][1] + pix[ hex[2]][1] * 33 + - 92 * (pix[ 0 ][f] - pix[ -hex[2]][f]); - FORC(2) color[1][2+c] = - 164 * pix[hex[4+c]][1] + 92 * pix[-2*hex[4+c]][1] + 33 * - (2*pix[0][f] - pix[3*hex[4+c]][f] - pix[-3*hex[4+c]][f]); - FORC4 rgb[c^!((row-sgrow) % 3)][row-top][col-left][1] = - LIM(color[1][c] >> 8,pix[0][1],pix[0][3]); - } - - for (pass=0; pass < passes; pass++) { - if (pass == 1) - memcpy (rgb+=4, buffer, 4*sizeof *rgb); - -/* Recalculate green from interpolated values of closer pixels: */ - if (pass) { - for (row=top+2; row < mrow-2; row++) - for (col=left+2; col < mcol-2; col++) { - if ((f = fcol(row,col)) == 1) continue; - pix = image + row*width + col; - hex = allhex[row % 3][col % 3][1]; - for (d=3; d < 6; d++) { - rix = &rgb[(d-2)^!((row-sgrow) % 3)][row-top][col-left]; - val = rix[-2*hex[d]][1] + 2*rix[hex[d]][1] - - rix[-2*hex[d]][f] - 2*rix[hex[d]][f] + 3*rix[0][f]; - rix[0][1] = LIM(val/3,pix[0][1],pix[0][3]); - } - } - } - -/* Interpolate red and blue values for solitary green pixels: */ - for (row=(top-sgrow+4)/3*3+sgrow; row < mrow-2; row+=3) - for (col=(left-sgcol+4)/3*3+sgcol; col < mcol-2; col+=3) { - rix = &rgb[0][row-top][col-left]; - h = fcol(row,col+1); - memset (diff, 0, sizeof diff); - for (i=1, d=0; d < 6; d++, i^=TS^1, h^=2) { - for (c=0; c < 2; c++, h^=2) { - g = 2*rix[0][1] - rix[i< 1) - diff[d] += SQR (rix[i< 1 && (d & 1)) - if (diff[d-1] < diff[d]) - FORC(2) color[c*2][d] = color[c*2][d-1]; - if (d < 2 || (d & 1)) { - FORC(2) rix[0][c*2] = CLIP(color[c*2][d]/2); - rix += TS*TS; - } - } - } - -/* Interpolate red for blue pixels and vice versa: */ - for (row=top+3; row < mrow-3; row++) - for (col=left+3; col < mcol-3; col++) { - if ((f = 2-fcol(row,col)) == 1) continue; - rix = &rgb[0][row-top][col-left]; - c = (row-sgrow) % 3 ? TS:1; - h = 3 * (c ^ TS ^ 1); - for (d=0; d < 4; d++, rix += TS*TS) { - i = d > 1 || ((d ^ c) & 1) || - ((ABS(rix[0][1]-rix[c][1])+ABS(rix[0][1]-rix[-c][1])) < - 2*(ABS(rix[0][1]-rix[h][1])+ABS(rix[0][1]-rix[-h][1]))) ? c:h; - rix[0][f] = CLIP((rix[i][f] + rix[-i][f] + - 2*rix[0][1] - rix[i][1] - rix[-i][1])/2); - } - } - -/* Fill in red and blue for 2x2 blocks of green: */ - for (row=top+2; row < mrow-2; row++) if ((row-sgrow) % 3) - for (col=left+2; col < mcol-2; col++) if ((col-sgcol) % 3) { - rix = &rgb[0][row-top][col-left]; - hex = allhex[row % 3][col % 3][1]; - for (d=0; d < ndir; d+=2, rix += TS*TS) - if (hex[d] + hex[d+1]) { - g = 3*rix[0][1] - 2*rix[hex[d]][1] - rix[hex[d+1]][1]; - for (c=0; c < 4; c+=2) rix[0][c] = - CLIP((g + 2*rix[hex[d]][c] + rix[hex[d+1]][c])/3); - } else { - g = 2*rix[0][1] - rix[hex[d]][1] - rix[hex[d+1]][1]; - for (c=0; c < 4; c+=2) rix[0][c] = - CLIP((g + rix[hex[d]][c] + rix[hex[d+1]][c])/2); - } - } - } - rgb = (ushort(*)[TS][TS][3]) buffer; - mrow -= top; - mcol -= left; - -/* Convert to CIELab and differentiate in all directions: */ - for (d=0; d < ndir; d++) { - for (row=2; row < mrow-2; row++) - for (col=2; col < mcol-2; col++) - cielab (rgb[d][row][col], lab[row][col]); - for (f=dir[d & 3],row=3; row < mrow-3; row++) - for (col=3; col < mcol-3; col++) { - lix = &lab[row][col]; - g = 2*lix[0][0] - lix[f][0] - lix[-f][0]; - drv[d][row][col] = SQR(g) - + SQR((2*lix[0][1] - lix[f][1] - lix[-f][1] + g*500/232)) - + SQR((2*lix[0][2] - lix[f][2] - lix[-f][2] - g*500/580)); - } - } - -/* Build homogeneity maps from the derivatives: */ - memset(homo, 0, ndir*TS*TS); - for (row=4; row < mrow-4; row++) - for (col=4; col < mcol-4; col++) { - for (tr=FLT_MAX, d=0; d < ndir; d++) - if (tr > drv[d][row][col]) - tr = drv[d][row][col]; - tr *= 8; - for (d=0; d < ndir; d++) - for (v=-1; v <= 1; v++) - for (h=-1; h <= 1; h++) - if (drv[d][row+v][col+h] <= tr) - homo[d][row][col]++; - } - -/* Average the most homogenous pixels for the final result: */ - if (height-top < TS+4) mrow = height-top+2; - if (width-left < TS+4) mcol = width-left+2; - for (row = MIN(top,8); row < mrow-8; row++) - for (col = MIN(left,8); col < mcol-8; col++) { - for (d=0; d < ndir; d++) - for (hm[d]=0, v=-2; v <= 2; v++) - for (h=-2; h <= 2; h++) - hm[d] += homo[d][row+v][col+h]; - for (d=0; d < ndir-4; d++) - if (hm[d] < hm[d+4]) hm[d ] = 0; else - if (hm[d] > hm[d+4]) hm[d+4] = 0; - for (max=hm[0],d=1; d < ndir; d++) - if (max < hm[d]) max = hm[d]; - max -= max >> 3; - memset (avg, 0, sizeof avg); - for (d=0; d < ndir; d++) - if (hm[d] >= max) { - FORC3 avg[c] += rgb[d][row][col][c]; - avg[3]++; - } - FORC3 image[(row+top)*width+col+left][c] = avg[c]/avg[3]; - } - } - free(buffer); - border_interpolate(8); -} -#undef fcol - -/* - Adaptive Homogeneity-Directed interpolation is based on - the work of Keigo Hirakawa, Thomas Parks, and Paul Lee. - */ -void CLASS ahd_interpolate() -{ - int i, j, top, left, row, col, tr, tc, c, d, val, hm[2]; - static const int dir[4] = { -1, 1, -TS, TS }; - unsigned ldiff[2][4], abdiff[2][4], leps, abeps; - ushort (*rgb)[TS][TS][3], (*rix)[3], (*pix)[4]; - short (*lab)[TS][TS][3], (*lix)[3]; - char (*homo)[TS][TS], *buffer; - - if (verbose) fprintf (stderr,_("AHD interpolation...\n")); - - cielab (0,0); - border_interpolate(5); - buffer = (char *) malloc (26*TS*TS); - merror (buffer, "ahd_interpolate()"); - rgb = (ushort(*)[TS][TS][3]) buffer; - lab = (short (*)[TS][TS][3])(buffer + 12*TS*TS); - homo = (char (*)[TS][TS]) (buffer + 24*TS*TS); - - for (top=2; top < height-5; top += TS-6) - for (left=2; left < width-5; left += TS-6) { - -/* Interpolate green horizontally and vertically: */ - for (row=top; row < top+TS && row < height-2; row++) { - col = left + (FC(row,left) & 1); - for (c = FC(row,col); col < left+TS && col < width-2; col+=2) { - pix = image + row*width+col; - val = ((pix[-1][1] + pix[0][c] + pix[1][1]) * 2 - - pix[-2][c] - pix[2][c]) >> 2; - rgb[0][row-top][col-left][1] = ULIM(val,pix[-1][1],pix[1][1]); - val = ((pix[-width][1] + pix[0][c] + pix[width][1]) * 2 - - pix[-2*width][c] - pix[2*width][c]) >> 2; - rgb[1][row-top][col-left][1] = ULIM(val,pix[-width][1],pix[width][1]); - } - } -/* Interpolate red and blue, and convert to CIELab: */ - for (d=0; d < 2; d++) - for (row=top+1; row < top+TS-1 && row < height-3; row++) - for (col=left+1; col < left+TS-1 && col < width-3; col++) { - pix = image + row*width+col; - rix = &rgb[d][row-top][col-left]; - lix = &lab[d][row-top][col-left]; - if ((c = 2 - FC(row,col)) == 1) { - c = FC(row+1,col); - val = pix[0][1] + (( pix[-1][2-c] + pix[1][2-c] - - rix[-1][1] - rix[1][1] ) >> 1); - rix[0][2-c] = CLIP(val); - val = pix[0][1] + (( pix[-width][c] + pix[width][c] - - rix[-TS][1] - rix[TS][1] ) >> 1); - } else - val = rix[0][1] + (( pix[-width-1][c] + pix[-width+1][c] - + pix[+width-1][c] + pix[+width+1][c] - - rix[-TS-1][1] - rix[-TS+1][1] - - rix[+TS-1][1] - rix[+TS+1][1] + 1) >> 2); - rix[0][c] = CLIP(val); - c = FC(row,col); - rix[0][c] = pix[0][c]; - cielab (rix[0],lix[0]); - } -/* Build homogeneity maps from the CIELab images: */ - memset (homo, 0, 2*TS*TS); - for (row=top+2; row < top+TS-2 && row < height-4; row++) { - tr = row-top; - for (col=left+2; col < left+TS-2 && col < width-4; col++) { - tc = col-left; - for (d=0; d < 2; d++) { - lix = &lab[d][tr][tc]; - for (i=0; i < 4; i++) { - ldiff[d][i] = ABS(lix[0][0]-lix[dir[i]][0]); - abdiff[d][i] = SQR(lix[0][1]-lix[dir[i]][1]) - + SQR(lix[0][2]-lix[dir[i]][2]); - } - } - leps = MIN(MAX(ldiff[0][0],ldiff[0][1]), - MAX(ldiff[1][2],ldiff[1][3])); - abeps = MIN(MAX(abdiff[0][0],abdiff[0][1]), - MAX(abdiff[1][2],abdiff[1][3])); - for (d=0; d < 2; d++) - for (i=0; i < 4; i++) - if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps) - homo[d][tr][tc]++; - } - } -/* Combine the most homogenous pixels for the final result: */ - for (row=top+3; row < top+TS-3 && row < height-5; row++) { - tr = row-top; - for (col=left+3; col < left+TS-3 && col < width-5; col++) { - tc = col-left; - for (d=0; d < 2; d++) - for (hm[d]=0, i=tr-1; i <= tr+1; i++) - for (j=tc-1; j <= tc+1; j++) - hm[d] += homo[d][i][j]; - if (hm[0] != hm[1]) - FORC3 image[row*width+col][c] = rgb[hm[1] > hm[0]][tr][tc][c]; - else - FORC3 image[row*width+col][c] = - (rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1; - } - } - } - free (buffer); -} -#undef TS - -void CLASS median_filter() -{ - ushort (*pix)[4]; - int pass, c, i, j, k, med[9]; - static const uchar opt[] = /* Optimal 9-element median search */ - { 1,2, 4,5, 7,8, 0,1, 3,4, 6,7, 1,2, 4,5, 7,8, - 0,3, 5,8, 4,7, 3,6, 1,4, 2,5, 4,7, 4,2, 6,4, 4,2 }; - - for (pass=1; pass <= med_passes; pass++) { - if (verbose) - fprintf (stderr,_("Median filter pass %d...\n"), pass); - for (c=0; c < 3; c+=2) { - for (pix = image; pix < image+width*height; pix++) - pix[0][3] = pix[0][c]; - for (pix = image+width; pix < image+width*(height-1); pix++) { - if ((pix-image+1) % width < 2) continue; - for (k=0, i = -width; i <= width; i += width) - for (j = i-1; j <= i+1; j++) - med[k++] = pix[j][3] - pix[j][1]; - for (i=0; i < sizeof opt; i+=2) - if (med[opt[i]] > med[opt[i+1]]) - SWAP (med[opt[i]] , med[opt[i+1]]); - pix[0][c] = CLIP(med[4] + pix[0][1]); - } - } - } -} - -void CLASS blend_highlights() -{ - int clip=INT_MAX, row, col, c, i, j; - static const float trans[2][4][4] = - { { { 1,1,1 }, { 1.7320508,-1.7320508,0 }, { -1,-1,2 } }, - { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } }; - static const float itrans[2][4][4] = - { { { 1,0.8660254,-0.5 }, { 1,-0.8660254,-0.5 }, { 1,0,1 } }, - { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } }; - float cam[2][4], lab[2][4], sum[2], chratio; - - if ((unsigned) (colors-3) > 1) return; - if (verbose) fprintf (stderr,_("Blending highlights...\n")); - FORCC if (clip > (i = 65535*pre_mul[c])) clip = i; - for (row=0; row < height; row++) - for (col=0; col < width; col++) { - FORCC if (image[row*width+col][c] > clip) break; - if (c == colors) continue; - FORCC { - cam[0][c] = image[row*width+col][c]; - cam[1][c] = MIN(cam[0][c],clip); - } - for (i=0; i < 2; i++) { - FORCC for (lab[i][c]=j=0; j < colors; j++) - lab[i][c] += trans[colors-3][c][j] * cam[i][j]; - for (sum[i]=0,c=1; c < colors; c++) - sum[i] += SQR(lab[i][c]); - } - chratio = sqrt(sum[1]/sum[0]); - for (c=1; c < colors; c++) - lab[0][c] *= chratio; - FORCC for (cam[0][c]=j=0; j < colors; j++) - cam[0][c] += itrans[colors-3][c][j] * lab[0][j]; - FORCC image[row*width+col][c] = cam[0][c] / colors; - } -} - -#define SCALE (4 >> shrink) -void CLASS recover_highlights() -{ - float *map, sum, wgt, grow; - int hsat[4], count, spread, change, val, i; - unsigned high, wide, mrow, mcol, row, col, kc, c, d, y, x; - ushort *pixel; - static const signed char dir[8][2] = - { {-1,-1}, {-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1} }; - - if (verbose) fprintf (stderr,_("Rebuilding highlights...\n")); - - grow = pow (2, 4-highlight); - FORCC hsat[c] = 32000 * pre_mul[c]; - for (kc=0, c=1; c < colors; c++) - if (pre_mul[kc] < pre_mul[c]) kc = c; - high = height / SCALE; - wide = width / SCALE; - map = (float *) calloc (high, wide*sizeof *map); - merror (map, "recover_highlights()"); - FORCC if (c != kc) { - memset (map, 0, high*wide*sizeof *map); - for (mrow=0; mrow < high; mrow++) - for (mcol=0; mcol < wide; mcol++) { - sum = wgt = count = 0; - for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++) - for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) { - pixel = image[row*width+col]; - if (pixel[c] / hsat[c] == 1 && pixel[kc] > 24000) { - sum += pixel[c]; - wgt += pixel[kc]; - count++; - } - } - if (count == SCALE*SCALE) - map[mrow*wide+mcol] = sum / wgt; - } - for (spread = 32/grow; spread--; ) { - for (mrow=0; mrow < high; mrow++) - for (mcol=0; mcol < wide; mcol++) { - if (map[mrow*wide+mcol]) continue; - sum = count = 0; - for (d=0; d < 8; d++) { - y = mrow + dir[d][0]; - x = mcol + dir[d][1]; - if (y < high && x < wide && map[y*wide+x] > 0) { - sum += (1 + (d & 1)) * map[y*wide+x]; - count += 1 + (d & 1); - } - } - if (count > 3) - map[mrow*wide+mcol] = - (sum+grow) / (count+grow); - } - for (change=i=0; i < high*wide; i++) - if (map[i] < 0) { - map[i] = -map[i]; - change = 1; - } - if (!change) break; - } - for (i=0; i < high*wide; i++) - if (map[i] == 0) map[i] = 1; - for (mrow=0; mrow < high; mrow++) - for (mcol=0; mcol < wide; mcol++) { - for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++) - for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) { - pixel = image[row*width+col]; - if (pixel[c] / hsat[c] > 1) { - val = pixel[kc] * map[mrow*wide+mcol]; - if (pixel[c] < val) pixel[c] = CLIP(val); - } - } - } - } - free (map); -} -#undef SCALE +//void CLASS border_interpolate (int border) +//{ +// unsigned row, col, y, x, f, c, sum[8]; +// +// for (row=0; row < height; row++) +// for (col=0; col < width; col++) { +// if (col==border && row >= border && row < height-border) +// col = width-border; +// memset (sum, 0, sizeof sum); +// for (y=row-1; y != row+2; y++) +// for (x=col-1; x != col+2; x++) +// if (y < height && x < width) { +// f = fcol(y,x); +// sum[f] += image[y*width+x][f]; +// sum[f+4]++; +// } +// f = fcol(row,col); +// FORCC if (c != f && sum[c+4]) +// image[row*width+col][c] = sum[c] / sum[c+4]; +// } +//} + +/* RT: delete interpolation functions */ + + +//void CLASS cielab (ushort rgb[3], short lab[3]) +//{ +// int c, i, j, k; +// float r, xyz[3]; +// static float cbrt[0x10000], xyz_cam[3][4]; +// +// if (!rgb) { +// for (i=0; i < 0x10000; i++) { +// r = i / 65535.0; +// cbrt[i] = r > 0.008856 ? pow(r,1/3.0) : 7.787*r + 16/116.0; +// } +// for (i=0; i < 3; i++) +// for (j=0; j < colors; j++) +// for (xyz_cam[i][j] = k=0; k < 3; k++) +// xyz_cam[i][j] += xyz_rgb[i][k] * rgb_cam[k][j] / d65_white[i]; +// return; +// } +// xyz[0] = xyz[1] = xyz[2] = 0.5; +// FORCC { +// xyz[0] += xyz_cam[0][c] * rgb[c]; +// xyz[1] += xyz_cam[1][c] * rgb[c]; +// xyz[2] += xyz_cam[2][c] * rgb[c]; +// } +// xyz[0] = cbrt[CLIP((int) xyz[0])]; +// xyz[1] = cbrt[CLIP((int) xyz[1])]; +// xyz[2] = cbrt[CLIP((int) xyz[2])]; +// lab[0] = 64 * (116 * xyz[1] - 16); +// lab[1] = 64 * 500 * (xyz[0] - xyz[1]); +// lab[2] = 64 * 200 * (xyz[1] - xyz[2]); +//} +// +//#define TS 512 /* Tile Size */ +//#define fcol(row,col) xtrans[(row+6) % 6][(col+6) % 6] +// +///* +// Frank Markesteijn's algorithm for Fuji X-Trans sensors +// */ +//void CLASS xtrans_interpolate (int passes) +//{ +// int c, d, f, g, h, i, v, ng, row, col, top, left, mrow, mcol; +// int val, ndir, pass, hm[8], avg[4], color[3][8]; +// static const short orth[12] = { 1,0,0,1,-1,0,0,-1,1,0,0,1 }, +// patt[2][16] = { { 0,1,0,-1,2,0,-1,0,1,1,1,-1,0,0,0,0 }, +// { 0,1,0,-2,1,0,-2,0,1,1,-2,-2,1,-1,-1,1 } }, +// dir[4] = { 1,TS,TS+1,TS-1 }; +// short allhex[3][3][2][8], *hex; +// ushort min, max, sgrow, sgcol; +// ushort (*rgb)[TS][TS][3], (*rix)[3], (*pix)[4]; +// short (*lab) [TS][3], (*lix)[3]; +// float (*drv)[TS][TS], diff[6], tr; +// char (*homo)[TS][TS], *buffer; +// +// if (verbose) +// fprintf (stderr,_("%d-pass X-Trans interpolation...\n"), passes); +// +// cielab (0,0); +// ndir = 4 << (passes > 1); +// buffer = (char *) malloc (TS*TS*(ndir*11+6)); +// merror (buffer, "xtrans_interpolate()"); +// rgb = (ushort(*)[TS][TS][3]) buffer; +// lab = (short (*) [TS][3])(buffer + TS*TS*(ndir*6)); +// drv = (float (*)[TS][TS]) (buffer + TS*TS*(ndir*6+6)); +// homo = (char (*)[TS][TS]) (buffer + TS*TS*(ndir*10+6)); +// +///* Map a green hexagon around each non-green pixel and vice versa: */ +// for (row=0; row < 3; row++) +// for (col=0; col < 3; col++) +// for (ng=d=0; d < 10; d+=2) { +// g = fcol(row,col) == 1; +// if (fcol(row+orth[d],col+orth[d+2]) == 1) ng=0; else ng++; +// if (ng == 4) { sgrow = row; sgcol = col; } +// if (ng == g+1) FORC(8) { +// v = orth[d ]*patt[g][c*2] + orth[d+1]*patt[g][c*2+1]; +// h = orth[d+2]*patt[g][c*2] + orth[d+3]*patt[g][c*2+1]; +// allhex[row][col][0][c^(g*2 & d)] = h + v*width; +// allhex[row][col][1][c^(g*2 & d)] = h + v*TS; +// } +// } +// +///* Set green1 and green3 to the minimum and maximum allowed values: */ +// for (row=2; row < height-2; row++) +// for (min=~(max=0), col=2; col < width-2; col++) { +// if (fcol(row,col) == 1 && (min=~(max=0))) continue; +// pix = image + row*width + col; +// hex = allhex[row % 3][col % 3][0]; +// if (!max) FORC(6) { +// val = pix[hex[c]][1]; +// if (min > val) min = val; +// if (max < val) max = val; +// } +// pix[0][1] = min; +// pix[0][3] = max; +// switch ((row-sgrow) % 3) { +// case 1: if (row < height-3) { row++; col--; } break; +// case 2: if ((min=~(max=0)) && (col+=2) < width-3 && row > 2) row--; +// } +// } +// +// for (top=3; top < height-19; top += TS-16) +// for (left=3; left < width-19; left += TS-16) { +// mrow = MIN (top+TS, height-3); +// mcol = MIN (left+TS, width-3); +// for (row=top; row < mrow; row++) +// for (col=left; col < mcol; col++) +// memcpy (rgb[0][row-top][col-left], image[row*width+col], 6); +// FORC3 memcpy (rgb[c+1], rgb[0], sizeof *rgb); +// +///* Interpolate green horizontally, vertically, and along both diagonals: */ +// for (row=top; row < mrow; row++) +// for (col=left; col < mcol; col++) { +// if ((f = fcol(row,col)) == 1) continue; +// pix = image + row*width + col; +// hex = allhex[row % 3][col % 3][0]; +// color[1][0] = 174 * (pix[ hex[1]][1] + pix[ hex[0]][1]) - +// 46 * (pix[2*hex[1]][1] + pix[2*hex[0]][1]); +// color[1][1] = 223 * pix[ hex[3]][1] + pix[ hex[2]][1] * 33 + +// 92 * (pix[ 0 ][f] - pix[ -hex[2]][f]); +// FORC(2) color[1][2+c] = +// 164 * pix[hex[4+c]][1] + 92 * pix[-2*hex[4+c]][1] + 33 * +// (2*pix[0][f] - pix[3*hex[4+c]][f] - pix[-3*hex[4+c]][f]); +// FORC4 rgb[c^!((row-sgrow) % 3)][row-top][col-left][1] = +// LIM(color[1][c] >> 8,pix[0][1],pix[0][3]); +// } +// +// for (pass=0; pass < passes; pass++) { +// if (pass == 1) +// memcpy (rgb+=4, buffer, 4*sizeof *rgb); +// +///* Recalculate green from interpolated values of closer pixels: */ +// if (pass) { +// for (row=top+2; row < mrow-2; row++) +// for (col=left+2; col < mcol-2; col++) { +// if ((f = fcol(row,col)) == 1) continue; +// pix = image + row*width + col; +// hex = allhex[row % 3][col % 3][1]; +// for (d=3; d < 6; d++) { +// rix = &rgb[(d-2)^!((row-sgrow) % 3)][row-top][col-left]; +// val = rix[-2*hex[d]][1] + 2*rix[hex[d]][1] +// - rix[-2*hex[d]][f] - 2*rix[hex[d]][f] + 3*rix[0][f]; +// rix[0][1] = LIM(val/3,pix[0][1],pix[0][3]); +// } +// } +// } +// +///* Interpolate red and blue values for solitary green pixels: */ +// for (row=(top-sgrow+4)/3*3+sgrow; row < mrow-2; row+=3) +// for (col=(left-sgcol+4)/3*3+sgcol; col < mcol-2; col+=3) { +// rix = &rgb[0][row-top][col-left]; +// h = fcol(row,col+1); +// memset (diff, 0, sizeof diff); +// for (i=1, d=0; d < 6; d++, i^=TS^1, h^=2) { +// for (c=0; c < 2; c++, h^=2) { +// g = 2*rix[0][1] - rix[i< 1) +// diff[d] += SQR (rix[i< 1 && (d & 1)) +// if (diff[d-1] < diff[d]) +// FORC(2) color[c*2][d] = color[c*2][d-1]; +// if (d < 2 || (d & 1)) { +// FORC(2) rix[0][c*2] = CLIP(color[c*2][d]/2); +// rix += TS*TS; +// } +// } +// } +// +///* Interpolate red for blue pixels and vice versa: */ +// for (row=top+3; row < mrow-3; row++) +// for (col=left+3; col < mcol-3; col++) { +// if ((f = 2-fcol(row,col)) == 1) continue; +// rix = &rgb[0][row-top][col-left]; +// c = (row-sgrow) % 3 ? TS:1; +// h = 3 * (c ^ TS ^ 1); +// for (d=0; d < 4; d++, rix += TS*TS) { +// i = d > 1 || ((d ^ c) & 1) || +// ((ABS(rix[0][1]-rix[c][1])+ABS(rix[0][1]-rix[-c][1])) < +// 2*(ABS(rix[0][1]-rix[h][1])+ABS(rix[0][1]-rix[-h][1]))) ? c:h; +// rix[0][f] = CLIP((rix[i][f] + rix[-i][f] + +// 2*rix[0][1] - rix[i][1] - rix[-i][1])/2); +// } +// } +// +///* Fill in red and blue for 2x2 blocks of green: */ +// for (row=top+2; row < mrow-2; row++) if ((row-sgrow) % 3) +// for (col=left+2; col < mcol-2; col++) if ((col-sgcol) % 3) { +// rix = &rgb[0][row-top][col-left]; +// hex = allhex[row % 3][col % 3][1]; +// for (d=0; d < ndir; d+=2, rix += TS*TS) +// if (hex[d] + hex[d+1]) { +// g = 3*rix[0][1] - 2*rix[hex[d]][1] - rix[hex[d+1]][1]; +// for (c=0; c < 4; c+=2) rix[0][c] = +// CLIP((g + 2*rix[hex[d]][c] + rix[hex[d+1]][c])/3); +// } else { +// g = 2*rix[0][1] - rix[hex[d]][1] - rix[hex[d+1]][1]; +// for (c=0; c < 4; c+=2) rix[0][c] = +// CLIP((g + rix[hex[d]][c] + rix[hex[d+1]][c])/2); +// } +// } +// } +// rgb = (ushort(*)[TS][TS][3]) buffer; +// mrow -= top; +// mcol -= left; +// +///* Convert to CIELab and differentiate in all directions: */ +// for (d=0; d < ndir; d++) { +// for (row=2; row < mrow-2; row++) +// for (col=2; col < mcol-2; col++) +// cielab (rgb[d][row][col], lab[row][col]); +// for (f=dir[d & 3],row=3; row < mrow-3; row++) +// for (col=3; col < mcol-3; col++) { +// lix = &lab[row][col]; +// g = 2*lix[0][0] - lix[f][0] - lix[-f][0]; +// drv[d][row][col] = SQR(g) +// + SQR((2*lix[0][1] - lix[f][1] - lix[-f][1] + g*500/232)) +// + SQR((2*lix[0][2] - lix[f][2] - lix[-f][2] - g*500/580)); +// } +// } +// +///* Build homogeneity maps from the derivatives: */ +// memset(homo, 0, ndir*TS*TS); +// for (row=4; row < mrow-4; row++) +// for (col=4; col < mcol-4; col++) { +// for (tr=FLT_MAX, d=0; d < ndir; d++) +// if (tr > drv[d][row][col]) +// tr = drv[d][row][col]; +// tr *= 8; +// for (d=0; d < ndir; d++) +// for (v=-1; v <= 1; v++) +// for (h=-1; h <= 1; h++) +// if (drv[d][row+v][col+h] <= tr) +// homo[d][row][col]++; +// } +// +///* Average the most homogenous pixels for the final result: */ +// if (height-top < TS+4) mrow = height-top+2; +// if (width-left < TS+4) mcol = width-left+2; +// for (row = MIN(top,8); row < mrow-8; row++) +// for (col = MIN(left,8); col < mcol-8; col++) { +// for (d=0; d < ndir; d++) +// for (hm[d]=0, v=-2; v <= 2; v++) +// for (h=-2; h <= 2; h++) +// hm[d] += homo[d][row+v][col+h]; +// for (d=0; d < ndir-4; d++) +// if (hm[d] < hm[d+4]) hm[d ] = 0; else +// if (hm[d] > hm[d+4]) hm[d+4] = 0; +// for (max=hm[0],d=1; d < ndir; d++) +// if (max < hm[d]) max = hm[d]; +// max -= max >> 3; +// memset (avg, 0, sizeof avg); +// for (d=0; d < ndir; d++) +// if (hm[d] >= max) { +// FORC3 avg[c] += rgb[d][row][col][c]; +// avg[3]++; +// } +// FORC3 image[(row+top)*width+col+left][c] = avg[c]/avg[3]; +// } +// } +// free(buffer); +// border_interpolate(8); +//} +//#undef fcol +// +// +//#undef TS + +//void CLASS median_filter() +//{ +// ushort (*pix)[4]; +// int pass, c, i, j, k, med[9]; +// static const uchar opt[] = /* Optimal 9-element median search */ +// { 1,2, 4,5, 7,8, 0,1, 3,4, 6,7, 1,2, 4,5, 7,8, +// 0,3, 5,8, 4,7, 3,6, 1,4, 2,5, 4,7, 4,2, 6,4, 4,2 }; +// +// for (pass=1; pass <= med_passes; pass++) { +// if (verbose) +// fprintf (stderr,_("Median filter pass %d...\n"), pass); +// for (c=0; c < 3; c+=2) { +// for (pix = image; pix < image+width*height; pix++) +// pix[0][3] = pix[0][c]; +// for (pix = image+width; pix < image+width*(height-1); pix++) { +// if ((pix-image+1) % width < 2) continue; +// for (k=0, i = -width; i <= width; i += width) +// for (j = i-1; j <= i+1; j++) +// med[k++] = pix[j][3] - pix[j][1]; +// for (i=0; i < sizeof opt; i+=2) +// if (med[opt[i]] > med[opt[i+1]]) +// SWAP (med[opt[i]] , med[opt[i+1]]); +// pix[0][c] = CLIP(med[4] + pix[0][1]); +// } +// } +// } +//} +// +//void CLASS blend_highlights() +//{ +// int clip=INT_MAX, row, col, c, i, j; +// static const float trans[2][4][4] = +// { { { 1,1,1 }, { 1.7320508,-1.7320508,0 }, { -1,-1,2 } }, +// { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } }; +// static const float itrans[2][4][4] = +// { { { 1,0.8660254,-0.5 }, { 1,-0.8660254,-0.5 }, { 1,0,1 } }, +// { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } }; +// float cam[2][4], lab[2][4], sum[2], chratio; +// +// if ((unsigned) (colors-3) > 1) return; +// if (verbose) fprintf (stderr,_("Blending highlights...\n")); +// FORCC if (clip > (i = 65535*pre_mul[c])) clip = i; +// for (row=0; row < height; row++) +// for (col=0; col < width; col++) { +// FORCC if (image[row*width+col][c] > clip) break; +// if (c == colors) continue; +// FORCC { +// cam[0][c] = image[row*width+col][c]; +// cam[1][c] = MIN(cam[0][c],clip); +// } +// for (i=0; i < 2; i++) { +// FORCC for (lab[i][c]=j=0; j < colors; j++) +// lab[i][c] += trans[colors-3][c][j] * cam[i][j]; +// for (sum[i]=0,c=1; c < colors; c++) +// sum[i] += SQR(lab[i][c]); +// } +// chratio = sqrt(sum[1]/sum[0]); +// for (c=1; c < colors; c++) +// lab[0][c] *= chratio; +// FORCC for (cam[0][c]=j=0; j < colors; j++) +// cam[0][c] += itrans[colors-3][c][j] * lab[0][j]; +// FORCC image[row*width+col][c] = cam[0][c] / colors; +// } +//} +// +//#define SCALE (4 >> shrink) +//void CLASS recover_highlights() +//{ +// float *map, sum, wgt, grow; +// int hsat[4], count, spread, change, val, i; +// unsigned high, wide, mrow, mcol, row, col, kc, c, d, y, x; +// ushort *pixel; +// static const signed char dir[8][2] = +// { {-1,-1}, {-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1} }; +// +// if (verbose) fprintf (stderr,_("Rebuilding highlights...\n")); +// +// grow = pow (2, 4-highlight); +// FORCC hsat[c] = 32000 * pre_mul[c]; +// for (kc=0, c=1; c < colors; c++) +// if (pre_mul[kc] < pre_mul[c]) kc = c; +// high = height / SCALE; +// wide = width / SCALE; +// map = (float *) calloc (high, wide*sizeof *map); +// merror (map, "recover_highlights()"); +// FORCC if (c != kc) { +// memset (map, 0, high*wide*sizeof *map); +// for (mrow=0; mrow < high; mrow++) +// for (mcol=0; mcol < wide; mcol++) { +// sum = wgt = count = 0; +// for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++) +// for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) { +// pixel = image[row*width+col]; +// if (pixel[c] / hsat[c] == 1 && pixel[kc] > 24000) { +// sum += pixel[c]; +// wgt += pixel[kc]; +// count++; +// } +// } +// if (count == SCALE*SCALE) +// map[mrow*wide+mcol] = sum / wgt; +// } +// for (spread = 32/grow; spread--; ) { +// for (mrow=0; mrow < high; mrow++) +// for (mcol=0; mcol < wide; mcol++) { +// if (map[mrow*wide+mcol]) continue; +// sum = count = 0; +// for (d=0; d < 8; d++) { +// y = mrow + dir[d][0]; +// x = mcol + dir[d][1]; +// if (y < high && x < wide && map[y*wide+x] > 0) { +// sum += (1 + (d & 1)) * map[y*wide+x]; +// count += 1 + (d & 1); +// } +// } +// if (count > 3) +// map[mrow*wide+mcol] = - (sum+grow) / (count+grow); +// } +// for (change=i=0; i < high*wide; i++) +// if (map[i] < 0) { +// map[i] = -map[i]; +// change = 1; +// } +// if (!change) break; +// } +// for (i=0; i < high*wide; i++) +// if (map[i] == 0) map[i] = 1; +// for (mrow=0; mrow < high; mrow++) +// for (mcol=0; mcol < wide; mcol++) { +// for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++) +// for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) { +// pixel = image[row*width+col]; +// if (pixel[c] / hsat[c] > 1) { +// val = pixel[kc] * map[mrow*wide+mcol]; +// if (pixel[c] < val) pixel[c] = CLIP(val); +// } +// } +// } +// } +// free (map); +//} +//#undef SCALE void CLASS tiff_get (unsigned base, unsigned *tag, unsigned *type, unsigned *len, unsigned *save) @@ -5139,7 +5127,7 @@ } } -int CLASS parse_tiff_ifd (int base); +/*RT int CLASS parse_tiff_ifd (int base);*/ void CLASS parse_makernote (int base, int uptag) { @@ -5244,6 +5232,11 @@ tag |= uptag << 16; if (tag == 2 && strstr(make,"NIKON") && !iso_speed) iso_speed = (get2(),get2()); + if ((tag == 0x25 || tag == 0x28) && strstr(make,"NIKON") && !iso_speed) { // Nikon ISOInfo Tags/ISO & ISO2 + uchar iso[1]; + fread (iso, 1, 1, ifp); + iso_speed = 100 * pow(2,(float)iso[0]/12.0-5); + } if (tag == 4 && len > 26 && len < 35) { if ((i=(get4(),get2())) != 0x7fff && !iso_speed) iso_speed = 50 * pow (2, i/32.0 - 4); @@ -5296,12 +5289,16 @@ cam_mul[2] = get4() << 2; } } - if (tag == 0x15 && type == 2 && is_raw) + //if (tag == 0x15 && type == 2 && is_raw) + if (tag == 0x15 && type == 2 && is_raw && strstr(model, "Hasselblad ") != model) // RT: don't overwrite already parsed Hasselblad model fread (model, 64, 1, ifp); if (strstr(make,"PENTAX")) { if (tag == 0x1b) tag = 0x1018; if (tag == 0x1c) tag = 0x1017; } + if (tag == 0x19 && !strcmp(make,"Hasselblad") && len == 0x300000) { // RT + parse_hasselblad_gain(); // RT + } // RT if (tag == 0x1d) while ((c = fgetc(ifp)) && c != EOF) serial = serial*10 + (isdigit(c) ? c - '0' : c % 10); @@ -5491,14 +5488,14 @@ while (entries--) { tiff_get (base, &tag, &type, &len, &save); switch (tag) { - case 33434: tiff_ifd[tiff_nifds-1].shutter = - shutter = getreal(type); break; + case 33434: tiff_ifd[tiff_nifds-1].shutter = shutter = getreal(type); break; case 33437: aperture = getreal(type); break; case 34855: iso_speed = get2(); break; + case 34866: if((!iso_speed) || iso_speed == 65535) iso_speed = get4();break; case 36867: case 36868: get_timestamp(0); break; case 37377: if ((expo = -getreal(type)) < 128) - tiff_ifd[tiff_nifds-1].shutter = + tiff_ifd[tiff_nifds-1].shutter = shutter = pow (2, expo); break; case 37378: aperture = pow (2, getreal(type)/2); break; case 37386: focal_len = getreal(type); break; @@ -5667,28 +5664,33 @@ } } -void CLASS parse_minolta (int base); -int CLASS parse_tiff (int base); +/*RT void CLASS parse_minolta (int base); */ +/*RT int CLASS parse_tiff (int base);*/ int CLASS parse_tiff_ifd (int base) { unsigned entries, tag, type, len, plen=16, save; - int ifd, use_cm=0, cfa, i, j, c, ima_len=0; + int ifd, use_cm=0, cfa, i, j, c, ima_len=0,cm_D65=0; char software[64], *cbuf, *cp; uchar cfa_pat[16], cfa_pc[] = { 0,1,2,3 }, tab[256]; - double cc[4][4], cm[4][3], cam_xyz[4][3], num; + double cc[2][4][4]; + double cm[2][4][3] = {NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN}; + double cam_xyz[4][3], num; double ab[]={ 1,1,1,1 }, asn[] = { 0,0,0,0 }, xyz[] = { 1,1,1 }; unsigned sony_curve[] = { 0,0,0,0,0,4095 }; unsigned *buf, sony_offset=0, sony_length=0, sony_key=0; struct jhead jh; - FILE *sfp; +/*RT*/ IMFILE *sfp; if (tiff_nifds >= sizeof tiff_ifd / sizeof tiff_ifd[0]) return 1; ifd = tiff_nifds++; for (j=0; j < 4; j++) for (i=0; i < 4; i++) - cc[j][i] = i == j; + { + cc[0][j][i] = i == j; + cc[1][j][i] = i == j; + } entries = get2(); if (entries > 512) return 1; while (entries--) { @@ -5758,7 +5760,8 @@ fgets (make, 64, ifp); break; case 272: /* Model */ - fgets (model, 64, ifp); + if (strstr(model, "Hasselblad ") != model) // RT: if Hasselblad, only parse the first model name (otherwise it can change from say Hasselblad CFV-50 to Hasselblad CW (ie the camera body model, not the back model which we are interested in) + fgets (model, 64, ifp); break; case 280: /* Panasonic RW2 offset */ if (type != 4) break; @@ -5818,6 +5821,9 @@ case 315: /* Artist */ fread (artist, 64, 1, ifp); break; + case 317: /* Predictor */ + tiff_ifd[ifd].predictor = getint(type); + break; case 322: /* TileWidth */ tiff_ifd[ifd].tile_width = getint(type); break; @@ -5833,6 +5839,9 @@ is_raw = 5; } break; + case 325: /* TileByteCounts */ + tiff_ifd[ifd].bytes = len > 1 ? ftell(ifp) : get4(); + break; case 330: /* SubIFDs */ if (!strcmp(model,"DSLR-A100") && tiff_ifd[ifd].width == 3872) { load_raw = &CLASS sony_arw_load_raw; @@ -5846,6 +5855,9 @@ fseek (ifp, i+4, SEEK_SET); } break; + case 339: + tiff_ifd[ifd].sample_format = getint(type); + break; case 400: strcpy (make, "Sarnoff"); maximum = 0xfff; @@ -6063,12 +6075,21 @@ case 61450: cblack[4] = cblack[5] = MIN(sqrt(len),64); case 50714: /* BlackLevel */ - if (!(cblack[4] * cblack[5])) - cblack[4] = cblack[5] = 1; - FORC (cblack[4] * cblack[5]) - cblack[6+c] = getreal(type); - black = 0; - break; + if(cblack[4] * cblack[5] == 0) { + int dblack[] = { 0,0,0,0 }; + black = getreal(type); + if ((unsigned)(filters+1) < 1000) break; + dblack[0] = dblack[1] = dblack[2] = dblack[3] = black; + if (colors == 3) + filters |= ((filters >> 2 & 0x22222222) | + (filters << 2 & 0x88888888)) & filters << 1; + FORC4 cblack[filters >> (c << 1) & 3] = dblack[c]; + } else { + FORC (cblack[4] * cblack[5]) + cblack[6+c] = getreal(type); + } + black = 0; + break; case 50715: /* BlackLevelDeltaH */ case 50716: /* BlackLevelDeltaV */ for (num=i=0; i < (len & 0xffff); i++) @@ -6085,13 +6106,13 @@ case 50721: /* ColorMatrix1 */ case 50722: /* ColorMatrix2 */ FORCC for (j=0; j < 3; j++) - cm[c][j] = getreal(type); + cm[tag-50721][c][j] = getreal(type); use_cm = 1; break; case 50723: /* CameraCalibration1 */ case 50724: /* CameraCalibration2 */ for (i=0; i < colors; i++) - FORCC cc[i][c] = getreal(type); + FORCC cc[tag-50723][i][c] = getreal(type); break; case 50727: /* AnalogBalance */ FORCC ab[c] = getreal(type); @@ -6114,6 +6135,11 @@ case 50752: read_shorts (cr2_slice, 3); break; + case 50778: + case 50779: + if( get2() == 21 ) + cm_D65 = (tag-50778); + break; case 50829: /* ActiveArea */ top_margin = getint(type); left_margin = getint(type); @@ -6146,21 +6172,27 @@ fread (buf, sony_length, 1, ifp); sony_decrypt (buf, sony_length/4, 1, sony_key); sfp = ifp; - if ((ifp = tmpfile())) { - fwrite (buf, sony_length, 1, ifp); - fseek (ifp, 0, SEEK_SET); - parse_tiff_ifd (-sony_offset); - fclose (ifp); - } +/*RT*/ ifp = fopen (buf, sony_length); +// if ((ifp = tmpfile())) { +// fwrite (buf, sony_length, 1, ifp); +// fseek (ifp, 0, SEEK_SET); + parse_tiff_ifd (-sony_offset); +// fclose (ifp); +// } + if(ifp) + fclose(ifp); ifp = sfp; free (buf); } + for (i=0; i < colors; i++) - FORCC cc[i][c] *= ab[i]; + FORCC cc[cm_D65][i][c] *= ab[i]; if (use_cm) { + if(cm_D65 == 1 && std::isnan(cm[1][0][0])) + cm_D65 = 0; FORCC for (i=0; i < 3; i++) for (cam_xyz[c][i]=j=0; j < colors; j++) - cam_xyz[c][i] += cc[c][j] * cm[j][i] * xyz[i]; + cam_xyz[c][i] += cc[cm_D65][c][j] * cm[cm_D65][j][i] * xyz[i]; cam_xyz_coeff (cmatrix, cam_xyz); } if (asn[0]) { @@ -6168,13 +6200,14 @@ FORCC cam_mul[c] = 1 / asn[c]; } if (!use_cm) - FORCC pre_mul[c] /= cc[c][c]; + FORCC pre_mul[c] /= cc[cm_D65][c][c]; return 0; } int CLASS parse_tiff (int base) { int doff; + /*RT*/ if (exif_base == -1) exif_base = base; fseek (ifp, base, SEEK_SET); order = get2(); @@ -6206,6 +6239,7 @@ shutter = tiff_ifd[i].shutter; tiff_ifd[i].shutter = shutter; } + for (i=0; i < tiff_nifds; i++) { if (max_samp < tiff_ifd[i].samples) max_samp = tiff_ifd[i].samples; @@ -6266,7 +6300,12 @@ case 8: load_raw = &CLASS eight_bit_load_raw; break; case 12: if (tiff_ifd[raw].phint == 2) load_flags = 6; - load_raw = &CLASS packed_load_raw; break; + if(!strncmp(make,"SONY",4) && tiff_ifd[raw].comp == 1) { // hack for some sony cameras which report as 12 bit uncompressed but in fact are 14 bit uncompressed + tiff_bps = 14; + } else { + load_raw = &CLASS packed_load_raw; + break; + } case 14: load_flags = 0; case 16: load_raw = &CLASS unpacked_load_raw; if (!strncmp(make,"OLYMPUS",7) && @@ -6305,6 +6344,7 @@ case 32803: load_raw = &CLASS kodak_65000_load_raw; } case 32867: case 34892: break; + case 8: break; default: is_raw = 0; } if (!dng_version) @@ -6390,7 +6430,7 @@ { const char *file, *ext; char *jname, *jfile, *jext; - FILE *save=ifp; +/*RT*/ IMFILE *save=ifp; ext = strrchr (ifname, '.'); file = strrchr (ifname, '/'); @@ -6412,13 +6452,14 @@ } else while (isdigit(*--jext)) { if (*jext != '9') { - (*jext)++; + (*jext)++; break; } *jext = '0'; } if (strcmp (jname, ifname)) { - if ((ifp = fopen (jname, "rb"))) { +/*RT*/ if ((ifp = fopen (jname))) { +// if ((ifp = fopen (jname, "rb"))) { if (verbose) fprintf (stderr,_("Reading metadata from %s ...\n"), jname); parse_tiff (12); @@ -6693,6 +6734,7 @@ load_raw = ph1.format < 3 ? &CLASS phase_one_load_raw : &CLASS phase_one_load_raw_c; maximum = 0xffff; + tiff_bps = 16; strcpy (make, "Phase One"); if (model[0]) return; switch (raw_height) { @@ -6761,7 +6803,11 @@ order = get2(); hlen = get4(); if (get4() == 0x48454150) /* "HEAP" */ +/*RT*/ { +/*RT*/ ciff_base = save+hlen; +/*RT*/ ciff_len = len-hlen; parse_ciff (save+hlen, len-hlen, 0); +/*RT*/ } if (parse_tiff (save+6)) apply_tiff(); fseek (ifp, save+len, SEEK_SET); } @@ -7033,7 +7079,8 @@ { static const struct { const char *prefix; - short black, maximum, trans[12]; + unsigned short black, maximum; // RT: Change to UShort + short trans[12]; } table[] = { { "AgfaPhoto DC-833m", 0, 0, /* DJC */ { 11438,-3762,-1115,-2409,9914,2497,-1227,2295,5300 } }, @@ -7977,12 +8024,12 @@ { 6596,-2079,-562,-4782,13016,1933,-970,1581,5181 } }, { "Sony DSC-RX100", 0, 0, { 8651,-2754,-1057,-3464,12207,1373,-568,1398,4434 } }, - { "Sony DSC-RX10", 0, 0, /* also RX10M2 */ + { "Sony DSC-RX10", 0, 0, { 6679,-1825,-745,-5047,13256,1953,-1580,2422,5183 } }, { "Sony DSC-RX1RM2", 0, 0, { 6629,-1900,-483,-4618,12349,2550,-622,1381,6514 } }, { "Sony DSC-RX1", 0, 0, - { 6344,-1612,-462,-4863,12477,2681,-865,1786,6899 } }, + { 6344,-1612,-462,-4863,12477,2681,-865,1786,6899 } }, { "Sony DSLR-A100", 0, 0xfeb, { 9437,-2811,-774,-8405,16215,2290,-710,596,7181 } }, { "Sony DSLR-A290", 0, 0, @@ -8088,6 +8135,33 @@ } break; } + if (load_raw == &CLASS sony_arw2_load_raw) { // RT: arw2 scale fix + black <<= 2; + tiff_bps += 2; + } + { /* Check for RawTherapee table overrides and extensions */ + int black_level, white_level; + short trans[12]; + if (dcraw_coeff_overrides(make, model, iso_speed, trans, &black_level, &white_level)) { + if (black_level > -1) { + black = (ushort)black_level; + } + if (white_level > -1) { + maximum = (ushort)white_level; + if(tiff_bps > 0) { + unsigned compare = ((uint64_t)1 << tiff_bps) - 1; // use uint64_t to avoid overflow if tiff_bps == 32 + while(maximum > compare) + maximum >>= 1; + } + } + if (trans[0]) { + for (j=0; j < 12; j++) { + ((double *)cam_xyz)[j] = trans[j] / 10000.0; + } + cam_xyz_coeff (rgb_cam,cam_xyz); + } + } + } } void CLASS simple_coeff (int index) @@ -8410,7 +8484,7 @@ tiff_flip = flip = filters = UINT_MAX; /* unknown */ raw_height = raw_width = fuji_width = fuji_layout = cr2_slice[0] = 0; maximum = height = width = top_margin = left_margin = 0; - cdesc[0] = desc[0] = artist[0] = make[0] = model[0] = model2[0] = 0; + cdesc[0] = desc[0] = artist[0] = make[0] = model[0] = model2[0] = model3[0] = 0; iso_speed = shutter = aperture = focal_len = unique_id = 0; tiff_nifds = 0; memset (tiff_ifd, 0, sizeof tiff_ifd); @@ -8442,13 +8516,20 @@ fread (head, 1, 32, ifp); fseek (ifp, 0, SEEK_END); flen = fsize = ftell(ifp); - if ((cp = (char *) memmem (head, 32, "MMMM", 4)) || - (cp = (char *) memmem (head, 32, "IIII", 4))) { + /*RT*/ if (fsize<100000) { + is_raw = 0; + return; + } + /* RT: changed string constant */ + if ((cp = (char *) memmem (head, 32, (char*)"MMMM", 4)) || + (cp = (char *) memmem (head, 32, (char*)"IIII", 4))) { parse_phase_one (cp-head); if (cp-head && parse_tiff(0)) apply_tiff(); } else if (order == 0x4949 || order == 0x4d4d) { if (!memcmp (head+6,"HEAPCCDR",8)) { data_offset = hlen; +/*RT*/ ciff_base = hlen; +/*RT*/ ciff_len = fsize - hlen; parse_ciff (hlen, flen-hlen, 0); load_raw = &CLASS canon_load_raw; } else if (parse_tiff(0)) apply_tiff(); @@ -8494,6 +8575,7 @@ fseek (ifp, 100+28*(shot_select > 0), SEEK_SET); parse_tiff (data_offset = get4()); parse_tiff (thumb_offset+12); +/*RT*/ exif_base = thumb_offset+12; apply_tiff(); } else if (!memcmp (head,"RIFF",4)) { fseek (ifp, 0, SEEK_SET); @@ -8607,9 +8689,10 @@ if (make[0] == 0) parse_smal (0, flen); if (make[0] == 0) { parse_jpeg(0); - if (!(strncmp(model,"ov",2) && strncmp(model,"RP_OV",5)) && - !fseek (ifp, -6404096, SEEK_END) && - fread (head, 1, 32, ifp) && !strcmp(head,"BRCMn")) { + //RT fix for the use of fseek below + if (!(strncmp(model,"ov",2) && strncmp(model,"RP_OV",5))) { + fseek (ifp, -6404096, SEEK_END); + if (fread (head, 1, 32, ifp) && !strcmp(head,"BRCMn")) { strcpy (make, "OmniVision"); data_offset = ftell(ifp) + 0x8000-32; width = raw_width; @@ -8618,6 +8701,7 @@ filters = 0x16161616; } else is_raw = 0; } + } for (i=0; i < sizeof corp / sizeof *corp; i++) if (strcasestr (make, corp[i])) /* Simplify company names */ @@ -8649,7 +8733,7 @@ if (height == 3136 && width == 4864) /* Pentax K20D and Samsung GX20 */ { height = 3124; width = 4688; filters = 0x16161616; } if (width == 4352 && (!strcmp(model,"K-r") || !strcmp(model,"K-x"))) - { width = 4309; filters = 0x16161616; } +/*RT*/ { width = 4308; filters = 0x16161616; } if (width >= 4960 && !strncmp(model,"K-5",3)) { left_margin = 10; width = 4950; filters = 0x16161616; } if (width == 4736 && !strcmp(model,"K-7")) @@ -8669,6 +8753,7 @@ case 0: case 1: load_raw = &CLASS packed_dng_load_raw; break; case 7: load_raw = &CLASS lossless_dng_load_raw; break; + case 8: load_raw = &CLASS deflate_dng_load_raw; break; case 34892: load_raw = &CLASS lossy_dng_load_raw; break; default: load_raw = 0; } @@ -8725,6 +8810,7 @@ if (height > width) pixel_aspect = 2; filters = 0; simple_coeff(0); + adobe_coeff (make, model); } else if (!strcmp(make,"Canon") && tiff_bps == 15) { switch (width) { case 3344: width -= 66; @@ -9034,24 +9120,53 @@ if (load_raw == &CLASS lossless_jpeg_load_raw) load_raw = &CLASS hasselblad_load_raw; if (raw_width == 7262) { + if (!strcmp(model, "H3D")) strcpy(model, "H3D-39"); // RT height = 5444; width = 7248; top_margin = 4; left_margin = 7; filters = 0x61616161; - } else if (raw_width == 7410 || raw_width == 8282) { - height -= 84; - width -= 82; + } else if (raw_width == 7410) { + if (!strcmp(model, "H4D")) strcpy(model, "H4D-40"); // RT + height = 5502; + width = 7328; top_margin = 4; left_margin = 41; filters = 0x61616161; + } else if (raw_width == 6542) { // RT, H3D-31, H3DII-31, H4D-31 + if (!strcmp(model, "H3D")) strcpy(model, "H3D-31"); + if (!strcmp(model, "H4D")) strcpy(model, "H4D-31"); + height = 4904; + width = 6524; + top_margin = 4; + left_margin = 8; + } else if (raw_width == 8282) { // RT, H3DII-50, H3DII-50MS, CFV-50, H4D-50 + if (!strcmp(model, "H3D")) strcpy(model, "H3DII-50"); + if (!strcmp(model, "H4D")) strcpy(model, "H4D-50"); + height = 6152; + width = 8196; + top_margin = 4; + left_margin = 44; + } else if (raw_width == 8374) { // RT, CFV-50c, H5D-50c, "H5D-50c MS", "H5D-200c MS" + if (!strcmp(model, "H5D")) strcpy(model, "H5D-50c"); + if (!strcmp(model, "CFV-2")) strcpy(model, "CFV-50c"); + height = 6208; + width = 8280; + top_margin = 96; + left_margin = 48; } else if (raw_width == 9044) { height = 6716; width = 8964; top_margin = 8; left_margin = 40; - black += load_flags = 256; - maximum = 0x8101; + // RT: removed black level / maximum adjustment, as it does not seem to be correct when there are clipped highlights. Tested with Hasselblad H4D-60. + //black += load_flags = 256; + //maximum = 0x8101; + } else if (raw_width == 4096) { // RT: CF-22 etc + if (!strcmp(model, "H3D")) strcpy(model, "H3D-22"); + else if (strstr(model3, "Hasselblad ") == model3) strcpy(model, &model3[11]); + if (strstr(model3, "ixpressCF132")) strcpy(model, "CF-22"); // ixpressCF132 / CF132 is same as CF-22, we use the simpler name + else if (strstr(model3, "Hasselblad96")) strcpy(model, "CFV"); // popularly called CFV-16 } else if (raw_width == 4090) { strcpy (model, "V96C"); height -= (top_margin = 6); @@ -9109,6 +9224,7 @@ filters = 0x16161616; } } else if (!strcmp(make,"Leica") || !strcmp(make,"Panasonic")) { + if(raw_width > 0) { // avoid divide by zero if ((flen - data_offset) / (raw_width*8/7) == raw_height) load_raw = &CLASS panasonic_load_raw; if (!load_raw) { @@ -9126,6 +9242,7 @@ } filters = 0x01010101 * (uchar) "\x94\x61\x49\x16" [((filters-1) ^ (left_margin & 1) ^ (top_margin << 1)) & 3]; + } } else if (!strcmp(model,"C770UZ")) { height = 1718; width = 2304; @@ -9201,13 +9318,15 @@ width -= 6; } else if (!strcmp(make,"Sony") && raw_width == 7392) { width -= 30; - } else if (!strcmp(make,"Sony") && raw_width == 8000) { - width -= 32; - if (!strncmp(model,"DSC",3)) { - tiff_bps = 14; - load_raw = &CLASS unpacked_load_raw; - black = 512; - } +// this was introduced with update to dcraw 9.27 +// but led to broken decode for compressed files from Sony DSC-RX1RM2 +// } else if (!strcmp(make,"Sony") && raw_width == 8000) { +// width -= 32; +// if (!strncmp(model,"DSC",3)) { +// tiff_bps = 14; +// load_raw = &CLASS unpacked_load_raw; +// black = 512; +// } } else if (!strcmp(model,"DSLR-A100")) { if (width == 3880) { height--; @@ -9357,6 +9476,20 @@ memcpy (rgb_cam, cmatrix, sizeof cmatrix); raw_color = 0; } + if(!strncmp(make, "Panasonic", 9) && !strncmp(model, "DMC-LX100",9)) + adobe_coeff (make, model); + if(!strncmp(make, "Samsung", 7) && !strncmp(model, "GX20",4)) + adobe_coeff (make, model); + if(!strncmp(make, "Samsung", 7) && !strncmp(model, "NX1",3)) + adobe_coeff (make, model); + if(!strncmp(make, "Pentax", 6) && (!strncmp(model, "K10D",4) || !strncmp(model, "K-70",4))) + adobe_coeff (make, model); + if(!strncmp(make, "Leica", 5) && !strncmp(model, "Q",1)) + adobe_coeff (make, model); + if(!strncmp(make, "Leica", 5) && !strncmp(model, "SL",2)) + adobe_coeff (make, model); + if(!strncmp(make, "XIAOYI", 6) && !strncmp(model, "M1",2)) + adobe_coeff (make, model); if (raw_color) adobe_coeff (make, model); if (load_raw == &CLASS kodak_radc_load_raw) if (raw_color) adobe_coeff ("Apple","Quicktake"); @@ -9371,9 +9504,9 @@ if (raw_width < width ) raw_width = width; } if (!tiff_bps) tiff_bps = 12; - if (!maximum) maximum = (1 << tiff_bps) - 1; + if (!maximum) maximum = ((uint64_t)1 << tiff_bps) - 1; // use uint64_t to avoid overflow if tiff_bps == 32 if (!load_raw || height < 22 || width < 22 || - tiff_bps > 16 || tiff_samples > 6 || colors > 4) + tiff_samples > 6 || colors > 4) is_raw = 0; #ifdef NO_JASPER if (load_raw == &CLASS redcine_load_raw) { @@ -9402,249 +9535,300 @@ if (flip == UINT_MAX) flip = 0; } -#ifndef NO_LCMS -void CLASS apply_profile (const char *input, const char *output) -{ - char *prof; - cmsHPROFILE hInProfile=0, hOutProfile=0; - cmsHTRANSFORM hTransform; - FILE *fp; - unsigned size; - - if (strcmp (input, "embed")) - hInProfile = cmsOpenProfileFromFile (input, "r"); - else if (profile_length) { - prof = (char *) malloc (profile_length); - merror (prof, "apply_profile()"); - fseek (ifp, profile_offset, SEEK_SET); - fread (prof, 1, profile_length, ifp); - hInProfile = cmsOpenProfileFromMem (prof, profile_length); - free (prof); - } else - fprintf (stderr,_("%s has no embedded profile.\n"), ifname); - if (!hInProfile) return; - if (!output) - hOutProfile = cmsCreate_sRGBProfile(); - else if ((fp = fopen (output, "rb"))) { - fread (&size, 4, 1, fp); - fseek (fp, 0, SEEK_SET); - oprof = (unsigned *) malloc (size = ntohl(size)); - merror (oprof, "apply_profile()"); - fread (oprof, 1, size, fp); - fclose (fp); - if (!(hOutProfile = cmsOpenProfileFromMem (oprof, size))) { - free (oprof); - oprof = 0; +//#ifndef NO_LCMS +//void CLASS apply_profile (const char *input, const char *output) +//{ +// char *prof; +// cmsHPROFILE hInProfile=0, hOutProfile=0; +// cmsHTRANSFORM hTransform; +// FILE *fp; +// unsigned size; +// +// if (strcmp (input, "embed")) +// hInProfile = cmsOpenProfileFromFile (input, "r"); +// else if (profile_length) { +// prof = (char *) malloc (profile_length); +// merror (prof, "apply_profile()"); +// fseek (ifp, profile_offset, SEEK_SET); +// fread (prof, 1, profile_length, ifp); +// hInProfile = cmsOpenProfileFromMem (prof, profile_length); +// free (prof); +// } else +// fprintf (stderr,_("%s has no embedded profile.\n"), ifname); +// if (!hInProfile) return; +// if (!output) +// hOutProfile = cmsCreate_sRGBProfile(); +// else if ((fp = fopen (output, "rb"))) { +// fread (&size, 4, 1, fp); +// fseek (fp, 0, SEEK_SET); +// oprof = (unsigned *) malloc (size = ntohl(size)); +// merror (oprof, "apply_profile()"); +// fread (oprof, 1, size, fp); +// fclose (fp); +// if (!(hOutProfile = cmsOpenProfileFromMem (oprof, size))) { +// free (oprof); +// oprof = 0; +// } +// } else +// fprintf (stderr,_("Cannot open file %s!\n"), output); +// if (!hOutProfile) goto quit; +// if (verbose) +// fprintf (stderr,_("Applying color profile...\n")); +// hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16, +// hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0); +// cmsDoTransform (hTransform, image, image, width*height); +// raw_color = 1; /* Don't use rgb_cam with a profile */ +// cmsDeleteTransform (hTransform); +// cmsCloseProfile (hOutProfile); +//quit: +// cmsCloseProfile (hInProfile); +//} +//#endif + +/* RT: DNG Float */ + +#include +#include + +static void decodeFPDeltaRow(Bytef * src, Bytef * dst, size_t tileWidth, size_t realTileWidth, int bytesps, int factor) { + // DecodeDeltaBytes + for (size_t col = factor; col < realTileWidth*bytesps; ++col) { + src[col] += src[col - factor]; + } + // Reorder bytes into the image + // 16 and 32-bit versions depend on local architecture, 24-bit does not + if (bytesps == 3) { + for (size_t col = 0; col < tileWidth; ++col) { + dst[col*3] = src[col]; + dst[col*3 + 1] = src[col + realTileWidth]; + dst[col*3 + 2] = src[col + realTileWidth*2]; } - } else - fprintf (stderr,_("Cannot open file %s!\n"), output); - if (!hOutProfile) goto quit; - if (verbose) - fprintf (stderr,_("Applying color profile...\n")); - hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16, - hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0); - cmsDoTransform (hTransform, image, image, width*height); - raw_color = 1; /* Don't use rgb_cam with a profile */ - cmsDeleteTransform (hTransform); - cmsCloseProfile (hOutProfile); -quit: - cmsCloseProfile (hInProfile); + } else { + union X { uint32_t x; uint8_t c; }; + if (((union X){1}).c) { + for (size_t col = 0; col < tileWidth; ++col) { + for (size_t byte = 0; byte < bytesps; ++byte) + dst[col*bytesps + byte] = src[col + realTileWidth*(bytesps-byte-1)]; // Little endian + } + } else { + for (size_t col = 0; col < tileWidth; ++col) { + for (size_t byte = 0; byte < bytesps; ++byte) + dst[col*bytesps + byte] = src[col + realTileWidth*byte]; + } + } + } + } -#endif -void CLASS convert_to_rgb() -{ - int row, col, c, i, j, k; - ushort *img; - float out[3], out_cam[3][4]; - double num, inverse[3][3]; - static const double xyzd50_srgb[3][3] = - { { 0.436083, 0.385083, 0.143055 }, - { 0.222507, 0.716888, 0.060608 }, - { 0.013930, 0.097097, 0.714022 } }; - static const double rgb_rgb[3][3] = - { { 1,0,0 }, { 0,1,0 }, { 0,0,1 } }; - static const double adobe_rgb[3][3] = - { { 0.715146, 0.284856, 0.000000 }, - { 0.000000, 1.000000, 0.000000 }, - { 0.000000, 0.041166, 0.958839 } }; - static const double wide_rgb[3][3] = - { { 0.593087, 0.404710, 0.002206 }, - { 0.095413, 0.843149, 0.061439 }, - { 0.011621, 0.069091, 0.919288 } }; - static const double prophoto_rgb[3][3] = - { { 0.529317, 0.330092, 0.140588 }, - { 0.098368, 0.873465, 0.028169 }, - { 0.016879, 0.117663, 0.865457 } }; - static const double aces_rgb[3][3] = - { { 0.432996, 0.375380, 0.189317 }, - { 0.089427, 0.816523, 0.102989 }, - { 0.019165, 0.118150, 0.941914 } }; - static const double (*out_rgb[])[3] = - { rgb_rgb, adobe_rgb, wide_rgb, prophoto_rgb, xyz_rgb, aces_rgb }; - static const char *name[] = - { "sRGB", "Adobe RGB (1998)", "WideGamut D65", "ProPhoto D65", "XYZ", "ACES" }; - static const unsigned phead[] = - { 1024, 0, 0x2100000, 0x6d6e7472, 0x52474220, 0x58595a20, 0, 0, 0, - 0x61637370, 0, 0, 0x6e6f6e65, 0, 0, 0, 0, 0xf6d6, 0x10000, 0xd32d }; - unsigned pbody[] = - { 10, 0x63707274, 0, 36, /* cprt */ - 0x64657363, 0, 40, /* desc */ - 0x77747074, 0, 20, /* wtpt */ - 0x626b7074, 0, 20, /* bkpt */ - 0x72545243, 0, 14, /* rTRC */ - 0x67545243, 0, 14, /* gTRC */ - 0x62545243, 0, 14, /* bTRC */ - 0x7258595a, 0, 20, /* rXYZ */ - 0x6758595a, 0, 20, /* gXYZ */ - 0x6258595a, 0, 20 }; /* bXYZ */ - static const unsigned pwhite[] = { 0xf351, 0x10000, 0x116cc }; - unsigned pcurve[] = { 0x63757276, 0, 1, 0x1000000 }; - - gamma_curve (gamm[0], gamm[1], 0, 0); - memcpy (out_cam, rgb_cam, sizeof out_cam); - raw_color |= colors == 1 || document_mode || - output_color < 1 || output_color > 6; - if (!raw_color) { - oprof = (unsigned *) calloc (phead[0], 1); - merror (oprof, "convert_to_rgb()"); - memcpy (oprof, phead, sizeof phead); - if (output_color == 5) oprof[4] = oprof[5]; - oprof[0] = 132 + 12*pbody[0]; - for (i=0; i < pbody[0]; i++) { - oprof[oprof[0]/4] = i ? (i > 1 ? 0x58595a20 : 0x64657363) : 0x74657874; - pbody[i*3+2] = oprof[0]; - oprof[0] += (pbody[i*3+3] + 3) & -4; - } - memcpy (oprof+32, pbody, sizeof pbody); - oprof[pbody[5]/4+2] = strlen(name[output_color-1]) + 1; - memcpy ((char *)oprof+pbody[8]+8, pwhite, sizeof pwhite); - pcurve[3] = (short)(256/gamm[5]+0.5) << 16; - for (i=4; i < 7; i++) - memcpy ((char *)oprof+pbody[i*3+2], pcurve, sizeof pcurve); - pseudoinverse ((double (*)[3]) out_rgb[output_color-1], inverse, 3); - for (i=0; i < 3; i++) - for (j=0; j < 3; j++) { - for (num = k=0; k < 3; k++) - num += xyzd50_srgb[i][k] * inverse[j][k]; - oprof[pbody[j*3+23]/4+i+2] = num * 0x10000 + 0.5; - } - for (i=0; i < phead[0]/4; i++) - oprof[i] = htonl(oprof[i]); - strcpy ((char *)oprof+pbody[2]+8, "auto-generated by dcraw"); - strcpy ((char *)oprof+pbody[5]+12, name[output_color-1]); - for (i=0; i < 3; i++) - for (j=0; j < colors; j++) - for (out_cam[i][j] = k=0; k < 3; k++) - out_cam[i][j] += out_rgb[output_color-1][i][k] * rgb_cam[k][j]; +// From DNG SDK dng_utils.h +static inline uint32_t DNG_HalfToFloat(uint16_t halfValue) { + int32_t sign = (halfValue >> 15) & 0x00000001; + int32_t exponent = (halfValue >> 10) & 0x0000001f; + int32_t mantissa = halfValue & 0x000003ff; + if (exponent == 0) { + if (mantissa == 0) { + // Plus or minus zero + return (uint32_t) (sign << 31); + } else { + // Denormalized number -- renormalize it + while (!(mantissa & 0x00000400)) { + mantissa <<= 1; + exponent -= 1; + } + exponent += 1; + mantissa &= ~0x00000400; + } + } else if (exponent == 31) { + if (mantissa == 0) { + // Positive or negative infinity, convert to maximum (16 bit) values. + return (uint32_t) ((sign << 31) | ((0x1eL + 127 - 15) << 23) | (0x3ffL << 13)); + } else { + // Nan -- Just set to zero. + return 0; + } } - if (verbose) - fprintf (stderr, raw_color ? _("Building histograms...\n") : - _("Converting to %s colorspace...\n"), name[output_color-1]); + // Normalized number + exponent += (127 - 15); + mantissa <<= 13; + // Assemble sign, exponent and mantissa. + return (uint32_t) ((sign << 31) | (exponent << 23) | mantissa); +} - memset (histogram, 0, sizeof histogram); - for (img=image[0], row=0; row < height; row++) - for (col=0; col < width; col++, img+=4) { - if (!raw_color) { - out[0] = out[1] = out[2] = 0; - FORCC { - out[0] += out_cam[0][c] * img[c]; - out[1] += out_cam[1][c] * img[c]; - out[2] += out_cam[2][c] * img[c]; - } - FORC3 img[c] = CLIP((int) out[c]); - } - else if (document_mode) - img[0] = img[fcol(row,col)]; - FORCC histogram[c][img[c] >> 3]++; - } - if (colors == 4 && output_color) colors = 3; - if (document_mode && filters) colors = 1; -} - -void CLASS fuji_rotate() -{ - int i, row, col; - double step; - float r, c, fr, fc; - unsigned ur, uc; - ushort wide, high, (*img)[4], (*pix)[4]; +static inline uint32_t DNG_FP24ToFloat(const uint8_t * input) { + int32_t sign = (input [0] >> 7) & 0x01; + int32_t exponent = (input [0] ) & 0x7F; + int32_t mantissa = (((int32_t) input [1]) << 8) | input[2]; + if (exponent == 0) { + if (mantissa == 0) { + // Plus or minus zero + return (uint32_t) (sign << 31); + } else { + // Denormalized number -- renormalize it + while (!(mantissa & 0x00010000)) { + mantissa <<= 1; + exponent -= 1; + } + exponent += 1; + mantissa &= ~0x00010000; + } + } else if (exponent == 127) { + if (mantissa == 0) { + // Positive or negative infinity, convert to maximum (24 bit) values. + return (uint32_t) ((sign << 31) | ((0x7eL + 128 - 64) << 23) | (0xffffL << 7)); + } else { + // Nan -- Just set to zero. + return 0; + } + } + // Normalized number + exponent += (128 - 64); + mantissa <<= 7; + // Assemble sign, exponent and mantissa. + return (uint32_t) ((sign << 31) | (exponent << 23) | mantissa); +} - if (!fuji_width) return; - if (verbose) - fprintf (stderr,_("Rotating image 45 degrees...\n")); - fuji_width = (fuji_width - 1 + shrink) >> shrink; - step = sqrt(0.5); - wide = fuji_width / step; - high = (height - fuji_width) / step; - img = (ushort (*)[4]) calloc (high, wide*sizeof *img); - merror (img, "fuji_rotate()"); - - for (row=0; row < high; row++) - for (col=0; col < wide; col++) { - ur = r = fuji_width + (row-col)*step; - uc = c = (row+col)*step; - if (ur > height-2 || uc > width-2) continue; - fr = r - ur; - fc = c - uc; - pix = image + ur*width + uc; - for (i=0; i < colors; i++) - img[row*wide+col][i] = - (pix[ 0][i]*(1-fc) + pix[ 1][i]*fc) * (1-fr) + - (pix[width][i]*(1-fc) + pix[width+1][i]*fc) * fr; - } - free (image); - width = wide; - height = high; - image = img; - fuji_width = 0; +static void expandFloats(Bytef * dst, int tileWidth, int bytesps) { + if (bytesps == 2) { + uint16_t * dst16 = (uint16_t *) dst; + uint32_t * dst32 = (uint32_t *) dst; + for (int index = tileWidth - 1; index >= 0; --index) { + dst32[index] = DNG_HalfToFloat(dst16[index]); + } + } else if (bytesps == 3) { + uint8_t * dst8 = ((uint8_t *) dst) + (tileWidth - 1) * 3; + uint32_t * dst32 = (uint32_t *) dst; + for (int index = tileWidth - 1; index >= 0; --index, dst8 -= 3) { + dst32[index] = DNG_FP24ToFloat(dst8); + } + } } -void CLASS stretch() +static void copyFloatDataToInt(float * src, ushort * dst, size_t size, float max) { + bool negative = false, nan = false; + +#ifdef _OPENMP +#pragma omp parallel for +#endif + for (size_t i = 0; i < size; ++i) { + if (src[i] < 0.0f) { + negative = true; + src[i] = 0.0f; + } else if (std::isnan(src[i])) { + nan = true; + src[i] = max; + } + // Copy the data to the integer buffer to build the thumbnail + dst[i] = (ushort)src[i]; + } + if (negative) + fprintf(stderr, "DNG Float: Negative data found in input file\n"); + if (nan) + fprintf(stderr, "DNG Float: NaN data found in input file\n"); +} + +void CLASS deflate_dng_load_raw() { + struct tiff_ifd * ifd = &tiff_ifd[0]; + while (ifd < &tiff_ifd[tiff_nifds] && ifd->offset != data_offset) ++ifd; + if (ifd == &tiff_ifd[tiff_nifds]) { + fprintf(stderr, "DNG Deflate: Raw image not found???\n"); + return; + } + + int predFactor; + switch(ifd->predictor) { + case 3: predFactor = 1; break; + case 34894: predFactor = 2; break; + case 34895: predFactor = 4; break; + default: predFactor = 0; break; + } + + if (ifd->sample_format == 3) { // Floating point data + float_raw_image = new float[raw_width * raw_height]; + +#ifdef _OPENMP +#pragma omp parallel for +#endif + for (size_t i = 0; i < raw_width * raw_height; ++i) + float_raw_image[i] = 0.0f; + } + + // NOTE: This reader is based on the official DNG SDK from Adobe. + // It assumes tiles without subtiles, but the standard does not say that + // subtiles or strips couldn't be used. + if (tile_length < INT_MAX) { + size_t tilesWide = (raw_width + tile_width - 1) / tile_width; + size_t tilesHigh = (raw_height + tile_length - 1) / tile_length; + size_t tileCount = tilesWide * tilesHigh; + //fprintf(stderr, "%dx%d tiles, %d total\n", tilesWide, tilesHigh, tileCount); + size_t tileOffsets[tileCount]; + for (size_t t = 0; t < tileCount; ++t) { + tileOffsets[t] = get4(); + } + size_t tileBytes[tileCount]; + uLongf maxCompressed = 0; + if (tileCount == 1) { + tileBytes[0] = maxCompressed = ifd->bytes; + } else { + fseek(ifp, ifd->bytes, SEEK_SET); + for (size_t t = 0; t < tileCount; ++t) { + tileBytes[t] = get4(); + //fprintf(stderr, "Tile %d at %d, size %d\n", t, tileOffsets[t], tileBytes[t]); + if (maxCompressed < tileBytes[t]) + maxCompressed = tileBytes[t]; + } + } + uLongf dstLen = tile_width * tile_length * 4; + +#ifdef _OPENMP +#pragma omp parallel +#endif { - ushort newdim, (*img)[4], *pix0, *pix1; - int row, col, c; - double rc, frac; + Bytef * cBuffer = new Bytef[maxCompressed]; + Bytef * uBuffer = new Bytef[dstLen]; - if (pixel_aspect == 1) return; - if (verbose) fprintf (stderr,_("Stretching the image...\n")); - if (pixel_aspect < 1) { - newdim = height / pixel_aspect + 0.5; - img = (ushort (*)[4]) calloc (width, newdim*sizeof *img); - merror (img, "stretch()"); - for (rc=row=0; row < newdim; row++, rc+=pixel_aspect) { - frac = rc - (c = rc); - pix0 = pix1 = image[c*width]; - if (c+1 < height) pix1 += width*4; - for (col=0; col < width; col++, pix0+=4, pix1+=4) - FORCC img[row*width+col][c] = pix0[c]*(1-frac) + pix1[c]*frac + 0.5; +#ifdef _OPENMP +#pragma omp for collapse(2) nowait +#endif + for (size_t y = 0; y < raw_height; y += tile_length) { + for (size_t x = 0; x < raw_width; x += tile_width) { + size_t t = (y / tile_length) * tilesWide + (x / tile_width); +#pragma omp critical +{ + fseek(ifp, tileOffsets[t], SEEK_SET); + fread(cBuffer, 1, tileBytes[t], ifp); +} + int err = uncompress(uBuffer, &dstLen, cBuffer, tileBytes[t]); + if (err != Z_OK) { + fprintf(stderr, "DNG Deflate: Failed uncompressing tile %d, with error %d\n", (int)t, err); + } else if (ifd->sample_format == 3) { // Floating point data + int bytesps = ifd->bps >> 3; + size_t thisTileLength = y + tile_length > raw_height ? raw_height - y : tile_length; + size_t thisTileWidth = x + tile_width > raw_width ? raw_width - x : tile_width; + for (size_t row = 0; row < thisTileLength; ++row) { + Bytef * src = uBuffer + row*tile_width*bytesps; + Bytef * dst = (Bytef *)&float_raw_image[(y+row)*raw_width + x]; + if (predFactor) + decodeFPDeltaRow(src, dst, thisTileWidth, tile_width, bytesps, predFactor); + expandFloats(dst, thisTileWidth, bytesps); + } + } else { // 32-bit Integer data + // TODO + } + } } - height = newdim; - } else { - newdim = width * pixel_aspect + 0.5; - img = (ushort (*)[4]) calloc (height, newdim*sizeof *img); - merror (img, "stretch()"); - for (rc=col=0; col < newdim; col++, rc+=1/pixel_aspect) { - frac = rc - (c = rc); - pix0 = pix1 = image[c]; - if (c+1 < width) pix1 += 4; - for (row=0; row < height; row++, pix0+=width*4, pix1+=width*4) - FORCC img[row*newdim+col][c] = pix0[c]*(1-frac) + pix1[c]*frac + 0.5; - } - width = newdim; - } - free (image); - image = img; -} - -int CLASS flip_index (int row, int col) -{ - if (flip & 4) SWAP(row,col); - if (flip & 2) row = iheight - 1 - row; - if (flip & 1) col = iwidth - 1 - col; - return row * iwidth + col; + + delete [] cBuffer; + delete [] uBuffer; +} + } + + if (ifd->sample_format == 3) { // Floating point data + copyFloatDataToInt(float_raw_image, raw_image, raw_width*raw_height, maximum); + } } +/* RT: removed unused functions */ + struct tiff_tag { ushort tag, type; int count; @@ -9667,594 +9851,11 @@ char desc[512], make[64], model[64], soft[32], date[20], artist[64]; }; -void CLASS tiff_set (struct tiff_hdr *th, ushort *ntag, - ushort tag, ushort type, int count, int val) -{ - struct tiff_tag *tt; - int c; - - tt = (struct tiff_tag *)(ntag+1) + (*ntag)++; - tt->val.i = val; - if (type == 1 && count <= 4) - FORC(4) tt->val.c[c] = val >> (c << 3); - else if (type == 2) { - count = strnlen((char *)th + val, count-1) + 1; - if (count <= 4) - FORC(4) tt->val.c[c] = ((char *)th)[val+c]; - } else if (type == 3 && count <= 2) - FORC(2) tt->val.s[c] = val >> (c << 4); - tt->count = count; - tt->type = type; - tt->tag = tag; -} - -#define TOFF(ptr) ((char *)(&(ptr)) - (char *)th) - -void CLASS tiff_head (struct tiff_hdr *th, int full) -{ - int c, psize=0; - struct tm *t; - - memset (th, 0, sizeof *th); - th->order = htonl(0x4d4d4949) >> 16; - th->magic = 42; - th->ifd = 10; - th->rat[0] = th->rat[2] = 300; - th->rat[1] = th->rat[3] = 1; - FORC(6) th->rat[4+c] = 1000000; - th->rat[4] *= shutter; - th->rat[6] *= aperture; - th->rat[8] *= focal_len; - strncpy (th->desc, desc, 512); - strncpy (th->make, make, 64); - strncpy (th->model, model, 64); - strcpy (th->soft, "dcraw v"DCRAW_VERSION); - t = localtime (×tamp); - sprintf (th->date, "%04d:%02d:%02d %02d:%02d:%02d", - t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); - strncpy (th->artist, artist, 64); - if (full) { - tiff_set (th, &th->ntag, 254, 4, 1, 0); - tiff_set (th, &th->ntag, 256, 4, 1, width); - tiff_set (th, &th->ntag, 257, 4, 1, height); - tiff_set (th, &th->ntag, 258, 3, colors, output_bps); - if (colors > 2) - th->tag[th->ntag-1].val.i = TOFF(th->bps); - FORC4 th->bps[c] = output_bps; - tiff_set (th, &th->ntag, 259, 3, 1, 1); - tiff_set (th, &th->ntag, 262, 3, 1, 1 + (colors > 1)); - } - tiff_set (th, &th->ntag, 270, 2, 512, TOFF(th->desc)); - tiff_set (th, &th->ntag, 271, 2, 64, TOFF(th->make)); - tiff_set (th, &th->ntag, 272, 2, 64, TOFF(th->model)); - if (full) { - if (oprof) psize = ntohl(oprof[0]); - tiff_set (th, &th->ntag, 273, 4, 1, sizeof *th + psize); - tiff_set (th, &th->ntag, 277, 3, 1, colors); - tiff_set (th, &th->ntag, 278, 4, 1, height); - tiff_set (th, &th->ntag, 279, 4, 1, height*width*colors*output_bps/8); - } else - tiff_set (th, &th->ntag, 274, 3, 1, "12435867"[flip]-'0'); - tiff_set (th, &th->ntag, 282, 5, 1, TOFF(th->rat[0])); - tiff_set (th, &th->ntag, 283, 5, 1, TOFF(th->rat[2])); - tiff_set (th, &th->ntag, 284, 3, 1, 1); - tiff_set (th, &th->ntag, 296, 3, 1, 2); - tiff_set (th, &th->ntag, 305, 2, 32, TOFF(th->soft)); - tiff_set (th, &th->ntag, 306, 2, 20, TOFF(th->date)); - tiff_set (th, &th->ntag, 315, 2, 64, TOFF(th->artist)); - tiff_set (th, &th->ntag, 34665, 4, 1, TOFF(th->nexif)); - if (psize) tiff_set (th, &th->ntag, 34675, 7, psize, sizeof *th); - tiff_set (th, &th->nexif, 33434, 5, 1, TOFF(th->rat[4])); - tiff_set (th, &th->nexif, 33437, 5, 1, TOFF(th->rat[6])); - tiff_set (th, &th->nexif, 34855, 3, 1, iso_speed); - tiff_set (th, &th->nexif, 37386, 5, 1, TOFF(th->rat[8])); - if (gpsdata[1]) { - tiff_set (th, &th->ntag, 34853, 4, 1, TOFF(th->ngps)); - tiff_set (th, &th->ngps, 0, 1, 4, 0x202); - tiff_set (th, &th->ngps, 1, 2, 2, gpsdata[29]); - tiff_set (th, &th->ngps, 2, 5, 3, TOFF(th->gps[0])); - tiff_set (th, &th->ngps, 3, 2, 2, gpsdata[30]); - tiff_set (th, &th->ngps, 4, 5, 3, TOFF(th->gps[6])); - tiff_set (th, &th->ngps, 5, 1, 1, gpsdata[31]); - tiff_set (th, &th->ngps, 6, 5, 1, TOFF(th->gps[18])); - tiff_set (th, &th->ngps, 7, 5, 3, TOFF(th->gps[12])); - tiff_set (th, &th->ngps, 18, 2, 12, TOFF(th->gps[20])); - tiff_set (th, &th->ngps, 29, 2, 12, TOFF(th->gps[23])); - memcpy (th->gps, gpsdata, sizeof th->gps); - } -} - -void CLASS jpeg_thumb() -{ - char *thumb; - ushort exif[5]; - struct tiff_hdr th; - - thumb = (char *) malloc (thumb_length); - merror (thumb, "jpeg_thumb()"); - fread (thumb, 1, thumb_length, ifp); - fputc (0xff, ofp); - fputc (0xd8, ofp); - if (strcmp (thumb+6, "Exif")) { - memcpy (exif, "\xff\xe1 Exif\0\0", 10); - exif[1] = htons (8 + sizeof th); - fwrite (exif, 1, sizeof exif, ofp); - tiff_head (&th, 0); - fwrite (&th, 1, sizeof th, ofp); - } - fwrite (thumb+2, 1, thumb_length-2, ofp); - free (thumb); -} - -void CLASS write_ppm_tiff() -{ - struct tiff_hdr th; - uchar *ppm; - ushort *ppm2; - int c, row, col, soff, rstep, cstep; - int perc, val, total, white=0x2000; - - perc = width * height * 0.01; /* 99th percentile white level */ - if (fuji_width) perc /= 2; - if (!((highlight & ~2) || no_auto_bright)) - for (white=c=0; c < colors; c++) { - for (val=0x2000, total=0; --val > 32; ) - if ((total += histogram[c][val]) > perc) break; - if (white < val) white = val; - } - gamma_curve (gamm[0], gamm[1], 2, (white << 3)/bright); - iheight = height; - iwidth = width; - if (flip & 4) SWAP(height,width); - ppm = (uchar *) calloc (width, colors*output_bps/8); - ppm2 = (ushort *) ppm; - merror (ppm, "write_ppm_tiff()"); - if (output_tiff) { - tiff_head (&th, 1); - fwrite (&th, sizeof th, 1, ofp); - if (oprof) - fwrite (oprof, ntohl(oprof[0]), 1, ofp); - } else if (colors > 3) - fprintf (ofp, - "P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\nTUPLTYPE %s\nENDHDR\n", - width, height, colors, (1 << output_bps)-1, cdesc); - else - fprintf (ofp, "P%d\n%d %d\n%d\n", - colors/2+5, width, height, (1 << output_bps)-1); - soff = flip_index (0, 0); - cstep = flip_index (0, 1) - soff; - rstep = flip_index (1, 0) - flip_index (0, width); - for (row=0; row < height; row++, soff += rstep) { - for (col=0; col < width; col++, soff += cstep) - if (output_bps == 8) - FORCC ppm [col*colors+c] = curve[image[soff][c]] >> 8; - else FORCC ppm2[col*colors+c] = curve[image[soff][c]]; - if (output_bps == 16 && !output_tiff && htons(0x55aa) != 0x55aa) - swab (ppm2, ppm2, width*colors*2); - fwrite (ppm, colors*output_bps/8, width, ofp); - } - free (ppm); -} - -int CLASS main (int argc, const char **argv) -{ - int arg, status=0, quality, i, c; - int timestamp_only=0, thumbnail_only=0, identify_only=0; - int user_qual=-1, user_black=-1, user_sat=-1, user_flip=-1; - int use_fuji_rotate=1, write_to_stdout=0, read_from_stdin=0; - const char *sp, *bpfile=0, *dark_frame=0, *write_ext; - char opm, opt, *ofname, *cp; - struct utimbuf ut; -#ifndef NO_LCMS - const char *cam_profile=0, *out_profile=0; -#endif - -#ifndef LOCALTIME - putenv ((char *) "TZ=UTC"); -#endif -#ifdef LOCALEDIR - setlocale (LC_CTYPE, ""); - setlocale (LC_MESSAGES, ""); - bindtextdomain ("dcraw", LOCALEDIR); - textdomain ("dcraw"); -#endif - - if (argc == 1) { - printf(_("\nRaw photo decoder \"dcraw\" v%s"), DCRAW_VERSION); - printf(_("\nby Dave Coffin, dcoffin a cybercom o net\n")); - printf(_("\nUsage: %s [OPTION]... [FILE]...\n\n"), argv[0]); - puts(_("-v Print verbose messages")); - puts(_("-c Write image data to standard output")); - puts(_("-e Extract embedded thumbnail image")); - puts(_("-i Identify files without decoding them")); - puts(_("-i -v Identify files and show metadata")); - puts(_("-z Change file dates to camera timestamp")); - puts(_("-w Use camera white balance, if possible")); - puts(_("-a Average the whole image for white balance")); - puts(_("-A Average a grey box for white balance")); - puts(_("-r Set custom white balance")); - puts(_("+M/-M Use/don't use an embedded color matrix")); - puts(_("-C Correct chromatic aberration")); - puts(_("-P Fix the dead pixels listed in this file")); - puts(_("-K Subtract dark frame (16-bit raw PGM)")); - puts(_("-k Set the darkness level")); - puts(_("-S Set the saturation level")); - puts(_("-n Set threshold for wavelet denoising")); - puts(_("-H [0-9] Highlight mode (0=clip, 1=unclip, 2=blend, 3+=rebuild)")); - puts(_("-t [0-7] Flip image (0=none, 3=180, 5=90CCW, 6=90CW)")); - puts(_("-o [0-6] Output colorspace (raw,sRGB,Adobe,Wide,ProPhoto,XYZ,ACES)")); -#ifndef NO_LCMS - puts(_("-o Apply output ICC profile from file")); - puts(_("-p Apply camera ICC profile from file or \"embed\"")); -#endif - puts(_("-d Document mode (no color, no interpolation)")); - puts(_("-D Document mode without scaling (totally raw)")); - puts(_("-j Don't stretch or rotate raw pixels")); - puts(_("-W Don't automatically brighten the image")); - puts(_("-b Adjust brightness (default = 1.0)")); - puts(_("-g

Set custom gamma curve (default = 2.222 4.5)")); - puts(_("-q [0-3] Set the interpolation quality")); - puts(_("-h Half-size color image (twice as fast as \"-q 0\")")); - puts(_("-f Interpolate RGGB as four colors")); - puts(_("-m Apply a 3x3 median filter to R-G and B-G")); - puts(_("-s [0..N-1] Select one raw image or \"all\" from each file")); - puts(_("-6 Write 16-bit instead of 8-bit")); - puts(_("-4 Linear 16-bit, same as \"-6 -W -g 1 1\"")); - puts(_("-T Write TIFF instead of PPM")); - puts(""); - return 1; - } - argv[argc] = ""; - for (arg=1; (((opm = argv[arg][0]) - 2) | 2) == '+'; ) { - opt = argv[arg++][1]; - if ((cp = (char *) strchr (sp="nbrkStqmHACg", opt))) - for (i=0; i < "114111111422"[cp-sp]-'0'; i++) - if (!isdigit(argv[arg+i][0])) { - fprintf (stderr,_("Non-numeric argument to \"-%c\"\n"), opt); - return 1; - } - switch (opt) { - case 'n': threshold = atof(argv[arg++]); break; - case 'b': bright = atof(argv[arg++]); break; - case 'r': - FORC4 user_mul[c] = atof(argv[arg++]); break; - case 'C': aber[0] = 1 / atof(argv[arg++]); - aber[2] = 1 / atof(argv[arg++]); break; - case 'g': gamm[0] = atof(argv[arg++]); - gamm[1] = atof(argv[arg++]); - if (gamm[0]) gamm[0] = 1/gamm[0]; break; - case 'k': user_black = atoi(argv[arg++]); break; - case 'S': user_sat = atoi(argv[arg++]); break; - case 't': user_flip = atoi(argv[arg++]); break; - case 'q': user_qual = atoi(argv[arg++]); break; - case 'm': med_passes = atoi(argv[arg++]); break; - case 'H': highlight = atoi(argv[arg++]); break; - case 's': - shot_select = abs(atoi(argv[arg])); - multi_out = !strcmp(argv[arg++],"all"); - break; - case 'o': - if (isdigit(argv[arg][0]) && !argv[arg][1]) - output_color = atoi(argv[arg++]); -#ifndef NO_LCMS - else out_profile = argv[arg++]; - break; - case 'p': cam_profile = argv[arg++]; -#endif - break; - case 'P': bpfile = argv[arg++]; break; - case 'K': dark_frame = argv[arg++]; break; - case 'z': timestamp_only = 1; break; - case 'e': thumbnail_only = 1; break; - case 'i': identify_only = 1; break; - case 'c': write_to_stdout = 1; break; - case 'v': verbose = 1; break; - case 'h': half_size = 1; break; - case 'f': four_color_rgb = 1; break; - case 'A': FORC4 greybox[c] = atoi(argv[arg++]); - case 'a': use_auto_wb = 1; break; - case 'w': use_camera_wb = 1; break; - case 'M': use_camera_matrix = 3 * (opm == '+'); break; - case 'I': read_from_stdin = 1; break; - case 'E': document_mode++; - case 'D': document_mode++; - case 'd': document_mode++; - case 'j': use_fuji_rotate = 0; break; - case 'W': no_auto_bright = 1; break; - case 'T': output_tiff = 1; break; - case '4': gamm[0] = gamm[1] = - no_auto_bright = 1; - case '6': output_bps = 16; break; - default: - fprintf (stderr,_("Unknown option \"-%c\".\n"), opt); - return 1; - } - } - if (arg == argc) { - fprintf (stderr,_("No files to process.\n")); - return 1; - } - if (write_to_stdout) { - if (isatty(1)) { - fprintf (stderr,_("Will not write an image to the terminal!\n")); - return 1; - } -#if defined(WIN32) || defined(DJGPP) || defined(__CYGWIN__) - if (setmode(1,O_BINARY) < 0) { - perror ("setmode()"); - return 1; - } -#endif - } - for ( ; arg < argc; arg++) { - status = 1; - raw_image = 0; - image = 0; - oprof = 0; - meta_data = ofname = 0; - ofp = stdout; - if (setjmp (failure)) { - if (fileno(ifp) > 2) fclose(ifp); - if (fileno(ofp) > 2) fclose(ofp); - status = 1; - goto cleanup; - } - ifname = argv[arg]; - if (!(ifp = fopen (ifname, "rb"))) { - perror (ifname); - continue; - } - status = (identify(),!is_raw); - if (user_flip >= 0) - flip = user_flip; - switch ((flip+3600) % 360) { - case 270: flip = 5; break; - case 180: flip = 3; break; - case 90: flip = 6; - } - if (timestamp_only) { - if ((status = !timestamp)) - fprintf (stderr,_("%s has no timestamp.\n"), ifname); - else if (identify_only) - printf ("%10ld%10d %s\n", (long) timestamp, shot_order, ifname); - else { - if (verbose) - fprintf (stderr,_("%s time set to %d.\n"), ifname, (int) timestamp); - ut.actime = ut.modtime = timestamp; - utime (ifname, &ut); - } - goto next; - } - write_fun = &CLASS write_ppm_tiff; - if (thumbnail_only) { - if ((status = !thumb_offset)) { - fprintf (stderr,_("%s has no thumbnail.\n"), ifname); - goto next; - } else if (thumb_load_raw) { - load_raw = thumb_load_raw; - data_offset = thumb_offset; - height = thumb_height; - width = thumb_width; - filters = 0; - colors = 3; - } else { - fseek (ifp, thumb_offset, SEEK_SET); - write_fun = write_thumb; - goto thumbnail; - } - } - if (load_raw == &CLASS kodak_ycbcr_load_raw) { - height += height & 1; - width += width & 1; - } - if (identify_only && verbose && make[0]) { - printf (_("\nFilename: %s\n"), ifname); - printf (_("Timestamp: %s"), ctime(×tamp)); - printf (_("Camera: %s %s\n"), make, model); - if (artist[0]) - printf (_("Owner: %s\n"), artist); - if (dng_version) { - printf (_("DNG Version: ")); - for (i=24; i >= 0; i -= 8) - printf ("%d%c", dng_version >> i & 255, i ? '.':'\n'); - } - printf (_("ISO speed: %d\n"), (int) iso_speed); - printf (_("Shutter: ")); - if (shutter > 0 && shutter < 1) - shutter = (printf ("1/"), 1 / shutter); - printf (_("%0.1f sec\n"), shutter); - printf (_("Aperture: f/%0.1f\n"), aperture); - printf (_("Focal length: %0.1f mm\n"), focal_len); - printf (_("Embedded ICC profile: %s\n"), profile_length ? _("yes"):_("no")); - printf (_("Number of raw images: %d\n"), is_raw); - if (pixel_aspect != 1) - printf (_("Pixel Aspect Ratio: %0.6f\n"), pixel_aspect); - if (thumb_offset) - printf (_("Thumb size: %4d x %d\n"), thumb_width, thumb_height); - printf (_("Full size: %4d x %d\n"), raw_width, raw_height); - } else if (!is_raw) - fprintf (stderr,_("Cannot decode file %s\n"), ifname); - if (!is_raw) goto next; - shrink = filters && (half_size || (!identify_only && - (threshold || aber[0] != 1 || aber[2] != 1))); - iheight = (height + shrink) >> shrink; - iwidth = (width + shrink) >> shrink; - if (identify_only) { - if (verbose) { - if (document_mode == 3) { - top_margin = left_margin = fuji_width = 0; - height = raw_height; - width = raw_width; - } - iheight = (height + shrink) >> shrink; - iwidth = (width + shrink) >> shrink; - if (use_fuji_rotate) { - if (fuji_width) { - fuji_width = (fuji_width - 1 + shrink) >> shrink; - iwidth = fuji_width / sqrt(0.5); - iheight = (iheight - fuji_width) / sqrt(0.5); - } else { - if (pixel_aspect < 1) iheight = iheight / pixel_aspect + 0.5; - if (pixel_aspect > 1) iwidth = iwidth * pixel_aspect + 0.5; - } - } - if (flip & 4) - SWAP(iheight,iwidth); - printf (_("Image size: %4d x %d\n"), width, height); - printf (_("Output size: %4d x %d\n"), iwidth, iheight); - printf (_("Raw colors: %d"), colors); - if (filters) { - int fhigh = 2, fwide = 2; - if ((filters ^ (filters >> 8)) & 0xff) fhigh = 4; - if ((filters ^ (filters >> 16)) & 0xffff) fhigh = 8; - if (filters == 1) fhigh = fwide = 16; - if (filters == 9) fhigh = fwide = 6; - printf (_("\nFilter pattern: ")); - for (i=0; i < fhigh; i++) - for (c = i && putchar('/') && 0; c < fwide; c++) - putchar (cdesc[fcol(i,c)]); - } - printf (_("\nDaylight multipliers:")); - FORCC printf (" %f", pre_mul[c]); - if (cam_mul[0] > 0) { - printf (_("\nCamera multipliers:")); - FORC4 printf (" %f", cam_mul[c]); - } - putchar ('\n'); - } else - printf (_("%s is a %s %s image.\n"), ifname, make, model); -next: - fclose(ifp); - continue; - } - if (meta_length) { - meta_data = (char *) malloc (meta_length); - merror (meta_data, "main()"); - } - if (filters || colors == 1) { - raw_image = (ushort *) calloc ((raw_height+7), raw_width*2); - merror (raw_image, "main()"); - } else { - image = (ushort (*)[4]) calloc (iheight, iwidth*sizeof *image); - merror (image, "main()"); - } - if (verbose) - fprintf (stderr,_("Loading %s %s image from %s ...\n"), - make, model, ifname); - if (shot_select >= is_raw) - fprintf (stderr,_("%s: \"-s %d\" requests a nonexistent image!\n"), - ifname, shot_select); - fseeko (ifp, data_offset, SEEK_SET); - if (raw_image && read_from_stdin) - fread (raw_image, 2, raw_height*raw_width, stdin); - else (*load_raw)(); - if (document_mode == 3) { - top_margin = left_margin = fuji_width = 0; - height = raw_height; - width = raw_width; - } - iheight = (height + shrink) >> shrink; - iwidth = (width + shrink) >> shrink; - if (raw_image) { - image = (ushort (*)[4]) calloc (iheight, iwidth*sizeof *image); - merror (image, "main()"); - crop_masked_pixels(); - free (raw_image); - } - if (zero_is_bad) remove_zeroes(); - bad_pixels (bpfile); - if (dark_frame) subtract (dark_frame); - quality = 2 + !fuji_width; - if (user_qual >= 0) quality = user_qual; - i = cblack[3]; - FORC3 if (i > cblack[c]) i = cblack[c]; - FORC4 cblack[c] -= i; - black += i; - i = cblack[6]; - FORC (cblack[4] * cblack[5]) - if (i > cblack[6+c]) i = cblack[6+c]; - FORC (cblack[4] * cblack[5]) - cblack[6+c] -= i; - black += i; - if (user_black >= 0) black = user_black; - FORC4 cblack[c] += black; - if (user_sat > 0) maximum = user_sat; -#ifdef COLORCHECK - colorcheck(); -#endif - if (is_foveon) { - if (document_mode || load_raw == &CLASS foveon_dp_load_raw) { - for (i=0; i < height*width*4; i++) - if ((short) image[0][i] < 0) image[0][i] = 0; - } else foveon_interpolate(); - } else if (document_mode < 2) - scale_colors(); - pre_interpolate(); - if (filters && !document_mode) { - if (quality == 0) - lin_interpolate(); - else if (quality == 1 || colors > 3) - vng_interpolate(); - else if (quality == 2 && filters > 1000) - ppg_interpolate(); - else if (filters == 9) - xtrans_interpolate (quality*2-3); - else - ahd_interpolate(); - } - if (mix_green) - for (colors=3, i=0; i < height*width; i++) - image[i][1] = (image[i][1] + image[i][3]) >> 1; - if (!is_foveon && colors == 3) median_filter(); - if (!is_foveon && highlight == 2) blend_highlights(); - if (!is_foveon && highlight > 2) recover_highlights(); - if (use_fuji_rotate) fuji_rotate(); -#ifndef NO_LCMS - if (cam_profile) apply_profile (cam_profile, out_profile); -#endif - convert_to_rgb(); - if (use_fuji_rotate) stretch(); -thumbnail: - if (write_fun == &CLASS jpeg_thumb) - write_ext = ".jpg"; - else if (output_tiff && write_fun == &CLASS write_ppm_tiff) - write_ext = ".tiff"; - else - write_ext = ".pgm\0.ppm\0.ppm\0.pam" + colors*5-5; - ofname = (char *) malloc (strlen(ifname) + 64); - merror (ofname, "main()"); - if (write_to_stdout) - strcpy (ofname,_("standard output")); - else { - strcpy (ofname, ifname); - if ((cp = strrchr (ofname, '.'))) *cp = 0; - if (multi_out) - sprintf (ofname+strlen(ofname), "_%0*d", - snprintf(0,0,"%d",is_raw-1), shot_select); - if (thumbnail_only) - strcat (ofname, ".thumb"); - strcat (ofname, write_ext); - ofp = fopen (ofname, "wb"); - if (!ofp) { - status = 1; - perror (ofname); - goto cleanup; - } - } - if (verbose) - fprintf (stderr,_("Writing data to %s ...\n"), ofname); - (*write_fun)(); - fclose(ifp); - if (ofp != stdout) fclose(ofp); -cleanup: - if (meta_data) free (meta_data); - if (ofname) free (ofname); - if (oprof) free (oprof); - if (image) free (image); - if (multi_out) { - if (++shot_select < is_raw) arg--; - else shot_select = 0; - } - } - return status; -} +/* RT: Delete from here */ +/*RT*/#undef SQR +/*RT*/#undef MAX +/*RT*/#undef MIN +/*RT*/#undef ABS +/*RT*/#undef LIM +/*RT*/#undef ULIM +/*RT*/#undef CLIP