Merge commit '638ecc4cde79b0296f2e04f683ca4cb881c28f36' as 'rtengine/libraw'

This commit is contained in:
Lawrence Lee
2023-11-12 11:49:00 -08:00
213 changed files with 77043 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
/* -*- C++ -*-
* Copyright 2019-2021 LibRaw LLC (info@libraw.org)
*
LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
LibRaw do not use RESTRICTED code from dcraw.c
LibRaw is free software; you can redistribute it and/or modify
it under the terms of the one of two licenses as you choose:
1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
(See file LICENSE.LGPL provided in LibRaw distribution archive for details).
2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
(See file LICENSE.CDDL provided in LibRaw distribution archive for details).
*/
#include "../../internal/dcraw_defs.h"
void LibRaw::fuji_rotate()
{
int i, row, col;
double step;
float r, c, fr, fc;
unsigned ur, uc;
ushort wide, high, (*img)[4], (*pix)[4];
if (!fuji_width)
return;
fuji_width = (fuji_width - 1 + shrink) >> shrink;
step = sqrt(0.5);
wide = fuji_width / step;
high = (height - fuji_width) / step;
// All real fuji/rotated images are small, so check against max_raw_memory_mb here is safe
if (INT64(wide) * INT64(high) * INT64(sizeof(*img)) >
INT64(imgdata.rawparams.max_raw_memory_mb) * INT64(1024 * 1024))
throw LIBRAW_EXCEPTION_TOOBIG;
img = (ushort(*)[4])calloc(high, wide * sizeof *img);
RUN_CALLBACK(LIBRAW_PROGRESS_FUJI_ROTATE, 0, 2);
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 > (unsigned)height - 2 || uc > (unsigned)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;
RUN_CALLBACK(LIBRAW_PROGRESS_FUJI_ROTATE, 1, 2);
}
void LibRaw::stretch()
{
ushort newdim, (*img)[4], *pix0, *pix1;
int row, col, c;
double rc, frac;
if (pixel_aspect == 1)
return;
RUN_CALLBACK(LIBRAW_PROGRESS_STRETCH, 0, 2);
if (pixel_aspect < 1)
{
newdim = height / pixel_aspect + 0.5;
img = (ushort(*)[4])calloc(width, newdim * sizeof *img);
for (rc = row = 0; row < newdim; row++, rc += pixel_aspect)
{
frac = rc - (c = rc);
pix0 = pix1 = image[c * width];
if (c + 1 < height)
pix1 += width * 4;
for (col = 0; col < width; col++, pix0 += 4, pix1 += 4)
FORCC img[row * width + col][c] =
pix0[c] * (1 - frac) + pix1[c] * frac + 0.5;
}
height = newdim;
}
else
{
newdim = width * pixel_aspect + 0.5;
img = (ushort(*)[4])calloc(height, newdim * sizeof *img);
for (rc = col = 0; col < newdim; col++, rc += 1 / pixel_aspect)
{
frac = rc - (c = rc);
pix0 = pix1 = image[c];
if (c + 1 < width)
pix1 += 4;
for (row = 0; row < height; row++, pix0 += width * 4, pix1 += width * 4)
FORCC img[row * newdim + col][c] =
pix0[c] * (1 - frac) + pix1[c] * frac + 0.5;
}
width = newdim;
}
free(image);
image = img;
RUN_CALLBACK(LIBRAW_PROGRESS_STRETCH, 1, 2);
}

View File

