Skip to content

Commit

Permalink
Merge branch 'master' into demo2
Browse files Browse the repository at this point in the history
  • Loading branch information
mrgaturus committed Dec 31, 2024
2 parents 08ba9d1 + 8189556 commit c2b72a7
Show file tree
Hide file tree
Showing 36 changed files with 2,918 additions and 1,210 deletions.
29 changes: 14 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,39 +73,38 @@ nopack
## ⚙️ Roadmap Features
- [x] Pen Pressure Support
- [x] Multithreading and SIMD Optimization
- [x] Anti-Aliased and Amazing Brush Engine
- [x] Anti-Aliased Bucket Fill + Gap Closing
- [x] Anti-Aliased and Amazing Brush Engine *
- [x] Anti-Aliased Bucket Fill + Gap Closing *
- [x] GPU Accelerated Canvas
- [x] Tiled Layering
- [x] Raster Layers
- [ ] Mask/Stencil Layers
- [x] Mask/Stencil Layers
- [x] Folder Layers
- [x] Fundamental Blending Modes
- [x] Clipping Group & Alpha Lock
- [x] 25 Blending Modes
- [x] Clipping Group
- [ ] Alpha Lock
- [ ] Selection Tools
- [ ] Transform Tool
- [ ] Perspective
- [ ] Mesh
- [ ] Liquify
- [ ] Selection Tools
- [x] Infinite Undo using Compressed Files
- [ ] Fundamental Filters
- [ ] Intuitive and Professional UI/UX
- [x] Infinite Undo using Compressed Files
- [x] Multi Platform Support
- [x] Linux/X11
- [x] Windows
- [ ] macOS

### 🕙 Planned Features
* Vector Layer
- Catmull
- Bezier
* Vector & Shape Layers
* On-canvas Text Layers
* Frame by Frame Animation
* On-canvas Text Tool
* Android and iPad

### ❌ Not-Planned Features
- Maximum Color Accuracy
- The Fastest Painting Software ever
- Very Realistic Brushes
- Perfect Color Accuracy
- Realistic Color Mixing
- The Fastest Painting Software Ever
- 1:1 Features with Similar Software
- AI, Machine Learning and Cryptocurrency
- AI, Machine Learning and NFT
6 changes: 3 additions & 3 deletions npainter.nimble
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Package

version = "0.2.0"
version = "0.0.2"
author = "Cristian Camilo Ruiz"
description = "fast and simple digital painting software"
license = "GPL-3.0"
srcDir = "src"
bin = @["npainter"]

# Dependencies
requires "nim >= 2.0.0"
requires "https://github.com/mrgaturus/nogui#head"
requires "nim >= 2.2.0"
requires "https://github.com/mrgaturus/nogui#d6e86ce"
requires "tinyfiledialogs"
Binary file modified proof.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions src/ux/docks/brush/section.nim
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,15 @@ controller CXBrushSection:
proc createSection =
let
button = button(iconFold0, self.cbFold)
combo = combobox(self.model).clear()
combo = combobox(self.model).glass()
# Set Attributes
self.button = button
self.combo = combo
# Create Section Layout
self.section =
vertical().child:
min: horizontal().child:
min: button.clear()
min: button.glass()
combo

new cxbrushsection(menu: UXMenu):
Expand All @@ -134,7 +134,7 @@ controller CXBrushSection:
widget UXButtonCover:
new uxbuttoncover(w: GUIWidget, cb: GUICallback):
result.flags = {wMouse}
result.add button("", cb).clear()
result.add button("", cb).glass()
result.add w

