Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for INAV OSD Fonts #27

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ If you think this is annoying you can donate some money [here](https://www.buyme
### Linux
The project builds on Ubuntu in CI but I haven't tried runnning it myself. I don't know enough about packaging for Linux to make release binaries so for now you need to build from source.

### OSD Font Files
This repo contains some font files from Betaflight, ArduPilot and INAV. These can be found in [resources/osd/](https://github.com/avsaase/walksnail-osd-tool/tree/master/resources/osd).

### Building from source
1. Install the [Rust toolchain](https://www.rust-lang.org/tools/install).
2. Run `cargo install --git https://github.com/avsaase/walksnail-osd-tool.git`. The executable will be installed in `$HOME/.cargo/bin/` and added to your path.
Expand Down
14 changes: 9 additions & 5 deletions backend/src/font/font_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@ impl FontFile {
}

fn verify_dimensions(width: u32, height: u32) -> Result<(), FontFileError> {
if (width != CharacterSize::Large.width() && width != CharacterSize::Small.width()) || height % width != 0 {
return Err(FontFileError::InvalidFontFileDimensions {
dimensions: Dimension { width, height },
});
}
let small_characters = width == CharacterSize::Small.width();
let large_characters = width == CharacterSize::Large.width();

if (!small_characters && !large_characters) ||
(height % CharacterSize::Small.height() != 0 && height % CharacterSize::Large.height() != 0) {
return Err(FontFileError::InvalidFontFileDimensions {
dimensions: Dimension { width, height },
});
}

Ok(())
}
Expand Down
Binary file added resources/osd/ardu_24.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/osd/ardu_36.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/osd/bf_24.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/osd/bf_36.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/osd/inav_36.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
98 changes: 98 additions & 0 deletions tools/Inav OSD tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from PIL import Image
import os
import re
import argparse
import sys

# Run `python -m pip install argparse pillow` to install above used libraries

# This is used to convert INAV fonts to OSD files
# https://github.com/iNavFlight/inav-configurator/tree/master/resources/osd

def get_image_number(filename):
match = re.findall(r"\d+", filename)
if match:
return list(map(int, match))
return None


def stack_images(directory, output_path, image_width, image_height):
images = []
max_height = 0
max_width = 0

# Open and resize images
for filename in os.listdir(directory):
if filename.endswith(".png") or filename.endswith(".jpg"):
img = Image.open(os.path.join(directory, filename))
img = img.convert("RGBA") # Convert image to RGBA mode

image_numbers = get_image_number(filename)
if image_numbers is None or len(image_numbers) == 1:
img = img.resize((image_width, image_height))
images.append([img])
max_height += img.height
max_width = max(max_width, img.width)
else:
start_num, end_num = image_numbers
num_images = end_num - start_num + 1
num_rows = (img.width - 1) // image_width + 1 # Calculate number of rows
for i in range(num_images):
row_idx = i // num_rows # Row index for positioning
col_idx = i % num_rows # Column index for positioning
sub_img = img.crop(
(
image_width * col_idx,
image_height * row_idx,
image_width * (col_idx + 1),
image_height * (row_idx + 1),
)
)
sub_img = sub_img.resize((image_width, image_height))
images.append([sub_img])
max_height += sub_img.height
max_width = max(max_width, sub_img.width)

stacked_image = Image.new("RGBA", (max_width, max_height), (0, 0, 0, 0)) # Transparent background

# Stack images vertically
y_offset = 0
x_offset = 0
for img_group in images:
img = img_group[0]
stacked_image.paste(img, (x_offset, y_offset), mask=img)
x_offset += img.width
if x_offset >= stacked_image.width:
x_offset = 0
y_offset += img.height

stacked_image.save(output_path)
print(f"Stacked image saved as {output_path}")


def main():
# Parse command-line arguments
parser = argparse.ArgumentParser(description="INAV OSD font generator")

required_group = parser.add_argument_group("required arguments")
required_group.add_argument("-d", "--directory", type=str, help="Path to the directory containing the images")
required_group.add_argument("-o", "--output_path", type=str, help="Path to save the stacked image")
required_group.add_argument("-w", "--image_width", type=int, help="Width of each image")

required_group.add_argument("-he", "--height", type=int, help="Height of each image")

args = parser.parse_args()

if not all(vars(args).values()):
parser.print_help()
sys.exit(0)

if args.output_path and not args.output_path.endswith(".png"):
args.output_path = os.path.splitext(args.output_path)[0] + ".png"

# Call stack_images with provided arguments
stack_images(args.directory, args.output_path, args.image_width, args.height)


if __name__ == "__main__":
main()