Browse Source

Add Comm Build System

theme-hidpi
Matt A. Tobin 3 years ago
parent
commit
75dd33577e
  1. 15
      .ycm_extra_conf.py
  2. 27
      aclocal.m4
  3. 1454
      build/autoconf/config.guess
  4. 76
      build/autoconf/mozconfig-find
  5. 76
      build/autoconf/mozconfig2client-mk
  6. 0
      build/dumbmake-dependencies
  7. 14
      build/mach_bootstrap.py
  8. 40
      build/pymake/make.py
  9. 3
      build/pypng/check-sync-exceptions
  10. 128
      build/pypng/exnumpy.py
  11. 537
      build/pypng/iccp.py
  12. 45
      build/pypng/mkiccp.py
  13. 99
      build/pypng/pdsimgtopng
  14. 73
      build/pypng/pipasgrey
  15. 44
      build/pypng/pipcat
  16. 56
      build/pypng/pipcolours
  17. 121
      build/pypng/pipcomposite
  18. 181
      build/pypng/pipdither
  19. 36
      build/pypng/piprgb
  20. 53
      build/pypng/pipscalez
  21. 127
      build/pypng/pipstack
  22. 67
      build/pypng/pipwindow
  23. 293
      build/pypng/plan9topng.py
  24. 172
      build/pypng/pngchunk
  25. 79
      build/pypng/pnghist
  26. 31
      build/pypng/pnglsch
  27. 151
      build/pypng/texttopng
  28. 463
      client.mk
  29. 16
      config/baseconfig.mk
  30. 7
      config/config.mk
  31. 2279
      config/configobj.py
  32. 94
      config/makefiles/autotargets.mk
  33. 117
      config/makefiles/makeutils.mk
  34. 25
      config/printconfigsetting.py
  35. 9
      config/recurse.mk
  36. 13
      config/rules.mk
  37. 38
      configure.in
  38. 32
      configure.py
  39. 7
      moz.build
  40. 7
      moz.configure

15
.ycm_extra_conf.py

@ -0,0 +1,15 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import imp, os, sys
old_bytecode = sys.dont_write_bytecode
sys.dont_write_bytecode = True
ycm_module = imp.load_source("_ycm_extra_conf", os.path.join("mozilla", ".ycm_extra_conf.py"))
sys.dont_write_bytecode = old_bytecode
# Expose the FlagsForFile function from mozilla/.ycm_extra_conf.py
FlagsForFile = ycm_module.FlagsForFile

27
aclocal.m4 vendored

@ -0,0 +1,27 @@
dnl
dnl Local autoconf macros used with UXP
dnl The contents of this file are under the Public Domain.
dnl
builtin(include, platform/build/autoconf/toolchain.m4)dnl
builtin(include, platform/build/autoconf/config.status.m4)dnl
builtin(include, platform/build/autoconf/nspr.m4)dnl
builtin(include, platform/build/autoconf/nss.m4)dnl
builtin(include, platform/build/autoconf/pkg.m4)dnl
builtin(include, platform/build/autoconf/codeset.m4)dnl
builtin(include, platform/build/autoconf/altoptions.m4)dnl
builtin(include, platform/build/autoconf/mozprog.m4)dnl
builtin(include, platform/build/autoconf/acwinpaths.m4)dnl
builtin(include, platform/build/autoconf/lto.m4)dnl
builtin(include, platform/build/autoconf/frameptr.m4)dnl
builtin(include, platform/build/autoconf/compiler-opts.m4)dnl
builtin(include, platform/build/autoconf/zlib.m4)dnl
builtin(include, platform/build/autoconf/expandlibs.m4)dnl
MOZ_PROG_CHECKMSYS()
# Read the user's .mozconfig script. We can't do this in
# configure.in: autoconf puts the argument parsing code above anything
# expanded from configure.in, and we need to get the configure options
# from .mozconfig in place before that argument parsing code.
dnl MOZ_READ_MOZCONFIG(platform)

1454
build/autoconf/config.guess vendored

File diff suppressed because it is too large Load Diff

76
build/autoconf/mozconfig-find

@ -0,0 +1,76 @@
#! /bin/sh
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# mozconfigfind - Loads options from .mozconfig onto configure's
# command-line. The .mozconfig file is searched for in the
# order:
# If $MOZCONFIG is set, use that.
# If one of $TOPSRCDIR/.mozconfig or $TOPSRCDIR/mozconfig exists, use it.
# If both exist, or if various legacy locations contain a mozconfig, error.
# Otherwise, use the default build options.
#
topsrcdir=$1
abspath() {
if uname -s | grep -q MINGW; then
# We have no way to figure out whether we're in gmake or pymake right
# now. gmake gives us Unix-style paths while pymake gives us Windows-style
# paths, so attempt to handle both.
regexes='^\([A-Za-z]:\|\\\\\|\/\) ^\/'
else
regexes='^\/'
fi
for regex in $regexes; do
if echo $1 | grep -q $regex; then
echo $1
return
fi
done
# If we're at this point, we have a relative path
echo `pwd`/$1
}
if [ -n "$MOZCONFIG" ] && ! [ -f "$MOZCONFIG" ]; then
echo "Specified MOZCONFIG \"$MOZCONFIG\" does not exist!" 1>&2
exit 1
fi
if [ -n "$MOZ_MYCONFIG" ]; then
echo "Your environment currently has the MOZ_MYCONFIG variable set to \"$MOZ_MYCONFIG\". MOZ_MYCONFIG is no longer supported. Please use MOZCONFIG instead." 1>&2
exit 1
fi
if [ -z "$MOZCONFIG" ] && [ -f "$topsrcdir/.mozconfig" ] && [ -f "$topsrcdir/mozconfig" ]; then
echo "Both \$topsrcdir/.mozconfig and \$topsrcdir/mozconfig are supported, but you must choose only one. Please remove the other." 1>&2
exit 1
fi
for _config in "$MOZCONFIG" \
"$topsrcdir/.mozconfig" \
"$topsrcdir/mozconfig"
do
if test -f "$_config"; then
abspath $_config
exit 0
fi
done
# We used to support a number of other implicit .mozconfig locations. We now
# detect if we were about to use any of these locations and issue an error if we
# find any.
for _config in "$topsrcdir/mozconfig.sh" \
"$topsrcdir/myconfig.sh" \
"$HOME/.mozconfig" \
"$HOME/.mozconfig.sh" \
"$HOME/.mozmyconfig.sh"
do
if test -f "$_config"; then
echo "You currently have a mozconfig at \"$_config\". This implicit location is no longer supported. Please move it to $topsrcdir/.mozconfig or specify it explicitly via \$MOZCONFIG." 1>&2
exit 1
fi
done

