PNG Image Optimisation

filled under:
February 14, 2011 

I did some research at work for PNG image optimization, to find out what are the best tools to compress PNG RGBA to paletted png with alpha channel, that would also fit into a good workflow.
In previous projects it was common to compress each picture individually and that, of course, was a long and repeating process. Screen’s often change during development and if you need to do a task over and over again, you might end up finding a solution that shortens the process.

My goal was find a solution and tools that would cover the following things:

  • Tools that are available on linux and OSX from commandline, to execute them via script
  • a good compression ratio in a short amount of time
  • Tools that transcode from rgba to paletted images with alpha cannel
  • Tools that can read, modify (optional) and delete Header-Chunks
  • Tools have to be opensourced or must be at least for free

For comparsion I’ve choose the following tools:

Tested Tools

Here is a brief overview of what matters: Short description + usage example. When I mention quantize, i mean the reductions of colors from rgba to a paletted color map with alpha channel.

Pngcrush

Can not quantize as described above but processes fast when optimizing the IDAT Stream.
Usage: pngcrush -reduce alpha24.png alpha24-crushed.png

Pngrewrite

Can handle only paletted images but the result then is very good.
Usage: pngrewrite alpha24.png alpha24-pngrewrite.png

ImageMagick

Comes with a hugh amount of features but is no good choice for compression, it produces partial bigger file sizes than the original. I was not able to archive any good results – thus I’m just mention it here for the completeness, but do not enlist it in the charts.

Pngnq

Can not quantize as described above but works very well for all other purposes.
Usage: pngnq -s1 -e"-nq256.png" -v alpha24.png

PNGOUT

The main feature is quantisation as we want it and it does the job very well. It “Quantizes a 32-bit RGBA PNG image to an 8 bit RGBA palette PNG using the neuquant algorithm”.
Usage: pngout alpha24.png alpha24-pngout-default.png

OptiPNG

Is seems to be very similar to Pngcrush (the Author states that OptiPNG is inspired by Pngcrush) but the results are a little bit better and the arguments differs.
Usage: optipng -full alpha24.png -out alpha24-optipng-full.png

Feature Overview

Benchmark and Comparsion

I’ve taken one RGBA TrueColor image with a dimension of 300×300, with lossless compression. The arguments used, are those in the usage examples described earlier.

Original file size in Bytes “4739″

tool size after optimization Saved Bytes
pngcrush 3954 785
optipng 3953 786
pngrewrite can’t handle format 0
pngnq 3344 1395
pngout 4096 634

As you can see, pngnq had the best results and therefor I’ll use it for the next step.

tool size after optimization Saved Bytes
pngcrush 3344 0
optipng 3329 15
pngrewrite 2604 740
pngout 2866 478

That means I’ve saved 2135 bytes and the optimised image is only 45,05% of the original file size. In comparsion to ImageOptim, that is a GUI App for OSX and only can save up to 25,5% (74,5% of original file size), the result ist pretty impressing. It is worth to mention, that none of the Adobe Products, besides Fireworks, can produce 8bit-paletted-with-alpha images.

Note:
The optimisation is meant for UI-images. Sprites, buttons, etc – not for user generated content or similar.

Buildprocess

If you need to do a task over and over again: automate it ;)

My idea is to have a source image in full quality, that gets processed via a script and then gets stored into a destination folder. However, some images needs a special treatment and therefore it makes sense to have a configuration file.

In order to not process images twice, you could modify the iTxt for instance, to mark it as processed and if the image has such header chunk – do not process it. If you drop a new image into the source folder, the image doesn’t have the chunk, thus it gets processed. But that’s just micro-tuning, because pngnq and pngrewrite are fast enough … IMHO … because you shouldn’t have more than 10 – 20 images to process.

tags:

tech-stuff-bla-bla-and-then-some

filled under:
March 24, 2009 

