brillo-rendering 2.0.0 → 3.0.0
raw patch · 7 files changed
+470/−52 lines, 7 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Brillo.Rendering: CompressedFormat :: Word32 -> CompressedFormat
+ Brillo.Rendering: PxCompressed :: CompressedFormat -> PixelFormat
+ Brillo.Rendering: Shader :: ShaderData -> Picture
+ Brillo.Rendering: ShaderData :: !Text -> !Text -> ![(Text, UniformValue)] -> !Float -> !Float -> ShaderData
+ Brillo.Rendering: UniformFloat :: !Float -> UniformValue
+ Brillo.Rendering: UniformInt :: !Int -> UniformValue
+ Brillo.Rendering: UniformVec2 :: !Float -> !Float -> UniformValue
+ Brillo.Rendering: UniformVec3 :: !Float -> !Float -> !Float -> UniformValue
+ Brillo.Rendering: UniformVec4 :: !Float -> !Float -> !Float -> !Float -> UniformValue
+ Brillo.Rendering: [shaderFragment] :: ShaderData -> !Text
+ Brillo.Rendering: [shaderHeight] :: ShaderData -> !Float
+ Brillo.Rendering: [shaderUniforms] :: ShaderData -> ![(Text, UniformValue)]
+ Brillo.Rendering: [shaderVertex] :: ShaderData -> !Text
+ Brillo.Rendering: [shaderWidth] :: ShaderData -> !Float
+ Brillo.Rendering: compressedBitmapDataOfByteString :: Int -> Int -> RowOrder -> CompressedFormat -> ByteString -> Bool -> BitmapData
+ Brillo.Rendering: compressedBitmapDataOfForeignPtr :: Int -> Int -> RowOrder -> CompressedFormat -> Int -> ForeignPtr Word8 -> Bool -> BitmapData
+ Brillo.Rendering: compressedBitmapOfByteString :: Int -> Int -> RowOrder -> CompressedFormat -> ByteString -> Bool -> Picture
+ Brillo.Rendering: compressedBitmapOfForeignPtr :: Int -> Int -> RowOrder -> CompressedFormat -> Int -> ForeignPtr Word8 -> Bool -> Picture
+ Brillo.Rendering: compressedRED_RGTC1 :: CompressedFormat
+ Brillo.Rendering: compressedRGBA_BPTC_UNORM :: CompressedFormat
+ Brillo.Rendering: compressedRGBA_S3TC_DXT1 :: CompressedFormat
+ Brillo.Rendering: compressedRGBA_S3TC_DXT3 :: CompressedFormat
+ Brillo.Rendering: compressedRGBA_S3TC_DXT5 :: CompressedFormat
+ Brillo.Rendering: compressedRGB_BPTC_SIGNED_FLOAT :: CompressedFormat
+ Brillo.Rendering: compressedRGB_BPTC_UNSIGNED_FLOAT :: CompressedFormat
+ Brillo.Rendering: compressedRGB_S3TC_DXT1 :: CompressedFormat
+ Brillo.Rendering: compressedRG_RGTC2 :: CompressedFormat
+ Brillo.Rendering: compressedSIGNED_RED_RGTC1 :: CompressedFormat
+ Brillo.Rendering: compressedSIGNED_RG_RGTC2 :: CompressedFormat
+ Brillo.Rendering: compressedSRGB_ALPHA_BPTC_UNORM :: CompressedFormat
+ Brillo.Rendering: compressedSRGB_ALPHA_S3TC_DXT1 :: CompressedFormat
+ Brillo.Rendering: compressedSRGB_ALPHA_S3TC_DXT3 :: CompressedFormat
+ Brillo.Rendering: compressedSRGB_ALPHA_S3TC_DXT5 :: CompressedFormat
+ Brillo.Rendering: compressedSRGB_S3TC_DXT1 :: CompressedFormat
+ Brillo.Rendering: data ShaderData
+ Brillo.Rendering: data UniformValue
+ Brillo.Rendering: defaultShaderVertex :: Text
+ Brillo.Rendering: newtype CompressedFormat
+ Brillo.Rendering: shader :: Float -> Float -> Text -> [(Text, UniformValue)] -> Picture
Files
- Brillo/Internals/Data/Picture.hs +178/−23
- Brillo/Internals/Rendering/Bitmap.hs +111/−2
- Brillo/Internals/Rendering/Picture.hs +36/−22
- Brillo/Internals/Rendering/Shader.hs +87/−2
- Brillo/Internals/Rendering/TrueTypeFont.hs +27/−2
- Brillo/Rendering.hs +30/−0
- brillo-rendering.cabal +1/−1
Brillo/Internals/Data/Picture.hs view
@@ -9,18 +9,29 @@ Path, Picture (..), + -- * Shaders+ UniformValue (..),+ ShaderData (..),+ defaultShaderVertex,+ shader,+ -- * Bitmaps Rectangle (..), BitmapData (..), PixelFormat (..), BitmapFormat (..), RowOrder (..),+ CompressedFormat (..), bitmapOfForeignPtr, bitmapDataOfForeignPtr, bitmapOfByteString, bitmapDataOfByteString, bitmapOfBMP, bitmapDataOfBMP,+ compressedBitmapOfForeignPtr,+ compressedBitmapDataOfForeignPtr,+ compressedBitmapOfByteString,+ compressedBitmapDataOfByteString, loadBMP, rectAtOrigin, )@@ -30,15 +41,17 @@ import Brillo.Internals.Rendering.Bitmap ( BitmapData (..), BitmapFormat (..),+ CompressedFormat (..), PixelFormat (..), Rectangle (..), RowOrder (..), ) import Codec.BMP (BMP, bmpDimensions, readBMP, unpackBMPToRGBA32) import Data.ByteString (ByteString)+import Data.ByteString qualified as BS import Data.ByteString.Unsafe qualified as BSU import Data.Data (Data, Typeable)-import Data.Text (Text)+import Data.Text (Text, pack) import Data.Word (Word8) import Foreign.ForeignPtr (ForeignPtr, newForeignPtr) import Foreign.Marshal.Alloc (finalizerFree, mallocBytes)@@ -66,6 +79,27 @@ type Path = [Point] +-- | A value that can be passed as a uniform to a custom shader.+data UniformValue+ = UniformFloat !Float+ | UniformVec2 !Float !Float+ | UniformVec3 !Float !Float !Float+ | UniformVec4 !Float !Float !Float !Float+ | UniformInt !Int+ deriving (Show, Eq, Data, Typeable)+++-- | Data needed to render a custom shader on a quad.+data ShaderData = ShaderData+ { shaderVertex :: !Text+ , shaderFragment :: !Text+ , shaderUniforms :: ![(Text, UniformValue)]+ , shaderWidth :: !Float+ , shaderHeight :: !Float+ }+ deriving (Show, Eq, Data, Typeable)++ -- | A 2D picture data Picture = -- Primitives -------------------------------------@@ -145,6 +179,13 @@ Rotate Float Picture | -- | A picture scaled by the given x and y factors. Scale Float Float Picture+ | -- Shaders ----------------------------------------++ {-| A picture rendered by a custom GLSL shader on a quad.+ The quad is centered at the origin with the dimensions from 'ShaderData'.+ Use the 'shader' convenience function to create one with the default vertex shader.+ -}+ Shader ShaderData | -- More Pictures ---------------------------------- -- | A picture consisting of several others.@@ -209,16 +250,10 @@ bitmapDataOfByteString :: Int -> Int -> BitmapFormat -> ByteString -> Bool -> BitmapData bitmapDataOfByteString width height fmt bs cacheMe =- unsafePerformIO $- do- let len = width * height * 4- ptr <- mallocBytes len- fptr <- newForeignPtr finalizerFree ptr-- BSU.unsafeUseAsCString bs $- \cstr -> copyBytes ptr (castPtr cstr) len-- return $ BitmapData len fmt (width, height) cacheMe fptr+ unsafePerformIO $ do+ let len = width * height * 4+ fptr <- copyByteStringToFPtr len bs+ return $ BitmapData len fmt (width, height) cacheMe fptr {-# NOINLINE bitmapDataOfByteString #-} @@ -231,23 +266,100 @@ -- | O(size). Copy a `BMP` file into a bitmap. bitmapDataOfBMP :: BMP -> BitmapData bitmapDataOfBMP bmp =- unsafePerformIO $- do- let (width, height) = bmpDimensions bmp- let bs = unpackBMPToRGBA32 bmp- let len = width * height * 4+ unsafePerformIO $ do+ let (width, height) = bmpDimensions bmp+ len = width * height * 4+ fptr <- copyByteStringToFPtr len (unpackBMPToRGBA32 bmp)+ return $+ BitmapData len (BitmapFormat BottomToTop PxRGBA) (width, height) True fptr+{-# NOINLINE bitmapDataOfBMP #-} - ptr <- mallocBytes len- fptr <- newForeignPtr finalizerFree ptr - BSU.unsafeUseAsCString bs $- \cstr -> copyBytes ptr (castPtr cstr) len+-- | Copy @len@ bytes from a `ByteString` into a freshly allocated `ForeignPtr`.+copyByteStringToFPtr :: Int -> ByteString -> IO (ForeignPtr Word8)+copyByteStringToFPtr len bs = do+ ptr <- mallocBytes len+ fptr <- newForeignPtr finalizerFree ptr+ BSU.unsafeUseAsCString bs $ \cstr -> copyBytes ptr (castPtr cstr) len+ pure fptr - return $- BitmapData len (BitmapFormat BottomToTop PxRGBA) (width, height) True fptr-{-# NOINLINE bitmapDataOfBMP #-} +{-| O(1). Use a `ForeignPtr` of GPU-compressed texel data as a bitmap. + The image is uploaded to the GPU as-is using+ `glCompressedTexImage2D`. The byte length is the exact size of the+ compressed payload, and @width@ / @height@ must match the dimensions+ of the decoded image. The chosen `CompressedFormat` must be supported+ by the running OpenGL implementation; query+ @Graphics.Rendering.OpenGL.GL.Texturing.Specification.compressedTextureFormats@+ at runtime if you are not sure.++ The boolean flag controls whether Brillo should cache the data+ between frames for speed. If you are programatically generating+ the image for each frame then use `False`. If you have loaded it+ from a file then use `True`.+-}+compressedBitmapOfForeignPtr ::+ Int ->+ Int ->+ RowOrder ->+ CompressedFormat ->+ Int ->+ ForeignPtr Word8 ->+ Bool ->+ Picture+compressedBitmapOfForeignPtr w h ro cf byteLen fptr cacheMe =+ Bitmap $+ compressedBitmapDataOfForeignPtr w h ro cf byteLen fptr cacheMe+++compressedBitmapDataOfForeignPtr ::+ Int ->+ Int ->+ RowOrder ->+ CompressedFormat ->+ Int ->+ ForeignPtr Word8 ->+ Bool ->+ BitmapData+compressedBitmapDataOfForeignPtr w h ro cf byteLen fptr cacheMe =+ BitmapData byteLen (BitmapFormat ro (PxCompressed cf)) (w, h) cacheMe fptr+++{-| O(size). Copy a `ByteString` of GPU-compressed texel data into a bitmap.++ See `compressedBitmapOfForeignPtr` for the constraints on the inputs.+-}+compressedBitmapOfByteString ::+ Int ->+ Int ->+ RowOrder ->+ CompressedFormat ->+ ByteString ->+ Bool ->+ Picture+compressedBitmapOfByteString w h ro cf bs cacheMe =+ Bitmap $+ compressedBitmapDataOfByteString w h ro cf bs cacheMe+++compressedBitmapDataOfByteString ::+ Int ->+ Int ->+ RowOrder ->+ CompressedFormat ->+ ByteString ->+ Bool ->+ BitmapData+compressedBitmapDataOfByteString w h ro cf bs cacheMe =+ unsafePerformIO $ do+ let byteLen = BS.length bs+ fptr <- copyByteStringToFPtr byteLen bs+ return $+ BitmapData byteLen (BitmapFormat ro (PxCompressed cf)) (w, h) cacheMe fptr+{-# NOINLINE compressedBitmapDataOfByteString #-}++ -- | Load an uncompressed 24 or 32bit RGBA BMP file as a bitmap. loadBMP :: FilePath -> IO Picture loadBMP filePath =@@ -263,3 +375,46 @@ -} rectAtOrigin :: Int -> Int -> Rectangle rectAtOrigin w h = Rectangle (0, 0) (w, h)+++-- Shaders -------------------------------------------------------------------++{-| The default vertex shader used for custom shaders.+Passes through vertex positions via the model-view-projection matrix+and forwards texture coordinates as @vLocalCoord@.+-}+defaultShaderVertex :: Text+defaultShaderVertex =+ pack $+ unlines+ [ "#version 120"+ , "varying vec2 vLocalCoord;"+ , "void main() {"+ , " vLocalCoord = gl_MultiTexCoord0.xy;"+ , " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;"+ , " gl_FrontColor = gl_Color;"+ , "}"+ ]+++{-| Create a 'Picture' that renders a custom GLSL fragment shader on a quad+of the given width and height, centered at the origin.++The fragment shader receives:++* @vLocalCoord@ (vec2): local coordinates from @(-w\/2, -h\/2)@ to @(w\/2, h\/2)@+* @gl_Color@: the current Brillo color (set via the 'Color' picture combinator)+* Any custom uniforms passed in the list++Uses the built-in vertex shader ('defaultShaderVertex').+-}+shader :: Float -> Float -> Text -> [(Text, UniformValue)] -> Picture+shader w h frag uniforms =+ Shader+ ShaderData+ { shaderVertex = defaultShaderVertex+ , shaderFragment = frag+ , shaderUniforms = uniforms+ , shaderWidth = w+ , shaderHeight = h+ }
Brillo/Internals/Rendering/Bitmap.hs view
@@ -8,12 +8,31 @@ BitmapFormat (..), PixelFormat (..), RowOrder (..),+ CompressedFormat (..),+ -- Common compressed format constants+ compressedRGB_S3TC_DXT1,+ compressedRGBA_S3TC_DXT1,+ compressedRGBA_S3TC_DXT3,+ compressedRGBA_S3TC_DXT5,+ compressedSRGB_S3TC_DXT1,+ compressedSRGB_ALPHA_S3TC_DXT1,+ compressedSRGB_ALPHA_S3TC_DXT3,+ compressedSRGB_ALPHA_S3TC_DXT5,+ compressedRED_RGTC1,+ compressedSIGNED_RED_RGTC1,+ compressedRG_RGTC2,+ compressedSIGNED_RG_RGTC2,+ compressedRGBA_BPTC_UNORM,+ compressedSRGB_ALPHA_BPTC_UNORM,+ compressedRGB_BPTC_SIGNED_FLOAT,+ compressedRGB_BPTC_UNSIGNED_FLOAT, bitmapPath, freeBitmapData, ) where import Data.Data (Data, Typeable)+import Data.Word (Word32) import Foreign (ForeignPtr, Ptr, Word8, free) @@ -64,11 +83,101 @@ deriving (Eq, Data, Typeable, Show, Ord, Enum, Bounded) --- | Pixel formats describe the order of the color channels in memory.+{-| Pixel formats describe how the texel data is laid out in memory.++ `PxRGBA` and `PxABGR` are uncompressed 32-bit-per-pixel formats.++ `PxCompressed` carries a hardware compressed format token that the GPU+ decodes on its own. The bitmap's `bitmapDataLength` field must hold the+ exact size in bytes of the compressed payload, since the size cannot be+ derived from width and height alone.+-} data PixelFormat = PxRGBA | PxABGR- deriving (Eq, Data, Typeable, Show, Ord, Enum, Bounded)+ | PxCompressed CompressedFormat+ deriving (Eq, Data, Typeable, Show, Ord)+++{-| Identifier of a GPU-side compressed texture format.++ The wrapped value is the OpenGL @internalformat@ enum (for example+ @0x83F1@ for @GL_COMPRESSED_RGBA_S3TC_DXT1_EXT@). Use the constants+ exported alongside this type, or supply an enum from+ "Graphics.Rendering.OpenGL.Raw" directly.+-}+newtype CompressedFormat = CompressedFormat Word32+ deriving (Eq, Data, Typeable, Show, Ord)+++-- S3TC / DXT --------------------------------------------------------------++compressedRGB_S3TC_DXT1 :: CompressedFormat+compressedRGB_S3TC_DXT1 = CompressedFormat 0x83F0+++compressedRGBA_S3TC_DXT1 :: CompressedFormat+compressedRGBA_S3TC_DXT1 = CompressedFormat 0x83F1+++compressedRGBA_S3TC_DXT3 :: CompressedFormat+compressedRGBA_S3TC_DXT3 = CompressedFormat 0x83F2+++compressedRGBA_S3TC_DXT5 :: CompressedFormat+compressedRGBA_S3TC_DXT5 = CompressedFormat 0x83F3+++compressedSRGB_S3TC_DXT1 :: CompressedFormat+compressedSRGB_S3TC_DXT1 = CompressedFormat 0x8C4C+++compressedSRGB_ALPHA_S3TC_DXT1 :: CompressedFormat+compressedSRGB_ALPHA_S3TC_DXT1 = CompressedFormat 0x8C4D+++compressedSRGB_ALPHA_S3TC_DXT3 :: CompressedFormat+compressedSRGB_ALPHA_S3TC_DXT3 = CompressedFormat 0x8C4E+++compressedSRGB_ALPHA_S3TC_DXT5 :: CompressedFormat+compressedSRGB_ALPHA_S3TC_DXT5 = CompressedFormat 0x8C4F+++-- RGTC --------------------------------------------------------------------++compressedRED_RGTC1 :: CompressedFormat+compressedRED_RGTC1 = CompressedFormat 0x8DBB+++compressedSIGNED_RED_RGTC1 :: CompressedFormat+compressedSIGNED_RED_RGTC1 = CompressedFormat 0x8DBC+++compressedRG_RGTC2 :: CompressedFormat+compressedRG_RGTC2 = CompressedFormat 0x8DBD+++compressedSIGNED_RG_RGTC2 :: CompressedFormat+compressedSIGNED_RG_RGTC2 = CompressedFormat 0x8DBE+++-- BPTC --------------------------------------------------------------------++compressedRGBA_BPTC_UNORM :: CompressedFormat+compressedRGBA_BPTC_UNORM = CompressedFormat 0x8E8C+++compressedSRGB_ALPHA_BPTC_UNORM :: CompressedFormat+compressedSRGB_ALPHA_BPTC_UNORM = CompressedFormat 0x8E8D+++compressedRGB_BPTC_SIGNED_FLOAT :: CompressedFormat+compressedRGB_BPTC_SIGNED_FLOAT = CompressedFormat 0x8E8E+++compressedRGB_BPTC_UNSIGNED_FLOAT :: CompressedFormat+compressedRGB_BPTC_UNSIGNED_FLOAT = CompressedFormat 0x8E8F instance Show BitmapData where
Brillo/Internals/Rendering/Picture.hs view
@@ -23,8 +23,9 @@ import Brillo.Internals.Data.Color (Color (RGBA)) import Brillo.Internals.Data.Picture ( BitmapFormat (pixelFormat, rowOrder),+ CompressedFormat (CompressedFormat), Picture (..),- PixelFormat (PxABGR, PxRGBA),+ PixelFormat (PxABGR, PxCompressed, PxRGBA), Rectangle (Rectangle, rectPos, rectSize), RowOrder (BottomToTop, TopToBottom), rectAtOrigin,@@ -42,6 +43,7 @@ import Brillo.Internals.Rendering.Shader ( renderArcSDF, renderCircleSDF,+ renderCustomShader, renderThickLineSDF, ) import Brillo.Internals.Rendering.State (@@ -385,6 +387,9 @@ -- Free uncachable texture objects. freeTexture tex+ -- Custom shader+ Shader shaderData ->+ renderCustomShader state.stateShaders shaderData Pictures ps -> mapM_ (drawPicture state circScale) ps @@ -398,11 +403,18 @@ mapM_ (handleError place) errors +{-| Report an OpenGL error.++These are logged to @stderr@ rather than thrown so that a single error from a+driver quirk (e.g. the shader-based rendering path on some Windows GL drivers)+doesn't crash the whole program. The error flag has already been cleared by+'checkErrors' reading it, so rendering can continue.+-} handleError :: String -> GLU.Error -> IO () handleError place err = case err of GLU.Error GLU.StackOverflow _ ->- error $+ hPutStrLn stderr $ unlines [ "Brillo / OpenGL Stack Overflow " ++ show place , " This program uses the Brillo vector graphics library, which tried to"@@ -419,7 +431,7 @@ , " transforms used when defining the Picture given to Brillo. Sorry." ] _ ->- error $+ hPutStrLn stderr $ unlines [ "Brillo / OpenGL Internal Error " ++ show place , " Please report this at https://github.com/ad-si/Brillo/issues."@@ -470,13 +482,8 @@ returning the new texture handle. -} installTexture :: BitmapData -> IO Texture-installTexture bitmapData@(BitmapData _ fmt (width, height) cacheMe fptr) =+installTexture bitmapData@(BitmapData byteLen fmt (width, height) cacheMe fptr) = do- let glFormat =- case pixelFormat fmt of- PxABGR -> GL.ABGR- PxRGBA -> GL.RGBA- -- Allocate texture handle for texture [tex] <- GL.genObjectNames 1 GL.textureBinding GL.Texture2D $= Just tex@@ -484,19 +491,26 @@ -- Sets the texture in imgData as the current texture -- This copies the data from the pointer into OpenGL texture memory, -- so it's ok if the foreignptr gets garbage collected after this.- withForeignPtr fptr $- \ptr ->- GL.texImage2D- GL.Texture2D- GL.NoProxy- 0- GL.RGBA8- ( GL.TextureSize2D- (gsizei width)- (gsizei height)- )- 0- (GL.PixelData glFormat GL.UnsignedByte ptr)+ let size = GL.TextureSize2D (gsizei width) (gsizei height)+ uploadRGBA8 glFormat ptr =+ GL.texImage2D GL.Texture2D GL.NoProxy 0 GL.RGBA8 size 0 $+ GL.PixelData glFormat GL.UnsignedByte ptr+ withForeignPtr fptr $ \ptr ->+ case pixelFormat fmt of+ PxCompressed (CompressedFormat compFmt) ->+ GL.compressedTexImage2D+ GL.Texture2D+ GL.NoProxy+ 0+ size+ 0+ ( GL.CompressedPixelData+ (GL.CompressedTextureFormat (fromIntegral compFmt))+ (fromIntegral byteLen)+ ptr+ )+ PxABGR -> uploadRGBA8 GL.ABGR ptr+ PxRGBA -> uploadRGBA8 GL.RGBA ptr -- Make a stable name that we can use to identify this data again. -- If the user gives us the same texture data at the same size then we
Brillo/Internals/Rendering/Shader.hs view
@@ -7,19 +7,27 @@ renderCircleSDF, renderArcSDF, renderThickLineSDF,+ renderCustomShader, ) where -import Control.Monad (unless)-import Data.IORef (IORef, newIORef, readIORef, writeIORef)+import Control.Monad (forM_, unless)+import Data.IORef (IORef, modifyIORef', newIORef, readIORef, writeIORef)+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Text (Text)+import Data.Text qualified as Text import Graphics.Rendering.OpenGL (($=)) import Graphics.Rendering.OpenGL.GL qualified as GL +import Brillo.Internals.Data.Picture (ShaderData (..), UniformValue (..)) + -- | State for shader-based rendering data ShaderState = ShaderState { circleProgram :: !(IORef (Maybe GL.Program)) , arcProgram :: !(IORef (Maybe GL.Program)) , lineProgram :: !(IORef (Maybe GL.Program))+ , customPrograms :: !(IORef (Map (Text, Text) GL.Program)) } @@ -29,11 +37,13 @@ circleRef <- newIORef Nothing arcRef <- newIORef Nothing lineRef <- newIORef Nothing+ customRef <- newIORef Map.empty return ShaderState { circleProgram = circleRef , arcProgram = arcRef , lineProgram = lineRef+ , customPrograms = customRef } @@ -461,3 +471,78 @@ where renderSegment ((ax, ay), (bx, by)) = renderLineSegmentSDF state ax ay bx by scaleFactor thickness color+++-- Custom shaders ------------------------------------------------------------++-- | Get or compile a custom shader program, caching by (vertex, fragment) source pair.+getCustomProgram :: ShaderState -> Text -> Text -> IO GL.Program+getCustomProgram state vertSrc fragSrc = do+ cache <- readIORef (customPrograms state)+ let key = (vertSrc, fragSrc)+ case Map.lookup key cache of+ Just prog -> return prog+ Nothing -> do+ prog <- createProgram (Text.unpack vertSrc) (Text.unpack fragSrc)+ modifyIORef' (customPrograms state) (Map.insert key prog)+ return prog+++-- | Set a single uniform value on a shader program.+setUniform :: GL.Program -> Text -> UniformValue -> IO ()+setUniform program name val = do+ loc <- GL.get (GL.uniformLocation program (Text.unpack name))+ case val of+ UniformFloat f ->+ GL.uniform loc $= (realToFrac f :: GL.GLfloat)+ UniformVec2 x y ->+ GL.uniform loc $= GL.Vector2 (realToFrac x :: GL.GLfloat) (realToFrac y)+ UniformVec3 x y z ->+ GL.uniform loc+ $= GL.Vector3 (realToFrac x :: GL.GLfloat) (realToFrac y) (realToFrac z)+ UniformVec4 x y z w ->+ GL.uniform loc+ $= GL.Vector4+ (realToFrac x :: GL.GLfloat)+ (realToFrac y)+ (realToFrac z)+ (realToFrac w)+ UniformInt i ->+ GL.uniform loc $= (fromIntegral i :: GL.GLint)+++{-| Render a custom shader on a quad centered at the origin.+The quad spans from @(-w\/2, -h\/2)@ to @(w\/2, h\/2)@ where+@w@ and @h@ come from 'ShaderData'. The fragment shader receives+@vLocalCoord@ with these local coordinates, and @gl_Color@ carries+the current Brillo color.+-}+renderCustomShader :: ShaderState -> ShaderData -> IO ()+renderCustomShader state sd = do+ program <- getCustomProgram state (shaderVertex sd) (shaderFragment sd)++ oldProgram <- GL.get GL.currentProgram+ GL.currentProgram $= Just program++ -- Set user uniforms+ forM_ (shaderUniforms sd) $ \(name, val) ->+ setUniform program name val++ -- Draw quad centered at origin+ let hw = shaderWidth sd / 2+ hh = shaderHeight sd / 2++ GL.renderPrimitive GL.Quads $ do+ GL.texCoord $ GL.TexCoord2 (realToFrac (-hw) :: GL.GLfloat) (realToFrac (-hh))+ GL.vertex $ GL.Vertex2 (realToFrac (-hw) :: GL.GLfloat) (realToFrac (-hh))++ GL.texCoord $ GL.TexCoord2 (realToFrac hw :: GL.GLfloat) (realToFrac (-hh))+ GL.vertex $ GL.Vertex2 (realToFrac hw :: GL.GLfloat) (realToFrac (-hh))++ GL.texCoord $ GL.TexCoord2 (realToFrac hw :: GL.GLfloat) (realToFrac hh)+ GL.vertex $ GL.Vertex2 (realToFrac hw :: GL.GLfloat) (realToFrac hh)++ GL.texCoord $ GL.TexCoord2 (realToFrac (-hw) :: GL.GLfloat) (realToFrac hh)+ GL.vertex $ GL.Vertex2 (realToFrac (-hw) :: GL.GLfloat) (realToFrac hh)++ GL.currentProgram $= oldProgram
Brillo/Internals/Rendering/TrueTypeFont.hs view
@@ -90,13 +90,38 @@ pure $ max scaleX scaleY +{-| The scale the current modelview matrix applies to the drawing.++Text nested in a scaled `Picture` covers correspondingly more pixels,+so the glyphs have to be rasterized at that size to stay crisp.+-}+getModelviewScale :: IO Float+getModelviewScale = do+ modelview <-+ get (GL.matrix (Just (GL.Modelview 0))) :: IO (GL.GLmatrix GL.GLfloat)+ components <- GL.getMatrixComponents GL.ColumnMajor modelview+ -- Column major, so the first two columns are the images of the x and y axis+ pure $ case components of+ xX : xY : xZ : _xW : yX : yY : yZ : _rest ->+ let+ scaleX = sqrt ((xX * xX) + (xY * xY) + (xZ * xZ))+ scaleY = sqrt ((yX * yX) + (yY * yY) + (yZ * yZ))+ in+ realToFrac $ max scaleX scaleY+ _ -> 1++ renderTrueTypeText :: FilePath -> Int -> Text -> IO () renderTrueTypeText fontPath pixelHeight str | T.null str = pure () | otherwise = do -- Detect DPI scale factor for HiDPI/Retina displays- scaleFactor <- getContentScale- let scaledHeight = round (fromIntegral pixelHeight * scaleFactor)+ contentScale <- getContentScale+ modelviewScale <- getModelviewScale+ let+ scaleFactorRaw = contentScale * modelviewScale+ scaleFactor = if scaleFactorRaw > 0 then scaleFactorRaw else 1+ scaledHeight = round (fromIntegral pixelHeight * scaleFactor) glyphs <- getOrLoadFont fontPath scaledHeight let fallbackGlyph = Map.lookup '?' glyphs
Brillo/Rendering.hs view
@@ -5,6 +5,12 @@ Vector, Path, + -- * Shaders+ UniformValue (..),+ ShaderData (..),+ defaultShaderVertex,+ shader,+ -- * Colors Color, makeColor,@@ -20,12 +26,35 @@ BitmapFormat (..), PixelFormat (..), RowOrder (..),+ CompressedFormat (..), bitmapOfForeignPtr, bitmapDataOfForeignPtr, bitmapOfByteString, bitmapDataOfByteString, bitmapOfBMP, bitmapDataOfBMP,+ compressedBitmapOfForeignPtr,+ compressedBitmapDataOfForeignPtr,+ compressedBitmapOfByteString,+ compressedBitmapDataOfByteString,++ -- ** Common compressed texture formats+ Bitmap.compressedRGB_S3TC_DXT1,+ Bitmap.compressedRGBA_S3TC_DXT1,+ Bitmap.compressedRGBA_S3TC_DXT3,+ Bitmap.compressedRGBA_S3TC_DXT5,+ Bitmap.compressedSRGB_S3TC_DXT1,+ Bitmap.compressedSRGB_ALPHA_S3TC_DXT1,+ Bitmap.compressedSRGB_ALPHA_S3TC_DXT3,+ Bitmap.compressedSRGB_ALPHA_S3TC_DXT5,+ Bitmap.compressedRED_RGTC1,+ Bitmap.compressedSIGNED_RED_RGTC1,+ Bitmap.compressedRG_RGTC2,+ Bitmap.compressedSIGNED_RG_RGTC2,+ Bitmap.compressedRGBA_BPTC_UNORM,+ Bitmap.compressedSRGB_ALPHA_BPTC_UNORM,+ Bitmap.compressedRGB_BPTC_SIGNED_FLOAT,+ Bitmap.compressedRGB_BPTC_UNSIGNED_FLOAT, loadBMP, -- * Rendering@@ -40,6 +69,7 @@ import Brillo.Internals.Data.Color import Brillo.Internals.Data.Picture+import Brillo.Internals.Rendering.Bitmap qualified as Bitmap import Brillo.Internals.Rendering.Common import Brillo.Internals.Rendering.Picture import Brillo.Internals.Rendering.State qualified as RS
brillo-rendering.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: brillo-rendering-version: 2.0.0+version: 3.0.0 license: MIT license-file: LICENSE author: Elise Huard, Ben Lippmeier, Adrian Sieber