Closed jonathanabennett closed 1 year ago
I suspect that your pathname contains a ~
that cannot be expanded by GTK. To address this, you need to use (namestring (truename (cu/display u)))
to expand it on the Lisp side.
That didn't help either, and when I (format t "~a" (cu/display u))
, I get the full path /Users/jonathanbennett/.roswell/lisp/quicklisp/local-projects/megastrike/data/images/units/vehicles/Pumapat.png
Can you load cl-gdk4
and try this in your application?
(let ((texture (gdk:make-texture :path "/path/to/your/image/file")))
(format t "WIDTH: ~A HEIGHT: ~A~%" (gdk:texture-width texture) (gdk:texture-height texture)))
The function gdk:make-texture
should signal an error when it fails to open the file. Once the texture is loaded successfully, you can create a picture with (gtk:make-picture :paintable texture)
.
the above snippet accurately reports the size of the image (84x72 px), but the image is still not rendering. It is a png file, is the a possible reason for the error?
EDIT: Using gtk:make-image
once more renders it (when using a texture or a file). This makes me wonder if I need to do something to set the size of the image when using a picture, but I do not see anything that I could change to set the picture size.
Have you tried a minimal example like this?
(define-application (:name picture-test
:id "org.bohonghuang.gtk4.issue-33")
(define-main-window (window (make-application-window :application *application*))
(setf (window-title window) "Picture Test"
(window-child window) (make-picture :filename "/path/to/your/image/file"))
(unless (widget-visible-p window)
(window-present window))))
The snippet is tested to run on both Windows and GNU/Linux, and the size of the picture defaults to the dimensions of the image file.
The minimum example you just posted works on MacOS as well (just tested it now). Is it possible that you can't have a picture inside a box or inside a ListBoxChild?
The minimum size of Picture
defaults to zeros, so you could use (setf (widget-size-request picture) '(100 100))
to set the size manually after creating a picture.
Ok, I've got that working. I'm struggling to figure out how I could draw the picture in a drawing area.
EDIT: Ok, here is the code that has gotten me the closest to managing to draw the images on the screen.
(let ((unit (function-that-returns-nil-or-the-occupant-of-a-hex)))
(when unit
(with-gdk-rgba (color "#000000")
(gdk:cairo-set-source-rgba cr color)
(gdk:cairo-set-source-pixbuf cr
(gdk:pixbuf-get-from-texture (cu/display unit)) ;; cu/display unit returns a GDK:Texture
(point-x (nth 5 hex-points))
(point-y (nth 5 hex-points))))
cairo:stroke)))
In the screenshot attached, there should be an image inside the top-left hex (the one that would be labeled 0101, but instead it is completely empty. It put something there, because the text telling you the hex number is gone from that hex, but whatever is there is invisible.
I have rewritten this down to the smallest possible code I can envision might do this:
(defun draw-test-window ()
(let ((window (gtk:make-application-window :application gtk:*application*))
(picture (gtk:make-drawing-area)))
(setf (gtk:window-child window) picture
(gtk:drawing-area-content-width picture) 500
(gtk:drawing-area-content-height picture) 500
(gtk:drawing-area-draw-func picture) (list (cffi:callback %draw-func)
(cffi:null-pointer)
(cffi:null-pointer)))
(unless (gtk:widget-visible-p window)
(gtk:window-present window))))
(defun draw-func (area cr width height)
(declare (ignore area)
(optimize (speed 3)
(debug 0)
(safety 0)))
(with-gdk-rgba (color "#000000")
(let ((texture (gdk:make-texture :path (namestring (merge-pathnames "images/units/mechs/Akuma.png" *data-folder*)))))
(gdk:cairo-set-source-pixbuf cr
(gdk:pixbuf-get-from-texture texture)
(coerce (the fixnum 100) 'double-float)
(coerce (the fixnum 100) 'double-float))
(cairo:fill-path))))
Having (cairo:stroke)
as the final line also does not draw anything on the screen. In both cases, I get a blank white screen which is 500 by 500 pixels. I expected the image to be displayed 100 pixel down from the top and over from the left.
EDIT:
Sorry, I forgot to include the callback function, but this was copy/pasted from your Cairo demo so I suspect you're familiar with it.
(cffi:defcallback %draw-func :void ((area :pointer)
(cr :pointer)
(width :int)
(height :int)
(data :pointer))
(declare (ignore data))
(let ((cairo:*context* (make-instance 'cairo:context
:pointer cr
:width width
:height height
:pixel-based-p nil)))
(draw-func (make-instance 'gir::object-instance
:class (gir:nget gtk:*ns* "DrawingArea")
:this area)
(make-instance 'gir::struct-instance
:class (gir:nget megastrike::*ns* "Context")
:this cr)
width height)))
Having
(cairo:stroke)
as the final line also does not draw anything on the screen. In both cases, I get a blank white screen which is 500 by 500 pixels. I expected the image to be displayed 100 pixel down from the top and over from the left.
You haven't set a path using functions like cairo:rectangle
, so there won't be anything drawn. The following code can help you draw your image in the center of the canvas.
(defun draw-func (area cr canvas-width canvas-height)
(declare (ignore area)
(optimize (speed 3)
(debug 0)
(safety 0)))
(let ((texture (gdk:make-texture :path (namestring (truename (merge-pathnames "images/units/mechs/Akuma.png" *data-folder*))))))
(let* ((canvas-width (coerce (the fixnum canvas-width) 'single-float))
(canvas-height (coerce (the fixnum canvas-height) 'single-float))
(width (coerce (the fixnum (gdk:texture-width texture)) 'single-float))
(height (coerce (the fixnum (gdk:texture-height texture)) 'single-float))
(x (/ (- canvas-width width) 2.0))
(y (/ (- canvas-height width) 2.0)))
(cairo:rectangle x y width height)
(gdk:cairo-set-source-pixbuf cr (gdk:pixbuf-get-from-texture texture) (coerce x 'double-float) (coerce y 'double-float))
(cairo:fill-path)
(with-gdk-rgba (color "#FF0000")
(cairo:rectangle x y width height)
(gdk:cairo-set-source-rgba cr color)
(cairo:set-line-width 3.0)
(cairo:stroke)))))
where x
and y
are the coordinates of the top-left corner of the image, you can change them to position the image wherever you want it to be drawn.
Ok, got that part working within my program. The problem now is a matter of scaling. Based on reading the GDK Documentation and a few examples in Python, it appears I need to use GdkPixbuf.Pixbuf.scale_simple, but I can't find it in GDK4. If I use (cairo:scale)
, it scales all the coordinates (obviously), meaning the x and y origin for the image shifts.
Okay, previously there were no bindings generated for GdkPixbuf because I thought it had fewer use cases. Now, I have added it in commit 6ab0131af4e3007032053fb8eb807a3456a9fe3d. Please upgrade to use it.
Next question. The images I have are all black and white. I'd like to mark which team they are on by painting the unit the team's color (which I've got in the format returned by gdk:rgba-to-string
). I looked at GdkPixbuf.Pixbuf.composite-color-simple, but it wants a GUINT32
rather than an RGBA color structure. Should I be controlling this in Cairo?
#xRRGGBBAA
. cffi
, the conversion between GdkRGBA
and the RGBA8888
should not be difficult:
(cffi:defcstruct gdk-rgba
(red :float)
(green :float)
(blue :float)
(alpha :float))
(cffi:defcstruct rgba8888 (red :uint8) (green :uint8) (blue :uint8) (alpha :uint8))
(defun rgba-color-value (rgba) (cffi:with-foreign-slots (((r1 red) (g1 green) (b1 blue) (a1 alpha)) (gobj:object-pointer rgba) (:struct gdk-rgba)) (cffi:with-foreign-object (pointer '(:struct rgba8888)) (cffi:with-foreign-slots (((r2 red) (g2 green) (b2 blue) (a2 alpha)) pointer (:struct rgba8888)) (setf r2 (round ( r1 #xFF)) g2 (round ( g1 #xFF)) b2 (round ( b1 #xFF)) a2 (round ( a1 #xFF)))) (cffi:mem-ref pointer :uint32))))
(with-gdk-rgba (rgba "#FF0000FF") (assert (= (rgba-color-value rgba) #xFF0000FF)))
I need to add images to my game. In cl-cffi-gtk, they did this via PixBufs but it seems like the Picture Class is the way I need to go. But I cannot get it to load. Here is my current code
frame
is rendered in another function. This same code works when I replacegtk:make-picture
withgtk:make-image
, but it seems like make-image is for displaying images at a fixed size, not at natural size (which is what I want).EDIT: Extraneous code rendering labels and other rows of this stat block have been removed. But the code fails whether they are commented out or not. And I am sure that
(cu/display u)
contains the path to the correct file becausegtk:make-image
puts a very tiny rendering of that image inside the frame whereasgtk:make-picture
does not.