/** \file * Big file that needs to be splitted by features * */ /* * Copyright (C) 2009 Trammell Hudson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "zebra.h" #include "vectorscope.h" #include "electronic_level.h" #include "dryos.h" #include "bmp.h" #include "version.h" #include "config.h" #include "menu.h" #include "property.h" #include "gui.h" #include "lens.h" #include "math.h" #include "beep.h" #include "raw.h" #include "shoot.h" #include "focus.h" #include "lvinfo.h" #include "powersave.h" #include "imgconv.h" #include "falsecolor.h" #include "histogram.h" /* todo: move battery stuff in battery.c */ #include "battery.h" #ifdef FEATURE_LCD_SENSOR_SHORTCUTS #include "lcdsensor.h" #endif #if defined(FEATURE_RAW_HISTOGRAM) || defined(FEATURE_RAW_ZEBRAS) || defined(FEATURE_RAW_SPOTMETER) #define FEATURE_RAW_OVERLAYS #endif #define DIGIC_ZEBRA_REGISTER 0xC0F140cc #define FAST_ZEBRA_GRID_COLOR 4 // invisible diagonal grid for zebras; must be unused and only from 0-15 // those colors will not be considered for histogram (so they should be very unlikely to appear in real situations) #define MZ_WHITE 0xFE12FE34 #define MZ_BLACK 0x00120034 #define MZ_GREEN 0xB68DB69E // spotmeter_formula modes #define SPTMTR_F_RGB_PERCENT 4 #ifdef CONFIG_KILL_FLICKER // this will block all Canon drawing routines when the camera is idle extern int kill_canon_gui_mode; #endif // but it will display ML graphics static void waveform_init(); //~ static void histo_init(); static void do_disp_mode_change(); static void show_overlay(); static void transparent_overlay_from_play(); static void transparent_overlay_offset_clear(void* priv, int delta); //~ static void draw_histogram_and_waveform(); static void schedule_transparent_overlay(); //~ static void defish_draw(); //~ static void defish_draw_lv_color(); static int zebra_color_word_row(int c, int y); static void spotmeter_step(); static int zebra_rgb_color(int underexposed, int clipR, int clipG, int clipB, int y); static int zebra_rgb_solid_color(int underexposed, int clipR, int clipG, int clipB); //~ static void defish_draw_play(); extern void zoom_sharpen_step(); extern void bv_auto_update(); void lens_display_set_dirty(); void update_disp_mode_bits_from_params(); //~ void uyvy2yrgb(uint32_t , int* , int* , int* , int* ); int toggle_disp_mode(); static void toggle_disp_mode_menu(void *priv, int delta); // in movie mode: skip the 16:9 bar when computing overlays // in photo mode: compute the overlays on full-screen image int FAST get_y_skip_offset_for_overlays() { // in playback mode, and skip 16:9 bars for movies, but cover the entire area for photos if (!lv) return is_pure_play_movie_mode() ? os.off_169 : 0; // in liveview, try not to overlap top and bottom bars int off = 0; if (lv && is_movie_mode() && video_mode_resolution <= 1) off = os.off_169; int t = get_ml_topbar_pos(); int b = get_ml_bottombar_pos(); int mid = os.y0 + (os.y_ex >> 1); if (t < mid && t + 25 > os.y0 + off) off = t + 25 - os.x0; if (t > mid) b = MIN(b, t); if (b < os.y_max - off) off = os.y_max - b; return off; } int FAST get_y_skip_offset_for_histogram() { // in playback mode, and skip 16:9 bars for movies, but cover the entire area for photos if (!lv) return is_pure_play_movie_mode() ? os.off_169 : 0; // in liveview, try not to overlap top and bottom bars int off = 0; if (lv && is_movie_mode() && video_mode_resolution <= 1) off = os.off_169; return off; } static int is_zoom_mode_so_no_zebras() { if (!lv) return 0; if (lv_dispsize == 1) return 0; /* always on in x1 */ if (lv_dispsize > 5) return 1; /* always disable in x10 zoom and also in the special x1 mode from some recent models (0x81) */ if (raw_lv_is_enabled()) return 0; /* exception: in raw mode we can record crop videos */ return 1; } // true if LV image reflects accurate luma of the final picture / video int lv_luma_is_accurate() { if (is_movie_mode()) return 1; int digic_iso_gain_photo = get_digic_iso_gain_photo(); return get_expsim() && digic_iso_gain_photo == 1024; } #ifdef FEATURE_SHOW_OVERLAY_FPS static int show_lv_fps = 0; // for debugging #endif #define WAVEFORM_WIDTH 180 #define WAVEFORM_HEIGHT 120 #define WAVEFORM_FACTOR (1 << waveform_size) // 1, 2 or 4 #define WAVEFORM_OFFSET (waveform_size <= 1 ? 80 : 0) #define WAVEFORM_FULLSCREEN (waveform_draw && waveform_size == 2) #define BVRAM_MIRROR_SIZE (BMPPITCH*540) CONFIG_INT("lv.disp.profiles", disp_profiles_0, 0); static CONFIG_INT("disp.mode", disp_mode, 0); static CONFIG_INT("disp.mode.a", disp_mode_a, 1); static CONFIG_INT("disp.mode.b", disp_mode_b, 1); static CONFIG_INT("disp.mode.c", disp_mode_c, 1); static CONFIG_INT("disp.mode.x", disp_mode_x, 1); static CONFIG_INT( "transparent.overlay", transparent_overlay, 0); static CONFIG_INT( "transparent.overlay.x", transparent_overlay_offx, 0); static CONFIG_INT( "transparent.overlay.y", transparent_overlay_offy, 0); static CONFIG_INT( "transparent.overlay.autoupd", transparent_overlay_auto_update, 0); static int transparent_overlay_hidden = 0; static CONFIG_INT( "global.draw", global_draw, 3 ); #define ZEBRAS_IN_QUICKREVIEW (global_draw > 1) #define ZEBRAS_IN_LIVEVIEW (global_draw & 1) static CONFIG_INT( "zebra.draw", zebra_draw, 1 ); #ifdef FEATURE_ZEBRA_FAST static CONFIG_INT( "zebra.colorspace", zebra_colorspace, 2 );// luma/rgb/lumafast #else static CONFIG_INT( "zebra.colorspace", zebra_colorspace, 0 );// luma/rgb/lumafast #endif static CONFIG_INT( "zebra.thr.hi", zebra_level_hi, 99 ); static CONFIG_INT( "zebra.thr.lo", zebra_level_lo, 0 ); static CONFIG_INT( "zebra.rec", zebra_rec, 1 ); static CONFIG_INT( "zebra.raw.under", zebra_raw_underexposure, 1 ); #define MZ_ZOOM_WHILE_RECORDING 1 #define MZ_ZOOMREC_N_FOCUS_RING 2 #define MZ_TAKEOVER_ZOOM_IN_BTN 3 #define MZ_ALWAYS_ON 4 static CONFIG_INT( "zoom.overlay", zoom_overlay_enabled, 0); static CONFIG_INT( "zoom.overlay.trig", zoom_overlay_trigger_mode, MZ_TAKEOVER_ZOOM_IN_BTN); static CONFIG_INT( "zoom.overlay.size", zoom_overlay_size, 1); static CONFIG_INT( "zoom.overlay.x", zoom_overlay_x, 1); #ifdef CONFIG_5D3 static CONFIG_INT( "zoom.overlay.pos", zoom_overlay_pos, 4); // less flicker when MZ is at the bottom #else static CONFIG_INT( "zoom.overlay.pos", zoom_overlay_pos, 1); #endif static CONFIG_INT( "zoom.overlay.split", zoom_overlay_split, 0); int get_zoom_overlay_trigger_mode() { #ifdef FEATURE_MAGIC_ZOOM if (!get_global_draw()) return 0; if (!zoom_overlay_enabled) return 0; return zoom_overlay_trigger_mode; #else return 0; #endif } int get_zoom_overlay_trigger_by_focus_ring() { #ifdef FEATURE_MAGIC_ZOOM int z = get_zoom_overlay_trigger_mode(); #ifdef CONFIG_ZOOM_BTN_NOT_WORKING_WHILE_RECORDING return z == 2 || z == 3; #else return z == 2; #endif #else return 0; #endif } static int get_zoom_overlay_trigger_by_halfshutter() { #ifdef FEATURE_MAGIC_ZOOM #ifdef CONFIG_ZOOM_BTN_NOT_WORKING_WHILE_RECORDING int z = get_zoom_overlay_trigger_mode(); return z == 1 || z == 3; #else return 0; #endif #else return 0; #endif } static int zoom_overlay_triggered_by_zoom_btn = 0; static int zoom_overlay_triggered_by_focus_ring_countdown = 0; static int is_zoom_overlay_triggered_by_zoom_btn() { if (!get_global_draw()) return 0; return zoom_overlay_triggered_by_zoom_btn; } static int zoom_overlay_dirty = 0; int should_draw_zoom_overlay() { #ifdef FEATURE_MAGIC_ZOOM if (!lv) return 0; if (!zoom_overlay_enabled) return 0; if (!zebra_should_run()) return 0; if (EXT_MONITOR_RCA) return 0; if (hdmi_code >= 5) return 0; #if defined(CONFIG_DISPLAY_FILTERS) && defined(CONFIG_CAN_REDIRECT_DISPLAY_BUFFER) && !defined(CONFIG_CAN_REDIRECT_DISPLAY_BUFFER_EASILY) extern int display_broken_for_mz(); /* tweaks.c */ if (display_broken_for_mz()) return 0; #endif if (zoom_overlay_size == 3 && video_mode_crop && is_movie_mode()) return 0; if (zoom_overlay_trigger_mode == 4) return true; #ifdef CONFIG_ZOOM_BTN_NOT_WORKING_WHILE_RECORDING if (zoom_overlay_triggered_by_zoom_btn || zoom_overlay_triggered_by_focus_ring_countdown) return true; #else int zt = zoom_overlay_triggered_by_zoom_btn; int zm = get_zoom_overlay_trigger_mode(); if (zt && (zm==1 || zm==2) && NOT_RECORDING) zt = 0; // in ZR and ZR+F modes, if triggered while recording, it should only work while recording if (zt || zoom_overlay_triggered_by_focus_ring_countdown) return true; #endif #endif return false; } int digic_zoom_overlay_enabled() { #ifdef FEATURE_MAGIC_ZOOM_FULL_SCREEN return zoom_overlay_size == 3 && should_draw_zoom_overlay(); #else return 0; #endif } int nondigic_zoom_overlay_enabled() { return zoom_overlay_size != 3 && should_draw_zoom_overlay(); } static CONFIG_INT( "focus.peaking", focus_peaking, 0); //~ static CONFIG_INT( "focus.peaking.method", focus_peaking_method, 1); static CONFIG_INT( "focus.peaking.filter.edges", focus_peaking_filter_edges, 0); // prefer texture details rather than strong edges static CONFIG_INT( "focus.peaking.thr", focus_peaking_pthr, 5); // 1% static CONFIG_INT( "focus.peaking.color", focus_peaking_color, 7); // R,G,B,C,M,Y,cc1,cc2 CONFIG_INT( "focus.peaking.grayscale", focus_peaking_grayscale, 0); // R,G,B,C,M,Y,cc1,cc2 #if defined(CONFIG_DISPLAY_FILTERS) && defined(FEATURE_FOCUS_PEAK_DISP_FILTER) static CONFIG_INT( "focus.peaking.disp", focus_peaking_disp, 0); // display as dots or blended #else #define focus_peaking_disp 0 #endif int focus_peaking_as_display_filter() { #if defined(CONFIG_DISPLAY_FILTERS) && defined(FEATURE_FOCUS_PEAK_DISP_FILTER) return lv && focus_peaking && focus_peaking_disp; #else return 0; #endif } static CONFIG_INT( "waveform.draw", waveform_draw, #ifdef CONFIG_4_3_SCREEN 1 #else 0 #endif ); static CONFIG_INT( "waveform.size", waveform_size, 0 ); static CONFIG_INT( "waveform.bg", waveform_bg, COLOR_ALMOST_BLACK ); // solid black int histogram_or_small_waveform_enabled() { return ( #ifdef FEATURE_HISTOGRAM ( (hist_draw) && #ifdef FEATURE_RAW_OVERLAYS !(RAW_HISTOBAR_ENABLED && can_use_raw_overlays_menu()) && #endif 1 ) || #endif (waveform_draw && !waveform_size) ) && get_expsim(); } CONFIG_INT( "clear.preview", clearscreen, 0); static CONFIG_INT( "clear.preview.delay", clearscreen_delay, 1000); // ms //~ CONFIG_INT( "clear.preview.mode", clearscreen_mode, 0); // 2 is always // keep old program logic //~ #define clearscreen (clearscreen_enabled ? clearscreen_mode+1 : 0) static CONFIG_INT( "spotmeter.size", spotmeter_size, 5 ); static CONFIG_INT( "spotmeter.draw", spotmeter_draw, 1 ); static CONFIG_INT( "spotmeter.formula", spotmeter_formula, 0 ); // 0 percent, 1 IRE AJ, 2 IRE Piers static CONFIG_INT( "spotmeter.position", spotmeter_position, 1 ); // fixed / attached to AF frame //~ static CONFIG_INT( "unified.loop", unified_loop, 2); // temporary; on/off/auto //~ static CONFIG_INT( "zebra.density", zebra_density, 0); //~ static CONFIG_INT( "hd.vram", use_hd_vram, 0); /** * Normal BMP VRAM has its origin in 720x480 center crop * But on HDMI you are allowed to go back 120x30 pixels (BMP_W_MINUS x BMP_H_MINUS). * * For mirror VRAM we'll keep the same addressing mode: * allocate full size (960x540) and use the pointer to 720x480 center crop. */ static uint8_t* bvram_mirror_start = 0; static uint8_t* bvram_mirror = 0; uint8_t* get_bvram_mirror() { return bvram_mirror; } //~ #define bvram_mirror bmp_vram_idle() #include "cropmarks.c" PROP_HANDLER(PROP_HOUTPUT_TYPE) { extern int ml_started; if (ml_started) redraw(); } #ifdef CONFIG_VARIANGLE_DISPLAY static volatile int lcd_position = 0; static volatile int display_dont_mirror_dirty; PROP_HANDLER(PROP_LCD_POSITION) { if (lcd_position != (int)buf[0]) display_dont_mirror_dirty = 1; lcd_position = buf[0]; redraw_after(100); } #endif /* from powersave.c */ extern int idle_globaldraw_disable; int get_global_draw() // menu setting, or off if { #ifdef FEATURE_GLOBAL_DRAW #ifdef LV_DISP_MODE lv_disp_mode = LV_DISP_MODE; #endif extern int ml_started; if (!ml_started) return 0; if (!global_draw) return 0; if (PLAY_MODE) return 1; // exception, always draw stuff in play mode #ifdef CONFIG_CONSOLE extern int console_visible; if (console_visible) return 0; #endif if (lv && ZEBRAS_IN_LIVEVIEW) { return lv_disp_mode == 0 && !idle_globaldraw_disable && bmp_is_on() && DISPLAY_IS_ON && !RECORDING_H264_STARTING && #ifdef CONFIG_KILL_FLICKER !(lv && kill_canon_gui_mode && !canon_gui_front_buffer_disabled() && !gui_menu_shown()) && #endif !LV_PAUSED && #ifdef CONFIG_5D3 !(hdmi_code >= 5 && video_mode_resolution>0) && // unusual VRAM parameters #endif job_state_ready_to_take_pic(); } if (!lv && ZEBRAS_IN_QUICKREVIEW) { return DISPLAY_IS_ON; } #endif return 0; } int get_global_draw_setting() // whatever is set in menu { return global_draw; } /** Store the waveform data for each of the WAVEFORM_WIDTH bins with * 128 levels */ static uint8_t* waveform = 0; #define WAVEFORM_UNSAFE(x,y) (waveform[(x) + (y) * WAVEFORM_WIDTH]) #define WAVEFORM(x,y) (waveform[COERCE((x), 0, WAVEFORM_WIDTH-1) + COERCE((y), 0, WAVEFORM_HEIGHT-1) * WAVEFORM_WIDTH]) /** Generate the histogram data from the YUV frame buffer. * * Walk the frame buffer two pixels at a time, in 32-bit chunks, * to avoid err70 while recording. * * Average two adjacent pixels to try to reduce noise slightly. * * Update the hist_max for the largest number of bin entries found * to scale the histogram to fit the display box from top to * bottom. */ #if defined(FEATURE_HISTOGRAM) || defined(FEATURE_WAVEFORM) || defined(FEATURE_VECTORSCOPE) #ifdef FEATURE_HISTOGRAM static void hist_add_pixel(uint32_t pixel, int Y) { if (histogram.is_rgb) { int R, G, B; //~ uyvy2yrgb(pixel, &Y, &R, &G, &B); COMPUTE_UYVY2YRGB(pixel, Y, R, G, B); // YRGB range: 0-255 uint32_t R_level = (R * HIST_WIDTH) >> 8; uint32_t G_level = (G * HIST_WIDTH) >> 8; uint32_t B_level = (B * HIST_WIDTH) >> 8; histogram.hist_r[R_level & (HIST_WIDTH-1)]++; histogram.hist_g[G_level & (HIST_WIDTH-1)]++; histogram.hist_b[B_level & (HIST_WIDTH-1)]++; } /* luma component is always computed, since we need histogram.max */ /* and it's much less expensive than RGB anyway */ histogram.total_px++; uint32_t hist_level = (Y * HIST_WIDTH) >> 8; // Ignore the 0 bin. It generates too much noise unsigned count = ++ (histogram.hist[ hist_level & (HIST_WIDTH-1)]); if( hist_level && count > histogram.max ) histogram.max = count; } #endif #ifdef FEATURE_WAVEFORM static inline void waveform_add_pixel(int x, int Y) { uint8_t* w = &WAVEFORM(((x-os.x0) * WAVEFORM_WIDTH) / os.x_ex, (Y * WAVEFORM_HEIGHT) >> 8); if ((*w) < 250) (*w)++; } #endif static void hist_build() { struct vram_info * lv = get_yuv422_vram(); uint32_t* buf = (uint32_t*)lv->vram; if (!buf) return; int x,y; #ifdef FEATURE_HISTOGRAM memset(&histogram, 0, sizeof(histogram)); #endif #ifdef FEATURE_WAVEFORM if (waveform_draw) { waveform_init(); } #endif #ifdef FEATURE_VECTORSCOPE int vectorscope_draw = vectorscope_should_draw(); if (vectorscope_draw) { vectorscope_start(); } #endif #ifdef FEATURE_RAW_HISTOGRAM if (RAW_HISTOGRAM_ENABLED && can_use_raw_overlays()) { hist_build_raw(); } #endif histogram.is_rgb = histogram.is_raw || /* RAW histogram is always RGB-based */ ((hist_type == 1 || /* Use YUV RGB histogram if selected */ hist_type == 2) && /* Fall back to YUV RGB if we can't use the RAW RGB histogram */ !EXT_MONITOR_RCA); /* However, we cannot use YUV RGB histogram on RCA monitors, because they use YUV411 instead of YUV422 */ if (!waveform_draw && !vectorscope_draw && (!hist_draw || histogram.is_raw)) { /* optimization: no YUV-based histogram/waveform/scope enabled * => no need to scan the entire image */ return; } int mz = nondigic_zoom_overlay_enabled(); int off = get_y_skip_offset_for_histogram(); for( y = os.y0 + off; y < os.y_max - off; y += 2 ) { for( x = os.x0 ; x < os.x_max ; x += 2 ) { uint32_t pixel = buf[BM2LV(x,y) >> 2]; // ignore magic zoom borders if (mz && (pixel == MZ_WHITE || pixel == MZ_BLACK || pixel == MZ_GREEN)) continue; int Y = UYVY_GET_AVG_Y(pixel); #ifdef FEATURE_HISTOGRAM if (hist_draw && !histogram.is_raw) { hist_add_pixel(pixel, Y); } #endif #ifdef FEATURE_WAVEFORM if (waveform_draw) { waveform_add_pixel(x, Y); } #endif #ifdef FEATURE_VECTORSCOPE if (vectorscope_draw) { int8_t U = (pixel >> 0) & 0xFF; int8_t V = (pixel >> 16) & 0xFF; vectorscope_addpixel(Y, U, V); } #endif } } } #endif #ifdef FEATURE_RAW_ZEBRAS static CONFIG_INT("raw.zebra", raw_zebra_enable, 2); /* 1 = always, 2 = photo only */ #define RAW_ZEBRA_ENABLE (raw_zebra_enable == 1 || (raw_zebra_enable == 2 && !lv)) static void FAST draw_zebras_raw() { if (!DISPLAY_IS_ON) return; if (!PLAY_OR_QR_MODE) return; if (!raw_update_params()) return; uint8_t * bvram = bmp_vram(); if (!bvram) return; int white = raw_info.white_level; int underexposed = zebra_raw_underexposure ? ev_to_raw(- (raw_info.dynamic_range - (zebra_raw_underexposure - 1) * 100) / 100.0) : 0; int zoom0 = (int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR); /* stop when zooming in playback */ for (int i = os.y0; i < os.y_max; i ++) { int y = BM2RAW_Y(i); for (int j = os.x0; j < os.x_max; j ++) { int x = BM2RAW_X(j); /* for dual ISO: show solid zebras if both sub-images are overexposed */ /* show semitransparent zebras if only one is overexposed */ /* impact on normal ISOs should be minimal */ int dark = (i + j) % 4; int r = dark ? raw_red_pixel_dark(x, y) : raw_red_pixel_bright(x, y); int g = dark ? raw_green_pixel_dark(x, y) : raw_green_pixel_bright(x, y); int b = dark ? raw_blue_pixel_dark(x, y) : raw_blue_pixel_bright(x, y); int u = raw_green_pixel_bright(x, y); /* define this to check if color channels are identified correctly */ #undef RAW_ZEBRA_TEST #ifdef RAW_ZEBRA_TEST { uint32_t* lv = get_yuv422_vram()->vram; int R = r > raw_info.black_level+16 ? (int)(log2f((r - raw_info.black_level) / 16.0f) * 255 / 10) : 1; int G = g > raw_info.black_level+16 ? (int)(log2f((g - raw_info.black_level) / 32.0f) * 255 / 10) : 1; int B = b > raw_info.black_level+16 ? (int)(log2f((b - raw_info.black_level) / 16.0f) * 255 / 10) : 1; int Y = (0.257 * R) + (0.504 * G) + (0.098 * B); int U = -(0.148 * R) - (0.291 * G) + (0.439 * B); int V = (0.439 * R) - (0.368 * G) - (0.071 * B); lv[BM2LV(j,i)/4] = UYVY_PACK(U,Y,V,Y); continue; } #endif int c = zebra_rgb_solid_color(u <= underexposed, r > white, g > white, b > white); if (c) { uint8_t* bp = (uint8_t*) &bvram[BM(j,i)]; uint8_t* mp = (uint8_t*) &bvram_mirror[BM(j,i)]; #define BP (*bp) #define MP (*mp) if (BP != 0 && BP != MP) continue; if ((MP & 0x80)) continue; BP = MP = c; #undef MP #undef BP } } if (!DISPLAY_IS_ON) break; if (!PLAY_OR_QR_MODE) break; if ((int)(int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) != zoom0) break; /* stop when zooming */ } } void FAST zebra_highlight_raw_advanced(struct raw_highlight_info * raw_highlight_info) { if (!DISPLAY_IS_ON) return; if (!raw_update_params()) return; if (lv) { static int aux = INT_MIN; if (!should_run_polling_action(2000, &aux)) return; } uint8_t * bvram = bmp_vram(); if (!bvram) return; int gray_projection = raw_highlight_info->gray_projection; for (int i = os.y0; i < os.y_max; i ++) { int y = BM2RAW_Y(i); int p_prev = 0; for (int j = os.x0; j < os.x_max; j ++) { int x = BM2RAW_X(j); int color = 0; int p = raw_get_gray_pixel(x, y, gray_projection); for (struct raw_highlight_info * hinf = raw_highlight_info; hinf->raw_level_lo || hinf->raw_level_hi; hinf++) { /* gray projection changed? re-sample the pixel */ if (hinf->gray_projection != gray_projection) { gray_projection = hinf->gray_projection; p = raw_get_gray_pixel(x, y, gray_projection); } /* draw line around the highlighted area? */ if (hinf->line_type && p_prev) { /* don't use exact checks in order to filter out some noise */ int sign = SGN(p - hinf->raw_level_lo); int sign_prev = SGN(p_prev - hinf->raw_level_lo); int zerocross = sign != sign_prev; if (unlikely(hinf->raw_level_hi != hinf->raw_level_lo)) { int sign = SGN(p - hinf->raw_level_hi); int sign_prev = SGN(p_prev - hinf->raw_level_hi); int zerocross_hi = sign != sign_prev; zerocross = zerocross || zerocross_hi; } if (zerocross) { color = hinf->color; } } /* fill the highlighted area with something? */ if (!color && hinf->fill_type) { int should_fill = hinf->fill_type == ZEBRA_FILL_DIAG ? (i + j) % 4 == 0 : hinf->fill_type == ZEBRA_FILL_50_PERCENT ? (i + j) % 2 : 1; if (should_fill) { if (p >= hinf->raw_level_lo && p <= hinf->raw_level_hi) { color = hinf->color; } } } if (color) { /* do not check further rules if a pixel was drawn */ /* (first rules have higher priority) */ break; } } p_prev = p; /* should we draw something? */ if (color || lv) { uint8_t* bp = (uint8_t*) &bvram[BM(j,i)]; uint8_t* mp = (uint8_t*) &bvram_mirror[BM(j,i)]; #define BP (*bp) #define MP (*mp) if (BP != 0 && BP != MP) continue; if ((MP & 0x80)) continue; BP = MP = color; #undef MP #undef BP } } if (!DISPLAY_IS_ON) break; } } static void FAST draw_zebras_raw_lv() { if (!raw_update_params()) return; uint8_t * const bvram = bmp_vram_real(); if (!bvram) return; uint8_t * const bvram_mirror = get_bvram_mirror(); if (!bvram_mirror) return; int white = raw_info.white_level; if (white > 16383) white = 15000; int underexposed = zebra_raw_underexposure ? ev_to_raw(- (raw_info.dynamic_range - (zebra_raw_underexposure - 1) * 100) / 100.0) : 0; int off = get_y_skip_offset_for_overlays(); for(int i = os.y0 + off; i < os.y_max - off; i += 2 ) { uint64_t * const b_row = (uint64_t*)( bvram + BM_R(i) ); // 2 pixels uint64_t * const m_row = (uint64_t*)( bvram_mirror + BM_R(i) ); // 2 pixels uint64_t* bp; // through bmp vram uint64_t* mp; // through mirror int y = BM2RAW_Y(i); if (y < raw_info.active_area.y1 || y > raw_info.active_area.y2) continue; for (int j = os.x0; j < os.x_max; j += 8) { bp = b_row + j/8; mp = m_row + j/8; #define BP (*bp) #define MP (*mp) if (BP != 0 && BP != MP) { little_cleanup(bp, mp); continue; } if ((MP & 0x80808080)) continue; int x = BM2RAW_X(j); if (x < raw_info.active_area.x1 || x > raw_info.active_area.x2) continue; /* for dual ISO: use dark lines for overexposure and bright lines for underexposure */ int r = raw_red_pixel_dark(x, y); int g = raw_green_pixel_dark(x, y); int b = raw_blue_pixel_dark(x, y); int u = raw_green_pixel_bright(x, y); uint64_t c = zebra_rgb_solid_color(u <= underexposed, r > white, g > white, b > white); c = c | (c << 32); MP = BP = c; #undef BP #undef MP } } } static MENU_UPDATE_FUNC(raw_zebra_update) { menu_checkdep_raw(entry, info); if (raw_zebra_enable) MENU_SET_WARNING(MENU_WARN_INFO, "Will use RAW RGB zebras %safter taking a pic.", raw_zebra_enable == 1 ? "in LiveView and " : ""); } #endif int get_under_and_over_exposure(int thr_lo, int thr_hi, int* under, int* over) { *under = -1; *over = -1; struct vram_info * lv = get_yuv422_vram(); if (!lv) return -1; *under = 0; *over = 0; int total = 0; void* vram = lv->vram; int x,y; for( y = os.y0 ; y < os.y_max; y ++ ) { uint32_t * const v_row = (uint32_t*)( vram + BM2LV_R(y) ); for( x = os.x0 ; x < os.x_max ; x += 2 ) { uint32_t pixel = v_row[x >> 1]; int Y, R, G, B; //~ uyvy2yrgb(pixel, &Y, &R, &G, &B); COMPUTE_UYVY2YRGB(pixel, Y, R, G, B); int M = MAX(R,G); M = MAX(M, B); if (pixel && Y < thr_lo) (*under)++; // try to ignore black bars if (M > thr_hi) (*over)++; total++; } } return total; } #ifdef FEATURE_ZEBRA #define ZEBRA_COLOR_WORD_SOLID(x) ( (x) | (x)<<8 | (x)<<16 | (x)<<24 ) static int zebra_rgb_color(int underexposed, int clipR, int clipG, int clipB, int y) { if (underexposed) return zebra_color_word_row(79, y); switch ((clipR ? 4 : 0) | (clipG ? 2 : 0) | (clipB ? 1 : 0)) { case 0b111: return ZEBRA_COLOR_WORD_SOLID(COLOR_BLACK); case 0b110: return ZEBRA_COLOR_WORD_SOLID(COLOR_YELLOW); case 0b101: return ZEBRA_COLOR_WORD_SOLID(COLOR_MAGENTA); case 0b011: return ZEBRA_COLOR_WORD_SOLID(COLOR_CYAN); case 0b100: return y&2 ? 0 : ZEBRA_COLOR_WORD_SOLID(COLOR_RED); case 0b001: return y&2 ? 0 : ZEBRA_COLOR_WORD_SOLID(COLOR_BLUE); case 0b010: return y&2 ? 0 : ZEBRA_COLOR_WORD_SOLID(COLOR_GREEN2); default: return 0; } } static int zebra_rgb_solid_color(int underexposed, int clipR, int clipG, int clipB) { if (underexposed) return ZEBRA_COLOR_WORD_SOLID(79); switch ((clipR ? 4 : 0) | (clipG ? 2 : 0) | (clipB ? 1 : 0)) { case 0b111: return ZEBRA_COLOR_WORD_SOLID(COLOR_BLACK); case 0b110: return ZEBRA_COLOR_WORD_SOLID(COLOR_YELLOW); case 0b101: return ZEBRA_COLOR_WORD_SOLID(COLOR_MAGENTA); case 0b011: return ZEBRA_COLOR_WORD_SOLID(COLOR_CYAN); case 0b100: return ZEBRA_COLOR_WORD_SOLID(COLOR_RED); case 0b001: return ZEBRA_COLOR_WORD_SOLID(COLOR_BLUE); case 0b010: return ZEBRA_COLOR_WORD_SOLID(COLOR_GREEN2); default: return 0; } } #endif #ifdef FEATURE_WAVEFORM /** Draw the waveform 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. */ static void waveform_draw_image( unsigned x_origin, unsigned y_origin, unsigned height ) { if (!PLAY_OR_QR_MODE) { if (!lv_luma_is_accurate()) return; } // Ensure that x_origin is quad-word aligned x_origin &= ~3; uint8_t * const bvram = bmp_vram(); if (!bvram) return; unsigned pitch = BMPPITCH; if( histogram.max == 0 ) histogram.max = 1; int i, y; // vertical line up to the hist size for (int k = 0; k < WAVEFORM_FACTOR; k++) { for( y=WAVEFORM_HEIGHT-1 ; y>=0 ; y-- ) { int y_bmp = y_origin + y * height / WAVEFORM_HEIGHT + k; if (y_bmp < 0) continue; if (y_bmp >= BMP_H_PLUS) continue; uint8_t * row = bvram + x_origin + y_bmp * pitch; //int y_next = (y-1) * height / WAVEFORM_HEIGHT; uint32_t pixel = 0; int w = WAVEFORM_WIDTH*WAVEFORM_FACTOR; for( i=0 ; i> 7; if( count > 42 - 5 ) count = COLOR_RED; else if( count > 0 ) count += 38 + 5; else // Draw a series of colored scales if( y == (WAVEFORM_HEIGHT*1)>>2 ) count = COLOR_BLUE; else if( y == (WAVEFORM_HEIGHT*2)>>2 ) count = 0xE; // pink else if( y == (WAVEFORM_HEIGHT*3)>>2 ) count = COLOR_BLUE; else count = waveform_bg; // transparent pixel |= (count << ((i & 3)<<3)); if( (i & 3) != 3 ) continue; // Draw the pixel, rounding down to the nearest // quad word write (and then nop to avoid err70). *(uint32_t*) ALIGN32(row + i) = pixel; #ifdef CONFIG_500D // err70?! asm( "nop" ); asm( "nop" ); asm( "nop" ); asm( "nop" ); asm( "nop" ); asm( "nop" ); asm( "nop" ); asm( "nop" ); #endif pixel = 0; } } bmp_draw_rect(60, x_origin-1, y_origin-1, WAVEFORM_WIDTH*WAVEFORM_FACTOR+1, height+1); } } #endif static int fps_ticks = 0; static void waveform_init() { #ifdef FEATURE_WAVEFORM if (!waveform) waveform = malloc(WAVEFORM_WIDTH * WAVEFORM_HEIGHT); bzero32(waveform, WAVEFORM_WIDTH * WAVEFORM_HEIGHT); #endif } static void bvram_mirror_clear() { ASSERT(bvram_mirror_start); BMP_LOCK( bzero32(bvram_mirror_start, BMP_VRAM_SIZE); ) cropmark_cache_dirty = 1; } void bvram_mirror_init() { if (!bvram_mirror_start) { #if defined(RSCMGR_MEMORY_PATCH_END) extern unsigned int ml_reserved_mem; bvram_mirror_start = (uint8_t*) (RESTARTSTART + ml_reserved_mem); #else bvram_mirror_start = (void*)malloc(BMP_VRAM_SIZE); #endif if (!bvram_mirror_start) { while(1) { bmp_printf(FONT_MED, 30, 30, "Failed to allocate BVRAM mirror"); msleep(100); } } // to keep the same addressing mode as with normal BMP VRAM - origin in 720x480 center crop bvram_mirror = bvram_mirror_start + BMP_HDMI_OFFSET; bvram_mirror_clear(); } } #ifdef FEATURE_FOCUS_PEAK static int get_focus_color(int thr, int d) { return focus_peaking_color == 0 ? COLOR_RED : focus_peaking_color == 1 ? 7 : focus_peaking_color == 2 ? COLOR_BLUE : focus_peaking_color == 3 ? 5 : focus_peaking_color == 4 ? 14 : focus_peaking_color == 5 ? 15 : focus_peaking_color == 6 ? (thr > 50 ? COLOR_RED : thr > 40 ? 19 /*orange*/ : thr > 30 ? 15 /*yellow*/ : thr > 20 ? 5 /*cyan*/ : 9 /*light blue*/) : focus_peaking_color == 7 ? ( d > 50 ? COLOR_RED : d > 40 ? 19 /*orange*/ : d > 30 ? 15 /*yellow*/ : d > 20 ? 5 /*cyan*/ : 9 /*light blue*/) : 1; } #endif #ifdef FEATURE_ZEBRA static inline int zebra_color_word_row(int c, int y) { if (!c) return 0; uint32_t cw = 0; switch(y % 4) { case 0: cw = c | c << 8; break; case 1: cw = c << 8 | c << 16; break; case 2: cw = c << 16 | c << 24; break; case 3: cw = c << 24 | c ; break; } return cw; } #endif #ifdef FEATURE_FOCUS_PEAK #define MAX_DIRTY_PIXELS 5000 static int* dirty_pixels = 0; static uint32_t* dirty_pixel_values = 0; static int dirty_pixels_num = 0; //~ static unsigned int* bm_hd_r_cache = 0; static uint16_t bm_lv_x_cache[BMP_W_PLUS - BMP_W_MINUS]; static void zebra_update_lut() { static int prev_bm2lv_sx = 0; int rebuild = 0; if(unlikely(prev_bm2lv_sx != bm2lv.sx)) { prev_bm2lv_sx = bm2lv.sx; rebuild = 1; } if(unlikely(rebuild)) { int xStart = os.x0 + 8; int xEnd = os.x_max - 8; for (int x = xStart; x < xEnd; x += 1) { bm_lv_x_cache[x - BMP_W_MINUS] = BM2LV_X(x) * 2 + 1; } } } #endif #ifdef FEATURE_ZEBRA #ifdef FEATURE_ZEBRA_FAST static int zebra_digic_dirty = 0; #endif static void draw_zebras( int Z ) { uint8_t * const bvram = bmp_vram_real(); int zd = Z && zebra_draw && (lv_luma_is_accurate() || PLAY_OR_QR_MODE) && (zebra_rec || NOT_RECORDING); // when to draw zebras if (zd) { #ifdef FEATURE_RAW_ZEBRAS if (RAW_ZEBRA_ENABLE && can_use_raw_overlays()) { if (lv) draw_zebras_raw_lv(); else draw_zebras_raw(); return; } #endif int zlh = zebra_level_hi * 255 / 100 - 1; int zll = zebra_level_lo * 255 / 100; #ifdef FEATURE_ZEBRA_FAST int only_over = (zebra_level_hi <= 100 && zebra_level_lo == 0); int only_under = (zebra_level_lo > 0 && zebra_level_hi > 100); int only_one = only_over || only_under; // fast zebras /* C0F140cc configurable "zebra" (actually solid color) -------- -------- -------- -------- ******** threshold **** bmp palette entry (0-F) **** zebra color (0-F) * type (1=under,0=over) ******* blinking flags maybe (0=no blink) */ if (zebra_colorspace == 2 && (lv || only_one)) // if both under and over are enabled, fall back to regular zebras in play mode { zebra_digic_dirty = 1; // if both zebras are enabled, alternate them (can't display both at the same time) // if only one is enabled, show them both int parity = (get_seconds_clock() / 2) % 2; int ov = (zebra_level_hi <= 100 && (zebra_level_lo == 0 || parity == 0)); int un = (zebra_level_lo > 0 && (zebra_level_hi > 100 || parity == 1)); if (ov) EngDrvOut(DIGIC_ZEBRA_REGISTER, 0x08000 | (FAST_ZEBRA_GRID_COLOR<<8) | zlh); else if (un) EngDrvOut(DIGIC_ZEBRA_REGISTER, 0x1B000 | (FAST_ZEBRA_GRID_COLOR<<8) | zll); // make invisible diagonal strips onto which the zebras will be displayed // only refresh this once per second static int last_s = 0; int s = get_seconds_clock(); if (s == last_s) return; last_s = s; alter_bitmap_palette_entry(FAST_ZEBRA_GRID_COLOR, 0, 256, 256); int off = get_y_skip_offset_for_overlays(); for(int y = os.y0 + off; y < os.y_max - off; y++) { #define color_zeb zebra_color_word_row(FAST_ZEBRA_GRID_COLOR, y) uint32_t * const b_row = (uint32_t*)( bvram + BM_R(y) ); // 4 pixels uint32_t * const m_row = (uint32_t*)( bvram_mirror + BM_R(y) ); // 4 pixels uint32_t* bp; // through bmp vram uint32_t* mp; // through mirror for (int x = os.x0; x < os.x_max; x += 4) { bp = b_row + (x >> 2); mp = m_row + (x >> 2); #define BP (*bp) #define MP (*mp) if (BP != 0 && BP != MP) { little_cleanup(bp, mp); continue; } if ((MP & 0x80808080)) continue; BP = MP = color_zeb; #undef MP #undef BP } } return; } #endif uint8_t * lvram = get_yuv422_vram()->vram; if (!lvram) return; int zlr = zlh; int zlg = zlh; int zlb = zlh; // draw zebra in 16:9 frame // y is in BM coords int off = get_y_skip_offset_for_overlays(); for(int y = os.y0 + off; y < os.y_max - off; y += 2 ) { #define color_over zebra_color_word_row(COLOR_RED, y) #define color_under zebra_color_word_row(COLOR_BLUE, y) #define color_over_2 zebra_color_word_row(COLOR_RED, y+1) #define color_under_2 zebra_color_word_row(COLOR_BLUE, y+1) #define color_rgb_under zebra_rgb_color(1, 0, 0, 0, y) #define color_rgb_under_2 zebra_rgb_color(1, 0, 0, 0, y+1) #define color_rgb_clipR zebra_rgb_color(0, 1, 0, 0, y) #define color_rgb_clipR_2 zebra_rgb_color(0, 1, 0, 0, y+1) #define color_rgb_clipG zebra_rgb_color(0, 0, 1, 0, y) #define color_rgb_clipG_2 zebra_rgb_color(0, 0, 1, 0, y+1) #define color_rgb_clipB zebra_rgb_color(0, 0, 0, 1, y) #define color_rgb_clipB_2 zebra_rgb_color(0, 0, 0, 1, y+1) #define color_rgb_clipRG zebra_rgb_color(0, 1, 1, 0, y) #define color_rgb_clipRG_2 zebra_rgb_color(0, 1, 1, 0, y+1) #define color_rgb_clipGB zebra_rgb_color(0, 0, 1, 1, y) #define color_rgb_clipGB_2 zebra_rgb_color(0, 0, 1, 1, y+1) #define color_rgb_clipRB zebra_rgb_color(0, 1, 0, 1, y) #define color_rgb_clipRB_2 zebra_rgb_color(0, 1, 0, 1, y+1) #define color_rgb_clipRGB zebra_rgb_color(0, 1, 1, 1, y) #define color_rgb_clipRGB_2 zebra_rgb_color(0, 1, 1, 1, y+1) uint32_t * const v_row = (uint32_t*)( lvram + BM2LV_R(y) ); // 2 pixels uint32_t * const b_row = (uint32_t*)( bvram + BM_R(y) ); // 4 pixels uint32_t * const m_row = (uint32_t*)( bvram_mirror + BM_R(y) ); // 4 pixels uint32_t* lvp; // that's a moving pointer through lv vram uint32_t* bp; // through bmp vram uint32_t* mp; // through mirror for (int x = os.x0; x < os.x_max; x += 4) { lvp = v_row + (BM2LV_X(x) >> 1); bp = b_row + (x >> 2); mp = m_row + (x >> 2); #define BP (*bp) #define MP (*mp) #define BN (*(bp + BMPPITCH/4)) #define MN (*(mp + BMPPITCH/4)) if (BP != 0 && BP != MP) { little_cleanup(bp, mp); continue; } if (BN != 0 && BN != MN) { little_cleanup(bp + (BMPPITCH >> 2), mp + (BMPPITCH >> 2)); continue; } if ((MP & 0x80808080) || (MN & 0x80808080)) continue; if (zebra_colorspace == 1 && !EXT_MONITOR_RCA) // rgb { int Y, R, G, B; //~ uyvy2yrgb(*lvp, &Y, &R, &G, &B); COMPUTE_UYVY2YRGB(*lvp, Y, R, G, B); if(unlikely(Y < zll)) // underexposed { BP = MP = color_rgb_under; BN = MN = color_rgb_under_2; } else { //~ BP = MP = zebra_rgb_color(Y < zll, R > zlh, G > zlh, B > zlh, y); //~ BN = MN = zebra_rgb_color(Y < zll, R > zlh, G > zlh, B > zlh, y+1); if (unlikely(R > zlr)) // R clipped { if (unlikely(G > zlg)) // RG clipped { if (B > zlb) // RGB clipped (all of them) { BP = MP = color_rgb_clipRGB; BN = MN = color_rgb_clipRGB_2; } else // only R and G clipped { BP = MP = color_rgb_clipRG; BN = MN = color_rgb_clipRG_2; } } else // R clipped, G not clipped { if (unlikely(B > zlb)) // only R and B clipped { BP = MP = color_rgb_clipRB; BN = MN = color_rgb_clipRB_2; } else // only R clipped { BP = MP = color_rgb_clipR; BN = MN = color_rgb_clipR_2; } } } else // R not clipped { if (unlikely(G > zlg)) // R not clipped, G clipped { if (unlikely(B > zlb)) // only G and B clipped { BP = MP = color_rgb_clipGB; BN = MN = color_rgb_clipGB_2; } else // only G clipped { BP = MP = color_rgb_clipG; BN = MN = color_rgb_clipG_2; } } else // R not clipped, G not clipped { if (unlikely(B > zlb)) // only B clipped { BP = MP = color_rgb_clipB; BN = MN = color_rgb_clipB_2; } else // nothing clipped { BN = MN = BP = MP = 0; } } } } } else // luma { int p0 = (*lvp) >> 8 & 0xFF; if (unlikely(p0 > zlh)) { BP = MP = color_over; BN = MN = color_over_2; } else if (unlikely(p0 < zll)) { BP = MP = color_under; BN = MN = color_under_2; } else BN = MN = BP = MP = 0; } #undef MP #undef BP #undef BN #undef MN } } } } #endif #ifdef FEATURE_FOCUS_PEAK /* superseded by the peak_d2xy algorithm (2012-09-01) static inline int peak_d1xy(uint8_t* p8) { int p_cc = (int)(*p8); int p_rc = (int)(*(p8 + 2)); int p_cd = (int)(*(p8 + vram_lv.pitch)); int e_dx = ABS(p_rc - p_cc); int e_dy = ABS(p_cd - p_cc); int e = MAX(e_dx, e_dy); return peak_scaling[MIN(e, 255)]; }*/ static inline int peak_d2xy_sharpen(uint8_t* p8) { int orig = (int)(*p8); int diff = orig * 4 - (int)(*(p8 + 2)); diff -= (int)(*(p8 - 2)); diff -= (int)(*(p8 + vram_lv.pitch)); diff -= (int)(*(p8 - vram_lv.pitch)); int v = orig + (diff << 2); return COERCE(v, 0, 255); } static inline int FAST calc_peak(const uint8_t* p8, const int pitch) { // approximate second derivative with a Laplacian kernel: // -1 // -1 4 -1 // -1 const int p8_xmin1 = (int)(*(p8 - 2)); const int p8_xplus1 = (int)(*(p8 + 2)); const int p8_ymin1 = (int)(*(p8 - pitch)); const int p8_yplus1 = (int)(*(p8 + pitch)); int result = ((int)(*p8) * 4); result -= p8_xplus1 + p8_xmin1 + p8_yplus1 + p8_ymin1; int e = ABS(result); if (focus_peaking_filter_edges) { // filter out strong edges where first derivative is strong // as these are usually false positives int d1x = ABS(p8_xplus1 - p8_xmin1); int d1y = ABS(p8_yplus1 - p8_ymin1); int d1 = MAX(d1x, d1y); e = MAX(e - ((d1 << focus_peaking_filter_edges) >> 2), 0) * 2; } return e; } static inline int FAST peak_d2xy(const uint8_t* p8) { return calc_peak(p8, vram_lv.pitch); } #ifdef FEATURE_FOCUS_PEAK_DISP_FILTER //~ static inline int peak_blend_solid(uint32_t* s, int e, int thr) { return 0x4C7F4CD5; } //~ static inline int peak_blend_raw(uint32_t* s, int e) { return (e << 8) | (e << 24); } static inline int peak_blend_alpha(uint32_t* s, int e) { // e=0 => cold (original color) // e=255 => hot (red) uint8_t* s8u = (uint8_t*)s; int8_t* s8s = (int8_t*)s; int y_cold = *(s8u+1); int u_cold = *(s8s); int v_cold = *(s8s+2); // red (255,0,0) const int y_hot = 76; const int u_hot = -43; const int v_hot = 127; int er = 255-e; int y = (y_cold * er + y_hot * e) >> 8; int u = (u_cold * er + u_hot * e) >> 8; int v = (v_cold * er + v_hot * e) >> 8; return UYVY_PACK(u,y,v,y); } static int peak_scaling[256]; void FAST peak_disp_filter() { uint32_t* src_buf; uint32_t* dst_buf; if (lv) { display_filter_get_buffers(&src_buf, &dst_buf); } else if (PLAY_OR_QR_MODE) { void* aux_buf = (void*)YUV422_HD_BUFFER_2; void* current_buf = get_yuv422_vram()->vram; if (!current_buf) return; int w = get_yuv422_vram()->width; int h = get_yuv422_vram()->height; int buf_size = w * h * 2; memcpy(aux_buf, current_buf, buf_size); src_buf = aux_buf; dst_buf = current_buf; } else return; static int thr = 50; static int thr_increment = 1; static int thr_delta = 0; #define FOCUSED_THR 64 // the percentage selected in menu represents how many pixels are considered in focus // let's say above some FOCUSED_THR // so, let's scale edge value so that e=thr maps to e=FOCUSED_THR for (int i = 0, i_fthr = 0; i < 255; i++, i_fthr += FOCUSED_THR) peak_scaling[i] = MIN(i_fthr / thr, 255); int n_over = 0; int n_total = 720 * (os.y_max - os.y0) / 2; #define PEAK_LOOP for (int i = 720 * (os.y0/2), max = 720 * (os.y_max/2); i < max; i++) // generic loop: //~ for (int i = 720 * (os.y0/2); i < 720 * (os.y_max/2); i++) //~ { //~ int e = peak_compute((uint8_t*)&src_buf[i] + 1); //~ dst_buf[i] = peak_blend(&src_buf[i], e, blend_thr); //~ if (unlikely(e > FOCUSED_THR)) n_over++; //~ } if (focus_peaking_disp == 4) // raw { PEAK_LOOP { int e = peak_d2xy((uint8_t*)&src_buf[i] + 1); e = MIN(e * 4, 255); dst_buf[i] = (e << 8) | (e << 24); } } else if (focus_peaking_grayscale) { if (focus_peaking_disp == 1) { PEAK_LOOP { int e = peak_d2xy((uint8_t*)&src_buf[i] + 1); e = peak_scaling[MIN(e, 255)]; if (likely(e < FOCUSED_THR)) dst_buf[i] = src_buf[i] & 0xFF00FF00; else { dst_buf[i] = 0x4C7F4CD5; // red n_over++; } } } else if (focus_peaking_disp == 2) // alpha { PEAK_LOOP { int e = peak_d2xy((uint8_t*)&src_buf[i] + 1); e = peak_scaling[MIN(e, 255)]; if (likely(e < 20)) dst_buf[i] = src_buf[i] & 0xFF00FF00; else dst_buf[i] = peak_blend_alpha(&src_buf[i], e); if (unlikely(e > FOCUSED_THR)) n_over++; } } else if (focus_peaking_disp == 3) // sharp { PEAK_LOOP { int e = peak_d2xy_sharpen((uint8_t*)&src_buf[i] + 1); dst_buf[i] = (src_buf[i] & 0xFF000000) | ((e & 0xFF) << 8); } } } else // color { if (focus_peaking_disp == 1) { PEAK_LOOP { int e = peak_d2xy((uint8_t*)&src_buf[i] + 1); e = peak_scaling[MIN(e, 255)]; if (likely(e < FOCUSED_THR)) dst_buf[i] = src_buf[i]; else { dst_buf[i] = 0x4C7F4CD5; // red n_over++; } } } else if (focus_peaking_disp == 2) // alpha { PEAK_LOOP { int e = peak_d2xy((uint8_t*)&src_buf[i] + 1); e = peak_scaling[MIN(e, 255)]; if (likely(e < 20)) dst_buf[i] = src_buf[i]; else dst_buf[i] = peak_blend_alpha(&src_buf[i], e); if (unlikely(e > FOCUSED_THR)) n_over++; } } else if (focus_peaking_disp == 3) // sharp { PEAK_LOOP { int e = peak_d2xy_sharpen((uint8_t*)&src_buf[i] + 1); dst_buf[i] = (src_buf[i] & 0xFFFF00FF) | ((e & 0xFF) << 8); } } } // update threshold for next iteration if (1000 * n_over / n_total > (int)focus_peaking_pthr) { if (thr_delta > 0) thr_increment++; else thr_increment = 1; thr += thr_increment; } else { if (thr_delta < 0) thr_increment++; else thr_increment = 1; thr -= thr_increment; } thr_increment = COERCE(thr_increment, -10, 10); thr = COERCE(thr, 10, 255); if (focus_peaking_disp == 3) thr = 64; } #endif static void FAST focus_found_pixel(int x, int y, int e, int thr, uint8_t * const bvram) { int color = get_focus_color(thr, e); //~ int color = COLOR_RED; color = (color << 8) | color; uint16_t * const b_row = (uint16_t*)( bvram + BM_R(y) ); // 2 pixels uint16_t * const m_row = (uint16_t*)( bvram_mirror + BM_R(y) ); // 2 pixels const int x_half = x >> 1; uint32_t pixel = b_row[x_half]; uint32_t mirror = m_row[x_half]; const int pos = x_half + (BMPPITCH >> 1); uint32_t pixel2 = b_row[pos]; uint32_t mirror2 = m_row[pos]; if (mirror & 0x8080) return; if (mirror2 & 0x8080) return; if (pixel != 0 && pixel != mirror) return; if (pixel2 != 0 && pixel2 != mirror2) return; if (dirty_pixels_num < MAX_DIRTY_PIXELS) { dirty_pixel_values[dirty_pixels_num] = pixel + (pixel2 << 16); dirty_pixels[dirty_pixels_num++] = (void*)&b_row[x_half] - (void*)bvram; } b_row[x_half] = b_row[pos] = m_row[x_half] = m_row[pos] = color; } static void focus_found_pixel_playback(int x, int y, int e, int thr, uint8_t * const bvram) { int color = get_focus_color(thr, e); uint16_t * const b_row = (uint16_t*)( bvram + BM_R(y) ); // 2 pixels uint16_t * const m_row = (uint16_t*)( bvram_mirror + BM_R(y) ); // 2 pixels const int x_half = x >> 1; uint16_t pixel = b_row[x_half]; uint16_t mirror = m_row[x_half]; if (mirror & 0x8080) return; if (pixel != 0 && pixel != mirror) return; bmp_putpixel_fast(bvram, x, y, color); bmp_putpixel_fast(bvram, x+1, y, color); } #endif // returns how the focus peaking threshold changed static int FAST draw_zebra_and_focus( int Z, int F ) { if (unlikely(!get_global_draw())) return 0; uint8_t * const bvram = bmp_vram_real(); if (unlikely(!bvram)) return 0; if (unlikely(!bvram_mirror)) return 0; #ifdef FEATURE_ZEBRA draw_zebras(Z); #endif #ifdef FEATURE_FOCUS_PEAK if (focus_peaking && focus_peaking_disp && !EXT_MONITOR_CONNECTED) { if (lv) { return 0; // it's drawn from display filters routine } else // display filters are not called in playback { if (F != 1) return 0; // problem: we need to update the threshold somehow peak_disp_filter(); return 0; } } static int thr = 50; static int thr_increment = 1; static int prev_thr = 50; static int thr_delta = 0; if (F && focus_peaking) { // clear previously written pixels if (unlikely(!dirty_pixels)) dirty_pixels = malloc(MAX_DIRTY_PIXELS * sizeof(int)); if (unlikely(!dirty_pixels)) return -1; if (unlikely(!dirty_pixel_values)) dirty_pixel_values = malloc(MAX_DIRTY_PIXELS * sizeof(int)); if (unlikely(!dirty_pixel_values)) return -1; int i; for (i = 0; i < dirty_pixels_num; i++) { #define B1 *(uint16_t*)(bvram + dirty_pixels[i]) #define B2 *(uint16_t*)(bvram + dirty_pixels[i] + BMPPITCH) #define M1 *(uint16_t*)(bvram_mirror + dirty_pixels[i]) #define M2 *(uint16_t*)(bvram_mirror + dirty_pixels[i] + BMPPITCH) if (likely((B1 == 0 || B1 == M1)) && likely((B2 == 0 || B2 == M2))) { B1 = M1 = dirty_pixel_values[i] & 0xFFFF; B2 = M2 = dirty_pixel_values[i] >> 16; } #undef B1 #undef B2 #undef M1 #undef M2 } dirty_pixels_num = 0; uint32_t vram = (uint32_t)CACHEABLE(YUV422_LV_BUFFER_DISPLAY_ADDR); if (!vram) return 0; int off = get_y_skip_offset_for_overlays(); int yStart = os.y0 + off + 8; int yEnd = os.y_max - off - 8; int xStart = os.x0 + 8; int xEnd = os.x_max - 8; int n_over = 0; #ifdef FEATURE_ANAMORPHIC_PREVIEW yStart = anamorphic_squeeze_bmp_y(yStart); yEnd = anamorphic_squeeze_bmp_y(yEnd); #endif const uint8_t* p8; // that's a moving pointer zebra_update_lut(); /** simple Laplacian filter * -1 * -1 4 -1 * -1 * * Big endian: * uyvy uyvy uyvy * uyvy uYvy uyvy * uyvy uyvy uyvy */ int n_total = 0; if (lv) // fast, realtime { n_total = ((yEnd - yStart) * (xEnd - xStart)) / 6; for(int y = yStart; y < yEnd; y += 3) { uint32_t row = vram + BM2LV_R(y); for (int x = xStart; x < xEnd; x += 2) { p8 = (uint8_t *)(row + bm_lv_x_cache[x - BMP_W_MINUS]); int e = peak_d2xy(p8); /* executed for 1% of pixels */ if (unlikely(e >= thr)) { n_over++; if (unlikely(dirty_pixels_num >= MAX_DIRTY_PIXELS)) break; // threshold too low, abort focus_found_pixel(x, y, e, thr, bvram); } } } } else // playback - can be slower and more accurate { n_total = ((yEnd - yStart) * (xEnd - xStart)); for(int y = yStart; y < yEnd; y ++) { uint32_t row = vram + BM2LV_R(y); for (int x = xStart; x < xEnd; x ++) { p8 = (uint8_t *)(row + bm_lv_x_cache[x - BMP_W_MINUS]); int e = peak_d2xy(p8); /* executed for 1% of pixels */ if (unlikely(e >= thr)) { n_over++; if (unlikely(dirty_pixels_num >= MAX_DIRTY_PIXELS)) // threshold too low, abort break; if (F==1) focus_found_pixel_playback(x, y, e, thr, bvram); } } } } //~ bmp_printf(FONT_LARGE, 10, 50, "%d ", thr); if (1000 * n_over / n_total > (int)focus_peaking_pthr) { if (thr_delta > 0) thr_increment++; else thr_increment = 1; thr += thr_increment; } else { if (thr_delta < 0) thr_increment++; else thr_increment = 1; thr -= thr_increment; } thr_increment = COERCE(thr_increment, -5, 5); int thr_min = 15; thr = COERCE(thr, thr_min, 255); thr_delta = thr - prev_thr; prev_thr = thr; if (n_over > MAX_DIRTY_PIXELS) return thr_delta; } return thr_delta; #endif return 0; } static void guess_focus_peaking_threshold() { #ifdef FEATURE_FOCUS_PEAK if (!focus_peaking) return; int prev_thr_delta = 1234; for (int i = 0; i < 50; i++) { int thr_delta = draw_zebra_and_focus(0,2); //~ bmp_printf(FONT_LARGE, 0, 0, "%x ", thr_delta); msleep(1000); if (!thr_delta) break; if (prev_thr_delta != 1234 && SGN(thr_delta) != SGN(prev_thr_delta)) break; prev_thr_delta = thr_delta; } #endif } // clear only zebra, focus assist and whatever else is in BMP VRAM mirror static void clrscr_mirror( void ) { if (!lv && !PLAY_OR_QR_MODE) return; if (!get_global_draw()) return; uint8_t * const bvram = bmp_vram(); if (!bvram) return; if (!bvram_mirror) return; int x, y; for( y = os.y0; y < os.y_max; y++ ) { for( x = os.x0; x < os.x_max; x += 4 ) { uint32_t* bp = (uint32_t*)bvram + BM(x,y)/4; uint32_t* mp = (uint32_t*)bvram_mirror + BM(x,y)/4; #define BP (*bp) #define MP (*mp) if (BP != 0) { if (BP == MP) BP = MP = 0; else little_cleanup(bp, mp); } #undef MP #undef BP } } } #ifdef FEATURE_ZEBRA static MENU_UPDATE_FUNC(zebra_draw_display) { unsigned z = CURRENT_VALUE; int over_disabled = (zebra_level_hi > 100); int under_disabled = (zebra_level_lo == 0); if (z) { MENU_SET_VALUE( "%s, ", zebra_colorspace == 0 ? "Luma" : zebra_colorspace == 1 ? "RGB" : "LumaFast" ); if (over_disabled) { MENU_APPEND_VALUE( "under %d%%", zebra_level_lo ); } else if (under_disabled) { MENU_APPEND_VALUE( "over %d%%", zebra_level_hi ); } else { MENU_APPEND_VALUE( "%d..%d%%", zebra_level_lo, zebra_level_hi ); } } #ifdef FEATURE_RAW_ZEBRAS if (z && can_use_raw_overlays_menu()) { raw_zebra_update(entry, info); if (RAW_ZEBRA_ENABLE) MENU_SET_VALUE("RAW RGB"); } #endif } static MENU_UPDATE_FUNC(zebra_param_not_used_for_raw) { #ifdef FEATURE_RAW_ZEBRAS if (raw_zebra_enable && can_use_raw_overlays_menu()) MENU_SET_WARNING(RAW_ZEBRA_ENABLE ? MENU_WARN_NOT_WORKING : MENU_WARN_ADVICE, "Not used for RAW zebras."); #endif } static MENU_UPDATE_FUNC(zebra_level_display) { int level = CURRENT_VALUE; if (level == 0 || level > 100) { MENU_SET_VALUE("Disabled"); MENU_SET_ICON(MNI_PERCENT_OFF, 0); MENU_SET_ENABLED(0); } else { MENU_SET_VALUE( "%d%% (%d)", level, 0, (level * 255 + 50) / 100 ); } zebra_param_not_used_for_raw(entry, info); } #endif #ifdef FEATURE_FOCUS_PEAK static MENU_UPDATE_FUNC(focus_peaking_display) { unsigned f = CURRENT_VALUE; if (f) MENU_SET_VALUE( "ON,%d.%d,%s%s", focus_peaking_pthr / 10, focus_peaking_pthr % 10, focus_peaking_color == 0 ? "R" : focus_peaking_color == 1 ? "G" : focus_peaking_color == 2 ? "B" : focus_peaking_color == 3 ? "C" : focus_peaking_color == 4 ? "M" : focus_peaking_color == 5 ? "Y" : focus_peaking_color == 6 ? "global" : focus_peaking_color == 7 ? "local" : "err", focus_peaking_grayscale ? ",Gray" : "" ); } static void focus_peaking_adjust_thr(void* priv, int delta) { focus_peaking_pthr = (int)focus_peaking_pthr + (focus_peaking_pthr < 10 ? 1 : 5) * delta; if ((int)focus_peaking_pthr > 50) focus_peaking_pthr = 1; if ((int)focus_peaking_pthr <= 0) focus_peaking_pthr = 50; } #endif #ifdef FEATURE_WAVEFORM static MENU_UPDATE_FUNC(waveform_print) { if (waveform_draw) MENU_SET_VALUE( waveform_size == 0 ? "Small" : waveform_size == 1 ? "Large" : "FullScreen" ); } #endif #ifdef FEATURE_GLOBAL_DRAW static MENU_UPDATE_FUNC(global_draw_display) { if (disp_profiles_0) { MENU_SET_RINFO("DISP %d", get_disp_mode()); if (entry->selected && info->can_custom_draw) bmp_printf(FONT(FONT_MED, COLOR_CYAN, COLOR_BLACK), 700 - font_med.width * strlen(Q_BTN_NAME), info->y + font_large.height, Q_BTN_NAME); } #ifdef CONFIG_5D3 if (hdmi_code >= 5 && video_mode_resolution>0) // unusual VRAM parameters MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Not compatible with HDMI 50p/60p."); #endif if (lv && lv_disp_mode && ZEBRAS_IN_LIVEVIEW) MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Press " INFO_BTN_NAME " (outside ML menu) to turn Canon displays off."); if (global_draw && lv && !ZEBRAS_IN_LIVEVIEW) { MENU_SET_ENABLED(0); } if (global_draw && !lv && !ZEBRAS_IN_QUICKREVIEW) { MENU_SET_ENABLED(0); } } #endif #ifdef FEATURE_MAGIC_ZOOM static MENU_UPDATE_FUNC(zoom_overlay_display) { if (zoom_overlay_enabled) MENU_SET_VALUE( "%s%s%s%s%s", zoom_overlay_trigger_mode == 0 ? "err" : #ifdef CONFIG_ZOOM_BTN_NOT_WORKING_WHILE_RECORDING zoom_overlay_trigger_mode == 1 ? "HalfS, " : zoom_overlay_trigger_mode == 2 ? "Focus, " : zoom_overlay_trigger_mode == 3 ? "F+HS, " : "ALW, ", #else zoom_overlay_trigger_mode == 1 ? "Zrec, " : zoom_overlay_trigger_mode == 2 ? "F+Zr, " : zoom_overlay_trigger_mode == 3 ? "(+), " : "ALW, ", #endif zoom_overlay_trigger_mode == 0 ? "" : zoom_overlay_size == 0 ? "Small, " : zoom_overlay_size == 1 ? "Med, " : zoom_overlay_size == 2 ? "Large, " : "FullScreen", zoom_overlay_trigger_mode == 0 || zoom_overlay_size == 3 ? "" : zoom_overlay_pos == 0 ? "AFbox, " : zoom_overlay_pos == 1 ? "TL, " : zoom_overlay_pos == 2 ? "TR, " : zoom_overlay_pos == 3 ? "BR, " : zoom_overlay_pos == 4 ? "BL, " : "err", zoom_overlay_trigger_mode == 0 || zoom_overlay_size == 3 ? "" : zoom_overlay_x == 0 ? "1:1" : zoom_overlay_x == 1 ? "2:1" : zoom_overlay_x == 2 ? "3:1" : zoom_overlay_x == 3 ? "4:1" : "err", zoom_overlay_trigger_mode == 0 || zoom_overlay_size == 3 ? "" : zoom_overlay_split == 0 ? "" : zoom_overlay_split == 1 ? ", Ss" : zoom_overlay_split == 2 ? ", Sz" : "err" ); if (EXT_MONITOR_RCA) MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Magic Zoom does not work with SD monitors"); else if (hdmi_code >= 5) MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Magic Zoom does not work in HDMI 1080i."); #if defined(CONFIG_DISPLAY_FILTERS) && defined(CONFIG_CAN_REDIRECT_DISPLAY_BUFFER) && !defined(CONFIG_CAN_REDIRECT_DISPLAY_BUFFER_EASILY) extern int display_broken_for_mz(); /* tweaks.c */ if (display_broken_for_mz()) MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "After using display filters, go outside LiveView and back."); #endif #if !defined(CONFIG_6D) && !defined(CONFIG_5D3) && !defined(CONFIG_EOSM) else if (is_movie_mode() && video_mode_fps > 30) MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Magic Zoom does not work well in current video mode"); #endif else if (is_movie_mode() && video_mode_crop && zoom_overlay_size == 3) MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Full-screen Magic Zoom does not work in crop mode"); else if (zoom_overlay_enabled && zoom_overlay_trigger_mode && !get_zoom_overlay_trigger_mode() && get_global_draw()) // MZ enabled, but for some reason it doesn't work in current mode MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "Magic Zoom is not available in this mode"); } #endif #ifdef FEATURE_SPOTMETER static MENU_UPDATE_FUNC(spotmeter_menu_display) { if (spotmeter_draw) { MENU_SET_VALUE( "%s%s", spotmeter_formula == 0 ? "Percent" : spotmeter_formula == 1 ? "0..255" : spotmeter_formula == 2 ? "RGB" : spotmeter_formula == 3 ? "RAW" : "Percent RGB", spotmeter_draw && spotmeter_position ? ", AFbox" : "" ); #ifdef FEATURE_RAW_SPOTMETER if (spotmeter_formula == 3) { menu_checkdep_raw(entry, info); } #endif } } #endif void get_spot_yuv_ex(int size_dxb, int dx, int dy, int* Y, int* U, int* V, int draw, int erase) { struct vram_info * vram = get_yuv422_vram(); if( !vram->vram ) return; const uint16_t* vr = (void*) vram->vram; const unsigned width = vram->width; //~ const unsigned pitch = vram->pitch; //~ const unsigned height = vram->height; int x, y; int xcb = os.x0 + os.x_ex/2 + dx; int ycb = os.y0 + os.y_ex/2 + dy; int xcl = BM2LV_X(xcb); int ycl = BM2LV_Y(ycb); int dxl = BM2LV_DX(size_dxb); if (erase) { // clean previous marker static int spy_pre_xcb = -1; static int spy_pre_ycb = -1; if ( spy_pre_xcb != -1 && spy_pre_ycb != -1 && (spy_pre_xcb != xcb || spy_pre_ycb != ycb) ) { bmp_draw_rect(0, spy_pre_xcb - size_dxb, spy_pre_ycb - size_dxb, 2*size_dxb, 2*size_dxb); } spy_pre_xcb = xcb; spy_pre_ycb = ycb; } if (draw) { bmp_draw_rect(COLOR_WHITE, xcb - size_dxb, ycb - size_dxb, 2*size_dxb, 2*size_dxb); } unsigned sy = 0; int32_t su = 0, sv = 0; // Y is unsigned, U and V are signed // Sum the values around the center for( y = ycl - dxl ; y <= ycl + dxl ; y++ ) { for( x = xcl - dxl ; x <= xcl + dxl ; x++ ) { uint16_t p = vr[ x + y * width ]; sy += (p >> 8) & 0xFF; if (x % 2) sv += (int8_t)(p & 0x00FF); else su += (int8_t)(p & 0x00FF); } } const int dx1_two_plus1 = (2 * dxl + 1); const int val = (dxl + 1) * dx1_two_plus1; sy /= dx1_two_plus1 * dx1_two_plus1; su /= val; sv /= val; *Y = sy; *U = su; *V = sv; } void get_spot_yuv(int dxb, int* Y, int* U, int* V) { get_spot_yuv_ex(dxb, 0, 0, Y, U, V, 1, 0); } // for surface cleaning static int spm_pre_xcb = -1; static int spm_pre_ycb = -1; int get_spot_motion(int dxb, int xcb, int ycb, int draw) { struct vram_info * vram = get_yuv422_vram(); if( !vram->vram ) return 0; const uint16_t* vr1 = (void*)YUV422_LV_BUFFER_DISPLAY_ADDR; const uint16_t* vr2 = (void*)get_fastrefresh_422_buf(); uint8_t * const bm = bmp_vram(); if (!bm) return 0; const unsigned width = vram->width; int x, y; const int xcl = BM2LV_X(xcb); const int ycl = BM2LV_Y(ycb); const int dxl = BM2LV_DX(dxb); // surface cleaning if ( spm_pre_xcb != -1 && spm_pre_ycb != -1 && draw && (spm_pre_xcb != xcb || spm_pre_ycb != ycb) ) { int p_xcl = BM2LV_X(spm_pre_xcb); int p_ycl = BM2LV_Y(spm_pre_ycb); int ymax = p_ycl + dxl+5; int xmax = p_xcl + dxl+5; int x, y; for( y = p_ycl - (dxl+5) ; y <= ymax ; y++ ) { const int yskip = y * BMPPITCH; for( x = p_xcl - (dxl+5) ; x <= xmax ; x++ ) { bm[x + yskip] = 0; } } } spm_pre_xcb = xcb; spm_pre_ycb = ycb; for (int ddxb = dxb; ddxb < dxb+5; ddxb++) { draw_line(xcb - ddxb, ycb - ddxb, xcb + ddxb, ycb - ddxb, COLOR_WHITE); draw_line(xcb + ddxb, ycb - ddxb, xcb + ddxb, ycb + ddxb, COLOR_WHITE); draw_line(xcb + ddxb, ycb + ddxb, xcb - ddxb, ycb + ddxb, COLOR_WHITE); draw_line(xcb - ddxb, ycb + ddxb, xcb - ddxb, ycb - ddxb, COLOR_WHITE); } unsigned D = 0; for( y = ycl - dxl ; y <= ycl + dxl ; y++ ) { const int yskip = y * width; const int y_skip_pitch = y * BMPPITCH; for( x = xcl - dxl ; x <= xcl + dxl ; x++ ) { const int pos = x + yskip; int p1 = (vr1[pos] >> 8) & 0xFF; int p2 = (vr2[pos] >> 8) & 0xFF; int dif = ABS(p1 - p2); D += dif; if (draw) bm[x + y_skip_pitch] = falsecolor_value_ex(5, dif & 0xFF); } } D <<= 1; const int val = (2 * dxl + 1); D /= val * val; return D; } int get_spot_focus(int dxb) { struct vram_info * vram = get_yuv422_vram(); if( !vram->vram ) return 0; const uint32_t* vr = (uint32_t*) vram->vram; // 2px const unsigned width = vram->width; //~ const unsigned pitch = vram->pitch; //~ const unsigned height = vram->height; int x, y; unsigned sf = 0; unsigned br = 0; int xcb = os.x0 + os.x_ex/2; int ycb = os.y0 + os.y_ex/2; int xcl = BM2LV_X(xcb); int ycl = BM2LV_Y(ycb); int dxl = BM2LV_DX(dxb); // Sum the absolute difference of values around the center for( y = ycl - dxl ; y <= ycl + dxl ; y++ ) { const int yskip = y * (width >> 1); for( x = xcl - dxl ; x <= xcl + dxl ; x++ ) { uint32_t p = vr[ x/2 + yskip ]; int32_t p0 = (p >> 24) & 0xFF; int32_t p1 = (p >> 8) & 0xFF; sf += ABS(p1 - p0); br += p1 + p0; } } return sf / (br >> 14); } #ifdef FEATURE_SPOTMETER static int spot_prev_xcb = 0; static int spot_prev_ycb = 0; static int spotmeter_dirty = 0; // will be called from prop_handler PROP_LV_AFFRAME // no BMP_LOCK here, please void spotmeter_erase() { if (!spotmeter_dirty) return; spotmeter_dirty = 0; int xcb = spot_prev_xcb; int ycb = spot_prev_ycb; int dx = spotmeter_formula == 2 ? 52 : (spotmeter_formula == SPTMTR_F_RGB_PERCENT ? 80: 26); int y0 = -13; uint32_t* M = (uint32_t*)get_bvram_mirror(); uint32_t* B = (uint32_t*)bmp_vram(); for(int y = (ycb&~1) + y0 ; y <= (ycb&~1) + 36 ; y++ ) { for(int x = xcb - dx ; x <= xcb + dx ; x+=4 ) { uint8_t* m = (uint8_t*)(&(M[BM(x,y)/4])); //32bit to 8bit if (*m == 0x80) *m = 0; m++; if (*m == 0x80) *m = 0; m++; if (*m == 0x80) *m = 0; m++; if (*m == 0x80) *m = 0; B[BM(x,y)/4] = 0; } } } /* spotmeter position in QR mode */ /* (in LiveView it's linked to focus box, but in QR we can't always move the LV box) */ static int spotmeter_playback_offset_x = 0; static int spotmeter_playback_offset_y = 0; static void spotmeter_step() { if (gui_menu_shown()) return; if (!get_global_draw()) return; if (digic_zoom_overlay_enabled()) return; // incorrect readings //~ if (!lv) return; if (!PLAY_OR_QR_MODE) { if (!lv_luma_is_accurate()) return; } struct vram_info * vram = get_yuv422_vram(); if( !vram->vram ) return; if (!PLAY_OR_QR_MODE) spotmeter_erase(); const uint16_t* vr = (uint16_t*) vram->vram; const unsigned width = vram->width; //~ const unsigned pitch = vram->pitch; //~ const unsigned height = vram->height; const unsigned dxb = spotmeter_size; //unsigned sum = 0; int x, y; int xcb = os.x0 + os.x_ex/2; int ycb = os.y0 + os.y_ex/2; if (spotmeter_position == 1) // AF frame { int aff_x0 = 360; int aff_y0 = 240; if (lv) { if (lv_dispsize == 1) get_afframe_pos(720, 480, &aff_x0, &aff_y0); } else { spotmeter_playback_offset_x = COERCE(spotmeter_playback_offset_x, -300, 300); spotmeter_playback_offset_y = COERCE(spotmeter_playback_offset_y, -200, 200); aff_x0 = 360 + spotmeter_playback_offset_x; aff_y0 = 240 + spotmeter_playback_offset_y; } xcb = N2BM_X(aff_x0); ycb = N2BM_Y(aff_y0); xcb = COERCE(xcb, os.x0 + 50, os.x_max - 50); ycb = COERCE(ycb, os.y0 + 50, os.y_max - 50); } // save coords, so we know where to erase the spotmeter from spot_prev_xcb = xcb; spot_prev_ycb = ycb; spotmeter_dirty = 1; int xcl = BM2LV_X(xcb); int ycl = BM2LV_Y(ycb); int dxl = BM2LV_DX(dxb); unsigned sy = 0; int32_t su = 0, sv = 0; // Y is unsigned, U and V are signed // Sum the values around the center for( y = ycl - dxl ; y <= ycl + dxl ; y++ ) { for( x = xcl - dxl ; x <= xcl + dxl ; x++ ) { uint16_t p = vr[ x + y * width ]; sy += (p >> 8) & 0xFF; if (x % 2) sv += (int8_t)(p & 0x00FF); else su += (int8_t)(p & 0x00FF); } } const int two_dx1_plus1 = (2 * dxl + 1); const int val = (dxl + 1) * two_dx1_plus1; sy /= two_dx1_plus1 * two_dx1_plus1; su /= val; sv /= val; // Scale to 100% const unsigned scaled = (101 * sy) >> 8; #ifdef FEATURE_RAW_SPOTMETER int raw_luma = 0; int raw_ev = 0; if (can_use_raw_overlays() && raw_update_params()) { const int xcr = BM2RAW_X(xcb); const int ycr = BM2RAW_Y(ycb); const int dxr = BM2RAW_DX(dxb); raw_luma = 0; int raw_count = 0; for( y = ycr - dxr ; y <= ycr + dxr ; y++ ) { if (y < raw_info.active_area.y1 || y > raw_info.active_area.y2) continue; for( x = xcr - dxr ; x <= xcr + dxr ; x++ ) { if (x < raw_info.active_area.x1 || x > raw_info.active_area.x2) continue; raw_luma += raw_get_pixel(x, y); raw_count++; /* define this to check if spotmeter reads from the right place; * you should see some gibberish on raw zebras, right inside the spotmeter box */ #ifdef RAW_SPOTMETER_TEST raw_set_pixel(raw_buf, x, y, rand()); #endif } } if (!raw_count) return; raw_luma /= raw_count; raw_ev = (int) roundf(10.0 * raw_to_ev(raw_luma)); } #endif // spotmeter color: // black on transparent, if brightness > 60% // white on transparent, if brightness < 50% // previous value otherwise // if false color is active, draw white on semi-transparent gray // protect the surroundings from zebras #ifndef RAW_SPOTMETER_TEST uint32_t* M = (uint32_t*)get_bvram_mirror(); uint32_t* B = (uint32_t*)bmp_vram(); int dx = spotmeter_formula == 2 ? 52 : 26; int y0 = arrow_keys_shortcuts_active() ? (int)(36 - font_med.height) : (int)(-13); for( y = (ycb&~1) + y0 ; y <= (ycb&~1) + 36 ; y++ ) { for( x = xcb - dx ; x <= xcb + dx ; x+=4 ) { uint8_t* m = (uint8_t*)(&(M[BM(x,y)/4])); //32bit to 8bit if (!(*m & 0x80)) *m = 0x80; m++; if (!(*m & 0x80)) *m = 0x80; m++; if (!(*m & 0x80)) *m = 0x80; m++; if (!(*m & 0x80)) *m = 0x80; B[BM(x,y)/4] = 0; } } #endif static int fg = 0; if (scaled > 60) fg = COLOR_BLACK; if (scaled < 50 || falsecolor_draw) fg = COLOR_WHITE; int bg = fg == COLOR_BLACK ? COLOR_WHITE : COLOR_BLACK; int fnt = FONT(SHADOW_FONT(FONT_MED), fg, bg); if (!arrow_keys_shortcuts_active()) { bmp_draw_rect(COLOR_WHITE, xcb - dxb, ycb - dxb, 2*dxb+1, 2*dxb+1); bmp_draw_rect(COLOR_BLACK, xcb - dxb + 1, ycb - dxb + 1, 2*dxb+1-2, 2*dxb+1-2); } ycb += dxb + 20; ycb -= font_med.height/2; #ifdef FEATURE_RAW_SPOTMETER if (spotmeter_formula == 3) { if (can_use_raw_overlays()) { bmp_printf( fnt | FONT_ALIGN_CENTER, xcb, ycb, "-%d.%d EV", -raw_ev/10, -raw_ev%10 ); } else // will fall back to percent if no raw data is available { goto fallback_from_raw; } } #endif if (spotmeter_formula <= 1) { #ifdef FEATURE_RAW_SPOTMETER fallback_from_raw: #endif bmp_printf( fnt | FONT_ALIGN_CENTER, xcb, ycb, "%3d%s", spotmeter_formula == 1 ? sy : scaled, spotmeter_formula == 1 ? "" : "%" ); } else if (spotmeter_formula == 2) { int uyvy = UYVY_PACK(su,sy,sv,sy); int R,G,B,Y; COMPUTE_UYVY2YRGB(uyvy, Y, R, G, B); bmp_printf( fnt | FONT_ALIGN_CENTER, xcb, ycb, "#%02x%02x%02x", R,G,B ); } else if (spotmeter_formula == SPTMTR_F_RGB_PERCENT) { int uyvy = UYVY_PACK(su,sy,sv,sy); int R,G,B,Y; COMPUTE_UYVY2YRGB(uyvy, Y, R, G, B); bmp_printf( fnt | FONT_ALIGN_CENTER, xcb, ycb, "%3d%s%3d%s%3d%s", R*100/255,"%", G*100/255, "%", B*100/255, "%" ); } } #endif #ifdef FEATURE_GHOST_IMAGE static MENU_UPDATE_FUNC(transparent_overlay_display) { if (transparent_overlay && (transparent_overlay_offx || transparent_overlay_offy)) MENU_SET_VALUE( "ON, dx=%d, dy=%d", transparent_overlay_offx, transparent_overlay_offy ); transparent_overlay_hidden = 0; } static void transparent_overlay_offset(int dx, int dy) { transparent_overlay_offx = COERCE((int)transparent_overlay_offx + dx, -650, 650); transparent_overlay_offy = COERCE((int)transparent_overlay_offy + dy, -400, 400); transparent_overlay_hidden = 0; redraw(); } static void transparent_overlay_center_or_toggle() { if (transparent_overlay_offx || transparent_overlay_offy) // if off-center, just center it { transparent_overlay_offset_clear(0, 0); transparent_overlay_offset(0, 0); } else // if centered, hide it or show it back { transparent_overlay_hidden = !transparent_overlay_hidden; redraw(); } } static void transparent_overlay_offset_clear(void* priv, int delta) { transparent_overlay_offx = transparent_overlay_offy = 0; } int handle_transparent_overlay(struct event * event) { if (transparent_overlay && event->param == BGMT_LV && PLAY_OR_QR_MODE) { schedule_transparent_overlay(); return 0; } if (!get_global_draw()) return 1; if (transparent_overlay && liveview_display_idle() && !gui_menu_shown()) { if (event->param == BGMT_PRESS_UP) { transparent_overlay_offset(0, -40); return 0; } if (event->param == BGMT_PRESS_DOWN) { transparent_overlay_offset(0, 40); return 0; } if (event->param == BGMT_PRESS_LEFT) { transparent_overlay_offset(-40, 0); return 0; } if (event->param == BGMT_PRESS_RIGHT) { transparent_overlay_offset(40, 0); return 0; } #if defined(BGMT_JOY_CENTER) if (event->param == BGMT_JOY_CENTER) #else if (event->param == BGMT_PRESS_SET) #endif { transparent_overlay_center_or_toggle(); return 0; } } return 1; } #endif static CONFIG_INT("electronic.level", electronic_level, 0); struct menu_entry zebra_menus[] = { #ifdef FEATURE_GLOBAL_DRAW { .name = "Global Draw", .priv = &global_draw, #ifdef FEATURE_OVERLAYS_IN_PLAYBACK_MODE .max = 3, #else .max = 1, #endif .select_Q = toggle_disp_mode_menu, .update = global_draw_display, .icon_type = IT_DICE_OFF, .edit_mode = EM_MANY_VALUES, .choices = (const char *[]) {"OFF", "LiveView", "QuickReview", "ON, all modes"}, .help = "Enable/disable ML overlay graphics (zebra, cropmarks...)", //.essential = FOR_LIVEVIEW, }, #endif #ifdef FEATURE_ZEBRA { .name = "Zebras", .priv = &zebra_draw, .update = zebra_draw_display, .max = 1, .help = "Zebra stripes: show overexposed or underexposed areas.", .depends_on = DEP_GLOBAL_DRAW | DEP_EXPSIM, .children = (struct menu_entry[]) { { .name = "Color Space", .priv = &zebra_colorspace, #ifdef FEATURE_ZEBRA_FAST .max = 2, #else .max = 1, #endif .choices = (const char *[]) {"Luma", "RGB", "Luma Fast"}, .icon_type = IT_DICE, .update = zebra_param_not_used_for_raw, .help = "Luma: red/blue. RGB: show color of the clipped channel(s).", }, { .name = "Underexposure", .priv = &zebra_level_lo, .min = 0, .max = 20, .icon_type = IT_PERCENT_OFF, .update = zebra_level_display, .help = "Underexposure threshold.", }, { .name = "Overexposure", .priv = &zebra_level_hi, .min = 70, .max = 101, .icon_type = IT_PERCENT_OFF, .update = zebra_level_display, .help = "Overexposure threshold.", }, #ifdef CONFIG_MOVIE { .name = "When recording", .priv = &zebra_rec, .max = 1, .choices = (const char *[]) {"Hide", "Show"}, .help = "You can hide zebras when recording.", }, #endif #ifdef FEATURE_RAW_ZEBRAS { .name = "Use RAW zebras", .priv = &raw_zebra_enable, .max = 2, .update = raw_zebra_update, .choices = (const char *[]) {"OFF", "Always", "Photo only"}, .help = "Use RAW zebras if possible.", }, { .name = "Raw zebra underexposure", .priv = &zebra_raw_underexposure, .max = 5, .choices = (const char *[]) {"OFF", "0 EV", "1 EV", "2 EV", "3 EV", "4 EV"}, .help = "RAW zebra underexposure threshold", .help2 = "(in EVs above the noise floor)" }, #endif MENU_EOL }, }, #endif #ifdef FEATURE_FOCUS_PEAK_DISP_FILTER #ifndef FEATURE_FOCUS_PEAK #error This requires FEATURE_FOCUS_PEAK. #endif #endif #ifdef FEATURE_FOCUS_PEAK { .name = "Focus Peak", .priv = &focus_peaking, .update = focus_peaking_display, .max = 1, .help = "Show which parts of the image are in focus.", .submenu_width = 650, .depends_on = DEP_GLOBAL_DRAW, .children = (struct menu_entry[]) { { .name = "Filter bias", .priv = &focus_peaking_filter_edges, .max = 2, .choices = (const char *[]) {"Strong edges", "Balanced", "Fine details"}, .help = "Fine-tune the focus detection algorithm:", .help2 = "Strong edges: looks for edges, works best in low light.\n" "Balanced: tries to cover both strong edges and fine details.\n" "Fine details: looks for microcontrast. Needs lots of light.\n", .icon_type = IT_DICE }, /* { .name = "Method", .priv = &focus_peaking_method, .max = 1, .choices = (const char *[]) {"1st deriv.", "2nd deriv.", "Nyquist H"}, .help = "Contrast detection method. 2: more accurate, 1: less noisy.", },*/ #ifdef FEATURE_FOCUS_PEAK_DISP_FILTER #ifndef CONFIG_DISPLAY_FILTERS #error This requires CONFIG_DISPLAY_FILTERS. #endif { .name = "Display type", .priv = &focus_peaking_disp, .max = 4, .choices = (const char *[]) {"Blinking dots", "Fine dots", "Alpha blend", "Sharpness", "Raw"}, .help = "How to display peaking. Alpha looks nicer, but image lags.", }, #endif { .name = "Threshold", .priv = &focus_peaking_pthr, .select = focus_peaking_adjust_thr, .max = 50, .icon_type = IT_PERCENT_LOG, .unit = UNIT_PERCENT_x10, .help = "How many pixels are considered in focus (percentage).", }, { .name = "Color", .priv = &focus_peaking_color, .max = 7, .choices = (const char *[]) {"Red", "Green", "Blue", "Cyan", "Magenta", "Yellow", "Global Focus", "Local Focus"}, .help = "Focus peaking color (fixed or color coding).", .icon_type = IT_DICE, }, { .name = "Grayscale image", .priv = &focus_peaking_grayscale, .max = 1, .help = "Display LiveView image in grayscale.", }, MENU_EOL }, }, #endif #ifdef FEATURE_MAGIC_ZOOM { .name = "Magic Zoom", .priv = &zoom_overlay_enabled, .update = zoom_overlay_display, .min = 0, .max = 1, .help = "Zoom box for checking focus. Can be used while recording.", .submenu_width = 650, .depends_on = DEP_GLOBAL_DRAW | DEP_LIVEVIEW, .children = (struct menu_entry[]) { { .name = "Trigger mode", .priv = &zoom_overlay_trigger_mode, .min = 1, .max = 4, #ifdef CONFIG_ZOOM_BTN_NOT_WORKING_WHILE_RECORDING .choices = (const char *[]) {"HalfShutter", "Focus Ring", "FocusR+HalfS", "Always On"}, .help = "Trigger Magic Zoom by focus ring or half-shutter.", #else .choices = (const char *[]) {"Zoom.REC", "Focus+ZREC", "ZoomIn (+)", "Always On"}, .help = "Zoom when recording / trigger from focus ring / Zoom button", #endif }, { .name = "Size", .priv = &zoom_overlay_size, #ifdef FEATURE_MAGIC_ZOOM_FULL_SCREEN // most new cameras can do fullscreen :) .max = 3, .help = "Size of zoom box (small / medium / large / full screen).", #else // old cameras - simple zoom box .max = 2, .help = "Size of zoom box (small / medium / large).", #endif .choices = (const char *[]) {"Small", "Medium", "Large", "FullScreen"}, .icon_type = IT_SIZE, }, { .name = "Position", .priv = &zoom_overlay_pos, .max = 4, .choices = (const char *[]) {"Focus box", "Top-Left", "Top-Right", "Bottom-Right", "Bottom-Left"}, .icon_type = IT_DICE, .help = "Position of zoom box (fixed or linked to focus box).", }, { .name = "Magnification", .priv = &zoom_overlay_x, .max = 2, .choices = (const char *[]) {"1:1", "2:1", "3:1", "4:1"}, .icon_type = IT_SIZE, .help = "1:1 displays recorded pixels, 2:1 displays them doubled.", }, #ifdef CONFIG_LV_FOCUS_INFO { .name = "Focus confirm", .priv = &zoom_overlay_split, .max = 2, .choices = (const char *[]) {"Green Bars", "SplitScreen", "SS ZeroCross"}, .icon_type = IT_DICE, .help = "How to show focus confirmation (green bars / split screen).", }, #endif /*{ .name = "Look-up Table", .priv = &zoom_overlay_lut, .max = 1, .choices = (const char *[]) {"OFF", "CineStyle"}, .help = "LUT for increasing contrast in the zoom box.", },*/ MENU_EOL }, }, #endif #ifdef FEATURE_CROPMARKS MENU_PLACEHOLDER("Cropmarks"), #endif #ifdef FEATURE_GHOST_IMAGE #ifndef FEATURE_CROPMARKS #error This requires FEATURE_CROPMARKS. #endif { .name = "Ghost image", .priv = &transparent_overlay, .update = transparent_overlay_display, .max = 1, .help = "Overlay any image in LiveView. In PLAY mode, press LV btn.", .depends_on = DEP_GLOBAL_DRAW, .works_best_in = DEP_LIVEVIEW, // it will actually go into LV if it's not .children = (struct menu_entry[]) { { .name = "Auto-update", .priv = &transparent_overlay_auto_update, .max = 1, .help = "Update the overlay whenever you take a picture.", }, MENU_EOL } }, #endif #ifdef FEATURE_SPOTMETER { .name = "Spotmeter", .priv = &spotmeter_draw, .max = 1, .update = spotmeter_menu_display, .help = "Exposure aid: display brightness from a small spot.", .depends_on = DEP_GLOBAL_DRAW | DEP_EXPSIM, .children = (struct menu_entry[]) { { .name = "Spotmeter Unit", .priv = &spotmeter_formula, #ifdef FEATURE_RAW_SPOTMETER .max = 4, #else .max = 3, #endif .choices = (const char *[]) {"Percent", "0..255", "RGB (HTML)", "RAW (EV)", "RGB (Percent)"}, .icon_type = IT_DICE, .help = "Measurement unit for brightness level(s).", .help2 = "Percentage of overall brightness level.\n" "8 bit RGB level.\n" "HTML like color codes.\n" "Negative value from clipping, in EV (RAW).\n" "RGB color in Percentage.\n" }, { .name = "Spot Position", .priv = &spotmeter_position, .max = 1, .choices = (const char *[]) {"Center", "Focus box"}, .icon_type = IT_DICE, .help = "Spotmeter position: center or linked to focus box.", }, MENU_EOL } }, #endif #ifdef FEATURE_FALSE_COLOR { .name = "False color", .priv = &falsecolor_draw, .update = falsecolor_display, .max = 1, .submenu_width = 700, .submenu_height = 160, .help = "Exposure aid: each brightness level is color-coded.", .depends_on = DEP_GLOBAL_DRAW | DEP_EXPSIM, .children = (struct menu_entry[]) { { .name = "Palette ", .priv = &falsecolor_palette, .max = 5, .icon_type = IT_DICE, .choices = CHOICES("Marshall", "SmallHD", "50-55%", "67-72%", "Banding detection", "GreenScreen"), .update = falsecolor_display_palette, .help = "False color palettes for exposure, banding, green screen...", }, MENU_EOL } }, #endif #ifdef FEATURE_HISTOGRAM { .name = "Histogram", .priv = &hist_draw, .max = 1, .update = hist_print, .help = "Exposure aid: shows the distribution of brightness levels.", .depends_on = DEP_GLOBAL_DRAW | DEP_EXPSIM, .submenu_width = 700, .children = (struct menu_entry[]) { { .name = "Histogram type", .priv = &hist_type, .update = raw_histo_update, #ifdef FEATURE_RAW_HISTOGRAM .max = 3, #else .max = 1, #endif .choices = (const char *[]) { "YUV-based, Luma", "YUV-based, RGB", "RAW-based (RGB)", "RAW HistoBar (MAX)", }, .icon_type = IT_DICE, .help = "Choose between YUV-based (JPG) or RAW-based histogram.", .help2 = "If RAW data is not available, it will fall back to YUV-based.", }, #ifdef FEATURE_RAW_HISTOGRAM { .name = "RAW EV indicator", .priv = &hist_meter, .max = 2, .choices = CHOICES("OFF", "Dynamic Range", "ETTR hint"), .help = "Choose an EV image indicator to display on the histogram.", .help2 = " \n" "Display the dynamic range at current ISO, from noise stdev.\n" "Show how many stops you can push the exposure to the right.\n" }, #endif { .name = "Scaling", .priv = &hist_log, .max = 1, .choices = (const char *[]) {"Linear", "Log"}, .help = "Linear or logarithmic histogram.", .icon_type = IT_DICE, }, { .name = "Clip warning", .priv = &hist_warn, .max = 1, .help = "Display warning dots when one color channel is clipped.", .help2 = "Numbers represent the percentage of pixels clipped.", }, MENU_EOL }, }, #endif #ifdef FEATURE_WAVEFORM { .name = "Waveform", .priv = &waveform_draw, .update = waveform_print, .max = 1, .help = "Exposure aid: useful for checking overall brightness.", .depends_on = DEP_GLOBAL_DRAW | DEP_EXPSIM, .children = (struct menu_entry[]) { { .name = "Waveform Size", .priv = &waveform_size, .max = 2, .choices = (const char *[]) {"Small", "Large", "FullScreen"}, .icon_type = IT_SIZE, .help = "Waveform size: Small / Large / FullScreen.", }, MENU_EOL }, //.essential = FOR_LIVEVIEW | FOR_PLAYBACK, }, #endif MENU_PLACEHOLDER("Vectorscope"), #ifdef FEATURE_LEVEL_INDICATOR { .name = "Level Indicator", .priv = &electronic_level, .max = 1, .help = "Electronic level indicator in 0.5 degree steps.", .depends_on = DEP_GLOBAL_DRAW, }, #endif }; static struct menu_entry livev_dbg_menus[] = { #ifdef FEATURE_SHOW_OVERLAY_FPS { .name = "Show Overlay FPS", .priv = &show_lv_fps, .max = 1, .help = "Show the frame rate of overlay loop (zebras, peaking...)" }, #endif }; #ifdef FEATURE_LV_DISPLAY_PRESETS struct menu_entry livev_cfg_menus[] = { { .name = "LV Display Presets", .priv = &disp_profiles_0, .max = 3, .choices = (const char *[]) {"OFF (1)", "2", "3", "4"}, .icon_type = IT_DICE_OFF, .help = "Num. of LV display presets. Switch with " INFO_BTN_NAME " or from LiveV.", .depends_on = DEP_LIVEVIEW, }, }; #endif /* void copy_zebras_from_mirror() { uint32_t* B = (uint32_t*)bmp_vram(); uint32_t* M = (uint32_t*)get_bvram_mirror(); ASSERT(B); ASSERT(M); get_yuv422_vram(); for (int i = os.y0; i < os.y_max; i++) { for (int j = os.x0; j < os.x_max; j+=4) { uint32_t p = B[BM(j,i)/4]; uint32_t m = M[BM(j,i)/4]; if (p != 0) continue; B[BM(j,i)/4] = m & ~0x80808080; #ifdef CONFIG_500D asm("nop"); asm("nop"); asm("nop"); asm("nop"); #endif } } } void clear_zebras_from_mirror() { uint8_t* M = (uint8_t*)get_bvram_mirror(); get_yuv422_vram(); for (int i = os.y0; i < os.y_max; i++) { for (int j = os.x0; j < os.x_max; j++) { uint8_t m = M[BM(j,i)]; if (m & 0x80) continue; M[BM(j,i)] = 0; #ifdef CONFIG_500D asm("nop"); asm("nop"); asm("nop"); asm("nop"); #endif } } } */ #ifdef FEATURE_OVERLAYS_IN_PLAYBACK_MODE static void trigger_zebras_for_qr() { fake_simple_button(MLEV_TRIGGER_ZEBRAS_FOR_PLAYBACK); } #endif PROP_HANDLER(PROP_GUI_STATE) { bmp_draw_request_stop(); // abort drawing any slow cropmarks lv_paused = 0; #ifdef FEATURE_OVERLAYS_IN_PLAYBACK_MODE if (ZEBRAS_IN_QUICKREVIEW && buf[0] == GUISTATE_QR) trigger_zebras_for_qr(); #endif #ifdef FEATURE_GHOST_IMAGE if (transparent_overlay && transparent_overlay_auto_update && buf[0] == GUISTATE_QR) { fake_simple_button(BGMT_LV); // update ghost image } #endif } #ifdef FEATURE_MAGIC_ZOOM static void zoom_overlay_toggle() { zoom_overlay_triggered_by_zoom_btn = !zoom_overlay_triggered_by_zoom_btn; if (!zoom_overlay_triggered_by_zoom_btn) { zoom_overlay_triggered_by_focus_ring_countdown = 0; } } int handle_zoom_overlay(struct event * event) { if (gui_menu_shown()) return 1; if (!lv) return 1; if (!get_global_draw()) return 1; #ifdef CONFIG_600D if (get_disp_pressed()) return 1; #endif #ifdef CONFIG_ZOOM_BTN_NOT_WORKING_WHILE_RECORDING if (event->param == BGMT_PRESS_HALFSHUTTER && get_zoom_overlay_trigger_by_halfshutter()) zoom_overlay_toggle(); if (is_zoom_overlay_triggered_by_zoom_btn() && !get_zoom_overlay_trigger_by_halfshutter()) zoom_overlay_toggle(); #else // zoom in when recording => enable Magic Zoom if (get_zoom_overlay_trigger_mode() && RECORDING_H264_STARTED && MVR_FRAME_NUMBER > 10 && event->param == #if defined(CONFIG_5D3) || defined(CONFIG_6D) BGMT_PRESS_ZOOM_IN #else BGMT_UNPRESS_ZOOM_IN #endif ) { zoom_overlay_toggle(); return 0; } // if magic zoom is enabled, Zoom In should always disable it if (is_zoom_overlay_triggered_by_zoom_btn() && event->param == BGMT_PRESS_ZOOM_IN) { zoom_overlay_toggle(); return 0; } if (get_zoom_overlay_trigger_mode() && lv_dispsize == 1 && event->param == BGMT_PRESS_ZOOM_IN) { #ifdef FEATURE_LCD_SENSOR_SHORTCUTS int lcd_sensor_trigger = (get_lcd_sensor_shortcuts() && display_sensor && DISPLAY_SENSOR_POWERED); #else int lcd_sensor_trigger = 0; #endif // magic zoom toggled by sensor+zoom in (modes Zr and Zr+F) if (get_zoom_overlay_trigger_mode() < 3 && lcd_sensor_trigger) { zoom_overlay_toggle(); return 0; } // (*): magic zoom toggled by zoom in, normal zoom by sensor+zoom in else if (get_zoom_overlay_trigger_mode() == MZ_TAKEOVER_ZOOM_IN_BTN && !get_halfshutter_pressed() && !lcd_sensor_trigger) { zoom_overlay_toggle(); return 0; } } #endif return 1; } void zoom_overlay_disable() { zoom_overlay_triggered_by_zoom_btn = 0; zoom_overlay_triggered_by_focus_ring_countdown = 0; } void zoom_overlay_set_countdown(int x) { zoom_overlay_triggered_by_focus_ring_countdown = x; } void digic_zoom_overlay_step(int force_off) { #ifdef FEATURE_MAGIC_ZOOM_FULL_SCREEN #ifndef CONFIG_CAN_REDIRECT_DISPLAY_BUFFER_EASILY #error This requires CONFIG_CAN_REDIRECT_DISPLAY_BUFFER_EASILY. #endif static int prev = 0; if (digic_zoom_overlay_enabled() && !force_off) { if (!prev) // first iteration after trigger { redraw(); } else { // center of AF frame int aff_x0_lv, aff_y0_lv; get_afframe_pos(720, 480, &aff_x0_lv, &aff_y0_lv); // Get the center of the AF frame in normalized coordinates // Translate it into LV coord space aff_x0_lv = N2LV_X(aff_x0_lv); aff_y0_lv = N2LV_Y(aff_y0_lv); // Translate it into HD coord space int aff_x0_hd = LV2HD_X(aff_x0_lv); int aff_y0_hd = LV2HD_Y(aff_y0_lv); // Find the top-left corner point in HD space int corner_x0 = COERCE(aff_x0_hd - vram_lv.width/2, 0, vram_hd.width - vram_lv.width); int corner_y0 = COERCE(aff_y0_hd - vram_lv.height/2, 0, vram_hd.height - vram_lv.height); // Compute offset for HD buffer int offset = corner_x0 * 2 + corner_y0 * vram_hd.pitch; // Redirect the display buffer to show the magnified area YUV422_LV_BUFFER_DISPLAY_ADDR = prev + offset; // and make sure the pitch is right EngDrvOut(0xc0f140e8, vram_hd.pitch - vram_lv.pitch); } prev = YUV422_HD_BUFFER_DMA_ADDR; } else { if (prev) { EngDrvOut(0xc0f140e8, 0); } prev = 0; } #endif } /** * Draw Magic Zoom overlay */ static void draw_zoom_overlay(int dirty) { if (zoom_overlay_size == 3) return; // fullscreen zoom done via digic //~ if (vram_width > 720) return; if (!lv) return; if (!get_global_draw()) return; //~ if (gui_menu_shown()) return; if (!bmp_is_on()) return; if (lv_dispsize != 1) return; //~ if (get_halfshutter_pressed() && clearscreen != 2) return; if (RECORDING_H264_STARTING) return; #ifndef CONFIG_LV_FOCUS_INFO zoom_overlay_split = 0; // 50D doesn't report focus #endif struct vram_info * lv = get_yuv422_vram(); struct vram_info * hd = get_yuv422_hd_vram(); if( !lv->vram ) return; if( !hd->vram ) return; if( !bmp_vram()) return; uint16_t* lvr = (uint16_t*) lv->vram; uint16_t* hdr = (uint16_t*) hd->vram; // select buffer where MZ should be written (camera-specific, guesswork) #if defined(CONFIG_5D2) || defined(CONFIG_EOSM) || defined(CONFIG_50D) #warning FIXME: this method uses busy waiting, which causes high CPU usage and overheating when using Magic Zoom void busy_vsync(int hd, int timeout_ms) { int timeout_us = timeout_ms * 1000; void* old = (void*)shamem_read(hd ? REG_EDMAC_WRITE_HD_ADDR : REG_EDMAC_WRITE_LV_ADDR); int t0 = *(uint32_t*)0xC0242014; while(1) { int t1 = *(uint32_t*)0xC0242014; int dt = MOD(t1 - t0, 1048576); void* new = (void*)shamem_read(hd ? REG_EDMAC_WRITE_HD_ADDR : REG_EDMAC_WRITE_LV_ADDR); if (old != new) break; if (dt > timeout_us) return; for (int i = 0; i < 100; i++) asm("nop"); // don't stress the digic too much } } lvr = (uint16_t*) shamem_read(REG_EDMAC_WRITE_LV_ADDR); busy_vsync(0, 20); #endif #if defined(CONFIG_DIGIC_V) && ! defined(CONFIG_EOSM) lvr = CACHEABLE(YUV422_LV_BUFFER_DISPLAY_ADDR); if ( lvr != CACHEABLE(YUV422_LV_BUFFER_1) && lvr != CACHEABLE(YUV422_LV_BUFFER_2) && lvr != CACHEABLE(YUV422_LV_BUFFER_3) && #ifdef YUV422_LV_BUFFER_4 lvr != CACHEABLE(YUV422_LV_BUFFER_4) && #endif 1) { /* refuse to draw on invalid buffer addresses */ return; } #endif if (!lvr) return; // center of AF frame int aff_x0_lv, aff_y0_lv; get_afframe_pos(720, 480, &aff_x0_lv, &aff_y0_lv); // Get the center of the AF frame in normalized coordinates // Translate it into LV coord space aff_x0_lv = N2LV_X(aff_x0_lv); aff_y0_lv = N2LV_Y(aff_y0_lv); // Translate it into HD coord space int aff_x0_hd = LV2HD_X(aff_x0_lv); int aff_y0_hd = LV2HD_Y(aff_y0_lv); int W = 0, H = 0; switch(zoom_overlay_size) { case 0: W = os.x_ex / 5; H = os.y_ex / 4; break; case 1: W = os.x_ex / 3; H = os.y_ex * 2/5; break; case 2: W = os.x_ex/2; H = os.y_ex/2; break; case 3: W = os.x_ex; H = os.y_ex; break; } // Magnification factor int X = zoom_overlay_x + 1; // Center of Magic Zoom box in the LV coordinate space int zb_x0_lv, zb_y0_lv; switch(zoom_overlay_pos) { case 0: // AFF zb_x0_lv = aff_x0_lv; zb_y0_lv = aff_y0_lv; break; case 1: // NW zb_x0_lv = W/2 + 50; zb_y0_lv = H/2 + 50; break; case 2: // NE zb_x0_lv = BM2LV_X(os.x_max) - W/2 - 50; zb_y0_lv = H/2 + 50; break; case 3: // SE zb_x0_lv = BM2LV_X(os.x_max) - W/2 - 50; zb_y0_lv = BM2LV_Y(os.y_max) - H/2 - 50; break; case 4: // SV zb_x0_lv = W/2 + 50; zb_y0_lv = BM2LV_Y(os.y_max) - H/2 - 50; break; default: return; } //~ bmp_printf(FONT_LARGE, 50, 50, "%d,%d %d,%d", W, H, aff_x0_lv); if (zoom_overlay_pos) { int w = W * lv->width / hd->width; int h = H * lv->width / hd->width; #ifdef CONFIG_1100D h /= 2; // LCD half-height fix #endif w /= X; h /= X; const int val_in_coerce_w = aff_x0_lv - (w>>1); const int coerce_w = COERCE(val_in_coerce_w, 0, 720-w); const int val_in_coerce_h1 = aff_y0_lv - (h>>1); const int val_in_coerce_h2 = aff_y0_lv + (h>>1); memset64(lvr + coerce_w + COERCE(val_in_coerce_h1 - 2, 0, lv->height) * lv->width, MZ_BLACK, w<<1); memset64(lvr + coerce_w + COERCE(val_in_coerce_h1 - 1, 0, lv->height) * lv->width, MZ_WHITE, w<<1); memset64(lvr + coerce_w + COERCE(val_in_coerce_h2 + 1, 0, lv->height) * lv->width, MZ_WHITE, w<<1); memset64(lvr + coerce_w + COERCE(val_in_coerce_h2 + 2, 0, lv->height) * lv->width, MZ_BLACK, w<<1); } //~ draw_circle(x0,y0,45,COLOR_WHITE); int y; int x0c = COERCE(zb_x0_lv - (W>>1), 0, lv->width-W); int y0c = COERCE(zb_y0_lv - (H>>1), 0, lv->height-H); extern int focus_value; extern int focus_min_value; //~ bmp_printf(FONT_MED, 300, 100, "%d %d ", focus_value, focus_min_value); int rawoff = COERCE(80 - focus_value, 0, 100) >> 2; if (focus_min_value > 60) rawoff = 1; // out of focus? // reverse the sign of split when perfect focus is achieved static int rev = 0; static int poff = 0; if (rawoff != 0 && poff == 0) rev = !rev; poff = rawoff; if (zoom_overlay_split == 1 /* non zerocross */) rev = 0; uint16_t* d = lvr + x0c + (y0c + 2) * lv->width; uint16_t* s = hdr + (aff_y0_hd - (H/2/X)) * hd->width + (aff_x0_hd - (W/2/X)); for (y = 2; y < H-2; y++) { int off = zoom_overlay_split ? (y < H/2 ? rawoff : -rawoff) : 0; if (rev) off = -off; #ifdef CONFIG_1100D if(y%2 == 0) // The 1100D has half-height LCD res so we line-skip one from the sensor #endif { yuvcpy_main((uint32_t*)d, (uint32_t*)(s + off), W, X); d += lv->width; } if (y%X==0) s += hd->width; } #ifdef CONFIG_1100D H /= 2; //LCD res fix (half height) #endif memset64(lvr + x0c + COERCE(0 + y0c, 0, 720) * lv->width, rawoff ? MZ_BLACK : MZ_GREEN, W<<1); memset64(lvr + x0c + COERCE(1 + y0c, 0, 720) * lv->width, rawoff ? MZ_WHITE : MZ_GREEN, W<<1); if (!rawoff) { memset64(lvr + x0c + COERCE(-2 + y0c, 0, 720) * lv->width, MZ_GREEN, W<<1); memset64(lvr + x0c + COERCE(-1 + y0c, 0, 720) * lv->width, MZ_GREEN, W<<1); memset64(lvr + x0c + COERCE(H + y0c, 0, 720) * lv->width, MZ_GREEN, W<<1); memset64(lvr + x0c + COERCE(H+1 + y0c, 0, 720) * lv->width, MZ_GREEN, W<<1); } memset64(lvr + x0c + COERCE(H-2 + y0c, 0, 720) * lv->width, rawoff ? MZ_WHITE : MZ_GREEN, W<<1); memset64(lvr + x0c + COERCE(H-1 + y0c, 0, 720) * lv->width, rawoff ? MZ_BLACK : MZ_GREEN, W<<1); #ifdef CONFIG_1100D H *= 2; // Undo it #endif if (dirty) bmp_fill(0, LV2BM_X(x0c), LV2BM_Y(y0c), LV2BM_DX(W), LV2BM_DY(H)); //~ bmp_fill(rawoff ? COLOR_BLACK : COLOR_GREEN1, x0c, y0c, W, 1); //~ bmp_fill(rawoff ? COLOR_WHITE : COLOR_GREEN2, x0c+1, y0c, W, 1); //~ bmp_fill(rawoff ? COLOR_WHITE : COLOR_GREEN2, x0c, y0c + H - 1, W, 1); //~ bmp_fill(rawoff ? COLOR_BLACK : COLOR_GREEN1, x0c, y0c + H, W, 1); } #endif // FEATURE_MAGIC_ZOOM int liveview_display_idle() { struct gui_task * current = gui_task_list.current; struct dialog * dialog = current->priv; extern thunk LiveViewApp_handler; #if defined(CONFIG_5D3) extern thunk LiveViewLevelApp_handler; #elif defined(CONFIG_DIGIC_V) extern thunk LiveViewShutterApp_handler; #endif #if defined(CONFIG_6D) extern thunk LiveViewWifiApp_handler; #endif #if defined(CONFIG_LVAPP_HACK_RELOC) extern uintptr_t new_LiveViewApp_handler; #endif return LV_NON_PAUSED && DISPLAY_IS_ON && !menu_active_and_not_hidden() && (// gui_menu_shown() || // force LiveView when menu is active, but hidden ( gui_state == GUISTATE_IDLE && (dialog->handler == (dialog_handler_t) &LiveViewApp_handler #if defined(CONFIG_LVAPP_HACK_RELOC) || dialog->handler == (dialog_handler_t) new_LiveViewApp_handler #endif #if defined(CONFIG_5D3) || dialog->handler == (dialog_handler_t) &LiveViewLevelApp_handler #endif #if defined(CONFIG_6D) || dialog->handler == (dialog_handler_t) &LiveViewWifiApp_handler #endif //~ for this, check value of get_current_dialog_handler() #if defined(CONFIG_DIGIC_V) && !defined(CONFIG_5D3) || dialog->handler == (dialog_handler_t) &LiveViewShutterApp_handler #endif ) && CURRENT_GUI_MODE <= 3 && #ifdef CURRENT_GUI_MODE_2 CURRENT_GUI_MODE_2 <= 3 && #endif job_state_ready_to_take_pic() && !mirror_down ) ); } // when it's safe to draw zebras and other on-screen stuff int zebra_should_run() { return liveview_display_idle() && get_global_draw() && !is_zoom_mode_so_no_zebras() && !(clearscreen == 1 && (get_halfshutter_pressed() || dofpreview)) && !WAVEFORM_FULLSCREEN; } #ifdef FEATURE_OVERLAYS_IN_PLAYBACK_MODE static int overlays_playback_running = 0; static void draw_overlays_playback() { overlays_playback_running = 1; if (!PLAY_OR_QR_MODE) { overlays_playback_running = 0; return; } extern int quick_review_allow_zoom; if (quick_review_allow_zoom && image_review_time == 0xff) { // wait for the camera to switch from QR to PLAY before drawing anything while (!PLAY_MODE) msleep(100); msleep(500); } while (!DISPLAY_IS_ON) msleep(100); if (!PLAY_OR_QR_MODE) { overlays_playback_running = 0; return; } if (QR_MODE) msleep(300); get_yuv422_vram(); // just to refresh VRAM params info_led_on(); if (PLAY_OR_QR_MODE) EngDrvOut(DIGIC_ZEBRA_REGISTER, 0); // disable Canon highlight warning, looks ugly with both on the screen :) BMP_LOCK( bvram_mirror_clear(); // may be filled with liveview cropmark / masking info, not needed in play mode clrscr(); #ifdef FEATURE_CROPMARKS cropmark_redraw(); #endif #ifdef FEATURE_DEFISHING_PREVIEW extern int defish_preview; if (defish_preview) { /* to refactor with CBR + separate file */ extern void defish_draw_play(); defish_draw_play(); } #endif #ifdef FEATURE_SPOTMETER if (spotmeter_draw) spotmeter_step(); #endif draw_histogram_and_waveform(1); #ifdef FEATURE_FALSE_COLOR if (falsecolor_draw) { draw_false_downsampled(); } else #endif { guess_focus_peaking_threshold(); draw_zebra_and_focus(1,1); } bvram_mirror_clear(); // may remain filled with playback zebras ) clean_d_cache(); // to avoid display artifacts info_led_off(); overlays_playback_running = 0; } #endif int should_draw_bottom_graphs() { if (!lv) return 0; if (gui_menu_shown()) return 0; int screen_layout = get_screen_layout(); if (screen_layout == SCREENLAYOUT_4_3 && lv_disp_mode == 0) return 1; return 0; } void draw_histogram_and_waveform(int allow_play) { if (menu_active_and_not_hidden()) return; if (!get_global_draw()) return; get_yuv422_vram(); #if defined(FEATURE_HISTOGRAM) || defined(FEATURE_WAVEFORM) || defined(FEATURE_VECTORSCOPE) if (0 || hist_draw || waveform_draw #if defined(FEATURE_VECTORSCOPE) || vectorscope_should_draw() #endif ) { hist_build(); /* also updates waveform and vectorscope */ } #endif if (menu_active_and_not_hidden()) return; // hack: not to draw histo over menu if (!get_global_draw()) return; if (!liveview_display_idle() && !(PLAY_OR_QR_MODE && allow_play) && !gui_menu_shown()) return; if (is_zoom_mode_so_no_zebras()) return; int screen_layout = get_screen_layout(); #ifdef FEATURE_HISTOGRAM if( hist_draw && !WAVEFORM_FULLSCREEN) { #ifdef CONFIG_4_3_SCREEN if (PLAY_OR_QR_MODE) BMP_LOCK( hist_draw_image( os.x0 + 500, 1); ) else #endif if (should_draw_bottom_graphs()) BMP_LOCK( hist_draw_image( os.x0 + 50, 480 - hist_height - 1); ) else if (screen_layout == SCREENLAYOUT_3_2) BMP_LOCK( hist_draw_image( os.x_max - HIST_WIDTH - 2, os.y_max - (lv ? os.off_169 + 10 : 0) - hist_height - 1); ) else BMP_LOCK( hist_draw_image( os.x_max - HIST_WIDTH - 5, os.y0 + 100); ) } #endif if (menu_active_and_not_hidden()) return; if (!get_global_draw()) return; if (!liveview_display_idle() && !(PLAY_OR_QR_MODE && allow_play) && !gui_menu_shown()) return; if (is_zoom_mode_so_no_zebras()) return; #ifdef FEATURE_WAVEFORM if( waveform_draw) { #ifdef CONFIG_4_3_SCREEN if (PLAY_OR_QR_MODE && WAVEFORM_FACTOR == 1) BMP_LOCK( waveform_draw_image( os.x0 + 100, 1, 54); ) else #endif if (should_draw_bottom_graphs() && WAVEFORM_FACTOR == 1) BMP_LOCK( waveform_draw_image( os.x0 + 250, 480 - 54, 54); ) else if (screen_layout == SCREENLAYOUT_3_2 && !WAVEFORM_FULLSCREEN) { if (WAVEFORM_FACTOR == 1) BMP_LOCK( waveform_draw_image( os.x0 + 4, os.y_max - (lv ? os.off_169 : 0) - (gui_menu_shown() ? 25 : 0) - 54, 54); ) else BMP_LOCK( waveform_draw_image( os.x_max - WAVEFORM_WIDTH*WAVEFORM_FACTOR - 4, os.y0 + 100, WAVEFORM_HEIGHT*WAVEFORM_FACTOR ); ); } else BMP_LOCK( waveform_draw_image( os.x_max - WAVEFORM_WIDTH*WAVEFORM_FACTOR - (WAVEFORM_FULLSCREEN ? 0 : 4), os.y_max - WAVEFORM_HEIGHT*WAVEFORM_FACTOR - WAVEFORM_OFFSET, WAVEFORM_HEIGHT*WAVEFORM_FACTOR ); ) } #endif #ifdef FEATURE_VECTORSCOPE vectorscope_redraw(); #endif } static void clearscreen_task( void* unused ) { idle_wakeup_reset_counters(0); TASK_LOOP { clearscreen_loop: msleep(100); //~ bmp_printf(FONT_MED, 100, 100, "%d %d %d", idle_countdown_display_dim, idle_countdown_display_off, idle_countdown_globaldraw); /* blink LED if screen is turned off */ idle_led_blink_step(k); if (!lv && !lv_paused) continue; #ifdef FEATURE_CLEAR_OVERLAYS if (clearscreen == 3) { if (liveview_display_idle() && !gui_menu_shown()) { bmp_off(); } else { bmp_on(); } } if (clearscreen == 4) { if (RECORDING) { bmp_off(); } else { bmp_on(); } } // clear overlays on shutter halfpress if (clearscreen == 1 && (get_halfshutter_pressed() || dofpreview) && !gui_menu_shown()) { BMP_LOCK( clrscr_mirror(); ) int i; for (i = 0; i < (int)clearscreen_delay/20; i++) { if (i % 10 == 0 && liveview_display_idle()) BMP_LOCK( update_lens_display(1,1); ) msleep(20); if (!(get_halfshutter_pressed() || dofpreview)) goto clearscreen_loop; } bmp_off(); while ((get_halfshutter_pressed() || dofpreview)) msleep(100); bmp_on(); #ifdef CONFIG_ZOOM_BTN_NOT_WORKING_WHILE_RECORDING msleep(100); if (get_zoom_overlay_trigger_by_halfshutter()) // this long press should not trigger MZ zoom_overlay_toggle(); #endif } #endif #ifdef FEATURE_POWERSAVE_LIVEVIEW idle_powersave_step(); #endif #ifdef FEATURE_CROPMARKS // since this task runs at 10Hz, I prefer cropmark redrawing here cropmark_step(); #endif } } TASK_CREATE( "cls_task", clearscreen_task, 0, 0x1a, 0x2000 ); //~ CONFIG_INT("disable.redraw", disable_redraw, 0); CONFIG_INT("display.dont.mirror", display_dont_mirror, 1); // this should be synchronized with // * graphics code (like zebra); otherwise zebras will remain frozen on screen // * gui_main_task (to make sure Canon won't call redraw in parallel => crash) void _redraw_do() { extern int ml_started; if (!ml_started) return; if (gui_menu_shown()) { menu_redraw(); return; } BMP_LOCK ( #ifdef CONFIG_VARIANGLE_DISPLAY if (display_dont_mirror && display_dont_mirror_dirty) { if (lcd_position == 1) { /* Canon stub, usually available only on cameras with variable displays */ extern void NormalDisplay(); NormalDisplay(); } display_dont_mirror_dirty = 0; } #endif //~ if (disable_redraw) //~ { //~ clrscr(); // safest possible redraw method :) //~ } //~ else { struct gui_task * current = gui_task_list.current; struct dialog * dialog = current->priv; if (dialog && streq(dialog->type, "DIALOG")) // if dialog seems valid { // to redraw, we need access to front buffer int front_buffer_disabled = canon_gui_front_buffer_disabled(); if (front_buffer_disabled) { /* temporarily enable front buffer to allow the redraw */ canon_gui_enable_front_buffer(0); } dialog_redraw(dialog); // try to redraw (this has semaphores for winsys) if (front_buffer_disabled) { /* disable it back */ #ifdef CONFIG_KILL_FLICKER idle_kill_flicker(); #else canon_gui_disable_front_buffer(); #endif } } else { clrscr(); // out of luck, fallback } } ) // ask other stuff to redraw afframe_set_dirty(); #ifdef FEATURE_CROPMARKS crop_set_dirty(cropmark_cache_is_valid() ? 2 : 10); #endif menu_set_dirty(); lens_display_set_dirty(); zoom_overlay_dirty = 1; } void redraw() { fake_simple_button(MLEV_REDRAW); } #ifdef FEATURE_GHOST_IMAGE static int transparent_overlay_flag = 0; void schedule_transparent_overlay() { transparent_overlay_flag = 1; } #endif static int lens_display_dirty = 0; void lens_display_set_dirty() { lens_display_dirty = 4; if (menu_active_but_hidden()) // in this case, menu will display bottom bar, force a redraw menu_set_dirty(); } int is_focus_peaking_enabled() { #ifdef FEATURE_FOCUS_PEAK return focus_peaking && (lv || (QR_MODE && ZEBRAS_IN_QUICKREVIEW)) && get_global_draw() && !should_draw_zoom_overlay() ; #else return 0; #endif } static void digic_zebra_cleanup() { #ifdef FEATURE_ZEBRA_FAST if (zebra_digic_dirty) { if (!DISPLAY_IS_ON) return; EngDrvOut(DIGIC_ZEBRA_REGISTER, 0); clrscr_mirror(); alter_bitmap_palette_entry(FAST_ZEBRA_GRID_COLOR, FAST_ZEBRA_GRID_COLOR, 256, 256); zebra_digic_dirty = 0; } #endif } #ifdef FEATURE_SHOW_OVERLAY_FPS void update_lv_fps() // to be called every 10 seconds { if (show_lv_fps) bmp_printf(FONT_MED, 50, 50, "%d.%d fps ", fps_ticks/10, fps_ticks%10); fps_ticks = 0; } #endif // Items which need a high FPS // Magic Zoom, Focus Peaking, zebra*, spotmeter*, false color* // * = not really high FPS, but still fluent static void livev_hipriority_task( void* unused ) { msleep(1000); #ifdef FEATURE_CROPMARKS find_cropmarks(); #endif #ifdef FEATURE_LV_DISPLAY_PRESETS update_disp_mode_bits_from_params(); #endif TASK_LOOP { //~ vsync(&YUV422_LV_BUFFER_DISPLAY_ADDR); fps_ticks++; while (is_mvr_buffer_almost_full()) { msleep(100); } int zd = zebra_draw && (lv_luma_is_accurate() || PLAY_OR_QR_MODE) && (zebra_rec || NOT_RECORDING); // when to draw zebras (should match the one from draw_zebra_and_focus) if (!zd) digic_zebra_cleanup(); #ifdef CONFIG_RAW_LIVEVIEW static int raw_flag = 0; #endif if (!zebra_should_run()) { while (clearscreen == 1 && (get_halfshutter_pressed() || dofpreview)) msleep(100); while (RECORDING_H264_STARTING) msleep(100); if (!zebra_should_run()) { digic_zebra_cleanup(); if (lv && !gui_menu_shown()) redraw(); #ifdef CONFIG_ELECTRONIC_LEVEL if (lv) disable_electronic_level(); #endif #ifdef CONFIG_RAW_LIVEVIEW if (raw_flag) { raw_lv_release(); raw_flag = 0; } #endif while (!zebra_should_run()) { msleep(100); } vram_params_set_dirty(); zoom_overlay_triggered_by_focus_ring_countdown = 0; crop_set_dirty(10); msleep(500); } if (!zebra_should_run()) { /* false alarm */ continue; } } #if 0 draw_cropmark_area(); // just for debugging struct vram_info * lv = get_yuv422_vram(); struct vram_info * hd = get_yuv422_hd_vram(); bmp_printf(FONT_MED, 100, 100, "ext:%d%d%d \nlv:%x %dx%d \nhd:%x %dx%d ", EXT_MONITOR_RCA, ext_monitor_hdmi, hdmi_code, lv->vram, lv->width, lv->height, hd->vram, hd->width, hd->height); #endif #ifdef CONFIG_RAW_LIVEVIEW int raw_needed = 0; /* if picture quality is raw, switch the LiveView to raw mode (photo, zoom 1x) */ int raw = pic_quality & 0x60000; if (raw && lv_dispsize == 1 && !is_movie_mode()) { /* only raw zebras, raw histogram and raw spotmeter are working in LV raw mode */ if (zebra_draw && raw_zebra_enable == 1) raw_needed = 1; /* raw zebras: always */ if (hist_draw && RAW_HISTOGRAM_ENABLED) raw_needed = 1; /* raw hisogram (any kind) */ if (spotmeter_draw && spotmeter_formula == 3) raw_needed = 1; /* spotmeter, units: raw */ } if (!raw_flag && raw_needed) { /* do we need any raw overlays? enable LV raw mode if we don't already have it */ raw_lv_request(); raw_flag = 1; } if (raw_flag && !raw_needed) { /* if we no longer need raw overlays, keep LiveView in normal mode (it does less stuff) */ raw_lv_release(); raw_flag = 0; } #endif int mz = should_draw_zoom_overlay(); lv_vsync(mz); guess_fastrefresh_direction(); #ifdef FEATURE_MAGIC_ZOOM if (mz) { //~ msleep(k % 50 == 0 ? MIN_MSLEEP : 10); if (zoom_overlay_dirty) BMP_LOCK( clrscr_mirror(); ) draw_zoom_overlay(zoom_overlay_dirty); //~ BMP_LOCK( if (lv) ) zoom_overlay_dirty = 0; //~ crop_set_dirty(10); // don't draw cropmarks while magic zoom is active // but redraw them after MZ is turned off //~ continue; } else #endif { if (!zoom_overlay_dirty) { redraw(); msleep(700); } // redraw cropmarks after MZ is turned off zoom_overlay_dirty = 1; msleep(10); #ifdef CONFIG_DISPLAY_FILTERS /* to refactor with CBR */ extern void display_filter_step(int frame_number); display_filter_step(k); #endif #ifdef FEATURE_FALSE_COLOR if (falsecolor_draw) { if (k % 4 == 0) BMP_LOCK( if (lv) draw_false_downsampled(); ) } else #endif { BMP_LOCK( if (lv) draw_zebra_and_focus( k % ((focus_peaking ? 5 : 3) * (RECORDING ? 5 : 1)) == 0, /* should redraw zebras? */ k % 2 == 1 /* should redraw focus peaking? */ ); ) } } #ifdef FEATURE_SPOTMETER // update spotmeter every second, not more often than that static int spotmeter_aux = 0; if (spotmeter_draw && should_run_polling_action(1000, &spotmeter_aux)) BMP_LOCK( if (lv) spotmeter_step(); ) #endif #ifdef CONFIG_ELECTRONIC_LEVEL if (electronic_level && k % 2) BMP_LOCK( if (lv) show_electronic_level(); ) #endif #ifdef FEATURE_REC_NOTIFY /* to refactor with CBR */ extern void rec_notify_continuous(int called_from_menu); if (k % 8 == 7) rec_notify_continuous(0); #endif #ifdef FEATURE_MAGIC_ZOOM if (zoom_overlay_triggered_by_focus_ring_countdown) { zoom_overlay_triggered_by_focus_ring_countdown--; } #endif int m = 100; if (lens_display_dirty) m = 10; if (should_draw_zoom_overlay()) m = 100; int kmm = k % m; if (!gui_menu_shown()) // don't update everything in one step, to reduce magic zoom flicker { #if defined(CONFIG_550D) || defined(CONFIG_5D2) || defined(CONFIG_50D) || defined(CONFIG_7D) if (kmm == 0) BMP_LOCK( if (lv) black_bars(); ) #endif if (kmm == 2) { BMP_LOCK( if (lv) update_lens_display(1,0); ); if (lens_display_dirty) lens_display_dirty--; } if (kmm == 8) { BMP_LOCK( if (lv) update_lens_display(0,1); ); if (lens_display_dirty) lens_display_dirty--; } } } } static void loprio_sleep() { msleep(200); while (is_mvr_buffer_almost_full()) msleep(100); } // Items which do not need a high FPS, but are CPU intensive // histogram, waveform... static void livev_lopriority_task( void* unused ) { msleep(500); TASK_LOOP { #ifdef FEATURE_CROPMARKS #ifdef FEATURE_GHOST_IMAGE if (transparent_overlay_flag) { transparent_overlay_from_play(); transparent_overlay_flag = 0; } #endif // here, redrawing cropmarks does not block fast zoom if (crop_enabled && cropmarks_play && PLAY_MODE && DISPLAY_IS_ON && (int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) <= 0) { msleep(500); if (PLAY_MODE && DISPLAY_IS_ON && ((int32_t)(int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) <= 0)) // double-check { cropmark_redraw(); if ((int32_t)(int32_t)MEM(IMGPLAY_ZOOM_LEVEL_ADDR) >= 0) redraw(); // whoops, CTRL-Z, CTRL-Z :) } } #endif loprio_sleep(); if (!zebra_should_run()) { if (WAVEFORM_FULLSCREEN && liveview_display_idle() && get_global_draw() && !is_zoom_mode_so_no_zebras() && !gui_menu_shown()) { if (get_halfshutter_pressed()) clrscr(); else draw_histogram_and_waveform(0); } continue; } loprio_sleep(); if (!gui_menu_shown()) { draw_histogram_and_waveform(0); } } } #define HIPRIORITY_TASK_PRIO 0x18 TASK_CREATE( "livev_hiprio_task", livev_hipriority_task, 0, HIPRIORITY_TASK_PRIO, 0x4000 ); TASK_CREATE( "livev_loprio_task", livev_lopriority_task, 0, 0x1f, 0x8000 ); // these may be out of order for config compatibility void update_disp_mode_bits_from_params() { //~ BMP_LOCK( uint32_t bits = (global_draw & 1 ? 1<<0 : 0) | (zebra_draw ? 1<<1 : 0) | #ifdef FEATURE_HISTOGRAM (hist_draw ? 1<<2 : 0) | #endif (crop_enabled ? 1<<3 : 0) | (waveform_draw ? 1<<4 : 0) | (falsecolor_draw ? 1<<5 : 0) | (spotmeter_draw ? 1<<6 : 0) | (global_draw & 2 ? 1<<7 : 0) | (focus_peaking ? 1<<8 : 0) | (zoom_overlay_enabled ? 1<<9 : 0) | (transparent_overlay ? 1<<10: 0) | (electronic_level ? 1<<11: 0) | //~ (defish_preview ? 1<<12: 0) | #ifdef FEATURE_VECTORSCOPE (vectorscope_should_draw() ? 1<<13: 0) | #else 0 | #endif 0; if (disp_mode == 1) disp_mode_a = bits; else if (disp_mode == 2) disp_mode_b = bits; else if (disp_mode == 3) disp_mode_c = bits; else disp_mode_x = bits; //~ ) } void update_disp_mode_params_from_bits() { //~ BMP_LOCK( uint32_t bits = disp_mode == 1 ? disp_mode_a : disp_mode == 2 ? disp_mode_b : disp_mode == 3 ? disp_mode_c : disp_mode_x; int global_draw_0 = bits & (1<<0) ? 1 : 0; zebra_draw = bits & (1<<1) ? 1 : 0; #ifdef FEATURE_HISTOGRAM hist_draw = bits & (1<<2) ? 1 : 0; #endif crop_enabled = bits & (1<<3) ? 1 : 0; waveform_draw = bits & (1<<4) ? 1 : 0; falsecolor_draw = bits & (1<<5) ? 1 : 0; spotmeter_draw = bits & (1<<6) ? 1 : 0; int global_draw_1 = bits & (1<<7) ? 1 : 0; focus_peaking = bits & (1<<8) ? 1 : 0; zoom_overlay_enabled = bits & (1<<9) ? 1 : 0; transparent_overlay = bits & (1<<10)? 1 : 0; electronic_level = bits & (1<<11)? 1 : 0; //~ defish_preview = bits & (1<<12)? 1 : 0; #ifdef FEATURE_VECTORSCOPE vectorscope_request_draw(bits & (1<<13)? 1 : 0); #endif global_draw = global_draw_0 + global_draw_1 * 2; //~ end: //~ ) } int get_disp_mode() { return disp_mode; } static void toggle_disp_mode_menu(void *priv, int delta) { if (!disp_profiles_0) menu_toggle_submenu(); else toggle_disp_mode(); } int toggle_disp_mode() { update_disp_mode_bits_from_params(); idle_wakeup_reset_counters(-3); disp_mode = MOD(disp_mode + 1, disp_profiles_0 + 1); BMP_LOCK( do_disp_mode_change(); ) //~ menu_set_dirty(); return disp_mode == 0; } static void do_disp_mode_change() { if (gui_menu_shown()) { update_disp_mode_params_from_bits(); return; } display_on(); bmp_on(); clrscr(); idle_globaldraw_dis(); //~ redraw(); bmp_printf(SHADOW_FONT(FONT_LARGE), 50, 50, "Display preset: %d", disp_mode); msleep(250); idle_globaldraw_en(); update_disp_mode_params_from_bits(); redraw(); } int handle_disp_preset_key(struct event * event) { // the INFO key may be also used for enabling powersaving right away // if display presets are off: pressing INFO will go to powersave (if any of those modes are enabled) // if display presets are on: powersave will act somewhat like an extra display preset if (event->param == BGMT_INFO) { if (!disp_profiles_0) return handle_powersave_key(event); if (!lv && !LV_PAUSED) return 1; if (IS_FAKE(event)) return 1; if (gui_menu_shown()) return 1; if (idle_is_powersave_enabled_on_info_disp_key()) { if (disp_mode == disp_profiles_0 && !idle_is_powersave_active()) return handle_powersave_key(event); else toggle_disp_mode(); // and wake up from powersave } else { toggle_disp_mode(); } return 0; } return 1; } #ifdef FEATURE_OVERLAYS_IN_PLAYBACK_MODE static int overlays_playback_displayed = 0; static void overlays_playback_clear() { if (overlays_playback_displayed) { clrscr(); digic_zebra_cleanup(); redraw(); overlays_playback_displayed = 0; } } /* called from GUI handler */ static void overlays_playback_toggle() { if (overlays_playback_running) return; if (!overlays_playback_displayed) { /* this may take about 1 second, so let's run it outside GuiMainTask */ overlays_playback_running = 1; task_create("lv_playback", 0x1a, 0x8000, draw_overlays_playback, 0); overlays_playback_displayed = 1; } else { overlays_playback_clear(); } } int handle_overlays_playback(struct event * event) { // enable LiveV stuff in Play mode if (PLAY_OR_QR_MODE) { switch(event->param) { #if defined(BTN_ZEBRAS_FOR_PLAYBACK) && defined(BTN_ZEBRAS_FOR_PLAYBACK_NAME) case BTN_ZEBRAS_FOR_PLAYBACK: /* used in PLAY mode (user pressed button to toggle overlays) */ overlays_playback_toggle(); return 0; #endif case MLEV_TRIGGER_ZEBRAS_FOR_PLAYBACK: /* used in QuickReview mode - always show the overlays, no toggle */ overlays_playback_displayed = 0; overlays_playback_toggle(); return 0; } if (event->param == GMT_OLC_INFO_CHANGED) return 1; #ifdef GMT_GUICMD_PRESS_BUTTON_SOMETHING else if (event->param == GMT_GUICMD_PRESS_BUTTON_SOMETHING) return 1; #endif else { /* some button pressed in play mode, while ML overlays are active? clear them */ overlays_playback_clear(); } } else { /* got out of play mode? ML overlays are for sure no longer active */ overlays_playback_displayed = 0; } return 1; } #endif static void zebra_init() { precompute_yuv2rgb(); menu_add( "Overlay", zebra_menus, COUNT(zebra_menus) ); menu_add( "Debug", livev_dbg_menus, COUNT(livev_dbg_menus) ); //~ menu_add( "Movie", movie_menus, COUNT(movie_menus) ); //~ menu_add( "Config", cfg_menus, COUNT(cfg_menus) ); #ifdef FEATURE_CROPMARKS menu_add( "Overlay", cropmarks_menu, COUNT(cropmarks_menu) ); #endif } INIT_FUNC(__FILE__, zebra_init); static void make_overlay() { //~ draw_cropmark_area(); msleep(1000); //~ bvram_mirror_init(); clrscr(); bmp_printf(FONT_MED, 0, 0, "Saving overlay..."); struct vram_info * vram = get_yuv422_vram(); uint8_t * const lvram = vram->vram; if (!lvram) return; uint8_t * const bvram = bmp_vram(); if (!bvram) return; // difficulty: in play mode, image buffer may have different size/position than in LiveView // => normalized xn and yn will fix this for (int yn = 0; yn < 480; yn++) { int y = N2BM_Y(yn); //~ int k; uint16_t * const v_row = (uint16_t*)( lvram + BM2LV_R(y)); // 1 pixel uint8_t * const b_row = (uint8_t*) ( bvram + BM_R(y)); // 1 pixel uint8_t * const m_row = (uint8_t*) ( bvram_mirror + BM_R(yn)); // 1 pixel uint16_t* lvp; // that's a moving pointer through lv vram uint8_t* bp; // through bmp vram uint8_t* mp; // through bmp vram mirror for (int xn = 0; xn < 720; xn++) { int x = N2BM_X(xn); lvp = v_row + BM2LV_X(x); bp = b_row + x; mp = m_row + xn; *bp = *mp = ((*lvp) * 41 >> 16) + 38; } } FILE* f = FIO_CreateFile("ML/DATA/overlay.dat"); if (f) { FIO_WriteFile( f, (const void *) UNCACHEABLE(bvram_mirror), BVRAM_MIRROR_SIZE); FIO_CloseFile(f); bmp_printf(FONT_MED, 0, 0, "Overlay saved. "); } else { bmp_printf(FONT_MED, 0, 0, "Overlay error. "); } msleep(1000); } static void show_overlay() { //~ bvram_mirror_init(); //~ struct vram_info * vram = get_yuv422_vram(); //~ uint8_t * const lvram = vram->vram; //~ int lvpitch = YUV422_LV_PITCH; get_yuv422_vram(); uint8_t * const bvram = bmp_vram_real(); if (!bvram) return; clrscr(); FILE* f = FIO_OpenFile("ML/DATA/overlay.dat", O_RDONLY | O_SYNC); if (!f) return; FIO_ReadFile(f, bvram_mirror, 960*480 ); FIO_CloseFile(f); for (int y = os.y0; y < os.y_max; y++) { int yn = BM2N_Y(y); int ym = yn - (int)transparent_overlay_offy; // normalized with offset applied //~ int k; //~ uint16_t * const v_row = (uint16_t*)( lvram + y * lvpitch ); // 1 pixel uint8_t * const b_row = (uint8_t*)( bvram + y * BMPPITCH); // 1 pixel uint8_t * const m_row = (uint8_t*)( bvram_mirror + ym * BMPPITCH); // 1 pixel uint8_t* bp; // through bmp vram uint8_t* mp; //through bmp vram mirror if (ym < 0 || ym > 480) continue; //~ int offm = 0; //~ int offb = 0; //~ if (transparent_overlay == 2) offm = 720/2; //~ if (transparent_overlay == 3) offb = 720/2; for (int x = os.x0; x < os.x_max; x++) { int xn = BM2N_X(x); int xm = xn - (int)transparent_overlay_offx; bp = b_row + x; mp = m_row + xm; if (((x+y) % 2) && xm >= 0 && xm <= 720) *bp = *mp; } } bvram_mirror_clear(); afframe_clr_dirty(); } static void transparent_overlay_from_play() { /* go to play mode if not already there */ enter_play_mode(); /* create overlay from current image */ make_overlay(); /* go to LiveView */ force_liveview(); /* the overlay will now be displayed from cropmarks.c */ } PROP_HANDLER(PROP_LV_ACTION) { zoom_overlay_triggered_by_focus_ring_countdown = 0; idle_globaldraw_disable = 0; if (buf[0] == 0) lv_paused = 0; #ifdef FEATURE_EXPO_OVERRIDE bv_auto_update(); #endif #ifdef FEATURE_LV_ZOOM_SETTINGS zoom_sharpen_step(); #endif } void peaking_benchmark() { int old_lv = lv; int old_peaking = focus_peaking; focus_peaking = 1; msleep(1000); fake_simple_button(BGMT_PLAY); msleep(2000); int a = get_seconds_clock(); lv = 1; // lie, to force using the liveview algorithm which is relevant for benchmarking for (int i = 0; i < 1000; i++) { draw_zebra_and_focus(0,1); } int b = get_seconds_clock(); NotifyBox(10000, "%d seconds => %d fps", b-a, 1000 / (b-a)); beep(); lv = old_lv; focus_peaking = old_peaking; }