unit ksBitmapHelper;
// 部分拷贝自 FlyUtils.TBitmapHelper
{$DEFINE FMX}
interface
uses
{$IFDEF FMX}
FMX.Graphics,
FMX.Utils,
FMX.Types,
FMX.Surfaces,
{$ELSE}
Vcl.Graphics,
{$ENDIF}
System.Types,
System.UITypes,
System.Classes;
{$IFDEF FMX}
{$ELSE}
resourcestring
SBitmapSavingFailed = 'Saving bitmap failed.';
SBitmapSavingFailedNamed = 'Saving bitmap failed (%s).';
{$ENDIF}
var
MonochromeChange_Threshold: Byte = 120;
MonochromeChange_Weighting_Red: Double = 0.3;
MonochromeChange_Weighting_Green: Double = 0.59;
MonochromeChange_Weighting_Blue: Double = 0.11;
type
///
/// 转黑白的方法
///
TMonochromeChangeType = (
///
/// 平均值
///
Average,
///
/// 权值
///
Weighting,
///
/// 最大值
///
Max);
///
///
/// TBitmap Save As BMP
///
///
/// BytesPerPixel = -1 表示自动
///
///
TKngStrBitmapHelper = class helper for TBitmap
public
function SaveAsBMPToFile(const AFileName: string; const BytesPerPixel: Integer = 3;
const MonochromeChangeType: TMonochromeChangeType = TMonochromeChangeType.Average): Boolean; overload;
procedure SaveAsBMPToFileDef(const AFileName: string); overload;
function SaveAsBMPToStream(const AStream: TStream; const BytesPerPixel: Integer = 3;
const MonochromeChangeType: TMonochromeChangeType = TMonochromeChangeType.Average): Boolean; overload;
procedure SaveAsBMPToStreamDef(Stream: TStream); overload;
{$IFDEF FMX}
///
/// 用于在线程中代替 LoadFromFile ,不用自己调用 Synchronize 了。
///
procedure SyncLoadFromFile(const AFileName: string);
///
/// 用于在线程中代替 LoadThumbnailFromFile ,不用自己调用 Synchronize 了。
///
///
/// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
///
procedure SyncLoadThumbnailFromFile(const AFileName: string; const AFitWidth, AFitHeight: Single;
const UseEmbedded: Boolean = True);
///
/// 用于在线程中代替 LoadFromStream ,不用自己调用 Synchronize 了。
///
procedure SyncLoadFromStream(Stream: TStream);
///
/// 用于在线程中代替 LoadThumbnailFromStream ,不用自己调用 Synchronize 了。
///
///
/// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
///
procedure SyncLoadThumbnailFromStream(const Stream: TStream; const AFitWidth, AFitHeight: Single;
const UseEmbedded: Boolean = True; const AutoCut: Boolean = True); overload;
function SyncLoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
const UseEmbedded: Boolean): Boolean; overload;
///
/// 用于在线程中代替 Assign ,不用自己调用 Synchronize 了。
///
procedure SyncAssign(Source: TPersistent);
{$ENDIF}
end;
{$IFDEF FMX}
TBitmapCodecDescriptorField = (Extension, Description);
TKngStrBitmapCodecManager = class helper for TBitmapCodecManager
strict private
class function GuessCodecClass(const Name: string; const Field: TBitmapCodecDescriptorField): TCustomBitmapCodecClass;
public
class function GetImageSize(const AFileName: string): TPointF; overload;
class function GetImageSize(const AStream: TStream): TPointF; overload;
class function LoadFromStream(const AStream: TStream; const Bitmap: TBitmapSurface;
const MaxSizeLimit: Cardinal = 0): Boolean;
///
/// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
///
class function LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
const UseEmbedded: Boolean; const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
///
/// UseEmbedded 只有Windows下可用,而且缩略图大小不可控
///
class function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
end;
{$ENDIF}
{$IFDEF FMX}
{$ELSE}
procedure GraphicToBitmap(const Src: Vcl.Graphics.TGraphic;
const Dest: Vcl.Graphics.TBitmap; const TransparentColor: Vcl.Graphics.TColor = Vcl.Graphics.clNone);
{$ENDIF}
const
SizeOftagBITMAPFILEHEADER = 14;
SizeOftagBITMAPINFOHEADER = 40;
RGB565ExtDataLen = 12;
{$IFDEF FMX}
function FillScanLineFormMemory(BitmapData: TBitmapData; ScanLineIndex, Width: integer;
InputData: Pointer; InputFormat: TPixelFormat): Boolean;
type
// copy form [广州]庾伟洪
TBitmap16bitFiler = class
class var
Color: array [0 .. $FFFF] of TAlphaColor;
class constructor Create;
class procedure FillScanLine(D: TBitmapData; ScanLine, Width: integer;
data: Pointer); inline;
end;
// PAlphaColorArray = ^TAlphaColorArray; //FMX.Utils,
// TWordArray = array [0 .. 0] of Word;
// PWordArray = ^TWordArray; //System.SysUtils
// y 行号 h 高度 w 宽度 b 一个像素用字节数
// FVideoBuf 数据内存
// bm.Map(TMapAccess.Write, bd);
// try
// for y := 0 to h - 1 do
// begin
// TBitmap16bitFiler.FillScanLine(bd, y, w, Addr(FVideoBuf[y * w * b]));
// end;
// finally
// bm.Unmap(bd);
// end;
{$ENDIF}
implementation
uses
{$IFDEF FMX}
FMX.Consts,
{$IFDEF ANDROID}
FMX.Graphics.Android,
Androidapi.JNIBridge,
Androidapi.Helpers,
FMX.Helpers.Android,
Androidapi.JNI.GraphicsContentViewText,
{$ENDIF}
{$IFDEF MSWINDOWS}
FMX.Canvas.D2D, Winapi.Wincodec, Winapi.Windows,
{$ENDIF}
{$ELSE}
Vcl.Consts,
{$ENDIF}
System.SysConst,
System.SysUtils,
System.Math;
//add by 爱吃猪头肉。
type
//感谢 yu 273637089 的测试和 提供 packed 信息。
{$IFDEF FMX}
{$ELSE}
tagRGBTRIPLE = packed record
rgbtBlue: Byte;
rgbtGreen: Byte;
rgbtRed: Byte;
end;
PRGBTripleArray = ^TRGBTripleArray;
TRGBTripleArray = array [Byte] of tagRGBTRIPLE;
{$ENDIF}
tagBITMAPFILEHEADER = packed record
bfType: Word;
bfSize: DWORD;
bfReserved1: Word;
bfReserved2: Word;
bfOffBits: DWORD;
end;
tagBITMAPINFOHEADER = packed record
biSize: DWORD;
biWidth: Longint;
biHeight: Longint;
biPlanes: Word;
biBitCount: Word;
biCompression: DWORD;
biSizeImage: DWORD;
biXPelsPerMeter: Longint;
biYPelsPerMeter: Longint;
biClrUsed: DWORD;
biClrImportant: DWORD;
end;
tagRGBQUAD = packed record
rgbBlue: Byte;
rgbGreen: Byte;
rgbRed: Byte;
rgbReserved: Byte;
end;
{$IFDEF FMX}
{$IFDEF ANDROID}
TBitmapCodecAndroid = class(FMX.Graphics.Android.TBitmapCodecAndroid)
private
class function IsGIFStream(const Stream: TStream): Boolean;
function LoadMovieFromStreamScaled(const AStream: TStream;
const Surface: TBitmapSurface; const FitSize: TPoint): Boolean;
function LoadMovieFromStream(const Stream: TStream;
const Surface: TBitmapSurface): Boolean;
function StretchIfNeed(const SrcBitmap: JBitmap;
const Bitmap: TBitmapSurface; const LoadOptions: JBitmapFactory_Options;
const MaxSizeLimit: Cardinal): Boolean;
class function GetMovieSize(const Stream: TStream): TPoint;
public
class function GetImageSize(const AFileName: string): TPointF; overload;
class function GetImageSize(const AStream: TStream): TPointF; overload;
function LoadFromStream(const AStream: TStream; const Bitmap: TBitmapSurface;
const MaxSizeLimit: Cardinal): Boolean; override;
function LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
const UseEmbedded: Boolean; const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
end;
{$ENDIF}
{$IFDEF MSWINDOWS}
TBitmapCodecWIC = class(FMX.Canvas.D2D.TCustomBitmapCodecWIC)
private
function DecodeFrame(const Frame: IWICBitmapFrameDecode; const Bitmap: TBitmapSurface;
const MaxSizeLimit: Cardinal = 0): Boolean;
public
class function GetImageSize(const AStream: TStream): TPointF; overload;
function LoadThumbnailFromStream(const AStream: TStream; const AFitWidth, AFitHeight: Single;
const UseEmbedded: Boolean; const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
function LoadThumbnailFromStream(const AStream: TStream; const ASrc, ADest: TRectF;
const UseEmbedded: Boolean; const Bitmap: TBitmapSurface): Boolean; overload;
end;
{$ENDIF}
{$ENDIF}
const
{ constants for the biCompression field }
{$EXTERNALSYM BI_RGB}
BI_RGB = 0;
{$EXTERNALSYM BI_RLE8}
BI_RLE8 = 1;
{$EXTERNALSYM BI_RLE4}
BI_RLE4 = 2;
{$EXTERNALSYM BI_BITFIELDS}
BI_BITFIELDS = 3;
const
RGB565_MASK_RED = $F800;
RGB565_MASK_GREEN = $07E0;
RGB565_MASK_BLUE = $001F;
// RGB565ExtDataLen = 12;
function rgb_24_2_565(r,g,b: Byte): UInt16;
begin
// r := r * 31 div 255;
// g := g * 64 div 255;
// b := b * 31 div 255;
// Result := r *2048 or g *32 or b;
// Result := ((r shl 8) and RGB565_MASK_RED) or ((g shl 3) and RGB565_MASK_GREEN) or (b shr 3);
Result := ((r shr 3) shl 11) or ((g shr 2 ) shl 5) or (b shr 3);
end;
{
return (USHORT)(((unsigned(r) << 8) & 0xF800) |
((unsigned(g) << 3) & 0x7E0) |
((unsigned(b) >> 3)));
}
procedure rgb565_2_rgb24(rgb24: TBytes; rgb565: UInt16);
begin
//extract RGB
rgb24[2] := (rgb565 and RGB565_MASK_RED) shr 11;
rgb24[1] := (rgb565 and RGB565_MASK_GREEN) shr 5;
rgb24[0] := (rgb565 and RGB565_MASK_BLUE);
//amplify the image
rgb24[2] := rgb24[2] shl 3;
rgb24[1] := rgb24[2] shl 2;
rgb24[0] := rgb24[2] shl 3;
end;
{
//extract RGB
rgb24[2] = (rgb565 & RGB565_MASK_RED) >> 11;
rgb24[1] = (rgb565 & RGB565_MASK_GREEN) >> 5;
rgb24[0] = (rgb565 & RGB565_MASK_BLUE);
//amplify the image
rgb24[2] <<= 3;
rgb24[1] <<= 2;
rgb24[0] <<= 3;
}
const
RGB555_MASK_RED = $7C00;
RGB555_MASK_GREEN = $03E0;
RGB555_MASK_BLUE = $001F;
function rgb_24_2_555(r,g,b: Byte): UInt16;
begin
Result := ((r shl 7) and RGB555_MASK_RED) or ((g shl 2) and RGB555_MASK_GREEN) or (b shr 3);
end;
procedure rgb555_2_rgb24(rgb24: TBytes; rgb555: UInt16);
begin
//extract RGB
rgb24[0] := (rgb555 shl 3) and $00F8;
rgb24[1] := (rgb555 shr 2) and $00F8;
rgb24[2] := (rgb555 shr 7) and $00F8;
end;
{$IFDEF FMX}
{$ELSE}
procedure GraphicToBitmap(const Src: Vcl.Graphics.TGraphic;
const Dest: Vcl.Graphics.TBitmap; const TransparentColor: Vcl.Graphics.TColor = Vcl.Graphics.clNone);
begin
// Do nothing if either source or destination are nil
if not Assigned(Src) or not Assigned(Dest) then
Exit;
if Src.Empty then exit;
// Size the bitmap
Dest.Width := Src.Width;
Dest.Height := Src.Height;
if Src.Transparent then
begin
// Source graphic is transparent, make bitmap behave transparently
Dest.Transparent := True;
if (TransparentColor <> Vcl.Graphics.clNone) then
begin
// Set destination as transparent using required colour key
Dest.TransparentColor := TransparentColor;
Dest.TransparentMode := Vcl.Graphics.tmFixed;
// Set background colour of bitmap to transparent colour
Dest.Canvas.Brush.Color := TransparentColor;
end
else
// No transparent colour: set transparency to automatic
Dest.TransparentMode := Vcl.Graphics.tmAuto;
end;
// Clear bitmap to required background colour and draw bitmap
Dest.Canvas.FillRect(System.Classes.Rect(0, 0, Dest.Width, Dest.Height));
Dest.Canvas.Draw(0, 0, Src);
end;
{$ENDIF}
//该代码片段来自于: http://www.sharejs.com/codes/delphi/2248
{ TKngStrBitmapHelper }
function TKngStrBitmapHelper.SaveAsBMPToFile(const AFileName: string; const BytesPerPixel: Integer = 3;
const MonochromeChangeType: TMonochromeChangeType = TMonochromeChangeType.Average): Boolean;
var
AStream: TStream;
begin
Result := False;
{$IFDEF FMX}
if IsEmpty then exit;
{$ELSE}
if Empty then exit;
{$ENDIF}
AStream := TFileStream.Create(AFileName, fmCreate);// or fmOpenReadWrite);
try
Result := SaveAsBMPToStream(AStream, BytesPerPixel);
// if Result then
// AStream.Size := AStream.Position;
finally
FreeAndNil(AStream);
end;
end;
procedure TKngStrBitmapHelper.SaveAsBMPToFileDef(const AFileName: string);
begin
if not SaveAsBMPToFile(AFileName) then
begin
{$IFDEF FMX}
raise EBitmapSavingFailed.CreateFMT(SBitmapSavingFailed, [AFileName]);
{$ELSE}
raise EInvalidGraphicOperation.CreateFmt(SBitmapSavingFailed, [AFileName]);
{$ENDIF}
end;
end;
//http://blog.csdn.net/pjpsmile/article/details/8985523
function TKngStrBitmapHelper.SaveAsBMPToStream(const AStream: TStream; const BytesPerPixel: Integer = 3;
const MonochromeChangeType: TMonochromeChangeType = TMonochromeChangeType.Average): Boolean;
var
I, CurrBytesPerPixel,
wWidth, nCol, wRow, wByteIdex,
bfReserved1, bfReserved2,
nBmpWidth, nBmpHeight, bufferSize: Integer;
BitmapInfo: tagBITMAPINFOHEADER;
BMF: tagBITMAPFILEHEADER;
{$IFDEF FMX}
Data: TBitmapData;
clr: TAlphaColor;
AlphaColorBuffer: PAlphaColor;
{$ELSE}
Pixs : pRGBTripleArray;
{$ENDIF}
bmpData: TBytes;
A32BitData: UInt32;
ABitIndex,
A8BitData: Byte;
RGBQUAD: tagRGBQUAD;
// FileSizeFix,
A16BitData,
GrayeData: UInt16;
begin
Result := False;
{$IFDEF FMX}
if IsEmpty then exit;
{$ELSE}
if Empty then exit;
{$ENDIF}
if not Assigned(AStream) then exit;
CurrBytesPerPixel := BytesPerPixel;
{$IFDEF FMX}
Map(TMapAccess.Read, Data);
{$ELSE}
{$ENDIF}
try
if CurrBytesPerPixel = -1 then
begin
{$IFDEF FMX}
CurrBytesPerPixel := Data.BytesPerPixel;
{$ELSE}
CurrBytesPerPixel := 3;
if PixelFormat = pf1bit then
CurrBytesPerPixel := 0;
if PixelFormat = pf4bit then
CurrBytesPerPixel := 2;
if PixelFormat = pf8bit then
CurrBytesPerPixel := 2;
if PixelFormat = pf15bit then
CurrBytesPerPixel := 2;
if PixelFormat = pf16bit then
CurrBytesPerPixel := 2;
if PixelFormat = pf24bit then
CurrBytesPerPixel := 3;
if PixelFormat = pf32bit then
CurrBytesPerPixel := 4;
{$ENDIF}
if not (CurrBytesPerPixel in [0,1,2,4]) then
CurrBytesPerPixel := 3;
end;
if not (CurrBytesPerPixel in [0,1,2,3,4]) then
exit;
//不打算支持 8 位的。
if CurrBytesPerPixel = 1 then exit;
{$IFDEF FMX}
nBmpWidth := Data.Width;
nBmpHeight := Data.Height;
{$ELSE}
nBmpWidth := Width;
nBmpHeight := Height;
{$ENDIF}
// 像素扫描
if CurrBytesPerPixel > 0 then
begin
wWidth := nBmpWidth * CurrBytesPerPixel;
if (wWidth mod 4) > 0 then
begin
wWidth := wWidth + 4 - (wWidth mod 4);
end;
end
else if (nBmpWidth mod 32) > 0 then
begin
wWidth := ((nBmpWidth div 32) + 1) * 4;;
end
else
begin
wWidth := (nBmpWidth div 8);
end;
bufferSize := nBmpHeight * wWidth;
// bmp文件头
BMF.bfType := $4D42;
BMF.bfSize := 14 + 40 + bufferSize;
BMF.bfReserved1 := 0;
BMF.bfReserved2 := 0;
BMF.bfOffBits := 14 + 40;
if (CurrBytesPerPixel = 0) then
begin
BMF.bfOffBits := BMF.bfOffBits + 2 * Sizeof(RGBQUAD);
BMF.bfSize := BMF.bfSize + 2 * Sizeof(RGBQUAD);
end;
if (CurrBytesPerPixel = 1) then
begin
BMF.bfOffBits := BMF.bfOffBits + 256 * Sizeof(RGBQUAD);
BMF.bfSize := BMF.bfSize + 256 * Sizeof(RGBQUAD);
end;
if (CurrBytesPerPixel = 2) then
begin
//多谢 [西安]老一门(yyimen@foxmail.com) 提供的 565 格式说明。
BMF.bfOffBits := BMF.bfOffBits + RGB565ExtDataLen;
BMF.bfSize := BMF.bfSize + RGB565ExtDataLen;
end;
// FileSizeFix := 0;
// if (BMF.bfSize mod 4) > 0 then
// begin
// FileSizeFix := 4 - BMF.bfSize mod 4;
// end;
// bufferSize := bufferSize + FileSizeFix;
// BMF.bfSize := BMF.bfSize + FileSizeFix;
// 保存bmp文件头
AStream.WriteBuffer(BMF, Sizeof(BMF));
// bmp信息头
FillChar(BitmapInfo, Sizeof(BitmapInfo), 0);
BitmapInfo.biSize := 40;
// if (CurrBytesPerPixel = 2) then
// begin
// //AcdSee 不支持这种大小的 BitmapInfo
// BitmapInfo.biSize := 40 + RGB565ExtDataLen;
// end;
BitmapInfo.biWidth := nBmpWidth;
BitmapInfo.biHeight := nBmpHeight;
BitmapInfo.biPlanes := 1;
if CurrBytesPerPixel > 0 then
begin
BitmapInfo.biBitCount := CurrBytesPerPixel * 8;
end
else
begin
BitmapInfo.biBitCount := 1;
end;
BitmapInfo.biSizeImage := bufferSize;
// if True then
// begin
// //96
// BitmapInfo.biXPelsPerMeter := $0EC4;
// BitmapInfo.biYPelsPerMeter := $0EC4;
// end
// else
// begin
// //72
// BitmapInfo.biXPelsPerMeter := $0B12;
// BitmapInfo.biYPelsPerMeter := $0B12;
// end;
BitmapInfo.biXPelsPerMeter := 0;
BitmapInfo.biYPelsPerMeter := 0;
if (CurrBytesPerPixel = 2) then
begin
//可以采用 RGB555 代替 RGB565
BitmapInfo.biCompression := BI_BITFIELDS; //0是 555 3 是 565
end;
// 保存bmp信息头
AStream.WriteBuffer(BitmapInfo, Sizeof(BitmapInfo));
if (CurrBytesPerPixel = 2) then
begin
// 保存 565 RGB Mask
A32BitData := RGB565_MASK_RED;
AStream.WriteBuffer(A32BitData, Sizeof(A32BitData));
A32BitData := RGB565_MASK_GREEN;
AStream.WriteBuffer(A32BitData, Sizeof(A32BitData));
A32BitData := RGB565_MASK_BLUE;
AStream.WriteBuffer(A32BitData, Sizeof(A32BitData));
if RGB565ExtDataLen >= 16 then
begin
A32BitData := 0;
AStream.WriteBuffer(A32BitData, Sizeof(A32BitData));
end;
end;
if (CurrBytesPerPixel = 0) or (CurrBytesPerPixel = 1) then
begin
//颜色表
RGBQUAD.rgbBlue := 0;
RGBQUAD.rgbGreen := 0;
RGBQUAD.rgbRed := 0;
RGBQUAD.rgbReserved := 0;
if (CurrBytesPerPixel = 1) then
begin
for I := 0 to 255 do
begin
AStream.WriteBuffer(RGBQUAD, Sizeof(RGBQUAD));
end;
BitmapInfo.biClrUsed := $FF;
end
else
begin
AStream.WriteBuffer(RGBQUAD, Sizeof(RGBQUAD));
RGBQUAD.rgbBlue := $FF;
RGBQUAD.rgbGreen := $FF;
RGBQUAD.rgbRed := $FF;
RGBQUAD.rgbReserved := 0;
AStream.WriteBuffer(RGBQUAD, Sizeof(RGBQUAD));
BitmapInfo.biClrUsed := 2;
end;
end;
// 像素扫描
SetLength(bmpData, wWidth);
{$IFDEF FMX}
AlphaColorBuffer := GetMemory(nBmpWidth * SizeOf(TAlphaColor));
try
{$ENDIF}
for nCol := nBmpHeight - 1 downto 0 do
begin
FillChar(bmpData[0], wWidth, 0);
wByteIdex := 0;
//0 是单色图
if (CurrBytesPerPixel = 0) or (CurrBytesPerPixel = 1) then
begin
A8BitData := 0;
ABitIndex := 0;
{$IFDEF FMX}
{$ELSE}
Pixs := ScanLine[nCol];
{$ENDIF}
for wRow := 0 to nBmpWidth - 1 do
begin
{$IFDEF FMX}
//X 是行坐标,Y 是 列坐标。
clr := Data.GetPixel(wRow, nCol);
{$ELSE}
{$ENDIF}
GrayeData := 0;
{$IFDEF FMX}
if MonochromeChangeType = TMonochromeChangeType.Average then
begin
GrayeData := TAlphaColorRec(clr).R + TAlphaColorRec(clr).G + TAlphaColorRec(clr).B;
GrayeData := GrayeData div 3;
end;
if MonochromeChangeType = TMonochromeChangeType.Weighting then
begin
GrayeData := Round((TAlphaColorRec(clr).R * MonochromeChange_Weighting_Red +
TAlphaColorRec(clr).G * MonochromeChange_Weighting_Green +
TAlphaColorRec(clr).B * MonochromeChange_Weighting_Blue) / 3);
end;
if MonochromeChangeType = TMonochromeChangeType.Max then
begin
GrayeData := System.Math.Max(System.Math.Max(TAlphaColorRec(clr).R, TAlphaColorRec(clr).G),
TAlphaColorRec(clr).B);
end;
{$ELSE}
if MonochromeChangeType = TMonochromeChangeType.Average then
begin
GrayeData := Pixs[wRow].rgbtRed + Pixs[wRow].rgbtGreen + Pixs[wRow].rgbtBlue;
GrayeData := GrayeData div 3;
end;
if MonochromeChangeType = TMonochromeChangeType.Weighting then
begin
GrayeData := Round((Pixs[wRow].rgbtRed * MonochromeChange_Weighting_Red +
Pixs[wRow].rgbtGreen * MonochromeChange_Weighting_Green +
Pixs[wRow].rgbtBlue * MonochromeChange_Weighting_Blue) / 3);
end;
if MonochromeChangeType = TMonochromeChangeType.Max then
begin
GrayeData := System.Math.Max(System.Math.Max(Pixs[wRow].rgbtRed, Pixs[wRow].rgbtGreen),
Pixs[wRow].rgbtBlue);
end;
{$ENDIF}
if GrayeData > MonochromeChange_Threshold then
begin
A8BitData := A8BitData or ($80 shr ABitIndex);
end;
inc(ABitIndex);
if ABitIndex > 7 then
begin
ABitIndex := 0;
if (CurrBytesPerPixel = 0) then
begin
bmpData[wByteIdex] := A8BitData;
A8BitData := 0;
inc(wByteIdex);
end;
end;
end;
if (CurrBytesPerPixel = 0) then
begin
if ABitIndex > 0 then
begin
bmpData[wByteIdex] := A8BitData;
A8BitData := 0;
end;
end;
end
else
begin
{$IFDEF FMX}
// for wRow := 0 to nBmpWidth - 1 do
// begin
// //X 是行坐标,Y 是 列坐标。
// clr := Data.GetPixel(wRow, nCol);
// case CurrBytesPerPixel of
// 1:
// begin
// //不支持。
// end;
// 2:
// begin
// A16BitData := rgb_24_2_565(TAlphaColorRec(clr).R, TAlphaColorRec(clr).G, TAlphaColorRec(clr).B);
// bmpData[wByteIdex + 0] := WordRec(A16BitData).Lo;
// bmpData[wByteIdex + 1] := WordRec(A16BitData).Hi;
// end;
// 3,4:
// begin
// bmpData[wByteIdex + 0] := TAlphaColorRec(clr).B;
// bmpData[wByteIdex + 1] := TAlphaColorRec(clr).G;
// bmpData[wByteIdex + 2] := TAlphaColorRec(clr).R;
// end;
// end;
// if CurrBytesPerPixel = 4 then
// begin
// bmpData[wByteIdex + 3] := TAlphaColorRec(clr).A;
// end;
// Inc(wByteIdex, CurrBytesPerPixel);
// end;
case CurrBytesPerPixel of
1:
begin
//不支持。
end;
2:
begin
ScanlineToAlphaColor(Data.GetScanline(nCol), AlphaColorBuffer, nBmpWidth, Data.PixelFormat);
AlphaColorToScanline(AlphaColorBuffer, Addr(bmpData[0]), nBmpWidth, TPixelFormat.BGR_565);
end;
3:
begin
ScanlineToAlphaColor(Data.GetScanline(nCol), AlphaColorBuffer, nBmpWidth, Data.PixelFormat);
AlphaColorToScanline(AlphaColorBuffer, Addr(bmpData[0]), nBmpWidth, TPixelFormat.BGR);
end;
4:
begin
if Data.PixelFormat = TPixelFormat.BGRA then
begin
Move(PByte(Data.GetScanline(nCol))[0], bmpData[0], Data.BytesPerPixel * nBmpWidth);
end
else
begin
ScanlineToAlphaColor(Data.GetScanline(nCol), AlphaColorBuffer, nBmpWidth, Data.PixelFormat);
AlphaColorToScanline(AlphaColorBuffer, Addr(bmpData[0]), nBmpWidth, TPixelFormat.BGRA);
end;
end;
end;
{$ELSE}
Pixs := ScanLine[nCol];
for wRow := 0 to nBmpWidth - 1 do
begin
case CurrBytesPerPixel of
1:
begin
//不支持。
end;
2:
begin
A16BitData := rgb_24_2_565(Pixs[wRow].rgbtRed, Pixs[wRow].rgbtGreen, Pixs[wRow].rgbtBlue);
bmpData[wByteIdex + 0] := WordRec(A16BitData).Lo;
bmpData[wByteIdex + 1] := WordRec(A16BitData).Hi;
end;
3,4:
begin
bmpData[wByteIdex + 0] := Pixs[wRow].rgbtBlue;
bmpData[wByteIdex + 1] := Pixs[wRow].rgbtGreen;
bmpData[wByteIdex + 2] := Pixs[wRow].rgbtRed;
end;
end;
if CurrBytesPerPixel = 4 then
begin
bmpData[wByteIdex + 3] := $FF;
end;
Inc(wByteIdex, CurrBytesPerPixel);
end;
{$ENDIF}
end;
AStream.WriteBuffer(bmpData, wWidth);
end;
{$IFDEF FMX}
finally
FreeMemory(AlphaColorBuffer);
end;
{$ENDIF}
// A8BitData := 0;
// for I := 0 to FileSizeFix - 1 do
// begin
// AStream.WriteBuffer(A8BitData, 1);
// end;
Result := True;
finally
{$IFDEF FMX}
Unmap(Data);
{$ELSE}
{$ENDIF}
end;
end;
procedure TKngStrBitmapHelper.SaveAsBMPToStreamDef(Stream: TStream);
begin
if not SaveAsBMPToStream(Stream) then
begin
{$IFDEF FMX}
raise EBitmapSavingFailed.Create(SBitmapSavingFailed);
{$ELSE}
raise EInvalidGraphicOperation.Create(SBitmapSavingFailed);
{$ENDIF}
end;
end;
{$IFDEF FMX}
procedure TKngStrBitmapHelper.SyncAssign(Source: TPersistent);
begin
TThread.Synchronize(nil,
procedure
begin
Assign(Source);
end)
end;
procedure TKngStrBitmapHelper.SyncLoadFromFile(const AFileName: string);
var
Surf: TBitmapSurface;
begin
Surf := TBitmapSurface.Create;
try
if TBitmapCodecManager.LoadFromFile(AFileName, Surf, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then
TThread.Synchronize(nil,
procedure
begin
Assign(Surf);
end)
else
raise EBitmapLoadingFailed.CreateFMT(SBitmapLoadingFailedNamed, [AFileName]);
finally
Surf.Free;
end;
end;
procedure TKngStrBitmapHelper.SyncLoadFromStream(Stream: TStream);
var
S: TStream;
Surf: TBitmapSurface;
begin
if Stream.Position > 0 then
begin
// need to create temp stream
S := TMemoryStream.Create;
try
S.CopyFrom(Stream, Stream.Size - Stream.Position);
S.Position := 0;
Surf := TBitmapSurface.Create;
try
if TBitmapCodecManager.LoadFromStream(S, Surf, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then
TThread.Synchronize(nil,
procedure
begin
Assign(Surf);
end)
else
raise EBitmapLoadingFailed.Create(SBitmapLoadingFailed);
finally
Surf.Free;
end;
finally
S.Free;
end;
end
else
if Stream.Size = 0 then
Clear(0)
else begin
Surf := TBitmapSurface.Create;
try
if TBitmapCodecManager.LoadFromStream(Stream, Surf, CanvasClass.GetAttribute(TCanvasAttribute.MaxBitmapSize)) then
TThread.Synchronize(nil,
procedure
begin
Assign(Surf);
end)
else
raise EBitmapLoadingFailed.Create(SBitmapLoadingFailed);
finally
Surf.Free;
end;
end;
end;
procedure TKngStrBitmapHelper.SyncLoadThumbnailFromFile(const AFileName: string; const AFitWidth, AFitHeight: Single;
const UseEmbedded: Boolean = True);
var
Surf: TBitmapSurface;
begin
Surf := TBitmapSurface.Create;
try
if TBitmapCodecManager.LoadThumbnailFromFile(AFileName, AFitWidth, AFitHeight, UseEmbedded, Surf) then
TThread.Synchronize(nil,
procedure
begin
Assign(Surf);
end)
else
raise EThumbnailLoadingFailed.CreateFMT(SThumbnailLoadingFailedNamed, [AFileName]);
finally
Surf.Free;
end;
end;
function TKngStrBitmapHelper.SyncLoadThumbnailFromStream(const AStream: TStream;
const ASrc, ADest: TRectF; const UseEmbedded: Boolean): Boolean;
var
S: TStream;
Surf: TBitmapSurface;
begin
if AStream.Position > 0 then
begin
// need to create temp stream
S := TMemoryStream.Create;
try
S.CopyFrom(AStream, AStream.Size - AStream.Position);
S.Position := 0;
Surf := TBitmapSurface.Create;
try
if TBitmapCodecManager.LoadThumbnailFromStream(S, ASrc, ADest, UseEmbedded, Surf) then
TThread.Synchronize(nil,
procedure
begin
Assign(Surf);
end)
else
raise EBitmapLoadingFailed.Create(SThumbnailLoadingFailed);
finally
Surf.Free;
end;
finally
S.Free;
end;
end
else
if AStream.Size = 0 then
Clear(0)
else begin
Surf := TBitmapSurface.Create;
try
if TBitmapCodecManager.LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Surf) then
TThread.Synchronize(nil,
procedure
begin
Assign(Surf);
end)
else
raise EBitmapLoadingFailed.Create(SThumbnailLoadingFailed);
finally
Surf.Free;
end;
end;
end;
procedure TKngStrBitmapHelper.SyncLoadThumbnailFromStream(const Stream: TStream;
const AFitWidth, AFitHeight: Single; const UseEmbedded: Boolean; const AutoCut: Boolean);
var
S: TStream;
Surf: TBitmapSurface;
begin
if Stream.Position > 0 then
begin
// need to create temp stream
S := TMemoryStream.Create;
try
S.CopyFrom(Stream, Stream.Size - Stream.Position);
S.Position := 0;
Surf := TBitmapSurface.Create;
try
if TBitmapCodecManager.LoadThumbnailFromStream(S, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Surf) then
TThread.Synchronize(nil,
procedure
begin
Assign(Surf);
end)
else
raise EBitmapLoadingFailed.Create(SThumbnailLoadingFailed);
finally
Surf.Free;
end;
finally
S.Free;
end;
end
else
if Stream.Size = 0 then
Clear(0)
else begin
Surf := TBitmapSurface.Create;
try
if TBitmapCodecManager.LoadThumbnailFromStream(Stream, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Surf) then
TThread.Synchronize(nil,
procedure
begin
Assign(Surf);
end)
else
raise EBitmapLoadingFailed.Create(SThumbnailLoadingFailed);
finally
Surf.Free;
end;
end;
end;
{ TKngStrBitmapCodecManager }
class function TKngStrBitmapCodecManager.GetImageSize(const AStream: TStream): TPointF;
var
CodecClass: TCustomBitmapCodecClass;
DataType: String;
begin
DataType := TImageTypeChecker.GetType(AStream);
CodecClass := GuessCodecClass(DataType, TBitmapCodecDescriptorField.Extension);
if CodecClass <> nil then
{$IFDEF ANDROID}
Result := TBitmapCodecAndroid(CodecClass).GetImageSize(AStream)
{$ELSE}
Result := TBitmapCodecWIC(CodecClass).GetImageSize(AStream)
{$ENDIF}
else
Result := TPointF.Zero;
end;
class function TKngStrBitmapCodecManager.GetImageSize(const AFileName: string): TPointF;
begin
Result := inherited GetImageSize(AFileName);
end;
class function TKngStrBitmapCodecManager.GuessCodecClass(const Name: string;
const Field: TBitmapCodecDescriptorField): TCustomBitmapCodecClass;
type
TPrivateMethodType = function (const Name: string; const Field: TBitmapCodecDescriptorField):
TCustomBitmapCodecClass of object;
var
AMethod: TMethod;
AInvoke: TPrivateMethodType absolute AMethod;
begin
AMethod.Code := @TBitmapCodecManager.GuessCodecClass;
AMethod.Data := Self;
Result := AInvoke(Name, Field);
end;
class function TKngStrBitmapCodecManager.LoadFromStream(const AStream: TStream;
const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
var
CodecClass: TCustomBitmapCodecClass;
Codec: TCustomBitmapCodec;
DataType: String;
begin
Result := False;
DataType := TImageTypeChecker.GetType(AStream);
CodecClass := GuessCodecClass(DataType, TBitmapCodecDescriptorField.Extension);
if CodecClass <> nil then
begin
Codec := CodecClass.Create;
try
{$IFDEF ANDROID}
Result := TBitmapCodecAndroid(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
{$ELSE}
Result := TBitmapCodecWIC(Codec).LoadFromStream(AStream, Bitmap, MaxSizeLimit);
{$ENDIF}
finally
Codec.Free;
end;
end
end;
class function TKngStrBitmapCodecManager.LoadThumbnailFromStream(
const AStream: TStream; const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
const Bitmap: TBitmapSurface): Boolean;
var
CodecClass: TCustomBitmapCodecClass;
Codec: TCustomBitmapCodec;
DataType: String;
begin
Result := False;
DataType := TImageTypeChecker.GetType(AStream);
CodecClass := GuessCodecClass(DataType, TBitmapCodecDescriptorField.Extension);
if CodecClass <> nil then
begin
Codec := CodecClass.Create;
try
{$IFDEF ANDROID}
Result := TBitmapCodecAndroid(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
{$ELSE}
Result := TBitmapCodecWIC(Codec).LoadThumbnailFromStream(AStream, ASrc, ADest, UseEmbedded, Bitmap);
{$ENDIF}
finally
Codec.Free;
end;
end
end;
class function TKngStrBitmapCodecManager.LoadThumbnailFromStream(
const AStream: TStream; const AFitWidth, AFitHeight: Single;
const UseEmbedded: Boolean; const AutoCut: Boolean;
const Bitmap: TBitmapSurface): Boolean;
var
CodecClass: TCustomBitmapCodecClass;
Codec: TCustomBitmapCodec;
DataType: String;
begin
Result := False;
DataType := TImageTypeChecker.GetType(AStream);
CodecClass := GuessCodecClass(DataType, TBitmapCodecDescriptorField.Extension);
if CodecClass <> nil then
begin
Codec := CodecClass.Create;
try
{$IFDEF ANDROID}
Result := TBitmapCodecAndroid(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Bitmap);
{$ELSE}
Result := TBitmapCodecWIC(Codec).LoadThumbnailFromStream(AStream, AFitWidth, AFitHeight, UseEmbedded, AutoCut, Bitmap);
{$ENDIF}
finally
Codec.Free;
end;
end
end;
{$IFDEF ANDROID}
{ TBitmapCodecAndroid }
class function TBitmapCodecAndroid.GetMovieSize(const Stream: TStream): TPoint;
type
TPrivateMethodType = function (const Stream: TStream): TPoint of object;
var
AMethod: TMethod;
AInvoke: TPrivateMethodType absolute AMethod;
begin
AMethod.Code := @TBitmapCodecAndroid.GetMovieSize;
AMethod.Data := Self;
Result := AInvoke(Stream);
end;
class function TBitmapCodecAndroid.GetImageSize(const AStream: TStream): TPointF;
var
TempStream: TMemoryStream;
TempArray: TJavaArray;
NativeBitmap: JBitmap;
LoadOptions: JBitmapFactory_Options;
SavePosition: Int64;
begin
if IsGIFStream(AStream) then
Result := GetMovieSize(AStream)
else begin
SavePosition := AStream.Position;
try
TempStream := TMemoryStream.Create;
try
TempStream.CopyFrom(AStream, AStream.Size);
TempArray := TJavaArray.Create(TempStream.Size);
Move(TempStream.Memory^, TempArray.Data^, TempStream.Size);
finally
TempStream.Free;
end;
LoadOptions := TJBitmapFactory_Options.JavaClass.init;
LoadOptions.inJustDecodeBounds := True;
TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
TempArray := nil;
Result := TPointF.Create(LoadOptions.outWidth, LoadOptions.outHeight);
finally
AStream.Position := SavePosition;
end;
end;
end;
class function TBitmapCodecAndroid.GetImageSize(const AFileName: string): TPointF;
begin
Result := inherited GetImageSize(AFileName);
end;
class function TBitmapCodecAndroid.IsGIFStream(const Stream: TStream): Boolean;
begin
Result := TImageTypeChecker.GetType(Stream) = SGIFImageExtension;
end;
function TBitmapCodecAndroid.LoadMovieFromStream(const Stream: TStream; const Surface: TBitmapSurface): Boolean;
var
PrevPosition: Int64;
TempArray: TJavaArray;
Movie: JMovie;
Bitmap: JBitmap;
Canvas: JCanvas;
begin
PrevPosition := Stream.Position;
try
TempArray := TJavaArray.Create(Stream.Size - Stream.Position);
Stream.ReadBuffer(TempArray.Data^, TempArray.Length);
finally
Stream.Position := PrevPosition;
end;
Movie := TJMovie.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length);
TempArray := nil;
Bitmap := TJBitmap.JavaClass.createBitmap(Movie.width, Movie.height, TJBitmap_Config.JavaClass.ARGB_8888);
//kngstr
if Bitmap = nil then
Exit(False);
try
Canvas := TJCanvas.JavaClass.init(Bitmap);
try
Movie.setTime(0);
Movie.draw(Canvas, 0, 0);
finally
Canvas := nil;
end;
Result := JBitmapToSurface(Bitmap, Surface);
finally
Bitmap.recycle;
end;
end;
function TBitmapCodecAndroid.StretchIfNeed(const SrcBitmap: JBitmap; const Bitmap: TBitmapSurface;
const LoadOptions: JBitmapFactory_Options; const MaxSizeLimit: Cardinal): Boolean;
var
R: TRectF;
ScaledBitmap: JBitmap;
begin
if (MaxSizeLimit > 0) and ((LoadOptions.outWidth > Integer(MaxSizeLimit)) or
(LoadOptions.outHeight > Integer(MaxSizeLimit))) then
begin
R := TRectF.Create(0, 0, LoadOptions.outWidth, LoadOptions.outHeight);
R.Fit(TRectF.Create(0, 0, MaxSizeLimit, MaxSizeLimit));
ScaledBitmap := TJBitmap.JavaClass.createScaledBitmap(SrcBitmap, R.Truncate.Width, R.Truncate.Height, True);
//kngstr
if ScaledBitmap = nil then
Exit(False);
try
Result := JBitmapToSurface(ScaledBitmap, Bitmap);
finally
ScaledBitmap.recycle;
end;
end
else
Result := JBitmapToSurface(SrcBitmap, Bitmap);
end;
function TBitmapCodecAndroid.LoadFromStream(const AStream: TStream;
const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
var
TempStream: TMemoryStream;
TempArray: TJavaArray;
NativeBitmap: JBitmap;
LoadOptions: JBitmapFactory_Options;
SavePosition: Int64;
begin
if IsGIFStream(AStream) then
Result := LoadMovieFromStream(AStream, Bitmap)
else
begin
SavePosition := AStream.Position;
try
TempStream := TMemoryStream.Create;
try
TempStream.CopyFrom(AStream, AStream.Size);
TempArray := TJavaArray.Create(TempStream.Size);
Move(TempStream.Memory^, TempArray.Data^, TempStream.Size);
finally
TempStream.Free;
end;
LoadOptions := TJBitmapFactory_Options.JavaClass.init;
NativeBitmap := TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
TempArray := nil;
//kngstr
if NativeBitmap = nil then
Exit(False);
try
if (LoadOptions.outWidth < 1) or (LoadOptions.outHeight < 1) then
Exit(False);
Result := StretchIfNeed(NativeBitmap, Bitmap, LoadOptions, MaxSizeLimit);
finally
NativeBitmap.recycle;
end;
finally
AStream.Position := SavePosition;
end;
end;
end;
function TBitmapCodecAndroid.LoadMovieFromStreamScaled(const AStream: TStream; const Surface: TBitmapSurface;
const FitSize: TPoint): Boolean;
var
TempStream: TMemoryStream;
TempArray: TJavaArray;
Movie: JMovie;
OrigBitmap, Bitmap: JBitmap;
Canvas: JCanvas;
OrigSize: TPoint;
LoadOptions: JBitmapFactory_Options;
SavePosition: Int64;
begin
SavePosition := AStream.Position;
try
TempStream := TMemoryStream.Create;
try
TempStream.CopyFrom(AStream, AStream.Size);
TempArray := TJavaArray.Create(TempStream.Size);
Move(TempStream.Memory^, TempArray.Data^, TempStream.Size);
finally
TempStream.Free;
end;
Movie := TJMovie.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length);
TempArray := nil;
OrigSize := TPoint.Create(Movie.width, Movie.height);
OrigBitmap := TJBitmap.JavaClass.createBitmap(OrigSize.X, OrigSize.Y, TJBitmap_Config.JavaClass.ARGB_8888);
if OrigBitmap = nil then //kngstr fixed
Exit(False);
try
Canvas := TJCanvas.JavaClass.init(OrigBitmap);
try
Movie.setTime(0);
Movie.draw(Canvas, 0, 0);
finally
Canvas := nil;
end;
Movie := nil;
Bitmap := TJBitmap.JavaClass.createBitmap(FitSize.X, FitSize.Y, TJBitmap_Config.JavaClass.ARGB_8888);
if Bitmap = nil then //kngstr fixed
Exit(False);
try
Canvas := TJCanvas.JavaClass.init(Bitmap);
try
Canvas.drawBitmap(OrigBitmap, TJRect.JavaClass.init(0, 0, OrigSize.X, OrigSize.Y), TJRect.JavaClass.init(0, 0,
FitSize.X, FitSize.y), nil);
finally
Canvas := nil;
end;
Result := JBitmapToSurface(Bitmap, Surface);
finally
Bitmap.recycle; //kngstr fixed
end;
finally
OrigBitmap.recycle;
end;
finally
AStream.Position := SavePosition;
end;
end;
//连续decode需要reset
//https://blog.csdn.net/boystray/article/details/77725648
//多图加载的优化
//https://blog.csdn.net/Android_app/article/details/45815093
//inSampleSize优化
//https://www.jianshu.com/p/f15cd2ed6ec0
procedure calculateInSampleSize(options: JBitmapFactory_Options; reqWidth, reqHeight: Integer);
var
width, height, suitedValue: Integer;
widthRatio, heightRatio: Integer;
begin
options.inSampleSize := 1;
width := options.outWidth;
height := options.outHeight;
if (height > reqHeight) or (width > reqWidth) then begin
//使用需要的宽高的最大值来计算比率
if reqHeight > reqWidth then
suitedValue := reqHeight
else
suitedValue := reqWidth;
heightRatio := height div suitedValue;
widthRatio := width div suitedValue;
if heightRatio > widthRatio then //用最大
options.inSampleSize := heightRatio
else
options.inSampleSize := widthRatio;
end;
end;
function TBitmapCodecAndroid.LoadThumbnailFromStream(const AStream: TStream;
const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
const Bitmap: TBitmapSurface): Boolean;
var
TempStream: TMemoryStream;
TempArray: TJavaArray;
NativeBitmap1, NativeBitmap2: JBitmap;
LoadOptions: JBitmapFactory_Options;
SavePosition: Int64;
begin
if IsGIFStream(AStream) then
Result := LoadMovieFromStreamScaled(AStream, Bitmap, TPoint.Create(Round(ADest.Width), Round(ADest.Height)))
else
begin
SavePosition := AStream.Position;
try
TempStream := TMemoryStream.Create;
try
TempStream.CopyFrom(AStream, AStream.Size);
TempArray := TJavaArray.Create(TempStream.Size);
Move(TempStream.Memory^, TempArray.Data^, TempStream.Size);
finally
TempStream.Free;
end;
LoadOptions := TJBitmapFactory_Options.JavaClass.init;
LoadOptions.inJustDecodeBounds := False;
NativeBitmap1 := TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
TempArray := nil;
//kngstr
if NativeBitmap1 = nil then
Exit(False);
try
if (LoadOptions.outWidth < 1) or (LoadOptions.outHeight < 1) then
Exit(False);
// 剪裁
if (not ASrc.IsEmpty) and (ASrc.Width < LoadOptions.outWidth) and (ASrc.Height < LoadOptions.outHeight) then begin
NativeBitmap2 := TJBitmap.JavaClass.createBitmap(NativeBitmap1, Round(ASrc.Left), Round(ASrc.Top), Round(ASrc.Width), Round(ASrc.Height));
//kngstr
if NativeBitmap2 = nil then
Exit(False);
NativeBitmap1.recycle;
NativeBitmap1 := nil;
NativeBitmap1 := NativeBitmap2;
end;
//缩放
NativeBitmap2 := TJBitmap.JavaClass.createScaledBitmap(NativeBitmap1, Round(ADest.Width), Round(ADest.Height), True);
//kngstr
if NativeBitmap2 = nil then
Exit(False);
try
Result := JBitmapToSurface(NativeBitmap2, Bitmap)
finally
NativeBitmap2.recycle;
end;
finally
NativeBitmap1.recycle;
end;
finally
AStream.Position := SavePosition;
end;
end;
end;
function TBitmapCodecAndroid.LoadThumbnailFromStream(const AStream: TStream;
const AFitWidth, AFitHeight: Single; const UseEmbedded: Boolean;
const AutoCut: Boolean; const Bitmap: TBitmapSurface): Boolean;
var
TempStream: TMemoryStream;
TempArray: TJavaArray;
NativeBitmap1, NativeBitmap2, NativeBitmap3: JBitmap;
LoadOptions: JBitmapFactory_Options;
SavePosition: Int64;
X, Y, W, H: Integer;
begin
if IsGIFStream(AStream) then
Result := LoadMovieFromStreamScaled(AStream, Bitmap, TPoint.Create(Round(AFitWidth), Round(AFitHeight)))
else
begin
SavePosition := AStream.Position;
try
TempStream := TMemoryStream.Create;
try
TempStream.CopyFrom(AStream, AStream.Size);
TempArray := TJavaArray.Create(TempStream.Size);
Move(TempStream.Memory^, TempArray.Data^, TempStream.Size);
finally
TempStream.Free;
end;
LoadOptions := TJBitmapFactory_Options.JavaClass.init;
//读取文件大小
LoadOptions.inJustDecodeBounds := True;
TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
//计算缩略尺寸
calculateInSampleSize(LoadOptions, Round(AFitWidth), Round(AFitHeight));
LoadOptions.inJustDecodeBounds := False;
NativeBitmap1 := TJBitmapFactory.JavaClass.decodeByteArray(TempArray, 0, TempArray.Length, LoadOptions);
TempArray := nil;
//kngstr
if NativeBitmap1 = nil then
Exit(False);
try
if (LoadOptions.outWidth < 1) or (LoadOptions.outHeight < 1) then
Exit(False);
NativeBitmap2 := TJBitmap.JavaClass.createScaledBitmap(NativeBitmap1, Round(AFitWidth), Round(AFitHeight), True);
//kngstr
if NativeBitmap2 = nil then
Exit(False);
try
if not AutoCut then
Result := JBitmapToSurface(NativeBitmap2, Bitmap)
else begin //临时处理下,截取大图的中间部分,尤其是分辨率特别大的
X := 0;
Y := 0;
W := Round(AFitWidth);
H := Round(AFitHeight);
if W > H then begin
X := (W - H) div 2;
W := H;
end
else if W < H then begin
Y := (H - W) div 2;
H := W;
end;
NativeBitmap3 := TJBitmap.JavaClass.createBitmap(NativeBitmap2, X, Y, W, H);
//kngstr
if NativeBitmap3 = nil then
Exit(False);
try
Result := JBitmapToSurface(NativeBitmap3, Bitmap);
finally
NativeBitmap3.recycle;
end;
end;
finally
NativeBitmap2.recycle;
end;
finally
NativeBitmap1.recycle;
end;
finally
AStream.Position := SavePosition;
end;
end;
end;
{$ENDIF}
{$ENDIF}
{$IFDEF FMX}
{ TBitmap16bitFiler }
class constructor TBitmap16bitFiler.Create;
var
i: integer;
begin
i := 0;
while i < $10000 do
begin
// 不用for因为优化会将循环变量的值保存在寄存器不更新到内存
Color[i] := PixelToAlphaColor(@i, TPixelFormat.BGR_565);
inc(i);
end;
end;
class procedure TBitmap16bitFiler.FillScanLine(D: TBitmapData;
ScanLine, Width: integer; data: Pointer);
var
SC: PAlphaColorArray;
DA: PWordArray;
I: Integer;
MinWidth: Integer;
begin
SC := D.GetScanline(ScanLine);
DA := data;
MinWidth := D.Width;
if (Width > 0) and (Width < MinWidth) then
MinWidth := Width;
for I := 0 to MinWidth - 1 do
begin
if D.PixelFormat = TPixelFormat.BGRA then
begin
SC[I] := Color[DA[I]];
end
else
begin
AlphaColorToPixel(Color[DA[I]], Addr(SC[I]), D.PixelFormat);
end;
end;
end;
function FillScanLineFormMemory(BitmapData: TBitmapData; ScanLineIndex, Width: integer;
InputData: Pointer; InputFormat: TPixelFormat): Boolean;
var
SC: PAlphaColorArray;
Buffer: PAlphaColor;
MinWidth: Integer;
begin
Result := False;
if ScanLineIndex < 0 then exit;
if ScanLineIndex >= BitmapData.Height then exit;
SC := BitmapData.GetScanline(ScanLineIndex);
MinWidth := BitmapData.Width;
if (Width > 0) and (Width < MinWidth) then
MinWidth := Width;
if InputFormat = BitmapData.PixelFormat then
begin
Move(PByte(InputData)[0], SC[0], BitmapData.BytesPerPixel * MinWidth);
end
else
begin
Buffer := GetMemory(MinWidth * SizeOf(TAlphaColor));
try
ScanlineToAlphaColor(InputData, Buffer, MinWidth, InputFormat);
AlphaColorToScanline(Buffer, SC, MinWidth, BitmapData.PixelFormat);
finally
FreeMemory(Buffer);
end;
end;
Result := True;
end;
{$ENDIF}
{ TBitmapCodecWIC }
{$IFDEF MSWINDOWS}
function TBitmapCodecWIC.DecodeFrame(const Frame: IWICBitmapFrameDecode;
const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean;
type
TPrivateMethodType = function (const Frame: IWICBitmapFrameDecode;
const Bitmap: TBitmapSurface; const MaxSizeLimit: Cardinal): Boolean of object;
var
AMethod: TMethod;
AInvoke: TPrivateMethodType absolute AMethod;
begin
AMethod.Code := @TBitmapCodecWIC.DecodeFrame;
AMethod.Data := Self;
Result := AInvoke(Frame, Bitmap, MaxSizeLimit);
end;
class function TBitmapCodecWIC.GetImageSize(const AStream: TStream): TPointF;
var
Decoder: IWICBitmapDecoder;
Frame: IWICBitmapFrameDecode;
W, H: UINT;
CopyStream: TMemoryStream;
Stream: IWICStream;
SavePosition: Int64;
begin
W := 0;
H := 0;
SavePosition := AStream.Position;
try
CopyStream := TMemoryStream.Create;
try
CopyStream.CopyFrom(AStream, AStream.Size);
ImagingFactory.CreateStream(Stream);
Stream.InitializeFromMemory(CopyStream.Memory, CopyStream.Size);
ImagingFactory.CreateDecoderFromStream(stream, GUID_NULL, WICDecodeMetadataCacheOnDemand, Decoder);
if Decoder <> nil then
begin
Decoder.GetFrame(0, Frame);
if Frame <> nil then
Frame.GetSize(W, H);
end;
Result := PointF(W, H);
finally
CopyStream.Free;
end;
finally
AStream.Position := SavePosition;
end;
end;
function TBitmapCodecWIC.LoadThumbnailFromStream(const AStream: TStream;
const ASrc, ADest: TRectF; const UseEmbedded: Boolean;
const Bitmap: TBitmapSurface): Boolean;
var
Decoder: IWICBitmapDecoder;
CopyStream: TMemoryStream;
Stream: IWICStream;
Frame: IWICBitmapFrameDecode;
Bmp: IWICBitmapSource;
Converter: IWICFormatConverter;
Scaler: IWICBitmapScaler;
Clipper: IWICBitmapClipper;
Width, Height: UINT;
WRect: WICRect;
LResult: HRESULT;
begin
Result := False;
CopyStream := TMemoryStream.Create;
try
CopyStream.CopyFrom(AStream, AStream.Size);
ImagingFactory.CreateStream(Stream);
Stream.InitializeFromMemory(CopyStream.Memory, CopyStream.Size);
//kngstr
ImagingFactory.CreateDecoderFromStream(Stream, GUID_NULL, WICDecodeMetadataCacheOnDemand, Decoder);
if Decoder <> nil then begin
Decoder.GetFrame(0, Frame);
if UseEmbedded then
Frame.GetThumbnail(Bmp);
if Bmp <> nil then begin
ImagingFactory.CreateFormatConverter(Converter);
if Succeeded(Converter.Initialize(Bmp, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nil, 0, 0)) then
begin
Converter.GetSize(Width, Height);
Bitmap.SetSize(Width, Height, TPixelFormat.BGRA);
Result := Succeeded(Converter.CopyPixels(nil, Bitmap.Pitch, Height * Cardinal(Bitmap.Pitch),
PByte(Bitmap.Bits)));
end;
end
else if Frame <> nil then begin
Frame.GetSize(Width, Height);
Clipper := nil;
if (not ASrc.IsEmpty) and (ASrc.Width < Width) and (ASrc.Height < Height) then begin
WRect.X := Trunc(ASrc.Left);
WRect.Y := Trunc(ASrc.Top);
WRect.Width := Trunc(ASrc.Width);
WRect.Height := Trunc(ASrc.Height);
// 剪裁
ImagingFactory.CreateBitmapClipper(Clipper);
if not Succeeded(Clipper.Initialize(frame, WRect)) then
Clipper := nil;
end;
LResult := S_FALSE;
// 缩放
ImagingFactory.CreateBitmapScaler(Scaler);
if (Clipper <> nil) then
LResult := Scaler.Initialize(Clipper, Trunc(ADest.Width), Trunc(ADest.Height), WICBitmapInterpolationModeLinear);
if not Succeeded(LResult) then
LResult := Scaler.Initialize(Frame, Trunc(ADest.Width), Trunc(ADest.Height), WICBitmapInterpolationModeLinear);
if Succeeded(LResult) then begin
ImagingFactory.CreateFormatConverter(Converter);
if Succeeded(Converter.Initialize(scaler, GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone, nil, 0, 0)) then begin
Converter.GetSize(Width, Height);
Bitmap.SetSize(Width, Height, TPixelFormat.BGRA);
Result := Succeeded(Converter.CopyPixels(nil, Bitmap.Pitch, Height * Cardinal(Bitmap.Pitch),
PByte(Bitmap.Bits)));
end;
end;
end;
end;
finally
CopyStream.Free;
end;
end;
function TBitmapCodecWIC.LoadThumbnailFromStream(const AStream: TStream;
const AFitWidth, AFitHeight: Single; const UseEmbedded, AutoCut: Boolean;
const Bitmap: TBitmapSurface): Boolean;
var
Decoder: IWICBitmapDecoder;
CopyStream: TMemoryStream;
Stream: IWICStream;
Frame: IWICBitmapFrameDecode;
Bmp: IWICBitmapSource;
Converter: IWICFormatConverter;
Scaler: IWICBitmapScaler;
R: TRectF;
Width, Height: UINT;
begin
Result := False;
CopyStream := TMemoryStream.Create;
try
CopyStream.CopyFrom(AStream, AStream.Size);
ImagingFactory.CreateStream(Stream);
Stream.InitializeFromMemory(CopyStream.Memory, CopyStream.Size);
//kngstr
ImagingFactory.CreateDecoderFromStream(Stream, GUID_NULL, WICDecodeMetadataCacheOnDemand, Decoder);
if Decoder <> nil then
begin
Decoder.GetFrame(0, Frame);
if UseEmbedded then
Frame.GetThumbnail(Bmp);
if Bmp <> nil then
begin
ImagingFactory.CreateFormatConverter(Converter);
if Succeeded(Converter.Initialize(Bmp, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nil, 0, 0)) then
begin
Converter.GetSize(Width, Height);
Bitmap.SetSize(Width, Height, TPixelFormat.BGRA);
Result := Succeeded(Converter.CopyPixels(nil, Bitmap.Pitch, Height * Cardinal(Bitmap.Pitch),
PByte(Bitmap.Bits)));
end;
end
else
if Frame <> nil then
begin
Frame.GetSize(Width, Height);
R := TRectF.Create(0, 0, Width, Height);
R.Fit(TRectF.Create(0, 0, AFitWidth, AFitHeight));
ImagingFactory.CreateBitmapScaler(Scaler);
if Succeeded(Scaler.Initialize(frame, Trunc(R.Width), Trunc(R.Height), WICBitmapInterpolationModeLinear)) then
begin
ImagingFactory.CreateFormatConverter(Converter);
if Succeeded(Converter.Initialize(scaler, GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone, nil, 0, 0)) then
begin
Converter.GetSize(Width, Height);
Bitmap.SetSize(Width, Height, TPixelFormat.BGRA);
Result := Succeeded(Converter.CopyPixels(nil, Bitmap.Pitch, Height * Cardinal(Bitmap.Pitch),
PByte(Bitmap.Bits)));
end;
end;
end;
end;
finally
CopyStream.Free;
end;
end;
{$ENDIF}
end.