Drawing a PNG background behind a Knob's SVG layer using addChildBelow()?

Hello,

I’m in over my head a little bit and could use some guidance. Here’s a piece of code that creates a knob overlay (SVG), then layers a red square beneath it:

template <typename TBase = VoxglitchKnob>
struct TLargeKnob : TBase
{
  TLargeKnob() {
    
    this->svgFile = "large_knob_overlay.svg";
    this->setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/" + this->svgFile)));
  
    // Draw big red square behind svg layer
    widget::SvgWidget* bg = new widget::SvgWidget;
    bg->setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/redsquare.svg")));
    this->fb->addChildBelow(bg, this->tw);
  }
};
typedef TLargeKnob<> VoxglitchLargeKnob;

And here’s what it looks like: image

So far, so good.

What I’m trying to achieve is to draw a PNG file behind the SVG, but I’m having no luck so far. I have created a widget for displaying PNG files that, by itself, works. It looks like this:

struct PNGPanel : TransparentWidget
{
  std::string png_file_path;
  float width;
  float height;
  float alpha = 1.0;

  PNGPanel(std::string png_file_path, float width, float height, float alpha = 1.0)
  {
    this->png_file_path = png_file_path;
    this->width = width;
    this->height = height;
    this->alpha = alpha;
  }

  void draw(const DrawArgs &args) override
  {
    std::shared_ptr<Image> img = APP->window->loadImage(asset::plugin(pluginInstance, this->png_file_path));

    int temp_width, temp_height;

    // Get the image size and store it in the width and height variables
    nvgImageSize(args.vg, img->handle, &temp_width, &temp_height);

    // Set the bounding box of the widget
    box.size = mm2px(Vec(width, height));

    // Paint the .png background
    NVGpaint paint = nvgImagePattern(args.vg, 0.0, 0.0, box.size.x, box.size.y, 0.0, img->handle, this->alpha);
    nvgBeginPath(args.vg);
    nvgRect(args.vg, 0.0, 0.0, box.size.x, box.size.y);
    nvgFillPaint(args.vg, paint);
    nvgFill(args.vg);

    Widget::draw(args);
  }
};

I thought… OK, maybe I can simply use that as my bottom layer? So I tried this:

template <typename TBase = VoxglitchKnob>
struct TLargeKnob : TBase
{
  TLargeKnob() {
    // Same as before, set the SVG overlay for the knob
    this->svgFile = "large_knob_overlay.svg";
    this->setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/" + this->svgFile)));

    // Try adding the PNGPanel to the background
    PNGPanel* bg = new PNGPanel(asset::plugin(pluginInstance, "res/components/png/Big-Knob.png"), 4.0, 4.0);
    this->fb->addChildBelow(bg, this->tw);
  }
};
typedef TLargeKnob<> VoxglitchLargeKnob;

This doesn’t work. In fact, when I try this, neither the SVG nor the PNG are displayed.

Any suggestions?

-=-=-=-

Let me provide a bit of background. Here’s an example front panel:

image

I’m using a PNG file for the background of the module. The PNG file looks like this:

image

Each knob has an SVG overlay which gives the appearance of the knob being turned. For the large knob, this looks like:

image

However, having the background part of the knob “baked in” to the background means that each module needs their own a custom PNG background. That’s pretty inefficient, considering that the background texture is 90% similar across all modules.

Instead of that strategy, I’d like to have one, single, larger background PNG that all of the modules use. This would

  1. Reduce the size of the distribution file and
  2. Make it trivial to swap in a new background texture.
  3. Maybe be more efficient for VCV Rack? (I’m not 100% sure of that.)

I’m working on a new theme system, so I’m spending some extra time to make things flexible.

(Panel design by Chris Corrado.)

4 Likes

Hold on! I think I have it working. I’ll post a follow up in a few minutes.

NOPE. Still floundering.

WOOO HOO! I got it working! :grinning: :star2: :sparkler:

I had to use addChildBottom(bg); instead of addChildBelow()

Here’s what’s working for me:

template <typename TBase = VoxglitchKnob>
struct TLargeKnob : TBase
{
  PNGPanel* bg;

    TLargeKnob() {

      // Add svg forground
	  this->svgFile = "large_knob_overlay.svg";
	  this->setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/" + this->svgFile)));

      // Add PNG background
      bg = new PNGPanel(asset::plugin(pluginInstance, "res/components/png/Big-Knob.png"), 22.0, 24.0, 1.0);
      this->addChildBottom(bg);  // << this is what I was missing
    }
};
5 Likes

I wanted to follow up with some more information. If the background PNG file doesn’t align to where you need it, you can use the setPosition() method to reposition it. Here’s an example:

bg = new PNGPanel(asset::plugin(pluginInstance, "res/components/png/Big-Knob.png"), 22.0, 24.0, 1.0);
this->addChildBottom(bg);
bg->setPosition(Vec(-4.0, -4.0));
4 Likes