set display mode 1024,768,32
 
#CONSTANT NULL = 0
#CONSTANT TRUE = 1
#CONSTANT FALSE = 0
#CONSTANT SLIDER_HORIZONTAL = 1
#CONSTANT SLIDER_VERTICAL = 2
#CONSTANT GUI_LAYERS_OFFSET = 400
#CONSTANT LAYER_HEIGHT = 25
REM should be a constant, but doing so gives a different result(?)
TEXT_OFFSET = (LAYER_HEIGHT/2 - text height("A")/2)
#CONSTANT ACTION_DRAW = 1
#CONSTANT ACTION_FILL = 2
#CONSTANT ACTION_LINE = 3
#CONSTANT ACTION_CIRCLE = 4
#CONSTANT ACTION_GRADIENT = 5
#CONSTANT ACTION_ADDLAYER = 6
#CONSTANT ACTION_DELETELAYER = 7
#CONSTANT ACTION_SPRAY = 8
 
 
GLOBAL _menuMouseFlag = 0
GLOBAL _menuFlag = 0
GLOBAL LAYER_INDEX = 0
GLOBAL CURRENT_LAYER = -1
GLOBAL CURRENT_ACTION = 1
 
 
rem
type Vector2D
   x as integer
   y as integer
endtype
 
 
rem
type Menu
   name as string
   active as boolean
   width as integer
   count as integer
endtype
 
rem
type MenuItem
   parentID as integer
   item as string
   enabled as boolean
   check as integer
endtype
 
 
rem
type toolButton
   x as integer
   y as integer
   size as integer
   active as boolean
   action as integer
   group as integer
endtype
 
 
rem isVisible = is the layer visible in the workspace
rem parentLink = current drawing layer if this layer is told to link to it (-1 if no link)
rem alpha = alpha transparency amount
rem name = name of this layer
type LAYER_UI
   isVisible as boolean
   parentLink as integer
   alpha as byte
   name as string
   order as byte
   memblock as byte
endtype
 
rem attributes for common values among all sliders of this type
type sliderComponentUI
   trackWidth as float
   trackHeight as float
   trackColor as dword
   thumbWidth as float
   thumbHeight as float
   thumbColor as dword
   orientation as byte
endtype
 
 
rem attributes for unique values for each slider
type sliderComponent
   x as integer
   y as integer
   min as float
   max as float
   value as float
   isDragging as boolean
   mOffsetX as float
   mOffsetY as float
   thumbX as float
   thumbY as float
   valueX as float
   valueY as float
   displayValue as boolean
   ui as sliderComponentUI
endtype
 
rem array to hold all sliders
dim sliders(5) as SliderComponent
 
sliderUI as sliderComponentUI
   sliderUI.trackWidth = 2
   sliderUI.trackHeight = 256
   sliderUI.trackColor = 0
   sliderUI.thumbWidth = 10
   sliderUI.thumbHeight = 4
   sliderUI.thumbColor = rgb(128,128,128)
   sliderUI.orientation = SLIDER_VERTICAL
 
brushSliderUI as sliderComponentUI
   brushSliderUI.trackWidth = 144
   brushSliderUI.trackHeight = 2
   brushSliderUI.trackColor = 0
   brushSliderUI.thumbWidth = 4
   brushSliderUI.thumbHeight = 10
   brushSliderUI.thumbColor = rgb(128,128,128)
   brushSliderUI.orientation = SLIDER_HORIZONTAL
 
slider as sliderComponent
   slider.x = 886
   slider.y = 14
   slider.min = 0
   slider.max = 255
   slider.value = 255
   slider.thumbX = 956+56
   slider.thumbY = slider.y
   slider.ui = sliderUI
   slider.displayValue = TRUE
   slider.valueX = 886
   slider.valueY = 1
 
rem color sliders
sliders(1) = slider : slider.x = 922 : slider.valueX = 922
sliders(2) = slider : slider.x = 958 : slider.valueX = 958
sliders(3) = slider : slider.x = 994 : slider.valueX = 994
sliders(4) = slider
 
rem brush size slider
slider.x = 876
slider.y = 326
slider.min = 1
slider.max = 10
slider.value = 1
slider.thumbX = slider.x
slider.thumbY = slider.y
slider.ui = brushSliderUI
slider.valueX = 978
slider.valueY = 308
 
sliders(5) = slider
 
 
rem array for drawing tools
dim tools(8) as toolButton
tempTool as toolButton
tempTool.x = 876
tempTool.y = 340
tempTool.size = 20
tempTool.active = FALSE
tempTool.action = ACTION_DRAW
tempTool.group = 1
 
tools(1) = tempTool : tempTool.x = tempTool.x+tempTool.size+4 : tempTool.action = ACTION_LINE
tools(2) = tempTool : tempTool.x = tempTool.x+tempTool.size+4 : tempTool.action = ACTION_CIRCLE
tools(3) = tempTool : tempTool.x = tempTool.x+tempTool.size+4 : tempTool.action = ACTION_FILL
tools(4) = tempTool : tempTool.x = tempTool.x+tempTool.size+4 : tempTool.action = ACTION_GRADIENT
tools(5) = tempTool : tempTool.x = tempTool.x+tempTool.size+4 : tempTool.action = ACTION_SPRAY
tools(6) = tempTool
tools(1).active = TRUE
 
 
tools(7).x = tools(5).x
tools(7).y = 746
tools(7).size = 20
tools(7).group = -1
tools(7).action = ACTION_ADDLAYER
tools(7).active = FALSE
 
tools(8).x = tools(6).x
tools(8).y = 746
tools(8).size = 20
tools(8).group = -1
tools(8).action = ACTION_DELETELAYER
tools(8).active = FALSE
 
 
 
