ysfx icon indicating copy to clipboard operation
ysfx copied to clipboard

Graphics

Open jpcima opened this issue 4 years ago • 13 comments

https://www.reaper.fm/sdk/js/gfx.php

Note (1): this appears to be the default font https://int10h.org/oldschool-pc-fonts/fontlist/font?tandy1k-ii_200l

Note (2): gfx_showmenu is synchronous this blocks @gfx from running, but not the entire host

jpcima avatar Nov 12 '21 02:11 jpcima

Did you have a particular implementation plan here already? Maybe myself or someone else in the community could help out.

EDIT: Nevermind, they map to this: https://github.com/jpcima/ysfx/blob/8ef69f9d6bef85b1b7da7cfe66b9f777f536cf4c/thirdparty/WDL/source/WDL/eel2/eel_lice.h

I think the REAPER JSFX likely use SWELL, the WDL "Simple Windows Emulation Layer" This is the foundational cross-platform GFX and windowing lib that REAPER itself is built with.

IE, for gfx.rect(), that might map to this:

https://github.com/justinfrankel/WDL/blob/master/WDL/swell/swell-gdi-generic.cpp#L279-L290

https://github.com/justinfrankel/WDL/blob/9bcb9595e4dd0a6f8fde3a944a564bfe1d36e702/WDL/swell/swell-gdi-lice.cpp#L739-L762

I am no expert though

For the JUCE example of using REAPER's embedded UI stuff, I had to figure out to draw the LICE bitmaps that REAPER hands to the render method into a JUCE component.

To do that is: https://github.com/juce-framework/JUCE/blob/master/examples/Plugins/ReaperEmbeddedViewPluginDemo.h#L329-L384

void doPaint (reaper::REAPER_FXEMBED_IBitmap* bitmap)
    {
        if (bitmap == nullptr || drawInfo == nullptr || bitmap->getWidth() <= 0 || bitmap->getHeight() <= 0)
            return 0;

        Image img (juce::Image::PixelFormat::ARGB, bitmap->getWidth(), bitmap->getHeight(), true);
        Graphics g (img);

        Image::BitmapData imgData { img, Image::BitmapData::readOnly };
        const auto pixelsWidth = imgData.pixelStride * imgData.width;

        auto* px = bitmap->getBits();
        const auto rowSpan = bitmap->getRowSpan();
        const auto numRows = bitmap->getHeight();

        for (int y = 0; y < numRows; ++y)
            std::memcpy (px + (y * rowSpan), imgData.getLinePointer (y), (size_t) pixelsWidth);
    }

So maybe that eel_lice.h header can be re-used, and then a method like this used to draw it on the JUCE Editor component? (REAPER_FXEMBED_IBitmap = LICE_IBitmap)

GavinRay97 avatar Nov 19 '21 15:11 GavinRay97

Did you have a particular implementation plan here already?

Kind of yes, but it's not set in stone, there would be 2 ways to go at this problem.

  1. to import SWELL+Lice+eel_lice.h which implements all of gfx as software renderer There's a bit of practical difficulty make it build it though, not entirely as trivial as it sounds.

  2. to provide a library API which lets a user implement the entire graphics interface