76
build/autoconf/mozconfig2client-mk

@ -0,0 +1,76 @@
#! /bin/sh
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# mozconfig2client-mk - Translates .mozconfig into options for client.mk.
# Prints defines to stdout.
#
# See mozconfig2configure for more details
print_header() {
cat <<EOF
# gmake
# This file is automatically generated for client.mk.
# Do not edit. Edit $FOUND_MOZCONFIG instead.
EOF
}
ac_add_options() {
for _opt
do
case "$_opt" in
--target=*)
echo $_opt | sed s/--target/CONFIG_GUESS/
;;
*)
echo "# $_opt is used by configure (not client.mk)"
;;
esac
done
}
ac_add_app_options() {
echo "# $* is used by configure (not client.mk)"
}
mk_add_options() {
for _opt
do
# Escape shell characters, space, tab, dollar, quote, backslash,
# and substitute '@<word>@' with '$(<word>)'.
_opt=`echo "$_opt" | sed -e 's/\([\"\\]\)/\\\\\1/g; s/@\([^@]*\)@/\$(\1)/g;'`
echo $_opt;
done
}
# Main
#--------------------------------------------------
scriptdir=`dirname $0`
topsrcdir=$1
# If the path changes, configure should be rerun
echo "# PATH=$PATH"
# If FOUND_MOZCONFIG isn't set, look for it and make sure the script doesn't error out
isfoundset=${FOUND_MOZCONFIG+yes}
if [ -z $isfoundset ]; then
FOUND_MOZCONFIG=`$scriptdir/mozconfig-find $topsrcdir`
if [ $? -ne 0 ]; then
echo '$(error Fix above errors before continuing.)'
else
isfoundset=yes
fi
fi
if [ -n $isfoundset ]; then
if [ "$FOUND_MOZCONFIG" ]
then
print_header
. "$FOUND_MOZCONFIG"
echo "FOUND_MOZCONFIG := $FOUND_MOZCONFIG"
fi
fi

0
build/dumbmake-dependencies

14
build/mach_bootstrap.py

@ -0,0 +1,14 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import unicode_literals
import os, sys
def bootstrap(topsrcdir, mozilla_dir=None):
if mozilla_dir is None:
mozilla_dir = os.path.join(topsrcdir, 'platform')
sys.path[0:0] = [mozilla_dir]
import build.mach_bootstrap
return build.mach_bootstrap.bootstrap(topsrcdir, mozilla_dir)

40
build/pymake/make.py

@ -0,0 +1,40 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# This is a wrapper around mozilla-central's pymake. If that isn't found then
# this uses client.py to pull it in.
import os
import sys
import subprocess
import shlex
def getpath(relpath):
thisdir = os.path.dirname(__file__)
return os.path.abspath(os.path.join(thisdir, *relpath))
PYMAKE = getpath(["..", "..", "platform", "build", "pymake", "make.py"])
def main(args):
if 'TINDERBOX_OUTPUT' in os.environ:
# When building on mozilla build slaves, execute mozmake instead. Until bug
# 978211, this is the easiest, albeit hackish, way to do this.
mozmake = os.path.join(os.path.dirname(__file__), '..', '..',
'mozmake.exe')
if os.path.exists(mozmake):
cmd = [mozmake]
cmd.extend(sys.argv[1:])
shell = os.environ.get('SHELL')
if shell and not shell.lower().endswith('.exe'):
cmd += ['SHELL=%s.exe' % shell]
sys.exit(subprocess.call(cmd))
if not os.path.exists(PYMAKE):
raise Exception("Pymake not found")
subprocess.check_call([sys.executable, PYMAKE] + args)
if __name__ == "__main__":
main(sys.argv[1:])

3
build/pypng/check-sync-exceptions

@ -0,0 +1,3 @@
# Nothing in this directory needs to be in sync with mozilla
# The contents are used only in c-c
*

128
build/pypng/exnumpy.py

