membrane

pez 2023-10-12T15:49:14.943619Z

šŸ‘‹

pez 2023-10-12T15:53:33.967889Z

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,

phronmophobic 2023-10-15T17:35:44.180069Z

@pez , If you get a chance, I would love to hear how this experiment turned out for you including the good, bad, or ugly.

pez 2023-10-15T18:12:03.403839Z

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. 😃

šŸ‘ 1
phronmophobic 2023-10-15T18:15:30.645659Z

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.

pez 2023-10-15T18:19:00.899329Z

It’s OK. But much easier to ā€œseeā€ what I am drawing with membrane.

šŸ‘ 1
phronmophobic 2023-10-15T19:33:03.414029Z

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)
  
  ,)

phronmophobic 2023-10-15T19:33:43.342509Z

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!

pez 2023-10-15T19:42:32.813279Z

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?

pez 2023-10-15T19:43:14.291119Z

Or to turn what compose returns to an image works too, of course.

phronmophobic 2023-10-15T19:43:47.263279Z

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)
  

phronmophobic 2023-10-15T19:44:23.891329Z

Or what do you mean by an image?

phronmophobic 2023-10-15T19:45:33.590409Z

For the most part, an image is just a rasterized array of pixels, but that's usually not that useful.

phronmophobic 2023-10-15T19:46:03.617479Z

It's possible to get the rasterized pixels, but I'm guessing you're looking for something else

pez 2023-10-15T19:47:46.795499Z

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.

šŸ‘ 1
pez 2023-10-15T19:48:53.852779Z

My ultimate goal here might be interesting…

phronmophobic 2023-10-15T19:49:24.449469Z

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 ...)

pez 2023-10-15T19:50:33.150879Z

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.

phronmophobic 2023-10-15T19:51:03.409229Z

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.

pez 2023-10-15T19:51:04.372429Z

I’ve been using jpg, because those are often smaller on disk.

phronmophobic 2023-10-15T19:51:38.126929Z

My suggestions would be to compose and manipulate views and then write the final result to disk with skia/save-image .

pez 2023-10-15T19:52:09.668419Z

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.

phronmophobic 2023-10-15T19:53:28.010319Z

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.

pez 2023-10-15T19:53:55.566779Z

Let’s hop in on a call. 😃

1
phronmophobic 2023-10-15T20:54:52.629309Z

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")))
  ,)

šŸ™ 1
phronmophobic 2023-10-15T23:24:13.554209Z

fyi, just released version 0.14.2-beta which fixes the layout issue with scissor-view.

šŸ™ 1
pez 2023-10-12T15:54:56.687719Z

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.

phronmophobic 2023-10-12T16:52:52.585769Z

Hey! The styled text from membrane.skia.paragraph only works with the skia backend.

phronmophobic 2023-10-12T16:55:34.046529Z

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.

pez 2023-10-12T16:55:41.608759Z

Thanks! I’ll try that.

pez 2023-10-12T16:55:55.870829Z

I have those.

phronmophobic 2023-10-12T16:56:11.228909Z

You should then be able to run things using the membrane.skia namespace instead of the membrane.java2d namespace.

phronmophobic 2023-10-12T16:57:29.829459Z

membrane.skia also has save-image

pez 2023-10-12T17:00:24.115499Z

Still a white rectangle. But I might be holding it wrong still. Will double check.

phronmophobic 2023-10-12T17:00:57.093129Z

I just tried your code and got

phronmophobic 2023-10-12T17:01:23.816269Z

If you're switching from the java2d backend to the skia backend, you may need to restart your repl šŸ˜•

šŸ™ 1
phronmophobic 2023-10-12T17:01:52.081999Z

java.awt doesn't always play well with other libs

phronmophobic 2023-10-12T17:06:57.666999Z

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)))

phronmophobic 2023-10-12T17:16:36.747489Z

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)

phronmophobic 2023-10-12T17:16:58.727039Z

phronmophobic 2023-10-12T17:18:43.035799Z

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.

pez 2023-10-12T17:28:52.416979Z

Awesome. I’ll restart my repl!

pez 2023-10-12T18:01:12.807019Z

Now things work. Thanks! Is there a way I can draw-to-image like with java2d, or shall I just use temp files?

phronmophobic 2023-10-12T18:02:12.005379Z

there’s no need to. if you have an element, you can just compose it directly

pez 2023-10-12T18:06:39.895539Z

I don’t have an element. I have another image that I want to overlay it on.

pez 2023-10-12T18:08:07.081629Z

Or, like so, with the correct font configured:

phronmophobic 2023-10-12T18:08:11.036949Z

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

pez 2023-10-12T18:08:41.108229Z

I’ll try that!