method update =
Expand Down Expand Up @@ -169,7 +169,7 @@ proc cxbrushsection*(label: string, w: GUIWidget): CXBrushSection =
# Create Header
let header =
horizontal().child:
min: button.clear()
min: button.glass()
label(label, hoLeft, veMiddle)
# Create Section Template
result.button = button
Expand Down
12 changes: 9 additions & 3 deletions src/ux/docks/layers/item.nim
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ icons "dock/layers", 32:
const blendname*: array[NBlendMode, cstring] = [
bmNormal: "Normal",
bmPassthrough: "Passthrough",
bmMask: "Mask",
bmStencil: "Stencil",
# -- Darker --
bmMultiply: "Multiply",
bmDarken: "Darken",
Expand Down Expand Up @@ -126,8 +128,12 @@ widget UXLayerThumb:
layer = self.layer[]
r = self.rect
# TODO: draw thumbnail texture here
ctx.color 0xFFFFFFFF'u32
if layer.kind != lkFolder:
case layer.kind
of lkColor16, lkColor8:
ctx.color rgba(255, 255, 255, 255)
ctx.fill rect(r)
of lkMask:
ctx.color rgba(0, 0, 0, 255)
ctx.fill rect(r)
# Draw Folder Collapsed
elif lpFolded notin layer.props.flags:
Expand Down Expand Up @@ -413,7 +419,7 @@ widget UXLayerItem:
# Check Layer Selected
let level = self.laLevel.level
var flags = self.flags - {wHold, wHidden}
if self.layer == self.layers.selected:
if self.layer == self.layers.target:
flags.incl(wHold)
# Check Level Folded/Hidden
if level.folded: flags.incl(wHidden)
Expand Down
86 changes: 57 additions & 29 deletions src/ux/docks/layers/layers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -36,37 +36,64 @@ icons "dock/layers", 16:

controller CXLayersDock:
attributes:
# Combomodel
layers: CXLayers
list: UXLayerList
mode: ComboModel
# Combobox Modes
modeMask: ComboModel
modeColor: ComboModel
modeCombo: UXComboBox
itemNormal: UXComboItem
itemPass: UXComboItem
# Usable Dock
{.public.}:
dock: UXDockContent

callback cbUpdate:
let
m = peek(self.layers.mode)[]
mode {.cursor.} = self.mode
let layer = self.layers.target
let mode = layer.props.mode
# Detach Passthrough
let pass = self.itemPass
if not isNil(pass.prev):
pass.detach()
wasMoved(pass.prev)
# Select Current Model
let model {.cursor.} =
if layer.kind != lkMask:
self.modeColor
else: self.modeMask
if layer.kind == lkFolder:
attachNext(self.itemNormal, pass)
# Select Without Callback
wasMoved(mode.onchange)
mode.select(ord m)
mode.onchange = self.cbChangeMode
wasMoved(model.onchange)
model.select(ord mode)
model.onchange = self.cbChangeMode
model(self.modeCombo, model)
send(self.modeCombo, wsLayout)

callback cbChangeMode:
let layer = self.layers.target
let model {.cursor.} =
if layer.kind != lkMask:
self.modeColor
else: self.modeMask
# Change Current Blend Mode
let m = react(self.layers.mode)
m[] = NBlendMode(self.mode.selected.value)
m[] = NBlendMode(model.selected.value)

callback cbStructure:
self.list.reload()

callback cbDummy:
discard

proc createCombo() =
self.mode =
self.itemNormal = comboitem(bmNormal)
self.itemPass = comboitem(bmPassthrough)
self.modeMask =
combomodel(): menu("").child:
comboitem(bmMask)
comboitem(bmStencil)
self.modeColor =
combomodel(): menu("").child:
comboitem(bmNormal)
self.itemNormal
self.itemPass
menuseparator("Dark")
comboitem(bmMultiply)
comboitem(bmDarken)
Expand Down Expand Up @@ -97,12 +124,12 @@ controller CXLayersDock:
comboitem(bmColor)
comboitem(bmLuminosity)
# Change Blending Callback
self.mode.onchange = self.cbChangeMode
self.modeColor.onchange = self.cbChangeMode
self.modeMask.onchange = self.cbChangeMode
self.modeCombo = combobox(self.modeColor)

proc createWidget: GUIWidget =
let
cb = self.cbDummy
la = self.layers
let la = self.layers
# Create Layer List
self.list = layerlist(self.layers)
self.list.reload()
Expand All @@ -112,7 +139,7 @@ controller CXLayersDock:
min: margin(4):
vertical().child:
form().child:
field("Blending"): combobox(self.mode)
field("Blending"): self.modeCombo
field("Opacity"): slider(la.opacity)
grid(2, 2).child:
cell(0, 0): button("Protect Alpha", iconAlpha, la.protect)
Expand All @@ -122,17 +149,18 @@ controller CXLayersDock:
# Layer Control
min: level().child:
# Layer Creation
tooltip("Add Layer", button(iconAddLayer, la.cbCreateLayer).clear())
tooltip("Add Mask", button(iconAddMask, cb).clear())
tooltip("Add Folder", button(iconAddFolder, la.cbCreateFolder).clear())
vseparator() # Layer Manipulation
tooltip("Duplicate Layer", button(iconDuplicate, la.cbDuplicateLayer).clear())
tooltip("Merge Layer", button(iconMerge, la.cbMergeLayer).clear())
tooltip("Clear Layer", button(iconClear, la.cbClearLayer).clear())
tooltip("Delete Layer", button(iconDelete, la.cbRemoveLayer).clear())
tooltip("Add Layer"): glass: button(iconAddLayer, la.cbCreateLayer)
tooltip("Add Mask"): glass: button(iconAddMask, la.cbCreateMask)
tooltip("Add Folder"): glass: button(iconAddFolder, la.cbCreateFolder)
vseparator()
# Layer Manipulation
tooltip("Duplicate Layer"): glass: button(iconDuplicate, la.cbDuplicateLayer)
tooltip("Merge Layer"): glass: button(iconMerge, la.cbMergeLayer)
tooltip("Clear Layer"): glass: button(iconClear, la.cbClearLayer)
tooltip("Delete Layer"): glass: button(iconDelete, la.cbRemoveLayer)
# Layer Reordering Buttons
tail: tooltip("Raise Layer", button(iconUp, la.cbRaiseLayer).clear())
tail: tooltip("Lower Layer", button(iconDown, la.cbLowerLayer).clear())
tail: glass: button(iconUp, la.cbRaiseLayer)
tail: glass: button(iconDown, la.cbLowerLayer)
# Layer Item
scrollview():
self.list
Expand Down
12 changes: 6 additions & 6 deletions src/ux/docks/navigator/navigator.nim
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ controller CXNavigatorDock:
min: horizontal().child:
level().child:
# Zoom Control
button(iconZoomFit, canvas.cbZoomReset).clear()
button(iconZoomPlus, canvas.cbZoomInc).clear()
button(iconZoomMinus, canvas.cbZoomDec).clear()
glass: button(iconZoomFit, canvas.cbZoomReset)
glass: button(iconZoomPlus, canvas.cbZoomInc)
glass: button(iconZoomMinus, canvas.cbZoomDec)
vseparator() # Angle Control
button(iconRotateReset, canvas.cbAngleReset).clear()
button(iconRotateLeft, canvas.cbAngleDec).clear()
button(iconRotateRight, canvas.cbAngleInc).clear()
glass: button(iconRotateReset, canvas.cbAngleReset)
glass: button(iconRotateLeft, canvas.cbAngleDec)
glass: button(iconRotateRight, canvas.cbAngleInc)
# Mirror Control
tail: button(iconMirrorVer, canvas.mirrorY)
tail: button(iconMirrorHor, canvas.mirrorX)
Expand Down
2 changes: 1 addition & 1 deletion src/ux/main.nim
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ controller NCMainFrame:
let state = npainterstate0proof(w, h, checker)
state.tool.cb = result.cbSelectTool
state.tool.react[] = stBrush
state.proof0default()
# Create Frame Docks
let
dispatch = npainterdispatch(state)
Expand All @@ -68,5 +67,6 @@ controller NCMainFrame:
let frame = result.createFrame()
result.frame = frame
# XXX: proof of concept
state.proof0default()
docks.proof0arrange()
result.proof0shortcuts()
2 changes: 1 addition & 1 deletion src/ux/main/menu.nim
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ controller NCMainMenu:
menuitem("Work in progress", self.dummy)

proc menuLogo: UXNoClick =
result = noclick button(iconLogo, self.dummy).clear()
result = noclick button(iconLogo, self.dummy).glass()

proc createMenu*(): UXMenuBar =
menubar().child:
Expand Down
12 changes: 9 additions & 3 deletions src/ux/state.nim
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ controller NPainterState:
if ueLayerTiles in flags:
update(self.engine.canvas)
if ueLayerProps in flags:
let layer {.cursor.} = layers.selected
let layer {.cursor.} = layers.target
let user {.cursor.} = cast[GUIWidget](layer.user)
# Reflect Props Changes to Widget
if ueLayerList notin flags:
Expand All @@ -75,11 +75,15 @@ controller NPainterState:

callback cbUndo:
let canvas = self.engine.canvas
wasMoved(canvas.image.status.clip)
# Dispatch Undo Step
let flags = undo(canvas.undo)
self.reactUndo(flags)

callback cbRedo:
let canvas = self.engine.canvas
wasMoved(canvas.image.status.clip)
# Dispatch Redo Step
let flags = redo(canvas.undo)
self.reactUndo(flags)

Expand Down Expand Up @@ -116,11 +120,13 @@ controller NPainterState:
proc proof0default*() =
# Locate Canvas to Center
let engine {.cursor.} = self.engine
self.canvas.x.peek[] = cfloat(engine.canvas.image.ctx.w) * 0.5
self.canvas.y.peek[] = cfloat(engine.canvas.image.ctx.h) * 0.5
let ctx {.cursor.} = engine.canvas.image.ctx
self.canvas.x.peek[] = cfloat(ctx.w) * 0.5
self.canvas.y.peek[] = cfloat(ctx.h) * 0.5
lorp self.canvas.zoom.peek[], -1.0
# Default Brush Values
proof0default(self.brush)
proof0default(self.layers)

# ---------------
# State Exporting
Expand Down
4 changes: 2 additions & 2 deletions src/ux/state/brush.nim
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ widget UXBrushDispatch:
self.prepareColor()

method event(state: ptr GUIState) =
if self.brush.engine.canvas.image.selected.kind == lkFolder:
if self.brush.engine.canvas.image.target.kind == lkFolder:
return
if state.kind == evCursorClick:
self.prepareDispatch()
Expand All @@ -633,7 +633,7 @@ widget UXBrushDispatch:
echo "brush reason: ", reason
let win = getWindow()
if reason == inHover:
if self.brush.engine.canvas.image.selected.kind != lkFolder:
if self.brush.engine.canvas.image.target.kind != lkFolder:
win.cursor(cursorBasic)
else: win.cursor(cursorForbidden)
elif reason == outHover:
Expand Down
Loading

0 comments on commit c2b72a7

Please sign in to comment.