rem for the menu system
dim menus() as Menu
dim menuItems() as MenuItem
 
MENU_FILE = addMenu("File")
MENU_EDIT = addMenu("Edit")
MENU_FILTER = addMenu("Filter")
 
addMenuItem(MENU_FILE,"New",1,-1)
addMenuItem(MENU_FILE,"Open",1,-1)
addMenuItem(MENU_FILE,"Save",1,-1)
addMenuItem(MENU_FILE,"Exit",1,-1)
 
addMenuItem(MENU_EDIT,"Resize",0,-1)
 
addMenuItem(MENU_FILTER,"Noise",1,-1)
addMenuItem(MENU_FILTER,"Blur",0,-1)
addMenuItem(MENU_FILTER,"",1,-1)
addMenuItem(MENU_FILTER,"",1,-1)
 
 
rem array of drawing layers
dim layerObjects(0) as LAYER_UI
 
rem create a new document and add 2 more default(blank) layers
new()
addLayer(TRUE)
addLayer(TRUE)
 
 
 
sync on
backdrop on
color backdrop 0x808080
 
 
color as dword
 
 
 
REPEAT
 
   if mouseclick() = 0 then drawFlag = 0
 
   _mc = mouseclick()
   if _mc = 0 then  _menuMouseFlag = 0
 
   v$ = _handleMenus$(_mc)
   `if v$ <> "" then value$ = v$
   if v$ <> "" then fireMenuAction(v$) : drawFlag = 0
 
   if _menuFlag = 0
      if mousex() > 874
         gosub _handleGUI
      else
         if mouseclick() = 1
            if drawFlag = 0
               start = 0
               oldmx = mousex() - sprite x(CURRENT_LAYER)
               oldmy = mousey() - sprite y(CURRENT_LAYER)
            endif
            drawFlag = 1
         endif
      endif
   endif
 
 
   if drawFlag = 1
      select CURRENT_ACTION
         case ACTION_DRAW : gosub _drawing : oldmx=mousex()-sprite x(CURRENT_LAYER) : oldmy=mousey()-sprite y(CURRENT_LAYER) : endcase
         case ACTION_FILL : gosub _flood : endcase
         case ACTION_LINE : gosub _line : endcase
         `case ACTION_CIRCLE : gosub _circle : endcase
        ` case ACTION_GRADIENT : gosub _gradient_fill : endcase
         case ACTION_SPRAY : gosub _spray : endcase
      endselect
   endif
 
 
   gosub _drawGUI
 
   _handleSliders(layerFlag)
 
   rem draw layers
   for i = 1 to array count(layerObjects())
      img = layerObjects(i).memblock
      if _menuFlag = 1
         `hide sprite img
      else
         if layerObjects(i).isVisible = TRUE
            show sprite img
            `sprite img,0,0,img
            set sprite alpha img, layerObjects(i).alpha
            `set sprite priority img, array count(layerObjects())-layerObjects(i).order
            set sprite priority img, layerObjects(i).order
         else
            hide sprite img
         endif
      endif
   next i
 
 
   sync
UNTIL spacekey()
end
 
 
REM draw a line
_line:
   if start = 0
      start = 1
      lx1 = mousex()
      ly1 = mousey()
   endif
 
   if start = 1
      lx2 = mousex()
      ly2 = mousey()
   endif
 
   ink 0,0
   line lx1,ly1,lx2,ly2
 
RETURN
 
 
REM flood fill (paint bucket)
_flood:
   color = rgb(sliders(1).value, sliders(2).value, sliders(3).value)
   floodFill(layerObjects(CURRENT_LAYER).memblock, mousex()-sprite x(CURRENT_LAYER), mousey()-sprite y(CURRENT_LAYER), color,sliders(4).value)
   make image from memblock layerObjects(CURRENT_LAYER).memblock, layerObjects(CURRENT_LAYER).memblock
RETURN
 
 
REM
_spray:
   if mousex() >= sprite x(CURRENT_LAYER) and mousey() >= sprite y(CURRENT_LAYER) and mousex() <= sprite x(CURRENT_LAYER)+sprite width(CURRENT_LAYER) and mousey() <= sprite y(CURRENT_LAYER)+sprite height(CURRENT_LAYER)
      color = rgb(sliders(1).value, sliders(2).value, sliders(3).value)
      mx = mousex() - sprite x(CURRENT_LAYER)
      my = mousey() - sprite y(CURRENT_LAYER)
 
      spray(layerObjects(CURRENT_LAYER).memblock, mx, my, color, sliders(4).value, sliders(5).value*10)
 
      make image from memblock layerObjects(CURRENT_LAYER).memblock, layerObjects(CURRENT_LAYER).memblock
   endif
 
RETURN
 
 
 
