Jump to content

XylemFlow

Members
  • Content Count

    66
  • Joined

  • Last visited

Posts posted by XylemFlow


  1. This is a rather complex issue that I need help resolving. TImage Mouse Move and Mouse Up events are not triggered after showing a modal window from a TMainMenu item, but only when a certain style is applied to the form. Attached is a project for replicating the issue. Mouse events are recorded with incrementing counters. Any help would be appreciated. I'm using Delphi 11.2 and running in Windows 10.

     

    Steps:

    1. Build for Windows 32-bit and run

    2. Select Dark mode with the check box, which will apply the style to the form

    3. Click Show 2nd Form in the File menu, which will show a 2nd form as a modal window

    4. Close the 2nd form. The main form will regain focus

    5. Move the cursor over the image and the counter won't increment

     

    Observations:

    1. The Mouse Down event is triggered after closing the 2nd window, but the Mouse Up event isn't. This causes a miss match in the counters.

    2. In the default light mode the menu items activate on mouse up, but in dark mode they activate on mouse down. Perhaps the main menu is still owning the mouse up and mouse move events somehow after closing the window because of this?

    3. A TMenuBar does not have the same issue, even though its items are activated on mouse down

    4. I can't find anything in the style editor for the dark style that I could edit to fix the issue.

    5. I could try putting something in the main form's OnActivate event to force the Image to regain ownership of the mouse events, but nothing I try seems to work.

     

    Dark Mode Demo.zip


  2. I guess that one issue will be that currently I think the message boxes show the text on the buttons in the user's own language according to their Windows regional settings.


  3. I use TDialogServiceSync.MessageDialog and TDialogServiceSync.ShowMessage in my Windows / mac OS application. However, I've added a dark mode to my application from a downloaded style and it doesn't seem possible to set the style of these message boxes. The only solution seems to be to create my own message dialog forms and call them using ShowModal. Firstly, will that give exactly the same behaviour? Secondly, does anyone have or know of any free code for message dialog forms? I'd like it to support the various button combination options (TMsgDlgButtons) and an icon depending on TMsgDlgType. If not I'll of course make them myself.


  4. I've had the same problem for months. Close and re-open, even restart Windows, doesn't fix them. Remove and re-add from customize toolbars doesn't fix them either. Ctrl+F12 does work, but I prefer to use the buttons. What else can I try?

     

    I also have several other issues with the Delphi 11.2 IDE. For example, in one of my longer units it seems always confused what lines things are on. If I search the file and click to go to a result it always goes to the wrong place (offset by a constant amount). Find Declaration (Ctrl+click) also never works in that file. Is this a bug with long units (almost 8000 lines) or does something need resetting? Again, restart doesn't help.


  5. 14 hours ago, programmerdelphi2k said:

    @XylemFlow

     

    Here I am RAD 11.3/FMX and using StyleBook 2:

    1. 1 StyleBook Default = Empty
    2. 1 StyleBook Dark = Win10ModernDark.Style

    Unfortunately, some components store their properties in Hexadecimal values, so it's not possible to see the values directly, you'll need to convert them. This usually happens for properties with "images", value trees, etc...
    The "WriteComponent" function is found in TStream subclasses such as TMemoryStream etc... as well as "ReadComponent".
    Here I will try to demonstrate a way to do it, note that using "MEMO" or any other class that makes use of "TStrings", together with "themes/skins/styles", will be very painful in general, because there is much more involved than just change the skin of the component!

     

    Thanks for sharing that. But your stylebookDefault shows nothing as I expected. That's my issue. I can see all the parameters of the dark style but not the parameters of the default style to compare against.


  6. On 6/1/2023 at 11:13 PM, programmerdelphi2k said:

    did you try save in disk using "WriteComponent" function?

    I'm not sure what you mean. The style object for my normal mode doesn't contain anything because only hard coded styles are used.

     

    Attached is a basic example of the issues I'm having. I build it in Delphi 11.2 for Windows 32-bit. When run you can click the checkbox to switch between styles. I have 2 issues when in Dark Mode.

    1. The borders of the form change. All the components on the form shift because of this. I'd like only the colours to change. I assume that I need to change some parameters in windowborderstyle in the Style Designer, but playing around with those parameters hasn't helped much.
    2. The main menu behaves differently. In normal light mode I can click File for example and then move the cursor over Edit and Edit will automatically open and File will close. However, that doesn't happen in Dark Mode. File just stays open. I don't know what to change in the style to fix it.

    If I had all the parameters of the default style (light) then I could compare with the parameters of the dark style to try to work out what the differences are and maybe fix these issues. But I don't know how to do that?

    Dark Mode Demo.zip


  7. I'm trying to create a 'Dark Mode' for my Windows application, but I want to keep the basic style of the controls and just change the colours. I have downloaded a VCL style (Windows 10 Charcoal) and converted it to an FMX style using the Bitmap Style Designer. It looks mostly ok. However, there are some annoying differences. The main one being that there's a thicker border around the forms. There are a few other issues with some controls.

     

    I can open the Style Designer to edit the parameters, but it's difficult to know what to change. I'd like to compare the parameters with the default style to see what's different, but how do I do that? The default style seems to be hardcoded and not visible in the Style Designer.


  8. 21 hours ago, Anders Melander said:

    Btw, I don't know if the following is relevant to what you're doing:

    https://blog.grijjy.com/2021/01/14/shader-programming/

     

    That could be very useful. I've often considered if I could use the 3D capabilities of FMX for my 2D graphics. I may give textures a go in my circle demo. Implementing the interpolation for downsampling with higher quality should just be a matter of writing it into the pixel shader. One down side is that different shaders need to be written to support all platforms, but that's not a big issue. The main issue is combining this with other drawing primitives such as lines, circles, text and others that I use from TCanvas. The shader requires a 3D component, so mixing the two to draw to a single canvas seems difficult.


  9. 2 hours ago, Rollo62 said:

    If your circles are that critical, maybe it's worth if you are looking into Skia4Delphi, which is the next, new hot thing in town.

    It is well-supported and in favor of Embarcadero too, but probably adding a lot of extra baggage too, but seems to have endless possibilities on the cons side :-)

    The circles is just an example. My users could load any image and then want to animate it at various scale and angles.
    I've tried Skia4Delphi. One issue for me is that it doesn't use the GPU when drawing to an off screen TBitmap, whereas TCanvasD2D does.

     

    1 hour ago, Anders Melander said:

    Unless you're running this on a potato you shouldn't really need the GPU for something as simple as this. Of course, the GPU will be faster but the CPU should be fast enough.

    Rotation, translation, and scaling can be done in one go with a 3x3 (well, 2x3 actually) affine transformation. You "just" need to find a library that does that (or write it yourself). Graphics32 can do it but it doesn't support FMX. I'm guessing Image32 can too.

    I've already benchmarked TCanvasD2D (using GPU) against TCanvasGDIPlus (without GPU) on Windows and TCanvasD2D is significantly faster. That tells me that the GPU is making a big difference even with a fast library. I'm not running on a potato either, but my users might be (I use a potato for testing to make sure that it will work for all user setups). A previous version of my software was developed in VCL and rendered the images with scale and rotation in software, so I have those libraries already. There was a significant performance boost moving to FMX, so there's no going back. I'm doing full screen animation at up to 30fps so I need to make use of any hardware boost available.

    You said that you think the code is AND-ing rather than OR-ing. What makes you think that rather than it just using nearest neighbour sub-sampling? Surely the Delphi code is just sending instructions to the GPU and the GPU is unlikely to be making an error like that.


  10. I think the issue is with BitmapScale. With the code below I get the text in the right position.

     

    procedure TForm1.Button1Click(Sender: TObject);
    var
      ARect: TRectF;
      s: string;
    begin
      with Image1.Bitmap do
      begin
        Canvas.Font.Family := 'Arial';
        Canvas.Fill.Color := TAlphaColorRec.Black;
        ARect.Top := 200;
        ARect.Left := 100;
        ARect.Width := 105;
        ARect.Height := 50;
        s := 'hellow Fmx';
        Canvas.BeginScene;
        Canvas.Clear(TAlphaColorRec.White);
    //    canvas.textout(20,20,'hellow Fmx');
        Canvas.FillText(ARect, s, false, 1, [], TTextAlign.Leading, TTextAlign.Leading);
        Canvas.EndScene;
    
        SaveToFile('testBmp.png');
      end;
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Image1.Bitmap.SetSize(Round(Image1.Width), Round(Image1.Height));
      Image1.Bitmap.BitmapScale := 1;
    end;

     

    • Like 1

  11. Thanks everyone for those suggestions. However, I don't think anyone has suggested a way to get the GPU to handle this. My hand written code is about as fast as the CPU can go (I also have code for downscaling by exactly a factor of 2 which is faster still). But my original question was about doing this on the GPU, because I'm dealing with animated real time graphics. I also need to draw these images onto a canvas at an angle, which I do by setting TCanvas.Matrix with TCanvas.DrawBitmap. I could write code to do shrink and rotate but that would be super slow compared to the GPU.

    // shrink a bitmap by a factor of 2. ABitmapOut size needs to be pre set
    procedure ShrinkFast(const ABitmap : TBitmap ; out ABitmapOut : TBitmap);
    Var
      Lx, Ly, R : integer;
      P1, P2, P3, P4, POut, PRowStart, PRowStartOut : pByte;
      W, HM, WL : integer;
      LRowSizeOut, LRowSize : integer;
      bdata, bdatao : TBitmapData;
    begin
      if (ABitmapOut.Width = 0) or (ABitmapOut.Height = 0) then Exit;
    
      ABitmap.Map(TMapAccess.Read, bdata);
      ABitmapOut.Map(TMapAccess.Write, bdatao);
    
      try
        W := ABitmapOut.Width;
        R := ABitmap.Width div W; // shrink ratio
        if R <> 2 then Exit;
    
        HM := ABitmapOut.Height - 1;
        WL := W - 3;
        PRowStart := pByte(bdata.GetScanline(0));
        LRowSize := bdata.Pitch;
        PRowStartOut := pByte(bdatao.GetScanline(0));
        LRowSizeOut := bdatao.Pitch;
        if R = 2 then begin
          for Ly := 0 to HM do begin
            P1 := PRowStart;
            P2 := P1 + LRowSize;
            P3 := P1 + 4;
            P4 := P2 + 4;
            POut := PRowStartOut;
            Lx := 0;
            // set output pixel to the average of the 2X2 box of input pixels
            while Lx < WL do begin // loop unrolled by 4
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // blue
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // green
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // red
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // alpha
              Inc(P1,5); Inc(P2,5); Inc(P3,5); Inc(P4,5); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // blue
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // green
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // red
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // alpha
              Inc(P1,5); Inc(P2,5); Inc(P3,5); Inc(P4,5); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // blue
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // green
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // red
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // alpha
              Inc(P1,5); Inc(P2,5); Inc(P3,5); Inc(P4,5); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // blue
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // green
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // red
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // alpha
              Inc(P1,5); Inc(P2,5); Inc(P3,5); Inc(P4,5); Inc(POut);
              Inc(Lx, 4);
            end;
            while Lx < W do begin
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // blue
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // green
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // red
              Inc(P1); Inc(P2); Inc(P3); Inc(P4); Inc(POut);
              POut^ := (P1^ + P2^ + P3^ + P4^) shr 2; // alpha
              Inc(P1,5); Inc(P2,5); Inc(P3,5); Inc(P4,5); Inc(POut);
              Inc(Lx);
            end;
            Inc(PRowStartOut, LRowSizeOut);
            Inc(PRowStart, LRowSize shl 1);
          end;
        end;
    
      finally
        ABitmap.Unmap(bdata);
        ABitmapOut.Unmap(bdatao);
      end;
    end;

     


  12. On 4/30/2023 at 6:46 PM, KodeZwerg said:

    How about using an ImageViewer control, put your bitmap in and give the control the "BestFit" property.

    That doesn't do a great job either. Looking into the code, it appears to use DrawBitmap as well. However it wouldn't help me anyway. I need to be able to render the images to a TCanvas. I'm updating the canvas for dragging the objects around in real time, which is why I need high performance.


  13. FMX mostly does a very good job of antialiasing graphics, however I notice that quality of TCanvas.DrawBitmap is poor when the destination rect is smaller than the source rect. I'm running on Windows for now. This is especially obvious when the image contains thin lines as the subsampling causes parts of the lines to disappear. I'm looking for a way to improve the quality without compromising too much on runtime. So ideally I'd like it to be done on the GPU. I feel that this is a job that the GPU should be doing. Below is an example with 3 different methods. The last method is my own code, which shows what I'm trying to achieve but is not done on the GPU and so is not as fast as I'd like it to be. It also won't work if I want to include some rotation as well as scale using TCanvas.Matrix. I have also tried changing the HighSpeed flag in the DrawBitmap function, but it doesn't seem to make a difference (it does when upscaling an image but not when downscaling). See the attached project code example.

     

    Is this something that GPUs can normally do and if so, why isn't DrawBitmap doing it? Is there an alternative that will also work on different platforms? If I reduce an image in something like Inkscape it will do a much better job, although I'm not sure if the GPU is being used for the downsampling.

     

    Rfoje.png

    Draw_bitmap_small.zip


  14. On 12/11/2022 at 2:32 AM, Nigel Thomas said:

    Not the answer you are looking for, but you are aware that many users these days have browser adblockers installed? I suspect depending on advertising revenue from website visits to support your application may not be the best way of funding your work on it.

    Thanks, but I've used this funding model for some time and it's already proved to be easily profitable enough and I've had no complaints about the ads. The whole internet seems to be based on advertising so I think people are used to it. On the other hand, very few people are willing to pay for software in my experience. I've found that a lot more income can be made from free software with advertising compared to the same paid software without advertising, simply because it is much more likely to become popular. Of course it depends on the type of software and the target users


  15. This isn't specifically a Delphi question. More of a general Windows development and deployment question. I'm looking into the best options for hosting my application. My application is free and should be available publicly to everyone who wants to download it. My revenue model is based on advertising on my website, so I want to drive all the download traffic through my website. Of course the best option is most likely to host it on my website, but the application is fairly large and the number of downloads it high. My current web host is unlikely to provide enough bandwidth without me upgrading it. I'd also like to prevent people sharing the download link and thus avoiding visiting my website. If it was hosted on my site I would simply change the download file name every few days. What other options are there with 3rd party hosting? I have looked into using the Windows Store, but even if I make the listing undiscoverable I can't avoid people sharing the link and avoid going through my website. It doesn't look like Windows store provides a feature for changing the URL on a regular basis, and if they did how would I synch my website to the new link each time?


  16. I made some progress on this by copying FMX.Canvas.D2D.pas locally and adding Path.SetFillMode(D2D1_FILL_MODE_WINDING); in TCanvasD2D.CreatePathGeometry just after Geometry.Open(Path). It works but modifying Delphi code is a horrible solution and to be able to switch fill mode at run time I would also need to add a property for it in TCanvas in FMX.Graphics.pas. Then I would need to modify FMX.Canvas.GDI.pas and FMX.Canvas.GPU.pas to make the same change. It's not a huge amount of work though. The interface for fill mode is already there. I wonder why they didn't implement such a basic feature?


  17. TCanvas doesn't appear to provide a way to switch between 'alternate' and 'winding' fill modes. The default is alternate. This is important when paths intersect themselves and prevents me rendering certain objects how I would like. I created a new feature request for this on the quality page a while ago. It got the following comment.

     

    Quote

     

    Under Windows, it's easy to fix

    for D2D Canvas the fill mode can be set by a call to Path.SetFillMode(D2D1_FILL_MODE_WINDING);

    for GDIP there's also an option P := TGPGraphicPath.Create(FillModeWinding);

    for GPU Canvas, it's a bit more complex, the rendering is based on a Stencill Buffer in "invert" mode...it's possible to change that but you have to take into account the clockwise direction of the lines to know when the stencill buffer must be enabled or disabled. It's also possible to use Tesselation.

     

     

    I'd like to try to do that as my application is currently Windows only. However, how do I actually go about calling implementation specific functions like Path.SetFillMode(D2D1_FILL_MODE_WINDING) in my code? I know how to find out the current implementation of a TCanvas with ACanvas.ClassName.


  18. TListBox is there in FMX. I'm not sure why you think it's not.

     

    Regarding rotating images. You may know this already but the best way to do it is by setting the Matrix property of the TCanvas you're drawing to. Use TMatrix.CreateRotation to create the matrix from a rotation angle. The advantage of this method is that the GPU takes care of all the transformations. It's also easy to combine with other transformations such as scaling or translation. TBitmap.Rotate is essentially doing that for you, but gives you less control.


  19. I have done this myself. I don't know of a guide to doing it. I'd just recommend to familiarise yourself with FMX first. You will have to rebuild your forms from scratch. You can then copy and paste most of the code and link the events, but some changes will be needed. The drawing commands are different and you need to get used to wrapping drawing commands with BeginScene and EndScene. Code using strings may need to be modified because of unicode. Finally, some visual components are just different and have differently named properties (the most common is that you will have to change .Caption to .Text). Other than that it's not too bad and you get quicker once you get into it.

    • Like 1

  20. Yes you can, but of course it will only compile under Windows. I have used several VCL features in my FMX application. I just place them inside {$IFDEF MSWINDOWS} so they don't compile under other platforms.

×