shoes / shoes3

a tiny graphical app kit for ruby
http://walkabout.mvmanila.com
Other
181 stars 19 forks source link

text_shape? #266

Open ccoupe opened 8 years ago

ccoupe commented 8 years ago

From #264, Shoes could have a new text_shape slot art method. A rendered string so it's just pixels, no string modifications allowed after creation. but the pixmap could be positioned and transformed like any other Shoe Art like oval or star.

label = text_shape text: "very long string", font: "Lacuna Bold 23,", x: 10, y: 600,
  width: 50:  height: 20

creates a 50px X 20px box and renders the text: string into it. In this example it would clip, truncate, deform and make a dogs mess of it. That's a user problem. Shoes just has a 50w X 20h block of pixels to deal with. Defaults would be tricky - without a width and height they would default to the rect that contains the complete string in [given] font. We'll want foreground (text) and background color so that some more :option strings to deal with.

Most of the work would appear to be canvas.c, ruby.c and perhaps image.c so obj-c skills would not be required. Discuss.

ccoupe commented 8 years ago

Now that I've done some looking around in the deep parts of Shoes, I think adding a 'chars_at' method to image would be better, name wise. Less confusion with text and string. Cairo does have a cairo_show_text(string) method so it seems possible. Not simple, but very possible.

passenger94 commented 8 years ago

i think this is a great idea, When you have a long text (not that long) it quickly kills shoes performance because of all those active objects that shoes have to redraw again and again, just scrolling becomes a pain. As we can't actually edit the text (it's possible but not fully implemented) there is less use for live text objects... Maybe it's possible to keep live objects for resizing and a pixels data for scrolling for example ? a buffer with all text and attributes ready to be re-rendered when resizing, but silent for cases where re rendering would be unnecessary ...

ccoupe commented 8 years ago

I like the concept too. I'm not so keen on the method name. Cairo-wise it's pretty straight forward task since we know how to render text to. Dealing with font would be a lot easier if Shoes had a font class but it doesn't (it's more like a module.function) Pangocairo is reasonably simple but bit wordy.

I suspect the facility has many use cases - how would one draw the labels of a pie chart using flows and stacks or put text inside the wedges. Maybe somebody wants to watermark or annotate an image. Lots of interesting things and as you mention if you build them out of a new or hidden textblock that would be very clever. I suspect @dredknight would find some uses.

I may end up creating a small group of C pangocairo routines in the next day or so since #264 will need them. @passenger94, if you want to work on this issue instead of new ruby-c macros let me know (or assign the issue to yourself). I've got plenty of things to do.

passenger94 commented 8 years ago

i'm looking into this, we'll see :-)

i'm trying to understand the plot plumbing and where are the pangocairo routines ... (i think i got the general view, now onto deep immersion)

if i understand correctly, you want to switch to pango for the text rendering there ?

dredknight commented 8 years ago

hehehe :). Thanks @ccoupe. I will be happy to see some examples on how this method is presented on the canvas.

ccoupe commented 8 years ago

Plot only draws text in a few place and as currently written recomputes text heights and widths far too often, values that could be precomputed and cached for shoes_plot_draw to find. The cairo toy interface has problems with some languages/character-sets - for US/UTF-8 - it's fine but that's not everything in the world. I don't expect a lot of trouble, I hope to have it done when you awake in the morning. See:

passenger94 commented 8 years ago

ok :-)

did you know :

Shoes.app do
    shape width: 100, heigth: 50 do
        transform :corner
        translate 20, 20
        scale 2.5
        skew 15.0, 20.0
        rotate -25
        para "deep transformed text\ntransformed deep text"
    end
end

shapetext

Doesn't work in image block, too bad :-D

passenger94 commented 8 years ago

after some hacking around for the sake of it, an image text block : image_text_block

works but it means adding a lot of methods to cImage (para, title ... potentially span, strong, etc..) and adding the transform methods to textblock and the text is still alive, which is kinda fun but not what we are after ! Probably ugly to manipulate, also.

Though, simply adding the transform methods to textblocks could be something to think about (dead simple : one only needs to create them with a call to the appropriate Macro and make ruby aware of them) text_transform

and yes it keeps styles after the transformation ! ... maybe oblique text in legends or the like ...

back to reach for some pixels out of text ...

ccoupe commented 8 years ago

Wow! I just knew there had to be existing way to get text into a shape. You found it!

Thinking out loud: perhaps a textblock.to_shape and/or textblock.to_image method would be the way to go?

passenger94 commented 8 years ago

On the shape side the functionality is there, one just need to use it, no special method is necessary, or do you feel a cleaner API would be a good idea ?

As for the image block, i first thought it was not worth the resulting noise in the api, as text is still alive that way, but, in my experimentations for fetching pixels out of a texblock, i realized that it's easy to just hijack the text drawing process and render to an independent surface, but then what ? ok we've got pixels but if we simply re-render that surface in lieu of the normal text rendering, the result is exactly the same : alive text which redraws every time a drawing is triggered. So, once i have a surface from the textblock, i'm thinking now to pipe into the image processing because of the embedded cache mechanism (means files created, potentially a lot, viable with RED warnings ??) ... if this doesn't work maybe the image block becomes an option to play with... On a second thought if we have big amount of texts we want to pixellize so it doesn't redraw again and again, image block or something similar would be a good way to materialize the peculiarity of that text block ...