REM handles drawing functions (with pencil tool)
_drawing:
   if layerObjects(CURRENT_LAYER).isVisible = TRUE
      `if mousex() > 0 and mousey() > 0 and mousex() <= 874 and mousey() <= 1024
      if mousex() >= sprite x(CURRENT_LAYER) and mousey() >= sprite y(CURRENT_LAYER) and mousex() <= sprite x(CURRENT_LAYER)+sprite width(CURRENT_LAYER) and mousey() <= sprite y(CURRENT_LAYER)+sprite height(CURRENT_LAYER)
         color = rgb(sliders(1).value, sliders(2).value, sliders(3).value)
         mx = mousex() - sprite x(CURRENT_LAYER)
         my = mousey() - sprite y(CURRENT_LAYER)
         bline(layerObjects(CURRENT_LAYER).memblock, oldmx,oldmy,mx,my, color, sliders(4).value,sliders(5).value-1)
         `oldmx = mx
         `oldmy = my
         make image from memblock layerObjects(CURRENT_LAYER).memblock, layerObjects(CURRENT_LAYER).memblock
      endif
   endif
RETURN
 
 
REM handles the GUI functions
_handleGUI:
 
   REM handle reordering of the layers
 
   count = array count(layerObjects())
 
   for i = 1 to array count(layerObjects())
      t = count - layerObjects(i).order
      if mousex() > 898 or layerFlag > 0
         if mouseclick() = 1 and layerFlag = 0
            if mousex() > 898 and mousex() < 1024 and mousey() > (GUI_LAYERS_OFFSET+(t*LAYER_HEIGHT)+2) and mousey() < (GUI_LAYERS_OFFSET+(t*LAYER_HEIGHT)+LAYER_HEIGHT)
               if t < (count-1) then layerFlag = i
               CURRENT_LAYER = i
               loffsety = mousey() - (GUI_LAYERS_OFFSET+t*LAYER_HEIGHT+2)
            endif
         endif
 
         if t < (count-1)
            rem moving a layer with mouse (reording)
            if layerFlag > 0
               if mouseclick() = 1
 
                  rem move layer above
                  if mousey() < (GUI_LAYERS_OFFSET+(t*LAYER_HEIGHT)+LAYER_HEIGHT)
                     if layerObjects(layerFlag).order < layerObjects(i).order
                        order = layerObjects(i).order
                        layerObjects(i).order = layerObjects(layerFlag).order
                        layerObjects(layerFlag).order = order
                     endif
                  endif
 
                  rem move layer below
                  if mousey() > GUI_LAYERS_OFFSET+(t*LAYER_HEIGHT)+2
                     if layerObjects(layerFlag).order > layerObjects(i).order
                        order = layerObjects(i).order
                        layerObjects(i).order = layerObjects(layerFlag).order
                        layerObjects(layerFlag).order = order
                     endif
                  endif
 
               endif
            endif
         endif
      endif
 
      if mousex() > 876 and mousex() < 896 and  mousey() > (GUI_LAYERS_OFFSET+(t*LAYER_HEIGHT)+3) and mousey() < (GUI_LAYERS_OFFSET+(t*LAYER_HEIGHT)+LAYER_HEIGHT)-1
         if mouseclick() = 1 and visibleFlag = 0
            visibleFlag = 1
            if layerObjects(i).isVisible = TRUE : layerObjects(i).isVisible = FALSE : else : layerObjects(i).isVisible = TRUE : endif
         endif
      endif
   next i
 
   rem tools
   if mouseclick() = 1 and toolFlag = 0 and layerFlag = 0
      toolFlag = 1
      for i = 1 to array count(tools())
         if mousex() > tools(i).x and mousex() < tools(i).x+tools(i).size and mousey() > tools(i).y and mousey() < tools(i).y+tools(i).size
            rem reset all tool states
            if tools(i).group > 0
               for t = 1 to array count(tools())
                  if tools(t).group = tools(i).group then tools(t).active = FALSE
               next t
               tools(i).active = TRUE
               CURRENT_ACTION = tools(i).action
            else
               fireButtonAction(tools(i).action)
            endif
            break
         endif
      next i
   endif
 
   if mouseclick() <> 1 then layerFlag = 0 : visibleFlag = 0 : toolFlag = 0
 
 
RETURN
 
 
 
 
REM handles component rendering
_drawGUI:
   REM GUI
   ink rgb(192,192,192),0
   box 872,0,1024,768
 
   REM draw layers
   count = array count(layerObjects())
   for t = 1 to count
      i = count - layerObjects(t).order
      rem if this layer is being moved by the user
      if t = layerFlag
        loy = mousey() - loffsety
      else
         loy = GUI_LAYERS_OFFSET+i*LAYER_HEIGHT
      endif
 
      if t = CURRENT_LAYER
         ink rgb(192,192,255),0
      else
         ink rgb(255,255,255),0
      endif
      box 874,loy+2,1024,loy+LAYER_HEIGHT
      ink 0,0
      text 898,loy+2 + TEXT_OFFSET, layerObjects(t).name + " - " + str$(i)
 
      rem checkbox for showing/hiding layer
      ink rgb(128,128,128),0
      box 876,loy+4,896,loy+LAYER_HEIGHT-2
 
      t1 = loy+4
      t2 = loy+LAYER_HEIGHT-2
      if layerObjects(t).isVisible = TRUE
         ink rgb(0,255,0),0
         line 877,t1+LAYER_HEIGHT/2,885,t2-1
         line 885,t2-1, 895,t1+1
      else
         ink rgb(255,0,0),0
         line 877,t1+1,895,t2-1
         line 877,t2-1,895,t1+1
      endif
 
   next t
 
 
 
   rem Tools
   for i = 1 to array count(tools())
      drawButton(tools(i).x, tools(i).y, tools(i).size, tools(i).active)
      drawIcon(tools(i).action, tools(i).x, tools(i).y)
   next i
 
 
 
   ink 0,0
   rem labels for color sliders
   center text sliders(1).x, 270, "R"
   center text sliders(2).x, 270, "G"
   center text sliders(3).x, 270, "B"
   center text sliders(4).x, 270, "A"
 
   rem divider line under color sliders
   box 872,304,1024,305
   ink rgb(255,255,255),0
   box 872,305,1024,306
 
   ink 0,0
   text 876,308,"Brush Size - "
 
   rem color sample
   ink rgb(sliders(1).value, sliders(2).value, sliders(3).value), 0
   box 874,286,1022,301
 
 
RETURN
 
 
rem draws a square button
function drawButton(x as integer, y as integer, size as integer, active as boolean)
 
   a as dword
   b as dword
 
   if active = FALSE
      a = 0xFFFFFF
      b = 0
   else
      a = 0
      b = 0xFFFFFF
   endif
   ink a,0
   box x, y,x+size, y+1
   box x, y,x+1, y+size
   ink b,0
   box x, y+size-1,x+size, y+size
   box x+size-1, y,x+size,y+size
 
endfunction
 
 
 
REM Create a new blank layer
REM if "blank" is false, it assumes the memblock used
REM for drawing operations will be created in an load image
REM routine.
REM Should you not plan on creating a memblock from a loaded
REM image after calling this, then "blank" must be TRUE
function addLayer(blank as boolean)
   array insert at bottom layerObjects()
   inc LAYER_INDEX, 1
   l as LAYER_UI
   l.isVisible = TRUE
   l.parentLink = -1
   l.alpha = 255
   l.name = "Layer "+str$(LAYER_INDEX)
   i = array count(layerObjects())
   l.order = i
   l.memblock = i
   layerObjects(i) = l
   CURRENT_LAYER = i
   if blank = TRUE then makeLayerMemblock(i)
endfunction i
 
 
 
REM create a memblock for drawing routines
function makeLayerMemblock(index as integer)
   `width = 872
   `height = 752
   width = 800
   height = 600
   size = width*height*4 + 12
   make memblock index, size
   write memblock dword index, 0, width
   write memblock dword index, 4, height
   write memblock dword index, 8, 32
 
   make image from memblock index, index
 
   `sprite index,0,16,index
   sprite index, 38,100,index
