unit tinyPNG;

//  file: tinyPNG.pas
//  file version: 0.35
//  last modified: 26.08.05
//  package: GRushControls
//  autor: Karpinskyj Alexandr aka homm
//      mailto: homm86@mail.ru
//      My humble Web-Page: http://www.homm86.narod.ru

        //  Switch this define on to get most functionality and error-free
        //  versilon of this decoder. All folowing defination are ingoring.
{$DEFINE MOSTCOMPATIBILITY}

        //  Capability loading gray not palettized images with 1,2,4,8 bit per
        //  pixel and with 16 bit per pixel if HIGHPRECISION also defined
{$DEFINE csG}

        //  Capability loading TrueColor RGB images with 24 bit per pixel and
        //  with 48 bit per pixel if HIGHPRECISION also defined.
{$DEFINE csRGB}

        //  Capability loading palettized images with 1,2,4,8 bit per pixel.
        //  HIGHPRECISION no matter.
{$DEFINE csIndexed}

        // * Capability loading gray not palettized images with alpha chanel
        //  with 16 bit per pixel and with 32 bit per pixel if HIGHPRECISION
        //  also defined.
{$DEFINE csGA}

        // * Capability loading TrueColor RGB images with alpha chanel with 32
        //  bit per pixel and with 64 bit per pixel if HIGHPRECISION also
        //  defined.
{$DEFINE csRGBA}

//  Remarks
//  -------
//  * - Alpha chalnel don't loading anyway. This swithes allow loading RGB and
//      Gray chanels from image with alpha chanel.


        //  Undefine to improve decoding speed, and make decoder else smaller.
        //  Files with correct heared and segment sequence in most cases have
        //  right CRC.
//{$DEFINE CHECKCRCS}

        //  Capability loading images with 16 bit per sample. I actually have
        //  not images with 16 bit per sample and even Photoshop 7 can not
        //  create this images, so this capacity not tested, and in most cases
        //  can be undefined.
{$DEFINE HIGHPRECISION}

        //  Capability loading images with interlaced data
        //  still NOT supported
{$DEFINE INTERLACE}

        //  Avoid all checks, including checks on unsupported formats, not only
        //  on file damage. CRC check also avoiding. This switch must be used
        //  only if application sure what source image is not contein errors.
        //  It is strongly recommended using this switch only for loading images
        //  from resources and private application data storage.
//{$DEFINE USEHACKS}

        //  Filters of internal PNG format. Do not crop filters except cases
        //  described above. Images, which have unrecognized filter interpritate
        //  as error, except case with USEHACKS. Look at folowing rules.
        //  - Defination ALLOWFILTERS not means defination all filters.
        //  - Defination of any filter not means defination ALLOWFILTERS.
        //  - Undefination ALLOWFILTERS means undefination ALL filters, but not
        //      means avoid filter check, if USEHACKS not says otherwise.
        //  (complex preprocessing in code make this rules posible)
{$DEFINE ALLOWFILTERS}
    {$DEFINE FILTER_SUB}
    {$DEFINE FILTER_UP}
    {$DEFINE FILTER_AVERANGE}
    {$DEFINE FILTER_PAETH}


//  function tinyLoadPNG(var TargetBitmap: PBitMap; FileImage: PStream): DWORD;
//******************************************************************************
//  The only public proceedure in this module, exists on this time. FileImage
//  MUST BE VALID strem on PNG-formatted content with headers. If loading is
//  imposible, the nil is returned by TargetBitmap and Error Code returning by
//  functin. If {$DEFINE USEHACKS} is defined, tinyERROR_OK always returning.
//  Return value not allways contain valid decoding error, but contain the most
//  probable error.
//******************************************************************************























interface

uses windows, KOL, MZLib;

{$IFDEF MOSTCOMPATIBILITY}
    {$DEFINE csG}
    {$DEFINE csRGB}
    {$DEFINE csIndexed}
    {$DEFINE csGA}
    {$DEFINE csRGBA}
    {$DEFINE CHECKCRCS}
    {$DEFINE HIGHPRECISION}
    {$DEFINE INTERLACE}
    {$UNDEF USEHACKS}
    {$DEFINE ALLOWFILTERS}
    {$DEFINE FILTER_SUB}
    {$DEFINE FILTER_UP}
    {$DEFINE FILTER_AVERANGE}
    {$DEFINE FILTER_PAETH}
{$ENDIF MOSTCOMPATIBILITY}

{$IFDEF USEHACKS}
    {$IFDEF CHECKCRCS}
        {$UNDEF CHECKCRCS}
    {$ENDIF CHECKCRCS}
{$ENDIF USEHACKS}

{$IFNDEF ALLOWFILTERS}
    {$UNDEF FILTER_SUB}
    {$UNDEF FILTER_UP}
    {$UNDEF FILTER_AVERANGE}
    {$UNDEF FILTER_PAETH}
{$ELSE ALLOWFILTERS}
    {$IFNDEF FILTER_SUB}
        {$IFNDEF FILTER_UP}
            {$IFNDEF FILTER_AVERANGE}
                {$IFNDEF FILTER_PAETH}
                    {$UNDEF ALLOWFILTERS}
                {$ENDIF FILTER_PAETH}
            {$ENDIF FILTER_AVERANGE}
        {$ENDIF FILTER_UP}
    {$ENDIF FILTER_SUB}
{$ENDIF ALLOWFILTERS}

{$IFDEF csG}
    {$DEFINE INCGRAY}
{$ENDIF csG}
{$IFDEF csGA}
    {$DEFINE INCGRAY}
{$ENDIF csGA}