Another option is to mark the text block as special (say rendered) and make it skip subsequent drawings but one need to keep placement functionality effective ... let's see hmmm no, stupid idea !! :sunglasses: But interesting question : as Shoes is endlessly redrawing everything, how does one guard some parts from beeing redrawn but yet still vsible ... (and at the appropriate place) ?

ccoupe commented 8 years ago

At this point I vote for the easy way and then see how well it works or how hard it is to use. I think just creating a new shape type like arc or oval which is just a rectangle with some extra style args for the string, font choice for creation. Rendering the string into the rectangle via cairo drawing into simple surface - https://www.cairographics.org/manual/cairo-Image-Surfaces.html. Of course we'd need a place to keep the cairo_surface ptr. It is not a Shoes image as we know them. It would share all methods that shapes respond to. That was the simple idea but I only did enough research to think it ought to work. It would need to work when combined into a container shape.

passenger94 commented 8 years ago

This is essentially what is going on now : textblock is just a special shape, which happens to be precomputed in a pango layout but lastly rendered in a cairo context (it even explicitly use some Shoes shape methods in C code). That's also why the text in a shape block works out of the box but not in an image block (those 2 are different beast, one need to add redirection facility to make it work on image blocks). Again, i'm able to render the text in a separate ImageSurface (controlled via an attribute in Shoes - ruby code -) and show that surface instead of directly the pango layout, but there is no point to do so unless we can cache the imageSurface, Redrawing is going to happen anyway (ideally the cache would be redrawn, at least theoretically we can skip subsequent pango calculations, nothing more - maybe more memory efficient ? - ) and i'm not even talking about placements whose computation is deeply tied/mixed/interleaved with pure pango/cairo drawings ... Right now the only solution i can think of, once an ImageSurface is drawn out of a textblock, is to make it, if possible, an Image object so it can be properly placed and the cache facility used

What i have now: gray background is a cairo.paint() on the ImageSurface to get visual feedback

tblockpixels

para "rendered para ! does it go outside of the window if " \
        "i write a long text or does it wrap and begin a new line ?", 
        render: true, stroke: blue

That thing is still live text, it wraps and length changes on resizing (until ImageSurface dimension is hit, in which case, of course, text disappears, another thing to manage ...)

passenger94 commented 8 years ago

proof of concept : tblockpixels2

rendering a textblock on a separate ImageSurface, if asked to do so, create a new cImage object, replace the cached_image surface of the cImage object with the one created from the textblock (maybe we can shrink this to a cached image only ...) reuse the cached surface for subsequent drawings ... (good news is apparently the cache is on memory, no files!)

Now the text is fixed at the "shape" it had at the first drawing

going to experiment to see how it behaves with other objects on the canvas, expecting troubles ....

passenger94 commented 8 years ago

ok i polished the thing, it's working ... well i have not explored every aspect of it because i hit one severe restriction : images and text don't mix correctly (i mean even in a regular Shoes app) in a flow at least, kind o a big mess ...

Shoes.app do
    para "some text "
    #    hybrid = para "rendered para ! does it go outside of the window if " \
    #        "i write a long text or does it wrap and begin a new line ? that part should disappear when not wrapped", 
    #        render: true, stroke: blue, margin: [20,20,20,20]
    hybrid = image "PATH/TO/YOUR/IMAGE"
    self.start { |slot|
        para "another text", stroke: green

        para "\nself.contents[] #{slot.contents.inspect}, responds to :highlight ? #{hybrid.respond_to?(:highlight)}" \
            " , responds to :blur ? #{hybrid.respond_to?(:blur)}"
        para "\nself.contents[1] #{slot.contents[1].inspect}, responds to :highlight ? " \
            "#{slot.contents[1].respond_to?(:highlight)} , responds to :blur ? #{slot.contents[1].respond_to?(:blur)}"
        para "hybrid size : #{hybrid.width}, #{hybrid.height}" 
    }
end

test_txtblok_image

also the start block necessary to be able to fetch updated info like image size, etc...

ccoupe commented 8 years ago

That demo makes creating a shape with explictit(l,t,h,w) much more appealing since they don't appear don't fit into slots or flows either.

passenger94 commented 8 years ago

Agree ! Adding an entry to Shape or textblock in manual seems the best refactorization avalaible!

ccoupe commented 8 years ago

I created a new text_shape branch, local and remote and after much head scratching it's also the best method name(IMO) to add to Shoes. In concept it's just like oval or star with some new arguments at creation. Since shapes don't respect flow/stack/slot layouts, it's of limited value.

ccoupe commented 7 years ago

From #294

Does text_shape support anti-aliasing? (Maybe we should move this conversation to #266)

Shoes does all char/string drawing with pangocairo (except, perhaps, some osx native control labels and dialogs) text_shape would be no different that normal Shoes behavior. You can reading the pango, cairo and pangocairo docs online if you want to know what can be done.