@@ -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 |
@@ -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) |
@@ -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 |
@@ -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,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) |
@@ -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:]) |
@@ -0,0 +1,3 @@ | |||
# Nothing in this directory needs to be in sync with mozilla | |||
# The contents are used only in c-c | |||
* |
@@ -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() | |||
@@ -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() |
@@ -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() |
@@ -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() | |||
@@ -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() |
@@ -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) |
@@ -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() |
@@ -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() |
@@ -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. These values represent | |||
# the cutoff points between two adjacent output codes. | |||
choosecode = zip(outcode[1:], outcode) | |||
p = cutoff | |||
choosecode = map(lambda x: x[0]*p+x[1]*(1.0-p), choosecode) | |||
def iterdither(): | |||
# Errors diffused downwards (into next row) | |||
ed = [0.0]*width | |||
flipped = False | |||
for row in pixels: | |||
row = map(incode.__getitem__, row) | |||
row = map(operator.add, ed, row) | |||
if flipped: | |||
row = row[::-1] | |||
targetrow = [0] * width | |||
for i,v in enumerate(row): | |||
# Clamp. Necessary because previously added errors may take | |||
# v out of range. | |||
v = max(0.0, min(v, 1.0)) | |||
# `it` will be the index of the chosen target colour; | |||
it = bisect_left(choosecode, v) | |||
t = outcode[it] | |||
targetrow[i] = it | |||
# err is the error that needs distributing. | |||
err = v - t | |||
# Sierra "Filter Lite" distributes * 2 | |||
# as per this diagram. 1 1 | |||
ef = err/2.0 | |||
# :todo: consider making rows one wider at each end and | |||
# removing "if"s | |||
if i+1 < width: | |||
row[i+1] += ef | |||
ef /= 2.0 | |||
ed[i] = ef | |||
if i: | |||
ed[i-1] += ef | |||
if flipped: | |||
ed = ed[::-1] | |||
targetrow = targetrow[::-1] | |||
yield targetrow | |||
flipped = not flipped | |||
info['bitdepth'] = bitdepth | |||
info['gamma'] = 1.0/targetdecode | |||
w = png.Writer(**info) | |||
w.write(out, iterdither()) | |||
def main(argv=None): | |||
# http://www.python.org/doc/2.4.4/lib/module-getopt.html | |||
from getopt import getopt | |||
import sys | |||
if argv is None: | |||
argv = sys.argv | |||
opt,argv = getopt(argv[1:], 'b:c:g:lo:') | |||
k = {} | |||
for o,v in opt: | |||
if o == '-b': | |||
k['bitdepth'] = int(v) | |||
if o == '-c': | |||
k['cutoff'] = float(v) | |||
if o == '-g': | |||
k['defaultgamma'] = float(v) | |||
if o == '-l': | |||
k['linear'] = True | |||
if o == '-o': | |||
k['targetgamma'] = float(v) | |||
if o == '-?': | |||
print >>sys.stderr, "pipdither [-b bits] [-c cutoff] [-g assumed-gamma] [-l] [in.png]" | |||
if len(argv) > 0: | |||
f = open(argv[0], 'rb') | |||
else: | |||
f = sys.stdin | |||
return dither(sys.stdout, f, **k) | |||
if __name__ == '__main__': | |||
main() |
@@ -0,0 +1,36 @@ | |||
#!/usr/bin/env python | |||
# $URL: http://pypng.googlecode.com/svn/trunk/code/piprgb $ | |||
# $Rev: 131 $ | |||
# piprgb | |||
# | |||
# Convert input image to RGB or RGBA format. Output will be colour type | |||
# 2 or 6, and will not have a tRNS chunk. | |||
import png | |||
def rgb(out, inp): | |||
"""Convert to RGB/RGBA.""" | |||
r = png.Reader(file=inp) | |||
r.preamble() | |||
if r.alpha or r.trns: | |||
get = r.asRGBA | |||
else: | |||
get = r.asRGB | |||
pixels,info = get()[2:4] | |||
w = png.Writer(**info) | |||
w.write(out, pixels) | |||
def main(argv=None): | |||
import sys | |||
if argv is None: | |||
argv = sys.argv | |||
if len(argv) > 1: | |||
f = open(argv[1], 'rb') | |||
else: | |||
f = sys.stdin | |||
return rgb(sys.stdout, f) | |||
if __name__ == '__main__': | |||
main() |
@@ -0,0 +1,53 @@ | |||
#!/usr/bin/env python | |||
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipscalez $ | |||
# $Rev: 131 $ | |||
# pipscalez | |||
# Enlarge an image by an integer factor horizontally and vertically. | |||
def rescale(inp, out, xf, yf): | |||
from array import array | |||
import png | |||
r = png.Reader(file=inp) | |||
_,_,pixels,meta = r.asDirect() | |||
typecode = 'BH'[meta['bitdepth'] > 8] | |||
planes = meta['planes'] | |||
# We are going to use meta in the call to Writer, so expand the | |||
# size. | |||
x,y = meta['size'] | |||
x *= xf | |||
y *= yf | |||
meta['size'] = (x,y) | |||
del x | |||
del y | |||
# Values per row, target row. | |||
vpr = meta['size'][0] * planes | |||
def iterscale(): | |||
for row in pixels: | |||
bigrow = array(typecode, [0]*vpr) | |||
row = array(typecode, row) | |||
for c in range(planes): | |||
channel = row[c::planes] | |||
for i in range(xf): | |||
bigrow[i*planes+c::xf*planes] = channel | |||
for _ in range(yf): | |||
yield bigrow | |||
w = png.Writer(**meta) | |||
w.write(out, iterscale()) | |||
def main(argv=None): | |||
import sys | |||
if argv is None: | |||
argv = sys.argv | |||
xf = int(argv[1]) | |||
if len(argv) > 2: | |||
yf = int(argv[2]) | |||
else: | |||
yf = xf | |||
return rescale(sys.stdin, sys.stdout, xf, yf) | |||
if __name__ == '__main__': | |||
main() |
@@ -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() |
@@ -0,0 +1,67 @@ | |||
#!/usr/bin/env python | |||
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipwindow $ | |||
# $Rev: 173 $ | |||
# pipwindow | |||
# Tool to crop/expand an image to a rectangular window. Come the | |||
# revolution this tool will allow the image and the window to be placed | |||
# arbitrarily (in particular the window can be bigger than the picture | |||
# and/or overlap it only partially) and the image can be OpenGL style | |||
# border/repeat effects (repeat, mirrored repeat, clamp, fixed | |||
# background colour, background colour from source file). For now it | |||
# only acts as crop. The window must be no greater than the image in | |||
# both x and y. | |||
def window(tl, br, inp, out): | |||
"""Place a window onto the image and cut-out the resulting | |||
rectangle. The window is an axis aligned rectangle opposite corners | |||
at *tl* and *br* (each being an (x,y) pair). *inp* specifies the | |||
input file which should be a PNG image. | |||
""" | |||
import png | |||
r = png.Reader(file=inp) | |||
x,y,pixels,meta = r.asDirect() | |||
if not (0 <= tl[0] < br[0] <= x): | |||
raise NotImplementedError() | |||
if not (0 <= tl[1] < br[1] <= y): | |||
raise NotImplementedError() | |||
# Compute left and right bounds for each row | |||
l = tl[0] * meta['planes'] | |||
r = br[0] * meta['planes'] | |||
def itercrop(): | |||
"""An iterator to perform the crop.""" | |||
for i,row in enumerate(pixels): | |||
if i < tl[1]: | |||
continue | |||
if i >= br[1]: | |||
# Same as "raise StopIteration" | |||
return | |||
yield row[l:r] | |||
meta['size'] = (br[0]-tl[0], br[1]-tl[1]) | |||
w = png.Writer(**meta) | |||
w.write(out, itercrop()) | |||
def main(argv=None): | |||
import sys | |||
if argv is None: | |||
argv = sys.argv | |||
argv = argv[1:] | |||
tl = (0,0) | |||
br = tuple(map(int, argv[:2])) | |||
if len(argv) >= 4: | |||
tl = br | |||
br = tuple(map(int, argv[2:4])) | |||
if len(argv) in (2, 4): | |||
f = sys.stdin | |||
else: | |||
f = open(argv[-1], 'rb') | |||
return window(tl, br, f, sys.stdout) | |||
if __name__ == '__main__': | |||
main() |
@@ -0,0 +1,293 @@ | |||
#!/usr/bin/env python | |||
# $Rev: 184 $ | |||
# $URL: http://pypng.googlecode.com/svn/trunk/code/plan9topng.py $ | |||
# Imported from //depot/prj/plan9topam/master/code/plan9topam.py#4 on | |||
# 2009-06-15. | |||
"""Command line tool to convert from Plan 9 image format to PNG format. | |||
Plan 9 image format description: | |||
http://plan9.bell-labs.com/magic/man2html/6/image | |||
""" | |||
# http://www.python.org/doc/2.3.5/lib/module-itertools.html | |||
import itertools | |||
# http://www.python.org/doc/2.3.5/lib/module-re.html | |||
import re | |||
# http://www.python.org/doc/2.3.5/lib/module-sys.html | |||
import sys | |||
def block(s, n): | |||
# See http://www.python.org/doc/2.6.2/library/functions.html#zip | |||
return zip(*[iter(s)]*n) | |||
def convert(f, output=sys.stdout) : | |||
"""Convert Plan 9 file to PNG format. Works with either uncompressed | |||
or compressed files. | |||
""" | |||
r = f.read(11) | |||
if r == 'compressed\n' : | |||
png(output, *decompress(f)) | |||
else : | |||
png(output, *glue(f, r)) | |||
def glue(f, r) : | |||
"""Return (metadata, stream) pair where `r` is the initial portion of | |||
the metadata that has already been read from the stream `f`. | |||
""" | |||
r = r + f.read(60-len(r)) | |||
return (r, f) | |||
def meta(r) : | |||
"""Convert 60 character string `r`, the metadata from an image file. | |||
Returns a 5-tuple (*chan*,*minx*,*miny*,*limx*,*limy*). 5-tuples may | |||
settle into lists in transit. | |||
As per http://plan9.bell-labs.com/magic/man2html/6/image the metadata | |||
comprises 5 words separated by blanks. As it happens each word starts | |||
at an index that is a multiple of 12, but this routine does not care | |||
about that.""" | |||
r = r.split() | |||
# :todo: raise FormatError | |||
assert len(r) == 5 | |||
r = [r[0]] + map(int, r[1:]) | |||
return r | |||
def bitdepthof(pixel) : | |||
"""Return the bitdepth for a Plan9 pixel format string.""" | |||
maxd = 0 | |||
for c in re.findall(r'[a-z]\d*', pixel) : | |||
if c[0] != 'x': | |||
maxd = max(maxd, int(c[1:])) | |||
return maxd | |||
def maxvalof(pixel): | |||
"""Return the netpbm MAXVAL for a Plan9 pixel format string.""" | |||
bitdepth = bitdepthof(pixel) | |||
return (2**bitdepth)-1 | |||
def pixmeta(metadata, f) : | |||
"""Convert (uncompressed) Plan 9 image file to pair of (*metadata*, | |||
*pixels*). This is intended to be used by PyPNG format. *metadata* | |||
is the metadata returned in a dictionary, *pixels* is an iterator that | |||
yields each row in boxed row flat pixel format. | |||
`f`, the input file, should be cued up to the start of the image data. | |||
""" | |||
chan,minx,miny,limx,limy = metadata | |||
rows = limy - miny | |||
width = limx - minx | |||
nchans = len(re.findall('[a-wyz]', chan)) | |||
alpha = 'a' in chan | |||
# Iverson's convention for the win! | |||
ncolour = nchans - alpha | |||
greyscale = ncolour == 1 | |||
bitdepth = bitdepthof(chan) | |||
maxval = 2**bitdepth - 1 | |||
# PNG style metadata | |||
meta=dict(size=(width,rows), bitdepth=bitdepthof(chan), | |||
greyscale=greyscale, alpha=alpha, planes=nchans) | |||
return itertools.imap(lambda x: itertools.chain(*x), | |||
block(unpack(f, rows, width, chan, maxval), width)), meta | |||
def png(out, metadata, f): | |||
"""Convert to PNG format. `metadata` should be a Plan9 5-tuple; `f` | |||
the input file (see :meth:`pixmeta`). | |||
""" | |||
import png | |||
pixels,meta = pixmeta(metadata, f) | |||
p = png.Writer(**meta) | |||
p.write(out, pixels) | |||
def spam(): | |||
"""Not really spam, but old PAM code, which is in limbo.""" | |||
if nchans == 3 or nchans == 1 : | |||
# PGM (P5) or PPM (P6) format. | |||
output.write('P%d\n%d %d %d\n' % (5+(nchans==3), width, rows, maxval)) | |||
else : | |||
# PAM format. | |||
output.write("""P7 | |||
WIDTH %d | |||
HEIGHT %d | |||
DEPTH %d | |||
MAXVAL %d | |||
""" % (width, rows, nchans, maxval)) | |||
def unpack(f, rows, width, pixel, maxval) : | |||
"""Unpack `f` into pixels. Assumes the pixel format is such that the depth | |||
is either a multiple or a divisor of 8. | |||
`f` is assumed to be an iterator that returns blocks of input such | |||
that each block contains a whole number of pixels. An iterator is | |||
returned that yields each pixel as an n-tuple. `pixel` describes the | |||
pixel format using the Plan9 syntax ("k8", "r8g8b8", and so on). | |||
""" | |||
def mask(w) : | |||
"""An integer, to be used as a mask, with bottom `w` bits set to 1.""" | |||
return (1 << w)-1 | |||
def deblock(f, depth, width) : | |||
"""A "packer" used to convert multiple bytes into single pixels. | |||
`depth` is the pixel depth in bits (>= 8), `width` is the row width in | |||
pixels. | |||
""" | |||
w = depth // 8 | |||
i = 0 | |||
for block in f : | |||
for i in range(len(block)//w) : | |||
p = block[w*i:w*(i+1)] | |||
i += w | |||
# Convert p to little-endian integer, x | |||
x = 0 | |||
s = 1 # scale | |||
for j in p : | |||
x += s * ord(j) | |||
s <<= 8 | |||
yield x | |||
def bitfunge(f, depth, width) : | |||
"""A "packer" used to convert single bytes into multiple pixels. | |||
Depth is the pixel depth (< 8), width is the row width in pixels. | |||
""" | |||
for block in f : | |||
col = 0 | |||
for i in block : | |||
x = ord(i) | |||
for j in range(8/depth) : | |||
yield x >> (8 - depth) | |||
col += 1 | |||
if col == width : | |||
# A row-end forces a new byte even if we haven't consumed | |||
# all of the current byte. Effectively rows are bit-padded | |||
# to make a whole number of bytes. | |||
col = 0 | |||
break | |||
x <<= depth | |||
# number of bits in each channel | |||
chan = map(int, re.findall(r'\d+', pixel)) | |||
# type of each channel | |||
type = re.findall('[a-z]', pixel) | |||
depth = sum(chan) | |||
# According to the value of depth pick a "packer" that either gathers | |||
# multiple bytes into a single pixel (for depth >= 8) or split bytes | |||
# into several pixels (for depth < 8) | |||
if depth >= 8 : | |||
# | |||
assert depth % 8 == 0 | |||
packer = deblock | |||
else : | |||
assert 8 % depth == 0 | |||
packer = bitfunge | |||
for x in packer(f, depth, width) : | |||
# x is the pixel as an unsigned integer | |||
o = [] | |||
# This is a bit yucky. Extract each channel from the _most_ | |||
# significant part of x. | |||
for j in range(len(chan)) : | |||
v = (x >> (depth - chan[j])) & mask(chan[j]) | |||
x <<= chan[j] | |||
if type[j] != 'x' : | |||
# scale to maxval | |||
v = v * float(maxval) / mask(chan[j]) | |||
v = int(v+0.5) | |||
o.append(v) | |||
yield o | |||
def decompress(f) : | |||
"""Decompress a Plan 9 image file. Assumes f is already cued past the | |||
initial 'compressed\n' string. | |||
""" | |||
r = meta(f.read(60)) | |||
return r, decomprest(f, r[4]) | |||
def decomprest(f, rows) : | |||
"""Iterator that decompresses the rest of a file once the metadata | |||
have been consumed.""" | |||
row = 0 | |||
while row < rows : | |||
row,o = deblock(f) | |||
yield o | |||
def deblock(f) : | |||
"""Decompress a single block from a compressed Plan 9 image file. | |||
Each block starts with 2 decimal strings of 12 bytes each. Yields a | |||
sequence of (row, data) pairs where row is the total number of rows | |||
processed according to the file format and data is the decompressed | |||
data for a set of rows.""" | |||
row = int(f.read(12)) | |||
size = int(f.read(12)) | |||
if not (0 <= size <= 6000) : | |||
raise 'block has invalid size; not a Plan 9 image file?' | |||
# Since each block is at most 6000 bytes we may as well read it all in | |||
# one go. | |||
d = f.read(size) | |||
i = 0 | |||
o = [] | |||
while i < size : | |||
x = ord(d[i]) | |||
i += 1 | |||
if x & 0x80 : | |||
x = (x & 0x7f) + 1 | |||
lit = d[i:i+x] | |||
i += x | |||
o.extend(lit) | |||
continue | |||
# x's high-order bit is 0 | |||
l = (x >> 2) + 3 | |||
# Offset is made from bottom 2 bits of x and all 8 bits of next | |||
# byte. http://plan9.bell-labs.com/magic/man2html/6/image doesn't | |||
# say whether x's 2 bits are most signiificant or least significant. | |||
# But it is clear from inspecting a random file, | |||
# http://plan9.bell-labs.com/sources/plan9/sys/games/lib/sokoban/images/cargo.bit | |||
# that x's 2 bit are most significant. | |||
# | |||
offset = (x & 3) << 8 | |||
offset |= ord(d[i]) | |||
i += 1 | |||
# Note: complement operator neatly maps (0 to 1023) to (-1 to | |||
# -1024). Adding len(o) gives a (non-negative) offset into o from | |||
# which to start indexing. | |||
offset = ~offset + len(o) | |||
if offset < 0 : | |||
raise 'byte offset indexes off the begininning of the output buffer; not a Plan 9 image file?' | |||
for j in range(l) : | |||
o.append(o[offset+j]) | |||
return row,''.join(o) | |||
def main(argv=None) : | |||
if argv is None : | |||
argv = sys.argv | |||
if len(sys.argv) <= 1 : | |||
return convert(sys.stdin) | |||
else : | |||
return convert(open(argv[1], 'rb')) | |||
if __name__ == '__main__' : | |||
sys.exit(main()) |
@@ -0,0 +1,172 @@ | |||
#!/usr/bin/env python | |||
# $URL: http://pypng.googlecode.com/svn/trunk/code/pngchunk $ | |||
# $Rev: 156 $ | |||
# pngchunk | |||
# Chunk editing/extraction tool. | |||
import struct | |||
import warnings | |||
# Local module. | |||
import png | |||
""" | |||
pngchunk [--gamma g] [--iccprofile file] [--sigbit b] [-c cHNK!] [-c cHNK:foo] [-c cHNK<file] | |||
The ``-c`` option is used to add or remove chunks. A chunk is specified | |||
by its 4 byte chunk type. If this is followed by a ``!`` then that | |||
chunk is removed from the PNG file. If the chunk type is followed by a | |||
``:`` then the chunk is replaced with the contents of the rest of the | |||
argument (this is probably only useful if the content is mostly ASCII, | |||
otherwise it's a pain to quote the contents, otherwise see...). A ``<`` | |||
can be used to take the contents of the chunk from the named file. | |||
""" | |||
def chunk(out, inp, l): | |||
"""Process the input PNG file to the output, chunk by chunk. Chunks | |||
can be inserted, removed, replaced, or sometimes edited. Generally, | |||
chunks are not inspected, so pixel data (in the ``IDAT`` chunks) | |||
cannot be modified. `l` should be a list of (*chunktype*, | |||
*content*) pairs. *chunktype* is usually the type of the PNG chunk, | |||
specified as a 4-byte Python string, and *content* is the chunk's | |||
content, also as a string; if *content* is ``None`` then *all* | |||
chunks of that type will be removed. | |||
This function *knows* about certain chunk types and will | |||
automatically convert from Python friendly representations to | |||
string-of-bytes. | |||
chunktype | |||
'gamma' 'gAMA' float | |||
'sigbit' 'sBIT' int, or tuple of length 1,2 or 3 | |||
Note that the length of the strings used to identify *friendly* | |||
chunk types is greater than 4, hence they cannot be confused with | |||
canonical chunk types. | |||
Chunk types, if specified using the 4-byte syntax, need not be | |||
official PNG chunks at all. Non-standard chunks can be created. | |||
""" | |||
def canonical(p): | |||
"""Take a pair (*chunktype*, *content*), and return canonical | |||
representation (*chunktype*, *content*) where `chunktype` is the | |||
4-byte PNG chunk type and `content` is a string. | |||
""" | |||
t,v = p | |||
if len(t) == 4: | |||
return t,v | |||
if t == 'gamma': | |||
t = 'gAMA' | |||
v = int(round(1e5*v)) | |||
v = struct.pack('>I', v) | |||
elif t == 'sigbit': | |||
t = 'sBIT' | |||
try: | |||
v[0] | |||
except TypeError: | |||
v = (v,) | |||
v = struct.pack('%dB' % len(v), *v) | |||
elif t == 'iccprofile': | |||
t = 'iCCP' | |||
# http://www.w3.org/TR/PNG/#11iCCP | |||
v = 'a color profile\x00\x00' + v.encode('zip') | |||
else: | |||
warnings.warn('Unknown chunk type %r' % t) | |||
return t[:4],v | |||
l = map(canonical, l) | |||
# Some chunks automagically replace ones that are present in the | |||
# source PNG. There can only be one of each of these chunk types. | |||
# Create a 'replace' dictionary to record these chunks. | |||
add = [] | |||
delete = set() | |||
replacing = set(['gAMA', 'sBIT', 'PLTE', 'tRNS', 'sPLT', 'IHDR']) | |||
replace = dict() | |||
for t,v in l: | |||
if v is None: | |||
delete.add(t) | |||
elif t in replacing: | |||
replace[t] = v | |||
else: | |||
add.append((t,v)) | |||
del l | |||
r = png.Reader(file=inp) | |||
chunks = r.chunks() | |||
def iterchunks(): | |||
for t,v in chunks: | |||
if t in delete: | |||
continue | |||
if t in replace: | |||
yield t,replace[t] | |||
del replace[t] | |||
continue | |||
if t == 'IDAT' and replace: | |||
# Insert into the output any chunks that are on the | |||
# replace list. We haven't output them yet, because we | |||
# didn't see an original chunk of the same type to | |||
# replace. Thus the "replace" is actually an "insert". | |||
for u,w in replace.items(): | |||
yield u,w | |||
del replace[u] | |||
if t == 'IDAT' and add: | |||
for item in add: | |||
yield item | |||
del add[:] | |||
yield t,v | |||
return png.write_chunks(out, iterchunks()) | |||
class Usage(Exception): | |||
pass | |||
def main(argv=None): | |||
import getopt | |||
import re | |||
import sys | |||
if argv is None: | |||
argv = sys.argv | |||
argv = argv[1:] | |||
try: | |||
try: | |||
opt,arg = getopt.getopt(argv, 'c:', | |||
['gamma=', 'iccprofile=', 'sigbit=']) | |||
except getopt.error, msg: | |||
raise Usage(msg) | |||
k = [] | |||
for o,v in opt: | |||
if o in ['--gamma']: | |||
k.append(('gamma', float(v))) | |||
if o in ['--sigbit']: | |||
k.append(('sigbit', int(v))) | |||
if o in ['--iccprofile']: | |||
k.append(('iccprofile', open(v, 'rb').read())) | |||
if o in ['-c']: | |||
type = v[:4] | |||
if not re.match('[a-zA-Z]{4}', type): | |||
raise Usage('Chunk type must consist of 4 letters.') | |||
if v[4] == '!': | |||
k.append((type, None)) | |||
if v[4] == ':': | |||
k.append((type, v[5:])) | |||
if v[4] == '<': | |||
k.append((type, open(v[5:], 'rb').read())) | |||
except Usage, err: | |||
print >>sys.stderr, ( | |||
"usage: pngchunk [--gamma d.dd] [--sigbit b] [-c cHNK! | -c cHNK:text-string]") | |||
print >>sys.stderr, err.message | |||
return 2 | |||
if len(arg) > 0: | |||
f = open(arg[0], 'rb') | |||
else: | |||
f = sys.stdin | |||
return chunk(sys.stdout, f, k) | |||
if __name__ == '__main__': | |||
main() |
@@ -0,0 +1,79 @@ | |||
#!/usr/bin/env python | |||
# $URL: http://pypng.googlecode.com/svn/trunk/code/pnghist $ | |||
# $Rev: 153 $ | |||
# PNG Histogram | |||
# Only really works on grayscale images. | |||
from array import array | |||
import getopt | |||
import png | |||
def decidemax(level): | |||
"""Given an array of levels, decide the maximum value to use for the | |||
histogram. This is normally chosen to be a bit bigger than the 99th | |||
percentile, but if the 100th percentile is not much more (within a | |||
factor of 2) then the 100th percentile is chosen. | |||
""" | |||
truemax = max(level) | |||
sl = level[:] | |||
sl.sort(reverse=True) | |||
i99 = int(round(len(level)*0.01)) | |||
if truemax <= 2*sl[i99]: | |||
return truemax | |||
return 1.05*sl[i99] | |||
def hist(out, inp, verbose=None): | |||
"""Open the PNG file `inp` and generate a histogram.""" | |||
r = png.Reader(file=inp) | |||
x,y,pixels,info = r.asDirect() | |||
bitdepth = info['bitdepth'] | |||
level = [0]*2**bitdepth | |||
for row in pixels: | |||
for v in row: | |||
level[v] += 1 | |||
maxlevel = decidemax(level) | |||
h = 100 | |||
outbitdepth = 8 | |||
outmaxval = 2**outbitdepth - 1 | |||
def genrow(): | |||
for y in range(h): | |||
y = h-y-1 | |||
# :todo: vary typecode according to outbitdepth | |||
row = array('B', [0]*len(level)) | |||
fl = y*maxlevel/float(h) | |||
ce = (y+1)*maxlevel/float(h) | |||
for x in range(len(row)): | |||
if level[x] <= fl: | |||
# Relies on row being initialised to all 0 | |||
continue | |||
if level[x] >= ce: | |||
row[x] = outmaxval | |||
continue | |||
frac = (level[x] - fl)/(ce - fl) | |||
row[x] = int(round(outmaxval*frac)) | |||
yield row | |||
w = png.Writer(len(level), h, gamma=1.0, | |||
greyscale=True, alpha=False, bitdepth=outbitdepth) | |||
w.write(out, genrow()) | |||
if verbose: print >>verbose, level | |||
def main(argv=None): | |||
import sys | |||
if argv is None: | |||
argv = sys.argv | |||
argv = argv[1:] | |||
opt,arg = getopt.getopt(argv, '') | |||
if len(arg) < 1: | |||
f = sys.stdin | |||
else: | |||
f = open(arg[0]) | |||
hist(sys.stdout, f) | |||
if __name__ == '__main__': | |||
main() |
@@ -0,0 +1,31 @@ | |||
#!/usr/bin/env python | |||
# $URL: http://pypng.googlecode.com/svn/trunk/code/pnglsch $ | |||
# $Rev: 107 $ | |||
# pnglsch | |||
# PNG List Chunks | |||
import png | |||
def list(out, inp): | |||
r = png.Reader(file=inp) | |||
for t,v in r.chunks(): | |||
add = '' | |||
if len(v) <= 28: | |||
add = ' ' + v.encode('hex') | |||
print >>out, "%s %10d%s" % (t, len(v), add) | |||
def main(argv=None): | |||
import sys | |||
if argv is None: | |||
argv = sys.argv | |||
arg = argv[1:] | |||
if len(arg) > 0: | |||
f = open(arg[0], 'rb') | |||
else: | |||
f = sys.stdin | |||
return list(sys.stdout, f) | |||
if __name__ == '__main__': | |||
main() |
@@ -0,0 +1,151 @@ | |||
#!/usr/bin/env python | |||
# $URL: http://pypng.googlecode.com/svn/trunk/code/texttopng $ | |||
# $Rev: 132 $ | |||
# Script to renders text as a PNG image. | |||
from array import array | |||
import itertools | |||
font = { | |||
32: '0000000000000000', | |||
33: '0010101010001000', | |||
34: '0028280000000000', | |||
35: '0000287c287c2800', | |||
36: '00103c5038147810', | |||
37: '0000644810244c00', | |||
38: '0020502054483400', | |||
39: '0010100000000000', | |||
40: '0008101010101008', | |||
41: '0020101010101020', | |||
42: '0010543838541000', | |||
43: '000010107c101000', | |||
44: '0000000000301020', | |||
45: '000000007c000000', | |||
46: '0000000000303000', | |||
47: '0000040810204000', | |||
48: '0038445454443800', | |||
49: '0008180808080800', | |||
50: '0038043840407c00', | |||
51: '003c041804043800', | |||
52: '00081828487c0800', | |||
53: '0078407804047800', | |||
54: '0038407844443800', | |||
55: '007c040810101000', | |||
56: '0038443844443800', | |||
57: '0038443c04040400', | |||
58: '0000303000303000', | |||
59: '0000303000301020', | |||
60: '0004081020100804', | |||
61: '0000007c007c0000', | |||
62: '0040201008102040', | |||
63: '0038440810001000', | |||
64: '00384c545c403800', | |||
65: '0038447c44444400', | |||
66: '0078447844447800', | |||
67: '0038444040443800', | |||
68: '0070484444487000', | |||
69: '007c407840407c00', | |||
70: '007c407840404000', | |||
71: '003844405c443c00', | |||
72: '0044447c44444400', | |||
73: '0038101010103800', | |||
74: '003c040404443800', | |||
75: '0044487048444400', | |||
76: '0040404040407c00', | |||
77: '006c545444444400', | |||
78: '004464544c444400', | |||
79: '0038444444443800', | |||
80: '0078447840404000', | |||
81: '0038444444443c02', | |||
82: '0078447844444400', | |||
83: '0038403804047800', | |||
84: '007c101010101000', | |||
85: '0044444444443c00', | |||
86: '0044444444281000', | |||
87: '0044445454543800', | |||
88: '0042241818244200', | |||
89: '0044443810101000', | |||
90: '007c081020407c00', | |||
91: '0038202020202038', | |||
92: '0000402010080400', | |||
93: '0038080808080838', | |||
94: '0010284400000000', | |||
95: '000000000000fe00', | |||
96: '0040200000000000', | |||
97: '000038043c443c00', | |||
98: '0040784444447800', | |||
99: '0000384040403800', | |||
100: '00043c4444443c00', | |||
101: '000038447c403c00', | |||
102: '0018203820202000', | |||
103: '00003c44443c0438', | |||
104: '0040784444444400', | |||
105: '0010003010101000', | |||
106: '0010003010101020', | |||
107: '0040404870484400', | |||
108: '0030101010101000', | |||
109: '0000385454444400', | |||
110: '0000784444444400', | |||
111: '0000384444443800', | |||
112: '0000784444784040', | |||
113: '00003c44443c0406', | |||
114: '00001c2020202000', | |||
115: '00003c4038047800', | |||
116: '0020203820201800', | |||
117: '0000444444443c00', | |||
118: '0000444444281000', | |||
119: '0000444454543800', | |||
120: '0000442810284400', | |||
121: '00004444443c0438', | |||
122: '00007c0810207c00', | |||
123: '0018202060202018', | |||
124: '0010101000101010', | |||
125: '003008080c080830', | |||
126: '0020540800000000', | |||
} | |||
def char(i): | |||
"""Get image data for the character `i` (a one character string). | |||
Returned as a list of rows. Each row is a tuple containing the | |||
packed pixels. | |||
""" | |||
i = ord(i) | |||
if i not in font: | |||
return [(0,)]*8 | |||
return map(lambda row: (ord(row),), font[i].decode('hex')) | |||
def texttoraster(m): | |||
"""Convert string *m* to a raster image (by rendering it using the | |||
font in *font*). A triple of (*width*, *height*, *pixels*) is | |||
returned; *pixels* is in boxed row packed pixel format. | |||
""" | |||
# Assumes monospaced font. | |||
x = 8*len(m) | |||
y = 8 | |||
return x,y,itertools.imap(lambda row: itertools.chain(*row), | |||
zip(*map(char, m))) | |||
def render(message, out): | |||
import png | |||
x,y,pixels = texttoraster(message) | |||
w = png.Writer(x, y, greyscale=True, bitdepth=1) | |||
w.write_packed(out, pixels) | |||
out.flush() | |||
def main(argv=None): | |||
import sys | |||
if argv is None: | |||
argv = sys.argv | |||
if len(argv) > 1: | |||
message = argv[1] | |||
else: | |||
message = sys.stdin.read() | |||
render(message, sys.stdout) | |||
if __name__ == '__main__': | |||
main() |
@@ -0,0 +1,463 @@ | |||
# 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/. | |||
# Build a comm application (Mozilla calendar, mail or suite). | |||
# | |||
# To build a tree, | |||
# 1. hg clone http://hg.mozilla.org/comm-central comm | |||
# 2. cd comm | |||
# 3. python client.py checkout | |||
# 4. create your .mozconfig file with | |||
# ac_add_options --enable-application=suite | |||
# (or mail, or calendar) | |||
# 5. gmake -f client.mk | |||
# | |||
# Other targets (gmake -f client.mk [targets...]), | |||
# build | |||
# clean | |||
# distclean | |||
# | |||
# See http://developer.mozilla.org/en/Build_Documentation for | |||
# more information. | |||
# | |||
# Options: | |||
# MOZ_BUILD_PROJECTS - Build multiple projects in subdirectories | |||
# of MOZ_OBJDIR | |||
# MOZ_OBJDIR - Destination object directory | |||
# MOZ_MAKE_FLAGS - Flags to pass to $(MAKE) | |||
# MOZ_PREFLIGHT_ALL } - Makefiles to run before any project in | |||
# MOZ_PREFLIGHT } MOZ_BUILD_PROJECTS, before each project, after | |||
# MOZ_POSTFLIGHT } each project, and after all projects; these | |||
# MOZ_POSTFLIGHT_ALL } variables contain space-separated lists | |||
# MOZ_UNIFY_BDATE - Set to use the same bdate for each project in | |||
# MOZ_BUILD_PROJECTS | |||
# | |||
####################################################################### | |||
# Defines | |||
comma := , | |||
CWD := $(CURDIR) | |||
ifneq (1,$(words $(CWD))) | |||
$(error The platform directory cannot be located in a path with spaces.) | |||
endif | |||
ifeq "$(CWD)" "/" | |||
CWD := /. | |||
endif | |||
ifndef TOPSRCDIR | |||
ifeq (,$(wildcard client.mk)) | |||
TOPSRCDIR := $(patsubst %/,%,$(dir $(MAKEFILE_LIST))) | |||
else | |||
TOPSRCDIR := $(CWD) | |||
endif | |||
endif | |||
SH := /bin/sh | |||
PERL ?= perl | |||
PYTHON ?= $(shell which python2.7 > /dev/null 2>&1 && echo python2.7 || echo python) | |||
CONFIG_GUESS_SCRIPT := $(wildcard $(TOPSRCDIR)/build/autoconf/config.guess) | |||
ifdef CONFIG_GUESS_SCRIPT | |||
CONFIG_GUESS := $(shell $(CONFIG_GUESS_SCRIPT)) | |||
endif | |||
#################################### | |||
# Sanity checks | |||
# Windows checks. | |||
ifneq (,$(findstring mingw,$(CONFIG_GUESS))) | |||
# check for CRLF line endings | |||
ifneq (0,$(shell $(PERL) -e 'binmode(STDIN); while (<STDIN>) { if (/\r/) { print "1"; exit } } print "0"' < $(TOPSRCDIR)/client.mk)) | |||
$(error This source tree appears to have Windows-style line endings. To \ | |||
convert it to Unix-style line endings, check \ | |||
"https://developer.mozilla.org/en-US/docs/Developer_Guide/Mozilla_build_FAQ\#Win32-specific_questions" \ | |||
for a workaround of this issue.) | |||
endif | |||
endif | |||
#################################### | |||
# Load mozconfig Options | |||
# See build pages, http://www.mozilla.org/build/ for how to set up mozconfig. | |||
MOZCONFIG_LOADER := build/autoconf/mozconfig2client-mk | |||
define CR | |||
endef | |||
# As $(shell) doesn't preserve newlines, use sed to replace them with an | |||
# unlikely sequence (||), which is then replaced back to newlines by make | |||
# before evaluation. $(shell) replacing newlines with spaces, || is always | |||
# followed by a space (since sed doesn't remove newlines), except on the | |||
# last line, so replace both '|| ' and '||'. | |||
# Also, make MOZ_PGO available to mozconfig when passed on make command line. | |||
MOZCONFIG_CONTENT := $(subst ||,$(CR),$(subst || ,$(CR),$(shell MOZ_PGO=$(MOZ_PGO) $(TOPSRCDIR)/$(MOZCONFIG_LOADER) $(TOPSRCDIR) | sed 's/$$/||/'))) | |||
$(eval $(MOZCONFIG_CONTENT)) | |||
export FOUND_MOZCONFIG | |||
# As '||' was used as a newline separator, it means it's not occurring in | |||
# lines themselves. It can thus safely be used to replaces normal spaces, | |||
# to then replace newlines with normal spaces. This allows to get a list | |||
# of mozconfig output lines. | |||
MOZCONFIG_OUT_LINES := $(subst $(CR), ,$(subst $(NULL) $(NULL),||,$(MOZCONFIG_CONTENT))) | |||
# Filter-out comments from those lines. | |||
START_COMMENT = \# | |||
MOZCONFIG_OUT_FILTERED := $(filter-out $(START_COMMENT)%,$(MOZCONFIG_OUT_LINES)) | |||
ifdef MOZ_PGO | |||
export MOZ_PGO | |||
endif | |||
# Automatically add -jN to make flags if not defined. N defaults to number of cores. | |||
ifeq (,$(findstring -j,$(MOZ_MAKE_FLAGS))) | |||
cores=$(shell $(PYTHON) -c 'import multiprocessing; print(multiprocessing.cpu_count())') | |||
MOZ_MAKE_FLAGS += -j$(cores) | |||
endif | |||
ifndef MOZ_OBJDIR | |||
MOZ_OBJDIR = obj-$(CONFIG_GUESS) | |||
else | |||
# On Windows Pymake builds check MOZ_OBJDIR doesn't start with "/" | |||
ifneq (,$(findstring mingw,$(CONFIG_GUESS))) | |||
ifeq (1_a,$(.PYMAKE)_$(firstword a$(subst /, ,$(MOZ_OBJDIR)))) | |||
$(error For Windows Pymake builds, MOZ_OBJDIR must be a Windows [and not MSYS] style path.) | |||
endif | |||
endif | |||
endif | |||
ifdef MOZ_BUILD_PROJECTS | |||
ifdef MOZ_CURRENT_PROJECT | |||
OBJDIR = $(MOZ_OBJDIR)/$(MOZ_CURRENT_PROJECT) | |||
MOZ_MAKE = $(MAKE) $(MOZ_MAKE_FLAGS) -C $(OBJDIR) | |||
BUILD_PROJECT_ARG = MOZ_BUILD_APP=$(MOZ_CURRENT_PROJECT) | |||
else | |||
OBJDIR = $(error Cannot find the OBJDIR when MOZ_CURRENT_PROJECT is not set.) | |||
MOZ_MAKE = $(error Cannot build in the OBJDIR when MOZ_CURRENT_PROJECT is not set.) | |||
endif | |||
else # MOZ_BUILD_PROJECTS | |||
OBJDIR = $(MOZ_OBJDIR) | |||
MOZ_MAKE = $(MAKE) $(MOZ_MAKE_FLAGS) -C $(OBJDIR) | |||
endif # MOZ_BUILD_PROJECTS | |||
# If we have a MOZ_OBJDIR that's set from the environment, ensure that it is an | |||
# absolute path. | |||
ifdef MOZ_OBJDIR | |||
MOZ_OBJDIR := $(shell $(PYTHON) -c "import os.path; print(os.path.join(\"$(TOPSRCDIR)\", \"$(MOZ_OBJDIR)\").replace('\\\\','/'))") | |||
endif | |||
# 'configure' scripts generated by autoconf. | |||
CONFIGURES := $(TOPSRCDIR)/configure | |||
CONFIGURES += $(TOPSRCDIR)/platform/configure | |||
CONFIGURES += $(TOPSRCDIR)/platform/js/src/configure | |||
# Make targets that are going to be passed to the real build system | |||
OBJDIR_TARGETS = install export libs clean realclean distclean maybe_clobber_profiledbuild upload sdk installer package package-compare stage-package source-package l10n-check automation/build | |||
####################################################################### | |||
# Rules | |||
# The default rule is build | |||
build:: | |||
# Define mkdir | |||
include $(TOPSRCDIR)/config/makefiles/makeutils.mk | |||
include $(TOPSRCDIR)/config/makefiles/autotargets.mk | |||
# Create a makefile containing the mk_add_options values from mozconfig, | |||
# but only do so when OBJDIR is defined (see further above). | |||
ifdef MOZ_BUILD_PROJECTS | |||
ifdef MOZ_CURRENT_PROJECT | |||
WANT_MOZCONFIG_MK = 1 | |||
else | |||
WANT_MOZCONFIG_MK = | |||
endif | |||
else | |||
WANT_MOZCONFIG_MK = 1 | |||
endif | |||
ifdef WANT_MOZCONFIG_MK | |||
# For now, only output "export" lines from mozconfig2client-mk output. | |||
MOZCONFIG_MK_LINES := $(filter export||%,$(MOZCONFIG_OUT_LINES)) | |||
$(OBJDIR)/.mozconfig.mk: $(FOUND_MOZCONFIG) $(call mkdir_deps,$(OBJDIR)) | |||
$(if $(MOZCONFIG_MK_LINES),( $(foreach line,$(MOZCONFIG_MK_LINES), echo "$(subst ||, ,$(line))";) )) > $@ | |||
ifdef MOZ_CURRENT_PROJECT | |||
echo export MOZ_CURRENT_PROJECT=$(MOZ_CURRENT_PROJECT) >> $@ | |||
endif | |||
# Include that makefile so that it is created. This should not actually change | |||
# the environment since MOZCONFIG_CONTENT, which MOZCONFIG_OUT_LINES derives | |||
# from, has already been eval'ed. | |||
include $(OBJDIR)/.mozconfig.mk | |||
endif | |||
# UPLOAD_EXTRA_FILES is appended to and exported from mozconfig, which makes | |||
# submakes as well as configure add even more to that, so just unexport it | |||
# for submakes to pick it from .mozconfig.mk and for configure to pick it | |||
# from mach environment. | |||
unexport UPLOAD_EXTRA_FILES | |||
# These targets are candidates for auto-running client.py | |||
ifeq (01,$(MAKELEVEL)$(if $(ALWAYS_RUN_CLIENT_PY),1,)) | |||
build profiledbuild configure:: run_client_py | |||
$(MAKE) -f $(TOPSRCDIR)/client.mk $@ | |||
else | |||
# Print out any options loaded from mozconfig. | |||
all build clean distclean export libs install realclean:: | |||
ifneq (,$(strip $(MOZCONFIG_OUT_FILTERED))) | |||
$(info Adding client.mk options from $(FOUND_MOZCONFIG):) | |||
$(foreach line,$(MOZCONFIG_OUT_FILTERED),$(info $(NULL) $(NULL) $(NULL) $(NULL) $(subst ||, ,$(line)))) | |||
endif | |||
# Windows equivalents | |||
build_all: build | |||
clobber clobber_all: clean | |||
# Do everything from scratch | |||
everything: clean build | |||
#################################### | |||
# Profile-Guided Optimization | |||
# To use this, you should set the following variables in your mozconfig | |||
# mk_add_options PROFILE_GEN_SCRIPT=/path/to/profile-script | |||
# | |||
# The profile script should exercise the functionality to be included | |||
# in the profile feedback. | |||
# | |||
# This is up here, outside of the MOZ_CURRENT_PROJECT logic so that this | |||
# is usable in multi-pass builds, where you might not have a runnable | |||
# application until all the build passes and postflight scripts have run. | |||
ifdef MOZ_OBJDIR | |||
PGO_OBJDIR = $(MOZ_OBJDIR) | |||
else | |||
PGO_OBJDIR := $(TOPSRCDIR) | |||
endif | |||
profiledbuild:: | |||
$(MAKE) -f $(TOPSRCDIR)/client.mk build MOZ_PROFILE_GENERATE=1 | |||
$(MAKE) -C $(PGO_OBJDIR) stage-package | |||
OBJDIR=${PGO_OBJDIR} $(PROFILE_GEN_SCRIPT) | |||
$(MAKE) -f $(TOPSRCDIR)/client.mk maybe_clobber_profiledbuild | |||
$(MAKE) -f $(TOPSRCDIR)/client.mk build MOZ_PROFILE_USE=1 | |||
##################################################### | |||
# Build date unification | |||
ifdef MOZ_UNIFY_BDATE | |||
ifndef MOZ_BUILD_DATE | |||
ifdef MOZ_BUILD_PROJECTS | |||
MOZ_BUILD_DATE = $(shell $(PYTHON) $(TOPSRCDIR)/platform/build/variables.py buildid_header | awk '{print $$3}') | |||
export MOZ_BUILD_DATE | |||
endif | |||
endif | |||
endif | |||
##################################################### | |||
# Preflight, before building any project | |||
build preflight_all:: | |||
ifeq (,$(MOZ_CURRENT_PROJECT)$(if $(MOZ_PREFLIGHT_ALL),,1)) | |||
# Don't run preflight_all for individual projects in multi-project builds | |||
# (when MOZ_CURRENT_PROJECT is set.) | |||
ifndef MOZ_BUILD_PROJECTS | |||
# Building a single project, OBJDIR is usable. | |||
set -e; \ | |||
for mkfile in $(MOZ_PREFLIGHT_ALL); do \ | |||
$(MAKE) -f $(TOPSRCDIR)/$$mkfile preflight_all TOPSRCDIR=$(TOPSRCDIR) OBJDIR=$(OBJDIR) MOZ_OBJDIR=$(MOZ_OBJDIR); \ | |||
done | |||
else | |||
# OBJDIR refers to the project-specific OBJDIR, which is not available at | |||
# this point when building multiple projects. Only MOZ_OBJDIR is available. | |||
set -e; \ | |||
for mkfile in $(MOZ_PREFLIGHT_ALL); do \ | |||
$(MAKE) -f $(TOPSRCDIR)/$$mkfile preflight_all TOPSRCDIR=$(TOPSRCDIR) MOZ_OBJDIR=$(MOZ_OBJDIR) MOZ_BUILD_PROJECTS='$(MOZ_BUILD_PROJECTS)'; \ | |||
done | |||
endif | |||
endif | |||
# If we're building multiple projects, but haven't specified which project, | |||
# loop through them. | |||
ifeq (,$(MOZ_CURRENT_PROJECT)$(if $(MOZ_BUILD_PROJECTS),,1)) | |||
configure build preflight postflight $(OBJDIR_TARGETS):: | |||
set -e; \ | |||
for app in $(MOZ_BUILD_PROJECTS); do \ | |||
$(MAKE) -f $(TOPSRCDIR)/client.mk $@ MOZ_CURRENT_PROJECT=$$app; \ | |||
done | |||
else | |||
# MOZ_CURRENT_PROJECT: either doing a single-project build, or building an | |||
# individual project in a multi-project build. | |||
#################################### | |||
# Configure | |||
MAKEFILE = $(wildcard $(OBJDIR)/Makefile) | |||
CONFIG_STATUS = $(wildcard $(OBJDIR)/config.status) | |||
CONFIG_CACHE = $(wildcard $(OBJDIR)/config.cache) | |||
EXTRA_CONFIG_DEPS := \ | |||
$(TOPSRCDIR)/aclocal.m4 \ | |||
$(TOPSRCDIR)/platform/aclocal.m4 \ | |||
$(TOPSRCDIR)/platform/old-configure.in \ | |||
$(wildcard $(TOPSRCDIR)/platform/build/autoconf/*.m4) \ | |||
$(TOPSRCDIR)/platform/js/src/aclocal.m4 \ | |||
$(TOPSRCDIR)/platform/js/src/old-configure.in \ | |||
$(NULL) | |||
$(CONFIGURES): %: %.in $(EXTRA_CONFIG_DEPS) | |||
@echo Generating $@ | |||
sed '1,/^divert/d' $< > $@ | |||
chmod +x $@ | |||
CONFIG_STATUS_DEPS := \ | |||
$(wildcard $(CONFIGURES)) \ | |||
$(wildcard $(TOPSRCDIR)/platform/nsprpub/configure) \ | |||
$(wildcard $(TOPSRCDIR)/platform/config/milestone.txt) \ | |||
$(wildcard $(TOPSRCDIR)/platform/ldap/sdks/c-sdk/configure) \ | |||
$(wildcard $(addsuffix confvars.sh,$(wildcard $(TOPSRCDIR)/*/))) \ | |||
$(NULL) | |||
CONFIGURE_ENV_ARGS += \ | |||
MAKE='$(MAKE)' \ | |||
$(NULL) | |||
# configure uses the program name to determine @srcdir@. Calling it without | |||
# $(TOPSRCDIR) will set @srcdir@ to '.'; otherwise, it is set to the full | |||
# path of $(TOPSRCDIR). | |||
ifeq ($(TOPSRCDIR),$(OBJDIR)) | |||
CONFIGURE = ./configure | |||
else | |||
CONFIGURE = $(TOPSRCDIR)/configure | |||
endif | |||
configure-files: $(CONFIGURES) | |||
configure-preqs = \ | |||
configure-files \ | |||
$(call mkdir_deps,$(OBJDIR)) \ | |||
$(if $(MOZ_BUILD_PROJECTS),$(call mkdir_deps,$(MOZ_OBJDIR))) \ | |||
$(NULL) | |||
configure:: $(configure-preqs) | |||
@echo cd $(OBJDIR); | |||
@echo $(CONFIGURE) $(CONFIGURE_ARGS) | |||
@cd $(OBJDIR) && $(BUILD_PROJECT_ARG) $(CONFIGURE_ENV_ARGS) $(CONFIGURE) $(CONFIGURE_ARGS) \ | |||
|| ( echo '*** Fix above errors and then restart with\ | |||
"$(MAKE) -f client.mk build"' && exit 1 ) | |||
@touch $(OBJDIR)/Makefile | |||
ifneq (,$(MAKEFILE)) | |||
$(OBJDIR)/Makefile: $(OBJDIR)/config.status | |||
$(OBJDIR)/config.status: $(CONFIG_STATUS_DEPS) | |||
else | |||
$(OBJDIR)/Makefile: $(CONFIG_STATUS_DEPS) | |||
endif | |||
@$(MAKE) -f $(TOPSRCDIR)/client.mk configure | |||
ifneq (,$(CONFIG_STATUS)) | |||
$(OBJDIR)/config/autoconf.mk: $(TOPSRCDIR)/config/autoconf.mk.in | |||
$(PYTHON) $(OBJDIR)/config.status -n --file=$(OBJDIR)/config/autoconf.mk | |||
endif | |||
#################################### | |||
# Preflight | |||
build preflight:: | |||
ifdef MOZ_PREFLIGHT | |||
set -e; \ | |||
for mkfile in $(MOZ_PREFLIGHT); do \ | |||
$(MAKE) -f $(TOPSRCDIR)/$$mkfile preflight TOPSRCDIR=$(TOPSRCDIR) OBJDIR=$(OBJDIR) MOZ_OBJDIR=$(MOZ_OBJDIR); \ | |||
done | |||
endif | |||
#################################### | |||
# Build it | |||
build:: $(OBJDIR)/Makefile $(OBJDIR)/config.status | |||
+$(MOZ_MAKE) | |||
#################################### | |||
# Other targets | |||
# Pass these target onto the real build system | |||
$(OBJDIR_TARGETS):: $(OBJDIR)/Makefile $(OBJDIR)/config.status | |||
+$(MOZ_MAKE) $@ | |||
#################################### | |||
# Postflight | |||
build postflight:: | |||
ifdef MOZ_POSTFLIGHT | |||
set -e; \ | |||
for mkfile in $(MOZ_POSTFLIGHT); do \ | |||
$(MAKE) -f $(TOPSRCDIR)/$$mkfile postflight TOPSRCDIR=$(TOPSRCDIR) OBJDIR=$(OBJDIR) MOZ_OBJDIR=$(MOZ_OBJDIR); \ | |||
done | |||
endif | |||
endif # MOZ_CURRENT_PROJECT | |||
endif # RAN_CLIENT_PY | |||
#################################### | |||
# Postflight, after building all projects | |||
build postflight_all:: | |||
ifeq (,$(MOZ_CURRENT_PROJECT)$(if $(MOZ_POSTFLIGHT_ALL),,1)) | |||
# Don't run postflight_all for individual projects in multi-project builds | |||
# (when MOZ_CURRENT_PROJECT is set.) | |||
ifndef MOZ_BUILD_PROJECTS | |||
# Building a single project, OBJDIR is usable. | |||
set -e; \ | |||
for mkfile in $(MOZ_POSTFLIGHT_ALL); do \ | |||
$(MAKE) -f $(TOPSRCDIR)/$$mkfile postflight_all TOPSRCDIR=$(TOPSRCDIR) OBJDIR=$(OBJDIR) MOZ_OBJDIR=$(MOZ_OBJDIR); \ | |||
done | |||
else | |||
# OBJDIR refers to the project-specific OBJDIR, which is not available at | |||
# this point when building multiple projects. Only MOZ_OBJDIR is available. | |||
set -e; \ | |||
for mkfile in $(MOZ_POSTFLIGHT_ALL); do \ | |||
$(MAKE) -f $(TOPSRCDIR)/$$mkfile postflight_all TOPSRCDIR=$(TOPSRCDIR) MOZ_OBJDIR=$(MOZ_OBJDIR) MOZ_BUILD_PROJECTS='$(MOZ_BUILD_PROJECTS)'; \ | |||
done | |||
endif | |||
endif | |||
cleansrcdir: | |||
@cd $(TOPSRCDIR); \ | |||
if [ -f Makefile ]; then \ | |||
$(MAKE) distclean ; \ | |||
else \ | |||
echo 'Removing object files from srcdir...'; \ | |||
rm -fr `find . -type d \( -name .deps -print -o -name CVS \ | |||
-o -exec test ! -d {}/CVS \; \) -prune \ | |||
-o \( -name '*.[ao]' -o -name '*.so' \) -type f -print`; \ | |||
build/autoconf/clean-config.sh; \ | |||
fi; | |||
echo-variable-%: | |||
@echo $($*) | |||
# This makefile doesn't support parallel execution. It does pass | |||
# MOZ_MAKE_FLAGS to sub-make processes, so they will correctly execute | |||
# in parallel. | |||
.NOTPARALLEL: | |||
.PHONY: checkout co real_checkout build profiledbuild cleansrcdir pull_all build_all clobber clobber_all pull_and_build_all everything configure preflight_all preflight postflight postflight_all $(OBJDIR_TARGETS) |
@@ -0,0 +1,16 @@ | |||
# This file is normally included by autoconf.mk, but it is also used | |||
# directly in python/mozbuild/mozbuild/base.py for gmake validation. | |||
# We thus use INCLUDED_AUTOCONF_MK to enable/disable some parts depending | |||
# whether a normal build is happening or whether the check is running. | |||
# When mach wants to know if we're to use mozmake, it runs: | |||
# make -f topsrcdir/config/baseconfig.mk | |||
# The first word of MAKEFILE_LIST is the main file we're running. Grabbing the | |||
# parent of that directory therefore gets us the topsrcdir of comm-central, | |||
# whence we get the mozilla directory to run the "real" baseconfig.mk logic. | |||
ifndef INCLUDED_AUTOCONF_MK | |||
topsrcdir := $(dir $(firstword $(MAKEFILE_LIST))).. | |||
endif | |||
MOZILLA_SRCDIR = $(topsrcdir)/platform | |||
include $(MOZILLA_SRCDIR)/config/baseconfig.mk |
@@ -0,0 +1,7 @@ | |||
# | |||
# 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/. | |||
# Just use mozilla-central's copy of config.mk now. | |||
include $(MOZILLA_DIR)/config/config.mk |
@@ -0,0 +1,94 @@ | |||
# -*- makefile -*- | |||
# vim:set ts=8 sw=8 sts=8 noet: | |||
# | |||
# 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/. | |||
# | |||
ifndef INCLUDED_AUTOTARGETS_MK #{ | |||
# Conditional does not wrap the entire file so multiple | |||
# includes will be able to accumulate dependencies. | |||
########################################################################### | |||
# AUTO_DEPS - A list of deps/targets drived from other macros. | |||
########################################################################### | |||
MKDIR ?= mkdir -p | |||
TOUCH ?= touch | |||
# declare for local use, rules.mk may not have been loaded | |||
space = $(NULL) $(NULL) | |||
# Deps will be considered intermediate when used as a pre-requisite for | |||
# wildcard targets. Inhibit their removal, mkdir -p is a standalone op. | |||
.PRECIOUS: %/.mkdir.done | |||
######################### | |||
##---] FUNCTIONS [---## | |||
######################### | |||
# Squeeze can be overzealous, restore root for abspath | |||
getPathPrefix =$(if $(filter /%,$(1)),/) | |||
# Squeeze '//' from the path, easily created by string functions | |||
_slashSqueeze =$(foreach val,$(getargv),$(call getPathPrefix,$(val))$(subst $(space),/,$(strip $(subst /,$(space),$(val))))) | |||
# Squeeze extraneous directory slashes from the path | |||
# o protect embedded spaces within the path | |||
# o replace //+ sequences with / | |||
slash_strip = \ | |||
$(strip \ | |||
$(subst <--[**]-->,$(space),\ | |||
$(call _slashSqueeze,\ | |||
$(subst $(space),<--[**]-->,$(1))\ | |||
))) | |||
# Extract directory path from a dependency file. | |||
mkdir_stem =$(foreach val,$(getargv),$(subst /.mkdir.done,$(NULL),$(val))) | |||
## Generate timestamp file for threadsafe directory creation | |||
mkdir_deps =$(foreach dir,$(getargv),$(call slash_strip,$(dir)/.mkdir.done)) | |||
####################### | |||
##---] TARGETS [---## | |||
####################### | |||
%/.mkdir.done: # mkdir -p -p => mkdir -p | |||
$(subst $(space)-p,$(null),$(MKDIR)) -p '$(dir $@)' | |||
# Make the timestamp old enough for not being a problem with symbolic links | |||
# targets depending on it. Use Jan 3, 1980 to accomodate any timezone where | |||
# 198001010000 would translate to something older than FAT epoch. | |||
@$(TOUCH) -t 198001030000 '$@' | |||
# A handful of makefiles are attempting "mkdir dot". | |||
# tbpl/valgrind builds are using this target | |||
# https://bugzilla.mozilla.org/show_bug.cgi?id=837754 | |||
.mkdir.done: | |||
@echo 'WARNING: $(MKDIR) -dot- requested by $(MAKE) -C $(CURDIR) $(MAKECMDGOALS)' | |||
@$(TOUCH) -t 198001030000 '$@' | |||
INCLUDED_AUTOTARGETS_MK = 1 | |||
endif #} | |||
## Accumulate deps and cleanup | |||
ifneq (,$(GENERATED_DIRS)) | |||
GENERATED_DIRS := $(strip $(sort $(GENERATED_DIRS))) | |||
tmpauto :=$(call mkdir_deps,GENERATED_DIRS) | |||
GENERATED_DIRS_DEPS +=$(tmpauto) | |||
GARBAGE_DIRS +=$(GENERATED_DIRS) | |||
endif | |||
################################################################# | |||
# One ring/dep to rule them all: | |||
# config/rules.mk::all target is available by default | |||
# Add $(AUTO_DEPS) as an explicit target dependency when needed. | |||
################################################################# | |||
AUTO_DEPS +=$(GENERATED_DIRS_DEPS) | |||
AUTO_DEPS := $(strip $(sort $(AUTO_DEPS))) | |||
# Complain loudly if deps have not loaded so getargv != $(NULL) | |||
$(call requiredfunction,getargv) |
@@ -0,0 +1,117 @@ | |||
# -*- makefile -*- | |||
# vim:set ts=8 sw=8 sts=8 noet: | |||
# | |||
# 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/. | |||
## Identify function argument types | |||
istype =$(if $(value ${1}),list,scalar) | |||
isval =$(if $(filter-out list,$(call istype,${1})),true) | |||
isvar =$(if $(filter-out scalar,$(call istype,${1})),true) | |||
# Access up to 9 arguments passed, option needed to emulate $* | |||
# Inline for function expansion, do not use $(call ) | |||
argv =$(strip | |||
argv +=$(if $(1), $(1))$(if $(2), $(2))$(if $(3), $(3))$(if $(4), $(4)) | |||
argv +=$(if $(5), $(5))$(if $(6), $(6))$(if $(7), $(7))$(if $(8), $(8)) | |||
argv +=$(if $(9), $(9)) | |||
argv +=$(if $(10), $(error makeutils.mk::argv can only handle 9 arguments)) | |||
argv +=) | |||
########################################################################### | |||
## Access function args as a simple list, inline within user functions. | |||
## Usage: $(info ** $(call banner,$(getargv))) | |||
## $(call banner,scalar) | |||
## $(call banner,list0 list1 list2) | |||
## $(call banner,ref) ; ref=foo bar tans | |||
## getarglist() would be a more accurate name but is longer to type | |||
getargv = $(if $(call isvar,$(1)),$($(1)),$(argv)) | |||
########################################################################### | |||
# Strip [n] leading options from an argument list. This will allow passing | |||
# extra args to user functions that will not propogate to sub-$(call )'s | |||
# Usage: $(call subargv,2) | |||
subargv =$(wordlist $(1),$(words $(getargv)),$(getargv)) | |||
########################################################################### | |||
# Intent: Display a distinct banner heading in the output stream | |||
# Usage: $(call banner,BUILDING: foo bar tans) | |||
# Debug: | |||
# target-preqs = \ | |||
# $(call banner,target-preqs-BEGIN) \ | |||
# foo bar tans \ | |||
# $(call banner,target-preqs-END) \ | |||
# $(NULL) | |||
# target: $(target-preqs) | |||
banner = \ | |||
$(info ) \ | |||
$(info ***************************************************************************) \ | |||
$(info ** $(getargv)) \ | |||
$(info ***************************************************************************) \ | |||
$(NULL) | |||
##################################################################### | |||
# Intent: Determine if a string or pattern is contained in a list | |||
# Usage: strcmp - $(call if_XinY,clean,$(MAKECMDGOALS)) | |||
# : pattern - $(call if_XinY,clean%,$(MAKECMDGOALS)) | |||
is_XinY =$(filter $(1),$(call subargv,3,$(getargv))) | |||
##################################################################### | |||
# Provide an alternate var to support testing | |||
ifdef MAKEUTILS_UNIT_TEST | |||
mcg_goals=TEST_MAKECMDGOALS | |||
else | |||
mcg_goals=MAKECMDGOALS | |||
endif | |||
# Intent: Conditionals for detecting common/tier target use | |||
isTargetStem = $(sort \ | |||
$(foreach var,$(getargv),\ | |||
$(foreach pat,$(var)% %$(var),\ | |||
$(call is_XinY,$(pat),${$(mcg_goals)})\ | |||
))) | |||
isTargetStemClean = $(call isTargetStem,clean) | |||
isTargetStemExport = $(call isTargetStem,export) | |||
isTargetStemLibs = $(call isTargetStem,libs) | |||
isTargetStemTools = $(call isTargetStem,tools) | |||
################################################## | |||
# Intent: Validation functions / unit test helpers | |||
errorifneq =$(if $(subst $(strip $(1)),$(NULL),$(strip $(2))),$(error expected [$(1)] but found [$(2)])) | |||
# Intent: verify function declaration exists | |||
requiredfunction =$(foreach func,$(1) $(2) $(3) $(4) $(5) $(6) $(7) $(8) $(9),$(if $(value $(func)),$(NULL),$(error required function [$(func)] is unavailable))) | |||
## http://www.gnu.org/software/make/manual/make.html#Call-Function | |||
## Usage: o = $(call map,origin,o map $(MAKE)) | |||
map = $(foreach val,$(2),$(call $(1),$(val))) | |||
## Disable checking for clean targets | |||
ifeq (,$(filter %clean clean%,$(MAKECMDGOALS))) #{ | |||
# Usage: $(call checkIfEmpty,[error|warning] foo NULL bar) | |||
checkIfEmpty =$(foreach var,$(wordlist 2,100,$(argv)),$(if $(strip $($(var))),$(NOP),$(call $(1),Variable $(var) does not contain a value))) | |||
# Usage: $(call errorIfEmpty,foo NULL bar) | |||
errorIfEmpty =$(call checkIfEmpty,error $(argv)) | |||
warnIfEmpty =$(call checkIfEmpty,warning $(argv)) | |||
endif #} | |||
########################################################################### | |||
## Common makefile library loader | |||
########################################################################### | |||
topORerr =$(if $(topsrcdir),$(topsrcdir),$(error topsrcdir is not defined)) | |||
ifdef USE_AUTOTARGETS_MK # mkdir_deps | |||
include $(topORerr)/config/makefiles/autotargets.mk | |||
endif | |||
## copy(src, dst): recursive copy | |||
copy_dir = (cd $(1)/. && $(TAR) $(TAR_CREATE_FLAGS) - .) | (cd $(2)/. && tar -xf -) |
@@ -0,0 +1,25 @@ | |||
# 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 configobj, sys | |||
try: | |||
(file, section, key) = sys.argv[1:] | |||
except ValueError: | |||
print "Usage: printconfigsetting.py <file> <section> <setting>" | |||
sys.exit(1) | |||
c = configobj.ConfigObj(file) | |||
try: | |||
s = c[section] | |||
except KeyError: | |||
print >>sys.stderr, "Section [%s] not found." % section | |||
sys.exit(1) | |||
try: | |||
print s[key] | |||
except KeyError: | |||
print >>sys.stderr, "Key %s not found." % key | |||
sys.exit(1) |
@@ -0,0 +1,9 @@ | |||
# 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/. | |||
ifndef INCLUDED_RULES_MK | |||
include $(topsrcdir)/config/rules.mk | |||
endif | |||
include $(MOZILLA_DIR)/config/recurse.mk |
@@ -0,0 +1,13 @@ | |||
# vim:set ts=8 sw=8 sts=8 noet: | |||
# | |||
# 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/. | |||
# | |||
ifndef topsrcdir | |||
$(error topsrcdir was not set)) | |||
endif | |||
# Use mozilla-central's copy of rules.mk. | |||
include $(topsrcdir)/platform/config/rules.mk |
@@ -0,0 +1,38 @@ | |||
dnl -*- Mode: Autoconf; tab-width: 4; indent-tabs-mode: nil; -*- | |||
dnl vi: set tabstop=4 shiftwidth=4 expandtab: | |||
dnl This Source Code Form is subject to the terms of the Mozilla Public | |||
dnl License, v. 2.0. If a copy of the MPL was not distributed with this | |||
dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
dnl | |||
dnl This is a not-really-autoconf script (which is to say, it's a shell script | |||
dnl that is piped through m4 first) that executes the mozilla-central python | |||
dnl configure, first doing a little bit of processing to handle mozconfig and | |||
dnl the --with-external-source-dir rules. | |||
dnl ======================================================== | |||
divert(0)dnl | |||
#!/bin/sh | |||
SRCDIR=$(dirname $0) | |||
TOPSRCDIR="$SRCDIR" | |||
MOZILLA_SRCDIR="${SRCDIR}/platform" | |||
export OLD_CONFIGURE="${MOZILLA_SRCDIR}"/old-configure | |||
# Ensure the comm-* values are used. | |||
export MOZ_SOURCE_CHANGESET=$(hg -R "$TOPSRCDIR" parent --template="{node}" 2>/dev/null) | |||
export MOZ_SOURCE_REPO=$(hg -R "$TOPSRCDIR" showconfig paths.default 2>/dev/null | sed -e "s/^ssh:/https:/") | |||
# If MOZCONFIG isn't set, use the .mozconfig from the current directory. This | |||
# overrides the lookup in mozilla-central's configure, which looks in the wrong | |||
# directory for this file. | |||
if test -z "$MOZCONFIG" -a -f "$SRCDIR"/.mozconfig; then | |||
export MOZCONFIG="$SRCDIR"/.mozconfig | |||
elif test -z "$MOZCONFIG" -a -f "$SRCDIR"/mozconfig; then | |||
export MOZCONFIG="$SRCDIR"/mozconfig | |||
fi | |||
# Execute the mozilla configure script in the current directory, adding the | |||
# parameter we need to run comm-central. Since the configure script is really | |||
# just a wrapper around invoking a python variant, execute the underlying python | |||
# directly. We use a copy of the underlying configure script to get paths | |||
# correct. | |||
set -- "$@" --with-external-source-dir="$TOPSRCDIR" | |||
which python2.7 > /dev/null && exec python2.7 "$TOPSRCDIR/configure.py" "$@" || exec python "$TOPSRCDIR/configure.py" "$@" |
@@ -0,0 +1,32 @@ | |||
# 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 print_function, unicode_literals | |||
import imp | |||
import os | |||
import sys | |||
base_dir = os.path.abspath(os.path.dirname(__file__)) | |||
sys.path.append(os.path.join(base_dir, 'platform', 'python', 'mozbuild')) | |||
from mozbuild.configure import ConfigureSandbox | |||
# We can't just import config_status since configure is shadowed by this file! | |||
f, pathname, desc = imp.find_module('configure', | |||
[os.path.join(base_dir, 'platform')]) | |||
config_status = imp.load_module('configure', f, pathname, desc).config_status | |||
def main(argv): | |||
config = {} | |||
sandbox = ConfigureSandbox(config, os.environ, argv) | |||
sandbox.run(os.path.join(os.path.dirname(__file__), 'moz.configure')) | |||
if sandbox._help: | |||
return 0 | |||
return config_status(config) | |||
if __name__ == '__main__': | |||
sys.exit(main(sys.argv)) |
@@ -0,0 +1,7 @@ | |||
# vim: set filetype=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 file needs to stay here even if empty so that mach will work, | |||
# specifically commands like mach file-info. |
@@ -0,0 +1,7 @@ | |||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- | |||
# vim: set filetype=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/. | |||
include('platform/moz.configure') |