endfunction
 
 
 
REM Loads an image into a new layer
function loadImage(imgPath as string)
   if path exist(imgPath)
      index = addLayer(0)
      load image imgPath,index,1
      make memblock from image layerObjects(index).memblock, index
      sprite index, 38,100,index
   endif
endfunction
 
 
 
REM
function spray(index as integer, x as integer, y as integer, color as dword, alpha as integer, size as integer)
   width = memblock dword(index, 0)
   height = memblock dword(index, 4)
 
   s = size/2
   for px = x-size to x+size
      for py = y-size to y+size
         if px > 0 and px <= width and py > 0 and py <= height
            if ((px - x)^2 + (py-y)^2) <= size
               pos = ((py-1)*width + px - 1)*4 + 12
               if rnd(1) = 1
                  write memblock dword index, pos, color
                  write memblock byte index, pos+3, alpha
               endif
            endif
         endif
      next py
   next px
endfunction
 
 
 
REM draws a square for painting based on "size"
function dotMemblockBrush(index as integer, x as integer, y as integer, color as dword, alpha as integer, size as integer)
   width = memblock dword(index, 0)
   height = memblock dword(index, 4)
 
   s = size/2
   for px = x-size to x+size
      for py = y-size to y+size
         if px > 0 and px <= width and py > 0 and py <= height
            pos = ((py-1)*width + px - 1)*4 + 12
            write memblock dword index, pos, color
            write memblock byte index, pos+3, alpha
         endif
      next py
   next px
endfunction
 
 
REM a single dot
function dotMemblock(index as integer, x as integer, y as integer, color as dword, alpha as integer)
   width = memblock dword(index, 0)
   height = memblock dword(index, 4)
   if x > 0 and y > 0 and x <= width and y <= height
      pos = ((y-1)*width + x - 1)*4 + 12
      write memblock dword index, pos, color
      write memblock byte index, pos+3, alpha
   endif
endfunction
 
 
 
REM determine how to handle each slider.
function _handleSliders(lf)
   for t = 1 to array count(sliders())
      if sliders(t).ui.orientation = SLIDER_HORIZONTAL then _handleSliderHorizontal(t, lf)
      if sliders(t).ui.orientation = SLIDER_VERTICAL then _handleSliderVERTICAL(t, lf)
   next t
