@@ -2,30 +2,39 @@ import std/[os, posix, strutils]
22import nimPNG
33
44import common/ [fb, process]
5- import common/ ffi/ stb_truetype
5+ import common/ ffi/ [ stb_truetype, neon_blit]
66
77const
8- ScreenWidth = 320
8+ ScreenWidth = 320
99 ScreenHeight = 240
1010
1111 DefaultBackground * = " /mnt/SDCARD/System/res/quarkbg.png"
12- DefaultFont * = " /mnt/SDCARD/System/res/TwCenMT.ttf"
13- FontSize = 24.0
12+ DefaultFont * = " /mnt/SDCARD/System/res/TwCenMT.ttf"
13+ FontSize = 24.0
1414
15- {.push optimization :speed, warnings :off .}
15+ {.push optimization : speed, warnings : off .}
1616
1717var childPid: Pid = - 1
1818
19+ var blitTmp: array [FbPixels , uint16 ]
20+
1921proc toRGB565 (r, g, b: uint8 ): uint16 {.inline .} =
20- (uint16 (EightToFive [r] ) shl 11 ) or
21- (uint16 (EightToSix [g] ) shl 5 ) or
22- uint16 (EightToFive [b] )
22+ (uint16 (r shr 3 ) shl 11 ) or
23+ (uint16 (g shr 2 ) shl 5 ) or
24+ uint16 (b shr 3 )
2325
2426proc fromRGB565 (pixel: uint16 , r, g, b: var uint8 ) {.inline .} =
2527 r = FiveToEight [(pixel shr 11 ) and 0x 1F ]
26- g = SixToEight [(pixel shr 5 ) and 0x 3F ]
28+ g = SixToEight [(pixel shr 5 ) and 0x 3F ]
2729 b = FiveToEight [pixel and 0x 1F ]
2830
31+ proc blendRGB565 (fg, bg: uint16 , alpha: uint32 ): uint16 {.inline .} =
32+ let inv = 256 'u32 - alpha
33+ let r = (uint32 (fg shr 11 ) * alpha + uint32 (bg shr 11 ) * inv) shr 8
34+ let g = ((uint32 (fg) and 0x 07E0 'u32 ) * alpha + (uint32 (bg) and 0x 07E0 'u32 ) * inv) shr 8
35+ let b = (uint32 (fg and 0x 1F 'u16 ) * alpha + uint32 (bg and 0x 1F 'u16 ) * inv) shr 8
36+ uint16 ((r shl 11 ) or (g and 0x 07E0 'u32 ) or b)
37+
2938proc loadFont (path: string ): seq [byte ] =
3039 let f = open (path, fmRead)
3140 defer : f.close ()
@@ -37,21 +46,21 @@ proc measureText(font: ptr stbtt_fontinfo, text: string, scale: cfloat): int =
3746 var x: cint = 0
3847 for i, ch in text:
3948 var advanceWidth, leftSideBearing: cint
40- stbtt_GetCodepointHMetrics (font, cint (ch), addr advanceWidth, addr leftSideBearing)
49+ stbtt_GetCodepointHMetrics (font, cint (ch), addr advanceWidth,
50+ addr leftSideBearing)
4151 x += advanceWidth
4252 if i < text.len - 1 :
43- let kern = stbtt_GetCodepointKernAdvance (font, cint (ch), cint (text[i+ 1 ]))
44- x += kern
53+ x += stbtt_GetCodepointKernAdvance (font, cint (ch), cint (text[i + 1 ]))
4554 result = int (cfloat (x) * scale)
4655
47- proc wrapText (text: string , font: ptr stbtt_fontinfo, scale: cfloat , maxWidth: int ): seq [string ] =
56+ proc wrapText (text: string , font: ptr stbtt_fontinfo, scale: cfloat ,
57+ maxWidth: int ): seq [string ] =
4858 result = @ []
4959 var currentLine = " "
5060 var currentWidth = 0
5161
5262 for word in text.split (' ' ):
5363 let wordWidth = measureText (font, word & " " , scale)
54-
5564 if currentWidth + wordWidth > maxWidth and currentLine.len > 0 :
5665 result .add (currentLine.strip ())
5766 currentLine = word & " "
@@ -63,19 +72,17 @@ proc wrapText(text: string, font: ptr stbtt_fontinfo, scale: cfloat, maxWidth: i
6372 if currentLine.len > 0 :
6473 result .add (currentLine.strip ())
6574
66- proc renderTextLine (fb: ptr UncheckedArray [uint16 ], font: ptr stbtt_fontinfo,
67- text: string , y: int , pixelHeight: float , color: uint16 ) =
75+ proc renderTextLine (fb: ptr UncheckedArray [uint16 ],
76+ font: ptr stbtt_fontinfo,
77+ text: string , y: int ,
78+ pixelHeight: float , color: uint16 ) =
6879 let scale = stbtt_ScaleForPixelHeight (font, cfloat (pixelHeight))
69-
7080 var ascent, descent, lineGap: cint
7181 stbtt_GetFontVMetrics (font, addr ascent, addr descent, addr lineGap)
7282
7383 let textWidth = measureText (font, text, scale)
7484 var x = (ScreenWidth - textWidth) div 2
75- let baseline = y + int (cfloat (ascent) * scale)
76-
77- var cr, cg, cb: uint8
78- fromRGB565 (color, cr, cg, cb)
85+ let baseline = y + int (cfloat (ascent) * scale)
7986
8087 var bitmapBuf = newSeqUninit [byte ](64 * 64 )
8188
@@ -105,31 +112,26 @@ proc renderTextLine(fb: ptr UncheckedArray[uint16], font: ptr stbtt_fontinfo,
105112 let fbBase = FbXBase [px]
106113
107114 for by in 0 ..< h:
108- let alpha = bitmapBuf[by * w + bx]
115+ let alpha = uint32 ( bitmapBuf[by * w + bx])
109116 if alpha == 0 : continue
110117 let py = charY + by
111118 if py < 0 or py >= ScreenHeight : continue
112119
113- if alpha == 255 :
114- fb[fbBase + py] = color
120+ let idx = fbBase + uint32 (py)
121+ if alpha >= 255 :
122+ fb[idx] = color
115123 else :
116- let bgPixel = fb[fbBase + py]
117- var bgR, bgG, bgB: uint8
118- fromRGB565 (bgPixel, bgR, bgG, bgB)
119- let inv = 255 'u32 - uint32 (alpha)
120- let newR = uint8 ((uint32 (cr) * uint32 (alpha) + uint32 (bgR) * inv) div 255 )
121- let newG = uint8 ((uint32 (cg) * uint32 (alpha) + uint32 (bgG) * inv) div 255 )
122- let newB = uint8 ((uint32 (cb) * uint32 (alpha) + uint32 (bgB) * inv) div 255 )
123- fb[fbBase + py] = toRGB565 (newR, newG, newB)
124+ fb[idx] = blendRGB565 (color, fb[idx], alpha)
124125 {.pop .}
125126
126127 var advanceWidth, leftSideBearing: cint
127- stbtt_GetCodepointHMetrics (font, cint (ch), addr advanceWidth, addr leftSideBearing)
128+ stbtt_GetCodepointHMetrics (font, cint (ch), addr advanceWidth,
129+ addr leftSideBearing)
128130 x += int (cfloat (advanceWidth) * scale)
129131
130132 if i < text.len - 1 :
131- let kern = stbtt_GetCodepointKernAdvance (font, cint (ch), cint (text[i + 1 ]))
132- x += int ( cfloat (kern ) * scale)
133+ x += int ( cfloat ( stbtt_GetCodepointKernAdvance (
134+ font, cint (ch), cint (text[i + 1 ])) ) * scale)
133135
134136proc display * (text: string ,
135137 backgroundPath: string = DefaultBackground ,
@@ -156,23 +158,27 @@ proc display*(text: string,
156158 let fb = cast [ptr UncheckedArray [uint16 ]](fbMap)
157159
158160 if not fileExists (backgroundPath):
159- raise newException (IOError , " display: background file not found: " & backgroundPath)
161+ raise newException (IOError ,
162+ " display: background file not found: " & backgroundPath)
160163
161164 let png = loadPNG32 (backgroundPath)
162165
163166 zeroMem (fbMap, FbSize )
164167
165- var srcIdx = 0
166- for ly in 0 ..< min (png.height, ScreenHeight ):
167- for lx in 0 ..< min (png.width, ScreenWidth ):
168- let r = png.data[srcIdx]
169- let g = png.data[srcIdx + 1 ]
170- let b = png.data[srcIdx + 2 ]
171- srcIdx += 4
172- fb[FbXBase [lx] + ly] = toRGB565 (r.uint8 , g.uint8 , b.uint8 )
168+ # commence claude's NEON fuckery
169+ rgba_to_rgb565 (
170+ cast [ptr UncheckedArray [uint8 ]](unsafeAddr png.data[0 ]),
171+ cast [ptr UncheckedArray [uint16 ]](addr blitTmp[0 ]),
172+ cint (min (png.width * png.height, FbPixels )))
173+
174+ blit_transposed (
175+ cast [ptr UncheckedArray [uint16 ]](addr blitTmp[0 ]),
176+ cast [ptr UncheckedArray [uint16 ]](fbMap),
177+ cint (ScreenWidth ), cint (ScreenHeight ))
173178
174179 if not fileExists (fontPath):
175- raise newException (IOError , " display: font file not found: " & fontPath)
180+ raise newException (IOError ,
181+ " display: font file not found: " & fontPath)
176182
177183 let fontData = loadFont (fontPath)
178184 var fontInfo: stbtt_fontinfo
@@ -182,13 +188,12 @@ proc display*(text: string,
182188
183189 let scale = stbtt_ScaleForPixelHeight (addr fontInfo, cfloat (FontSize ))
184190 let lines = wrapText (text, addr fontInfo, scale, ScreenWidth - 40 )
185-
186191 let lineHeight = int (FontSize * 1.2 )
187192 var startY = (ScreenHeight - lines.len * lineHeight) div 2
188193
189194 for line in lines:
190195 renderTextLine (fb, addr fontInfo, line, startY, FontSize ,
191- toRGB565 (255 , 255 , 255 ))
196+ toRGB565 (255 , 255 , 255 ))
192197 startY += lineHeight
193198
194199 if duration == 0 :
@@ -216,7 +221,7 @@ proc showUsage(progName: string) =
216221 stderr.writeLine (" -b Background PNG image (default: quarkbg.png)" )
217222 stderr.writeLine (" -d Display duration in milliseconds (default: 0 = forever)" )
218223 stderr.writeLine (" -f Font file path (default: TwCenMT.ttf)" )
219- stderr.writeLine (" -p Don't fork into the background (only applies if duration is 0)" )
224+ stderr.writeLine (" -p Don't fork into background (only applies if duration is 0)" )
220225
221226proc main () =
222227 var
@@ -256,10 +261,10 @@ proc main() =
256261 if i + 1 <= paramCount ():
257262 let durationStr = paramStr (i + 1 ).strip ()
258263 if durationStr.len > 0 :
259- try :
260- duration = parseInt (durationStr)
264+ try : duration = parseInt (durationStr)
261265 except ValueError :
262- stderr.writeLine (" display: invalid duration value: '" & durationStr & " '" ); quit (1 )
266+ stderr.writeLine (" display: invalid duration: '" & durationStr & " '" )
267+ quit (1 )
263268 inc i
264269 else :
265270 stderr.writeLine (" display: -d requires a duration argument" )
0 commit comments