123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- #!/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()
|