(Apologies for cross-posting. There is no mapserver bug here, only a browser limitation workaround which might be helpful to some users.)

Mapserver's AGG/PNG driver w/ QUANTIZE_FORCE=ON and TRANSPARENT ON generates an 8-bit PNG with per-palette-entry alpha transparency. If you use a transparent background, you'll face a problem: IE6- (incorrectly) only renders pixels with a very high opacity. The attached script rewrites an 8-bit PNG's tRNS header, making all non-completely-transparent pixels opaque. (It is easy to modify this to use a threshold > 0.) The attached script, reimplemented at the tilecache level if the requesting User-Agent matches /MSIE (5\.5|6)/, worked well for me because:

1. Mapserver always renders AGG/PNG. No need to special-case GD/PNG to get transparent images for IE6-. No need to cache duplicate images. 2. Avoids having to use the IE 5.5/6 PNG alpha-transparency hack, which decimates performance [1].

Hope someone else might find this useful too.

-Dave

[1] http://trac.openlayers.org/ticket/77#comment:2


David Fuhry wrote:
I generated the attached tile.png with AGG/PNG and QUANTIZE_FORCE=ON, using a --with-experimental-png build of mapserver (to get PNG-8 transparency). It's an 8-bit PNG image with a transparent background. The image looks great in every browser except IE6, which only renders certain pieces of lines (see other attached image). Is this a known problem?

I've tried INTERLACE=OFF with no success. Changing the driver to GD/PNG works, but of course I don't get the AGG smoothness. There is no AGG/GIF driver yet, so that's not an option. And finally, a 24-bit PNG is not an option since IE<7 doesn't support alpha transparency.

I'm now beginning to wonder if the misrendering is due to alpha transparency (not supported by IE6-) on the 8-bit image. I didn't think that 8-bit PNGs supported alpha transparency (GIFs don't) but according to this article: http://www.sitepoint.com/blogs/2007/09/18/png8-the-clear-winner/ indexed PNGs can store alpha channel values for one *or more* palette entries. If this is the case, then a welcome FORMATOPTION might be QUANTIZE_ROUND_TRANSPARENCY which would round palette entries' transparency up to 100 or down to 0. Hmmm... but then (since there's no background color) AGG smoothness would be lost, and one might as well use GD/PNG, so maybe not.

I'll look into the IE htc (css behavior: ...) hack as a possible solution. Better suggestions appreciated.

Thanks,

Dave Fuhry

OUTPUTFORMAT
  NAME "AGG_PNG8"
  DRIVER AGG/PNG
  IMAGEMODE RGB
  FORMATOPTION "QUANTIZE_FORCE=ON"
  FORMATOPTION "QUANTIZE_COLORS=256"
  FORMATOPTION "INTERLACE=OFF"
  TRANSPARENT ON
END

------------------------------------------------------------------------


------------------------------------------------------------------------


------------------------------------------------------------------------

_______________________________________________
mapserver-dev mailing list
[EMAIL PROTECTED]
http://lists.osgeo.org/mailman/listinfo/mapserver-dev
#!/usr/bin/python

import sys
from cStringIO import StringIO
import Image
import os

# some code adapted from the Python Imaging Library's PngImagePlugin.py

def i16(c):
    return ord(c[1]) + (ord(c[0])<<8)
def i32(c):
    return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
def i16_to_c(i):
    return chr(i>>8) + chr(i - ((i>>8)<<8))

PNG_MAGIC = "\211PNG\r\n\032\n"

def validate_crc(file_crc, cid, data):
    "Read and verify checksum"
    crc1 = Image.core.crc32(data, Image.core.crc32(cid))
    crc2 = i16(file_crc[:2]), i16(file_crc[2:4])
    if crc1 != crc2:
        raise SyntaxError, "broken PNG file"\
	    "(bad header checksum in %s)" % cid

def read_header(f2):
  h = f2.read(8)
  cid = h[4:]
  pos = f.tell()
  cont_len = i32(h)
  return cid, pos, cont_len


orig_f = open(sys.argv[1], "rb")

f = StringIO()
png_data = orig_f.read()
png_data_len = len(png_data)
f.write(png_data)
f.seek(0, os.SEEK_SET)

orig_f.close()

if f.read(8) != PNG_MAGIC:
  raise SyntaxError, "not a PNG file"

cid = None
while 1:
  cid, pos, cont_len = read_header(f)

  if pos + cont_len > png_data_len:
    raise SyntaxError, "Content length " + str(cont_len) + " plus pos " + str(pos) + " points beyond end of file at " + str(png_data_len)

  #print cid, pos, cont_len
  #if cid == "IHDR":
    #size = i32(cont), i32(cont[4:])
    #print "IHDR: image size is", size
  #if cid == "PLTE":
    #print "PLTE: num palette entries:", (cont_len / 3)
    #print "palette content:"
    #print cont
    #for i in range(0, cont_len):
    #  print ord(cont[i])
    #break
  if cid == "tRNS":
    #print "tRNS: num tRNS entries:", cont_len

    # set nontransparent pixels to fully opaque
    for i in range(0, cont_len):
      t_val = ord(f.read(1))
      if t_val > 0:
        f.seek(-1, os.SEEK_CUR)
	f.write(chr(255))

    # compute checksum of changed values
    f.seek(-1 * cont_len, os.SEEK_CUR)
    tRNS_cont = f.read(cont_len)
    new_crc = Image.core.crc32(tRNS_cont, Image.core.crc32("tRNS"))
    f.write(i16_to_c(new_crc[0]))
    f.write(i16_to_c(new_crc[1]))
    validate_crc(i16_to_c(new_crc[0]) + i16_to_c(new_crc[1]), "tRNS", tRNS_cont)

    # seek backwards by four
    f.seek(-4, os.SEEK_CUR)

    #cont = f.read(cont_len)
    #print "tRNS content:"
    #for i in range(0, cont_len):
    #  print ord(cont[i])
  elif cid == "IEND":
    break
  else:
    f.seek(cont_len, os.SEEK_CUR)

  f.seek(4, os.SEEK_CUR)

f.seek(0, os.SEEK_SET)
sys.stdout.write(f.read())
_______________________________________________
Dev mailing list
[email protected]
http://openlayers.org/mailman/listinfo/dev

Reply via email to