]> code.delx.au - refind/blobdiff - libeg/load_icns.c
Added icons and libeg files
[refind] / libeg / load_icns.c
diff --git a/libeg/load_icns.c b/libeg/load_icns.c
new file mode 100644 (file)
index 0000000..6af234d
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * 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.
+ */
+
+#include "libegint.h"
+
+//
+// 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) {
+        Print(L" 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...
+        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;
+        }
+        
+        /* FUTURE: try to load a different size and scale it later
+            if (DataPtr == NULL && FetchPixelSize == 32) {
+                FetchPixelSize = 128;
+                continue;
+            }
+        */
+        break;
+    }
+    
+    if (DataPtr == NULL)
+        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) {
+            Print(L" 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;
+}
+
+/* EOF */