summaryrefslogtreecommitdiffstats
path: root/Third_Party
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2024-11-30 16:45:30 -0800
committeryum <yum.food.vr@gmail.com>2024-11-30 16:45:30 -0800
commit10609e0207123ad8fb20cf578672b95f8c7ad1d6 (patch)
tree8a27da3641223e290df6eef4ed51d7f0857a193b /Third_Party
parent77628b080b779924499d369b94623cbdd0781147 (diff)
Fix gen_atlas
Now it generates things in a fixed-sized grid which is derived from charset's max codepoint. It does not interleave gaps, instead putting a big gap at the end/bottom of the atlas.
Diffstat (limited to 'Third_Party')
-rw-r--r--Third_Party/gen_atlas169
1 files changed, 85 insertions, 84 deletions
diff --git a/Third_Party/gen_atlas b/Third_Party/gen_atlas
index 7cc6c41..dfb0e61 100644
--- a/Third_Party/gen_atlas
+++ b/Third_Party/gen_atlas
@@ -6,130 +6,131 @@ import os
import json
from PIL import Image
-def calculate_cell_dimensions(total_size, num_cells):
- """
- Distribute pixels evenly across cells, handling remainder.
- Returns a list of cell sizes that add up to total_size.
- """
- base_size = total_size // num_cells
- remainder = total_size % num_cells
-
- # Create array of cell sizes
- cell_sizes = []
- for i in range(num_cells):
- # Add an extra pixel to cells until remainder is used up
- # This distributes remainder pixels evenly across cells
- extra = 1 if i < remainder else 0
- cell_sizes.append(base_size + extra)
-
- return cell_sizes
+# Define the character ranges we want to include
+CHAR_RANGES = [
+ (32, 126), # Printable ASCII
+ # Add more ranges here as needed, e.g.:
+ # (160, 255), # Extended Latin
+]
-def generate_atlas(font_path, resolution):
- # Calculate cell dimensions for 12x12 grid
- cell_widths = calculate_cell_dimensions(resolution, 12)
- cell_heights = calculate_cell_dimensions(resolution, 12)
-
- # Use the most common cell size
- cell_width = max(cell_widths)
- cell_height = max(cell_heights)
+def calculate_grid_size(char_ranges):
+ """Calculate the smallest square grid that fits the highest character code"""
+ max_char = max(end for _, end in char_ranges)
+ grid_size = 1
+ while grid_size * grid_size < max_char:
+ grid_size += 1
+ return grid_size
- # Construct the command with explicit glyph range for printable ASCII
+def generate_atlas(font_path, resolution, draw_grid=False):
+ # Calculate grid size based on character ranges
+ grid_size = calculate_grid_size(CHAR_RANGES)
+ cell_size = resolution // grid_size
+
+ # Convert character ranges to command-line format
+ chars_str = ", ".join(f"[{start}, {end}]" for start, end in CHAR_RANGES)
+
cmd = [
"msdf-atlas-gen/build/bin/Debug/msdf-atlas-gen.exe",
"-font", font_path,
- "-type", "msdf",
+ "-type", "mtsdf",
"-format", "png",
"-imageout", "atlas.png",
"-json", "atlas.json",
"-size", "32",
"-pxrange", "2",
"-dimensions", str(resolution), str(resolution),
- "-chars", "[32, 126]",
+ "-chars", chars_str,
"-uniformgrid",
- "-uniformcols", "12",
- "-uniformcell", str(cell_width), str(cell_height),
- "-uniformorigin", "on"
+ "-uniformcols", str(grid_size),
+ "-uniformcell", str(cell_size), str(cell_size),
+ "-uniformorigin", "on",
+ "-coloringstrategy", "inktrap",
+ "-errorcorrection", "auto",
+ "-miterlimit", "1.0",
+ "-scanline"
]
try:
+ print("Running msdf-atlas-gen...")
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
+ # Print the output
+ if result.stdout:
+ print("msdf-atlas-gen output:")
+ print(result.stdout)
+
# Rearrange the atlas to include gaps for non-printable characters
- rearrange_atlas(resolution, cell_width, cell_height)
+ rearrange_atlas(resolution, cell_size, cell_size, draw_grid)
return True
except subprocess.CalledProcessError as e:
print(f"Error generating atlas: {e}")
print(f"Error output: {e.stderr}")
return False
-def rearrange_atlas(resolution, cell_width, cell_height):
- """Rearrange the atlas to include gaps for non-printable characters"""
- # Load the original atlas and JSON data
- original = Image.open("atlas.png")
- with open("atlas.json", 'r') as f:
- data = json.load(f)
-
- # Create the new atlas image
- new_atlas = Image.new('RGB', (resolution, resolution), (0, 0, 0))
-
- # Calculate exact cell dimensions
- exact_cell_width = resolution / 12
- exact_cell_height = resolution / 12
-
- for glyph in data['glyphs']:
- if 'unicode' not in glyph or 'atlasBounds' not in glyph:
- continue
-
- char_code = glyph['unicode']
-
- # Calculate grid position using floor division to avoid rounding errors
- grid_x = (char_code % 12) * resolution // 12
- grid_y = (char_code // 12) * resolution // 12
-
- # Extract original bounds
- bounds = glyph['atlasBounds']
- orig_x = round(float(bounds['left'])) # Use round instead of int
- orig_y = resolution - round(float(bounds['top'])) # Use round instead of int
- width = round(float(bounds['right']) - float(bounds['left']))
- height = round(float(bounds['top']) - float(bounds['bottom']))
-
- # Extract the glyph from the original atlas
- glyph_region = original.crop((
- orig_x,
- orig_y,
- orig_x + width,
- orig_y + height
- ))
-
- # Paste the glyph directly at the grid position
- new_atlas.paste(glyph_region, (grid_x, grid_y))
-
- # Draw grid lines
- from PIL import ImageDraw
- draw = ImageDraw.Draw(new_atlas)
+def draw_grid_lines(image, resolution):
+ """Draw red grid lines on the image"""
+ draw = ImageDraw.Draw(image)
+ grid_size = calculate_grid_size(CHAR_RANGES)
# Draw vertical lines
- for x in range(12):
- line_x = x * resolution // 12 # Use integer division
+ for x in range(grid_size):
+ line_x = x * resolution // grid_size
draw.line([(line_x, 0), (line_x, resolution-1)], fill=(255, 0, 0), width=1)
# Draw horizontal lines
- for y in range(12):
- line_y = y * resolution // 12 # Use integer division
+ for y in range(grid_size):
+ line_y = y * resolution // grid_size
draw.line([(0, line_y), (resolution-1, line_y)], fill=(255, 0, 0), width=1)
# Draw the final borders
draw.line([(resolution-1, 0), (resolution-1, resolution-1)], fill=(255, 0, 0), width=1)
draw.line([(0, resolution-1), (resolution-1, resolution-1)], fill=(255, 0, 0), width=1)
+
+def rearrange_atlas(resolution, cell_width, cell_height, draw_grid=False):
+ """Rearrange the atlas to include gaps for non-printable characters"""
+ original = Image.open("atlas.png")
+ new_atlas = Image.new('RGBA', (resolution, resolution), (0, 0, 0, 255))
+
+ grid_size = calculate_grid_size(CHAR_RANGES)
+ cell_size = resolution // grid_size
+
+ # Track current position in the source atlas
+ source_index = 0
+
+ # Process each character range
+ for start, end in CHAR_RANGES:
+ for ascii_code in range(start, end + 1):
+ # Calculate source position (original atlas)
+ source_x = (source_index % grid_size) * cell_size
+ source_y = (source_index // grid_size) * cell_size
+
+ # Calculate target position (new atlas)
+ target_x = ((ascii_code + 1) % grid_size) * cell_size
+ target_y = ((ascii_code + 1) // grid_size) * cell_size
+
+ # Extract and paste the glyph
+ glyph = original.crop((
+ source_x,
+ source_y,
+ source_x + cell_size,
+ source_y + cell_size
+ ))
+ new_atlas.paste(glyph, (target_x, target_y))
+ source_index += 1
+
+ # Draw the grid lines only if requested
+ if draw_grid:
+ draw_grid_lines(new_atlas, resolution)
- # Save the rearranged atlas
- new_atlas.save("atlas_rearranged.png")
+ # Save the rearranged atlas, overwriting the original
+ new_atlas.save("atlas.png")
print("Atlas rearranged successfully!")
def main():
parser = argparse.ArgumentParser(description='Generate a font atlas using msdf-atlas-gen')
parser.add_argument('font_path', help='Path to the font file (.ttf/.otf)')
parser.add_argument('resolution', type=int, help='Total atlas resolution (width=height)')
+ parser.add_argument('--grid', action='store_true', help='Draw grid lines on the output atlas')
args = parser.parse_args()
@@ -138,7 +139,7 @@ def main():
print(f"Error: Font file not found at {args.font_path}")
return
- generate_atlas(args.font_path, args.resolution)
+ generate_atlas(args.font_path, args.resolution, args.grid)
if __name__ == "__main__":
main()