@ -0,0 +1,128 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/exnumpy.py $
# $Rev: 126 $
# Numpy example.
# Original code created by Mel Raab, modified by David Jones.
'''
Example code integrating RGB PNG files, PyPNG and NumPy
(abstracted from Mel Raab's functioning code)
'''
# http://www.python.org/doc/2.4.4/lib/module-itertools.html
import itertools
import numpy
import png
''' If you have a PNG file for an RGB image,
and want to create a numpy array of data from it.
'''
# Read the file "picture.png" from the current directory. The `Reader`
# class can take a filename, a file-like object, or the byte data
# directly; this suggests alternatives such as using urllib to read
# an image from the internet:
# png.Reader(file=urllib.urlopen('http://www.libpng.org/pub/png/PngSuite/basn2c16.png'))
pngReader=png.Reader(filename='picture.png')
# Tuple unpacking, using multiple assignment, is very useful for the
# result of asDirect (and other methods).
# See
# http://docs.python.org/tutorial/introduction.html#first-steps-towards-programming
row_count, column_count, pngdata, meta = pngReader.asDirect()
bitdepth=meta['bitdepth']
plane_count=meta['planes']
# Make sure we're dealing with RGB files
assert plane_count == 3
''' Boxed row flat pixel:
list([R,G,B, R,G,B, R,G,B],
[R,G,B, R,G,B, R,G,B])
Array dimensions for this example: (2,9)
Create `image_2d` as a two-dimensional NumPy array by stacking a
sequence of 1-dimensional arrays (rows).
The NumPy array mimics PyPNG's (boxed row flat pixel) representation;
it will have dimensions ``(row_count,column_count*plane_count)``.
'''
# The use of ``numpy.uint16``, below, is to convert each row to a NumPy
# array with data type ``numpy.uint16``. This is a feature of NumPy,
# discussed further in
# http://docs.scipy.org/doc/numpy/user/basics.types.html .
# You can use avoid the explicit conversion with
# ``numpy.vstack(pngdata)``, but then NumPy will pick the array's data
# type; in practice it seems to pick ``numpy.int32``, which is large enough
# to hold any pixel value for any PNG image but uses 4 bytes per value when
# 1 or 2 would be enough.
# --- extract 001 start
image_2d = numpy.vstack(itertools.imap(numpy.uint16, pngdata))
# --- extract 001 end
# Do not be tempted to use ``numpy.asarray``; when passed an iterator
# (`pngdata` is often an iterator) it will attempt to create a size 1
# array with the iterator as its only element.
# An alternative to the above is to create the target array of the right
# shape, then populate it row by row:
if 0:
image_2d = numpy.zeros((row_count,plane_count*column_count),
dtype=numpy.uint16)
for row_index, one_boxed_row_flat_pixels in enumerate(pngdata):
image_2d[row_index,:]=one_boxed_row_flat_pixels
del pngReader
del pngdata
''' Reconfigure for easier referencing, similar to
Boxed row boxed pixel:
list([ (R,G,B), (R,G,B), (R,G,B) ],
[ (R,G,B), (R,G,B), (R,G,B) ])
Array dimensions for this example: (2,3,3)
``image_3d`` will contain the image as a three-dimensional numpy
array, having dimensions ``(row_count,column_count,plane_count)``.
'''
# --- extract 002 start
image_3d = numpy.reshape(image_2d,
(row_count,column_count,plane_count))
# --- extract 002 end
''' ============= '''
''' Convert NumPy image_3d array to PNG image file.
If the data is three-dimensional, as it is above, the best thing
to do is reshape it into a two-dimensional array with a shape of
``(row_count, column_count*plane_count)``. Because a
two-dimensional numpy array is an iterator, it can be passed
directly to the ``png.Writer.write`` method.
'''
row_count, column_count, plane_count = image_3d.shape
assert plane_count==3
pngfile = open('picture_out.png', 'wb')
try:
# This example assumes that you have 16-bit pixel values in the data
# array (that's what the ``bitdepth=16`` argument is for).
# If you don't, then the resulting PNG file will likely be
# very dark. Hey, it's only an example.
pngWriter = png.Writer(column_count, row_count,
greyscale=False,
alpha=False,
bitdepth=16)
# As of 2009-04-13 passing a numpy array that has an element type
# that is a numpy integer type (for example, the `image_3d` array has an
# element type of ``numpy.uint16``) generates a deprecation warning.
# This is probably a bug in numpy; it may go away in the future.
# The code still works despite the warning.
# See http://code.google.com/p/pypng/issues/detail?id=44
# --- extract 003 start
pngWriter.write(pngfile,
numpy.reshape(image_3d, (-1, column_count*plane_count)))
# --- extract 003 end
finally:
pngfile.close()

537
build/pypng/iccp.py

