Server-side PNG-to-GIF for MSIE 5 & 6

Internet Explorer 5/6 users will get a GIF instead of PNG, with entirely server-side code.

Demo

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

How it works:

  1. PNG graphics are used in the design and upload to the website.
  2. The XHTML/HTML document(s) simply request nonexistent .pnggif files when this functionality is desired. E.g., <img src="/images/
  3. Place this in an .htaccess file with your images. Note you will have to change the base, for example from /php-tests/images/ to /images/.
    RewriteEngine on
    RewriteCond %{HTTP_USER_AGENT} MSIE\s(5|6)
    RewriteRule ^.*\.pnggif$ /php-tests/images/pnggif.php [NC,L]
    RewriteRule ^(.*)\.pnggif$ /php-tests/images/$1.png [NC,L]
    That code will send Internet Explorer 5 and 6 users to pnggif.php. (This is a silent redirect; their browser never knows what happens.) Mozilla/Opera/Safari users get sent the real .png file. (Because the first rule has the [L] or "last" flag, IE users do not “fall through” to the next rule.)
  4. The pnggif.php file, something like the code snippet below, will send the browser somefile.gif if it exists (where somefile.png is the PNG in question). This means you can create a custom GIF replacement for the PNG files. If it does not already exist, the PHP file uses GD2 to automatically create and save the GIF file from the PNG file using transparency dither, because I’m awesome. (Admittedly it should use a better algorithm; this is a simple noise/random dither.) PHP file pnggif.php:
    <?php
    /** Server-Side PNG-GIF Conversion for MSIE
      *
      * Takes a PNG image and if MSIE 5 or 6 is detected, throws a GIF back.
      * This is supposed to go in your /images/ directory. It, when paired with
      * the proper .htaccess file, will intercept your browser's calls for
      * any .pnggif files, which don't really exist on your server, and will
      * give your browser the .png file (which DOES really exist on your server)
      * unless the browser is Internet Explorer (MSIE) 5 or 6, which suck at PNG,
      * in which case it makes & caches, if necessary, and serves up a GIF
      * version, which IE should be able to deal with.
      *
      * @author Alan Hogan
      * @version 1.00
      * @copyright Alan Hogan, 10 November, 2007
      * @site http://alanhogan.com
      **/

    //$myLocation = "/images/";
    $myLocation = "/php-tests/images/";

    //Get the path of what was actually reQuested
    $reqPath = $_ENV['REQUEST_URI'];

    //if($strpos($reqPath, $myLocation) === 0) //ASsume true
    //ReLative path
    $relPath = substr($reqPath,strlen($myLocation), strlen($reqPath)-strlen($myLocation)-7);
    //The 7 above is for .pnggif
    $pngRel = realpath(".")."/".$relPath.".png";
    $gifRel = realpath(".")."/".$relPath.".gif";


    if ((strpos($_ENV['HTTP_USER_AGENT'], 'MSIE 6') !== false
       || strpos($_ENV['HTTP_USER_AGENT'], 'MSIE 5') !== false)
       && strpos($_ENV['HTTP_USER_AGENT'], 'Opera') === false
       && strpos($_ENV['HTTP_USER_AGENT'], 'Gecko') === false
       && strpos($_ENV['HTTP_USER_AGENT'], 'Safari') === false) {
       // We are dealing with IE less than 7. Hooray for crap.

       if(file_exists($gifRel)) {
          header("Content-type: image/gif");
          readfile($gifRel);
       }
       elseif(file_exists($pngRel)){
          $im = imagecreatefrompng($pngRel);
          $transparentColor = imagecolorallocate($im, 0xfe, 0x3, 0xf4 );
          //$ditherHistory = array(); //stores number of times it was transparent
          $height = imagesy($im);
          $width = imagesx($im);
          for($x = 0; $x < $width; $x++){
             for($y = 0; $y < $height; $y++) {
                $alpha = (imagecolorat($im,$x,$y) & 0x7F000000) >> 24;//127 is completely TRANSPARENT, 0 opaque
                //DITHER!
                if ($alpha > 3 && (
                      $alpha >=127-3 ||
                      (rand(0,127))>=(127-$alpha)
                   )){
                   imagesetpixel($im,$x,$y,$transparentColor);
                }

             }
          }
          imagecolortransparent($im, $transparentColor);
          imagegif($im, $gifRel);//save
          header("Content-type: image/gif");
          readfile($gifRel); //pass thru to browser

       } else {
          //Error.
          die("ERROR sorry, couldn't find ".$pngRel);
       }

    } else {
       //Hooray! Good browser!
       if(file_exists($pngRel)){
          header("Content-type: image/png");
          readfile($pngRel); //pass thru to browser
       }
       else {
          //error
          die("ERROR sorry, couldn't find ".$pngRel);
       }
    }

    Make sure to customize the $myLocation variable above, just as with the .htaccess file!

    Please note how we not only check for existence of "MSIE 5" or "MSIE 6" in the user agent string, but also for "Opera," as such browsers can identify as IE for moronic websites which exclude non-IE browsers. Still, when “cloaked,” they often still leave a clue to their true identity in their user agent string.

  5. End result: Every browser gets its most compatible format of image with no client-side scripting.

Pros and Cons

This method two or three benefits over JavaScript-based solutions:

  1. Works even with JavaScript disabled
  2. No momentary flicker of a background on the PNGs
  3. Easily works for PNGs used as background images (many JS-based solutions do not fix these)

And of course there is a big drawback, too.

  1. Alpha transparency is necessarily reduced to a dithered on/on transparency when creating the alternate GIF image.

Parting Thoughts

Is there any reason you can’t mix this solution with the client-side script solutions? No! Go for it. If you thought this was interesting, useful, stupid, clever, etc., drop me a line via my contact form on AlanHogan.com. Thanks.