-Changes:
-I was a little hasty on that last update. A couple new bugs from 1.1.0 have
-been fixed.
-
-Version 1.1.0:
-Date: 2009-03-16
-
-Changes:
-Added Vista support.
-Fixed a number of minor bugs. Many thanks to Dvir Berebi for pointing
- them out.
-
-Version 1.0.5:
-Date: 2009-03-15
-
-Changes:
-Fixed bug when operating on low bit count images (1 or 4) with odd dimensions.
-
-Version 1.0.4:
-Date: 2007-05-25
-
-Changes:
-Made function not break quite so bad when reading a PNG file on a Vista icon.
- Now, you shouldn't be loading Vista icons anyways, but since I'm trying to
- upgrade to Vista compatible and I need a comparison, I've got to.
-
-Version 1.0.3:
-Date: 2007-05-25
-
-Changes:
-Okay, this one was just stupid. When adding large image support, I messed up
- and when reading, it doubled the image size. Now, it's fixed.
-I took the opportunity to also add a dummy AND map for 32 images on those
- readers who might be looking for it (though it's not supposed to be used.)
-
-Version 1.0.2:
-Date: 2007-05-24
-
-Sorry about two versions so quickly back to back, but something needed to be
-done with speed...things were getting too slow. I'm sure you'll be okay.
-
-Changes:
-Told palette determination to stop at 257 colors or is 32 bit because the
- palette is not used at that point and gets really slow when it starts
- getting into the high numbers, for instance, in photographs or gradient
- truecolor images with lots of unique colors.
-After experimenting, it appears that Windows XP does in fact support 256x256
- images and larger by setting the entry value to 0 in the 1 byte that holds
- that value and storing the true dimentions in the image header later. Of
- course, it still doesn't use these images in normal operation. XP will
- resize these and use them if no other images are available.
-Wrapped main documentation (this) to the 80th column for easier reading.
-
-Version 1.0.1:
-Date: 2007-05-23
-
-Thank you everyone for actively using the implementation on my site and
-illuminating me very quickly to a number of glaring bugs in this class.
-
-Changes:
-Added version history.
-Fixed bug with non-standard sizes in AND map reading AND writing.
-Fixed bug with palette images using non-black color in backgrounds.
-Fixed bug with height/width reversal reading files.
-
-
-Version 1.0.0:
-Date: 2007-05-17
-Original release date.
-
-
-Foreword:
-If you are simply in the effort of making an ICO file, may I recommend visiting
-my site, http://www.flobi.com/ , and clicking on floIcon. I have a fully
-functional implementation (on which the sample.php is based) where you can also
-see recent icons submitted by other visitors. No registration required, no
-intrusive ads. (As of this writing, there aren't actually any ads at all, I
-might add google ads at some point.)
-
-If you are trying to get an idea of how ICO files, work, may I recommend the
-page I used, http://www.daubnet.com/formats/ICO.html . It does not fully cover
-icon files, but it does a very good job of what it does. Any additional
-information, I will try to post at
-http://www.flobi.com/test/floIcon/more_on_icons.php for your convenience.
-
-If you are trying to get an idea of how image resource files work, I recommend
-ANY other class that deals with images. This class essentially plots points on
-the image, and that's not perticularly advanced.
-
-For any purpose, I wish you luck and feel free to contact me with any bugs,
-comments, questions, etc. - Flobi
-
-Summary:
-This class parses ICO files. It reads directly from the ICO file, headers
-first, so that if you are only retrieving 1 image, the entire ICO file need not
-be parsed. It supports merging ICO files. It supports adding PHP image
-resources as new images to the file. It has an export capability that can
-easily be written to a new (or the same) ICO file for saving ICO files. All
-sizes from 1x1 to 255x255 pixels and 1, 4, 8, 24 (plus transparency) and 32 bit
-images are supported. Image retrieval by size is supported.
-
-Included is a fully functional sample that allows users to upload ICO, GIF,
-JPEG and PNG files into a session icon file as well as remove images from the
-file and download the completed file (sample.php).
-
-Known Limitations: Does not support Vista icons. Does not support inversion
-palette method (because of the limitations of the PHP image resource).
-
-Addendum on Limitations:
-Windows Vista has added support for 256x256 size icons and now stores files as
-PNG's. This class is for older ICO files. A new class is currently under
-development that supports the new Windows Vista format.
-
-Palette inversion (my name for this technique) is the technique of using a black
-pixel (0, 0, 0) with a 1 "AND" pixel. White pixels with a 1 "AND" show
-transparent (or "don't" show). Black pixels with a 1 "AND" show inverted
-(sometimes). Because this method isn't uniformly supported or documented and
-the PHP image resource doesn't support it, I have decided to not as well. This
-does not apply to 32 bit images which include alpha transparency and no AND map.
-
-Though other functions exist, these are the only ones I believe offer usefulness
-to be public.
-floIcon public functions:
- readICO($file, $offset = 0)
- Loads the icon from the specified filepath ($file) starting at the
- specified file offset ($offset). This function MERGES the loaded icon
- images into the floIcon object.
-
- formatICO($offset = 0)
- Returns the current floIcon object formatted as an .ICO file with the
- file offset altered by $offset. If there are too many or too large
- images, causing any images saved past the 4,294,967,296th byte, this
- will return false. (This is well outside PHP's default memory
- allocation.)
-
- addImage($imageResource, $desiredBitCount = 1, $pngIfWidthExceeds = 48)
- Adds an icon image to the icon file based on the passed image resource
- ($imageResource). It will automatically determine the bit count, but
- can be persuaded to increase that to $desiredBitCount if that value is
- greater than the determined bit count.
-
- NOTE: The image resource is saved by REFERRENCE. So, if you edit it
- then call getImage, the resource returned will be the same, editions and
- all. Destruction of the resource will cause a new resource to be
- created in getImage().
-
- getImage($offset)
- Returns the php image resource either assigned by addImage or created
- dynamically at calltime by the data loaded with readICO(). The offset
- specified here ($offset) is the array offset starting at 0 and ending
- at countImages().
-
- getBestImage($height = 32, $width = 32)
- Returns the php images resource of the highest quality image closest to
- the size specified. This could be useful when you only want to display
- the icon in an icon list with a single representative icon. A resized
- copy of the highest quality available image will be returned if there is
- no 32 or 24 bit icon present at the speficied dimentions.
-
- sortImagesBySize()
- Sorts the $this->images array by order of size, smallest to largest.
- This is the optimal sorting for icon files.
-
- countImages()
- Returns a count of how many images are present in the current floIcon
- object.
-
-floIcon public variables:
- $images
- Contains a numerically indexed array of floIconImage objects.
- $updated
- True if an image has been added since load or last formatICO, otherwise
- false.
-
-floIconImage public functions:
- getHeader()
- Returns an associative array containing the information from the ICO
- image header.
-
- getEntry()
- Returns an associative array containing the information from the ICO
- entry header.
-
- NOTE: If this icon image was created by php image resource, this may not
- have accurate information until saving from floIcon with the formatICO()
- function. Specifically, offset information will be inaccurate.
-
- getImageResource()
- Returns a php image resource. Same as floIcon's getImage() function.
-
- setImageResource($imageResource, $desiredBitCount = 1, $pngIfWidthExceeds = 48)
- Changes this icon image based on the passed image resource
- ($imageResource). It will automatically determine the bit count, but can
- be persuaded to increase that to $desiredBitCount if that value is
- greater than the determined bit count.
-
- NOTE: The image resource is saved by REFERRENCE. So, if you edit it
- then call getImageResource, the resource returned will be the same,
- editions and all. Destruction of the resource will cause a new resource
- to be created in getImageResource().
-
- dealocateResource()
- This destroys the image resource variable, freeing up memory. The image
- will automatically be recreated when getImageResource is executed.
-*/
-class floIcon {
- /*
- * $images is an associative array of offset integer => floIconImage object
- */
- var $images; // Array of floIconImage objects.
- var $updated = false;
- function floIcon() {
- $this->images = array();
- }
- function countImages() {
- return count($this->images);
- }
- function getBestImage($height = 32, $width = 32) {
- $best = false;
- $bestEntry = array();
- $secondBest = false;
- $secondBestEntry = array();
- foreach ($this->images as $key => $image) {
- $entry = $image->getEntry();
- $header = $image->getHeader();
- if (!@$entry["BitCount"]) {
- $entry["BitCount"] = $header["BitCount"];
- }
- if ($entry["Height"] == $height && $entry["Width"] == $width && $entry["BitCount"] == 32) {
- return $image->getImageResource();
- } elseif ($entry["Height"] == $height && $entry["Width"] == $width && $entry["BitCount"] > min(4, @$bestEntry["BitCount"])) {
- $best = $image;
- $bestEntry = $entry;
- } elseif (
- !$secondBest or
- $entry["Height"] >= $secondBestEntry["Height"] &&
- $entry["Width"] >= $secondBestEntry["Width"] &&
- $secondBestEntry["BitCount"] >= $secondBestEntry["BitCount"] and
- (
- $entry["Height"] <= 64 && $entry["Height"] > $secondBestEntry["Height"] and
- $entry["Height"] > 64 && $entry["Height"] < $secondBestEntry["Height"]
- ) ||
- (
- $entry["Width"] <= 64 && $entry["Width"] > $secondBestEntry["Width"] and
- $entry["Width"] > 64 && $entry["Width"] < $secondBestEntry["Width"]
- ) ||
- $secondBestEntry["BitCount"] > $secondBestEntry["BitCount"]
- ) {
- $secondBest = $image;
- $secondBestEntry = $entry;
- }
- }
- if ($best) {
- return $best->getImageResource();
- } elseif ($secondBest) {
- if ($secondBestEntry["Width"] != $width || $secondBestEntry["Height"] != $height) {
- $imageResource = $secondBest->getImageResource();
- $newImageResource = imagecreatetruecolor($width, $height);
- imagesavealpha($newImageResource, true);
- imagealphablending($newImageResource, false);
- imagecopyresampled($newImageResource, $imageResource, 0, 0, 0, 0, $width, $height, $secondBestEntry["Width"], $secondBestEntry["Height"]);
- $this->addImage($newImageResource, 32);
- return $newImageResource;
- } else {
- return $secondBest->getImageResource();
- }
- }
- }
- /*
- * readICO merges the icon images from the file to the current list
- */
- function readICO($file, $offset = 0) {
- if (file_exists($file) && filesize($file) > 0 && $filePointer = fopen($file, "r")) {
- fseek($filePointer, $offset);
- $header = unpack("SReserved/SType/SCount", fread($filePointer, 6));
- for ($t = 0; $t < $header["Count"]; $t++) {
- $newImage = new floIconImage();
- if ($newImage->readImageFromICO($filePointer, 6 + ($t * 16))) {
- $this->images[] = $newImage;
- }
- }
- fclose($filePointer);
- }
- }
- function sortImagesBySize() {
- usort($this->images, array("floIcon", "_cmpObj"));
- }
- function formatICO($offset = 0) {
- $this->updated = false;
- $output = "";
- $output .= pack("SSS", 0, 1, count($this->images));
- $output_images = "";
- foreach ($this->images as $image) {
- $newImageOffset = $offset + // Whatever offset we've been given.
- 6 // Header.
- + (count($this->images) * 16) // Entries.
- + strlen($output_images);
- if ($newImageOffset > pow(256, 4) /* 4 bytes available for position */ ) {
- return false;
- }
- $output .= $image->formatEntryForIco($newImageOffset); // The images already in there.
- $output_images .= $image->formatImageForIco();
- }
- return $output.$output_images;
- }
- function _cmpObj($a, $b) {
- $aSize = $a->getSize();
- $bSize = $b->getSize();
- if ($aSize == $bSize) {
- return 0;
- }
- return ($aSize > $bSize)?1:-1;
- }
-
- function addImage($imageResource, $desiredBitCount = 1, $pngIfWidthExceeds = 48) {
- $this->updated = true;
- $newImage = new floIconImage();
- $newImage->setImageResource($imageResource, $desiredBitCount, $pngIfWidthExceeds);
- $this->images[] = $newImage;
- }
- function getImage($offset) {
- if (isset($this->images[$offset])) {
- return $this->images[$offset]->getImageResource();
- } else {
- return false;
- }
- }
- /*
- * getSize computes the
- */
- function getSize() {
- // Compute headers.
- $computedSize = 6; // Always 6 bytes.
- // Add image entry headers
- $computedSize += count($this->images) * 16; // Entry headers are always 16 bytes.
- foreach ($this->images as $image) {
- $computedSize += $image->getSize() + $image->getHeaderSize(); // getSize does not include the header.
- }