packages feed

stb-image-0.2: example/viewer.hs

-- | Very simple image viewer using OpenGL\/GLUT, provided 
-- as an example\/test for 'Codec.Image.STB'.

module Main where

--------------------------------------------------------------------------------

import Data.Bits

import Graphics.Rendering.OpenGL
import Graphics.UI.GLUT

import Data.Bitmap.IO
import Data.Bitmap.OpenGL

import Codec.Image.STB

import System.Environment
import System.Exit
import System.IO.Unsafe

import Control.Monad
--import Foreign

--------------------------------------------------------------------------------

main = do
  initialDisplayMode $= [ RGBAMode, DoubleBuffered ]

  args <- getArgs
  nonGLUTArgs <- initialize "viewer" args
  
  fname <- case nonGLUTArgs of
    [fn] -> return fn
    _    -> putStrLn "usage: viewer <imagefile.ext>" >> exitFailure
         
  img <- loadImage fname >>= \result -> case result of
    Left err  -> putStrLn err >> exitFailure
    Right img -> return img
    
  let (x,y) = bitmapSize img
      c = bitmapNChannels img

  putStrLn $ "\"" ++ fname ++ "\" loaded"
  putStrLn $ "resolution = " ++ show x ++ " x " ++ show y ++ ", " ++ show c ++ " bytes per pixel"  

  createWindow ("viewer - " ++ show fname)

  eimg <- extendImage img 
  tex <- makeSimpleBitmapTexture eimg

  displayCallback       $= display img tex
  reshapeCallback       $= Just reshape
  keyboardMouseCallback $= Just keyboard

  drawBuffer $= BackBuffers
  postRedisplay Nothing

  mainLoop
  
-- reshape callback
reshape _ = postRedisplay Nothing
  
-- keyboard callback
keyboard key keyState mod pos = 
  case key of 
    Char '\ESC' -> exitWith ExitSuccess
    _ -> return ()    

--------------------------------------------------------------------------------  
  
-- glVertex2d / glTexCoord2d, convenient because the OpenGL vertex / texCoord functions are too polymorphic

vt :: Double -> Double -> IO ()
vt x y = vertex (Vertex2 (realToFrac x :: GLdouble) (realToFrac y :: GLdouble))

tc :: Double -> Double -> IO ()
tc x y = texCoord (TexCoord2 (realToFrac x :: GLdouble) (realToFrac y :: GLdouble))

--------------------------------------------------------------------------------  

-- display callback  
display img tex = do
  let (xsize,ysize) = bitmapSize img
  clear [ColorBuffer] 

  size@(Size xres yres) <- get windowSize
  viewport $= ( Position 0 0 , size )
  let winaspect = fromIntegral xres  / fromIntegral yres  :: Double
      picaspect = fromIntegral xsize / fromIntegral ysize :: Double
  matrixMode $= Projection >> loadIdentity
  ortho (-1) 1 (-1) 1 (-1) 1
          
  texture Texture2D $= Enabled
  textureBinding Texture2D $= Just tex
  let (x,y) = if picaspect > winaspect then (1,winaspect/picaspect) else (picaspect/winaspect,1)
      r = fromIntegral xsize / fromIntegral (nextPowerOfTwo xsize) 
      b = fromIntegral ysize / fromIntegral (nextPowerOfTwo ysize) 
  renderPrimitive Quads $ do 
    tc 0 0 ; vt (-x) ( y)
    tc r 0 ; vt ( x) ( y)
    tc r b ; vt ( x) (-y)
    tc 0 b ; vt (-x) (-y)
         
  swapBuffers
  
--------------------------------------------------------------------------------  
  
log2 :: Int -> Int
log2 n = case n of
  0 -> -1
  _ -> 1 + log2 (shiftR n 1) 
 
nextPowerOfTwo :: Int -> Int      
nextPowerOfTwo n = 2 ^ ( 1 + log2 (n-1) )
  
-- extend the image to have power-of-two sizes, for old videocards
extendImage :: Image -> IO Image  
extendImage bm = do
  let (oldx,oldy) = bitmapSize bm
      (newx,newy) = (nextPowerOfTwo oldx, nextPowerOfTwo newx)
  copySubImage' bm (0,0) (oldx,oldy) (newx,newy) (0,0)
  
--------------------------------------------------------------------------------