@@ -0,0 +1,259 @@
/* -*- C++ -*-
* Copyright 2019-2021 LibRaw LLC (info@libraw.org)
*
LibRaw is free software; you can redistribute it and/or modify
it under the terms of the one of two licenses as you choose:
1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
(See file LICENSE.LGPL provided in LibRaw distribution archive for details).
2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
(See file LICENSE.CDDL provided in LibRaw distribution archive for details).
*/
#include "../../internal/libraw_cxx_defs.h"
int LibRaw::dcraw_process(void)
{
int quality, i;
int iterations = -1, dcb_enhance = 1, noiserd = 0;
float preser = 0;
float expos = 1.0;
CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW);
// CHECK_ORDER_HIGH(LIBRAW_PROGRESS_PRE_INTERPOLATE);
try
{
int no_crop = 1;
if (~O.cropbox[2] && ~O.cropbox[3])
no_crop = 0;
libraw_decoder_info_t di;
get_decoder_info(&di);
bool is_bayer = (imgdata.idata.filters || P1.colors == 1);
int subtract_inline =
!O.bad_pixels && !O.dark_frame && is_bayer && !IO.zero_is_bad;
int rc = raw2image_ex(subtract_inline); // allocate imgdata.image and copy data!
if (rc != LIBRAW_SUCCESS)
return rc;
// Adjust sizes
int save_4color = O.four_color_rgb;
if (IO.zero_is_bad)
{
remove_zeroes();
SET_PROC_FLAG(LIBRAW_PROGRESS_REMOVE_ZEROES);
}
if (O.bad_pixels && no_crop)
{
bad_pixels(O.bad_pixels);
SET_PROC_FLAG(LIBRAW_PROGRESS_BAD_PIXELS);
}
if (O.dark_frame && no_crop)
{
subtract(O.dark_frame);
SET_PROC_FLAG(LIBRAW_PROGRESS_DARK_FRAME);
}
/* pre subtract black callback: check for it above to disable subtract
* inline */
if (callbacks.pre_subtractblack_cb)
(callbacks.pre_subtractblack_cb)(this);
quality = 2 + !IO.fuji_width;
if (O.user_qual >= 0)
quality = O.user_qual;
if (!subtract_inline || !C.data_maximum)
{
adjust_bl();
subtract_black_internal();
}
if (!(di.decoder_flags & LIBRAW_DECODER_FIXEDMAXC))
adjust_maximum();
if (O.user_sat > 0)
C.maximum = O.user_sat;
if (P1.is_foveon)
{
if (load_raw == &LibRaw::x3f_load_raw)
{
// Filter out zeroes
for (int q = 0; q < S.height * S.width; q++)
{
for (int c = 0; c < 4; c++)
if ((short)imgdata.image[q][c] < 0)
imgdata.image[q][c] = 0;
}
}
SET_PROC_FLAG(LIBRAW_PROGRESS_FOVEON_INTERPOLATE);
}
if (O.green_matching && !O.half_size)
{
green_matching();
}
if (callbacks.pre_scalecolors_cb)
(callbacks.pre_scalecolors_cb)(this);
if (!O.no_auto_scale)
{
scale_colors();
SET_PROC_FLAG(LIBRAW_PROGRESS_SCALE_COLORS);
}
if (callbacks.pre_preinterpolate_cb)
(callbacks.pre_preinterpolate_cb)(this);
pre_interpolate();
SET_PROC_FLAG(LIBRAW_PROGRESS_PRE_INTERPOLATE);
if (O.dcb_iterations >= 0)
iterations = O.dcb_iterations;
if (O.dcb_enhance_fl >= 0)
dcb_enhance = O.dcb_enhance_fl;
if (O.fbdd_noiserd >= 0)
noiserd = O.fbdd_noiserd;
/* pre-exposure correction callback */
if (O.exp_correc > 0)
{
expos = O.exp_shift;
preser = O.exp_preser;
exp_bef(expos, preser);
}
if (callbacks.pre_interpolate_cb)
(callbacks.pre_interpolate_cb)(this);
/* post-exposure correction fallback */
if (P1.filters && !O.no_interpolation)
{
if (noiserd > 0 && P1.colors == 3 && P1.filters > 1000)
fbdd(noiserd);
if (P1.filters > 1000 && callbacks.interpolate_bayer_cb)
(callbacks.interpolate_bayer_cb)(this);
else if (P1.filters == 9 && callbacks.interpolate_xtrans_cb)
(callbacks.interpolate_xtrans_cb)(this);
else if (quality == 0)
lin_interpolate();
else if (quality == 1 || P1.colors > 3)
vng_interpolate();
else if (quality == 2 && P1.filters > 1000)
ppg_interpolate();
else if (P1.filters == LIBRAW_XTRANS)
{
// Fuji X-Trans
xtrans_interpolate(quality > 2 ? 3 : 1);
}
else if (quality == 3)
ahd_interpolate(); // really don't need it here due to fallback op
else if (quality == 4)
dcb(iterations, dcb_enhance);
else if (quality == 11)
dht_interpolate();
else if (quality == 12)
aahd_interpolate();
// fallback to AHD
else
{
ahd_interpolate();
imgdata.process_warnings |= LIBRAW_WARN_FALLBACK_TO_AHD;
}
SET_PROC_FLAG(LIBRAW_PROGRESS_INTERPOLATE);
}
if (IO.mix_green)
{
for (P1.colors = 3, i = 0; i < S.height * S.width; i++)
imgdata.image[i][1] = (imgdata.image[i][1] + imgdata.image[i][3]) >> 1;
SET_PROC_FLAG(LIBRAW_PROGRESS_MIX_GREEN);
}
if (callbacks.post_interpolate_cb)
(callbacks.post_interpolate_cb)(this);
else if (!P1.is_foveon && P1.colors == 3 && O.med_passes > 0)
{
median_filter();
SET_PROC_FLAG(LIBRAW_PROGRESS_MEDIAN_FILTER);
}
if (O.highlight == 2)
{
blend_highlights();
SET_PROC_FLAG(LIBRAW_PROGRESS_HIGHLIGHTS);
}
if (O.highlight > 2)
{
recover_highlights();
SET_PROC_FLAG(LIBRAW_PROGRESS_HIGHLIGHTS);
}
if (O.use_fuji_rotate)
{
fuji_rotate();
SET_PROC_FLAG(LIBRAW_PROGRESS_FUJI_ROTATE);
}
if (!libraw_internal_data.output_data.histogram)
{
libraw_internal_data.output_data.histogram =
(int(*)[LIBRAW_HISTOGRAM_SIZE])malloc(
sizeof(*libraw_internal_data.output_data.histogram) * 4);
}
#ifndef NO_LCMS
if (O.camera_profile)
{
apply_profile(O.camera_profile, O.output_profile);
SET_PROC_FLAG(LIBRAW_PROGRESS_APPLY_PROFILE);
}
#endif
if (callbacks.pre_converttorgb_cb)
(callbacks.pre_converttorgb_cb)(this);
convert_to_rgb();
SET_PROC_FLAG(LIBRAW_PROGRESS_CONVERT_RGB);
if (callbacks.post_converttorgb_cb)
(callbacks.post_converttorgb_cb)(this);
if (O.use_fuji_rotate)
{
stretch();
SET_PROC_FLAG(LIBRAW_PROGRESS_STRETCH);
}
O.four_color_rgb = save_4color; // also, restore
return 0;
}
catch (const std::bad_alloc&)
{
recycle();
return LIBRAW_UNSUFFICIENT_MEMORY;
}
catch (const LibRaw_exceptions& err)
{
EXCEPTION_HANDLER(err);
}
}

View File

