14.25: Camera Raw's Light and Color sliders match Photoshop's

This commit is contained in:
ZacharyZhang-NY committed 2026-10-11 13:03:24 -04:00
1 parent 15bb440bc9
commit efadd6f7c6
17 files changed
+513 -136

No files matched your search

+73 -68
View File
@@ -201,18 +201,6 @@ static double camera_clamp(double value) {
return value;
}
static double srgb_to_linear(double encoded) {
if (encoded <= 0.04045) return encoded / 12.92;
return pow((encoded + 0.055) / 1.055, 2.4);
}
static double linear_to_srgb(double linear) {
if (linear <= 0) return 0;
if (linear >= 1) return 1;
if (linear <= 0.0031308) return linear * 12.92;
return 1.055 * pow(linear, 1.0 / 2.4) - 0.055;
}
static double rec709(double r, double g, double b) {
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
@@ -255,47 +243,79 @@ static double tone_highlights(double y, double amount) {
return camera_clamp(y + amount * (amount >= 0 ? 1.0 : 2.5) * tone_bump(1.0 - y));
}
// The top quarter is the white point: +1 maps 0.875 to 1, −1 pulls everything above 0.75 down to 0.75.
static double tone_whites(double y, double amount) {
if (y <= 0.75) return y;
return camera_clamp(0.75 + (y - 0.75) * (1.0 + amount));
// A measured table's color for an sRGB color (0…1), between its eight nearest entries.
static void table_lookup(const uint8_t *table, int size, const double in[3], double out[3]) {
double f[3];
int i[3];
for (int c = 0; c < 3; ++c) {
double x = camera_clamp(in[c]) * (size - 1);
i[c] = x >= size - 1 ? size - 2 : (int)x;
f[c] = x - i[c];
}
out[0] = out[1] = out[2] = 0;
for (int corner = 0; corner < 8; ++corner) {
int dr = corner >> 2, dg = (corner >> 1) & 1, db = corner & 1;
double w = (dr ? f[0] : 1 - f[0]) * (dg ? f[1] : 1 - f[1]) * (db ? f[2] : 1 - f[2]);
if (w == 0) continue;
const uint8_t *p = table + (((size_t)(i[0] + dr) * size + (i[1] + dg)) * size + (i[2] + db)) * 3;
out[0] += w * p[0];
out[1] += w * p[1];
out[2] += w * p[2];
}
out[0] /= 255.0;
out[1] /= 255.0;
out[2] /= 255.0;
}
// The bottom quarter is the black point. Negative amounts crush toward 0; positive ones lift toward 0.25.
static double tone_blacks(double y, double amount) {
if (y >= 0.25) return y;
return camera_clamp(0.25 + (y - 0.25) * (1.0 - amount));
void camera_raw_compose(float *out, int grid, const CameraRawStage *stages, int count, int size) {
for (int r = 0; r < grid; ++r) {
for (int g = 0; g < grid; ++g) {
for (int b = 0; b < grid; ++b) {
double color[3] = {(double)r / (grid - 1), (double)g / (grid - 1), (double)b / (grid - 1)};
for (int s = 0; s < count; ++s) camera_raw_stage_color(&stages[s], size, color);
float *o = out + (((size_t)r * grid + g) * grid + b) * 3;
o[0] = (float)color[0];
o[1] = (float)color[1];
o[2] = (float)color[2];
}
}
}
}
static void vibrance_and_saturation(double *r, double *g, double *b, double vibrance, double saturation) {
double lum = rec709(*r, *g, *b);
double maxc = fmax(*r, fmax(*g, *b));
double minc = fmin(*r, fmin(*g, *b));
double chroma = maxc - minc;
double sat = maxc <= 1e-8 ? 0 : chroma / maxc;
double hue = 0;
if (chroma > 1e-8) {
if (*r >= *g && *r >= *b) hue = 60.0 * fmod((*g - *b) / chroma, 6.0);
else if (*g >= *r && *g >= *b) hue = 60.0 * ((*b - *r) / chroma + 2.0);
else hue = 60.0 * ((*r - *g) / chroma + 4.0);
if (hue < 0) hue += 360.0;
void camera_raw_stage_color(const CameraRawStage *stage, int size, double *rgb) {
double next[3] = {0, 0, 0};
for (int t = 0; t < 4; ++t) {
double w = stage->weight[t];
if (w == 0) continue;
double looked[3];
if (stage->table[t]) table_lookup(stage->table[t], size, rgb, looked);
else memcpy(looked, rgb, sizeof looked);
for (int c = 0; c < 3; ++c) next[c] += w * looked[c];
}
double skin = 0;
if (hue >= 10.0 && hue <= 50.0) {
skin = hue <= 30.0 ? (hue - 10.0) / 20.0 : (50.0 - hue) / 20.0;
skin *= camera_clamp((sat - 0.15) / 0.35);
for (int c = 0; c < 3; ++c) rgb[c] = camera_clamp(next[c]);
}
// A composed table's color for an sRGB color, between its eight nearest entries.
static void composed_lookup(const float *table, int grid, double *r, double *g, double *b) {
double in[3] = {*r, *g, *b}, f[3], out[3] = {0, 0, 0};
int i[3];
for (int c = 0; c < 3; ++c) {
double x = camera_clamp(in[c]) * (grid - 1);
i[c] = x >= grid - 1 ? grid - 2 : (int)x;
f[c] = x - i[c];
}
double amount = vibrance * (1.0 - sat);
if (vibrance > 0) amount *= 1.0 - 0.7 * skin;
double factor = 1.0 + amount;
*r = camera_clamp(lum + (*r - lum) * factor);
*g = camera_clamp(lum + (*g - lum) * factor);
*b = camera_clamp(lum + (*b - lum) * factor);
lum = rec709(*r, *g, *b);
factor = 1.0 + saturation;
*r = camera_clamp(lum + (*r - lum) * factor);
*g = camera_clamp(lum + (*g - lum) * factor);
*b = camera_clamp(lum + (*b - lum) * factor);
for (int corner = 0; corner < 8; ++corner) {
int dr = corner >> 2, dg = (corner >> 1) & 1, db = corner & 1;
double w = (dr ? f[0] : 1 - f[0]) * (dg ? f[1] : 1 - f[1]) * (db ? f[2] : 1 - f[2]);
if (w == 0) continue;
const float *p = table + (((size_t)(i[0] + dr) * grid + (i[1] + dg)) * grid + (i[2] + db)) * 3;
out[0] += w * p[0];
out[1] += w * p[1];
out[2] += w * p[2];
}
*r = camera_clamp(out[0]);
*g = camera_clamp(out[1]);
*b = camera_clamp(out[2]);
}
static void write_premultiplied(uint8_t *p, double r, double g, double b, double alpha) {
@@ -304,18 +324,10 @@ static void write_premultiplied(uint8_t *p, double r, double g, double b, double
p[2] = (uint8_t)fmin(alpha, fmax(0.0, round(b * alpha)));
}
void adjust_camera_raw(uint8_t *rgba, size_t width, size_t height, size_t stride,
double redGain, double greenGain, double blueGain, double exposure, double contrast,
double highlights, double shadows, double whites, double blacks,
double vibrance, double saturation, int clipping) {
double light = exp2(exposure);
double contrastScale = 1.0 + contrast / 100.0;
void adjust_camera_raw(uint8_t *rgba, size_t width, size_t height, size_t stride, const float *before,
const float *after, int grid, double highlights, double shadows, int clipping) {
double highlightAmount = highlights / 100.0;
double shadowAmount = shadows / 100.0;
double whiteAmount = whites / 100.0;
double blackAmount = blacks / 100.0;
double vibranceAmount = vibrance / 100.0;
double saturationAmount = saturation / 100.0;
for (size_t y = 0; y < height; ++y) {
uint8_t *row = rgba + y * stride;
for (size_t x = 0; x < width; ++x) {
@@ -325,17 +337,10 @@ void adjust_camera_raw(uint8_t *rgba, size_t width, size_t height, size_t stride
double r = fmin(255.0, p[0] * 255.0 / alpha) / 255.0;
double g = fmin(255.0, p[1] * 255.0 / alpha) / 255.0;
double b = fmin(255.0, p[2] * 255.0 / alpha) / 255.0;
r = camera_clamp(srgb_to_linear(r) * redGain * light);
g = camera_clamp(srgb_to_linear(g) * greenGain * light);
b = camera_clamp(srgb_to_linear(b) * blueGain * light);
r = camera_clamp(0.5 + (linear_to_srgb(r) - 0.5) * contrastScale);
g = camera_clamp(0.5 + (linear_to_srgb(g) - 0.5) * contrastScale);
b = camera_clamp(0.5 + (linear_to_srgb(b) - 0.5) * contrastScale);
scale_luminance(&r, &g, &b, tone_highlights(rec709(r, g, b), highlightAmount));
scale_luminance(&r, &g, &b, tone_shadows(rec709(r, g, b), shadowAmount));
scale_luminance(&r, &g, &b, tone_whites(rec709(r, g, b), whiteAmount));
scale_luminance(&r, &g, &b, tone_blacks(rec709(r, g, b), blackAmount));
vibrance_and_saturation(&r, &g, &b, vibranceAmount, saturationAmount);
if (before) composed_lookup(before, grid, &r, &g, &b);
if (highlightAmount != 0) scale_luminance(&r, &g, &b, tone_highlights(rec709(r, g, b), highlightAmount));
if (shadowAmount != 0) scale_luminance(&r, &g, &b, tone_shadows(rec709(r, g, b), shadowAmount));
if (after) composed_lookup(after, grid, &r, &g, &b);
if (clipping == 1) {
int rc = r >= 254.5 / 255.0, gc = g >= 254.5 / 255.0, bc = b >= 254.5 / 255.0;
r = rc ? 1 : 0;