const   tinyERROR_OK =                                  $0000;
        tinyERROR_NotPNGFile =                          $0001;
        tinyERROR_UnexpectedEOF =                       $0002;
        tinyERROR_UnexpectedEOF_or_WrongCRC =           $0003;
        tinyERROR_WrongHeader =                         $0004;
        tinyERROR_UnsupportedImageFormat =              $0005;
        tinyERROR_NotIncludedFormat =                   $0006;
        tinyERROR_WrongSegmentsSequence =               $0007;
        tinyERROR_UnsupportedColorFormat =              $0008;
        tinyERROR_UncompressorInitError =               $0009;
        tinyERROR_UnexpectedEOF_or_DataStreamError =    $000A;
        tinyERROR_UnsupportedDataFilter =               $000B;  


function tinyLoadPNG(var TargetBitmap: PBitMap; FileImage: PStream): DWORD;
function tinyLoadPNGResource(var TargetBitmap: PBitMap; Inst : HInst; ResName : PChar; ResType : PChar): DWORD;

implementation

const
    PNGMagic: array[0..7] of Byte = (137,80,78,71,13,10,26,10);

    IHDR = 'IHDR'; 
    IDAT = 'IDAT'; 
    IEND = 'IEND'; 
    PLTE = 'PLTE';
    gAMA = 'gAMA';

type
    TByteArray = array of byte;
    TDWORDArray = array of DWORD;

    TcuFlags = set of (
        cu_RawBuffer,
        cu_sFile,
        cu_BitMap,
        cu_BITMAPINFO,
        cu_RowBuffer,
        cu_Inflate,
        cu_InterlaceBuffer
    );

    TImageProperties = record          
        Width: cardinal;
        Height: cardinal;
        DIBBits: Pointer;
        Interlaced: boolean;
        HasAlpha: boolean;
        HasGamma: boolean;
        HasPallete: boolean;
    end;

    TChunkType = array[0..3] of Char;

    TPNGChunkHeader = packed record
        Length: cardinal;
        ChunkType: TChunkType;
    end;

    PIHDRChunk = ^TIHDRChunk;
    TIHDRChunk = packed record
        Width: cardinal;
        Height: cardinal;
        BitDepth: byte;
        ColorType: byte;
        Compression: byte;
        Filter: byte;
        Interlaced: byte;
    end;

    TConversionMethod = procedure(Source: pointer; Target: pointer; Count: cardinal);

    TColorManager = packed record
        RowConversion: TConversionMethod;
        SourceBitsPerSample: byte;
        TargetBitsPerSample: byte;
        SourceBytesPerPixel: byte;
        TargetBytesPerPixel: byte;
        SourceBytesPerRow: DWORD;
        TargetBytesPerRow: DWORD;
        FilteredBytes: DWORD;
        {$IFDEF INTERLACE}
        SourceSample: byte;
        Mask: byte;
        {$ENDIF INTERLACE}
    end;

//****************************************************************
//****************************************************************
var     CM: TColorManager;
        IP: TImageProperties;
        Header: TPNGChunkHeader;
        Description: TIHDRChunk;
        InflateStream: TZState;

        RowBuffer: array[Boolean] of PChar;
        RawBuffer: Pointer;
        {$IFDEF INTERLACE}
        InterlaceBuffer: Pointer;
        {$ENDIF INTERLACE}
        BITMAPINFO: PBITMAPINFO;
        sFile: PStream;
        sFile_Size: DWORD;
        sFile_Position: DWORD;
        Bitmap: THandle;
        {$IFDEF CHECKCRCS}
        CurrentCRC: cardinal;
        {$ENDIF CHECKCRCS}
        cuFlags: TcuFlags;
        CurrentSource: pointer;
        IDATSize: Integer;


const   RowStart: array[0..6] of DWORD = (0,0,4,0,2,0,1);
        ColumnStart: array[0..6] of DWORD = (0,4,0,2,0,1,0);
        RowIncrement: array[0..6] of DWORD = (8,8,8,4,4,2,2);
        ColumnIncrement: array[0..6] of DWORD = (8,8,4,4,2,2,1);
        PassMask: array[0..6] of byte = ($80,$08,$88,$22,$AA,$55,$FF);

//****************************************************************
procedure  ConvertS0D16(Source: pointer; Target: pointer; Count: cardinal);
asm
    push ebx
@@01:
    mov bl, byte ptr [eax]
    mov byte ptr [edx], bl
    add eax, $02
    inc edx
    dec ecx
    jnz @@01
    pop ebx
end;
//****************************************************************
procedure  ConvertS2D8(Source: pointer; Target: pointer; Count: cardinal);
asm
    push ebx
    dec ecx
@@01:
    mov ebx, dword ptr [eax]
    bswap ebx
    shr ebx, 8
    mov dword ptr [edx], ebx
    add eax, $03
    add edx, $03
    dec ecx
    jnz @@01
    mov ebx, [eax -1]
    bswap ebx
    mov word ptr [edx], bx
    shr ebx, 16
    mov byte ptr [edx+2], bl
    pop ebx
end;
//****************************************************************
procedure  ConvertS2D16(Source: pointer; Target: pointer; Count: cardinal);
asm
    push ebx
    push edi
    mov edi, ecx
@@01:
    mov bl, [eax]
    mov bh, [eax+2]
    mov cl, [eax+4]
    mov [edx+2], bl
    mov [edx+1], bh
    mov [edx], cl
    add eax, $06
    add edx, $03
    dec edi
    jnz @@01
    pop edi
    pop ebx
end;
//****************************************************************
procedure  ConvertS3D1(Source: pointer; Target: pointer; Count: cardinal);
asm
    push ebx
    push edi
    xor edi, edi
@@01:
    and edi, $07
    jnz @@02
    mov bl, [eax]
    inc eax