@@ -0,0 +1,292 @@
/* -*- C++ -*-
* Copyright 2019-2021 LibRaw LLC (info@libraw.org)
*
LibRaw is free software; you can redistribute it and/or modify
it under the terms of the one of two licenses as you choose:
1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
(See file LICENSE.LGPL provided in LibRaw distribution archive for details).
2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
(See file LICENSE.CDDL provided in LibRaw distribution archive for details).
*/
#include "../../internal/libraw_cxx_defs.h"
libraw_processed_image_t *LibRaw::dcraw_make_mem_thumb(int *errcode)
{
if (!T.thumb)
{
if (!ID.toffset && !(imgdata.thumbnail.tlength > 0 &&
load_raw == &LibRaw::broadcom_load_raw) // RPi
)
{
if (errcode)
*errcode = LIBRAW_NO_THUMBNAIL;
}
else
{
if (errcode)
*errcode = LIBRAW_OUT_OF_ORDER_CALL;
}
return NULL;
}
if (T.tlength < 64u)
{
if (errcode)
*errcode = EINVAL;
return NULL;
}
if (INT64(T.tlength) > 1024ULL * 1024ULL * LIBRAW_MAX_THUMBNAIL_MB)
{
if (errcode)
*errcode = LIBRAW_TOO_BIG;
return NULL;
}
if (T.tformat == LIBRAW_THUMBNAIL_BITMAP)
{
libraw_processed_image_t *ret = (libraw_processed_image_t *)::malloc(
sizeof(libraw_processed_image_t) + T.tlength);
if (!ret)
{
if (errcode)
*errcode = ENOMEM;
return NULL;
}
memset(ret, 0, sizeof(libraw_processed_image_t));
ret->type = LIBRAW_IMAGE_BITMAP;
ret->height = T.theight;
ret->width = T.twidth;
if (T.tcolors > 0 && T.tcolors < 4)
ret->colors = T.tcolors;
else
ret->colors = 3; // defaults
ret->bits = 8;
ret->data_size = T.tlength;
memmove(ret->data, T.thumb, T.tlength);
if (errcode)
*errcode = 0;
return ret;
}
else if (T.tformat == LIBRAW_THUMBNAIL_JPEG)
{
ushort exif[5];
int mk_exif = 0;
if (strcmp(T.thumb + 6, "Exif"))
mk_exif = 1;
int dsize = T.tlength + mk_exif * (sizeof(exif) + sizeof(tiff_hdr));
libraw_processed_image_t *ret = (libraw_processed_image_t *)::malloc(
sizeof(libraw_processed_image_t) + dsize);
if (!ret)
{
if (errcode)
*errcode = ENOMEM;
return NULL;
}
memset(ret, 0, sizeof(libraw_processed_image_t));
ret->type = LIBRAW_IMAGE_JPEG;
ret->data_size = dsize;
ret->data[0] = 0xff;
ret->data[1] = 0xd8;
if (mk_exif)
{
struct tiff_hdr th;
memcpy(exif, "\xff\xe1 Exif\0\0", 10);
exif[1] = htons(8 + sizeof th);
memmove(ret->data + 2, exif, sizeof(exif));
tiff_head(&th, 0);
memmove(ret->data + (2 + sizeof(exif)), &th, sizeof(th));
memmove(ret->data + (2 + sizeof(exif) + sizeof(th)), T.thumb + 2,
T.tlength - 2);
}
else
{
memmove(ret->data + 2, T.thumb + 2, T.tlength - 2);
}
if (errcode)
*errcode = 0;
return ret;
}
else
{
if (errcode)
*errcode = LIBRAW_UNSUPPORTED_THUMBNAIL;
return NULL;
}
}
// jlb
// macros for copying pixels to either BGR or RGB formats
#define FORBGR for (c = P1.colors - 1; c >= 0; c--)
#define FORRGB for (c = 0; c < P1.colors; c++)
void LibRaw::get_mem_image_format(int *width, int *height, int *colors,
int *bps) const
{
*width = S.width;
*height = S.height;
if (imgdata.progress_flags < LIBRAW_PROGRESS_FUJI_ROTATE)
{
if (O.use_fuji_rotate)
{
if (IO.fuji_width)
{
int fuji_width = (IO.fuji_width - 1 + IO.shrink) >> IO.shrink;
*width = (ushort)(fuji_width / sqrt(0.5));
*height = (ushort)((*height - fuji_width) / sqrt(0.5));
}
else
{
if (S.pixel_aspect < 0.995)
*height = (ushort)(*height / S.pixel_aspect + 0.5);
if (S.pixel_aspect > 1.005)
*width = (ushort)(*width * S.pixel_aspect + 0.5);
}
}
}
if (S.flip & 4)
{
std::swap(*width, *height);
}
*colors = P1.colors;
*bps = O.output_bps;
}
int LibRaw::copy_mem_image(void *scan0, int stride, int bgr)
{
// the image memory pointed to by scan0 is assumed to be in the format
// returned by get_mem_image_format
if ((imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK) <
LIBRAW_PROGRESS_PRE_INTERPOLATE)
return LIBRAW_OUT_OF_ORDER_CALL;
if (libraw_internal_data.output_data.histogram)
{
int perc, val, total, t_white = 0x2000, c;
perc = S.width * S.height * O.auto_bright_thr;
if (IO.fuji_width)
perc /= 2;
if (!((O.highlight & ~2) || O.no_auto_bright))
for (t_white = c = 0; c < P1.colors; c++)
{
for (val = 0x2000, total = 0; --val > 32;)
if ((total += libraw_internal_data.output_data.histogram[c][val]) >
perc)
break;
if (t_white < val)
t_white = val;
}
gamma_curve(O.gamm[0], O.gamm[1], 2, (t_white << 3) / O.bright);
}
int s_iheight = S.iheight;
int s_iwidth = S.iwidth;
int s_width = S.width;
int s_hwight = S.height;
S.iheight = S.height;
S.iwidth = S.width;
if (S.flip & 4)
SWAP(S.height, S.width);
uchar *ppm;
ushort *ppm2;
int c, row, col, soff, rstep, cstep;
soff = flip_index(0, 0);
cstep = flip_index(0, 1) - soff;
rstep = flip_index(1, 0) - flip_index(0, S.width);
for (row = 0; row < S.height; row++, soff += rstep)
{
uchar *bufp = ((uchar *)scan0) + row * stride;
ppm2 = (ushort *)(ppm = bufp);
// keep trivial decisions in the outer loop for speed
if (bgr)
{
if (O.output_bps == 8)
{
for (col = 0; col < S.width; col++, soff += cstep)
FORBGR *ppm++ = imgdata.color.curve[imgdata.image[soff][c]] >> 8;
}
else
{
for (col = 0; col < S.width; col++, soff += cstep)
FORBGR *ppm2++ = imgdata.color.curve[imgdata.image[soff][c]];
}
}
else
{
if (O.output_bps == 8)
{
for (col = 0; col < S.width; col++, soff += cstep)
FORRGB *ppm++ = imgdata.color.curve[imgdata.image[soff][c]] >> 8;
}
else
{
for (col = 0; col < S.width; col++, soff += cstep)
FORRGB *ppm2++ = imgdata.color.curve[imgdata.image[soff][c]];
}
}
// bufp += stride; // go to the next line
}
S.iheight = s_iheight;
S.iwidth = s_iwidth;
S.width = s_width;
S.height = s_hwight;
return 0;
}
#undef FORBGR
#undef FORRGB
libraw_processed_image_t *LibRaw::dcraw_make_mem_image(int *errcode)
{
int width, height, colors, bps;
get_mem_image_format(&width, &height, &colors, &bps);
int stride = width * (bps / 8) * colors;
unsigned ds = height * stride;
libraw_processed_image_t *ret = (libraw_processed_image_t *)::malloc(
sizeof(libraw_processed_image_t) + ds);
if (!ret)
{
if (errcode)
*errcode = ENOMEM;
return NULL;
}
memset(ret, 0, sizeof(libraw_processed_image_t));
// metadata init
ret->type = LIBRAW_IMAGE_BITMAP;
ret->height = height;
ret->width = width;
ret->colors = colors;
ret->bits = bps;
ret->data_size = ds;
copy_mem_image(ret->data, stride, 0);
return ret;
}
void LibRaw::dcraw_clear_mem(libraw_processed_image_t *p)
{
if (p)
::free(p);
}

View File

