(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