Allow position as a fraction of resolution

UEFI recommends y=.382 (1 - 1 / (golden ratio)), make this the default.
Fractional position starts with a decimal point: x=.5 y=.382
Pixel offset relative to the center is set as before: x=123 y=456
If the coordinates were omitted, old default was: x=0 y=0
This commit is contained in:
Lauri Kenttä
2026-03-28 21:51:13 +02:00
parent d40ce9f6c5
commit 683ee49110
4 changed files with 135 additions and 63 deletions

View File

@@ -8,12 +8,13 @@ boot=MS
# Multiple image lines may be present, in which case one will be picked by random.
# The image line may contain the following parts:
# Any of the following:
# - "n=(number)", a weight for this image in the randomization process. Default: n=1.
# - "x=(number)" or "x=keep", the x offset from the center. Default: x=0.
# - "y=(number)" or "y=keep", the y offset from the center. Default: y=0.
# - "o=(0|90|180|270|keep)", the screen orientation, degrees anticlockwise. Default: o=keep.
# - "x=(number) y=(number)" positions the image relative to the screen center.
# - "x=.(decimal) y=.(decimal)" positions the image as a fraction of the resolution.
# - "o=(0|90|180|270|keep)", the screen orientation, degrees anticlockwise.
# - "n=(number)", a weight for this image in the randomization process.
# - Default values: o=keep x=.5 y=.382 n=1
# One of the following:
# - "keep" to keep the firmware logo. Also keeps coordinates by default.
# - "keep" to keep the firmware logo (and coordinates, unless specified).
# - "remove" to remove the BGRT. Makes x and y meaningless.
# - "black" to use only a black image. Makes x and y meaningless.
# - "path=file.bmp" to read an image file.
@@ -21,9 +22,9 @@ boot=MS
# Examples:
# - image=remove
# - image=black
# - image= x=0 y=-200 path=topimage.bmp
# - image= x=0 y=.0 path=topimage.bmp
# - image= n=1 o=90 path=sideways.bmp
# - image= n=50 y=999999 o=keep path=probable.bmp
# - image= n=50 y=999999 path=probable.bmp
# The above examples together would produce
# - 1/54 chance for the default OS logo
# - 1/54 chance for black screen
@@ -31,7 +32,7 @@ boot=MS
# - 1/54 chance for splash.bmp, centered, orientation set to 90 degrees
# - 50/54 chance for probable.bmp, at the bottom edge, explicitly default orientation
# Default: just one image.
image= y=-200 path=splash.bmp
image= path=splash.bmp
# Preferred resolution. Use 0x0 for maximum and -1x-1 for original.
resolution=0x0

View File