@@ -0,0 +1,406 @@
/* -*- C++ -*-
* Copyright 2019-2021 LibRaw LLC (info@libraw.org)
*
LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
LibRaw do not use RESTRICTED code from dcraw.c
LibRaw is free software; you can redistribute it and/or modify
it under the terms of the one of two licenses as you choose:
1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
(See file LICENSE.LGPL provided in LibRaw distribution archive for details).
2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
(See file LICENSE.CDDL provided in LibRaw distribution archive for details).
*/
#include "../../internal/dcraw_defs.h"
void LibRaw::hat_transform(float *temp, float *base, int st, int size, int sc)
{
int i;
for (i = 0; i < sc; i++)
temp[i] = 2 * base[st * i] + base[st * (sc - i)] + base[st * (i + sc)];
for (; i + sc < size; i++)
temp[i] = 2 * base[st * i] + base[st * (i - sc)] + base[st * (i + sc)];
for (; i < size; i++)
temp[i] = 2 * base[st * i] + base[st * (i - sc)] +
base[st * (2 * size - 2 - (i + sc))];
}
#if !defined(LIBRAW_USE_OPENMP)
void LibRaw::wavelet_denoise()
{
float *fimg = 0, *temp, thold, mul[2], avg, diff;
int scale = 1, size, lev, hpass, lpass, row, col, nc, c, i, wlast, blk[2];
ushort *window[4];
static const float noise[] = {0.8002f, 0.2735f, 0.1202f, 0.0585f,
0.0291f, 0.0152f, 0.0080f, 0.0044f};
while (maximum << scale < 0x10000)
scale++;
maximum <<= --scale;
black <<= scale;
FORC4 cblack[c] <<= scale;
if ((size = iheight * iwidth) < 0x15550000)
fimg = (float *)malloc((size * 3 + iheight + iwidth + 128) * sizeof *fimg);
temp = fimg + size * 3;
if ((nc = colors) == 3 && filters)
nc++;
FORC(nc)
{ /* denoise R,G1,B,G3 individually */
for (i = 0; i < size; i++)
fimg[i] = 256 * sqrt((double)(image[i][c] << scale));
for (hpass = lev = 0; lev < 5; lev++)
{
lpass = size * ((lev & 1) + 1);
for (row = 0; row < iheight; row++)
{
hat_transform(temp, fimg + hpass + row * iwidth, 1, iwidth, 1 << lev);
for (col = 0; col < iwidth; col++)
fimg[lpass + row * iwidth + col] = temp[col] * 0.25;
}
for (col = 0; col < iwidth; col++)
{
hat_transform(temp, fimg + lpass + col, iwidth, iheight, 1 << lev);
for (row = 0; row < iheight; row++)
fimg[lpass + row * iwidth + col] = temp[row] * 0.25;
}
thold = threshold * noise[lev];
for (i = 0; i < size; i++)
{
fimg[hpass + i] -= fimg[lpass + i];
if (fimg[hpass + i] < -thold)
fimg[hpass + i] += thold;
else if (fimg[hpass + i] > thold)
fimg[hpass + i] -= thold;
else
fimg[hpass + i] = 0;
if (hpass)
fimg[i] += fimg[hpass + i];
}
hpass = lpass;
}
for (i = 0; i < size; i++)
image[i][c] = CLIP(SQR(fimg[i] + fimg[lpass + i]) / 0x10000);
}
if (filters && colors == 3)
{ /* pull G1 and G3 closer together */
for (row = 0; row < 2; row++)
{
mul[row] = 0.125 * pre_mul[FC(row + 1, 0) | 1] / pre_mul[FC(row, 0) | 1];
blk[row] = cblack[FC(row, 0) | 1];
}
for (i = 0; i < 4; i++)
window[i] = (ushort *)fimg + width * i;
for (wlast = -1, row = 1; row < height - 1; row++)
{
while (wlast < row + 1)
{
for (wlast++, i = 0; i < 4; i++)
window[(i + 3) & 3] = window[i];
for (col = FC(wlast, 1) & 1; col < width; col += 2)
window[2][col] = BAYER(wlast, col);
}
thold = threshold / 512;
for (col = (FC(row, 0) & 1) + 1; col < width - 1; col += 2)
{
avg = (window[0][col - 1] + window[0][col + 1] + window[2][col - 1] +
window[2][col + 1] - blk[~row & 1] * 4) *
mul[row & 1] +
(window[1][col] + blk[row & 1]) * 0.5;
avg = avg < 0 ? 0 : sqrt(avg);
diff = sqrt((double)BAYER(row, col)) - avg;
if (diff < -thold)
diff += thold;
else if (diff > thold)
diff -= thold;
else
diff = 0;
BAYER(row, col) = CLIP(SQR(avg + diff) + 0.5);
}
}
}
free(fimg);
}
#else /* LIBRAW_USE_OPENMP */
void LibRaw::wavelet_denoise()
{
float *fimg = 0, *temp, thold, mul[2], avg, diff;
int scale = 1, size, lev, hpass, lpass, row, col, nc, c, i, wlast, blk[2];
ushort *window[4];
static const float noise[] = {0.8002, 0.2735, 0.1202, 0.0585,
0.0291, 0.0152, 0.0080, 0.0044};
while (maximum << scale < 0x10000)
scale++;
maximum <<= --scale;
black <<= scale;
FORC4 cblack[c] <<= scale;
if ((size = iheight * iwidth) < 0x15550000)
fimg = (float *)malloc((size * 3 + iheight + iwidth) * sizeof *fimg);
temp = fimg + size * 3;
if ((nc = colors) == 3 && filters)
nc++;
#pragma omp parallel default(shared) private( \
i, col, row, thold, lev, lpass, hpass, temp, c) firstprivate(scale, size)
{
temp = (float *)malloc((iheight + iwidth) * sizeof *fimg);
FORC(nc)
{ /* denoise R,G1,B,G3 individually */
#pragma omp for
for (i = 0; i < size; i++)
fimg[i] = 256 * sqrt((double)(image[i][c] << scale));
for (hpass = lev = 0; lev < 5; lev++)
{
lpass = size * ((lev & 1) + 1);
#pragma omp for
for (row = 0; row < iheight; row++)
{
hat_transform(temp, fimg + hpass + row * iwidth, 1, iwidth, 1 << lev);
for (col = 0; col < iwidth; col++)
fimg[lpass + row * iwidth + col] = temp[col] * 0.25;
}
#pragma omp for
for (col = 0; col < iwidth; col++)
{
hat_transform(temp, fimg + lpass + col, iwidth, iheight, 1 << lev);
for (row = 0; row < iheight; row++)
fimg[lpass + row * iwidth + col] = temp[row] * 0.25;
}
thold = threshold * noise[lev];
#pragma omp for
for (i = 0; i < size; i++)
{
fimg[hpass + i] -= fimg[lpass + i];
if (fimg[hpass + i] < -thold)
fimg[hpass + i] += thold;
else if (fimg[hpass + i] > thold)
fimg[hpass + i] -= thold;
else
fimg[hpass + i] = 0;
if (hpass)
fimg[i] += fimg[hpass + i];
}
hpass = lpass;
}
#pragma omp for
for (i = 0; i < size; i++)
image[i][c] = CLIP(SQR(fimg[i] + fimg[lpass + i]) / 0x10000);
}
free(temp);
} /* end omp parallel */
/* the following loops are hard to parallelize, no idea yes,
* problem is wlast which is carrying dependency
* second part should be easier, but did not yet get it right.
*/
if (filters && colors == 3)
{ /* pull G1 and G3 closer together */
for (row = 0; row < 2; row++)
{
mul[row] = 0.125 * pre_mul[FC(row + 1, 0) | 1] / pre_mul[FC(row, 0) | 1];
blk[row] = cblack[FC(row, 0) | 1];
}
for (i = 0; i < 4; i++)
window[i] = (ushort *)fimg + width * i;
for (wlast = -1, row = 1; row < height - 1; row++)
{
while (wlast < row + 1)
{
for (wlast++, i = 0; i < 4; i++)
window[(i + 3) & 3] = window[i];
for (col = FC(wlast, 1) & 1; col < width; col += 2)
window[2][col] = BAYER(wlast, col);
}
thold = threshold / 512;
for (col = (FC(row, 0) & 1) + 1; col < width - 1; col += 2)
{
avg = (window[0][col - 1] + window[0][col + 1] + window[2][col - 1] +
window[2][col + 1] - blk[~row & 1] * 4) *
mul[row & 1] +
(window[1][col] + blk[row & 1]) * 0.5;
avg = avg < 0 ? 0 : sqrt(avg);
diff = sqrt((double)BAYER(row, col)) - avg;
if (diff < -thold)
diff += thold;
else if (diff > thold)
diff -= thold;
else
diff = 0;
BAYER(row, col) = CLIP(SQR(avg + diff) + 0.5);
}
}
}
free(fimg);
}
#endif
void LibRaw::median_filter()
{
ushort(*pix)[4];
int pass, c, i, j, k, med[9];
static const uchar opt[] = /* Optimal 9-element median search */
{1, 2, 4, 5, 7, 8, 0, 1, 3, 4, 6, 7, 1, 2, 4, 5, 7, 8, 0,
3, 5, 8, 4, 7, 3, 6, 1, 4, 2, 5, 4, 7, 4, 2, 6, 4, 4, 2};
for (pass = 1; pass <= med_passes; pass++)
{
RUN_CALLBACK(LIBRAW_PROGRESS_MEDIAN_FILTER, pass - 1, med_passes);
for (c = 0; c < 3; c += 2)
{
for (pix = image; pix < image + width * height; pix++)
pix[0][3] = pix[0][c];
for (pix = image + width; pix < image + width * (height - 1); pix++)
{
if ((pix - image + 1) % width < 2)
continue;
for (k = 0, i = -width; i <= width; i += width)
for (j = i - 1; j <= i + 1; j++)
med[k++] = pix[j][3] - pix[j][1];
for (i = 0; i < int(sizeof opt); i += 2)
if (med[opt[i]] > med[opt[i + 1]])
SWAP(med[opt[i]], med[opt[i + 1]]);
pix[0][c] = CLIP(med[4] + pix[0][1]);
}
}
}
}
void LibRaw::blend_highlights()
{
int clip = INT_MAX, row, col, c, i, j;
static const float trans[2][4][4] = {
{{1, 1, 1}, {1.7320508f, -1.7320508f, 0}, {-1, -1, 2}},
{{1, 1, 1, 1}, {1, -1, 1, -1}, {1, 1, -1, -1}, {1, -1, -1, 1}}};
static const float itrans[2][4][4] = {
{{1, 0.8660254f, -0.5}, {1, -0.8660254f, -0.5}, {1, 0, 1}},
{{1, 1, 1, 1}, {1, -1, 1, -1}, {1, 1, -1, -1}, {1, -1, -1, 1}}};
float cam[2][4], lab[2][4], sum[2], chratio;
if ((unsigned)(colors - 3) > 1)
return;
RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS, 0, 2);
FORCC if (clip > (i = 65535 * pre_mul[c])) clip = i;
for (row = 0; row < height; row++)
for (col = 0; col < width; col++)
{
FORCC if (image[row * width + col][c] > clip) break;
if (c == colors)
continue;
FORCC
{
cam[0][c] = image[row * width + col][c];
cam[1][c] = MIN(cam[0][c], clip);
}
for (i = 0; i < 2; i++)
{
FORCC for (lab[i][c] = j = 0; j < colors; j++) lab[i][c] +=
trans[colors - 3][c][j] * cam[i][j];
for (sum[i] = 0, c = 1; c < colors; c++)
sum[i] += SQR(lab[i][c]);
}
chratio = sqrt(sum[1] / sum[0]);
for (c = 1; c < colors; c++)
lab[0][c] *= chratio;
FORCC for (cam[0][c] = j = 0; j < colors; j++) cam[0][c] +=
itrans[colors - 3][c][j] * lab[0][j];
FORCC image[row * width + col][c] = cam[0][c] / colors;
}
RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS, 1, 2);
}
#define SCALE (4 >> shrink)
void LibRaw::recover_highlights()
{
float *map, sum, wgt, grow;
int hsat[4], count, spread, change, val, i;
unsigned high, wide, mrow, mcol, row, col, kc, c, d, y, x;
ushort *pixel;
static const signed char dir[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, 1},
{1, 1}, {1, 0}, {1, -1}, {0, -1}};
grow = pow(2.0, 4 - highlight);
FORC(unsigned(colors)) hsat[c] = 32000 * pre_mul[c];
for (kc = 0, c = 1; c < (unsigned)colors; c++)
if (pre_mul[kc] < pre_mul[c])
kc = c;
high = height / SCALE;
wide = width / SCALE;
map = (float *)calloc(high, wide * sizeof *map);
FORC(unsigned(colors)) if (c != kc)
{
RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS, c - 1, colors - 1);
memset(map, 0, high * wide * sizeof *map);
for (mrow = 0; mrow < high; mrow++)
for (mcol = 0; mcol < wide; mcol++)
{
sum = wgt = count = 0;
for (row = mrow * SCALE; row < (mrow + 1) * SCALE; row++)
for (col = mcol * SCALE; col < (mcol + 1) * SCALE; col++)
{
pixel = image[row * width + col];
if (pixel[c] / hsat[c] == 1 && pixel[kc] > 24000)
{
sum += pixel[c];
wgt += pixel[kc];
count++;
}
}
if (count == SCALE * SCALE)
map[mrow * wide + mcol] = sum / wgt;
}
for (spread = 32 / grow; spread--;)
{
for (mrow = 0; mrow < high; mrow++)
for (mcol = 0; mcol < wide; mcol++)
{
if (map[mrow * wide + mcol])
continue;
sum = count = 0;
for (d = 0; d < 8; d++)
{
y = mrow + dir[d][0];
x = mcol + dir[d][1];
if (y < high && x < wide && map[y * wide + x] > 0)
{
sum += (1 + (d & 1)) * map[y * wide + x];
count += 1 + (d & 1);
}
}
if (count > 3)
map[mrow * wide + mcol] = -(sum + grow) / (count + grow);
}
for (change = i = 0; i < int(high * wide); i++)
if (map[i] < 0)
{
map[i] = -map[i];
change = 1;
}
if (!change)
break;
}
for (i = 0; i < int(high * wide); i++)
if (map[i] == 0)
map[i] = 1;
for (mrow = 0; mrow < high; mrow++)
for (mcol = 0; mcol < wide; mcol++)
{
for (row = mrow * SCALE; row < (mrow + 1) * SCALE; row++)
for (col = mcol * SCALE; col < (mcol + 1) * SCALE; col++)
{
pixel = image[row * width + col];
if (pixel[c] / hsat[c] > 1)
{
val = pixel[kc] * map[mrow * wide + mcol];
if (pixel[c] < val)
pixel[c] = CLIP(val);
}
}
}
}
free(map);
}
#undef SCALE

