]> git.wh0rd.org Git - tt-rss.git/commitdiff
replace floIcon with jimIcon from https://github.com/jimparis/jimIcon
authorAndrew Dolgov <fox@madoka.volgo-balt.ru>
Wed, 26 Jun 2013 04:37:07 +0000 (08:37 +0400)
committerAndrew Dolgov <fox@madoka.volgo-balt.ru>
Wed, 26 Jun 2013 04:37:07 +0000 (08:37 +0400)
.gitignore
lib/floIcon.php [new file with mode: 0644]
lib/jimIcon.php [new file with mode: 0644]

index ac3c24319c24a9f4975ff0fc63d94e0334fb7c6c..b4853806cf7ebe3780d9808cb60f1618e7023a78 100644 (file)
@@ -2,7 +2,6 @@
 *~
 *.DS_Store
 #*
-lib/floIcon.php
 .idea/*
 config.php
 feed-icons/*
diff --git a/lib/floIcon.php b/lib/floIcon.php
new file mode 100644 (file)
index 0000000..1020286
--- /dev/null
@@ -0,0 +1,20 @@
+<?
+// Compatibility shim for hooking up jimIcon to Tiny Tiny RSS.
+
+require_once "jimIcon.php";
+
+class floIconIcon {
+        function getImageResource() {
+                return $this->img;
+        }
+}
+
+class floIcon {
+        function readICO($file) {
+                $jim = new jimIcon();
+                $icon = new floIconIcon();
+                $icon->img = $jim->fromiconstring(file_get_contents($file));
+                $this->images = array($icon);
+        }
+}
+?>
diff --git a/lib/jimIcon.php b/lib/jimIcon.php
new file mode 100644 (file)
index 0000000..940701b
--- /dev/null
@@ -0,0 +1,270 @@
+<?php
+
+// Simple .ICO parsing.  The ICO format is insanely complex and this may
+// fail to correctly handle some technically valid files, but it works
+// on the majority I've found.
+//
+// jimIcon was written in 2013 by Jim Paris <jim@jtan.com> and is
+// released under the terms of the CC0:
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this software to
+// the public domain worldwide. This software is distributed without
+// any arranty.
+//
+// You may have received a copy of the CC0 Public Domain Dedication
+// along with this software.  If not, see
+//   http://creativecommons.org/publicdomain/zero/1.0/
+
+class jimIcon {
+        // Get an image color from a string
+        function get_color($str, $img) {
+                $b = ord($str[0]);
+                $g = ord($str[1]);
+                $r = ord($str[2]);
+                if (strlen($str) > 3) {
+                        $a = 127 - (ord($str[3]) / 2);
+                        if ($a != 0 && $a != 127)
+                                $this->had_alpha = 1;
+                } else {
+                        $a = 0;
+                }
+                if ($a != 127)
+                        $this->all_transaprent = 0;
+                return imagecolorallocatealpha($img, $r, $g, $b, $a);
+        }
+
+        // Given a string with the contents of an .ICO,
+        // return a GD image of the icon, or false on error.
+        function fromiconstring($ico) {
+                $this->error = "(unknown error)";
+                $this->had_alpha = 0;
+
+                // Read header
+                if (strlen($ico) < 6) {
+                        $this->error = "too short";
+                        return false;
+                }
+                $h = unpack("vzero/vtype/vnum", $ico);
+
+                // Must be ICO format with at least one image
+                if ($h["zero"] != 0 || $h["type"] != 1 || $h["num"] == 0) {
+                        // See if we can just parse it with GD directly
+                        // if it's not ICO format; maybe it was a mislabeled
+                        // PNG or something.
+                        $i = @imagecreatefromstring($ico);
+                        if ($i) {
+                                imagesavealpha($i, true);
+                                return $i;
+                        }
+                        $this->error = "not ICO or other image";
+                        return false;
+                }
+
+                // Read directory entries to find the biggest image
+                $most_pixels = 0;
+                for ($i = 0; $i < $h["num"]; $i++) {
+                        $entry = substr($ico, 6 + 16 * $i, 16);
+                        if (!$entry || strlen($entry) < 16)
+                                continue;
+                        $e = unpack("Cwidth/" .
+                                    "Cheight/" .
+                                    "Ccolors/" .
+                                    "Czero/" .
+                                    "vplanes/" .
+                                    "vbpp/" .
+                                    "Vsize/" .
+                                    "Voffset/",
+                                    $entry);
+                        if ($e["width"] == 0)
+                                $e["width"] = 256;
+                        if ($e["height"] == 0)
+                                $e["height"] = 256;
+                        if ($e["zero"] != 0) {
+                                $this->error = "nonzero reserved field";
+                                return false;
+                        }
+                        $pixels = $e["width"] * $e["height"];
+                        if ($pixels > $most_pixels) {
+                                $most_pixels = $pixels;
+                                $most = $e;
+                        }
+                }
+                if ($most_pixels == 0) {
+                        $this->error = "no pixels";
+                        return false;
+                }
+                $e = $most;
+
+                // Extract image data
+                $data = substr($ico, $e["offset"], $e["size"]);
+                if (!$data || strlen($data) != $e["size"]) {
+                        $this->error = "bad image data";
+                        return false;
+                }
+
+                // See if we can parse it (might be PNG format here)
+                $i = @imagecreatefromstring($data);
+                if ($i) {
+                        imagesavealpha($img, true);
+                        return $i;
+                }
+
+                // Must be a BMP.  Parse it ourselves.
+                $img = imagecreatetruecolor($e["width"], $e["height"]);
+                imagesavealpha($img, true);
+                $bg = imagecolorallocatealpha($img, 255, 0, 0, 127);
+                imagefill($img, 0, 0, $bg);
+
+                // Skip over the BITMAPCOREHEADER or BITMAPINFOHEADER;
+                // we'll just assume the palette and pixel data follow
+                // in the most obvious format as described by the icon
+                // directory entry.
+                $bitmapinfo = unpack("Vsize", $data);
+                if ($bitmapinfo["size"] == 40) {
+                        $info = unpack("Vsize/" .
+                                       "Vwidth/" .
+                                       "Vheight/" .
+                                       "vplanes/" .
+                                       "vbpp/" .
+                                       "Vcompress/" .
+                                       "Vsize/" .
+                                       "Vxres/" .
+                                       "Vyres/" .
+                                       "Vpalcolors/" .
+                                       "Vimpcolors/", $data);
+                        if ($e["bpp"] == 0) {
+                                $e["bpp"] = $info["bpp"];
+                        }
+                }
+                $data = substr($data, $bitmapinfo["size"]);
+
+                $height = $e["height"];
+                $width = $e["width"];
+                $bpp = $e["bpp"];
+
+                // For indexed images, we only support 1, 4, or 8 BPP
+                switch ($bpp) {
+                case 1:
+                case 4:
+                case 8:
+                        $indexed = 1;
+                        break;
+                case 24:
+                case 32:
+                        $indexed = 0;
+                        break;
+                default:
+                        $this->error = "bad BPP $bpp";
+                        return false;
+                }
+
+                $offset = 0;
+                if ($indexed) {
+                        $palette = array();
+                        $this->all_transparent = 1;
+                        for ($i = 0; $i < (1 << $bpp); $i++) {
+                                $entry = substr($data, $i * 4, 4);
+                                $palette[$i] = $this->get_color($entry, $img);
+                        }
+                        $offset = $i * 4;
+
+                        // Hack for some icons: if everything was transparent,
+                        // discard alpha channel.
+                        if ($this->all_transparent) {
+                                for ($i = 0; $i < (1 << $bpp); $i++) {
+                                        $palette[$i] &= 0xffffff;
+                                }
+                        }
+                }
+
+                // Assume image data follows in bottom-up order.
+                // First the "XOR" image
+                if ((strlen($data) - $offset) < ($bpp * $height * $width / 8)) {
+                        $this->error = "short data";
+                        return false;
+                }
+                $XOR = array();
+                for ($y = $height - 1; $y >= 0; $y--) {
+                        $x = 0;
+                        while ($x < $width) {
+                                if (!$indexed) {
+                                        $bytes = $bpp / 8;
+                                        $entry = substr($data, $offset, $bytes);
+                                        $pixel = $this->get_color($entry, $img);
+                                        $XOR[$y][$x] = $pixel;
+                                        $x++;
+                                        $offset += $bytes;
+                                } elseif ($bpp == 1) {
+                                        $p = ord($data[$offset]);
+                                        for ($b = 0x80; $b > 0; $b >>= 1) {
+                                                if ($p & $b) {
+                                                        $pixel = $palette[1];
+                                                } else {
+                                                        $pixel = $palette[0];
+                                                }
+                                                $XOR[$y][$x] = $pixel;
+                                                $x++;
+                                        }
+                                        $offset++;
+                                } elseif ($bpp == 4) {
+                                        $p = ord($data[$offset]);
+                                        $pixel1 = $palette[$p >> 4];
+                                        $pixel2 = $palette[$p & 0x0f];
+                                        $XOR[$y][$x] = $pixel1;
+                                        $XOR[$y][$x+1] = $pixel2;
+                                        $x += 2;
+                                        $offset++;
+                                } elseif ($bpp == 8) {
+                                        $pixel = $palette[ord($data[$offset])];
+                                        $XOR[$y][$x] = $pixel;
+                                        $x += 1;
+                                        $offset++;
+                                } else {
+                                        $this->error = "bad BPP";
+                                        return false;
+                                }
+                        }
+                        // End of row padding
+                        while ($offset & 3)
+                                $offset++;
+                }
+
+                // Now the "AND" image, which is 1 bit per pixel.  Ignore
+                // if some of our image data already had alpha values,
+                // or if there isn't enough data left.
+                if ($this->had_alpha ||
+                    ((strlen($data) - $offset) < ($height * $width / 8))) {
+                        // Just return what we've got
+                        for ($y = 0; $y < $height; $y++) {
+                                for ($x = 0; $x < $width; $x++) {
+                                        imagesetpixel($img, $x, $y,
+                                                      $XOR[$y][$x]);
+                                }
+                        }
+                        return $img;
+                }
+
+                // Mask what we have with the "AND" image
+                for ($y = $height - 1; $y >= 0; $y--) {
+                        $x = 0;
+                        while ($x < $width) {
+                                for ($b = 0x80;
+                                     $b > 0 && $x < $width; $b >>= 1) {
+                                        if (!(ord($data[$offset]) & $b)) {
+                                                imagesetpixel($img, $x, $y,
+                                                              $XOR[$y][$x]);
+                                        }
+                                        $x++;
+                                }
+                                $offset++;
+                        }
+
+                        // End of row padding
+                        while ($offset & 3)
+                                $offset++;
+                }
+                return $img;
+        }
+}
+?>
\ No newline at end of file