@@02:
    xor bh, bh
    shl bx, 1
    mov [edx], bh
    inc edx
    inc edi
    dec ecx
    jnz @@01
    pop edi
    pop ebx
end;
//****************************************************************
procedure  ConvertS3D2(Source: pointer; Target: pointer; Count: cardinal);
asm
    push ebx
    push edi
    xor edi, edi
@@01:
    and edi, $03
    jnz @@02
    mov bl, [eax]
    inc eax
@@02:
    xor bh, bh
    shl bx, 2
    mov [edx], bh
    inc edx
    inc edi
    dec ecx
    jnz @@01
    pop edi
    pop ebx
end;
//****************************************************************
procedure  ConvertS3D4(Source: pointer; Target: pointer; Count: cardinal);
asm
    push ebx
    push edi
    xor edi, edi
@@01:
    and edi, $01
    jnz @@02
    mov bl, [eax]
    inc eax
@@02:
    xor bh, bh
    shl bx, 4
    mov [edx], bh
    inc edx
    inc edi
    dec ecx
    jnz @@01
    pop edi
    pop ebx
end;
//****************************************************************
procedure  ConvertS4D8(Source: pointer; Target: pointer; Count: cardinal);
asm
    push ebx
@@01:
    mov bl, byte ptr [eax]
    mov byte ptr [edx], bl
    add eax, $02
    inc edx
    dec ecx
    jnz @@01
    pop ebx
end;
//****************************************************************
procedure  ConvertS4D16(Source: pointer; Target: pointer; Count: cardinal);
asm
    push ebx
@@01:
    mov bl, byte ptr [eax]
    mov byte ptr [edx], bl
    add eax, $04
    inc edx
    dec ecx
    jnz @@01
    pop ebx
end;
//****************************************************************
procedure  ConvertS6D8(Source: pointer; Target: pointer; Count: cardinal);
asm
    push ebx
    dec ecx
@@01:
    mov ebx, dword ptr [eax]
    bswap ebx
    shr ebx, 8
    mov dword ptr [edx], ebx
    add eax, $04
    add edx, $03
    dec ecx
    jnz @@01
    mov ebx, dword ptr [eax]
    bswap ebx
    shr ebx, 8
    mov word ptr [edx], bx
    shr ebx, 8
    mov byte ptr [edx+2], bh
    pop ebx
end;
//****************************************************************
procedure  ConvertS6D16(Source: pointer; Target: pointer; Count: cardinal);
asm
    push ebx
    push edi
    mov edi, ecx
@@01:
    mov bl, [eax]
    mov bh, [eax+2]
    mov cl, [eax+4]
    mov [edx+2], bl
    mov [edx+1], bh
    mov [edx], cl
    add eax, $08
    add edx, $03
    dec edi
    jnz @@01

    pop edi
    pop ebx
end;
//****************************************************************
{$IFDEF ALLOWFILTERS}
procedure Filter_SUB(Applyto: Pointer);
asm
    push ebx
    xor edx, edx
    mov dl, [CM.SourceBytesPerPixel]
    mov ecx, [CM.FilteredBytes]
    sub ecx, edx
    mov ebx, eax
    add eax, edx
@@01:
    mov dl, [eax]
    add dl, [ebx]
    mov [eax], dl
    inc eax
    inc ebx
    dec ecx
    jnz @@01
    pop ebx
end;
//****************************************************************
procedure Filter_UP(Applyto: Pointer; PrevRow: pointer);
asm
    push ebx
    mov ecx, [CM.FilteredBytes]
@@01:
    mov bl, [eax]
    add bl, [edx]
    mov [eax], bl
    inc eax
    inc edx
    dec ecx
    jnz @@01
    pop ebx
end;
//****************************************************************
procedure Filter_AVERAGE(Applyto: Pointer; PrevRow: pointer);
asm
    push edi
    push esi
    mov edi, edx
    mov esi, eax
    xor edx, edx
    mov dl, [CM.SourceBytesPerPixel]
@@02:
    mov cl, [edi]
    shr cl, 1
    mov ch, [eax]
    add cl, ch
    mov [eax], cl
    inc edi
    inc eax
    dec edx
    jnz @@02
    mov ecx, [CM.FilteredBytes]
    xor edx, edx
    mov dl, [CM.SourceBytesPerPixel]
    sub ecx, edx
@@01:
    mov dl, [edi]
    mov dh, [esi]
    add dl, dh
    rcr dl, 1
    mov dh, [eax]
    add dh,dl
    mov [eax], dh
    inc eax
    inc esi
    inc edi
    dec ecx
    jnz @@01
    pop esi
    pop edi
end;
//****************************************************************
function PaethPredictor(A,B,C: byte): byte;
var     PA,PB,PC: smallint;
begin
    PA:=Abs(B-C);
    PB:=Abs(A-C);
    PC:=Abs(A-C+B-C);
    if (PA<=PB) and (PA<=PC) then Result:=A
    else if (PB<=PC) then Result:=B else Result:=C;
end;
//****************************************************************
procedure Filter_PAETH(Applyto: Pointer; PrevRow: pointer);
asm
    push ebx
    push edi
    push esi
    push ebp

    mov esi, edx
    mov edi, eax
    xor ebx, ebx
    mov bl, [CM.SourceBytesPerPixel]
@@02:
    mov al, [esi + ebx - 1]
    mov ah, [edi + ebx - 1]
    add al, ah
    mov [edi + ebx - 1], al
    dec ebx
    jnz @@02
    mov bl, [CM.SourceBytesPerPixel]
    mov ebp, [CM.FilteredBytes]
    sub ebp, ebx
