-
Content Count
2771 -
Joined
-
Last visited
-
Days Won
147
Everything posted by Anders Melander
-
What is the future roadmap of Graphics32 ?
Anders Melander replied to Rollo62's topic in Cross-platform
Interesting. It looks like it should be easy to adapt that algorithm to the Graphics32 transformation framework. I'll give it go and compare it to the existing one. Turns out there's almost nothing gained from using that method. While the matrix setup (which runs only once) is slightly faster, the transformation matrix (which is used once per target pixel) is exactly the same and thus yields the same performance and result.- 22 replies
-
- graphics32
- vcl
-
(and 1 more)
Tagged with:
-
Is it really that bad to Use boolean parameters to make your functions do two things?
Anders Melander replied to Mike Torrettinni's topic in General Help
Apart from the braces I don't see a problem with that example. It's a convenience or wrapper function and while it's not clear from the context the function doesn't appear to do two completely different things based on the value. A kitten dies every time you do that. Remember that anyone reading the code that uses the function will have no idea that the parameter name makes the intent clear. EnableDisableControls(True); // WTH? EnableDisableControls(False); // WTF? -
What is the future roadmap of Graphics32 ?
Anders Melander replied to Rollo62's topic in Cross-platform
If the internal pixel data format is the same (which I suspect they are), then you can wrap a TImage32 in a TBitmap32 via a Graphics32 memory backend. They will both be operating on the same block of pixel memory so take care if you resize the TImage32 while the TBitmap32 is associated with it. I don't think you can do it the other way round (wrap TBitmap32 in a TImage32) since TImage32 doesn't have a backend concept. Of course you can always copy the pixel data from one to the other, manipulate it and then copy it back, but that's not very efficient.- 22 replies
-
- graphics32
- vcl
-
(and 1 more)
Tagged with:
-
What is the future roadmap of Graphics32 ?
Anders Melander replied to Rollo62's topic in Cross-platform
Hi Angus and welcome Well, if all you want is a hammer then all the other tools may indeed seem like bloat. The VPR polygon renderer and the line methods solve two different but similar problems. Remember that the purpose of Graphics32 is to do things as fast as possible. This means specialization over generalization. VPR is a generalized vector renderer that does many things reasonably well while the line methods does one single thing as fast as possible. That's why, instead of a single line method with a bunch of parameters, we have individual methods for vertical lines, horizontal lines and "other" lines. It's also why each of these methods are found in different variants each supporting different feature combinations: clipped or unclipped coordinates, transparency, anti alias or not, patterns, etc. And for different numeric types: integer, floating point or fixed precision. It's all about avoiding compromises that would hurt performance and give the user of Graphics32 control over the choices that have an impact on performance. Replacing the line methods with VPR would be like putting all-weather tires on a Formula 1 car. The MoveTo/LineTo methods, like their TCanvas counterparts, are purely high level convenience methods that will be superseded and can be removed once the TCanvas32 rewrite gets merged. But that's being blocked by the 2.0 non-release Interesting. It looks like it should be easy to adapt that algorithm to the Graphics32 transformation framework. I'll give it go and compare it to the existing one.- 22 replies
-
- graphics32
- vcl
-
(and 1 more)
Tagged with:
-
Compute nearest color
Anders Melander replied to FPiette's topic in Algorithms, Data Structures and Class Design
Please post it here (or in a Git repository somewhere) when you're done. It's probably too computationally expensive for the stuff I use color distance functions for, but I'd like to give it a go regardless. Here are the ones I use for stuff like flood fill and magic wand selection with tolarance, dithering etc. The ColorDistanceRGBA function gives a fast approximation of "visual" difference and is based on this: https://www.compuphase.com/cmetric.htm interface //------------------------------------------------------------------------------ // // Color distance functions // //------------------------------------------------------------------------------ function ColorDistanceSimpleRGB(Old, New: TColor32): Integer; function ColorDistanceRGBA(Old, New: TColor32): Integer; // Note: Not linear. Visual difference. function ColorDistanceRGBALinear(Old, New: TColor32): Integer; function ColorDistanceRGBLinear(Old, New: TColor32): Integer; function ColorDistanceAverageRGB(Old, New: TColor32): Integer; // The HSV values of the first (Old) parameter is cached so when comparing // multiple values you should attempt to keep the first parameter constant. function ColorDistanceHue(Old, New: TColor32): Integer; function ColorDistanceSaturation(Old, New: TColor32): Integer; function ColorDistanceBrightness(Old, New: TColor32): Integer; implementation //------------------------------------------------------------------------------ // // Color distance functions // //------------------------------------------------------------------------------ function ColorDistanceSimpleRGB(Old, New: TColor32): Integer; begin if (Old <> New) then Result := 255 else Result := 0; end; function ColorDistanceRGBA(Old, New: TColor32): Integer; begin if (Old = New) then Exit(0); if (Old and $FF000000 = 0) xor (New and $FF000000 = 0) then Exit(255); var AlphaOld := AlphaComponent(Old); var AlphaNew := AlphaComponent(New); if (AlphaOld <> AlphaNew) then begin if (AlphaOld <> 255) then Old := ColorScale(Old, AlphaOld); if (AlphaNew <> 255) then New := ColorScale(New, AlphaNew); EMMS; end; // See "Colour metric" by Thiadmer Riemersma // http://www.compuphase.com/cmetric.htm // Modified to consider alpha. Adjusted to maintain 0..255 range var Mean := (RedComponent(Old) + RedComponent(New)) div 2; var dA := AlphaNew - AlphaOld; var dR := RedComponent(New) - RedComponent(Old); var dG := GreenComponent(New) - GreenComponent(Old); var dB := BlueComponent(New) - BlueComponent(Old); Result := Round(Sqrt(( dA*dA + (((512+Mean)*dR*dR) shr 8) + ((dG*dG) shl 2) + (((767-Mean)*dB*dB) shr 8)) / 4)); end; function ColorDistanceRGBALinear(Old, New: TColor32): Integer; begin if (Old = New) then Exit(0); if (Old and $FF000000 = 0) xor (New and $FF000000 = 0) then Exit(255); var AlphaOld := AlphaComponent(Old); var AlphaNew := AlphaComponent(New); if (AlphaOld <> AlphaNew) then begin if (AlphaOld <> 255) then Old := ColorScale(Old, AlphaOld); if (AlphaNew <> 255) then New := ColorScale(New, AlphaNew); EMMS; end; var dA := AlphaNew - AlphaOld; var dR := RedComponent(New) - RedComponent(Old); var dG := GreenComponent(New) - GreenComponent(Old); var dB := BlueComponent(New) - BlueComponent(Old); Result := Round(Sqrt(( dA*dA + dR*dR + dG*dG + dB*dB) / 4 )); end; function ColorDistanceRGBLinear(Old, New: TColor32): Integer; begin if (Old = New) then Result := 0 else // All transparent colors are considered equal regardless of RGB if (Old and $FF000000 <> $FF000000) and (New and $FF000000 <> $FF000000) then Result := 0 else // Difference in transparency = max difference if (Old and $FF000000 = 0) xor (New and $FF000000 = 0) then Result := 255 else begin var dR := RedComponent(New) - RedComponent(Old); var dG := GreenComponent(New) - GreenComponent(Old); var dB := BlueComponent(New) - BlueComponent(Old); Result := Round(Sqrt(( dR*dR + dG*dG + dB*dB) / 3 )); end; end; function ColorDistanceAverageRGB(Old, New: TColor32): Integer; asm AND EAX,$00FFFFFF AND EDX,$00FFFFFF MOVD MM0,EAX MOVD MM1,EDX PSADBW MM0,MM1 MOVD EAX,MM0 IMUL EAX,$555555 SHR EAX,24 //Result := ((C shr 16 and $FF) + (C shr 8 and $FF) + (C and $FF)) div 3; end; var HSLCacheColor: TColor32 = 0; HSLCacheH: Byte = 0; HSLCacheS: Byte = 0; HSLCacheL: Byte = 0; function ColorDistanceHue(Old, New: TColor32): Integer; var Z, A, B: Byte; begin if (Old = New) then Exit(0); if (Old and $FF000000 = 0) xor (New and $FF000000 = 0) then Exit(255); if (Old and $FF000000 <> New and $FF000000) then begin if (Old and $FF000000 <> $FF000000) then Old := ColorScale(Old, AlphaComponent(Old)); if (New and $FF000000 <> $FF000000) then New := ColorScale(New, AlphaComponent(New)); EMMS; end; if (Old <> HSLCacheColor) then begin HSLCacheColor := Old; RGBtoHSL(Old, HSLCacheH, HSLCacheS, HSLCacheL); end; A := HSLCacheH; RGBtoHSL(New, B, Z, Z); Result := Abs(A - B); end; function ColorDistanceSaturation(Old, New: TColor32): Integer; var Z, A, B: Byte; begin if (Old = New) then Exit(0); if (Old and $FF000000 = 0) xor (New and $FF000000 = 0) then Exit(255); if (Old and $FF000000 <> New and $FF000000) then begin if (Old and $FF000000 <> $FF000000) then Old := ColorScale(Old, AlphaComponent(Old)); if (New and $FF000000 <> $FF000000) then New := ColorScale(New, AlphaComponent(New)); EMMS; end; if (Old <> HSLCacheColor) then begin HSLCacheColor := Old; RGBtoHSL(Old, HSLCacheH, HSLCacheS, HSLCacheL); end; A := HSLCacheS; RGBtoHSL(New, Z, B, Z); Result := Abs(A - B); end; function ColorDistanceBrightness(Old, New: TColor32): Integer; var Z, A, B: Byte; begin if (Old = New) then Exit(0); if (Old and $FF000000 = 0) xor (New and $FF000000 = 0) then Exit(255); if (Old and $FF000000 <> New and $FF000000) then begin if (Old and $FF000000 <> $FF000000) then Old := ColorScale(Old, AlphaComponent(Old)); if (New and $FF000000 <> $FF000000) then New := ColorScale(New, AlphaComponent(New)); EMMS; end; if (Old <> HSLCacheColor) then begin HSLCacheColor := Old; RGBtoHSL(Old, HSLCacheH, HSLCacheS, HSLCacheL); end; A := HSLCacheL; RGBtoHSL(New, Z, Z, B); Result := Abs(A - B); end; -
Security - How freaky can you get!
Anders Melander replied to Clément's topic in Algorithms, Data Structures and Class Design
Good one -
What is the future roadmap of Graphics32 ?
Anders Melander replied to Rollo62's topic in Cross-platform
The Graphics32 team First of all I'm not the maintainer of Graphics32. I'm just a contributor. I don't even have admin rights to the main repository. As far as I'm concerned the current project lead is @CWBudde1 but I'm not sure he agrees and he's also been largely absent in a long time. Of the remaining 4 members, micha137 hasn't contributed anything significant in a year and Michael Hansen, Mattias Andersson and Andre Beckedorf hasn't been active in a decade. https://github.com/graphics32/graphics32/graphs/contributors (I'm not sure how reliable that page is as I seem to be absent from it). Roadmap The last roadmap I know of was from 2012 (posted in the old graphics32 newsgroups) and concerned the "mythical version 2.0". Since then some of the items on the roadmap has been implemented, some has been superseded and some things not on the roadmap has been added. At present only the Graphics32 issue tracker at Github gives any indication of the direction the project might move. Lack of progress From my POW the inability to make a decision and actually release version 2.0, incomplete or not, has been one of the reasons why the project has stalled. For example the present version supports Delphi 7 and later which greatly limits what can be done - or what people are willing to do. A new version would drop support for ancient versions of Delphi and only support XE and later. In April 2019 it was decided, by Christian, Angus and I as far as I recall, that we should just release version 2.0 as-is. But again, without anyone to take the lead, nothing happened. In my opinion the greatest blow to Graphics32 was the complete loss of the Graphics32 community. This happened when the project was moved to Github and people stopped using the newsgroups. Github is great for managing the project but it's not a community platform. Without a community we're left with individual developers that might still have an interest in the project itself but soon burns out or simply isn't interested in developing in a void with no interaction with others or feedback from the users. Apart from the discussions and QAs, a big part of the old newsgroups was that people posted examples of what they did with Graphics32 and how they did it, extension libraries and graphic algorithm implementations. Luckily I still have an almost complete local copy of the newsgroups and it's still a great resource for inspiration, examples and solutions. Actually it seems the usenet server is still alive: nntp://news.graphics32.org Of course one needs a news client to access them and I'm probably one of the last people on earth to have one installed Image32 I have read the Image32 documentation and I think I've looked at the source once but beyond that I have little knowledge about the project. I would be very surprised if Angus didn't use the techniques used in Graphics32 as inspiration. Although I know he loves to write things from scratch I doubt that he doesn't use Graphics32 as a reference. Anyway, I can only guess. As far as I can see the "architecture" of Image32 is that there isn't one; While Graphics32 is an object oriented framework on top of some highly optimized low level routines, Image32 is more of a monolithic design - one class does everything. As far as I remember this dislike of OO was one of the reasons he wanted to write his own. Another was that he felt Graphics32 had become bloated. I don't agree but that's beside the point. Current state of the project As I see it the current version (i.e. head in the master branch) is stable. There are no grave issues and nothing technical that hinders future adoption of the library into new projects. The documentation has fallen behind and in a few places it is no longer correct. The examples aren't that great but at least they compile and do what they were written to do. The future As I'm just another contributor I can only speak to what I would like to see happen. Someone has to take the lead. It could be me but then I would have to stop contributing code. I can't do both. Also, although I do have opinions about how and what should be done my area of expertise is architecture and implementation. Get 2.0 released or simply abandon the idea of major release versions. As I said above before the 2.0 problem is resolved there will be little or no progress. Move the documentation into a wiki so we're actually able to maintain it. Currently it seems updating the documentation requires custom tools and I for one don't need the hassle of building, installing and maintaining some tool just to keep the docs up to date. It's hard enough to find the motivation to do so without that. Separate the examples from the showcases and write some (or a lot) of small, simple examples that demonstrate how to get started with Graphics32 and how to solve the most common tasks/problems. Even I hardly look at the current examples when I need to figure out how to do something as they are mostly too advanced or bury the relevant code in unrelated gimmicks. I can imagine that they must be undecipherable to a new user. Get rid of or repair the code contributed by cowboys. Some of the newer features of Graphics32 was contributed (and accepted) without regard for the fact that other people should be able to maintain it (for example if the author went awol or decided to work on another project instead). The code should be commented, the algorithms used should be documented, etc. Even if none of the above happens I think Graphics32 will be safe for the immediate future. It's a stable and fairly complete library and there are enough people and projects using it that someone else is bound to pick up the mantle if all the current contributors get hit by a bus.- 22 replies
-
- graphics32
- vcl
-
(and 1 more)
Tagged with:
-
When sorting a “StringList” is very costly
Anders Melander replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
It does. You just need to keep the attribution, copyright and license notice in the source. -
I tried with a 5500x4000 bitmap and I did actually manage to produce some lag. I then changed the resampler to TDraftResampler and the lag was completely gone. Same with TNearestResampler.
-
Did you set ImgView.RepaintMode=rmOptimized ? Try to set a breakpoint in line 2380 of GR32_Image ("SourceRect := GetBitmapRect;") to verify that the correct version is being used. Can you reproduce the problem with the small example that I posted earlier (remember to make the changes I later mentioned and set RepaintMode):
-
When sorting a “StringList” is very costly
Anders Melander replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
Nicklaus Wirth disagrees: https://en.wikipedia.org/wiki/Wirth's_law Bonus quote: What Intel giveth, Microsoft taketh away: https://en.wikipedia.org/wiki/Andy_and_Bill's_law -
The design time functionality hasn't changed so you don't need to recompile the packages. The easiest way to test it out would be to just copy the modified GR32_Image.pas to the source folder of your application. That way your application will compile with the new GR32_Image but your existing Graphics32 installation will not be affected. Once the changes are merged into the main Graphics32 branch you can update your Graphics32 installation and remove the local copy of GR32_Image.
-
@wadepm Here's the branch with the optimized scroll: https://github.com/graphics32/graphics32/tree/TImgView32_optimized_scroll Note that the optimization only comes into play if you're not using the rmFull repaint mode (see TImgView.RepaintMode). Give it a spin!
-
Well, I couldn't resist the challenge so I have a nicely working implementation now. I'll create a pull request with it tonight (it's 6 in the morning here in Denmark) when I wake up again .
-
I think TLinearKernel (or just the TLinearResampler) will yield the best down-sample quality with good performance. The links I posted explains why. The update mechanism redraws the areas of the bitmap that changes. When you pan the bitmap doesn't change but the view of it does. Therefore the optimized update can't handle the pan so the whole viewport is repainted instead. I'm currently investigating if I can shoehorn a pan optimization into the existing framework with minimal changes. That's what I do when previewing large bitmaps (for PixelFormat=pf32bit) but I only do it do avoid out of memory: // Get RGBA from source ImageView.Bitmap.BeginUpdate; try // Limit preview image size const MaxPixels = 1024*1024; var Pixels := TBitmap(FImage).Width * TBitmap(FImage).Height; if (Pixels >= MaxPixels) then begin var Scale := Sqrt(MaxPixels / Pixels); // Proportionally scale bitmap so the result doesn't contain more than the desired number of pixels ImageView.Bitmap.SetSize(Round(TBitmap(FImage).Width * Scale), Round(TBitmap(FImage).Height * Scale)); // Stretch draw the source bitmap onto the scaled bitmap TBitmap(FImage).Canvas.Lock; try SetStretchBltMode(ImageView.Bitmap.Canvas.Handle, COLORONCOLOR); ImageView.Bitmap.Draw(ImageView.Bitmap.BoundsRect, TBitmap(FImage).Canvas.ClipRect, TBitmap(FImage).Canvas.Handle); finally TBitmap(FImage).Canvas.Unlock; end; end else begin ImageView.Bitmap.SetSize(TBitmap(FImage).Width, TBitmap(FImage).Height); for var Row := 0 to TBitmap(FImage).Height-1 do MoveLongword(TBitmap(FImage).ScanLine[Row]^, ImageView.Bitmap.ScanLine[Row]^, TBitmap(FImage).Width); end; finally ImageView.Bitmap.EndUpdate; end; ImageView.Bitmap.Changed;
-
When sorting a “StringList” is very costly
Anders Melander replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
If the TStrings.Objects isn't used for anything then, instead of separating the strings and checked states into two list and then sort both simultaneously, you could just stuff the checked state into TStrings.Objects and then use the standard TStrings.Sort. Or you could sort a TList<integer> of indices, using TList<>.Sort and then reassemble the sorted string list based on the result. -
Performance of MOVE vs + for concatenating mixed type values
Anders Melander replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Less questions. More autonomy. -
It's sampling every time it draws something. That doesn't mean that it necessarily resamples the whole bitmap though. It will only sample the parts that it needs in order to paint the visible part of the bitmap. Read this: https://graphics32.github.io/Docs/Additional Topics/Sampling and Rasterization.htm https://graphics32.github.io/Docs/Additional Topics/Repaint Optimization.htm Of course when scale < 1 then the whole bitmap will probably be visible and everything will be sampled. As far as I remember there's no repaint optimization with regard to panning - I guess that's something I could look into if I run out of other stuff to do Anyway, even if you did pan 1 pixel at a time (which I doubt you're doing) it should be fast enough that you shouldn't experience any stutter. Of course it depends on the type of sampler/kernel you're using, your hardware and the size of the bitmap but on my old system I can pan a zoomed (in or out) 4000x4000 multi layer bitmap without any noticeable stutter.
-
It's not the look that's the problem. It's the usability. The DevExpress grid has both a preview pane and an in-place edit form feature. I'm referring to the edit form, I suspect you're referring the the preview pane. In-place editor form: Preview: That sounds more sensible to me. Like a property inspector. The problem with putting stuff into the grid is that is interferes with the nice overview the grid layout provides. Regardless it would still be a good idea to create a mockup before investing too much time on this.
-
I don't really know what type Source is but assuming it's a TBitmap32 then loading a PNG with LoadFromFile will internally use a TPicture to load the file and then assign the TPicture.Graphic (which will be a TPNGImage) to the TBitmap32 using a generic import routine that replaces the transparency with white. This matches what you're observing. As far as I can tell everything works "as designed". What did you expect this would do? If you want to display a 32-bit TBitmap with alpha using TImage then I suggest you create a 32-bit RGBA bmp file and experiment with getting TImage do display that. You have too many variable factors in your approach. Get one thing working first. Then move on to the next problem. The above has nothing to do with Graphics32.
-
You didn't answer my question: Have you verified that the transparent PNG pixels are still transparent after conversion to TBitmap32? As far as I can tell, from the 4 lines of code you posted, you're not really using graphics32 to draw anything here. If you draw a TBitmap32 onto a Windows GDI device (Image.Canvas.Handle) then the GDI StretchDIBits function will be used to draw it - hence no transparency. The primary reason to use graphics32 is that it allows you to do the composition (blending, merging, layers, rendering, etc) really fast and then draw the final image onto the screen. If what you actually want is to draw something onto the screen with transparency then maybe you shouldn't be using graphics32 at all? If you really want to draw a TBitmap32 onto a GDI device with transparency then you should assign it to a TBitmap, set TBitmap.AlphaFormat=afDefined and then draw the TBitmap instead. TBitmap will use the AlphaBlend function in this case. var Bitmap := TBitmap.Create; try Bitmap.Assign(Bitmap32); Bitmap.AlphaFormat := afDefined; Bitmap.DrawTo(Image.Canvas.Handle, 0, 0); finally Bitmap.Free; end; Btw, are you really sure you want to draw onto the TImage.Canvas? That seems.... um... like a mistake...
-
Maybe you should make a proof of concept using a DevExpress trial first and have your users test it. We use DevExpress grids and have that feature enabled on a few of them and the users hate it. It looks like a good idea but the usability sucks. Maybe that's just the way DevExpress has implemented it though.
-
I don't use LoadBitmap32FromPNG and it's not part of Graphics32. Have you verified that the transparent PNG pixels are still transparent after conversion to TBitmap32? Other than that the problem could be the blend and combine mode used when drawing but that should be the same regardless of the origin of the pixel data.
-
Yes. That was just a brain fart on my behalf.
-
Well that was easy. Luckily the bug was in my own code. Here's the code that works: procedure TFormMain.ImgViewMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer); begin if (Button = mbLeft) then begin FPanning := True; ImgView.Cursor := crSizeAll; FStartPos := Point(X, Y); end else if (Button = mbMiddle) then ImgView.Scale := 1; end; procedure TFormMain.ImgViewMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer); begin if (not FPanning) then Exit; var NewPos := Point(X, Y); var Delta := FStartPos - NewPos; FStartPos := NewPos; if (Delta.X <> 0) or (Delta.Y <> 0) then ImgView.Scroll(Delta.X, Delta.Y); end;