Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
Bazaar
Status & Roadmap
FAQ
Authors & License
Forums
Funding Ultimate++
Search on this site
Search in forums












SourceForge.net Logo
Home » U++ Library support » Draw, Display, Images, Bitmaps, Icons » How to efficiently update a large Image?
How to efficiently update a large Image? [message #22563] Mon, 27 July 2009 12:24 Go to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Hi,

I need to frequently update a small amount of pixels in a relatively large image (e.g. 1920x1200) and then draw the parts of image on screen. Unfortunately the following code is too slow:
ImageBuffer ib(image);
// .. here's the manipulation of the ib
image=ib;


Help, anybody?

(I used to have my own graphics layer implemented for GDI and X11 but they just badly broke down due to the recent Draw virtualization.)

// Tom
Re: How to efficiently update a large Image? [message #22564 is a reply to message #22563] Mon, 27 July 2009 13:39 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Tom1 wrote on Mon, 27 July 2009 06:24

Hi,

I need to frequently update a small amount of pixels in a relatively large image (e.g. 1920x1200) and then draw the parts of image on screen. Unfortunately the following code is too slow:
ImageBuffer ib(image);
// .. here's the manipulation of the ib
image=ib;


Help, anybody?




The question is why it is slow...

The first thing to try is

ib.SetKind(....)

(I guess it will be ib.SetKind(IMAGE_OPAQUE)).

this would prevent optimization scan.

Just for better undestanding, what is slow here? Are we speaking about milliseconds or second?

Quote:


(I used to have my own graphics layer implemented for GDI and X11 but they just badly broke down due to the recent Draw virtualization.)

// Tom


In most cases, dynamic_cast<SystemDraw *> makes original code work just fine (SystemDraw has all interface and attributes of former Draw).

Mirek
Re: How to efficiently update a large Image? [message #22566 is a reply to message #22564] Mon, 27 July 2009 16:47 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Hi Mirek,

SetKind(IMAGE_OPAQUE) gives marginal improvement.

Now I see the visit from Image to ImageBuffer and back to Image does not take more than about 1.5 ms. However, after this visit the DrawImage does something more than usual and becomes slow. It takes about 30 ms to update a small stripe of an image using DrawImage whereas without the ImageBuffer visit, this takes less than a millisecond.

I also noticed that the application allocates and deallocates several megabytes worth of memory when running probably because of what is happening around this image or imagebuffer.

--

Is there any way to directly update the Image pixel contents without ImageBuffer and the associated DrawImage overhead?

(I need to get familiar with the dynamic_cast thing soon... It was not quite as easy as I had hoped it would be.)

Thanks,

Tom
Re: How to efficiently update a large Image? [message #22569 is a reply to message #22566] Mon, 27 July 2009 17:15 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Tom1 wrote on Mon, 27 July 2009 10:47

Hi Mirek,

SetKind(IMAGE_OPAQUE) gives marginal improvement.

Now I see the visit from Image to ImageBuffer and back to Image does not take more than about 1.5 ms. However, after this visit the DrawImage does something more than usual and becomes slow. It takes about 30 ms to update a small stripe of an image using DrawImage whereas without the ImageBuffer visit, this takes less than a millisecond.



It is because the raster needs to be converted to windows object (HBMP) that then can be painted on the screen.

That said, Image is really optimized for thousands of small images, not single big one...

That said (2), there is SetSurface function (which is sort of semi-public), maybe it can be helpful somehow in this case.

Another option is to divide the big image into many smaller ones.

Quote:


I also noticed that the application allocates and deallocates several megabytes worth of memory when running probably because of what is happening around this image or imagebuffer.



Most like HBMP associated data.

Quote:


Is there any way to directly update the Image pixel contents without ImageBuffer and the associated DrawImage overhead?



No, but I believe it should not really be a problem. Image->ImageBuffer->Image is extremely fast. The problem is that it is necessarry to move those pixels to GDI afterwards.

Mirek
Re: How to efficiently update a large Image? [message #22570 is a reply to message #22569] Mon, 27 July 2009 17:18 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
P.S.: In Bazaar, there is RasterCtrl package and RasterCtrlTest. AFAIK, these are classes for handling extremely large images. Maybe it could help.

Mirek
Re: How to efficiently update a large Image? [message #22581 is a reply to message #22570] Tue, 28 July 2009 11:16 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Hi Mirek,

Thanks for the analysis. The SetSurface note was especially helpful. Namely, it appears to use the same GDI function I used in my own graphics library and therefore it should give the performance I need.

I noticed the SetSurface(Draw& w, ..) in Image.h should probably be updated to SetSurface(SystemDraw& w,.. ) to work.

--

The SetSurface() also appears to be the far most efficient way to put ImageBuffers (e.g. the ones created with Painter) on screen.

That in mind, may I suggest adding a version of SetSurface that uses a source rectangle within the image buffer and a target rectangle for the control, and can therefore optimally update a smaller area of a Control.

Something like:

SetSurface(SystemDraw& w, Rect src, Rect target, Size sz, RGBA *pixelbuffer)

This would further boost the Painter when used on screen as the ImageBuffer would need to be updated only when the content changes and the Paint routine would update the screen using the current buffer contents and only update the areas required by Paint.

Thanks for your help Mirek,

Tom
Re: How to efficiently update a large Image? [message #22587 is a reply to message #22581] Tue, 28 July 2009 16:29 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Tom1 wrote on Tue, 28 July 2009 05:16


SetSurface(SystemDraw& w, Rect src, Rect target, Size sz, RGBA *pixelbuffer)



If you can provide X11 and Win32 versions, I would be happy to add it.

Anyway, now thinking, maybe it should be Draw& after all, because that is what we have in Paint, with runtime check that it is in fact SystemDraw...

Mirek
Re: How to efficiently update a large Image? [message #22588 is a reply to message #22587] Tue, 28 July 2009 16:32 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Even better idea:

SetSurface that is optimized as current one if given Draw is SystemDraw (and the target is screen), but uses Image for all other cases (e.g. printer).

Mirek
Re: How to efficiently update a large Image? [message #22597 is a reply to message #22588] Wed, 29 July 2009 09:18 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Hi Mirek,

Here's the raw GDI code for SetSurface with added flexibility:

void SetSurface(HDC dc, Rect &dest, Point &srcoff, Size &bufsz, const RGBA *pixels){
	GuiLock __;
	BitmapInfo32__ bi(bufsz.cx, bufsz.cy);
	::SetDIBitsToDevice(dc,dest.left,dest.top,dest.Width(),dest.Height(),srcoff.x,-srcoff.y-dest.Height()+bufsz.cy,0,bufsz.cy,pixels, bi, DIB_RGB_COLORS);
}

void SetSurface(SystemDraw &w, Rect &dest, Point &srcoff, Size &bufsz, const RGBA *pixels){
	SetSurface(w.GetHandle(),dest,srcoff,bufsz,pixels);
}


I'm not quite confident I can reproduce this in X11 in a truly powerful way, so I hope someone with solid X11 knowledge will pick it up.

// Tom
Re: How to efficiently update a large Image? [message #22633 is a reply to message #22597] Sun, 02 August 2009 15:11 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
OK, I have added X11 variant and via-Image variant (for non-screen targets), maybe changing parameters a little.

According to my benchmarks, using optimized SetSurface is about 2.5x faster than using Image.

Thanks for the idea.

Mirek

[Updated on: Sun, 02 August 2009 18:55]

Report message to a moderator

Re: How to efficiently update a large Image? [message #22899 is a reply to message #22633] Wed, 26 August 2009 07:57 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Hi Mirek,

Sorry for the delay in my response -- been out of the office for a while.

Anyway, thanks for adding this functionality. In my application it really gives me far more that 2.5x speed boost.

Unfortunately, there is still some problem with X11. Generally, the new SetSurface works OK with partial updates and offsets, but when dragging another window over a window with image contents updated with SetSurface, the view gets severely scrambled.

virtual void Paint(Draw &draw){
  // The image to draw is in ImageBuffer ib;
  Rect paintrect=draw.GetPaintRect();
  SetSurface(draw,paintrect,ib,ib.GetSize(),Point(paintrect.left,paintrect.top));
}


With GDI it's working correctly.

// Tom
Re: How to efficiently update a large Image? [message #22911 is a reply to message #22899] Fri, 28 August 2009 00:24 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Tom1 wrote on Wed, 26 August 2009 01:57

Hi Mirek,

Sorry for the delay in my response -- been out of the office for a while.

Anyway, thanks for adding this functionality. In my application it really gives me far more that 2.5x speed boost.

Unfortunately, there is still some problem with X11. Generally, the new SetSurface works OK with partial updates and offsets, but when dragging another window over a window with image contents updated with SetSurface, the view gets severely scrambled.



I am sorry, but I have trouble reproducing the problem.

My current testcase:

#include <CtrlLib/CtrlLib.h>

using namespace Upp;

struct MyApp : TopWindow {
	RGBA pixels[65536];

	virtual void Paint(Draw& w) {
		Rect r = w.GetPaintRect();
		w.DrawRect(r, LtGray());
		Point p = r.TopLeft();
		if(p.x < 256 && p.y < 256)
			SetSurface(w, r, pixels, Size(256, 256), p);
	}

	MyApp() {
		RGBA *t = pixels;
		for(int x = 0; x < 256; x++)
			for(int y = 0; y < 256; y++) {
				int d = (x - 128) * (x - 128) + (y - 128) * (y - 128);
				RGBA c = Black();
				if(d > 120 * 120)
					c = White();
				if(d < 120 * 120)
					c.r = 255 - min(255 * d / (120 * 120), 255);
				*t++ = c;
			}
	}
};

GUI_APP_MAIN
{
	MyApp().Run();
}


Could you create some testcase that shows the problem?

Mirek


virtual void Paint(Draw &draw){
  // The image to draw is in ImageBuffer ib;
  Rect paintrect=draw.GetPaintRect();
  SetSurface(draw,paintrect,ib,ib.GetSize(),Point(paintrect.left,paintrect.top));
}


With GDI it's working correctly.

// Tom
[/quote]
Re: How to efficiently update a large Image? [message #22944 is a reply to message #22911] Tue, 01 September 2009 08:24 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Mirek,

Here's the testcase. Only after running your test code I realized the problem was related to having a control inside the main window. So, I guess this has something to do with the control offset within the parent control.

#include <CtrlLib/CtrlLib.h>
#include <Painter/Painter.h>

using namespace Upp;

class PainterCtrl : public Ctrl {
	ImageBuffer ib;

public:
	virtual void Paint(Painter &pntr,Size &sz)=0;

	virtual void Layout(){
		Size sz=GetSize();
		ib.Clear();
		ib.Create(sz);
		BufferPainter pntr(ib);
		Paint(pntr,sz);
	}

	virtual void Paint(Draw &draw) {
		Rect paintrect=draw.GetPaintRect();
		
		Point p = paintrect.TopLeft();
		if(p.x < ib.GetSize().cx && p.y < ib.GetSize().cy) 
			SetSurface(draw, paintrect, ib, ib.GetSize(), p);
	}
	
};

struct ExampleCtrl : PainterCtrl {
	
	virtual void Paint(Painter &pntr,Size &sz){
		pntr.Move(0,0).Line(sz.cx,0).Line(sz.cx,sz.cy).Line(0,sz.cy).Close().Fill(White()).Stroke(5,Black());
		pntr.Move(0,0).Line(sz.cx,sz.cy).Stroke(5,Black());           
	}
};

class ExampleTopWindow: public TopWindow{
public:
	ExampleCtrl ec;
	
	virtual void Layout(){
		ec.SetRect(20,0,GetSize().cx-20,GetSize().cy);
	}

	ExampleTopWindow(){
		Add(ec);
	}
};

GUI_APP_MAIN
{
   ExampleTopWindow win;
   win.Sizeable();
   win.Open();
   win.Run();
}


(This example also sort of demonstrates how to boost application performance when drawing complex items with e.g. Painter and then not having to regenerate them each time the window gets an OS initiated paint request.)

// Tom
Re: How to efficiently update a large Image? [message #22950 is a reply to message #22944] Wed, 02 September 2009 11:37 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Tom1 wrote on Tue, 01 September 2009 02:24

Mirek,

Here's the testcase. Only after running your test code I realized the problem was related to having a control inside the main window. So, I guess this has something to do with the control offset within the parent control.

#include <CtrlLib/CtrlLib.h>
#include <Painter/Painter.h>

using namespace Upp;

class PainterCtrl : public Ctrl {
	ImageBuffer ib;

public:
	virtual void Paint(Painter &pntr,Size &sz)=0;

	virtual void Layout(){
		Size sz=GetSize();
		ib.Clear();
		ib.Create(sz);
		BufferPainter pntr(ib);
		Paint(pntr,sz);
	}

	virtual void Paint(Draw &draw) {
		Rect paintrect=draw.GetPaintRect();
		
		Point p = paintrect.TopLeft();
		if(p.x < ib.GetSize().cx && p.y < ib.GetSize().cy) 
			SetSurface(draw, paintrect, ib, ib.GetSize(), p);
	}
	
};

struct ExampleCtrl : PainterCtrl {
	
	virtual void Paint(Painter &pntr,Size &sz){
		pntr.Move(0,0).Line(sz.cx,0).Line(sz.cx,sz.cy).Line(0,sz.cy).Close().Fill(White()).Stroke(5,Black());
		pntr.Move(0,0).Line(sz.cx,sz.cy).Stroke(5,Black());           
	}
};

class ExampleTopWindow: public TopWindow{
public:
	ExampleCtrl ec;
	
	virtual void Layout(){
		ec.SetRect(20,0,GetSize().cx-20,GetSize().cy);
	}

	ExampleTopWindow(){
		Add(ec);
	}
};

GUI_APP_MAIN
{
   ExampleTopWindow win;
   win.Sizeable();
   win.Open();
   win.Run();
}


(This example also sort of demonstrates how to boost application performance when drawing complex items with e.g. Painter and then not having to regenerate them each time the window gets an OS initiated paint request.)

// Tom


Thanks for testcase. You are right, the cause was that offset of child widget was not taken into account.

Hopefully fixed now.

Mirek
Re: How to efficiently update a large Image? [message #22968 is a reply to message #22950] Thu, 03 September 2009 11:45 Go to previous message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Thanks Mirek, it works beautifully now!

// Tom
Previous Topic: Color menu choice
Next Topic: Support for 2bpp tiff images
Goto Forum:
  


Current Time: Fri Apr 19 02:50:04 CEST 2024

Total time taken to generate the page: 0.03931 seconds