merge two images lazarus / 39-Graphics ---------------------------------------------------------------------- klausbits · 2016-07-07 09:00 Hi. I want to interpolate between two images like this 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 var picture1,picture2,picture3: Tpicture;picture1:= Tpicture.create;picture2:= Tpicture.create;picture3:= Tpicture.create; picture1.LoadFromFile(d:\image1.jpg);picture2.LoadFromFile(d:\image2.j pg); picture3 = 0.7*picture1 + 0.3*picture2; how can I do this? ---------------------------------------------------------------------- Thaddy · 2016-07-07 09:06 You need to set transparency levels for those pictures so the one shines through the other. ---------------------------------------------------------------------- klausbits · 2016-07-07 09:26 to Thaddy, thanks for the tip. I have to think about this if it serves my purpose. Rather I thought to have red3:=0.7*red1+0.3*red2; green3:=... blue3:=... ---------------------------------------------------------------------- Thaddy · 2016-07-07 09:35 @Klausbits: Yup, that is basically what transparency does so why re-invent wheels? I never got further than an octagonal one.... You got the algorithm almost spot on but think about weights of the values. Now let your drivers do the real work ---------------------------------------------------------------------- klausbits · 2016-07-07 09:55 to Thaddy, I am not sure thsat this is the same (pardon me, I am a physicist;-) How would you program your proposal? ---------------------------------------------------------------------- circular · 2016-07-07 13:11 How to do that with BGRABitmap: 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 uses BGRABitmap, BGRABitmapTypes;var bmp1,bmp2,bmp3: TBGRABitmap;begin  bmp1 := TBGRABitmap.Create('d:\image1.jpg');  bmp2 := TBGRABitmap.Create('d:\image2.jpg');  bmp3 := TBGRABitmap.Create(bmp1.Width,bmp1.Height); //note that size may be different between bmp1 and bmp2  bmp3.CrossFade(Rect(0,0,bmp3.Width,bmp3.Height), bmp1, bmp2, round(0.3*255), dmSet);  bmp1.Free;  bmp2.Free;  ...end; It works with images that have transparent parts as well. ---------------------------------------------------------------------- klausbits · 2016-07-07 14:27 to circular: thank you, I'll try it. Meanwile I worked around with colorm:=picm.bitmap.Canvas.Pixels[nw,nh];      //color of picm, but has only 256 colors :-( or fpcolorm1:=picm1.bitmap.Canvas.getColor(nw,nh);  //FPcolor of picm1 does not work :-( ---------------------------------------------------------------------- klausbits · 2016-07-07 18:43 many thanks for your tips. Here my own solution, I hope it works: 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              //see diary 6.July 2016    picm:= TPicture.Create;    picm1:= TPicture.Create;    for n:=0 to nwidth-1 do    begin//        picture2[n]:= TPicture.Create;   //already defined      nr:=n;               //real of n      mr:=nr*nmax/nwidth;    //real of m      m:=floor(mr);      dmr:=mr-m;            //fade position      Str(m:4,nstring);      fastring:=files+'\b'+nstring+'.jpg';      Str(m+1:4,nstring);      fbstring:=files+'\b'+nstring+'.jpg';      picm.LoadFromFile(fastring);      //image 1      picm1.LoadFromFile(fbstring);     //image 2      for nh:=0 to nheight-1 do      begin        for nw:=0 to nwidth-1 do        //for every pixel do        begin          colorm:=picm.bitmap.Canvas.Pixels[nw,nh];      //color of picm        RedGreenBlue(colorm, mRed, mGreen, mBlue);     //bytes! -> only 512 different colors?          colorm1:=picm1.bitmap.Canvas.Pixels[nw,nh];      //color of picm1          RedGreenBlue(colorm1, m1Red, m1Green, m1Blue);//          fpcolorm1:=picm1.bitmap.Canvas.getColor(nw,nh);  //FPcolor of picm1 does not work          nred:=round((1-dmr)*mRed+dmr*m1Red);             //to be scaled by 256?  or 8?          ngreen:=round((1-dmr)*mGreen+dmr*m1Green);        nblue:=round((1-dmr)*mBlue+dmr*m1Blue); //         function RGBToColor(R, G, B: Byte): TColor;          colorn:=RGBToColor(nred, ngreen, nblue);          picture2[n].bitmap.Canvas.Pixels[nw,nh]:=colorn;   //image 3        end;      end;    end;    Str(n:4,nstring);    fastring:=files+'\bb'+nstring+'.jpg';  // bb... different name to not overwrite b... files of user    picture2[n].SaveToFile(fastring); //  picture2[n].free; //  end; ---------------------------------------------------------------------- circular · 2016-07-07 19:44 That's the idea. ---------------------------------------------------------------------- russellman · 2023-10-19 18:18 I use BGRABitmap. Hear is a PDF file that show how to do this. https://drive.google.com/file/d/1- HWs7X5YNKkbDUdx9WpcBZ_RByodXFKb/view?usp=share_link uses    BGRABitmap, BGRABitmapTypes; procedure TForm1.FormDropFiles(Sender: TObject; const FileNames: array of string   ); begin   ///If first image is drop on form.   if tic = False then begin     ///Create bmp1 TBGRABitmap.     bmp1 := TBGRABitmap.Create;     ///Load the first image into bmp1.     bmp1.LoadFromFile(FileNames[0]);     ///Create (bmp2) a TBGRABitmap canvas. The size of bmp1.     bmp2 := TBGRABitmap.Create(bmp1.Width,bmp1.Height);     ///Puts bmp1 (image data) into bmp2. Using Linear Blend.     bmp2.PutImage(0, 0, bmp1, dmLinearBlend );     ///bmp1 Free memory.     bmp1.Free;     ///Togels to All other images.     tic := True   ///All other images are drop on form.   end else begin     ///Create bmp1 TBGRABitmap.     bmp1 := TBGRABitmap.Create;     ///Load the first image into bmp1.     bmp1.LoadFromFile(FileNames[0]);     ///Puts bmp1 (image data) into bmp2. Using Linear Blend.     ///bmp2 was not freed.     bmp2.PutImage(0, 0, bmp1, dmLinearBlend);     ///bmp1 Free memory.     bmp1.Free;   end; ///end if else().   ///Draw bmp2 to Image1.   Image1.Picture.Assign(bmp2);   Image1.Picture.SaveToFile(Saved.png'); end; ---------------------------------------------------------------------- r.lukasiak · 2023-10-25 04:56 Hi everyone, I see the post is quite old but since #russellman recently revived it, I'll give it a try since it's about something I need :-) I was playing with CrossFade and it's somehow working but there is something I can't figure out. I have 2 BGRABitmaps, lets name them img1 and img2. Img1 is non- transparent picture but img2 is partly transparent. I need to merge them but without any calculating partly transparent pixels whatsoever. Img2 consists of either 100% transparent pixels or 100% opaque color pixels. I need the 100% opaque pixels to just replace the pixels from img1 and the 100% transparent pixels just keep the original pixels from img1. so my first shot was: 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 img.CrossFade(Rect(0,0,img1.Width,img1.Height), img1, img2, 255, dmSet);but it resulted in img be the same as img2, it took just 1ms though. Then I started playing with mode value but all of them resulted in the same, img = img1, just taking 5-8ms. Next I changed 255 to 254 and this did the trick, I got expected result but it took 26-28ms, depending on the mode. I just don't understand why 255 doesn't give the result I expected. It looks like 254 causes the function to calculate the img1 and img2 pixels, that's why it's taking so much more time.  Even though it visually looks good, img2 seems to cover img1 in "just" 254/255, am I wrong? Is AFadePosition and equivalent of opacity? Setting a value between 0 and 254 gives results as if it were but everything changes with 255. Searching for any answer led me to this post and #russellman's using PutImage instead of CrossFade. By these functions names I'd say that CrossFade really calculates transparency according to AFadePosition whereas PutImage just drops one image onto another. PutImage indeed gives expected result in just 7ms. How PutImage different to CrossFade? Could someone shed more light on it? Thanks in advance! ---------------------------------------------------------------------- circular · 2023-10-25 14:05 Hello @r.lukasiak CrossFade is designed to create a transition between two images based on the fade position. When fade position is 0 to be equivalent of putting the first image (img1 in your case), and when fade position is 255 to be equivalent of putting the second image (img2). In between, it is supposed to be the intermediate value. When using as the fade position different from 0 or 255, the function will perform some interpolation between the two images, which is why you noticed an increase in computation time. For example, if at one pixel, the first image is opaque and the second is transparent, with fade position 128 the resulting pixel should be half-transparent. if your goal is to just overlay img2 on img1 without any transitional effect, using PutImage is the more appropriate and efficient choice. CrossFade is intended for scenarios where you want a smooth transition between two images. Note that the mode supplied to CrossFade applies after the interpolation has been made between the two images. In most scenarios, you would use dmSet if you want to replace the content of the target or dmDrawWithTransparency if there is a background you want to preserve. I hope this clarifies things! Let me know if you have any further questions or if the result you get does not seem consistent with the explanations. ---------------------------------------------------------------------- r.lukasiak · 2023-10-26 05:09 Thank you very much for this explanation. Everthying makes perfect sense. However I'm still not sure why FadePosition=255 makes img1 disappear and the result=img2. 0-254 works exactly as you explanation indicates, giving a mix of both pics, depending on the FadePosition value. And indeed, PutImage  does exactly what I needed and it's really fast. thanks a lot! ---------------------------------------------------------------------- circular · 2023-10-26 06:34 I am happy the explanation made sense. Your assumption that there isn't a big difference between FadePosition=254 and FadePosition=255 is spot on. This is surprising indeed that there would be a significant different. A couple of scenarios might explain this: - if the draw mode is dmSetExceptTransparent: if the pixel has alpha 254 it won't be displayed but if it has alpha 255 it will. - if you draw the final image with parameter Opaque=True on a control but in fact the final image has transparent pixels: if a pixel has an alpha of 1, it will still be fully displayed on the control. Both cases can be addressed for example by first filling the target image with a background color. Were any of these scenarios applicable to your tests? ---------------------------------------------------------------------- Dzandaa · 2023-10-26 09:50 Hi, Is this that you want? PictOrig, PictDest, NewPict: TBGRABitmap; // Must be of the same size PBDest: TImage;  PictOrig: Picture with transparency PictDest: Picture without transparency NewPict: Result 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 // **************************// ***** Filter Picture *****// **************************procedure TMergeColorsForm.BFxPictureClick(Sender: TObject);var Pxo, Pxd, Pxn: PBGRAPixel; i: integer;begin if((PictOrig = nil) or (PictDest = nil)) then exit; if((PictOrig.Width <> PictDest.Width) or (PictOrig.Height <> PictDest.Height)) then exit; if(NewPict <> nil) then FreeAndNil(NewPict); NewPict := TBGRABitmap.Create(PictOrig.Width, PictOrig.Height);  Pxo := PictOrig.Data; Pxd := PictDest.Data; Pxn := NewPict.Data; for i := Pred(PictOrig.NbPixels) downto 0 do begin   if(Pxo^.alpha <> 0) then Pxn^:= Pxo^ else Pxn^ := Pxd^;   inc(Pxo);   inc(Pxd);   inc(Pxn); end; PBDest.Picture.Assign(NewPict) ; PBDest.Repaint;end;                         B-> ---------------------------------------------------------------------- r.lukasiak · 2023-10-27 03:12 @circular, the first scenario is exactly what I experiences. FadePosition=254 is giving me expected results, but FadePosition=255 completely ignored img1 and just returned img2 (or blended img1 and img2 but with abolsute bias towards img2).  Anyway I'm glad to know it's normal that FadePosition=255 behaves this one, it was just weird to me but now it's all clear. Cheers! @Dzandaa it seems to do exactly what I need but isn't it the same that PutImage does? Is there maybe some advantage of your solution over PutImage? thank you all! ---------------------------------------------------------------------- Dzandaa · 2023-10-27 09:18 @r.lukasiak I really don't know I just read your first post and tried to find a solution Check the speed for both. B-> ---------------------------------------------------------------------- circular · 2023-10-27 13:10 Quote from: r.lukasiak on October 27, 2023, 05:12:35 am@circular, the first scenario is exactly what I experiences. FadePosition=254 is giving me expected results, but FadePosition=255 completely ignored img1 and just returned img2 (or blended img1 and img2 but with abolsute bias towards img2).  Anyway I'm glad to know it's normal that FadePosition=255 behaves this one, it was just weird to me but now it's all clear. Cheers!Hi! So if I understand, you are using dmSetExceptTransparent for the draw mode? In this case, yes, it is expected that when alpha is 255 the pixel is shown and otherwise not. If you're happy with PutImage that's alright then. Otherwise you may want another Porter-Duff mode like "atop". I invite you to look at the table there to determine exactly what you are looking for: https://www .stat.auckland.ac.nz/~paul/Reports/GraphicsEngine/compositing/composit ing.html These modes are not all readily available in BGRABitmap, but it is implementable if we know exactly what you would like to do. The code proposed by @Dzanda can be adapted to one of the modes. For now, it is similar to PutImage without doing the alpha blending. ---------------------------------------------------------------------- r.lukasiak · 2023-10-30 13:42 @circular QuoteHi! So if I understand, you are using dmSetExceptTransparent for the draw mode? In this case, yes, it is expected that when alpha is 255 the pixel is shown and otherwise not.With FadePosition=255 I actually get the same result regardless the draw mode. img2 always overlaps img1. I just took a look at the CrossFade code and it looks like with FadePosition = 0 and FadePosition = 255 the result is fixed, the draw mode doesn't matter, only values 1-254 make the difference. @Dzandaa QuoteI really don't know It was silly asking you this question without actually testing your code. I finally got around to do it and it seems that your solution is ~3x faster than PutImage. Taking a look at PutImage code I see it's doing some blending and your solution doesn't. That's exactly what I was looking for, in my case alpha is either 0 or 255 so no need for any kind of blending. Thank you very much! ---------------------------------------------------------------------- circular · 2023-10-30 14:31 Hi @r.lukasiak Quote from: r.lukasiak on October 30, 2023, 02:42:27 pmWith FadePosition=255 I actually get the same result regardless the draw mode. img2 always overlaps img1. I just took a look at the CrossFade code and it looks like with FadePosition = 0 and FadePosition = 255 the result is fixed, the draw mode doesn't matter, only values 1-254 make the difference. As a matter of fact, yes, the mode parameter is used (passe as parameter to FillRect): 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  if AFadePosition = 0 then    FillRect(ARect, Source1, mode) else  if AFadePosition = 255 then    FillRect(ARect, Source2, mode) else    InternalCrossFade(ARect, Source1,Source2, AFadePosition,nil, mode);end; Well it seems you have what you need anyway. ---------------------------------------------------------------------- r.lukasiak · 2023-10-30 15:43 ohh true... I overlooked it. Anyway, yes, I got what I needed. I'd say, even more than I expected :-) Thanks!