/*
 * libeg/load_icns.c
 * Loading function for .icns Apple icon images
 *
 * Copyright (c) 2006 Christoph Pfisterer
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 *  * Neither the name of Christoph Pfisterer nor the names of the
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
//#if !defined(LODEPNG)
#include "libegint.h"

#ifndef DEBUG_ALL
#define DEBUG_IMG 0
#else
#define DEBUG_IMG DEBUG_ALL
#endif

#if DEBUG_IMG == 0
#define DBG(...)
#else
#define DBG(...) DebugLog(DEBUG_IMG, __VA_ARGS__)
#endif


//
// Decompress .icns RLE data
//

VOID egDecompressIcnsRLE(IN OUT UINT8 **CompData, IN OUT UINTN *CompLen, IN UINT8 *PixelData, IN UINTN PixelCount)
{
    UINT8 *cp;
    UINT8 *cp_end;
    UINT8 *pp;
    UINTN pp_left;
    UINTN len, i;
    UINT8 value;
    
    // setup variables
    cp = *CompData;
    cp_end = cp + *CompLen;
    pp = PixelData;
    pp_left = PixelCount;
    
    // decode
    while (cp + 1 < cp_end && pp_left > 0) {
        len = *cp++;
        if (len & 0x80) {   // compressed data: repeat next byte
            len -= 125;
            if (len > pp_left)
                break;
            value = *cp++;
            for (i = 0; i < len; i++) {
                *pp = value;
                pp += 4;
            }
        } else {            // uncompressed data: copy bytes
            len++;
            if (len > pp_left || cp + len > cp_end)
                break;
            for (i = 0; i < len; i++) {
                *pp = *cp++;
                pp += 4;
            }
        }
        pp_left -= len;
    }
    
  if (pp_left > 0) {
    DBG(" egDecompressIcnsRLE: still need %d bytes of pixel data\n", pp_left);
  }
    
    // record what's left of the compressed data stream
    *CompData = cp;
    *CompLen = (UINTN)(cp_end - cp);
}

//
// Load Apple .icns icons
//

EG_IMAGE * egDecodeICNS(IN UINT8 *FileData, IN UINTN FileDataLength, IN UINTN IconSize, IN BOOLEAN WantAlpha)
{
    EG_IMAGE            *NewImage;
    UINT8               *Ptr, *BufferEnd, *DataPtr, *MaskPtr;
    UINT32              BlockLen, DataLen, MaskLen;
    UINTN               FetchPixelSize, PixelCount, i;
    UINT8               *CompData;
    UINTN               CompLen;
    UINT8               *SrcPtr;
    EG_PIXEL            *DestPtr;
    
    if (FileDataLength < 8 || FileData == NULL ||
        FileData[0] != 'i' || FileData[1] != 'c' || FileData[2] != 'n' || FileData[3] != 's') {
        // not an icns file...
      DBG("not icns\n");
      return NULL;
    }
    
    FetchPixelSize = IconSize;
    for (;;) {
        DataPtr = NULL;
        DataLen = 0;
        MaskPtr = NULL;
        MaskLen = 0;
        
        Ptr = FileData + 8;
        BufferEnd = FileData + FileDataLength;
        // iterate over tagged blocks in the file
        while (Ptr + 8 <= BufferEnd) {
            BlockLen = ((UINT32)Ptr[4] << 24) + ((UINT32)Ptr[5] << 16) + ((UINT32)Ptr[6] << 8) + (UINT32)Ptr[7];
            if (Ptr + BlockLen > BufferEnd)   // block continues beyond end of file
                break;
            
            // extract the appropriate blocks for each pixel size
            if (FetchPixelSize == 128) {
                if (Ptr[0] == 'i' && Ptr[1] == 't' && Ptr[2] == '3' && Ptr[3] == '2') {
                    if (Ptr[8] == 0 && Ptr[9] == 0 && Ptr[10] == 0 && Ptr[11] == 0) {
                        DataPtr = Ptr + 12;
                        DataLen = BlockLen - 12;
                    }
                } else if (Ptr[0] == 't' && Ptr[1] == '8' && Ptr[2] == 'm' && Ptr[3] == 'k') {
                    MaskPtr = Ptr + 8;
                    MaskLen = BlockLen - 8;
                }
                
            } else if (FetchPixelSize == 48) {
                if (Ptr[0] == 'i' && Ptr[1] == 'h' && Ptr[2] == '3' && Ptr[3] == '2') {
                    DataPtr = Ptr + 8;
                    DataLen = BlockLen - 8;
                } else if (Ptr[0] == 'h' && Ptr[1] == '8' && Ptr[2] == 'm' && Ptr[3] == 'k') {
                    MaskPtr = Ptr + 8;
                    MaskLen = BlockLen - 8;
                }
                
            } else if (FetchPixelSize == 32) {
                if (Ptr[0] == 'i' && Ptr[1] == 'l' && Ptr[2] == '3' && Ptr[3] == '2') {
                    DataPtr = Ptr + 8;
                    DataLen = BlockLen - 8;
                } else if (Ptr[0] == 'l' && Ptr[1] == '8' && Ptr[2] == 'm' && Ptr[3] == 'k') {
                    MaskPtr = Ptr + 8;
                    MaskLen = BlockLen - 8;
                }
                
            } else if (FetchPixelSize == 16) {
                if (Ptr[0] == 'i' && Ptr[1] == 's' && Ptr[2] == '3' && Ptr[3] == '2') {
                    DataPtr = Ptr + 8;
                    DataLen = BlockLen - 8;
                } else if (Ptr[0] == 's' && Ptr[1] == '8' && Ptr[2] == 'm' && Ptr[3] == 'k') {
                    MaskPtr = Ptr + 8;
                    MaskLen = BlockLen - 8;
                }
                
            }
            
            Ptr += BlockLen;
        }
        //TODO - take different larger size if not found
        // FUTURE: try to load a different size and scale it later
            if (DataPtr == NULL && FetchPixelSize == 128) {
                FetchPixelSize = 32;
                continue;
            }
        
        break;
    }
    
  if (DataPtr == NULL) {
    DBG("not found such IconSize\n");
    return NULL;   // no image found
  }
    
    // allocate image structure and buffer
    NewImage = egCreateImage(FetchPixelSize, FetchPixelSize, WantAlpha);
    if (NewImage == NULL)
        return NULL;
    PixelCount = FetchPixelSize * FetchPixelSize;
    
    if (DataLen < PixelCount * 3) {
        
        // pixel data is compressed, RGB planar
        CompData = DataPtr;
        CompLen  = DataLen;
        egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, r), PixelCount);
        egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, g), PixelCount);
        egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, b), PixelCount);
        // possible assertion: CompLen == 0
        if (CompLen > 0) {
            DBG(" egLoadICNSIcon: %d bytes of compressed data left\n", CompLen);
        }
        
    } else {
        
        // pixel data is uncompressed, RGB interleaved
        SrcPtr  = DataPtr;
        DestPtr = NewImage->PixelData;
        for (i = 0; i < PixelCount; i++, DestPtr++) {
            DestPtr->r = *SrcPtr++;
            DestPtr->g = *SrcPtr++;
            DestPtr->b = *SrcPtr++;
        }
        
    }
    
    // add/set alpha plane
    if (MaskPtr != NULL && MaskLen >= PixelCount && WantAlpha)
        egInsertPlane(MaskPtr, PLPTR(NewImage, a), PixelCount);
    else
        egSetPlane(PLPTR(NewImage, a), WantAlpha ? 255 : 0, PixelCount);
    
    // FUTURE: scale to originally requested size if we had to load another size
    
    return NewImage;
}
//#endif
/* EOF */
