Hi guys,
I am used to write my own GUI-widgets in python with wx. Now I want to do the same with nim. I would like to do the drawing with pixie and the windowing with NiGui.
How can this be done? How can I paint a pixie-image in a NiGui window?
For the case this is a stupid question: Apologies I am just beginning to explore nim.
Thanks
Here an example program, which uses copyMem from a Pixie image to a NiGui canvas.
(Tested under Linux/Gtk)
import std/math
import pixie
import NiGui
const width = 500
const height = 500
app.init()
var window = newWindow("Pixie+NiGui Example")
window.width = width
window.height = height
var control1 = newControl()
window.add(control1)
control1.widthMode = WidthMode_Fill
control1.heightMode = HeightMode_Fill
var niGuiImage = nigui.newImage()
niGuiImage.resize(width, height)
control1.onDraw = proc (event: DrawEvent) =
let canvas = event.control.canvas
let image = newImage(width, height)
let ctx = newContext(image)
# Background
image.fill(rgba(255, 255, 255, 255))
# Line
ctx.strokeStyle = rgba(0, 0, 255, 255)
ctx.lineWidth = 10
ctx.lineCap = RoundCap
ctx.strokeSegment(segment(vec2(10, 10), vec2(width - 10, height - 10)))
# Rect
ctx.fillStyle = rgba(0, 255, 0, 255)
ctx.translate(100, 200)
ctx.rotate(45 * PI / 180)
ctx.fillRect(rect(vec2(0, 0), vec2(100, 100)))
ctx.strokeRect(rect(vec2(0, 0), vec2(100, 100)))
# Text
block:
const text = "Text with Pixie"
let font = readFont("/usr/share/fonts/noto/NotoSans-Regular.ttf")
font.size = 40
font.paint.color = color(0, 0, 0)
image.fillText(font.typeset(text), translate(vec2(200, 40)))
# Draw Pixie image to drawing area
var niGuiImageData = niGuiImage.beginPixelDataAccess()
copyMem(niGuiImageData[0].addr, image.data[0].addr, width * height * 4)
niGuiImage.endPixelDataAccess()
event.control.canvas.drawImage(niGuiImage, 0, 0)
# Text native
block:
const text = "Text with NiGui"
canvas.fontSize = 40 * 1.1
canvas.textColor = nigui.rgb(0, 0, 0)
canvas.drawText(text, 200, 90)
window.show()
app.run()
Maybe because it says:
This library is still in development and is not ready to be used.
right at the top of windy's readme? 😉
Simple reason: Because I need native look & feel! The plan is to use as much standard widgets as possible and only implement what can't be achieved otherwise.
Thanks for your high quality libraries anyway.
ctx.strokeStyle = rgba(0, 0, 255, 255)
However they appear in red!
How can I fix this (and again sorry for my beginner questions).
This was fun to track down!
event.control.canvas.drawImage(niGuiImage, 0, 0)
At least on Linux, this line passes image to the Cairo library that actually does the drawing. niGuiImage here and Pixie's image is just an array of bytes.
If we copy raw bytes from one array to another it's only logical to check if they're compatible:
Pixie uses RGBA format: 4 bytes per pixel, blue color is encoded as 0 0 255 255.
Cairo actually has a couple supported formats for in-memory images, but only one is hard-coded and used in NiGui:
CAIRO_FORMAT_ARGB32
each pixel is a 32-bit quantity, with alpha in the upper 8 bits, then red, then green, then blue. The 32-bit quantities are stored native-endian. Pre-multiplied alpha is used. (That is, 50% transparent red is 0x80800000, not 0x80ff0000.)
Important part is "32-bit quantities are stored native-endian". On the little-endian machines (e.g. x86_64) format is not ARGB, but BGRA!
So, when you copy Pixie's image to NiGui - Pixie puts blue pixels (r: 0 g: 0 b: 255 a: 255) in the image correctly, then NiGui calls Cairo library and it reads pixels as red (b: 0 g: 0 r: 255 a: 255) instead.
It's not a bug per se, just if you want to use Pixie and NiGui together you need a little more code to convert between two formats (left as an exercise for the reader).
P.S. I haven't checked the windows part of NiGui, it might use some other format too (hopefully not).
Thanks for figuring this out. Colors are fine when I replace copyMem by the following code
var nimage = cast[cstring](niGuiImageData[0].addr)
var pimage = cast[cstring](image.data[0].addr)
var j = 0
for i in 1..(canvas.width*canvas.height):
nimage[j] = pimage[j]
nimage[j+1] = pimage[j+2]
nimage[j+2] = pimage[j+1]
nimage[j+3] = pimage[j+3]
j += 4