@ -0,0 +1,537 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/iccp.py $
# $Rev: 182 $
# iccp
#
# International Color Consortium Profile
#
# Tools for manipulating ICC profiles.
#
# An ICC profile can be extracted from a PNG image (iCCP chunk).
#
#
# Non-standard ICCP tags.
#
# Apple use some (widespread but) non-standard tags. These can be
# displayed in Apple's ColorSync Utility.
# - 'vcgt' (Video Card Gamma Tag). Table to load into video
# card LUT to apply gamma.
# - 'ndin' Apple display native information.
# - 'dscm' Apple multi-localized description strings.
# - 'mmod' Apple display make and model information.
#
# References
#
# [ICC 2001] ICC Specification ICC.1:2001-04 (Profile version 2.4.0)
# [ICC 2004] ICC Specification ICC.1:2004-10 (Profile version 4.2.0.0)
import struct
import png
class FormatError(Exception):
pass
class Profile:
"""An International Color Consortium Profile (ICC Profile)."""
def __init__(self):
self.rawtagtable = None
self.rawtagdict = {}
self.d = dict()
def fromFile(self, inp, name='<unknown>'):
# See [ICC 2004]
profile = inp.read(128)
if len(profile) < 128:
raise FormatError("ICC Profile is too short.")
size, = struct.unpack('>L', profile[:4])
profile += inp.read(d['size'] - len(profile))
return self.fromString(profile, name)
def fromString(self, profile, name='<unknown>'):
self.d = dict()
d = self.d
if len(profile) < 128:
raise FormatError("ICC Profile is too short.")
d.update(
zip(['size', 'preferredCMM', 'version',
'profileclass', 'colourspace', 'pcs'],
struct.unpack('>L4sL4s4s4s', profile[:24])))
if len(profile) < d['size']:
warnings.warn(
'Profile size declared to be %d, but only got %d bytes' %
(d['size'], len(profile)))
d['version'] = '%08x' % d['version']
d['created'] = readICCdatetime(profile[24:36])
d.update(
zip(['acsp', 'platform', 'flag', 'manufacturer', 'model'],
struct.unpack('>4s4s3L', profile[36:56])))
if d['acsp'] != 'acsp':
warnings.warn('acsp field not present (not an ICC Profile?).')
d['deviceattributes'] = profile[56:64]
d['intent'], = struct.unpack('>L', profile[64:68])
d['pcsilluminant'] = readICCXYZNumber(profile[68:80])
d['creator'] = profile[80:84]
d['id'] = profile[84:100]
ntags, = struct.unpack('>L', profile[128:132])
d['ntags'] = ntags
fmt = '4s2L' * ntags
# tag table
tt = struct.unpack('>' + fmt, profile[132:132+12*ntags])
tt = group(tt, 3)
# Could (should) detect 2 or more tags having the same sig. But
# we don't. Two or more tags with the same sig is illegal per
# the ICC spec.
# Convert (sig,offset,size) triples into (sig,value) pairs.
rawtag = map(lambda x: (x[0], profile[x[1]:x[1]+x[2]]), tt)
self.rawtagtable = rawtag
self.rawtagdict = dict(rawtag)
tag = dict()
# Interpret the tags whose types we know about
for sig, v in rawtag:
if sig in tag:
warnings.warn("Duplicate tag %r found. Ignoring." % sig)
continue
v = ICCdecode(v)
if v is not None:
tag[sig] = v
self.tag = tag
return self
def greyInput(self):
"""Adjust ``self.d`` dictionary for greyscale input device.
``profileclass`` is 'scnr', ``colourspace`` is 'GRAY', ``pcs``
is 'XYZ '.
"""
self.d.update(dict(profileclass='scnr',
colourspace='GRAY', pcs='XYZ '))
return self
def maybeAddDefaults(self):
if self.rawtagdict:
return
self._addTags(
cprt='Copyright unknown.',
desc='created by $URL: http://pypng.googlecode.com/svn/trunk/code/iccp.py $ $Rev: 182 $',
wtpt=D50(),
)
def addTags(self, **k):
self.maybeAddDefaults()
self._addTags(**k)
def _addTags(self, **k):
"""Helper for :meth:`addTags`."""
for tag, thing in k.items():
if not isinstance(thing, (tuple, list)):
thing = (thing,)
typetag = defaulttagtype[tag]
self.rawtagdict[tag] = encode(typetag, *thing)
return self
def write(self, out):
"""Write ICC Profile to the file."""
if not self.rawtagtable:
self.rawtagtable = self.rawtagdict.items()
tags = tagblock(self.rawtagtable)
self.writeHeader(out, 128 + len(tags))
out.write(tags)
out.flush()
return self
def writeHeader(self, out, size=999):
"""Add default values to the instance's `d` dictionary, then
write a header out onto the file stream. The size of the
profile must be specified using the `size` argument.
"""
def defaultkey(d, key, value):
"""Add ``[key]==value`` to the dictionary `d`, but only if
it does not have that key already.
"""
if key in d:
return
d[key] = value
z = '\x00' * 4
defaults = dict(preferredCMM=z,
version='02000000',
profileclass=z,
colourspace=z,
pcs='XYZ ',
created=writeICCdatetime(),
acsp='acsp',
platform=z,
flag=0,
manufacturer=z,
model=0,
deviceattributes=0,
intent=0,
pcsilluminant=encodefuns()['XYZ'](*D50()),
creator=z,
)
for k,v in defaults.items():
defaultkey(self.d, k, v)
hl = map(self.d.__getitem__,
['preferredCMM', 'version', 'profileclass', 'colourspace',
'pcs', 'created', 'acsp', 'platform', 'flag',
'manufacturer', 'model', 'deviceattributes', 'intent',
'pcsilluminant', 'creator'])
# Convert to struct.pack input
hl[1] = int(hl[1], 16)
out.write(struct.pack('>L4sL4s4s4s12s4s4sL4sLQL12s4s', size, *hl))
out.write('\x00' * 44)
return self
def encodefuns():
"""Returns a dictionary mapping ICC type signature sig to encoding
function. Each function returns a string comprising the content of
the encoded value. To form the full value, the type sig and the 4
zero bytes should be prefixed (8 bytes).
"""
def desc(ascii):
"""Return textDescription type [ICC 2001] 6.5.17. The ASCII part is
filled in with the string `ascii`, the Unicode and ScriptCode parts
are empty."""
ascii += '\x00'
l = len(ascii)
return struct.pack('>L%ds2LHB67s' % l,
l, ascii, 0, 0, 0, 0, '')
def text(ascii):
"""Return textType [ICC 2001] 6.5.18."""
return ascii + '\x00'
def curv(f=None, n=256):
"""Return a curveType, [ICC 2001] 6.5.3. If no arguments are
supplied then a TRC for a linear response is generated (no entries).
If an argument is supplied and it is a number (for *f* to be a
number it means that ``float(f)==f``) then a TRC for that
gamma value is generated.
Otherwise `f` is assumed to be a function that maps [0.0, 1.0] to
[0.0, 1.0]; an `n` element table is generated for it.
"""
if f is None:
return struct.pack('>L', 0)
try:
if float(f) == f:
return struct.pack('>LH', 1, int(round(f*2**8)))
except (TypeError, ValueError):
pass
assert n >= 2
table = []
M = float(n-1)
for i in range(n):
x = i/M
table.append(int(round(f(x) * 65535)))
return struct.pack('>L%dH' % n, n, *table)
def XYZ(*l):
return struct.pack('>3l', *map(fs15f16, l))
return locals()
# Tag type defaults.
# Most tags can only have one or a few tag types.
# When encoding, we associate a default tag type with each tag so that
# the encoding is implicit.
defaulttagtype=dict(
A2B0='mft1',
A2B1='mft1',
A2B2='mft1',
bXYZ='XYZ',
bTRC='curv',
B2A0='mft1',
B2A1='mft1',
B2A2='mft1',
calt='dtim',
targ='text',
chad='sf32',
chrm='chrm',
cprt='desc',
crdi='crdi',
dmnd='desc',
dmdd='desc',
devs='',
gamt='mft1',
kTRC='curv',
gXYZ='XYZ',
gTRC='curv',
lumi='XYZ',
meas='',
bkpt='XYZ',
wtpt='XYZ',
ncol='',
ncl2='',
resp='',
pre0='mft1',
pre1='mft1',
pre2='mft1',
desc='desc',
pseq='',
psd0='data',
psd1='data',
psd2='data',
psd3='data',
ps2s='data',
ps2i='data',
rXYZ='XYZ',
rTRC='curv',
scrd='desc',
scrn='',
tech='sig',
bfd='',
vued='desc',
view='view',
)
def encode(tsig, *l):
"""Encode a Python value as an ICC type. `tsig` is the type
signature to (the first 4 bytes of the encoded value, see [ICC 2004]
section 10.
"""
fun = encodefuns()
if tsig not in fun:
raise "No encoder for type %r." % tsig
v = fun[tsig](*l)
# Padd tsig out with spaces.
tsig = (tsig + ' ')[:4]
return tsig + '\x00'*4 + v
def tagblock(tag):
"""`tag` should be a list of (*signature*, *element*) pairs, where
*signature* (the key) is a length 4 string, and *element* is the
content of the tag element (another string).
The entire tag block (consisting of first a table and then the
element data) is constructed and returned as a string.
"""
n = len(tag)
tablelen = 12*n
# Build the tag table in two parts. A list of 12-byte tags, and a
# string of element data. Offset is the offset from the start of
# the profile to the start of the element data (so the offset for
# the next element is this offset plus the length of the element
# string so far).
offset = 128 + tablelen + 4
# The table. As a string.
table = ''
# The element data
element = ''
for k,v in tag:
table += struct.pack('>4s2L', k, offset + len(element), len(v))
element += v
return struct.pack('>L', n) + table + element
def iccp(out, inp):
profile = Profile().fromString(*profileFromPNG(inp))
print >>out, profile.d
print >>out, map(lambda x: x[0], profile.rawtagtable)
print >>out, profile.tag
def profileFromPNG(inp):
"""Extract profile from PNG file. Return (*profile*, *name*)
pair."""
r = png.Reader(file=inp)
_,chunk = r.chunk('iCCP')
i = chunk.index('\x00')
name = chunk[:i]
compression = chunk[i+1]
assert compression == chr(0)
profile = chunk[i+2:].decode('zlib')
return profile, name
def iccpout(out, inp):
"""Extract ICC Profile from PNG file `inp` and write it to
the file `out`."""
out.write(profileFromPNG(inp)[0])
def fs15f16(x):
"""Convert float to ICC s15Fixed16Number (as a Python ``int``)."""
return int(round(x * 2**16))
def D50():
"""Return D50 illuminant as an (X,Y,Z) triple."""
# See [ICC 2001] A.1
return (0.9642, 1.0000, 0.8249)
def writeICCdatetime(t=None):
"""`t` should be a gmtime tuple (as returned from
``time.gmtime()``). If not supplied, the current time will be used.
Return an ICC dateTimeNumber in a 12 byte string.
"""
import time
if t is None:
t = time.gmtime()
return struct.pack('>6H', *t[:6])
def readICCdatetime(s):
"""Convert from 12 byte ICC representation of dateTimeNumber to
ISO8601 string. See [ICC 2004] 5.1.1"""
return '%04d-%02d-%02dT%02d:%02d:%02dZ' % struct.unpack('>6H', s)
def readICCXYZNumber(s):
"""Convert from 12 byte ICC representation of XYZNumber to (x,y,z)
triple of floats. See [ICC 2004] 5.1.11"""
return s15f16l(s)
def s15f16l(s):
"""Convert sequence of ICC s15Fixed16 to list of float."""
# Note: As long as float has at least 32 bits of mantissa, all
# values are preserved.
n = len(s)//4
t = struct.unpack('>%dl' % n, s)
return map((2**-16).__mul__, t)
# Several types and their byte encodings are defined by [ICC 2004]
# section 10. When encoded, a value begins with a 4 byte type
# signature. We use the same 4 byte type signature in the names of the
# Python functions that decode the type into a Pythonic representation.
def ICCdecode(s):
"""Take an ICC encoded tag, and dispatch on its type signature
(first 4 bytes) to decode it into a Python value. Pair (*sig*,
*value*) is returned, where *sig* is a 4 byte string, and *value* is
some Python value determined by the content and type.
"""
sig = s[0:4].strip()
f=dict(text=RDtext,
XYZ=RDXYZ,
curv=RDcurv,
vcgt=RDvcgt,
sf32=RDsf32,
)
if sig not in f:
return None
return (sig, f[sig](s))
def RDXYZ(s):
"""Convert ICC XYZType to rank 1 array of trimulus values."""
# See [ICC 2001] 6.5.26
assert s[0:4] == 'XYZ '
return readICCXYZNumber(s[8:])
def RDsf32(s):
"""Convert ICC s15Fixed16ArrayType to list of float."""
# See [ICC 2004] 10.18
assert s[0:4] == 'sf32'
return s15f16l(s[8:])
def RDmluc(s):
"""Convert ICC multiLocalizedUnicodeType. This types encodes
several strings together with a language/country code for each
string. A list of (*lc*, *string*) pairs is returned where *lc* is
the 4 byte language/country code, and *string* is the string
corresponding to that code. It seems unlikely that the same
language/country code will appear more than once with different
strings, but the ICC standard does not prohibit it."""
# See [ICC 2004] 10.13
assert s[0:4] == 'mluc'
n,sz = struct.unpack('>2L', s[8:16])
assert sz == 12
record = []
for i in range(n):
lc,l,o = struct.unpack('4s2L', s[16+12*n:28+12*n])
record.append(lc, s[o:o+l])
# How are strings encoded?
return record
def RDtext(s):
"""Convert ICC textType to Python string."""
# Note: type not specified or used in [ICC 2004], only in older
# [ICC 2001].
# See [ICC 2001] 6.5.18
assert s[0:4] == 'text'
return s[8:-1]
def RDcurv(s):
"""Convert ICC curveType."""
# See [ICC 2001] 6.5.3
assert s[0:4] == 'curv'
count, = struct.unpack('>L', s[8:12])
if count == 0:
return dict(gamma=1)
table = struct.unpack('>%dH' % count, s[12:])
if count == 1:
return dict(gamma=table[0]*2**-8)
return table
def RDvcgt(s):
"""Convert Apple CMVideoCardGammaType."""
# See
# http://developer.apple.com/documentation/GraphicsImaging/Reference/ColorSync_Manager/Reference/reference.html#//apple_ref/c/tdef/CMVideoCardGammaType
assert s[0:4] == 'vcgt'
tagtype, = struct.unpack('>L', s[8:12])
if tagtype != 0:
return s[8:]
if tagtype == 0:
# Table.
channels,count,size = struct.unpack('>3H', s[12:18])
if size == 1:
fmt = 'B'
elif size == 2:
fmt = 'H'
else:
return s[8:]
l = len(s[18:])//size
t = struct.unpack('>%d%s' % (l, fmt), s[18:])
t = group(t, count)
return size, t
return s[8:]
def group(s, n):
# See
# http://www.python.org/doc/2.6/library/functions.html#zip
return zip(*[iter(s)]*n)
def main(argv=None):
import sys
from getopt import getopt
if argv is None:
argv = sys.argv
argv = argv[1:]
opt,arg = getopt(argv, 'o:')
if len(arg) > 0:
inp = open(arg[0], 'rb')
else:
inp = sys.stdin
for o,v in opt:
if o == '-o':
f = open(v, 'wb')
return iccpout(f, inp)
return iccp(sys.stdout, inp)
if __name__ == '__main__':
main()

