diff options
Diffstat (limited to 'converter/other/cameratopam')
21 files changed, 5697 insertions, 0 deletions
diff --git a/converter/other/cameratopam/COPYRIGHT b/converter/other/cameratopam/COPYRIGHT new file mode 100644 index 00000000..efc77975 --- /dev/null +++ b/converter/other/cameratopam/COPYRIGHT @@ -0,0 +1,35 @@ +Cameratopam is derived from Dave Coffin's Dcraw program. + +Bryan Henderson derived it in April 2005. Bryan mainly just split it up +into manageable pieces and replaced the parts that generate Netpbm formats +and the command line parser. + +Dcraw and therefore Cameratopam is Copyright 1997-2005 by Dave Coffin, +dcoffin a cybercom o net, and Dave licenses it to the public freely, +except that the Foveon code (in foveon.c in Netpbm) is copyrighted by +someone else (I don't know who). + +Dave licenses his copyright to the public freely, with the words, "Any +code not declared GPL is free for all uses." Only the code in +foveon.c in Netpbm was declared GPL in Dave's work. + +The foveon code is licensed to the public by its unknown copyright owner +under GPL. + +The Netpbm maintainer distributes the entire Cameratopam package under +GPL. + +Dave's distribution to Bryan may be a copyright violation, but +strangely enough, Bryan's distribution to others is not. I.e. you DO +have license from every copyright owner to do GPL-compatible things +with this code. + +The problem with Dave's distribution is that he combined the Foveon +code and his code into a single work and distributed it with GPL terms +applying only to the Foveon code. The combined work may be a +derivative work of the Foveon code and therefore cannot be distributed +with the permission of the Foveon copyright owner, who gives that +permission only on the condition that the distributor license the +entire derivative work under GPL. Dave did not do that. He did not, +for example, prohibit Bryan from distributing Dave's part of the code +in object code only format. diff --git a/converter/other/cameratopam/Makefile b/converter/other/cameratopam/Makefile new file mode 100644 index 00000000..c50c9176 --- /dev/null +++ b/converter/other/cameratopam/Makefile @@ -0,0 +1,37 @@ +ifeq ($(SRCDIR)x,x) + SRCDIR = $(CURDIR)/../../.. + BUILDDIR = $(SRCDIR) +endif +SUBDIR = converter/other/cameratopam +VPATH=.:$(SRCDIR)/$(SUBDIR) + +ifneq ($(JPEGLIB),NONE) + ifneq ($(JPEGHDR_DIR)x,x) + INCLUDES += -I$(JPEGHDR_DIR) + CFLAGS += -DHAVE_JPEG + endif +endif + +include $(BUILDDIR)/Makefile.config + + +.PHONY: all +all: cameratopam + +OBJECTS = util.o identify.o cameratopam.o camera.o foveon.o decode.o \ + canon.o ljpeg.o dng.o + +MERGE_OBJECTS = + +BINARIES = cameratopam +MERGEBINARIES = +SCRIPTS = + +include $(SRCDIR)/Makefile.common + +cameratopam: $(OBJECTS) $(NETPBMLIB) $(LIBOPT) + $(LD) -o $@ $(LDFLAGS) \ + $(OBJECTS) $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR)) \ + $(MATHLIB) $(LDLIBS) \ + $(RPATH) $(LADD) + diff --git a/converter/other/cameratopam/bayer.h b/converter/other/cameratopam/bayer.h new file mode 100644 index 00000000..7ac06ec3 --- /dev/null +++ b/converter/other/cameratopam/bayer.h @@ -0,0 +1,43 @@ +/* + In order to inline this calculation, I make the risky + assumption that all filter patterns can be described + by a repeating pattern of eight rows and two columns + + Return values are either 0/1/2/3 = G/M/C/Y or 0/1/2/3 = R/G1/B/G2 + */ +#define FC(row,col) \ + (filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3) + +#define BAYER(row,col) \ + image[((row) >> shrink)*iwidth + ((col) >> shrink)][FC(row,col)] + +/* + PowerShot 600 PowerShot A50 PowerShot Pro70 Pro90 & G1 + 0xe1e4e1e4: 0x1b4e4b1e: 0x1e4b4e1b: 0xb4b4b4b4: + + 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 + 0 G M G M G M 0 C Y C Y C Y 0 Y C Y C Y C 0 G M G M G M + 1 C Y C Y C Y 1 M G M G M G 1 M G M G M G 1 Y C Y C Y C + 2 M G M G M G 2 Y C Y C Y C 2 C Y C Y C Y + 3 C Y C Y C Y 3 G M G M G M 3 G M G M G M + 4 C Y C Y C Y 4 Y C Y C Y C + PowerShot A5 5 G M G M G M 5 G M G M G M + 0x1e4e1e4e: 6 Y C Y C Y C 6 C Y C Y C Y + 7 M G M G M G 7 M G M G M G + 0 1 2 3 4 5 + 0 C Y C Y C Y + 1 G M G M G M + 2 C Y C Y C Y + 3 M G M G M G + + All RGB cameras use one of these Bayer grids: + + 0x16161616: 0x61616161: 0x49494949: 0x94949494: + + 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 + 0 B G B G B G 0 G R G R G R 0 G B G B G B 0 R G R G R G + 1 G R G R G R 1 B G B G B G 1 R G R G R G 1 G B G B G B + 2 B G B G B G 2 G R G R G R 2 G B G B G B 2 R G R G R G + 3 G R G R G R 3 B G B G B G 3 R G R G R G 3 G B G B G B + */ + diff --git a/converter/other/cameratopam/camera.c b/converter/other/cameratopam/camera.c new file mode 100644 index 00000000..98d6d37a --- /dev/null +++ b/converter/other/cameratopam/camera.c @@ -0,0 +1,1781 @@ +#define _BSD_SOURCE + /* Make sure strcasecmp is in string.h */ +#define _XOPEN_SOURCE + /* Make sure putenv is in stdlib.h */ + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <sys/types.h> + +#ifdef HAVE_JPEG +#include <jpeglib.h> +#endif + +#include "pm.h" +#include "mallocvar.h" + +#include "global_variables.h" +#include "util.h" +#include "decode.h" +#include "bayer.h" +#include "ljpeg.h" +#include "dng.h" + +#include "camera.h" + +#if HAVE_INT64 + typedef int64_t INT64; + static bool const have64BitArithmetic = true; +#else + /* We define INT64 to something that lets the code compile, but we + should not execute any INT64 code, because it will get the wrong + result. + */ + typedef int INT64; + static bool const have64BitArithmetic = false; +#endif + +#ifndef LONG_BIT +#define LONG_BIT (8 * sizeof (long)) +#endif + +#define FORC3 for (c=0; c < 3; c++) +#define FORC4 for (c=0; c < colors; c++) + +static void +merror (const void *ptr, const char *where) +{ + if (ptr == NULL) + pm_error ("Out of memory in %s", where); +} + + + + +static void +adobe_copy_pixel (int row, int col, unsigned short **rp, bool use_secondary) +{ + unsigned r=row, c=col; + + if (fuji_secondary && use_secondary) (*rp)++; + if (filters) { + if (fuji_width) { + r = row + fuji_width - 1 - (col >> 1); + c = row + ((col+1) >> 1); + } + if (r < height && c < width) + BAYER(r,c) = **rp < 0x1000 ? curve[**rp] : **rp; + *rp += 1 + fuji_secondary; + } else + for (c=0; c < tiff_samples; c++) { + image[row*width+col][c] = **rp < 0x1000 ? curve[**rp] : **rp; + (*rp)++; + } + if (fuji_secondary && use_secondary) (*rp)--; +} + +void +adobe_dng_load_raw_lj() +{ + int save, twide, trow=0, tcol=0, jrow, jcol; + struct jhead jh; + unsigned short *rp; + + while (1) { + save = ftell(ifp); + fseek (ifp, get4(ifp), SEEK_SET); + if (!ljpeg_start (ifp, &jh)) break; + if (trow >= raw_height) break; + if (jh.high > raw_height-trow) + jh.high = raw_height-trow; + twide = jh.wide; + if (filters) twide *= jh.clrs; + else colors = jh.clrs; + if (fuji_secondary) twide /= 2; + if (twide > raw_width-tcol) + twide = raw_width-tcol; + + for (jrow=0; jrow < jh.high; jrow++) { + ljpeg_row (&jh); + for (rp=jh.row, jcol=0; jcol < twide; jcol++) + adobe_copy_pixel (trow+jrow, tcol+jcol, &rp, use_secondary); + } + fseek (ifp, save+4, SEEK_SET); + if ((tcol += twide) >= raw_width) { + tcol = 0; + trow += jh.high; + } + free (jh.row); + } +} + + + +void +adobe_dng_load_raw_nc() +{ + unsigned short *pixel, *rp; + int row, col; + + pixel = calloc (raw_width * tiff_samples, sizeof *pixel); + merror (pixel, "adobe_dng_load_raw_nc()"); + for (row=0; row < raw_height; row++) { + read_shorts (ifp, pixel, raw_width * tiff_samples); + for (rp=pixel, col=0; col < raw_width; col++) + adobe_copy_pixel (row, col, &rp, use_secondary); + } + free (pixel); +} + + + +static int nikon_curve_offset; + +void +nikon_compressed_load_raw(void) +{ + static const unsigned char nikon_tree[] = { + 0,1,5,1,1,1,1,1,1,2,0,0,0,0,0,0, + 5,4,3,6,2,7,1,0,8,9,11,10,12 + }; + int csize, row, col, i, diff; + unsigned short vpred[4], hpred[2], *curve; + + init_decoder(); + make_decoder (nikon_tree, 0); + + fseek (ifp, nikon_curve_offset, SEEK_SET); + read_shorts (ifp, vpred, 4); + csize = get2(ifp); + curve = calloc (csize, sizeof *curve); + merror (curve, "nikon_compressed_load_raw()"); + read_shorts (ifp, curve, csize); + + fseek (ifp, data_offset, SEEK_SET); + getbits(ifp, -1); + + for (row=0; row < height; row++) + for (col=0; col < raw_width; col++) + { + diff = ljpeg_diff (first_decode); + if (col < 2) { + i = 2*(row & 1) + (col & 1); + vpred[i] += diff; + hpred[col] = vpred[i]; + } else + hpred[col & 1] += diff; + if ((unsigned) (col-left_margin) >= width) continue; + diff = hpred[col & 1]; + if (diff >= csize) diff = csize-1; + BAYER(row,col-left_margin) = curve[diff]; + } + maximum = curve[csize-1]; + free (curve); +} + +void +nikon_load_raw() +{ + int irow, row, col, i; + + getbits(ifp, -1); + for (irow=0; irow < height; irow++) { + row = irow; + if (model[0] == 'E') { + row = irow * 2 % height + irow / (height/2); + if (row == 1 && atoi(model+1) < 5000) { + fseek (ifp, 0, SEEK_END); + fseek (ifp, ftell(ifp)/2, SEEK_SET); + getbits(ifp, -1); + } + } + for (col=0; col < raw_width; col++) { + i = getbits(ifp, 12); + if ((unsigned) (col-left_margin) < width) + BAYER(row,col-left_margin) = i; + if (tiff_data_compression == 34713 && (col % 10) == 9) + getbits(ifp, 8); + } + } +} + +/* + Figure out if a NEF file is compressed. These fancy heuristics + are only needed for the D100, thanks to a bug in some cameras + that tags all images as "compressed". + */ +int +nikon_is_compressed() +{ + unsigned char test[256]; + int i; + + if (tiff_data_compression != 34713) + return 0; + if (strcmp(model,"D100")) + return 1; + fseek (ifp, data_offset, SEEK_SET); + fread (test, 1, 256, ifp); + for (i=15; i < 256; i+=16) + if (test[i]) return 1; + return 0; +} + +/* + Returns 1 for a Coolpix 990, 0 for a Coolpix 995. + */ +int +nikon_e990() +{ + int i, histo[256]; + const unsigned char often[] = { 0x00, 0x55, 0xaa, 0xff }; + + memset (histo, 0, sizeof histo); + fseek (ifp, 2064*1540*3/4, SEEK_SET); + for (i=0; i < 2000; i++) + histo[fgetc(ifp)]++; + for (i=0; i < 4; i++) + if (histo[often[i]] > 400) + return 1; + return 0; +} + +/* + Returns 1 for a Coolpix 2100, 0 for anything else. + */ +int +nikon_e2100() +{ + unsigned char t[12]; + int i; + + fseek (ifp, 0, SEEK_SET); + for (i=0; i < 1024; i++) { + fread (t, 1, 12, ifp); + if (((t[2] & t[4] & t[7] & t[9]) >> 4 + & t[1] & t[6] & t[8] & t[11] & 3) != 3) + return 0; + } + return 1; +} + +/* + Separates a Pentax Optio 33WR from a Nikon E3700. + */ +int +pentax_optio33() +{ + int i, sum[] = { 0, 0 }; + unsigned char tail[952]; + + fseek (ifp, -sizeof tail, SEEK_END); + fread (tail, 1, sizeof tail, ifp); + for (i=0; i < sizeof tail; i++) + sum[(i>>2) & 1] += tail[i]; + return sum[0] < sum[1]*4; +} + +/* + Separates a Minolta DiMAGE Z2 from a Nikon E4300. + */ +int +minolta_z2() +{ + int i; + char tail[424]; + + fseek (ifp, -sizeof tail, SEEK_END); + fread (tail, 1, sizeof tail, ifp); + for (i=0; i < sizeof tail; i++) + if (tail[i]) return 1; + return 0; +} + +void +nikon_e2100_load_raw() +{ + unsigned char data[3432], *dp; + unsigned short pixel[2288], *pix; + int row, col; + + for (row=0; row <= height; row+=2) { + if (row == height) { + fseek (ifp, ((width==1616) << 13) - (-ftell(ifp) & -2048), SEEK_SET); + row = 1; + } + fread (data, 1, width*3/2, ifp); + for (dp=data, pix=pixel; pix < pixel+width; dp+=12, pix+=8) { + pix[0] = (dp[2] >> 4) + (dp[ 3] << 4); + pix[1] = (dp[2] << 8) + dp[ 1]; + pix[2] = (dp[7] >> 4) + (dp[ 0] << 4); + pix[3] = (dp[7] << 8) + dp[ 6]; + pix[4] = (dp[4] >> 4) + (dp[ 5] << 4); + pix[5] = (dp[4] << 8) + dp[11]; + pix[6] = (dp[9] >> 4) + (dp[10] << 4); + pix[7] = (dp[9] << 8) + dp[ 8]; + } + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col] & 0xfff); + } +} + +void +nikon_e950_load_raw() +{ + int irow, row, col; + + getbits(ifp, -1); + for (irow=0; irow < height; irow++) { + row = irow * 2 % height; + for (col=0; col < width; col++) + BAYER(row,col) = getbits(ifp, 10); + for (col=28; col--; ) + getbits(ifp, 8); + } + maximum = 0x3dd; +} + +/* + The Fuji Super CCD is just a Bayer grid rotated 45 degrees. + */ +void +fuji_s2_load_raw() +{ + unsigned short pixel[2944]; + int row, col, r, c; + + fseek (ifp, (2944*24+32)*2, SEEK_CUR); + for (row=0; row < 2144; row++) { + read_shorts(ifp, pixel, 2944); + for (col=0; col < 2880; col++) { + r = row + ((col+1) >> 1); + c = 2143 - row + (col >> 1); + BAYER(r,c) = pixel[col]; + } + } +} + +void +fuji_s3_load_raw() +{ + unsigned short pixel[4352]; + int row, col, r, c; + + fseek (ifp, (4352*2+32)*2, SEEK_CUR); + for (row=0; row < 1440; row++) { + read_shorts(ifp, pixel, 4352); + for (col=0; col < 4288; col++) { + r = 2143 + row - (col >> 1); + c = row + ((col+1) >> 1); + BAYER(r,c) = pixel[col]; + } + } +} + +static void fuji_common_load_raw (int ncol, int icol, int nrow) +{ + unsigned short pixel[2048]; + int row, col, r, c; + + for (row=0; row < nrow; row++) { + read_shorts(ifp, pixel, ncol); + for (col=0; col <= icol; col++) { + r = icol - col + (row >> 1); + c = col + ((row+1) >> 1); + BAYER(r,c) = pixel[col]; + } + } +} + +void +fuji_s5000_load_raw() +{ + fseek (ifp, (1472*4+24)*2, SEEK_CUR); + fuji_common_load_raw (1472, 1423, 2152); +} + +void +fuji_s7000_load_raw() +{ + fuji_common_load_raw (2048, 2047, 3080); +} + +/* + The Fuji Super CCD SR has two photodiodes for each pixel. + The secondary has about 1/16 the sensitivity of the primary, + but this ratio may vary. + */ +void +fuji_f700_load_raw() +{ + unsigned short pixel[2944]; + int row, col, r, c, val; + + for (row=0; row < 2168; row++) { + read_shorts(ifp, pixel, 2944); + for (col=0; col < 1440; col++) { + r = 1439 - col + (row >> 1); + c = col + ((row+1) >> 1); + val = pixel[col+16 + use_secondary*1472]; + BAYER(r,c) = val; + } + } +} + +void +rollei_load_raw() +{ + unsigned char pixel[10]; + unsigned iten=0, isix, i, buffer=0, row, col, todo[16]; + + isix = raw_width * raw_height * 5 / 8; + while (fread (pixel, 1, 10, ifp) == 10) { + for (i=0; i < 10; i+=2) { + todo[i] = iten++; + todo[i+1] = pixel[i] << 8 | pixel[i+1]; + buffer = pixel[i] >> 2 | buffer << 6; + } + for ( ; i < 16; i+=2) { + todo[i] = isix++; + todo[i+1] = buffer >> (14-i)*5; + } + for (i=0; i < 16; i+=2) { + row = todo[i] / raw_width - top_margin; + col = todo[i] % raw_width - left_margin; + if (row < height && col < width) + BAYER(row,col) = (todo[i+1] & 0x3ff); + } + } + maximum = 0x3ff; +} + +void +phase_one_load_raw() +{ + int row, col, a, b; + unsigned short *pixel, akey, bkey; + + fseek (ifp, 8, SEEK_CUR); + fseek (ifp, get4(ifp) + 296, SEEK_CUR); + akey = get2(ifp); + bkey = get2(ifp); + fseek (ifp, data_offset + 12 + top_margin*raw_width*2, SEEK_SET); + pixel = calloc (raw_width, sizeof *pixel); + merror (pixel, "phase_one_load_raw()"); + for (row=0; row < height; row++) { + read_shorts(ifp, pixel, raw_width); + for (col=0; col < raw_width; col+=2) { + a = pixel[col+0] ^ akey; + b = pixel[col+1] ^ bkey; + pixel[col+0] = (b & 0xaaaa) | (a & 0x5555); + pixel[col+1] = (a & 0xaaaa) | (b & 0x5555); + } + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col+left_margin]; + } + free (pixel); +} + +void +ixpress_load_raw() +{ + unsigned short pixel[4090]; + int row, col; + + order = 0x4949; + fseek (ifp, 304 + 6*2*4090, SEEK_SET); + for (row=height; --row >= 0; ) { + read_shorts(ifp, pixel, 4090); + for (col=0; col < width; col++) + BAYER(row,col) = pixel[width-1-col]; + } +} + +void +leaf_load_raw() +{ + unsigned short *pixel; + int r, c, row, col; + + pixel = calloc (raw_width, sizeof *pixel); + merror (pixel, "leaf_load_raw()"); + for (r=0; r < height-32; r+=32) + FORC3 for (row=r; row < r+32; row++) { + read_shorts(ifp, pixel, raw_width); + for (col=0; col < width; col++) + image[row*width+col][c] = pixel[col]; + } + free (pixel); +} + +/* + For this function only, raw_width is in bytes, not pixels! + */ +void +packed_12_load_raw() +{ + int row, col; + + getbits(ifp, -1); + for (row=0; row < height; row++) { + for (col=0; col < width; col++) + BAYER(row,col) = getbits(ifp, 12); + for (col = width*3/2; col < raw_width; col++) + getbits(ifp, 8); + } +} + +void +unpacked_load_raw() +{ + unsigned short *pixel; + int row, col; + + pixel = calloc (raw_width, sizeof *pixel); + merror (pixel, "unpacked_load_raw()"); + for (row=0; row < height; row++) { + read_shorts(ifp, pixel, raw_width); + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col]; + } + free (pixel); +} + +void +olympus_e300_load_raw() +{ + unsigned char *data, *dp; + unsigned short *pixel, *pix; + int dwide, row, col; + + dwide = raw_width * 16 / 10; + data = malloc (dwide + raw_width*2); + merror (data, "olympus_e300_load_raw()"); + pixel = (unsigned short *) (data + dwide); + for (row=0; row < height; row++) { + fread (data, 1, dwide, ifp); + for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=3, pix+=2) { + if (((dp-data) & 15) == 15) dp++; + pix[0] = dp[1] << 8 | dp[0]; + pix[1] = dp[2] << 4 | dp[1] >> 4; + } + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col] & 0xfff); + } + free (data); +} + +void +olympus_cseries_load_raw() +{ + int irow, row, col; + + for (irow=0; irow < height; irow++) { + row = irow * 2 % height + irow / (height/2); + if (row < 2) { + fseek (ifp, data_offset - row*(-width*height*3/4 & -2048), SEEK_SET); + getbits(ifp, -1); + } + for (col=0; col < width; col++) + BAYER(row,col) = getbits(ifp, 12); + } +} + +void +eight_bit_load_raw() +{ + unsigned char *pixel; + int row, col; + + pixel = calloc (raw_width, sizeof *pixel); + merror (pixel, "eight_bit_load_raw()"); + for (row=0; row < height; row++) { + fread (pixel, 1, raw_width, ifp); + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col]; + } + free (pixel); + maximum = 0xff; +} + +void +casio_qv5700_load_raw() +{ + unsigned char data[3232], *dp; + unsigned short pixel[2576], *pix; + int row, col; + + for (row=0; row < height; row++) { + fread (data, 1, 3232, ifp); + for (dp=data, pix=pixel; dp < data+3220; dp+=5, pix+=4) { + pix[0] = (dp[0] << 2) + (dp[1] >> 6); + pix[1] = (dp[1] << 4) + (dp[2] >> 4); + pix[2] = (dp[2] << 6) + (dp[3] >> 2); + pix[3] = (dp[3] << 8) + (dp[4] ); + } + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col] & 0x3ff); + } + maximum = 0x3fc; +} + +void +nucore_load_raw() +{ + unsigned short *pixel; + int irow, row, col; + + pixel = calloc (width, 2); + merror (pixel, "nucore_load_raw()"); + for (irow=0; irow < height; irow++) { + read_shorts(ifp, pixel, width); + row = irow/2 + height/2 * (irow & 1); + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col]; + } + free (pixel); +} + +static int radc_token (int tree) +{ + int t; + static struct decode *dstart[18], *dindex; + static const int *s, source[] = { + 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, + 2,0, 2,1, 2,3, 3,2, 4,4, 5,6, 6,7, 7,5, 7,8, + 2,1, 2,4, 3,0, 3,2, 3,3, 4,7, 5,5, 6,6, 6,8, + 2,3, 3,1, 3,2, 3,4, 3,5, 3,6, 4,7, 5,0, 5,8, + 2,3, 2,6, 3,0, 3,1, 4,4, 4,5, 4,7, 5,2, 5,8, + 2,4, 2,7, 3,3, 3,6, 4,1, 4,2, 4,5, 5,0, 5,8, + 2,6, 3,1, 3,3, 3,5, 3,7, 3,8, 4,0, 5,2, 5,4, + 2,0, 2,1, 3,2, 3,3, 4,4, 4,5, 5,6, 5,7, 4,8, + 1,0, 2,2, 2,-2, + 1,-3, 1,3, + 2,-17, 2,-5, 2,5, 2,17, + 2,-7, 2,2, 2,9, 2,18, + 2,-18, 2,-9, 2,-2, 2,7, + 2,-28, 2,28, 3,-49, 3,-9, 3,9, 4,49, 5,-79, 5,79, + 2,-1, 2,13, 2,26, 3,39, 4,-16, 5,55, 6,-37, 6,76, + 2,-26, 2,-13, 2,1, 3,-39, 4,16, 5,-55, 6,-76, 6,37 + }; + + if (free_decode == first_decode) + for (s=source, t=0; t < 18; t++) { + dstart[t] = free_decode; + s = make_decoder_int (s, 0); + } + if (tree == 18) { + if (model[2] == '4') + return (getbits(ifp, 5) << 3) + 4; /* DC40 */ + else + return (getbits(ifp, 6) << 2) + 2; /* DC50 */ + } + for (dindex = dstart[tree]; dindex->branch[0]; ) + dindex = dindex->branch[getbits(ifp, 1)]; + return dindex->leaf; +} + +#define FORYX for (y=1; y < 3; y++) for (x=col+1; x >= col; x--) + +#define PREDICTOR (c ? (buf[c][y-1][x] + buf[c][y][x+1]) / 2 \ +: (buf[c][y-1][x+1] + 2*buf[c][y-1][x] + buf[c][y][x+1]) / 4) + +void +kodak_radc_load_raw() +{ + int row, col, tree, nreps, rep, step, i, c, s, r, x, y, val; + short last[3] = { 16,16,16 }, mul[3], buf[3][3][386]; + + init_decoder(); + getbits(ifp, -1); + for (i=0; i < sizeof(buf)/sizeof(short); i++) + buf[0][0][i] = 2048; + for (row=0; row < height; row+=4) { + for (i=0; i < 3; i++) + mul[i] = getbits(ifp, 6); + FORC3 { + val = ((0x1000000/last[c] + 0x7ff) >> 12) * mul[c]; + s = val > 65564 ? 10:12; + x = ~(-1 << (s-1)); + val <<= 12-s; + for (i=0; i < sizeof(buf[0])/sizeof(short); i++) + buf[c][0][i] = (buf[c][0][i] * val + x) >> s; + last[c] = mul[c]; + for (r=0; r <= !c; r++) { + buf[c][1][width/2] = buf[c][2][width/2] = mul[c] << 7; + for (tree=1, col=width/2; col > 0; ) { + if ((tree = radc_token(tree))) { + col -= 2; + if (tree == 8) + FORYX buf[c][y][x] = radc_token(tree+10) * mul[c]; + else + FORYX buf[c][y][x] = radc_token(tree+10) * 16 + PREDICTOR; + } else + do { + nreps = (col > 2) ? radc_token(9) + 1 : 1; + for (rep=0; rep < 8 && rep < nreps && col > 0; rep++) { + col -= 2; + FORYX buf[c][y][x] = PREDICTOR; + if (rep & 1) { + step = radc_token(10) << 4; + FORYX buf[c][y][x] += step; + } + } + } while (nreps == 9); + } + for (y=0; y < 2; y++) + for (x=0; x < width/2; x++) { + val = (buf[c][y+1][x] << 4) / mul[c]; + if (val < 0) val = 0; + if (c) + BAYER(row+y*2+c-1,x*2+2-c) = val; + else + BAYER(row+r*2+y,x*2+y) = val; + } + memcpy (buf[c][0]+!c, buf[c][2], sizeof buf[c][0]-2*!c); + } + } + for (y=row; y < row+4; y++) + for (x=0; x < width; x++) + if ((x+y) & 1) { + val = (BAYER(y,x)-2048)*2 + (BAYER(y,x-1)+BAYER(y,x+1))/2; + if (val < 0) val = 0; + BAYER(y,x) = val; + } + } + maximum = 0x1fff; /* wild guess */ +} + +#undef FORYX +#undef PREDICTOR + +#ifndef HAVE_JPEG +void kodak_jpeg_load_raw() {} +#else + +static boolean +fill_input_buffer (j_decompress_ptr cinfo) +{ + static char jpeg_buffer[4096]; + size_t nbytes; + + nbytes = fread (jpeg_buffer, 1, 4096, ifp); + swab (jpeg_buffer, jpeg_buffer, nbytes); + cinfo->src->next_input_byte = jpeg_buffer; + cinfo->src->bytes_in_buffer = nbytes; + return TRUE; +} + +void +kodak_jpeg_load_raw() +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPARRAY buf; + JSAMPLE (*pixel)[3]; + int row, col; + + cinfo.err = jpeg_std_error (&jerr); + jpeg_create_decompress (&cinfo); + jpeg_stdio_src (&cinfo, ifp); + cinfo.src->fill_input_buffer = fill_input_buffer; + jpeg_read_header (&cinfo, TRUE); + jpeg_start_decompress (&cinfo); + if ((cinfo.output_width != width ) || + (cinfo.output_height*2 != height ) || + (cinfo.output_components != 3 )) { + pm_error ("incorrect JPEG dimensions"); + } + buf = (*cinfo.mem->alloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, width*3, 1); + + while (cinfo.output_scanline < cinfo.output_height) { + row = cinfo.output_scanline * 2; + jpeg_read_scanlines (&cinfo, buf, 1); + pixel = (void *) buf[0]; + for (col=0; col < width; col+=2) { + BAYER(row+0,col+0) = pixel[col+0][1] << 1; + BAYER(row+1,col+1) = pixel[col+1][1] << 1; + BAYER(row+0,col+1) = pixel[col][0] + pixel[col+1][0]; + BAYER(row+1,col+0) = pixel[col][2] + pixel[col+1][2]; + } + } + jpeg_finish_decompress (&cinfo); + jpeg_destroy_decompress (&cinfo); + maximum = 0xff << 1; +} + +#endif + +void +kodak_dc120_load_raw() +{ + static const int mul[4] = { 162, 192, 187, 92 }; + static const int add[4] = { 0, 636, 424, 212 }; + unsigned char pixel[848]; + int row, shift, col; + + for (row=0; row < height; row++) { + fread (pixel, 848, 1, ifp); + shift = row * mul[row & 3] + add[row & 3]; + for (col=0; col < width; col++) + BAYER(row,col) = (unsigned short) pixel[(col + shift) % 848]; + } + maximum = 0xff; +} + +void +kodak_dc20_coeff (float const juice) +{ + static const float my_coeff[3][4] = + { { 2.25, 0.75, -1.75, -0.25 }, + { -0.25, 0.75, 0.75, -0.25 }, + { -0.25, -1.75, 0.75, 2.25 } }; + static const float flat[3][4] = + { { 1, 0, 0, 0 }, + { 0, 0.5, 0.5, 0 }, + { 0, 0, 0, 1 } }; + int r, g; + + for (r=0; r < 3; r++) + for (g=0; g < 4; g++) + coeff[r][g] = my_coeff[r][g] * juice + flat[r][g] * (1-juice); + use_coeff = 1; +} + +void +kodak_easy_load_raw() +{ + unsigned char *pixel; + unsigned row, col, icol; + + if (raw_width > width) + black = 0; + pixel = calloc (raw_width, sizeof *pixel); + merror (pixel, "kodak_easy_load_raw()"); + for (row=0; row < height; row++) { + fread (pixel, 1, raw_width, ifp); + for (col=0; col < raw_width; col++) { + icol = col - left_margin; + if (icol < width) + BAYER(row,icol) = (unsigned short) curve[pixel[col]]; + else + black += curve[pixel[col]]; + } + } + free (pixel); + if (raw_width > width) + black /= (raw_width - width) * height; + if (!strncmp(model,"DC2",3)) + black = 0; + maximum = curve[0xff]; +} + +void +kodak_compressed_load_raw() +{ + unsigned char c, blen[256]; + unsigned short raw[6]; + unsigned row, col, len, save, i, israw=0, bits=0, pred[2]; + INT64 bitbuf=0; + int diff; + + assert(have64BitArithmetic); + + for (row=0; row < height; row++) + for (col=0; col < width; col++) + { + if ((col & 255) == 0) { /* Get the bit-lengths of the */ + len = width - col; /* next 256 pixel values */ + if (len > 256) len = 256; + save = ftell(ifp); + for (israw=i=0; i < len; i+=2) { + c = fgetc(ifp); + if ((blen[i+0] = c & 15) > 12 || + (blen[i+1] = c >> 4) > 12 ) + israw = 1; + } + bitbuf = bits = pred[0] = pred[1] = 0; + if (len % 8 == 4) { + bitbuf = fgetc(ifp) << 8; + bitbuf += fgetc(ifp); + bits = 16; + } + if (israw) + fseek (ifp, save, SEEK_SET); + } + if (israw) { /* If the data is not compressed */ + switch (col & 7) { + case 0: + read_shorts(ifp, raw, 6); + diff = raw[0] >> 12 << 8 | raw[2] >> 12 << 4 | raw[4] >> 12; + break; + case 1: + diff = raw[1] >> 12 << 8 | raw[3] >> 12 << 4 | raw[5] >> 12; + break; + default: + diff = raw[(col & 7) - 2] & 0xfff; + } + } else { /* If the data is compressed */ + len = blen[col & 255]; /* Number of bits for this pixel */ + if (bits < len) { /* Got enough bits in the buffer? */ + for (i=0; i < 32; i+=8) + bitbuf += (INT64) fgetc(ifp) << (bits+(i^8)); + bits += 32; + } + diff = bitbuf & (0xffff >> (16-len)); /* Pull bits from buffer */ + bitbuf >>= len; + bits -= len; + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + pred[col & 1] += diff; + diff = pred[col & 1]; + } + BAYER(row,col) = curve[diff]; + } +} + +void +kodak_yuv_load_raw() +{ + unsigned char c, blen[384]; + unsigned row, col, len, bits=0; + INT64 bitbuf=0; + int i, li=0, si, diff, six[6], y[4], cb=0, cr=0, rgb[3]; + unsigned short *ip; + + assert(have64BitArithmetic); + + for (row=0; row < height; row+=2) + for (col=0; col < width; col+=2) { + if ((col & 127) == 0) { + len = (width - col + 1) * 3 & -4; + if (len > 384) len = 384; + for (i=0; i < len; ) { + c = fgetc(ifp); + blen[i++] = c & 15; + blen[i++] = c >> 4; + } + li = bitbuf = bits = y[1] = y[3] = cb = cr = 0; + if (len % 8 == 4) { + bitbuf = fgetc(ifp) << 8; + bitbuf += fgetc(ifp); + bits = 16; + } + } + for (si=0; si < 6; si++) { + len = blen[li++]; + if (bits < len) { + for (i=0; i < 32; i+=8) + bitbuf += (INT64) fgetc(ifp) << (bits+(i^8)); + bits += 32; + } + diff = bitbuf & (0xffff >> (16-len)); + bitbuf >>= len; + bits -= len; + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + six[si] = diff; + } + y[0] = six[0] + y[1]; + y[1] = six[1] + y[0]; + y[2] = six[2] + y[3]; + y[3] = six[3] + y[2]; + cb += six[4]; + cr += six[5]; + for (i=0; i < 4; i++) { + ip = image[(row+(i >> 1))*width + col+(i & 1)]; + rgb[0] = y[i] + cr; + rgb[1] = y[i]; + rgb[2] = y[i] + cb; + FORC3 if (rgb[c] > 0) ip[c] = curve[rgb[c]]; + } + } + maximum = 0xe74; +} + +static void sony_decrypt (unsigned *data, int len, int start, int key) +{ + static uint32_t pad[128]; + unsigned int p; + + if (start) { + for (p=0; p < 4; p++) + pad[p] = key = key * 48828125 + 1; + pad[3] = pad[3] << 1 | (pad[0]^pad[2]) >> 31; + for (p=4; p < 127; p++) + pad[p] = (pad[p-4]^pad[p-2]) << 1 | (pad[p-3]^pad[p-1]) >> 31; + + /* Convert to big-endian */ + + for (p=0; p < 127; p++) { + union { + unsigned char bytes[4]; + uint32_t word; + } u; + + u.bytes[0] = pad[p] >> 24; + u.bytes[1] = pad[p] >> 16; + u.bytes[2] = pad[p] >> 8; + u.bytes[3] = pad[p] >> 0; + + pad[p] = u.word; + } + } + while (len--) + *data++ ^= pad[p++ & 0x7f] = pad[(p+1) & 0x7f] ^ pad[(p+65) & 0x7f]; +} + +void +sony_load_raw() +{ + unsigned char head[40]; + struct pixel { + unsigned char bytes[2]; + }; + struct pixel * pixelrow; + unsigned i, key, row, col; + + fseek (ifp, 200896, SEEK_SET); + fseek (ifp, (unsigned) fgetc(ifp)*4 - 1, SEEK_CUR); + order = 0x4d4d; + key = get4(ifp); + fseek (ifp, 164600, SEEK_SET); + fread (head, 1, 40, ifp); + sony_decrypt ((void *) head, 10, 1, key); + for (i=26; i-- > 22; ) + key = key << 8 | head[i]; + fseek (ifp, data_offset, SEEK_SET); + MALLOCARRAY(pixelrow, raw_width); + merror (pixelrow, "sony_load_raw()"); + for (row=0; row < height; row++) { + fread (pixelrow, 2, raw_width, ifp); + sony_decrypt ((void *) pixelrow, raw_width/2, !row, key); + for (col=9; col < left_margin; col++) + black += pixelrow[col].bytes[0] * 256 + pixelrow[col].bytes[1]; + for (col=0; col < width; col++) + BAYER(row,col) = + pixelrow[col+left_margin].bytes[0] * 256 + + pixelrow[col+left_margin].bytes[1]; + } + free (pixelrow); + if (left_margin > 9) + black /= (left_margin-9) * height; + maximum = 0x3ff0; +} + +void +parse_minolta(FILE * const ifp) +{ + int save, tag, len, offset, high=0, wide=0; + + fseek (ifp, 4, SEEK_SET); + offset = get4(ifp) + 8; + while ((save=ftell(ifp)) < offset) { + tag = get4(ifp); + len = get4(ifp); + switch (tag) { + case 0x505244: /* PRD */ + fseek (ifp, 8, SEEK_CUR); + high = get2(ifp); + wide = get2(ifp); + break; + case 0x574247: /* WBG */ + get4(ifp); + camera_red = get2(ifp); + camera_red /= get2(ifp); + camera_blue = get2(ifp); + camera_blue = get2(ifp) / camera_blue; + break; + case 0x545457: /* TTW */ + parse_tiff(ifp, ftell(ifp)); + } + fseek (ifp, save+len+8, SEEK_SET); + } + raw_height = high; + raw_width = wide; + data_offset = offset; +} + +/* + CIFF block 0x1030 contains an 8x8 white sample. + Load this into white[][] for use in scale_colors(). + */ +static void ciff_block_1030() +{ + static const unsigned short key[] = { 0x410, 0x45f3 }; + int i, bpp, row, col, vbits=0; + unsigned long bitbuf=0; + + get2(ifp); + if (get4(ifp) != 0x80008) return; + if (get4(ifp) == 0) return; + bpp = get2(ifp); + if (bpp != 10 && bpp != 12) return; + for (i=row=0; row < 8; row++) + for (col=0; col < 8; col++) { + if (vbits < bpp) { + bitbuf = bitbuf << 16 | (get2(ifp) ^ key[i++ & 1]); + vbits += 16; + } + white[row][col] = + bitbuf << (LONG_BIT - vbits) >> (LONG_BIT - bpp); + vbits -= bpp; + } +} + +/* + Parse a CIFF file, better known as Canon CRW format. + */ +void +parse_ciff(FILE * const ifp, + int const offset, + int const length) +{ + int tboff, nrecs, i, type, len, roff, aoff, save, wbi=-1; + static const int remap[] = { 1,2,3,4,5,1 }; + static const int remap_10d[] = { 0,1,3,4,5,6,0,0,2,8 }; + static const int remap_s70[] = { 0,1,2,9,4,3,6,7,8,9,10,0,0,0,7,0,0,8 }; + unsigned short key[] = { 0x410, 0x45f3 }; + + if (strcmp(model,"Canon PowerShot G6") && + strcmp(model,"Canon PowerShot S70") && + strcmp(model,"Canon PowerShot Pro1")) + key[0] = key[1] = 0; + fseek (ifp, offset+length-4, SEEK_SET); + tboff = get4(ifp) + offset; + fseek (ifp, tboff, SEEK_SET); + nrecs = get2(ifp); + for (i = 0; i < nrecs; i++) { + type = get2(ifp); + len = get4(ifp); + roff = get4(ifp); + aoff = offset + roff; + save = ftell(ifp); + if (type == 0x080a) { /* Get the camera make and model */ + fseek (ifp, aoff, SEEK_SET); + fread (make, 64, 1, ifp); + fseek (ifp, aoff+strlen(make)+1, SEEK_SET); + fread (model, 64, 1, ifp); + } + if (type == 0x102a) { /* Find the White Balance index */ + fseek (ifp, aoff+14, SEEK_SET); /* 0=auto, 1=daylight, 2=cloudy ... */ + wbi = get2(ifp); + if (((!strcmp(model,"Canon EOS DIGITAL REBEL") || + !strcmp(model,"Canon EOS 300D DIGITAL"))) && wbi == 6) + wbi++; + } + if (type == 0x102c) { /* Get white balance (G2) */ + if (!strcmp(model,"Canon PowerShot G1") || + !strcmp(model,"Canon PowerShot Pro90 IS")) { + fseek (ifp, aoff+120, SEEK_SET); + white[0][1] = get2(ifp); + white[0][0] = get2(ifp); + white[1][0] = get2(ifp); + white[1][1] = get2(ifp); + } else { + fseek (ifp, aoff+100, SEEK_SET); + goto common; + } + } + if (type == 0x0032) { /* Get white balance (D30 & G3) */ + if (!strcmp(model,"Canon EOS D30")) { + fseek (ifp, aoff+72, SEEK_SET); +common: + camera_red = get2(ifp) ^ key[0]; + camera_red =(get2(ifp) ^ key[1]) / camera_red; + camera_blue = get2(ifp) ^ key[0]; + camera_blue /= get2(ifp) ^ key[1]; + } else if (!strcmp(model,"Canon PowerShot G6") || + !strcmp(model,"Canon PowerShot S70")) { + fseek (ifp, aoff+96 + remap_s70[wbi]*8, SEEK_SET); + goto common; + } else if (!strcmp(model,"Canon PowerShot Pro1")) { + fseek (ifp, aoff+96 + wbi*8, SEEK_SET); + goto common; + } else { + fseek (ifp, aoff+80 + (wbi < 6 ? remap[wbi]*8 : 0), SEEK_SET); + if (!camera_red) + goto common; + } + } + if (type == 0x10a9) { /* Get white balance (D60) */ + if (!strcmp(model,"Canon EOS 10D")) + wbi = remap_10d[wbi]; + fseek (ifp, aoff+2 + wbi*8, SEEK_SET); + camera_red = get2(ifp); + camera_red /= get2(ifp); + camera_blue = get2(ifp); + camera_blue = get2(ifp) / camera_blue; + } + if (type == 0x1030 && (wbi == 6 || wbi == 15)) { + fseek (ifp, aoff, SEEK_SET); /* Get white sample */ + ciff_block_1030(); + } + if (type == 0x1031) { /* Get the raw width and height */ + fseek (ifp, aoff+2, SEEK_SET); + raw_width = get2(ifp); + raw_height = get2(ifp); + } + if (type == 0x180e) { /* Get the timestamp */ + fseek (ifp, aoff, SEEK_SET); + timestamp = get4(ifp); + } + if (type == 0x580e) + timestamp = len; + if (type == 0x1810) { /* Get the rotation */ + fseek (ifp, aoff+12, SEEK_SET); + flip = get4(ifp); + } + if (type == 0x1835) { /* Get the decoder table */ + fseek (ifp, aoff, SEEK_SET); + crw_init_tables (get4(ifp)); + } + if (type >> 8 == 0x28 || type >> 8 == 0x30) /* Get sub-tables */ + parse_ciff(ifp, aoff, len); + fseek (ifp, save, SEEK_SET); + } + if (wbi == 0 && !strcmp(model,"Canon EOS D30")) + camera_red = -1; /* Use my auto WB for this photo */ +} + +void +parse_rollei(FILE * const ifp) +{ + char line[128], *val; + int tx=0, ty=0; + struct tm t; + time_t ts; + + fseek (ifp, 0, SEEK_SET); + do { + fgets (line, 128, ifp); + if ((val = strchr(line,'='))) + *val++ = 0; + else + val = line + strlen(line); + if (!strcmp(line,"DAT")) + sscanf (val, "%d.%d.%d", &t.tm_mday, &t.tm_mon, &t.tm_year); + if (!strcmp(line,"TIM")) + sscanf (val, "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec); + if (!strcmp(line,"HDR")) + data_offset = atoi(val); + if (!strcmp(line,"X ")) + raw_width = atoi(val); + if (!strcmp(line,"Y ")) + raw_height = atoi(val); + if (!strcmp(line,"TX ")) + tx = atoi(val); + if (!strcmp(line,"TY ")) + ty = atoi(val); + } while (strncmp(line,"EOHD",4)); + t.tm_year -= 1900; + t.tm_mon -= 1; + putenv((char*)"TZ="); + if ((ts = mktime(&t)) > 0) + timestamp = ts; + data_offset += tx * ty * 2; + strcpy (make, "Rollei"); + strcpy (model,"d530flex"); +} + + + +void +parse_mos(FILE * const ifp, + int const offset) +{ + char data[40]; + int skip, from, i, neut[4]; + + fseek (ifp, offset, SEEK_SET); + while (1) { + fread (data, 1, 8, ifp); + if (strcmp(data,"PKTS")) break; + fread (data, 1, 40, ifp); + skip = get4(ifp); + from = ftell(ifp); +#ifdef USE_LCMS + if (!strcmp(data,"icc_camera_profile")) { + profile_length = skip; + profile_offset = from; + } +#endif + if (!strcmp(data,"NeutObj_neutrals")) { + for (i=0; i < 4; i++) + fscanf (ifp, "%d", neut+i); + camera_red = (float) neut[2] / neut[1]; + camera_blue = (float) neut[2] / neut[3]; + } + parse_mos(ifp, from); + fseek (ifp, skip+from, SEEK_SET); + } +} + +void +nikon_e950_coeff() +{ + int r, g; + static const float my_coeff[3][4] = + { { -1.936280, 1.800443, -1.448486, 2.584324 }, + { 1.405365, -0.524955, -0.289090, 0.408680 }, + { -1.204965, 1.082304, 2.941367, -1.818705 } }; + + for (r=0; r < 3; r++) + for (g=0; g < 4; g++) + coeff[r][g] = my_coeff[r][g]; + use_coeff = 1; +} + + + +static double getrat() +{ + double num = get4(ifp); + return num / get4(ifp); +} + + + +static void +parse_makernote(FILE * const ifp) +{ + unsigned base=0, offset=0, entries, tag, type, len, save; + static const int size[] = { 1,1,1,2,4,8,1,1,2,4,8,4,8 }; + short sorder; + char buf[10]; +/* + The MakerNote might have its own TIFF header (possibly with + its own byte-order!), or it might just be a table. + */ + sorder = order; + fread (buf, 1, 10, ifp); + if (!strncmp (buf,"KC" ,2) || /* these aren't TIFF format */ + !strncmp (buf,"MLY",3)) return; + if (!strcmp (buf,"Nikon")) { + base = ftell(ifp); + order = get2(ifp); + if (get2(ifp) != 42) goto quit; + offset = get4(ifp); + fseek (ifp, offset-8, SEEK_CUR); + } else if (!strncmp (buf,"FUJIFILM",8) || + !strcmp (buf,"Panasonic")) { + order = 0x4949; + fseek (ifp, 2, SEEK_CUR); + } else if (!strcmp (buf,"OLYMP") || + !strcmp (buf,"LEICA") || + !strcmp (buf,"EPSON")) + fseek (ifp, -2, SEEK_CUR); + else if (!strcmp (buf,"AOC") || + !strcmp (buf,"QVC")) + fseek (ifp, -4, SEEK_CUR); + else fseek (ifp, -10, SEEK_CUR); + + entries = get2(ifp); + while (entries--) { + tag = get2(ifp); + type = get2(ifp); + len = get4(ifp); + save = ftell(ifp); + if (len * size[type < 13 ? type:0] > 4) + fseek (ifp, get4(ifp)+base, SEEK_SET); + + if (tag == 0xc && len == 4) { + camera_red = getrat(); + camera_blue = getrat(); + } + if (tag == 0x14 && len == 2560 && type == 7) { + fseek (ifp, 1248, SEEK_CUR); + goto get2_256; + } + if (strstr(make,"PENTAX")) { + if (tag == 0x1b) tag = 0x1018; + if (tag == 0x1c) tag = 0x1017; + } + if (tag == 0x8c) + nikon_curve_offset = ftell(ifp) + 2112; + if (tag == 0x96) + nikon_curve_offset = ftell(ifp) + 2; + if (tag == 0x97) { + if (!strcmp(model,"NIKON D100 ")) { + fseek (ifp, 72, SEEK_CUR); + camera_red = get2(ifp) / 256.0; + camera_blue = get2(ifp) / 256.0; + } else if (!strcmp(model,"NIKON D2H")) { + fseek (ifp, 10, SEEK_CUR); + goto get2_rggb; + } else if (!strcmp(model,"NIKON D70")) { + fseek (ifp, 20, SEEK_CUR); + camera_red = get2(ifp); + camera_red /= get2(ifp); + camera_blue = get2(ifp); + camera_blue/= get2(ifp); + } + } + if (tag == 0xe0 && len == 17) { + get2(ifp); + raw_width = get2(ifp); + raw_height = get2(ifp); + } + if (tag == 0x200 && len == 4) + black = (get2(ifp)+get2(ifp)+get2(ifp)+get2(ifp))/4; + if (tag == 0x201 && len == 4) { + camera_red = get2(ifp); + camera_red /= get2(ifp); + camera_blue = get2(ifp); + camera_blue = get2(ifp) / camera_blue; + } + if (tag == 0x401 && len == 4) { + black = (get4(ifp)+get4(ifp)+get4(ifp)+get4(ifp))/4; + } + if (tag == 0xe80 && len == 256 && type == 7) { + fseek (ifp, 48, SEEK_CUR); + camera_red = get2(ifp) * 508 * 1.078 / 0x10000; + camera_blue = get2(ifp) * 382 * 1.173 / 0x10000; + } + if (tag == 0xf00 && len == 614 && type == 7) { + fseek (ifp, 188, SEEK_CUR); + goto get2_256; + } + if (tag == 0x1017) + camera_red = get2(ifp) / 256.0; + if (tag == 0x1018) + camera_blue = get2(ifp) / 256.0; + if (tag == 0x2011 && len == 2) { +get2_256: + order = 0x4d4d; + camera_red = get2(ifp) / 256.0; + camera_blue = get2(ifp) / 256.0; + } + if (tag == 0x4001) { + fseek (ifp, strstr(model,"EOS-1D") ? 68:50, SEEK_CUR); +get2_rggb: + camera_red = get2(ifp); + camera_red /= get2(ifp); + camera_blue = get2(ifp); + camera_blue = get2(ifp) / camera_blue; + } + fseek (ifp, save+4, SEEK_SET); + } +quit: + order = sorder; +} + + + +static void +get_timestamp(FILE * const ifp) +{ +/* + Since the TIFF DateTime string has no timezone information, + assume that the camera's clock was set to Universal Time. + */ + struct tm t; + time_t ts; + + if (fscanf (ifp, "%d:%d:%d %d:%d:%d", &t.tm_year, &t.tm_mon, + &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec) != 6) + return; + t.tm_year -= 1900; + t.tm_mon -= 1; + putenv((char*)"TZ=UTC"); /* Remove this to assume local time */ + if ((ts = mktime(&t)) > 0) + timestamp = ts; +} + +static void +parse_exif(FILE * const ifp, int base) +{ + int entries, tag, type, len, val, save; + + entries = get2(ifp); + while (entries--) { + tag = get2(ifp); + type = get2(ifp); + len = get4(ifp); + val = get4(ifp); + save = ftell(ifp); + fseek (ifp, base+val, SEEK_SET); + if (tag == 0x9003 || tag == 0x9004) + get_timestamp(ifp); + if (tag == 0x927c) { + if (!strncmp(make,"SONY",4)) + data_offset = base+val+len; + else + parse_makernote(ifp); + } + fseek (ifp, save, SEEK_SET); + } +} + +static int +parse_tiff_ifd(FILE * const ifp, int base, int level) +{ + unsigned entries, tag, type, len, plen=16, save; + int done=0, use_cm=0, cfa, i, j, c; + static const int size[] = { 1,1,1,2,4,8,1,1,2,4,8,4,8 }; + char software[64]; + static const int flip_map[] = { 0,1,3,2,4,6,7,5 }; + unsigned char cfa_pat[16], cfa_pc[] = { 0,1,2,3 }, tab[256]; + unsigned short scale[4]; + double dblack, cc[4][4], cm[4][3]; + double ab[]={ 1,1,1,1 }, asn[] = { 0,0,0,0 }, xyz[] = { 1,1,1 }; + + for (j=0; j < 4; j++) + for (i=0; i < 4; i++) + cc[j][i] = i == j; + entries = get2(ifp); + if (entries > 512) return 1; + while (entries--) { + tag = get2(ifp); + type = get2(ifp); + len = get4(ifp); + save = ftell(ifp); + if (tag > 50700 && tag < 50800) done = 1; + if (len * size[type < 13 ? type:0] > 4) + fseek (ifp, get4(ifp)+base, SEEK_SET); + switch (tag) { + case 0x11: + camera_red = get4(ifp) / 256.0; + break; + case 0x12: + camera_blue = get4(ifp) / 256.0; + break; + case 0x100: /* ImageWidth */ + if (strcmp(make,"Canon") || level) + raw_width = type==3 ? get2(ifp) : get4(ifp); + break; + case 0x101: /* ImageHeight */ + if (strcmp(make,"Canon") || level) + raw_height = type==3 ? get2(ifp) : get4(ifp); + break; + case 0x102: /* Bits per sample */ + fuji_secondary = len == 2; + if (level) maximum = (1 << get2(ifp)) - 1; + break; + case 0x103: /* Compression */ + tiff_data_compression = get2(ifp); + break; + case 0x106: /* Kodak color format */ + kodak_data_compression = get2(ifp); + break; + case 0x10f: /* Make */ + fgets (make, 64, ifp); + break; + case 0x110: /* Model */ + fgets (model, 64, ifp); + break; + case 0x111: /* StripOffset */ + data_offset = get4(ifp); + break; + case 0x112: /* Orientation */ + flip = flip_map[(get2(ifp)-1) & 7]; + break; + case 0x115: /* SamplesPerPixel */ + tiff_samples = get2(ifp); + break; + case 0x131: /* Software tag */ + fgets (software, 64, ifp); + if (!strncmp(software,"Adobe",5)) + make[0] = 0; + break; + case 0x132: /* DateTime tag */ + get_timestamp(ifp); + break; + case 0x144: /* TileOffsets */ + if (level) { + data_offset = ftell(ifp); + } else { + strcpy (make, "Leaf"); + data_offset = get4(ifp); + } + break; + case 0x14a: /* SubIFD tag */ + if (len > 2 && !is_dng && !strcmp(make,"Kodak")) + len = 2; + while (len--) { + i = ftell(ifp); + fseek (ifp, get4(ifp)+base, SEEK_SET); + if (parse_tiff_ifd(ifp, base, level+1)) break; + fseek (ifp, i+4, SEEK_SET); + } + break; + case 33405: /* Model2 */ + fgets (model2, 64, ifp); + break; + case 33422: /* CFAPattern */ + if ((plen=len) > 16) plen = 16; + fread (cfa_pat, 1, plen, ifp); + for (colors=cfa=i=0; i < plen; i++) { + colors += !(cfa & (1 << cfa_pat[i])); + cfa |= 1 << cfa_pat[i]; + } + if (cfa == 070) memcpy (cfa_pc,"\003\004\005",3); /* CMY */ + if (cfa == 072) memcpy (cfa_pc,"\005\003\004\001",4); /* GMCY */ + goto guess_cfa_pc; + case 34665: /* EXIF tag */ + fseek (ifp, get4(ifp)+base, SEEK_SET); + parse_exif(ifp, base); + break; + case 50706: /* DNGVersion */ + is_dng = 1; + if (flip == 7) flip = 4; /* Adobe didn't read the TIFF spec. */ + break; + case 50710: /* CFAPlaneColor */ + if (len > 4) len = 4; + colors = len; + fread (cfa_pc, 1, colors, ifp); +guess_cfa_pc: + FORC4 tab[cfa_pc[c]] = c; + for (i=16; i--; ) + filters = filters << 2 | tab[cfa_pat[i % plen]]; + break; + case 50711: /* CFALayout */ + if (get2(ifp) == 2) { + fuji_width = (raw_width+1)/2; + filters = 0x49494949; + } + break; + case 0x123: + case 0x90d: + case 50712: /* LinearizationTable */ + if (len > 0x1000) + len = 0x1000; + read_shorts(ifp, curve, len); + for (i=len; i < 0x1000; i++) + maximum = curve[i] = curve[i-1]; + break; + case 50714: /* BlackLevel */ + case 50715: /* BlackLevelDeltaH */ + case 50716: /* BlackLevelDeltaV */ + for (dblack=i=0; i < len; i++) + dblack += getrat(); + black += dblack/len + 0.5; + break; + case 50717: /* WhiteLevel */ + maximum = get2(ifp); + break; + case 50718: /* DefaultScale */ + for (i=0; i < 4; i++) + scale[i] = get4(ifp); + if (scale[1]*scale[2] == 2*scale[0]*scale[3]) ymag = 2; + break; + case 50721: /* ColorMatrix1 */ + case 50722: /* ColorMatrix2 */ + FORC4 for (j=0; j < 3; j++) + cm[c][j] = getrat(); + use_cm = 1; + break; + case 50723: /* CameraCalibration1 */ + case 50724: /* CameraCalibration2 */ + for (i=0; i < colors; i++) + FORC4 cc[i][c] = getrat(); + case 50727: /* AnalogBalance */ + FORC4 ab[c] = getrat(); + break; + case 50728: /* AsShotNeutral */ + FORC4 asn[c] = getrat(); + break; + case 50729: /* AsShotWhiteXY */ + xyz[0] = getrat(); + xyz[1] = getrat(); + xyz[2] = 1 - xyz[0] - xyz[1]; + } + fseek (ifp, save+4, SEEK_SET); + } + for (i=0; i < colors; i++) + FORC4 cc[i][c] *= ab[i]; + if (use_cm) + dng_coeff (cc, cm, xyz); + if (asn[0]) + FORC4 pre_mul[c] = 1 / asn[c]; + if (!use_cm) + FORC4 pre_mul[c] /= cc[c][c]; + + if (is_dng || level) return done; + + if ((raw_height & 1) && !strncmp (make,"OLYMPUS",7)) + raw_height++; + + if (make[0] == 0 && raw_width == 680 && raw_height == 680) { + strcpy (make, "Imacon"); + strcpy (model,"Ixpress"); + } + return done; +} + +void +parse_tiff(FILE * const ifp, int base) +{ + int doff; + + fseek (ifp, base, SEEK_SET); + order = get2(ifp); + if (order != 0x4949 && order != 0x4d4d) return; + get2(ifp); + while ((doff = get4(ifp))) { + fseek (ifp, doff+base, SEEK_SET); + if (parse_tiff_ifd(ifp, base, 0)) break; + } + if (!is_dng && !strncmp(make,"Kodak",5)) { + fseek (ifp, 12+base, SEEK_SET); + parse_tiff_ifd(ifp, base, 2); + } +} + + + +/* + Many cameras have a "debug mode" that writes JPEG and raw + at the same time. The raw file has no header, so try to + to open the matching JPEG file and read its metadata. + */ +void +parse_external_jpeg(const char * const ifname) +{ + const char *file, *ext; + char * jfile; + char * jext; + char * jname; + + ext = strrchr (ifname, '.'); + file = strrchr (ifname, '/'); + if (!file) file = strrchr (ifname, '\\'); + if (!file) file = ifname-1; + file++; + if (strlen(ext) != 4 || ext-file != 8) return; + jname = malloc (strlen(ifname) + 1); + merror (jname, "parse_external()"); + strcpy (jname, ifname); + jfile = jname + (file - ifname); + jext = jname + (ext - ifname); + if (strcasecmp (ext, ".jpg")) { + strcpy (jext, isupper(ext[1]) ? ".JPG":".jpg"); + memcpy (jfile, file+4, 4); + memcpy (jfile+4, file, 4); + } else + while (isdigit(*--jext)) { + if (*jext != '9') { + (*jext)++; + break; + } + *jext = '0'; + } + if (strcmp (jname, ifname)) { + FILE * ifP; + ifP = fopen (jname, "rb"); + if (ifP) { + if (verbose) + pm_message ("Reading metadata from %s...", jname); + parse_tiff(ifP, 12); + fclose (ifP); + } + } + if (!timestamp) + pm_message ( "Failed to read metadata from %s", jname); + free (jname); +} + diff --git a/converter/other/cameratopam/camera.h b/converter/other/cameratopam/camera.h new file mode 100644 index 00000000..a1e884cf --- /dev/null +++ b/converter/other/cameratopam/camera.h @@ -0,0 +1,131 @@ +#include <stdio.h> + +void +parse_ciff(FILE * const ifp, + int const offset, + int const length); + +void +parse_external_jpeg(const char * const ifname); + +void +parse_tiff(FILE * const ifp, int base); + +void +parse_minolta(FILE * const ifp); + +void +parse_rollei(FILE * const ifp); + +void +parse_mos(FILE * const ifp, + int const offset); + +void +adobe_dng_load_raw_lj(void); + +void +adobe_dng_load_raw_nc(void); + +int +nikon_is_compressed(void); + +void +nikon_compressed_load_raw(void); + +void +nikon_e950_load_raw(void); + +void +nikon_e950_coeff(void); + +int +nikon_e990(void); + +int +nikon_e2100(void); + +void +nikon_e2100_load_raw(void); + +int +minolta_z2(void); + +void +fuji_s2_load_raw(void); + +void +fuji_s3_load_raw(void); + +void +fuji_s5000_load_raw(void); + +void +unpacked_load_raw(void); + +void +fuji_s7000_load_raw(void); + +void +fuji_f700_load_raw(void); + +void +packed_12_load_raw(void); + +void +eight_bit_load_raw(void); + +void +phase_one_load_raw(void); + +void +ixpress_load_raw(void); + +void +leaf_load_raw(void); + +void +olympus_e300_load_raw(void); + +void +olympus_cseries_load_raw(void); + +void +sony_load_raw(void); + +void +kodak_easy_load_raw(void); + +void +kodak_compressed_load_raw(void); + +void +kodak_yuv_load_raw(void); + +void +kodak_dc20_coeff (float const juice); + +void +kodak_radc_load_raw(void); + +void +kodak_jpeg_load_raw(void); + +void +kodak_dc120_load_raw(void); + +void +rollei_load_raw(void); + +void +casio_qv5700_load_raw(void); + +void +nucore_load_raw(void); + +void +nikon_load_raw(void); + +int +pentax_optio33(void); + diff --git a/converter/other/cameratopam/cameratopam.c b/converter/other/cameratopam/cameratopam.c new file mode 100644 index 00000000..92773c91 --- /dev/null +++ b/converter/other/cameratopam/cameratopam.c @@ -0,0 +1,906 @@ +/* + This is derived from Dave Coffin's raw photo decoder, dcraw.c, + Copyright 1997-2005 by Dave Coffin, dcoffin a cybercom o net. + + See the COPYRIGHT file in the same directory as this file for + information on copyright and licensing. + */ + + +#define _BSD_SOURCE 1 /* Make sure string.h contains strcasecmp() */ +#define _XOPEN_SOURCE /* Make sure unistd.h contains swab() */ + +#include <ctype.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> +#include <fcntl.h> +#include <float.h> +#include <limits.h> +#include <math.h> +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef __CYGWIN__ +#include <io.h> +#endif +#ifdef WIN32 + #include <winsock2.h> + #pragma comment(lib, "ws2_32.lib") + #define strcasecmp stricmp + typedef __int64 INT64; + static bool const have64BitArithmetic = true; +#else + #include <unistd.h> +#endif + +#include "mallocvar.h" +#include "shhopt.h" +#include "pam.h" + +#include "global_variables.h" +#include "util.h" +#include "decode.h" +#include "identify.h" +#include "bayer.h" +#include "foveon.h" +#include "dng.h" + +#if HAVE_INT64 + typedef int64_t INT64; + static bool const have64BitArithmetic = true; +#else + /* We define INT64 to something that lets the code compile, but we + should not execute any INT64 code, because it will get the wrong + result. */ + typedef int INT64; + static bool const have64BitArithmetic = false; +#endif + +/* + 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; +short order; +char make[64], model[70], model2[64], *meta_data; +time_t timestamp; +int data_offset, meta_offset, meta_length; +int tiff_data_compression, kodak_data_compression; +int raw_height, raw_width, top_margin, left_margin; +int height, width, fuji_width, colors, tiff_samples; +int black, maximum, clip_max; +int iheight, iwidth, shrink; +int is_dng, is_canon, is_foveon, use_coeff, use_gamma; +int trim, flip, xmag, ymag; +int zero_after_ff; +unsigned filters; +unsigned short (*image)[4], white[8][8], curve[0x1000]; +int fuji_secondary; +float cam_mul[4], pre_mul[4], coeff[3][4]; +int histogram[3][0x2000]; +jmp_buf failure; +bool use_secondary; +bool verbose; + +#ifdef USE_LCMS +#include <lcms.h> +int profile_offset, profile_length; +#endif + +#define CLASS + +#define FORC3 for (c=0; c < 3; c++) +#define FORC4 for (c=0; c < colors; c++) + +static void CLASS merror (const void *ptr, const char *where) +{ + if (ptr == NULL) + pm_error ("Out of memory in %s", where); +} + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; /* "-" means Standard Input */ + float bright; + float red_scale; + float blue_scale; + const char * profile; + unsigned int identify_only; + unsigned int verbose; + unsigned int half_size; + unsigned int four_color_rgb; + unsigned int document_mode; + unsigned int quick_interpolate; + unsigned int use_auto_wb; + unsigned int use_camera_wb; + unsigned int use_camera_rgb; + unsigned int use_secondary; + unsigned int no_clip_color; + unsigned int linear; +}; + + +static struct cmdlineInfo cmdline; + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo *cmdlineP) { +/*---------------------------------------------------------------------------- + Note that many of the strings that this function returns in the + *cmdlineP structure are actually in the supplied argv array. And + sometimes, one of these strings is actually just a suffix of an entry + in argv! +-----------------------------------------------------------------------------*/ + optStruct3 opt; + optEntry *option_def; + unsigned int option_def_index; + unsigned int brightSpec, red_scaleSpec, blue_scaleSpec, + profileSpec; + + MALLOCARRAY_NOFAIL(option_def, 100); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; + opt.allowNegNum = FALSE; + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "bright", + OPT_FLOAT, &cmdlineP->bright, &brightSpec, 0); + OPTENT3(0, "red_scale", + OPT_FLOAT, &cmdlineP->red_scale, &red_scaleSpec, 0); + OPTENT3(0, "blue_scale", + OPT_FLOAT, &cmdlineP->blue_scale, &blue_scaleSpec, 0); + OPTENT3(0, "profile", + OPT_STRING, &cmdlineP->profile, &profileSpec, 0); + OPTENT3(0, "identify_only", + OPT_FLAG, NULL, &cmdlineP->identify_only, 0); + OPTENT3(0, "verbose", + OPT_FLAG, NULL, &cmdlineP->verbose, 0); + OPTENT3(0, "half_size", + OPT_FLAG, NULL, &cmdlineP->half_size, 0); + OPTENT3(0, "four_color_rgb", + OPT_FLAG, NULL, &cmdlineP->four_color_rgb, 0); + OPTENT3(0, "document_mode", + OPT_FLAG, NULL, &cmdlineP->document_mode, 0); + OPTENT3(0, "quick_interpolate", + OPT_FLAG, NULL, &cmdlineP->quick_interpolate, 0); + OPTENT3(0, "balance_auto", + OPT_FLAG, NULL, &cmdlineP->use_auto_wb, 0); + OPTENT3(0, "balance_camera", + OPT_FLAG, NULL, &cmdlineP->use_camera_wb, 0); + OPTENT3(0, "use_secondary", + OPT_FLAG, NULL, &cmdlineP->use_secondary, 0); + OPTENT3(0, "no_clip_color", + OPT_FLAG, NULL, &cmdlineP->no_clip_color, 0); + OPTENT3(0, "rgb", + OPT_FLAG, NULL, &cmdlineP->use_camera_rgb, 0); + OPTENT3(0, "linear", + OPT_FLAG, NULL, &cmdlineP->linear, 0); + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + + if (!brightSpec) + cmdlineP->bright = 1.0; + if (!red_scaleSpec) + cmdlineP->red_scale = 1.0; + if (!blue_scaleSpec) + cmdlineP->blue_scale = 1.0; + if (!profileSpec) + cmdlineP->profile = NULL; + + + if (argc - 1 == 0) + cmdlineP->inputFileName = strdup("-"); /* he wants stdin */ + else if (argc - 1 == 1) + cmdlineP->inputFileName = strdup(argv[1]); + else + pm_error("Too many arguments. The only argument accepted " + "is the input file name"); +} + + +/* + Seach from the current directory up to the root looking for + a ".badpixels" file, and fix those pixels now. + */ +static void CLASS bad_pixels() +{ + FILE *fp=NULL; + char *fname, *cp, line[128]; + int len, time, row, col, r, c, rad, tot, n, fixed=0; + + if (!filters) return; + for (len=16 ; ; len *= 2) { + fname = malloc (len); + if (!fname) return; + if (getcwd (fname, len-12)) break; + free (fname); + if (errno != ERANGE) return; + } +#ifdef WIN32 + 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) && FC(r,c) == FC(row,col)) { + tot += BAYER(r,c); + n++; + } + BAYER(row,col) = tot/n; + if (cmdline.verbose) { + if (!fixed++) + pm_message ("Fixed bad pixels at: %d,%d", col, row); + } + } + fclose (fp); +} + +static void CLASS scale_colors() +{ + int row, col, c, val, shift=0; + int min[4], max[4], count[4]; + double sum[4], dmin; + + maximum -= black; + if (cmdline.use_auto_wb || (cmdline.use_camera_wb && camera_red == -1)) { + FORC4 min[c] = INT_MAX; + FORC4 max[c] = count[c] = sum[c] = 0; + for (row=0; row < height; row++) + for (col=0; col < width; col++) + FORC4 { + val = image[row*width+col][c]; + if (!val) continue; + if (min[c] > val) min[c] = val; + if (max[c] < val) max[c] = val; + val -= black; + if (val > maximum-25) continue; + if (val < 0) val = 0; + sum[c] += val; + count[c]++; + } + FORC4 pre_mul[c] = count[c] / sum[c]; + } + if (cmdline.use_camera_wb && camera_red != -1) { + FORC4 count[c] = sum[c] = 0; + for (row=0; row < 8; row++) + for (col=0; col < 8; col++) { + c = FC(row,col); + if ((val = white[row][col] - black) > 0) + sum[c] += val; + count[c]++; + } + val = 1; + FORC4 if (sum[c] == 0) val = 0; + if (val) + FORC4 pre_mul[c] = count[c] / sum[c]; + else if (camera_red && camera_blue) + memcpy (pre_mul, cam_mul, sizeof pre_mul); + else + pm_message ("Cannot use camera white balance."); + } + if (!use_coeff) { + pre_mul[0] *= cmdline.red_scale; + pre_mul[2] *= cmdline.blue_scale; + } + dmin = DBL_MAX; + FORC4 if (dmin > pre_mul[c]) + dmin = pre_mul[c]; + FORC4 pre_mul[c] /= dmin; + + while (maximum << shift < 0x8000) shift++; + FORC4 pre_mul[c] *= 1 << shift; + maximum <<= shift; + + if (cmdline.linear || cmdline.bright < 1) { + maximum *= cmdline.bright; + if (maximum > 0xffff) + maximum = 0xffff; + FORC4 pre_mul[c] *= cmdline.bright; + } + if (cmdline.verbose) { + fprintf (stderr, "Scaling with black=%d, pre_mul[] =", black); + FORC4 fprintf (stderr, " %f", pre_mul[c]); + fputc ('\n', stderr); + } + clip_max = cmdline.no_clip_color ? 0xffff : maximum; + for (row=0; row < height; row++) + for (col=0; col < width; col++) + FORC4 { + val = image[row*width+col][c]; + if (!val) continue; + val -= black; + val *= pre_mul[c]; + if (val < 0) val = 0; + if (val > clip_max) val = clip_max; + image[row*width+col][c] = val; + } +} + +/* + This algorithm is officially called: + + "Interpolation using a Threshold-based variable number of gradients" + + described in http://www-ise.stanford.edu/~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. + */ +static void CLASS vng_interpolate() +{ + static const signed char *cp, terms[] = { + -2,-2,+0,-1,0,(char)0x01, -2,-2,+0,+0,1,(char)0x01, -2,-1,-1,+0,0,(char)0x01, + -2,-1,+0,-1,0,(char)0x02, -2,-1,+0,+0,0,(char)0x03, -2,-1,+0,+1,1,(char)0x01, + -2,+0,+0,-1,0,(char)0x06, -2,+0,+0,+0,1,(char)0x02, -2,+0,+0,+1,0,(char)0x03, + -2,+1,-1,+0,0,(char)0x04, -2,+1,+0,-1,1,(char)0x04, -2,+1,+0,+0,0,(char)0x06, + -2,+1,+0,+1,0,(char)0x02, -2,+2,+0,+0,1,(char)0x04, -2,+2,+0,+1,0,(char)0x04, + -1,-2,-1,+0,0,(char)0x80, -1,-2,+0,-1,0,(char)0x01, -1,-2,+1,-1,0,(char)0x01, + -1,-2,+1,+0,1,(char)0x01, -1,-1,-1,+1,0,(char)0x88, -1,-1,+1,-2,0,(char)0x40, + -1,-1,+1,-1,0,(char)0x22, -1,-1,+1,+0,0,(char)0x33, -1,-1,+1,+1,1,(char)0x11, + -1,+0,-1,+2,0,(char)0x08, -1,+0,+0,-1,0,(char)0x44, -1,+0,+0,+1,0,(char)0x11, + -1,+0,+1,-2,1,(char)0x40, -1,+0,+1,-1,0,(char)0x66, -1,+0,+1,+0,1,(char)0x22, + -1,+0,+1,+1,0,(char)0x33, -1,+0,+1,+2,1,(char)0x10, -1,+1,+1,-1,1,(char)0x44, + -1,+1,+1,+0,0,(char)0x66, -1,+1,+1,+1,0,(char)0x22, -1,+1,+1,+2,0,(char)0x10, + -1,+2,+0,+1,0,(char)0x04, -1,+2,+1,+0,1,(char)0x04, -1,+2,+1,+1,0,(char)0x04, + +0,-2,+0,+0,1,(char)0x80, +0,-1,+0,+1,1,(char)0x88, +0,-1,+1,-2,0,(char)0x40, + +0,-1,+1,+0,0,(char)0x11, +0,-1,+2,-2,0,(char)0x40, +0,-1,+2,-1,0,(char)0x20, + +0,-1,+2,+0,0,(char)0x30, +0,-1,+2,+1,1,(char)0x10, +0,+0,+0,+2,1,(char)0x08, + +0,+0,+2,-2,1,(char)0x40, +0,+0,+2,-1,0,(char)0x60, +0,+0,+2,+0,1,(char)0x20, + +0,+0,+2,+1,0,(char)0x30, +0,+0,+2,+2,1,(char)0x10, +0,+1,+1,+0,0,(char)0x44, + +0,+1,+1,+2,0,(char)0x10, +0,+1,+2,-1,1,(char)0x40, +0,+1,+2,+0,0,(char)0x60, + +0,+1,+2,+1,0,(char)0x20, +0,+1,+2,+2,0,(char)0x10, +1,-2,+1,+0,0,(char)0x80, + +1,-1,+1,+1,0,(char)0x88, +1,+0,+1,+2,0,(char)0x08, +1,+0,+2,-1,0,(char)0x40, + +1,+0,+2,+1,0,(char)0x10 + }, chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 }; + unsigned short (*brow[5])[4], *pix; + int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4]; + int row, col, shift, x, y, x1, x2, y1, y2, t, weight, grads, color, diag; + int g, diff, thold, num, c; + + for (row=0; row < 8; row++) { /* Precalculate for bilinear */ + for (col=1; col < 3; col++) { + ip = code[row][col & 1]; + memset (sum, 0, sizeof sum); + for (y=-1; y <= 1; y++) + for (x=-1; x <= 1; x++) { + shift = (y==0) + (x==0); + if (shift == 2) continue; + color = FC(row+y,col+x); + *ip++ = (width*y + x)*4 + color; + *ip++ = shift; + *ip++ = color; + sum[color] += 1 << shift; + } + FORC4 + if (c != FC(row,col)) { + *ip++ = c; + *ip++ = sum[c]; + } + } + } + for (row=1; row < height-1; row++) { /* Do bilinear interpolation */ + for (col=1; col < width-1; col++) { + pix = image[row*width+col]; + ip = code[row & 7][col & 1]; + memset (sum, 0, sizeof sum); + for (g=8; g--; ) { + diff = pix[*ip++]; + diff <<= *ip++; + sum[*ip++] += diff; + } + for (g=colors; --g; ) { + c = *ip++; + pix[c] = sum[c] / *ip++; + } + } + } + if (cmdline.quick_interpolate) + return; + + for (row=0; row < 8; row++) { /* Precalculate for VNG */ + for (col=0; col < 2; col++) { + ip = code[row][col]; + for (cp=terms, t=0; t < 64; t++) { + y1 = *cp++; x1 = *cp++; + y2 = *cp++; x2 = *cp++; + weight = *cp++; + grads = *cp++; + color = FC(row+y1,col+x1); + if (FC(row+y2,col+x2) != color) continue; + diag = (FC(row,col+1) == color && FC(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<<g) *ip++ = g; + *ip++ = -1; + } + *ip++ = INT_MAX; + for (cp=chood, g=0; g < 8; g++) { + y = *cp++; x = *cp++; + *ip++ = (y*width + x) * 4; + color = FC(row,col); + if (FC(row+y,col+x) != color && FC(row+y*2,col+x*2) == color) + *ip++ = (y*width + x) * 8 + color; + else + *ip++ = 0; + } + } + } + brow[4] = calloc (width*3, sizeof **brow); + merror (brow[4], "vng_interpolate()"); + for (row=0; row < 3; row++) + brow[row] = brow[4] + row*width; + for (row=2; row < height-2; row++) { /* Do VNG interpolation */ + for (col=2; col < width-2; col++) { + pix = image[row*width+col]; + ip = code[row & 7][col & 1]; + memset (gval, 0, sizeof gval); + while ((g = ip[0]) != INT_MAX) { /* Calculate gradients */ + num = (diff = pix[g] - pix[ip[1]]) >> 31; + gval[ip[3]] += (diff = ((diff ^ num) - num) << ip[2]); + ip += 5; + if ((g = ip[-1]) == -1) continue; + gval[g] += diff; + while ((g = *ip++) != -1) + gval[g] += diff; + } + ip++; + gmin = gmax = gval[0]; /* Choose a threshold */ + for (g=1; g < 8; g++) { + if (gmin > 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 = FC(row,col); + for (num=g=0; g < 8; g++,ip+=2) { /* Average the neighbors */ + if (gval[g] <= thold) { + FORC4 + if (c == color && ip[1]) + sum[c] += (pix[c] + pix[ip[1]]) >> 1; + else + sum[c] += pix[ip[0] + c]; + num++; + } + } + FORC4 { /* Save to buffer */ + t = pix[color]; + if (c != color) { + t += (sum[c] - sum[color])/num; + if (t < 0) t = 0; + if (t > clip_max) t = clip_max; + } + brow[2][col][c] = 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]); +} + +#ifdef USE_LCMS +static void +apply_profile(FILE * const ifP, + const char * const pfname) +{ + char *prof; + cmsHPROFILE hInProfile=NULL, hOutProfile; + cmsHTRANSFORM hTransform; + + if (pfname) + hInProfile = cmsOpenProfileFromFile (pfname, "r"); + else if (profile_length) { + prof = 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); + } + if (!hInProfile) return; + if (cmdline.verbose) + pm_message( "Applying color profile..."); + maximum = 0xffff; + use_gamma = use_coeff = 0; + + hOutProfile = cmsCreate_sRGBProfile(); + hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16, + hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0); + cmsDoTransform (hTransform, image, image, width*height); + + cmsDeleteTransform (hTransform); + cmsCloseProfile (hInProfile); + cmsCloseProfile (hOutProfile); +} +#else +static void +apply_profile(FILE * const ifP, + const char * const pfname) +{ +} +#endif + +/* + Convert the entire image to RGB colorspace and build a histogram. + */ +static void CLASS convert_to_rgb() +{ + int row, col, r, g, c=0; + unsigned short *img; + float rgb[3]; + + if (cmdline.document_mode) + colors = 1; + memset (histogram, 0, sizeof histogram); + for (row = trim; row < height-trim; row++) + for (col = trim; col < width-trim; col++) { + img = image[row*width+col]; + if (cmdline.document_mode) + c = FC(row,col); + if (colors == 4 && !use_coeff) /* Recombine the greens */ + img[1] = (img[1] + img[3]) >> 1; + if (colors == 1) /* RGB from grayscale */ + for (r=0; r < 3; r++) + rgb[r] = img[c]; + else if (use_coeff) { /* RGB via coeff[][] */ + for (r=0; r < 3; r++) + for (rgb[r]=g=0; g < colors; g++) + rgb[r] += img[g] * coeff[r][g]; + } else /* RGB from RGB (easy) */ + goto norgb; + for (r=0; r < 3; r++) { + if (rgb[r] < 0) rgb[r] = 0; + if (rgb[r] > clip_max) rgb[r] = clip_max; + img[r] = rgb[r]; + } +norgb: + for (r=0; r < 3; r++) + histogram[r][img[r] >> 3]++; + } +} + +static void CLASS fuji_rotate() +{ + int i, wide, high, row, col; + double step; + float r, c, fr, fc; + unsigned ur, uc; + unsigned short (*img)[4], (*pix)[4]; + + if (!fuji_width) return; + if (cmdline.verbose) + pm_message ("Rotating image 45 degrees..."); + fuji_width = (fuji_width + shrink) >> shrink; + step = sqrt(0.5); + wide = fuji_width / step; + high = (height - fuji_width) / step; + img = calloc (wide*high, 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 CLASS flip_image() +{ + unsigned *flag; + int size, base, dest, next, row, col, temp; + + struct imageCell { + unsigned char contents[8]; + }; + struct imageCell * img; + struct imageCell hold; + + switch ((flip+3600) % 360) { + case 270: flip = 5; break; + case 180: flip = 3; break; + case 90: flip = 6; + } + img = (struct imageCell *) image; + size = height * width; + flag = calloc ((size+31) >> 5, sizeof *flag); + merror (flag, "flip_image()"); + for (base = 0; base < size; base++) { + if (flag[base >> 5] & (1 << (base & 31))) + continue; + dest = base; + hold = img[base]; + while (1) { + if (flip & 4) { + row = dest % height; + col = dest / height; + } else { + row = dest / width; + col = dest % width; + } + if (flip & 2) + row = height - 1 - row; + if (flip & 1) + col = width - 1 - col; + next = row * width + col; + if (next == base) break; + flag[next >> 5] |= 1 << (next & 31); + img[dest] = img[next]; + dest = next; + } + img[dest] = hold; + } + free (flag); + if (flip & 4) { + temp = height; + height = width; + width = temp; + temp = ymag; + ymag = xmag; + xmag = temp; + } +} + +/* + Write the image as an RGB PAM image + */ +static void CLASS write_pam_nonlinear (FILE *ofp) +{ + unsigned char lut[0x10000]; + int perc, c, val, total, i, row, col; + float white=0, r; + struct pam pam; + tuple * tuplerow; + + pam.size = sizeof(pam); + pam.len = PAM_STRUCT_SIZE(tuple_type); + pam.file = ofp; + pam.width = xmag*(width-trim*2); + pam.height = ymag*(height-trim*2); + pam.depth = 3; + pam.format = PAM_FORMAT; + pam.maxval = 255; + strcpy(pam.tuple_type, "RGB"); + + pnm_writepaminit(&pam); + + tuplerow = pnm_allocpamrow(&pam); + + perc = width * height * 0.01; /* 99th percentile white point */ + if (fuji_width) perc /= 2; + FORC3 { + for (val=0x2000, total=0; --val > 32; ) + if ((total += histogram[c][val]) > perc) break; + if (white < val) white = val; + } + white *= 8 / cmdline.bright; + for (i=0; i < 0x10000; i++) { + r = i / white; + val = 256 * ( !use_gamma ? r : + r <= 0.018 ? r*4.5 : pow(r,0.45)*1.099-0.099 ); + if (val > 255) val = 255; + lut[i] = val; + } + for (row=trim; row < height-trim; row++) { + for (col=trim; col < width-trim; col++) { + unsigned int plane; + for (plane=0; plane < pam.depth; ++plane) { + unsigned int copy; + for (copy=0; copy < xmag; ++copy) { + unsigned int const pamcol = xmag*(col-trim)+copy; + tuplerow[pamcol][plane] = lut[image[row*width+col][plane]]; + } + } + } + { + unsigned int copy; + for (copy=0; copy < ymag; ++copy) + pnm_writepamrow(&pam, tuplerow); + } + } + pnm_freepamrow(tuplerow); +} + +/* + Write the image to a 16-bit PAM file with linear color space + */ +static void CLASS write_pam_linear (FILE *ofp) +{ + int row; + + struct pam pam; + tuple * tuplerow; + + if (maximum < 256) maximum = 256; + + pam.size = sizeof(pam); + pam.len = PAM_STRUCT_SIZE(tuple_type); + pam.file = ofp; + pam.width = width-trim*2; + pam.height = height-trim*2; + pam.depth = 3; + pam.format = PAM_FORMAT; + pam.maxval = MAX(maximum, 256); + strcpy(pam.tuple_type, "RGB"); + + pnm_writepaminit(&pam); + + tuplerow = pnm_allocpamrow(&pam); + + for (row = trim; row < height-trim; row++) { + unsigned int col; + for (col = trim; col < width-trim; col++) { + unsigned int const pamCol = col - trim; + unsigned int plane; + for (plane = 0; plane < 3; ++plane) + tuplerow[pamCol][plane] = image[row*width+col][plane]; + } + pnm_writepamrow(&pam, tuplerow); + } + pnm_freepamrow(tuplerow); +} + + + +static void CLASS +writePam(FILE * const ofP, + bool const linear) { + + if (linear) + write_pam_linear(ofP); + else + write_pam_nonlinear(ofP); +} + + + + +static void CLASS +convertIt(FILE * const ifP, + FILE * const ofP, + loadRawFn const load_raw) { + + shrink = cmdline.half_size && filters; + iheight = (height + shrink) >> shrink; + iwidth = (width + shrink) >> shrink; + image = calloc (iheight*iwidth*sizeof *image + meta_length, 1); + merror (image, "main()"); + meta_data = (char *) (image + iheight*iwidth); + if (cmdline.verbose) + pm_message ("Loading %s %s image ...", make, model); + + use_secondary = cmdline.use_secondary; /* for load_raw() */ + + ifp = ifP; /* Set global variable for (*load_raw)() */ + + load_raw(); + bad_pixels(); + height = iheight; + width = iwidth; + if (is_foveon) { + if (cmdline.verbose) + pm_message ("Foveon interpolation..."); + foveon_interpolate(coeff); + } else { + scale_colors(); + } + if (shrink) filters = 0; + trim = 0; + if (filters && !cmdline.document_mode) { + trim = 1; + if (cmdline.verbose) + pm_message ("%s interpolation...", + cmdline.quick_interpolate ? "Bilinear":"VNG"); + vng_interpolate(); + } + fuji_rotate(); + apply_profile(ifP, cmdline.profile); + if (cmdline.verbose) + pm_message ("Converting to RGB colorspace..."); + convert_to_rgb(); + + if (flip) { + if (cmdline.verbose) + pm_message ("Flipping image %c:%c:%c...", + flip & 1 ? 'H':'0', flip & 2 ? 'V':'0', + flip & 4 ? 'T':'0'); + flip_image(); + } + writePam(ofP, cmdline.linear); +} + + +int +main (int argc, char **argv) { + + FILE * const ofP = stdout; + + FILE * ifP; + int rc; + loadRawFn load_raw; + + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + verbose = cmdline.verbose; + + ifP = pm_openr(cmdline.inputFileName); + + image = NULL; + + rc = identify(ifP, + cmdline.use_secondary, cmdline.use_camera_rgb, + cmdline.red_scale, cmdline.blue_scale, + cmdline.four_color_rgb, cmdline.inputFileName, + &load_raw); + if (rc != 0) + pm_error("Unable to identify the format of the input image"); + else { + if (cmdline.identify_only) { + pm_message ("Input is a %s %s image.", make, model); + } else { + if (cmdline.verbose) + pm_message ("Input is a %s %s image.", make, model); + convertIt(ifP, ofP, load_raw); + } + } + pm_close(ifP); + pm_close(ofP); + free(image); + + return 0; +} + + + diff --git a/converter/other/cameratopam/canon.c b/converter/other/cameratopam/canon.c new file mode 100644 index 00000000..a34771d0 --- /dev/null +++ b/converter/other/cameratopam/canon.c @@ -0,0 +1,172 @@ +#include <string.h> +#include "mallocvar.h" +#include "pm.h" +#include "global_variables.h" +#include "util.h" +#include "decode.h" +#include "bayer.h" +#include "canon.h" + + +void +canon_600_load_raw(void) { + unsigned char data[1120], *dp; + unsigned short pixel[896], *pix; + int irow, orow, col; + + for (irow=orow=0; irow < height; irow++) + { + fread (data, 1120, 1, ifp); + for (dp=data, pix=pixel; dp < data+1120; dp+=10, pix+=8) + { + pix[0] = (dp[0] << 2) + (dp[1] >> 6 ); + pix[1] = (dp[2] << 2) + (dp[1] >> 4 & 3); + pix[2] = (dp[3] << 2) + (dp[1] >> 2 & 3); + pix[3] = (dp[4] << 2) + (dp[1] & 3); + pix[4] = (dp[5] << 2) + (dp[9] & 3); + pix[5] = (dp[6] << 2) + (dp[9] >> 2 & 3); + pix[6] = (dp[7] << 2) + (dp[9] >> 4 & 3); + pix[7] = (dp[8] << 2) + (dp[9] >> 6 ); + } + for (col=0; col < width; col++) + BAYER(orow,col) = pixel[col]; + for (col=width; col < 896; col++) + black += pixel[col]; + if ((orow+=2) > height) + orow = 1; + } + black /= (896 - width) * height; + maximum = 0x3ff; +} + + + +void +canon_a5_load_raw(void) { + unsigned char data[1940], *dp; + unsigned short pixel[1552], *pix; + int row, col; + + for (row=0; row < height; row++) { + fread (data, raw_width * 10 / 8, 1, ifp); + for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=10, pix+=8) + { + pix[0] = (dp[1] << 2) + (dp[0] >> 6); + pix[1] = (dp[0] << 4) + (dp[3] >> 4); + pix[2] = (dp[3] << 6) + (dp[2] >> 2); + pix[3] = (dp[2] << 8) + (dp[5] ); + pix[4] = (dp[4] << 2) + (dp[7] >> 6); + pix[5] = (dp[7] << 4) + (dp[6] >> 4); + pix[6] = (dp[6] << 6) + (dp[9] >> 2); + pix[7] = (dp[9] << 8) + (dp[8] ); + } + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col] & 0x3ff); + for (col=width; col < raw_width; col++) + black += pixel[col] & 0x3ff; + } + if (raw_width > width) + black /= (raw_width - width) * height; + maximum = 0x3ff; +} + + + +/* + Return 0 if the image starts with compressed data, + 1 if it starts with uncompressed low-order bits. + + In Canon compressed data, 0xff is always followed by 0x00. + */ +static int +canon_has_lowbits() +{ + unsigned char test[0x4000]; + int ret=1, i; + + fseek (ifp, 0, SEEK_SET); + fread (test, 1, sizeof test, ifp); + for (i=540; i < sizeof test - 1; i++) + if (test[i] == 0xff) { + if (test[i+1]) return 1; + ret=0; + } + return ret; +} + + + +void +canon_compressed_load_raw(void) { + unsigned short *pixel, *prow; + int lowbits, i, row, r, col, save, val; + unsigned irow, icol; + struct decode *decode, *dindex; + int block, diffbuf[64], leaf, len, diff, carry=0, pnum=0, base[2]; + unsigned char c; + + MALLOCARRAY(pixel, raw_width*8); + if (pixel == NULL) + pm_error("Unable to allocate space for %u pixels", raw_width*8); + lowbits = canon_has_lowbits(); + if (!lowbits) maximum = 0x3ff; + fseek (ifp, 540 + lowbits*raw_height*raw_width/4, SEEK_SET); + zero_after_ff = 1; + getbits(ifp, -1); + for (row = 0; row < raw_height; row += 8) { + for (block=0; block < raw_width >> 3; block++) { + memset (diffbuf, 0, sizeof diffbuf); + decode = first_decode; + for (i=0; i < 64; i++ ) { + for (dindex=decode; dindex->branch[0]; ) + dindex = dindex->branch[getbits(ifp, 1)]; + leaf = dindex->leaf; + decode = second_decode; + if (leaf == 0 && i) break; + if (leaf == 0xff) continue; + i += leaf >> 4; + len = leaf & 15; + if (len == 0) continue; + diff = getbits(ifp, len); + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + if (i < 64) diffbuf[i] = diff; + } + diffbuf[0] += carry; + carry = diffbuf[0]; + for (i=0; i < 64; i++ ) { + if (pnum++ % raw_width == 0) + base[0] = base[1] = 512; + pixel[(block << 6) + i] = ( base[i & 1] += diffbuf[i] ); + } + } + if (lowbits) { + save = ftell(ifp); + fseek (ifp, 26 + row*raw_width/4, SEEK_SET); + for (prow=pixel, i=0; i < raw_width*2; i++) { + c = fgetc(ifp); + for (r=0; r < 8; r+=2, prow++) { + val = (*prow << 2) + ((c >> r) & 3); + if (raw_width == 2672 && val < 512) val += 2; + *prow = val; + } + } + fseek (ifp, save, SEEK_SET); + } + for (r=0; r < 8; r++) { + irow = row - top_margin + r; + if (irow >= height) continue; + for (col = 0; col < raw_width; col++) { + icol = col - left_margin; + if (icol < width) + BAYER(irow,icol) = pixel[r*raw_width+col]; + else + black += pixel[r*raw_width+col]; + } + } + } + free (pixel); + if (raw_width > width) + black /= (raw_width - width) * height; +} + diff --git a/converter/other/cameratopam/canon.h b/converter/other/cameratopam/canon.h new file mode 100644 index 00000000..041cdc4d --- /dev/null +++ b/converter/other/cameratopam/canon.h @@ -0,0 +1,13 @@ +#ifndef CANON_H_INCLUDED +#define CANON_H_INCLUDED + +void +canon_600_load_raw(void); + +void +canon_a5_load_raw(void); + +void +canon_compressed_load_raw(void); + +#endif diff --git a/converter/other/cameratopam/decode.c b/converter/other/cameratopam/decode.c new file mode 100644 index 00000000..e3a62fab --- /dev/null +++ b/converter/other/cameratopam/decode.c @@ -0,0 +1,172 @@ +#include <string.h> + +#include "pm_c_util.h" +#include "pm.h" + +#include "decode.h" + +struct decode first_decode[2048], *second_decode, *free_decode; + + + +void init_decoder() { + memset (first_decode, 0, sizeof first_decode); + free_decode = first_decode; +} + + + +/* + Construct a decode tree according the specification in *source. + The first 16 bytes specify how many codes should be 1-bit, 2-bit + 3-bit, etc. Bytes after that are the leaf values. + + For example, if the source is + + { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0, + 0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff }, + + then the code is + + 00 0x04 + 010 0x03 + 011 0x05 + 100 0x06 + 101 0x02 + 1100 0x07 + 1101 0x01 + 11100 0x08 + 11101 0x09 + 11110 0x00 + 111110 0x0a + 1111110 0x0b + 1111111 0xff + */ + + +unsigned char * +make_decoder(const unsigned char * const source, + int const level) { + + struct decode *cur; + static int leaf; + int i, next; + + if (level==0) + leaf=0; + cur = free_decode++; + if (free_decode > first_decode+2048) { + pm_error ("decoder table overflow"); + } + for (i=next=0; i <= leaf && next < 16; ) + i += source[next++]; + if (i > leaf) { + if (level < next) { + cur->branch[0] = free_decode; + make_decoder (source, level+1); + cur->branch[1] = free_decode; + make_decoder (source, level+1); + } else + cur->leaf = source[16 + leaf++]; + } + return (unsigned char *) source + 16 + leaf; +} + + + +const int * +make_decoder_int(const int * const source, + int const level) { + + const int * p; + struct decode *cur; + + p = source; /* initial value */ + + cur = free_decode++; + if (level < p[0]) { + cur->branch[0] = free_decode; + p = make_decoder_int (p, level+1); + cur->branch[1] = free_decode; + p = make_decoder_int (p, level+1); + } else { + cur->leaf = p[1]; + p += 2; + } + return p; +} + + + +void +crw_init_tables(unsigned int const table) { + + unsigned int const clippedTableNum = MIN(2, table); + + static const unsigned char first_tree[3][29] = { + { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0, + 0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff }, + + { 0,2,2,3,1,1,1,1,2,0,0,0,0,0,0,0, + 0x03,0x02,0x04,0x01,0x05,0x00,0x06,0x07,0x09,0x08,0x0a,0x0b,0xff }, + + { 0,0,6,3,1,1,2,0,0,0,0,0,0,0,0,0, + 0x06,0x05,0x07,0x04,0x08,0x03,0x09,0x02,0x00,0x0a,0x01,0x0b,0xff }, + }; + + static const unsigned char second_tree[3][180] = { + { 0,2,2,2,1,4,2,1,2,5,1,1,0,0,0,139, + 0x03,0x04,0x02,0x05,0x01,0x06,0x07,0x08, + 0x12,0x13,0x11,0x14,0x09,0x15,0x22,0x00,0x21,0x16,0x0a,0xf0, + 0x23,0x17,0x24,0x31,0x32,0x18,0x19,0x33,0x25,0x41,0x34,0x42, + 0x35,0x51,0x36,0x37,0x38,0x29,0x79,0x26,0x1a,0x39,0x56,0x57, + 0x28,0x27,0x52,0x55,0x58,0x43,0x76,0x59,0x77,0x54,0x61,0xf9, + 0x71,0x78,0x75,0x96,0x97,0x49,0xb7,0x53,0xd7,0x74,0xb6,0x98, + 0x47,0x48,0x95,0x69,0x99,0x91,0xfa,0xb8,0x68,0xb5,0xb9,0xd6, + 0xf7,0xd8,0x67,0x46,0x45,0x94,0x89,0xf8,0x81,0xd5,0xf6,0xb4, + 0x88,0xb1,0x2a,0x44,0x72,0xd9,0x87,0x66,0xd4,0xf5,0x3a,0xa7, + 0x73,0xa9,0xa8,0x86,0x62,0xc7,0x65,0xc8,0xc9,0xa1,0xf4,0xd1, + 0xe9,0x5a,0x92,0x85,0xa6,0xe7,0x93,0xe8,0xc1,0xc6,0x7a,0x64, + 0xe1,0x4a,0x6a,0xe6,0xb3,0xf1,0xd3,0xa5,0x8a,0xb2,0x9a,0xba, + 0x84,0xa4,0x63,0xe5,0xc5,0xf3,0xd2,0xc4,0x82,0xaa,0xda,0xe4, + 0xf2,0xca,0x83,0xa3,0xa2,0xc3,0xea,0xc2,0xe2,0xe3,0xff,0xff }, + + { 0,2,2,1,4,1,4,1,3,3,1,0,0,0,0,140, + 0x02,0x03,0x01,0x04,0x05,0x12,0x11,0x06, + 0x13,0x07,0x08,0x14,0x22,0x09,0x21,0x00,0x23,0x15,0x31,0x32, + 0x0a,0x16,0xf0,0x24,0x33,0x41,0x42,0x19,0x17,0x25,0x18,0x51, + 0x34,0x43,0x52,0x29,0x35,0x61,0x39,0x71,0x62,0x36,0x53,0x26, + 0x38,0x1a,0x37,0x81,0x27,0x91,0x79,0x55,0x45,0x28,0x72,0x59, + 0xa1,0xb1,0x44,0x69,0x54,0x58,0xd1,0xfa,0x57,0xe1,0xf1,0xb9, + 0x49,0x47,0x63,0x6a,0xf9,0x56,0x46,0xa8,0x2a,0x4a,0x78,0x99, + 0x3a,0x75,0x74,0x86,0x65,0xc1,0x76,0xb6,0x96,0xd6,0x89,0x85, + 0xc9,0xf5,0x95,0xb4,0xc7,0xf7,0x8a,0x97,0xb8,0x73,0xb7,0xd8, + 0xd9,0x87,0xa7,0x7a,0x48,0x82,0x84,0xea,0xf4,0xa6,0xc5,0x5a, + 0x94,0xa4,0xc6,0x92,0xc3,0x68,0xb5,0xc8,0xe4,0xe5,0xe6,0xe9, + 0xa2,0xa3,0xe3,0xc2,0x66,0x67,0x93,0xaa,0xd4,0xd5,0xe7,0xf8, + 0x88,0x9a,0xd7,0x77,0xc4,0x64,0xe2,0x98,0xa5,0xca,0xda,0xe8, + 0xf3,0xf6,0xa9,0xb2,0xb3,0xf2,0xd2,0x83,0xba,0xd3,0xff,0xff }, + + { 0,0,6,2,1,3,3,2,5,1,2,2,8,10,0,117, + 0x04,0x05,0x03,0x06,0x02,0x07,0x01,0x08, + 0x09,0x12,0x13,0x14,0x11,0x15,0x0a,0x16,0x17,0xf0,0x00,0x22, + 0x21,0x18,0x23,0x19,0x24,0x32,0x31,0x25,0x33,0x38,0x37,0x34, + 0x35,0x36,0x39,0x79,0x57,0x58,0x59,0x28,0x56,0x78,0x27,0x41, + 0x29,0x77,0x26,0x42,0x76,0x99,0x1a,0x55,0x98,0x97,0xf9,0x48, + 0x54,0x96,0x89,0x47,0xb7,0x49,0xfa,0x75,0x68,0xb6,0x67,0x69, + 0xb9,0xb8,0xd8,0x52,0xd7,0x88,0xb5,0x74,0x51,0x46,0xd9,0xf8, + 0x3a,0xd6,0x87,0x45,0x7a,0x95,0xd5,0xf6,0x86,0xb4,0xa9,0x94, + 0x53,0x2a,0xa8,0x43,0xf5,0xf7,0xd4,0x66,0xa7,0x5a,0x44,0x8a, + 0xc9,0xe8,0xc8,0xe7,0x9a,0x6a,0x73,0x4a,0x61,0xc7,0xf4,0xc6, + 0x65,0xe9,0x72,0xe6,0x71,0x91,0x93,0xa6,0xda,0x92,0x85,0x62, + 0xf3,0xc5,0xb2,0xa4,0x84,0xba,0x64,0xa5,0xb3,0xd2,0x81,0xe5, + 0xd3,0xaa,0xc4,0xca,0xf2,0xb1,0xe4,0xd1,0x83,0x63,0xea,0xc3, + 0xe2,0x82,0xf1,0xa3,0xc2,0xa1,0xc1,0xe3,0xa2,0xe1,0xff,0xff } + }; + + init_decoder(); + make_decoder ( first_tree[clippedTableNum], 0); + second_decode = free_decode; + make_decoder (second_tree[clippedTableNum], 0); +} + diff --git a/converter/other/cameratopam/decode.h b/converter/other/cameratopam/decode.h new file mode 100644 index 00000000..b0addc82 --- /dev/null +++ b/converter/other/cameratopam/decode.h @@ -0,0 +1,22 @@ +struct decode { + struct decode *branch[2]; + int leaf; +}; + +extern struct decode * free_decode; +extern struct decode first_decode[2048]; +extern struct decode * second_decode; + +void +init_decoder(void); + +void +crw_init_tables(unsigned int const table); + +const int * +make_decoder_int (const int * const source, + int const level); + +unsigned char * +make_decoder(const unsigned char * const source, + int const level); diff --git a/converter/other/cameratopam/dng.c b/converter/other/cameratopam/dng.c new file mode 100644 index 00000000..44d45b02 --- /dev/null +++ b/converter/other/cameratopam/dng.c @@ -0,0 +1,73 @@ +#include <string.h> +#include "global_variables.h" + +#include "dng.h" + +void +dng_coeff (double cc[4][4], double cm[4][3], double xyz[3]) +{ + static const double rgb_xyz[3][3] = { /* RGB from XYZ */ + { 3.240479, -1.537150, -0.498535 }, + { -0.969256, 1.875992, 0.041556 }, + { 0.055648, -0.204043, 1.057311 } }; +#if 0 + static 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 } }; +#endif + double cam_xyz[4][3], xyz_cam[3][4], invert[3][6], num; + int i, j, k; + + memset (cam_xyz, 0, sizeof cam_xyz); + for (i=0; i < colors; i++) + for (j=0; j < 3; j++) + for (k=0; k < colors; k++) + cam_xyz[i][j] += cc[i][k] * cm[k][j] * xyz[j]; + + for (i=0; i < colors; i++) { + for (num=j=0; j < 3; j++) + num += cam_xyz[i][j]; + for (j=0; j < 3; j++) + cam_xyz[i][j] /= num; + pre_mul[i] = 1 / num; + } + for (i=0; i < 3; i++) { + for (j=0; j < 6; j++) + invert[i][j] = j == i+3; + for (j=0; j < 3; j++) + for (k=0; k < colors; k++) + invert[i][j] += cam_xyz[k][i] * cam_xyz[k][j]; + } + for (i=0; i < 3; i++) { + num = invert[i][i]; + for (j=0; j < 6; j++) /* Normalize row i */ + invert[i][j] /= num; + for (k=0; k < 3; k++) { /* Subtract it from other rows */ + if (k==i) continue; + num = invert[k][i]; + for (j=0; j < 6; j++) + invert[k][j] -= invert[i][j] * num; + } + } + memset (xyz_cam, 0, sizeof xyz_cam); + for (i=0; i < 3; i++) + for (j=0; j < colors; j++) + for (k=0; k < 3; k++) + xyz_cam[i][j] += invert[i][k+3] * cam_xyz[j][k]; + + memset (coeff, 0, sizeof coeff); + for (i=0; i < 3; i++) + for (j=0; j < colors; j++) + for (k=0; k < 3; k++) + coeff[i][j] += rgb_xyz[i][k] * xyz_cam[k][j]; + + for (num=j=0; j < colors; j++) + num += coeff[1][j]; + for (i=0; i < 3; i++) { + for (j=0; j < colors; j++) + coeff[i][j] /= num; + } + use_coeff = 1; +} + diff --git a/converter/other/cameratopam/dng.h b/converter/other/cameratopam/dng.h new file mode 100644 index 00000000..01e7e0a5 --- /dev/null +++ b/converter/other/cameratopam/dng.h @@ -0,0 +1,2 @@ +void +dng_coeff (double cc[4][4], double cm[4][3], double xyz[3]); diff --git a/converter/other/cameratopam/foveon.c b/converter/other/cameratopam/foveon.c new file mode 100644 index 00000000..0198940c --- /dev/null +++ b/converter/other/cameratopam/foveon.c @@ -0,0 +1,790 @@ +/* This code is licensed to the public by its copyright owners under GPL. */ + +#define _XOPEN_SOURCE /* get M_PI */ + +#include <stdio.h> +#include <assert.h> +#include <string.h> +#include <float.h> +#include <math.h> + +#include "mallocvar.h" +#include "pm.h" +#include "global_variables.h" +#include "decode.h" +#include "foveon.h" + +#if HAVE_INT64 + typedef int64_t INT64; + static bool const have64BitArithmetic = true; +#else + /* We define INT64 to something that lets the code compile, but we + should not execute any INT64 code, because it will get the wrong + result. + */ + typedef int INT64; + static bool const have64BitArithmetic = false; +#endif + + +#define FORC3 for (c=0; c < 3; c++) +#define FORC4 for (c=0; c < colors; c++) + + + +static char * +foveon_gets(int const offset, + char * const str, + int const len) { + + unsigned int i; + fseek (ifp, offset, SEEK_SET); + for (i=0; i < len-1; ++i) { + /* It certains seems wrong that we're reading a 16 bit integer + and assigning it to char, but that's what Dcraw does. + */ + unsigned short val; + pm_readlittleshortu(ifp, &val); + str[i] = val; + if (str[i] == 0) + break; + } + str[i] = 0; + return str; +} + + + +void +parse_foveon(FILE * const ifp) { + long fliplong; + long pos; + long magic; + long junk; + long entries; + + fseek (ifp, 36, SEEK_SET); + pm_readlittlelong(ifp, &fliplong); + flip = fliplong; + fseek (ifp, -4, SEEK_END); + pm_readlittlelong(ifp, &pos); + fseek (ifp, pos, SEEK_SET); + pm_readlittlelong(ifp, &magic); + if (magic != 0x64434553) + return; /* SECd */ + pm_readlittlelong(ifp, &junk); + pm_readlittlelong(ifp, &entries); + while (entries--) { + long off, len, tag; + long save; + long pent; + int i, poff[256][2]; + char name[64]; + long sec_; + + pm_readlittlelong(ifp, &off); + pm_readlittlelong(ifp, &len); + pm_readlittlelong(ifp, &tag); + + save = ftell(ifp); + fseek (ifp, off, SEEK_SET); + pm_readlittlelong(ifp, &sec_); + if (sec_ != (0x20434553 | (tag << 24))) return; + switch (tag) { + case 0x47414d49: /* IMAG */ + if (data_offset) + break; + data_offset = off + 28; + fseek (ifp, 12, SEEK_CUR); + { + long wlong, hlong; + pm_readlittlelong(ifp, &wlong); + pm_readlittlelong(ifp, &hlong); + raw_width = wlong; + raw_height = hlong; + } + break; + case 0x464d4143: /* CAMF */ + meta_offset = off + 24; + meta_length = len - 28; + if (meta_length > 0x20000) + meta_length = 0x20000; + break; + case 0x504f5250: /* PROP */ + pm_readlittlelong(ifp, &junk); + pm_readlittlelong(ifp, &pent); + fseek (ifp, 12, SEEK_CUR); + off += pent*8 + 24; + if (pent > 256) pent=256; + for (i=0; i < pent*2; i++) { + long x; + pm_readlittlelong(ifp, &x); + poff[0][i] = off + x*2; + } + for (i=0; i < pent; i++) { + foveon_gets (poff[i][0], name, 64); + if (!strcmp (name, "CAMMANUF")) + foveon_gets (poff[i][1], make, 64); + if (!strcmp (name, "CAMMODEL")) + foveon_gets (poff[i][1], model, 64); + if (!strcmp (name, "WB_DESC")) + foveon_gets (poff[i][1], model2, 64); + if (!strcmp (name, "TIME")) + timestamp = atoi (foveon_gets (poff[i][1], name, 64)); + } + } + fseek (ifp, save, SEEK_SET); + } + is_foveon = 1; +} + + + +void +foveon_coeff(bool * const useCoeffP, + float coeff[3][4]) { + + static const float foveon[3][3] = + { { 1.4032, -0.2231, -0.1016 }, + { -0.5263, 1.4816, 0.0170 }, + { -0.0112, 0.0183, 0.9113 } }; + + int i, j; + + for (i=0; i < 3; ++i) + for (j=0; j < 3; ++j) + coeff[i][j] = foveon[i][j]; + *useCoeffP = 1; +} + + + +static void +foveon_decoder (unsigned int const huff[1024], + unsigned int const code) { + + struct decode *cur; + int len; + unsigned int code2; + + cur = free_decode++; + if (free_decode > first_decode+2048) { + pm_error ("decoder table overflow"); + } + if (code) { + unsigned int i; + for (i=0; i < 1024; i++) { + if (huff[i] == code) { + cur->leaf = i; + return; + } + } + } + if ((len = code >> 27) > 26) + return; + code2 = (len+1) << 27 | (code & 0x3ffffff) << 1; + + cur->branch[0] = free_decode; + foveon_decoder (huff, code2); + cur->branch[1] = free_decode; + foveon_decoder (huff, code2+1); +} + + + +static void +foveon_load_camf() { + unsigned int i, val; + long key; + + fseek (ifp, meta_offset, SEEK_SET); + pm_readlittlelong(ifp, &key); + fread (meta_data, 1, meta_length, ifp); + for (i=0; i < meta_length; i++) { + key = (key * 1597 + 51749) % 244944; + assert(have64BitArithmetic); + val = key * (INT64) 301593171 >> 24; + meta_data[i] ^= ((((key << 8) - val) >> 1) + val) >> 17; + } +} + + + +void +foveon_load_raw() { + + struct decode *dindex; + short diff[1024], pred[3]; + unsigned int huff[1024], bitbuf=0; + int row, col, bit=-1, c, i; + + assert(have64BitArithmetic); + + for (i=0; i < 1024; ++i) + pm_readlittleshort(ifp, &diff[i]); + + for (i=0; i < 1024; ++i) { + long x; + pm_readlittlelong(ifp, &x); + huff[i] = x; + } + init_decoder(); + foveon_decoder (huff, 0); + + for (row=0; row < height; row++) { + long junk; + memset (pred, 0, sizeof pred); + if (!bit) + pm_readlittlelong(ifp, &junk); + for (col=bit=0; col < width; col++) { + FORC3 { + for (dindex=first_decode; dindex->branch[0]; ) { + if ((bit = (bit-1) & 31) == 31) + for (i=0; i < 4; i++) + bitbuf = (bitbuf << 8) + fgetc(ifp); + dindex = dindex->branch[bitbuf >> bit & 1]; + } + pred[c] += diff[dindex->leaf]; + } + FORC3 image[row*width+col][c] = pred[c]; + } + } + foveon_load_camf(); + maximum = clip_max = 0xffff; +} + + + +static int +sget4(char const s[]) { + return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24; +} + + + +static char * +foveon_camf_param (const char * const block, + const char * const param) { + unsigned idx, num; + char *pos, *cp, *dp; + + for (idx=0; idx < meta_length; idx += sget4(pos+8)) { + pos = meta_data + idx; + if (strncmp (pos, "CMb", 3)) break; + if (pos[3] != 'P') continue; + if (strcmp (block, pos+sget4(pos+12))) continue; + cp = pos + sget4(pos+16); + num = sget4(cp); + dp = pos + sget4(cp+4); + while (num--) { + cp += 8; + if (!strcmp (param, dp+sget4(cp))) + return dp+sget4(cp+4); + } + } + return NULL; +} + + + +static void * +foveon_camf_matrix (int dim[3], + const char * const name) { + + unsigned i, idx, type, ndim, size, *mat; + char *pos, *cp, *dp; + + for (idx=0; idx < meta_length; idx += sget4(pos+8)) { + pos = meta_data + idx; + if (strncmp (pos, "CMb", 3)) break; + if (pos[3] != 'M') continue; + if (strcmp (name, pos+sget4(pos+12))) continue; + dim[0] = dim[1] = dim[2] = 1; + cp = pos + sget4(pos+16); + type = sget4(cp); + if ((ndim = sget4(cp+4)) > 3) break; + dp = pos + sget4(cp+8); + for (i=ndim; i--; ) { + cp += 12; + dim[i] = sget4(cp); + } + if ((size = dim[0]*dim[1]*dim[2]) > meta_length/4) break; + MALLOCARRAY(mat, size); + if (mat == NULL) + pm_error("Unable to allocate memory for size=%d", size); + for (i=0; i < size; i++) + if (type && type != 6) + mat[i] = sget4(dp + i*4); + else + mat[i] = sget4(dp + i*2) & 0xffff; + return mat; + } + pm_message ("'%s' matrix not found!", name); + return NULL; +} + + + +static int +foveon_fixed (void * const ptr, + int const size, + const char * const name) { + void *dp; + int dim[3]; + + dp = foveon_camf_matrix (dim, name); + if (!dp) return 0; + memcpy (ptr, dp, size*4); + free (dp); + return 1; +} + +static float foveon_avg (unsigned short *pix, int range[2], float cfilt) +{ + int i; + float val, min=FLT_MAX, max=-FLT_MAX, sum=0; + + for (i=range[0]; i <= range[1]; i++) { + sum += val = + (short)pix[i*4] + ((short)pix[i*4]-(short)pix[(i-1)*4]) * cfilt; + if (min > val) min = val; + if (max < val) max = val; + } + return (sum - min - max) / (range[1] - range[0] - 1); +} + +static short *foveon_make_curve (double max, double mul, double filt) +{ + short *curve; + int size; + unsigned int i; + double x; + + size = 4*M_PI*max / filt; + MALLOCARRAY(curve, size+1); + if (curve == NULL) + pm_error("Out of memory for %d-element curve array", size); + + curve[0] = size; + for (i=0; i < size; ++i) { + x = i*filt/max/4; + curve[i+1] = (cos(x)+1)/2 * tanh(i*filt/mul) * mul + 0.5; + } + return curve; +} + +static void foveon_make_curves +(short **curvep, float dq[3], float div[3], float filt) +{ + double mul[3], max=0; + int c; + + FORC3 mul[c] = dq[c]/div[c]; + FORC3 if (max < mul[c]) max = mul[c]; + FORC3 curvep[c] = foveon_make_curve (max, mul[c], filt); +} + +static int foveon_apply_curve (short *curve, int i) +{ + if (abs(i) >= (unsigned short)curve[0]) return 0; + return i < 0 ? -(unsigned short)curve[1-i] : (unsigned short)curve[1+i]; +} + +void +foveon_interpolate(float coeff[3][4]) { + + static const short hood[] = { + -1,-1, -1,0, -1,1, 0,-1, 0,1, 1,-1, 1,0, 1,1 }; + short *pix, prev[3], *curve[8], (*shrink)[3]; + float cfilt=0.8, ddft[3][3][2], ppm[3][3][3]; + float cam_xyz[3][3], correct[3][3], last[3][3], trans[3][3]; + float chroma_dq[3], color_dq[3], diag[3][3], div[3]; + float (*black)[3], (*sgain)[3], (*sgrow)[3]; + float fsum[3], val, frow, num; + int row, col, c, i, j, diff, sgx, irow, sum, min, max, limit; + int dim[3], dscr[2][2], (*smrow[7])[3], total[4], ipix[3]; + int work[3][3], smlast, smred, smred_p=0, dev[3]; + int satlev[3], keep[4], active[4]; + unsigned *badpix; + double dsum=0, trsum[3]; + char str[128], *cp; + + foveon_fixed (dscr, 4, "DarkShieldColRange"); + foveon_fixed (ppm[0][0], 27, "PostPolyMatrix"); + foveon_fixed (ddft[1][0], 12, "DarkDrift"); + foveon_fixed (&cfilt, 1, "ColumnFilter"); + foveon_fixed (satlev, 3, "SaturationLevel"); + foveon_fixed (keep, 4, "KeepImageArea"); + foveon_fixed (active, 4, "ActiveImageArea"); + foveon_fixed (chroma_dq, 3, "ChromaDQ"); + foveon_fixed (color_dq, 3, + foveon_camf_param ("IncludeBlocks", "ColorDQ") ? + "ColorDQ" : "ColorDQCamRGB"); + + if (!(cp = foveon_camf_param ("WhiteBalanceIlluminants", model2))) + { pm_message ( "Invalid white balance \"%s\"", model2); + return; } + foveon_fixed (cam_xyz, 9, cp); + foveon_fixed (correct, 9, + foveon_camf_param ("WhiteBalanceCorrections", model2)); + memset (last, 0, sizeof last); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + FORC3 last[i][j] += correct[i][c] * cam_xyz[c][j]; + + sprintf (str, "%sRGBNeutral", model2); + if (foveon_camf_param ("IncludeBlocks", str)) + foveon_fixed (div, 3, str); + else { +#define LAST(x,y) last[(i+x)%3][(c+y)%3] + for (i=0; i < 3; i++) + FORC3 diag[c][i] = LAST(1,1)*LAST(2,2) - LAST(1,2)*LAST(2,1); +#undef LAST + FORC3 div[c] = + diag[c][0]*0.3127 + diag[c][1]*0.329 + diag[c][2]*0.3583; + } + num = 0; + FORC3 if (num < div[c]) num = div[c]; + FORC3 div[c] /= num; + + memset (trans, 0, sizeof trans); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + FORC3 trans[i][j] += coeff[i][c] * last[c][j] * div[j]; + FORC3 trsum[c] = trans[c][0] + trans[c][1] + trans[c][2]; + dsum = (6*trsum[0] + 11*trsum[1] + 3*trsum[2]) / 20; + for (i=0; i < 3; i++) + FORC3 last[i][c] = trans[i][c] * dsum / trsum[i]; + memset (trans, 0, sizeof trans); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + FORC3 trans[i][j] += (i==c ? 32 : -1) * last[c][j] / 30; + + foveon_make_curves (curve, color_dq, div, cfilt); + FORC3 chroma_dq[c] /= 3; + foveon_make_curves (curve+3, chroma_dq, div, cfilt); + FORC3 dsum += chroma_dq[c] / div[c]; + curve[6] = foveon_make_curve (dsum, dsum, cfilt); + curve[7] = foveon_make_curve (dsum*2, dsum*2, cfilt); + + sgain = foveon_camf_matrix (dim, "SpatialGain"); + if (!sgain) return; + sgrow = calloc (dim[1], sizeof *sgrow); + sgx = (width + dim[1]-2) / (dim[1]-1); + + black = calloc (height, sizeof *black); + for (row=0; row < height; row++) { + for (i=0; i < 6; i++) + ddft[0][0][i] = ddft[1][0][i] + + row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]); + FORC3 black[row][c] = + ( foveon_avg (image[row*width]+c, dscr[0], cfilt) + + foveon_avg (image[row*width]+c, dscr[1], cfilt) * 3 + - ddft[0][c][0] ) / 4 - ddft[0][c][1]; + } + memcpy (black, black+8, sizeof *black*8); + memcpy (black+height-11, black+height-22, 11*sizeof *black); + memcpy (last, black, sizeof last); + + for (row=1; row < height-1; row++) { + FORC3 if (last[1][c] > last[0][c]) { + if (last[1][c] > last[2][c]) + black[row][c] = + (last[0][c] > last[2][c]) ? last[0][c]:last[2][c]; + } else + if (last[1][c] < last[2][c]) + black[row][c] = + (last[0][c] < last[2][c]) ? last[0][c]:last[2][c]; + memmove (last, last+1, 2*sizeof last[0]); + memcpy (last[2], black[row+1], sizeof last[2]); + } + FORC3 black[row][c] = (last[0][c] + last[1][c])/2; + FORC3 black[0][c] = (black[1][c] + black[3][c])/2; + + val = 1 - exp(-1/24.0); + memcpy (fsum, black, sizeof fsum); + for (row=1; row < height; row++) + FORC3 fsum[c] += black[row][c] = + (black[row][c] - black[row-1][c])*val + black[row-1][c]; + memcpy (last[0], black[height-1], sizeof last[0]); + FORC3 fsum[c] /= height; + for (row = height; row--; ) + FORC3 last[0][c] = black[row][c] = + (black[row][c] - fsum[c] - last[0][c])*val + last[0][c]; + + memset (total, 0, sizeof total); + for (row=2; row < height; row+=4) + for (col=2; col < width; col+=4) { + FORC3 total[c] += (short) image[row*width+col][c]; + total[3]++; + } + for (row=0; row < height; row++) + FORC3 black[row][c] += fsum[c]/2 + total[c]/(total[3]*100.0); + + for (row=0; row < height; row++) { + for (i=0; i < 6; i++) + ddft[0][0][i] = ddft[1][0][i] + + row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]); + pix = (short*)image[row*width]; + memcpy (prev, pix, sizeof prev); + frow = row / (height-1.0) * (dim[2]-1); + if ((irow = frow) == dim[2]-1) irow--; + frow -= irow; + for (i=0; i < dim[1]; i++) + FORC3 sgrow[i][c] = sgain[ irow *dim[1]+i][c] * (1-frow) + + sgain[(irow+1)*dim[1]+i][c] * frow; + for (col=0; col < width; col++) { + FORC3 { + diff = pix[c] - prev[c]; + prev[c] = pix[c]; + ipix[c] = pix[c] + floor ((diff + (diff*diff >> 14)) * cfilt + - ddft[0][c][1] + - ddft[0][c][0] + * ((float) col/width - 0.5) + - black[row][c] ); + } + FORC3 { + work[0][c] = ipix[c] * ipix[c] >> 14; + work[2][c] = ipix[c] * work[0][c] >> 14; + work[1][2-c] = ipix[(c+1) % 3] * ipix[(c+2) % 3] >> 14; + } + FORC3 { + for (val=i=0; i < 3; i++) + for ( j=0; j < 3; j++) + val += ppm[c][i][j] * work[i][j]; + ipix[c] = floor ((ipix[c] + floor(val)) * + ( sgrow[col/sgx ][c] * (sgx - col%sgx) + + sgrow[col/sgx+1][c] * (col%sgx) ) / sgx / + div[c]); + if (ipix[c] > 32000) ipix[c] = 32000; + pix[c] = ipix[c]; + } + pix += 4; + } + } + free (black); + free (sgrow); + free (sgain); + + if ((badpix = foveon_camf_matrix (dim, "BadPixels"))) { + for (i=0; i < dim[0]; i++) { + col = (badpix[i] >> 8 & 0xfff) - keep[0]; + row = (badpix[i] >> 20 ) - keep[1]; + if ((unsigned)(row-1) > height-3 || (unsigned)(col-1) > width-3) + continue; + memset (fsum, 0, sizeof fsum); + for (sum=j=0; j < 8; j++) + if (badpix[i] & (1 << j)) { + FORC3 fsum[c] += + image[(row+hood[j*2])*width+col+hood[j*2+1]][c]; + sum++; + } + if (sum) FORC3 image[row*width+col][c] = fsum[c]/sum; + } + free (badpix); + } + + /* Array for 5x5 Gaussian averaging of red values */ + smrow[6] = calloc (width*5, sizeof **smrow); + if (smrow[6] == NULL) + pm_error("Out of memory"); + for (i=0; i < 5; i++) + smrow[i] = smrow[6] + i*width; + + /* Sharpen the reds against these Gaussian averages */ + for (smlast=-1, row=2; row < height-2; row++) { + while (smlast < row+2) { + for (i=0; i < 6; i++) + smrow[(i+5) % 6] = smrow[i]; + pix = (short*)image[++smlast*width+2]; + for (col=2; col < width-2; col++) { + smrow[4][col][0] = + (pix[0]*6 + (pix[-4]+pix[4])*4 + pix[-8]+pix[8] + 8) >> 4; + pix += 4; + } + } + pix = (short*)image[row*width+2]; + for (col=2; col < width-2; col++) { + smred = ( 6 * smrow[2][col][0] + + 4 * (smrow[1][col][0] + smrow[3][col][0]) + + smrow[0][col][0] + smrow[4][col][0] + 8 ) >> 4; + if (col == 2) + smred_p = smred; + i = pix[0] + ((pix[0] - ((smred*7 + smred_p) >> 3)) >> 3); + if (i > 32000) i = 32000; + pix[0] = i; + smred_p = smred; + pix += 4; + } + } + + /* Adjust the brighter pixels for better linearity */ + FORC3 { + i = satlev[c] / div[c]; + if (maximum > i) maximum = i; + } + clip_max = maximum; + limit = maximum * 9 >> 4; + for (pix=(short*)image[0]; pix < (short *) image[height*width]; pix+=4) { + if (pix[0] <= limit || pix[1] <= limit || pix[2] <= limit) + continue; + min = max = pix[0]; + for (c=1; c < 3; c++) { + if (min > pix[c]) min = pix[c]; + if (max < pix[c]) max = pix[c]; + } + i = 0x4000 - ((min - limit) << 14) / limit; + i = 0x4000 - (i*i >> 14); + i = i*i >> 14; + FORC3 pix[c] += (max - pix[c]) * i >> 14; + } + /* + Because photons that miss one detector often hit another, + the sum R+G+B is much less noisy than the individual colors. + So smooth the hues without smoothing the total. + */ + for (smlast=-1, row=2; row < height-2; row++) { + while (smlast < row+2) { + for (i=0; i < 6; i++) + smrow[(i+5) % 6] = smrow[i]; + pix = (short*)image[++smlast*width+2]; + for (col=2; col < width-2; col++) { + FORC3 smrow[4][col][c] = (pix[c-4]+2*pix[c]+pix[c+4]+2) >> 2; + pix += 4; + } + } + pix = (short*)image[row*width+2]; + for (col=2; col < width-2; col++) { + FORC3 dev[c] = -foveon_apply_curve (curve[7], pix[c] - + ((smrow[1][col][c] + + 2*smrow[2][col][c] + + smrow[3][col][c]) >> 2)); + sum = (dev[0] + dev[1] + dev[2]) >> 3; + FORC3 pix[c] += dev[c] - sum; + pix += 4; + } + } + for (smlast=-1, row=2; row < height-2; row++) { + while (smlast < row+2) { + for (i=0; i < 6; i++) + smrow[(i+5) % 6] = smrow[i]; + pix = (short*)image[++smlast*width+2]; + for (col=2; col < width-2; col++) { + FORC3 smrow[4][col][c] = + (pix[c-8]+pix[c-4]+pix[c]+pix[c+4]+pix[c+8]+2) >> 2; + pix += 4; + } + } + pix = (short*)image[row*width+2]; + for (col=2; col < width-2; col++) { + for (total[3]=375, sum=60, c=0; c < 3; c++) { + for (total[c]=i=0; i < 5; i++) + total[c] += smrow[i][col][c]; + total[3] += total[c]; + sum += pix[c]; + } + if (sum < 0) sum = 0; + j = total[3] > 375 ? (sum << 16) / total[3] : sum * 174; + FORC3 pix[c] += foveon_apply_curve (curve[6], + ((j*total[c] + 0x8000) >> 16) + - pix[c]); + pix += 4; + } + } + + /* Transform the image to a different colorspace */ + for (pix=(short*)image[0]; pix < (short *) image[height*width]; pix+=4) { + FORC3 pix[c] -= foveon_apply_curve (curve[c], pix[c]); + sum = (pix[0]+pix[1]+pix[1]+pix[2]) >> 2; + FORC3 pix[c] -= foveon_apply_curve (curve[c], pix[c]-sum); + FORC3 { + for (dsum=i=0; i < 3; i++) + dsum += trans[c][i] * pix[i]; + if (dsum < 0) dsum = 0; + if (dsum > 24000) dsum = 24000; + ipix[c] = dsum + 0.5; + } + FORC3 pix[c] = ipix[c]; + } + + /* Smooth the image bottom-to-top and save at 1/4 scale */ + MALLOCARRAY(shrink, (width/4) * (height/4)); + if (shrink == NULL) + pm_error("Out of memory allocating 1/4 scale array"); + + for (row = height/4; row > 0; --row) { + for (col=0; col < width/4; ++col) { + ipix[0] = ipix[1] = ipix[2] = 0; + for (i=0; i < 4; i++) + for (j=0; j < 4; j++) + FORC3 ipix[c] += image[(row*4+i)*width+col*4+j][c]; + FORC3 + if (row+2 > height/4) + shrink[row*(width/4)+col][c] = ipix[c] >> 4; + else + shrink[row*(width/4)+col][c] = + (shrink[(row+1)*(width/4)+col][c]*1840 + + ipix[c]*141 + 2048) >> 12; + } + } + /* From the 1/4-scale image, smooth right-to-left */ + for (row=0; row < (height & ~3); ++row) { + ipix[0] = ipix[1] = ipix[2] = 0; + if ((row & 3) == 0) + for (col = width & ~3 ; col--; ) + FORC3 smrow[0][col][c] = ipix[c] = + (shrink[(row/4)*(width/4)+col/4][c]*1485 + + ipix[c]*6707 + 4096) >> 13; + + /* Then smooth left-to-right */ + ipix[0] = ipix[1] = ipix[2] = 0; + for (col=0; col < (width & ~3); col++) + FORC3 smrow[1][col][c] = ipix[c] = + (smrow[0][col][c]*1485 + ipix[c]*6707 + 4096) >> 13; + + /* Smooth top-to-bottom */ + if (row == 0) + memcpy (smrow[2], smrow[1], sizeof **smrow * width); + else + for (col=0; col < (width & ~3); col++) + FORC3 smrow[2][col][c] = + (smrow[2][col][c]*6707 + smrow[1][col][c]*1485 + 4096) + >> 13; + + /* Adjust the chroma toward the smooth values */ + for (col=0; col < (width & ~3); col++) { + for (i=j=30, c=0; c < 3; c++) { + i += smrow[2][col][c]; + j += image[row*width+col][c]; + } + j = (j << 16) / i; + for (sum=c=0; c < 3; c++) { + ipix[c] = foveon_apply_curve( + curve[c+3], + ((smrow[2][col][c] * j + 0x8000) >> 16) - + image[row*width+col][c]); + sum += ipix[c]; + } + sum >>= 3; + FORC3 { + i = image[row*width+col][c] + ipix[c] - sum; + if (i < 0) i = 0; + image[row*width+col][c] = i; + } + } + } + free (shrink); + free (smrow[6]); + for (i=0; i < 8; i++) + free (curve[i]); + + /* Trim off the black border */ + active[1] -= keep[1]; + active[3] -= 2; + i = active[2] - active[0]; + for (row = 0; row < active[3]-active[1]; row++) + memcpy (image[row*i], image[(row+active[1])*width+active[0]], + i * sizeof *image); + width = i; + height = row; +} diff --git a/converter/other/cameratopam/foveon.h b/converter/other/cameratopam/foveon.h new file mode 100644 index 00000000..57be2244 --- /dev/null +++ b/converter/other/cameratopam/foveon.h @@ -0,0 +1,14 @@ +#include "pm.h" + +void +parse_foveon(FILE * const ifp); + +void +foveon_interpolate(float coeff[3][4]); + +void +foveon_load_raw(void); + +void +foveon_coeff(bool * const useCoeffP, + float coeff[3][4]); diff --git a/converter/other/cameratopam/global_variables.h b/converter/other/cameratopam/global_variables.h new file mode 100644 index 00000000..c8732d5a --- /dev/null +++ b/converter/other/cameratopam/global_variables.h @@ -0,0 +1,55 @@ +/* These are unfortunately global variables used by various modules. + + It would be nice to clean up this code and get rid of all of these. +*/ +#include <stdio.h> +#include <time.h> +#include "pm_c_util.h" + +extern FILE * ifp; +extern int flip; +extern int data_offset; +extern int raw_width; +extern int raw_height; +extern int height; +extern int width; +extern char * meta_data; +extern int meta_offset; +extern int meta_length; +extern char make[64]; +extern char model[70]; +extern char model2[64]; +extern time_t timestamp; +extern int is_foveon; +extern int is_dng; +extern int is_canon; +extern unsigned short (*image)[4]; +extern int maximum; +extern int clip_max; +extern short order; +extern int zero_after_ff; +extern int shrink; +extern int iwidth; +extern unsigned int filters; +extern int black; +extern unsigned short white[8][8]; +extern int top_margin; +extern int left_margin; +extern int fuji_width; +extern int tiff_samples; +extern int tiff_data_compression; +extern int kodak_data_compression; +extern int fuji_secondary; +extern int use_coeff; +extern int use_gamma; +extern int xmag; +extern int ymag; +extern float cam_mul[4]; +#define camera_red cam_mul[0] +#define camera_blue cam_mul[2] +extern float pre_mul[4]; +extern int colors; +extern unsigned short curve[0x1000]; +extern float coeff[3][4]; +extern int use_secondary; +extern bool verbose; diff --git a/converter/other/cameratopam/identify.c b/converter/other/cameratopam/identify.c new file mode 100644 index 00000000..a101c8ad --- /dev/null +++ b/converter/other/cameratopam/identify.c @@ -0,0 +1,1183 @@ +#define _BSD_SOURCE /* Make sure strcasecmp() is in string.h */ +#include <string.h> + +#include "pm.h" + +#include "global_variables.h" +#include "util.h" +#include "foveon.h" +#include "canon.h" +#include "dng.h" +#include "ljpeg.h" +#include "camera.h" + +#include "identify.h" + + +#if HAVE_INT64 + static bool const have64BitArithmetic = true; +#else + static bool const have64BitArithmetic = false; +#endif + + +static loadRawFn load_raw; + +/* This does the same as the function of the same name in the GNU C library */ +static const char *memmem_internal (const char *haystack, size_t haystacklen, + const char *needle, size_t needlelen) +{ + const char *c; + for (c = haystack; c <= haystack + haystacklen - needlelen; c++) + if (!memcmp (c, needle, needlelen)) + return c; + return NULL; +} + +/* + Thanks to Adobe for providing these excellent CAM -> XYZ matrices! + */ +static void +adobe_coeff() +{ + static const struct { + const char *prefix; + short trans[12]; + } table[] = { + { "Canon EOS D2000C", + { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } }, + { "Canon EOS D30", + { 9805,-2689,-1312,-5803,13064,3068,-2438,3075,8775 } }, + { "Canon EOS D60", + { 6188,-1341,-890,-7168,14489,2937,-2640,3228,8483 } }, + { "Canon EOS 10D", + { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } }, + { "Canon EOS 20D", + { 6599,-537,-891,-8071,15783,2424,-1983,2234,7462 } }, + { "Canon EOS-1Ds Mark II", + { 6517,-602,-867,-8180,15926,2378,-1618,1771,7633 } }, + { "Canon EOS-1D Mark II", + { 6264,-582,-724,-8312,15948,2504,-1744,1919,8664 } }, + { "Canon EOS-1DS", + { 4374,3631,-1743,-7520,15212,2472,-2892,3632,8161 } }, + { "Canon EOS-1D", + { 6906,-278,-1017,-6649,15074,1621,-2848,3897,7611 } }, + { "Canon EOS", + { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } }, + { "Canon PowerShot 600", + { -3822,10019,1311,4085,-157,3386,-5341,10829,4812,-1969,10969,1126 } }, + { "Canon PowerShot A50", + { -5300,9846,1776,3436,684,3939,-5540,9879,6200,-1404,11175,217 } }, + { "Canon PowerShot A5", + { -4801,9475,1952,2926,1611,4094,-5259,10164,5947,-1554,10883,547 } }, + { "Canon PowerShot G1", + { -4778,9467,2172,4743,-1141,4344,-5146,9908,6077,-1566,11051,557 } }, + { "Canon PowerShot G2", + { 9087,-2693,-1049,-6715,14382,2537,-2291,2819,7790 } }, + { "Canon PowerShot G3", + { 9212,-2781,-1073,-6573,14189,2605,-2300,2844,7664 } }, + { "Canon PowerShot G5", + { 9757,-2872,-933,-5972,13861,2301,-1622,2328,7212 } }, + { "Canon PowerShot G6", + { 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } }, + { "Canon PowerShot Pro1", + { 10062,-3522,-999,-7643,15117,2730,-765,817,7323 } }, + { "Canon PowerShot Pro70", + { -4155,9818,1529,3939,-25,4522,-5521,9870,6610,-2238,10873,1342 } }, + { "Canon PowerShot Pro90", + { -4963,9896,2235,4642,-987,4294,-5162,10011,5859,-1770,11230,577 } }, + { "Canon PowerShot S30", + { 10566,-3652,-1129,-6552,14662,2006,-2197,2581,7670 } }, + { "Canon PowerShot S40", + { 8510,-2487,-940,-6869,14231,2900,-2318,2829,9013 } }, + { "Canon PowerShot S45", + { 8163,-2333,-955,-6682,14174,2751,-2077,2597,8041 } }, + { "Canon PowerShot S50", + { 8882,-2571,-863,-6348,14234,2288,-1516,2172,6569 } }, + { "Canon PowerShot S70", + { 9976,-3810,-832,-7115,14463,2906,-901,989,7889 } }, + { "Contax N Digital", + { 7777,1285,-1053,-9280,16543,2916,-3677,5679,7060 } }, + { "EPSON R-D1", + { 6827,-1878,-732,-8429,16012,2564,-704,592,7145 } }, + { "FUJIFILM FinePix E550", + { 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } }, + { "FUJIFILM FinePix F700", + { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } }, + { "FUJIFILM FinePix S20Pro", + { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } }, + { "FUJIFILM FinePix S2Pro", + { 12492,-4690,-1402,-7033,15423,1647,-1507,2111,7697 } }, + { "FUJIFILM FinePix S3Pro", + { 11807,-4612,-1294,-8927,16968,1988,-2120,2741,8006 } }, + { "FUJIFILM FinePix S5000", + { 8754,-2732,-1019,-7204,15069,2276,-1702,2334,6982 } }, + { "FUJIFILM FinePix S5100", + { 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } }, + { "FUJIFILM FinePix S7000", + { 10190,-3506,-1312,-7153,15051,2238,-2003,2399,7505 } }, + { "Kodak DCS315C", + { 17523,-4827,-2510,756,8546,-137,6113,1649,2250 } }, + { "Kodak DCS330C", + { 20620,-7572,-2801,-103,10073,-396,3551,-233,2220 } }, + { "KODAK DCS420", + { 10868,-1852,-644,-1537,11083,484,2343,628,2216 } }, + { "KODAK DCS460", + { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } }, + { "KODAK EOSDCS1", + { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } }, + { "KODAK EOSDCS3B", + { 9898,-2700,-940,-2478,12219,206,1985,634,1031 } }, + { "Kodak DCS520C", + { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } }, + { "Kodak DCS560C", + { 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } }, + { "Kodak DCS620C", + { 23617,-10175,-3149,-2054,11749,-272,2586,-489,3453 } }, + { "Kodak DCS620X", + { 13095,-6231,154,12221,-21,-2137,895,4602,2258 } }, + { "Kodak DCS660C", + { 18244,-6351,-2739,-791,11193,-521,3711,-129,2802 } }, + { "Kodak DCS720X", + { 11775,-5884,950,9556,1846,-1286,-1019,6221,2728 } }, + { "Kodak DCS760C", + { 16623,-6309,-1411,-4344,13923,323,2285,274,2926 } }, + { "Kodak DCS Pro SLR", + { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } }, + { "Kodak DCS Pro 14nx", + { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } }, + { "Kodak DCS Pro 14", + { 7791,3128,-776,-8588,16458,2039,-2455,4006,6198 } }, + { "Kodak ProBack645", + { 16414,-6060,-1470,-3555,13037,473,2545,122,4948 } }, + { "Kodak ProBack", + { 21179,-8316,-2918,-915,11019,-165,3477,-180,4210 } }, + { "LEICA DIGILUX 2", + { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } }, + { "Leaf Valeo", + { 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 } }, + { "Minolta DiMAGE 5", + { 8983,-2942,-963,-6556,14476,2237,-2426,2887,8014 } }, + { "Minolta DiMAGE 7", + { 9144,-2777,-998,-6676,14556,2281,-2470,3019,7744 } }, + { "Minolta DiMAGE A1", + { 9274,-2547,-1167,-8220,16323,1943,-2273,2720,8340 } }, + { "MINOLTA DiMAGE A200", + { 8560,-2487,-986,-8112,15535,2771,-1209,1324,7743 } }, + { "Minolta DiMAGE A2", + { 9097,-2726,-1053,-8073,15506,2762,-966,981,7763 } }, + { "MINOLTA DYNAX 7D", + { 10239,-3104,-1099,-8037,15727,2451,-927,925,6871 } }, + { "NIKON D100", + { 5915,-949,-778,-7516,15364,2282,-1228,1337,6404 } }, + { "NIKON D1H", + { 7577,-2166,-926,-7454,15592,1934,-2377,2808,8606 } }, + { "NIKON D1X", + { 7620,-2173,-966,-7604,15843,1805,-2356,2811,8439 } }, + { "NIKON D1", + { 7559,-2130,-965,-7611,15713,1972,-2478,3042,8290 } }, + { "NIKON D2H", + { 5710,-901,-615,-8594,16617,2024,-2975,4120,6830 } }, + { "NIKON D70", + { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } }, + { "NIKON E995", /* copied from E5000 */ + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E2500", + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E4500", + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E5000", + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E5400", + { 9349,-2987,-1001,-7919,15766,2266,-2098,2680,6839 } }, + { "NIKON E5700", + { -5368,11478,2368,5537,-113,3148,-4969,10021,5782,778,9028,211 } }, + { "NIKON E8400", + { 7842,-2320,-992,-8154,15718,2599,-1098,1342,7560 } }, + { "NIKON E8700", + { 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } }, + { "NIKON E8800", + { 7971,-2314,-913,-8451,15762,2894,-1442,1520,7610 } }, + { "OLYMPUS C5050", + { 10508,-3124,-1273,-6079,14294,1901,-1653,2306,6237 } }, + { "OLYMPUS C5060", + { 10445,-3362,-1307,-7662,15690,2058,-1135,1176,7602 } }, + { "OLYMPUS C70", + { 10793,-3791,-1146,-7498,15177,2488,-1390,1577,7321 } }, + { "OLYMPUS C80", + { 8606,-2509,-1014,-8238,15714,2703,-942,979,7760 } }, + { "OLYMPUS E-10", + { 12745,-4500,-1416,-6062,14542,1580,-1934,2256,6603 } }, + { "OLYMPUS E-1", + { 11846,-4767,-945,-7027,15878,1089,-2699,4122,8311 } }, + { "OLYMPUS E-20", + { 13173,-4732,-1499,-5807,14036,1895,-2045,2452,7142 } }, + { "OLYMPUS E-300", + { 7828,-1761,-348,-5788,14071,1830,-2853,4518,6557 } }, + { "PENTAX *ist D", + { 9651,-2059,-1189,-8881,16512,2487,-1460,1345,10687 } }, + { "Panasonic DMC-LC1", + { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } }, + { "SONY DSC-F828", + { 7924,-1910,-777,-8226,15459,2998,-1517,2199,6818,-7242,11401,3481 } }, + { "SONY DSC-V3", + { 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } } + }; + double cc[4][4], cm[4][3], xyz[] = { 1,1,1 }; + char name[130]; + int i, j; + + for (i=0; i < 4; i++) + for (j=0; j < 4; j++) + cc[i][j] = i == j; + sprintf (name, "%s %s", make, model); + for (i=0; i < sizeof table / sizeof *table; i++) + if (!strncmp (name, table[i].prefix, strlen(table[i].prefix))) { + for (j=0; j < 12; j++) + cm[0][j] = table[i].trans[j]; + dng_coeff (cc, cm, xyz); + break; + } +} + +/* + Identify which camera created this file, and set global variables + accordingly. Return nonzero if the file cannot be decoded. + */ +int +identify(FILE * const ifp, + bool const use_secondary, + bool const use_camera_rgb, + float const red_scale, + float const blue_scale, + unsigned int const four_color_rgb, + const char * const inputFileName, + loadRawFn * const loadRawFnP) +{ + char head[32]; + char * c; + unsigned hlen, fsize, i; + static const struct { + int fsize; + char make[12], model[15], withjpeg; + } table[] = { + { 62464, "Kodak", "DC20" ,0 }, + { 124928, "Kodak", "DC20" ,0 }, + { 311696, "ST Micro", "STV680 VGA" ,0 }, /* SPYz */ + { 787456, "Creative", "PC-CAM 600" ,0 }, + { 2465792, "NIKON", "E950" ,1 }, /* or E800,E700 */ + { 2940928, "NIKON", "E2100" ,1 }, /* or E2500 */ + { 4771840, "NIKON", "E990" ,1 }, /* or E995 */ + { 4775936, "NIKON", "E3700" ,1 }, /* or Optio 33WR */ + { 5869568, "NIKON", "E4300" ,1 }, /* or DiMAGE Z2 */ + { 5865472, "NIKON", "E4500" ,0 }, + { 1976352, "CASIO", "QV-2000UX" ,0 }, + { 3217760, "CASIO", "QV-3*00EX" ,0 }, + { 6218368, "CASIO", "QV-5700" ,0 }, + { 7530816, "CASIO", "QV-R51" ,1 }, + { 7684000, "CASIO", "QV-4000" ,0 }, + { 7753344, "CASIO", "EX-Z55" ,1 }, + { 9313536, "CASIO", "EX-P600" ,1 }, + { 10979200, "CASIO", "EX-P700" ,1 }, + { 3178560, "PENTAX", "Optio S" ,1 }, /* 8-bit */ + { 4841984, "PENTAX", "Optio S" ,1 }, /* 12-bit */ + { 6114240, "PENTAX", "Optio S4" ,1 }, /* or S4i */ + { 12582980, "Sinar", "" ,0 } }; + static const char *corp[] = + { "Canon", "NIKON", "EPSON", "Kodak", "OLYMPUS", "PENTAX", + "MINOLTA", "Minolta", "Konica", "CASIO" }; + float tmp; + + /* What format is this file? Set make[] if we recognize it. */ + + raw_height = raw_width = fuji_width = flip = 0; + make[0] = model[0] = model2[0] = 0; + memset (white, 0, sizeof white); + timestamp = tiff_samples = 0; + data_offset = meta_length = tiff_data_compression = 0; + zero_after_ff = is_dng = fuji_secondary = filters = 0; + black = is_foveon = use_coeff = 0; + use_gamma = xmag = ymag = 1; + for (i=0; i < 4; i++) { + cam_mul[i] = 1 & i; + pre_mul[i] = 1; + } + colors = 3; + for (i=0; i < 0x1000; i++) curve[i] = i; + maximum = 0xfff; +#ifdef USE_LCMS + profile_length = 0; +#endif + + order = get2(ifp); + hlen = get4(ifp); + fseek (ifp, 0, SEEK_SET); + fread (head, 1, 32, ifp); + fseek (ifp, 0, SEEK_END); + fsize = ftell(ifp); + if ((c = (char*)memmem_internal(head, 32, "MMMMRawT", 8))) { + strcpy (make, "Phase One"); + data_offset = c - head; + fseek (ifp, data_offset + 8, SEEK_SET); + fseek (ifp, get4(ifp) + 136, SEEK_CUR); + raw_width = get4(ifp); + fseek (ifp, 12, SEEK_CUR); + raw_height = get4(ifp); + } else if (order == 0x4949 || order == 0x4d4d) { + if (!memcmp (head+6, "HEAPCCDR", 8)) { + data_offset = hlen; + parse_ciff(ifp, hlen, fsize - hlen); + } else { + parse_tiff(ifp, 0); + if (!strncmp(make,"NIKON",5) && filters == 0) + make[0] = 0; + } + } else if (!memcmp (head, "\0MRM", 4)) + parse_minolta(ifp); + else if (!memcmp (head, "\xff\xd8\xff\xe1", 4) && + !memcmp (head+6, "Exif", 4)) { + fseek (ifp, 4, SEEK_SET); + fseek (ifp, 4 + get2(ifp), SEEK_SET); + if (fgetc(ifp) != 0xff) + parse_tiff(ifp, 12); + } else if (!memcmp (head, "BM", 2)) { + data_offset = 0x1000; + order = 0x4949; + fseek (ifp, 38, SEEK_SET); + if (get4(ifp) == 2834 && get4(ifp) == 2834) { + strcpy (model, "BMQ"); + flip = 3; + goto nucore; + } + } else if (!memcmp (head, "BR", 2)) { + strcpy (model, "RAW"); +nucore: + strcpy (make, "Nucore"); + order = 0x4949; + fseek (ifp, 10, SEEK_SET); + data_offset += get4(ifp); + get4(ifp); + raw_width = get4(ifp); + raw_height = get4(ifp); + if (model[0] == 'B' && raw_width == 2597) { + raw_width++; + data_offset -= 0x1000; + } + } else if (!memcmp (head+25, "ARECOYK", 7)) { + strcpy (make, "Contax"); + strcpy (model,"N Digital"); + fseek (ifp, 60, SEEK_SET); + camera_red = get4(ifp); + camera_red /= get4(ifp); + camera_blue = get4(ifp); + camera_blue = get4(ifp) / camera_blue; + } else if (!memcmp (head, "FUJIFILM", 8)) { + long data_offset_long; + fseek (ifp, 84, SEEK_SET); + parse_tiff(ifp, get4(ifp)+12); + fseek (ifp, 100, SEEK_SET); + pm_readbiglong(ifp, &data_offset_long); + data_offset = data_offset_long; + } else if (!memcmp (head, "DSC-Image", 9)) + parse_rollei(ifp); + else if (!memcmp (head, "FOVb", 4)) + parse_foveon(ifp); + else + for (i=0; i < sizeof table / sizeof *table; i++) + if (fsize == table[i].fsize) { + strcpy (make, table[i].make ); + strcpy (model, table[i].model); + if (table[i].withjpeg) + parse_external_jpeg(inputFileName); + } + parse_mos(ifp, 8); + parse_mos(ifp, 3472); + + for (i=0; i < sizeof corp / sizeof *corp; i++) + if (strstr (make, corp[i])) /* Simplify company names */ + strcpy (make, corp[i]); + if (!strncmp (make, "KODAK", 5)) + make[16] = model[16] = 0; + c = make + strlen(make); /* Remove trailing spaces */ + while (*--c == ' ') *c = 0; + c = model + strlen(model); + while (*--c == ' ') *c = 0; + i = strlen(make); /* Remove make from model */ + if (!strncmp (model, make, i++)) + memmove (model, model+i, 64-i); + make[63] = model[63] = model2[63] = 0; + + if (make[0] == 0) { + pm_message ("unrecognized file format."); + return 1; + } + +/* File format is OK. Do we know this camera? */ +/* Start with some useful defaults: */ + + top_margin = left_margin = 0; + if ((raw_height | raw_width) < 0) + raw_height = raw_width = 0; + height = raw_height; + width = raw_width; + if (fuji_width) { + width = height + fuji_width; + height = width - 1; + ymag = 1; + } + load_raw = NULL; + if (is_dng) { + strcat (model, " DNG"); + if (!filters) + colors = tiff_samples; + if (tiff_data_compression == 1) + load_raw = adobe_dng_load_raw_nc; + if (tiff_data_compression == 7) + load_raw = adobe_dng_load_raw_lj; + goto dng_skip; + } + +/* We'll try to decode anything from Canon or Nikon. */ + + if (!filters) filters = 0x94949494; + if ((is_canon = !strcmp(make,"Canon"))) + load_raw = memcmp (head+6,"HEAPCCDR",8) ? + lossless_jpeg_load_raw : canon_compressed_load_raw; + if (!strcmp(make,"NIKON")) + load_raw = nikon_is_compressed() ? + nikon_compressed_load_raw : nikon_load_raw; + +/* Set parameters based on camera name (for non-DNG files). */ + + if (is_foveon) { + if (!have64BitArithmetic) + pm_error("This program was built without 64 bit arithmetic " + "capability and the Foveon format requires it."); + if (height*2 < width) ymag = 2; + if (width < height) xmag = 2; + filters = 0; + load_raw = foveon_load_raw; + foveon_coeff(&use_coeff, coeff); + } else if (!strcmp(model,"PowerShot 600")) { + height = 613; + width = 854; + colors = 4; + filters = 0xe1e4e1e4; + load_raw = canon_600_load_raw; + } else if (!strcmp(model,"PowerShot A5") || + !strcmp(model,"PowerShot A5 Zoom")) { + height = 773; + width = 960; + raw_width = 992; + colors = 4; + filters = 0x1e4e1e4e; + load_raw = canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot A50")) { + height = 968; + width = 1290; + raw_width = 1320; + colors = 4; + filters = 0x1b4e4b1e; + load_raw = canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot Pro70")) { + height = 1024; + width = 1552; + colors = 4; + filters = 0x1e4b4e1b; + load_raw = canon_a5_load_raw; + black = 34; + } else if (!strcmp(model,"PowerShot Pro90 IS")) { + width = 1896; + colors = 4; + filters = 0xb4b4b4b4; + } else if (is_canon && raw_width == 2144) { + height = 1550; + width = 2088; + top_margin = 8; + left_margin = 4; + if (!strcmp(model,"PowerShot G1")) { + colors = 4; + filters = 0xb4b4b4b4; + } + } else if (is_canon && raw_width == 2224) { + height = 1448; + width = 2176; + top_margin = 6; + left_margin = 48; + } else if (is_canon && raw_width == 2376) { + height = 1720; + width = 2312; + top_margin = 6; + left_margin = 12; + } else if (is_canon && raw_width == 2672) { + height = 1960; + width = 2616; + top_margin = 6; + left_margin = 12; + } else if (is_canon && raw_width == 3152) { + height = 2056; + width = 3088; + top_margin = 12; + left_margin = 64; + maximum = 0xfa0; + } else if (is_canon && raw_width == 3160) { + height = 2328; + width = 3112; + top_margin = 12; + left_margin = 44; + } else if (is_canon && raw_width == 3344) { + height = 2472; + width = 3288; + top_margin = 6; + left_margin = 4; + } else if (!strcmp(model,"EOS D2000C")) { + filters = 0x61616161; + black = curve[200]; + } else if (!strcmp(model,"EOS-1D")) { + raw_height = height = 1662; + raw_width = width = 2496; + data_offset = 288912; + filters = 0x61616161; + } else if (!strcmp(model,"EOS-1DS")) { + raw_height = height = 2718; + raw_width = width = 4082; + data_offset = 289168; + filters = 0x61616161; + } else if (is_canon && raw_width == 3516) { + top_margin = 14; + left_margin = 42; + goto canon_cr2; + } else if (is_canon && raw_width == 3596) { + top_margin = 12; + left_margin = 74; + goto canon_cr2; + } else if (is_canon && raw_width == 5108) { + top_margin = 13; + left_margin = 98; + maximum = 0xe80; +canon_cr2: + height = raw_height - top_margin; + width = raw_width - left_margin; + } else if (!strcmp(model,"D1")) { + camera_red *= 256/527.0; + camera_blue *= 256/317.0; + } else if (!strcmp(model,"D1X")) { + width = 4024; + ymag = 2; + } else if (!strcmp(model,"D100")) { + if (tiff_data_compression == 34713 && load_raw == nikon_load_raw) + raw_width = (width += 3) + 3; + } else if (!strcmp(model,"D2H")) { + width = 2482; + left_margin = 6; + } else if (!strcmp(model,"D2X")) { + width = 4312; + pre_mul[0] = 1.514; + pre_mul[2] = 1.727; + } else if (fsize == 2465792) { + height = 1203; + width = 1616; + filters = 0x4b4b4b4b; + colors = 4; + load_raw = nikon_e950_load_raw; + nikon_e950_coeff(); + pre_mul[0] = 1.18193; + pre_mul[2] = 1.16452; + pre_mul[3] = 1.17250; + } else if (!strcmp(model,"E990")) { + if (!timestamp && !nikon_e990()) goto cp_e995; + height = 1540; + width = 2064; + colors = 4; + filters = 0xb4b4b4b4; + nikon_e950_coeff(); + pre_mul[0] = 1.196; + pre_mul[1] = 1.246; + pre_mul[2] = 1.018; + } else if (!strcmp(model,"E995")) { +cp_e995: + strcpy (model, "E995"); + height = 1540; + width = 2064; + colors = 4; + filters = 0xe1e1e1e1; + } else if (!strcmp(model,"E2100")) { + if (!timestamp && !nikon_e2100()) goto cp_e2500; + width = 1616; + height = 1206; + load_raw = nikon_e2100_load_raw; + pre_mul[0] = 1.945; + pre_mul[2] = 1.040; + } else if (!strcmp(model,"E2500")) { +cp_e2500: + strcpy (model, "E2500"); + width = 1616; + height = 1204; + colors = 4; + filters = 0x4b4b4b4b; + } else if (!strcmp(model,"E3700")) { + if (!timestamp && pentax_optio33()) goto optio_33wr; + height = 1542; + width = 2064; + load_raw = nikon_e2100_load_raw; + pre_mul[0] = 1.818; + pre_mul[2] = 1.618; + } else if (!strcmp(model,"Optio 33WR")) { +optio_33wr: + strcpy (make, "PENTAX"); + strcpy (model,"Optio 33WR"); + height = 1542; + width = 2064; + load_raw = nikon_e2100_load_raw; + flip = 1; + filters = 0x16161616; + pre_mul[0] = 1.331; + pre_mul[2] = 1.820; + } else if (!strcmp(model,"E4300")) { + if (!timestamp && minolta_z2()) goto dimage_z2; + height = 1710; + width = 2288; + filters = 0x16161616; + pre_mul[0] = 508; + pre_mul[1] = 256; + pre_mul[2] = 322; + } else if (!strcmp(model,"DiMAGE Z2")) { +dimage_z2: + strcpy (make, "MINOLTA"); + strcpy (model,"DiMAGE Z2"); + height = 1710; + width = 2288; + filters = 0x16161616; + load_raw = nikon_e2100_load_raw; + pre_mul[0] = 508; + pre_mul[1] = 256; + pre_mul[2] = 450; + } else if (!strcmp(model,"E4500")) { + height = 1708; + width = 2288; + colors = 4; + filters = 0xb4b4b4b4; + } else if (!strcmp(model,"R-D1")) { + tiff_data_compression = 34713; + load_raw = nikon_load_raw; + } else if (!strcmp(model,"FinePixS2Pro")) { + height = 3584; + width = 3583; + fuji_width = 2144; + filters = 0x61616161; + load_raw = fuji_s2_load_raw; + black = 128; + strcpy (model+7, " S2Pro"); + } else if (!strcmp(model,"FinePix S3Pro")) { + height = 3583; + width = 3584; + fuji_width = 2144; + if (fsize > 18000000 && use_secondary) + data_offset += 4352*2*1444; + filters = 0x49494949; + load_raw = fuji_s3_load_raw; + maximum = 0xffff; + } else if (!strcmp(model,"FinePix S5000")) { + height = 2499; + width = 2500; + fuji_width = 1423; + filters = 0x49494949; + load_raw = fuji_s5000_load_raw; + maximum = 0x3e00; + } else if (!strcmp(model,"FinePix S5100") || + !strcmp(model,"FinePix S5500")) { + height = 1735; + width = 2304; + data_offset += width*10; + filters = 0x49494949; + load_raw = unpacked_load_raw; + maximum = 0xffff; + } else if (!strcmp(model,"FinePix E550") || + !strcmp(model,"FinePix F810") || + !strcmp(model,"FinePix S7000")) { + height = 3587; + width = 3588; + fuji_width = 2047; + filters = 0x49494949; + load_raw = fuji_s7000_load_raw; + maximum = 0x3e00; + } else if (!strcmp(model,"FinePix F700") || + !strcmp(model,"FinePix S20Pro")) { + height = 2523; + width = 2524; + fuji_width = 1440; + filters = 0x49494949; + load_raw = fuji_f700_load_raw; + maximum = 0x3e00; + } else if (!strcmp(model,"Digital Camera KD-400Z")) { + height = 1712; + width = 2312; + raw_width = 2336; + data_offset = 4034; + fseek (ifp, 2032, SEEK_SET); + goto konica_400z; + } else if (!strcmp(model,"Digital Camera KD-510Z")) { + data_offset = 4032; + pre_mul[0] = 1.297; + pre_mul[2] = 1.438; + fseek (ifp, 2032, SEEK_SET); + goto konica_510z; + } else if (!strcasecmp(make,"MINOLTA")) { + load_raw = unpacked_load_raw; + maximum = 0xf7d; + if (!strncmp(model,"DiMAGE A",8)) { + if (!strcmp(model,"DiMAGE A200")) { + filters = 0x49494949; + tmp = camera_red; + camera_red = 1 / camera_blue; + camera_blue = 1 / tmp; + } + load_raw = packed_12_load_raw; + maximum = model[8] == '1' ? 0xf8b : 0xfff; + } else if (!strncmp(model,"ALPHA",5) || + !strncmp(model,"DYNAX",5) || + !strncmp(model,"MAXXUM",6)) { + load_raw = packed_12_load_raw; + maximum = 0xffb; + } else if (!strncmp(model,"DiMAGE G",8)) { + if (model[8] == '4') { + data_offset = 5056; + pre_mul[0] = 1.602; + pre_mul[2] = 1.441; + fseek (ifp, 2078, SEEK_SET); + height = 1716; + width = 2304; + } else if (model[8] == '5') { + data_offset = 4016; + fseek (ifp, 1936, SEEK_SET); +konica_510z: + height = 1956; + width = 2607; + raw_width = 2624; + } else if (model[8] == '6') { + data_offset = 4032; + fseek (ifp, 2030, SEEK_SET); + height = 2136; + width = 2848; + } + filters = 0x61616161; +konica_400z: + load_raw = unpacked_load_raw; + maximum = 0x3df; + order = 0x4d4d; + camera_red = get2(ifp); + camera_blue = get2(ifp); + camera_red /= get2(ifp); + camera_blue /= get2(ifp); + } + if (pre_mul[0] == 1 && pre_mul[2] == 1) { + pre_mul[0] = 1.42; + pre_mul[2] = 1.25; + } + } else if (!strcmp(model,"*ist D")) { + load_raw = unpacked_load_raw; + } else if (!strcmp(model,"*ist DS")) { + height--; + load_raw = packed_12_load_raw; + } else if (!strcmp(model,"Optio S")) { + if (fsize == 3178560) { + height = 1540; + width = 2064; + load_raw = eight_bit_load_raw; + camera_red *= 4; + camera_blue *= 4; + pre_mul[0] = 1.391; + pre_mul[2] = 1.188; + } else { + height = 1544; + width = 2068; + raw_width = 3136; + load_raw = packed_12_load_raw; + maximum = 0xf7c; + pre_mul[0] = 1.137; + pre_mul[2] = 1.453; + } + } else if (!strncmp(model,"Optio S4",8)) { + height = 1737; + width = 2324; + raw_width = 3520; + load_raw = packed_12_load_raw; + maximum = 0xf7a; + pre_mul[0] = 1.980; + pre_mul[2] = 1.570; + } else if (!strcmp(model,"STV680 VGA")) { + height = 484; + width = 644; + load_raw = eight_bit_load_raw; + flip = 2; + filters = 0x16161616; + black = 16; + pre_mul[0] = 1.097; + pre_mul[2] = 1.128; + } else if (!strcmp(make,"Phase One")) { + switch (raw_height) { + case 2060: + strcpy (model, "LightPhase"); + height = 2048; + width = 3080; + top_margin = 5; + left_margin = 22; + pre_mul[0] = 1.331; + pre_mul[2] = 1.154; + break; + case 2682: + strcpy (model, "H10"); + height = 2672; + width = 4012; + top_margin = 5; + left_margin = 26; + break; + case 4128: + strcpy (model, "H20"); + height = 4098; + width = 4098; + top_margin = 20; + left_margin = 26; + pre_mul[0] = 1.963; + pre_mul[2] = 1.430; + break; + case 5488: + strcpy (model, "H25"); + height = 5458; + width = 4098; + top_margin = 20; + left_margin = 26; + pre_mul[0] = 2.80; + pre_mul[2] = 1.20; + } + filters = top_margin & 1 ? 0x94949494 : 0x49494949; + load_raw = phase_one_load_raw; + maximum = 0xffff; + } else if (!strcmp(model,"Ixpress")) { + height = 4084; + width = 4080; + filters = 0x49494949; + load_raw = ixpress_load_raw; + maximum = 0xffff; + pre_mul[0] = 1.963; + pre_mul[2] = 1.430; + } else if (!strcmp(make,"Sinar") && !memcmp(head,"8BPS",4)) { + fseek (ifp, 14, SEEK_SET); + height = get4(ifp); + width = get4(ifp); + filters = 0x61616161; + data_offset = 68; + load_raw = unpacked_load_raw; + maximum = 0xffff; + } else if (!strcmp(make,"Leaf")) { + if (height > width) + filters = 0x16161616; + load_raw = unpacked_load_raw; + maximum = 0x3fff; + strcpy (model, "Valeo"); + if (raw_width == 2060) { + filters = 0; + load_raw = leaf_load_raw; + maximum = 0xffff; + strcpy (model, "Volare"); + } + } else if (!strcmp(model,"DIGILUX 2") || !strcmp(model,"DMC-LC1")) { + height = 1928; + width = 2568; + data_offset = 1024; + load_raw = unpacked_load_raw; + maximum = 0xfff0; + } else if (!strcmp(model,"E-1")) { + filters = 0x61616161; + load_raw = unpacked_load_raw; + maximum = 0xfff0; + black = 1024; + } else if (!strcmp(model,"E-10")) { + load_raw = unpacked_load_raw; + maximum = 0xfff0; + black = 2048; + } else if (!strncmp(model,"E-20",4)) { + load_raw = unpacked_load_raw; + maximum = 0xffc0; + black = 2560; + } else if (!strcmp(model,"E-300")) { + width -= 21; + load_raw = olympus_e300_load_raw; + if (fsize > 15728640) { + load_raw = unpacked_load_raw; + maximum = 0xfc30; + } else + black = 62; + } else if (!strcmp(make,"OLYMPUS")) { + load_raw = olympus_cseries_load_raw; + if (!strcmp(model,"C5050Z") || + !strcmp(model,"C8080WZ")) + filters = 0x16161616; + } else if (!strcmp(model,"N Digital")) { + height = 2047; + width = 3072; + filters = 0x61616161; + data_offset = 0x1a00; + load_raw = packed_12_load_raw; + maximum = 0xf1e; + } else if (!strcmp(model,"DSC-F828")) { + width = 3288; + left_margin = 5; + load_raw = sony_load_raw; + filters = 0x9c9c9c9c; + colors = 4; + black = 491; + } else if (!strcmp(model,"DSC-V3")) { + width = 3109; + left_margin = 59; + load_raw = sony_load_raw; + } else if (!strcasecmp(make,"KODAK")) { + filters = 0x61616161; + if (!strcmp(model,"NC2000F")) { + width -= 4; + left_margin = 1; + for (i=176; i < 0x1000; i++) + curve[i] = curve[i-1]; + pre_mul[0] = 1.509; + pre_mul[2] = 2.686; + } else if (!strcmp(model,"EOSDCS3B")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"EOSDCS1")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"DCS315C")) { + black = 8; + } else if (!strcmp(model,"DCS330C")) { + black = 8; + } else if (!strcmp(model,"DCS420")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"DCS460")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"DCS460A")) { + width -= 4; + left_margin = 2; + colors = 1; + filters = 0; + } else if (!strcmp(model,"DCS520C")) { + black = 180; + } else if (!strcmp(model,"DCS560C")) { + black = 188; + } else if (!strcmp(model,"DCS620C")) { + black = 180; + } else if (!strcmp(model,"DCS620X")) { + black = 185; + } else if (!strcmp(model,"DCS660C")) { + black = 214; + } else if (!strcmp(model,"DCS660M")) { + black = 214; + colors = 1; + filters = 0; + } else if (!strcmp(model,"DCS760M")) { + colors = 1; + filters = 0; + } + switch (tiff_data_compression) { + case 0: /* No compression */ + case 1: + load_raw = kodak_easy_load_raw; + break; + case 7: /* Lossless JPEG */ + load_raw = lossless_jpeg_load_raw; + case 32867: + break; + case 65000: /* Kodak DCR compression */ + if (!have64BitArithmetic) + pm_error("This program was built without 64 bit arithmetic " + "capability, and Kodak DCR compression requires it."); + if (kodak_data_compression == 32803) + load_raw = kodak_compressed_load_raw; + else { + load_raw = kodak_yuv_load_raw; + height = (height+1) & -2; + width = (width +1) & -2; + filters = 0; + } + break; + default: + pm_message ("%s %s uses unrecognized compression method %d.", + make, model, tiff_data_compression); + return 1; + } + if (!strcmp(model,"DC20")) { + height = 242; + if (fsize < 100000) { + width = 249; + raw_width = 256; + } else { + width = 501; + raw_width = 512; + } + data_offset = raw_width + 1; + colors = 4; + filters = 0x8d8d8d8d; + kodak_dc20_coeff (1.0); + pre_mul[1] = 1.179; + pre_mul[2] = 1.209; + pre_mul[3] = 1.036; + load_raw = kodak_easy_load_raw; + } else if (strstr(model,"DC25")) { + strcpy (model, "DC25"); + height = 242; + if (fsize < 100000) { + width = 249; + raw_width = 256; + data_offset = 15681; + } else { + width = 501; + raw_width = 512; + data_offset = 15937; + } + colors = 4; + filters = 0xb4b4b4b4; + load_raw = kodak_easy_load_raw; + } else if (!strcmp(model,"Digital Camera 40")) { + strcpy (model, "DC40"); + height = 512; + width = 768; + data_offset = 1152; + load_raw = kodak_radc_load_raw; + } else if (strstr(model,"DC50")) { + strcpy (model, "DC50"); + height = 512; + width = 768; + data_offset = 19712; + load_raw = kodak_radc_load_raw; + } else if (strstr(model,"DC120")) { + strcpy (model, "DC120"); + height = 976; + width = 848; + if (tiff_data_compression == 7) + load_raw = kodak_jpeg_load_raw; + else + load_raw = kodak_dc120_load_raw; + } + } else if (!strcmp(make,"Rollei")) { + switch (raw_width) { + case 1316: + height = 1030; + width = 1300; + top_margin = 1; + left_margin = 6; + break; + case 2568: + height = 1960; + width = 2560; + top_margin = 2; + left_margin = 8; + } + filters = 0x16161616; + load_raw = rollei_load_raw; + pre_mul[0] = 1.8; + pre_mul[2] = 1.3; + } else if (!strcmp(model,"PC-CAM 600")) { + height = 768; + data_offset = width = 1024; + filters = 0x49494949; + load_raw = eight_bit_load_raw; + pre_mul[0] = 1.14; + pre_mul[2] = 2.73; + } else if (!strcmp(model,"QV-2000UX")) { + height = 1208; + width = 1632; + data_offset = width * 2; + load_raw = eight_bit_load_raw; + } else if (!strcmp(model,"QV-3*00EX")) { + height = 1546; + width = 2070; + raw_width = 2080; + load_raw = eight_bit_load_raw; + } else if (!strcmp(model,"QV-4000")) { + height = 1700; + width = 2260; + load_raw = unpacked_load_raw; + maximum = 0xffff; + } else if (!strcmp(model,"QV-5700")) { + height = 1924; + width = 2576; + load_raw = casio_qv5700_load_raw; + } else if (!strcmp(model,"QV-R51")) { + height = 1926; + width = 2576; + raw_width = 3904; + load_raw = packed_12_load_raw; + pre_mul[0] = 1.340; + pre_mul[2] = 1.672; + } else if (!strcmp(model,"EX-Z55")) { + height = 1960; + width = 2570; + raw_width = 3904; + load_raw = packed_12_load_raw; + pre_mul[0] = 1.520; + pre_mul[2] = 1.316; + } else if (!strcmp(model,"EX-P600")) { + height = 2142; + width = 2844; + raw_width = 4288; + load_raw = packed_12_load_raw; + pre_mul[0] = 1.797; + pre_mul[2] = 1.219; + } else if (!strcmp(model,"EX-P700")) { + height = 2318; + width = 3082; + raw_width = 4672; + load_raw = packed_12_load_raw; + pre_mul[0] = 1.758; + pre_mul[2] = 1.504; + } else if (!strcmp(make,"Nucore")) { + filters = 0x61616161; + load_raw = unpacked_load_raw; + if (width == 2598) { + filters = 0x16161616; + load_raw = nucore_load_raw; + flip = 2; + } + } + if (!use_coeff) adobe_coeff(); +dng_skip: + if (!load_raw || !height) { + pm_message ("This program cannot handle data from %s %s.", + make, model); + return 1; + } +#ifdef NO_JPEG + if (load_raw == kodak_jpeg_load_raw) { + pm_message ("decoder was not linked with libjpeg."); + return 1; + } +#endif + if (!raw_height) raw_height = height; + if (!raw_width ) raw_width = width; + if (use_camera_rgb && colors == 3) + use_coeff = 0; + if (use_coeff) /* Apply user-selected color balance */ + for (i=0; i < colors; i++) { + coeff[0][i] *= red_scale; + coeff[2][i] *= blue_scale; + } + if (four_color_rgb && filters && colors == 3) { + for (i=0; i < 32; i+=4) { + if ((filters >> i & 15) == 9) + filters |= 2 << i; + if ((filters >> i & 15) == 6) + filters |= 8 << i; + } + colors++; + pre_mul[3] = pre_mul[1]; + if (use_coeff) + for (i=0; i < 3; i++) + coeff[i][3] = coeff[i][1] /= 2; + } + fseek (ifp, data_offset, SEEK_SET); + + *loadRawFnP = load_raw; + + return 0; +} diff --git a/converter/other/cameratopam/identify.h b/converter/other/cameratopam/identify.h new file mode 100644 index 00000000..012b807c --- /dev/null +++ b/converter/other/cameratopam/identify.h @@ -0,0 +1,11 @@ +typedef void (*loadRawFn)(); + +int +identify(FILE * const ifp, + bool const use_secondary, + bool const use_camera_rgb, + float const red_scale, + float const blue_scale, + unsigned int const four_color_rgb, + const char * const inputFileName, + loadRawFn * const loadRawFnP); diff --git a/converter/other/cameratopam/ljpeg.c b/converter/other/cameratopam/ljpeg.c new file mode 100644 index 00000000..18423f4f --- /dev/null +++ b/converter/other/cameratopam/ljpeg.c @@ -0,0 +1,141 @@ +#define _BSD_SOURCE /* Make sure string.h containst strcasecmp() */ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <limits.h> + +#include "pm.h" + +#include "global_variables.h" +#include "util.h" +#include "decode.h" +#include "bayer.h" + +#include "ljpeg.h" + + +/* + Not a full implementation of Lossless JPEG, just + enough to decode Canon, Kodak and Adobe DNG images. + */ + +int +ljpeg_start (FILE * ifp, struct jhead *jh) +{ + int i, tag, len; + unsigned char data[256], *dp; + + init_decoder(); + for (i=0; i < 4; i++) + jh->huff[i] = free_decode; + fread (data, 2, 1, ifp); + if (data[0] != 0xff || data[1] != 0xd8) return 0; + do { + fread (data, 2, 2, ifp); + tag = data[0] << 8 | data[1]; + len = (data[2] << 8 | data[3]) - 2; + if (tag <= 0xff00 || len > 255) return 0; + fread (data, 1, len, ifp); + switch (tag) { + case 0xffc3: + jh->bits = data[0]; + jh->high = data[1] << 8 | data[2]; + jh->wide = data[3] << 8 | data[4]; + jh->clrs = data[5]; + break; + case 0xffc4: + for (dp = data; dp < data+len && *dp < 4; ) { + jh->huff[*dp] = free_decode; + dp = make_decoder (++dp, 0); + } + } + } while (tag != 0xffda); + jh->row = calloc (jh->wide*jh->clrs, 2); + if (jh->row == NULL) + pm_error("Out of memory in ljpeg_start()"); + for (i=0; i < 4; i++) + jh->vpred[i] = 1 << (jh->bits-1); + zero_after_ff = 1; + getbits(ifp, -1); + return 1; +} + +int +ljpeg_diff (struct decode *dindex) +{ + int len, diff; + + while (dindex->branch[0]) + dindex = dindex->branch[getbits(ifp, 1)]; + diff = getbits(ifp, len = dindex->leaf); + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + return diff; +} + +void +ljpeg_row (struct jhead *jh) +{ + int col, c, diff; + unsigned short *outp=jh->row; + + for (col=0; col < jh->wide; col++) + for (c=0; c < jh->clrs; c++) { + diff = ljpeg_diff (jh->huff[c]); + *outp = col ? outp[-jh->clrs]+diff : (jh->vpred[c] += diff); + outp++; + } +} + +void +lossless_jpeg_load_raw(void) +{ + int jwide, jrow, jcol, val, jidx, i, row, col; + struct jhead jh; + int min=INT_MAX; + + if (!ljpeg_start (ifp, &jh)) return; + jwide = jh.wide * jh.clrs; + + for (jrow=0; jrow < jh.high; jrow++) { + ljpeg_row (&jh); + for (jcol=0; jcol < jwide; jcol++) { + val = curve[jh.row[jcol]]; + jidx = jrow*jwide + jcol; + if (raw_width == 5108) { + i = jidx / (1680*jh.high); + if (i < 2) { + row = jidx / 1680 % jh.high; + col = jidx % 1680 + i*1680; + } else { + jidx -= 2*1680*jh.high; + row = jidx / 1748; + col = jidx % 1748 + 2*1680; + } + } else if (raw_width == 3516) { + row = jidx / 1758; + col = jidx % 1758; + if (row >= raw_height) { + row -= raw_height; + col += 1758; + } + } else { + row = jidx / raw_width; + col = jidx % raw_width; + } + if ((unsigned) (row-top_margin) >= height) continue; + if ((unsigned) (col-left_margin) < width) { + BAYER(row-top_margin,col-left_margin) = val; + if (min > val) min = val; + } else + black += val; + } + } + free (jh.row); + if (raw_width > width) + black /= (raw_width - width) * height; + if (!strcasecmp(make,"KODAK")) + black = min; +} + + diff --git a/converter/other/cameratopam/ljpeg.h b/converter/other/cameratopam/ljpeg.h new file mode 100644 index 00000000..60832a3d --- /dev/null +++ b/converter/other/cameratopam/ljpeg.h @@ -0,0 +1,17 @@ +struct jhead { + int bits, high, wide, clrs, vpred[4]; + struct decode *huff[4]; + unsigned short *row; +}; + +void +lossless_jpeg_load_raw(void); + +int +ljpeg_start (FILE * ifp, struct jhead *jh); + +int +ljpeg_diff (struct decode *dindex); + +void +ljpeg_row (struct jhead *jh); diff --git a/converter/other/cameratopam/util.c b/converter/other/cameratopam/util.c new file mode 100644 index 00000000..b3ccf7e9 --- /dev/null +++ b/converter/other/cameratopam/util.c @@ -0,0 +1,83 @@ +#define _XOPEN_SOURCE /* Make sure unistd.h contains swab() */ +#include <unistd.h> +#include <stdio.h> + +#include "pm.h" +#include "global_variables.h" +#include "util.h" + +#ifndef LONG_BITS +#define LONG_BITS (8 * sizeof(long)) +#endif +/* + Get a 2-byte integer, making no assumptions about CPU byte order. + Nor should we assume that the compiler evaluates left-to-right. + */ +unsigned short +get2(FILE * const ifp) +{ + unsigned char a, b; + + a = fgetc(ifp); b = fgetc(ifp); + + if (order == 0x4949) /* "II" means little-endian */ + return a | b << 8; + else /* "MM" means big-endian */ + return a << 8 | b; +} + +/* + Same for a 4-byte integer. + */ +int +get4(FILE * const ifp) +{ + unsigned char a, b, c, d; + + a = fgetc(ifp); b = fgetc(ifp); + c = fgetc(ifp); d = fgetc(ifp); + + if (order == 0x4949) + return a | b << 8 | c << 16 | d << 24; + else + return a << 24 | b << 16 | c << 8 | d; +} + +/* + Faster than calling get2() multiple times. + */ +void +read_shorts (FILE * const ifp, unsigned short *pixel, int count) +{ + fread (pixel, 2, count, ifp); + if ((order == 0x4949) == (BYTE_ORDER == BIG_ENDIAN)) + swab (pixel, pixel, count*2); +} + +/* + getbits(-1) initializes the buffer + getbits(n) where 0 <= n <= 25 returns an n-bit integer + */ +unsigned +getbits (FILE * const ifp, int nbits) +{ + static unsigned long bitbuf=0; + static int vbits=0; + unsigned c, ret; + + if (nbits == 0) return 0; + if (nbits == -1) + ret = bitbuf = vbits = 0; + else { + ret = bitbuf << (LONG_BITS - vbits) >> (LONG_BITS - nbits); + vbits -= nbits; + } + while (vbits < LONG_BITS - 7) { + c = fgetc(ifp); + bitbuf = (bitbuf << 8) + c; + if (c == 0xff && zero_after_ff) + fgetc(ifp); + vbits += 8; + } + return ret; +} diff --git a/converter/other/cameratopam/util.h b/converter/other/cameratopam/util.h new file mode 100644 index 00000000..bf9006d3 --- /dev/null +++ b/converter/other/cameratopam/util.h @@ -0,0 +1,16 @@ +#ifndef UTIL_H_INCLUDED +#define UTIL_H_INCLUDED + +unsigned short +get2(FILE * const ifp); + +int +get4(FILE * const ifp); + +void +read_shorts (FILE * const ifp, unsigned short *pixel, int count); + +unsigned int +getbits (FILE * const ifp, int nbits); + +#endif |