40 changed files with 7105 additions and 0 deletions
@ -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) |
File diff suppressed because it is too large
Load Diff
@ -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. T |