45
build/pypng/mkiccp.py

@ -0,0 +1,45 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/mkiccp.py $
# $Rev: 182 $
# Make ICC Profile
# References
#
# [ICC 2001] ICC Specification ICC.1:2001-04 (Profile version 2.4.0)
# [ICC 2004] ICC Specification ICC.1:2004-10 (Profile version 4.2.0.0)
import struct
# Local module.
import iccp
def black(m):
"""Return a function that maps all values from [0.0,m] to 0, and maps
the range [m,1.0] into [0.0, 1.0] linearly.
"""
m = float(m)
def f(x):
if x <= m:
return 0.0
return (x-m)/(1.0-m)
return f
# For monochrome input the required tags are (See [ICC 2001] 6.3.1.1):
# profileDescription [ICC 2001] 6.4.32
# grayTRC [ICC 2001] 6.4.19
# mediaWhitePoint [ICC 2001] 6.4.25
# copyright [ICC 2001] 6.4.13
def agreyprofile(out):
it = iccp.Profile().greyInput()
it.addTags(kTRC=black(0.07))
it.write(out)
def main():
import sys
agreyprofile(sys.stdout)
if __name__ == '__main__':
main()

99
build/pypng/pdsimgtopng

@ -0,0 +1,99 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pdsimgtopng $
# $Rev: 154 $
# PDS Image to PNG
import re
import struct
import png
class FormatError(Exception):
pass
def pdskey(s, k):
"""Lookup key `k` in string `s`. Returns value (as a string), or
raises exception if not found.
"""
assert re.match(r' *\^?[:\w]+$', k)
safere = '^' + re.escape(k) +r' *= *(\w+)'
m = re.search(safere, s, re.MULTILINE)
if not m:
raise FormatError("Can't find %s." % k)
return m.group(1)
def img(inp):
"""Open the PDS IMG file `inp` and return (*pixels*, *info*).
*pixels* is an iterator over the rows, *info* is the information
dictionary.
"""
err = __import__('sys').stderr
consumed = 1024
s = inp.read(consumed)
record_type = pdskey(s, 'RECORD_TYPE')
if record_type != 'FIXED_LENGTH':
raise FormatError(
"Can only deal with FIXED_LENGTH record type (found %s)" %
record_type)
record_bytes = int(pdskey(s,'RECORD_BYTES'))
file_records = int(pdskey(s, 'FILE_RECORDS'))
label_records = int(pdskey(s, 'LABEL_RECORDS'))
remaining = label_records * record_bytes - consumed
s += inp.read(remaining)
consumed += remaining
image_pointer = int(pdskey(s, '^IMAGE'))
# "^IMAGE" locates a record. Records are numbered starting from 1.
image_index = image_pointer - 1
image_offset = image_index * record_bytes
gap = image_offset - consumed
assert gap >= 0
if gap:
inp.read(gap)
# This assumes there is only one OBJECT in the file, and it is the
# IMAGE.
height = int(pdskey(s, ' LINES'))
width = int(pdskey(s, ' LINE_SAMPLES'))
sample_type = pdskey(s, ' SAMPLE_TYPE')
sample_bits = int(pdskey(s, ' SAMPLE_BITS'))
# For Messenger MDIS, SAMPLE_BITS is reported as 16, but only values
# from 0 ot 4095 are used.
bitdepth = 12
if sample_type == 'MSB_UNSIGNED_INTEGER':
fmt = '>H'
else:
raise 'Unknown sample type: %s.' % sample_type
sample_bytes = (1,2)[bitdepth > 8]
row_bytes = sample_bytes * width
fmt = fmt[:1] + str(width) + fmt[1:]
def rowiter():
for y in range(height):
yield struct.unpack(fmt, inp.read(row_bytes))
info = dict(greyscale=True, alpha=False, bitdepth=bitdepth,
size=(width,height), gamma=1.0)
return rowiter(), info
def main(argv=None):
import sys
if argv is None:
argv = sys.argv
argv = argv[1:]
arg = argv
if len(arg) >= 1:
f = open(arg[0], 'rb')
else:
f = sys.stdin
pixels,info = img(f)
w = png.Writer(**info)
w.write(sys.stdout, pixels)
if __name__ == '__main__':
main()

