From b8897a6351ce92e6c60bfa34e9518fef37a239b9 Mon Sep 17 00:00:00 2001 From: Dany LE Date: Sun, 6 Mar 2022 00:58:28 +0100 Subject: [PATCH] FreeType face now is cached as bitmap font --- Diya/DiyaBoot.class.st | 10 ++- Diya/DiyaFontStyle.class.st | 37 +++++----- Diya/DiyaRectangle.class.st | 3 +- Diya/DiyaRootNode.class.st | 1 + Diya/DiyaText.class.st | 59 +++++++++++----- Diya/LibC.extension.st | 12 ++++ Diya/OpenGLFontTex.class.st | 119 ++++++++++++++++++++++++--------- Diya/OpenGLTexImage2D.class.st | 5 ++ Diya/OpenGLTexture.class.st | 1 - 9 files changed, 177 insertions(+), 70 deletions(-) create mode 100644 Diya/LibC.extension.st diff --git a/Diya/DiyaBoot.class.st b/Diya/DiyaBoot.class.st index 70e7c6d..f0381c2 100644 --- a/Diya/DiyaBoot.class.st +++ b/Diya/DiyaBoot.class.st @@ -27,6 +27,12 @@ DiyaBoot class >> startx [ self uniqueInstance run ] +{ #category : #events } +DiyaBoot >> GLinit. [ + OpenGL viewportX: 0 Y:0 W: display w H: display h. + OpenGL enable: GL_TEXTURE_2D. +] + { #category : #events } DiyaBoot >> createGLContext [ context := SDL2 glCreateContext: window. @@ -107,9 +113,10 @@ DiyaBoot >> render [ rec := root addNode: (DiyaRectangle size: 50@50 shader: GLSimpleShader uniqueInstance) at: 250 @ 500. rec rotation: (Float pi / -8.0). rec scale: 1.5@1.5. - text := root addNode: (DiyaText data: String loremIpsum shader: TotoShader uniqueInstance) at: 20@320. + text := root addNode: (DiyaText data: String loremIpsum shader: TotoShader uniqueInstance) at: 20@320. text extent: 200@320. "text rotation:(Float pi / 4.0)." + self GLinit. OpenGL viewportX: 0 Y:0 W: display w H: display h. "TODO: maybe give node to customize this" [ running ] whileTrue: [ @@ -118,7 +125,6 @@ DiyaBoot >> render [ ]. DiyaRenderer uniqueInstance render. SDL2 glSwapWindow: window. - "SDL2 delay: 50. " ]. ] diff --git a/Diya/DiyaFontStyle.class.st b/Diya/DiyaFontStyle.class.st index 78fcdff..dcbad13 100644 --- a/Diya/DiyaFontStyle.class.st +++ b/Diya/DiyaFontStyle.class.st @@ -2,9 +2,9 @@ Class { #name : #DiyaFontStyle, #superclass : #DiyaBaseObject, #instVars : [ + 'texmap', 'face', - 'name', - 'charmap' + 'name' ], #category : #'Diya-Fonts' } @@ -21,27 +21,13 @@ DiyaFontStyle >> face [ { #category : #accessing } DiyaFontStyle >> face: anObject [ - face := anObject. - name := anObject styleName + face := anObject ] { #category : #initialization } DiyaFontStyle >> initialize [ super initialize. - charmap := Dictionary new. -] - -{ #category : #initialization } -DiyaFontStyle >> loadChar: charCode size: size [ - |tex dic| - "lookup in the char map" - dic := charmap at: charCode ifAbsentPut: Dictionary new. - tex := dic at: size ifAbsentPut: [ - face setPixelWidth:0 height: size. - face loadCharacter: charCode flags: (1 << 2). - OpenGLFontTex fromFace: face - ]. - ^tex + texmap := Dictionary new. ] { #category : #accessing } @@ -53,3 +39,18 @@ DiyaFontStyle >> name [ DiyaFontStyle >> name: anObject [ name := anObject ] + +{ #category : #accessing } +DiyaFontStyle >> texmap [ + ^ texmap +] + +{ #category : #initialization } +DiyaFontStyle >> textureOf: size [ + |tex| + tex := texmap at: size ifAbsentPut: [ + "" + OpenGLFontTex fromFace: face ofSize: size + ]. + ^tex +] diff --git a/Diya/DiyaRectangle.class.st b/Diya/DiyaRectangle.class.st index 3b89f86..da9e7e0 100644 --- a/Diya/DiyaRectangle.class.st +++ b/Diya/DiyaRectangle.class.st @@ -14,16 +14,15 @@ DiyaRectangle >> draw [ } doWithIndex: [:e :i| context buffer at: i put: e]. shader use. shader setUniform: #u_time value: DiyaClock uniqueInstance elapsedTime asFloat. + shader setUniform: #u_transform value: {GL_TRUE. self tf asGLBuffer}. shader setUniform: #u_projection value: {GL_FALSE. context projection buffer}. shader setUniform: #u_resolution value: { context resolution x. context resolution y }. - shader setUniform: #u_transform value: {GL_TRUE. self tf asGLBuffer}. context mouse ifNotNil: [ "in shader, window origin is bottom left conor of the window the mouse position should be transformed to this coodinate" shader setUniform: #u_mouse value: { context mouse x. context resolution y - context mouse y }. ]. context vao enableAttribute: 0. - context vbo bind: GL_ARRAY_BUFFER. OpenGLVertexArray vertexAttributePointerIndex: 0 size:3 type: GL_FLOAT normalized: GL_FALSE stride: 0 pointer: nil. context vbo subData: GL_ARRAY_BUFFER offset:0 data: context buffer. OpenGL drawArrays: GL_QUADS first:0 count: 4. diff --git a/Diya/DiyaRootNode.class.st b/Diya/DiyaRootNode.class.st index eec9ab5..7d3bfca 100644 --- a/Diya/DiyaRootNode.class.st +++ b/Diya/DiyaRootNode.class.st @@ -8,6 +8,7 @@ Class { DiyaRootNode >> draw [ OpenGL clearColorR: 1.0 G: 0.0 B: 1.0 A:0. OpenGL clear: GL_COLOR_BUFFER_BIT. + context vbo bind: GL_ARRAY_BUFFER. ] { #category : #initialization } diff --git a/Diya/DiyaText.class.st b/Diya/DiyaText.class.st index 296a636..263484d 100644 --- a/Diya/DiyaText.class.st +++ b/Diya/DiyaText.class.st @@ -36,12 +36,11 @@ DiyaText >> data: anObject [ { #category : #accessing } DiyaText >> draw [ - | offset| + | offset tex2D| data ifNil: [ ^self ]. offset := 0.0@0.0. OpenGL enable: GL_CULL_FACE. OpenGL enable: GL_BLEND. - OpenGL enable: GL_TEXTURE_2D. OpenGL blendFnWithSfactor: GL_SRC_ALPHA dfactor: GL_ONE_MINUS_SRC_ALPHA. self shader use. shader setUniform: #u_time value: DiyaClock uniqueInstance elapsedTime asFloat. @@ -53,9 +52,15 @@ DiyaText >> draw [ OpenGL pixelstorei: GL_UNPACK_ALIGNMENT param: 1. "configure vao vbo for texture QUAD" context texture0 active. + tex2D := style textureOf: self fontSize. + context texture0 setImage2D: tex2D. + OpenGLTexture parameteri: GL_TEXTURE_2D pname: GL_TEXTURE_WRAP_S param: GL_CLAMP_TO_EDGE. + OpenGLTexture parameteri: GL_TEXTURE_2D pname: GL_TEXTURE_WRAP_T param: GL_CLAMP_TO_EDGE. + OpenGLTexture parameteri: GL_TEXTURE_2D pname: GL_TEXTURE_MIN_FILTER param: GL_LINEAR. + OpenGLTexture parameteri: GL_TEXTURE_2D pname: GL_TEXTURE_MAG_FILTER param: GL_LINEAR. context vao enableAttribute: 0. OpenGLVertexArray vertexAttributePointerIndex: 0 size:4 type: GL_FLOAT normalized: GL_FALSE stride: 16 pointer: nil . - data do:[:c | self drawCharacter: c at: offset]. + data do:[:c | self drawCharacter: c asciiValue at: offset withTexture:tex2D. ]. context vao disableAttribute: 0. "reset value" OpenGL pixelstorei: GL_UNPACK_ALIGNMENT param: 4. @@ -65,36 +70,56 @@ DiyaText >> draw [ ] { #category : #accessing } -DiyaText >> drawCharacter: c at: offset [ - |tex2D| - tex2D := style loadChar: c asciiValue size: self fontSize. - context texture0 setImage2D: tex2D. - OpenGLTexture parameteri: GL_TEXTURE_2D pname: GL_TEXTURE_WRAP_S param: GL_CLAMP_TO_EDGE. - OpenGLTexture parameteri: GL_TEXTURE_2D pname: GL_TEXTURE_WRAP_T param: GL_CLAMP_TO_EDGE. - OpenGLTexture parameteri: GL_TEXTURE_2D pname: GL_TEXTURE_MIN_FILTER param: GL_LINEAR. - OpenGLTexture parameteri: GL_TEXTURE_2D pname: GL_TEXTURE_MAG_FILTER param: GL_LINEAR. - "fill the buffer" - ((offset x > extent x) and: (tex2D width > 0)) ifTrue:[ +DiyaText >> drawCharacter: c at: offset withTexture: tex2D [ + |x y w h charsize texcoord| + c = (Character space asciiValue) ifTrue:[ + ^offset setX: (offset x + ((tex2D spacing )* (self scale x)) ) setY: offset y. + ]. + charsize := tex2D charSize: c. + ((offset x > extent x) and: (charsize x > 0)) ifTrue:[ offset setX: 0.0 setY: (offset y )- (tex2D linespace * (self scale y)).]. - self fillVerticesBuffer: context buffer at: offset tex: tex2D. + charsize := tex2D charSize: c. + texcoord := tex2D texCoordOf: c. + x := offset x "+ ((tex bearing x )*(self scale x))". + y := offset y - ((tex2D cellh) * (self scale y)) "(((tex height) - (tex bearing y))*(self scale y))". + w := (charsize x)*(self scale x). + h := (charsize y)*(self scale y). + {x. y + h. texcoord origin x. texcoord origin y. + x. y. texcoord origin x. texcoord corner y. + x + w. y. texcoord corner x. texcoord corner y. + x. y + h. texcoord origin x. texcoord origin y. + x + w. y. texcoord corner x. texcoord corner y. + x + w. y + h. texcoord corner x. texcoord origin y. } withIndexDo: [ :e :i| context buffer at:i put: e ]. context vbo subData: GL_ARRAY_BUFFER offset: 0 data: context buffer. OpenGL drawArrays: GL_TRIANGLES first:0 count:6. - offset setX: (offset x + ((tex2D advance x )* (self scale x)) ) setY: offset y. + offset setX: (offset x + ((charsize x )* (self scale x)) ) setY: offset y. ] { #category : #accessing } DiyaText >> fillVerticesBuffer: buffer at: offset tex: tex [ |x y w h| - x := offset x + ((tex bearing x )*(self scale x)). + "x := offset x + ((tex bearing x )*(self scale x)). y := offset y - (((tex height) - (tex bearing y))*(self scale y)). w := (tex width)*(self scale x). - h := (tex height)*(self scale y). + h := (tex height)*(self scale y)." + x := 0. + y := 0. + w := tex width. + h := tex height. {x. y + h. 0.0. 0.0. x. y. 0.0. 1.0. x + w. y. 1.0. 1.0. x. y + h. 0.0. 0.0. x + w. y. 1.0. 1.0. x + w. y + h. 1.0. 0.0. } withIndexDo: [ :e :i| buffer at:i put: e ] + + +"{x. y + h. 0.0. 0.0. + x. y. 0.0. 1.0. + x + w. y. 1.0. 1.0. + x. y + h. 0.0. 0.0. + x + w. y. 1.0. 1.0. + x + w. y + h. 1.0. 0.0. }" ] { #category : #accessing } diff --git a/Diya/LibC.extension.st b/Diya/LibC.extension.st new file mode 100644 index 0000000..e0c39c5 --- /dev/null +++ b/Diya/LibC.extension.st @@ -0,0 +1,12 @@ +Extension { #name : #LibC } + +{ #category : #'*Diya' } +LibC >> memset:pointer value: value size: size [ + ^self ffiCall: #(void *memset(void *pointer, int value, size_t size)) + +] + +{ #category : #'*Diya' } +LibC class >> memset:pointer value: value size: size [ + ^self uniqueInstance memset: pointer value: value size: size +] diff --git a/Diya/OpenGLFontTex.class.st b/Diya/OpenGLFontTex.class.st index b1a2265..1fa83c4 100644 --- a/Diya/OpenGLFontTex.class.st +++ b/Diya/OpenGLFontTex.class.st @@ -2,9 +2,9 @@ Class { #name : #OpenGLFontTex, #superclass : #OpenGLTexImage2D, #instVars : [ - 'bearing', - 'advance', - 'linespace' + 'charmap', + 'cellw', + 'cellh' ], #pools : [ 'OpenGLConstants', @@ -14,58 +14,117 @@ Class { } { #category : #'instance creation' } -OpenGLFontTex class >> fromFace: face [ - ^self new fromFace: face; yourself +OpenGLFontTex class >> fromFace: face ofSize: size [ + ^self new fromFace: face ofSize: size; yourself +] + +{ #category : #'instance creation' } +OpenGLFontTex >> blitPixel8: bitmap at: offset size: size [ + size = (0@0) ifTrue:[^self]. + 0 to: size y - 1 do: [ :i| + LibC memCopy: (bitmap getHandle + (i* (size x))) to:(data getHandle + ((i + offset y) * width + (offset x) )) size: size x + ] ] { #category : #accessing } -OpenGLFontTex >> advance [ - ^ advance +OpenGLFontTex >> cellh [ + ^ cellh ] { #category : #accessing } -OpenGLFontTex >> advance: anObject [ - advance := anObject +OpenGLFontTex >> cellw [ + ^ cellw ] { #category : #accessing } -OpenGLFontTex >> bearing [ - ^ bearing +OpenGLFontTex >> charSize: c [ + ^(self charmap at: c + 1) extent ] { #category : #accessing } -OpenGLFontTex >> bearing: anObject [ - bearing := anObject +OpenGLFontTex >> charmap [ + ^ charmap ] -{ #category : #accessing } -OpenGLFontTex >> fromFace: aFTFace [ - |size glyph rec| - glyph := aFTFace glyph. - size := glyph width * glyph height. - data := FFIExternalArray externalNewType: #uint8 size:size. - rec := (FTFaceRec fromHandle: aFTFace getHandle). +{ #category : #'instance creation' } +OpenGLFontTex >> createBitmapFontFrom: bitmaps metrics: metrics maxBearing: maxbearing [ + |currChar offset| + data ifNotNil: [data free ]. + charmap := OrderedCollection new. + data := FFIExternalArray externalNewType: GLubyte size:cellw * cellh * 256. + LibC memset: data getHandle value: 0 size: data size. data autoRelease. - LibC memCopy: (rec glyph bitmap buffer) to:data getHandle size: size. + currChar := 1. + offset := 0@0. + width := cellw * 16. + height := cellh * 16. + "linespace := (rec size metrics height / 64) asInteger." + 0 to: 15 do: [ :row| + 0 to: 15 do: [ :col| + offset := (col * cellw) @ (row*cellh). + charmap add: (Rectangle origin: offset extent: (((metrics at: currChar) first x) @ cellh)). + self blitPixel8: (bitmaps at: currChar) at: (offset x) @ ((offset y) + maxbearing - ((metrics at: currChar) last) ) size: (metrics at: currChar) first. + currChar := currChar + 1. + ] + ]. +] + +{ #category : #'instance creation' } +OpenGLFontTex >> fromFace: face ofSize: size [ + |minhang maxbearing rec metrics bitmaps | + face setPixelWidth:0 height: size. + "set up a buffer for 256 characters" + bitmaps := OrderedCollection new. + metrics := OrderedCollection new. + cellw := 0. + cellh := 0. + minhang := 0. + maxbearing := 0. + rec := (FTFaceRec fromHandle: face getHandle). + 0 to: 255 do: [ :c | + |bmp bmpsize| + face loadCharacter: c flags: (1 << 2). + metrics add: { ((rec glyph metrics width) /64)@ ((rec glyph metrics height) /64). (rec glyph metrics horiBearingY) / 64}. + maxbearing := maxbearing max: ((rec glyph metrics horiBearingY) / 64). + cellw := cellw max: ((rec glyph metrics width) / 64). + minhang := minhang min: ((( rec glyph metrics horiBearingY) - (rec glyph metrics height )) / 64). + "copy buffer" + bmpsize := (rec glyph bitmap width)*(rec glyph bitmap rows). + bmp := FFIExternalArray externalNewType: GLubyte size:bmpsize. + LibC memCopy: rec glyph bitmap buffer to: bmp getHandle size: bmpsize. + bitmaps add: bmp. + ]. + cellh := maxbearing - minhang. + self createBitmapFontFrom: bitmaps metrics: metrics maxBearing: maxbearing. + bitmaps do:[:p| p free]. +] + +{ #category : #initialization } +OpenGLFontTex >> initialize [ + super initialize. + charmap := OrderedCollection new. + data := nil. level := 0. - internalFormat := GL_ALPHA. - width := glyph width. - height := glyph height. - linespace := (rec size metrics height / 64) asInteger. border := 0. format := GL_ALPHA. + internalFormat := GL_ALPHA. type := GL_UNSIGNED_BYTE. target := GL_TEXTURE_2D. - bearing := (glyph hBearingX )@ (glyph hBearingY). - advance := (glyph advanceX )@ (glyph advanceY). ] { #category : #accessing } OpenGLFontTex >> linespace [ - ^ linespace + ^ cellh ] { #category : #accessing } -OpenGLFontTex >> linespace: anObject [ - linespace := anObject +OpenGLFontTex >> spacing [ + ^ cellw / 2 +] + +{ #category : #accessing } +OpenGLFontTex >> texCoordOf: c [ + |rec| + rec := self charmap at: c + 1. + ^ Rectangle origin: ((rec origin )/ (self extent) ) asFloatPoint corner: ((rec corner) / (self extent)) asFloatPoint ] diff --git a/Diya/OpenGLTexImage2D.class.st b/Diya/OpenGLTexImage2D.class.st index 65952fa..254aed1 100644 --- a/Diya/OpenGLTexImage2D.class.st +++ b/Diya/OpenGLTexImage2D.class.st @@ -52,6 +52,11 @@ OpenGLTexImage2D >> debug [ stdlog: 'internalformat :',internalFormat hex ] +{ #category : #accessing } +OpenGLTexImage2D >> extent [ + ^ width @ height +] + { #category : #accessing } OpenGLTexImage2D >> format [ ^ format diff --git a/Diya/OpenGLTexture.class.st b/Diya/OpenGLTexture.class.st index 51d97f1..7c356ca 100644 --- a/Diya/OpenGLTexture.class.st +++ b/Diya/OpenGLTexture.class.st @@ -76,7 +76,6 @@ OpenGLTexture >> bind: target [ { #category : #initialization } OpenGLTexture >> delete [ self class delete: 1 pointer: textureID getHandle. - textureID free. ] { #category : #initialization }