I’ve installed today a new svn, trac and a websvn at work on a ubuntu machine with plesk on it. Actually it’s really easy but there seems to be no instant-setup guide, so I had to google quite extensive. Well I haven’t configuered it fully yet, but the svn seems to work fine, even though I’d like to make use of the pre-commit-hook (having the users commenting the commits, with at least 3 words); but that’s something I’ll change later. Currently we use mantis as bugtracker but I see some requirements for more. Trac seems to be a good alternative. Personally I’d like to use JIRA and Confluence – but I am to lazy to convince my boss to buy it and of course – 2400 Bucks *is* < insert the f-word here > money.

However – my work-life balance *seems* to be *unbalanced* and the affected personal relationships seems to truely suck atm; But therefor my boss said he’s satisfied with my work – and I have only 7 days left until the employment probation period is over and I am fully employed. – I guess that would be a classic scenario of a double-edged sword.

< insert your favourite f-word right here >

work around for zend validate’s markAsError respectively addError

filled under:
February 5, 2009 

Yesterday I ran into a problem with zend form and zend validate which was quite confusing. I wanted to check if a multiCheckbox has less or equal 2 selections. I really thought there would be some “shipped” validator but there isn’t. Then I’ve tried to write my own validator for that purpose, but Zend Validate didn’t pass the MultiCheckbox values as an array but as strings and I wasnt in the mood to hack the Zend “core” itself. Then I tried to use addError(), markAsError() or set _errorsExists explicit but that didn’t work either, as described here. Maybe I am blind and there is an much easier way … but the following works quite fine for me.

  1. Create an Validator which always fails.
  2. Check right after the isPost() condition if “your condition” matches.
  3. Assign the “will-always-fail” validator to your element and you’re done.
// save as 'Zend/Validate/CustomError.php
require_once 'Zend/Validate/Abstract.php';

class Zend_Validate_CustomError extends Zend_Validate_Abstract  {

    const SOME_ERROR = 'someError';

	// configure to whatever you like, or override it with setMessage()
    protected $_messageTemplates = array(
        self::SOME_ERROR => "is not valid"
    );

	// sets the element as invalid
    public function isValid($value)
    {
        $valueString = (string) $value;
        $this->_setValue($valueString);
		$this->_error();
        return false;
    }

}

And in your form controller add right after the isPost() condition:

if(sizeof($_POST['yourelement'])>2) {
	$customError = new Zend_Validate_CustomError();
	$customError->setMessage('You must select less than 2');
	self::getElement('yourelement')->addValidator($customError);
}
tags:

Saturday things

filled under:
January 18, 2009 

I’m currently trying to get some multi step/wirzard forms in Zend done and it seems to work as far as I can tell. I’ve used this little class as inspiration and it works so far.

I really like multi step forms as user but as developer I hate em. Nevertheless it’s 1:40am/Saturday and I am doing stuff for work. Actually I should go out and do party but that is not the case and so I code.

Oh ya – at least I did /some/ tagging, when I went to the grocery and there are still /some/ beers in the fridge.

cheers!

tags:

flickr.groups.pools.getPhotos and php_serial

filled under:
December 4, 2008 

If you try to get a serialized array (php_serial) from the flicker api from a group (flickr.groups.pools.getPhotos), you may find your self a little bit lost, because you do not get the photos you want, only the amount of how many. You double check if everything is set to public, your account is ok and even try to upgrade it – but that’s not it. You used the usefull API querybuilder from flicker and then it works in that case and you wonder why it doesn’t in your application. The simple solution is, it doesn’t work with the php_serial format. Skip this parameter and you get your set as XML response. The only thing you need to do is, use the XMLReader and iterate through the result set.

It took me quite a while to figure this out – like 1,5 hours – so eventually this post is usefull for someone ;)

tags:

simple method to add a safari css

filled under:
November 5, 2008 

There are some selector hacks out there, but I don’t like them – ie: (@media screen and (-webkit-min-device-pixel-ratio:0) {}). My personal favourite is done with Prototype because I use it a lot and it is pretty simple:

<link rel="stylesheet" type="text/css" media="none" href="/css/safari.css" id="safari" />
<script type="text/javascript">
	// matches all safari/chrome versions
	if(Prototype.Browser.WebKit) {
		$('safari').media="all";
	}
</script>

If you set the media attribute to none – no media device will display it. By adding the little if statement, you can check if the Browser is WebKit, which is the engine name of safari or chrome and alter the media value to all.

