Text anti aliasing lazarus / 39-Graphics ---------------------------------------------------------------------- user5 · 2020-09-06 09:32 Does anyone know what the algorithms are for applying anti aliasing to block text and other shapes? I have seen the code on this forum for standard shapes, lines etc. but I'm talking about applying a TrueType kind of edge softening to images on video frames that can change and morph into any shape at all. Just by studying TrueType text edge softening up close I can see that the edge softening only applies to 'curved' portions of raster scan text images but I haven't got it all figured out yet. ---------------------------------------------------------------------- Handoko · 2020-09-06 09:55 For lines, Xiaolin Wu's line algorithm: https://stackoverflow.com/questions/3613130/simple-anti-aliasing- function-for-delphi-7#3613953 https://en.wikipedia.org/wiki/Xiaolin_Wu's_line_algorithm For 3D objects, FSAA and multi sample anti-aliasing: https://en.wikipedia.org/wiki/Spatial_anti- aliasing#Super_sampling_/_full-scene_anti-aliasing For font, subpixel rendering: https://szafranek.net/blog/2009/02/22/font-smoothing-explained/ ---------------------------------------------------------------------- circular · 2020-09-06 15:51 Roughly explained, you need to draw the pictures 3 times as wide in grayscale. Then shrink it by assigning 3 values to one pixel into each channel (red, green, blue). ---------------------------------------------------------------------- user5 · 2020-09-07 14:43 Thank you for your responses. I hope that what you say isn't over my head but I will check out Handoko's suggestions and ponder what circular said which I assume involves BGRABitmap and that's fine with me. I may have some questions again later if that's okay. By the way, I now think that BGRABitmap is more reliable than RawImage though the latter has its very useful applications. ---------------------------------------------------------------------- user5 · 2020-09-07 15:09 I thought of something else. I was thinking about what circular said and it reminded of an edge softening sub-program that I've been working on which can also be used in reverse to do de-edging. It does its work in a way similar to what circular said, if I understand him correctly. This edging sub-program and another de-edging feature make the background shrink down (perfectly proportional) onto and into the foreground image which obliterates the previous anti aliasing so that it can be re-edged later. The great thing about this technique is that it preserves any existing internal edge softening. It would be nice to offer the user the ability to apply a TrueType kind of edge softening as well as the current ones. I will shortly check out your suggestions. ---------------------------------------------------------------------- Handoko · 2020-09-07 15:39 I believe what circular meant is subpixel rendering too. Subpixel rendering is a bit too technical for me to understand. But it is interesting to see how it works: https://en.wikipedia.org/wiki/Subpixel_rendering ---------------------------------------------------------------------- circular · 2020-09-07 20:18 Yes I am talking about sub pixel rendering. Basically you have two things to consider: - in a way, because the RGB channels are next to each other, the actual resolution of the screen is 3 times as wide. So a simple approach for example would be to render a grayscale image using each channel as a pixel. This kind of works but is not really nice, because of the second point: - secondly, the color perceived depends on the 3 channels, so for example, if you have a thin line that would be 1 third of a pixel, of you change let's say only the red channel, then it would appear as a red line, even though you wanted a white line. To avoid this, you need to distribue the value to the adjacent channels. Examples (values from 0 to 9) grayscale     0    0    9             0     0    0 RGB            0    3    3             3     0    0 color         ---dark cyan--    ---dark red---- perceived         ----dark gray-----   -----black----- you get some blurry effect though: grayscale     0    0    0              9    9    9             0     0    0 RGB            0    0    3              6    9    6             3     0    0 color           ---dark blue---    ---pale green--      ---dark red---- perceived    --black--  ---pale orange/pale sky------   --black-- so probably you could get some mixed mechanism like: grayscale     0    0    0              9    9    6             0     0    0 RGB gray     0    0    0              6    6    6             0     0    0  remainder    0    0    0              3    3    0             0     0    0 RGB remain  0   0    1              2     2    1            0     0  0 RGB             0   0    1              8     8    7            0     0    0 color            --dkdkblue--       --cream---            ---black---- perceived     --black--   ----lightsilver----             ---black---- for a colored image as input, then you would need do that 3 times, one for each channel. ---------------------------------------------------------------------- winni · 2020-09-07 22:46 Hi! And I hope that @circulars explanation shows why AntiAliasing is such a thieve of CPU cycles. And so of time. Winni ---------------------------------------------------------------------- user5 · 2020-09-08 11:48 A picture says a thousand words and it might help if I show you exactly what I'm trying to do. I'm sorry that I didn't do this before. The attached pictures are of an Arial Bold TrueType font before and after edge softening has been applied. b1.gif shows a part of the letter B in raster scan mode. b2.gif shows that letter after anti aliasing has been applied. All I want to know is how to do that anti aliasing. I know that the algorithms and possibly the code for this exist somewhere because the edge softening exists.     The bold letter B is just a type of shape and as a shape changes its form then its edging must also change. circular, thank you for the information though I'm unsure how to apply it. You know more about this than I do and I'm floundering a bit. b3.gif shows a different type of anti aliasing, something that I did on my own. ---------------------------------------------------------------------- user5 · 2020-09-08 11:55 By the way, the anti aliasing shown in b2.gif was made by Lazarus! How does Lazarus do it? ---------------------------------------------------------------------- flowCRANE · 2020-09-08 12:00 Quote from: user5 on September 08, 2020, 01:55:37 pmBy the way, the anti aliasing shown in b2.gif was made by Lazarus! So how? Lazarus is an IDE, so please provide more details. Maybe the better question is: how the TFont.Quality works and what causes it in LCL/widgetset? ---------------------------------------------------------------------- user5 · 2020-09-08 12:55 I'll try. As the attached code shows, the user picks a font and then pastes in over the form some memo box text in the chosen anti aliased font. The text appears in a non-transparent, movable banner that can be made transparent if desired. It occurred to me that maybe Lazarus didn't do the anti aliasing. Maybe the font itself does the edge softening and Lazarus merely sends it along. I am working with BMPs and PNGs that need this TrueType kind of edge softening. If text can be anti aliased this way then this should be applicable to any shape. Can the Lazarus antialiasing property be applied to BMPs and PNGs? It seems to me if I tried that but it didn't work out.   Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i //The user chooses a font.procedure TForm5.MenuItem14Click(Sender: TObject);begin if FontDialog1.execute then  StaticText10.font := FontDialog1.font;end; //The user displays memo text in the chosen font. The movable text appears over the form//in a non-transparent colored banner that can be made transparent.procedure TForm5.MenuItem17Click(Sender: TObject);var tempnum:integer;    tempstr1:string;begin  text12visible := true;  statictext10loaded := true;  StaticText10.Visible := true;  StaticText10.Left := 34;  StaticText10.Top := 62;  StaticText10.Caption := Memo1.Text;   if (custom = false) then  begin   if (statictext10.left + statictext10.width) > (paintbox6.left + 263) then    begin     tempnum := (statictext10.left + statictext10.width) - (paintbox6.left + 263);     statictext10.width := statictext10.width - tempnum;    end;    if (statictext10.top + statictext10.height) > (paintbox6.top + 202) then    begin     tempnum := (statictext10.top + statictext10.height) - (paintbox6.top + 202);     statictext10.height := statictext10.height - tempnum;    end;  end;    if (custom) then    begin     statictext10.Width := statictext10origwidth;     statictext10.height := statictext10origheight;     if (statictext10.left + statictext10.width) > (paintbox6.left + 551) then      begin       tempnum := (statictext10.left + statictext10.width) - (paintbox6.left + 551);       statictext10.width := statictext10.width - tempnum;    end;      if (statictext10.top + statictext10.height) > (paintbox6.top + 408) then      begin       tempnum := (statictext10.top + statictext10.height) - (paintbox6.top + 408);       statictext10.height := statictext10.height - tempnum;      end;    end; statictext10.height := statictext10newheight;if (statictext10.top + statictext10.height) > (paintbox6.top + 408) then begin  tempnum := (statictext10.top + statictext10.height) - (paintbox6.top + 408);  statictext10.height := statictext10.height - tempnum; endelse statictext10.height := statictext10newheight;ArrangeText; statictext10.font.Quality := fqAntialiased;form78.ShowSettings; if (statictext10loaded = true) and (borderextended = true) and (custom = true) then AdjustWidthNow('StaticText10');  end; ---------------------------------------------------------------------- Handoko · 2020-09-08 13:05 Quote from: user5 on September 08, 2020, 02:55:22 pmIt occurred to me that maybe Lazarus didn't do the anti aliasing. If you use TStaticText you can set the quality: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TStaticText.Font.Quality = (fqDefault, fqDraft, fqProof, fqNonAntialiased, fqAntialiased,    fqCleartype, fqCleartypeNatural); TCanvas.Font has such setting too. ---------------------------------------------------------------------- marcov · 2020-09-08 14:15 Search for "signed distance font" both on google and on this forum.  This is a GPU acceleratable technique, and there are sources (from me) on this forum. ---------------------------------------------------------------------- user5 · 2020-09-08 14:28 I don't need apply this type of anti aliasing to just fonts and text. I need to apply it to TImages, bitmaps, bgrabitmaps, BMPs and PNGs like the program currently does using a different method.     I'm talking about transparent TImages and bitmaps that have a foreground image surrounded by a solid color bacground that will flawlessly go transparent. The edging that the Lazarus program does now is applied to the foreground image based on what is UNDER the TImage, bmp, png etc. when the image background is transparent.     Just by talking about this I may have a plan forming. The BGRABitmap edging sub-program currently does its obverse edging by comparing and mixing colors in a certain way but it occurred to me that maybe all I need do is simply modify it a bit to do TrueType anti aliasing. Apparently I will have to come up with those algorithms and code on my own and that will take more time. At least I have that as a fallback plan. The program can optionally go at an object from four directions to apply edging. I really do want this though it will take me away from other stuff that I want to do. ---------------------------------------------------------------------- circular · 2020-09-08 15:23 If you have the actual polygon definition, you can draw it in white in a TBGRABitmap, and then draw that mask with FillClearTypeMask function of the target TBGRABitmap. If you only have the the aliased image, you can create an image containing a mask of the non-transparent pixels, and then you can determine the contour using BGRAVectorize. Then you can do an antialiased fill poly of that. Though that would not be ClearType because ClearType antialias is not implemented yet for polygons. If you come up with some code to do that, I would be happy to know. ---------------------------------------------------------------------- ChrisR · 2020-09-08 19:35 One solution to this problem would be to use OpenGL. The project  font_gl.lpi https://github.com/neurolabusc/Metal- Demos/tree/master/font uses multi-channel signed distance field fonts. This allows nicely scalable, sharp looking text. This does require a custom shader. For bitmaps and lines, the multi-sampling built into OpenGL will anti-alias edges nicely. To see the benefit of multisampling on lines, try the cube_gl.lpi project https://github.com/neurolabusc/Metal-Demos/tree/master/cube and compare the difference between the default   ViewGPU1.MultiSampling:=4;  versus   ViewGPU1.MultiSampling:=1; ---------------------------------------------------------------------- user5 · 2020-09-10 21:55 I'm working now on some code to do TrueType or semi-TrueType edge softening and at some point I may have to rotate the image. I plan to use BGRABitmap for the rotation but for purposes of illustration the code below uses TImage canvas pixels and it rotates the image -90 degrees. I've studied topics on rotation is this forum and on the Web but I haven't been able to get anything else to work correctly. I wouldn't be surprised if I'm doing something wrong. Anyway, is there a way to rotate the image 90 degrees clockwise back to its original position without having to go counter clockwise around the dial until the image is back to its first position?     Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button23Click(Sender: TObject);var x,y:integer;begin for y := 0 to (image13.height - 1) do  begin   for x := 0 to (image13.width - 1) do    begin      image16.canvas.pixels[y,x] := image13.canvas.pixels[x,y];     end;  end; end; ---------------------------------------------------------------------- user5 · 2020-09-10 22:10 I'm working now on some code to do TrueType or semi-TrueType edge softening. At some point I may have to rotate the image. I plan to use BGRABitmap but for purposes of illustration the code below uses TImage canvas pixels and it rotates the image -90 degrees. I've studied topics on rotation is this forum and on the Web but I haven't been able to get anything else to work correctly. I wouldn't be surprised if I'm doing something wrong. Anyway, is there a way to rotate the image 90 degrees clockwise back to its original position without having to go counter clockwise around the dial until the image is back to its first position?   Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button23Click(Sender: TObject);var x,y:integer;begin for y := 0 to (image13.height - 1) do  begin   for x := 0 to (image13.width - 1) do    begin      image16.canvas.pixels[y,x] := image13.canvas.pixels[x,y];     end;  end; end; ---------------------------------------------------------------------- winni · 2020-09-10 22:50 Hi! I show you a simple procedure to rotate a TBitmap clockwise and counterclockwise. For demonstration purpose all with TBitmap - awful slow. But it's easy to convert it to BGRAbitmap. Take a Form, an Image >= 100x300, a Button. And this 2 procedures: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function rotate90 (Source: TBitmap; clockwise: Boolean) : TBitmap;var dest : TBitmap;      x,y : Integer;      col : TColor;begindest := TBitmap.create;dest.setSize (Source.height,source.width);for x := 0 to Source.width - 1 do  begin     for y := 0 to Source.Height - 1 do     begin       col := Source.Canvas.pixels[x,y];       if clockwise then dest.canvas.pixels[y, dest.Height-x-1] := col else            dest.Canvas.Pixels[dest.Width-y-1, x] := col;       end;//y     end; // x result := dest;end; // proc    procedure TForm1.TestClick(Sender: TObject);  var  bmp, rot, rot2 : TBitmap; beginImage1.Canvas.FillRect( 0,0,Image1.Width,Image1.Height);bmp := TBitmap.Create;bmp.setSize(50,80);bmp.Canvas.Pen.Color := clRed;bmp.Canvas.Line(0,0,bmp.width, bmp.Height);bmp.Canvas.brush.Color := clLime;bmp.Canvas.FillRect(0,0,10,10);image1.Canvas.Draw(0,0,bmp);rot := rotate90(bmp, true);Image1.Canvas.Draw(0,100,rot);rot2 := rotate90 (rot, false);Image1.Canvas.Draw(0,200,rot2);bmp.Free;rot.free;rot2.Fre e;end;   Happy rotating! Winni ---------------------------------------------------------------------- circular · 2020-09-10 23:41 @user5: in general, I suggest you avoid using Image.Canvas. The image component draw the bitmap it contains. So that is rather Image.Picture.Bitmap.Canvas or something like that. This image needs to be sized of course. If you are just using the area to draw things, then TPaintBox makes more sense. There you have no bitmap, just the control's canvas. Also note that you are not supposed to access the control's canvas outside of its OnPaint event. Even though can work on Windows and Linux, it never does on MacOS. ---------------------------------------------------------------------- winni · 2020-09-11 00:12 Quote from: circular on September 11, 2020, 01:41:39 am Also note that you are not supposed to access the control's canvas outside of its OnPaint event. Even though can work on Windows and Linux, it never does on MacOS. This true for all kind of visible canvas - but again: This is NOT true for a TImage. Winni ---------------------------------------------------------------------- Blaazen · 2020-09-11 00:49 In fact, you can paint on TImage.Canvas at OnPaint event conditionally. See code of TCustomImage.Paint: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i begin  // detect loop  if FUseAncestorCanvas then exit;   if csDesigning in ComponentState  then DrawFrame;    if Picture.Graphic = nil  then Exit;   C := inherited Canvas;  R := DestRect;  C.AntialiasingMode := FAntialiasingMode;  FPainting:=true;  try    if Assigned(FOnPaintBackground) then      FOnPaintBackground(Self, C, R);    C.StretchDraw(R, Picture.Graphic);  finally    FPainting:=false;  end;   FUseAncestorCanvas := True;  try    inherited Paint;  finally    FUseAncestorCanvas := False;  end;end; If Picture.Graphic is nil then there's exit, i.e. OnPaintBackground and OnPaint are not triggered at all. ---------------------------------------------------------------------- winni · 2020-09-11 10:50 Hi! To clarify the situation with the TImage : There are two different Canvas in a TImage: The Canvas which is shown to you: Image.Canvas And the Canvas of the persistent data in the picture, which maybe a Bitmap, a PNG, a JPG . The Picture data like Image.Picture.PNG.Canvas are not visible to you. If the the Canvases both are unlocked the data is exchanged between them. The code shown by @Blaazen is the painting on the Background, the invisible data in the picture. You can draw at any time on an Image without violating any rules. Winni ---------------------------------------------------------------------- circular · 2020-09-11 18:03 Quote from: winni on September 11, 2020, 02:12:33 amQuote from: circular on September 11, 2020, 01:41:39 amAlso note that you are not supposed to access the control's canvas outside of its OnPaint event. Even though can work on Windows and Linux, it never does on MacOS. This true for all kind of visible canvas - but again: This is NOT true for a TImage. That's why I am specifying "control's canvas". Otherwise you can access the canvas of a bitmap anytime indeed. In the case of a TImage, that would be Image1.Picture.Bitmap.Canvas. ---------------------------------------------------------------------- user5 · 2020-09-24 16:19 Hey everyone. Some of you recently helped me on my quest to achieve TrueType/ClearType/super Wu type of edge softening/anti aliasing. TrueType is Apple and ClearType is Microsoft. I am happy to report that the results are very encouraging to say the least. I'm talking about applying this kind of anti aliasing to all foreground shapes, text and images in pictures and videos. The edging of this new process is based on what pictures or videos are under the edged foreground image when the background surrounding that foreground image is made transparent, even if the edged picture or video frame is itself moving.     The processing time it takes to apply this type of edging to videos is pretty slow but the quality of the results appears at this point to exceed my expectations. In addition to this, I'm also working on another new faster edging feature that will do semi-TrueType edging. All of these methods are designed to work only on raster scan foreground images on transparencies since any existing edge softening screws up the results. Thus, the de-edging tools I made remove existing anti aliasing so that it can be re-applied later after other work is done to improve the image and make it be truly and universally transparent. The program does all this graphically without relying on any existing standard algorithms using BGRABitmap fast pixel access. The reason that the former process is slow to process videos is due to the many passes that are made though each video frame and it would be impossible w/o BGRA. An interesting thing is that these methods can be applied to both solid color backgrounds and non-changing or changing muli-colored backgrounds.     Anyway, all this has been made possible by Lazarus and the Forum and I thank you very much. I am excited about this. I was unsure whether to post this note here or in the third party section. I hope that posting it here is okay. I had hoped that some of the folks who helped me here in the graphics section would see this note since they took so much effort, patience and time to help me. They sure know their stuff. Much work remains but the hard part is done.