Mercurial > repos > tabletprog
view samples/freetype.tp @ 331:61f5b794d939
Breaking change: method call syntax now always uses the syntactic receiver as the actual receiver. This makes its behavior different from function call syntax, but solves some problems with methods being shadowed by local variables and the like.
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 28 Mar 2015 14:21:04 -0700 |
parents | e70f9d3f19f8 |
children | 577406b25f89 |
line wrap: on
line source
#{ import: [ video timer ] from: (sdl subsystems) import: [ quit ] from: (sdl eventTypes) import: [ streaming ] from: (sdl textureAccess) import: [ bgra8888 ] from: (sdl pixelFormats) import: [ render linearDesign noHinting ] from: (freetype loadFlags) makeAtlas <- :renderer face fontSize dpi color { face setCharSize: fontSize res: dpi slot <- face glyphSlot glyphs <- #[] //TODO: Use a bytearray once that has an append method pixels <- #[] foreach: (face charmap) :char glyphIdx { face loadGlyph: glyphIdx flags: (render or linearDesign or noHinting) pixelStart <- pixels length _width <- slot bitmapWidth _height <- slot bitmapRows pitch <- slot bitmapPitch buffer <- slot bitmapData y <- 0 while: { y < _height } do: { x <- 0 idx <- y * pitch while: { x < _width } do: { pixels append: (buffer get: idx) x <- x + 1 idx <- idx + 1 } y <- y + 1 } glyphs append: #{ width <- _width height <- _height pixelOffset <- pixelStart hAdvance <- slot linearHoriAdvance vAdvance <- slot linearVertAdvance leftOffset <- (slot bitmapLeft) topOffset <- (slot bitmapTop) charCode <- char glyphIndex <- glyphIdx atlasX <- -1 atlasY <- -1 atlasRect <- { sdl rect: atlasX atlasY size: width height } destRect <- :x y { sdl rect: x + leftOffset y - topOffset size: width height } <= <- :other { if: height > (other height) { true } else: { if: height < (other height) { false } else: { width >= (other width) } } } } } glyphs sort maxDim <- 2048 aWidth <- 128 minSize <- maxDim * maxDim minSizeWidth <- -1 aHeight <- maxDim while: { aWidth <= maxDim } do: { print: "Checking width: " . aWidth . "\n" curX <- 0 curY <- 0 minHeight <- 0 avail <- glyphs foldr: [] with: :acc val { val | acc } while: { (not: (avail empty?)) && curY <= maxDim } do: { curGlyph <- avail value if: curX + (curGlyph width) < aWidth { if: curY + (curGlyph height) < maxDim { curX <- curX + (curGlyph width) avail <- avail tail if: (curGlyph height) > minHeight { minHeight <- curGlyph height } } else: { curY <- maxDim + 1 } } else: { skinny <- option none if: aWidth > curX { availPixels <- aWidth - curX skinny <- avail find: :val { (val width) <= availPixels } } skinny value: :curGlyph { curX <- curX + (curGlyph width) if: (curGlyph height) > minHeight { minHeight <- curGlyph height } avail <- avail filter: :glyph { (glyph charCode) != (curGlyph charCode) } } none: { curY <- curY + minHeight minHeight <- 0 curX <- 0 } } } if: (avail empty?) { aHeight <- curY + minHeight p2Height <- 1 while: { p2Height < aHeight } do: { p2Height <- lshift: p2Height by: 1 } size <- aWidth * p2Height if: size < minSize { minSize <- size minSizeWidth <- aWidth } } aWidth <- aWidth * 2 } if: minSizeWidth > -1 { print: "Best width: " . minSizeWidth . ", height: " . (minSize / minSizeWidth) . "\n" aWidth <- minSizeWidth aHeight <- minSize / minSizeWidth (renderer createTexture: bgra8888 access: streaming width: aWidth height: aHeight) value: :drawTex { drawTex blendMode!: ((sdl blendModes) blend) //TODO: Use "update" with a static texture drawTex lockRect: (sdl rect: 0 0 size: aWidth aHeight) with: :bytearr pitch { n <- aHeight * pitch i <- 0 while: { i < n } do: { bytearr set: i 0u8 i <- i + 1 } curX <- 0 curY <- 0 minHeight <- 0 avail <- glyphs foldr: [] with: :acc val { val | acc } while: { not: (avail empty?) } do: { curGlyph <- avail value if: curX + (curGlyph width) < aWidth { curGlyph atlasX!: curX curGlyph atlasY!: curY y <- 0 dstY <- curY idx <- curGlyph pixelOffset while: { y < (curGlyph height) } do: { dstIdx <- dstY * pitch + curX * 4 x <- 0 while: { x < (curGlyph width) } do: { //FIXME: This will probably only work on little endian machines bytearr set: dstIdx (pixels get: idx) dstIdx <- dstIdx + 1 bytearr set: dstIdx (color r) dstIdx <- dstIdx + 1 bytearr set: dstIdx (color g) dstIdx <- dstIdx + 1 bytearr set: dstIdx (color b) dstIdx <- dstIdx + 1 idx <- idx + 1 x <- x + 1 } y <- y + 1 dstY <- dstY + 1 } curX <- curX + (curGlyph width) avail <- avail tail if: (curGlyph height) > minHeight { minHeight <- curGlyph height } } else: { skinny <- option none if: aWidth > curX { availPixels <- aWidth - curX skinny <- avail find: :val { (val width) <= availPixels } } skinny value: :curGlyph { curGlyph atlasX!: curX curGlyph atlasY!: curY y <- 0 dstY <- curY idx <- curGlyph pixelOffset while: { y < (curGlyph height) } do: { dstIdx <- dstY * pitch + curX * 4 x <- 0 while: { x < (curGlyph width) } do: { //FIXME: This will probably only work on little endian machines bytearr set: dstIdx (pixels get: idx) dstIdx <- dstIdx + 1 bytearr set: dstIdx (color r) dstIdx <- dstIdx + 1 bytearr set: dstIdx (color g) dstIdx <- dstIdx + 1 bytearr set: dstIdx (color b) dstIdx <- dstIdx + 1 idx <- idx + 1 x <- x + 1 } y <- y + 1 dstY <- dstY + 1 } curX <- curX + (curGlyph width) if: (curGlyph height) > minHeight { minHeight <- curGlyph height } avail <- avail filter: :glyph { (glyph charCode) != (curGlyph charCode) } } none: { curY <- curY + minHeight minHeight <- 0 curX <- 0 } } } } glyphDict <- dict hash foreach: glyphs :idx glyph { glyphDict set: (glyph charCode) glyph } _pixelFactor <- ((face unitsPerEm) f64) * 72.0 / (fontSize * (dpi f64)) option value: #{ texture <- drawTex width <- aWidth height <- aHeight glyphs <- glyphDict drawString:at:useKerning? <- :str :xPos yPos :kern? { //pixels to font units designPosition <- (xPos f64) * _pixelFactor charIdx <- 0 last <- 0u32 useKerning? <- kern? && (face hasKerning?) while: { charIdx < (str byte_length) } do: { //TODO: UTF-8 char <- (str byte: charIdx) uint32 glyph <- glyphs get: char else: { glyphs get: 0u32 else: { false } } texture copyRect: (glyph atlasRect) To: (glyph destRect: xPos yPos) designPosition <- designPosition + ((glyph hAdvance) f64) if: charIdx > 0 && useKerning? { (face getKerning: last (glyph glyphIndex) mode: ((freetype kerning) unscaled)) value: :kern { designPosition <- designPosition + ((kern x) f64) } none: {} } xPos <- (designPosition / _pixelFactor + 0.5) truncInt32 last <- glyph glyphIndex charIdx <- charIdx + 1 } } } } none: { print: "Failed to create texture for atlas" option none } } else: { print: "Font is too big for a 2048x2048 texture!" option none } } hex2 <- :num { val <- hex: num if: (val length) < 2 { val <- "0" . val } val } main <- :args { retcode <- 0 dpi <- 96 arg <- 1 expectVal <- false optName <- "" windowWidth <- 512 windowHeight <- 512 posArgs <- #[] while: { arg < (args length) } do: { curArg <- args get: arg if: expectVal { if: optName = "--dpi" || optName = "-d" { dpi <- curArg int32 } else: { if: optName = "--height" { windowHeight <- curArg int32 } else: { if: optName = "--width" { windowWidth <- curArg int32 } else: { print: "Unrecognized option: " . optName . "\n" } } } expectVal <- false } else: { if: (curArg startsWith?: "-") { expectVal <- true optName <- curArg } else: { posArgs append: curArg } } arg <- arg + 1 } path <- posArgs get: 0 str <- if: (posArgs length) > 1 { posArgs get: 1 } else: { "" } ft <- freetype init maybeFace <- ft faceFromPath: path index: 0 charCodes <- #[] maybeFace value: :face { charMap <- face charmap foreach: charMap :char glyph { print: "Char: " . char . ", glyph index: " . glyph . "\n" charCodes append: char } if: (sdl init: (video or timer)) = 0 { (sdl createWindow: "Freetype Test" pos: 0 0 size: windowWidth windowHeight flags: 0u32) value: :window { (window createRenderer: -1 flags: ((window renderOpts) accelerated)) value: :renderer { renderer drawColor!: (sdl r: 255u8 g: 255u8 b: 255u8) (makeAtlas: renderer face 12.0 dpi (sdl r: 0u8 g: 0u8 b: 0u8)) value: :atlas { continue? <- true while: { continue? } do: { renderer clear if: (str length) > 0 { atlas drawString: str at: 0 windowHeight / 3 useKerning?: true atlas drawString: str at: 0 windowHeight * 2 / 3 useKerning?: false } else: { y <- 0 x <- 0 while: { y < (atlas height) } do: { copyWidth <- if: (atlas width) < windowWidth { atlas width } else: { windowWidth } copyHeight <- if: (atlas height) < windowHeight { atlas height } else: { windowHeight } (atlas texture) copyRect: (sdl rect: 0 y size: copyWidth copyHeight) To: (sdl rect: x 0 size: copyWidth copyHeight) y <- y + windowHeight x <- x + copyWidth } } renderer present event <- option none while: { event <- sdl pollEvent event value? } do: { event value: :ev { if: (ev type) = quit { continue? <- false } } none: {} } } } none: { retcode <- -1 } } none: { print: "Failed to create renderer\n" retcode <- 1 } window destroy } none: { print: "Failed to create window\n" retcode <- 1 } } else: { print: "Failed to initialize SDL\n" retcode <- 1 } } none: { retcode <- 1 print: "Failed to load font face from " . path . "\n" } ft destroy retcode } }