Giter VIP home page Giter VIP logo

Comments (13)

andersmelander avatar andersmelander commented on June 2, 2024 1

Thanks. I'll investigate later.
Until then you can use TImage32.ForceFullInvalidate. That should work.

from graphics32.

andersmelander avatar andersmelander commented on June 2, 2024

P.S. The test application works correctly on the release/2.0 branch.

The repaint mechanism was pretty broken in earlier versions so my guess is that it just repainted everything every time something changed. Now it works as designed but unfortunately that also means that a lot of code that did things incorrectly no longer work as it did before.

Anyway, as I explained earlier, TCustomLayer.Changed does nothing. The layers doesn't know which area it should invalidate. Only you know that.

Do like this instead (I changed the event to MouseMove instead of MouseDown just so there's more action):

procedure TMainForm.imgMainMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);

  procedure InvalidateCross(const p: TPoint);
  begin
    // Vertical
    imgMain.Invalidate(Rect(p.x, 0, p.x+1, imgMain.Buffer.Height));
    // Horizontal
    imgMain.Invalidate(Rect(0, p.y, imgMain.Buffer.Width, p.y+1));
  end;

begin
  // Invalidate old position so it gets erased
  InvalidateCross(FCurrent);

  FCurrent := Point(X, Y);

  // Invalidate new position so it gets drawn
  InvalidateCross(FCurrent);
end;

If you want to see what get's invalidated and repainted, define UPDATERECT_DEBUGDRAW in GR32_Image.pas
There are some similar options for the repaint optimizer in GR32_MicroTiles.pas

You might also want to look at the examples in Examples\General\Optimized repaint

from graphics32.

zedxxx avatar zedxxx commented on June 2, 2024

In the real application, it is not so easy to manually invalidate everything. It would be great if the library took care of everything itself, as before. Now I will probably have to use Invalidate without specifying a TRect.

from graphics32.

andersmelander avatar andersmelander commented on June 2, 2024

Now I will probably have to use Invalidate without specifying a TRect.

Yes, it's unfortunate but there really isn't any way for the layer to know what and where you are drawing, before you are drawing it.

Btw, here's your example with the cross painted as marching ants (stippled).
Issue 311 - Repaint TCustomLayer and draw TBitmapLayer at the same time.zip

2024-04-22_22-16-52.mp4

from graphics32.

zedxxx avatar zedxxx commented on June 2, 2024

I still think there is a bug somewhere in the caching/notification/messaging mechanism in GR32. If I change my original code like this:

procedure TMainForm.imgMainMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
begin
  FCurrent := Point(X, Y);
  
  imgMain.Invalidate;
  //FCustomLayer.Changed;

  FBitmapLayer.Bitmap.Clear(SetAlpha(clLightGreen32, 100));
end;

The code still doesn't work correctly. The only way to fix this is to call Changed and Invalidate together:

imgMain.Invalidate;
FCustomLayer.Changed;
FBitmapLayer.Bitmap.Clear(SetAlpha(clLightGreen32, 100));

BUT there is no any visible problem when I call Changed (or single Invalidate) and draw TBitmapLayer from different notification events (e.g. from OnTimer and OnMouseDown):

procedure TMainForm.imgMainMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
begin
  FCurrent := Point(X, Y);
  FBitmapLayer.Bitmap.Clear(SetAlpha(clLightGreen32, 100));
end;

procedure TMainForm.tmr1Timer(Sender: TObject); // interval = 50 ms
begin
  imgMain.Invalidate;
end;
CustomLayerTest_rjuFDSWXSD.mp4

from graphics32.

zedxxx avatar zedxxx commented on June 2, 2024

This works:

procedure TMainForm.imgMainMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
begin
  FCurrent := Point(X, Y);

  imgMain.Invalidate;
  Application.ProcessMessages;

  FBitmapLayer.Bitmap.Clear(SetAlpha(clLightGreen32, 100));
end;

from graphics32.

zedxxx avatar zedxxx commented on June 2, 2024

Application.ProcessMessages can be removed if you disable repaint optimizations: imgMain.RepaintMode := rmFull

from graphics32.

andersmelander avatar andersmelander commented on June 2, 2024

I still think there is a bug somewhere in the caching/notification/messaging mechanism in GR32. If I change my original code like this: [...] The code still doesn't work correctly.

I can't reproduce any problem with that so there must be difference in our test code somewhere
I can't rule out that there's a bug somewhere but from what I've seen so far everything works as designed.

Application.ProcessMessages can be removed if you disable repaint optimization

In this case the ProcessMessages just processes WM_PAINT messages so you could replace it with Update which does the same without any of the nasty side-effects of ProvcessMessages.

from graphics32.

zedxxx avatar zedxxx commented on June 2, 2024

Yes, Invalidate + Update or Invalidate with TRect fix the problem. Single Invalidate doesn't work.

procedure TMainForm.imgMainMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer;
  Layer: TCustomLayer);
begin
  FCurrent := Point(X, Y);

  // 1. Invalidate + Update
  imgMain.Invalidate;
  imgMain.Update;

  // 2. Invalidate with TRect
  //imgMain.Invalidate(Rect(0, 0, imgMain.Width, imgMain.Height));

  FBitmapLayer.Bitmap.Clear(SetAlpha(clLightGreen32, 100));
end;

Which is the preferred way 1 or 2? Or are they doing the same thing at the same speed?

from graphics32.

andersmelander avatar andersmelander commented on June 2, 2024

Number 1 is is probably the most efficient. There's also TImage32.ForceFullInvalidate which might be even more efficient as it disables repaint optimization for the current paint cycle.

For a full invalidation TImage32.Invalidate, without the call to Update, should work.
Update simply processes WM_PAINT messages which should happen automatically anyway unless you are doing all this in a tight loop where Windows doesn't get a chance to process the message queue.

If you take the code I posted above and change InvalidateCross to simply call Invalidate it should work (it does for me):

procedure TMainForm.InvalidateCross(const p: TPoint);
begin
  imgMain.Invalidate;
end;

If that doesn't work then there's something else going on and we need to look at what Delphi version, Windows etc. you are using.

from graphics32.

zedxxx avatar zedxxx commented on June 2, 2024

If you take the code I posted above and change InvalidateCross to simply call Invalidate it should work (it does for me)

Yes, as I wrote before, it works from a different event, but doesn't from the same one. It's important to me.

My modified test app: CustomLayerTest.zip

from graphics32.

zedxxx avatar zedxxx commented on June 2, 2024

Until then you can use TImage32.ForceFullInvalidate. That should work.

Yes, this function works.

from graphics32.

andersmelander avatar andersmelander commented on June 2, 2024

Okay, now I understand what's going on.

The problem is that you are using optimized repaint and drawing on the buffer without notifying the repaint optimizer what it needs to repaint.

Invalidate does a general invalidation of the buffer and asks Windows to repaint the control (i.e. generate a WM_PAINT message).
When the WM_PAINT message is processed and the buffer is being updated, the internal logic examines the "dirty" areas that needs to be repainted. If there are zero areas then everything is repainted, otherwise each area is repainted.

So when you have no visible layers, and have not invalidated any specific areas, then there are no dirty areas and everything is repainted. When you have a single visible layer, then the area covered by that layer is repainted. This matches what you are seeing exactly.

The solution is to disable the repaint optimizer, which you might as well do since you aren't using its features, by setting RepaintMode=rmFull. The other solution is to use ForceFullInvalidate, but it would be better to use rmFull and avoid the overhead of the repaint optimizer.

from graphics32.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.