diff --git a/src/figdraw/common/fontglyphs.nim b/src/figdraw/common/fontglyphs.nim index 0fd47db..bb8f829 100644 --- a/src/figdraw/common/fontglyphs.nim +++ b/src/figdraw/common/fontglyphs.nim @@ -72,54 +72,64 @@ proc hash*( let variant = clampGlyphVariantSubpixelStep(subpixelVariant) result = hash((2344, glyph.fontId, glyph.rune, lcdFiltering, variant)) -proc generateGlyph*(glyph: GlyphPosition, lcdFiltering = false, subpixelVariant = 0) = +proc generateGlyph*( + glyph: GlyphPosition, + lcdFiltering = false, + subpixelVariant = 0, + force = false, + upload = true, +): Image {.discardable.} = if unicode.isWhiteSpace(glyph.rune): - return + return nil let variant = clampGlyphVariantSubpixelStep(subpixelVariant) hashFill = glyph.hash(lcdFiltering = lcdFiltering, subpixelVariant = variant) - if not hasImage(hashFill.ImageId): - let - fontId = glyph.fontId - font = getPixieFont(fontId) - - var - text = $glyph.rune - arrangement = pixie.typeset( - @[newSpan(text, font)], - bounds = glyph.rect.wh.scaled(), - hAlign = CenterAlign, - vAlign = TopAlign, - wrap = false, - ) - if variant > 0: - let subpixelOffset = variant.float32 / glyphVariantSubpixelSteps.float32 - for i in 0 ..< arrangement.positions.len: - arrangement.positions[i].x += subpixelOffset - - let snappedBounds = arrangement.computeBounds().snapToPixels() - - let - lh = font.defaultLineHeight() - bounds = rect(0, 0, scaled(snappedBounds.w + snappedBounds.x), scaled(lh)) - - if bounds.w == 0 or bounds.h == 0: - error "GEN IMG: ", rune = $glyph.rune, wh = repr wh, snapped = repr snappedBounds - return - - try: - font.paint = parseHex"FFFFFF" - var image = newImage(bounds.w.int, bounds.h.int) - image.fillText(arrangement) - if lcdFiltering: - image.applyLcdFilter() - - # put into cache + if (not force) and hasImage(hashFill.ImageId): + return nil + + let + fontId = glyph.fontId + font = getPixieFont(fontId) + + var + text = $glyph.rune + arrangement = pixie.typeset( + @[newSpan(text, font)], + bounds = glyph.rect.wh.scaled(), + hAlign = CenterAlign, + vAlign = TopAlign, + wrap = false, + ) + if variant > 0: + let subpixelOffset = variant.float32 / glyphVariantSubpixelSteps.float32 + for i in 0 ..< arrangement.positions.len: + arrangement.positions[i].x += subpixelOffset + + let snappedBounds = arrangement.computeBounds().snapToPixels() + + let + lh = font.defaultLineHeight() + bounds = rect(0, 0, scaled(snappedBounds.w + snappedBounds.x), scaled(lh)) + + if bounds.w == 0 or bounds.h == 0: + debug "GEN IMG: ", rune = $glyph.rune, wh = repr wh, snapped = repr snappedBounds + return nil + + try: + font.paint = parseHex"FFFFFF" + var image = newImage(bounds.w.int, bounds.h.int) + image.fillText(arrangement) + if lcdFiltering: + image.applyLcdFilter() + + # put into cache + if upload: loadImage(hashFill.ImageId, image) - except PixieError: - discard + return image + except PixieError: + return nil iterator glyphs*(arrangement: GlyphArrangement): GlyphPosition = var idx = 0 diff --git a/src/figdraw/common/imgutils.nim b/src/figdraw/common/imgutils.nim index 35d7eec..8941d24 100644 --- a/src/figdraw/common/imgutils.nim +++ b/src/figdraw/common/imgutils.nim @@ -105,4 +105,4 @@ proc loadImage*(filePath: string): ImageId = proc loadImage*(id: ImageId, image: Image) = var imgObj = ImgObj(id: id, kind: PixieImg, pimg: image) - sendImageCached(imgObj) + sendImage(imgObj) diff --git a/src/figdraw/figrender.nim b/src/figdraw/figrender.nim index cbe75b6..24f31ab 100644 --- a/src/figdraw/figrender.nim +++ b/src/figdraw/figrender.nim @@ -343,13 +343,17 @@ proc renderText(ctx: BackendContext, node: Fig) {.forbids: [AppMainThreadEff].} ctx.setTextSubpixelShift(subpixelShift) if glyphId notin ctx.entries: - glyph.generateGlyph( - lcdFiltering = lcdFiltering, subpixelVariant = subpixelVariant + let img = glyph.generateGlyph(lcdFiltering = lcdFiltering, + subpixelVariant = subpixelVariant, + force = true, + upload = false, ) - warn "missing glyph image in context", - glyphId = glyphId, glyphRune = $glyph.rune, glyphRuneRepr = repr(glyph.rune) - ctx.setTextSubpixelShift(0.0'f32) - continue + ctx.putImage(glyphId, img) + if glyphId in ctx.entries: + debug "missing glyph image in context", + glyphId = glyphId, glyphRune = $glyph.rune, glyphRuneRepr = repr(glyph.rune) + ctx.setTextSubpixelShift(0.0'f32) + continue var drawPos = glyphPos if invertText: diff --git a/src/figdraw/windowing/siwinshim.nim b/src/figdraw/windowing/siwinshim.nim index 59d3424..ff86b2f 100644 --- a/src/figdraw/windowing/siwinshim.nim +++ b/src/figdraw/windowing/siwinshim.nim @@ -99,28 +99,72 @@ proc siwinWindowTitle*[BackendState]( "figdraw: " & backend & " + " & suffix proc newSiwinWindow*( - size: IVec2, fullscreen = false, title = "FigDraw", vsync = true, msaa = 0'i32 + size: IVec2, + fullscreen = false, + title = "FigDraw", + vsync = true, + msaa = 0'i32, + resizable = true, + frameless = false, + transparent = false, ): Window = let forceOpenGl = runtimeForceOpenGlRequested() let window = when UseMetalBackend and not NeedSiwinOpenGLContext: when defined(macosx): - newMetalWindowCocoa(size = size, title = title) + newMetalWindowCocoa( + size = size, + title = title, + resizable = resizable, + frameless = frameless, + transparent = transparent, + ) else: {.error: "siwinshim: Metal backend requires macOS".} else: when defined(macosx): - newOpenglWindowCocoa(size = size, title = title, vsync = vsync, msaa = msaa) + newOpenglWindowCocoa( + size = size, + title = title, + vsync = vsync, + msaa = msaa, + resizable = resizable, + frameless = frameless, + transparent = transparent, + ) else: let globals = sharedSiwinGlobals() when UseVulkanBackend: if forceOpenGl: - newOpenglWindow(globals, size = size, title = title, vsync = vsync) + newOpenglWindow( + globals, + size = size, + title = title, + vsync = vsync, + resizable = resizable, + frameless = frameless, + transparent = transparent, + ) else: # Use a non-GL window for Vulkan so siwin's GL swap path does not flicker. - newSoftwareRenderingWindow(globals, size = size, title = title) + newSoftwareRenderingWindow( + globals, + size = size, + title = title, + resizable = resizable, + frameless = frameless, + transparent = transparent, + ) else: - newOpenglWindow(globals, size = size, title = title, vsync = vsync) + newOpenglWindow( + globals, + size = size, + title = title, + vsync = vsync, + resizable = resizable, + frameless = frameless, + transparent = transparent, + ) when NeedSiwinOpenGLContext: when UseVulkanBackend: if forceOpenGl: @@ -140,6 +184,9 @@ proc newSiwinWindow*( title = "FigDraw", vsync = true, msaa = 0'i32, + resizable = true, + frameless = false, + transparent = false, ): Window = ## Compatibility overload. Prefer creating a window first, then renderer. let forceOpenGl = runtimeForceOpenGlRequested() or renderer.forceOpenGlByEnv() @@ -173,7 +220,14 @@ proc newSiwinWindow*( discard renderer result = newSiwinWindow( - size = size, fullscreen = fullscreen, title = title, vsync = vsync, msaa = msaa + size = size, + fullscreen = fullscreen, + title = title, + vsync = vsync, + msaa = msaa, + resizable = resizable, + frameless = frameless, + transparent = transparent, ) proc backingSize*(window: Window): IVec2 =