endfunction
 
 
REM Horizontal Slider
REM handles slider UI and renders the components
function _handleSliderHorizontal(i as integer, lf as integer)
   local ui as sliderComponentUI
   ui = sliders(i).ui
   rem draw track
   ink ui.trackColor,0
   y# = sliders(i).y - ui.trackHeight/2
   box sliders(i).x, y#, sliders(i).x+ui.trackWidth, y#+ui.trackHeight
 
   rem draw thumb
   ink ui.thumbColor,0
   y# = sliders(i).y - ui.thumbHeight/2
   box sliders(i).thumbX, y#, sliders(i).thumbX+ui.thumbWidth, y#+ui.thumbHeight
 
 
 
   if lf = FALSE
      rem handle mouse events
      if mouseclick() = 1 and sliders(i).isDragging = 0
         if mousex() >= sliders(i).thumbX and mousex() <= sliders(i).thumbX+ui.thumbWidth
            if mousey() >= y# and mousey() <= y#+ui.thumbHeight
               sliders(i).isDragging = 1
               sliders(i).mOffsetX = sliders(i).thumbX - mousex()
            endif
         endif
      endif
   endif
 
   if sliders(i).isDragging = 1
      sliders(i).thumbX = mousex() + sliders(i).mOffsetX
 
      if sliders(i).thumbX < sliders(i).x then sliders(i).thumbX = sliders(i).x
      if sliders(i).thumbX > (sliders(i).x+ui.trackWidth)-ui.thumbWidth then sliders(i).thumbX = (sliders(i).x+ui.trackWidth)-ui.thumbWidth
 
      rem set slider value
      sliders(i).value = (((sliders(i).max - sliders(i).min) * (sliders(i).thumbX-sliders(i).x)) / (ui.trackWidth-ui.thumbWidth)) + sliders(i).min
 
   endif
 
   rem display current value
   if sliders(i).displayValue = TRUE
      ink 0,0
      `text 924, sliders(i).y-7, str$(int(sliders(i).value))
      text sliders(i).valueX, sliders(i).valueY, str$(int(sliders(i).value))
   endif
 
   if mouseclick() <> 1 then sliders(i).isDragging = 0
 
endfunction
 
 
 
 
rem Vertical Slider
function _handleSliderVertical(i as integer, lf as integer)
   local ui as sliderComponentUI
   ui = sliders(i).ui
   rem draw track
   ink ui.trackColor,0
   x# = sliders(i).x - ui.trackWidth/2
   box x#,sliders(i).y,x#+ui.trackWidth,sliders(i).y+ui.trackHeight
 
   rem draw thumb
   ink ui.thumbColor,0
   x# = sliders(i).x - ui.thumbWidth/2
   box x#,sliders(i).thumbY,x#+ui.thumbWidth,sliders(i).thumbY+ui.thumbHeight
 
 
   if lf = FALSE
      rem handle mouse events
      if mouseclick() = 1 and sliders(i).isDragging = 0
         if mousex() >= x# and mousex() <= x#+ui.thumbWidth
            if mousey() >= sliders(i).thumbY and mousey() <= sliders(i).thumbY+ui.thumbHeight
               sliders(i).isDragging = 1
               sliders(i).mOffsetY = sliders(i).thumbY - mousey()
            endif
         endif
      endif
   endif
 
   if sliders(i).isDragging = 1
      sliders(i).thumbY = mousey() + sliders(i).mOffsetY
 
      if sliders(i).thumbY < sliders(i).y then sliders(i).thumbY = sliders(i).y
      if sliders(i).thumbY > (sliders(i).y+ui.trackHeight)-ui.thumbHeight then sliders(i).thumbY = (sliders(i).y+ui.trackHeight)-ui.thumbHeight
 
      rem set slider value
      sliders(i).value = sliders(i).max - (((sliders(i).max - sliders(i).min) * (sliders(i).thumbY-sliders(i).y)) / (ui.trackHeight-ui.thumbHeight)) + sliders(i).min
   endif
 
   rem display current value
   if sliders(i).displayValue = TRUE
      ink 0,0
      center text sliders(i).valueX, sliders(i).valueY, str$(int(sliders(i).value))
   endif
 
   if mouseclick() <> 1 then sliders(i).isDragging = 0
 
endfunction
 
 
REM
function fireButtonAction(action as integer)
   select action
      case ACTION_ADDLAYER : addLayer(TRUE) : endcase
      case ACTION_DELETELAYER : deleteLayer(CURRENT_LAYER) : endcase
   endselect
endfunction
 
 
REM
function drawIcon(icon as integer, x as integer, y as integer)
   select icon
      case ACTION_DRAW : _draw_icon_draw(x,y) : endcase
      case ACTION_FILL : _draw_icon_fill(x,y) : endcase
      case ACTION_LINE : _draw_icon_line(x,y) : endcase
      case ACTION_CIRCLE : _draw_icon_circle(x,y) : endcase
      case ACTION_GRADIENT : _draw_icon_gradient(x,y) : endcase
      case ACTION_ADDLAYER : _draw_icon_add(x,y) : endcase
      case ACTION_DELETELAYER : _draw_icon_minus(x,y) : endcase
      case ACTION_SPRAY : _draw_icon_spray(x,y) : endcase
   endselect
 
endfunction
 
 
REM spray can icon
function _draw_icon_spray(x,y)
   ink 0,0
   dot x+10,y+8
   dot x+5,y+10
   dot x+15,y+10
   dot x+7,y+5
   dot x+10,y+5
   dot x+13,y+5
   dot x+7,y+15
   dot x+10,y+17
   dot x+13,y+15
endfunction
 
 
 
REM +
function _draw_icon_add(x,y)
   ink 0,0
   box x+2,y+8,x+18,y+12
   box x+8,y+2,x+12,y+18
endfunction
 
REM -
function _draw_icon_minus(x,y)
   ink 0,0
   box x+4,y+8,x+16,y+12
endfunction
 
 
REM
function _draw_icon_draw(x,y)
   restore pencil
   for a = 0 to 19
      for b = 0 to 19
         read z
         if z <> -1
            ink z,0
            dot x+b,y+a
         endif
      next b
   next a
endfunction
 
 
REM
function _draw_icon_gradient(x,y)
   box x+4,y+4,x+16,y+16,rgb(192,192,192),0xFFFFFF,0,rgb(192,192,192)
endfunction
 
 
REM
function _draw_icon_fill(x,y)
   ink 0,0
   box x+4,y+4,x+16,y+16
endfunction
 
 
REM
function _draw_icon_line(x,y)
   ink 0,0
   line x+3,y+17,x+17,y+3
endfunction
 
 
REM
function _draw_icon_circle(x,y)
   ink 0,0
   circle x+10,y+10,7
endfunction
 
 
REM draws a line
function bline(index,x1,y1,x2,y2,color as dword,alpha as integer,size as integer)
   dx = x2-x1
   dy = y2-y1
 
   dotMemblockBrush(index, x1, y1, color, alpha,size)
 
   if abs(dx) > abs(dy)
      m# = (0.0 + dy)/dx
      b# = y1 - m#*x1
 
      if dx < 0
         dx = -1
      else
         dx = 1
      endif
      while x1 <> x2
         x1 = x1 + dx
         dotMemblockBrush(index, x1, m#*x1+b#, color, alpha,size)
      endwhile
   else
      if dy <> 0
         m# = (0.0 + dx)/dy
         b# = x1 - m#*y1
         if dy < 0
            dy = -1
         else
            dy = 1
         endif
         while y1 <> y2
            y1 = y1 + dy
            dotMemblockBrush(index, m#*y1+b#, y1, color, alpha,size)
         endwhile
      endif
   endif
endfunction
 
 
REM
function floodFill(index as integer, x as integer, y as integer, color as dword, alpha as integer)
   width = memblock dword(index, 0)
   height = memblock dword(index, 4)
   if x > 0 and x <= width and y > 0 and y <= height
      pos = ((y-1)*width + x - 1)*4 + 12
      tc as dword
      tc = memblock dword(index, pos)
      if tc <> color then _floodFill(index,x,y,x,y,tc,color,alpha,0)
   endif
endfunction
 
 
 
REM Recursive flood fill
function _floodFill(index as integer, rx as integer, ry as integer,x as integer, y as integer, targetColor as dword, fillColor as dword, alpha as integer,count as integer)
   width = memblock dword(index, 0)
   height = memblock dword(index, 4)
 
   rem this limit prevents stack overflows that may occur from using this form of algorithm
   if count > 50000 then exitfunction
 
   rem check bounds
   if x > 0 and x <= width and y > 0 and y <= height
      pos = ((y-1)*width + x - 1)*4 + 12
      temp as dword
      temp = memblock dword(index, pos)
      rem if this point's color equals the target color
      if temp <> targetColor
         exitfunction
      else
         rem color this pixel
         dotMemblock(index, x, y, fillColor,alpha)
 
         inc count
 
         rem branch off
         if x+1 <> rx then _floodFill(index,x,y,x+1,y,targetColor, fillColor,alpha,count)
         if x-1 <> rx then _floodFill(index,x,y,x-1,y,targetColor, fillColor,alpha,count)
         if y+1 <> ry then _floodFill(index,x,y,x,y+1,targetColor, fillColor,alpha,count)
         if y-1 <> ry then _floodFill(index,x,y,x,y-1,targetColor, fillColor,alpha,count)
      endif
   endif
 
endfunction
 
 
 
 
 
 
 
 
Function _open_save(filter As String,initdir As String,dtitle As String,defext As String,open As Boolean)
   `filter = "Text Documents ( *.txt )|*.txt|All Files ( *.* )|*.*|"
   `initdir = "C:"
   `dtitle = "Open ~ Test"
   `defext = "txt"
   `open = 1 For Open Dialogu, 0 for save dialogue
 
 
   `Get DLL numbers
   user32 As Integer
   comdlg32 As Integer
 
   `Load in required DLL's
   user32 = _find_fee_dll()
   Load DLL "user32.dll",user32
   comdlg32 = _find_fee_dll()
   Load DLL "comdlg32.dll",comdlg32
 
 
   `Get handle ( unique ID ) to the calling ( this ) window
   hwnd As DWord
   hwnd = Call DLL(user32,"GetActiveWindow")
 
 
   `Unload DLL as it is no longer needed
   Delete DLL user32
 
 
   `Get the Memblock Number
   OPENFILENAME As Integer
   OPENFILENAME = _find_free_mem()
   `Make The Memblock containing the OPENFILENAME structure
   Make MemBlock OPENFILENAME,76
 
 
   `Get the pointer to the just created Structure
   lpofn As DWord
   lpofn = Get MemBlock Ptr(OPENFILENAME)
 
 
   `Declare temp variables to hold data for OPENFILENAME structure
   size As Integer
   filebuffer As String
   filebufferptr As DWord
   flags As DWord
 
   filter = filter + "|"
   initdir = initdir + "|"
   dtitle = dtitle + "|"
   defext = defext + "|"
   filebuffer = "|" + Space$(255) + "|"
   filebufferptr = _get_str_ptr(filebuffer)
   flags = 0x00001000 || 0x00000004 || 0x00000002
   size = 0
 
   Write MemBlock DWord OPENFILENAME,0,76                     : `lStructSize
   Write MemBlock DWord OPENFILENAME,4,hwnd                   : `hwndOwner
   `Write MemBlock DWord OPENFILENAME,8,NULL                   : `hInstance
   Write MemBlock DWord OPENFILENAME,12,_get_str_ptr(filter)  : `lpstrFilter
   `Write MemBlock DWord OPENFILENAME,16,0                     : `lpstrCustomFilter
   `Write MemBlock DWord OPENFILENAME,20,NULL                  : `nMaxCustFilter
   Write MemBlock DWord OPENFILENAME,24,1                     : `nFilterIndex
   Write MemBlock DWord OPENFILENAME,28,filebufferptr         : `lpstrFile
   Write MemBlock DWord OPENFILENAME,32,256                   : `nMaxFile
   `Write MemBlock DWord OPENFILENAME,36,0                     : `lpstrFileTitle
   `Write MemBlock DWord OPENFILENAME,40,NULL                  : `nMaxFileTitle
   Write MemBlock DWord OPENFILENAME,44,_get_str_ptr(initdir) : `lpstrInitialDir
   Write MemBlock DWord OPENFILENAME,48,_get_str_ptr(dtitle)  : `lpstrTitle
   Write MemBlock DWord OPENFILENAME,52,flags                 : `Flags
   `Write MemBlock Word OPENFILENAME,56,NULL                   : `nFileOffset
   `Write MemBlock Word OPENFILENAME,58,NULL                   : `nFileExtension
   Write MemBlock DWord OPENFILENAME,60,_get_str_ptr(defext)  : `lpstrDefExt
   `Write MemBlock DWord OPENFILENAME,64,NULL                  : `lCustData
   `Write MemBlock DWord OPENFILENAME,68,NULL                  : `lpfnHook
   `Write MemBlock DWord OPENFILENAME,72,0                     : `lpTemplateName
 
 
   `Call the Command to open the dialouge
   retval As DWord
   If open
      retval = Call DLL(comdlg32,"GetOpenFileNameA",lpofn)
   Else
      retval = Call DLL(comdlg32,"GetSaveFileNameA",lpofn)
   EndIf
 
   `Check if it was sucecfull
   If retval <> 0
      code$ = _get_str(filebufferptr,256)
   Else
      retval = Call DLL(comdlg32,"CommDlgExtendedError")
      Select retval
         Case 0xFFFF : code$ = "The dialog box could not be created. The common dialog box function's call to the DialogBox function failed. For example, this error occurs if the common dialog box call specifies an invalid window handle." : EndCase
         Case 0x0006 : code$ = "The common dialog box function failed to find a specified resource." : EndCase
         Case 0x0004 : code$ = "The ENABLETEMPLATE flag was set in the Flags member of the initialization structure for the corresponding common dialog box, but you failed to provide a corresponding instance handle." : EndCase
         Case 0x0002 : code$ = "The common dialog box function failed during initialization. This error often occurs when sufficient memory is not available." : EndCase
         Case 0x000B : code$ = "The ENABLEHOOK flag was set in the Flags member of the initialization structure for the corresponding common dialog box, but you failed to provide a pointer to a corresponding hook procedure." : EndCase
         Case 0x0008 : code$ = "The common dialog box function failed to lock a specified resource." : EndCase
         Case 0x0003 : code$ = "The ENABLETEMPLATE flag was set in the Flags member of the initialization structure for the corresponding common dialog box, but you failed to provide a corresponding template." : EndCase
         Case 0x0007 : code$ = "The common dialog box function failed to load a specified string." : EndCase
         Case 0x0001 : code$ = "The lStructSize member of the initialization structure for the corresponding common dialog box is invalid." : EndCase
         Case 0x0005 : code$ = "The common dialog box function failed to load a specified string." : EndCase
         Case 0x3003 : code$ = "The buffer pointed to by the lpstrFile member of the OPENFILENAME structure is too small for the file name specified by the user. The first two bytes of the lpstrFile buffer contain an integer value specifying the size, in TCHARs, required to receive the full name." : EndCase
         Case 0x0009 : code$ = "The common dialog box function was unable to allocate memory for internal structures." : EndCase
         Case 0x3002 : code$ = "A file name is invalid." : EndCase
         Case 0x000A : code$ = "The common dialog box function was unable to lock the memory associated with a handle." : EndCase
         Case 0x3001 : code$ = "An attempt to subclass a list box failed because sufficient memory was not available." : EndCase
         Case Default : code$ = "WHOOPS!" : EndCase
      EndSelect
   EndIF
 
   Delete DLL comdlg32
 
 