@@01:
    mov al, [edi]
    mov dl, [esi + ebx]
    mov cl, [esi]
    call PaethPredictor
    mov ah, [edi + ebx]
    add al, ah
    mov [edi + ebx], al
    inc esi
    inc edi
    dec ebp
    jnz @@01

    pop ebp
    pop esi
    pop edi
    pop ebx
end;
{$ENDIF ALLOWFILTERS}
//****************************************************************
{$IFDEF INTERLACE}
procedure Expand(Src: Pointer; Dst: Pointer; DestCount: dword);
asm
    push ebx
    push edi
    push esi
    mov edi, edx
    mov esi, eax
    mov bl, byte ptr [CM.Mask]
    xor edx, edx
    mov dl, byte ptr [CM.TargetBytesPerPixel]
@@01:
    rol bl, 1
    jnb @2
    mov bh, dl
@@02:
    mov al, byte ptr [esi]
    mov byte ptr [edi], al
    inc esi
    inc edi
    dec bh
    jnz @@02
    jmp @1
@2:
    add edi, edx
@1:
    dec ecx
    jnz @@01
    pop esi
    pop edi
    pop ebx
end;
{$ENDIF INTERLACE}
//****************************************************************
procedure CleanUp;
    procedure FreeMem_and_NULL (var ptr: pointer);
    begin
        FreeMem(ptr);
        ptr := nil;
    end;
begin
    if cu_RawBuffer in cuFlags then
        FreeMem_and_NULL(RawBuffer);
    if cu_sFile in cuFlags then
        sFile.Position := sFile_Position;
    if cu_BITMAPINFO in cuFlags then
        FreeMem_and_NULL(Pointer(BITMAPINFO));
    if cu_Inflate in cuFlags then
        InflateEnd(InflateStream);
    if cu_RowBuffer in cuFlags then begin
        FreeMem_and_NULL(pointer(RowBuffer[false]));
        FreeMem_and_NULL(pointer(RowBuffer[true]));
    end;
    if cu_BitMap in cuFlags then begin
        DeleteObject(Bitmap);
        Bitmap := 0;
        IP.DIBBits := nil;
    end;
    {$IFDEF INTERLACE}
    if cu_InterlaceBuffer in cuFlags then
        FreeMem_and_NULL(InterlaceBuffer);
    {$ENDIF INTERLACE}
end;
//****************************************************************
function IsChunk(ChunkType: TChunkType): boolean;
const   Mask = not $20202020;
begin
    Result:=(Cardinal(Header.ChunkType) and Mask)=(Cardinal(ChunkType) and Mask);
end;
//****************************************************************
{$IFNDEF USEHACKS}
function ReadDataAndCheckCRC: Boolean;
{$ELSE USEHACKS}
procedure ReadDataAndCheckCRC;
{$ENDIF USEHACKS}
var     FileCRC: cardinal;
begin
    ReallocMem(RawBuffer, Header.Length);
    {$IFNDEF USEHACKS}
    Result := false;
    if (sFile_Size - sFile.Position) >= (Header.Length + sizeof(FileCRC)) then begin
    {$ENDIF USEHACKS}
        sFile.Read(RawBuffer^, Header.Length);
        sFile.Read(FileCRC, sizeof(FileCRC));
    {$IFNDEF USEHACKS}
    end else exit;
    {$ENDIF USEHACKS}
    asm
        mov eax, [FileCRC]
        bswap eax
        mov [FileCRC], eax
    end;
    {$IFDEF CHECKCRCS}
    CurrentCRC := CRC32(CurrentCRC, RawBuffer, Header.Length);
    if CurrentCRC <> FileCRC then
        exit;
    {$ENDIF CHECKCRCS}
    {$IFNDEF USEHACKS}
    result := true;
    {$ENDIF}
end;
//****************************************************************
{$IFNDEF USEHACKS}
function LoadAndSwapHeader: Boolean;
{$ELSE USEHACKS}
procedure LoadAndSwapHeader;
{$ENDIF USEHACKS}
begin
    {$IFNDEF USEHACKS}
    result := false;
    if (sFile_Size - sFile.Position) >= sizeof(Header) then
    {$ENDIF}
        sFile.Read(Header, sizeof(Header))
    {$IFNDEF USEHACKS}
    else exit
    {$ENDIF};
    {$IFDEF CHECKCRCS}
    CurrentCRC:=CRC32(0, @Header.ChunkType, 4);
    {$ENDIF CHECKCRCS}
    asm
        mov eax, [Header.Length]
        bswap eax
        mov [Header.Length], eax
    end;
    {$IFNDEF USEHACKS}
    result := true;
    {$ENDIF}
