/** \file * Common functions for image buffers * http://magiclantern.wikia.com/wiki/VRAM */ #include "dryos.h" #include "property.h" #include "propvalues.h" #include "bmp.h" #include "menu.h" #include "shoot.h" #include "zebra.h" #include "raw.h" //~ #define CONFIG_DEBUGMSG 1 /** BMP total: 960x540 BMP crop : 720x480 LCD: BMP and LV are usually 720x480, 1:1 HDMI: BMP may have half-resolution compared to LV, in one or both directions ___________________________________________________________________________________________________________________________ |HDMI: LV0,0 BMy-30| | | | | | | |BMx-120 BM0,0| BMx720 | BMx840 | |----------------------+---------------------------------------------------------------------------+----------------------| | |LCD: LV0,0 | | | os.x0,y0 _ _ _ _ _|_\ _____________________________________________________________________ | _ _ _ _ | | | /|___________________________16:9 gray/black bar_______________________| | A | | | | | | | | | | | | | | | | here we usually | | | | | | | have pillarboxes | | | | | | | included in LV | | HD (recording) buffer | | | | | | | may or may nt include the 16:9 bars (camera-specific) | | | | | | | but never includes pillarboxes | | | | | | | | | os.y_ex | | | | | | | (in BM space) | | | | | | | | | | | | | | | | | | | | | | | | |_____________________________________________________________________| | | | | | |_____________________________________________________________________| | _ _V_ _ | | | : : | | | BMy480 | :<----------------------- os.x_ex (BM space) ------------------------>: | | |----------------------+---------------------------------------------------------------------------+----------------------| | | | | | | | | |_______________BMy510_|___________________________________________________________________________|______________________| */ // cached LUTs for BM2LV-like macros int bm2lv_x_cache[BMP_W_PLUS - BMP_W_MINUS]; //~ int bm2n_x_cache [BMP_W_PLUS - BMP_W_MINUS]; //~ int bm2hd_r_cache[BMP_H_PLUS - BMP_H_MINUS]; int y_times_BMPPITCH_cache[BMP_H_PLUS - BMP_H_MINUS]; static void vram_update_luts() { for (int x = BMP_W_MINUS; x < BMP_W_PLUS; x++) { bm2lv_x_cache[x - BMP_W_MINUS] = BM2LV_Xu(x); //~ bm2n_x_cache[x - BMP_W_MINUS] = BM2N_Xu(x); } for (int y = BMP_H_MINUS; y < BMP_H_PLUS; y++) { //~ bm2hd_r_cache[y - BMP_H_MINUS] = BM2HD_Ru(y); y_times_BMPPITCH_cache[y - BMP_H_MINUS] = y * BMPPITCH; } } struct vram_info vram_lv = { .pitch = 720 * 2, .width = 720, .height = 480, }; struct vram_info vram_hd = { .pitch = 1056 * 2, .width = 1056, .height = 704, }; /*struct vram_info vram_bm = { .pitch = 960, .width = 720, .height = 480, };*/ struct trans2d bm2lv = { .tx = 0, .ty = 0, .sx = 1024, .sy = 1024, }; struct trans2d lv2hd = { .tx = 0, .ty = 0, .sx = 2048, // dummy .sy = 2048, // dummy }; struct trans2d lv2raw = { .tx = 0, .ty = 0, .sx = 2048, .sy = 2048, }; // area from BMP where the LV image (3:2) is effectively drawn, without black bars // in this area we'll draw cropmarks, zebras and so on struct bmp_ov_loc_size os = { .x0 = 0, .y0 = 0, .x_ex = 480, .y_ex = 720, }; //~ int lv_ratio_num = 3; //~ int lv_ratio_den = 2; //~ int hd_ratio_num = 3; //~ int hd_ratio_den = 2; static int vram_params_dirty = 1; void vram_params_set_dirty() { vram_params_dirty = 1; bmp_mute_flag_reset(); afframe_set_dirty(); } static uint32_t hd_size = 0; static void vram_params_update_if_dirty() { #ifdef REG_EDMAC_WRITE_LV_ADDR // EDMAC sizes may update after prop handlers, so check if their values changed // if so, recompute all VRAM params hd_size = shamem_read(REG_EDMAC_WRITE_HD_ADDR + 8); static uint32_t prev_hd_size = 0; if (prev_hd_size != hd_size) vram_params_dirty = 1; prev_hd_size = hd_size; #endif if (vram_params_dirty) { BMP_LOCK( if (vram_params_dirty) { _update_vram_params(); vram_params_dirty = 0; } ) } } #if CONFIG_DEBUGMSG static int increment = 4; int* vram_params[] = { &increment, //~ &vram_bm.width, &vram_bm.height, &os.x0, &os.y0, &os.x_ex, &os.y_ex, &vram_lv.width, &vram_lv.height, &bm2lv.tx, &bm2lv.ty, &bm2lv.sx, &bm2lv.sy, &vram_hd.width, &vram_hd.height, //~ &hd_ratio_num, &hd_ratio_den , &lv2hd.tx, &lv2hd.ty, &lv2hd.sx, &lv2hd.sy, }; char vram_param_names[][12] = { "increment ", //~ "bmp.width ", "bmp.height", "os.x_left ", "os.y_top ", "os.x_ex ", "os.y_ex ", "lv.width ", "lv.height ", "bm2lv.tx ", "bm2lv.ty ", "bm2lv.sx ", "bm2lv.sy ", "hd.width ", "hd.height ", //~ "ratio_num ", "ratio_den ", "lv2hd.tx* ", "lv2hd.ty* ", "lv2hd.sx* ", "lv2hd.sy* ", }; #endif static int digital_zoom_ratio = 0; static int logical_connect=0; PROP_HANDLER(PROP_DIGITAL_ZOOM_RATIO) { digital_zoom_ratio = buf[0]; vram_params_set_dirty(); } PROP_HANDLER(PROP_LOGICAL_CONNECT) { logical_connect = buf[0]; vram_params_set_dirty(); } void _update_vram_params() { #if CONFIG_DEBUGMSG if (is_menu_active("VRAM")) return; #endif //~ msleep(100); // just to make sure all prop handlers finished after mode change if (!ext_monitor_hdmi) hdmi_code = 0; // 5D doesn't revert it, maybe other cameras too // force a redraw when you connect the external monitor static int prev_hdmi_code = 0; static int prev_EXT_MONITOR_RCA = 0; if (prev_hdmi_code != hdmi_code || prev_EXT_MONITOR_RCA != EXT_MONITOR_RCA) redraw(); prev_hdmi_code = hdmi_code; prev_EXT_MONITOR_RCA = EXT_MONITOR_RCA; // LV crop area (black bars) os.x0 = hdmi_code >= 5 ? 75 - 120 : (hdmi_code == 2 ? 40 : EXT_MONITOR_RCA ? (video_system_pal ? 40 : 40) : 0); os.y0 = hdmi_code >= 5 ? 0 - 30 : (hdmi_code == 2 ? 24 : EXT_MONITOR_RCA ? (video_system_pal ? 29 : 25) : 0); os.x_ex = hdmi_code >= 5 ? 810 : (hdmi_code == 2 || EXT_MONITOR_RCA) ? 640 : 720; os.y_ex = hdmi_code >= 5 ? 540 : (hdmi_code == 2 || EXT_MONITOR_RCA) ? 388 : 480; #if defined(CONFIG_4_3_SCREEN) if (!EXT_MONITOR_CONNECTED) { if (!lv) /* Playback, QR, in the middle of taking a picture... */ { os.y0 = 52; // black bar is at the top in play mode, 48 with additional info os.y_ex = 428; // 480 - os.y0; // screen height is 480px in total } else { os.y_ex = 424; // 480 * 8/9; // BMP is 4:3, image is 3:2; } } #else if (PLAY_MODE && hdmi_code == 2) { os.y_ex = 480 - 52; os.y0 = 52; } #endif os.x_max = os.x0 + os.x_ex; os.y_max = os.y0 + os.y_ex; os.off_43 = (os.x_ex - os.x_ex * 8/9) / 2; os.off_169 = (os.y_ex - os.y_ex * 3/2 * 9/16) / 2; os.off_1610 = (os.y_ex - os.y_ex * 3/2 * 10/16) / 2; // LV buffer (used for display) // these buffer sizes include any black bars #if defined(CONFIG_5DC) vram_lv.width = 540; vram_lv.height = 426; vram_lv.pitch = vram_lv.width * 2; os.x0 = 0; os.y0 = 26; os.x_ex = 720; os.y_ex = 480-52; os.x_max = os.x0 + os.x_ex; os.y_max = os.y0 + os.y_ex; os.off_43 = 0; os.off_169 = 0; os.off_1610 = 0; #elif defined(CONFIG_40D) //~ vram_lv.width = 720; // we only know the HD buffer for now... let's try to pretend it can be used as LV :) //~ vram_lv.height = 480; // we only know the HD buffer for now... let's try to pretend it can be used as LV :) vram_lv.width = 768; // real width is 1024 in yuv411, but ML code assumes yuv422 vram_lv.height = 680; vram_lv.pitch = vram_lv.width * 2; os.x0 = 0; //~ os.y0 = 0; os.y0 = lv ? 0 : 48; os.x_ex = 720; //~ os.y_ex = 480; os.y_ex = 480 - os.y0; os.x_max = os.x0 + os.x_ex; os.y_max = os.y0 + os.y_ex; os.off_43 = 0; os.off_169 = 0; os.off_1610 = 0; //~ os.off_169 = (os.y_ex - os.y_ex * 4/3 * 9/16) / 2; //~ os.off_1610 = (os.y_ex - os.y_ex * 4/3 * 10/16) / 2; #else #ifdef CONFIG_1100D vram_lv.width = 720; vram_lv.height = 240; #else vram_lv.width = hdmi_code >= 5 ? (is_movie_mode() && video_mode_resolution > 0 && video_mode_crop ? 960 : 1920) : EXT_MONITOR_RCA ? 540 : 720; vram_lv.height = hdmi_code >= 5 ? (is_movie_mode() && video_mode_fps > 30 ? 540 : 1080) : EXT_MONITOR_RCA ? (video_system_pal ? 572 : 480) : 480; #endif vram_lv.pitch = vram_lv.width * 2; #endif #ifdef CONFIG_5DC bm2lv.sx = 1024 * vram_lv.width / 720; bm2lv.sy = 1024 * vram_lv.height / (480-52); bm2lv.tx = 0; bm2lv.ty = -26; #elif CONFIG_40D bm2lv.sx = 1024 * vram_lv.width / 720; bm2lv.sy = 1024 * vram_lv.height / 480; //~ bm2lv.sy = 1024 * vram_lv.height / (480-48); bm2lv.tx = 0; bm2lv.ty = 0; //~ bm2lv.ty = (PLAY_MODE || QR_MODE)? -48 : 0; #else // bmp to lv transformation // LCD: (0,0) -> (0,0) // HDMI: (-120,-30) -> (0,0) and scaling factor is 2 bm2lv.tx = hdmi_code >= 5 ? 240 : EXT_MONITOR_RCA ? 4 : 0; bm2lv.ty = hdmi_code >= 5 ? (video_mode_resolution>0 ? 30 : 60) : 0; bm2lv.sx = hdmi_code >= 5 ? 2048 : EXT_MONITOR_RCA ? 768 : 1024; bm2lv.sy = 1024 * vram_lv.height / (hdmi_code >= 5 ? 540 : 480); // no black bars at top or bottom #endif //~ lv_ratio_num = hdmi_code >= 5 ? 16 : 3; //~ lv_ratio_den = hdmi_code >= 5 ? 9 : 2; // HD buffer (used for recording) //~ hd_ratio_num = recording ? (video_mode_resolution < 2 ? 16 : 4) : 3; //~ hd_ratio_den = recording ? (video_mode_resolution < 2 ? 9 : 3) : 2; #if defined(CONFIG_40D) vram_hd.width = vram_lv.width; vram_hd.height = vram_lv.height; vram_hd.pitch = vram_lv.pitch; //~ vram_hd.width = 1024; //~ vram_hd.height = 680; //~ vram_hd.pitch = vram_hd.width * 2; #elif defined(CONFIG_5DC) vram_hd.width = 1872; vram_hd.height = 1664; vram_hd.pitch = vram_lv.pitch; #else vram_hd.pitch = hd_size & 0xFFFF; vram_hd.width = vram_hd.pitch / 2; vram_hd.height = ((hd_size >> 16) & 0xFFFF) #if !defined(CONFIG_DIGIC_V) + 1 #endif ; #endif // gray bars for 16:9 or 4:3 #if defined(CONFIG_600D) int bar_x = is_movie_mode() && video_mode_resolution >= 2 ? os.off_43 : 0; int bar_y = is_movie_mode() && video_mode_resolution <= 1 ? os.off_169 : 0; #elif defined(CONFIG_1100D) || defined(CONFIG_DIGIC_V) int bar_x = 0; int bar_y = is_movie_mode() && video_mode_resolution == 1 ? os.off_169 : 0; #elif defined(CONFIG_500D) || defined(CONFIG_7D) //TODO: 650D/6D/EOSM used to have this one enabled too...which one is correct? int bar_x = 0; int bar_y = 0; #else int bar_x = RECORDING && video_mode_resolution >= 2 ? os.off_43 : 0; int bar_y = RECORDING && video_mode_resolution <= 1 ? os.off_169 : 0; #endif vram_update_luts(); lv2hd.sx = 1024 * vram_hd.width / BM2LV_DX(os.x_ex - bar_x * 2); lv2hd.sy = 1024 * vram_hd.height / BM2LV_DY(os.y_ex - bar_y * 2); // HD buffer does not contain pillarboxes, LV does // and HD may or may not contain bars // the offset needs to be specified in HD units lv2hd.tx = -LV2HD_DX(BM2LV_X(os.x0 + bar_x)); lv2hd.ty = -LV2HD_DY(BM2LV_Y(os.y0 + bar_y)); //~ #ifndef CONFIG_5DC if (!lv) // HD buffer not active, use LV instead { lv2hd.sx = lv2hd.sy = 1024; lv2hd.tx = lv2hd.ty = 0; vram_hd.pitch = vram_lv.pitch; vram_hd.width = vram_lv.width; vram_hd.height = vram_lv.height; } //~ #endif vram_update_luts(); #ifdef CONFIG_RAW_LIVEVIEW raw_set_dirty(); #endif } /*void trans_test() { for (int i = 0; i < 1000; i += 10) { // should say almost "i" bmp_printf(FONT_MED, 50, 50, "%d => %d %d %d %d %d %d ", i, BM2LV_X(LV2BM_X(i)), BM2LV_Y(LV2BM_Y(i)), LV2HD_X(HD2LV_X(i)), LV2HD_Y(HD2LV_Y(i)), BM2HD_X(HD2BM_X(i)), BM2HD_Y(HD2BM_Y(i)) ); msleep(300); } }*/ #include "bmp.h" void yuv422_buffer_check() { if (!YUV422_LV_BUFFER_DISPLAY_ADDR) { /* YUV buffer might be unitialized - e.g. when you start the camera in photo mode */ /* Going to PLAY mode will fix it only if you have some image there */ /* If not... we are out of luck, no idea what to do. */ /* This check is only needed if we want to display some YUV image created from scratch, * in playback mode (e.g. mlv_play, pic_view) */ bmp_printf(FONT_MED, 50, 200, "YUV buffer was not initialized.\n" "Please take a picture or go to LiveView." ); } } static inline void * get_yuv422buffer(int offset) { /* 5D3 1.2.3, 1.3.4 has quad-buffered LV, so the old switch can't work */ #if defined(CONFIG_1100D) || defined(CONFIG_6D) || defined(CONFIG_5D3_123) || defined(CONFIG_5D3_134) return (void*)CACHEABLE(YUV422_LV_BUFFER_DISPLAY_ADDR); // Good enough #else if (YUV422_LV_BUFFER_DISPLAY_ADDR == 0) { /* YUV buffer not initialized, can't display anything here */ return 0; } if (YUV422_LV_BUFFER_DISPLAY_ADDR == YUV422_LV_BUFFER_1) offset += 0; else if (YUV422_LV_BUFFER_DISPLAY_ADDR == YUV422_LV_BUFFER_2) offset += 1; else offset += 2; switch (offset) { case 0: case 3: default: return (void*)CACHEABLE(YUV422_LV_BUFFER_1); case 1: case 4: return (void*)CACHEABLE(YUV422_LV_BUFFER_2); case 2: case 5: return (void*)CACHEABLE(YUV422_LV_BUFFER_3); } #endif } void* get_lcd_422_buf() { return get_yuv422buffer(0); } static int fastrefresh_direction = 0; static unsigned old_buffer_pos = 0; void guess_fastrefresh_direction() { if (old_buffer_pos == YUV422_LV_BUFFER_DISPLAY_ADDR) return; if (old_buffer_pos == YUV422_LV_BUFFER_1 && YUV422_LV_BUFFER_DISPLAY_ADDR == YUV422_LV_BUFFER_2) fastrefresh_direction = 1; if (old_buffer_pos == YUV422_LV_BUFFER_1 && YUV422_LV_BUFFER_DISPLAY_ADDR == YUV422_LV_BUFFER_3) fastrefresh_direction = 0; old_buffer_pos = YUV422_LV_BUFFER_DISPLAY_ADDR; } void* get_fastrefresh_422_buf() { return get_yuv422buffer(fastrefresh_direction ? 1 : 2); } // Unfortunately this doesn't work on every 1100D model yet :( static void* get_fastrefresh_422_other_buf() { return get_yuv422buffer(fastrefresh_direction ? 2 : 1); } #ifdef CONFIG_500D int first_video_clip = 1; #endif struct vram_info * get_yuv422_vram() { vram_params_update_if_dirty(); if (digic_zoom_overlay_enabled()) // compute histograms and such on full-screen image { vram_lv.vram = (void*)CACHEABLE(YUV422_LV_BUFFER_1); return &vram_lv; } #ifdef CONFIG_DISPLAY_FILTERS int d = display_filter_enabled(); if (d) { uint32_t* src_buf; uint32_t* dst_buf; display_filter_get_buffers(&src_buf, &dst_buf); vram_lv.vram = (void*)(d == 1 ? dst_buf : src_buf); return &vram_lv; } #endif #ifdef CONFIG_500D // workaround for issue 1108 - zebras flicker on first clip if (lv && !is_movie_mode()) first_video_clip = 0; // starting in photo mode is OK if (first_video_clip) { vram_lv.vram = CACHEABLE(get_lcd_422_buf()); return &vram_lv; } #endif extern int lv_paused; if (gui_state == GUISTATE_PLAYMENU || lv_paused || QR_MODE) vram_lv.vram = CACHEABLE(get_lcd_422_buf()); else vram_lv.vram = CACHEABLE(get_fastrefresh_422_buf()); return &vram_lv; } struct vram_info * get_yuv422_hd_vram() { vram_params_update_if_dirty(); //~ #ifndef CONFIG_5DC if (!lv) // play/quickreview, HD buffer not active => use LV instead return get_yuv422_vram(); //~ #endif vram_hd.vram = CACHEABLE(YUV422_HD_BUFFER_DMA_ADDR); return &vram_hd; } void vram_clear_lv() { struct vram_info * lv_vram = get_yuv422_vram(); if (!lv_vram->vram) return; memset(lv_vram->vram, 0, lv_vram->height * lv_vram->pitch); } // those properties may signal a screen layout change PROP_HANDLER(PROP_HDMI_CHANGE) { vram_params_set_dirty(); } PROP_HANDLER(PROP_HDMI_CHANGE_CODE) { vram_params_set_dirty(); } PROP_HANDLER(PROP_USBRCA_MONITOR) { vram_params_set_dirty(); } PROP_HANDLER(PROP_LV_DISPSIZE) { vram_params_set_dirty(); } PROP_HANDLER(PROP_MVR_REC_START) { vram_params_set_dirty(); #ifdef CONFIG_500D static int prev; if (prev && !buf[0]) first_video_clip = 0; prev = buf[0]; #endif } PROP_HANDLER(PROP_SHOOTING_TYPE) { vram_params_set_dirty(); } PROP_HANDLER(PROP_LV_ACTION) { vram_params_set_dirty(); } PROP_HANDLER(PROP_GUI_STATE) { vram_params_set_dirty(); } PROP_HANDLER(PROP_VIDEO_MODE) { vram_params_set_dirty(); } PROP_HANDLER(PROP_LV_MOVIE_SELECT) { vram_params_set_dirty(); } #if CONFIG_DEBUGMSG static void vram_print( void * priv, int x, int y, int selected ) { unsigned menu_font = selected ? FONT(FONT_LARGE, COLOR_WHITE, COLOR_BLACK) : MENU_FONT; bmp_printf( menu_font, x, y, "%s = %d", vram_param_names[(int)priv], MEM(vram_params[(int)priv]) ); menu_draw_icon(x,y,MNI_NONE,0); } static void vram_toggle(void* priv, int delta) { MEM(vram_params[(int)priv]) += priv ? delta : SGN(delta); crop_set_dirty(1); //~ update_vram_params_calc(); } static void vram_toggle_fwd(void* priv, int unused) { vram_toggle(priv, increment); } static void vram_toggle_rev(void* priv, int unused) { vram_toggle(priv, -increment); } //~ static void vram_toggle_delta(void* priv) { menu_quinternary_toggle(&increment, 1); } #define VRAM_MENU_ENTRY(x) { \ .priv = (void *) x, \ .update = vram_print, \ .select = vram_toggle, \ .select_Q = vram_toggle_rev, \ }, \ static struct menu_entry vram_menus[] = { VRAM_MENU_ENTRY(0) VRAM_MENU_ENTRY(1) VRAM_MENU_ENTRY(2) VRAM_MENU_ENTRY(3) VRAM_MENU_ENTRY(4) VRAM_MENU_ENTRY(5) VRAM_MENU_ENTRY(6) VRAM_MENU_ENTRY(7) VRAM_MENU_ENTRY(8) VRAM_MENU_ENTRY(9) VRAM_MENU_ENTRY(10) VRAM_MENU_ENTRY(11) VRAM_MENU_ENTRY(12) VRAM_MENU_ENTRY(13) VRAM_MENU_ENTRY(14) VRAM_MENU_ENTRY(15) VRAM_MENU_ENTRY(16) //~ VRAM_MENU_ENTRY(17) //~ VRAM_MENU_ENTRY(18) //~ VRAM_MENU_ENTRY(19) //~ VRAM_MENU_ENTRY(20) }; void vram_init() { menu_add("VRAM", vram_menus, COUNT(vram_menus)); old_buffer_pos = YUV422_LV_BUFFER_1; } INIT_FUNC(__FILE__, vram_init); #endif