If you do not use prototype, you can do it like this:

<script type="text/javascript">
	// targets safari 3 - not 2!
	if(window.devicePixelRatio) {
		document.getElementById('safari')media="all";
	}

	// targets all safari or better said webKit engines
	if(navigator.userAgent.indexOf('AppleWebKit/') > -1) {
		document.getElementById('safari')media="all";
	}

	// prototype way + targeting old and new webKit with two stylesheets
	if(Prototype.Browser.WebKit) {
		var mySafari = (window.devicePixelRatio) ? 'safari3' : 'safari2';
		document.getElementById(mySafari)media="all";
	}
</script>

Note, this method shouldn’t be used for “accessible” Websites, because it relies on javascript. You can do it then serverside as well by “sniffing” the UserAgent from the HTTP Request and add your little switch there – whereas this isn’t a 100% safe method though, because some people alter their UserAgent strings and there are tons of vendors/versions thus your detection may fail from time to time.

An alternative to this way is to load the needed script on the fly, so you can reduce a HTTP Request, by writing the link tag dynamically to your document. However – the describe method above is shorter ;)

Pass Phrazr

filled under:
September 9, 2008 

Passwords are sometimes hard to remember, or at least hard to read, because good passwords are cryptic. That’s a fact and I am still fine with it, because I pay attention to obvious security flaws or risks. However, there are some situations when you do not need cryptic passwords – like “first time passwords”, or “single time passwords”. For this particular case I came up with a nice idea and I’d like to call it pass phrazr (phraser).

Instead of cryptic passwords, use phrases and or simple sentences. If you just enter some phrases as possible pass phrases it doesn’t work very well, but if you use a directory it does. Plus, you can serve i18ln passwords if you add a locale.

The easiest sentence is SVO structured but “Tim like Dogs” sucks and isn’t fancy at all, so I’m going to use a more stylish composition. Factual phrases with *izm nouns instead of boring objects! RaaaaR.

Here’s a simple MySQL Table:

CREATE TABLE passphrazes (
	passphraze_word 	varchar(30) 					NOT NULL,
	passphraze_type 	enum('subject','verb','noun') 	NOT NULL,
	PRIMARY KEY (passphraze_word)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

If you still wonder about it – the magic goes here:

SELECT concat(
	(SELECT passphraze_word
		FROM passphrazes
		WHERE passphraze_type = 'subject'
		ORDER BY RAND()
		LIMIT 1),
	'-',
	(SELECT passphraze_word
		FROM passphrazes
		WHERE passphraze_type = 'verb'
		ORDER BY RAND()
		LIMIT 1),
	'-',
	(SELECT passphraze_word
		FROM passphrazes
		WHERE passphraze_type = 'noun'
		ORDER BY RAND()
		LIMIT 1)
);

No clue jet? Well, by selecting random rows you can generate funny phrases like:

He allocated nahualism.
She reinforced mithridatism.
They served social-evolutionism.

Then style it and additionally pimp it more secure (replace spaces with dashes, plus signs, or even camelize it) in your favourite coding language:

He-allocated-nahualism.
SheReinforcedMithridatism.
They+Served+social-evolutionism.

The use case is hopefully obvious. If someone sign up at your site and you generate the first time password for the user, why not something meaningful? There are tons of possibilities to combine words unique.

*updated*

areal-records.com finally online

filled under:
July 8, 2008 

It took us quite a while until we were all satisfied but now it’s done and you can view it over here:

» http://areal-records.com/

The site relies on prototype, some web 2.0 gimmickry, scriptaculous and the pear framework, a wonderful combine script for CSS and JS. Firefox 3 still needs some »pimping« but for all the other browsers it should work fine, as far as I can tell.

It’s midnight.

email regex

filled under:
February 23, 2008 

if you need a very easy regex, to validate emails – here’s some I wrote:

^((?P<localpart>([\w\-_]+)([\w\-_]+([\.]{1}[^\.\.]+))([\w\-_]+))
@(?P<domain>([\wäüö]+[\w\-\._äüö]+[\wäüö]+))
\.(?P<tld>([a-z]{2,6})))$