end;
//****************************************************************
{$IFNDEF USEHACKS}
function SetupColorDepthAndMakeBitmap: Boolean;
{$ELSE USEHACKS}
procedure SetupColorDepthAndMakeBitmap;
{$ENDIF USEHACKS}
begin
    {$IFNDEF USEHACKS}
    result := false;
    {$ENDIF}
    if BITMAPINFO = nil then begin
        BITMAPINFO := AllocMem(sizeof(TBITMAPINFO));
        Include(cuFlags, cu_BITMAPINFO);
    end;
    with CM do begin
        SourceBitsPerSample := Description.BitDepth;
        TargetBitsPerSample := 8;
        case Description.ColorType of
        {$IFDEF csG}
            0: begin
                case SourceBitsPerSample of
                    1:  RowConversion := ConvertS3D1;
                    2:  RowConversion := ConvertS3D2;
                    4:  RowConversion := ConvertS3D4;
                    8:  RowConversion := @Move;
                    {$IFDEF HIGHPRECISION}
                    16: RowConversion := ConvertS0D16;
                    {$ENDIF HIGHPRECISION}
                {$IFNDEF USEHACKS}
                else exit;
                {$ENDIF USEHACKS}
                end;
                TargetBytesPerPixel := 1;
                {$IFDEF INTERLACE}
                SourceSample := 1;
                {$ENDIF INTERLACE}
                {$IFDEF HIGHPRECISION}
                if SourceBitsPerSample = 16 then
                    SourceBytesPerPixel := 2
                else
                {$ENDIF HIGHPRECISION}
                    SourceBytesPerPixel := 8 div SourceBitsPerSample;
                BITMAPINFO.bmiHeader.biBitCount := 8;
            end;
        {$ENDIF csG}
        {$IFDEF csRGB}
            2: begin
                case SourceBitsPerSample of
                    8:  begin
                            RowConversion := ConvertS2D8;
                            SourceBytesPerPixel := 3;
                        end;
                    {$IFDEF HIGHPRECISION}
                    16: begin
                            RowConversion := ConvertS2D16;
                            SourceBytesPerPixel := 6;
                        end;
                    {$ENDIF HIGHPRECISION}
                {$IFNDEF USEHACKS}
                else exit;
                {$ENDIF USEHACKS}
                end;
                {$IFDEF INTERLACE}
                SourceSample := 3;
                {$ENDIF INTERLACE}
                TargetBytesPerPixel := 3;
                BITMAPINFO.bmiHeader.biBitCount := 24;
            end;
        {$ENDIF csRGB}
        {$IFDEF csIndexed}
            3: begin
                case SourceBitsPerSample of
                    1:  RowConversion := ConvertS3D1;
                    2:  RowConversion := ConvertS3D2;
                    4:  RowConversion := ConvertS3D4;
                    8:  RowConversion := @Move;
                {$IFNDEF USEHACKS}
                else exit;
                {$ENDIF USEHACKS}
                end;
                {$IFDEF INTERLACE}
                SourceSample := 1;
                {$ENDIF INTERLACE}
                TargetBytesPerPixel := 1;
                SourceBytesPerPixel := 8 div SourceBitsPerSample;
                BITMAPINFO.bmiHeader.biBitCount := 8;
            end;
        {$ENDIF csIndexed}
        {$IFDEF csGA}
            4: begin
                case SourceBitsPerSample of
                    8:  begin
                            RowConversion := ConvertS4D8;
                            SourceBytesPerPixel := 2;
                        end;
                    {$IFDEF HIGHPRECISION}
                    16: begin
                            RowConversion := ConvertS4D16;
                            SourceBytesPerPixel := 4;
                        end;
                    {$ENDIF HIGHPRECISION}
                {$IFNDEF USEHACKS}
                else exit;
                {$ENDIF USEHACKS}
                end;
                {$IFDEF INTERLACE}
                SourceSample := 2;
                {$ENDIF INTERLACE}
                TargetBytesPerPixel := 1;
                BITMAPINFO.bmiHeader.biBitCount := 8;
            end;
        {$ENDIF csGA}
        {$IFDEF csRGBA}
            6: begin
                case SourceBitsPerSample of
                    8:  begin
                            RowConversion := ConvertS6D8;
                            SourceBytesPerPixel := 4;
                        end;
                    {$IFDEF HIGHPRECISION}
                    16: begin
                            RowConversion := ConvertS6D16;
                            SourceBytesPerPixel := 8;
                        end;
                    {$ENDIF HIGHPRECISION}
                {$IFNDEF USEHACKS}
                else exit;
                {$ENDIF USEHACKS}
                end;
                {$IFDEF INTERLACE}
                SourceSample := 4;
                {$ENDIF INTERLACE}
                TargetBytesPerPixel := 3;
                BITMAPINFO.bmiHeader.biBitCount := 24;
            end;
        {$ENDIF csRGBA}
        {$IFNDEF USEHACKS}
        else exit;
        {$ENDIF USEHACKS}
        end;

        // IMPORTANT: If SourceBitsPerSample less then 8 SourceBytesPerPixel
        // means Source Pixels Per Byte, otherwise it means what means
        if SourceBitsPerSample < 8 then begin
            SourceBytesPerRow := (IP.Width+SourceBytesPerPixel - 1) div SourceBytesPerPixel;
            // IMPORTANT: Now switch SourceBytesPerPixel to 1 if SourceBitsPerSample
            // less then 8. Old value not used more.
            SourceBytesPerPixel := 1;
        end else
            SourceBytesPerRow := IP.Width * SourceBytesPerPixel;
        TargetBytesPerRow := ((TargetBytesPerPixel * IP.Width +3) shr 2) shl 2;
    end;
    with BITMAPINFO^ do begin
        bmiHeader.biHeight := IP.Height;
        bmiHeader.biWidth := IP.Width;
        bmiHeader.biPlanes := 1;
        bmiHeader.biSize := sizeof(bmiHeader);
    end;
    Bitmap := CreateDIBSection(0, BITMAPINFO^, DIB_RGB_COLORS, IP.DIBBits, 0, 0);
    {$IFNDEF USEHACKS}
    if Bitmap = 0 then exit;
    {$ENDIF USEHACKS}
    include(cuFlags, cu_Bitmap);
    {$IFNDEF USEHACKS}
    if IP.DIBBits = nil then exit;
    result := true;
    {$ENDIF USEHACKS}
