Revision e873d56e8eec42c9fa6649d37fee9cc8550b0d74 authored by bouncyball on 15 May 2016, 10:46:04 UTC, committed by bouncyball on 15 May 2016, 10:46:04 UTC
1 parent 1cf2d54
histogram.c
#include "dryos.h"
#include "vram.h"
#include "bmp.h"
#include "version.h"
#include "config.h"
#include "lens.h"
#include "math.h"
#include "raw.h"
#include "menu.h"
#include "imgconv.h"
#include "histogram.h"
#include "module.h"
#include "zebra.h"
#include "falsecolor.h"
#if defined(FEATURE_HISTOGRAM)
extern int FAST get_y_skip_offset_for_overlays();
extern int nondigic_zoom_overlay_enabled();
CONFIG_INT( "hist.draw", hist_draw, 1 );
#ifdef FEATURE_RAW_HISTOGRAM
CONFIG_INT( "hist.type", hist_type, 2 );
#else
CONFIG_INT( "hist.type", hist_type, 1 );
#endif
CONFIG_INT( "hist.warn", hist_warn, 1 );
CONFIG_INT( "hist.log", hist_log, 1 );
CONFIG_INT( "hist.meter", hist_meter, 2);
struct Histogram histogram;
#ifdef FEATURE_RAW_HISTOGRAM
#define HIST_METER_DYNAMIC_RANGE 1
#define HIST_METER_ETTR_HINT 2
static void histobar_refresh();
void FAST hist_build_raw()
{
if (!raw_update_params()) return;
memset(&histogram, 0, sizeof(histogram));
histogram.is_raw = 1;
int step = lv ? 4 : 2;
char r2ev[16384];
for (int i = 0; i < 16384; i++)
r2ev[i] = COERCE((raw_to_ev(i) + 12) * (HIST_WIDTH-1) / 12, 0, HIST_WIDTH-1);
for (int i = os.y0; i < os.y_max; i += step)
{
int y = BM2RAW_Y(i);
if (y < raw_info.active_area.y1+8 || y > raw_info.active_area.y2-8) continue;
for (int j = os.x0; j < os.x_max; j += 8)
{
int x = BM2RAW_X(j);
if (x < raw_info.active_area.x1+8 || x > raw_info.active_area.x2-8) continue;
int r = raw_red_pixel_dark(x, y);
int g = raw_green_pixel_dark(x, y);
int b = raw_blue_pixel_dark(x, y);
/* ignore bad pixels */
if (r == 0 || g == 0 || b == 0) continue;
int ir = r2ev[r];
int ig = r2ev[g];
int ib = r2ev[b];
histogram.hist_r[ir]++;
histogram.hist_g[ig]++;
histogram.hist_b[ib]++;
histogram.total_px++;
}
}
/* in dark areas, spread the histogram count to show solid histogram instead of isolated bars */
for (int i = 0; i < 5000; i++)
{
int ev0 = r2ev[i];
int evplus = r2ev[i+1];
int evminus = r2ev[i-1];
if (evplus - evminus > 2) /* will there be a gap? fill it */
{
int num_bins = evplus - evminus - 1;
int delta_r = histogram.hist_r[ev0] / num_bins;
int delta_g = histogram.hist_g[ev0] / num_bins;
int delta_b = histogram.hist_b[ev0] / num_bins;
for (int e = evminus+1; e <= evplus-1; e++)
{
histogram.hist_r[e] += delta_r;
histogram.hist_g[e] += delta_g;
histogram.hist_b[e] += delta_b;
histogram.hist_r[ev0] -= delta_r;
histogram.hist_g[ev0] -= delta_g;
histogram.hist_b[ev0] -= delta_b;
}
}
}
for (int i = 0; i < HIST_WIDTH; i++)
{
histogram.max = MAX(histogram.max, histogram.hist_r[i]);
histogram.max = MAX(histogram.max, histogram.hist_g[i]);
histogram.max = MAX(histogram.max, histogram.hist_b[i]);
}
histobar_refresh();
}
MENU_UPDATE_FUNC(raw_histo_update)
{
if (RAW_HISTOGRAM_ENABLED)
{
menu_checkdep_raw(entry, info);
}
if (RAW_HISTOBAR_ENABLED)
{
MENU_SET_WARNING(MENU_WARN_INFO, "Will use Histobar in LiveView, RAW histogram after taking a pic.");
}
else if (RAW_HISTOGRAM_ENABLED)
{
MENU_SET_WARNING(MENU_WARN_INFO, "Will use RAW histogram in LiveView and after taking a pic.");
}
}
#endif
static int hist_rgb_color(int y, int sizeR, int sizeG, int sizeB)
{
switch ((y > sizeR ? 0 : 1) |
(y > sizeG ? 0 : 2) |
(y > sizeB ? 0 : 4))
{
case 0b000: return COLOR_ALMOST_BLACK;
case 0b001: return COLOR_RED;
case 0b010: return COLOR_GREEN2;
case 0b100: return COLOR_LIGHT_BLUE;
case 0b011: return COLOR_YELLOW;
case 0b110: return COLOR_CYAN;
case 0b101: return COLOR_MAGENTA;
case 0b111: return COLOR_WHITE;
}
return 0;
}
static void hist_dot(int x, int y, int fg_color, int bg_color, int radius, int label)
{
x &= ~3;
y &= ~3;
for (int r = 0; r < radius; r++)
{
draw_circle(x, y, r, fg_color);
draw_circle(x + 1, y, r, fg_color);
}
draw_circle(x, y, radius, bg_color);
if (label)
{
if (label < 10)
bmp_printf(
SHADOW_FONT(FONT(FONT_MED, COLOR_WHITE, fg_color)),
x - 4,
y - font_med.height/2,
"%d", label
);
else
bmp_printf(
SHADOW_FONT(FONT(FONT_SMALL, COLOR_WHITE, fg_color)),
x - 8,
y - font_small.height/2,
"%d", label
);
}
}
static int hist_dot_radius(int over, int hist_total_px)
{
// overexposures stronger than 1% are displayed at max radius (10)
int p = 100 * over / hist_total_px;
if (p > 1) return 10;
// for smaller overexposure percentages, use dot radius to suggest the amount
unsigned p1000 = 100 * 1000 * over / hist_total_px;
int plog = p1000 ? (int)log2f(p1000) : 0;
return MIN(plog, 10);
}
static int hist_dot_label(int over, int hist_total_px)
{
return 100 * over / hist_total_px;
}
static int (*auto_ettr_export_correction)(int* out) = MODULE_FUNCTION(auto_ettr_export_correction);
/** Draw the histogram image into the bitmap framebuffer.
*
* Draw one pixel at a time; it seems to be ok with err70.
* Since there is plenty of math per pixel this doesn't
* swamp the bitmap framebuffer hardware.
*/
void hist_draw_image(
unsigned x_origin,
unsigned y_origin
)
{
#ifdef FEATURE_RAW_HISTOGRAM
if (RAW_HISTOBAR_ENABLED && lv && can_use_raw_overlays_menu())
return;
#endif
if (!PLAY_OR_QR_MODE)
{
if (!lv_luma_is_accurate()) return;
}
uint8_t * const bvram = bmp_vram();
if (!bvram) return;
// Align the x origin, just in case
x_origin &= ~3;
uint8_t * row = bvram + x_origin + y_origin * BMPPITCH;
if( histogram.max == 0 )
histogram.max = 1;
unsigned i, y;
int log_max = log_length(histogram.max);
#ifdef FEATURE_RAW_HISTOGRAM
const int v = (1200 - raw_info.dynamic_range) * HIST_WIDTH / 1200;
int underexposed_level = COERCE(v, 0, HIST_WIDTH-1);
int stops_until_overexposure = 0;
#endif
for( i=0 ; i < HIST_WIDTH ; i++ )
{
// Scale by the maximum bin value
const uint32_t size = hist_log ? log_length(histogram.hist[i]) * hist_height / log_max : (histogram.hist[i] * hist_height) / histogram.max;
const uint32_t sizeR = hist_log ? log_length(histogram.hist_r[i]) * hist_height / log_max : (histogram.hist_r[i] * hist_height) / histogram.max;
const uint32_t sizeG = hist_log ? log_length(histogram.hist_g[i]) * hist_height / log_max : (histogram.hist_g[i] * hist_height) / histogram.max;
const uint32_t sizeB = hist_log ? log_length(histogram.hist_b[i]) * hist_height / log_max : (histogram.hist_b[i] * hist_height) / histogram.max;
uint8_t * col = row + i;
// vertical line up to the hist size
for( y=hist_height ; y>0 ; y-- , col += BMPPITCH )
{
if (histogram.is_rgb)
*col = hist_rgb_color(y, sizeR, sizeG, sizeB);
else
*col = y > size ? COLOR_BG :
#if defined(FEATURE_FALSE_COLOR)
falsecolor_fordraw(((i << 8) / HIST_WIDTH) & 0xFF);
#else
COLOR_WHITE;
#endif /* defined(FEATURE_FALSE_COLOR) */
}
#if defined(FEATURE_HISTOGRAM)
/* draw clip warnings */
if (hist_warn && i == HIST_WIDTH - 1)
{
unsigned int thr = histogram.total_px / 100000; // start at 0.0001 with a tiny dot
thr = MAX(thr, 1);
int yw = y_origin + 12 + (hist_log ? hist_height - 24 : 0);
int bg = (hist_log ? COLOR_WHITE : COLOR_BLACK);
if (histogram.is_rgb)
{
unsigned int over_r = histogram.hist_r[i] + histogram.hist_r[i-1];
unsigned int over_g = histogram.hist_g[i] + histogram.hist_g[i-1];
unsigned int over_b = histogram.hist_b[i] + histogram.hist_b[i-1];
if (over_r > thr) hist_dot(x_origin + HIST_WIDTH/2 - 25, yw, COLOR_RED, bg, hist_dot_radius(over_r, histogram.total_px), hist_dot_label(over_r, histogram.total_px));
if (over_g > thr) hist_dot(x_origin + HIST_WIDTH/2 , yw, COLOR_GREEN1, bg, hist_dot_radius(over_g, histogram.total_px), hist_dot_label(over_g, histogram.total_px));
if (over_b > thr) hist_dot(x_origin + HIST_WIDTH/2 + 25, yw, COLOR_LIGHT_BLUE, bg, hist_dot_radius(over_b, histogram.total_px), hist_dot_label(over_b, histogram.total_px));
}
else
{
unsigned int over = histogram.hist[i] + histogram.hist[i-1];
if (over > thr) hist_dot(x_origin + HIST_WIDTH/2, yw, COLOR_RED, bg, hist_dot_radius(over, histogram.total_px), hist_dot_label(over, histogram.total_px));
}
}
#endif
#ifdef FEATURE_RAW_HISTOGRAM
/* divide the histogram in 12 equal slices - each slice is 1 EV */
if (histogram.is_raw)
{
static unsigned bar_pos;
if (i == 0) bar_pos = 0;
int H = hist_height - MAX(MAX(sizeR, sizeG), sizeB) - 1;
int h = hist_height - MIN(MIN(sizeR, sizeG), sizeB) - 1;
/* mark what's below the noise floor with... noise */
if ((int)i <= underexposed_level && i%2==0)
{
for (int y = y_origin + ((i/2)%2)*2; y < (int)y_origin + hist_height; y += 4)
{
int noise_color =
(y < (int)y_origin + H) ? COLOR_GRAY(60) : /* noise color on top of histogram */
(y < (int)y_origin + h) ? COLOR_WHITE : /* noise color where histogram has a solid color, but not white */
COLOR_BLACK ; /* noise color where histogram is white */
bmp_putpixel(x_origin + i, y, noise_color);
}
}
/* draw full-stop (EV) bars */
if (i == bar_pos)
{
int dy = (i < font_med.width * 4) ? font_med.height : 0;
draw_line(x_origin + i, y_origin + dy, x_origin + i, y_origin + h, COLOR_GRAY(50));
bar_pos = (((bar_pos+1)*12/HIST_WIDTH) + 1) * HIST_WIDTH/12;
}
/* compute a basic ETTR hint */
unsigned int thr = histogram.total_px / 10000;
if (histogram.hist_r[i] > thr || histogram.hist_g[i] > thr || histogram.hist_b[i] > thr)
stops_until_overexposure = 120 - (i * 120 / (HIST_WIDTH-1));
}
#endif
}
/* draw histogram border */
bmp_draw_rect(60, x_origin-1, y_origin-1, HIST_WIDTH+2, hist_height+2);
#ifdef FEATURE_RAW_HISTOGRAM
if (histogram.is_raw)
{
char msg[10];
switch (hist_meter)
{
case HIST_METER_DYNAMIC_RANGE:
{
int dr = (raw_info.dynamic_range + 5) / 10;
snprintf(msg, sizeof(msg), "D%d.%d", dr/10, dr%10);
break;
}
case HIST_METER_ETTR_HINT:
{
if (!stops_until_overexposure)
stops_until_overexposure = INT_MIN;
#ifdef CONFIG_MODULES
int ettr_stops = INT_MIN;
if (auto_ettr_export_correction(&ettr_stops) == 1)
if (ettr_stops != INT_MIN)
stops_until_overexposure = (ettr_stops+5)/10;
#endif
if (stops_until_overexposure != INT_MIN)
snprintf(msg, sizeof(msg), "E%s%d.%d", FMT_FIXEDPOINT1(stops_until_overexposure));
else
snprintf(msg, sizeof(msg), "OVER");
break;
}
default:
snprintf(msg, sizeof(msg), "RAW");
break;
}
bmp_printf(SHADOW_FONT(FONT_MED), x_origin+4, y_origin, msg);
}
#endif
}
MENU_UPDATE_FUNC(hist_print)
{
#if defined(FEATURE_HISTOGRAM)
if (hist_draw)
{
int raw = 0;
#ifdef FEATURE_RAW_HISTOGRAM
raw = RAW_HISTOGRAM_ENABLED && can_use_raw_overlays_menu();
#endif
MENU_SET_VALUE(
"%s%s",
raw ? (RAW_HISTOBAR_ENABLED ? "RAW HistoBar" : "RAW RGB") :
hist_type == 0 ? "Luma (YUV)" :
hist_type == 1 ? "RGB (YUV)" : "RAW N/A",
hist_log ? ", Log" : (raw && RAW_HISTOBAR_ENABLED) ? "" : ", Linear"
);
}
#endif
}
#endif /* defined(FEATURE_HISTOGRAM) */
MENU_UPDATE_FUNC(hist_warn_display)
{
#if defined(FEATURE_HISTOGRAM)
MENU_SET_VALUE(
"Clip warning : %s",
hist_warn == 0 ? "OFF" :
hist_warn == 1 ? "0.001% px" :
hist_warn == 2 ? "0.01% px" :
hist_warn == 3 ? "0.1% px" :
hist_warn == 4 ? "1% px" :
"Gradual"
);
#endif /* defined(FEATURE_HISTOGRAM) */
}
#ifdef FEATURE_RAW_HISTOGRAM
/* speed:
* 0 = slowest, but 100% accurate (only for GRAY_PROJECTION_GREEN for now)
* 1 = sample at LiveView resolution (720x480)
* 2 = LiveView resolution downsampled by 2 on each axis
* 3 = LiveView resolution downsampled by 3 on each axis
* and so on, until 16
*/
int FAST raw_hist_get_percentile_levels(int* percentiles_x10, int* output_raw_values, int n, int gray_projection, int speed)
{
if (!raw_update_params()) goto err;
get_yuv422_vram();
int* hist = malloc(16384*4);
if (!hist) goto err;
memset(hist, 0, 16384*4);
int off = get_y_skip_offset_for_histogram();
if (speed == 0 && gray_projection == GRAY_PROJECTION_GREEN)
{
/* time: 1-2 seconds on full raw 5D3 */
//~ int t0 = get_ms_clock_value();
for (struct raw_pixblock * row = (struct raw_pixblock *) raw_info.buffer + raw_info.active_area.y1 * raw_info.width / 8 + (raw_info.active_area.x1 + 7) / 8; (void*)row < (void*)raw_info.buffer + raw_info.pitch * raw_info.active_area.y2; row += 2 * raw_info.width / 8)
{
struct raw_pixblock * row2 = row + raw_info.pitch / sizeof(struct raw_pixblock);
struct raw_pixblock * p;
struct raw_pixblock * q;
for (p = row, q = row2; (void*)p < (void*)row + raw_info.jpeg.width * 14/8; p++, q++)
{
/**
* p: abcdefgh abcdefgh
* q: abcdefgh abcdefgh
*
* rgrgrgrg rgrgrgrg
* gbgbgbgb gbgbgbgb
*/
//~ int pa = ((int)(p->a));
int pb = ((int)(p->b_lo | (p->b_hi << 12)));
//~ int pc = ((int)(p->c_lo | (p->c_hi << 10)));
int pd = ((int)(p->d_lo | (p->d_hi << 8)));
//~ int pe = ((int)(p->e_lo | (p->e_hi << 6)));
int pf = ((int)(p->f_lo | (p->f_hi << 4)));
//~ int pg = ((int)(p->g_lo | (p->g_hi << 2)));
int ph = ((int)(p->h));
int qa = ((int)(q->a));
//~ int qb = ((int)(q->b_lo | (q->b_hi << 12)));
int qc = ((int)(q->c_lo | (q->c_hi << 10)));
//~ int qd = ((int)(q->d_lo | (q->d_hi << 8)));
int qe = ((int)(q->e_lo | (q->e_hi << 6)));
//~ int qf = ((int)(q->f_lo | (q->f_hi << 4)));
int qg = ((int)(q->g_lo | (q->g_hi << 2)));
//~ int qh = ((int)(q->h));
hist[pb]++;
hist[pd]++;
hist[pf]++;
hist[ph]++;
hist[qa]++;
hist[qc]++;
hist[qe]++;
hist[qg]++;
/* to check if we sample only the active area */
//~ p->a = rand();
}
}
//~ int t1 = get_ms_clock_value();
//~ NotifyBox(5000, "%d ", t1 - t0);
//~ save_dng("A:/foo.dng");
}
else
{
speed = COERCE(speed, 1, 16);
for (int i = os.y0 + off; i < os.y_max - off; i += speed)
{
int y = BM2RAW_Y(i);
for (int j = os.x0; j < os.x_max; j += speed)
{
int x = BM2RAW_X(j);
int px = raw_get_gray_pixel(x, y, gray_projection);
hist[px & 16383]++;
}
}
}
int total = 0;
int i;
for( i=0 ; i < 16384 ; i++ )
total += hist[i];
for (int k = 0; k < n; k++)
{
int thr = (uint64_t)total * percentiles_x10[k] / 1000 - 2; // 50% => median; allow up to 2 stuck pixels
int n = 0;
int ans = -1;
for( i=0 ; i < 16384; i++ )
{
n += hist[i];
if (n >= thr)
{
ans = i;
break;
}
}
output_raw_values[k] = ans;
}
free(hist);
return 1;
err:
for (int k = 0; k < n; k++)
{
output_raw_values[k] = -1;
}
return -1;
}
int raw_hist_get_percentile_level(int percentile_x10, int gray_projection, int speed)
{
int ans;
raw_hist_get_percentile_levels(&percentile_x10, &ans, 1, gray_projection, speed);
return ans;
}
int raw_hist_get_overexposure_percentage(int gray_projection)
{
if (!raw_update_params()) return -1;
get_yuv422_vram();
/* use some tolerance when checking for overexposure, because white level might vary a little */
int white = raw_info.white_level * 80 / 100;
int over = 0;
int total = 0;
int step = lv ? 4 : 2;
for (int i = os.y0; i < os.y_max; i += step)
{
int y = BM2RAW_Y(i);
for (int j = os.x0; j < os.x_max; j += step)
{
int x = BM2RAW_X(j);
int px = raw_get_gray_pixel(x, y, gray_projection);
if (px >= white) over++;
total++;
}
}
/* percentage x100 */
return over * 10000 / total;
}
#include "lvinfo.h"
static int histobar_ev[15];
static int histobar_stops;
static int histobar_clipped;
static int histobar_stops_until_overexposure;
static int histobar_midtone_level; /* median */
static int histobar_shadow_level; /* at 5th percentile, as in ETTR */
static void histobar_refresh()
{
int stops = COERCE((raw_info.dynamic_range + 50) / 100, 0, 14);
int i_prev = 0;
for(int ev = 0 ; ev < stops ; ev++ )
{
int evh = COERCE(ev - stops + 12, 0, 12);
int i = evh * HIST_WIDTH / 12;
int pixels_in_this_stop = 0;
for (int j = i_prev; j < i; j++)
{
int max = MAX(MAX(histogram.hist_r[i], histogram.hist_g[i]), histogram.hist_b[i]);
pixels_in_this_stop += max;
}
histobar_ev[ev] = pixels_in_this_stop;
i_prev = i;
}
histobar_stops = stops;
int i = HIST_WIDTH-1;
histobar_clipped = histogram.hist_r[i] + histogram.hist_g[i] + histogram.hist_b[i];
int stops_until_overexposure = INT_MIN;
int acc = 0;
int shadow_px = histogram.total_px / 20;
int midtone_px = histogram.total_px / 2;
for( i=0 ; i < HIST_WIDTH ; i++ )
{
int thr = histogram.total_px / 10000;
int max = MAX(MAX(histogram.hist_r[i], histogram.hist_g[i]), histogram.hist_b[i]);
int ev_till_right_x10 = 120 - (i * 120 / (HIST_WIDTH-1));
int ev_till_right = ev_till_right_x10 / 10;
int ev_from_left = histobar_stops - ev_till_right;
if (max > thr)
{
stops_until_overexposure = ev_till_right_x10;
}
if (acc < midtone_px && acc + max >= midtone_px)
{
histobar_midtone_level = ev_from_left;
}
if (acc < shadow_px && acc + max >= shadow_px)
{
histobar_shadow_level = ev_from_left;
}
acc += max;
}
#ifdef CONFIG_MODULES
int ettr_stops = INT_MIN;
if (auto_ettr_export_correction(&ettr_stops) == 1)
if (ettr_stops != INT_MIN)
stops_until_overexposure = (ettr_stops+5)/10;
#endif
histobar_stops_until_overexposure = stops_until_overexposure;
#ifdef CONFIG_QEMU
/* double-check median and shadow levels (compare with histobar readings) */
int prctiles[2] = {500, 50};
int raw_levels[2];
int ev_levels[2];
raw_hist_get_percentile_levels(prctiles, raw_levels, 2, GRAY_PROJECTION_MAX_RGB, 0);
ev_levels[0] = (int)roundf(raw_to_ev(raw_levels[0]) * 10);
ev_levels[1] = (int)roundf(raw_to_ev(raw_levels[1]) * 10);
printf("Median : %d = %s%d.%d EV\n", raw_levels[0], FMT_FIXEDPOINT1(ev_levels[0]));
printf("Shadow 5%%: %d = %s%d.%d EV\n", 0, raw_levels[1], FMT_FIXEDPOINT1(ev_levels[1]));
#endif
lens_display_set_dirty();
}
static LVINFO_UPDATE_FUNC(histobar_update)
{
#ifdef FEATURE_RAW_HISTOGRAM
if (!RAW_HISTOBAR_ENABLED)
return;
#endif
if (!lv_luma_is_accurate())
return;
if (!can_use_raw_overlays())
return;
int w = fontspec_font(item->fontspec)->width - 4;
int h = fontspec_font(item->fontspec)->height - 4;
item->width = histobar_stops * w;
item->height = h;
item->custom_drawing = 1;
if (can_draw)
{
for(int ev = 0 ; ev < histobar_stops ; ev++ )
{
int pixels_in_this_stop = histobar_ev[ev];
int thr = histogram.total_px / 10000;
int full = (pixels_in_this_stop > thr);
int x = item->x - item->width/2 + ev * w + 1;
int y = item->y-2;
int h = item->height;
int fh = h; /* fill height */
int fg = item->color_fg; /* outline color */
int bg0 = item->color_bg; /* background color */
int bg = bg0; /* fill color */
if (full)
{
bg = fg;
}
if (ev == histobar_midtone_level)
{
fg = bg = COLOR_YELLOW;
}
if (ev < histobar_shadow_level && full)
{
fh = h/3;
}
if (ev == histobar_stops-1 && histobar_clipped > thr)
{
fg = bg = COLOR_RED;
fh = h;
}
else if (ev == 0 && full)
{
fg = bg = COLOR_LIGHT_BLUE;
}
bmp_fill(bg0, x, y, w-2, h-fh);
bmp_fill(bg, x, y+h-fh, w-2, fh);
bmp_draw_rect(fg, x, y, w-2, h);
}
}
}
static LVINFO_UPDATE_FUNC(histobar_indic_update)
{
LVINFO_BUFFER(10);
if (!hist_meter)
return;
#ifdef FEATURE_RAW_HISTOGRAM
if (!RAW_HISTOBAR_ENABLED)
return;
#endif
if (!lv_luma_is_accurate())
return;
if (!can_use_raw_overlays())
return;
switch (hist_meter)
{
case HIST_METER_DYNAMIC_RANGE:
{
int dr = (raw_info.dynamic_range + 5) / 10;
snprintf(buffer, sizeof(buffer), SYM_DR"%d.%d", dr/10, dr%10);
break;
}
case HIST_METER_ETTR_HINT:
{
int stops_until_overexposure = histobar_stops_until_overexposure;
if (stops_until_overexposure != INT_MIN)
snprintf(buffer, sizeof(buffer), SYM_ETTR"%s%d.%d", FMT_FIXEDPOINT1(stops_until_overexposure));
else
snprintf(buffer, sizeof(buffer), "OVER");
break;
}
}
}
static struct lvinfo_item info_items[] = {
{
.name = "HistoBar",
.which_bar = LV_PREFER_TOP_BAR,
.update = histobar_update,
.preferred_position = -1,
},
{
.name = "EV indic",
.which_bar = LV_PREFER_TOP_BAR,
.update = histobar_indic_update,
.preferred_position = -1,
},
};
static void hist_init()
{
lvinfo_add_items(info_items, COUNT(info_items));
}
INIT_FUNC("hist", hist_init);
#endif
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...