https://bitbucket.org/daniel_fort/magic-lantern
Raw File
Tip revision: 22238632e9e49f207595ed03024791b4c5834beb authored by Bilal Fakhouri on 02 July 2018, 22:58:09 UTC
crop_rec.c Added new preset "mv1080 in EOS M" it's worked for patching mv1080 into mv720 in Canon 700D ,, and the two cameras shares the same values but it didn't work for EOS M the resolution stayed 1736x696 unlike in canon 700D 1736x1158 .
Tip revision: 2223863
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;

    /* mapping from 14-bit RAW to EV on the 12-bit histogram:
     * above raw_info.white_level: last bin (HIST_WIDTH-1)
     * 12 stops below that: first bin (0)
     * raw_to_ev returns 0 at white level or above,
     * and negative floating point values below */
    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);
        qprintf("[HIST] RAW %d => %d (white=%d)\n", i, r2ev[i], raw_info.white_level);
    }

    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];
                unsigned int over_g = histogram.hist_g[i];
                unsigned int over_b = histogram.hist_b[i];

                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();
        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();
        //~ 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
back to top