end;
//****************************************************************
{$IFNDEF USEHACKS}
function ReadRow(Buffer: Pointer; bytes: integer): Boolean;
{$ELSE USEHACKS}
procedure ReadRow(Buffer: Pointer; bytes: integer);
{$ENDIF USEHACKS}
var     ZLibResult: Integer;
begin
    {$IFNDEF USEHACKS}
    result := false;
    {$ENDIF USEHACKS}
    InflateStream.NextOutput := Buffer;
    InflateStream.AvailableOutput := Bytes;
    repeat
        if InflateStream.AvailableInput = 0 then begin
            IDATSize:=0;
            while IDATSize=0 do begin
                {$IFNDEF USEHACKS}
                if not IsChunk(IDAT) then Exit;
                if not ReadDataAndCheckCRC then exit;
                {$ELSE USEHACKS}
                ReadDataAndCheckCRC;
                {$ENDIF USEHACKS}
                CurrentSource := RawBuffer;
                IDATSize:=Header.Length;
                {$IFNDEF USEHACKS}
                if not LoadAndSwapHeader then exit;
                {$ELSE USEHACKS}
                LoadAndSwapHeader;
                {$ENDIF USEHACKS}
            end;
        end;
        InflateStream.NextInput := CurrentSource;
        InflateStream.AvailableInput := IDATSize-(Integer(CurrentSource)-Integer(RawBuffer));
        ZLibResult := Inflate(InflateStream, Z_PARTIAL_FLUSH);
        CurrentSource := InflateStream.NextInput;
        if ZLibResult = Z_STREAM_END then begin
            if (InflateStream.AvailableOutput <> 0) or (InflateStream.AvailableInput<>0) then exit;
            Break;
        end;
        if ZLibResult <> Z_OK then exit;
    until InflateStream.AvailableOutput = 0;
    {$IFNDEF USEHACKS}
    result := true;
    {$ENDIF USEHACKS}
end;
//****************************************************************
procedure CreateGrayPalette;
var     Entries: integer;
        val: byte;
        i: integer;
        mult: byte;
begin
    if Description.BitDepth = 16 then
        Entries := 255
    else
        Entries := (1 shl Description.BitDepth)-1;
    BITMAPINFO := AllocMem(sizeof(TRGBQuad)*256 + sizeof(TBITMAPINFO));
    Include(cuFlags, cu_BITMAPINFO);
    mult := (255 div Entries);
    for i := 0 to Entries do begin
        val := mult*i;
        BITMAPINFO.bmiColors[i] := TRGBQuad((val shl 16) or (val shl 8) or val);
    end;
end;
//****************************************************************
procedure LoadPalette;
var     Entries: integer;
begin
    Entries := Header.Length div 3;
    BITMAPINFO := AllocMem(sizeof(TRGBQuad)*256 + sizeof(TBITMAPINFO));
    Include(cuFlags, cu_BITMAPINFO);
    asm
        push eax
        push ecx
        push edi
        push esi
        mov esi, [RawBuffer]
        mov edi, [BITMAPINFO]
        lea edi, TBITMAPINFO(edi).bmiColors
        mov ecx, [Entries]
        dec ecx
        jz @@02
    @@01:
        mov eax, [esi]
        bswap eax
        shr eax, 8
        mov [edi], eax
        add edi, 4
        add esi, 3
        dec ecx
        jnz @@01
    @@02:
        dec esi
        mov eax, [esi]
        bswap eax
        and eax, $00FFFFFF
        mov [edi], eax
        pop esi
        pop edi
        pop ecx
        pop eax
    end;
    IP.HasPallete := true;
end;



//****************************************************************
//****************************************************************
//****************************************************************
//****************************************************************
function tinyLoadPNGResource(var TargetBitmap: PBitMap; Inst : HInst; ResName : PChar; ResType : PChar): DWORD;
var     Stream: PStream;
begin
    Stream := NewMemoryStream;
    Resource2Stream(Stream, Inst, ResName, ResType);
    Stream.Position := 0;
    Result := tinyLoadPNG(TargetBitmap, Stream);
    Stream.Free;
end;
//****************************************************************
function tinyLoadPNG(var TargetBitmap: PBitMap; FileImage: PStream): DWORD;
var     Row: DWORD;
        Magic: array[0..7] of byte;
        EvenRow: boolean;
        {$IFDEF INTERLACE}
        Pass: integer;
        InterlaceRowBytes: integer;
        InterlaceWidth: integer;
        {$ENDIF INTERLACE}
        tempRes: Boolean;

begin
    {$IFNDEF USEHACKS}
    TargetBitmap := nil;
    {$ELSE USEHACKS}
    result := tinyERROR_OK;
    {$ENDIF USEHACKS}
    cuFlags := [];
    sFile := FileImage;
    sFile_Position := sFile.Position;
    sFile_Size := sFile.Size;
    include (cuFlags, cu_sFile);
    {$IFNDEF USEHACKS}
    tempRes := sFile_Size - sFile_Position > (Sizeof(Magic) + sizeof(Header) + sizeof(Description));
    if tempRes then begin
        sFile.Read(Magic,sizeof(Magic));
        tempRes := CompareMem(@Magic, @PNGMagic, 8);
    end;
    if not tempRes then begin
        result := tinyERROR_NotPNGFile;
        CleanUp;
        exit;
    end;
    {$ELSE USEHACKS}
    sFile.Seek (8, spCurrent);
    {$ENDIF USEHACKS};

//begin****Initialization**********
    RawBuffer := nil;
    ZeroMemory(@IP, sizeof(IP));
    ZeroMemory(@CM, sizeof(CM));
    ZeroMemory(@InflateStream, sizeof(InflateStream));
//end******Initialization**********