73
build/pypng/pipasgrey

@ -0,0 +1,73 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipasgrey $
# $Rev: 187 $
# pipasgrey
# Convert image to grey (L, or LA), but only if that involves no colour
# change.
def asgrey(out, inp, quiet=False):
"""Convert image to greyscale, but only when no colour change. This
works by using the input G channel (green) as the output L channel
(luminance) and checking that every pixel is grey as we go. A non-grey
pixel will raise an error, but if `quiet` is true then the grey pixel
check is suppressed.
"""
from array import array
import png
r = png.Reader(file=inp)
_,_,pixels,info = r.asDirect()
if info['greyscale']:
w = png.Writer(**info)
return w.write(out, pixels)
planes = info['planes']
targetplanes = planes - 2
alpha = info['alpha']
width = info['size'][0]
typecode = 'BH'[info['bitdepth'] > 8]
# Values per target row
vpr = width * (targetplanes)
def iterasgrey():
for i,row in enumerate(pixels):
row = array(typecode, row)
targetrow = array(typecode, [0]*vpr)
# Copy G (and possibly A) channel.
green = row[0::planes]
if alpha:
targetrow[0::2] = green
targetrow[1::2] = row[3::4]
else:
targetrow = green
# Check R and B channel match.
if not quiet and (
green != row[0::planes] or green != row[2::planes]):
raise ValueError('Row %i contains non-grey pixel.' % i)
yield targetrow
info['greyscale'] = True
del info['planes']
w = png.Writer(**info)
w.write(out, iterasgrey())
def main(argv=None):
from getopt import getopt
import sys
if argv is None:
argv = sys.argv
argv = argv[1:]
opt,argv = getopt(argv, 'q')
quiet = False
for o,v in opt:
if o == '-q':
quiet = True
if len(argv) > 0:
f = open(argv[0], 'rb')
else:
f = sys.stdin
return asgrey(sys.stdout, f, quiet)
if __name__ == '__main__':
main()

