Mercurial > games > semicongine
changeset 873:1ed09c1bc79c
add: packed textures for font-atlas
author | Sam <sam@basx.dev> |
---|---|
date | Sat, 20 Jan 2024 21:28:17 +0700 |
parents | 1ee397815b0b |
children | a91219ef6ef9 |
files | semicongine/core/imagetypes.nim semicongine/resources/font.nim tests/test_font.nim |
diffstat | 3 files changed, 29 insertions(+), 38 deletions(-) [+] |
line wrap: on
line diff
--- a/semicongine/core/imagetypes.nim Sat Jan 20 20:17:03 2024 +0700 +++ b/semicongine/core/imagetypes.nim Sat Jan 20 21:28:17 2024 +0700 @@ -59,12 +59,12 @@ image[].imagedata[y * image.width + x] = value -proc newImage*[T: Pixel](width, height: int, imagedata: seq[T]= @[]): Image[T] = +proc newImage*[T: Pixel](width, height: int, imagedata: openArray[T]= []): Image[T] = assert width > 0 and height > 0 assert imagedata.len == width * height or imagedata.len == 0 result = new Image[T] - result.imagedata = (if imagedata.len == 0: newSeq[T](width * height) else: imagedata) + result.imagedata = (if imagedata.len == 0: newSeq[T](width * height) else: @imagedata) assert width * height == result.imagedata.len result.width = width
--- a/semicongine/resources/font.nim Sat Jan 20 20:17:03 2024 +0700 +++ b/semicongine/resources/font.nim Sat Jan 20 21:28:17 2024 +0700 @@ -1,6 +1,7 @@ import times import std/tables import std/strformat +import std/sequtils import std/streams import std/os import std/unicode @@ -39,6 +40,7 @@ if stbtt_InitFont(addr fontinfo, addr indata[0], 0) == 0: raise newException(Exception, "An error occured while loading PNG file") + result.name = name result.fontscale = float32(stbtt_ScaleForPixelHeight(addr fontinfo, cfloat(lineHeightPixels))) # ensure all codepoints are available in the font @@ -47,10 +49,11 @@ warn &"Loading font {name}: Codepoint '{codePoint}' ({cint(codePoint)}) has no glyph" var - offsetX = 0 bitmaps: Table[Rune, (cstring, cint, cint)] topOffsets: Table[Rune, int] images: seq[Image[GrayPixel]] + let empty_image = newImage[GrayPixel](1, 1, [0'u8]) + for codePoint in codePoints: var width, height: cint @@ -64,66 +67,54 @@ addr width, addr height, addr offX, addr offY ) + topOffsets[codePoint] = offY if width > 0 and height > 0: var bitmap = newSeq[GrayPixel](width * height) for i in 0 ..< width * height: bitmap[i] = GrayPixel(data[i]) images.add newImage[GrayPixel](int(width), int(height), bitmap) + else: + images.add empty_image - bitmaps[codePoint] = (data, width, height) - result.maxHeight = max(result.maxHeight, int(height)) - offsetX += width - topOffsets[codePoint] = offY - assert offsetX < MAX_TEXTURE_WIDTH, &"Font size too big, choose a smaller lineHeightPixels when loading the font (required texture width is {offsetX} but max is {MAX_TEXTURE_WIDTH}), must be smaller than {lineHeightPixels * float(MAX_TEXTURE_WIDTH) / float(offsetX) } (approx.)" + + free(data) let packed = pack(images) - packed.atlas.writePNG("tmp.png") - result.name = name result.fontAtlas = Texture( name: name & "_texture", isGrayscale: true, - grayImage: newImage[GrayPixel](offsetX, result.maxHeight), - sampler: FONTSAMPLER_SOFT + grayImage: packed.atlas, + sampler: FONTSAMPLER_SOFT, ) - for codePoint in codePoints: - let - bitmap = bitmaps[codePoint][0] - width = bitmaps[codePoint][1] - height = bitmaps[codePoint][2] - - offsetX = 0 - for codePoint in codePoints: + let w = float32(packed.atlas.width) + let h = float32(packed.atlas.height) + for i in 0 ..< codePoints.len: let - bitmap = bitmaps[codePoint][0] - width = bitmaps[codePoint][1] - height = bitmaps[codePoint][2] - - # bitmap data - for y in 0 ..< height: - for x in 0 ..< width: - result.fontAtlas.grayImage[x + offsetX, y] = uint8(bitmap[y * width + x]) - + codePoint = codePoints[i] + image = images[i] + coord = (x: float32(packed.coords[i].x), y: float32(packed.coords[i].y)) + iw = float32(image.width) + ih = float32(image.height) # horizontal spaces: var advance, leftBearing: cint stbtt_GetCodepointHMetrics(addr fontinfo, cint(codePoint), addr advance, addr leftBearing) result.glyphs[codePoint] = GlyphInfo( - dimension: newVec2f(float32(width), float32(height)), + dimension: newVec2f(float32(image.width), float32(image.height)), uvs: [ - newVec2f((float32(offsetX) + 0.5) / float32(result.fontAtlas.grayImage.width), (float32(height) - 1.0) / float32(result.maxHeight)), - newVec2f((float32(offsetX) + 0.5) / float32(result.fontAtlas.grayImage.width), 0.5 / float32(result.maxHeight)), - newVec2f((float32(offsetX + width) - 1.0) / float32(result.fontAtlas.grayImage.width), 0.5 / float32(result.maxHeight)), - newVec2f((float32(offsetX + width) - 1.0) / float32(result.fontAtlas.grayImage.width), (float32(height) - 1.0) / float32(result.maxHeight)), + newVec2f((coord.x + 0.5 ) / w, (coord.y + ih - 0.5) / h), + newVec2f((coord.x + 0.5 ) / w, (coord.y + 0.5) / h), + newVec2f((coord.x + iw - 0.5) / w, (coord.y + 0.5) / h), + newVec2f((coord.x + iw - 0.5) / w, (coord.y + ih - 0.5) / h), ], topOffset: float32(topOffsets[codePoint]), leftOffset: float32(leftBearing) * result.fontscale, advance: float32(advance) * result.fontscale, ) - offsetX += width - free(bitmap) + for codePointAfter in codePoints: result.kerning[(codePoint, codePointAfter)] = float32(stbtt_GetCodepointKernAdvance( addr fontinfo,
--- a/tests/test_font.nim Sat Jan 20 20:17:03 2024 +0700 +++ b/tests/test_font.nim Sat Jan 20 21:28:17 2024 +0700 @@ -10,9 +10,9 @@ # build scene var scene = Scene(name: "main") # var font = loadFont("DejaVuSans.ttf", lineHeightPixels=90'f32, charset="abcdefghijklmnopqrstuvwxyz ".toRunes) - var font = loadFont("DejaVuSans.ttf", lineHeightPixels=90'f32) + var font = loadFont("DejaVuSans.ttf", lineHeightPixels=180'f32) var textbox = initText(32, font, "", color=newVec4f(1, 0, 0, 1)) - let fontscale = 0.005 + let fontscale = 0.001 scene.add textbox textbox.mesh.transform = scale(fontscale, fontscale) engine.loadScene(scene)