//begin****First Loop**************
    {$IFNDEF USEHACKS}
    if not LoadAndSwapHeader then begin
        result := tinyERROR_UnexpectedEOF;
        CleanUp;
        exit;
    end;
    {$ELSE USEHACKS}
    LoadAndSwapHeader;
    {$ENDIF USEHACKS}

    {$IFNDEF USEHACKS}
    if IsChunk(IHDR) then begin
    {$ENDIF USEHACKS}
        {$IFNDEF USEHACKS}
        tempRes := ReadDataAndCheckCRC;
        include (cuFlags, cu_RawBuffer);
        if not tempRes then begin
            result := tinyERROR_UnexpectedEOF_or_WrongCRC;
            CleanUp;
            exit;
        end;
        {$ELSE USEHACKS}
        ReadDataAndCheckCRC;
        {$ENDIF USEHACKS}
        {$IFNDEF USEHACKS}
        if Header.Length = sizeof(Description) then
        {$ENDIF USEHACKS}
            Move(RawBuffer^, Description, sizeof(Description))
        {$IFNDEF USEHACKS}
        else begin
            result := tinyERROR_WrongHeader;
            CleanUp;
            exit;
        end;
        if (Description.Width = 0) or (Description.Height = 0)
            or (Description.Filter <> 0) or (Description.Compression <> 0)  then begin
            result := tinyERROR_WrongHeader;
            CleanUp;
            Exit;
        end
        {$ENDIF USEHACKS};
        asm
            mov eax, dword ptr [Description]
            bswap eax
            mov dword ptr [Description], eax
            mov eax, dword ptr [Description+4]
            bswap eax
            mov dword ptr [Description+4], eax
        end;
        with IP do begin
            Width := Description.Width;
            Height := Description.Height;
            HasAlpha := Boolean(Description.ColorType and $04);
            Interlaced := Boolean(Description.Interlaced);
            {$IFNDEF USEHACKS}
            {$IFDEF INTERLACE}
            if Description.Interlaced > 1 then begin
                result := tinyERROR_UnsupportedImageFormat;
                CleanUp;
                exit;
            end;
            {$ELSE INTERLACE}
            if Interlaced then begin
                result := tinyERROR_NotIncludedFormat;
                CleanUp;
                exit;
            end;
            {$ENDIF INTERLACE}
            {$ENDIF USEHACKS}

            while sFile.Position < sFile_size do begin
                {$IFNDEF USEHACKS}
                if not LoadAndSwapHeader then begin
                    result := tinyERROR_UnexpectedEOF;
                    CleanUp;
                    exit;
                end;
                {$ELSE USEHACKS}
                LoadAndSwapHeader;
                {$ENDIF USEHACKS}
//**************
                if IsChunk(PLTE) then begin
                    {$IFNDEF USEHACKS}
                    if HasPallete or (Header.Length mod 3<>0) or (Header.Length > 256*3)
                        or (Description.ColorType in [0, 4]) then begin
                        result := tinyERROR_WrongSegmentsSequence;
                        CleanUp;
                        exit;
                    end;
                    if not ReadDataAndCheckCRC then begin
                        result := tinyERROR_UnexpectedEOF_or_WrongCRC;
                        CleanUp;
                        exit;
                    end;
                    {$ELSE USEHACKS}
                    ReadDataAndCheckCRC;
                    {$ENDIF USEHACKS}
                    LoadPalette;
                    Continue;
                end;
//**************
                if IsChunk(IDAT) then begin
                    {$IFNDEF USEHACKS}
                    if (Description.ColorType = 3) and (not HasPallete) then begin
                        result := tinyERROR_WrongSegmentsSequence;
                        CleanUp;
                        exit;
                    end;
                    {$ENDIF USEHACKS}
                    break;
                end;
//**************
                {$IFNDEF USEHACKS}
                if (sFile_Size - sFile.Position) >= Header.Length + 4 then
                {$ENDIF USEHACKS}
                    sFile.Seek(Header.Length + 4, spCurrent)
                {$IFNDEF USEHACKS}
                else begin
                    result := tinyERROR_UnexpectedEOF;
                    CleanUp;
                    exit;
                end;
                if IsChunk(IEND) then begin
                    result := tinyERROR_WrongSegmentsSequence;
                    CleanUp;
                    exit;
                end
                {$ENDIF USEHACKS};
            end; //while sFile.Position < sFile_size do begin
        end; //with IP do begin
    {$IFNDEF USEHACKS}
    end else begin //if IsChunk(IHDR)
        result := tinyERROR_WrongSegmentsSequence;
        CleanUp;
        exit;
    end;
    {$ENDIF USEHACKS}
//end******First Loop**************