44
build/pypng/pipcat

@ -0,0 +1,44 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipcat $
# $Rev: 77 $
# http://www.python.org/doc/2.4.4/lib/module-itertools.html
import itertools
import sys
import png
def cat(out, l):
"""Concatenate the list of images. All input images must be same
height and have the same number of channels. They are concatenated
left-to-right. `out` is the (open file) destination for the
output image. `l` should be a list of open files (the input
image files).
"""
l = map(lambda f: png.Reader(file=f), l)
# Ewgh, side effects.
map(lambda r: r.preamble(), l)
# The reference height; from the first image.
height = l[0].height
# The total target width
width = 0
for i,r in enumerate(l):
if r.height != height:
raise Error('Image %d, height %d, does not match %d.' %
(i, r.height, height))
width += r.width
pixel,info = zip(*map(lambda r: r.asDirect()[2:4], l))
tinfo = dict(info[0])
del tinfo['size']
w = png.Writer(width, height, **tinfo)
def itercat():
for row in itertools.izip(*pixel):
yield itertools.chain(*row)
w.write(out, itercat())
def main(argv):
return cat(sys.stdout, map(lambda n: open(n, 'rb'), argv[1:]))
if __name__ == '__main__':
main(sys.argv)

56
build/pypng/pipcolours

