Component repaint problem

6 posts / 0 new
Last post
loca
Offline
Last seen: 2 weeks 5 days ago
Joined: 26 Nov 2008 - 21:10
Component repaint problem

Hi there,

I have a problem with some Components and the paint function. I'll try to explain it easily:

I have a big component (TestComposite) that contains an Array of components (Test).

Each Test component contains 3 components inside:
SideBar* leftSideBar;
MiddleBar* middleBar;
SideBar* rightSideBar;

I have a Value::Listener that when it detects a ValueChanged it redraws the MiddleBar. I want it to
repaint ONLY the middlebar because to redraw each sidebar is a hard proccess.

The problem is that if my big component (TestComposite) has more than one Test component, although I only call
middleBar->repaint(); it repaints the sidebars too.

Is weird because on the first Test it doesn't repaint the leftSideBar,
and in the last Test it doesn't repaint the rightSideBar

I include the code I'm trying and some screenshots to help understanding.

Is really important for me to avoid the sidebars redrawing each time cause as I said is a hard proccess.

/////////////////////////////////////////////////////////////////////////////////////////////////////
// My example classes

class SideBar: public Component
{
	
public:
	SideBar() { 
		setOpaque(true);
		setSize(5,20); 
	}
	
	virtual void paint(Graphics& g) {
		printf("\n\tSideBar::paint getWidth() %d getHeight() %d", getWidth(), getHeight());
		g.fillAll(Colours::red);
	}
	
};

class MiddleBar: public Component//, public Value::Listener
{
	
public:
	MiddleBar() { 
		setOpaque(true);
		setSize(10,20); 
	}
	
	virtual void paint(Graphics& g) {
		printf("\n\tMiddleBar::paint %d getHeight() %d", getWidth(), getHeight());
		g.fillAll(Colours::blue);
	}
	
	/*
	virtual void 	valueChanged (Value &value)
	{
		printf("\n\t\tMiddleBar::valueChanged");
		repaint();
	}
	 */
	
};

class Test: public Component, public Value::Listener
{
	
private:
	
	SideBar*	leftSideBar;
	MiddleBar*	middleBar;
	SideBar*	rightSideBar;
	
	Value		value;
	
public:
	Test(Value & val) : value(val) {
		setOpaque(true);
		
		addAndMakeVisible(leftSideBar = new SideBar());
		addAndMakeVisible(middleBar = new MiddleBar());
		addAndMakeVisible(rightSideBar = new SideBar());			
		
		int offset = 10;
		
		leftSideBar->setBounds(offset, offset, leftSideBar->getWidth(), leftSideBar->getHeight());
		middleBar->setBounds(leftSideBar->getWidth() + offset*2, offset, middleBar->getWidth(), middleBar->getHeight());
		rightSideBar->setBounds(leftSideBar->getWidth() + offset*3 + middleBar->getWidth() + offset, offset, rightSideBar->getWidth(), rightSideBar->getHeight());
			
		setSize(100, 100);
		value.addListener(this);
	}
	
	virtual void paint(Graphics& g) {
		printf("\nTest::paint");
		g.fillAll(Colours::green);
	}
	
	
	virtual void 	valueChanged (Value &value)
	{
		printf("\n\t\tTest::valueChanged");
		middleBar->repaint();
	}
	
};

class TestComposite: public Component
{
	
private:
	Array<Test*> components;
	
	
public:
	
	TestComposite() {
		setOpaque(true);
		setSize(400, 200);
	}
	
	void add(Test *test)
	{
		components.add(test);
		addAndMakeVisible(test);
		if (components.size() > 0)
		{
			setSize((components[0]->getWidth()+20)*(components.size()+1), components[0]->getHeight()+20*2);
		}
	}
	
	virtual void resized()
	{
		printf("\nTestComposite::resized %d getHeight() %d", getWidth(), getHeight());
		
		int xOffset = 20;
		for (int i=0; i<components.size(); i++)
		{
			components[i]->setBounds(xOffset, 20, components[i]->getWidth(), components[i]->getHeight());
			xOffset += components[i]->getWidth()+20;
		}
	}
	
	virtual void paint(Graphics& g) {
		printf("\nTestComposite::paint");
		g.fillAll(Colours::yellow);
	}
	
};

/////////////////////////////////////////////////////////////////////////////////////////////////////
// how I create the components in the juce app

Value values[4];

....

	addAndMakeVisible(mTestComposite = new TestComposite());
	mTestComposite->setBounds(0,0, 200, 200);
	
	Test* test0 = new Test(values[0]);
	mTestComposite->add(test0);
	Test* test1 = new Test(values[1]);
	mTestComposite->add(test1);	
	Test* test2 = new Test(values[2]);
	mTestComposite->add(test2);	
	Test* test3 = new Test(values[3]);
	mTestComposite->add(test3);	
	
////////////////////////////////////////////////////////////////////////////////////////////////////
// How I update the values on a timer callback

void nSMonitorSectionContentComponent::timerCallback()
{
	printf("\n\n\n--------------------------------------\nSMonitorSectionContentComponent::timerCallback\n");
	for(int i = 0; i < 4; i++)
	{
		int n = mRand.nextInt(100);
		if (n%2) values[i].setValue(((float)values[i].getValue())+1);
		else values[i].setValue(((float)values[i].getValue())-1);
	}
}
	

