Skip to content

Commit

Permalink
Render PNG in color
Browse files Browse the repository at this point in the history
  • Loading branch information
kostmo committed Nov 17, 2023
1 parent 4783852 commit 217c35f
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 22 deletions.
1 change: 1 addition & 0 deletions src/Swarm/Game/Entity/Cosmetic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ data HiFiColor
RGBColor
-- | background
RGBColor
deriving (Show)

newtype WorldAttr = WorldAttr
{ attrSuffix :: String
Expand Down
10 changes: 9 additions & 1 deletion src/Swarm/Game/Scenario.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ module Swarm.Game.Scenario (
scenarioSeed,
scenarioAttrs,
scenarioEntities,
scenarioCosmetics,
scenarioRecipes,
scenarioKnown,
scenarioWorlds,
Expand Down Expand Up @@ -62,6 +63,7 @@ import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.List.NonEmpty qualified as NE
import Data.Map qualified as M
import Data.Maybe (catMaybes, isNothing, listToMaybe)
import Swarm.Game.Entity.Cosmetic
import Data.Sequence (Seq)
import Data.Set qualified as Set
import Data.Text (Text)
Expand Down Expand Up @@ -128,6 +130,7 @@ data Scenario = Scenario
, _scenarioSeed :: Maybe Int
, _scenarioAttrs :: [CustomAttr]
, _scenarioEntities :: EntityMap
, _scenarioCosmetics :: M.Map WorldAttr HiFiColor
, _scenarioRecipes :: [Recipe Entity]
, _scenarioKnown :: [Text]
, _scenarioWorlds :: NonEmpty WorldDescription
Expand All @@ -148,7 +151,8 @@ instance FromJSONE (EntityMap, WorldMap) Scenario where
emRaw <- liftE (v .:? "entities" .!= [])

parsedAttrs <- liftE (v .:? "attrs" .!= [])
let attrsUnion = Set.fromList (map (fst . toAttrPair) parsedAttrs) <> M.keysSet worldAttributes
let mergedCosmetics = worldAttributes <> M.fromList (map toHifiPair parsedAttrs)
attrsUnion = M.keysSet mergedCosmetics

em <- case run . runThrow $ buildEntityMap attrsUnion emRaw of
Right x -> return x
Expand Down Expand Up @@ -233,6 +237,7 @@ instance FromJSONE (EntityMap, WorldMap) Scenario where
<*> liftE (v .:? "seed")
<*> pure parsedAttrs
<*> pure em
<*> pure mergedCosmetics
<*> v ..:? "recipes" ..!= []
<*> pure known
<*> pure allWorlds
Expand Down Expand Up @@ -275,6 +280,9 @@ scenarioAttrs :: Lens' Scenario [CustomAttr]
-- | Any custom entities used for this scenario.
scenarioEntities :: Lens' Scenario EntityMap

-- | High-fidelity color map for entities
scenarioCosmetics :: Lens' Scenario (M.Map WorldAttr HiFiColor)

-- | Any custom recipes used in this scenario.
scenarioRecipes :: Lens' Scenario [Recipe Entity]

Expand Down
16 changes: 16 additions & 0 deletions src/Swarm/Game/Scenario/Style.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import Data.Aeson
import Data.Set (Set)
import Data.Text (Text)
import GHC.Generics (Generic)
import Data.Colour.SRGB (RGB (..), sRGB24read, toSRGB24)
import Data.Text qualified as T
import Swarm.Game.Entity.Cosmetic

data StyleFlag
= Standout
Expand Down Expand Up @@ -51,3 +54,16 @@ instance ToJSON CustomAttr where
defaultOptions
{ omitNothingFields = True
}

-- | TODO Fail if neither fg nor bg are provided
toHifiPair :: CustomAttr -> (WorldAttr, HiFiColor)
toHifiPair (CustomAttr n maybeFg maybeBg _) =
(WorldAttr n, c)
where
c = case (maybeFg, maybeBg) of
(Just f, Just b) -> FgAndBg (conv f) (conv b)
(Just f, Nothing) -> FgOnly (conv f)
(Nothing, Just b) -> BgOnly (conv b)
(Nothing, Nothing) -> BgOnly $ RGB 0 0 0

conv (HexColor x) = toSRGB24 . sRGB24read $ T.unpack x
42 changes: 22 additions & 20 deletions src/Swarm/Game/World/Render.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@
-- TUI-independent world rendering.
module Swarm.Game.World.Render where

import Brick (AttrMap, applyAttrMappings, attrMapLookup)
import Data.Text qualified as T
import Codec.Picture
import Control.Applicative ((<|>))
import Control.Effect.Lift (sendIO)
import Control.Lens (to, view, (^.))
import Data.Bifunctor (first)
import Data.Colour.SRGB (RGB (..))
import Swarm.Game.Entity.Cosmetic
import Control.Lens (view, (^.))
import Data.List.NonEmpty qualified as NE
import Data.Map qualified as M
import Data.Maybe (fromMaybe)
import Data.Vector qualified as V
import Swarm.Doc.Gen (loadStandaloneScenario)
import Swarm.Game.Display (defaultChar, displayAttr)
import Swarm.Game.Display (defaultChar, displayAttr, Attribute (AWorld))
import Swarm.Game.ResourceLoading (initNameGenerator, readAppData)
import Swarm.Game.Scenario (Scenario, area, scenarioAttrs, scenarioWorlds, ul, worldName)
import Swarm.Game.Scenario (Scenario, area, scenarioWorlds, ul, worldName, scenarioCosmetics)
import Swarm.Game.Scenario.Status (seedLaunchParams)
import Swarm.Game.Scenario.Topography.Area (AreaDimensions (..), getAreaDimensions, isEmpty, upperLeftToBottomRight)
import Swarm.Game.Scenario.Topography.Cell
Expand All @@ -25,8 +27,6 @@ import Swarm.Game.State
import Swarm.Game.Universe
import Swarm.Game.World qualified as W
import Swarm.TUI.Editor.Util (getContentAt, getMapRectangle)
import Swarm.TUI.View.Attribute.Attr (getWorldAttrName, swarmAttrMap, toAttrName)
import Swarm.TUI.View.Attribute.CustomStyling (toAttrPair)
import Swarm.Util (surfaceEmpty)
import Swarm.Util.Effect (simpleErrorHandle)
import Swarm.Util.Erasable (erasableToMaybe)
Expand All @@ -49,20 +49,23 @@ getDisplayChar = maybe ' ' facadeChar . erasableToMaybe . cellEntity
where
facadeChar (EntityFacade _ d) = view defaultChar d

getDisplayColor :: AttrMap -> PCell EntityFacade -> PixelRGBA8
getDisplayColor aMap (Cell terr cellEnt _) =
getDisplayColor :: M.Map WorldAttr HiFiColor -> PCell EntityFacade -> PixelRGBA8
getDisplayColor aMap (Cell _terr cellEnt _) =
maybe transparent facadeColor $ erasableToMaybe cellEnt
where
transparent = PixelRGBA8 0 0 0 0
facadeColor (EntityFacade _ d) = PixelRGBA8 255 0 255 255
where
-- facadeColor (EntityFacade _ d) = case attrForeColor attr of
-- SetTo c -> case c of
-- RGBColor r g b -> PixelRGBA8 r g b 255
-- _ -> transparent
-- _ -> transparent
facadeColor (EntityFacade _ d) = maybe transparent mkPixelColor $ case d ^. displayAttr of
AWorld n -> M.lookup (WorldAttr $ T.unpack n) aMap
_ -> Nothing

attr = attrMapLookup (d ^. displayAttr . to toAttrName) aMap
mkPixelColor :: HiFiColor -> PixelRGBA8
mkPixelColor h = PixelRGBA8 r g b 255
where
RGB r g b = case h of
FgOnly c -> c
BgOnly c -> c
-- TODO: if displayChar is whitespace, use bg color. Otherwise use fg color.
FgAndBg _ c -> c

getDisplayGrid :: Scenario -> GameState -> Maybe AreaDimensions -> [[PCell EntityFacade]]
getDisplayGrid myScenario gs maybeSize =
Expand All @@ -83,10 +86,9 @@ getDisplayGrid myScenario gs maybeSize =
mkCosmic = Cosmic $ worldName firstScenarioWorld
boundingBox = (W.locToCoords upperLeftLocation, W.locToCoords lowerRightLocation)

getRenderableGrid :: RenderOpts -> FilePath -> IO ([[PCell EntityFacade]], AttrMap)
getRenderableGrid :: RenderOpts -> FilePath -> IO ([[PCell EntityFacade]], M.Map WorldAttr HiFiColor)
getRenderableGrid (RenderOpts maybeSeed _ _ maybeSize) fp = simpleErrorHandle $ do
(myScenario, (worldDefs, entities, recipes)) <- loadStandaloneScenario fp
let aMap = applyAttrMappings (map (first getWorldAttrName . toAttrPair) $ myScenario ^. scenarioAttrs) swarmAttrMap
appDataMap <- readAppData
nameGen <- initNameGenerator appDataMap
let gsc = GameStateConfig nameGen entities recipes worldDefs
Expand All @@ -96,7 +98,7 @@ getRenderableGrid (RenderOpts maybeSeed _ _ maybeSize) fp = simpleErrorHandle $
myScenario
(seedLaunchParams maybeSeed)
gsc
return (getDisplayGrid myScenario gs maybeSize, aMap)
return (getDisplayGrid myScenario gs maybeSize, myScenario ^. scenarioCosmetics)

doRenderCmd :: RenderOpts -> FilePath -> IO ()
doRenderCmd opts@(RenderOpts _ asPng _ _) mapPath =
Expand Down
2 changes: 1 addition & 1 deletion src/Swarm/TUI/View/Attribute/CustomStyling.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Data.Colour.SRGB (sRGB24read)
import Data.Set (toList)
import Data.Text qualified as T
import Graphics.Vty.Attributes
import Swarm.Game.Entity.Cosmetic (WorldAttr (..))
import Swarm.Game.Entity.Cosmetic (WorldAttr (..), HiFiColor)
import Swarm.Game.Scenario.Style
import Swarm.TUI.View.Attribute.Util

Expand Down

0 comments on commit 217c35f

Please sign in to comment.