View File

@@ -0,0 +1,31 @@
/* -*- C++ -*-
* Copyright 2019-2021 LibRaw LLC (info@libraw.org)
*
Placeholder functions to build LibRaw w/o postprocessing tools
LibRaw is free software; you can redistribute it and/or modify
it under the terms of the one of two licenses as you choose:
1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
(See file LICENSE.LGPL provided in LibRaw distribution archive for details).
2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
(See file LICENSE.CDDL provided in LibRaw distribution archive for details).
*/
#include "../../internal/libraw_cxx_defs.h"
int LibRaw::dcraw_process(void)
{
return LIBRAW_NOT_IMPLEMENTED;
}
void LibRaw::fuji_rotate() {}
void LibRaw::convert_to_rgb_loop(float /*out_cam*/ [3][4]) {}
libraw_processed_image_t *LibRaw::dcraw_make_mem_image(int *) {
return NULL;
}
libraw_processed_image_t *LibRaw::dcraw_make_mem_thumb(int *){ return NULL;}
void LibRaw::lin_interpolate_loop(int * /*code*/, int /*size*/) {}
void LibRaw::scale_colors_loop(float /*scale_mul*/[4]) {}

View File

@@ -0,0 +1,190 @@
/* -*- C++ -*-
* Copyright 2019-2021 LibRaw LLC (info@libraw.org)
*
LibRaw is free software; you can redistribute it and/or modify
it under the terms of the one of two licenses as you choose:
1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
(See file LICENSE.LGPL provided in LibRaw distribution archive for details).
2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
(See file LICENSE.CDDL provided in LibRaw distribution archive for details).
*/
#include "../../internal/libraw_cxx_defs.h"
#define TBLN 65535
void LibRaw::exp_bef(float shift, float smooth)
{
// params limits
if (shift > 8)
shift = 8;
if (shift < 0.25)
shift = 0.25;
if (smooth < 0.0)
smooth = 0.0;
if (smooth > 1.0)
smooth = 1.0;
unsigned short *lut = (ushort *)malloc((TBLN + 1) * sizeof(unsigned short));
if (shift <= 1.0)
{
for (int i = 0; i <= TBLN; i++)
lut[i] = (unsigned short)((float)i * shift);
}
else
{
float x1, x2, y1, y2;
float cstops = log(shift) / log(2.0f);
float room = cstops * 2;
float roomlin = powf(2.0f, room);
x2 = (float)TBLN;
x1 = (x2 + 1) / roomlin - 1;
y1 = x1 * shift;
y2 = x2 * (1 + (1 - smooth) * (shift - 1));
float sq3x = powf(x1 * x1 * x2, 1.0f / 3.0f);
float B = (y2 - y1 + shift * (3 * x1 - 3.0f * sq3x)) /
(x2 + 2.0f * x1 - 3.0f * sq3x);
float A = (shift - B) * 3.0f * powf(x1 * x1, 1.0f / 3.0f);
float CC = y2 - A * powf(x2, 1.0f / 3.0f) - B * x2;
for (int i = 0; i <= TBLN; i++)
{
float X = (float)i;
float Y = A * powf(X, 1.0f / 3.0f) + B * X + CC;
if (i < x1)
lut[i] = (unsigned short)((float)i * shift);
else
lut[i] = Y < 0 ? 0 : (Y > TBLN ? TBLN : (unsigned short)(Y));
}
}
for (int i = 0; i < S.height * S.width; i++)
{
imgdata.image[i][0] = lut[imgdata.image[i][0]];
imgdata.image[i][1] = lut[imgdata.image[i][1]];
imgdata.image[i][2] = lut[imgdata.image[i][2]];
imgdata.image[i][3] = lut[imgdata.image[i][3]];
}
if (C.data_maximum <= TBLN)
C.data_maximum = lut[C.data_maximum];
if (C.maximum <= TBLN)
C.maximum = lut[C.maximum];
free(lut);
}
void LibRaw::convert_to_rgb_loop(float out_cam[3][4])
{
int row, col, c;
float out[3];
ushort *img;
memset(libraw_internal_data.output_data.histogram, 0,
sizeof(int) * LIBRAW_HISTOGRAM_SIZE * 4);
if (libraw_internal_data.internal_output_params.raw_color)
{
for (img = imgdata.image[0], row = 0; row < S.height; row++)
{
for (col = 0; col < S.width; col++, img += 4)
{
for (c = 0; c < imgdata.idata.colors; c++)
{
libraw_internal_data.output_data.histogram[c][img[c] >> 3]++;
}
}
}
}
else if (imgdata.idata.colors == 3)
{
for (img = imgdata.image[0], row = 0; row < S.height; row++)
{
for (col = 0; col < S.width; col++, img += 4)
{
out[0] = out_cam[0][0] * img[0] + out_cam[0][1] * img[1] +
out_cam[0][2] * img[2];
out[1] = out_cam[1][0] * img[0] + out_cam[1][1] * img[1] +
out_cam[1][2] * img[2];
out[2] = out_cam[2][0] * img[0] + out_cam[2][1] * img[1] +
out_cam[2][2] * img[2];
img[0] = CLIP((int)out[0]);
img[1] = CLIP((int)out[1]);
img[2] = CLIP((int)out[2]);
libraw_internal_data.output_data.histogram[0][img[0] >> 3]++;
libraw_internal_data.output_data.histogram[1][img[1] >> 3]++;
libraw_internal_data.output_data.histogram[2][img[2] >> 3]++;
}
}
}
else if (imgdata.idata.colors == 4)
{
for (img = imgdata.image[0], row = 0; row < S.height; row++)
{
for (col = 0; col < S.width; col++, img += 4)
{
out[0] = out_cam[0][0] * img[0] + out_cam[0][1] * img[1] +
out_cam[0][2] * img[2] + out_cam[0][3] * img[3];
out[1] = out_cam[1][0] * img[0] + out_cam[1][1] * img[1] +
out_cam[1][2] * img[2] + out_cam[1][3] * img[3];
out[2] = out_cam[2][0] * img[0] + out_cam[2][1] * img[1] +
out_cam[2][2] * img[2] + out_cam[2][3] * img[3];
img[0] = CLIP((int)out[0]);
img[1] = CLIP((int)out[1]);
img[2] = CLIP((int)out[2]);
libraw_internal_data.output_data.histogram[0][img[0] >> 3]++;
libraw_internal_data.output_data.histogram[1][img[1] >> 3]++;
libraw_internal_data.output_data.histogram[2][img[2] >> 3]++;
libraw_internal_data.output_data.histogram[3][img[3] >> 3]++;
}
}
}
}
void LibRaw::scale_colors_loop(float scale_mul[4])
{
unsigned size = S.iheight * S.iwidth;
if (C.cblack[4] && C.cblack[5])
{
int val;
for (unsigned i = 0; i < size; i++)
{
for (unsigned c = 0; c < 4; c++)
{
if (!(val = imgdata.image[i][c])) continue;
val -= C.cblack[6 + i / S.iwidth % C.cblack[4] * C.cblack[5] +
i % S.iwidth % C.cblack[5]];
val -= C.cblack[c];
val *= scale_mul[c];
imgdata.image[i][c] = CLIP(val);
}
}
}
else if (C.cblack[0] || C.cblack[1] || C.cblack[2] || C.cblack[3])
{
for (unsigned i = 0; i < size; i++)
{
for (unsigned c = 0; c < 4; c++)
{
int val = imgdata.image[i][c];
if (!val) continue;
val -= C.cblack[c];
val *= scale_mul[c];
imgdata.image[i][c] = CLIP(val);
}
}
}
else // BL is zero
{
for (unsigned i = 0; i < size; i++)
{
for (unsigned c = 0; c < 4; c++)
{
int val = imgdata.image[i][c];
val *= scale_mul[c];
imgdata.image[i][c] = CLIP(val);
}
}
}
}