The TestComposite is the yellow bit
Each Test component is the green bits
The SideBars are red
The MiddleBar are blue

I only want to update the blue bits :'( but you can see the output in the image...

Thanks
A

attachment: 

Edited by: user1 on 15 Aug 2013 - 01:38
jules
Offline
Last seen: 1 hour 5 min ago
Joined: 29 Apr 2013 - 18:37
Re: Component repaint problem

Have you tried using JUCE_ENABLE_REPAINT_DEBUGGING to see exactly what clip region is being drawn?

loca
Offline
Last seen: 2 weeks 5 days ago
Joined: 26 Nov 2008 - 21:10
Re: Component repaint problem

Hi Jules,

thanks for the fast reply! I just tried but I don't know what the conclusions are...
I comment all my g.fillAll(xxx) to see your colours better

The only bit that I see that change each 1 second (my timer period to change values) are the bits where the middleBars are (the other colours keep the same unless i move the window or something like that)... so I don't know what's going on...

Although I only see that bits changing, I can see in the output how the draw function is being call for the sidebars as well...

--------------------------------------
SMonitorSectionContentComponent::timerCallback

		Test::valueChanged
		Test::valueChanged
		Test::valueChanged
		Test::valueChanged
TestComposite::paint
Test::paint
	MiddleBar::paint 10 getHeight() 20
	SideBar::paint getWidth() 5 getHeight() 20
Test::paint
	SideBar::paint getWidth() 5 getHeight() 20
	MiddleBar::paint 10 getHeight() 20
	SideBar::paint getWidth() 5 getHeight() 20
Test::paint
	SideBar::paint getWidth() 5 getHeight() 20
	MiddleBar::paint 10 getHeight() 20
	SideBar::paint getWidth() 5 getHeight() 20
Test::paint
	SideBar::paint getWidth() 5 getHeight() 20
	MiddleBar::paint 10 getHeight() 20
TestComposite::paint
Test::paint
	SideBar::paint getWidth() 5 getHeight() 20
	MiddleBar::paint 10 getHeight() 20
	SideBar::paint getWidth() 5 getHeight() 20
Test::paint
	SideBar::paint getWidth() 5 getHeight() 20
	MiddleBar::paint 10 getHeight() 20
	SideBar::paint getWidth() 5 getHeight() 20
Test::paint
	SideBar::paint getWidth() 5 getHeight() 20
	MiddleBar::paint 10 getHeight() 20
	SideBar::paint getWidth() 5 getHeight() 20
Test::paint
	SideBar::paint getWidth() 5 getHeight() 20
	MiddleBar::paint 10 getHeight() 20
	SideBar::paint getWidth() 5 getHeight() 20

Any idea?
A

attachment: 
jules
Offline
Last seen: 1 hour 5 min ago
Joined: 29 Apr 2013 - 18:37
Re: Component repaint problem

Interesting.. You've managed to hit a very obscure problem/bug in CoreGraphics.

Imagine you have a complex clip region that contains two visible regions at (0, 0, 10, 10) and (50, 0, 10, 10), and you intersect this with (20, 0, 10, 10). You should end up with an empty clip region. But CoreGraphics thinks that the result has the bounding box (20, 0, 10, 10), which is the intersection of the original bounding box (0, 0, 60, 10) and the new area (20, 0, 10, 10). Since CoreGraphics provides no way of asking whether the current clip region is empty other than getting its bounding box, there's no way to correctly determine whether the intersection operation has really created an empty clip region.

The upshot of that is that when you're rendering a complex region like in your example, and there are components that lie in-between the areas that are getting drawn, but which don't actually overlapping them, the mechanism for determining whether these components should be painted will give some false positives. No actual harm done, because everything will still look correct, but it leads to some extra work.

As for fixes.. not easy! You could use my software renderer instead of the CoreGraphics renderer, and that'll probably work correctly. Or just make these components repaint efficiently! (Try Component::setBufferedToImage perhaps?)

loca
Offline
Last seen: 2 weeks 5 days ago
Joined: 26 Nov 2008 - 21:10
Re: Component repaint problem

Hola Jules,

I tried with your idea of
#define USE_COREGRAPHICS_RENDERING 0
and it worked!!!... at least with the "simple" example I sent you. With my real code I'm having a problem because now is not painting the sides components unless they have a background :twisted:

Anyway, I'll have a look at it because I think is my bug now...

Thanks for your help,
A

RiphRaph
Offline
Last seen: 6 months 4 weeks ago
Joined: 12 Oct 2010 - 10:11
Re: Component repaint problem

Hi,
I've got a similar problem with my audio plugin.
One of my component (a vu-meter) is opaque but still paints the main component (only on Mac).
This yields very high CPU pics with the RTAS format.
If I define USE_COREGRAPHICS_RENDERING to 0, the performance gets much better however
the comboBox are not as well drawn as it was bedore : text is not centered, and is a bit blurred.
Has anybody an idea of what's going on and how to fix it?
Thanks
Raph