š
Hi! I am running into problems trying to style som text with skia.membrane.paragraph , or if it is saving/drawing it to an image. All I get is a white rectangle. Correctly sized, afaict. Hereās what I am trying with:
(defn- create-text [text-width title author description]
(let [style #:text-style {;:color [1 1 1]
:font-families ["NimbusSans" "Menlo"]
:font-size 32}]
(para/paragraph [{:text (str title "\n")
:style (merge style #:text-style {:weight :bold})}
{:text (str author "\n")
:style (merge style #:text-style {:slant :italic})}
{:text description
:style style}]
text-width)))
(comment
(def text (create-text 1160 "ImageMagick + Pango + Babashka = ā„ļø"
"Peter Strƶmberg"
"Where there's a will there's a way. I can now hack on my ImageMagick + Pango script in my Babashka REPL on my Mac."))
(java2d/save-image "sometext.png" text (ui/bounds text))
:rcf)
What am I doing wrong? Note that Iāve commented out the :color style. Because I thought that if the rectangle is white, then drawing white text might be one of the problems. But It is all a white filled, empty, rectangle anyway,@pez , If you get a chance, I would love to hear how this experiment turned out for you including the good, bad, or ugly.
I never figured out how to compose the image using membrane. Neither how to avoid creating a temporary image using skia. So right now I have this:
(defn- create-text
[{:keys [width title author description]}]
(let [style #:text-style {:color [1 1 1]
:font-families ["Nimbus Sans" "Arial"]
:font-size 32}]
(para/paragraph [{:text (str title "\n")
:style (merge style #:text-style {:font-style #:font-style {:weight :bold}})}
{:text (str author "\n")
:style (merge style #:text-style {:font-style #:font-style {:slant :italic}})}
{:text description
:style style}]
width)))
(comment
(def text (create-text {:width 1160
:title "ImageMagick + Pango + Babashka = ā„ļø"
:author "Peter Strƶmberg"
:description "Where there's a will there's a way. I can now hack on my ImageMagick + Pango script in my Babashka REPL on my Mac."}))
(skia/save-image (str (doto (fs/tmp-path! "rcf" "sometext.png") println)) text nil nil 0 false)
:rcf)
(defn add-texts! [image slug texts]
(let [g (.createGraphics image)
text-padding-w 30
text-padding-h 20
margin-bottom 80
image-height (.getHeight image)
rect-width (.getWidth image)
text-width (- rect-width (* text-padding-w 2))
text (create-text (assoc texts :width text-width))
text-x text-padding-w
text-image-path (str (fs/tmp-path! slug "texts.png"))
_text-image? (skia/save-image text-image-path text nil nil 0 false) ;; TODO: Handle error?
text-image-file (io/as-file text-image-path)
text-image (ImageIO/read text-image-file)
rect-height (+ (.getHeight text-image) (* text-padding-h 2))
rect-y (- image-height rect-height margin-bottom)
text-y (+ rect-y text-padding-h)]
(doto g
(.setComposite (AlphaComposite/getInstance AlphaComposite/SRC_OVER 0.5))
(.setColor Color/BLACK)
(.fillRect 0 rect-y rect-width rect-height)
(.setComposite (AlphaComposite/getInstance AlphaComposite/SRC_OVER 1))
(.drawImage text-image text-x text-y nil)
.dispose)
image))
Which works, but anyway. šThanks for reporting back. Glad you got it working. I'll write an example with membrane, but tbh, java2d image API isn't so bad.
Itās OK. But much easier to āseeā what I am drawing with membrane.
Here's one other way to write it:
(defn add-texts2 [[image-width image-height] texts]
(let [text-padding-w 30
text-padding-h 20
margin-bottom 80
image-height image-height
rect-width image-width
text-width (- rect-width (* text-padding-w 2))
text (create-text (assoc texts :width text-width))
text-x text-padding-w
rect-height (+ (ui/height text) (* text-padding-h 2))
rect-y (- image-height rect-height margin-bottom)
text-y (+ rect-y text-padding-h)]
[(ui/translate
0 rect-y
(ui/filled-rectangle [0 0 0 0.5]
rect-width
rect-height))
(ui/translate text-x text-y
text)]))
(defn compose [background text]
[background
(add-texts2 (ui/bounds background)
{:width 1160
:title "ImageMagick + Pango + Babashka = ā„ļø"
:author "Peter Strƶmberg"
:description "Where there's a will there's a way. I can now hack on my ImageMagick + Pango script in my Babashka REPL on my Mac."})])
(comment
(skia/save-image "overlay.png"
(add-texts2 [800 400]
{:width 1160
:title "ImageMagick + Pango + Babashka = ā„ļø"
:author "Peter Strƶmberg"
:description "Where there's a will there's a way. I can now hack on my ImageMagick + Pango script in my Babashka REPL on my Mac."}
)
nil nil 0 false)
;; compose with another image
(skia/save-image "composite.png"
(compose (ui/image "mybackground.png")
{:width 1160
:title "ImageMagick + Pango + Babashka = ā„ļø"
:author "Peter Strƶmberg"
:description "Where there's a will there's a way. I can now hack on my ImageMagick + Pango script in my Babashka REPL on my Mac."}
)
nil nil 0 false)
,)Not totally sure that's what you're looking for. Anyway, I'm happy to answer any other questions or do a virtual pair session sometime if you're interested!
Looks pretty close to what Iām looking for. š Iām composing an image, so if there is a way to make compose return an image?
Or to turn what compose returns to an image works too, of course.
The second save-image in the rcf will save that to disk:
(skia/save-image "composite.png"
(compose (ui/image "mybackground.png")
{:width 1160
:title "ImageMagick + Pango + Babashka = ā„ļø"
:author "Peter Strƶmberg"
:description "Where there's a will there's a way. I can now hack on my ImageMagick + Pango script in my Babashka REPL on my Mac."}
)
nil nil 0 false)
Or what do you mean by an image?
For the most part, an image is just a rasterized array of pixels, but that's usually not that useful.
It's possible to get the rasterized pixels, but I'm guessing you're looking for something else
I want something that I can crop, rescale, save as jpg or png. Stuff like that. java.awt.image has had what I need there so far.
My ultimate goal here might be interestingā¦
There are descriptions for most of those operations and I can add any that are missing:
⢠crop -> (ui/scissor-view offset bounds view)
⢠rescale -> (ui/scale sx sy view)
⢠save as png (skia/save-image ...)
Iām adding metadata from some blog posts as overlays on the images. Because Twitter/X has stopped showing metadata for links. It only shows an image.
Most everything works on views which are just data descriptions of the graphical elements and transformations. Views can be displayed, measured, inspected, and composed with other views. There are also several drawing implementations based on various different graphics libraries.
Iāve been using jpg, because those are often smaller on disk.
My suggestions would be to compose and manipulate views and then write the final result to disk with skia/save-image .
I think I can make do with writing a temporary png, and then do a last step reading that png file and converting it to jpg.
Yea, that also works, but I think it's easier to work with views programmatically unless I'm missing something. I would be happy to hop on a call for a bit now or later if you're interested and think that would be helpful.
Letās hop in on a call. š
Ok, here's the code I was running locally:
(defn add-texts2 [[image-width image-height] texts]
(let [text-padding-w 30
text-padding-h 20
margin-bottom 80
image-height image-height
rect-width image-width
text-width (- rect-width (* text-padding-w 2))
text (create-text (assoc texts :width text-width))
text-x text-padding-w
rect-height (+ (ui/height text) (* text-padding-h 2))
rect-y (- image-height rect-height margin-bottom)
text-y (+ rect-y text-padding-h)]
[(ui/translate
0 rect-y
(ui/filled-rectangle [0 0 0 0.5]
rect-width
rect-height))
(ui/translate text-x text-y
text)]))
(defn compose [background text]
[background
(add-texts2 (ui/bounds background)
{:width 1160
:title "ImageMagick + Pango + Babashka = ā„ļø"
:author "Peter Strƶmberg"
:description "Where there's a will there's a way. I can now hack on my ImageMagick + Pango script in my Babashka REPL on my Mac."})])
(comment
(skia/save-image "overlay.png"
(add-texts2 [800 400]
{:width 1160
:title "ImageMagick + Pango + Babashka = ā„ļø"
:author "Peter Strƶmberg"
:description "Where there's a will there's a way. I can now hack on my ImageMagick + Pango script in my Babashka REPL on my Mac."}
)
nil nil 0 false)
;; compose with another image
(skia/save-image "composite.jpeg"
(compose (ui/image "mybackground.png")
{:width 1160
:title "ImageMagick + Pango + Babashka = ā„ļø"
:author "Peter Strƶmberg"
:description "Where there's a will there's a way. I can now hack on my ImageMagick + Pango script in my Babashka REPL on my Mac."}
)
nil nil 85 false)
,)
(defn crop [img]
(let [[w h] ( ui/bounds img)]
[(ui/translate
0
(- (* h (/ 1 3)))
(ui/scissor-view
[0
(* h (/ 1 3))]
[w
(* h (/ 1 3))]
img))]))
(defmacro wrap-errors [body]
`(try
(ui/try-draw
~body
(fn [draw# e#]
(draw# (ui/label e#))))
(catch Exception e#
(ui/label e#))))
(def mpos (atom nil))
(defn debug []
(wrap-errors
(let [size [400 600]]
(ui/on
:mouse-move
(fn [pos]
(reset! mpos pos)
nil)
(ui/vertical-layout
(ui/label @mpos)
(crop
(compose (ui/image "mybackground.png")
{:width 1160
:title "ImageMagick + Pango + Babashka = ā„ļø"
:author "Peter Strƶmberg"
:description "Where there's a will there's a way. I can now hack on my ImageMagick + Pango script in my Babashka REPL on my Mac."}
)) []
(ui/filled-rectangle [1 0 0]
10 10))))))
(comment
(skia/run
#'debug)
(skia/run (constantly
(ui/label "hello world")))
,)fyi, just released version 0.14.2-beta which fixes the layout issue with scissor-view.
What I really want is white text on a transparent background as an image. Because I am later placing the text image on another image.
Hey! The styled text from membrane.skia.paragraph only works with the skia backend.
If you're on the jvm you can get the skia deps with:
com.phronemophobic.membrane/skialib-macosx-aarch64 {:mvn/version "0.14-beta"}
com.phronemophobic.membrane/skialib-linux-x86-64 {:mvn/version "0.14-beta"}
com.phronemophobic.membrane/skialib-macosx-x86-64 {:mvn/version "0.14-beta"}
You only need the dep that matches your system, but including extra doesn't hurt.Thanks! Iāll try that.
I have those.
You should then be able to run things using the membrane.skia namespace instead of the membrane.java2d namespace.
membrane.skia also has save-image
Still a white rectangle. But I might be holding it wrong still. Will double check.
I just tried your code and got
If you're switching from the java2d backend to the skia backend, you may need to restart your repl š
java.awt doesn't always play well with other libs
Here's the version that gets the bold and italic to work:
(defn- create-text [text-width title author description]
(let [style #:text-style {;:color [1 1 1]
:font-families ["NimbusSans" "Menlo"]
:font-size 32}]
(para/paragraph [{:text (str title "\n")
:style (merge style #:text-style {:font-style
#:font-style {:weight :bold}})}
{:text (str author "\n")
:style (merge style #:text-style {:font-style
#:font-style {:slant :italic}})}
{:text description
:style style}]
text-width)))Here's the code for white text on a transparent background:
(defn- create-text [text-width title author description]
(let [style #:text-style {:color [1 1 1]
:font-families ["NimbusSans" "Menlo"]
:font-size 32}]
(para/paragraph [{:text (str title "\n")
:style (merge style #:text-style {:font-style
#:font-style {:weight :bold}})}
{:text (str author "\n")
:style (merge style #:text-style {:font-style
#:font-style {:slant :italic}})}
{:text description
:style style}]
text-width)))
(comment
(def text (create-text 1160 "ImageMagick + Pango + Babashka = ā„ļø"
"Peter Strƶmberg"
"Where there's a will there's a way. I can now hack on my ImageMagick + Pango script in my Babashka REPL on my Mac."))
(skia/save-image "sometext.png" text nil
nil 0 false
)
(skia/run (fn []
(ui/fill-bordered
[0 0 0]
0
(create-text 1160 "ImageMagick + Pango + Babashka = ā„ļø"
"Peter Strƶmberg"
"Where there's a will there's a way. I can now hack on my ImageMagick + Pango script in my Babashka REPL on my Mac."))
))
:rcf)By default, skia/save-image will fill a white background since that's what folks usually want, but you can skip the clear step by passing false as the last parameter.
Awesome. Iāll restart my repl!
Now things work. Thanks! Is there a way I can draw-to-image like with java2d, or shall I just use temp files?
thereās no need to. if you have an element, you can just compose it directly
https://github.com/phronmophobic/membrane/blob/master/docs/tutorial.md#groups
I donāt have an element. I have another image that I want to overlay it on.
Or, like so, with the correct font configured:
iām away from keyboard, but you can get the image by calling ui/image with the path and overlay by putting them both in a vector
Iāll try that!