From e636d9aba84516fd9c823c4af5c1cad56367af6a Mon Sep 17 00:00:00 2001 From: IanSB Date: Tue, 10 Dec 2019 23:06:53 +0000 Subject: [PATCH] Add pixel aspect ratio enforcement and fix pi overscan bug --- src/geometry.c | 197 +++++++++++++++++++++++++++++++--------------- src/geometry.h | 2 + src/osd.c | 4 +- src/rgb_to_hdmi.c | 19 +++-- 4 files changed, 149 insertions(+), 73 deletions(-) diff --git a/src/geometry.c b/src/geometry.c index 7d7a6316..89f12104 100644 --- a/src/geometry.c +++ b/src/geometry.c @@ -42,10 +42,12 @@ static param_t params[] = { { SETUP_MODE, "Setup Mode", "setup_mode", 0,NUM_SETUP-1, 1 }, { H_OFFSET, "H Offset", "h_offset", 0, 512, 4 }, { V_OFFSET, "V Offset", "v_offset", 0, 512, 1 }, - { MIN_H_WIDTH, "Min H Width", "min_h_width", 200, 1920, 8 }, + { MIN_H_WIDTH, "Min H Width", "min_h_width", 150, 1920, 8 }, {MIN_V_HEIGHT, "Min V Height", "min_v_height", 150, 1200, 2 }, - { MAX_H_WIDTH, "Max H Width", "max_h_width", 100, 1920, 8 }, - {MAX_V_HEIGHT, "Max V Height", "max_v_height", 100, 1200, 2 }, + { MAX_H_WIDTH, "Max H Width", "max_h_width", 200, 1920, 8 }, + {MAX_V_HEIGHT, "Max V Height", "max_v_height", 200, 1200, 2 }, + { H_ASPECT, "H Pixel Aspect", "h_aspect", 0, 8, 1 }, + { V_ASPECT, "V Pixel Aspect", "v_aspect", 0, 8, 1 }, { FB_SIZEX2, "FB Size", "fb_size", 0, 3, 1 }, { FB_BPP, "FB Bits/Pixel", "fb_bits_pixel", 4, 8, 4 }, { CLOCK, "Clock Frequency", "clock_frequency", 1000000, 40000000, 1000 }, @@ -61,10 +63,12 @@ typedef struct { int setup_mode; // dummy entry for setup int h_offset; // horizontal offset (in psync clocks) int v_offset; // vertical offset (in lines) - int min_h_width; // active horizontal width (in 8-bit characters) - int min_v_height; // active vertical height (in lines) - int max_h_width; // framebuffer width in pixels - int max_v_height; // framebuffer height (in pixels, before any doubling is applied) + int min_h_width; // active horizontal width (in 8-bit characters) + int min_v_height; // active vertical height (in lines) + int max_h_width; // framebuffer width in pixels + int max_v_height; // framebuffer height (in pixels, before any doubling is applied) + int h_aspect; // horizontal pixel aspect ratio + int v_aspect; // vertical pixel aspect ratio int fb_sizex2; // if 1 then double frame buffer height if 2 double width if 3 then both int fb_bpp; // framebuffer bits per pixel int clock; // cpld clock (in Hz) @@ -94,6 +98,8 @@ void geometry_init(int version) { mode7_geometry.min_v_height = 270 & 0xfffffffe; mode7_geometry.max_h_width = 504 & 0xfffffff8; mode7_geometry.max_v_height = 270 & 0xfffffffe; + mode7_geometry.h_aspect = 3; + mode7_geometry.v_aspect = 4; mode7_geometry.fb_sizex2 = 1; mode7_geometry.fb_bpp = 4; mode7_geometry.clock = 12000000; @@ -109,6 +115,8 @@ void geometry_init(int version) { default_geometry.min_v_height= 270 & 0xfffffffe; default_geometry.max_h_width = 672 & 0xfffffff8; default_geometry.max_v_height= 270 & 0xfffffffe; + default_geometry.h_aspect = 1; + default_geometry.v_aspect = 2; default_geometry.fb_sizex2 = 1; default_geometry.fb_bpp = 8; default_geometry.clock = 16000000; @@ -159,6 +167,10 @@ int geometry_get_value(int num) { return geometry->max_h_width & 0xfffffff8; case MAX_V_HEIGHT: return geometry->max_v_height & 0xfffffffe; + case H_ASPECT: + return geometry->h_aspect; + case V_ASPECT: + return geometry->v_aspect; case FB_SIZEX2: return geometry->fb_sizex2; case FB_BPP: @@ -224,6 +236,12 @@ void geometry_set_value(int num, int value) { case MAX_V_HEIGHT: geometry->max_v_height = value & 0xfffffffe; break; + case H_ASPECT: + geometry->h_aspect = value; + break; + case V_ASPECT: + geometry->v_aspect = value; + break; case FB_SIZEX2: geometry->fb_sizex2 = value; break; @@ -302,6 +320,9 @@ void geometry_get_fb_params(capture_info_t *capinfo) { int geometry_min_v_height = geometry->min_v_height; int geometry_max_h_width = geometry->max_h_width; int geometry_max_v_height = geometry->max_v_height; + + int h_aspect = geometry->h_aspect; + int v_aspect = geometry->v_aspect; //if (overscan == OVERSCAN_AUTO && (geometry->setup_mode == SETUP_NORMAL || geometry->setup_mode == SETUP_CLOCK)) { //reduce max area by 4% to hide offscreen imperfections @@ -358,18 +379,76 @@ void geometry_get_fb_params(capture_info_t *capinfo) { //log_info("unadujusted integer = %d, %d, %d, %d, %d, %d", geometry_h_offset, geometry_v_offset, geometry_min_h_width, geometry_min_v_height, geometry_max_h_width, geometry_max_v_height); - if (scaling == SCALING_INTEGER && overscan == OVERSCAN_AUTO && (geometry->setup_mode == SETUP_NORMAL || geometry->setup_mode == SETUP_CLOCK)) { - int h_size43_adj = h_size43; - if (m7scaling == M7_UNEVEN) { - h_size43_adj = mode7 ? (h_size43 * 3 / 4) : h_size43; + if (geometry->setup_mode == SETUP_MIN + || (overscan == OVERSCAN_MIN && (geometry->setup_mode == SETUP_NORMAL || geometry->setup_mode == SETUP_CLOCK))) { + geometry_max_h_width = geometry_min_h_width; + geometry_max_v_height = geometry_min_v_height; + } + + if (overscan == OVERSCAN_HALF && (geometry->setup_mode == SETUP_NORMAL || geometry->setup_mode == SETUP_CLOCK)) { + geometry_max_h_width = (((geometry_max_h_width - geometry_min_h_width) >> 1) + geometry_min_h_width) & 0xfffffff8; + geometry_max_v_height = (((geometry_max_v_height - geometry_min_v_height) >> 1) + geometry_min_v_height) & 0xfffffffe; + geometry_h_offset = geometry_h_offset - (((geometry_max_h_width - geometry_min_h_width) >> 3) << 2); + geometry_v_offset = geometry_v_offset - ((geometry_max_v_height - geometry_min_v_height) >> 1); + geometry_min_h_width = geometry_max_h_width; + geometry_min_v_height = geometry_max_v_height; + } + + if (geometry->setup_mode == SETUP_MAX + || (overscan == OVERSCAN_MAX && (geometry->setup_mode == SETUP_NORMAL || geometry->setup_mode == SETUP_CLOCK)) + || (overscan == OVERSCAN_AUTO && (scaling == SCALING_MANUAL43 || scaling == SCALING_MANUAL) && (geometry->setup_mode == SETUP_NORMAL || geometry->setup_mode == SETUP_CLOCK))) { + geometry_h_offset = geometry_h_offset - (((geometry_max_h_width - geometry_min_h_width) >> 3) << 2); + geometry_v_offset = geometry_v_offset - ((geometry_max_v_height - geometry_min_v_height) >> 1); + geometry_min_h_width = geometry_max_h_width; + geometry_min_v_height = geometry_max_v_height; + } + + int h_size43_adj = h_size43; + if (mode7 && m7scaling == M7_UNEVEN) { + h_size43_adj = h_size43 * 3 / 4; + if (h_aspect !=0 && v_aspect !=0) { + h_aspect = 1; + v_aspect = 2; } - int hs = h_size43_adj / geometry_min_h_width; - int new_geometry_min_h_width = h_size43_adj / hs; + } + + int hscale = h_size43_adj / geometry_min_h_width; + int vscale = v_size43 / geometry_min_v_height; + + if (scaling == SCALING_INTEGER) { + if (h_aspect != 0 && v_aspect !=0) { + int new_hs = hscale; + int new_vs = vscale; + double h_ratio; + double v_ratio; + int abort_count = 0; + do { + h_ratio = (double)hscale / h_aspect; + v_ratio = (double)vscale / v_aspect; + if (h_ratio != v_ratio) { + if (h_ratio > v_ratio) { + new_hs = ((int)v_ratio) * h_aspect; + } else { + new_vs = ((int)h_ratio) * v_aspect; + } + //log_info("Aspect doesn't match: %d, %d, %d, %d, %f, %f, %d, %d", hscale, vscale, h_aspect, v_aspect, h_ratio, v_ratio, new_hs, new_vs); + if (new_hs !=0 && new_vs != 0) { + hscale = new_hs; + vscale = new_vs; + + } + //log_info("Aspect after loop: %d, %d", hscale, vscale); + } + abort_count++; + } while (new_hs !=0 && new_vs != 0 && h_ratio != v_ratio && abort_count < 10); + } + //log_info("Final aspect: %d, %d", hscale, vscale); + + int new_geometry_min_h_width = h_size43_adj / hscale; if (new_geometry_min_h_width > geometry_max_h_width) { new_geometry_min_h_width = geometry_max_h_width; } - int vs = v_size43 / geometry_min_v_height; - int new_geometry_min_v_height = v_size43 / vs; + int new_geometry_min_v_height = v_size43 / vscale; if (new_geometry_min_v_height > geometry_max_v_height) { new_geometry_min_v_height = geometry_max_v_height; } @@ -378,30 +457,7 @@ void geometry_get_fb_params(capture_info_t *capinfo) { geometry_min_h_width = new_geometry_min_h_width; geometry_min_v_height = new_geometry_min_v_height; } - - if ((overscan == OVERSCAN_MIN && (geometry->setup_mode == SETUP_NORMAL || geometry->setup_mode == SETUP_CLOCK)) || geometry->setup_mode == SETUP_MIN) { - geometry_max_h_width = geometry_min_h_width; - geometry_max_v_height = geometry_min_v_height; - } - - if (overscan == OVERSCAN_HALF && (geometry->setup_mode == SETUP_NORMAL || geometry->setup_mode == SETUP_CLOCK)) { - geometry_max_h_width = (((geometry_max_h_width - geometry_min_h_width) >> 1) + geometry_min_h_width) & 0xfffffff8; - geometry_max_v_height = (((geometry_max_v_height - geometry_min_v_height) >> 1) + geometry_min_v_height) & 0xfffffffe; - geometry_h_offset = geometry_h_offset - (((geometry_max_h_width - geometry_min_h_width) >> 3) << 2); - geometry_v_offset = geometry_v_offset - ((geometry_max_v_height - geometry_min_v_height) >> 1); - geometry_min_h_width = geometry_max_h_width; - geometry_min_v_height = geometry_max_v_height; - } - - if (geometry->setup_mode == SETUP_MAX - || (overscan == OVERSCAN_MAX && (geometry->setup_mode == SETUP_NORMAL || geometry->setup_mode == SETUP_CLOCK)) - || (overscan == OVERSCAN_AUTO && (scaling == SCALING_MANUAL43 || scaling == SCALING_MANUAL) && (geometry->setup_mode == SETUP_NORMAL || geometry->setup_mode == SETUP_CLOCK))) { - geometry_h_offset = geometry_h_offset - (((geometry_max_h_width - geometry_min_h_width) >> 3) << 2); - geometry_v_offset = geometry_v_offset - ((geometry_max_v_height - geometry_min_v_height) >> 1); - geometry_min_h_width = geometry_max_h_width; - geometry_min_v_height = geometry_max_v_height; - } - + //log_info(" adujusted integer = %d, %d, %d, %d", geometry_h_offset, geometry_v_offset, geometry_min_h_width, geometry_min_v_height); if (geometry_h_offset < 0) { @@ -415,29 +471,10 @@ void geometry_get_fb_params(capture_info_t *capinfo) { if (capinfo->h_offset < 0) { capinfo->h_offset = 0; } + capinfo->v_offset = geometry_v_offset; capinfo->chars_per_line = (geometry_min_h_width >> 3) << double_width; capinfo->nlines = geometry_min_v_height; - capinfo->width = geometry_max_h_width << double_width; //adjust the width for capinfo according to sizex2 setting; - capinfo->height = geometry_max_v_height << double_height; //adjust the height for capinfo according to sizex2 setting - - - - int standard_width = geometry_min_h_width; - if (scaling == SCALING_INTEGER && m7scaling == M7_UNEVEN) { - standard_width = mode7 ? (geometry_min_h_width * 4 / 3) : geometry_min_h_width; // workaround mode 7 width so it looks like other modes - } - int standard_height = geometry_min_v_height; - int adjusted_width = geometry_min_h_width << double_width; - int adjusted_height = geometry_min_v_height << double_height; - - double hscalef = (double) h_size43 / standard_width; - int hscale = (int) hscalef; - int hborder = ((h_size - standard_width * hscale) << double_width) / hscale; // (h_size - adjusted_width * hscale) / hscale; - - double vscalef = (double) v_size43 / standard_height; - int vscale = (int) vscalef; - int vborder = ((v_size - standard_height * vscale) << double_height) / vscale; //log_info("scaling size = %d, %d, %d, %f",standard_width, adjusted_width, adjusted_height, ratio); //log_info("scaling h = %d, %d, %f, %d, %d, %d, %d",h_size, h_size43, hscalef, hscale, hborder, hborder43, newhborder43); @@ -445,19 +482,52 @@ void geometry_get_fb_params(capture_info_t *capinfo) { caphscale = 2; capvscale = 2; - + + int standard_width = geometry_min_h_width; + if (m7scaling == M7_UNEVEN) { + standard_width = mode7 ? (geometry_min_h_width * 4 / 3) : geometry_min_h_width; // workaround mode 7 width so it looks like other modes + } + int standard_height = geometry_min_v_height; + switch (scaling) { case SCALING_INTEGER: + { + + int adjusted_width = geometry_min_h_width << double_width; + int adjusted_height = geometry_min_v_height << double_height; + + int hborder = ((h_size - standard_width * hscale) << double_width) / hscale; + if ((hborder + adjusted_width) > h_size) { + log_info("Handling invalid H ratio"); + hborder = (h_size - adjusted_width) / hscale; + } + + int vborder = ((v_size - standard_height * vscale) << double_height) / vscale; + if ((vborder + adjusted_height) > v_size) { + log_info("Handling invalid V ratio"); + vborder = (v_size - adjusted_height) / vscale; + } + capinfo->width = adjusted_width + hborder; capinfo->height = adjusted_height + vborder; - caphscale = (h_size << 1) / capinfo->width; - capvscale = (v_size << 1) / capinfo->height; + + caphscale = (h_size << 1) / capinfo->width; + capvscale = (v_size << 1) / capinfo->height; + } break; case SCALING_MANUAL43: + { + double hscalef = (double) h_size43 / standard_width; + double vscalef = (double) v_size43 / standard_height; capinfo->width = (geometry_max_h_width << double_width ) + (int)((double)((h_size - h_size43) << double_width) / hscalef); capinfo->height = (geometry_max_v_height << double_height) + (int)((double)((v_size - v_size43) << double_height) / vscalef); + } break; case SCALING_MANUAL: + { + capinfo->width = geometry_max_h_width << double_width; //adjust the width for capinfo according to sizex2 setting; + capinfo->height = geometry_max_v_height << double_height; //adjust the height for capinfo according to sizex2 setting + } break; }; @@ -470,9 +540,6 @@ void geometry_get_fb_params(capture_info_t *capinfo) { } //log_info("size= %d, %d, %d, %d, %d, %d, %d",capinfo->chars_per_line, capinfo->nlines, geometry_min_h_width, geometry_min_v_height,capinfo->width, capinfo->height, capinfo->sizex2); - - - } int get_hscale() { diff --git a/src/geometry.h b/src/geometry.h index cd068687..f480afbf 100644 --- a/src/geometry.h +++ b/src/geometry.h @@ -32,6 +32,8 @@ enum { MIN_V_HEIGHT, MAX_H_WIDTH, MAX_V_HEIGHT, + H_ASPECT, + V_ASPECT, FB_SIZEX2, FB_BPP, CLOCK, diff --git a/src/osd.c b/src/osd.c index 68070612..568f945d 100644 --- a/src/osd.c +++ b/src/osd.c @@ -2776,7 +2776,7 @@ void osd_update(uint32_t *osd_base, int bytes_per_line) { allow1220font = 1; break; } - if (((capinfo->sizex2 & 1) || capinfo->height >=400) && (bufferCharWidth >= LINELEN) && allow1220font) { // if frame buffer is large enough and not 8bpp use SAA5050 font + if (((capinfo->sizex2 & 1) && capinfo->nlines >= NLINES * 10) && (bufferCharWidth >= LINELEN) && allow1220font) { // if frame buffer is large enough and not 8bpp use SAA5050 font for (int line = 0; line < NLINES; line++) { int attr = attributes[line]; int len = (attr & ATTR_DOUBLE_SIZE) ? (LINELEN >> 1) : LINELEN; @@ -2966,7 +2966,7 @@ void osd_update_fast(uint32_t *osd_base, int bytes_per_line) { allow1220font = 1; break; } - if (((capinfo->sizex2 & 1) || capinfo->height >=400) && (bufferCharWidth >= LINELEN) && allow1220font) { // if frame buffer is large enough and not 8bpp use SAA5050 font + if (((capinfo->sizex2 & 1) && capinfo->nlines >= NLINES * 10) && (bufferCharWidth >= LINELEN) && allow1220font) { // if frame buffer is large enough and not 8bpp use SAA5050 font for (int line = 0; line < NLINES; line++) { int attr = attributes[line]; int len = (attr & ATTR_DOUBLE_SIZE) ? (LINELEN >> 1) : LINELEN; diff --git a/src/rgb_to_hdmi.c b/src/rgb_to_hdmi.c index 6b7152b7..452cd230 100644 --- a/src/rgb_to_hdmi.c +++ b/src/rgb_to_hdmi.c @@ -302,16 +302,25 @@ static int last_height = -1; int width = capinfo->width >> ((capinfo->sizex2 & 2) >> 1); int height = capinfo->height >> (capinfo->sizex2 & 1); if (!mode7 || get_m7scaling() == M7_EVEN) { - h_overscan = h_size - (h_size / width * width); + h_overscan = (h_size - (h_size / width * width)); } - v_overscan = v_size - (v_size / height * height); + v_overscan = (v_size - (v_size / height * height)); + + if (h_overscan != 0) { // add 1 if non zero to work around scaler issues + h_overscan += 1; + } + if (v_overscan != 0) { // add 1 if non zero to work around scaler issues + v_overscan += 1; + } + } - if (h_overscan > 8) { + + if (h_overscan > 32) { log_info("**** H overscan too big = %d", h_overscan); //sanity check h_overscan = 0; } - if (v_overscan > 8) { + if (v_overscan > 32) { log_info("**** V overscan too big = %d", v_overscan); //sanity check v_overscan = 0; } @@ -322,10 +331,8 @@ static int last_height = -1; int top_overscan = v_overscan >> 1; int bottom_overscan = top_overscan + (v_overscan & 1); - log_info("Overscan L=%d, R=%d, T=%d, B=%d",left_overscan, right_overscan, top_overscan, bottom_overscan); - /* Initialise a framebuffer... */ RPI_PropertyInit(); RPI_PropertyAddTag(TAG_ALLOCATE_BUFFER, 0x02000000);