summaryrefslogtreecommitdiffstats
path: root/build/pypng/pipcomposite
diff options
context:
space:
mode:
Diffstat (limited to 'build/pypng/pipcomposite')
-rw-r--r--build/pypng/pipcomposite121
1 files changed, 121 insertions, 0 deletions
diff --git a/build/pypng/pipcomposite b/build/pypng/pipcomposite
new file mode 100644
index 0000000..21dd283
--- /dev/null
+++ b/build/pypng/pipcomposite
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+# $URL: http://pypng.googlecode.com/svn/trunk/code/pipcomposite $
+# $Rev: 208 $
+# pipcomposite
+# Image alpha compositing.
+
+"""
+pipcomposite [--background #rrggbb] file.png
+
+Composite an image onto a background and output the result. The
+background colour is specified with an HTML-style triple (3, 6, or 12
+hex digits), and defaults to black (#000).
+
+The output PNG has no alpha channel.
+
+It is valid for the input to have no alpha channel, but it doesn't
+make much sense: the output will equal the input.
+"""
+
+import sys
+
+def composite(out, inp, background):
+ import png
+
+ p = png.Reader(file=inp)
+ w,h,pixel,info = p.asRGBA()
+
+ outinfo = dict(info)
+ outinfo['alpha'] = False
+ outinfo['planes'] -= 1
+ outinfo['interlace'] = 0
+
+ # Convert to tuple and normalise to same range as source.
+ background = rgbhex(background)
+ maxval = float(2**info['bitdepth'] - 1)
+ background = map(lambda x: int(0.5 + x*maxval/65535.0),
+ background)
+ # Repeat background so that it's a whole row of sample values.
+ background *= w
+
+ def iterrow():
+ for row in pixel:
+ # Remove alpha from row, then create a list with one alpha
+ # entry _per channel value_.
+ # Squirrel the alpha channel away (and normalise it).
+ t = map(lambda x: x/maxval, row[3::4])
+ row = list(row)
+ del row[3::4]
+ alpha = row[:]
+ for i in range(3):
+ alpha[i::3] = t
+ assert len(alpha) == len(row) == len(background)
+ yield map(lambda a,v,b: int(0.5 + a*v + (1.0-a)*b),
+ alpha, row, background)
+
+ w = png.Writer(**outinfo)
+ w.write(out, iterrow())
+
+def rgbhex(s):
+ """Take an HTML style string of the form "#rrggbb" and return a
+ colour (R,G,B) triple. Following the initial '#' there can be 3, 6,
+ or 12 digits (for 4-, 8- or 16- bits per channel). In all cases the
+ values are expanded to a full 16-bit range, so the returned values
+ are all in range(65536).
+ """
+
+ assert s[0] == '#'
+ s = s[1:]
+ assert len(s) in (3,6,12)
+
+ # Create a target list of length 12, and expand the string s to make
+ # it length 12.
+ l = ['z']*12
+ if len(s) == 3:
+ for i in range(4):
+ l[i::4] = s
+ if len(s) == 6:
+ for i in range(2):
+ l[i::4] = s[i::2]
+ l[i+2::4] = s[i::2]
+ if len(s) == 12:
+ l[:] = s
+ s = ''.join(l)
+ return map(lambda x: int(x, 16), (s[:4], s[4:8], s[8:]))
+
+class Usage(Exception):
+ pass
+
+def main(argv=None):
+ import getopt
+ import sys
+
+ if argv is None:
+ argv = sys.argv
+
+ argv = argv[1:]
+
+ try:
+ try:
+ opt,arg = getopt.getopt(argv, '',
+ ['background='])
+ except getopt.error, msg:
+ raise Usage(msg)
+ background = '#000'
+ for o,v in opt:
+ if o in ['--background']:
+ background = v
+ except Usage, err:
+ print >>sys.stderr, __doc__
+ print >>sys.stderr, str(err)
+ return 2
+
+ if len(arg) > 0:
+ f = open(arg[0], 'rb')
+ else:
+ f = sys.stdin
+ return composite(sys.stdout, f, background)
+
+
+if __name__ == '__main__':
+ main()