@ -0,0 +1,56 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipcolours $
# $Rev: 96 $
# pipcolours - extract all colours present in source image.
def colours(out, inp):
import itertools
import png
r = png.Reader(file=inp)
_,_,pixels,info = r.asDirect()
planes = info['planes']
col = set()
for row in pixels:
# Ewgh, side effects on col
map(col.add, png.group(row, planes))
col,planes = channel_reduce(col, planes)
col = list(col)
col.sort()
col = list(itertools.chain(*col))
width = len(col)//planes
greyscale = planes in (1,2)
alpha = planes in (2,4)
bitdepth = info['bitdepth']
w = png.Writer(width, 1,
bitdepth=bitdepth, greyscale=greyscale, alpha=alpha)
w.write(out, [col])
def channel_reduce(col, planes):
"""Attempt to reduce the number of channels in the set of
colours."""
if planes >= 3:
def isgrey(c):
return c[0] == c[1] == c[2]
if min(map(isgrey, col)) == True:
# Every colour is grey.
col = set(map(lambda x: x[0::3], col))
planes -= 2
return col,planes
def main(argv=None):
import sys
if argv is None:
argv = sys.argv
argv = argv[1:]
if len(argv) > 0:
f = open(argv[0], 'rb')
else:
f = sys.stdin
return colours(sys.stdout, f)
if __name__ == '__main__':
main()

121
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()

181
build/pypng/pipdither

@ -0,0 +1,181 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipdither $
# $Rev: 150 $
# pipdither
# Error Diffusing image dithering.
# Now with serpentine scanning.
# See http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT
# http://www.python.org/doc/2.4.4/lib/module-bisect.html
from bisect import bisect_left
import png
def dither(out, inp,
bitdepth=1, linear=False, defaultgamma=1.0, targetgamma=None,
cutoff=0.75):
"""Dither the input PNG `inp` into an image with a smaller bit depth
and write the result image onto `out`. `bitdepth` specifies the bit
depth of the new image.
Normally the source image gamma is honoured (the image is
converted into a linear light space before being dithered), but
if the `linear` argument is true then the image is treated as
being linear already: no gamma conversion is done (this is
quicker, and if you don't care much about accuracy, it won't
matter much).
Images with no gamma indication (no ``gAMA`` chunk) are normally
treated as linear (gamma = 1.0), but often it can be better
to assume a different gamma value: For example continuous tone
photographs intended for presentation on the web often carry
an implicit assumption of being encoded with a gamma of about
0.45 (because that's what you get if you just "blat the pixels"
onto a PC framebuffer), so ``defaultgamma=0.45`` might be a
good idea. `defaultgamma` does not override a gamma value
specified in the file itself: It is only used when the file
does not specify a gamma.
If you (pointlessly) specify both `linear` and `defaultgamma`,
`linear` wins.
The gamma of the output image is, by default, the same as the input
image. The `targetgamma` argument can be used to specify a
different gamma for the output image. This effectively recodes the
image to a different gamma, dithering as we go. The gamma specified
is the exponent used to encode the output file (and appears in the
output PNG's ``gAMA`` chunk); it is usually less than 1.
"""
# Encoding is what happened when the PNG was made (and also what
# happens when we output the PNG). Decoding is what we do to the
# source PNG in order to process it.
# The dithering algorithm is not completely general; it
# can only do bit depth reduction, not arbitrary palette changes.
import operator
maxval = 2**bitdepth - 1
r = png.Reader(file=inp)
# If image gamma is 1 or gamma is not present and we are assuming a
# value of 1, then it is faster to pass a maxval parameter to
# asFloat (the multiplications get combined). With gamma, we have
# to have the pixel values from 0.0 to 1.0 (as long as we are doing
# gamma correction here).
# Slightly annoyingly, we don't know the image gamma until we've
# called asFloat().
_,_,pixels,info = r.asDirect()
planes = info['planes']
assert planes == 1
width = info['size'][0]
sourcemaxval = 2**info['bitdepth'] - 1
if linear:
gamma = 1
else:
gamma = info.get('gamma') or defaultgamma
# Convert gamma from encoding gamma to the required power for
# decoding.
decode = 1.0/gamma
# Build a lookup table for decoding; convert from pixel values to linear
# space:
sourcef = 1.0/sourcemaxval
incode = map(sourcef.__mul__, range(sourcemaxval+1))
if decode != 1.0:
incode = map(decode.__rpow__, incode)
# Could be different, later on. targetdecode is the assumed gamma
# that is going to be used to decoding the target PNG. It is the
# reciprocal of the exponent that we use to encode the target PNG.
# This is the value that we need to build our table that we use for
# converting from linear to target colour space.
if targetgamma is None:
targetdecode = decode
else:
targetdecode = 1.0/targetgamma
# The table we use for encoding (creating the target PNG), still
# maps from pixel value to linear space, but we use it inverted, by
# searching through it with bisect.
targetf = 1.0/maxval
outcode = map(targetf.__mul__, range(maxval+1))
if targetdecode != 1.0:
outcode = map(targetdecode.__rpow__, outcode)
# The table used for choosing output codes. T