How to work without a draw( ); loop in C4 - c4

So I'm coming from a bit of a processing background (albiet not much) where I'm used to working with a draw loop that runs over and over when the app is running.
I can't seem to find something similar in xcode and C4. Is there anything like that anyone can suggest?
What I'm basically doing is just creating a bouncing ball app using C4 vectors as properties in a custom class
Here is my custom class Header/Implementation file:
#interface splitSquare : C4Shape
#property C4Vector *accel;
#property C4Vector *velocity;
#property C4Vector *location;
-(id)initWithPoint:(CGPoint)point;
#end
-(id)initWithPoint:(CGPoint)point{
self = [super init];
if (self){
CGRect frame = CGRectMake(0, 0, 50, 50);
[self ellipse:frame];
self.lineWidth = 0.0f;
self.fillColor = [UIColor colorWithRed:[C4Math randomInt:100]/100.0f
green:[C4Math randomInt:100]/100.0f
blue:0.5f
alpha:1.0f];
_location = [C4Vector vectorWithX:point.x Y:point.y Z:0.0f];
self.origin = _location.CGPoint;
_accel = [C4Vector vectorWithX:0.04f Y:0.05f Z:0.0f];
_velocity = [C4Vector vectorWithX:0.004f Y:0.0005f Z:0.0f];
C4Log(#"The current value of velocity x is %f and y is %f", _velocity.x, _velocity.y);
}
I'm doing something pretty simple (adding accel to velocity and velocity to location, then assign location to the shapes origin). In the main C4WorkSpace I have this code:
#interface C4WorkSpace()
-(void)updateVectors;
#end
#implementation C4WorkSpace
{
splitSquare *testShape;
C4Timer *timer;
}
-(void)setup {
testShape = [[splitSquare alloc] initWithPoint:point2];
[self.canvas addShape:testShape];
testShape.animationDuration = 0.25f;
timer = [C4Timer automaticTimerWithInterval:0.25f target:self method:#"updateVectors" repeats:YES];
}
-(void)updateVectors{
//accel add to vel, vel added to location
[testShape.velocity add:testShape.accel];
[testShape.location add:testShape.velocity];
testShape.origin = testShape.location.CGPoint;
testShape.fillColor = [UIColor colorWithRed:[C4Math randomInt:100]/100.0f
green:[C4Math randomInt:100]/100.0f
blue:0.5f
alpha:1.0f];
}
#end
So this is what I'm doing now with a C4 timer getting called, but I feel like there has to be a more elegant way to do this. Right now the animation is jumpy and although it doesn't throw an error it doesn't seem to work properly (every time I run the iOS simulator it runs for a second then jumps back into xcode and shows me the current thread).
Any/all suggestions would be awesome thanks!

You're actually right on track with using a timer to create an update loop. Instead of having an entire update for the screen, C4 updates only the content it needs to update at any given time. This is why there's no draw loop like in other apis. A major reason for this is to be lighter on system resources (i.e. less drawing / less updating = longer battery life for mobile devices).
To address your issues above, I've slightly modified the code you sent over.
Here's the splitSquare header:
#import "C4Shape.h"
#interface splitSquare : C4Shape
#property C4Vector *accel;
#property C4Vector *velocity;
#property C4Vector *location;
-(id)initWithPoint:(CGPoint)point;
-(void)beginUpdating;
#end
NOTE: I added a beginUpdating method to the header.
Here's the splitSquare implementation:
#import "splitSquare.h"
#implementation splitSquare {
C4Timer *updateVectorTimer;
}
-(id)initWithPoint:(CGPoint)point{
self = [super init];
if (self){
CGRect frame = CGRectMake(0, 0, 50, 50);
[self ellipse:frame];
self.lineWidth = 0.0f;
self.fillColor = [UIColor colorWithRed:[C4Math randomInt:100]/100.0f
green:[C4Math randomInt:100]/100.0f
blue:0.5f
alpha:1.0f];
_location = [C4Vector vectorWithX:point.x Y:point.y Z:0.0f];
self.origin = _location.CGPoint;
_accel = [C4Vector vectorWithX:0.04f Y:0.05f Z:0.0f];
_velocity = [C4Vector vectorWithX:0.004f Y:0.0005f Z:0.0f];
C4Log(#"The current value of velocity x is %f and y is %f",
_velocity.x,
_velocity.y);
}
return self;
}
-(void)beginUpdating {
updateVectorTimer = [C4Timer automaticTimerWithInterval:1/60.0f
target:self
method:#"updateVectors"
repeats:YES];
}
-(void)updateVectors{
//accel add to vel, vel added to location
[self.velocity add:self.accel];
[self.location add:self.velocity];
self.origin = self.location.CGPoint;
self.fillColor = [UIColor colorWithRed:[C4Math randomInt:100]/100.0f
green:[C4Math randomInt:100]/100.0f
blue:0.5f
alpha:1.0f];
}
#end
NOTE: I added the timer directly to the splitSquare object.
The main thing for making your animation smoother happens in the beginUpdating method when you create the update timer.
Originally, you were animating for 0.25 seconds before updating and triggering the timer every 0.25 seconds as well. I've changed the animation duration so that it is immediate (0.0f seconds).
I then changed the update timer to run at 60fps by triggering the timer at '1/60.0f' seconds so that the animation is smoother.
I made an alternative implementation (just to show that it can be done this way as well) by adding the animation and timer to the splitSquare method itself. The C4WorkSpace changed to the following:
#import "C4WorkSpace.h"
#import "splitSquare.h"
#implementation C4WorkSpace {
splitSquare *testShape;
}
-(void)setup {
testShape = [[splitSquare alloc] initWithPoint:CGPointMake(100,100)];
[self.canvas addShape:testShape];
[testShape beginUpdating];
}
#end
Instead of managing the animation of the object itself, the workspace simply starts tells the object to beginUpdating.
A bit about what's going on.
One of the main differences, as you know, is that there is no default draw loop. Part of the reason for this is that as the developer you're almost never really dealing with the actual drawing/rendering of objects to the screen. When you create a visual object and add that to the canvas, the rendering is done by the system itself so that drawing is much more efficient on system resources. This is the ideal way of dealing with media not only in C4 but also in iOS programming.
Not having a draw loop is definitely different and takes some getting used to. But, it's really quite a nice way of working.
In essence, the draw-loop in other apis gets triggered by something like a timer every 30 or 60 seconds. So, adding a timer the way you did to update your shape is exactly what you want to do if you need loop-like functionality.
The nice thing about NOT having a timer in C4 is that you can simply add one to your workspace to control the firing of updates. BUT an even nicer thing is that you can add timers to individual objects so that they can update themselves. This means that you can also have multiple timers in multiple objects updating at different times / delays to create a more dynamic set of actions than you could ever get with only a single running loop.

Related

Qwt plot is not reploting

I have a Qwt plot defined in a class method:
plot = new QwtPlot();
const int margin = 5;
plot->setContentsMargins( margin, margin, margin, 0 );
plot->setTitle( "Support polygon" );
plot->setCanvasBackground( Qt::white );
plot->setAxisScale( QwtPlot::yLeft, -0.8,0.8 );
plot->setAxisScale( QwtPlot::xBottom, -0.8,0.8 );
QBoxLayout *layout = new QBoxLayout(QBoxLayout::LeftToRight);
layout->addWidget(plot);
setLayout(layout);
curve_ = new QwtPlotCurve();
curve_->attach( plot );
xData = new double[4];
yData = new double[4];
QTimer *replotTimer_ = new QTimer(this);
connect(replotTimer_, SIGNAL(timeout()), this, SLOT(updateMe_()));
replotTimer_->start(100);
the data is updated when the timer calls the callback function updateMe_() and the plot should be updated
void Support_polygon::updateMe_()
{
curve_->setRawSamples(xData,yData,4);
plot->replot();
}
xData and yData are also being modified in a thread, whose callback function is:
void Support_polygon::callback_()
{
msg_mutex.lock();
for (size_t ii=0; ii<msg.contacts.size(); ii++)
{
xData[ii] = 1.4f*float(std::rand())/float(RAND_MAX)-0.7;
yData[ii] = 1.4f*float(std::rand())/float(RAND_MAX)-0.7;
}
msg_mutex.unlock();
}
(right now I'm only putting random numbers, but when this works the data will be passed by a ROS message, that's why is in a different thread)
The problem is that the plot is never updated. As if replot() is never called. I tested and all callback functions are being called.
Surprisingly, the plot gets updated if I resize the window... if I keep re-sizing, the plot gets updated while I do it.
The replot call is being done on the main thread by the timer timeout signal. I don't understand what is going on.
Even though in the documentation it says that setAutoReplot is not recommended and that performance wise is better to use replot, I tested setting setAutoReplot to true
plot->setAutoReplot( true );
removed the replot call in updateMe_(), and everything works.
But to me this seems like a bug.
Note: I'm using Qwt 6.1.2.
note: after many more issues with qwt I switched to qtcustomplot. Only one header and one cpp file. My two cents in case anyone is looking for a substitute.

Cocos3d: Crashing when loading scene in separate thread or background thread:

I am trying to load several big models in code and show the scenes. Generally, it is taking long time for loading and showing scene on screen as it need to extract lot of resources from a pod model.
So, i thought of populate the first scene in main thread and remaining others in a separate thread. But, it is crashing when i move my part code into separate thread. Here is my sample code:
-(void) loadFirstScene
{
CC3PODResourceNode* podRezNode = [CC3PODResourceNode nodeWithName: #"FirstModel"];
podRezNode.resource = [IntroducingPODResource resourceFromFile: #"FirstModel.pod"];
podRezNode.shouldCullBackFaces = NO;
podRezNode.location = cc3v(0.0, -10.0, 0.2);
podRezNode.isTouchEnabled = YES;
[self addChild: podRezNode];
[NSThread detachNewThreadSelector:#selector(loadScenesInThread) toTarget:self
withObject:nil];
}
// Crashing if i add the below function in separate thread or background thread
-(void) loadScenesInThread
{
CC3PODResourceNode* podRezNode = [CC3PODResourceNode nodeWithName: #"SecondModel"];
podRezNode.resource = [IntroducingPODResource resourceFromFile: #"SecondModel.pod"];
podRezNode.shouldCullBackFaces = NO;
podRezNode.location = cc3v(0.0, -10.0, -5.0);
podRezNode.isTouchEnabled = YES;
[self addChild: podRezNode];
podRezNode = [CC3PODResourceNode nodeWithName: #"ThirdModel"];
podRezNode.resource = [IntroducingPODResource resourceFromFile: #"ThirdModel.pod"];
podRezNode.shouldCullBackFaces = NO;
podRezNode.location = cc3v(0.0, -10.0, -5.0);
podRezNode.isTouchEnabled = YES;
[self addChild: podRezNode];
// .. do more
}
Could someone guide me how would i handle such situation?
You can not create or access OpenGL resources in a thread that's different from the thread the OpenGL context was created on. Your only option is to use any "async" method cocos2d offers, or example for loading textures.

How to properly apply torque or angular impulse to body in PlayN?

I've been trying to apply an angular impulse to a Body object in PlayN, but to no avail. Whichever value (radials) I enter, the angle of the body never changes. I have tried to set the torque as well with no results.
Example code that doesn't work:
BodyDef def = new BodyDef();
def.type = BodyType.DYNAMIC;
Body body = world.createBody(def);
float degToRad = (float) (180 / Math.PI);
float radials = (float) (50 / degToRad);
// None of the following options work.
body.applyAngularImpulse(radials); // Immediate angular change.
body.applyTorque(radials); // Angular change over time.
How can I get a valid body object to change its angle without manually setting its angular velocity (e.g. with setAngularVelocity)?
Thanks in advance!
I did notice that the torque and angular velocity are reset by calling the setWake method, which I never do manually, but it is invoked by the Island class:
public void setAwake(boolean flag) {
...
m_angularVelocity = 0.0f;
m_torque = 0.0f;
...
}
Note: Setting the angular velocity is not an option because I rely on the physics simulation. I've found an article for Box2D angle rotation, but it didn't change the outcome of the applyAngularImpulse method.
I've debugged some, and come to the conclusion the values that I've been using were way too low. When going through the applyAngularImpulse code I noticed a few things:
public void applyAngularImpulse(float impulse) {
if (m_type != BodyType.DYNAMIC) {
return;
}
if (isAwake() == false) {
setAwake(true);
}
m_angularVelocity += m_invI * impulse;
}
m_invI is a really low value, something like 2.44882598E-4 so it makes sense if I use a too low value, that there is just no visible effect. If calculate the impulse from the question's example multiplied by the m_invI field:
m_invI = 0.00044882598 = 2.449E-4
impulse = 50 / 180 / PI = 0.08841941282 = 8.842E-2
m_invI * impulse = 2.449E-4 * 8.842E-2 = 0.00002165237 = 2.165E-5 <--
That means just an angular velocity increment of 0.00002165237 per update cycle... Which is way too low to be noticeable it seems.
Also, while browsing the Box2D forums for similar situations, I found it there's a best practice to include the body's inertia into your angular impulse calculation. Like this:
body.applyAngularImpulse(speedValue * body.getInertia());
The inertia tends to be a high value (+4000) for me.
Hope this can be some help to others that have trouble applying an angular impulse or torque.
In my case I forgot to call
body.setFixedRotation(false);
Even though I was setting mass, an inertia of 0 was reported and all torques and angular impulses were ignored.
Apparently it is fixed by default.
I had the same problem.
In my case I accidentally put my points in CW order (leading to negative area and zero mass). This was my incorrect code:
// incorrect version
triangleVertices.push(new B2Vec2(width / 2, 0.0)); // top
triangleVertices.push(new B2Vec2(0.0, height)); // left
triangleVertices.push(new B2Vec2(width, heightM)); // right
That looks like CCW, right? Nope, not in my coordinate system (+Y is down).
// corrected version
triangleVertices.push(new B2Vec2(width / 2, 0.0)); // top
triangleVertices.push(new B2Vec2(width, heightM)); // right
triangleVertices.push(new B2Vec2(0.0, height)); // left

How to trace the missing pixels when using drawLine

We know that for drawing on an image in qt, qpainter is used. Recently, I used drawLine() function to draw whatever an user is scribbling. This was done by passing the lastPoint and currentPoint from the mouseMoveEvent to a custom function which implements drawLine(). I have passed the arguments for that custom function as given below:
void myPaint::mouseMoveEvent(QMouseEvent *event) {
qDebug() << event->pos();
if ((event->buttons() & Qt::LeftButton) && scribbling) {
pixelList.append(event->pos());
drawLineTo(event->pos());
lastPoint = event->pos();
}
}
Now with the help of qDebug() I noticed that some pixels are missed while drawing but the drawing is precise. I looked up the source of qt-painting where I saw that drawLine() was calling drawLines() which was making use of qpainterPath to have a shape drawn on the image.
My question is that, is there anyway to track these "missed" pixels or any approach to find all the pixels which have been drawn?
Thanks!
void myPaint::drawLineTo(const QPoint &endPoint) {
QPainter painter(image); //image is initialized in the constructor of myPaint
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(QPen(Qt::blue, myPenWidth, Qt::SolidLine, Qt::RoundCap,Qt::RoundJoin));
painter.drawLine(lastPoint, endPoint);
modified = true;
lastPoint = endPoint; //at the mousePressEvent, the event->pos() will be stored as
// lastPoint
update();
}
For a start, don't draw in a mouseEvent(). Actually handling a mouseevent should be done as quick as possible. Also, it is not a good idea to look at the Qt source, it can be confusing. Rather assume that what Qt gives you work, and first try to answer "What I am doing wrong?". As I said drawing in a mouse event is definitely wrong.
Your description is really subjective, maybe an image of your output is better. Are you trying to emulate a pen (like in windows paint)? In this case do the mouse button has to be down ? is that the purpose of your variable scribbling?
There is more. following the documentation, QMouseEvent::buttons() always return a combination of all buttons for mouse move event. Which make sense : the mouse movements are independent of the buttons. It means
if ((event->buttons() & Qt::LeftButton)
will always be true.
Let's assume you want to draw the path of your mouse when the left button is pressed. Then you use something like :
void myPaint::mousePressEvent(QMouseEvent *event){
scribbling = true;
pixelList.clear();
}
void myPaint::mouseReleaseEvent(QMouseEvent *event){
scribbling = false;
}
void myPaint::mouseMoveEvent(QMouseEvent *event) {
if ( scribbling) {
pixelList.append(event->pos());
}
}
void myPaint::paintEvent(){
QPainter painter(this)
//some painting here
if ( scribbling) {
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(QPen(Qt::blue, myPenWidth, Qt::SolidLine, Qt::RoundCap,Qt::RoundJoin));
// here draw your path
// for example if your path can be made of lines,
// or you just put the points if they are close from each other
}
//other painting here
}
If after all of this you don't have a good rendering, try using float precision (slower), ie QMouseEvent::posF() instead of QMouseEvent::pos().
EDIT :
"I want to know whether there is any way to calculate all the sub-pixels between any two pixels that we send as arguments to drawLine"
Yes there is. I don't know why you need to do such thing but is really simple. A line can be characterized with the equation
y = ax + b
Both of the endpoints of the line p0 = (x0, y0) and p1 = (x1, y1) satisfy this equation so you can easily find a and b. Now all you need to do is increment from x0 to x1 by the amount of
pixels you want (say 1), and to compute the corresponding y value, each time saving point(x,y).
So will go over all of the points saved in pixelList and repeat this process for any two consecutive points.

gdi+ Graphics::DrawImage really slow~~

I am using a GDI+ Graphic to draw a 4000*3000 image to screen, but it is really slow. It takes about 300ms. I wish it just occupy less than 10ms.
Bitmap *bitmap = Bitmap::FromFile("XXXX",...);
//--------------------------------------------
// this part takes about 300ms, terrible!
int width = bitmap->GetWidth();
int height = bitmap->GetHeight();
DrawImage(bitmap,0,0,width,height);
//------------------------------------------
I cannot use CachedBitmap, because I want to edit the bitmap later.
How can I improve it? Or is any thing wrong?
This native GDI function also draws the image into the screen, and it just take 1 ms:
SetStretchBltMode(hDC, COLORONCOLOR);
StretchDIBits(hDC, rcDest.left, rcDest.top,
rcDest.right-rcDest.left, rcDest.bottom-rcDest.top,
0, 0, width, height,
BYTE* dib, dibinfo, DIB_RGB_COLORS, SRCCOPY);
//--------------------------------------------------------------
If I want to use StretchDIBits, I need to pass BITMAPINFO, But how can I get BITMAPINFO from a Gdi+ Bitmap Object? I did the experiment by FreeImage lib, I call StretchDIBits using FreeImageplus object, it draw really fast. But now I need to draw Bitmap, and write some algorithm on Bitmap's bits array, how can I get BITMAPINFO if I have an Bitmap object? It's really annoying -___________-|
If you're using GDI+, the TextureBrush class is what you need for rendering images fast. I've written a couple of 2d games with it, getting around 30 FPS or so.
I've never written .NET code in C++, so here's a C#-ish example:
Bitmap bmp = new Bitmap(...)
TextureBrush myBrush = new TextureBrush(bmp)
private void Paint(object sender, PaintEventArgs e):
{
//Don't draw the bitmap directly.
//Only draw TextureBrush inside the Paint event.
e.Graphics.FillRectangle(myBrush, ...)
}
You have a screen of 4000 x 3000 resolution? Wow!
If not, you should draw only the visible part of the image, it would be much faster...
[EDIT after first comment] My remark is indeed a bit stupid, I suppose DrawImage will mask/skip unneeded pixels.
After your edit (showing StretchDIBits), I guess a possible source of speed difference might come from the fact that StretchDIBits is hardware accelerated ("If the driver cannot support the JPEG or PNG file image" is a hint...) while DrawImage might be (I have no proof for that!) coded in C, relying on CPU power instead of GPU's one...
If I recall correctly, DIB images are fast (despite being "device independent"). See High Speed Win32 Animation: "use CreateDIBSection to do high speed animation". OK, it applies to DIB vs. GDI, in old Windows version (1996!) but I think it is still true.
[EDIT] Maybe Bitmap::GetHBITMAP function might help you to use StretchDIBits (not tested...).
Just a thought; instead of retrieving the width and height of the image before drawing, why not cache these values when you load the image?
Explore the impact of explicitly setting the interpolation mode to NearestNeighbor (where, in your example, it looks like interpolation is not actually needed! But 300ms is the kind of cost of doing high-quality interpolation when no interpolation is needed, so its worth a try)
Another thing to explore is changing the colour depth of the bitmap.
Unfortunately when I had a similar problem, I found that GDI+ is known to be much slower than GDI and not generally hardware accelerated, but now Microsoft have moved on to WPF they will not come back to improve GDI+!
All the graphics card manufacturers have moved onto 3D performance and don't seem interested in 2D acceleration, and there's no clear source of information on which functions are or can be hardware accelerated or not. Very frustrating because having written an app in .NET using GDI+, I am not happy to change to a completely different technology to speed it up to reasonable levels.
i don't think they'll make much of a different, but since you're not actually needing to resize the image, try using the overload of DrawImage that doesn't (attempt) to resize:
DrawImage(bitmap,0,0);
Like i said, i doubt it will make any difference, because i'm sure that DrawImage checks the Width and Height of the bitmap, and if there's no resizing needed, just calls this overload. (i would hope it doesn't bother going through all 12 million pixels performing no actual work).
Update: My ponderings are wrong. i had since found out, but guys comment reminded me of my old answer: you want to specify the destination size; even though it matches the source size:
DrawImage(bitmap, 0, 0, bitmap.GetWidth, bitmap.GetHeight);
The reason is because of dpi differences between the dpi of bitmap and the dpi of the destination. GDI+ will perform scaling to get the image to come out the right "size" (i.e. in inches)
What i've learned on my own since last October is that you really want to draw a "cached" version of your bitmap. There is a CachedBitmap class in GDI+. There are some tricks to using it. But in there end i have a function bit of (Delphi) code that does it.
The caveat is that the CachedBitmap can become invalid - meaning it can't be used to draw. This happens if the user changes resolutions or color depths (e.g. Remote Desktop). In that case the DrawImage will fail, and you have to re-created the CachedBitmap:
class procedure TGDIPlusHelper.DrawCachedBitmap(image: TGPImage;
var cachedBitmap: TGPCachedBitmap;
Graphics: TGPGraphics; x, y: Integer; width, height: Integer);
var
b: TGPBitmap;
begin
if (image = nil) then
begin
//i've chosen to not throw exceptions during paint code - it gets very nasty
Exit;
end;
if (graphics = nil) then
begin
//i've chosen to not throw exceptions during paint code - it gets very nasty
Exit;
end;
//Check if we have to invalidate the cached image because of size mismatch
//i.e. if the user has "zoomed" the UI
if (CachedBitmap <> nil) then
begin
if (CachedBitmap.BitmapWidth <> width) or (CachedBitmap.BitmapHeight <> height) then
FreeAndNil(CachedBitmap); //nil'ing it will force it to be re-created down below
end;
//Check if we need to create the "cached" version of the bitmap
if CachedBitmap = nil then
begin
b := TGDIPlusHelper.ResizeImage(image, width, height);
try
CachedBitmap := TGPCachedBitmap.Create(b, graphics);
finally
b.Free;
end;
end;
if (graphics.DrawCachedBitmap(cachedBitmap, x, y) <> Ok) then
begin
//The calls to DrawCachedBitmap failed
//The API is telling us we have to recreate the cached bitmap
FreeAndNil(cachedBitmap);
b := TGDIPlusHelper.ResizeImage(image, width, height);
try
CachedBitmap := TGPCachedBitmap.Create(b, graphics);
finally
b.Free;
end;
graphics.DrawCachedBitmap(cachedBitmap, x, y);
end;
end;
The cachedBitmap is passed in by reference. The first call to DrawCachedBitmap it cached version will be created. You then pass it in subsequent calls, e.g.:
Image imgPrintInvoice = new Image.FromFile("printer.png");
CachedBitmap imgPrintInvoiceCached = null;
...
int glyphSize = 16 * (GetCurrentDpi() / 96);
DrawCachedBitmap(imgPrintInvoice , ref imgPrintInvoiceCached , graphics,
0, 0, glyphSize, glyphSize);
i use the routine to draw glyphs on buttons, taking into account the current DPI. The same could have been used by the Internet Explorer team to draw images when the user is running high dpi (ie is very slow drawing zoomed images, because they use GDI+).
/*
First sorry for ma English, and the code is partly in polish, but it's simple to understand.
I had the same problem and I found the best solution. Here it is.
Dont use: Graphics graphics(hdc); graphics.DrawImage(gpBitmap, 0, 0); It is slow.
Use: GetHBITMAP(Gdiplus::Color(), &g_hBitmap) for HBITMAP and draw using my function ShowBitmapStretch().
You can resize it and it is much faster! Artur Czekalski / Poland
*/
//--------Global-----------
Bitmap *g_pGDIBitmap; //for loading picture
int gRozXOkna, gRozYOkna; //size of working window
int gRozXObrazu, gRozYObrazu; //Size of picture X,Y
HBITMAP g_hBitmap = NULL; //for displaying on window
//------------------------------------------------------------------------------
int ShowBitmapStretch(HDC hdc, HBITMAP hBmp, int RozX, int RozY, int RozXSkal, int RozYSkal, int PozX, int PozY)
{
if (hBmp == NULL) return -1;
HDC hdc_mem = CreateCompatibleDC(hdc); //utworzenie kontekstu pamięciowego
if (NULL == hdc_mem) return -2;
//Trzeba połączyć BMP z hdc_mem, tzn. umieścić bitmapę w naszym kontekście pamięciowym
if (DeleteObject(SelectObject(hdc_mem, hBmp)) == NULL) return -3;
SetStretchBltMode(hdc, COLORONCOLOR); //important! for smoothness
if (StretchBlt(hdc, PozX, PozY, RozXSkal, RozYSkal, hdc_mem, 0, 0, RozX, RozY, SRCCOPY) == 0) return -4;
if (DeleteDC(hdc_mem) == 0) return -5;
return 0; //OK
}
//---------------------------------------------------------------------------
void ClearBitmaps(void)
{
if (g_hBitmap) { DeleteObject(g_hBitmap); g_hBitmap = NULL; }
if (g_pGDIBitmap) { delete g_pGDIBitmap; g_pGDIBitmap = NULL; }
}
//---------------------------------------------------------------------------
void MyOpenFile(HWND hWnd, szFileName)
{
ClearBitmaps(); //Important!
g_pGDIBitmap = new Bitmap(szFileName); //load a picture from file
if (g_pGDIBitmap == 0) return;
//---Checking if picture was loaded
gRozXObrazu = g_pGDIBitmap->GetWidth();
gRozYObrazu = g_pGDIBitmap->GetHeight();
if (gRozXObrazu == 0 || gRozYObrazu == 0) return;
//---Uworzenie bitmapy do wyświatlaia; DO IT ONCE HERE!
g_pGDIBitmap->GetHBITMAP(Gdiplus::Color(), &g_hBitmap); //creates a GDI bitmap from this Bitmap object
if (g_hBitmap == 0) return;
//---We need to force the window to redraw itself
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
}
//---------------------------------------------------------------------------
void MyOnPaint(HDC hdc, HWND hWnd) //in case WM_PAINT; DO IT MANY TIMES
{
if (g_hBitmap)
{
double SkalaX = 1.0, SkalaY = 1.0; //scale
if (gRozXObrazu > gRozXOkna || gRozYObrazu > gRozYOkna || //too big picture, więc zmniejsz;
(gRozXObrazu < gRozXOkna && gRozYObrazu < gRozYOkna)) //too small picture, można powiększyć
{
SkalaX = (double)gRozXOkna / (double)gRozXObrazu; //np. 0.7 dla zmniejszania; FOR DECREASE
SkalaY = (double)gRozYOkna / (double)gRozYObrazu; //np. 1.7 dla powiększania; FOR INCREASE
if (SkalaY < SkalaX) SkalaX = SkalaY; //ZAWSZE wybierz większe skalowanie, czyli mniejszą wartość i utaw w SkalaX
}
if (ShowBitmapStretch(hdc, g_hBitmap, gRozXObrazu, gRozYObrazu, (int)(gRozXObrazu*SkalaX), (int)(gRozYObrazu*SkalaX), 0, 0, msg) < 0) return;
Try using copy of Bitmap from file. FromFile function on some files returns "slow" image, but its copy will draw faster.
Bitmap *bitmap = Bitmap::FromFile("XXXX",...);
Bitmap *bitmap2 = new Bitmap(bitmap); // make copy
DrawImage(bitmap2,0,0,width,height);
I have made some researching and wasn't able to find a way to render images with GDI/GDI+ more faster than
Graphics.DrawImage/DrawImageUnscaled
and at the same time simple like it.
Till I discovered
ImageList.Draw(GFX,Point,Index)
and yeah it's really so fast and simple.

Resources