EndFunction code$
 
 
 
Function _get_str_ptr(pstr As String)
   `pstr$ should be a "|" ( NULL ) seperated string.
 
   memnum As Integer
   strlen As Integer
   char As Byte
   memptr As DWord
   strptr As DWord
 
   memnum = _find_free_mem()
   strlen = Len(pstr)
 
   Make MemBlock memnum,strlen
 
   For i = 1 To strlen
      If Mid$(pstr,i) = "|"
         char = 0
      Else
         char = Asc(Mid$(pstr,i))
      EndIf
      Write MemBlock Byte memnum,(i - 1),char
   Next i
 
   memptr = Get MemBlock Ptr(memnum)
 
   strptr = Make Memory(strlen)
 
   Copy Memory strptr,memptr,strlen
 
   Delete MemBlock memnum
 
 
EndFunction strptr
 
 
 
Function _get_str(strptr As DWord,strsize As Integer)
   `strptr is the pointer returned by _get_str_ptr()
   `strsize is the Integer length of the string specified by the pointer
 
   memnum As Integer
   memptr As DWord
   str As String
   char As String
 
   memnum = _find_free_mem()
 
   Make MemBlock memnum,strsize
 
   memptr = Get MemBlock Ptr(memnum)
 
   Copy Memory memptr,strptr,strsize
 
   For i = 1 To strsize
      str = str + Chr$(MemBlock Byte(memnum,i - 1))
   Next i
 
EndFunction str
 
 
 
Function _find_fee_dll()
   retval = 0
   Repeat
      Inc retval
   Until DLL Exist(retval) = 0
EndFunction retval
 
 
 
Function _find_free_mem()
   retval = 50
   Repeat
      Inc retval
   Until MemBlock Exist(retval) = 0
EndFunction retval
 
 
 
 
 
REM add a new dropdown menu to the bar
REM returns the index for referencing this menu
function addMenu(name as string)
   m as Menu
   m.name = name
   array insert at bottom menus()
   count = array count(menus())
   menus(count) = m
endfunction count
 
 
REM add a new menu item to an existing menu
function addMenuItem(pid as integer, item as string, enabled as boolean, check as integer)
   m as MenuItem
   m.parentID = pid
   m.item = item
   m.enabled = enabled
   m.check = check
   array insert at bottom menuItems()
   menuItems(array count(menuItems())) = m
 
   rem make sure menu is drawn wide enough to handle this item's name
   width = text width(item)+6
   if width > menus(pid).width then menus(pid).width = width
 
   menus(pid).count = menus(pid).count + 1
endfunction
 
 
REM handle drawing and user-interaction of menus
function _handleMenus$(mc as integer)
   v$ = ""
   offsetX = 0
   active = -1
   activeOffset = 0
   rem text height, vertical spacing
   tw = 16
 
   rem draw menu bar across top
   ink rgb(192,192,192),0
   box 0,0,876,tw
 
   for i = 0 to array count(menus())
      width = text width(menus(i).name)
 
      if mc = 1
         if mousex() >= offsetX and mousex() <= offsetX+width+6 and mousey() >= 0 and mousey() <= tw
            if  _menuMouseFlag = 0
                _menuMouseFlag = 1
               if menus(i).active = 0
                  menus(i).active = 1
               else
                  menus(i).active = 0
               endif
               for t = 0 to array count(menus())
                  if t <> i then menus(t).active = 0
               next t
            endif
         endif
      endif
 
      if menus(i).active = 1
         active = i
         activeOffset = offsetX
         ink rgb(0,180,250),0
         box offsetX,0,offsetX+width+6,tw
      `else
         `ink rgb(192,192,192),0
      endif
      `box offsetX,0,offsetX+width+6,tw
      ink 0,0
      text offsetX+3,0,menus(i).name
      offsetX = offsetX + width + 6
   next i
 
 
 
   if active > -1
 
      _menuFlag = 1
      ink rgb(192,192,192),0
      box activeOffset,tw,activeOffset+menus(active).width,tw+menus(active).count*tw
      offsetY = 0
      ink 0,0
      for i = 0 to array count(menuItems())
         if menuItems(i).parentID = active
            inc offsetY
            if mousex() > activeOffset and mousex() < activeOffset+menus(active).width and mousey() > offsetY*tw and mousey() < (offsetY+1)*tw
               ink rgb(0,180,250),0
               box activeOffset+1,offsetY*tw+1,activeOffset+menus(active).width-1,(offsetY+1)*tw-1
               rem if this item was clicked
               if mc = 1
                  if _menuMouseFlag = 0
                     _menuMouseFlag = 1
                     if menuItems(i).enabled = 1
                        v$ = menus(active).name + "-" + menuItems(i).item
                        menus(active).active = 0
                        exitfunction v$
                     endif
                  endif
               endif
            endif
            if menuItems(i).enabled = 1
               ink 0,0
            else
               ink rgb(120,120,120),0
            endif
            text activeOffset+3,offsetY*tw,menuItems(i).item
         endif
      next i
   else
      _menuFlag = 0
   endif
 