@@ -68,54 +68,89 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir,
return TRUE;
}
static void SetBMPWithRandom(struct HackBGRT_config* config, int weight, enum HackBGRT_action action, int x, int y, int o, const CHAR16* path) {
static void SetBMPWithRandom(struct HackBGRT_config* config, const struct HackBGRT_image_config* image, int weight) {
config->image_weight_sum += weight;
UINT32 random = (((UINT64) Random() & 0xffffffff) * config->image_weight_sum) >> 32;
UINT32 limit = ((UINT64) 0xffffffff * weight) >> 32;
Log(config->debug, L"%s n=%d, action=%d, x=%d, y=%d, o=%d, path=%s, rand=%x/%x\n", random <= limit ? L"Using" : L"Skipping", weight, action, x, y, o, path, random, limit);
Log(
config->debug,
L"image n=%d, action=%d, x=%d (mode %d), y=%d (mode %d), o=%d, path=%s, rand=%x/%x, chosen=%d\n",
weight, image->action, image->x, image->x_mode, image->y, image->y_mode, image->orientation, image->path, random, limit, (random <= limit)
);
if (random <= limit) {
config->action = action;
config->image_path = path;
config->orientation = o;
config->image_x = x;
config->image_y = y;
config->image = *image;
}
}
static int ParseCoordinate(const CHAR16* str, enum HackBGRT_action action) {
if (str && ((L'0' <= str[0] && str[0] <= L'9') || str[0] == L'-')) {
return str[0] == L'-' ? -(int)Atoi(str+1) : (int)Atoi(str);
static void ParseCoordinate(const CHAR16* str, int* out_int, enum HackBGRT_coordinate_mode* out_mode) {
if (!str) {
return;
}
if ((str && StrnCmp(str, L"keep", 4) == 0) || action == HackBGRT_KEEP) {
return HackBGRT_coord_keep;
if (StrnCmp(str, L"keep", 4) == 0) {
*out_int = 0;
*out_mode = HackBGRT_COORDINATE_MODE_KEEP;
return;
}
if (str[0] == L'.' && L'0' <= str[1] && str[1] <= L'9') {
int result = 0, i = 1, length = 0;
for (length = 0; length < HackBGRT_FRACTION_DIGITS; ++length) {
result = 10 * result;
if (L'0' <= str[i] && str[i] <= L'9') {
result += str[i] - L'0';
i += 1;
}
}
*out_mode = HackBGRT_COORDINATE_MODE_FRACTION;
*out_int = result;
return;
}
int neg = str[0] == L'-' ? 1 : 0;
if (L'0' <= str[neg] && str[neg] <= L'9') {
*out_int = Atoi(str + neg) * (neg ? -1 : 1);
*out_mode = HackBGRT_COORDINATE_MODE_CENTERED;
return;
}
return 0;
}
static void ReadConfigImage(struct HackBGRT_config* config, const CHAR16* line) {
const CHAR16* n = StrStrAfter(line, L"n=");
const CHAR16* x = StrStrAfter(line, L"x=");
const CHAR16* y = StrStrAfter(line, L"y=");
const CHAR16* o = StrStrAfter(line, L"o=");
const CHAR16* f = StrStrAfter(line, L"path=");
enum HackBGRT_action action = HackBGRT_KEEP;
if (f) {
action = HackBGRT_REPLACE;
struct HackBGRT_image_config image = {
.action = HackBGRT_ACTION_KEEP,
.orientation = HackBGRT_ORIENTATION_KEEP,
};
const CHAR16* tmp;
image.path = StrStrAfter(line, L"path=");
if (image.path) {
image.action = HackBGRT_ACTION_REPLACE;
// Default: x centered, y 38.2 % (= 1 - 1 / golden_ratio)
image.x_mode = image.y_mode = HackBGRT_COORDINATE_MODE_FRACTION;
image.x = HackBGRT_FRACTION_HALF;
image.y = HackBGRT_FRACTION_381966011;
} else if (StrStr(line, L"remove")) {
action = HackBGRT_REMOVE;
image.action = HackBGRT_ACTION_REMOVE;
} else if (StrStr(line, L"black")) {
action = HackBGRT_REPLACE;
image.action = HackBGRT_ACTION_REPLACE;
image.path = 0;
} else if (StrStr(line, L"keep")) {
action = HackBGRT_KEEP;
image.action = HackBGRT_ACTION_KEEP;
image.x_mode = image.y_mode = HackBGRT_COORDINATE_MODE_KEEP;
} else {
Log(1, L"Invalid image line: %s\n", line);
return;
}
int weight = n && (!f || n < f) ? Atoi(n) : 1;
int x_val = ParseCoordinate(x, action);
int y_val = ParseCoordinate(y, action);
int o_val = o ? ParseCoordinate(o, action) : HackBGRT_coord_keep;
SetBMPWithRandom(config, weight, action, x_val, y_val, o_val, f);
ParseCoordinate(StrStrAfter(line, L"x="), &image.x, &image.x_mode);
ParseCoordinate(StrStrAfter(line, L"y="), &image.y, &image.y_mode);
if (StrStrAfter(line, L"o=keep")) {
image.orientation = HackBGRT_ORIENTATION_KEEP;
} else if ((tmp = StrStrAfter(line, L"o="))) {
// convert orientation in degrees to number 0-3 (* 90 degrees)
int i = tmp[0] == L'-' ? -(int)Atoi(tmp+1) : (int)Atoi(tmp);
image.orientation = (i / 90) & 3;
}
int weight = 1;
if ((tmp = StrStrAfter(line, L"n="))) {
weight = Atoi(tmp);
}
SetBMPWithRandom(config, &image, weight);
}
static void ReadConfigResolution(struct HackBGRT_config* config, const CHAR16* line) {

View File

@@ -6,15 +6,47 @@
* Possible actions to perform on the BGRT.
*/
enum HackBGRT_action {
HackBGRT_KEEP = 0, HackBGRT_REPLACE, HackBGRT_REMOVE
HackBGRT_ACTION_KEEP = 0,
HackBGRT_ACTION_REPLACE,
HackBGRT_ACTION_REMOVE
};
/**
* Special values for the image coordinates.
* @see struct HackBGRT_config
*/
enum HackBGRT_coordinate {
HackBGRT_coord_keep = -1000001
enum HackBGRT_coordinate_mode {
HackBGRT_COORDINATE_MODE_KEEP = 0,
HackBGRT_COORDINATE_MODE_CENTERED,
HackBGRT_COORDINATE_MODE_FRACTION,
};
/**
* Constants for the fractional coordinates.
*/
enum HackBGRT_fraction {
HackBGRT_FRACTION_DIGITS = 4,
HackBGRT_FRACTION_ONE = 10000,
HackBGRT_FRACTION_HALF = 5000,
HackBGRT_FRACTION_381966011 = 3820,
};
/**
* Possible values for the orientation.
*/
enum HackBGRT_orientation {
HackBGRT_ORIENTATION_KEEP = -1,
};
/**
* The configuration for one image.
*/
struct HackBGRT_image_config {
enum HackBGRT_action action;
const CHAR16* path;
enum HackBGRT_coordinate_mode x_mode, y_mode;
int x, y;
int orientation;
};
/**
@@ -22,12 +54,8 @@ enum HackBGRT_coordinate {
*/
struct HackBGRT_config {
int debug, log;
enum HackBGRT_action action;
const CHAR16* image_path;
int image_x;
int image_y;
struct HackBGRT_image_config image;
int image_weight_sum;
int orientation;
int resolution_x;
int resolution_y;
int old_resolution_x;

View File

@@ -21,7 +21,6 @@ EFI_RUNTIME_SERVICES *RT;
*/
static struct HackBGRT_config config = {
.log = 1,
.action = HackBGRT_KEEP,
};
/**
@@ -163,13 +162,13 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
}
Log(config.debug, L" - ACPI table @%x: %s, revision = %d, OEM ID = %s\n", (UINTN)entry, TmpStr(entry->signature, 4), entry->revision, TmpStr(entry->oem_id, 6));
switch (action) {
case HackBGRT_KEEP:
case HackBGRT_ACTION_KEEP:
if (!bgrt) {
Log(config.debug, L" -> Returning this one for later use.\n");
bgrt = (ACPI_BGRT*) entry;
}
break;
case HackBGRT_REMOVE:
case HackBGRT_ACTION_REMOVE:
Log(config.debug, L" -> Deleting.\n");
for (int k = j+1; k < entry_arr_length; ++k) {
entry_arr[k-1] = entry_arr[k];
@@ -179,13 +178,13 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
xsdt->length -= sizeof(entry_arr[0]);
--j;
break;
case HackBGRT_REPLACE:
case HackBGRT_ACTION_REPLACE:
Log(config.debug, L" -> Replacing.\n");
entry_arr[j] = (UINTN) bgrt;
}
bgrt_count += 1;
}
if (!bgrt_count && action == HackBGRT_REPLACE && bgrt) {
if (!bgrt_count && action == HackBGRT_ACTION_REPLACE && bgrt) {
Log(config.debug, L" - Adding missing BGRT.\n");
xsdt = CreateXsdt(xsdt, entry_arr_length + 1);
entry_arr = (UINT64*)&xsdt[1];
@@ -303,13 +302,13 @@ static void CropBMP(BMP* bmp, int w, int h) {
*/
void HackBgrt(EFI_FILE_HANDLE base_dir) {
// REMOVE: simply delete all BGRT entries.
if (config.action == HackBGRT_REMOVE) {
HandleAcpiTables(config.action, 0);
if (config.image.action == HackBGRT_ACTION_REMOVE) {
HandleAcpiTables(config.image.action, 0);
return;
}
// KEEP/REPLACE: first get the old BGRT entry.
ACPI_BGRT* bgrt = HandleAcpiTables(HackBGRT_KEEP, 0);
ACPI_BGRT* bgrt = HandleAcpiTables(HackBGRT_ACTION_KEEP, 0);
// Get the old BMP and position (relative to screen center), if possible.
const int old_valid = bgrt && VerifyAcpiSdtChecksum(bgrt);
@@ -324,7 +323,7 @@ void HackBgrt(EFI_FILE_HANDLE base_dir) {
// Missing BGRT?
if (!bgrt) {
// Keep missing = do nothing.
if (config.action == HackBGRT_KEEP) {
if (config.image.action == HackBGRT_ACTION_KEEP) {
return;
}
// Replace missing = allocate new.
@@ -351,13 +350,13 @@ void HackBgrt(EFI_FILE_HANDLE base_dir) {
// Get the image (either old or new).
BMP* new_bmp = old_bmp;
if (config.action == HackBGRT_REPLACE) {
new_bmp = LoadBMP(base_dir, config.image_path);
if (config.image.action == HackBGRT_ACTION_REPLACE) {
new_bmp = LoadBMP(base_dir, config.image.path);
}
// No image = no need for BGRT.
if (!new_bmp) {
HandleAcpiTables(HackBGRT_REMOVE, 0);
HandleAcpiTables(HackBGRT_ACTION_REMOVE, 0);
return;
}
@@ -366,16 +365,23 @@ void HackBgrt(EFI_FILE_HANDLE base_dir) {
// Set the image address and orientation.
bgrt->image_address = (UINTN) new_bmp;
const int new_orientation = config.orientation == HackBGRT_coord_keep ? old_orientation : ((config.orientation / 90) & 3);
const int new_orientation = config.image.orientation == HackBGRT_ORIENTATION_KEEP ? old_orientation : config.image.orientation;
bgrt->status = new_orientation << 1;
// New center coordinates.
const int new_x = config.image_x == HackBGRT_coord_keep ? old_x : config.image_x;
const int new_y = config.image_y == HackBGRT_coord_keep ? old_y : config.image_y;
const int new_swap = new_orientation & 1;
const int new_reso_x = new_swap ? config.resolution_y : config.resolution_x;
const int new_reso_y = new_swap ? config.resolution_x : config.resolution_y;
const int new_x =
config.image.x_mode == HackBGRT_COORDINATE_MODE_KEEP ? old_x :
config.image.x_mode == HackBGRT_COORDINATE_MODE_CENTERED ? config.image.x :
(config.image.x - HackBGRT_FRACTION_HALF) * new_reso_x / HackBGRT_FRACTION_ONE;
const int new_y =
config.image.y_mode == HackBGRT_COORDINATE_MODE_KEEP ? old_y :
config.image.y_mode == HackBGRT_COORDINATE_MODE_CENTERED ? config.image.y :
(config.image.y - HackBGRT_FRACTION_HALF) * new_reso_y / HackBGRT_FRACTION_ONE;
// Calculate absolute position.
const int max_x = new_reso_x - new_bmp->width;
const int max_y = new_reso_y - new_bmp->height;
@@ -383,15 +389,17 @@ void HackBgrt(EFI_FILE_HANDLE base_dir) {
bgrt->image_offset_y = max(0, min(max_y, new_y + (new_reso_y - new_bmp->height) / 2));
Log(config.debug,
L"BMP at (%d, %d), center (%d, %d), resolution (%d, %d), orientation %d.\n",
L"Screen %dx%d, BMP %dx%d, center (%d, %d) = corner (%d, %d), orientation %d.\n",
new_reso_x, new_reso_y,
new_bmp->width, new_bmp->height,
new_x, new_y,
(int) bgrt->image_offset_x, (int) bgrt->image_offset_y,
new_x, new_y, new_reso_x, new_reso_y,
new_orientation * 90
);
// Store this BGRT in the ACPI tables.
SetAcpiSdtChecksum(bgrt);
HandleAcpiTables(HackBGRT_REPLACE, bgrt);
HandleAcpiTables(HackBGRT_ACTION_REPLACE, bgrt);
}
/**