addImage($file, $sizes); } } /** * Add an image to the generator. * * This function adds a source image to the generator. It serves two main purposes: add a source image if one was * not supplied to the constructor and to add additional source images so that different images can be supplied for * different sized images in the resulting ICO file. For instance, a small source image can be used for the small * resolutions while a larger source image can be used for large resolutions. * * @param string $file Path to the source image file. * @param array $sizes Optional. An array of sizes (each size is an array with a width and height) that the source * image should be rendered at in the generated ICO file. If sizes are not supplied, the size * of the source image will be used. * @return boolean true on success and false on failure. */ public function addImage($file, $sizes = array()) { if (false === ($im = $this->loadImageFile($file))) { return false; } if (empty($sizes)) { $sizes = array(imagesx($im), imagesy($im)); } // If just a single size was passed, put it in array. if (!is_array($sizes[0])) { $sizes = array($sizes); } foreach ((array)$sizes as $size) { list($width, $height) = $size; $new_im = imagecreatetruecolor($width, $height); imagecolortransparent($new_im, imagecolorallocatealpha($new_im, 0, 0, 0, 127)); imagealphablending($new_im, false); imagesavealpha($new_im, true); $source_width = imagesx($im); $source_height = imagesy($im); if (false === imagecopyresampled( $new_im, $im, 0, 0, 0, 0, $width, $height, $source_width, $source_height) ) { continue; } $this->addImageData($new_im); } return true; } /** * Write the ICO file data to a file path. * * @param string $file Path to save the ICO file data into. * @return boolean true on success and false on failure. */ public function saveIco($file) { if (false === ($data = $this->getIcoData())) { return false; } if (false === ($fh = fopen($file, 'w'))) { return false; } if (false === (fwrite($fh, $data))) { fclose($fh); return false; } fclose($fh); return true; } /** * Generate the final ICO data by creating a file header and adding the image data. */ protected function getIcoData() { if (!is_array($this->images) || empty($this->images)) { return false; } $data = pack('vvv', 0, 1, count($this->images)); $pixel_data = ''; $icon_dir_entry_size = 16; $offset = 6 + ($icon_dir_entry_size * count($this->images)); foreach ($this->images as $image) { $data .= pack( 'CCCCvvVV', $image['width'], $image['height'], $image['color_palette_colors'], 0, 1, $image['bits_per_pixel'], $image['size'], $offset ); $pixel_data .= $image['data']; $offset += $image['size']; } $data .= $pixel_data; unset($pixel_data); return $data; } /** * Take a GD image resource and change it into a raw BMP format. */ protected function addImageData($im) { $width = imagesx($im); $height = imagesy($im); $pixel_data = array(); $opacity_data = array(); $current_opacity_val = 0; for ($y = $height - 1; $y >= 0; $y--) { for ($x = 0; $x < $width; $x++) { $color = imagecolorat($im, $x, $y); $alpha = ($color & 0x7F000000) >> 24; $alpha = (1 - ($alpha / 127)) * 255; $color &= 0xFFFFFF; $color |= 0xFF000000 & ($alpha << 24); $pixel_data[] = $color; $opacity = ($alpha <= 127) ? 1 : 0; $current_opacity_val = ($current_opacity_val << 1) | $opacity; if ((($x + 1) % 32) == 0) { $opacity_data[] = $current_opacity_val; $current_opacity_val = 0; } } if (($x % 32) > 0) { while (($x++ % 32) > 0) { $current_opacity_val = $current_opacity_val << 1; } $opacity_data[] = $current_opacity_val; $current_opacity_val = 0; } } $image_header_size = 40; $color_mask_size = $width * $height * 4; $opacity_mask_size = (ceil($width / 32) * 4) * $height; $data = pack('VVVvvVVVVVV', 40, $width, ($height * 2), 1, 32, 0, 0, 0, 0, 0, 0); foreach ($pixel_data as $color) { $data .= pack('V', $color); } foreach ($opacity_data as $opacity) { $data .= pack('N', $opacity); } $image = array( 'width' => $width, 'height' => $height, 'color_palette_colors' => 0, 'bits_per_pixel' => 32, 'size' => $image_header_size + $color_mask_size + $opacity_mask_size, 'data' => $data, ); $this->images[] = $image; } /** * Read in the source image file and convert it into a GD image resource. */ protected function loadImageFile($file) { if (!is_string($file) || empty($file)) { return false; } // Run a cheap check to verify that it is an image file. if (false === ($size = getimagesize($file))) { return false; } if (false === ($file_data = file_get_contents($file))) { return false; } if (false === ($im = imagecreatefromstring($file_data))) { return false; } unset($file_data); return $im; } }