{-# LANGUAGE DeriveDataTypeable #-}
{-# OPTIONS_HADDOCK hide #-}
module Graphics.Gloss.Internals.Rendering.Picture
(renderPicture)
where
import Graphics.Gloss.Internals.Rendering.State
import Graphics.Gloss.Internals.Rendering.Common
import Graphics.Gloss.Internals.Rendering.Circle
import Graphics.Gloss.Internals.Rendering.Bitmap
import Graphics.Gloss.Internals.Data.Picture
import Graphics.Gloss.Internals.Data.Color
import System.Mem.StableName
import Foreign.ForeignPtr
import Data.IORef
import Data.List
import Control.Monad
import Graphics.Rendering.OpenGL (($=), get)
import qualified Graphics.Rendering.OpenGL.GL as GL
import qualified Graphics.Rendering.OpenGL.GLU.Errors as GLU
import qualified Graphics.UI.GLUT as GLUT
renderPicture
:: State
-> Float
-> Picture
-> IO ()
renderPicture state circScale picture
= do
setLineSmooth (stateLineSmooth state)
setBlendAlpha (stateBlendAlpha state)
checkErrors "before drawPicture."
drawPicture state circScale picture
checkErrors "after drawPicture."
drawPicture :: State -> Float -> Picture -> IO ()
drawPicture state circScale picture
= {-# SCC "drawComponent" #-}
case picture of
Blank
-> return ()
Line path
-> GL.renderPrimitive GL.LineStrip
$ vertexPFs path
Polygon path
| stateWireframe state
-> GL.renderPrimitive GL.LineLoop
$ vertexPFs path
| otherwise
-> GL.renderPrimitive GL.Polygon
$ vertexPFs path
Circle radius
-> renderCircle 0 0 circScale radius 0
ThickCircle radius thickness
-> renderCircle 0 0 circScale radius thickness
Arc a1 a2 radius
-> renderArc 0 0 circScale radius a1 a2 0
ThickArc a1 a2 radius thickness
-> renderArc 0 0 circScale radius a1 a2 thickness
Text str
-> do
GL.blend $= GL.Disabled
GL.preservingMatrix $ GLUT.renderString GLUT.Roman str
GL.blend $= GL.Enabled
Color col p
| stateColor state
-> do oldColor <- get GL.currentColor
let RGBA r g b a = col
GL.currentColor $= GL.Color4 (gf r) (gf g) (gf b) (gf a)
drawPicture state circScale p
GL.currentColor $= oldColor
| otherwise
-> drawPicture state circScale p
Translate posX posY (Circle radius)
-> renderCircle posX posY circScale radius 0
Translate posX posY (ThickCircle radius thickness)
-> renderCircle posX posY circScale radius thickness
Translate posX posY (Arc a1 a2 radius)
-> renderArc posX posY circScale radius a1 a2 0
Translate posX posY (ThickArc a1 a2 radius thickness)
-> renderArc posX posY circScale radius a1 a2 thickness
Translate tx ty (Rotate deg p)
-> GL.preservingMatrix
$ do GL.translate (GL.Vector3 (gf tx) (gf ty) 0)
GL.rotate (gf deg) (GL.Vector3 0 0 (-1))
drawPicture state circScale p
Translate tx ty p
-> GL.preservingMatrix
$ do GL.translate (GL.Vector3 (gf tx) (gf ty) 0)
drawPicture state circScale p
Rotate _ (Circle radius)
-> renderCircle 0 0 circScale radius 0
Rotate _ (ThickCircle radius thickness)
-> renderCircle 0 0 circScale radius thickness
Rotate deg (Arc a1 a2 radius)
-> renderArc 0 0 circScale radius (a1-deg) (a2-deg) 0
Rotate deg (ThickArc a1 a2 radius thickness)
-> renderArc 0 0 circScale radius (a1-deg) (a2-deg) thickness
Rotate deg p
-> GL.preservingMatrix
$ do GL.rotate (gf deg) (GL.Vector3 0 0 (-1))
drawPicture state circScale p
Scale sx sy p
-> GL.preservingMatrix
$ do GL.scale (gf sx) (gf sy) 1
let mscale = max sx sy
drawPicture state (circScale * mscale) p
Bitmap imgData ->
let (width, height) = bitmapSize imgData
in
drawPicture state circScale $
BitmapSection (rectAtOrigin width height) imgData
BitmapSection
Rectangle
{ rectPos = imgSectionPos
, rectSize = imgSectionSize }
imgData@BitmapData
{ bitmapSize = (width, height)
, bitmapCacheMe = cacheMe }
->
do
let rowInfo =
let defTexCoords =
map (\(x,y) -> (x / fromIntegral width, y / fromIntegral height)) $
[ vecMap (+eps) (+eps) $ toFloatVec imgSectionPos
, vecMap (subtract eps) (+eps) $ toFloatVec $
( fst imgSectionPos + fst imgSectionSize
, snd imgSectionPos )
, vecMap (subtract eps) (subtract eps) $ toFloatVec $
( fst imgSectionPos + fst imgSectionSize
, snd imgSectionPos + snd imgSectionSize )
, vecMap (+eps) (subtract eps) $ toFloatVec $
( fst imgSectionPos
, snd imgSectionPos + snd imgSectionSize )
]
:: [(Float,Float)]
toFloatVec = vecMap fromIntegral fromIntegral
vecMap :: (a -> c) -> (b -> d) -> (a,b) -> (c,d)
vecMap f g (x,y) = (f x, g y)
eps = 0.001 :: Float
in
case rowOrder (bitmapFormat imgData) of
BottomToTop -> defTexCoords
TopToBottom -> reverse defTexCoords
tex <- loadTexture (stateTextures state) imgData cacheMe
GL.textureWrapMode GL.Texture2D GL.S $= (GL.Repeated, GL.Repeat)
GL.textureWrapMode GL.Texture2D GL.T $= (GL.Repeated, GL.Repeat)
GL.textureFilter GL.Texture2D $= ((GL.Nearest, Nothing), GL.Nearest)
GL.texture GL.Texture2D $= GL.Enabled
GL.textureFunction $= GL.Combine
GL.textureBinding GL.Texture2D $= Just (texObject tex)
oldColor <- get GL.currentColor
GL.currentColor $= GL.Color4 1.0 1.0 1.0 1.0
GL.renderPrimitive GL.Polygon $
forM_ (bitmapPath (fromIntegral $ fst imgSectionSize) (fromIntegral $ snd imgSectionSize) `zip` rowInfo) $
\((polygonCoordX, polygonCoordY), (textureCoordX,textureCoordY)) ->
do
GL.texCoord $ GL.TexCoord2 (gf textureCoordX) (gf textureCoordY)
GL.vertex $ GL.Vertex2 (gf polygonCoordX) (gf polygonCoordY)
GL.currentColor $= oldColor
GL.texture GL.Texture2D $= GL.Disabled
freeTexture tex
Pictures ps
-> mapM_ (drawPicture state circScale) ps
checkErrors :: String -> IO ()
checkErrors place
= do errors <- get $ GLU.errors
when (not $ null errors)
$ mapM_ (handleError place) errors
handleError :: String -> GLU.Error -> IO ()
handleError place err
= case err of
GLU.Error GLU.StackOverflow _
-> error $ unlines
[ "Gloss / OpenGL Stack Overflow " ++ show place
, " This program uses the Gloss vector graphics library, which tried to"
, " draw a picture using more nested transforms (Translate/Rotate/Scale)"
, " than your OpenGL implementation supports. The OpenGL spec requires"
, " all implementations to have a transform stack depth of at least 32,"
, " and Gloss tries not to push the stack when it doesn't have to, but"
, " that still wasn't enough."
, ""
, " You should complain to your harware vendor that they don't provide"
, " a better way to handle this situation at the OpenGL API level."
, ""
, " To make this program work you'll need to reduce the number of nested"
, " transforms used when defining the Picture given to Gloss. Sorry." ]
GLU.Error GLU.InvalidOperation _
-> return ()
_
-> error $ unlines
[ "Gloss / OpenGL Internal Error " ++ show place
, " Please report this on haskell-gloss@googlegroups.com."
, show err ]
loadTexture
:: IORef [Texture]
-> BitmapData
-> Bool
-> IO Texture
loadTexture refTextures imgData@BitmapData{ bitmapSize=(width,height) } cacheMe
= do textures <- readIORef refTextures
name <- makeStableName imgData
let mTexCached
= find (\tex -> texName tex == name
&& texWidth tex == width
&& texHeight tex == height)
textures
case mTexCached of
Just tex
-> return tex
Nothing
-> do tex <- installTexture imgData
when cacheMe
$ writeIORef refTextures (tex : textures)
return tex
installTexture
:: BitmapData
-> IO Texture
installTexture bitmapData@(BitmapData _ fmt (width,height) cacheMe fptr)
= do
let glFormat
= case pixelFormat fmt of
PxABGR -> GL.ABGR
PxRGBA -> GL.RGBA
[tex] <- GL.genObjectNames 1
GL.textureBinding GL.Texture2D $= Just tex
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)
name <- makeStableName bitmapData
return Texture
{ texName = name
, texWidth = width
, texHeight = height
, texData = fptr
, texObject = tex
, texCacheMe = cacheMe }
freeTexture :: Texture -> IO ()
freeTexture tex
| texCacheMe tex = return ()
| otherwise = GL.deleteObjectNames [texObject tex]
setBlendAlpha :: Bool -> IO ()
setBlendAlpha state
| state
= do GL.blend $= GL.Enabled
GL.blendFunc $= (GL.SrcAlpha, GL.OneMinusSrcAlpha)
| otherwise
= do GL.blend $= GL.Disabled
GL.blendFunc $= (GL.One, GL.Zero)
setLineSmooth :: Bool -> IO ()
setLineSmooth state
| state = GL.lineSmooth $= GL.Enabled
| otherwise = GL.lineSmooth $= GL.Disabled
vertexPFs :: [(Float, Float)] -> IO ()
vertexPFs [] = return ()
vertexPFs ((x, y) : rest)
= do GL.vertex $ GL.Vertex2 (gf x) (gf y)
vertexPFs rest
{-# INLINE vertexPFs #-}