View File

@@ -0,0 +1,308 @@
/* -*- C++ -*-
* Copyright 2019-2021 LibRaw LLC (info@libraw.org)
*
LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
LibRaw do not use RESTRICTED code from dcraw.c
LibRaw is free software; you can redistribute it and/or modify
it under the terms of the one of two licenses as you choose:
1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
(See file LICENSE.LGPL provided in LibRaw distribution archive for details).
2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
(See file LICENSE.CDDL provided in LibRaw distribution archive for details).
*/
#include "../../internal/dcraw_defs.h"
void LibRaw::convert_to_rgb()
{
float out_cam[3][4];
double num, inverse[3][3];
static const double(*out_rgb[])[3] = {
LibRaw_constants::rgb_rgb, LibRaw_constants::adobe_rgb,
LibRaw_constants::wide_rgb, LibRaw_constants::prophoto_rgb,
LibRaw_constants::xyz_rgb, LibRaw_constants::aces_rgb,
LibRaw_constants::dcip3d65_rgb, LibRaw_constants::rec2020_rgb};
static const char *name[] = {"sRGB", "Adobe RGB (1998)",
"WideGamut D65", "ProPhoto D65",
"XYZ", "ACES",
"DCI-P3 D65", "Rec. 2020"};
static const unsigned phead[] = {
1024, 0, 0x2100000, 0x6d6e7472, 0x52474220, 0x58595a20, 0,
0, 0, 0x61637370, 0, 0, 0x6e6f6e65, 0,
0, 0, 0, 0xf6d6, 0x10000, 0xd32d};
unsigned pbody[] = {10, 0x63707274, 0, 36, /* cprt */
0x64657363, 0, 60, /* desc, len is strlen(longest_string) + 12 */
0x77747074, 0, 20, /* wtpt */
0x626b7074, 0, 20, /* bkpt */
0x72545243, 0, 14, /* rTRC */
0x67545243, 0, 14, /* gTRC */
0x62545243, 0, 14, /* bTRC */
0x7258595a, 0, 20, /* rXYZ */
0x6758595a, 0, 20, /* gXYZ */
0x6258595a, 0, 20}; /* bXYZ */
static const unsigned pwhite[] = {0xf351, 0x10000, 0x116cc};
unsigned pcurve[] = {0x63757276, 0, 1, 0x1000000};
RUN_CALLBACK(LIBRAW_PROGRESS_CONVERT_RGB, 0, 2);
gamma_curve(gamm[0], gamm[1], 0, 0);
memcpy(out_cam, rgb_cam, sizeof out_cam);
raw_color |= colors == 1 || output_color < 1 || output_color > 8;
if (!raw_color)
{
size_t prof_desc_len;
std::vector<char> prof_desc;
int i, j, k;
prof_desc_len = snprintf(NULL, 0, "%s gamma %g toe slope %g", name[output_color - 1],
floorf(1000.f / gamm[0] + .5f) / 1000.f, floorf(gamm[1] * 1000.0f + .5f) / 1000.f) +
1;
prof_desc.resize(prof_desc_len);
sprintf(prof_desc.data(), "%s gamma %g toe slope %g", name[output_color - 1], floorf(1000.f / gamm[0] + .5f) / 1000.f,
floorf(gamm[1] * 1000.0f + .5f) / 1000.f);
oprof = (unsigned *)calloc(phead[0], 1);
memcpy(oprof, phead, sizeof phead);
if (output_color == 5)
oprof[4] = oprof[5];
oprof[0] = 132 + 12 * pbody[0];
for (i = 0; i < (int)pbody[0]; i++)
{
oprof[oprof[0] / 4] = i ? (i > 1 ? 0x58595a20 : 0x64657363) : 0x74657874;
pbody[i * 3 + 2] = oprof[0];
oprof[0] += (pbody[i * 3 + 3] + 3) & -4;
}
memcpy(oprof + 32, pbody, sizeof pbody);
oprof[pbody[5] / 4 + 2] = unsigned(prof_desc_len + 1);
memcpy((char *)oprof + pbody[8] + 8, pwhite, sizeof pwhite);
pcurve[3] = (short)(256 / gamm[5] + 0.5) << 16;
for (i = 4; i < 7; i++)
memcpy((char *)oprof + pbody[i * 3 + 2], pcurve, sizeof pcurve);
pseudoinverse((double(*)[3])out_rgb[output_color - 1], inverse, 3);
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
{
for (num = k = 0; k < 3; k++)
num += LibRaw_constants::xyzd50_srgb[i][k] * inverse[j][k];
oprof[pbody[j * 3 + 23] / 4 + i + 2] = num * 0x10000 + 0.5;
}
for (i = 0; i < (int)phead[0] / 4; i++)
oprof[i] = htonl(oprof[i]);
strcpy((char *)oprof + pbody[2] + 8, "auto-generated by dcraw");
if (pbody[5] + 12 + prof_desc.size() < phead[0])
strcpy((char *)oprof + pbody[5] + 12, prof_desc.data());
for (i = 0; i < 3; i++)
for (j = 0; j < colors; j++)
for (out_cam[i][j] = k = 0; k < 3; k++)
out_cam[i][j] += out_rgb[output_color - 1][i][k] * rgb_cam[k][j];
}
convert_to_rgb_loop(out_cam);
if (colors == 4 && output_color)
colors = 3;
RUN_CALLBACK(LIBRAW_PROGRESS_CONVERT_RGB, 1, 2);
}
void LibRaw::scale_colors()
{
unsigned bottom, right, size, row, col, ur, uc, i, x, y, c, sum[8];
int val;
double dsum[8], dmin, dmax;
float scale_mul[4], fr, fc;
ushort *img = 0, *pix;
RUN_CALLBACK(LIBRAW_PROGRESS_SCALE_COLORS, 0, 2);
if (user_mul[0])
memcpy(pre_mul, user_mul, sizeof pre_mul);
if (use_auto_wb || (use_camera_wb &&
(cam_mul[0] < -0.5 // LibRaw 0.19 and older: fallback to auto only if cam_mul[0] is set to -1
|| (cam_mul[0] <= 0.00001f // New default: fallback to auto if no cam_mul parsed from metadata
&& !(imgdata.rawparams.options & LIBRAW_RAWOPTIONS_CAMERAWB_FALLBACK_TO_DAYLIGHT))
)))
{
memset(dsum, 0, sizeof dsum);
bottom = MIN(greybox[1] + greybox[3], height);
right = MIN(greybox[0] + greybox[2], width);
for (row = greybox[1]; row < bottom; row += 8)
for (col = greybox[0]; col < right; col += 8)
{
memset(sum, 0, sizeof sum);
for (y = row; y < row + 8 && y < bottom; y++)
for (x = col; x < col + 8 && x < right; x++)
FORC4
{
if (filters)
{
c = fcol(y, x);
val = BAYER2(y, x);
}
else
val = image[y * width + x][c];
if (val > (int)maximum - 25)
goto skip_block;
if ((val -= cblack[c]) < 0)
val = 0;
sum[c] += val;
sum[c + 4]++;
if (filters)
break;
}
FORC(8) dsum[c] += sum[c];
skip_block:;
}
FORC4 if (dsum[c]) pre_mul[c] = dsum[c + 4] / dsum[c];
}
if (use_camera_wb && cam_mul[0] > 0.00001f)
{
memset(sum, 0, sizeof sum);
for (row = 0; row < 8; row++)
for (col = 0; col < 8; col++)
{
c = FC(row, col);
if ((val = white[row][col] - cblack[c]) > 0)
sum[c] += val;
sum[c + 4]++;
}
if (imgdata.color.as_shot_wb_applied)
{
// Nikon sRAW: camera WB already applied:
pre_mul[0] = pre_mul[1] = pre_mul[2] = pre_mul[3] = 1.0;
}
else if (sum[0] && sum[1] && sum[2] && sum[3])
FORC4 pre_mul[c] = (float)sum[c + 4] / sum[c];
else if (cam_mul[0] > 0.00001f && cam_mul[2] > 0.00001f)
memcpy(pre_mul, cam_mul, sizeof pre_mul);
else
{
imgdata.process_warnings |= LIBRAW_WARN_BAD_CAMERA_WB;
}
}
// Nikon sRAW, daylight
if (imgdata.color.as_shot_wb_applied && !use_camera_wb && !use_auto_wb &&
cam_mul[0] > 0.00001f && cam_mul[1] > 0.00001f && cam_mul[2] > 0.00001f)
{
for (c = 0; c < 3; c++)
pre_mul[c] /= cam_mul[c];
}
if (pre_mul[1] == 0)
pre_mul[1] = 1;
if (pre_mul[3] == 0)
pre_mul[3] = colors < 4 ? pre_mul[1] : 1;
if (threshold)
wavelet_denoise();
maximum -= black;
for (dmin = DBL_MAX, dmax = c = 0; c < 4; c++)
{
if (dmin > pre_mul[c])
dmin = pre_mul[c];
if (dmax < pre_mul[c])
dmax = pre_mul[c];
}
if (!highlight)
dmax = dmin;
if (dmax > 0.00001 && maximum > 0)
FORC4 scale_mul[c] = (pre_mul[c] /= dmax) * 65535.0 / maximum;
else
FORC4 scale_mul[c] = 1.0;
if (filters > 1000 && (cblack[4] + 1) / 2 == 1 && (cblack[5] + 1) / 2 == 1)
{
FORC4 cblack[FC(c / 2, c % 2)] +=
cblack[6 + c / 2 % cblack[4] * cblack[5] + c % 2 % cblack[5]];
cblack[4] = cblack[5] = 0;
}
size = iheight * iwidth;
scale_colors_loop(scale_mul);
if ((aber[0] != 1 || aber[2] != 1) && colors == 3)
{
for (c = 0; c < 4; c += 2)
{
if (aber[c] == 1)
continue;
img = (ushort *)malloc(size * sizeof *img);
for (i = 0; i < size; i++)
img[i] = image[i][c];
for (row = 0; row < iheight; row++)
{
ur = fr = (row - iheight * 0.5) * aber[c] + iheight * 0.5;
if (ur > (unsigned)iheight - 2)
continue;
fr -= ur;
for (col = 0; col < iwidth; col++)
{
uc = fc = (col - iwidth * 0.5) * aber[c] + iwidth * 0.5;
if (uc > (unsigned)iwidth - 2)
continue;
fc -= uc;
pix = img + ur * iwidth + uc;
image[row * iwidth + col][c] =
(pix[0] * (1 - fc) + pix[1] * fc) * (1 - fr) +
(pix[iwidth] * (1 - fc) + pix[iwidth + 1] * fc) * fr;
}
}
free(img);
}
}
RUN_CALLBACK(LIBRAW_PROGRESS_SCALE_COLORS, 1, 2);
}
// green equilibration
void LibRaw::green_matching()
{
int i, j;
double m1, m2, c1, c2;
int o1_1, o1_2, o1_3, o1_4;
int o2_1, o2_2, o2_3, o2_4;
ushort(*img)[4];
const int margin = 3;
int oj = 2, oi = 2;
float f;
const float thr = 0.01f;
if (half_size || shrink)
return;
if (FC(oj, oi) != 3)
oj++;
if (FC(oj, oi) != 3)
oi++;
if (FC(oj, oi) != 3)
oj--;
img = (ushort(*)[4])calloc(height * width, sizeof *image);
memcpy(img, image, height * width * sizeof *image);
for (j = oj; j < height - margin; j += 2)
for (i = oi; i < width - margin; i += 2)
{
o1_1 = img[(j - 1) * width + i - 1][1];
o1_2 = img[(j - 1) * width + i + 1][1];
o1_3 = img[(j + 1) * width + i - 1][1];
o1_4 = img[(j + 1) * width + i + 1][1];
o2_1 = img[(j - 2) * width + i][3];
o2_2 = img[(j + 2) * width + i][3];
o2_3 = img[j * width + i - 2][3];
o2_4 = img[j * width + i + 2][3];
m1 = (o1_1 + o1_2 + o1_3 + o1_4) / 4.0;
m2 = (o2_1 + o2_2 + o2_3 + o2_4) / 4.0;
c1 = (abs(o1_1 - o1_2) + abs(o1_1 - o1_3) + abs(o1_1 - o1_4) +
abs(o1_2 - o1_3) + abs(o1_3 - o1_4) + abs(o1_2 - o1_4)) /
6.0;
c2 = (abs(o2_1 - o2_2) + abs(o2_1 - o2_3) + abs(o2_1 - o2_4) +
abs(o2_2 - o2_3) + abs(o2_3 - o2_4) + abs(o2_2 - o2_4)) /
6.0;
if ((img[j * width + i][3] < maximum * 0.95) && (c1 < maximum * thr) &&
(c2 < maximum * thr))
{
f = image[j * width + i][3] * m1 / m2;
image[j * width + i][3] = f > 0xffff ? 0xffff : f;
}
}
free(img);
}