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.