|
|
unit ImagingQuartz;
{$I ImagingOptions.inc}
{$IFNDEF MACOSX} {$FATAL 'Mac OSX only'} {$ENDIF}
interface
uses Types, SysUtils, Classes, ImagingTypes, Imaging, ImagingUtility, {$IFDEF DCC} Macapi.CocoaTypes, Macapi.CoreFoundation, Macapi.CoreGraphics, Macapi.QuartzCore, Macapi.ImageIO {$ELSE} CGBase, CGShading, CGColor, CGColorSpace, CGContext, CGBitmapContext, CGImageSource, CGImageDestination, CGDataProvider, CGDataConsumer, CFDictionary, CGAffineTransforms, CGPath {$ENDIF};
type TQuartzImageHandler = class private FDefaultColorSpace: CGColorSpaceRef; function FindImageFormat(ImgRef: CGImageRef): TImageFormat; public constructor Create; destructor Destroy; override; function LoadImage(Stream: TCustomMemoryStream; var Images: TDynImageDataArray; OnlyFirstImage: Boolean): Boolean; end;
implementation
function CGRectFromRect(const R: TRect): CGRect; begin Result.origin.x := R.Left; Result.origin.Y := R.Top; Result.size.Width := R.Right - R.Left; Result.size.Height := R.Bottom - R.Top; end;
{ TMacImageHandler }
constructor TQuartzImageHandler.Create; begin FDefaultColorSpace := CGColorSpaceCreateDeviceRGB; end;
destructor TQuartzImageHandler.Destroy; begin CGColorSpaceRelease(FDefaultColorSpace); inherited; end;
function TQuartzImageHandler.FindImageFormat(ImgRef: CGImageRef): TImageFormat; var ColorSpaceRef: CGColorSpaceRef; ColorModel: CGColorSpaceModel; AlphaInfo: CGImageAlphaInfo; BitmapInfo: CGBitmapInfo; BitsPerPixel, Components, BitsPerComponent: Integer;
isMask: integer; intent: CGColorRenderingIntent; begin Result := ifUnknown; AlphaInfo := CGImageGetAlphaInfo(ImgRef); BitmapInfo := CGImageGetBitmapInfo(ImgRef); ColorSpaceRef := CGImageGetColorSpace(ImgRef);
intent := CGImageGetRenderingIntent(ImgRef); isMask := CGImageIsMask(ImgRef);
{ Also check BitmapInfo kCGBitmapByteOrderDefault = NO kCGBitmapByteOrder16Little = NO kCGBitmapByteOrder32Little = NO kCGBitmapByteOrder16Big = NO kCGBitmapByteOrder32Big = NO
float formats }
if ColorSpaceRef <> nil then begin try BitsPerPixel := CGImageGetBitsPerPixel(ImgRef); BitsPerComponent := CGImageGetBitsPerComponent(ImgRef); ColorModel := CGColorSpaceGetModel(ColorSpaceRef); Components := CGColorSpaceGetNumberOfComponents(ColorSpaceRef);
if (ColorModel = kCGColorSpaceModelMonochrome) and (Components = 1) then begin // Grayscale formats if AlphaInfo = kCGImageAlphaFirst then begin if (BitsPerComponent = 8) and (BitsPerPixel = 16) then Result := ifA8Gray8 else if (BitsPerComponent = 16) and (BitsPerPixel = 32) then Result := ifA16Gray16; end else if AlphaInfo = kCGImageAlphaNone then begin if BitsPerPixel = 8 then Result := ifGray8 else if BitsPerPixel = 16 then Result := ifGray16; end; end else if ColorModel = kCGColorSpaceModelRGB then begin // RGB if (BitsPerPixel = 16) and (AlphaInfo = kCGImageAlphaNoneSkipFirst) then begin Result := ifX1R5G5B5; end else if AlphaInfo = kCGImageAlphaFirst then begin if (BitsPerComponent = 8) and (BitsPerPixel = 32) then Result := ifA8R8G8B8 else if (BitsPerComponent = 16) and (BitsPerPixel = 64) then Result := ifA16R16G16B16; end else if AlphaInfo = kCGImageAlphaNone then begin if (BitsPerComponent = 8) and (BitsPerPixel = 24) then Result := ifR8G8B8 else if (BitsPerComponent = 16) and (BitsPerPixel = 48) then Result := ifR16G16B16; end; end; finally CGColorSpaceRelease(ColorSpaceRef); end; end; end;
function TQuartzImageHandler.LoadImage(Stream: TCustomMemoryStream; var Images: TDynImageDataArray; OnlyFirstImage: Boolean): Boolean; var Provider: CGDataProviderRef; PixelsData: CFDataRef; PixelsPtr, DestPtr: PByteArray; ImgSourceRef: CGImageSourceRef; ImgRef: CGImageRef; CtxRef: CGContextRef; I, Count, Y: Integer; Width, Height, BytesPerRow, WidthBytes: Integer; ImgFormat: TImageFormat; begin Result := False; Provider := CGDataProviderCreateWithData(nil, Stream.Memory, Stream.Size, nil); if Provider <> nil then begin ImgSourceRef := CGImageSourceCreateWithDataProvider(Provider, nil); if ImgSourceRef <> nil then begin Count := CGImageSourceGetCount(ImgSourceRef); if (Count > 1) and OnlyFirstImage then Count := 1; SetLength(Images, Count);
for I := 0 to Count - 1 do begin ImgRef := CGImageSourceCreateImageAtIndex(ImgSourceRef, I, nil); if ImgRef <> nil then begin Width := CGImageGetWidth(ImgRef); Height := CGImageGetHeight(ImgRef); BytesPerRow := CGImageGetBytesPerRow(ImgRef); ImgFormat := FindImageFormat(ImgRef);
if ImgFormat = ifUnknown then begin NewImage(Width, Height, ifA8R8G8B8, Images[I]); CtxRef := CGBitmapContextCreate(Images[I].Bits, Width, Height, 8, Width * 4, FDefaultColorSpace, kCGImageAlphaPremultipliedFirst); CGContextDrawImage(CtxRef, CGRectFromRect(Rect(0, 0, Width, Height)), ImgRef); CGContextRelease(CtxRef); end else begin NewImage(Width, Height, ImgFormat, Images[I]); DestPtr := PByteArray(Images[I].Bits); WidthBytes := Images[I].Size div Height;
PixelsData := CGDataProviderCopyData(CGImageGetDataProvider(ImgRef)); PixelsPtr := PByteArray(CFDataGetBytePtr(PixelsData));
for Y := 0 to Height - 1 do begin // Move(PixelsPtr[Y * BytesPerRow], DestPtr[Y * WidthBytes], WidthBytes); end; CFRelease(PixelsData); end;
CGImageRelease(ImgRef); end; end; CFRelease(ImgSourceRef); end; end; end;
end.
|