/** \file * Lens focus and zoom related things */ /* * 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 "dryos.h" #include "lens.h" #include "property.h" #include "bmp.h" #include "config.h" #include "menu.h" #include "math.h" #include "version.h" #include "module.h" #include "raw.h" #include "zebra.h" #include "cropmarks.h" #include "battery.h" #include "lens.h" #include "shoot.h" #include "hdr.h" #include "fps.h" #include "picstyle.h" #include "focus.h" #include "lvinfo.h" #include "powersave.h" // for movie logging static char* mvr_logfile_buffer = 0; /* delay to be waited after mirror is locked */ CONFIG_INT("mlu.lens.delay", lens_mlu_delay, 7); static void update_stuff(); //~ extern struct semaphore * bv_sem; void bv_update_lensinfo(); void bv_auto_update(); static void lensinfo_set_aperture(int raw); static void bv_expsim_shift(); static CONFIG_INT("movie.log", movie_log, 0); #ifdef CONFIG_FULLFRAME #define SENSORCROPFACTOR 10 #define crop_info 0 #elif defined(CONFIG_600D) static PROP_INT(PROP_DIGITAL_ZOOM_RATIO, digital_zoom_ratio); #define DIGITAL_ZOOM ((is_movie_mode() && video_mode_crop && video_mode_resolution == 0) ? digital_zoom_ratio : 100) #define SENSORCROPFACTOR (16 * DIGITAL_ZOOM / 100) CONFIG_INT("crop.info", crop_info, 0); #else #define SENSORCROPFACTOR 16 CONFIG_INT("crop.info", crop_info, 0); #endif #define FOCUS_UNITS_METRIC 0 #define FOCUS_UNITS_IMPERIAL 1 CONFIG_INT("focus.units", focus_units, FOCUS_UNITS_METRIC); //~ static struct semaphore * lens_sem; static struct semaphore * focus_done_sem; //~ static struct semaphore * job_sem; struct lens_info lens_info = { .name = "NO LENS NAME" }; const char * lens_format_dist( unsigned mm) { static char dist[ 32 ]; if( mm > 100000 ) //100 m { snprintf( dist, sizeof(dist), SYM_INFTY); } else if(focus_units == FOCUS_UNITS_IMPERIAL) { int inches = (mm * 10 / 254); if( inches > 24 ) // 2 ft { snprintf( dist, sizeof(dist), "%dft", (inches + 6) / 12); //+6 to round properly } else { snprintf( dist, sizeof(dist),"%din", inches); } } else { if ( mm >= 10000 ) // 10 m { snprintf( dist, sizeof(dist), "%d"SYM_SMALL_M, mm / 1000); } else if( mm >= 1000 ) // 1 m { int meters_x100 = mm / 10; snprintf( dist, sizeof(dist), "%s%d.%02d"SYM_SMALL_M, FMT_FIXEDPOINT2(meters_x100)); } else { snprintf( dist, sizeof(dist),"%d"SYM_SMALL_C SYM_SMALL_M, mm / 10 ); } } return (dist); } /* end of aj_lens_format_dist() */ void update_lens_display(int top, int bottom) { if (top) draw_ml_topbar(); if (bottom) draw_ml_bottombar(); //~ lvinfo_display(top, bottom); //~ info_print_screen(); } int should_draw_bottom_bar() { if (gui_menu_shown()) return 1; if (!get_global_draw()) return 0; //~ if (EXT_MONITOR_CONNECTED) return 1; if (canon_gui_front_buffer_disabled()) return 1; if (is_canon_bottom_bar_dirty()) { crop_set_dirty(5); afframe_set_dirty(); return 0; } if (lv_disp_mode == 0) return 1; return 0; } int raw2shutter_ms(int raw_shutter) { if (!raw_shutter) return 0; return (int) roundf(powf(2.0, (56.0f - raw_shutter)/8.0f) * 1000.0f); } int shutter_ms_to_raw(int shutter_ms) { if (shutter_ms == 0) return 160; return (int) roundf(56.0f - log2f((float)shutter_ms / 1000.0f) * 8.0f); } int shutterf_to_raw(float shutterf) { if (shutterf == 0) return 160; return (int) roundf(56.0f - log2f(shutterf) * 8.0f); } float raw2shutterf(int raw_shutter) { if (!raw_shutter) return 0.0; return powf(2.0, (56.0 - raw_shutter)/8.0); } int raw2iso(int raw_iso) { int iso = (int) roundf(100.0f * powf(2.0f, (raw_iso - 72.0f)/8.0f)); if (iso >= 100 && iso <= 6400) iso = values_iso[raw2index_iso(raw_iso)]; else if (raw_iso == 123) iso = 8000; else if (raw_iso == 125) iso = 10000; else if (raw_iso == 131) iso = 16000; else if (raw_iso == 133) iso = 20000; else if (iso > 100000) iso = ((iso+500)/1000) * 1000; else if (iso > 10000) iso = ((iso+50)/100) * 100; else if (iso >= 70 && iso < 80) iso = ((iso+5)/10) * 10; else if (iso >= 15) iso = ((iso+2)/5) * 5; else if (iso > 5) iso = iso&~1; return iso; } int expo_value_rounding_ok(int raw, int is_aperture) { if (is_aperture) { /* exception: aperture limits are allowed, even if they are not multiples of 1/2 or 1/3 EV */ if (raw == lens_info.raw_aperture_min || raw == lens_info.raw_aperture_max) return 1; } int r = ABS(raw) % 8; if (r != 0 && r != 3 && r != 4 && r != 5) return 0; return 1; } int round_shutter(int tv, int slowest_shutter) { int tvr; tv = MIN(tv, FASTEST_SHUTTER_SPEED_RAW); /* note: it's possible to get a valid shutter just by altering the requested value by 1 */ /* ... unless we hit some limits */ tvr = MAX(tv , slowest_shutter); if (expo_value_rounding_ok(tvr, 0)) return tvr; tvr = MAX(tv - 1, slowest_shutter); if (expo_value_rounding_ok(tvr, 0)) return tvr; tvr = MAX(tv + 1, slowest_shutter); if (expo_value_rounding_ok(tvr, 0)) return tvr; tvr = MAX(tv - 2, slowest_shutter); if (expo_value_rounding_ok(tvr, 0)) return tvr; tvr = MAX(tv + 2, slowest_shutter); if (expo_value_rounding_ok(tvr, 0)) return tvr; return 0; } int round_aperture(int av) { int avr; /* note: if we don't hit the limits, we can find a valid aperture just by altering the requested value by 1 */ /* but if we do hit them... surprises may happen :) */ avr = COERCE(av , lens_info.raw_aperture_min, lens_info.raw_aperture_max); if (expo_value_rounding_ok(avr, 1)) return avr; avr = COERCE(av - 1, lens_info.raw_aperture_min, lens_info.raw_aperture_max); if (expo_value_rounding_ok(avr, 1)) return avr; avr = COERCE(av + 1, lens_info.raw_aperture_min, lens_info.raw_aperture_max); if (expo_value_rounding_ok(avr, 1)) return avr; avr = COERCE(av - 2, lens_info.raw_aperture_min, lens_info.raw_aperture_max); if (expo_value_rounding_ok(avr, 1)) return avr; avr = COERCE(av + 2, lens_info.raw_aperture_min, lens_info.raw_aperture_max); if (expo_value_rounding_ok(avr, 1)) return avr; return 0; } int round_expo_comp(int ae) { int aer; /* note: it's possible to get a valid exposure compensation just by altering the requested value by 1 */ aer = COERCE(ae , -MAX_AE_EV * 8, MAX_AE_EV * 8); if (expo_value_rounding_ok(aer, 0)) return aer; aer = COERCE(ae - 1, -MAX_AE_EV * 8, MAX_AE_EV * 8); if (expo_value_rounding_ok(aer, 0)) return aer; aer = COERCE(ae + 1, -MAX_AE_EV * 8, MAX_AE_EV * 8); if (expo_value_rounding_ok(aer, 0)) return aer; return 0; } int round_flash_expo_comp(int fec) { int fecr; /* note: it's possible to get a valid exposure compensation just by altering the requested value by 1 */ fecr = COERCE(fec , FLASH_MIN_EV * 8, FLASH_MAX_EV * 8); if (expo_value_rounding_ok(fecr, 0)) return fecr; fecr = COERCE(fec - 1, FLASH_MIN_EV * 8, FLASH_MAX_EV * 8); if (expo_value_rounding_ok(fecr, 0)) return fecr; fecr = COERCE(fec + 1, FLASH_MIN_EV * 8, FLASH_MAX_EV * 8); if (expo_value_rounding_ok(fecr, 0)) return fecr; return 0; } char* get_shootmode_name(int shooting_mode) { return is_movie_mode() ? ( shooting_mode == SHOOTMODE_C ? "MovieC1" : shooting_mode == SHOOTMODE_C2 ? "MovieC2" : shooting_mode == SHOOTMODE_C3 ? "MovieC3" : "Movie" ) : shooting_mode == SHOOTMODE_P ? "P" : shooting_mode == SHOOTMODE_M ? "M" : shooting_mode == SHOOTMODE_TV ? "Tv" : shooting_mode == SHOOTMODE_AV ? "Av" : shooting_mode == SHOOTMODE_CA ? "CA" : shooting_mode == SHOOTMODE_AP ? "A+" : shooting_mode == SHOOTMODE_ADEP ? "ADEP" : shooting_mode == SHOOTMODE_AUTO ? "Auto" : shooting_mode == SHOOTMODE_LANDSCAPE ? "Landscape" : shooting_mode == SHOOTMODE_PORTRAIT ? "Portrait" : shooting_mode == SHOOTMODE_NOFLASH ? "NoFlash" : shooting_mode == SHOOTMODE_MACRO ? "Macro" : shooting_mode == SHOOTMODE_SPORTS ? "Sports" : shooting_mode == SHOOTMODE_NIGHT ? "Night" : shooting_mode == SHOOTMODE_NIGHTH ? "Night Handheld" : shooting_mode == SHOOTMODE_HDR ? "HDR Backlight" : shooting_mode == SHOOTMODE_BULB ? "Bulb" : shooting_mode == SHOOTMODE_C ? "C1" : shooting_mode == SHOOTMODE_C2 ? "C2" : shooting_mode == SHOOTMODE_C3 ? "C3" : "Unknown"; } char* get_shootmode_name_short(int shooting_mode) { return is_movie_mode() ? ( shooting_mode == SHOOTMODE_C ? "Mv1" : shooting_mode == SHOOTMODE_C2 ? "Mv2" : shooting_mode == SHOOTMODE_C3 ? "Mv3" : "Mv" ) : shooting_mode == SHOOTMODE_P ? "P" : shooting_mode == SHOOTMODE_M ? "M" : shooting_mode == SHOOTMODE_TV ? "Tv" : shooting_mode == SHOOTMODE_AV ? "Av" : shooting_mode == SHOOTMODE_CA ? "CA" : shooting_mode == SHOOTMODE_AP ? "A+" : shooting_mode == SHOOTMODE_ADEP ? "AD" : shooting_mode == SHOOTMODE_AUTO ? "[]" : shooting_mode == SHOOTMODE_LANDSCAPE ? "LD" : shooting_mode == SHOOTMODE_PORTRAIT ? ":)" : shooting_mode == SHOOTMODE_NOFLASH ? "NF" : shooting_mode == SHOOTMODE_MACRO ? "MC" : shooting_mode == SHOOTMODE_SPORTS ? "SP" : shooting_mode == SHOOTMODE_NIGHT ? "NI" : shooting_mode == SHOOTMODE_NIGHTH ? "NH" : shooting_mode == SHOOTMODE_HDR ? "HB" : shooting_mode == SHOOTMODE_BULB ? "B" : shooting_mode == SHOOTMODE_C ? "C1" : shooting_mode == SHOOTMODE_C2 ? "C2" : shooting_mode == SHOOTMODE_C3 ? "C3" : "?" ; } int FAST get_ml_topbar_pos() { const int bar_height = 32; int bmp_ymax = (hdmi_code >= 5) ? 510 : 480; if (gui_menu_shown()) { return (hdmi_code >= 5) ? 40 : 2; // force it at the top of menu } else { int screen_layout = get_screen_layout(); switch (screen_layout) { case SCREENLAYOUT_16_9: return os.y0 + os.off_169 + 2; // meters just below 16:9 border case SCREENLAYOUT_16_10: return os.y0 + os.off_1610 + 2; // meters just below 16:9 border case SCREENLAYOUT_UNDER_3_2: return MIN(os.y_max + 2, bmp_ymax - 2*bar_height); case SCREENLAYOUT_UNDER_16_9: return MIN(os.y_max - os.off_169 + 4, bmp_ymax - 2*bar_height); default: return os.y0 + 2; // just above the 16:9 frame } } } int FAST get_ml_bottombar_pos() { const int bar_height = 32; if (gui_menu_shown()) { return 480 + (hdmi_code >= 5 ? 40 : 0) - bar_height; // force it at the bottom of menu } else { int screen_layout = get_screen_layout(); switch (screen_layout) { case SCREENLAYOUT_16_9: return os.y_max - os.off_169 - bar_height; case SCREENLAYOUT_16_10: return os.y_max - os.off_1610 - bar_height; case SCREENLAYOUT_UNDER_3_2: case SCREENLAYOUT_UNDER_16_9: return get_ml_topbar_pos() + bar_height; default: return os.y_max - bar_height - 2; // just above the 16:9 frame } } } void draw_ml_bottombar() { if (!should_draw_bottom_bar()) return; lvinfo_display(0,1); } static int round_nicely(int x, int digits) { int x0 = x; /* round X to N significant digits */ /* e.g. 1234 rounded to 2 digits => 1200 */ int thr = powi(10, digits); if (x < thr/10) return x; int f = 1; while (x >= thr) { if ((x + 2) / 5 == 25) { /* exception: allow 125, 1250 and so on, because Canon does it as well */ x = (x + 2) / 5; f *= 5; } else { x = (x + 5) / 10; f *= 10; } } /* re-round to cancel accumulated errors, if any */ x = (x0 + f/2) / f; /* avoid ending in odd digits if the number is large (e.g. allow 11, 13, 19, 21, but don't allow 61 or 79) */ int last_digit = x % 10; int next_digit = (x / 10) % 10; if ((last_digit == 1 || last_digit == 3 || last_digit == 7 || last_digit == 9) && next_digit >= 5) { f *= 2; x = (x0 + f/2) / f; } return x * f; } // Pretty prints the shutter speed given the shutter reciprocal (times 1000) as input // To be used in movie mode; it doesn't try too hard to be consistent with Canon values const char * lens_format_shutter_reciprocal(int shutter_reciprocal_x1000, int digits) { static char shutter[32]; if (shutter_reciprocal_x1000 == 0) { snprintf(shutter, sizeof(shutter), "N/A"); } else if (shutter_reciprocal_x1000 >= 10000000) { snprintf(shutter, sizeof(shutter), SYM_1_SLASH"%dK", (shutter_reciprocal_x1000+500000)/1000000); } else if (shutter_reciprocal_x1000 > 24000) { int shutter_rounded = round_nicely(shutter_reciprocal_x1000, digits); if (digits <= 2) { snprintf(shutter, sizeof(shutter), SYM_1_SLASH"%d", shutter_rounded/1000); } else { /* todo: compute how many digits should be after the decimal point? */ shutter_rounded = (shutter_rounded + 5) / 10; snprintf(shutter, sizeof(shutter), SYM_1_SLASH"%s%d.%02d", FMT_FIXEDPOINT2(shutter_rounded)); } } else if (shutter_reciprocal_x1000 > 3000) { snprintf(shutter, sizeof(shutter), SYM_1_SLASH"%d", (shutter_reciprocal_x1000+500)/1000); } else { int shutter_x10 = (100000/shutter_reciprocal_x1000+5)/10; if (shutter_x10 % 10 && shutter_x10 < 40) snprintf(shutter, sizeof(shutter), "%d.%d\"", shutter_x10 / 10, shutter_x10 % 10); else snprintf(shutter, sizeof(shutter), "%d\"", (shutter_x10+5) / 10); } return shutter; } // Pretty prints the shutter speed given the raw shutter value as input // To be used in photo mode; it will try to be somewhat consistent with Canon values const char * lens_format_shutter(int raw_shutter) { static char shutter[16]; if(raw_shutter >= 70 && raw_shutter - 15 < COUNT(values_shutter)) { snprintf(shutter, sizeof(shutter), SYM_1_SLASH"%d", values_shutter[raw_shutter-15]); } else if(raw_shutter >= 15 && raw_shutter < 70) { uint16_t value = values_shutter[raw_shutter-15]; if(value % 10 != 0) { snprintf(shutter, sizeof(shutter), "%d.%d\"", value / 10, value % 10); } else { snprintf(shutter, sizeof(shutter), "%d\"", value / 10); } } else if (raw_shutter == SHUTTER_BULB) { snprintf(shutter, sizeof(shutter), "BULB"); } else { //this should never happen, but if it does, just print the raw value snprintf(shutter, sizeof(shutter), "RAW:%d", raw_shutter); } return shutter; } const char * lens_format_aperture(int raw_aperture) { int f = RAW2VALUE(aperture, raw_aperture); static char aperture[16]; if (f < 100) { snprintf(aperture, sizeof(aperture), SYM_F_SLASH"%d.%d", f / 10, f % 10); } else { snprintf(aperture, sizeof(aperture), SYM_F_SLASH"%d", f / 10); } return aperture; } const char * lens_format_iso(int raw_iso) { static char iso[16]; if (raw_iso) { snprintf(iso, sizeof(iso), SYM_ISO"%d", raw2iso(raw_iso)); } else { snprintf(iso, sizeof(iso), SYM_ISO"Auto"); } return iso; } void free_space_show_photomode() { int free_space_32k = get_free_space_32k(get_shooting_card()); int fsg = free_space_32k >> 15; int fsgr = free_space_32k - (fsg << 15); int fsgf = (fsgr * 10) >> 15; int time_indic_x = 720 - 160; int x = time_indic_x + 2 * font_med.width; int y = 452; bmp_printf( FONT(SHADOW_FONT(FONT_LARGE), COLOR_FG_NONLV, bmp_getpixel(x-10,y+10)), x, y, "%d.%dGB", fsg, fsgf ); } void draw_ml_topbar() { if (!get_global_draw()) return; lvinfo_display(1,0); } static volatile int lv_focus_requests = 0; static volatile int lv_focus_done = 1; static volatile int lv_focus_error = 0; PROP_HANDLER( PROP_LV_FOCUS_DONE ) { /* turn off the LED we enabled in lens_focus */ info_led_off(); lv_focus_requests = 0; //~ bmp_printf(FONT_MED, 50, 100, "Focus status: 0x%x ", buf[0]); static int last_pos = 0; static int retries = 2; int error_flag = buf[0] & 0x1000; int focus_changed = last_pos != lens_info.focus_pos; int lens_stuck = error_flag && !focus_changed; if (lens_stuck) { printf("Lens stuck? (%d, %x)\n", retries, buf[0]); } else { printf("Lens moving (%d, %x)\n", lens_info.focus_pos - last_pos, buf[0]); } if (lens_stuck && retries == 0) { /* only trigger the error if the lens did not move at all * after 2 retries */ NotifyBox(1000, "Focus: soft limit reached"); lv_focus_error = 1; /* assume the error was handled (e.g. by reversing direction) * and allow 2 retries for the next attempt */ retries = 2; } else { /* assume all is fine (not sure if correct, but seems to work) */ lv_focus_done = 1; if (lens_stuck) { retries--; } else { retries = 2; } } last_pos = lens_info.focus_pos; } static void lens_focus_wait(void) { for (int i = 0; i < 100; i++) { if (lv_focus_done) return; if (lv_focus_error) return; if (!lv) return; if (is_manual_focus()) return; msleep(10); } NotifyBox(1000, "Focus not confirmed"); lv_focus_error = 1; } // this is compatible with all cameras so far, but allows only 3 speeds int lens_focus( int num_steps, int stepsize, int wait, int extra_delay ) { lv_focus_done = 0; lv_focus_error = 0; if (!lv) return 0; if (is_manual_focus()) return 0; if (num_steps < 0) { num_steps = -num_steps; stepsize = -stepsize; } stepsize = COERCE(stepsize, -3, 3); int focus_cmd = stepsize; if (stepsize < 0) focus_cmd = 0x8000 - stepsize; for (int i = 0; i < num_steps; i++) { if (lv && !mirror_down && lens_info.job_state == 0) { if (wait) { lv_focus_done = 0; info_led_on(); #ifdef CONFIG_FOCUS_COMMANDS_PROP_NOT_CONFIRMED /* in old models, each focus command is confirmed by pfAfComplete interrupt */ /* it's not safe to send commands before that (camera crashes) */ /* properties are not confirmed, so prop_request_change_wait would time out */ /* not all cameras having this string require this though (550D, maybe 7D as well) */ /* todo: VxWorks cameras may require this too */ extern volatile int pfAfComplete_counter; int old = pfAfComplete_counter; prop_request_change(PROP_LV_LENS_DRIVE_REMOTE, &focus_cmd, 4); while (pfAfComplete_counter == old) { msleep(10); } #else /* request and wait for confirmation */ prop_request_change_wait(PROP_LV_LENS_DRIVE_REMOTE, &focus_cmd, 4, 1000); /* also wait for confirmation from PROP_LV_FOCUS_DONE */ lens_focus_wait(); #endif /* also wait a little more if user want so (for really stubborn lenses) */ if (extra_delay) { msleep(extra_delay); } } else { /* keep sending focus commands, but not more than 3 * from the last PROP_LV_FOCUS_DONE event * this fixes some crashes that appear to be caused * by filling Canon's focus request queue with our junk * * (not sure what their buffer size is, but with 5, it crashes) */ lv_focus_requests++; if (lv_focus_requests < 3) { info_led_on(); prop_request_change(PROP_LV_LENS_DRIVE_REMOTE, &focus_cmd, 4); } /* open-loop delay, without waiting for confirmation; at least 10ms */ msleep(MAX(10, extra_delay)); } } } #ifdef FEATURE_MAGIC_ZOOM if (get_zoom_overlay_trigger_by_focus_ring()) zoom_overlay_set_countdown(300); #endif idle_wakeup_reset_counters(-10); lens_display_set_dirty(); /* return 1 on success, 0 on error */ return lv_focus_error ? 0 : 1; } void lens_wait_readytotakepic(int wait) { int i; for (i = 0; i < wait * 20; i++) { if (ml_shutdown_requested) return; if (sensor_cleaning) { msleep(50); continue; } if (shooting_mode == SHOOTMODE_M && lens_info.raw_shutter == 0) { msleep(50); continue; } if (job_state_ready_to_take_pic() && burst_count > 0 && ((icu_uilock & 0xFF) == 0)) break; msleep(50); if (NOT_RECORDING) info_led_on(); } if (NOT_RECORDING) info_led_off(); } static int mirror_locked = 0; int mlu_lock_mirror_if_needed() // called by lens_take_picture; returns 0 if success, 1 if camera took a picture instead of locking mirror { #ifdef CONFIG_5DC if (get_mlu()) set_mlu(0); // can't trigger shutter with MLU active, so just turn it off return 0; #endif if (drive_mode == DRIVE_SELFTIMER_2SEC || drive_mode == DRIVE_SELFTIMER_REMOTE || drive_mode == DRIVE_SELFTIMER_CONTINUOUS) return 0; if (get_mlu() && CURRENT_GUI_MODE) { SetGUIRequestMode(0); int iter = 20; while (iter-- && !display_idle()) msleep(50); msleep(500); } //~ NotifyBox(1000, "MLU locking"); if (get_mlu() && !lv) { if (!mirror_locked) { int fn = get_shooting_card()->file_number; #if defined(CONFIG_5D2) || defined(CONFIG_50D) SW1(1,50); SW2(1,250); SW2(0,50); SW1(0,50); #elif defined(CONFIG_40D) call("FA_Release"); #else call("Release"); #endif msleep(500); if (get_shooting_card()->file_number != fn) // Heh... camera took a picture instead. Cool. return 1; if (lv) // we have somehow got into LiveView, where MLU does nothing... so, no need to wait return 0; mirror_locked = 1; msleep(MAX(0, get_mlu_delay(lens_mlu_delay) - 500)); } } //~ NotifyBox(1000, "MLU locked"); return 0; } #define AF_BUTTON_NOT_MODIFIED 100 static int orig_af_button_assignment = AF_BUTTON_NOT_MODIFIED; // to preview AF patterns void assign_af_button_to_halfshutter() { if (ml_shutdown_requested) return; if (orig_af_button_assignment == AF_BTN_HALFSHUTTER) return; //~ take_semaphore(lens_sem, 0); lens_wait_readytotakepic(64); if (ml_shutdown_requested) return; if (orig_af_button_assignment == AF_BUTTON_NOT_MODIFIED) orig_af_button_assignment = cfn_get_af_button_assignment(); cfn_set_af_button(AF_BTN_HALFSHUTTER); //~ give_semaphore(lens_sem); } // to prevent AF void assign_af_button_to_star_button() { if (ml_shutdown_requested) return; if (orig_af_button_assignment == AF_BTN_STAR) return; //~ take_semaphore(lens_sem, 0); lens_wait_readytotakepic(64); if (ml_shutdown_requested) return; if (orig_af_button_assignment == AF_BUTTON_NOT_MODIFIED) orig_af_button_assignment = cfn_get_af_button_assignment(); cfn_set_af_button(AF_BTN_STAR); //~ give_semaphore(lens_sem); } void restore_af_button_assignment() { if (orig_af_button_assignment != AF_BUTTON_NOT_MODIFIED) orig_af_button_assignment = COERCE(orig_af_button_assignment, 0, 10); // just in case, so we don't read invalid values from config file if (orig_af_button_assignment == AF_BUTTON_NOT_MODIFIED) return; //~ take_semaphore(lens_sem, 0); lens_wait_readytotakepic(64); cfn_set_af_button(orig_af_button_assignment); msleep(100); if (cfn_get_af_button_assignment() == (int)orig_af_button_assignment) orig_af_button_assignment = AF_BUTTON_NOT_MODIFIED; // success //~ give_semaphore(lens_sem); } // keep retrying until it succeeds, or until the 3-second timeout expires void restore_af_button_assignment_at_shutdown() { for (int i = 0; i < 30; i++) { if (orig_af_button_assignment == AF_BUTTON_NOT_MODIFIED) break; restore_af_button_assignment(); info_led_blink(1,50,50); } } /* also used in bulb_take_pic */ volatile int ml_taking_pic = 0; int lens_setup_af(int should_af) { ASSERT(should_af != AF_DONT_CHANGE); if (!is_manual_focus()) { if (should_af == AF_ENABLE) assign_af_button_to_halfshutter(); else if (should_af == AF_DISABLE) assign_af_button_to_star_button(); else return 0; return 1; } return 0; } void lens_cleanup_af() { restore_af_button_assignment(); } /* please try to call take_a_pic() instead of this one */ int lens_take_picture( int wait_to_finish, int should_af ) { if (ml_taking_pic) { return -1; } ml_taking_pic = 1; printf("[LENS] taking picture @ %s %s %s %s\n", lens_format_iso(lens_info.raw_iso), lens_format_shutter(lens_info.raw_shutter), lens_format_aperture(lens_info.raw_aperture), should_af == AF_ENABLE ? "AF" : should_af == AF_DISABLE ? "no AF" : "" ); int file_number_before = get_shooting_card()->file_number; if (should_af != AF_DONT_CHANGE) { lens_setup_af(should_af); } //~ take_semaphore(lens_sem, 0); lens_wait_readytotakepic(64); // in some cases, the MLU setting is ignored; if ML can't detect this properly, this call will actually take a picture // if it happens (e.g. with LV active, but camera in QR mode), that's it, we won't try taking another one // side effects should be minimal #if defined(CONFIG_EOSM) call("Release"); //EOSM is mirrorless no need to check for MLU goto end; #else int took_pic = mlu_lock_mirror_if_needed(); if (took_pic) goto end; #endif #if defined(CONFIG_5D2) || defined(CONFIG_50D) if (get_mlu()) { SW1(1,50); SW2(1,250); SW2(0,50); SW1(0,50); } else { #ifdef CONFIG_5D2 int status = 0; PtpDps_remote_release_SW1_SW2_worker(&status); #else call("Release"); #endif } #elif defined(CONFIG_5DC) call("rssRelease"); #elif defined(CONFIG_40D) call("FA_Release"); #else call("Release"); #endif #if defined(CONFIG_7D) /* on EOS 7D the code to trigger SW1/SW2 is buggy that the metering somehow locks up when exposure time is >1.x seconds. * This causes the camera not to shut down when the card door is opened. * There is a workaround: Just wait until shooting is possible again and then reset SW1. * Then the camera will shut down clean. */ lens_wait_readytotakepic(64); SW1(0,0); #endif end:; /* always wait for the photo capture process to start */ /* additional delays given by drive mode? */ switch (drive_mode) { case DRIVE_SELFTIMER_2SEC: msleep(2000); break; case DRIVE_SELFTIMER_REMOTE: case DRIVE_SELFTIMER_CONTINUOUS: msleep(10000); break; } /* wait until job_state becomes valid, i.e. exposure started (timeout 2 seconds) */ for (int i = 0; i < 100 && lens_info.job_state == 0; i++) { msleep(20); } int ret = 0; if( !wait_to_finish ) { //~ give_semaphore(lens_sem); goto finish; } else { /* wait until the camera is ready to take a new image */ lens_wait_readytotakepic(wait_to_finish); /* wait until the image file gets saved (timeout 2 seconds) */ for (int i = 0; i < 100 && get_shooting_card()->file_number == file_number_before; i++) { /* reachable? not sure, might be model-dependent */ msleep(20); } //~ give_semaphore(lens_sem); ret = lens_info.job_state; goto finish; } finish: if (should_af != AF_DONT_CHANGE) { lens_cleanup_af(); } ml_taking_pic = 0; return ret; } #ifdef FEATURE_MOVIE_LOGGING /** Write the current lens info into the logfile */ static void mvr_update_logfile( struct lens_info * info, int force ) { if( mvr_logfile_buffer == 0 ) return; static unsigned last_iso; static unsigned last_shutter; static unsigned last_aperture; static unsigned last_focal_len; static unsigned last_focus_dist; static int last_second; // Check if nothing changed and not forced. Do not write. if( !force && last_iso == info->iso && last_shutter == info->shutter && last_aperture == info->aperture && last_focal_len == info->focal_len && last_focus_dist == info->focus_dist ) return; // Don't update more often than once per second if (!force && last_second == get_seconds_clock() ) return; // Record the last settings so that we know if anything changes last_iso = info->iso; last_shutter = info->shutter; last_aperture = info->aperture; last_focal_len = info->focal_len; last_focus_dist = info->focus_dist; last_second = get_seconds_clock(); struct tm now; LoadCalendarFromRTC( &now ); MVR_LOG_APPEND ( "%02d:%02d:%02d,%d,%d,%d.%d,%d,%d\n", now.tm_hour, now.tm_min, now.tm_sec, info->iso, info->shutter, info->aperture / 10, info->aperture % 10, info->focal_len, info->focus_dist ); } /** Create a logfile for each movie. * Record a logfile with the lens info for each movie. */ static void mvr_create_logfile( unsigned event ) { if (!movie_log) return; if( event == 0 ) { // Movie stopped - write the log file char name[100]; snprintf(name, sizeof(name), "%s/MVI_%04d.LOG", get_dcim_dir(), get_shooting_card()->file_number); FILE * mvr_logfile = mvr_logfile = FIO_CreateFile( name ); if (!mvr_logfile) { bmp_printf( FONT_MED, 0, 40, "Unable to create movie log! fd=%x\n%s", (unsigned) mvr_logfile, name ); return; } FIO_WriteFile( mvr_logfile, mvr_logfile_buffer, strlen(mvr_logfile_buffer) ); FIO_CloseFile( mvr_logfile ); fio_free(mvr_logfile_buffer); mvr_logfile_buffer = 0; return; } if( event != 2 ) return; // Movie starting mvr_logfile_buffer = fio_malloc(MVR_LOG_BUF_SIZE); snprintf( mvr_logfile_buffer, MVR_LOG_BUF_SIZE, "# Magic Lantern %s\n\n", build_version ); struct tm now; LoadCalendarFromRTC( &now ); MVR_LOG_APPEND ( "Start : %4d/%02d/%02d %02d:%02d:%02d\n", now.tm_year + 1900, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec ); MVR_LOG_APPEND ( "Lens name : %s\n", lens_info.name ); int sr_x1000 = get_current_shutter_reciprocal_x1000(); MVR_LOG_APPEND ( "ISO : %d%s\n" "Shutter : 1/%d.%03ds\n" "Aperture : f/%d.%d\n" "Focal length : %d mm\n" "Focus distance : %d mm\n", lens_info.iso, get_htp() ? " D+" : "", sr_x1000/1000, sr_x1000%1000, lens_info.aperture / 10, lens_info.aperture % 10, lens_info.focal_len, lens_info.focus_dist * 10 ); MVR_LOG_APPEND ( "White Balance : %d%s, %s %d, %s %d\n", lens_info.wb_mode == WB_KELVIN ? lens_info.kelvin : lens_info.wb_mode, lens_info.wb_mode == WB_KELVIN ? "K" : lens_info.wb_mode == 0 ? " - Auto" : lens_info.wb_mode == 1 ? " - Sunny" : lens_info.wb_mode == 2 ? " - Cloudy" : lens_info.wb_mode == 3 ? " - Tungsten" : lens_info.wb_mode == 4 ? " - Fluorescent" : lens_info.wb_mode == 5 ? " - Flash" : lens_info.wb_mode == 6 ? " - Custom" : lens_info.wb_mode == 8 ? " - Shade" : " - unknown", lens_info.wbs_gm > 0 ? "Green" : "Magenta", ABS(lens_info.wbs_gm), lens_info.wbs_ba > 0 ? "Amber" : "Blue", ABS(lens_info.wbs_ba) ); #ifdef FEATURE_PICSTYLE MVR_LOG_APPEND ( "Picture Style : %s (%d,%d,%d,%d)\n", get_picstyle_name(lens_info.raw_picstyle), lens_get_sharpness(), lens_get_contrast(), ABS(lens_get_saturation()) < 10 ? lens_get_saturation() : 0, ABS(lens_get_color_tone()) < 10 ? lens_get_color_tone() : 0 ); #endif /* todo: refactor these with callbacks (these calls are private) */ extern void fps_mvr_log(char* mvr_logfile_buffer); extern void hdr_mvr_log(char* mvr_logfile_buffer); extern void bitrate_mvr_log(char* mvr_logfile_buffer); fps_mvr_log(mvr_logfile_buffer); hdr_mvr_log(mvr_logfile_buffer); bitrate_mvr_log(mvr_logfile_buffer); MVR_LOG_APPEND ( "\n\nCSV data:\n%s\n", "Time,ISO,Shutter,Aperture,Focal_Len,Focus_Dist" ); // Force the initial values to be written mvr_update_logfile( &lens_info, 1 ); } #endif static inline uint16_t bswap16( uint16_t val ) { return ((val << 8) & 0xFF00) | ((val >> 8) & 0x00FF); } PROP_HANDLER( PROP_MVR_REC_START ) { /* there might be a false trigger at startup - issue #1992 */ extern int ml_started; if (!ml_started) return; mvr_rec_start_shoot(buf[0]); #ifdef FEATURE_MOVIE_LOGGING mvr_create_logfile( *(unsigned*) buf ); #endif } PROP_HANDLER( PROP_LENS_NAME ) { if( len > sizeof(lens_info.name) ) len = sizeof(lens_info.name); memcpy( (char*)lens_info.name, buf, len ); } PROP_HANDLER(PROP_LENS) { uint8_t* info = (uint8_t *) buf; #ifdef CONFIG_5DC lens_info.lens_exists = 0; lens_info.raw_aperture_min = info[2]; lens_info.raw_aperture_max = info[3]; lens_info.lens_id = 0; lens_info.lens_focal_min = 0; lens_info.lens_focal_max = 0; lens_info.lens_extender = 0; lens_info.lens_version = 0; lens_info.lens_capabilities = 0; #else lens_info.lens_exists = info[0]; lens_info.raw_aperture_min = info[1]; lens_info.raw_aperture_max = info[2]; lens_info.lens_id = (info[3] << 8) | info[4]; lens_info.lens_focal_min = (info[5] << 8) | info[6]; lens_info.lens_focal_max = (info[7] << 8) | info[8]; lens_info.lens_extender = info[0xE]; /* not all models support this feature */ if(len >= 0x1C) { lens_info.lens_version = (info[0x19] << 16) | (info[0x1A] << 8) | info[0x1B]; lens_info.lens_capabilities = info[0x1C]; /* not sure how big the lens serial is; exiftool shows 5 bytes in htmlDump */ uint32_t lens_serial_lo = info[0x18] | (info[0x17] << 8) | (info[0x16] << 16) | (info[0x15] << 24) ; uint32_t lens_serial_hi = info[0x14] ; lens_info.lens_serial = (uint64_t) lens_serial_lo | ((uint64_t) lens_serial_hi << 32); } else { lens_info.lens_version = 0; lens_info.lens_capabilities = 0; } #endif if (lens_info.raw_aperture < lens_info.raw_aperture_min || lens_info.raw_aperture > lens_info.raw_aperture_max) { int raw = COERCE(lens_info.raw_aperture, lens_info.raw_aperture_min, lens_info.raw_aperture_max); lensinfo_set_aperture(raw); // valid limits changed } //~ bv_update_lensinfo(); } PROP_HANDLER(PROP_LV_LENS_STABILIZE) { //~ NotifyBox(2000, "%x ", buf[0]); lens_info.IS = (buf[0] & 0x000F0000) >> 16; // not sure, but lower word seems to be AF/MF status } // it may be slow; if you need faster speed, replace this with a binary search or something better #define RAWVAL_FUNC(param) \ int raw2index_##param(int raw) \ { \ int i; \ for (i = 0; i < COUNT(codes_##param); i++) \ if(codes_##param[i] >= raw) return i; \ return 0; \ }\ \ int val2raw_##param(int val) \ { \ unsigned i; \ for (i = 0; i < COUNT(codes_##param); i++) \ if(values_##param[i] >= val) return codes_##param[i]; \ return -1; \ } RAWVAL_FUNC(iso) RAWVAL_FUNC(shutter) RAWVAL_FUNC(aperture) static void lensinfo_set_iso(int raw) { lens_info.raw_iso = raw; lens_info.iso = RAW2VALUE(iso, raw); update_stuff(); } static void lensinfo_set_shutter(int raw) { //~ bmp_printf(FONT_MED, 600, 100, "liss %d %d ", raw, caller); lens_info.raw_shutter = raw; lens_info.shutter = RAW2VALUE(shutter, raw); update_stuff(); } static void lensinfo_set_aperture(int raw) { if (raw) { if (lens_info.raw_aperture_min && lens_info.raw_aperture_max) raw = COERCE(raw, lens_info.raw_aperture_min, lens_info.raw_aperture_max); lens_info.raw_aperture = raw; lens_info.aperture = RAW2VALUE(aperture, raw); } else { lens_info.aperture = lens_info.raw_aperture = 0; } //~ BMP_LOCK( lens_info.aperture = (int)roundf(10.0 * sqrtf(powf(2.0, (raw-8.0)/8.0))); ) update_stuff(); } extern int bv_auto; #if defined(CONFIG_NO_MANUAL_EXPOSURE_MOVIE) && !defined(CONFIG_NO_DEDICATED_MOVIE_MODE) /* * If we don't have manual exposure controls in movie mode, we need to use expo override (500D/1100D/50D). * * In cameras with a dedicated movie mode (500D, 1100D), exposure properties are triggered * by Canon's auto exposure algorithm. Therefore, we can't sync expo override from properties. * * If there is no dedicated movie mode on the mode dial (50D), we can simply enable ExpSim, * and this will not use any auto exposure algorithm from Canon. * Therefore, we can safely sync expo override from properties, just like on cameras with manual exposure controls. * */ #define CONFIG_MOVIE_EXPO_OVERRIDE_DISABLE_SYNC_WITH_PROPS #endif static int iso_ack = -1; PROP_HANDLER( PROP_ISO ) { if (!CONTROL_BV) lensinfo_set_iso(buf[0]); #ifdef FEATURE_EXPO_OVERRIDE else if ( buf[0] && !gui_menu_shown() #if defined(ISO_ADJUSTMENT_ACTIVE) || defined(CONFIG_MOVIE_EXPO_OVERRIDE_DISABLE_SYNC_WITH_PROPS) && ISO_ADJUSTMENT_ACTIVE #endif ) { /* when you adjust ISO from Canon menu, sync expo override too */ /* this should work even on cameras without manual exposure control, since it's safeguarded by ISO_ADJUSTMENT_ACTIVE */ /* (that's why ISO_ADJUSTMENT_ACTIVE is mandatory for cameras with CONFIG_NO_MANUAL_EXPOSURE_MOVIE, and optional on others) */ bv_set_rawiso(buf[0]); } bv_auto_update(); #endif lens_display_set_dirty(); iso_ack = buf[0]; } void iso_auto_restore_hack() { if (iso_ack == 0) lensinfo_set_iso(0); } PROP_HANDLER( PROP_ISO_AUTO ) { uint32_t raw = *(uint32_t *) buf; #if defined(FRAME_ISO) if (lv && is_movie_mode()) raw = (uint8_t)FRAME_ISO; #endif lens_info.raw_iso_auto = raw; lens_info.iso_auto = RAW2VALUE(iso, raw); update_stuff(); } #if defined(FRAME_ISO) PROP_HANDLER( PROP_BV ) // camera-specific { if (lv && is_movie_mode()) { uint32_t raw_iso = (uint8_t)FRAME_ISO; if (raw_iso) { lens_info.raw_iso_auto = raw_iso; lens_info.iso_auto = RAW2VALUE(iso, raw_iso); update_stuff(); } } } #endif PROP_HANDLER( PROP_SHUTTER ) { if (!CONTROL_BV) { if (shooting_mode != SHOOTMODE_AV && shooting_mode != SHOOTMODE_P) lensinfo_set_shutter(buf[0]); } #ifdef FEATURE_EXPO_OVERRIDE else if (buf[0] // sync expo override to Canon values #if !defined(CONFIG_100D) // any other cameras which need this ? // symptoms: http://www.magiclantern.fm/forum/index.php?topic=16040.msg187050#msg187050 && (ABS(buf[0] - lens_info.raw_shutter) > 3) // some cameras may attempt to round shutter value to 1/2 or 1/3 stops // especially when pressing half-shutter #endif #ifdef CONFIG_MOVIE_EXPO_OVERRIDE_DISABLE_SYNC_WITH_PROPS && !is_movie_mode() #endif #ifdef CONFIG_6D && !(buf[0] == FASTEST_SHUTTER_SPEED_RAW ) #endif ) { bv_set_rawshutter(buf[0]); } bv_auto_update(); #endif lens_display_set_dirty(); } PROP_HANDLER( PROP_APERTURE ) { //~ NotifyBox(2000, "%x %x %x %x ", buf[0], CONTROL_BV, lens_info.raw_aperture_min, lens_info.raw_aperture_max); if (!CONTROL_BV) { lensinfo_set_aperture(buf[0]); } #ifdef FEATURE_EXPO_OVERRIDE else if (buf[0] && !gui_menu_shown() #ifdef CONFIG_MOVIE_EXPO_OVERRIDE_DISABLE_SYNC_WITH_PROPS && !is_movie_mode() #endif ) { bv_set_rawaperture(COERCE(buf[0], lens_info.raw_aperture_min, lens_info.raw_aperture_max)); } bv_auto_update(); #endif lens_display_set_dirty(); } PROP_HANDLER( PROP_APERTURE_AUTO ) { /* this gets updated in Tv mode (where PROP_APERTURE is not updated); same for P, Auto and so on */ /* it becomes 0 when camera is no longer metering */ if (shooting_mode == SHOOTMODE_M || shooting_mode == SHOOTMODE_AV) { /* in these modes, aperture is not automatic */ /* however, this property sometimes becomes 0 in these modes as well, but this is not desired */ if (buf[0] == 0) return; } if (!CONTROL_BV) { /* expo override turned off? */ lensinfo_set_aperture(buf[0]); } lens_display_set_dirty(); } PROP_HANDLER( PROP_SHUTTER_AUTO ) { /* this gets updated in Av mode (where PROP_SHUTTER is not updated); same for P, Auto and so on */ /* it becomes 0 when camera is no longer metering */ if (shooting_mode == SHOOTMODE_M || shooting_mode == SHOOTMODE_TV) { /* in these modes, shutter is not automatic */ /* however, this property sometimes becomes 0 in these modes as well, but this is not desired */ if (buf[0] == 0) return; } if (!CONTROL_BV) { /* expo override turned off? */ /* todo: double-check if it's still needed */ if (ABS(buf[0] - lens_info.raw_shutter) > 3) lensinfo_set_shutter(buf[0]); } lens_display_set_dirty(); } PROP_HANDLER( PROP_AE ) { const uint32_t value = *(uint32_t *) buf; lens_info.ae = (int8_t)value; update_stuff(); } PROP_HANDLER( PROP_WB_MODE_LV ) { const uint32_t value = *(uint32_t *) buf; lens_info.wb_mode = value; } PROP_HANDLER(PROP_WBS_GM) { const int8_t value = *(int8_t *) buf; lens_info.wbs_gm = value; } PROP_HANDLER(PROP_WBS_BA) { const int8_t value = *(int8_t *) buf; lens_info.wbs_ba = value; } PROP_HANDLER( PROP_WB_KELVIN_LV ) { const uint32_t value = *(uint32_t *) buf; lens_info.kelvin = value; } #if !defined(CONFIG_5DC) && !defined(CONFIG_40D) static uint16_t custom_wb_gains[128]; PROP_HANDLER(PROP_CUSTOM_WB) { ASSERT(len <= sizeof(custom_wb_gains)); memcpy(custom_wb_gains, buf, len); const uint16_t * gains = (uint16_t *) buf; lens_info.WBGain_R = gains[16]; lens_info.WBGain_G = gains[18]; lens_info.WBGain_B = gains[19]; } #endif void lens_set_custom_wb_gains(int gain_R, int gain_G, int gain_B) { #if !defined(CONFIG_VXWORKS) // normalize: green gain should be always 1 //~ gain_G = COERCE(gain_G, 4, 32000); //~ gain_R = COERCE(gain_R * 1024 / gain_G, 128, 32000); //~ gain_B = COERCE(gain_B * 1024 / gain_G, 128, 32000); //~ gain_G = 1024; gain_G = COERCE(gain_G, 128, 8192); gain_R = COERCE(gain_R, 128, 8192); gain_B = COERCE(gain_B, 128, 8192); // round off a bit to get nice values in menu gain_R = ((gain_R + 8) >> 4) << 4; gain_B = ((gain_B + 8) >> 4) << 4; custom_wb_gains[16] = gain_R; custom_wb_gains[18] = gain_G; custom_wb_gains[19] = gain_B; prop_request_change(PROP_CUSTOM_WB, custom_wb_gains, 0); int mode = WB_CUSTOM; prop_request_change(PROP_WB_MODE_LV, &mode, 4); prop_request_change(PROP_WB_MODE_PH, &mode, 4); #endif } #define LENS_GET(param) \ int lens_get_##param() \ { \ return lens_info.param; \ } PROP_INT(PROP_WB_KELVIN_PH, wb_kelvin_ph); void lens_set_kelvin(int k) { k = COERCE(k, KELVIN_MIN, KELVIN_MAX); int mode = WB_KELVIN; if (k > 10000 || k < 2500) // workaround for 60D; out-of-range values are ignored in photo mode { int lim = k > 10000 ? 10000 : 2500; if ((k > 10000 && (int)wb_kelvin_ph < lim) || (k < 2500 && (int)wb_kelvin_ph > lim)) { prop_request_change(PROP_WB_KELVIN_PH, &lim, 4); msleep(20); } } prop_request_change(PROP_WB_MODE_LV, &mode, 4); prop_request_change(PROP_WB_KELVIN_LV, &k, 4); prop_request_change(PROP_WB_MODE_PH, &mode, 4); prop_request_change(PROP_WB_KELVIN_PH, &k, 4); msleep(20); } void lens_set_kelvin_value_only(int k) { k = COERCE(k, KELVIN_MIN, KELVIN_MAX); if (k > 10000 || k < 2500) // workaround for 60D; out-of-range values are ignored in photo mode { int lim = k > 10000 ? 10000 : 2500; prop_request_change(PROP_WB_KELVIN_PH, &lim, 4); msleep(10); } prop_request_change(PROP_WB_KELVIN_LV, &k, 4); prop_request_change(PROP_WB_KELVIN_PH, &k, 4); msleep(10); } void split_iso(int raw_iso, unsigned int* analog_iso, int* digital_gain) { if (!raw_iso) { *analog_iso = 0; *digital_gain = 0; return; } int rounded = ((raw_iso+3)/8) * 8; if (get_htp()) rounded -= 8; *analog_iso = COERCE(rounded, 72, MAX_ANALOG_ISO); // analog ISO range: 100-3200 (100-25600 on 5D3) *digital_gain = raw_iso - *analog_iso; } void iso_components_update() { split_iso(lens_info.raw_iso, &lens_info.iso_analog_raw, &lens_info.iso_digital_ev); lens_info.iso_equiv_raw = lens_info.raw_iso; int digic_gain = get_digic_iso_gain_movie(); if (lens_info.iso_equiv_raw && digic_gain != 1024 && is_movie_mode()) { lens_info.iso_equiv_raw = lens_info.iso_equiv_raw + (gain_to_ev_scaled(digic_gain, 8) - 80); } } static void update_stuff() { focus_calc_dof(); //~ if (gui_menu_shown()) lens_display_set_dirty(); #ifdef FEATURE_MOVIE_LOGGING if (movie_log) mvr_update_logfile( &lens_info, 0 ); // do not force it #endif iso_components_update(); } static void focus_ring_powersave_fix() { if (!RECORDING && is_manual_focus()) { /* Canon bug? turning the focus ring doesn't reset powersave timer */ powersave_prolong(); } } /* only used for requesting a refresh of PROP_LV_LENS; * raw data is model-dependent, do not use directly */ static struct prop_lv_lens lv_lens_raw; PROP_HANDLER( PROP_LV_LENS ) { ASSERT(len <= sizeof(lv_lens_raw)); memcpy(&lv_lens_raw, buf, sizeof(lv_lens_raw)); const struct prop_lv_lens * const lv_lens = (void*) buf; lens_info.focal_len = bswap16( lv_lens->focal_len ); lens_info.focus_dist = bswap16( lv_lens->focus_dist ); lens_info.focus_pos = (int16_t) bswap16( lv_lens->focus_pos ); if (lens_info.focal_len > 1000) // bogus values lens_info.focal_len = 0; //~ uint32_t lrswap = SWAP_ENDIAN(lv_lens->lens_rotation); //~ uint32_t lsswap = SWAP_ENDIAN(lv_lens->lens_step); //~ lens_info.lens_rotation = *((float*)&lrswap); //~ lens_info.lens_step = *((float*)&lsswap); static unsigned old_focus_dist = 0; static int old_focus_pos = 0; static unsigned old_focal_len = 0; int focus_dist_changed = (old_focus_dist && lens_info.focus_dist != old_focus_dist); int focus_pos_changed = (lens_info.focus_pos != old_focus_pos); int lens_not_zoomed = (old_focal_len && lens_info.focal_len == old_focal_len); if (lv && lens_not_zoomed && (focus_pos_changed || focus_dist_changed)) { #ifdef FEATURE_MAGIC_ZOOM if (get_zoom_overlay_trigger_by_focus_ring()) zoom_overlay_set_countdown(300); #endif idle_wakeup_reset_counters(-11); lens_display_set_dirty(); focus_ring_powersave_fix(); #ifdef FEATURE_LV_ZOOM_SETTINGS zoom_focus_ring_trigger(); #endif } old_focus_dist = lens_info.focus_dist; old_focus_pos = lens_info.focus_pos; old_focal_len = lens_info.focal_len; update_stuff(); } /* called once per second */ void _prop_lv_lens_request_update() { /* this property is normally active only in LiveView * however, the MPU can be tricked into sending its value outside LiveView as well * (Canon code also updates these values outside LiveView, when taking a picture) * the input data should not be used, but... better safe than sorry * this should send MPU message 06 04 09 00 00 * and the MPU is expected to reply with the complete property (much larger) * size is model-specific, but should not be larger than sizeof(lv_lens_raw) */ prop_request_change(PROP_LV_LENS, &lv_lens_raw, 0); } /** * This tells whether the camera is ready to take a picture (or not) * 5D2: the sequence is: 0 11 10 8 0 * that means: 0 = idle, 11 = very busy (exposing), 10 = exposed, but processing (can take the next picture), 8 = done processing, just saving to card * also, when job state is 11, we can't change camera settings, but when it's 10, we can * 5D3: the sequence is: 0 0x16 0x14 0x10 0 * other cameras may have different values * * => hypothesis: the general sequence is: * * 0 max something_smaller something_even_smaller and so on * * so, we only want to avoid the situation when job_state == max_job_state * */ static int max_job_state = 0; int job_state_ready_to_take_pic() { if (max_job_state == 0) return 1; return (int)lens_info.job_state < max_job_state; } PROP_HANDLER( PROP_LAST_JOB_STATE ) { const uint32_t state = *(uint32_t*) buf; lens_info.job_state = state; if (max_job_state == 0 && state != 0) max_job_state = state; if (max_job_state && (int)state == max_job_state) { mirror_locked = 0; hdr_flag_picture_was_taken(); } #ifdef CONFIG_JOB_STATE_DEBUG static char jmsg[100] = ""; STR_APPEND(jmsg, "%d ", state); bmp_printf(FONT_MED,0,0, jmsg); #endif } PROP_HANDLER(PROP_STROBO_AECOMP) { lens_info.flash_ae = (int8_t) buf[0]; } int lens_set_flash_ae(int fae) { fae = round_flash_expo_comp(fae); prop_request_change_wait(PROP_STROBO_AECOMP, &fae, 4, 100); return lens_info.flash_ae == fae; } PROP_HANDLER(PROP_HALF_SHUTTER) { update_stuff(); lens_display_set_dirty(); //~ bv_auto_update(); } static struct menu_entry lens_menus[] = { #ifdef FEATURE_MOVIE_LOGGING { .name = "Movie Logging", .priv = &movie_log, .max = 1, .help = "Save metadata for each movie, e.g. MVI_1234.LOG", .depends_on = DEP_MOVIE_MODE, }, #endif }; static MENU_UPDATE_FUNC(lens_name_display) { if(!lens_info.lens_exists) { MENU_SET_VALUE("(no lens)"); return; } MENU_SET_VALUE("%s", lens_info.name ); } static MENU_UPDATE_FUNC(lens_id_display) { if(!lens_info.lens_exists) { MENU_SET_VALUE("(no lens)"); return; } /* exiftool displays this as decimal */ MENU_SET_VALUE("0x%04X (%d)", lens_info.lens_id, lens_info.lens_id); } static MENU_UPDATE_FUNC(lens_serial_display) { if(!lens_info.lens_exists) { MENU_SET_VALUE("(no lens)"); return; } if(lens_info.lens_serial) { MENU_SET_VALUE( "%02x%08X", /* to match exiftool display */ (uint32_t)(lens_info.lens_serial >> 32), (uint32_t)lens_info.lens_serial ); } else { MENU_SET_VALUE("(none)"); } } static MENU_UPDATE_FUNC(lens_extender_display) { if(!lens_info.lens_exists) { MENU_SET_VALUE("(no lens)"); return; } MENU_SET_VALUE("0x%02X", lens_info.lens_extender ); } static MENU_UPDATE_FUNC(lens_version_display) { uint8_t v2 = lens_info.lens_version >> 16; uint8_t v1 = lens_info.lens_version >> 8; uint8_t v0 = lens_info.lens_version; if(!lens_info.lens_exists) { MENU_SET_VALUE("(no lens)"); return; } if(lens_info.lens_version) { MENU_SET_VALUE("v%d.%d.%d", v2, v1, v0); } else { MENU_SET_VALUE("(none)"); } } static MENU_UPDATE_FUNC(lens_capabilities_display) { if(!lens_info.lens_exists) { MENU_SET_VALUE("(no lens)"); return; } MENU_SET_VALUE("0x%02X", lens_info.lens_capabilities); } static MENU_UPDATE_FUNC(lens_focal_display) { char *unit = "mm"; float factor = 1.0f; if(!lens_info.lens_exists) { MENU_SET_VALUE("(no lens)"); return; } if(focus_units == 1) { unit = "in"; factor = 1/2.54; } if(lens_info.lens_focal_min == lens_info.lens_focal_max) { MENU_SET_VALUE("%d %s", (int)(lens_info.lens_focal_min * factor), unit); } else { MENU_SET_VALUE("%d-%d %s", (int)(lens_info.lens_focal_min * factor), (int)(lens_info.lens_focal_max * factor), unit); } } static struct menu_entry tweak_menus[] = { { .name = "Lens Info Prefs", .select = menu_open_submenu, .children = (struct menu_entry[]) { #ifndef CONFIG_FULLFRAME { .name = "Crop Factor Display", .priv = &crop_info, .max = 1, .choices = CHOICES("OFF", "ON, 35mm eq."), .help = "Display the 35mm equiv. focal length including crop factor.", .depends_on = DEP_LIVEVIEW | DEP_CHIPPED_LENS, }, #endif { .name = "Focus Distance Units", .priv = &focus_units, .choices = CHOICES("mm/cm", "ft/in"), .max = 1, .help = "Can select between Metric and Imperial focus distance units", }, MENU_EOL } } }; /* better place for this menu? */ static struct menu_entry lens_info_menus[] = { { .name = "Lens info", .select = menu_open_submenu, .submenu_width = 700, .children = (struct menu_entry[]) { { .name = "Name", .update = &lens_name_display, .help = "Show current lens name (as reported by your lens or adapter).", .help2 = "Read-only.", }, { .name = "Focal len", .update = &lens_focal_display, .help = "Show current lens focal length.", .help2 = "Read-only. Zoom lenses are only updated in LiveView.", }, { .name = "Lens ID", .update = &lens_id_display, .help = "Show current lens ID. Should match exiftool TEST.CR2 -LensType -b.", .help2 = "Read-only. Lenses from different manufacturers may have the same ID.", }, { .name = "Serial num", .update = &lens_serial_display, .help = "Show current lens serial number. Not all cameras report this.", .help2 = "Read-only. Should match exiftool TEST.CR2 -LensSerialNumber .", }, { .name = "Version", .update = &lens_version_display, .help = "Show current lens version string.", .help2 = "Read-only.", }, { .name = "Capability", .update = &lens_capabilities_display, .help = "Show current lens capability bits.", .help2 = "Read-only.", }, { .name = "Extender", .update = &lens_extender_display, .help = "Show current lens extender information byte.", .help2 = "Read-only.", }, MENU_EOL }, } }; // hack to show this at the end of prefs menu void crop_factor_menu_init() { menu_add("Prefs", tweak_menus, COUNT(tweak_menus)); menu_add("Debug", lens_info_menus, COUNT(lens_info_menus)); /* hack: lens name is usually long */ /* force all submenu values to the left to maintain a nice layout */ /* todo: better backend support? */ lens_info_menus[0].children[0].parent_menu->split_pos = -10; } static void lens_init( void* unused ) { focus_done_sem = create_named_semaphore( "focus_sem", 1 ); #ifndef CONFIG_5DC menu_add("Movie Tweaks", lens_menus, COUNT(lens_menus)); #endif } INIT_FUNC( "lens", lens_init ); // picture style, contrast... // ------------------------------------------- PROP_HANDLER(PROP_PICTURE_STYLE) { const uint32_t raw = *(uint32_t *) buf; lens_info.raw_picstyle = raw; lens_info.picstyle = get_prop_picstyle_index(raw); } extern struct prop_picstyle_settings picstyle_settings[]; // get contrast/saturation/etc from the current picture style #define LENS_GET_FROM_PICSTYLE(param) \ int \ lens_get_##param() \ { \ int i = lens_info.picstyle; \ if (!i) return -10; \ return picstyle_settings[i].param; \ } \ #define LENS_GET_FROM_OTHER_PICSTYLE(param) \ int \ lens_get_from_other_picstyle_##param(int picstyle_index) \ { \ return picstyle_settings[picstyle_index].param; \ } \ // set contrast/saturation/etc in the current picture style (change is permanent!) #define LENS_SET_IN_PICSTYLE(param,lo,hi) \ void \ lens_set_##param(int value) \ { \ if (value < lo || value > hi) return; \ int i = lens_info.picstyle; \ if (!i) return; \ picstyle_settings[i].param = value; \ prop_request_change(PROP_PICSTYLE_SETTINGS(i), &picstyle_settings[i], 24); \ } \ LENS_GET_FROM_PICSTYLE(contrast) LENS_GET_FROM_PICSTYLE(sharpness) LENS_GET_FROM_PICSTYLE(saturation) LENS_GET_FROM_PICSTYLE(color_tone) LENS_GET_FROM_OTHER_PICSTYLE(contrast) LENS_GET_FROM_OTHER_PICSTYLE(sharpness) LENS_GET_FROM_OTHER_PICSTYLE(saturation) LENS_GET_FROM_OTHER_PICSTYLE(color_tone) LENS_SET_IN_PICSTYLE(contrast, -4, 4) LENS_SET_IN_PICSTYLE(sharpness, -1, 7) LENS_SET_IN_PICSTYLE(saturation, -4, 4) LENS_SET_IN_PICSTYLE(color_tone, -4, 4) void SW1(int v, int wait) { v = COERCE(v, 0, 1); prop_request_change_wait(PROP_REMOTE_SW1, &v, 0, 1000); /* todo: remove the wait argument */ if (wait) msleep(wait); } void SW2(int v, int wait) { v = COERCE(v, 0, 1); prop_request_change_wait(PROP_REMOTE_SW2, &v, 0, 1000); /* todo: remove the wait argument */ if (wait) msleep(wait); } /** exposure primitives (the "clean" way, via properties) */ static int prop_set_rawaperture(unsigned aperture) { // Canon likes only numbers in 1/3 or 1/2-stop increments int r = aperture % 8; if (r != 0 && r != 4 && r != 3 && r != 5 && aperture != lens_info.raw_aperture_min && aperture != lens_info.raw_aperture_max) { return 0; } lens_wait_readytotakepic(64); aperture = COERCE(aperture, lens_info.raw_aperture_min, lens_info.raw_aperture_max); prop_request_change_wait(PROP_APERTURE, &aperture, 4, 200); return lens_info.raw_aperture == aperture; } static int prop_set_rawaperture_approx(unsigned new_av) { /* aperture is very tricky; even if we respect the rounding rules * the values might be refused for no apparent reason * so we'll use trial and error, until a value gets accepted */ /* first try to set it exactly */ if (prop_set_rawaperture(new_av)) return 1; /* then try to set a value close to the requested one, until it works */ for (int d = 1; d < 4; d++) { if (prop_set_rawaperture(new_av + d)) return 1; if (prop_set_rawaperture(new_av - d)) return 1; } return 0; } static int prop_set_rawshutter(unsigned shutter) { // Canon likes numbers in 1/3 or 1/2-stop increments if (is_movie_mode()) { int r = shutter % 8; if (r != 0 && r != 4 && r != 3 && r != 5) return 0; } if (shutter < 16) return 0; if (shutter > FASTEST_SHUTTER_SPEED_RAW) return 0; lens_wait_readytotakepic(64); int s0 = shutter; prop_request_change_wait( PROP_SHUTTER, &shutter, 4, 100); if (lens_info.raw_shutter != s0 && !(CONTROL_BV && lv)) { /* no confirmation? try set shutter 2 stops away from final value, and back */ int sx = shutter > 128 ? shutter - 16 : shutter + 16; prop_request_change_wait( PROP_SHUTTER, &sx, 4, 100); prop_request_change_wait( PROP_SHUTTER, &shutter, 4, 100); } return lens_info.raw_shutter == s0; } static int prop_set_rawshutter_approx(unsigned shutter) { lens_wait_readytotakepic(64); shutter = COERCE(shutter, 16, FASTEST_SHUTTER_SPEED_RAW); // 30s ... 1/8000 or 1/4000 /* Some cameras accept any shutter speeds in 1/8 EV increments (in the valid range), * while others only accepts the following values modulo 8: 0,3,4,5. * Therefore, it is possible to get a rounding error of max 1 unit (1/8 EV) * * Let's first see what Canon firmware gives us. */ prop_request_change_wait( PROP_SHUTTER, &shutter, 4, 100); int delta = (int)lens_info.raw_shutter - (int)shutter; if (ABS(delta) == 2) { /* if we get a rounding error of 2, try altering the shutter speed by one; * it will most likely get it right this time */ shutter -= SGN(delta); prop_request_change_wait( PROP_SHUTTER, &shutter, 4, 100); delta = (int)lens_info.raw_shutter - (int)shutter; } return ABS(delta) <= 1; } static int prop_set_rawiso(unsigned iso) { lens_wait_readytotakepic(64); if (iso) iso = COERCE(iso, MIN_ISO, MAX_ISO); // ISO 100-25600 prop_request_change_wait( PROP_ISO, &iso, 4, 100); return lens_info.raw_iso == iso; } static int prop_set_rawiso_approx(unsigned iso) { /* first try to set it exactly */ if (prop_set_rawiso(iso)) return 1; if (iso) { /* then try to set a value close to the requested one, until it works */ for (int d = 1; d < 4; d++) { if (prop_set_rawiso(iso + d)) return 1; if (prop_set_rawiso(iso - d)) return 1; } } return 0; } /** Exposure primitives (the "dirty" way, via BV control, bypasses protections) */ #ifdef FEATURE_EXPO_OVERRIDE extern int bv_iso; extern int bv_tv; extern int bv_av; int expo_override_active() { return CONTROL_BV && lv; } void bv_update_lensinfo() { if (CONTROL_BV) // sync lens info and camera properties with overriden values { lensinfo_set_iso(bv_iso + (get_htp() ? 8 : 0)); lensinfo_set_shutter(bv_tv); lensinfo_set_aperture(bv_av); } } void bv_apply_tv(int tv) { if (is_movie_mode()) CONTROL_BV_TV = COERCE(tv, 0x5C, 0xA0); // try to extend shutter range, 1/24 ... 1/8000 else CONTROL_BV_TV = COERCE(tv, 0x60, 0x98); // 600D: [LV] ERROR >> Tv:0x10, TvMax:0x98, TvMin:0x60 } void bv_apply_av(int av) { if (lens_info.raw_aperture_min == 0 && lens_info.raw_aperture_max == 0) { /* if this is 0, exposure override has no effect; use f2.8 as a dummy value */ CONTROL_BV_AV = 32; return; } CONTROL_BV_AV = COERCE(av, lens_info.raw_aperture_min, lens_info.raw_aperture_max); } void bv_apply_iso(int iso) { CONTROL_BV_ISO = COERCE(iso, 72, MAX_ISO_BV); } int bv_set_rawshutter(unsigned shutter) { //~ bmp_printf(FONT_MED, 600, 300, "bvsr %d ", shutter); bv_tv = shutter; bv_apply_tv(bv_tv); bv_update_lensinfo(); bv_expsim_shift(); //~ NotifyBox(2000, "%d > %d?", raw2shutter_ms(shutter), 1000/video_mode_fps); msleep(400); if (is_movie_mode() && raw2shutter_ms(shutter+1) > 1000/video_mode_fps) return 0; return shutter != 0; } int bv_set_rawiso(unsigned iso) { if (iso == 0) return 0; if (iso >= MIN_ISO && iso <= MAX_ISO_BV) { if (get_htp()) iso -= 8; // quirk: with exposure override and HTP, image is brighter by 1 stop than with Canon settings bv_iso = iso; bv_apply_iso(iso); bv_update_lensinfo(); bv_expsim_shift(); return 1; } else { return 0; } } int bv_set_rawaperture(unsigned aperture) { if (aperture >= lens_info.raw_aperture_min && aperture <= lens_info.raw_aperture_max) { bv_av = aperture; bv_apply_av(bv_av); bv_update_lensinfo(); bv_expsim_shift(); return 1; } else { return 0; } } static void bv_expsim_shift_try_iso(int newiso) { #ifndef FEATURE_LV_DISPLAY_GAIN #error This requires FEATURE_LV_DISPLAY_GAIN. #endif #define MAX_GAIN_EV 6 int e = 0; if (newiso < 72) e = 72 - newiso; else if (newiso > MAX_ISO_BV + MAX_GAIN_EV*8) e = MAX_ISO_BV + MAX_GAIN_EV*8 - newiso; e = e * 10/8; static int prev_e = 0; if (e != prev_e) { /* if (ABS(e) > 2) { NotifyBox(2000, "Preview %sexposed by %d.%d EV", e > 0 ? "over" : "under", ABS(e)/10, ABS(e)%10); } else NotifyBoxHide(); */ } prev_e = e; int g = 1024; while (newiso > MAX_ISO_BV && g < (1024 << MAX_GAIN_EV)) { g *= 2; newiso -= 8; } bv_apply_iso(newiso); set_photo_digital_iso_gain_for_bv(g); } static void bv_expsim_shift() { set_photo_digital_iso_gain_for_bv(1024); if (!lv) return; if (!get_expsim()) return; if (!CONTROL_BV) return; if (!is_movie_mode()) { int tv_fps_shift = fps_get_shutter_speed_shift(bv_tv); if (is_bulb_mode()) // try to perform expsim in bulb mode, based on bulb timer setting { int tv = get_bulb_shutter_raw_equiv() + tv_fps_shift; if (tv < SHUTTER_1_30) { int delta = SHUTTER_1_30 - tv; bv_apply_tv(SHUTTER_1_30); bv_expsim_shift_try_iso(bv_iso + delta); return; } else { bv_apply_tv(tv); bv_apply_iso(bv_iso); return; } } else { bv_apply_tv(bv_tv); if (bv_tv < SHUTTER_1_30) // shutter speeds slower than 1/30 -> can't be obtained, raise ISO or open up aperture instead { int delta = SHUTTER_1_30 - bv_tv - tv_fps_shift; bv_apply_tv(SHUTTER_1_30); bv_expsim_shift_try_iso(bv_iso + delta); return; } else if (tv_fps_shift) // FPS override enabled { bv_expsim_shift_try_iso(bv_iso - tv_fps_shift); return; } } // no shifting, make sure we use unaltered values bv_apply_tv(bv_tv); bv_apply_av(bv_av); bv_apply_iso(bv_iso); } return; } static int bv_auto_should_enable() { if (!bv_auto) return 0; if (!lv) return 0; extern int zoom_auto_exposure; if (zoom_auto_exposure && lv_dispsize > 1) return 0; // otherwise it would interfere with auto exposure if (LVAE_DISP_GAIN) // compatibility problem, disable it return 0; if (bv_auto == 1) // always enable (except for situations where it's known to cause problems) { return 1; // tricky situations were handled before these if's } return 0; } void bv_auto_update() { //~ take_semaphore(bv_sem, 0); if (!bv_auto) return; //~ take_semaphore(lens_sem, 0); if (bv_auto_should_enable()) bv_enable(); else bv_disable(); bv_expsim_shift(); lens_display_set_dirty(); //~ give_semaphore(lens_sem); //~ give_semaphore(bv_sem); } #endif /** Camera control functions */ int lens_set_rawaperture( int aperture) { int ok = prop_set_rawaperture(aperture); // first try to set via property #ifdef FEATURE_EXPO_OVERRIDE bv_auto_update(); // auto flip between "BV" or "normal" if (CONTROL_BV) return bv_set_rawaperture(aperture); #endif return ok; } int lens_set_rawiso( int iso ) { int ok = prop_set_rawiso(iso); // first try to set via property #ifdef FEATURE_EXPO_OVERRIDE bv_auto_update(); // auto flip between "BV" or "normal" if (CONTROL_BV) return bv_set_rawiso(iso); #endif return ok; } int lens_set_rawshutter( int shutter ) { int ok = prop_set_rawshutter(shutter); // first try to set via property #ifdef FEATURE_EXPO_OVERRIDE bv_auto_update(); // auto flip between "BV" or "normal" if (CONTROL_BV) return bv_set_rawshutter(shutter); #endif return ok; } int lens_set_ae( int ae ) { ae = round_expo_comp(ae); prop_request_change_wait( PROP_AE, &ae, 4, 100); return lens_info.ae == ae; } void lens_set_drivemode( int dm ) { if (dm < 0) return; if (dm > 0x20) return; lens_wait_readytotakepic(64); prop_request_change( PROP_DRIVE, &dm, 4 ); msleep(10); } void lens_set_wbs_gm(int value) { value = COERCE(value, -9, 9); prop_request_change(PROP_WBS_GM, &value, 4); } void lens_set_wbs_ba(int value) { value = COERCE(value, -9, 9); prop_request_change(PROP_WBS_BA, &value, 4); } // Functions to change camera settings during bracketing // They will check the operation and retry if necessary // Used for HDR bracketing static int hdr_set_something(int (*set_something)(int), int arg) { // first try to set it a few times... for (int i = 0; i < 5; i++) { if (ml_shutdown_requested) return 0; if (set_something(arg)) return 1; } // didn't work, let's wait for job state... lens_wait_readytotakepic(64); for (int i = 0; i < 5; i++) { if (ml_shutdown_requested) return 0; if (set_something(arg)) return 1; } // now this is really extreme... okay, one final try while (lens_info.job_state) msleep(100); for (int i = 0; i < 5; i++) { if (ml_shutdown_requested) return 0; if (set_something(arg)) return 1; } // I give up return 0; } int hdr_set_rawiso(int iso) { return hdr_set_something((int(*)(int))prop_set_rawiso_approx, iso); } /* returns 0 on failure, -1 if it reached exposure limits, 1 otherwise */ int hdr_set_rawshutter(int shutter) { /* fixme: check why 13 and not 16 */ int in_range = shutter < FASTEST_SHUTTER_SPEED_RAW && shutter > 13; int ok = hdr_set_something((int(*)(int))prop_set_rawshutter_approx, shutter); return ok ? (in_range ? 1 : -1) : 0; } int hdr_set_rawaperture(int aperture) { int in_range = aperture < lens_info.raw_aperture_max && aperture > lens_info.raw_aperture_min; int ok = hdr_set_something((int(*)(int))prop_set_rawaperture_approx, aperture); return ok ? (in_range ? 1 : -1) : 0; } int hdr_set_ae(int ae) { int in_range = ABS(ae) < MAX_AE_EV * 8; int ok = hdr_set_something((int(*)(int))lens_set_ae, ae); return ok ? (in_range ? 1 : -1) : 0; } int hdr_set_flash_ae(int fae) { int in_range = fae < FLASH_MAX_EV * 8 && fae > FLASH_MIN_EV * 8; int ok = hdr_set_something((int(*)(int))lens_set_flash_ae, fae); return ok ? (in_range ? 1 : -1) : 0; } int get_max_analog_iso() { return MAX_ANALOG_ISO; } int get_max_ae_ev() { return MAX_AE_EV; } #ifdef AE_VALUE int get_ae_value() { return AE_VALUE; } int get_bv() { return APEX10_RAW2TV(lens_info.raw_shutter) + APEX10_RAW2AV(lens_info.raw_aperture) - APEX10_RAW2SV(lens_info.iso_equiv_raw) + APEX10_RAW2EC(get_ae_value()); } #endif #ifdef AE_STATE int get_ae_state() { return AE_STATE; } #endif #include "lvinfo.h" static LVINFO_UPDATE_FUNC(clock_update) { LVINFO_BUFFER(8); struct tm now; LoadCalendarFromRTC( &now ); snprintf(buffer, sizeof(buffer), "%02d:%02d", now.tm_hour, now.tm_min); } static LVINFO_UPDATE_FUNC(disp_preset_update) { LVINFO_BUFFER(8); /* only display this if the feature is enabled */ extern int disp_profiles_0; if (disp_profiles_0) { snprintf(buffer, sizeof(buffer), "DISP %d", get_disp_mode() ); } } static LVINFO_UPDATE_FUNC(picq_update) { LVINFO_BUFFER(16); if (!is_movie_mode()) { int raw = pic_quality & 0x60000; int jpg = pic_quality & 0x10000; int rawsize = pic_quality & 0xF; int jpegtype = pic_quality >> 24; int jpegsize = (pic_quality >> 8) & 0xFF; snprintf(buffer, sizeof(buffer), "%s%s%s", rawsize == 1 ? "mRAW" : rawsize == 2 ? "sRAW" : rawsize == 7 ? "sRAW1" : rawsize == 8 ? "sRAW2" : raw ? "RAW" : "", jpg == 0 ? "" : (raw ? "+" : "JPG-"), jpg == 0 ? "" : ( jpegsize == 0 ? (jpegtype == 3 ? "L" : "l") : jpegsize == 1 ? (jpegtype == 3 ? "M" : "m") : jpegsize == 2 ? (jpegtype == 3 ? "S" : "s") : jpegsize == 0x0e ? (jpegtype == 3 ? "S1" : "s1") : jpegsize == 0x0f ? (jpegtype == 3 ? "S2" : "s2") : jpegsize == 0x10 ? (jpegtype == 3 ? "S3" : "s3") : "err" ) ); } int raw_lv = raw_lv_is_enabled(); if (raw_lv) { /* make it obvious that LiveView is in RAW mode */ /* (primarily for troubleshooting the raw backend, proper raw_lv_request/release calls and Magic Zoom slowdowns) */ if (is_movie_mode()) { /* todo: icon? */ snprintf(buffer, sizeof(buffer), "RAW"); } item->color_fg = raw_lv == 1 ? COLOR_GREEN1 : COLOR_GRAY(20); } } static LVINFO_UPDATE_FUNC(alo_htp_update) { LVINFO_BUFFER(8); int alo = get_alo(); snprintf(buffer, sizeof(buffer), get_htp() ? "HTP" : alo == ALO_LOW ? "alo" : alo == ALO_STD ? "Alo" : alo == ALO_HIGH ? "ALO" : "" ); } #ifdef FEATURE_PICSTYLE static LVINFO_UPDATE_FUNC(picstyle_update) { LVINFO_BUFFER(12); if (is_movie_mode()) { /* picture style has no effect on raw video => don't display */ if (raw_lv_is_enabled()) return; } else { /* when shooting RAW photos, picture style only affects the preview => don't display */ int jpg = pic_quality & 0x10000; if (!jpg) return; } snprintf(buffer, sizeof(buffer), "%s", (char*)get_picstyle_name(lens_info.raw_picstyle) ); } #endif static LVINFO_UPDATE_FUNC(temp_update) { #ifdef EFIC_CELSIUS LVINFO_BUFFER(8); int t = EFIC_CELSIUS; snprintf(buffer, sizeof(buffer), "%d"SYM_DEGREE"C", t); if (t >= 60) { item->color_bg = COLOR_RED; } else if (t >= 50) { item->color_bg = COLOR_ORANGE; } #endif } static LVINFO_UPDATE_FUNC(mvi_number_update) { LVINFO_BUFFER(12); if (is_movie_mode() && !raw_lv_is_enabled()) { snprintf(buffer, sizeof(buffer), "MVI_%04d", get_shooting_card()->file_number); } } static LVINFO_UPDATE_FUNC(fps_update) { LVINFO_BUFFER(8); if (is_movie_mode()) { int f = fps_get_current_x1000(); snprintf(buffer, sizeof(buffer), "%2d.%03d", f / 1000, f % 1000 ); } } static LVINFO_UPDATE_FUNC(free_space_update) { LVINFO_BUFFER(8); if (RECORDING) { /* leave space for the recording indicators */ return; } int free_space_32k = get_free_space_32k(get_shooting_card()); int fsg = free_space_32k >> 15; int fsgr = free_space_32k - (fsg << 15); int fsgf = (fsgr * 10) >> 15; snprintf(buffer, sizeof(buffer), "%d.%dGB", fsg, fsgf ); } static LVINFO_UPDATE_FUNC(mode_update) { LVINFO_BUFFER(8); snprintf(buffer, sizeof(buffer), get_shootmode_name_short(shooting_mode_custom)); } static LVINFO_UPDATE_FUNC(focal_len_update) { LVINFO_BUFFER(16); if (lens_info.lens_exists) { snprintf(buffer, sizeof(buffer), "%d%s", crop_info ? (lens_info.focal_len * SENSORCROPFACTOR + 5) / 10 : lens_info.focal_len, crop_info ? "eq" : SYM_SMALL_M SYM_SMALL_M ); } } static LVINFO_UPDATE_FUNC(is_update) { LVINFO_BUFFER(4); if (lens_info.IS) { int is_color = lens_info.IS == 0 ? COLOR_WHITE : // IS off lens_info.IS == 4 ? COLOR_GRAY(50) : // IS active, but not engaged lens_info.IS == 8 ? COLOR_BLACK : // IS disabled on sigma lenses? lens_info.IS == 0xC ? COLOR_CYAN : // IS starting? lens_info.IS == 0xE ? COLOR_WHITE : // IS active and kicking COLOR_RED; // unknown snprintf(buffer, sizeof(buffer), "IS"); item->color_fg = is_color; } } static LVINFO_UPDATE_FUNC(av_update) { LVINFO_BUFFER(8); if (lens_info.raw_aperture && lens_info.lens_exists) { snprintf(buffer, sizeof(buffer), lens_format_aperture(lens_info.raw_aperture)); } if (CONTROL_BV) { /* mark the "exposure override" mode */ item->color_bg = 18; } } static LVINFO_UPDATE_FUNC(tv_update) { LVINFO_BUFFER(16); if (is_bulb_mode()) { snprintf(buffer, sizeof(buffer), "BULB"); } else if (is_movie_mode()) { snprintf(buffer, sizeof(buffer), "%s", lens_format_shutter_reciprocal(get_current_shutter_reciprocal_x1000(), 2)); } else if (lens_info.raw_shutter) { snprintf(buffer, sizeof(buffer), "%s", lens_format_shutter(lens_info.raw_shutter)); } if (CONTROL_BV) { /* mark the "exposure override" mode */ item->color_bg = 18; } } static int (*dual_iso_is_active)() = MODULE_FUNCTION(dual_iso_is_active); static int (*dual_iso_get_recovery_iso)() = MODULE_FUNCTION(dual_iso_get_recovery_iso); static LVINFO_UPDATE_FUNC(iso_update) { LVINFO_BUFFER(16); if (hdr_video_enabled()) { int iso_low, iso_high; hdr_get_iso_range(&iso_low, &iso_high); iso_low = raw2iso(get_effective_hdr_iso_for_display(iso_low)); iso_high = raw2iso(get_effective_hdr_iso_for_display(iso_high)); snprintf(buffer, sizeof(buffer), SYM_ISO"%d/%d", iso_low, iso_high); } else if (dual_iso_is_active()) { snprintf(buffer, sizeof(buffer), SYM_ISO"%d/%d", raw2iso(lens_info.iso_analog_raw), raw2iso(dual_iso_get_recovery_iso()) ); } else if (is_movie_mode()) { snprintf(buffer, sizeof(buffer), SYM_ISO); if (!lens_info.raw_iso) { /* Auto ISO? */ STR_APPEND(buffer, "A"); } /* this includes ML ISO digital gains, if any */ int iso_equiv_raw = lens_info.iso_equiv_raw; #ifdef FEATURE_FPS_OVERRIDE iso_equiv_raw += fps_get_iso_correction_evx8(); #endif int digital_gain = iso_equiv_raw - lens_info.raw_iso; if (digital_gain > 1) { /* avoid ISO 125, 250... */ item->color_fg = COLOR_ORANGE; } int lv_iso = (FRAME_ISO & 0xFF) + (get_htp() ? 8 : 0); if (ABS(lv_iso - lens_info.raw_iso) > 3) { /* for some reason, the ISO being used is different from the one reported in properties */ iso_equiv_raw += lv_iso - lens_info.raw_iso; } if (raw_lv_is_enabled()) { /* the only ISOs used are the full-stop ones; * digital gain is only applied to display, not recorded */ iso_equiv_raw = (lv_iso+3)/8*8; item->color_fg = COLOR_WHITE; } int iso = raw2iso(iso_equiv_raw); if (iso > 1600) { /* think twice before increasing ISO above this value */ item->color_fg = COLOR_ORANGE; } STR_APPEND(buffer, "%d", iso); } else /* photo mode */ { if (!lens_info.raw_iso && lens_info.iso_auto) { snprintf(buffer, sizeof(buffer), SYM_ISO"A%d", raw2iso(lens_info.raw_iso_auto)); } else { snprintf(buffer, sizeof(buffer), "%s", lens_format_iso(lens_info.raw_iso)); } } if (get_htp()) { STR_APPEND(buffer, "D+"); } #ifdef ISO_ADJUSTMENT_ACTIVE if (ISO_ADJUSTMENT_ACTIVE) { item->color_bg = COLOR_LIGHT_BLUE; } else #endif if (CONTROL_BV) { /* mark the "exposure override" mode */ item->color_bg = 18; } } static LVINFO_UPDATE_FUNC(wb_update) { LVINFO_BUFFER(16); if( lens_info.wb_mode == WB_KELVIN ) { snprintf(buffer, sizeof(buffer), "%dK", lens_info.kelvin); } else { snprintf(buffer, sizeof(buffer), "%s", (uniwb_is_active() ? "UniWB" : (lens_info.wb_mode == 0 ? "AutoWB" : (lens_info.wb_mode == 1 ? "Sunny" : (lens_info.wb_mode == 2 ? "Cloudy" : (lens_info.wb_mode == 3 ? "Tungst." : (lens_info.wb_mode == 4 ? "Fluor." : (lens_info.wb_mode == 5 ? "Flash" : (lens_info.wb_mode == 6 ? "Custom" : (lens_info.wb_mode == 8 ? "Shade" : "unk"))))))))) ); } int gm = lens_info.wbs_gm; int ba = lens_info.wbs_ba; if (gm || ba) { /* a dot is smaller than a space */ if (buffer[strlen(buffer)-1] != '.') { STR_APPEND(buffer, "."); } } if (gm) { STR_APPEND(buffer, "%s%d", gm > 0 ? "G" : "M", ABS(gm)); } if (ba) { STR_APPEND(buffer, "%s%d", ba > 0 ? "A" : "B", ABS(ba)); } } /* in focus.c */ extern LVINFO_UPDATE_FUNC(focus_dist_update); static LVINFO_UPDATE_FUNC(af_mf_update) { LVINFO_BUFFER(4); snprintf(buffer, sizeof(buffer), is_manual_focus() ? "MF" : "AF"); } static LVINFO_UPDATE_FUNC(batt_update) { item->height = 30; #ifdef CONFIG_BATTERY_INFO item->width = 70; #else item->width = 20; #endif item->custom_drawing = 1; if (can_draw) { int xr = item->x - item->width/2; int y_origin = item->y; xr += 4; #ifdef CONFIG_BATTERY_INFO int bat = GetBatteryLevel(); #else int bat = battery_level_bars == 0 ? 5 : battery_level_bars == 1 ? 30 : 100; #endif int col = battery_level_bars == 0 ? COLOR_RED : battery_level_bars == 1 ? COLOR_YELLOW : COLOR_WHITE; #ifdef CONFIG_BATTERY_INFO bmp_printf(SHADOW_FONT(FONT(FONT_MED, col, item->color_bg)), xr+16, y_origin + 30 - font_med.height, "%d%%", bat); #endif bat = bat * 20 / 100; bmp_fill(col, xr+2, y_origin-3, 8, 3); bmp_draw_rect(col, xr-2, y_origin, 16, 27); bmp_draw_rect(col, xr-1, y_origin + 1, 14, 25); bmp_fill(col, xr+2, y_origin + 23 - bat, 8, bat); } } static LVINFO_UPDATE_FUNC(ae_update) { LVINFO_BUFFER(8); switch(shooting_mode) { /* in semi-auto modes, this shows the exposure compensation dialed by user (all fine) */ case SHOOTMODE_P: case SHOOTMODE_AV: case SHOOTMODE_TV: { int ae = lens_info.ae * 10/8; snprintf(buffer, sizeof(buffer), "%s%d.%d", FMT_FIXEDPOINT1S(ae)); /* note: it may be unclear what this is at first sight; maybe some symbol in the font can help? */ break; } /* in M mode, the behavior is not consistent across cameras (on some it's 0, on others it's Canon metering) */ /* it may be a better idea to use AE_VALUE, but for me, Canon metering is completely irrelevant */ /* so I recommend looking at the ETTR indicator and histogram instead */ /* in other modes, no idea */ /* note: merging this indicator with the ETTR one may be a good idea (maybe also with a nice graphical meter) */ default: break; } } static struct lvinfo_item info_items[] = { /* Top bar */ { .name = "Clock", .which_bar = LV_TOP_BAR_ONLY, .update = clock_update, .preferred_position = -50, .priority = -1, }, { .name = "Disp preset", .which_bar = LV_TOP_BAR_ONLY, .update = disp_preset_update, }, { .name = "Pic Quality", .which_bar = LV_TOP_BAR_ONLY, .update = picq_update, }, { .name = "ALO/HTP", .which_bar = LV_TOP_BAR_ONLY, .update = alo_htp_update, .priority = -1, }, #ifdef FEATURE_PICSTYLE { .name = "Pic.Style", .which_bar = LV_TOP_BAR_ONLY, .update = picstyle_update, .priority = -1, }, #endif { .name = "Temperature", .which_bar = LV_TOP_BAR_ONLY, .update = temp_update, .priority = 1, }, { .name = "MVI number", .which_bar = LV_TOP_BAR_ONLY, .update = mvi_number_update, }, { .name = "FPS", .which_bar = LV_TOP_BAR_ONLY, .update = fps_update, }, { .name = "Free space", .which_bar = LV_TOP_BAR_ONLY, .update = free_space_update, }, /* Bottom bar */ { .name = "Mode", .which_bar = LV_BOTTOM_BAR_ONLY, .update = mode_update, .priority = 1, .preferred_position = -128, }, { .name = "Focal len", .which_bar = LV_BOTTOM_BAR_ONLY, .update = focal_len_update, }, { .name = "IS", .which_bar = LV_BOTTOM_BAR_ONLY, .update = is_update, .priority = -1, }, { .name = "Aperture", .which_bar = LV_BOTTOM_BAR_ONLY, .update = av_update, .priority = 1, }, { .name = "Shutter", .which_bar = LV_BOTTOM_BAR_ONLY, .update = tv_update, .priority = 1, }, { .name = "ISO", .which_bar = LV_BOTTOM_BAR_ONLY, .update = iso_update, .priority = 1, }, { .name = "White Balance", .which_bar = LV_BOTTOM_BAR_ONLY, .update = wb_update, .priority = 1, }, { .name = "Focus dist", .which_bar = LV_BOTTOM_BAR_ONLY, .update = focus_dist_update, }, { .name = "AF/MF", .which_bar = LV_BOTTOM_BAR_ONLY, .update = af_mf_update, .priority = -1, }, { .name = "Exposure Compensation", .which_bar = LV_BOTTOM_BAR_ONLY, .update = ae_update, .preferred_position = 50, }, { .name = "Battery", .which_bar = LV_BOTTOM_BAR_ONLY, .update = batt_update, .preferred_position = 127, }, }; static void lens_info_init() { lvinfo_add_items(info_items, COUNT(info_items)); } INIT_FUNC("lens_info", lens_info_init);