Skip to content

Instantly share code, notes, and snippets.

@alfaproject
Created October 18, 2012 09:28
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alfaproject/3910694 to your computer and use it in GitHub Desktop.
Save alfaproject/3910694 to your computer and use it in GitHub Desktop.
PHP script to optimize images file size recursively in a given in a folder
/*
* Strip JFIF headers from the JPEG image on stdin, and write
* the result to stdout. Won't work unmodified on Windows
* because of the text/binary problem. Not thoroughly tested.
*/
#include <stdio.h>
#include <stdlib.h>
void fail(const char* msg) {
fputs(msg, stderr);
exit(1);
}
void copy_rest(FILE* f, FILE* g) {
char buf[4096];
int len;
while ((len = fread(buf, 1, sizeof buf, f)) > 0) {
if (fwrite(buf, 1, len, g) != len)
fail("write error");
}
if (len < 0)
fail("read error");
}
void skip_jfif(FILE* f, FILE* g) {
int a,b;
a = getc(f); b = getc(f);
if (a != 0xFF || b != 0xD8)
fail("not a JPEG file");
putc(a,g); putc(b,g);
// 0xFFE9 is APP0 marker to begin JFIF segment
while (a = getc(f), b = getc(f), a == 0xFF && b == 0xE0) {
// Next 2 bytes after APP0 are length of JFIF segment *including* APP0
// so seek forward (0x???? - 2) bytes
a = getc(f); b = getc(f);
if (a == 0xEF || b == 0xEF)
fail("stop confusing me with weird test cases");
fseek(f, a * 256 + b - 2, SEEK_CUR);
}
if (a != 0xEF) putc(a,g);
if (b != 0xEF) putc(b,g);
copy_rest(f,g);
}
int main() {
skip_jfif(stdin,stdout);
return 0;
}
#!/usr/local/bin/php
<?php
function do_gif($image)
{
$iniTime = microtime(true);
$iniSize = filesize($image);
// install gifsicle http://www.lcdf.org/gifsicle/
system('gifsicle -b --careful -k 256 -O3 '. escapeshellarg($image));
clearstatcache(true, $image);
$endSize = filesize($image);
$endTime = round((microtime(true) - $iniTime) * 1000);
$sizeGain = round($endSize / $iniSize * 100, 1);
if ($endSize != $iniSize) {
echo "Time: {$endTime}ms\tSize: $sizeGain% ($iniSize > $endSize)\t$image\n";
return ($iniSize - $endSize);
} else {
echo "Time: {$endTime}ms\tSize: $iniSize\t$image\n";
return 0;
}
}
function do_jpeg($image)
{
$iniTime = microtime(true);
$iniSize = filesize($image);
// create 2 temporary files
$tmp1 = tempnam(dirname($image), 'imgopt_');
$tmp2 = tempnam(dirname($image), 'imgopt_');
// jpegtran is part of libjpeg (almost surely already on your system, if not, it's here: http://www.ijg.org/)
system('jpegtran -copy none -optimize -outfile '. escapeshellarg($tmp1) .' '. escapeshellarg($image));
// jfifremove is included with this script, be sure to compile and install
system('jfifremove < '. escapeshellarg($tmp1) .' > '. escapeshellarg($tmp2));
unlink($tmp1);
$endSize = filesize($tmp2);
$endTime = round((microtime(true) - $iniTime) * 1000);
$sizeGain = round($endSize / $iniSize * 100, 1);
if ($endSize > 0 && $endSize < $iniSize) {
rename($tmp2, $image);
echo "Time: {$endTime}ms\tSize: $sizeGain% ($iniSize > $endSize)\t$image\n";
return ($iniSize - $endSize);
} else {
unlink($tmp2);
echo "Time: {$endTime}ms\tSize: $iniSize\t$image\n";
return 0;
}
}
function do_png($image)
{
$iniTime = microtime(true);
$iniSize = filesize($image);
// create a temporary file
$tmp = tempnam(dirname($image), 'imgopt_');
// OptiPNG
// http://optipng.sourceforge.net/
unlink($tmp);
system('optipng -q -zc1-9 -zm1-9 -zs0-3 -f0-5 -out '. escapeshellarg($tmp) .' '. escapeshellarg($image));
// pngout
// http://www.jonof.id.au/kenutils
system('pngout -q -y '. escapeshellarg($tmp));
if (file_exists($tmp .'.png')) {
rename($tmp .'.png', $tmp);
} elseif (file_exists($tmp .'.PNG')) {
rename($tmp .'.PNG', $tmp);
}
// re-compress using 7zip algorithm
// advpng (part of AdvanceCOMP)
// http://advancemame.sourceforge.net/comp-readme.html
system('advpng -q -z4 '. escapeshellarg($tmp));
$endSize = filesize($tmp);
$endTime = round((microtime(true) - $iniTime) * 1000);
$sizeGain = round($endSize / $iniSize * 100, 1);
if ($endSize > 0 && $endSize < $iniSize) {
rename($tmp, $image);
echo "Time: {$endTime}ms\tSize: $sizeGain% ($iniSize > $endSize)\t$image\n";
return ($iniSize - $endSize);
} else {
unlink($tmp);
echo "Time: {$endTime}ms\tSize: $iniSize\t$image\n";
return 0;
}
}
function recurseImages($dir, &$initialBytes, &$bytesSaved)
{
$images = $initialBytes = $bytesSaved = 0;
$stack[] = $dir;
while ($stack) {
sort($stack);
$thisdir = array_pop($stack);
if ($dircont = scandir($thisdir)) {
for ($i = 0; isset($dircont[$i]); $i++) {
if ($dircont[$i]{0} !== '.') {
$current_file = $thisdir .'/'. $dircont[$i];
if (!is_link($current_file)) {
if (is_dir($current_file)) {
$stack[] = $current_file;
} else {
switch (strtoupper(pathinfo($current_file, PATHINFO_EXTENSION))) {
case 'GIF':
$initialBytes += filesize($current_file);
$bytesSaved += do_gif($current_file);
chmod($current_file, 0644);
$images++;
break;
case 'PNG':
$initialBytes += filesize($current_file);
$bytesSaved += do_png($current_file);
chmod($current_file, 0644);
$images++;
break;
case 'JPG':
case 'JPEG':
$initialBytes += filesize($current_file);
$bytesSaved += do_jpeg($current_file);
chmod($current_file, 0644);
$images++;
break;
}
}
}
}
}
}
}
return $images;
}
if (is_dir($argv[1])) {
$time = time();
$initialBytes = $bytesSaved = 0;
$path = realpath($argv[1]);
$images = recurseImages($path, $initialBytes, $bytesSaved);
$time = time() - $time;
echo "Time: {$time}s\tImages: $images\tInitial bytes: $initialBytes\tBytes saved: $bytesSaved (". round($bytesSaved / $initialBytes * 100, 1) ."%)\n";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment