123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843 |
- <?php
- /***************************************************************************
- * Original floIcon copyright (C) 2007 by Joshua Hatfield. *
- * *
- * In order to use any part of this floIcon Class, you must comply with *
- * the license in 'license.doc'. In particular, you may not remove this *
- * copyright notice. *
- * *
- * Much time and thought has gone into this software and you are *
- * benefitting. We hope that you share your changes too. What goes *
- * around, comes around. *
- ***************************************************************************
- Version 1.1.1:
- Date: 2009-03-16
- 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();
- $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.
- }
- }
- }
- class floIconImage {
- var $_imageResource = null;
- var $_entry = "";
- var $_entryIconFormat = "";
- var $_header = "";
- var $_headerIconFormat = "";
- var $_imageIconFormat = ""; // Includes palette and mask.
- function formatEntryForIco($offset) {
- // Format the entry, this has to be done here because we need the offset to get the full information.
- $this->_entry["FileOffset"] = $offset;
- $this->_entryIconFormat = pack("CCCCSSLL",
- $this->_entry["Width"]>=256?0:$this->_entry["Width"],
- $this->_entry["Height"]>=256?0:$this->_entry["Height"],
- $this->_entry["ColorCount"],
- $this->_entry["Reserved"],
- $this->_entry["Planes"],
- $this->_entry["BitCount"],
- $this->_entry["SizeInBytes"],
- $this->_entry["FileOffset"]
- );
- return $this->_entryIconFormat;
- }
- function formatImageForIco() {
- // Format the entry, this has to be done here because we need the offset to get the full information.
- return ($this->_headerIconFormat.$this->_imageIconFormat);
- }
- // Will move $bitCount UP to $desiredBitCount if $bitCount is found to be less than it.
- function setImageResource($imageResource, $desiredBitCount = 1, $pngIfWidthExceeds = 48) {
- imagesavealpha($imageResource, true);
- imagealphablending($imageResource, false);
- $height = imagesy($imageResource);
- $width = imagesx($imageResource);
-
- // Parse resource to determine header and icon format
- // Find Palette information
- $is_32bit = false; // Start with an assumption and get proven wrong.
- $hasTransparency = 0;
- $blackColor = false;
- $bitCount = 0;
- $realPalette = array();
- $realIndexPalette = array();
- for ($x = 0; $x < $width && !$is_32bit; $x++) {
- for ($y = 0; $y < $height && !$is_32bit; $y++) {
- $colorIndex = imagecolorat($imageResource, $x, $y);
- $color = imagecolorsforindex($imageResource, $colorIndex);
- if ($color["alpha"] == 0) {
- // No point continuing if there's more than 256 colors or it's 32bit.
- if (count($realPalette) < 257 && !$is_32bit) {
- $inRealPalette = false;
- foreach($realPalette as $realPaletteKey => $realPaletteColor) {
- if (
- $color["red"] == $realPaletteColor["red"] and
- $color["green"] == $realPaletteColor["green"] and
- $color["blue"] == $realPaletteColor["blue"]
- ) {
- $inRealPalette = $realPaletteKey;
- break;
- }
- }
- if ($inRealPalette === false) {
- $realIndexPalette[$colorIndex] = count($realPalette);
- if (
- $blackColor === false and
- $color["red"] == 0 and
- $color["green"] == 0 and
- $color["blue"] == 0
- ) {
- $blackColor = count($realPalette);
- }
- $realPalette[] = $color;
- } else {
- $realIndexPalette[$colorIndex] = $inRealPalette;
- }
- }
- } else {
- $hasTransparency = 1;
- }
- if ($color["alpha"] != 0 && $color["alpha"] != 127) {
- $is_32bit = true;
- }
- }
- }
- if ($is_32bit) {
- $colorCount = 0;
- $bitCount = 32;
- } else {
- if ($hasTransparency && $blackColor === false) {
- // We need a black color to facilitate transparency. Unfortunately, this can
- // increase the palette size by 1 if there's no other black color.
- $blackColor = count($realPalette);
- $color = array(
- "red" => 0,
- "blue" => 0,
- "green" => 0,
- "alpha" => 0
- );
- $realPalette[] = $color;
- }
- $colorCount = count($realPalette);
- if ($colorCount > 256 || $colorCount == 0) {
- $bitCount = 24;
- } elseif ($colorCount > 16) {
- $bitCount = 8;
- // 8 bit
- } elseif ($colorCount > 2) {
- $bitCount = 4;
- // 4 bit
- } else {
- $bitCount = 1;
- // 1 bit
- }
- if ($desiredBitCount > $bitCount) {
- $bitCount = $desiredBitCount;
- }
- switch ($bitCount) {
- case 24:
- $colorCount = 0;
- break;
- case 8:
- $colorCount = 256;
- break;
- case 4:
- $colorCount = 16;
- break;
- case 1:
- $colorCount = 2;
- break;
- }
- }
- // Create $this->_imageIconFormat...
- $this->_imageIconFormat = "";
- if ($bitCount < 24) {
- $iconPalette = array();
- // Save Palette
- foreach ($realIndexPalette as $colorIndex => $paletteIndex) {
- $color = $realPalette[$paletteIndex];
- $this->_imageIconFormat .= pack("CCCC", $color["blue"], $color["green"], $color["red"], 0);
- }
- while (strlen($this->_imageIconFormat) < $colorCount * 4) {
- $this->_imageIconFormat .= pack("CCCC", 0, 0, 0, 0);
- }
- // Save Each Pixel as Palette Entry
- $byte = 0; // For $bitCount < 8 math
- $bitPosition = 0; // For $bitCount < 8 math
- for ($y = 0; $y < $height; $y++) {
- for ($x = 0; $x < $width; $x++) {
- $color = imagecolorat($imageResource, $x, $height-$y-1);
- if (isset($realIndexPalette[$color])) {
- $color = $realIndexPalette[$color];
- } else {
- $color = $blackColor;
- }
- if ($bitCount < 8) {
- $bitPosition += $bitCount;
- $colorAdjusted = $color * pow(2, 8 - $bitPosition);
- $byte += $colorAdjusted;
- if ($bitPosition == 8) {
- $this->_imageIconFormat .= chr($byte);
- $bitPosition = 0;
- $byte = 0;
- }
- } else {
- $this->_imageIconFormat .= chr($color);
- }
- }
- // Each row ends with dumping the remaining bits and filling up to the 32bit line with 0's.
- if ($bitPosition) {
- $this->_imageIconFormat .= chr($byte);
- $bitPosition = 0;
- $byte = 0;
- }
- if (strlen($this->_imageIconFormat)%4) $this->_imageIconFormat .= str_repeat(chr(0), 4-(strlen($this->_imageIconFormat)%4));
- }
- } else {
- // Save each pixel.
- for ($y = 0; $y < $height; $y++) {
- for ($x = 0; $x < $width; $x++) {
- $color = imagecolorat($imageResource, $x, $height-$y-1);
- $color = imagecolorsforindex($imageResource, $color);
- if ($bitCount == 24) {
- if ($color["alpha"]) {
- $this->_imageIconFormat .= pack("CCC", 0, 0, 0);
- } else {
- $this->_imageIconFormat .= pack("CCC", $color["blue"], $color["green"], $color["red"]);
- }
- } else {
- $color["alpha"] = round((127-$color["alpha"]) / 127 * 255);
- $this->_imageIconFormat .= pack("CCCC", $color["blue"], $color["green"], $color["red"], $color["alpha"]);
- }
- }
- if (strlen($this->_imageIconFormat)%4) $this->_imageIconFormat .= str_repeat(chr(0), 4-(strlen($this->_imageIconFormat)%4));
- }
- }
- // save AND map (transparency)
- $byte = 0; // For $bitCount < 8 math
- $bitPosition = 0; // For $bitCount < 8 math
- for ($y = 0; $y < $height; $y++) {
- for ($x = 0; $x < $width; $x++) {
- if ($bitCount < 32) {
- $color = imagecolorat($imageResource, $x, $height-$y-1);
- $color = imagecolorsforindex($imageResource, $color);
- $color = $color["alpha"] == 127?1:0;
- } else {
- $color = 0;
- }
- $bitPosition += 1;
- $colorAdjusted = $color * pow(2, 8 - $bitPosition);
- $byte += $colorAdjusted;
- if ($bitPosition == 8) {
- $this->_imageIconFormat .= chr($byte);
- $bitPosition = 0;
- $byte = 0;
- }
- }
- // Each row ends with dumping the remaining bits and filling up to the 32bit line with 0's.
- if ($bitPosition) {
- $this->_imageIconFormat .= chr($byte);
- $bitPosition = 0; // For $bitCount < 8 math
- $byte = 0;
- }
- while (strlen($this->_imageIconFormat)%4) {
- $this->_imageIconFormat .= chr(0);
- }
- }
- if ($colorCount >= 256) {
- $colorCount = 0;
- }
- // Create header information...
- $this->_header = array(
- "Size" => 40,
- "Width" => $width,
- "Height" => $height*2,
- "Planes" => 1,
- "BitCount" => $bitCount,
- "Compression" => 0,
- "ImageSize" => strlen($this->_imageIconFormat),
- "XpixelsPerM" => 0,
- "YpixelsPerM" => 0,
- "ColorsUsed" => $colorCount,
- "ColorsImportant" => 0,
- );
- $this->_headerIconFormat = pack("LLLSSLLLLLL",
- $this->_header["Size"],
- $this->_header["Width"],
- $this->_header["Height"],
- $this->_header["Planes"],
- $this->_header["BitCount"],
- $this->_header["Compression"],
- $this->_header["ImageSize"],
- $this->_header["XpixelsPerM"],
- $this->_header["YpixelsPerM"],
- $this->_header["ColorsUsed"],
- $this->_header["ColorsImportant"]
- );
- $this->_entry = array(
- "Width" => $width,
- "Height" => $height,
- "ColorCount" => $colorCount,
- "Reserved" => 0,
- "Planes" => 1,
- "BitCount" => $bitCount,
- "SizeInBytes" => $this->_header["Size"] + $this->_header["ImageSize"],
- "FileOffset" => -1,
- );
- $this->_entryIconFormat = ""; // This won't get set until it's needed with the offset.
- $this->_imageResource = $imageResource;
- // Make png if width exceeds limit for old ico style
- if ($width > $pngIfWidthExceeds) {
- // I wish there were a better way to get the info than this. If anyone needs a version that doesn't use OB, I can have one that creates a TMP file.
- ob_start();
- imagepng($imageResource);
- $imageAsPng = ob_get_contents();
- ob_end_clean();
- $this->_headerIconFormat = "";
- $this->_imageIconFormat = $imageAsPng;
- }
-
- }
- function _createImageResource() {
- if ($newImage = @imagecreatefromstring($this->_headerIconFormat.$this->_imageIconFormat)) {
- // Vista supports PNG.
- $this->_headerIconFormat = "";
- $this->_imageIconFormat = $this->_headerIconFormat.$this->_imageIconFormat;
- imagesavealpha($newImage, true);
- imagealphablending($newImage, false);
- $this->_imageResource = $newImage;
- } elseif ($this->_entry["Height"] <= 1024 && $this->_entry["Width"] <= 1024) {
- $newImage = imagecreatetruecolor($this->_entry["Width"], $this->_entry["Height"]);
- imagesavealpha($newImage, true);
- imagealphablending($newImage, false);
- $readPosition = 0;
- $palette = array();
- if ($this->_header["BitCount"] < 24) {
- // Read Palette for low bitcounts
- $colorsInPalette = $this->_header["ColorsUsed"]?$this->_header["ColorsUsed"]:$this->_entry["ColorCount"];
- for ($t = 0; $t < pow(2, $this->_header["BitCount"]); $t++) {
- $blue = ord($this->_imageIconFormat[$readPosition++]);
- $green = ord($this->_imageIconFormat[$readPosition++]);
- $red = ord($this->_imageIconFormat[$readPosition++]);
- $readPosition++; // Unused "Reserved" value.
- $existingPaletteEntry = imagecolorexactalpha($newImage, $red, $green, $blue, 0);
- if ($existingPaletteEntry >= 0) {
- $palette[] = $existingPaletteEntry;
- } else {
- $palette[] = imagecolorallocatealpha($newImage, $red, $green, $blue, 0);
- }
- }
- // XOR
- for ($y = 0; $y < $this->_entry["Height"]; $y++) {
- $colors = array();
- for ($x = 0; $x < $this->_entry["Width"]; $x++) {
- if ($this->_header["BitCount"] < 8) {
- $color = array_shift($colors);
- if (is_null($color)) {
- $byte = ord($this->_imageIconFormat[$readPosition++]);
- $tmp_color = 0;
- for ($t = 7; $t >= 0; $t--) {
- $bit_value = pow(2, $t);
- $bit = floor($byte / $bit_value);
- $byte = $byte - ($bit * $bit_value);
- $tmp_color += $bit * pow(2, $t%$this->_header["BitCount"]);
- if ($t%$this->_header["BitCount"] == 0) {
- array_push($colors, $tmp_color);
- $tmp_color = 0;
- }
- }
- $color = array_shift($colors);
- }
- } else {
- $color = ord($this->_imageIconFormat[$readPosition++]);
- }
- imagesetpixel($newImage, $x, $this->_entry["Height"]-$y-1, $palette[$color]) or die("can't set pixel");
- }
- // All rows end on the 32 bit
- if ($readPosition%4) $readPosition += 4-($readPosition%4);
- }
- } else {
- // BitCount >= 24, No Palette.
- // marking position because some icons mark all pixels transparent when using an AND map.
- $markPosition = $readPosition;
- $retry = true;
- $ignoreAlpha = false;
- while ($retry) {
- $alphas = array();
- $retry = false;
- for ($y = 0; $y < $this->_entry["Height"] and !$retry; $y++) {
- for ($x = 0; $x < $this->_entry["Width"] and !$retry; $x++) {
- $blue = ord($this->_imageIconFormat[$readPosition++]);
- $green = ord($this->_imageIconFormat[$readPosition++]);
- $red = ord($this->_imageIconFormat[$readPosition++]);
- if ($this->_header["BitCount"] < 32) {
- $alpha = 0;
- } elseif($ignoreAlpha) {
- $alpha = 0;
- $readPosition++;
- } else {
- $alpha = ord($this->_imageIconFormat[$readPosition++]);
- $alphas[$alpha] = $alpha;
- $alpha = 127-round($alpha/255*127);
- }
- $paletteEntry = imagecolorexactalpha($newImage, $red, $green, $blue, $alpha);
- if ($paletteEntry < 0) {
- $paletteEntry = imagecolorallocatealpha($newImage, $red, $green, $blue, $alpha);
- }
- imagesetpixel($newImage, $x, $this->_entry["Height"]-$y-1, $paletteEntry) or die("can't set pixel");
- }
- if ($readPosition%4) $readPosition += 4-($readPosition%4);
- }
- if ($this->_header["BitCount"] == 32 && isset($alphas[0]) && count($alphas) == 1) {
- $retry = true;
- $readPosition = $markPosition;
- $ignoreAlpha = true;
- }
- }
- }
- // AND map
- if ($this->_header["BitCount"] < 32 || $ignoreAlpha) {
- // Bitcount == 32, No AND (if using alpha).
- $palette[-1] = imagecolorallocatealpha($newImage, 0, 0, 0, 127);
- imagecolortransparent($newImage, $palette[-1]);
- for ($y = 0; $y < $this->_entry["Height"]; $y++) {
- $colors = array();
- for ($x = 0; $x < $this->_entry["Width"]; $x++) {
- $color = array_shift($colors);
- if (is_null($color)) {
- $byte = ord($this->_imageIconFormat[$readPosition++]);
- $tmp_color = 0;
- for ($t = 7; $t >= 0; $t--) {
- $bit_value = pow(2, $t);
- $bit = floor($byte / $bit_value);
- $byte = $byte - ($bit * $bit_value);
- array_push($colors, $bit);
- }
- $color = array_shift($colors);
- }
- if ($color) {
- imagesetpixel($newImage, $x, $this->_entry["Height"]-$y-1, $palette[-1]) or die("can't set pixel");
- }
- }
- // All rows end on the 32 bit.
- if ($readPosition%4) $readPosition += 4-($readPosition%4);
- }
- }
- if ($this->_header["BitCount"] < 24) {
- imagetruecolortopalette($newImage, true, pow(2, $this->_header["BitCount"]));
- }
- }
- $this->_imageResource = $newImage;
- }
- // this function expects that $_entry, $_header and $_imageIconFormat have already been read, specifically from readImageFromICO.
- // Don't call this function except from there.
- function readImageFromICO($filePointer, $entryOffset) {
- $tmpPosition = ftell($filePointer); // So any other applications won't loose their position.
- // Get the entry.
- fseek($filePointer, $entryOffset);
- $this->_entryIconFormat = fread($filePointer, 16);
- $this->_entry = unpack("CWidth/CHeight/CColorCount/CReserved/SPlanes/SBitCount/LSizeInBytes/LFileOffset", $this->_entryIconFormat);
- // Position the file pointer.
- fseek($filePointer, $this->_entry["FileOffset"]);
- // Get the header.
- $this->_headerIconFormat = fread($filePointer, 40);
- $this->_header = unpack("LSize/LWidth/LHeight/SPlanes/SBitCount/LCompression/LImageSize/LXpixelsPerM/LYpixelsPerM/LColorsUsed/LColorsImportant", $this->_headerIconFormat);
- // Get the image.
- $this->_imageIconFormat = @fread($filePointer, $this->_entry["SizeInBytes"] - strlen($this->_headerIconFormat));
- fseek($filePointer, $tmpPosition); // So any other applications won't loose their position.
- if ($newImage = @imagecreatefromstring($this->_headerIconFormat.$this->_imageIconFormat)) {
- // This is a PNG, the supposed header information is useless.
- $this->_header = array (
- "Size" => 0,
- "Width" => imagesx($newImage),
- "Height" => imagesy($newImage) * 2,
- "Planes" => 0,
- "BitCount" => 32,
- "Compression" => 0,
- "ImageSize" => strlen($this->_imageIconFormat),
- "XpixelsPerM" => 0,
- "YpixelsPerM" => 0,
- "ColorsUsed" => 0,
- "ColorsImportant" => 0,
- );
- imagedestroy($newImage);
- }
- // Support for larger images requires entry marked as 0.
- if ($this->_entry["Width"] == 0) {
- $this->_entry["Width"] = $this->_header["Width"];
- }
- if ($this->_entry["Height"] == 0) {
- $this->_entry["Height"] = $this->_header["Height"]/2;
- }
- }
- function getHeader() {
- return $this->_header;
- }
- function getEntry() {
- return $this->_entry;
- }
- function floIconImage() {
- }
- function getHeaderSize() {
- return strlen($this->_headerIconFormat);
- }
- function getSize() {
- return strlen($this->_imageIconFormat);
- }
- function getImageResource() {
- if (!$this->_imageResource) $this->_createImageResource();
- return $this->_imageResource;
- }
- function dealocateResource() {
- @imagedestroy($this->_imageResource);
- $this->_imageResource = null;
- }
- }
- ?>
|