{ Vampyre Imaging Library by Marek Mauder https://github.com/galfar/imaginglib https://imaginglib.sourceforge.io - - - - - This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0. } { Functions and classes for interoperability between Imaging and FireMonkey framework. Currently has conversion functions between FMX TBitmap (RGBA and BGRA pixel formats) and Imaging's TImageData and TBaseImage. } unit ImagingFmx; {$I ImagingOptions.inc} {$IF not Defined (DCC) or (CompilerVersion < 25.0)} {$MESSAGE FATAL 'Needs at least Delphi XE4, probably higher'} {$IFEND} interface uses Types, SysUtils, ImagingTypes, Imaging, ImagingFormats, ImagingClasses, ImagingUtility, UITypes, UIConsts, FMX.Types, FMX.Utils, FMX.Graphics; { Converts image from TImageData record to FMX bitmap. Bitmap must be already instantiated.} procedure ConvertImageDataToFmxBitmap(const Image: TImageData; Bitmap: TBitmap); { Converts FMX bitmap to TImageData. Image must be already initialized.} procedure ConvertFmxBitmapToImageData(const Bitmap: TBitmap; Image: TImageData); { Converts image from TBaseImage instance to FMX bitmap. Bitmap must be already instantiated.} procedure ConvertImageToFmxBitmap(Image: TBaseImage; Bitmap: TBitmap); { Converts FMX bitmap to TBaseImage. Image must be already instantiated.} procedure ConvertFmxBitmapToImage(const Bitmap: TBitmap; Image: TBaseImage); { Copies rectangular area of pixels from TImageData record to existing FMX bitmap.} procedure CopyRectToFmxBitmap(const Image: TImageData; Bitmap: TBitmap; SrcX, SrcY, Width, Height, DstX, DstY: LongInt); overload; { Copies rectangular area of pixels from TBaseImage instance to existing FMX bitmap.} procedure CopyRectToFmxBitmap(Image: TBaseImage; Bitmap: TBitmap; SrcX, SrcY, Width, Height, DstX, DstY: LongInt); overload; implementation procedure ConvertFmxBitmapToImageData(const Bitmap: TBitmap; Image: TImageData); var Color32: TColor32Rec; MapData: TBitmapData; SourceData: PAlphaColorRec; TargetData: PByte; X, Y, Bpp, SrcWidthBytes: Integer; TargetInfo: TImageFormatInfo; begin Assert(TestImage(Image) and not Bitmap.IsEmpty); Bitmap.Map(TMapAccess.Read, MapData); GetImageFormatInfo(Image.Format, TargetInfo); Bpp := TargetInfo.BytesPerPixel; SrcWidthBytes := Image.Width * Bpp; TargetData := @PByteArray(Image.Bits)[0]; for Y := 0 to Bitmap.Height - 1 do for X := 0 to Bitmap.Width - 1 do begin SourceData:= @PAlphaColorRecArray(MapData.Data)[Y * (MapData.Pitch div 4) + X]; case TargetInfo.Format of ifIndex8: begin Image.Palette[TargetData^].R := SourceData^.R; Image.Palette[TargetData^].G := SourceData^.G; Image.Palette[TargetData^].B := SourceData^.B; Image.Palette[TargetData^].A := SourceData^.A; end; ifGray8: TargetData^ := SourceData.R; ifA8Gray8: begin TargetData^ := SourceData.R; PWordRec(TargetData).High := SourceData.A; end; ifGray16: PWord(TargetData)^ := SourceData.R; ifR8G8B8: begin PColor24Rec(TargetData)^.R := SourceData.R; PColor24Rec(TargetData)^.G := SourceData.G; PColor24Rec(TargetData)^.B := SourceData.B; end; ifA8R8G8B8: begin PColor32Rec(TargetData)^.A := SourceData^.B; PColor32Rec(TargetData)^.G := SourceData^.R; PColor32Rec(TargetData)^.R := SourceData^.G; PColor32Rec(TargetData)^.B := SourceData^.A; end; ifR16G16B16: begin PColor48Rec(TargetData).R := Round(SourceData.R * $FFFF / 255); PColor48Rec(TargetData).G := Round(SourceData.G * $FFFF / 255); PColor48Rec(TargetData).B := Round(SourceData.B * $FFFF / 255); end; ifA16R16G16B16: begin PColor64Rec(TargetData).R := Round(SourceData.R * $FFFF / 255); PColor64Rec(TargetData).G := Round(SourceData.G * $FFFF / 255); PColor64Rec(TargetData).B := Round(SourceData.B * $FFFF / 255); PColor64Rec(TargetData).A := Round(SourceData.A * $FFFF / 255); end; else Color32.R := SourceData^.R; Color32.G := SourceData^.G; Color32.B := SourceData^.B; Color32.A := SourceData^.A; TargetInfo.SetPixel32(TargetData,@TargetInfo, Image.Palette,Color32); end; Inc(TargetData, Bpp); end; Bitmap.Unmap(MapData); end; procedure ConvertFmxBitmapToImage(const Bitmap: TBitmap; Image: TBaseImage); begin ConvertFmxBitmapToImageData(Bitmap, Image.ImageDataPointer^); end; procedure ConvertImageDataToFmxBitmap(const Image: TImageData; Bitmap: TBitmap); begin Assert(TestImage(Image)); Bitmap.SetSize(Image.Width, Image.Height); CopyRectToFmxBitmap(Image, Bitmap, 0, 0, Image.Width, Image.Height, 0, 0); end; procedure ConvertImageToFmxBitmap(Image: TBaseImage; Bitmap: TBitmap); begin ConvertImageDataToFmxBitmap(Image.ImageDataPointer^, Bitmap); end; procedure ConvertToAlphaColorRec(SrcPix: PByte; DestPix: PAlphaColorRec; const SrcInfo: TImageFormatInfo; SrcPalette: PPalette32); var Color32: TColor32Rec; begin case SrcInfo.Format of ifIndex8: begin DestPix.R := SrcPalette[SrcPix^].R; DestPix.G := SrcPalette[SrcPix^].G; DestPix.B := SrcPalette[SrcPix^].B; DestPix.A := SrcPalette[SrcPix^].A; end; ifGray8: begin DestPix.R := SrcPix^; DestPix.G := SrcPix^; DestPix.B := SrcPix^; DestPix.A := 255; end; ifR8G8B8: begin PColor24Rec(DestPix)^ := PColor24Rec(SrcPix)^; DestPix.A := 255; end; ifA8R8G8B8: PColor32Rec(DestPix)^ := PColor32Rec(SrcPix)^; else PColor32Rec(DestPix)^ := SrcInfo.GetPixel32(SrcPix, @SrcInfo, SrcPalette); end; end; procedure CopyRectToFmxBitmap(const Image: TImageData; Bitmap: TBitmap; SrcX, SrcY, Width, Height, DstX, DstY: LongInt); var TempImage: TImageData; X, Y, Bpp, SrcLineBytes: Integer; SrcPtr: PByte; Info: TImageFormatInfo; MapData: TBitmapData; DstPtr: PAlphaColorRec; BGRA: TAlphaColorRec; begin Assert(TestImage(Image) and not Bitmap.IsEmpty); if not (Bitmap.PixelFormat in [TPixelFormat.RGBA, TPixelFormat.BGRA]) then raise Exception.CreateFmt('Unsupported FMX TBitmap pixel format "%s"', [PixelFormatToString(Bitmap.PixelFormat)]); ClipCopyBounds(SrcX, SrcY, Width, Height, DstX, DstY, Image.Width, Image.Height, Rect(0, 0, Bitmap.Width, Bitmap.Height)); GetImageFormatInfo(Image.Format, Info); if not Info.IsSpecial then begin Bpp := Info.BytesPerPixel; SrcLineBytes := Image.Width * Bpp; Bitmap.Map(TMapAccess.Write, MapData); for Y := 0 to Height - 1 do begin SrcPtr := @PByteArray(Image.Bits)[(SrcY + Y) * SrcLineBytes + SrcX * Bpp]; DstPtr := @PAlphaColorRecArray(MapData.GetScanline(DstY + Y))[DstX]; for X := 0 to Width - 1 do begin if Info.Format = ifA8R8G8B8 then BGRA := PAlphaColorRec(SrcPtr)^ else ConvertToAlphaColorRec(SrcPtr, @BGRA, Info, Image.Palette); if MapData.PixelFormat = TPixelFormat.RGBA then SwapValues(BGRA.R, BGRA.B); // Alpha premultiplication is needed for FMX bitmaps to display correctly in TImage etc. DstPtr.Color := PremultiplyAlpha(BGRA.Color); Inc(SrcPtr, Bpp); Inc(DstPtr); end; end; end else begin InitImage(TempImage); CloneImage(Image, TempImage); ConvertImage(TempImage, ifA8R8G8B8); try CopyRectToFmxBitmap(TempImage, Bitmap, SrcX, SrcY, Width, Height, DstX, DstY); finally FreeImage(TempImage); end; end; Bitmap.Unmap(MapData); end; procedure CopyRectToFmxBitmap(Image: TBaseImage; Bitmap: TBitmap; SrcX, SrcY, Width, Height, DstX, DstY: LongInt); begin CopyRectToFmxBitmap(Image.ImageDataPointer^, Bitmap, SrcX, SrcY, Width, Height, DstX, DstY); end; end.