packages feed

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 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