Jump to content

angusj

Members
  • Content Count

    132
  • Joined

  • Last visited

  • Days Won

    8

Posts posted by angusj


  1. 2 hours ago, DigitalWolF said:

    Structure viewer is one of the core features of Hextor.

    I found it. I thought opening the structures tab with a PE file loaded might show the structure automatically, but I realise now that I have to click on what's on my PC a very tiny toolbar icon that drops a menu to manually load the the structure definition. That's just fine with me (except for the tiny icon 😜).

    Anyhow, thanks again for sharing your work.


  2. On 8/18/2022 at 3:50 AM, DigitalWolF said:

    Feedback and feature suggestions are welcome.

    My first impression is it looks very promising. Thanks for sharing!

    1. Semi-urgent request:

    Either automatically or manually resize toolbar icons as they are currently unreadably small on high DPI displays.

    Suggest SVG icons as they resize very well.

    2. Suggestion:

    I see support for viewing image files (which makes sense since they can be embedded as resources),

    but I would really like to see viewers for file structures (eg PE File format and zip file structures) 

     

     


  3. 2 hours ago, Attila Kovacs said:

    So you are classify your own library as a short time solution?

    No. What I'm saying is if you just need this to solve a short term problem - ie convert 1000s of images as a one off, then a hidden bug is not likely to cause grief. But if this code is going to be used for a very long time, then I'd recommend digging a little deeper and trying to locate the root cause of your memory consumption. (And I really am trying to help, not gang up on you.)

     

    • Like 1

  4. 18 minutes ago, Attila Kovacs said:

    It's always your best guess.

    In fairness, I think David asks a valid question. While it's certainly possible this presumed leak is due to a bug in Delphi code, based on probabilities it's much more likely in your code. Even though it's now working (without TJpegImage or whatever), it's likely still there, just not currently causing problems. If a short term fix is all you need then that's great. But it you're counting on this to work in a presumably unattended server long term, it's usually better to invest the time to find out the definitive cause now than have things blow up later when it's far less convenient.


  5. 2 hours ago, Attila Kovacs said:

    With your lib, in 8 threads, converting 13GB/5200 jpg's the application takes 500MB-1GBPeak RAM.

    With my converter, TPicture.LoadFromFile, DrawTo TBitmap, Resize to another TBitmap, assign to JPEGImage, save: RAM is linear up to 13GB+.

    There is no memory leak, something in Windows is holding some working RAM or whatever. I don't care anymore. 

    Thanks, I'm very pleased to hear that. And I'm now curious as to how the performance compares with your converter?

    Also, I could fairly easily modify Delphi's JPEG unit to expose the functions in the JPEG OBJ files and bypass TBitmap entirely but I'm not sure it's worth the effort thought it could improve performance .


  6. The attached file contains a utility I created some time ago to work around Delphi's IDE that was almost unusable on high DPI screens.

    (And I still use it in Delphi 10.4.)

    In it you'll see a file called IDEFontSizeFix.pas that does (or at least demonstrates) most of what you're asking.

    fancyshortcuts.zip


  7. 6 minutes ago, xstrider said:

     

    But your earlier post promised a stand alone solution ("It requires no specific graphics library to use, just a few extra functions (also included below)") but this doesn't seem to be true.

    I wasn't suggesting that you download Image32, just a couple of extra functions. And MakePath was only a shortcut to demonstrate path construction, which of course you'd generate in your own way. And even with FlattenCBezier, I was (I think quite reasonably) expecting that your graphics library could handle this since cubic bezier paths are a very common graphics data structure. Anyhow, I'm sorry I dissapointed you.


  8. 12 hours ago, xstrider said:

    Hi Angus, I just discovered your GetSmoothPath() routine. I'm very interested to use it - but I can't make it work.

    Hi xstrider.

    I've actually modified this function again since posting the link above, but I didn't want to abuse this thread since it's not specifically about my routine.

    Anyhow, here's the link to the up to date documentation on this function that's been renamed SmoothToCubicBezier (which I think better describes what the function does).

    This can be found in the Img32.Extra unit that part of my Image32 Library on GitHub.

    Both MakePath and FlattenCBezier are found in Img32.Vector.

     

    12 hours ago, xstrider said:

    I also wonder why do you convert an array of Doubles to an array of integers and then assign the result again to an array of doubles?

    I'm not sure what you're referring to here. The MakePath function parameter is array of double, and the entire Image32 library uses double coordinate values.

     

    12 hours ago, xstrider said:

    How should I use the the tGPGraphicsPath.Flatten of GDI+? It doesn't accept a tPathD as an argument.

    It appears you're using GDIPlus and I'm not familiar with it. However, while GraphicsPath does accept cubic bezier input (and will flatten these), these beziers appear to be cubic bezier splines, which are very different to cubic bezier arrays. Nevertheless you could copy FlattenCBezier from my library and simply add the flattened path using GraphicsPath's AddPolygon method.

     

    I hope that helps.

     

    ps: Here's a link to a short video that demonstrates this smoothing (together with vectorizing monochrome raster images, and path simplification).

    • Like 1

  9. And I still think Idera has the math all wrong. If they sold Delphi Developer at 1/3 the current price, I believe they'd more than triple their sales. As a hobby developer I'd be prepared to spend that instead of using the CE, which I'm currently using.

     

    Edit: At the very least Idera should do some market research and ask CE users what they'd be prepared to spend for the Developer edition. Heaven forbid, they might even attract some new developers instead of just bleeding their current user base.

    • Like 2
    • Thanks 1

  10. Here's my GetSmoothPath() routine. It requires no specific graphics library to use, just a few extra functions (also included below).

    This function generates an array of control points that's very easily converted into a flattened cubic bezier path using just about any 2D graphics library.

    (nb: The code below has been written with simplicity as the focus rather than performance.)

     

     

    uses
      SysUtils, Math;
    
    type
    
      TPointD = record
        X, Y: double;
      end;
    
      TPathD = array of TPointD;
      TArrayOfDouble = array of double;
    
    
    function DistanceSqrd(const pt1, pt2: TPointD): double;
    begin
      result := Sqr(pt1.X - pt2.X) + Sqr(pt1.Y - pt2.Y);
    end;
    
    function Distance(const pt1, pt2: TPointD): double;
    begin
      Result := Sqrt(DistanceSqrd(pt1, pt2));
    end;
    
    function OffsetPoint(const pt: TPointD; dx, dy: double): TPointD;
    begin
      result.x := pt.x + dx;
      result.y := pt.y + dy;
    end;
    
    function GetAvgUnitVector(const vec1, vec2: TPointD): TPointD;
    var
      inverseHypot: Double;
    begin
      Result.X := (vec1.X + vec2.X) * 0.5;
      Result.y := (vec1.Y + vec2.Y) * 0.5;
      inverseHypot := 1 / Hypot(Result.X, Result.Y);
      Result.X := Result.X * inverseHypot;
      Result.Y := Result.Y * inverseHypot;
    end;
    
    procedure MakeSymmetric(var val1, val2: double);
    begin
      val1 := (val1 + val2) * 0.5;
      val2 := val1;
    end;
    
    function GetUnitVector(const pt1, pt2: TPointD): TPointD;
    var
      dx, dy, inverseHypot: Double;
    begin
      if (pt1.x = pt2.x) and (pt1.y = pt2.y) then
      begin
        Result.X := 0;
        Result.Y := 0;
        Exit;
      end;
      dx := (pt2.X - pt1.X);
      dy := (pt2.Y - pt1.Y);
      inverseHypot := 1 / Hypot(dx, dy);
      dx := dx * inverseHypot;
      dy := dy * inverseHypot;
      Result.X := dx;
      Result.Y := dy;
    end;
    
    // GetSmoothPath - returns cubic bezier control points
    //   parameters: 1. path for smoothing
    //               2. whether or not the smoothed path will closed
    //               3. percent smoothness (0..100)
    //               4. maximum dist control pts from path pts (0 = no limit)
    //               5. symmetric vs asymmmetric control pts
    function GetSmoothPath(const path: TPathD; pathIsClosed: Boolean;
      percentOffset, maxCtrlOffset: double;
      symmetric: Boolean): TPathD;
    var
      i, len, prev: integer;
      vec: TPointD;
      pl: TArrayOfDouble;
      unitVecs: TPathD;
      d, d1,d2: double;
    begin
      Result := nil;
      len := Length(path);
      if len < 3 then Exit;
      d :=  Max(0, Min(100, percentOffset))/200;
      if maxCtrlOffset <= 0 then maxCtrlOffset := MaxDouble;
    
      SetLength(Result, len *3 +1);
      prev := len-1;
      SetLength(pl, len);
      SetLength(unitVecs, len);
      for i := 0 to len -1 do
      begin
        pl[i] := Distance(path[prev], path[i]);
        unitVecs[i] := GetUnitVector(path[prev], path[i]);
        prev := i;
      end;
    
      Result[len*3] := path[0];
      for i := 0 to len -1 do
      begin
        if i = len -1 then
        begin
          vec := GetAvgUnitVector(unitVecs[i], unitVecs[0]);
          d2 := pl[0]*d;
        end else
        begin
          vec := GetAvgUnitVector(unitVecs[i], unitVecs[i+1]);
          d2 := pl[i+1]*d;
        end;
        d1 := pl[i]*d;
        if symmetric then MakeSymmetric(d1, d2);
        if i = 0 then
          Result[len*3-1] := OffsetPoint(path[i],
            -vec.X * Min(maxCtrlOffset, d1), -vec.Y * Min(maxCtrlOffset, d1))
        else
          Result[i*3-1] := OffsetPoint(path[i],
            -vec.X * Min(maxCtrlOffset, d1), -vec.Y * Min(maxCtrlOffset, d1));
        Result[i*3] := path[i];
        Result[i*3+1] := OffsetPoint(path[i],
          vec.X * Min(maxCtrlOffset, d2), vec.Y * Min(maxCtrlOffset, d2));
      end;
      if not pathIsClosed then
      begin
        Result[1] := Result[0];
        dec(len);
        Result[len*3-1] := Result[len*3];
        SetLength(Result, Len*3 +1);
      end;
    end;

     

    And here's what it produces ...

    the path to smooth (black),

    the cubic bezier control path produced by GetSmoothPath() (blue) 

    and the flattened cubic bezier path (2D graphics library of you choice required) (red).

     

    var
        TPathD path;
    begin
        path := MakePath([190,120, 260,270, 560,120, 190,490]);
        path := GetSmoothPath(path, true, 20, 0, false);
        path := ThirdParty2DGraphicsLibrary.FlattenCBezier(path); 
    end;

     

    test20.thumb.png.d97635270def988873c388657a5bd5ff.png

     

    var
        TPathD path;
    begin
        path := MakePath([190,120, 260,270, 560,120, 190,490]);
        path := GetSmoothPath(path, true, 80, 0, false);
        path := ThirdParty2DGraphicsLibrary.FlattenCBezier(path); 
    end;

    test80.thumb.png.f2e5c34379525f78150f0aef978729bb.png

     

    On 5/20/2022 at 8:45 PM, A.M. Hoornweg said:

    The problem:  The polygons may never intersect (

    Edit: The best way to avoid intersections is to make sure you have enough data points before generating your curves.

    • Like 4

  11. 5 hours ago, Dale M said:

    You just have dig for it under the hamburger icon at the top right, Account, Mark site read.

    Thanks Dale. I have been doing that. I just find it somewhat tedious having to do that every time i visit DP. IMHO, having a "Mark site read" button at the bottom as in desktop view would be much more convenient. Cheers.

    • Like 1

  12. On 2/20/2022 at 3:54 AM, Bent Normann Olsen said:

    I have taken steps in removing the current edition. Sorry for any inconveniences. Perhaps it's just not ment to be.

    16 hours ago, corneliusdavid said:

    I clicked on the link and it can't be found; searched for the title and couldn't be found either.

    See above. While I also missed downloading and reading Normann's book, it seems he wasn't encouraged by the initial feedback. I think there's a lessen in that. Normann had put considerable effort into writing this book and uploaded it as a free resource and, from what I can glean from this thread, it was mostly of high quality.


  13. 37 minutes ago, Vincent Parrett said:

    I suspect that might not be using the standard windows drag and drop - more likely an OLE one.

    Is there a difference between OLE and "the standard windows drag and drop"?

    "The OLE DoDragDrop function calls this method (IDropSource::QueryContinueDrag) during a drag-and-drop operation."

    https://docs.microsoft.com/en-us/windows/win32/api/oleidl/nn-oleidl-idropsource

    Of course, this is very different to Delphi's custom drag drop stuff.


  14. Quote

    Detecting start of drag operations

    guessing ...

     

    startDragEvent := SetWinEventHook(EVENT_OBJECT_DRAGSTART, EVENT_OBJECT_DRAGSTART, 0, @DragStartEvent, 0, 0, WINEVENT_OUTOFCONTEXT);
    
    procedure DragStartEvent(hWinEventHook: THandle; event: DWORD; hwnd: HWND; idObject, idChild: Longint; idEventThread, dwmsEventTime: DWORD); stdcall;
    begin
      //do stuff
    end;

    Note: All OLE drag events are initiated by DoDragDrop

    https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-dodragdrop

    Since the drag souce (IDropSource, IDataObject) is initiated by another process so you'll certainly need a system event hook.


  15. On 2/2/2021 at 4:02 AM, Anders Melander said:

    Yes but it's a nasty mess. Better than nothing I guess.

    I wholeheartedly agree with you Anders that TBitmap.AlphaFormat is broken and should be deprecated.

    Not only does it not behave in an intuitive manner, but Premultiplying and UnPremultiplying images is (mildly) destructive.

    But there's another potentially bigger problem - when writing an afDefined Bitmap to stream, UnPreMultiplyAlpha is called and hence the image will no longer be properly rendered.

     

    ps: Isn't it fun digging up old threads 😜.

     

×