only the method 2. would have the ability of accelerated rendering. (decent candidates for JUCE's graphics contexts, which can render both to GL and raster)

jpcima avatar Nov 19 '21 15:11 jpcima

only the method 2. would have the ability of accelerated rendering. (decent candidates for JUCE's graphics contexts, which can render both to GL and raster)

Probably better to do the second one, I've noticed you've also kept the core of it decoupled from JUCE which is nice given the flexibility of re-using it in other environments/context

GavinRay97 avatar Nov 20 '21 16:11 GavinRay97

Probably better to do the second one, I've noticed you've also kept the core of it decoupled from JUCE which is nice given the flexibility of re-using it in other environments/context

It's a goal, given that this lib is not only for plugins but also for hosts, starting with Carla.

A thing about these graphics is they are modeled after Windows GDI software rendering. I'd really like to get Lice in if that's possible. Some parts of Swell give problems (eg. the windowing which pulls the Gdk dep and it's desired to avoid this) It might get away with it, if linking it with a Swell subset which is restricted to the GDI code only. That remains to verify.

jpcima avatar Nov 20 '21 18:11 jpcima

There is success so far with getting Lice into the master branch. That means that drawing is going to be kept inside the library, which keeps the usage simple.

The library user should only provide the framebuffer of the window, and other information like mouse and key data, and Retina status.

jpcima avatar Nov 22 '21 10:11 jpcima

The library user should only provide the framebuffer of the window, and other information like mouse and key data, and Retina status.

What exactly is a framebuffer?

Is it something you can get from a window pointer/handle, like HWND on Windows or XID (now called Window I think) in X11 on Linux?

GavinRay97 avatar Nov 23 '21 16:11 GavinRay97

The framebuffer is an image that keeps the pixel data in RAM memory. The current master has it implemented, and shows how to use it together with a juce::Image.

This is checked working, which means the hardest work is done, next it will be about adding the graphics primitives. (which should be a copy-and-paste from eel_lice)

jpcima avatar Nov 23 '21 22:11 jpcima

The framebuffer is an image that keeps the pixel data in RAM memory. The current master has it implemented, and shows how to use it together with a juce::Image.

This is checked working, which means the hardest work is done, next it will be about adding the graphics primitives. (which should be a copy-and-paste from eel_lice)

We have liftoff! 🚀 Video clip below is incredible! Build was done ~5 minutes ago:

https://user-images.githubusercontent.com/26604994/143162633-b999bdb8-24bb-4b6a-88e0-64089a873235.mp4

https://user-images.githubusercontent.com/26604994/143163511-def1635f-98ae-4be1-8e23-632e7a01b0ea.mp4

GavinRay97 avatar Nov 24 '21 02:11 GavinRay97

Yes, I've added a few more right now but there still remains to do some major ones. All I make so far is a tiny example, and not tried any actual plugins. Any elaborate ones which are worth testing?

desc:000 gfx2

out_pin:out

@sample
spl=0.0;

@gfx 600 400
gfx_r=rand(1);
gfx_g=rand(1);
gfx_b=rand(1);

cx=rand(gfx_w);
cy=rand(gfx_h);
cr=rand(10)+5;
gfx_circle(cx, cy, cr, 1);

n=n+1;
(n<500)?(gfx_clear=-1.0):(gfx_clear=0.0;n=0);

jpcima avatar Nov 24 '21 03:11 jpcima

Any elaborate ones which are worth testing?

@JoepVanlier ("Saike") sets the bar for JSFX development IMO, his plugins are probably the golden standard as far as unit testing:

Big repo of them here:

  • https://github.com/JoepVanlier/JSFX

The ones under the Basics category would probably be good "first-goal" bars since they are more minimal in terms of UI than his other plugins:

  • https://github.com/JoepVanlier/JSFX/tree/master/Basics
    • The BandSplitter and BandJoiner plugin UI's seem to work mostly
    • The StereoBub series can render the dial UI's

There's also Geraint Luff's JSFX, which are fantastic:

  • https://github.com/geraintluff/jsfx
    • None of these seem to load UI's
    • It may be because he uses some unconventional structuring
    • IE: image

GavinRay97 avatar Nov 24 '21 03:11 GavinRay97

ReEQ is another one. https://github.com/Justin-Johnson/ReJJ

ghost avatar Nov 25 '21 07:11 ghost

Graphics are mostly working now, needs just these few features such as cursor and popup.

jpcima avatar Nov 29 '21 19:11 jpcima

The status of gfx is supposed to be now implemented 100%. The popup has needed a rewrite of gfx processing at plugin-side. More precisely, the @gfx is allowed to block indefinitely (by gfx_showmenu), and so it can't happen on the main UI thread except by creating a modal loop; as a solution, @gfx has been moved into a background thread.

jpcima avatar Dec 04 '21 10:12 jpcima