Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


angusj last won the day on May 23

angusj had the most liked content!

Community Reputation

84 Excellent

1 Follower

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. angusj

    Rounded polygon

    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; 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; Edit: The best way to avoid intersections is to make sure you have enough data points before generating your curves.
  2. 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.
  3. 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.
  4. angusj

    Detecting start of drag operations

    That seems likely be the solution to Dave Nottage's problem, though it isn't the answer to his question 😁.
  5. angusj

    Detecting start of drag operations

    Despite the lack of documentation on EVENT_OBJECT_DRAGSTART here: https://docs.microsoft.com/en-us/windows/win32/winauto/event-constants it seems likely that this event is only triggered by "UI Automation" Drag-and-Drop https://docs.microsoft.com/en-us/windows/win32/winauto/ui-automation-support-for-drag-and-drop
  6. angusj

    Detecting start of drag operations

    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.
  7. angusj

    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.
  8. angusj

    32bit bitmap

    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 😜.
  9. angusj

    tImagecollection, how to improve downscaling?

    Delete this line. It's only (sort of) useful for images without alpha transparency.
  10. Edit: New Vcl.Imaging.QOI unit uploaded to GitHub. See https://github.com/AngusJohnson/TQoiImage
  11. OK, all fixed now. I've attached a working example. The example loads a QOI file and saves it to BMP, then loads the saved BMP and saves that to a second QOI, and finally loads the second QOI and saves as a second BMP. Edit: Another minor bugfix (with thanks to feedback from kadaif). Edit2: Deleted obsolete attachment. See post below.
  12. The following seems OK reading QOI files/streams ... Edit: Just spotted an occassional artefact in the rendered images that I'm now chasing. I've also written the WriteToStream code (that seems bug free) that I'll upload once I've found and fixed the bug in the ReadFromStream code above. Edit2: Code sample above deleted. See bugfixed version in zip package below.
  13. Persisting with my dance analogy, being so late to the dance everyone has partnered up. IOW, browers must support formats that webpages use, and webpages will use what browsers support. But as I said above, there's little cost to browsers supporting QOI given the simplicity of the compression and decompression algorithms. Time will tell.
  14. QOI is certainly very late to the dance but she's very pretty (very simple to implement and good compression). So I wouldn't be surprised if browsers quickly supported this new format too.