endfunction v$
 
 
 
REM
function fireMenuAction(action as string)
 
   if action = "File-New" then new()
   if action = "File-Open" then open()
   if action = "File-Save" then save()
   if action = "File-Exit" then exitApp()
   if action = "Filter-Noise" then noise()
 
endfunction
 
 
REM
function noise()
   width = memblock dword(CURRENT_LAYER, 0)
   height = memblock dword(CURRENT_LAYER, 4)
   white as dword
   white = rgb(255,255,255)
   for x = 0 to width-1
      for y = 0 to height-1
         pos = ((y*width)+x)*4 + 12
         if rnd(1) = 0
            white = rgb(1,1,1)
         else
            white = rgb(255,255,255)
         endif
         write memblock dword CURRENT_LAYER, pos,white
      next y
   next x
 
   make image from memblock CURRENT_LAYER,CURRENT_LAYER
endfunction
 
 
REM not finished
REM need to fix adapt UDT to not use parrallel indices
function deleteLayer(layer as integer)
   delete memblock layer
   delete sprite layer
   delete image layer
   dec LAYER_INDEX, 1
   CURRENT_LAYER = 1
   array delete element layerObjects(), layer
 
endfunction
 
 
 
rem new image, white as default background for 1st layer
function new()
   for i = 1 to array count(layerObjects())
      delete memblock i
      delete sprite i
      delete image i
   next i
   LAYER_INDEX = 0
   CURRENT_LAYER = -1
   undim layerObjects(0)
   dim layerObjects(0) as LAYER_UI
 
   addLayer(TRUE)
 
   width = memblock dword(CURRENT_LAYER, 0)
   height = memblock dword(CURRENT_LAYER, 4)
 
   rem make default background white
   for x = 0 to width-1
      for y = 0 to height-1
         pos = (y*width + x)*4 + 12
         write memblock dword CURRENT_LAYER, pos, rgb(255,255,255)
      next y
   next x
 
   make image from memblock CURRENT_LAYER,CURRENT_LAYER
 
endfunction
 
 
rem open an image
function open()
   for i = 1 to array count(layerObjects())
      delete memblock i
      delete sprite i
      delete image i
   next i
   LAYER_INDEX = 0
   CURRENT_LAYER = -1
   `empty array layerObjects()
 
   undim layerObjects(0)
   dim layerObjects(0) as LAYER_UI
 
   loadImage(_open_save("All Files ( *.* )|*.*|","C:","Open File","jpg",1))
endfunction
 
 
 
function save()
   file$ = _open_save("All Files ( *.* )|*.*|","C:","Save File","jpg",0)
endfunction
 
 
 
function exitApp()
   end
endfunction
 
 
 
pencil:
data -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,-1,-1,0,12517376,12517376,12517376,0,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,-1,-1,0,12517376,-1,-1,0,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,-1,0,0,12517376,-1,0,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,-1,0,12636233,0,0,0,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,0,12636233,11649842,10465069,0,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,0,12636233,11649842,0,0,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,0,12636233,11649842,10465069,0,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,0,11649842,10465069,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,0,12636233,11649842,10465069,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,0,0,10465069,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1