module Graphics.Rendering.Rect.Border(renderBorder, Border(..), BorderStyle(..), topColour, rightColour, bottomColour, leftColour) where import Graphics.Rendering.Rect.CSS.Border import Graphics.Rendering.Rect.Types import qualified Data.ByteString.Char8 as B8 import Control.Monad.IO.Class (MonadIO(..)) import Linear (M44) borderFragmentShader :: B8.ByteString borderFragmentShader = B8.pack $ unlines [ "#version 330 core", "uniform float widths[4];", "uniform vec4 colours[4];", "uniform int styles[5];", "uniform vec2 boxSize;", "in vec2 coord;", "out vec4 fcolour;", "", "const int TOP = 0;", "const int RIGHT = 1;", "const int BOTTOM = 2;", "const int LEFT = 3;", "const int NONE = 4;", "", "const int NOBORDER = 0;", "const int SOLID = 1;", "const int DASHED = 2;", "const int DOTTED = 3;", "const int DOUBLE = 4;", "const int GROOVE = 5;", "const int RIDGE = 6;", "const int INSET = 7;", "const int OUTSET = 8;", "", "bool inTrap(float x, float y, float width, int height, int left, int right) {", " float a = y/widths[height];", " return a*widths[left] <= x && x <= width - widths[right]*a;", "}", "", "void main() {", " int side = NONE;", " if (coord.y < abs(widths[TOP]) &&", " inTrap(coord.x, coord.y, boxSize.x, TOP, LEFT, RIGHT))", " side = TOP;", " else if (coord.x < abs(widths[LEFT]) &&", " inTrap(coord.y, coord.x, boxSize.y, LEFT, TOP, BOTTOM))", " side = LEFT;", " else if (boxSize.x - coord.x < abs(widths[RIGHT]) &&", " inTrap(coord.y, boxSize.x-coord.x, boxSize.y, RIGHT,TOP,BOTTOM))", " side = RIGHT;", " else if (boxSize.y - coord.y < abs(widths[BOTTOM])) side = BOTTOM;", "", " vec2 pos = coord;", " if (side == RIGHT || side == BOTTOM) pos = boxSize - coord;", " if (side == LEFT || side == RIGHT) pos = pos.yx;", "", " int segment = int(floor(pos.x/widths[side]/2));", " float width = widths[side];", " vec2 dotCenter = vec2(segment*width*2 + width, width/2);", " int stroke3 = int(floor(3*pos.y/widths[side]));", " int stroke = int(floor(2*pos.y/widths[side]));", " bool topleft = side == TOP || side == LEFT;", " if (!topleft) stroke = abs(1 - stroke);", " if (styles[side] == SOLID) fcolour = colours[side];", " else if (styles[side] == DASHED)", " fcolour = segment % 2 == 0 ? colours[side] : vec4(0);", " else if (styles[side] == DOTTED)", " fcolour = distance(pos, dotCenter) < widths[side]/2 ?", " colours[side] : vec4(0);", " else if (styles[side] == DOUBLE && stroke3 != 1) fcolour = colours[side];", " else if (styles[side] == GROOVE)", " fcolour = colours[side] + vec4(stroke == 0 ? -0.1 : +0.1);", " else if (styles[side] == RIDGE)", " fcolour = colours[side] + vec4(stroke == 0 ? +0.1 : -0.1);", " else if (styles[side] == INSET)", " fcolour = colours[side] + vec4(topleft ? -0.2 : +0.2);", " else if (styles[side] == OUTSET)", " fcolour = colours[side] + vec4(topleft ? +0.2 : -0.2);", " else fcolour = vec4(0.0);", "}" ] renderBorder :: (MonadIO m, MonadIO n) => n (Border -> Rects -> M44 Float -> m ()) renderBorder = do inner <- renderRectWith borderFragmentShader [ "widths[0]", "widths[1]", "widths[2]", "widths[3]", "colours[0]", "colours[1]", "colours[2]", "colours[3]", "styles[0]", "styles[1]", "styles[2]", "styles[3]", "styles[4]"] return $ \self rects -> let (b, p) = (borderBox rects, paddingBox rects) in inner [] [ u $ top p - top b, u $ right b - right p, u $ bottom b - bottom p, u $ left p - left b, c $ topColour self, c $ rightColour self, c $ bottomColour self, c $ leftColour self, u $ fromEnum $ topStyle self, u $ fromEnum $ rightStyle self, u $ fromEnum $ bottomStyle self, u $ fromEnum $ leftStyle self, u (0 :: Int) ] borderBox borderBox rects