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.