summaryrefslogtreecommitdiffstats
path: root/build/pypng/pipstack
diff options
context:
space:
mode:
Diffstat (limited to 'build/pypng/pipstack')
-rw-r--r--build/pypng/pipstack127
1 files changed, 127 insertions, 0 deletions
diff --git a/build/pypng/pipstack b/build/pypng/pipstack
new file mode 100644
index 0000000..5523670
--- /dev/null
+++ b/build/pypng/pipstack
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+# $URL: http://pypng.googlecode.com/svn/trunk/code/pipstack $
+# $Rev: 190 $
+
+# pipstack
+# Combine input PNG files into a multi-channel output PNG.
+
+"""
+pipstack file1.png [file2.png ...]
+
+pipstack can be used to combine 3 greyscale PNG files into a colour, RGB,
+PNG file. In fact it is slightly more general than that. The number of
+channels in the output PNG is equal to the sum of the numbers of
+channels in the input images. It is an error if this sum exceeds 4 (the
+maximum number of channels in a PNG image is 4, for an RGBA image). The
+output colour model corresponds to the number of channels: 1 -
+greyscale; 2 - greyscale+alpha; 3 - RGB; 4 - RGB+alpha.
+
+In this way it is possible to combine 3 greyscale PNG files into an RGB
+PNG (a common expected use) as well as more esoteric options: rgb.png +
+grey.png = rgba.png; grey.png + grey.png = greyalpha.png.
+
+Color Profile, Gamma, and so on.
+
+[This is not implemented yet]
+
+If an input has an ICC Profile (``iCCP`` chunk) then the output will
+have an ICC Profile, but only if it is possible to combine all the input
+ICC Profiles. It is possible to combine all the input ICC Profiles
+only when: they all use the same Profile Connection Space; the PCS white
+point is the same (specified in the header; should always be D50);
+possibly some other things I haven't thought of yet.
+
+If some of the inputs have a ``gAMA`` chunk (specifying gamma) and
+an output ICC Profile is being generated, then the gamma information
+will be incorporated into the ICC Profile.
+
+When the output is an RGB colour type and the output ICC Profile is
+synthesized, it is necessary to supply colorant tags (``rXYZ`` and so
+on). These are taken from ``sRGB``.
+
+If the input images have ``gAMA`` chunks and no input image has an ICC
+Profile then the output image will have a ``gAMA`` chunk, but only if
+all the ``gAMA`` chunks specify the same value. Otherwise a warning
+will be emitted and no ``gAMA`` chunk. It is possible to add or replace
+a ``gAMA`` chunk using the ``pipchunk`` tool.
+
+gAMA, pHYs, iCCP, sRGB, tIME, any other chunks.
+"""
+
+class Error(Exception):
+ pass
+
+def stack(out, inp):
+ """Stack the input PNG files into a single output PNG."""
+
+ from array import array
+ import itertools
+ # Local module
+ import png
+
+ if len(inp) < 1:
+ raise Error("Required input is missing.")
+
+ l = map(png.Reader, inp)
+ # Let data be a list of (pixel,info) pairs.
+ data = map(lambda p: p.asDirect()[2:], l)
+ totalchannels = sum(map(lambda x: x[1]['planes'], data))
+
+ if not (0 < totalchannels <= 4):
+ raise Error("Too many channels in input.")
+ alpha = totalchannels in (2,4)
+ greyscale = totalchannels in (1,2)
+ bitdepth = []
+ for b in map(lambda x: x[1]['bitdepth'], data):
+ try:
+ if b == int(b):
+ bitdepth.append(b)
+ continue
+ except (TypeError, ValueError):
+ pass
+ # Assume a tuple.
+ bitdepth += b
+ # Currently, fail unless all bitdepths equal.
+ if len(set(bitdepth)) > 1:
+ raise NotImplemented("Cannot cope when bitdepths differ - sorry!")
+ bitdepth = bitdepth[0]
+ arraytype = 'BH'[bitdepth > 8]
+ size = map(lambda x: x[1]['size'], data)
+ # Currently, fail unless all images same size.
+ if len(set(size)) > 1:
+ raise NotImplemented("Cannot cope when sizes differ - sorry!")
+ size = size[0]
+ # Values per row
+ vpr = totalchannels * size[0]
+ def iterstack():
+ # the izip call creates an iterator that yields the next row
+ # from all the input images combined into a tuple.
+ for irow in itertools.izip(*map(lambda x: x[0], data)):
+ row = array(arraytype, [0]*vpr)
+ # output channel
+ och = 0
+ for i,arow in enumerate(irow):
+ # ensure incoming row is an array
+ arow = array(arraytype, arow)
+ n = data[i][1]['planes']
+ for j in range(n):
+ row[och::totalchannels] = arow[j::n]
+ och += 1
+ yield row
+ w = png.Writer(size[0], size[1],
+ greyscale=greyscale, alpha=alpha, bitdepth=bitdepth)
+ w.write(out, iterstack())
+
+
+def main(argv=None):
+ import sys
+
+ if argv is None:
+ argv = sys.argv
+ argv = argv[1:]
+ arg = argv[:]
+ return stack(sys.stdout, arg)
+
+
+if __name__ == '__main__':
+ main()