//begin***Data Transfer************
    {$IFDEF INCGRAY}
    if Description.ColorType in [0,4] then
        CreateGrayPalette;
    {$ENDIF INCGRAY}
    {$IFNDEF USEHACKS}
    if not SetupColorDepthAndMakeBitmap then begin
        result := tinyERROR_UnsupportedColorFormat;
        CleanUp;
        exit;
    end;
    {$ELSE USEHACKS}
    SetupColorDepthAndMakeBitmap;
    {$ENDIF USEHACKS}
    with CM do begin
        {$IFNDEF USEHACKS}
        if InflateInit(InflateStream) < 0 then begin
            result := tinyERROR_UncompressorInitError;
            CleanUp;
            exit;
        end;
        {$ELSE USEHACKS}
        InflateInit(InflateStream);
        {$ENDIF USEHACKS}
        include(cuFlags, cu_Inflate);
        RowBuffer[True] := AllocMem(SourceBytesPerRow + 1);
        RowBuffer[False] := AllocMem(SourceBytesPerRow + 1);
        include(cuFlags, cu_RowBuffer);
        EvenRow := True;
        {$IFDEF INTERLACE}
        if IP.Interlaced then begin
            InterlaceBuffer := AllocMem(TargetBytesPerPixel * IP.Width);
            include(cuFlags, cu_InterlaceBuffer);
            for Pass := 0 to 6 do begin
                if IP.Width <= ColumnStart[Pass] then Continue;
                InterlaceWidth := (IP.Width + ColumnIncrement[Pass]-1 - ColumnStart[Pass]) div ColumnIncrement[Pass];
                InterlaceRowBytes := SourceSample * ((InterlaceWidth*SourceBitsPerSample+7) div 8);
                Row := RowStart[Pass];
                Mask := PassMask[Pass];
                ZeroMemory(Pointer(RowBuffer[not EvenRow]), SourceBytesPerRow + 1);
                while Row < IP.Height do begin
                    {$IFNDEF USEHACKS}
                    if not ReadRow(RowBuffer[EvenRow], InterlaceRowBytes+1) then begin
                        result := tinyERROR_UnexpectedEOF_or_DataStreamError;
                        CleanUp;
                        exit;
                    end;
                    {$ELSE USEHACKS}
                    ReadRow(RowBuffer[EvenRow], InterlaceRowBytes+1);
                    {$ENDIF USEHACKS}
                    {$IFDEF ALLOWFILTERS}
                    FilteredBytes := InterlaceRowBytes;
                    case Byte(RowBuffer[EvenRow][0]) of
                        0: ;
                        {$IFDEF FILTER_SUB}
                        1:  Filter_SUB(@RowBuffer[EvenRow][1]);
                        {$ENDIF FILTER_SUB}
                        {$IFDEF FILTER_UP}
                        2:  Filter_UP(@RowBuffer[EvenRow][1], @RowBuffer[not EvenRow][1]);
                        {$ENDIF FILTER_UP}
                        {$IFDEF FILTER_AVERANGE}
                        3:  Filter_AVERAGE(@RowBuffer[EvenRow][1], @RowBuffer[not EvenRow][1]);
                        {$ENDIF FILTER_AVERANGE}
                        {$IFDEF FILTER_PAETH}
                        4:  Filter_PAETH(@RowBuffer[EvenRow][1], @RowBuffer[not EvenRow][1]);
                        {$ENDIF FILTER_PAETH}
                    {$IFNDEF USEHACKS}
                    else
                        result := tinyERROR_UnsupportedDataFilter;
                        CleanUp;
                        exit;
                    {$ENDIF USEHACKS}
                    end;
                    {$ELSE ALLOWFILTERS}
                    {$IFNDEF USEHACKS}
                    if Byte(RowBuffer[EvenRow][0]) <> 0 then begin
                        result := tinyERROR_UnsupportedDataFilter;
                        CleanUp;
                        exit;
                    end;
                    {$ENDIF USEHACKS}
                    {$ENDIF ALLOWFILTERS}
                    RowConversion(Pointer(RowBuffer[EvenRow]+1), InterlaceBuffer, InterlaceWidth);
                    Expand(InterlaceBuffer, Pointer(DWORD(IP.DIBBits) + TargetBytesPerRow*(IP.Height -Row-1)), IP.Width);
                    EvenRow := not EvenRow;
                    Inc(Row,RowIncrement[Pass]);
                end;
            end;
        end else begin
        {$ENDIF INTERLACE}
            for Row := 0 to IP.Height-1 do begin
                {$IFNDEF USEHACKS}
                if not ReadRow(RowBuffer[EvenRow], SourceBytesPerRow + 1) then begin
                    result := tinyERROR_UnexpectedEOF_or_DataStreamError;
                    CleanUp;
                    exit;
                end;
                {$ELSE USEHACKS}
                ReadRow(RowBuffer[EvenRow], SourceBytesPerRow + 1);
                {$ENDIF USEHACKS}
                {$IFDEF ALLOWFILTERS}
                FilteredBytes := SourceBytesPerRow;
                case Byte(RowBuffer[EvenRow][0]) of
                    0: ;
                    {$IFDEF FILTER_SUB}
                    1:  Filter_SUB(@RowBuffer[EvenRow][1]);
                    {$ENDIF FILTER_SUB}
                    {$IFDEF FILTER_UP}
                    2:  Filter_UP(@RowBuffer[EvenRow][1], @RowBuffer[not EvenRow][1]);
                    {$ENDIF FILTER_UP}
                    {$IFDEF FILTER_AVERANGE}
                    3:  Filter_AVERAGE(@RowBuffer[EvenRow][1], @RowBuffer[not EvenRow][1]);
                    {$ENDIF FILTER_AVERANGE}
                    {$IFDEF FILTER_PAETH}
                    4:  Filter_PAETH(@RowBuffer[EvenRow][1], @RowBuffer[not EvenRow][1]);
                    {$ENDIF FILTER_PAETH}
                {$IFNDEF USEHACKS}
                else
                    result := tinyERROR_UnsupportedDataFilter;
                    CleanUp;
                    exit;
                {$ENDIF USEHACKS}
                end;
                {$ELSE ALLOWFILTERS}
                {$IFNDEF USEHACKS}
                if Byte(RowBuffer[EvenRow][0]) <> 0 then begin
                    result := tinyERROR_UnsupportedDataFilter;
                    CleanUp;
                    exit;
                end;
                {$ENDIF USEHACKS}
                {$ENDIF ALLOWFILTERS}
                RowConversion(Pointer(RowBuffer[EvenRow]+1), Pointer(DWORD(IP.DIBBits) + TargetBytesPerRow*(IP.Height -Row-1)), IP.Width);
                EvenRow := not EvenRow;
            end;
        {$IFDEF INTERLACE}
        end;
        {$ENDIF INTERLACE}
    end; //with CM do begin
//end*****Data Transfer************

    exclude(cuFlags, cu_Bitmap);
    CleanUp;
    TargetBitmap := NewBitmap(0, 0);
    TargetBitmap.Handle := Bitmap;
    result := tinyERROR_OK;
end;
end.
