[Pkg-phototools-commits] [SCM] openimageio branch, upstream, updated. upstream/1.1.3+dfsg0-2-g46e86ea

Matteo F. Vescovi mfv.debian at gmail.com
Thu Jan 31 16:40:06 UTC 2013


The following commit has been merged in the upstream branch:
commit 3e146639f85b6bb0dec60a93e16c9d9e2a2147db
Author: Matteo F. Vescovi <mfv.debian at gmail.com>
Date:   Thu Jan 31 17:22:21 2013 +0100

    Imported Upstream version 1.1.4+dfsg0

diff --git a/CHANGES b/CHANGES
index b93fae4..0a0ee2b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,30 @@
 Changes:
 
 
+Release 1.1.4 (27 Jan 2013)
+---------------------------
+* ImageBufAlgo::make_texture() allows you to do the same thing that
+  maketx does, but from inside an application and without launching a
+  shell invocation of maketx.
+* oiiotool now recognizes --metamatch and --nometamatch arguments which
+  cause metadata names matching (or only info NOT matching) the given
+  regular expression to be printed with --info.
+* oiiotool --zover does z (depth) composites (it's like a regular "over",
+  but uses the z depth at each pixel to determine which of the two images
+  is the foreground and which is the background).
+* ImageBufAlgo::fixNonFinite didn't work properly with 'half' image buffers.
+* Performance improvements when reading and writing images.
+* Fix error when writing tiled 'float' TIFF images, corrupted output.
+  (Could easily happen when using 'maketx' to convert float images into
+  TIFF textures.)
+* Eliminate warnings when compiling with Clang 3.2.
+* New CMake variable "USE_EXTERNAL_TBB" can optionally be set to force use
+  of an external TBB library rather than the embedded one.
+* Additional testsuite tests (doesn't affect users, but makes bugs easier
+  to catch).
+* Fix build problem with SHA1.cpp on some platforms.
+
+
 Release 1.1.3 (9 Jan 2013)
 ---------------------------
 * Build fix: incorrectly named OpenEXR 2.x files.
diff --git a/site/spi/Makefile-bits-arnold b/site/spi/Makefile-bits-arnold
index 6423ec9..57bd057 100644
--- a/site/spi/Makefile-bits-arnold
+++ b/site/spi/Makefile-bits-arnold
@@ -19,7 +19,7 @@ endif
 ## Spinux (current)
 ifeq ($(SP_ARCH), spinux1_x86_64)
     platform=spinux1
-    COMPILER=gcc44m64
+    SPCOMP2_COMPILER=gcc44m64
     INSTALL_SPCOMP2_LOCATION = /shots/spi/home/lib/SpComp2
     # At SPI, we have two "flavors" of spinux.  One is based on Foresight, which
     # uses a special libGL (below).  The other is based on Fedora which uses
@@ -27,11 +27,34 @@ ifeq ($(SP_ARCH), spinux1_x86_64)
     SPINUX_GL_LIB = /usr/lib64/xorg.nvidia.3d/libGL.so
     MY_CMAKE_FLAGS += $(if $(wildcard ${SPINUX_GL_LIB}), -DOPENGL_gl_LIBRARY=${SPINUX_GL_LIB})
     ifeq (${OCIO_PATH},)
-        OCIO_PATH="${INSTALL_SPCOMP2_LOCATION}/OpenColorIO/${platform}-${COMPILER}/v2"
+        OCIO_PATH="${INSTALL_SPCOMP2_LOCATION}/OpenColorIO/${platform}-${SPCOMP2_COMPILER}/v2"
     endif
     ifeq (${FIELD3D_HOME},)
-        FIELD3D_HOME="${INSTALL_SPCOMP2_LOCATION}/Field3D/${platform}-${COMPILER}/v227"
+        FIELD3D_HOME="${INSTALL_SPCOMP2_LOCATION}/Field3D/${platform}-${SPCOMP2_COMPILER}/v227"
     endif
+
+    ## custom compiler: clang
+    ifeq (${LLVM_DIRECTORY},)
+        LLVM_DIRECTORY := /shots/spi/home/lib/arnold/spinux1/llvm_3.2
+    endif
+    ifeq (${COMPILER}, clang)
+        MY_CMAKE_FLAGS += \
+           -DCMAKE_C_COMPILER=${LLVM_DIRECTORY}/bin/clang \
+           -DCMAKE_CXX_COMPILER=${LLVM_DIRECTORY}/bin/clang++
+    endif
+
+    ifeq (${COMPILER}, gcc463)
+    MY_CMAKE_FLAGS += \
+         -DCMAKE_C_COMPILER=/net/soft_scratch/apps/arnold/tools/gcc-4.6.3-test/bin/gcc \
+         -DCMAKE_CXX_COMPILER=/net/soft_scratch/apps/arnold/tools/gcc-4.6.3-test/bin/g++
+    endif
+    ifeq (${COMPILER}, gcc470)
+    MY_CMAKE_FLAGS += \
+         -DCMAKE_C_COMPILER=/net/soft_scratch/apps/arnold/tools/gcc-4.7.0-test/bin/gcc \
+         -DCMAKE_CXX_COMPILER=/net/soft_scratch/apps/arnold/tools/gcc-4.7.0-test/bin/g++
+    endif
+
+
     MY_CMAKE_FLAGS += \
 	  -DILMBASE_CUSTOM=1 \
 	  -DILMBASE_CUSTOM_LIBRARIES="SpiImath SpiHalf SpiIlmThread SpiIex" \
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 46a85cd..d0b2849 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,7 +3,7 @@ project (OpenImageIO)
 # Release version of the library
 set (OIIO_VERSION_MAJOR 1)
 set (OIIO_VERSION_MINOR 1)
-set (OIIO_VERSION_PATCH 3)
+set (OIIO_VERSION_PATCH 4)
 
 cmake_minimum_required (VERSION 2.6)
 if (NOT CMAKE_VERSION VERSION_LESS 2.8.4)
@@ -83,6 +83,8 @@ set (NOTHREADS OFF CACHE BOOL "Compile with no threads or locking")
 set (PYTHON_VERSION 2.6)
 set (USE_EXTERNAL_PUGIXML OFF CACHE BOOL
      "Use an externally built shared library version of the pugixml library")
+set (USE_EXTERNAL_TBB OFF CACHE BOOL
+     "Use system TBB library instead of bundled.")
 
 set (SOVERSION ${OIIO_VERSION_MAJOR}.${OIIO_VERSION_MINOR}
      CACHE STRING "Set the SO version in the SO name of the output library")
@@ -311,10 +313,11 @@ if (DEFINED CMAKE_VERSION AND NOT CMAKE_VERSION VERSION_LESS 2.8)
 endif()
 
 # List all the individual testsuite tests here:
-oiio_add_tests (ico gpsread misnamed-file nonwhole-tiles
+oiio_add_tests (gpsread misnamed-file nonwhole-tiles
                 oiiotool oiiotool-composite oiiotool-fixnan 
                 perchannel
-                sgi rla psd dpx png
+                dpx ico png psd rla sgi
+                maketx
                 texture-blurtube texture-fill texture-filtersize
                 texture-gray texture-grid
                 texture-missing texture-overscan
@@ -333,6 +336,8 @@ oiio_add_tests (tiff-suite tiff-depths
     IMAGEDIR libtiffpic
     URL http://www.remotesensing.org/libtiff/images.html)
 
+oiio_add_tests (tiff-misc)
+
 oiio_add_tests (openexr-suite openexr-multires openexr-chroma openexr-v2
     IMAGEDIR openexr-images
     URL http://www.openexr.com/downloads.html)
diff --git a/src/cmake/modules/FindTBB.cmake b/src/cmake/modules/FindTBB.cmake
new file mode 100644
index 0000000..f9e3e0f
--- /dev/null
+++ b/src/cmake/modules/FindTBB.cmake
@@ -0,0 +1,283 @@
+# Locate Intel Threading Building Blocks include paths and libraries
+# FindTBB.cmake can be found at https://code.google.com/p/findtbb/
+# Written by Hannes Hofmann <hannes.hofmann _at_ informatik.uni-erlangen.de>
+# Improvements by Gino van den Bergen <gino _at_ dtecta.com>,
+#   Florian Uhlig <F.Uhlig _at_ gsi.de>,
+#   Jiri Marsik <jiri.marsik89 _at_ gmail.com>
+
+# The MIT License
+#
+# Copyright (c) 2011 Hannes Hofmann
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# GvdB: This module uses the environment variable TBB_ARCH_PLATFORM which defines architecture and compiler.
+#   e.g. "ia32/vc8" or "em64t/cc4.1.0_libc2.4_kernel2.6.16.21"
+#   TBB_ARCH_PLATFORM is set by the build script tbbvars[.bat|.sh|.csh], which can be found
+#   in the TBB installation directory (TBB_INSTALL_DIR).
+#
+# GvdB: Mac OS X distribution places libraries directly in lib directory.
+#
+# For backwards compatibility, you may explicitely set the CMake variables TBB_ARCHITECTURE and TBB_COMPILER.
+# TBB_ARCHITECTURE [ ia32 | em64t | itanium ]
+#   which architecture to use
+# TBB_COMPILER e.g. vc9 or cc3.2.3_libc2.3.2_kernel2.4.21 or cc4.0.1_os10.4.9
+#   which compiler to use (detected automatically on Windows)
+
+# This module respects
+# TBB_INSTALL_DIR or $ENV{TBB21_INSTALL_DIR} or $ENV{TBB_INSTALL_DIR}
+
+# This module defines
+# TBB_INCLUDE_DIRS, where to find task_scheduler_init.h, etc.
+# TBB_LIBRARY_DIRS, where to find libtbb, libtbbmalloc
+# TBB_DEBUG_LIBRARY_DIRS, where to find libtbb_debug, libtbbmalloc_debug
+# TBB_INSTALL_DIR, the base TBB install directory
+# TBB_LIBRARIES, the libraries to link against to use TBB.
+# TBB_DEBUG_LIBRARIES, the libraries to link against to use TBB with debug symbols.
+# TBB_FOUND, If false, don't try to use TBB.
+# TBB_INTERFACE_VERSION, as defined in tbb/tbb_stddef.h
+
+
+if (WIN32)
+    # has em64t/vc8 em64t/vc9
+    # has ia32/vc7.1 ia32/vc8 ia32/vc9
+    set(_TBB_DEFAULT_INSTALL_DIR "C:/Program Files/Intel/TBB" "C:/Program Files (x86)/Intel/TBB")
+    set(_TBB_LIB_NAME "tbb")
+    set(_TBB_LIB_MALLOC_NAME "${_TBB_LIB_NAME}malloc")
+    set(_TBB_LIB_DEBUG_NAME "${_TBB_LIB_NAME}_debug")
+    set(_TBB_LIB_MALLOC_DEBUG_NAME "${_TBB_LIB_MALLOC_NAME}_debug")
+    if (MSVC71)
+        set (_TBB_COMPILER "vc7.1")
+    endif(MSVC71)
+    if (MSVC80)
+        set(_TBB_COMPILER "vc8")
+    endif(MSVC80)
+    if (MSVC90)
+        set(_TBB_COMPILER "vc9")
+    endif(MSVC90)
+    if(MSVC10)
+        set(_TBB_COMPILER "vc10")
+    endif(MSVC10)
+    # Todo: add other Windows compilers such as ICL.
+    set(_TBB_ARCHITECTURE ${TBB_ARCHITECTURE})
+endif (WIN32)
+
+if (UNIX)
+    if (APPLE)
+        # MAC
+        set(_TBB_DEFAULT_INSTALL_DIR "/Library/Frameworks/Intel_TBB.framework/Versions")
+        # libs: libtbb.dylib, libtbbmalloc.dylib, *_debug
+        set(_TBB_LIB_NAME "tbb")
+        set(_TBB_LIB_MALLOC_NAME "${_TBB_LIB_NAME}malloc")
+        set(_TBB_LIB_DEBUG_NAME "${_TBB_LIB_NAME}_debug")
+        set(_TBB_LIB_MALLOC_DEBUG_NAME "${_TBB_LIB_MALLOC_NAME}_debug")
+        # default flavor on apple: ia32/cc4.0.1_os10.4.9
+        # Jiri: There is no reason to presume there is only one flavor and
+        #       that user's setting of variables should be ignored.
+        if(NOT TBB_COMPILER)
+            set(_TBB_COMPILER "cc4.0.1_os10.4.9")
+        elseif (NOT TBB_COMPILER)
+            set(_TBB_COMPILER ${TBB_COMPILER})
+        endif(NOT TBB_COMPILER)
+        if(NOT TBB_ARCHITECTURE)
+            set(_TBB_ARCHITECTURE "ia32")
+        elseif(NOT TBB_ARCHITECTURE)
+            set(_TBB_ARCHITECTURE ${TBB_ARCHITECTURE})
+        endif(NOT TBB_ARCHITECTURE)
+    else (APPLE)
+        # LINUX
+        set(_TBB_DEFAULT_INSTALL_DIR "/opt/intel/tbb" "/usr/local/include" "/usr/include")
+        set(_TBB_LIB_NAME "tbb")
+        set(_TBB_LIB_MALLOC_NAME "${_TBB_LIB_NAME}malloc")
+        set(_TBB_LIB_DEBUG_NAME "${_TBB_LIB_NAME}_debug")
+        set(_TBB_LIB_MALLOC_DEBUG_NAME "${_TBB_LIB_MALLOC_NAME}_debug")
+        # has em64t/cc3.2.3_libc2.3.2_kernel2.4.21 em64t/cc3.3.3_libc2.3.3_kernel2.6.5 em64t/cc3.4.3_libc2.3.4_kernel2.6.9 em64t/cc4.1.0_libc2.4_kernel2.6.16.21
+        # has ia32/*
+        # has itanium/*
+        set(_TBB_COMPILER ${TBB_COMPILER})
+        set(_TBB_ARCHITECTURE ${TBB_ARCHITECTURE})
+    endif (APPLE)
+endif (UNIX)
+
+if (CMAKE_SYSTEM MATCHES "SunOS.*")
+# SUN
+# not yet supported
+# has em64t/cc3.4.3_kernel5.10
+# has ia32/*
+endif (CMAKE_SYSTEM MATCHES "SunOS.*")
+
+
+#-- Clear the public variables
+set (TBB_FOUND "NO")
+
+
+#-- Find TBB install dir and set ${_TBB_INSTALL_DIR} and cached ${TBB_INSTALL_DIR}
+# first: use CMake variable TBB_INSTALL_DIR
+if (TBB_INSTALL_DIR)
+    set (_TBB_INSTALL_DIR ${TBB_INSTALL_DIR})
+endif (TBB_INSTALL_DIR)
+# second: use environment variable
+if (NOT _TBB_INSTALL_DIR)
+    if (NOT "$ENV{TBB_INSTALL_DIR}" STREQUAL "")
+        set (_TBB_INSTALL_DIR $ENV{TBB_INSTALL_DIR})
+    endif (NOT "$ENV{TBB_INSTALL_DIR}" STREQUAL "")
+    # Intel recommends setting TBB21_INSTALL_DIR
+    if (NOT "$ENV{TBB21_INSTALL_DIR}" STREQUAL "")
+        set (_TBB_INSTALL_DIR $ENV{TBB21_INSTALL_DIR})
+    endif (NOT "$ENV{TBB21_INSTALL_DIR}" STREQUAL "")
+    if (NOT "$ENV{TBB22_INSTALL_DIR}" STREQUAL "")
+        set (_TBB_INSTALL_DIR $ENV{TBB22_INSTALL_DIR})
+    endif (NOT "$ENV{TBB22_INSTALL_DIR}" STREQUAL "")
+    if (NOT "$ENV{TBB30_INSTALL_DIR}" STREQUAL "")
+        set (_TBB_INSTALL_DIR $ENV{TBB30_INSTALL_DIR})
+    endif (NOT "$ENV{TBB30_INSTALL_DIR}" STREQUAL "")
+endif (NOT _TBB_INSTALL_DIR)
+# third: try to find path automatically
+if (NOT _TBB_INSTALL_DIR)
+    if (_TBB_DEFAULT_INSTALL_DIR)
+        set (_TBB_INSTALL_DIR ${_TBB_DEFAULT_INSTALL_DIR})
+    endif (_TBB_DEFAULT_INSTALL_DIR)
+endif (NOT _TBB_INSTALL_DIR)
+# sanity check
+if (NOT _TBB_INSTALL_DIR)
+    message ("ERROR: Unable to find Intel TBB install directory. ${_TBB_INSTALL_DIR}")
+else (NOT _TBB_INSTALL_DIR)
+# finally: set the cached CMake variable TBB_INSTALL_DIR
+if (NOT TBB_INSTALL_DIR)
+    set (TBB_INSTALL_DIR ${_TBB_INSTALL_DIR} CACHE PATH "Intel TBB install directory")
+    mark_as_advanced(TBB_INSTALL_DIR)
+endif (NOT TBB_INSTALL_DIR)
+
+
+#-- A macro to rewrite the paths of the library. This is necessary, because
+#   find_library() always found the em64t/vc9 version of the TBB libs
+macro(TBB_CORRECT_LIB_DIR var_name)
+#    if (NOT "${_TBB_ARCHITECTURE}" STREQUAL "em64t")
+        string(REPLACE em64t "${_TBB_ARCHITECTURE}" ${var_name} ${${var_name}})
+#    endif (NOT "${_TBB_ARCHITECTURE}" STREQUAL "em64t")
+    string(REPLACE ia32 "${_TBB_ARCHITECTURE}" ${var_name} ${${var_name}})
+    string(REPLACE vc7.1 "${_TBB_COMPILER}" ${var_name} ${${var_name}})
+    string(REPLACE vc8 "${_TBB_COMPILER}" ${var_name} ${${var_name}})
+    string(REPLACE vc9 "${_TBB_COMPILER}" ${var_name} ${${var_name}})
+    string(REPLACE vc10 "${_TBB_COMPILER}" ${var_name} ${${var_name}})
+endmacro(TBB_CORRECT_LIB_DIR var_content)
+
+
+#-- Look for include directory and set ${TBB_INCLUDE_DIR}
+set (TBB_INC_SEARCH_DIR ${_TBB_INSTALL_DIR}/include)
+# Jiri: tbbvars now sets the CPATH environment variable to the directory
+#       containing the headers.
+find_path(TBB_INCLUDE_DIR
+    tbb/task_scheduler_init.h
+    PATHS ${TBB_INC_SEARCH_DIR} ENV CPATH
+)
+mark_as_advanced(TBB_INCLUDE_DIR)
+
+
+#-- Look for libraries
+# GvdB: $ENV{TBB_ARCH_PLATFORM} is set by the build script tbbvars[.bat|.sh|.csh]
+if (NOT $ENV{TBB_ARCH_PLATFORM} STREQUAL "")
+    set (_TBB_LIBRARY_DIR 
+         ${_TBB_INSTALL_DIR}/lib/$ENV{TBB_ARCH_PLATFORM}
+         ${_TBB_INSTALL_DIR}/$ENV{TBB_ARCH_PLATFORM}/lib
+        )
+endif (NOT $ENV{TBB_ARCH_PLATFORM} STREQUAL "")
+# Jiri: This block isn't mutually exclusive with the previous one
+#       (hence no else), instead I test if the user really specified
+#       the variables in question.
+if ((NOT ${TBB_ARCHITECTURE} STREQUAL "") AND (NOT ${TBB_COMPILER} STREQUAL ""))
+    # HH: deprecated
+    message(STATUS "[Warning] FindTBB.cmake: The use of TBB_ARCHITECTURE and TBB_COMPILER is deprecated and may not be supported in future versions. Please set \$ENV{TBB_ARCH_PLATFORM} (using tbbvars.[bat|csh|sh]).")
+    # Jiri: It doesn't hurt to look in more places, so I store the hints from
+    #       ENV{TBB_ARCH_PLATFORM} and the TBB_ARCHITECTURE and TBB_COMPILER
+    #       variables and search them both.
+    set (_TBB_LIBRARY_DIR "${_TBB_INSTALL_DIR}/${_TBB_ARCHITECTURE}/${_TBB_COMPILER}/lib" ${_TBB_LIBRARY_DIR})
+endif ((NOT ${TBB_ARCHITECTURE} STREQUAL "") AND (NOT ${TBB_COMPILER} STREQUAL ""))
+
+# GvdB: Mac OS X distribution places libraries directly in lib directory.
+list(APPEND _TBB_LIBRARY_DIR ${_TBB_INSTALL_DIR}/lib)
+
+# Jiri: No reason not to check the default paths. From recent versions,
+#       tbbvars has started exporting the LIBRARY_PATH and LD_LIBRARY_PATH
+#       variables, which now point to the directories of the lib files.
+#       It all makes more sense to use the ${_TBB_LIBRARY_DIR} as a HINTS
+#       argument instead of the implicit PATHS as it isn't hard-coded
+#       but computed by system introspection. Searching the LIBRARY_PATH
+#       and LD_LIBRARY_PATH environment variables is now even more important
+#       that tbbvars doesn't export TBB_ARCH_PLATFORM and it facilitates
+#       the use of TBB built from sources.
+find_library(TBB_LIBRARY ${_TBB_LIB_NAME} HINTS ${_TBB_LIBRARY_DIR}
+        PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH)
+find_library(TBB_MALLOC_LIBRARY ${_TBB_LIB_MALLOC_NAME} HINTS ${_TBB_LIBRARY_DIR}
+        PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH)
+
+#Extract path from TBB_LIBRARY name
+get_filename_component(TBB_LIBRARY_DIR ${TBB_LIBRARY} PATH)
+
+#TBB_CORRECT_LIB_DIR(TBB_LIBRARY)
+#TBB_CORRECT_LIB_DIR(TBB_MALLOC_LIBRARY)
+mark_as_advanced(TBB_LIBRARY TBB_MALLOC_LIBRARY)
+
+#-- Look for debug libraries
+# Jiri: Changed the same way as for the release libraries.
+find_library(TBB_LIBRARY_DEBUG ${_TBB_LIB_DEBUG_NAME} HINTS ${_TBB_LIBRARY_DIR}
+        PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH)
+find_library(TBB_MALLOC_LIBRARY_DEBUG ${_TBB_LIB_MALLOC_DEBUG_NAME} HINTS ${_TBB_LIBRARY_DIR}
+        PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH)
+
+# Jiri: Self-built TBB stores the debug libraries in a separate directory.
+#       Extract path from TBB_LIBRARY_DEBUG name
+get_filename_component(TBB_LIBRARY_DEBUG_DIR ${TBB_LIBRARY_DEBUG} PATH)
+
+#TBB_CORRECT_LIB_DIR(TBB_LIBRARY_DEBUG)
+#TBB_CORRECT_LIB_DIR(TBB_MALLOC_LIBRARY_DEBUG)
+mark_as_advanced(TBB_LIBRARY_DEBUG TBB_MALLOC_LIBRARY_DEBUG)
+
+
+if (TBB_INCLUDE_DIR)
+    if (TBB_LIBRARY)
+        set (TBB_FOUND "YES")
+        set (TBB_LIBRARIES ${TBB_LIBRARY} ${TBB_MALLOC_LIBRARY} ${TBB_LIBRARIES})
+        set (TBB_DEBUG_LIBRARIES ${TBB_LIBRARY_DEBUG} ${TBB_MALLOC_LIBRARY_DEBUG} ${TBB_DEBUG_LIBRARIES})
+        set (TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR} CACHE PATH "TBB include directory" FORCE)
+        set (TBB_LIBRARY_DIRS ${TBB_LIBRARY_DIR} CACHE PATH "TBB library directory" FORCE)
+        # Jiri: Self-built TBB stores the debug libraries in a separate directory.
+        set (TBB_DEBUG_LIBRARY_DIRS ${TBB_LIBRARY_DEBUG_DIR} CACHE PATH "TBB debug library directory" FORCE)
+        mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARY_DIRS TBB_DEBUG_LIBRARY_DIRS TBB_LIBRARIES TBB_DEBUG_LIBRARIES)
+        message(STATUS "Found Intel TBB")
+    endif (TBB_LIBRARY)
+endif (TBB_INCLUDE_DIR)
+
+if (NOT TBB_FOUND)
+    message("ERROR: Intel TBB NOT found!")
+    message(STATUS "Looked for Threading Building Blocks in ${_TBB_INSTALL_DIR}")
+    # do only throw fatal, if this pkg is REQUIRED
+    if (TBB_FIND_REQUIRED)
+        message(FATAL_ERROR "Could NOT find TBB library.")
+    endif (TBB_FIND_REQUIRED)
+endif (NOT TBB_FOUND)
+
+endif (NOT _TBB_INSTALL_DIR)
+
+if (TBB_FOUND)
+	set(TBB_INTERFACE_VERSION 0)
+	FILE(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _TBB_VERSION_CONTENTS)
+	STRING(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" TBB_INTERFACE_VERSION "${_TBB_VERSION_CONTENTS}")
+	set(TBB_INTERFACE_VERSION "${TBB_INTERFACE_VERSION}")
+endif (TBB_FOUND)
diff --git a/src/doc/oiiotool.tex b/src/doc/oiiotool.tex
index a229a25..47c0826 100644
--- a/src/doc/oiiotool.tex
+++ b/src/doc/oiiotool.tex
@@ -285,6 +285,18 @@ If verbose mode is not turned on, only the resolution and data format
 are printed.
 \apiend
 
+\apiitem{--metamatch \emph{regex} \\
+--no-metamatch \emph{regex}}
+\NEW
+Regular expressions to restrict which metadata are output when using
+{\cf oiiotool --info -v}.  The {\cf --metamatch} expression causes only
+metadata whose name matches to print; non-matches are not output.  The
+{\cf --no-metamatch} expression causes metadata whose name matches to be
+suppressed; others (non-matches) are printed.  It is not advised to use
+both of these options at the same time (probably nothing bad will
+happen, but it's hard to reason about the behavior in that case).
+\apiend
+
 \apiitem{--stats}
 Prints detailed statistical information about each input image as it is
 read.
@@ -709,9 +721,24 @@ consisting of the \emph{absolute value} of he old pixel value.
 \apiend
 
 \apiitem{--over}
+\index{composite}
 Replace the \emph{two} top images with a new image that is the
-Porter/Duff ``over'' composite of the first image over the second
-image.
+Porter/Duff ``over'' composite with the first image as the foreground
+and the second image as the background.
+Both input images must have the same number and order of channels
+and must contain an alpha channel.
+\apiend
+
+\apiitem{--zover}
+\index{depth composite}
+\NEW
+Replace the \emph{two} top images with a new image that is a \emph{depth
+composite} of the two images -- the operation is the 
+Porter/Duff ``over'' composite, but each pixel individually will choose
+which of the two images is the foreground and which background, depending on
+the ``Z'' channel values for that pixel (larger Z means farther away).
+Both input images must have the same number and order of channels
+and must contain both depth/Z and alpha channels.
 \apiend
 
 \apiitem{--flip}
diff --git a/src/doc/openimageio.tex b/src/doc/openimageio.tex
index de854bf..dd0fae4 100644
--- a/src/doc/openimageio.tex
+++ b/src/doc/openimageio.tex
@@ -85,7 +85,7 @@
 \date{{\large 
 %Editor: Larry Gritz \\[2ex]
 Date: 18 Nov, 2012
-\\ (with corrections, 5 Jan 2013)
+\\ (with corrections, 25 Jan 2013)
 }}
 
 
diff --git a/src/include/CMakeLists.txt b/src/include/CMakeLists.txt
index 4705673..6c6da5d 100644
--- a/src/include/CMakeLists.txt
+++ b/src/include/CMakeLists.txt
@@ -22,7 +22,7 @@ list(APPEND public_headers ${CMAKE_BINARY_DIR}/include/version.h)
 install (FILES ${public_headers} DESTINATION ${INCLUDE_INSTALL_DIR}
          COMPONENT developer)
 
-if (USE_TBB)
+if (USE_TBB AND NOT USE_EXTERNAL_TBB)
     install (DIRECTORY tbb DESTINATION ${INCLUDE_INSTALL_DIR}
              COMPONENT developer)
 endif ()
diff --git a/src/include/imagebufalgo.h b/src/include/imagebufalgo.h
index 298d4a8..829c0aa 100644
--- a/src/include/imagebufalgo.h
+++ b/src/include/imagebufalgo.h
@@ -365,6 +365,14 @@ bool OIIO_API over (ImageBuf &R, const ImageBuf &A, const ImageBuf &B,
                      ROI roi = ROI(), int threads = 0);
 
 
+/// Just like ImageBufAlgo::over(), but inputs A and B must have
+/// designated 'z' channels, and on a pixel-by-pixel basis, the z values
+/// will determine which of A or B will be considered the foreground or
+/// background (lower z is foreground).
+bool OIIO_API zover (ImageBuf &R, const ImageBuf &A, const ImageBuf &B,
+                     ROI roi = ROI(), int threads = 0);
+
+
 /// Render a text string into image R, essentially doing an "over" of
 /// the character into the existing pixel data.  The baseline of the
 /// first character will start at position (x,y).  The font is given by
@@ -416,6 +424,123 @@ bool OIIO_API histogram_draw (ImageBuf &R,
 
 
 
+enum OIIO_API MakeTextureMode {
+    MakeTxTexture, MakeTxShadow, MakeTxEnvLatl, _MakeTxLast
+};
+
+/// Turn an image file (filename) into a tiled, MIP-mapped, texture file
+/// (outputfilename).  The 'mode' describes what type of texture file we
+/// are creating.  If the outstream pointer is not NULL, it should point
+/// to a stream (for example, &std::out, or a pointer to a local 
+/// std::stringstream to capture output), which is where console output
+/// and error messages will be deposited.
+///
+/// The 'config' is an ImageSpec that contains all the information and
+/// special instructions for making the texture.  Anything set in config
+/// (format, tile size, or named metadata) will take precedence over
+/// whatever is specified by the input file itself.  Additionally, named
+/// metadata that starts with "maketx:" will not be output to the file
+/// itself, but may contain instructions controlling how the texture is
+/// created.  The full list of supported configuration options is:
+///
+/// Named fields:
+///    format         Data format of the texture file (default: UNKNOWN =
+///                     same format as the input)
+///    tile_width     Preferred tile size (default: 64x64x1)
+///    tile_height    
+///    tile_depth     
+/// Metadata in config.extra_attribs:
+///    compression (string)   Default: "zip"
+///    fovcot (float)         Default: aspect ratio of the image resolution
+///    planarconfig (string)  Default: "separate"
+///    worldtocamera (matrix) World-to-camera matrix of the view.
+///    worldtoscreen (matrix) World-to-screen space matrix of the view.
+///    wrapmodes (string)     Default: "black,black"
+///    maketx:verbose (int)   How much detail should go to outstream (0).
+///    maketx:stats (int)     If nonzero, print stats to outstream (0).
+///    maketx:resize (int)    If nonzero, resize to power of 2. (0)
+///    maketx:nomipmap (int)  If nonzero, only output the top MIP level (0).
+///    maketx:updatemode (int) If nonzero, write new output only if the
+///                              output file doesn't already exist, or is
+///                              older than the input file. (0)
+///    maketx:constant_color_detect (int)
+///                           If nonzero, detect images that are entirely
+///                             one color, and change them to be low
+///                             resolution (default: 0).
+///    maketx:monochrome_detect (int)
+///                           If nonzero, change RGB images which have 
+///                              R==G==B everywhere to single-channel 
+///                              grayscale (default: 0).
+///    maketx:opaquedetect (int)
+///                           If nonzero, drop the alpha channel if alpha
+///                              is 1.0 in all pixels (default: 0).
+///    maketx:unpremult (int) If nonzero, unpremultiply color by alpha before
+///                              color conversion, then multiply by alpha
+///                              after color conversion (default: 0).
+///    maketx:incolorspace (string)
+///    maketx:outcolorspace (string) 
+///                           These two together will apply a color conversion
+///                               (with OpenColorIO, if compiled). Default: ""
+///    maketx:checknan (int)  If nonzero, will consider it an error if the
+///                               input image has any NaN pixels. (0)
+///    maketx:fixnan (string) If set to "black" or "box3", will attempt
+///                               to repair any NaN pixels found in the
+///                               input image (default: "none").
+///    maketx:set_full_to_pixels (int)
+///                           If nonzero, doctors the full/display window
+///                               of the texture to be identical to the
+///                               pixel/data window and reset the origin
+///                               to 0,0 (default: 0).
+///    maketx:filtername (string)
+///                           If set, will specify the name of a high-quality
+///                           filter to use when resampling for MIPmap levels.
+///                           Default: "", use simple bilinear resampling.
+///    maketx:nchannels (int) If nonzero, will specify how many channels
+///                              the output texture should have, padding with
+///                              0 values or dropping channels, if it doesn't
+///                              the number of channels in the input.
+///                              (default: 0, meaning keep all input channels)
+///    maketx:fileformatname (string)
+///                           If set, will specify the output file format.
+///                               (default: "", meaning infer the format from
+///                               the output filename)
+///    maketx:prman_metadata (int)
+///                           If set, output some metadata that PRMan will
+///                               need for its textures. (0)
+///    maketx:oiio_options (int)
+///                           (Deprecated; all are handled by default)
+///    maketx:prman_options (int)
+///                           If nonzero, override a whole bunch of settings
+///                               as needed to make textures that are
+///                               compatible with PRMan. (0)
+///    maketx:mipimages (string)
+///                           Semicolon-separated list of alternate images
+///                               to be used for individual MIPmap levels,
+///                               rather than simply downsizing. (default: "")
+///    maketx:full_command_line (string)
+///                           The command or program used to generate this
+///                               call, will be embedded in the metadata.
+///                               (default: "")
+///    maketx:ignore_unassoc (int)
+///                           If nonzero, will disbelieve any evidence that
+///                               the input image is unassociated alpha. (0)
+///
+bool OIIO_API make_texture (MakeTextureMode mode,
+                            const std::string &filename,
+                            const std::string &outputfilename,
+                            const ImageSpec &config,
+                            std::ostream *outstream = NULL);
+
+/// Version of make_texture that takes multiple filenames (reserved for
+/// future expansion, such as assembling several faces into a cube map).
+bool OIIO_API make_texture (MakeTextureMode mode,
+                            const std::vector<std::string> &filenames,
+                            const std::string &outputfilename,
+                            const ImageSpec &config,
+                            std::ostream *outstream = NULL);
+                                
+
+
 /// Helper template for generalized multithreading for image processing
 /// functions.  Some function/functor f is applied to every pixel the
 /// region of interest roi, dividing the region into multiple threads if
@@ -431,7 +556,7 @@ bool OIIO_API histogram_draw (ImageBuf &R,
 ///                       float scale, ROI roi);
 /// Then you can parallelize it as follows:
 ///     ImageBuf R /*result*/, A /*input*/;
-///     ROI roi = get_roi (R);
+///     ROI roi = get_roi (R.spec());
 ///     parallel_image (boost::bind(my_image_op,boost::ref(R),
 ///                                 boost::cref(A),3.14,_1), roi);
 ///
diff --git a/src/include/imageio.h b/src/include/imageio.h
index 85858b8..58c6890 100644
--- a/src/include/imageio.h
+++ b/src/include/imageio.h
@@ -1191,8 +1191,14 @@ OIIO_API int quantize (float value, int quant_black, int quant_white,
 /// conversion.  If dst_type is UNKNWON, it will be assumed to be the
 /// same as src_type.
 OIIO_API bool convert_types (TypeDesc src_type, const void *src,
-                              TypeDesc dst_type, void *to, int n,
-                              int alpha_channel = -1, int z_channel = -1);
+                              TypeDesc dst_type, void *dst, int n);
+
+/// DEPRECATED -- for some reason we had a convert_types that took
+/// alpha_channel and z_channel parameters, but never did anything
+/// with them.
+OIIO_API bool convert_types (TypeDesc src_type, const void *src,
+                             TypeDesc dst_type, void *dst, int n,
+                             int alpha_channel, int z_channel = -1);
 
 /// Helper routine for data conversion: Convert an image of nchannels x
 /// width x height x depth from src to dst.  The src and dst may have
diff --git a/src/libOpenImageIO/CMakeLists.txt b/src/libOpenImageIO/CMakeLists.txt
index bd5dbe3..f18ee66 100644
--- a/src/libOpenImageIO/CMakeLists.txt
+++ b/src/libOpenImageIO/CMakeLists.txt
@@ -36,6 +36,7 @@ set (libOpenImageIO_srcs exif.cpp formatspec.cpp imagebuf.cpp
                           color_ocio.cpp
                           imagebufalgo.cpp imagebufalgo_orient.cpp
                           imagebufalgo_yee.cpp imagebufalgo_opencv.cpp
+                          maketexture.cpp
                           ../libutil/argparse.cpp
                           ../libutil/errorhandler.cpp 
                           ../libutil/filesystem.cpp 
@@ -62,7 +63,13 @@ if (NOT USE_EXTERNAL_PUGIXML)
 endif ()
 
 # Include our own TBB if using it
-if (USE_TBB)
+if (USE_TBB AND USE_EXTERNAL_TBB)
+    message (STATUS "System TBB library will be used.")
+    find_package (TBB REQUIRED)
+    include_directories (${TBB_INCLUDE_DIRS})
+    set (libOpenImageIO_srcs ${libOpenImageIO_srcs})
+elseif (USE_TBB AND NOT USE_EXTERNAL_TBB)
+    message (STATUS "Built-in TBB library will be used.")
     set (libOpenImageIO_srcs ${libOpenImageIO_srcs} ../libutil/tbb_misc.cpp)
 endif ()
 
@@ -202,7 +209,11 @@ target_link_libraries (OpenImageIO
                            ${VISIBILITY_COMMAND} ${VISIBILITY_MAP_COMMAND}
                            ${Boost_LIBRARIES})
 
-
+# Link against system TBB library if specified
+if (USE_TBB AND USE_EXTERNAL_TBB)
+    message (STATUS "Linking TBB: ${TBB_LIBRARIES}")
+    target_link_libraries (OpenImageIO ${TBB_LIBRARIES})
+endif ()
 
 # Include OpenColorIO if using it
 if (USE_OCIO AND OCIO_FOUND)
diff --git a/src/libOpenImageIO/imagebufalgo.cpp b/src/libOpenImageIO/imagebufalgo.cpp
index bd4af14..f4d598a 100644
--- a/src/libOpenImageIO/imagebufalgo.cpp
+++ b/src/libOpenImageIO/imagebufalgo.cpp
@@ -1103,6 +1103,10 @@ ImageBufAlgo::resize (ImageBuf &dst, const ImageBuf &src,
 namespace
 {
 
+// Make sure isfinite is defined for 'half'
+inline bool isfinite (half h) { return h.isFinite(); }
+
+
 template<typename SRCTYPE>
 bool fixNonFinite_ (ImageBuf &dst, const ImageBuf &src,
                     ImageBufAlgo::NonFiniteFixMode mode,
@@ -1123,7 +1127,7 @@ bool fixNonFinite_ (ImageBuf &dst, const ImageBuf &src,
         if (! dst.copy (src))
             return false;
         
-        ImageBuf::Iterator<SRCTYPE> pixel (dst);
+        ImageBuf::Iterator<SRCTYPE,SRCTYPE> pixel (dst);
         while (pixel.valid()) {
             bool fixed = false;
             for (int c = 0;  c < nchannels;  ++c) {
@@ -1159,7 +1163,7 @@ bool fixNonFinite_ (ImageBuf &dst, const ImageBuf &src,
         if (! dst.copy (src))
             return false;
         
-        ImageBuf::Iterator<SRCTYPE> pixel (dst);
+        ImageBuf::Iterator<SRCTYPE,SRCTYPE> pixel (dst);
         
         while (pixel.valid()) {
             bool fixed = false;
@@ -1175,7 +1179,7 @@ bool fixNonFinite_ (ImageBuf &dst, const ImageBuf &src,
                     int left   = pixel.y() - boxwidth;
                     int right  = pixel.y() + boxwidth;
                     
-                    ImageBuf::Iterator<SRCTYPE> it (dst, top, bottom, left, right);
+                    ImageBuf::Iterator<SRCTYPE,SRCTYPE> it (dst, top, bottom, left, right);
                     while (it.valid()) {
                         SRCTYPE v = it[c];
                         if (isfinite (v)) {
@@ -1220,9 +1224,7 @@ ImageBufAlgo::fixNonFinite (ImageBuf &dst, const ImageBuf &src,
     case TypeDesc::FLOAT :
         return fixNonFinite_<float> (dst, src, mode, pixelsFixed);
     case TypeDesc::HALF  :
-         // This use of float here is on purpose to allow for simpler
-         // implementations that work on all data types
-        return fixNonFinite_<float> (dst, src, mode, pixelsFixed);
+        return fixNonFinite_<half> (dst, src, mode, pixelsFixed);
     case TypeDesc::DOUBLE:
         return fixNonFinite_<double> (dst, src, mode, pixelsFixed);
     default:
@@ -1241,10 +1243,43 @@ ImageBufAlgo::fixNonFinite (ImageBuf &dst, const ImageBuf &src,
 
 namespace {   // anonymous namespace
 
+static bool
+decode_over_channels (const ImageBuf &R, int &nchannels, 
+                      int &alpha, int &z, int &colors)
+{
+    if (! R.initialized()) {
+        alpha = -1;
+        z = -1;
+        colors = 0;
+        return false;
+    }
+    const ImageSpec &spec (R.spec());
+    alpha =  spec.alpha_channel;
+    bool has_alpha = (alpha >= 0);
+    z = spec.z_channel;
+    bool has_z = (z >= 0);
+    nchannels = spec.nchannels;
+    colors = nchannels - has_alpha - has_z;
+    if (! has_alpha && colors == 4) {
+        // No marked alpha channel, but suspiciously 4 channel -- assume
+        // it's RGBA. 
+        has_alpha = true;
+        colors -= 1;
+        // Assume alpha is the highest channel that's not z
+        alpha = nchannels - 1;
+        if (alpha == z)
+            --alpha;
+    }
+    return true;
+}
+
+
+
 // Fully type-specialized version of over.
 template<class Rtype, class Atype, class Btype>
-bool
-over_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, ROI roi)
+static bool
+over_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, ROI roi,
+           bool zcomp=false)
 {
     if (R.spec().format != BaseTypeFromC<Rtype>::value ||
         A.spec().format != BaseTypeFromC<Atype>::value ||
@@ -1254,23 +1289,12 @@ over_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, ROI roi)
         return false;   // double check that types match
     }
 
-    // Output image R.
-    const ImageSpec &specR = R.spec();
-    int channels_R = specR.nchannels;
-
-    // Input image A.
-    const ImageSpec &specA = A.spec();
-    int alpha_index_A =  specA.alpha_channel;
-    int has_alpha_A = (alpha_index_A >= 0);
-    int channels_A = specA.nchannels;
-
-    // Input image B.
-    const ImageSpec &specB = B.spec();
-    int alpha_index_B =  specB.alpha_channel;
-    int has_alpha_B = (alpha_index_B >= 0);
-    int channels_B = specB.nchannels;
-
-    int channels_AB = std::min (channels_A, channels_B);
+    // It's already guaranteed that R, A, and B have matching channel
+    // ordering, and have an alpha channel.  So just decode one.
+    int nchannels = 0, alpha_channel = 0, z_channel = 0, ncolor_channels = 0;
+    decode_over_channels (R, nchannels, alpha_channel,
+                          z_channel, ncolor_channels);
+    bool has_z = (z_channel >= 0);
 
     ImageBuf::ConstIterator<Atype, float> a (A);
     ImageBuf::ConstIterator<Btype, float> b (B);
@@ -1281,32 +1305,41 @@ over_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, ROI roi)
 
         if (! a.valid()) {
             if (! b.valid()) {
-                // a and b invalid.
-                for (int c = 0; c < channels_R; c++) { r[c] = 0.0f; }
+                // a and b are both invalid -- make it an "empty" pixel
+                for (int c = 0; c < nchannels; c++)
+                    r[c] = 0.0f;
             } else {
-                // a invalid, b valid.
-                for (int c = 0; c < channels_B; c++) { r[c] = b[c]; }
-                if (! has_alpha_B) { r[3] = 1.0f; }
+                // a invalid, b valid -- copy B
+                for (int c = 0; c < nchannels; ++c)
+                    r[c] = b[c];
             }
             continue;
         }
 
         if (! b.valid()) {
-            // a valid, b invalid.
-            for (int c = 0; c < channels_A; c++) { r[c] = a[c]; }
-            if (! has_alpha_A) { r[3] = 1.0f; }
+            // a valid, b invalid -- copy A
+            for (int c = 0; c < nchannels; ++c)
+                r[c] = a[c];
             continue;
         }
 
         // At this point, a and b are valid.
-        float alpha_A = has_alpha_A 
-                        ? clamp (a[alpha_index_A], 0.0f, 1.0f) : 1.0f;
-        float one_minus_alpha_A = 1.0f - alpha_A;
-        for (int c = 0;  c < channels_AB;  c++)
-            r[c] = a[c] + one_minus_alpha_A * b[c];
-        if (channels_R != channels_AB) {
-            // R has 4 channels, A or B has 3 channels -> alpha channel is 3.
-            r[3] = alpha_A + one_minus_alpha_A * (has_alpha_B ? b[3] : 1.0f);
+
+        if (!zcomp || a[z_channel] <= b[z_channel]) {
+            // A over B
+            float alpha = clamp (a[alpha_channel], 0.0f, 1.0f);
+            float one_minus_alpha = 1.0f - alpha;
+            for (int c = 0;  c < nchannels;  c++)
+                r[c] = a[c] + one_minus_alpha * b[c];
+            if (has_z)
+                r[z_channel] = (alpha != 0.0) ? a[z_channel] : b[z_channel];
+        } else {
+            // B over A -- because we're doing a Z composite
+            float alpha = clamp (b[alpha_channel], 0.0f, 1.0f);
+            float one_minus_alpha = 1.0f - alpha;
+            for (int c = 0;  c < nchannels;  c++)
+                r[c] = b[c] + one_minus_alpha * a[c];
+            r[z_channel] = (alpha != 0.0) ? b[z_channel] : a[z_channel];
         }
     }
     return true;
@@ -1319,98 +1352,138 @@ bool
 ImageBufAlgo::over (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, ROI roi,
                     int nthreads)
 {
-    // Output image R.
     const ImageSpec &specR = R.spec();
-    int alpha_R =  specR.alpha_channel;
-    int has_alpha_R = (alpha_R >= 0);
-    int channels_R = specR.nchannels;
-    int non_alpha_R = channels_R - has_alpha_R;
-    bool initialized_R = R.initialized();
-
-    // Input image A.
     const ImageSpec &specA = A.spec();
-    int alpha_A =  specA.alpha_channel;
-    int has_alpha_A = (alpha_A >= 0);
-    int channels_A = specA.nchannels;
-    int non_alpha_A = has_alpha_A ? (channels_A - 1) : 3;
-    bool A_not_34 = channels_A != 3 && channels_A != 4;
-
-    // Input image B.
     const ImageSpec &specB = B.spec();
-    int alpha_B =  specB.alpha_channel;
-    int has_alpha_B = (alpha_B >= 0);
-    int channels_B = specB.nchannels;
-    int non_alpha_B = has_alpha_B ? (channels_B - 1) : 3;
-    bool B_not_34 = channels_B != 3 && channels_B != 4;
 
+    int nchannels_R, nchannels_A, nchannels_B;
+    int alpha_R, alpha_A, alpha_B;
+    int z_R, z_A, z_B;
+    int colors_R, colors_A, colors_B;
+    bool initialized_R = decode_over_channels (R, nchannels_R, alpha_R,
+                                               z_R, colors_R);
+    bool initialized_A = decode_over_channels (A, nchannels_A, alpha_A,
+                                               z_A, colors_A);
+    bool initialized_B = decode_over_channels (B, nchannels_B, alpha_B,
+                                               z_B, colors_B);
+
+    if (! initialized_A || ! initialized_B) {
+        R.error ("Can't 'over' uninitialized images");
+        return false;
+    }
+
+    // Fail if the input images don't have an alpha channel.
+    if (alpha_A < 0 || alpha_B < 0 || (initialized_R && alpha_R < 0)) {
+        R.error ("'over' requires alpha channels");
+        return false;
+    }
+    // Fail for mismatched channel counts
+    if (colors_A != colors_B || colors_A < 1) {
+        R.error ("Can't 'over' images with mismatched color channel counts (%d vs %d)",
+                 colors_A, colors_B);
+        return false;
+    }
+    // Fail for unaligned alpha or z channels
+    if (alpha_A != alpha_B || z_A != z_B ||
+        (initialized_R && alpha_R != alpha_A) ||
+        (initialized_R && z_R != z_A)) {
+        R.error ("Can't 'over' images with mismatched channel order",
+                 colors_A, colors_B);
+        return false;
+    }
+    
     // At present, this operation only supports ImageBuf's containing
     // float pixel data.
-    if (R.spec().format != TypeDesc::TypeFloat ||
-        A.spec().format != TypeDesc::TypeFloat ||
-        B.spec().format != TypeDesc::TypeFloat) {
-        R.error ("Unsupported pixel data format combination '%s / %s / %s'",
-                   R.spec().format, A.spec().format, B.spec().format);
+    if ((initialized_R && specR.format != TypeDesc::TypeFloat) ||
+        specA.format != TypeDesc::TypeFloat ||
+        specB.format != TypeDesc::TypeFloat) {
+        R.error ("Unsupported pixel data format combination '%s = %s over %s'",
+                 specR.format, specA.format, specB.format);
         return false;
     }
 
-    // Fail if the input images have a Z channel.
-    if (specA.z_channel >= 0 || specB.z_channel >= 0) {
-        R.error ("'over' does not support Z channels");
-        return false;
+    // Uninitialized R -> size it to the union of A and B.
+    if (! initialized_R) {
+        ImageSpec newspec = specA;
+        set_roi (newspec, roi_union (get_roi(specA), get_roi(specB)));
+        R.reset ("over", newspec);
     }
 
-    // If input images A and B have different number of non-alpha channels
-    // then return false.
-    if (non_alpha_A != non_alpha_B) {
-        R.error ("inputs had different numbers of color channels");
+    // Specified ROI -> use it. Unspecified ROI -> initialize from R.
+    if (! roi.defined())
+        roi = get_roi (R.spec());
+
+    parallel_image (boost::bind (over_impl<float,float,float>, boost::ref(R),
+                                 boost::cref(A), boost::cref(B), _1, false),
+                    roi, nthreads);
+    return ! R.has_error();
+}
+
+
+
+bool
+ImageBufAlgo::zover (ImageBuf &R, const ImageBuf &A, const ImageBuf &B,
+                     ROI roi, int nthreads)
+{
+    const ImageSpec &specR = R.spec();
+    const ImageSpec &specA = A.spec();
+    const ImageSpec &specB = B.spec();
+
+    int nchannels_R, nchannels_A, nchannels_B;
+    int alpha_R, alpha_A, alpha_B;
+    int z_R, z_A, z_B;
+    int colors_R, colors_A, colors_B;
+    bool initialized_R = decode_over_channels (R, nchannels_R, alpha_R,
+                                               z_R, colors_R);
+    bool initialized_A = decode_over_channels (A, nchannels_A, alpha_A,
+                                               z_A, colors_A);
+    bool initialized_B = decode_over_channels (B, nchannels_B, alpha_B,
+                                               z_B, colors_B);
+
+    if (! initialized_A || ! initialized_B) {
+        R.error ("Can't 'zover' uninitialized images");
         return false;
     }
-
-    // A or B has number of channels different than 3 and 4, and it does
-    // not have an alpha channel.
-    if ((A_not_34 && !has_alpha_A) || (B_not_34 && !has_alpha_B)) {
-        R.error ("inputs must have alpha channels (or be implicitly RGB or RGBA)");
+    // Fail if the input images don't have a Z channel.
+    if (z_A < 0 || z_B < 0 || (initialized_R && z_R < 0)) {
+        R.error ("'zover' requires Z channels");
         return false;
     }
-
-    // A or B has zero or one channel -> return false.
-    if (channels_A <= 1 || channels_B <= 1) {
-        R.error ("unsupported number of channels");
+    // Fail if the input images don't have an alpha channel.
+    if (alpha_A < 0 || alpha_B < 0 || (initialized_R && alpha_R < 0)) {
+        R.error ("'zover' requires alpha channels");
+        return false;
+    }
+    // Fail for mismatched channel counts
+    if (colors_A != colors_B || colors_A < 1) {
+        R.error ("Can't 'zover' images with mismatched color channel counts (%d vs %d)",
+                 colors_A, colors_B);
+        return false;
+    }
+    // Fail for unaligned alpha or z channels
+    if (alpha_A != alpha_B || z_A != z_B ||
+        (initialized_R && alpha_R != alpha_A) ||
+        (initialized_R && z_R != z_A)) {
+        R.error ("Can't 'zover' images with mismatched channel order",
+                 colors_A, colors_B);
+        return false;
+    }
+    
+    // At present, this operation only supports ImageBuf's containing
+    // float pixel data.
+    if ((initialized_R && specR.format != TypeDesc::TypeFloat) ||
+        specA.format != TypeDesc::TypeFloat ||
+        specB.format != TypeDesc::TypeFloat) {
+        R.error ("Unsupported pixel data format combination '%s = %s zover %s'",
+                 specR.format, specA.format, specB.format);
         return false;
     }
 
-    // Initialized R -> use as allocated.  
     // Uninitialized R -> size it to the union of A and B.
-    ImageSpec newspec = ImageSpec ();
-    ROI union_AB = roi_union (get_roi(specA), get_roi(specB));
-    set_roi (newspec, union_AB);
-    if ((! has_alpha_A && ! has_alpha_B)
-        || (has_alpha_A && ! has_alpha_B && alpha_A == channels_A - 1)
-        || (! has_alpha_A && has_alpha_B && alpha_B == channels_B - 1)) {
-        if (! initialized_R) {
-            newspec.nchannels = 4;
-            newspec.alpha_channel =  3;
-            R.reset ("over", newspec);
-        } else {
-            if (non_alpha_R != 3 || alpha_R != 3) {
-                R.error ("unsupported channel layout");
-                return false;
-            }
-        }
-    } else if (has_alpha_A && has_alpha_B && alpha_A == alpha_B) {
-        if (! initialized_R) {
-            newspec.nchannels = channels_A;
-            newspec.alpha_channel =  alpha_A;
-            R.reset ("over", newspec);
-        } else {
-            if (non_alpha_R != non_alpha_A || alpha_R != alpha_A) {
-                R.error ("unsupported channel layout");
-                return false;
-            }
-        }
-    } else {
-        R.error ("unsupported channel layout");
-        return false;
+    if (! initialized_R) {
+        ImageSpec newspec = specA;
+        set_roi (newspec, roi_union (get_roi(specA), get_roi(specB)));
+        R.reset ("zover", newspec);
     }
 
     // Specified ROI -> use it. Unspecified ROI -> initialize from R.
@@ -1418,8 +1491,8 @@ ImageBufAlgo::over (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, ROI roi,
         roi = get_roi (R.spec());
 
     parallel_image (boost::bind (over_impl<float,float,float>, boost::ref(R),
-                                 boost::cref(A), boost::cref(B), _1),
-                           roi, nthreads);
+                                 boost::cref(A), boost::cref(B), _1, true),
+                    roi, nthreads);
     return ! R.has_error();
 }
 
diff --git a/src/libOpenImageIO/imageinput.cpp b/src/libOpenImageIO/imageinput.cpp
index d7ea193..b3dd7f6 100644
--- a/src/libOpenImageIO/imageinput.cpp
+++ b/src/libOpenImageIO/imageinput.cpp
@@ -41,6 +41,9 @@
 #include "imageio.h"
 #include "imageio_pvt.h"
 
+#include <boost/scoped_array.hpp>
+
+
 OIIO_NAMESPACE_ENTER
 {
     using namespace pvt;
@@ -200,7 +203,7 @@ ImageInput::read_scanlines (int ybegin, int yend, int z,
 
     const imagesize_t limit = 16*1024*1024;   // Allocate 16 MB, or 1 scanline
     int chunk = std::max (1, int(limit / native_scanline_bytes));
-    std::vector<unsigned char> buf (chunk * native_scanline_bytes);
+    boost::scoped_array<char> buf (new char [chunk * native_scanline_bytes]);
 
     bool ok = true;
     int scanline_values = m_spec.width * nchans;
@@ -281,7 +284,7 @@ ImageInput::read_native_scanlines (int ybegin, int yend, int z,
 
     size_t native_pixel_bytes = m_spec.pixel_bytes (true);
     size_t native_ystride = m_spec.width * native_pixel_bytes;
-    std::vector<char> buf (native_ystride);
+    boost::scoped_array<char> buf (new char [native_ystride]);
     yend = std::min (yend, spec().y+spec().height);
     for (int y = ybegin;  y < yend;  ++y) {
         bool ok = read_native_scanline (y, z, &buf[0]);
@@ -333,7 +336,7 @@ ImageInput::read_tile (int x, int y, int z, TypeDesc format, void *data,
     // Complex case -- either changing data type or stride
     size_t tile_values = (size_t)m_spec.tile_pixels() * m_spec.nchannels;
 
-    std::vector<char> buf (m_spec.tile_bytes(true));
+    boost::scoped_array<char> buf (new char [m_spec.tile_bytes(true)]);
     bool ok = read_native_tile (x, y, z, &buf[0]);
     if (! ok)
         return false;
@@ -510,7 +513,7 @@ ImageInput::read_native_tiles (int xbegin, int xend, int ybegin, int yend,
     stride_t tilezstride = tileystride * m_spec.tile_height;
     stride_t ystride = (xend-xbegin) * pixel_bytes;
     stride_t zstride = (yend-ybegin) * ystride;
-    std::vector<char> pels (m_spec.tile_bytes(true));
+    boost::scoped_array<char> pels (new char [m_spec.tile_bytes(true)]);
     for (int z = zbegin;  z < zend;  z += m_spec.tile_depth) {
         for (int y = ybegin;  y < yend;  y += m_spec.tile_height) {
             for (int x = xbegin;  x < xend;  x += m_spec.tile_width) {
@@ -562,7 +565,7 @@ ImageInput::read_native_tiles (int xbegin, int xend, int ybegin, int yend,
     stride_t subset_ystride = (xend-xbegin) * subset_bytes;
     stride_t subset_zstride = (yend-ybegin) * subset_ystride;
 
-    std::vector<char> pels (m_spec.tile_bytes(true));
+    boost::scoped_array<char> pels (new char [m_spec.tile_bytes(true)]);
     for (int z = zbegin;  z < zend;  z += m_spec.tile_depth) {
         for (int y = ybegin;  y < yend;  y += m_spec.tile_height) {
             for (int x = xbegin;  x < xend;  x += m_spec.tile_width) {
diff --git a/src/libOpenImageIO/imageio.cpp b/src/libOpenImageIO/imageio.cpp
index 6112cf9..434eb4e 100644
--- a/src/libOpenImageIO/imageio.cpp
+++ b/src/libOpenImageIO/imageio.cpp
@@ -228,10 +228,6 @@ pvt::contiguize (const void *src, int nchannels,
         return _contiguize ((const float *)src, nchannels, 
                             xstride, ystride, zstride,
                             (float *)dst, width, height, depth);
-    case TypeDesc::DOUBLE :
-        return _contiguize ((const double *)src, nchannels, 
-                            xstride, ystride, zstride,
-                            (double *)dst, width, height, depth);
     case TypeDesc::INT8:
     case TypeDesc::UINT8 :
         return _contiguize ((const char *)src, nchannels, 
@@ -254,6 +250,10 @@ pvt::contiguize (const void *src, int nchannels,
         return _contiguize ((const long long *)src, nchannels, 
                             xstride, ystride, zstride,
                             (long long *)dst, width, height, depth);
+    case TypeDesc::DOUBLE :
+        return _contiguize ((const double *)src, nchannels, 
+                            xstride, ystride, zstride,
+                            (double *)dst, width, height, depth);
     default:
         ASSERT (0 && "OpenImageIO::contiguize : bad format");
         return NULL;
@@ -269,24 +269,21 @@ pvt::convert_to_float (const void *src, float *dst, int nvals,
     switch (format.basetype) {
     case TypeDesc::FLOAT :
         return (float *)src;
+    case TypeDesc::UINT8 :
+        convert_type ((const unsigned char *)src, dst, nvals);
+        break;
     case TypeDesc::HALF :
         convert_type ((const half *)src, dst, nvals);
         break;
-    case TypeDesc::DOUBLE :
-        convert_type ((const double *)src, dst, nvals);
+    case TypeDesc::UINT16 :
+        convert_type ((const unsigned short *)src, dst, nvals);
         break;
     case TypeDesc::INT8:
         convert_type ((const char *)src, dst, nvals);
         break;
-    case TypeDesc::UINT8 :
-        convert_type ((const unsigned char *)src, dst, nvals);
-        break;
     case TypeDesc::INT16 :
         convert_type ((const short *)src, dst, nvals);
         break;
-    case TypeDesc::UINT16 :
-        convert_type ((const unsigned short *)src, dst, nvals);
-        break;
     case TypeDesc::INT :
         convert_type ((const int *)src, dst, nvals);
         break;
@@ -299,6 +296,9 @@ pvt::convert_to_float (const void *src, float *dst, int nvals,
     case TypeDesc::UINT64 :
         convert_type ((const unsigned long long *)src, dst, nvals);
         break;
+    case TypeDesc::DOUBLE :
+        convert_type ((const double *)src, dst, nvals);
+        break;
     default:
         ASSERT (0 && "ERROR to_float: bad format");
         return NULL;
@@ -400,8 +400,7 @@ pvt::convert_from_float (const float *src, void *dst, size_t nvals,
 
 bool
 convert_types (TypeDesc src_type, const void *src, 
-               TypeDesc dst_type, void *dst, int n,
-               int alpha_channel, int z_channel)
+               TypeDesc dst_type, void *dst, int n)
 {
     // If no conversion is necessary, just memcpy
     if ((src_type == dst_type || dst_type.basetype == TypeDesc::UNKNOWN)) {
@@ -409,39 +408,29 @@ convert_types (TypeDesc src_type, const void *src,
         return true;
     }
 
-    // Conversions are via a temporary float array
-    bool use_tmp = false;
-    boost::scoped_array<float> tmp;
-    float *buf;
-    if (src_type == TypeDesc::FLOAT) {
-        buf = (float *) src;
-    } else {
-        tmp.reset (new float[n]);  // Will be freed when tmp exists its scope
-        buf = tmp.get();
-        use_tmp = true;
+    if (dst_type == TypeDesc::TypeFloat) {
+        // Special case -- converting non-float to float
+        pvt::convert_to_float (src, (float *)dst, n, src_type);
+        return true;
     }
 
-    if (use_tmp) {
-        // Convert from 'src_type' to float (or nothing, if already float)
-        switch (src_type.basetype) {
-        case TypeDesc::UINT8 : convert_type ((const unsigned char *)src, buf, n); break;
-        case TypeDesc::UINT16 : convert_type ((const unsigned short *)src, buf, n); break;
-        case TypeDesc::FLOAT : convert_type ((const float *)src, buf, n); break;
-        case TypeDesc::HALF :  convert_type ((const half *)src, buf, n); break;
-        case TypeDesc::DOUBLE : convert_type ((const double *)src, buf, n); break;
-        case TypeDesc::INT8 :  convert_type ((const char *)src, buf, n);  break;
-        case TypeDesc::INT16 : convert_type ((const short *)src, buf, n); break;
-        case TypeDesc::INT :   convert_type ((const int *)src, buf, n); break;
-        case TypeDesc::UINT :  convert_type ((const unsigned int *)src, buf, n);  break;
-        case TypeDesc::INT64 : convert_type ((const long long *)src, buf, n); break;
-        case TypeDesc::UINT64 : convert_type ((const unsigned long long *)src, buf, n);  break;
-        default:         return false;  // unknown format
+    // Conversion is to a non-float type
+
+    boost::scoped_array<float> tmp;   // In case we need a lot of temp space
+    float *buf = (float *)src;
+    if (src_type != TypeDesc::TypeFloat) {
+        // If src is also not float, convert through an intermediate buffer
+        if (n <= 4096)  // If < 16k, use the stack
+            buf = ALLOCA (float, n);
+        else {
+            tmp.reset (new float[n]);  // Freed when tmp exists its scope
+            buf = tmp.get();
         }
+        pvt::convert_to_float (src, buf, n, src_type);
     }
 
-    // Convert float to 'dst_type' (just a copy if dst is float)
+    // Convert float to 'dst_type'
     switch (dst_type.basetype) {
-    case TypeDesc::FLOAT :  memcpy (dst, buf, n * sizeof(float));       break;
     case TypeDesc::UINT8 :  convert_type (buf, (unsigned char *)dst, n);  break;
     case TypeDesc::UINT16 : convert_type (buf, (unsigned short *)dst, n); break;
     case TypeDesc::HALF :   convert_type (buf, (half *)dst, n);   break;
@@ -460,6 +449,17 @@ convert_types (TypeDesc src_type, const void *src,
 
 
 
+// Deprecated version -- keep for link compatibiity
+bool
+convert_types (TypeDesc src_type, const void *src, 
+               TypeDesc dst_type, void *dst, int n,
+               int alpha_channel, int z_channel)
+{
+    return convert_types (src_type, src, dst_type, dst, n);
+}
+
+
+
 bool
 convert_image (int nchannels, int width, int height, int depth,
                const void *src, TypeDesc src_type,
diff --git a/src/libOpenImageIO/imageoutput.cpp b/src/libOpenImageIO/imageoutput.cpp
index 6ba0775..eaeac6e 100644
--- a/src/libOpenImageIO/imageoutput.cpp
+++ b/src/libOpenImageIO/imageoutput.cpp
@@ -45,6 +45,8 @@
 #include "imageio.h"
 #include "imageio_pvt.h"
 
+#include <boost/scoped_array.hpp>
+
 
 OIIO_NAMESPACE_ENTER
 {
@@ -485,7 +487,7 @@ ImageOutput::copy_image (ImageInput *in)
     // a time, to minimize mem footprint.
     bool native = supports("channelformats") && inspec.channelformats.size();
     TypeDesc format = native ? TypeDesc::UNKNOWN : inspec.format;
-    std::vector<char> pixels (inspec.image_bytes(native));
+    boost::scoped_array<char> pixels (new char [inspec.image_bytes(native)]);
     bool ok = in->read_image (format, &pixels[0]);
     if (ok)
         ok = write_image (format, &pixels[0]);
diff --git a/src/libOpenImageIO/imagespeed_test.cpp b/src/libOpenImageIO/imagespeed_test.cpp
index 4d926d3..de175dd 100644
--- a/src/libOpenImageIO/imagespeed_test.cpp
+++ b/src/libOpenImageIO/imagespeed_test.cpp
@@ -40,23 +40,30 @@
 #include "unittest.h"
 
 #include <iostream>
+#include <vector>
+
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
 
 OIIO_NAMESPACE_USING;
 
 static bool verbose = false;
 static int iterations = 1;
 static int ntrials = 1;
-static ustring input_filename;
+static int numthreads = 0;
+static int autotile_size = 64;
+static bool iter_only = false;
+static std::vector<ustring> input_filename;
 static std::vector<char> buffer;
 static ImageCache *imagecache = NULL;
-static ImageSpec spec;
+static imagesize_t total_image_pixels = 0;
 
 
 
 static int
 parse_files (int argc, const char *argv[])
 {
-    input_filename = ustring(argv[0]);
+    input_filename.push_back (ustring(argv[0]));
     return 0;
 }
 
@@ -69,16 +76,18 @@ getargs (int argc, char *argv[])
     ArgParse ap;
     ap.options ("imagespeed_test\n"
                 OIIO_INTRO_STRING "\n"
-                "Usage:  imagespeed_test [options]",
+                "Usage:  imagespeed_test [options] filename...",
                 "%*", parse_files, "",
                 "--help", &help, "Print help message",
                 "-v", &verbose, "Verbose mode",
-//                "--threads %d", &numthreads, 
-//                    ustring::format("Number of threads (default: %d)", numthreads).c_str(),
+                "--threads %d", &numthreads, 
+                    ustring::format("Number of threads (default: %d)", numthreads).c_str(),
                 "--iters %d", &iterations,
                     ustring::format("Number of iterations (default: %d)", iterations).c_str(),
                 "--trials %d", &ntrials, "Number of trials",
-//                "--wedge", &wedge, "Do a wedge test",
+                "--autotile %d", &autotile_size, 
+                    ustring::format("Autotile size (when used; default: %d)", autotile_size).c_str(),
+                "--iteronly", &iter_only, "Run iteration tests only (not read tests)",
                 NULL);
     if (ap.parse (argc, (const char**)argv) < 0) {
         std::cerr << ap.geterror() << std::endl;
@@ -96,11 +105,53 @@ getargs (int argc, char *argv[])
 static void
 time_read_image ()
 {
-    ImageInput *in = ImageInput::open (input_filename.c_str());
-    ASSERT (in);
-    in->read_image (TypeDesc::TypeFloat, &buffer[0]);
-    in->close ();
-    delete in;
+    BOOST_FOREACH (ustring filename, input_filename) {
+        ImageInput *in = ImageInput::open (filename.c_str());
+        ASSERT (in);
+        in->read_image (TypeDesc::TypeFloat, &buffer[0]);
+        in->close ();
+        delete in;
+    }
+}
+
+
+
+static void
+time_read_scanline_at_a_time ()
+{
+    BOOST_FOREACH (ustring filename, input_filename) {
+        ImageInput *in = ImageInput::open (filename.c_str());
+        ASSERT (in);
+        const ImageSpec &spec (in->spec());
+        size_t pixelsize = spec.nchannels * sizeof(float);
+        imagesize_t scanlinesize = spec.width * pixelsize;
+        for (int y = 0; y < spec.height;  ++y) {
+            in->read_scanline (y+spec.y, 0, TypeDesc::TypeFloat,
+                               &buffer[scanlinesize*y]);
+        }
+        in->close ();
+        delete in;
+    }
+}
+
+
+
+static void
+time_read_64_scanlines_at_a_time ()
+{
+    BOOST_FOREACH (ustring filename, input_filename) {
+        ImageInput *in = ImageInput::open (filename.c_str());
+        ASSERT (in);
+        const ImageSpec &spec (in->spec());
+        size_t pixelsize = spec.nchannels * sizeof(float);
+        imagesize_t scanlinesize = spec.width * pixelsize;
+        for (int y = 0; y < spec.height;  y += 64) {
+            in->read_scanlines (y+spec.y, std::min(y+spec.y+64, spec.y+spec.height),
+                                0, TypeDesc::TypeFloat, &buffer[scanlinesize*y]);
+        }
+        in->close ();
+        delete in;
+    }
 }
 
 
@@ -108,9 +159,11 @@ time_read_image ()
 static void
 time_read_imagebuf ()
 {
-    ImageBuf ib (input_filename.string(), imagecache);
-    ib.read (0, 0, true, TypeDesc::TypeFloat);
     imagecache->invalidate_all (true);
+    BOOST_FOREACH (ustring filename, input_filename) {
+        ImageBuf ib (filename.string(), imagecache);
+        ib.read (0, 0, true, TypeDesc::TypeFloat);
+    }
 }
 
 
@@ -118,11 +171,178 @@ time_read_imagebuf ()
 static void
 time_ic_get_pixels ()
 {
-    imagecache->get_pixels (input_filename, 0, 0, spec.x, spec.x+spec.width,
-                            spec.y, spec.y+spec.height,
-                            spec.z, spec.z+spec.depth,
-                            TypeDesc::TypeFloat, &buffer[0]);
     imagecache->invalidate_all (true);
+    BOOST_FOREACH (ustring filename, input_filename) {
+        const ImageSpec spec = (*imagecache->imagespec (filename));
+        imagecache->get_pixels (filename, 0, 0, spec.x, spec.x+spec.width,
+                                spec.y, spec.y+spec.height,
+                                spec.z, spec.z+spec.depth,
+                                TypeDesc::TypeFloat, &buffer[0]);
+    }
+}
+
+
+
+static void
+test_read (const std::string &explanation,
+           void (*func)(), int autotile=64, int autoscanline=1)
+{
+    imagecache->invalidate_all (true);  // Don't hold anything
+    imagecache->attribute ("autotile", autotile);
+    imagecache->attribute ("autoscanline", autoscanline);
+    double t = time_trial (func, ntrials);
+    double rate = double(total_image_pixels) / t;
+    std::cout << "  " << explanation << ": "
+              << Strutil::timeintervalformat(t,2) 
+              << " = " << Strutil::format("%5.1f",rate/1.0e6) << " Mpel/s\n";
+}
+
+
+
+static float
+time_loop_pixels_1D (ImageBuf &ib, int iters)
+{
+    ASSERT (ib.localpixels() && ib.pixeltype() == TypeDesc::TypeFloat);
+    const ImageSpec &spec (ib.spec());
+    imagesize_t npixels = spec.image_pixels();
+    int nchannels = spec.nchannels;
+    double sum = 0.0f;
+    for (int i = 0;  i < iters;  ++i) {
+        const float *f = (const float *) ib.pixeladdr (spec.x, spec.y, spec.z);
+        ASSERT (f);
+        for (imagesize_t p = 0;  p < npixels;  ++p) {
+            sum += f[0];
+            f += nchannels;
+        }
+    }
+    // std::cout << float(sum/npixels/iters) << "\n";
+    return float(sum/npixels/iters);
+}
+
+
+
+static float
+time_loop_pixels_3D (ImageBuf &ib, int iters)
+{
+    ASSERT (ib.localpixels() && ib.pixeltype() == TypeDesc::TypeFloat);
+    const ImageSpec &spec (ib.spec());
+    imagesize_t npixels = spec.image_pixels();
+    int nchannels = spec.nchannels;
+    double sum = 0.0f;
+    for (int i = 0;  i < iters;  ++i) {
+        const float *f = (const float *) ib.pixeladdr (spec.x, spec.y, spec.z);
+        ASSERT (f);
+        for (int z = spec.z, ze = spec.z+spec.depth; z < ze; ++z) {
+            for (int y = spec.y, ye = spec.y+spec.height; y < ye; ++y) {
+                for (int x = spec.x, xe = spec.x+spec.width; x < xe; ++x) {
+                    sum += f[0];
+                    f += nchannels;
+                }
+            }
+        }
+    }
+    // std::cout << float(sum/npixels/iters) << "\n";
+    return float(sum/npixels/iters);
+}
+
+
+
+static float
+time_loop_pixels_3D_getchannel (ImageBuf &ib, int iters)
+{
+    ASSERT (ib.pixeltype() == TypeDesc::TypeFloat);
+    const ImageSpec &spec (ib.spec());
+    imagesize_t npixels = spec.image_pixels();
+    double sum = 0.0f;
+    for (int i = 0;  i < iters;  ++i) {
+        for (int z = spec.z, ze = spec.z+spec.depth; z < ze; ++z) {
+            for (int y = spec.y, ye = spec.y+spec.height; y < ye; ++y) {
+                for (int x = spec.x, xe = spec.x+spec.width; x < xe; ++x) {
+                    sum += ib.getchannel (x, y, 0);
+                }
+            }
+        }
+    }
+    // std::cout << float(sum/npixels/iters) << "\n";
+    return float(sum/npixels/iters);
+}
+
+
+
+static float
+time_iterate_pixels (ImageBuf &ib, int iters)
+{
+    ASSERT (ib.pixeltype() == TypeDesc::TypeFloat);
+    const ImageSpec &spec (ib.spec());
+    imagesize_t npixels = spec.image_pixels();
+    double sum = 0.0f;
+    for (int i = 0;  i < iters;  ++i) {
+        for (ImageBuf::ConstIterator<float,float> p (ib);  !p.done();  ++p) {
+            sum += p[0];
+        }
+    }
+    // std::cout << float(sum/npixels/iters) << "\n";
+    return float(sum/npixels/iters);
+}
+
+
+
+static float
+time_iterate_pixels_slave_pos (ImageBuf &ib, int iters)
+{
+    ASSERT (ib.pixeltype() == TypeDesc::TypeFloat);
+    const ImageSpec &spec (ib.spec());
+    imagesize_t npixels = spec.image_pixels();
+    double sum = 0.0f;
+    for (int i = 0;  i < iters;  ++i) {
+        ImageBuf::ConstIterator<float,float> slave (ib);
+        for (ImageBuf::ConstIterator<float,float> p (ib);  !p.done();  ++p) {
+            slave.pos (p.x(), p.y());
+            sum += p[0];
+        }
+    }
+    // std::cout << float(sum/npixels/iters) << "\n";
+    return float(sum/npixels/iters);
+}
+
+
+
+static float
+time_iterate_pixels_slave_incr (ImageBuf &ib, int iters)
+{
+    ASSERT (ib.pixeltype() == TypeDesc::TypeFloat);
+    const ImageSpec &spec (ib.spec());
+    imagesize_t npixels = spec.image_pixels();
+    double sum = 0.0f;
+    for (int i = 0;  i < iters;  ++i) {
+        ImageBuf::ConstIterator<float,float> slave (ib);
+        for (ImageBuf::ConstIterator<float,float> p (ib);  !p.done();  ++p) {
+            sum += p[0];
+            ++slave;
+        }
+    }
+    // std::cout << float(sum/npixels/iters) << "\n";
+    return float(sum/npixels/iters);
+}
+
+
+
+static void
+test_pixel_iteration (const std::string &explanation,
+                      float (*func)(ImageBuf&,int),
+                      bool preload, int iters=100, int autotile=64)
+{
+    imagecache->invalidate_all (true);  // Don't hold anything
+    // Force the whole image to be read at once
+    imagecache->attribute ("autotile", autotile);
+    imagecache->attribute ("autoscanline", 1);
+    ImageBuf ib (input_filename[0].string(), imagecache);
+    ib.read (0, 0, preload, TypeDesc::TypeFloat);
+    double t = time_trial (boost::bind(func,boost::ref(ib),iters), ntrials);
+    double rate = double(ib.spec().image_pixels()) / (t/iters);
+    std::cout << "  " << explanation << ": "
+              << Strutil::timeintervalformat(t/iters,3) 
+              << " = " << Strutil::format("%5.1f",rate/1.0e6) << " Mpel/s\n";
 }
 
 
@@ -131,37 +351,88 @@ int
 main (int argc, char **argv)
 {
     getargs (argc, argv);
-
-    if (input_filename.empty()) {
+    if (input_filename.size() == 0) {
         std::cout << "Error: Must supply a filename.\n";
         return -1;
     }
 
+    OIIO::attribute ("threads", numthreads);
+
     imagecache = ImageCache::create ();
     imagecache->attribute ("forcefloat", 1);
 
     // Allocate a buffer big enough (for floats)
-    bool ok = imagecache->get_imagespec (input_filename, spec);
-    ASSERT (ok);
-    imagecache->invalidate_all (true);  // Don't hold anything
-    buffer.resize (spec.image_pixels()*spec.nchannels*sizeof(float), 0);
-
-    {
-        double t = time_trial (time_read_image, ntrials);
-        std::cout << "image_read speed: " << Strutil::timeintervalformat(t,2) << "\n";
-    }
-
-    {
-        double t = time_trial (time_read_imagebuf, ntrials);
-        std::cout << "ImageBuf read speed: " << Strutil::timeintervalformat(t,2) << "\n";
+    bool all_scanline = true;
+    total_image_pixels = 0;
+    imagesize_t maxpelchans = 0;
+    for (size_t i = 0;  i < input_filename.size();  ++i) {
+        ImageSpec spec;
+        if (! imagecache->get_imagespec (input_filename[i], spec, 0, 0, true)) {
+            std::cout << "File \"" << input_filename[i] << "\" could not be opened.\n";
+            return -1;
+        }
+        total_image_pixels += spec.image_pixels();
+        maxpelchans = std::max (maxpelchans, spec.image_pixels()*spec.nchannels);
+        all_scanline &= (spec.tile_width == 0);
     }
+    imagecache->invalidate_all (true);  // Don't hold anything
 
-    {
-        double t = time_trial (time_ic_get_pixels, ntrials);
-        std::cout << "ImageCache get_pixels speed: " << Strutil::timeintervalformat(t,2) << "\n";
+    if (! iter_only) {
+        std::cout << "Timing various ways of reading images:\n";
+        buffer.resize (maxpelchans*sizeof(float), 0);
+        test_read ("read_image                                   ",
+                   time_read_image, 0, 0);
+        if (all_scanline) {
+            test_read ("read_scanline (1 at a time)                  ",
+                       time_read_scanline_at_a_time, 0, 0);
+            test_read ("read_scanlines (64 at a time)                ",
+                       time_read_64_scanlines_at_a_time, 0, 0);
+        }
+        test_read ("ImageBuf read                                ",
+                   time_read_imagebuf, 0, 0);
+        test_read ("ImageCache get_pixels                        ",
+                   time_ic_get_pixels, 0, 0);
+        test_read ("ImageBuf read (autotile)                     ",
+                   time_read_imagebuf, autotile_size, 0);
+        test_read ("ImageCache get_pixels (autotile)             ",
+                   time_ic_get_pixels, autotile_size, 0);
+        if (all_scanline) {  // don't bother for tiled images
+            test_read ("ImageBuf read (autotile+autoscanline)        ",
+                       time_read_imagebuf, autotile_size, 1);
+            test_read ("ImageCache get_pixels (autotile+autoscanline)",
+                       time_ic_get_pixels, autotile_size, 1);
+        }
+        if (verbose)
+            std::cout << "\n" << imagecache->getstats(2) << "\n";
+        std::cout << "\n";
     }
 
-    imagecache->invalidate_all (true);  // Don't hold anything
+    const int iters = 64;
+    std::cout << "Timing ways of iterating over an image:\n";
+
+    test_pixel_iteration ("Loop pointers on loaded image (\"1D\")    ",
+                          time_loop_pixels_1D, true, iters);
+    test_pixel_iteration ("Loop pointers on loaded image (\"3D\")    ",
+                          time_loop_pixels_3D, true, iters);
+    test_pixel_iteration ("Loop + getchannel on loaded image (\"3D\")",
+                          time_loop_pixels_3D_getchannel, true, iters/32);
+    test_pixel_iteration ("Loop + getchannel on cached image (\"3D\")",
+                          time_loop_pixels_3D_getchannel, false, iters/32);
+    test_pixel_iteration ("Iterate over a loaded image             ",
+                          time_iterate_pixels, true, iters);
+    test_pixel_iteration ("Iterate over a cache image              ",
+                          time_iterate_pixels, false, iters);
+    test_pixel_iteration ("Iterate over a loaded image (pos slave)",
+                          time_iterate_pixels_slave_pos, true, iters);
+    test_pixel_iteration ("Iterate over a cache image (pos slave) ",
+                          time_iterate_pixels_slave_pos, false, iters);
+    test_pixel_iteration ("Iterate over a loaded image (incr slave)",
+                          time_iterate_pixels_slave_incr, true, iters);
+    test_pixel_iteration ("Iterate over a cache image (incr slave) ",
+                          time_iterate_pixels_slave_incr, false, iters);
+
+    if (verbose)
+        std::cout << "\n" << imagecache->getstats(2) << "\n";
 
     ImageCache::destroy (imagecache);
     return unit_test_failures;
diff --git a/src/maketx/maketx.cpp b/src/libOpenImageIO/maketexture.cpp
similarity index 54%
copy from src/maketx/maketx.cpp
copy to src/libOpenImageIO/maketexture.cpp
index ca17b9d..503bada 100644
--- a/src/maketx/maketx.cpp
+++ b/src/libOpenImageIO/maketexture.cpp
@@ -1,5 +1,5 @@
 /*
-  Copyright 2008 Larry Gritz and the other authors and contributors.
+  Copyright 2013 Larry Gritz and the other authors and contributors.
   All Rights Reserved.
 
   Redistribution and use in source and binary forms, with or without
@@ -57,133 +57,7 @@
 OIIO_NAMESPACE_USING
 
 
-// # FIXME: Refactor all statics into a struct
-
-// Basic runtime options
-static std::string full_command_line;
-static std::vector<std::string> filenames;
-static std::string outputfilename;
-static std::string dataformatname = "";
-static std::string fileformatname = "";
-static std::vector<std::string> mipimages;
-//static float ingamma = 1.0f, outgamma = 1.0f;
-static bool verbose = false;
-static bool stats = false;
-static int nthreads = 0;    // default: use #cores threads if available
-static int tile[3] = { 64, 64, 1 };
-static std::string channellist;
-static std::string compression = "zip";
-static bool updatemode = false;
-static double stat_readtime = 0;
-static double stat_writetime = 0;
-static double stat_resizetime = 0;
-static double stat_miptime = 0;
-static double stat_colorconverttime = 0;
-static bool checknan = false;
-static std::string fixnan = "none"; // none, black, box3
-static bool set_full_to_pixels = false;
-static int found_nonfinite = 0;
 static spin_mutex maketx_mutex;   // for anything that needs locking
-static std::string filtername = "box";
-static Filter2D *filter = NULL;
-
-// Conversion modes.  If none are true, we just make an ordinary texture.
-static bool mipmapmode = false;
-static bool shadowmode = false;
-static bool shadowcubemode = false;
-static bool volshadowmode = false;
-static bool envlatlmode = false;
-static bool envcubemode = false;
-static bool lightprobemode = false;
-static bool vertcrossmode = false;
-static bool latl2envcubemode = false;
-
-// Options controlling file metadata or mipmap creation
-static float fov = 90;
-static float fovcot = 0.0f;
-static std::string wrap = "black";
-static std::string swrap;
-static std::string twrap;
-static bool doresize = false;
-//static float opaquewidth = 0;  // should be volume shadow epsilon
-static Imath::M44f Mcam(0.0f), Mscr(0.0f);  // Initialize to 0
-static bool separate = false;
-static bool nomipmap = false;
-static bool embed_hash = false; // Ignored.
-static bool prman_metadata = false;
-static bool constant_color_detect = false;
-static bool monochrome_detect = false;
-static bool opaque_detect = false;
-static int nchannels = -1;
-static bool prman = false;
-static bool oiio = false;
-static bool src_samples_border = false; // are src edge samples on the border?
-static bool ignore_unassoc = false;  // ignore unassociated alpha tags
-
-static bool unpremult = false;
-static std::string incolorspace;
-static std::string outcolorspace;
-static ColorConfig colorconfig;
-
-
-// forward decl
-static void write_mipmap (ImageBuf &img, const ImageSpec &outspec_template,
-                          std::string outputfilename, ImageOutput *out,
-                          TypeDesc outputdatatype, bool mipmap);
-
-
-
-static std::string
-filter_help_string ()
-{
-    std::string s ("Select filter for resizing (choices:");
-    for (int i = 0, e = Filter2D::num_filters();  i < e;  ++i) {
-        FilterDesc d;
-        Filter2D::get_filterdesc (i, &d);
-        s.append (" ");
-        s.append (d.name);
-    }
-    s.append (", default=box)");
-    return s;
-}
-
-
-
-static std::string
-colortitle_help_string ()
-{
-    std::string s ("Color Management Options ");
-    if(ColorConfig::supportsOpenColorIO()) {
-        s += "(OpenColorIO enabled)";
-    }
-    else {
-        s += "(OpenColorIO DISABLED)";
-    }
-    return s;
-}
-
-
-
-static std::string
-colorconvert_help_string ()
-{
-    std::string s = "Apply a color space conversion to the image. "
-    "If the output color space is not the same bit depth "
-    "as input color space, it is your responsibility to set the data format "
-    "to the proper bit depth using the -d option. ";
-    
-    s += " (choices: ";
-    if (colorconfig.error() || colorconfig.getNumColorSpaces()==0) {
-        s += "NONE";
-    } else {
-        for (int i=0; i < colorconfig.getNumColorSpaces(); ++i) {
-            if (i!=0) s += ", ";
-            s += colorconfig.getColorSpaceNameByIndex(i);
-        }
-    }
-    s += ")";
-    return s;
-}
 
 
 
@@ -208,199 +82,55 @@ setup_filter (const std::string &filtername)
 
 
 
-static int
-parse_files (int argc, const char *argv[])
-{
-    for (int i = 0;  i < argc;  i++)
-        filenames.push_back (argv[i]);
-    return 0;
-}
-
-
-
-static void
-getargs (int argc, char *argv[])
-{
-    bool help = false;
-    
-    ArgParse ap;
-    ap.options ("maketx -- convert images to tiled, MIP-mapped textures\n"
-                OIIO_INTRO_STRING "\n"
-                "Usage:  maketx [options] file...",
-                  "%*", parse_files, "",
-                  "--help", &help, "Print help message",
-                  "-v", &verbose, "Verbose status messages",
-                  "-o %s", &outputfilename, "Output filename",
-                  "--threads %d", &nthreads, "Number of threads (default: #cores)",
-                  "-u", &updatemode, "Update mode",
-                  "--format %s", &fileformatname, "Specify output file format (default: guess from extension)",
-                  "--nchannels %d", &nchannels, "Specify the number of output image channels.",
-                  "-d %s", &dataformatname, "Set the output data format to one of: "
-                          "uint8, sint8, uint16, sint16, half, float",
-                  "--tile %d %d", &tile[0], &tile[1], "Specify tile size",
-                  "--separate", &separate, "Use planarconfig separate (default: contiguous)",
-                  "--compression %s", &compression, "Set the compression method (default = zip, if possible)",
-                  "--fov %f", &fov, "Field of view for envcube/shadcube/twofish",
-                  "--fovcot %f", &fovcot, "Override the frame aspect ratio. Default is width/height.",
-                  "--wrap %s", &wrap, "Specify wrap mode (black, clamp, periodic, mirror)",
-                  "--swrap %s", &swrap, "Specific s wrap mode separately",
-                  "--twrap %s", &twrap, "Specific t wrap mode separately",
-                  "--resize", &doresize, "Resize textures to power of 2 (default: no)",
-                  "--noresize %!", &doresize, "Do not resize textures to power of 2 (deprecated)",
-                  "--filter %s", &filtername, filter_help_string().c_str(),
-                  "--nomipmap", &nomipmap, "Do not make multiple MIP-map levels",
-                  "--checknan", &checknan, "Check for NaN/Inf values (abort if found)",
-                  "--fixnan %s", &fixnan, "Attempt to fix NaN/Inf values in the image (options: none, black, box3)",
-                  "--fullpixels", &set_full_to_pixels, "Set the 'full' image range to be the pixel data window",
-                  "--Mcamera %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f",
-                          &Mcam[0][0], &Mcam[0][1], &Mcam[0][2], &Mcam[0][3], 
-                          &Mcam[1][0], &Mcam[1][1], &Mcam[1][2], &Mcam[1][3], 
-                          &Mcam[2][0], &Mcam[2][1], &Mcam[2][2], &Mcam[2][3], 
-                          &Mcam[3][0], &Mcam[3][1], &Mcam[3][2], &Mcam[3][3], 
-                          "Set the camera matrix",
-                  "--Mscreen %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f",
-                          &Mscr[0][0], &Mscr[0][1], &Mscr[0][2], &Mscr[0][3], 
-                          &Mscr[1][0], &Mscr[1][1], &Mscr[1][2], &Mscr[1][3], 
-                          &Mscr[2][0], &Mscr[2][1], &Mscr[2][2], &Mscr[2][3], 
-                          &Mscr[3][0], &Mscr[3][1], &Mscr[3][2], &Mscr[3][3], 
-                          "Set the camera matrix",
-                  "--hash", &embed_hash, "Embed SHA-1 hash of pixels in the header",
-                  "--prman-metadata", &prman_metadata, "Add prman specific metadata",
-                  "--constant-color-detect", &constant_color_detect, "Create 1-tile textures from constant color inputs",
-                  "--monochrome-detect", &monochrome_detect, "Create 1-channel textures from monochrome inputs",
-                  "--opaque-detect", &opaque_detect, "Drop alpha channel that is always 1.0",
-                  "--ignore-unassoc", &ignore_unassoc, "Ignore unassociated alpha tags in input (don't autoconvert)",
-                  "--stats", &stats, "Print runtime statistics",
-                  "--mipimage %L", &mipimages, "Specify an individual MIP level",
-//FIXME           "-c %s", &channellist, "Restrict/shuffle channels",
-//FIXME           "-debugdso"
-//FIXME           "-note %s", &note, "Append a note to the image comments",
-                  "<SEPARATOR>", "Basic modes (default is plain texture):",
-                  "--shadow", &shadowmode, "Create shadow map",
-//                  "--shadcube", &shadowcubemode, "Create shadow cube (file order: px,nx,py,ny,pz,nz) (UNIMPLEMENTED)",
-//                  "--volshad", &volshadowmode, "Create volume shadow map (UNIMP)",
-                  "--envlatl", &envlatlmode, "Create lat/long environment map",
-                  "--envcube", &envcubemode, "Create cubic env map (file order: px, nx, py, ny, pz, nz) (UNIMP)",
-//                  "--lightprobe", &lightprobemode, "Convert a lightprobe to cubic env map (UNIMP)",
-//                  "--latl2envcube", &latl2envcubemode, "Convert a lat-long env map to a cubic env map (UNIMP)",
-//                  "--vertcross", &vertcrossmode, "Convert a vertical cross layout to a cubic env map (UNIMP)",
-                  "<SEPARATOR>", colortitle_help_string().c_str(),
-                  "--colorconvert %s %s", &incolorspace, &outcolorspace,
-                          colorconvert_help_string().c_str(),
-                  "--unpremult", &unpremult, "Unpremultiply before color conversion, then premultiply "
-                          "after the color conversion.  You'll probably want to use this flag "
-                          "if your image contains an alpha channel.",
-                  "<SEPARATOR>", "Configuration Presets",
-                  "--prman", &prman, "Use PRMan-safe settings for tile size, planarconfig, and metadata.",
-                  "--oiio", &oiio, "Use OIIO-optimized settings for tile size, planarconfig, metadata.",
-                  NULL);
-    if (ap.parse (argc, (const char**)argv) < 0) {
-        std::cerr << ap.geterror() << std::endl;
-        ap.usage ();
-        exit (EXIT_FAILURE);
-    }
-    if (help) {
-        ap.usage ();
-        exit (EXIT_FAILURE);
-    }
-    full_command_line = ap.command_line ();
-
-    int optionsum = ((int)shadowmode + (int)shadowcubemode + (int)volshadowmode +
-                     (int)envlatlmode + (int)envcubemode +
-                     (int)lightprobemode + (int)vertcrossmode +
-                     (int)latl2envcubemode);
-    if (optionsum > 1) {
-        std::cerr << "maketx ERROR: At most one of the following options may be set:\n"
-                  << "\t--shadow --shadcube --volshad --envlatl --envcube\n"
-                  << "\t--lightprobe --vertcross --latl2envcube\n";
-        ap.usage ();
-        exit (EXIT_FAILURE);
-    }
-    if (optionsum == 0)
-        mipmapmode = true;
-    
-    if (prman && oiio) {
-        std::cerr << "maketx ERROR: '--prman' compatibility, and '--oiio' optimizations are mutually exclusive.\n";
-        std::cerr << "\tIf you'd like both prman and oiio compatibility, you should choose --prman\n";
-        std::cerr << "\t(at the expense of oiio-specific optimizations)\n";
-        ap.usage ();
-        exit (EXIT_FAILURE);
-    }
-
-    if (filenames.size() < 1) {
-        std::cerr << "maketx ERROR: Must have at least one input filename specified.\n";
-        ap.usage();
-        exit (EXIT_FAILURE);
-    }
-
-    filter = setup_filter (filtername);
-    if (! filter) {
-        std::cerr << "maketx ERROR: could not make filter '" << filtername << "\n";
-        exit (EXIT_FAILURE);
-    }
-
-    if (embed_hash && verbose) {
-        std::cerr << "maketx WARNING: The --embed_hash option is deprecated, and no longer necessary.\n";
-        std::cerr << "                 (Hashes are always computed.)\n";
-    }
-    
-//    std::cout << "Converting " << filenames[0] << " to " << outputfilename << "\n";
-}
-
-
-
 static TypeDesc
-set_prman_options(TypeDesc out_dataformat)
+set_prman_options(TypeDesc out_dataformat, ImageSpec &configspec)
 {
-    // Force planar image handling, and also emit prman metadata
-    separate = true;
-    prman_metadata = true;
-    
+    // Force separate planar image handling, and also emit prman metadata
+    configspec.attribute ("planarconfig", "separate");
+    configspec.attribute ("maketx:prman_metadata", 1);
+
     // 8-bit : 64x64
-    if (out_dataformat == TypeDesc::UINT8 ||
-        out_dataformat == TypeDesc::INT8) {
-        tile[0] = 64;
-        tile[1] = 64;
+    if (out_dataformat == TypeDesc::UINT8 || out_dataformat == TypeDesc::INT8) {
+        configspec.tile_width = 64;
+        configspec.tile_height = 64;
     }
-    
+
     // 16-bit : 64x32
     // Force u16 -> s16
     // In prman's txmake (last tested in 15.0)
     // specifying -short creates a signed int representation
-    if (out_dataformat == TypeDesc::UINT16) {
+    if (out_dataformat == TypeDesc::UINT16)
         out_dataformat = TypeDesc::INT16;
+
+    if (out_dataformat == TypeDesc::INT16) {
+        configspec.tile_width = 64;
+        configspec.tile_height = 32;
     }
-    
-    if (out_dataformat == TypeDesc::UINT16 ||
-        out_dataformat == TypeDesc::INT16) {
-        tile[0] = 64;
-        tile[1] = 32;
-    }
-    
+
     // Float: 32x32
     // In prman's txmake (last tested in 15.0)
     // specifying -half or -float make 32x32 tile size
-    if (out_dataformat == TypeDesc::HALF ||
-        out_dataformat == TypeDesc::FLOAT ||
-        out_dataformat == TypeDesc::DOUBLE) {
-        tile[0] = 32;
-        tile[1] = 32;
+    if (out_dataformat == TypeDesc::DOUBLE)
+        out_dataformat = TypeDesc::FLOAT;
+    if (out_dataformat == TypeDesc::HALF || out_dataformat == TypeDesc::FLOAT) {
+        configspec.tile_width = 32;
+        configspec.tile_height = 32;
     }
-    
+
     return out_dataformat;
 }
 
 
 
 static TypeDesc
-set_oiio_options(TypeDesc out_dataformat)
+set_oiio_options(TypeDesc out_dataformat, ImageSpec &configspec)
 {
     // Interleaved channels are faster to read
-    separate = false;
+    configspec.attribute ("planarconfig", "contig");
     
     // Force fixed tile-size across the board
-    tile[0] = 64;
-    tile[1] = 64;
+    configspec.tile_width = 64;
+    configspec.tile_height = 64;
     
     return out_dataformat;
 }
@@ -419,52 +149,11 @@ datestring (time_t t)
 
 
 
-// Run func over all pixels of dst, but split into separate threads for
-// bands of the image.  Assumes that the calling profile of func is:
-//     func (dst, src, xbegin, xend, ybegin, yend);
-// Also assumes that every pixel processed is approximately the same
-// cost, so it just divides the image space into equal-sized bands without
-// worrying about any sophisticated load balancing.
-template <class Func>
-void
-parallel_image (Func func, ImageBuf *dst, const ImageBuf *src, 
-                int xbegin, int xend, int ybegin, int yend, int nthreads)
-{
-    const ImageSpec &dstspec (dst->spec());
-
-    // Don't parallelize with too few pixels
-    if (dstspec.image_pixels() < 1000)
-        nthreads = 1;
-    // nthreads < 1 means try to make enough threads to fill all cores
-    if (nthreads < 1) {
-        nthreads = boost::thread::hardware_concurrency();
-    }
-
-    if (nthreads > 1) {
-        boost::thread_group threads;
-        int blocksize = std::max (1, ((xend-xbegin) + nthreads-1) / nthreads);
-        for (int i = 0;  i < nthreads;  ++i) {
-            int x0 = xbegin + i*blocksize;
-            int x1 = std::min (xbegin + (i+1)*blocksize, xend);
-//            std::cerr << "  launching " << x0 << ' ' << x1 << ' '
-//                      << ybegin << ' ' << yend << "\n";
-            threads.add_thread (new boost::thread (func, dst, src,
-                                                   x0, x1,
-                                                   ybegin, yend));
-        }
-        threads.join_all ();
-    } else {
-        func (dst, src, xbegin, xend, ybegin, yend);
-    }
-}
-
-
-
 // Copy src into dst, but only for the range [x0,x1) x [y0,y1).
 static void
-copy_block (ImageBuf *dst, const ImageBuf *src,
-            int x0, int x1, int y0, int y1)
+copy_block (ImageBuf *dst, const ImageBuf *src, ROI roi)
 {
+    int x0 = roi.xbegin, x1 = roi.xend, y0 = roi.ybegin, y1 = roi.yend;
     const ImageSpec &dstspec (dst->spec());
     float *pel = (float *) alloca (dstspec.pixel_bytes());
     for (int y = y0;  y < y1;  ++y) {
@@ -480,16 +169,17 @@ copy_block (ImageBuf *dst, const ImageBuf *src,
 // Resize src into dst using a good quality filter, 
 // for the pixel range [x0,x1) x [y0,y1).
 static void
-resize_block_HQ (ImageBuf *dst, const ImageBuf *src,
-                 int x0, int x1, int y0, int y1)
+resize_block_HQ (ImageBuf *dst, const ImageBuf *src, ROI roi, Filter2D *filter)
 {
+    int x0 = roi.xbegin, x1 = roi.xend, y0 = roi.ybegin, y1 = roi.yend;
     ImageBufAlgo::resize (*dst, *src, x0, x1, y0, y1, filter);
 }
 
 
 
 static void
-interppixel_NDC_clamped (const ImageBuf &buf, float x, float y, float *pixel)
+interppixel_NDC_clamped (const ImageBuf &buf, float x, float y, float *pixel,
+                         bool envlatlmode)
 {
     int fx = buf.spec().full_x;
     int fy = buf.spec().full_y;
@@ -541,9 +231,9 @@ interppixel_NDC_clamped (const ImageBuf &buf, float x, float y, float *pixel)
 // interppixel_NDC_full or interppixel_NDC_clamped, for the pixel range
 // [x0,x1) x [y0,y1).
 static void
-resize_block (ImageBuf *dst, const ImageBuf *src,
-              int x0, int x1, int y0, int y1)
+resize_block (ImageBuf *dst, const ImageBuf *src, ROI roi, bool envlatlmode)
 {
+    int x0 = roi.xbegin, x1 = roi.xend, y0 = roi.ybegin, y1 = roi.yend;
     const ImageSpec &srcspec (src->spec());
     bool src_is_crop = (srcspec.x > srcspec.full_x ||
                         srcspec.y > srcspec.full_y ||
@@ -565,7 +255,7 @@ resize_block (ImageBuf *dst, const ImageBuf *src,
             if (src_is_crop)
                 src->interppixel_NDC_full (s, t, pel);
             else
-                interppixel_NDC_clamped (*src, s, t, pel);
+                interppixel_NDC_clamped (*src, s, t, pel, envlatlmode);
             dst->setpixel (x, y, pel);
         }
     }
@@ -575,9 +265,9 @@ resize_block (ImageBuf *dst, const ImageBuf *src,
 
 // Copy src into dst, but only for the range [x0,x1) x [y0,y1).
 static void
-check_nan_block (ImageBuf* /*dst*/, const ImageBuf* src,
-                 int x0, int x1, int y0, int y1)
+check_nan_block (const ImageBuf* src, ROI roi, int &found_nonfinite)
 {
+    int x0 = roi.xbegin, x1 = roi.xend, y0 = roi.ybegin, y1 = roi.yend;
     const ImageSpec &spec (src->spec());
     float *pel = (float *) alloca (spec.pixel_bytes());
     for (int y = y0;  y < y1;  ++y) {
@@ -602,7 +292,6 @@ check_nan_block (ImageBuf* /*dst*/, const ImageBuf* src,
 static void
 fix_latl_edges (ImageBuf &buf)
 {
-    ASSERT (envlatlmode && "only call fix_latl_edges for latlong maps");
     int n = buf.nchannels();
     float *left = ALLOCA (float, n);
     float *right = ALLOCA (float, n);
@@ -665,95 +354,310 @@ formatres (const ImageSpec &spec, bool extended=false)
 
 
 static void
-make_texturemap (const char *maptypename = "texture map")
+maketx_merge_spec (ImageSpec &dstspec, const ImageSpec &srcspec)
 {
-    if (filenames.size() != 1) {
-        std::cerr << "maketx ERROR: " << maptypename 
-                  << " requires exactly one input filename\n";
-        exit (EXIT_FAILURE);
+    for (size_t i = 0, e = srcspec.extra_attribs.size(); i < e; ++i) {
+        const ImageIOParameter &p (srcspec.extra_attribs[i]);
+        ustring name = p.name();
+        if (Strutil::istarts_with (name.string(), "maketx:")) {
+            // Special instruction -- don't copy it to the destination spec
+        } else {
+            // just an attribute that should be set upon output
+            dstspec.attribute (name.string(), p.type(), p.data());
+        }
+    }
+}
+
+
+
+static bool
+write_mipmap (ImageBufAlgo::MakeTextureMode mode,
+              ImageBuf &img, const ImageSpec &outspec_template,
+              std::string outputfilename, ImageOutput *out,
+              TypeDesc outputdatatype, bool mipmap,
+              Filter2D *filter, const ImageSpec &configspec,
+              std::ostream &outstream,
+              double &stat_writetime, double &stat_miptime)
+{
+    bool envlatlmode = (mode == ImageBufAlgo::MakeTxEnvLatl);
+
+    ImageSpec outspec = outspec_template;
+    outspec.set_format (outputdatatype);
+
+    if (mipmap && !out->supports ("multiimage") && !out->supports ("mipmap")) {
+        outstream << "maketx ERROR: \"" << outputfilename
+                  << "\" format does not support multires images\n";
+        return false;
+    }
+
+    if (! mipmap && ! strcmp (out->format_name(), "openexr")) {
+        // Send hint to OpenEXR driver that we won't specify a MIPmap
+        outspec.attribute ("openexr:levelmode", 0 /* ONE_LEVEL */);
+    }
+
+    if (mipmap && ! strcmp (out->format_name(), "openexr")) {
+        outspec.attribute ("openexr:roundingmode", 0 /* ROUND_DOWN */);
+    }
+
+    // OpenEXR always uses border sampling for environment maps
+    bool src_samples_border;
+    if (envlatlmode && !strcmp(out->format_name(), "openexr")) {
+        src_samples_border = true;
+        outspec.attribute ("oiio:updirection", "y");
+        outspec.attribute ("oiio:sampleborder", 1);
+    }
+    if (envlatlmode && src_samples_border)
+        fix_latl_edges (img);
+
+    Timer writetimer;
+    if (! out->open (outputfilename.c_str(), outspec)) {
+        outstream << "maketx ERROR: Could not open \"" << outputfilename
+                  << "\" : " << out->geterror() << "\n";
+        return false;
     }
 
-    if (! Filesystem::exists (filenames[0])) {
-        std::cerr << "maketx ERROR: \"" << filenames[0] << "\" does not exist\n";
-        exit (EXIT_FAILURE);
+    // Write out the image
+    bool verbose = configspec.get_int_attribute ("maketx:verbose");
+    if (verbose) {
+        outstream << "  Writing file: " << outputfilename << std::endl;
+        outstream << "  Filter \"" << filter->name() << "\" width = " 
+                  << filter->width() << "\n";
+        outstream << "  Top level is " << formatres(outspec) << std::endl;
     }
-    if (outputfilename.empty()) 
-        outputfilename = Filesystem::replace_extension (filenames[0], ".tx");
+
+    if (! img.write (out)) {
+        // ImageBuf::write transfers any errors from the ImageOutput to
+        // the ImageBuf.
+        outstream << "maketx ERROR: Write failed \" : " << img.geterror() << "\n";
+        out->close ();
+        return false;
+    }
+
+    stat_writetime += writetimer();
+
+    if (mipmap) {  // Mipmap levels:
+        if (verbose)
+            outstream << "  Mipmapping...\n" << std::flush;
+        std::vector<std::string> mipimages;
+        std::string mipimages_unsplit = configspec.get_string_attribute ("maketx:mipimages");
+        if (mipimages_unsplit.length())
+            Strutil::split (mipimages_unsplit, mipimages, ";");
+        ImageBuf tmp;
+        ImageBuf *big = &img, *small = &tmp;
+        while (outspec.width > 1 || outspec.height > 1) {
+            Timer miptimer;
+            ImageSpec smallspec;
+
+            if (mipimages.size()) {
+                // Special case -- the user specified a custom MIP level
+                small->reset (mipimages[0]);
+                small->read (0, 0, true, TypeDesc::FLOAT);
+                smallspec = small->spec();
+                if (smallspec.nchannels != outspec.nchannels) {
+                    outstream << "WARNING: Custom mip level \"" << mipimages[0]
+                              << " had the wrong number of channels.\n";
+                    ImageBuf *t = new ImageBuf (mipimages[0], smallspec);
+                    ImageBufAlgo::setNumChannels(*t, *small, outspec.nchannels);
+                    std::swap (t, small);
+                    delete t;
+                }
+                smallspec.tile_width = outspec.tile_width;
+                smallspec.tile_height = outspec.tile_height;
+                smallspec.tile_depth = outspec.tile_depth;
+                mipimages.erase (mipimages.begin());
+            } else {
+                // Resize a factor of two smaller
+                smallspec = outspec;
+                smallspec.width = big->spec().width;
+                smallspec.height = big->spec().height;
+                smallspec.depth = big->spec().depth;
+                if (smallspec.width > 1)
+                    smallspec.width /= 2;
+                if (smallspec.height > 1)
+                    smallspec.height /= 2;
+                smallspec.full_width = smallspec.width;
+                smallspec.full_height = smallspec.height;
+                smallspec.full_depth = smallspec.depth;
+                smallspec.set_format (TypeDesc::FLOAT);
+
+                // Trick: to get the resize working properly, we reset
+                // both display and pixel windows to match, and have 0
+                // offset, AND doctor the big image to have its display
+                // and pixel windows match.  Don't worry, the texture
+                // engine doesn't care what the upper MIP levels have
+                // for the window sizes, it uses level 0 to determine
+                // the relatinship between texture 0-1 space (display
+                // window) and the pixels.
+                smallspec.x = 0;
+                smallspec.y = 0;
+                smallspec.full_x = 0;
+                smallspec.full_y = 0;
+                small->alloc (smallspec);  // Realocate with new size
+                big->set_full (big->xbegin(), big->xend(), big->ybegin(),
+                               big->yend(), big->zbegin(), big->zend());
+
+                if (filter->name() == "box" && filter->width() == 1.0f)
+                    ImageBufAlgo::parallel_image (boost::bind(resize_block, small, big, _1, envlatlmode),
+                                                  OIIO::get_roi(small->spec()));
+                else
+                    ImageBufAlgo::parallel_image (boost::bind(resize_block_HQ, small, big, _1, filter),
+                                                  OIIO::get_roi(small->spec()));
+            }
+
+            stat_miptime += miptimer();
+            outspec = smallspec;
+            outspec.set_format (outputdatatype);
+            if (envlatlmode && src_samples_border)
+                fix_latl_edges (*small);
+
+            Timer writetimer;
+            // If the format explicitly supports MIP-maps, use that,
+            // otherwise try to simulate MIP-mapping with multi-image.
+            ImageOutput::OpenMode mode = out->supports ("mipmap") ?
+                ImageOutput::AppendMIPLevel : ImageOutput::AppendSubimage;
+            if (! out->open (outputfilename.c_str(), outspec, mode)) {
+                outstream << "maketx ERROR: Could not append \"" << outputfilename
+                          << "\" : " << out->geterror() << "\n";
+                return false;
+            }
+            if (! small->write (out)) {
+                // ImageBuf::write transfers any errors from the
+                // ImageOutput to the ImageBuf.
+                outstream << "maketx ERROR writing \"" << outputfilename
+                          << "\" : " << small->geterror() << "\n";
+                out->close ();
+                return false;
+            }
+            stat_writetime += writetimer();
+            if (verbose) {
+                outstream << "    " << formatres(smallspec) << std::endl;
+            }
+            std::swap (big, small);
+        }
+    }
+
+    if (verbose)
+        outstream << "  Wrote file: " << outputfilename << std::endl;
+    writetimer.reset ();
+    writetimer.start ();
+    if (! out->close ()) {
+        outstream << "maketx ERROR writing \"" << outputfilename
+                  << "\" : " << out->geterror() << "\n";
+        return false;
+    }
+    stat_writetime += writetimer ();
+    return true;
+}
+
+
+
+bool
+ImageBufAlgo::make_texture (ImageBufAlgo::MakeTextureMode mode,
+                            const std::string &filename,
+                            const std::string &outputfilename,
+                            const ImageSpec &configspec,
+                            std::ostream *outstream)
+{
+    std::vector<std::string> filenames;
+    filenames.push_back (filename);
+    return make_texture (mode, filenames, outputfilename, configspec, outstream);
+}
+
+
+
+bool
+ImageBufAlgo::make_texture (ImageBufAlgo::MakeTextureMode mode,
+                            const std::vector<std::string> &filenames,
+                            const std::string &_outputfilename,
+                            const ImageSpec &_configspec,
+                            std::ostream *outstream_ptr)
+{
+    ASSERT (mode >= 0 && mode < ImageBufAlgo::_MakeTxLast);
+    Timer alltime;
+    ImageSpec configspec = _configspec;
+//    const char *modenames[] = { "texture map", "shadow map",
+//                                "latlong environment map" };
+    std::stringstream localstream; // catch output when user doesn't want it
+    std::ostream &outstream (outstream_ptr ? *outstream_ptr : localstream);
+
+    double stat_readtime = 0;
+    double stat_writetime = 0;
+    double stat_resizetime = 0;
+    double stat_miptime = 0;
+    double stat_colorconverttime = 0;
+
+    std::string filename = filenames[0];
+    if (! Filesystem::exists (filename)) {
+        outstream << "maketx ERROR: \"" << filename << "\" does not exist\n";
+        return false;
+    }
+    std::string outputfilename = _outputfilename.length() ? _outputfilename
+        : Filesystem::replace_extension (filename, ".tx");
 
     // When was the input file last modified?
-    std::time_t in_time = Filesystem::last_write_time (filenames[0]);
+    std::time_t in_time = Filesystem::last_write_time (filename);
 
     // When in update mode, skip making the texture if the output already
     // exists and has the same file modification time as the input file.
+    bool updatemode = configspec.get_int_attribute ("maketx:updatemode");
     if (updatemode && Filesystem::exists (outputfilename) &&
         (in_time == Filesystem::last_write_time (outputfilename))) {
-        std::cout << "maketx: no update required for \"" 
+        outstream << "maketx: no update required for \"" 
                   << outputfilename << "\"\n";
-        return;
+        return true;
     }
 
+    bool shadowmode = (mode == ImageBufAlgo::MakeTxShadow);
+    bool envlatlmode = (mode == ImageBufAlgo::MakeTxEnvLatl);
 
     // Find an ImageIO plugin that can open the output file, and open it
-    std::string outformat = fileformatname.empty() ? outputfilename : fileformatname;
+    std::string outformat = configspec.get_string_attribute ("maketx:fileformatname",
+                                                             outputfilename);
     ImageOutput *out = ImageOutput::create (outformat.c_str());
     if (! out) {
-        std::cerr 
+        outstream 
             << "maketx ERROR: Could not find an ImageIO plugin to write " 
             << outformat << " files:" << geterror() << "\n";
-        exit (EXIT_FAILURE);
+        return false;
     }
     if (! out->supports ("tiles")) {
-        std::cerr << "maketx ERROR: \"" << outputfilename
+        outstream << "maketx ERROR: \"" << outputfilename
                   << "\" format does not support tiled images\n";
-        exit (EXIT_FAILURE);
+        return false;
     }
 
-    ImageBuf src (filenames[0]);
-    src.init_spec (filenames[0], 0, 0); // force it to get the spec, not read
+    ImageBuf src (filename);
+    src.init_spec (filename, 0, 0); // force it to get the spec, not read
 
     // The cache might mess with the apparent data format.  But for the 
     // purposes of what we should output, figure it out now, before the
     // file has been read and cached.
     TypeDesc out_dataformat = src.spec().format;
 
-    // Figure out which data format we want for output
-    if (! dataformatname.empty()) {
-        if (dataformatname == "uint8")
-            out_dataformat = TypeDesc::UINT8;
-        else if (dataformatname == "int8" || dataformatname == "sint8")
-            out_dataformat = TypeDesc::INT8;
-        else if (dataformatname == "uint16")
-            out_dataformat = TypeDesc::UINT16;
-        else if (dataformatname == "int16" || dataformatname == "sint16")
-            out_dataformat = TypeDesc::INT16;
-        else if (dataformatname == "half")
-            out_dataformat = TypeDesc::HALF;
-        else if (dataformatname == "float")
-            out_dataformat = TypeDesc::FLOAT;
-        else if (dataformatname == "double")
-            out_dataformat = TypeDesc::DOUBLE;
-    }
-    
+    if (configspec.format != TypeDesc::UNKNOWN)
+        out_dataformat = configspec.format;
     
     // We cannot compute the prman / oiio options until after out_dataformat
     // has been determined, as it's required (and can potentially change 
     // out_dataformat too!)
-    
-    if (prman) out_dataformat = set_prman_options (out_dataformat);
-    else if (oiio) out_dataformat = set_oiio_options (out_dataformat);
-    
+    if (configspec.get_int_attribute("maketx:prman_options"))
+        out_dataformat = set_prman_options (out_dataformat, configspec);
+    else if (configspec.get_int_attribute("maketx:oiio_options"))
+        out_dataformat = set_oiio_options (out_dataformat, configspec);
+
     // Read the full file locally if it's less than 1 GB, otherwise
     // allow the ImageBuf to use ImageCache to manage memory.
     bool read_local = (src.spec().image_bytes() < size_t(1024*1024*1024));
 
+    bool verbose = configspec.get_int_attribute ("maketx:verbose");
     if (verbose)
-        std::cout << "Reading file: " << filenames[0] << std::endl;
+        outstream << "Reading file: " << filename << std::endl;
     Timer readtimer;
     if (! src.read (0, 0, read_local)) {
-        std::cerr 
+        outstream 
             << "maketx ERROR: Could not read \"" 
-            << filenames[0] << "\" : " << src.geterror() << "\n";
-        exit (EXIT_FAILURE);
+            << filename << "\" : " << src.geterror() << "\n";
+        return false;
     }
     stat_readtime += readtimer();
     
@@ -763,7 +667,7 @@ make_texturemap (const char *maptypename = "texture map")
     // wrap mode at runtime.
     std::vector<float> constantColor(src.nchannels());
     bool isConstantColor = false;
-    if (constant_color_detect &&
+    if (configspec.get_int_attribute("maketx:constant_color_detect") &&
         src.spec().x == 0 && src.spec().y == 0 && src.spec().z == 0 &&
         src.spec().full_x == 0 && src.spec().full_y == 0 &&
         src.spec().full_z == 0 && src.spec().full_width == src.spec().width &&
@@ -773,9 +677,9 @@ make_texturemap (const char *maptypename = "texture map")
         if (isConstantColor) {
             // Reset the image, to a new image, at the tile size
             ImageSpec newspec = src.spec();
-            newspec.width  = std::min (tile[0], src.spec().width);
-            newspec.height = std::min (tile[1], src.spec().height);
-            newspec.depth  = std::min (tile[2], src.spec().depth);
+            newspec.width  = std::min (configspec.tile_width, src.spec().width);
+            newspec.height = std::min (configspec.tile_height, src.spec().height);
+            newspec.depth  = std::min (configspec.tile_depth, src.spec().depth);
             newspec.full_width  = newspec.width;
             newspec.full_height = newspec.height;
             newspec.full_depth  = newspec.depth;
@@ -783,33 +687,37 @@ make_texturemap (const char *maptypename = "texture map")
             src.reset(name, newspec);
             ImageBufAlgo::fill (src, &constantColor[0]);
             if (verbose) {
-                std::cout << "  Constant color image detected. ";
-                std::cout << "Creating " << newspec.width << "x" << newspec.height << " texture instead.\n";
+                outstream << "  Constant color image detected. ";
+                outstream << "Creating " << newspec.width << "x" << newspec.height << " texture instead.\n";
             }
         }
     }
     
+    int nchannels = configspec.get_int_attribute ("maketx:nchannels", -1);
+
     // If requested -- and alpha is 1.0 everywhere -- drop it.
-    if (opaque_detect && src.spec().alpha_channel == src.nchannels()-1 &&
-          nchannels < 0 &&
+    if (configspec.get_int_attribute("maketx:opaque_detect") &&
+          src.spec().alpha_channel == src.nchannels()-1 &&
+          nchannels <= 0 &&
           ImageBufAlgo::isConstantChannel(src,src.spec().alpha_channel,1.0f)) {
         ImageBuf newsrc(src.name() + ".noalpha", src.spec());
         ImageBufAlgo::setNumChannels (newsrc, src, src.nchannels()-1);
         src.copy (newsrc);
         if (verbose) {
-            std::cout << "  Alpha==1 image detected. Dropping the alpha channel.\n";
+            outstream << "  Alpha==1 image detected. Dropping the alpha channel.\n";
         }
     }
 
     // If requested - and we're a monochrome image - drop the extra channels
-    if (monochrome_detect && nchannels < 0 &&
+    if (configspec.get_int_attribute("maketx:monochrome_detect") &&
+          nchannels <= 0 &&
           src.nchannels() == 3 && src.spec().alpha_channel < 0 &&  // RGB only
           ImageBufAlgo::isMonochrome(src)) {
         ImageBuf newsrc(src.name() + ".monochrome", src.spec());
         ImageBufAlgo::setNumChannels (newsrc, src, 1);
         src.copy (newsrc);
         if (verbose) {
-            std::cout << "  Monochrome image detected. Converting to single channel texture.\n";
+            outstream << "  Monochrome image detected. Converting to single channel texture.\n";
         }
     }
 
@@ -820,17 +728,17 @@ make_texturemap (const char *maptypename = "texture map")
         ImageBufAlgo::setNumChannels (newsrc, src, nchannels);
         src.copy (newsrc);
         if (verbose) {
-            std::cout << "  Overriding number of channels to " << nchannels << "\n";
+            outstream << "  Overriding number of channels to " << nchannels << "\n";
         }
     }
     
     if (shadowmode) {
         // Some special checks for shadow maps
         if (src.spec().nchannels != 1) {
-            std::cerr << "maketx ERROR: shadow maps require 1-channel images,\n"
-                      << "\t\"" << filenames[0] << "\" is " 
+            outstream << "maketx ERROR: shadow maps require 1-channel images,\n"
+                      << "\t\"" << filename << "\" is " 
                       << src.spec().nchannels << " channels\n";
-            exit (EXIT_FAILURE);
+            return false;
         }
         // Shadow maps only make sense for floating-point data.
         if (out_dataformat != TypeDesc::FLOAT &&
@@ -839,7 +747,7 @@ make_texturemap (const char *maptypename = "texture map")
             out_dataformat = TypeDesc::FLOAT;
     }
 
-    if (set_full_to_pixels) {
+    if (configspec.get_int_attribute("maketx:set_full_to_pixels")) {
         // User requested that we treat the image as uncropped or not
         // overscan
         ImageSpec &spec (src.specmod());
@@ -882,10 +790,8 @@ make_texturemap (const char *maptypename = "texture map")
         dstspec.full_height = dstspec.height;
         dstspec.full_depth = dstspec.depth;
     }
-    if (orig_was_overscan) {
-        swrap = "black";
-        twrap = "black";
-    }
+    if (orig_was_overscan)
+        configspec.attribute ("wrapmodes", "black,black");
 
     if ((dstspec.x < 0 || dstspec.y < 0 || dstspec.z < 0) &&
         (out && !out->supports("negativeorigin"))) {
@@ -907,13 +813,18 @@ make_texturemap (const char *maptypename = "texture map")
     }
 
     // Make the output tiled, regardless of input
-    dstspec.tile_width  = tile[0];
-    dstspec.tile_height = tile[1];
-    dstspec.tile_depth  = tile[2];
-
-    dstspec.attribute ("compression", compression);
-
-    if (ignore_unassoc)
+    dstspec.tile_width  = configspec.tile_width  ? configspec.tile_width  : 64;
+    dstspec.tile_height = configspec.tile_height ? configspec.tile_height : 64;
+    dstspec.tile_depth  = configspec.tile_depth  ? configspec.tile_depth  : 1;
+
+    // Try to force zip (still can be overriden by configspec
+    dstspec.attribute ("compression", "zip");
+    // Always prefer contiguous channels, unless overridden by configspec
+    dstspec.attribute ("planarconfig", "contig");
+    // Default to black wrap mode, unless overridden by configspec
+    dstspec.attribute ("wrapmodes", "black,black");
+
+    if (configspec.get_int_attribute ("maketx:ignore_unassoc"))
         dstspec.erase_attribute ("oiio:UnassociatedAlpha");
 
     // Put a DateTime in the out file, either now, or matching the date
@@ -925,57 +836,32 @@ make_texturemap (const char *maptypename = "texture map")
         time (&date);    // not update: get the time now
     dstspec.attribute ("DateTime", datestring(date));
 
-    std::string software = Strutil::format ("OpenImageIO %s : %s",
-                                      OIIO_VERSION_STRING, full_command_line);
-    dstspec.attribute ("Software", software);
+    std::string cmdline = configspec.get_string_attribute ("maketx:full_command_line");
+    if (! cmdline.empty()) {
+        // Append command to image history
+        std::string history = dstspec.get_string_attribute ("Exif:ImageHistory");
+        if (history.length() && ! Strutil::iends_with (history, "\n"))
+            history += std::string("\n");
+        history += cmdline;
+        dstspec.attribute ("Exif:ImageHistory", history);
+    }
 
-    // Append command to image history
-    std::string history = dstspec.get_string_attribute ("Exif:ImageHistory");
-    if (history.length() && ! Strutil::iends_with (history, "\n"))
-        history += std::string("\n");
-    history += full_command_line;
-    dstspec.attribute ("Exif:ImageHistory", history);
-    
+    bool prman_metadata = configspec.get_int_attribute ("maketx:prman_metadata");
     if (shadowmode) {
         dstspec.attribute ("textureformat", "Shadow");
         if (prman_metadata)
             dstspec.attribute ("PixarTextureFormat", "Shadow");
     } else if (envlatlmode) {
         dstspec.attribute ("textureformat", "LatLong Environment");
-        swrap = "periodic";
-        twrap = "clamp";
+        configspec.attribute ("wrapmodes", "periodic,clamp");
         if (prman_metadata)
             dstspec.attribute ("PixarTextureFormat", "Latlong Environment");
     } else {
         dstspec.attribute ("textureformat", "Plain Texture");
-        if(prman_metadata)
+        if (prman_metadata)
             dstspec.attribute ("PixarTextureFormat", "Plain Texture");
     }
 
-    if (Mcam != Imath::M44f(0.0f))
-        dstspec.attribute ("worldtocamera", TypeDesc::TypeMatrix, &Mcam);
-    if (Mscr != Imath::M44f(0.0f))
-        dstspec.attribute ("worldtoscreen", TypeDesc::TypeMatrix, &Mscr);
-
-    // FIXME - check for valid strings in the wrap mode
-    if (! shadowmode) {
-        std::string wrapmodes = (swrap.size() ? swrap : wrap) + ',' + 
-                                (twrap.size() ? twrap : wrap);
-        dstspec.attribute ("wrapmodes", wrapmodes);
-    }
-    
-    if(fovcot == 0.0f) {
-        fovcot = static_cast<float>(srcspec.full_width) / 
-            static_cast<float>(srcspec.full_height);
-    }
-    dstspec.attribute ("fovcot", fovcot);
-
-    if (separate)
-        dstspec.attribute ("planarconfig", "separate");
-    else {
-        dstspec.erase_attribute("planarconfig");
-        dstspec.erase_attribute("tiff:planarconfig");
-    }
     // FIXME -- should we allow tile sizes to reduce if the image is
     // smaller than the tile size?  And when we do, should we also try
     // to make it bigger in the other direction to make the total tile
@@ -983,39 +869,40 @@ make_texturemap (const char *maptypename = "texture map")
 
     // If --checknan was used and it's a floating point image, check for
     // nonfinite (NaN or Inf) values and abort if they are found.
-    if (checknan && (srcspec.format.basetype == TypeDesc::FLOAT ||
+    if (configspec.get_int_attribute("maketx:checknan") &&
+                    (srcspec.format.basetype == TypeDesc::FLOAT ||
                      srcspec.format.basetype == TypeDesc::HALF ||
                      srcspec.format.basetype == TypeDesc::DOUBLE)) {
-        found_nonfinite = false;
-        parallel_image (check_nan_block, &src, &src,
-                        dstspec.x, dstspec.x+dstspec.width,
-                        dstspec.y, dstspec.y+dstspec.height, nthreads);
+        int found_nonfinite = 0;
+        ImageBufAlgo::parallel_image (boost::bind(check_nan_block, &src, _1, boost::ref(found_nonfinite)),
+                                      OIIO::get_roi(dstspec));
         if (found_nonfinite) {
             if (found_nonfinite > 3)
-                std::cerr << "maketx ERROR: ...and Nan/Inf at "
+                outstream << "maketx ERROR: ...and Nan/Inf at "
                           << (found_nonfinite-3) << " other pixels\n";
-            exit (EXIT_FAILURE);
+            return false;
         }
     }
     
     // Fix nans/infs (if requested
     ImageBufAlgo::NonFiniteFixMode fixmode = ImageBufAlgo::NONFINITE_NONE;
+    std::string fixnan = configspec.get_string_attribute("maketx:fixnan");
     if (fixnan.empty() || fixnan == "none") { }
     else if (fixnan == "black") { fixmode = ImageBufAlgo::NONFINITE_BLACK; }
     else if (fixnan == "box3") { fixmode = ImageBufAlgo::NONFINITE_BOX3; }
     else {
-        std::cerr << "maketx ERROR: Unknown --fixnan mode " << " fixnan\n";
-        exit (EXIT_FAILURE);
+        outstream << "maketx ERROR: Unknown --fixnan mode " << " fixnan\n";
+        return false;
     }
     
     int pixelsFixed = 0;
     if (!ImageBufAlgo::fixNonFinite (src, src, fixmode, &pixelsFixed)) {
-        std::cerr << "maketx ERROR: Error fixing nans/infs.\n";
-        exit (EXIT_FAILURE);
+        outstream << "maketx ERROR: Error fixing nans/infs.\n";
+        return false;
     }
     
     if (verbose && pixelsFixed>0) {
-        std::cout << "  Warning: " << pixelsFixed << " nan/inf pixels fixed.\n";
+        outstream << "  Warning: " << pixelsFixed << " nan/inf pixels fixed.\n";
     }
     
     
@@ -1026,6 +913,8 @@ make_texturemap (const char *maptypename = "texture map")
     // independently color convert the constant color metadata
     ImageBuf * ccSrc = &src;    // Ptr to cc'd src image
     ImageBuf colorBuffer;
+    std::string incolorspace = configspec.get_string_attribute ("incolorspace");
+    std::string outcolorspace = configspec.get_string_attribute ("outcolorspace");
     if (!incolorspace.empty() && !outcolorspace.empty() && incolorspace != outcolorspace) {
         if (src.spec().format != TypeDesc::FLOAT) {
             ImageSpec floatSpec = src.spec();
@@ -1035,39 +924,41 @@ make_texturemap (const char *maptypename = "texture map")
         }
         
         Timer colorconverttimer;
+        ColorConfig colorconfig;
         if (verbose) {
-            std::cout << "  Converting from colorspace " << incolorspace 
+            outstream << "  Converting from colorspace " << incolorspace 
                       << " to colorspace " << outcolorspace << std::endl;
         }
         
         if (colorconfig.error()) {
-            std::cerr << "Error Creating ColorConfig\n";
-            std::cerr << colorconfig.geterror() << std::endl;
-            exit (EXIT_FAILURE);
+            outstream << "Error Creating ColorConfig\n";
+            outstream << colorconfig.geterror() << std::endl;
+            return false;
         }
         
         ColorProcessor * processor = colorconfig.createColorProcessor (
             incolorspace.c_str(), outcolorspace.c_str());
         
         if (!processor || colorconfig.error()) {
-            std::cerr << "Error Creating Color Processor." << std::endl;
-            std::cerr << colorconfig.geterror() << std::endl;
-            exit (EXIT_FAILURE);
+            outstream << "Error Creating Color Processor." << std::endl;
+            outstream << colorconfig.geterror() << std::endl;
+            return false;
         }
         
+        bool unpremult = configspec.get_int_attribute ("maketx:unpremult");
         if (unpremult && verbose)
-            std::cout << "  Unpremulting image..." << std::endl;
+            outstream << "  Unpremulting image..." << std::endl;
         
         if (!ImageBufAlgo::colorconvert (*ccSrc, src, processor, unpremult)) {
-            std::cerr << "Error applying color conversion to image.\n";
-            exit (EXIT_FAILURE);
+            outstream << "Error applying color conversion to image.\n";
+            return false;
         }
         
         if (isConstantColor) {
             if (!ImageBufAlgo::colorconvert (&constantColor[0],
                 static_cast<int>(constantColor.size()), processor, unpremult)) {
-                std::cerr << "Error applying color conversion to constant color.\n";
-                exit (EXIT_FAILURE);
+                outstream << "Error applying color conversion to constant color.\n";
+                return false;
             }
         }
 
@@ -1080,7 +971,7 @@ make_texturemap (const char *maptypename = "texture map")
     dstspec.set_format (TypeDesc::FLOAT);
 
     // Handle resize to power of two, if called for
-    if (doresize  &&  ! shadowmode) {
+    if (configspec.get_int_attribute("maketx:resize")  &&  ! shadowmode) {
         dstspec.width = pow2roundup (dstspec.width);
         dstspec.height = pow2roundup (dstspec.height);
         dstspec.full_width = dstspec.width;
@@ -1096,18 +987,26 @@ make_texturemap (const char *maptypename = "texture map")
     if (orig_was_crop)
         do_resize = true;
     // resize if we're converting from non-border sampling to border sampling
-    if (envlatlmode && ! src_samples_border && 
-        (Strutil::iequals(fileformatname,"openexr") ||
+    // (converting TO an OpenEXR environment map).
+    if (envlatlmode && 
+        (Strutil::iequals(configspec.get_string_attribute("maketx:fileformatname"),"openexr") ||
          Strutil::iends_with(outputfilename,".exr")))
         do_resize = true;
 
     if (do_resize && orig_was_overscan &&
         out && !out->supports("displaywindow")) {
-        std::cerr << "maketx ERROR: format " << out->format_name()
+        outstream << "maketx ERROR: format " << out->format_name()
                   << " does not support separate display windows,\n"
                   << "              which is necessary when combining resizing"
                   << " and an input image with overscan.";
-        exit (EXIT_FAILURE);
+        return false;
+    }
+
+    std::string filtername = configspec.get_string_attribute ("maketx:filtername", "box");
+    Filter2D *filter = setup_filter (filtername);
+    if (! filter) {
+        outstream << "maketx ERROR: could not make filter '" << filtername << "\n";
+        return false;
     }
 
     Timer resizetimer;
@@ -1120,23 +1019,20 @@ make_texturemap (const char *maptypename = "texture map")
             // the original copy.
             toplevel = ccSrc;
         } else {
-            parallel_image (copy_block, &dst, ccSrc,
-                            dstspec.x, dstspec.x+dstspec.width,
-                            dstspec.y, dstspec.y+dstspec.height, nthreads);
+            ImageBufAlgo::parallel_image (boost::bind(copy_block,&dst,ccSrc,_1),
+                                          OIIO::get_roi(dstspec));
         }
     } else {
         // Resize
         if (verbose)
-            std::cout << "  Resizing image to " << dstspec.width 
+            outstream << "  Resizing image to " << dstspec.width 
                       << " x " << dstspec.height << std::endl;
         if (filtername == "box" && filter->width() == 1.0f)
-            parallel_image (resize_block, &dst, ccSrc,
-                            dstspec.x, dstspec.x+dstspec.width,
-                            dstspec.y, dstspec.y+dstspec.height, nthreads);
+            ImageBufAlgo::parallel_image (boost::bind(resize_block, &dst, ccSrc, _1, envlatlmode),
+                                          OIIO::get_roi(dstspec));
         else
-            parallel_image (resize_block_HQ, &dst, ccSrc,
-                            dstspec.x, dstspec.x+dstspec.width,
-                            dstspec.y, dstspec.y+dstspec.height, nthreads);
+            ImageBufAlgo::parallel_image (boost::bind(resize_block_HQ, &dst, ccSrc, _1, filter),
+                                          OIIO::get_roi(dstspec));
     }
     stat_resizetime += resizetimer();
 
@@ -1172,7 +1068,7 @@ make_texturemap (const char *maptypename = "texture map")
         desc += "SHA-1=";
         desc += hash_digest;
         if (verbose)
-            std::cout << "  SHA-1: " << hash_digest << std::endl;
+            outstream << "  SHA-1: " << hash_digest << std::endl;
         updatedDesc = true;
         dstspec.attribute ("oiio:SHA-1", hash_digest);
     }
@@ -1191,7 +1087,7 @@ make_texturemap (const char *maptypename = "texture map")
         desc += "ConstantColor=";
         desc += os.str();
         if (verbose)
-            std::cout << "  ConstantColor: " << os.str() << std::endl;
+            outstream << "  ConstantColor: " << os.str() << std::endl;
         updatedDesc = true;
         dstspec.attribute ("oiio:ConstantColor", os.str());
     }
@@ -1200,247 +1096,44 @@ make_texturemap (const char *maptypename = "texture map")
         dstspec.attribute ("ImageDescription", desc);
     }
 
+
+    if (configspec.get_float_attribute("fovcot") == 0.0f)
+        configspec.attribute("fovcot", float(srcspec.full_width) / 
+                                       float(srcspec.full_height));
+
+
+    maketx_merge_spec (dstspec, configspec);
+
     // Write out, and compute, the mipmap levels for the speicifed image
-    write_mipmap (*toplevel, dstspec, outputfilename,
-                  out, out_dataformat, !shadowmode && !nomipmap);
+    bool nomipmap = configspec.get_int_attribute ("maketx:nomipmap");
+    bool ok = write_mipmap (mode, *toplevel, dstspec, outputfilename,
+                            out, out_dataformat, !shadowmode && !nomipmap,
+                            filter, configspec, outstream,
+                            stat_writetime, stat_miptime);
     delete out;  // don't need it any more
 
     // If using update mode, stamp the output file with a modification time
     // matching that of the input file.
-    if (updatemode)
+    if (ok && updatemode)
         Filesystem::last_write_time (outputfilename, in_time);
-}
-
-
-
-static void
-write_mipmap (ImageBuf &img, const ImageSpec &outspec_template,
-              std::string outputfilename, ImageOutput *out,
-              TypeDesc outputdatatype, bool mipmap)
-{
-    ImageSpec outspec = outspec_template;
-    outspec.set_format (outputdatatype);
-
-    if (mipmap && !out->supports ("multiimage") && !out->supports ("mipmap")) {
-        std::cerr << "maketx ERROR: \"" << outputfilename
-                  << "\" format does not support multires images\n";
-        exit (EXIT_FAILURE);
-    }
 
-    if (! mipmap && ! strcmp (out->format_name(), "openexr")) {
-        // Send hint to OpenEXR driver that we won't specify a MIPmap
-        outspec.attribute ("openexr:levelmode", 0 /* ONE_LEVEL */);
-    }
-
-    if (mipmap && ! strcmp (out->format_name(), "openexr")) {
-        outspec.attribute ("openexr:roundingmode", 0 /* ROUND_DOWN */);
-    }
-
-    // OpenEXR always uses border sampling for environment maps
-    if ((envlatlmode || envcubemode) &&
-            !strcmp(out->format_name(), "openexr")) {
-        src_samples_border = true;
-        outspec.attribute ("oiio:updirection", "y");
-        outspec.attribute ("oiio:sampleborder", 1);
-    }
-    if (envlatlmode && src_samples_border)
-        fix_latl_edges (img);
-
-    Timer writetimer;
-    if (! out->open (outputfilename.c_str(), outspec)) {
-        std::cerr << "maketx ERROR: Could not open \"" << outputfilename
-                  << "\" : " << out->geterror() << "\n";
-        exit (EXIT_FAILURE);
-    }
-
-    // Write out the image
-    if (verbose) {
-        std::cout << "  Writing file: " << outputfilename << std::endl;
-        std::cout << "  Filter \"" << filter->name() << "\" width = " 
-                  << filter->width() << "\n";
-        std::cout << "  Top level is " << formatres(outspec) << std::endl;
-    }
-
-    if (! img.write (out)) {
-        // ImageBuf::write transfers any errors from the ImageOutput to
-        // the ImageBuf.
-        std::cerr << "maketx ERROR: Write failed \" : " << img.geterror() << "\n";
-        out->close ();
-        exit (EXIT_FAILURE);
-    }
-
-    stat_writetime += writetimer();
-
-    if (mipmap) {  // Mipmap levels:
-        if (verbose)
-            std::cout << "  Mipmapping...\n" << std::flush;
-        ImageBuf tmp;
-        ImageBuf *big = &img, *small = &tmp;
-        while (outspec.width > 1 || outspec.height > 1) {
-            Timer miptimer;
-            ImageSpec smallspec;
-
-            if (mipimages.size()) {
-                // Special case -- the user specified a custom MIP level
-                small->reset (mipimages[0]);
-                small->read (0, 0, true, TypeDesc::FLOAT);
-                smallspec = small->spec();
-                if (smallspec.nchannels != outspec.nchannels) {
-                    std::cout << "WARNING: Custom mip level \"" << mipimages[0]
-                              << " had the wrong number of channels.\n";
-                    ImageBuf *t = new ImageBuf (mipimages[0], smallspec);
-                    ImageBufAlgo::setNumChannels(*t, *small, outspec.nchannels);
-                    std::swap (t, small);
-                    delete t;
-                }
-                smallspec.tile_width = outspec.tile_width;
-                smallspec.tile_height = outspec.tile_height;
-                smallspec.tile_depth = outspec.tile_depth;
-                mipimages.erase (mipimages.begin());
-            } else {
-                // Resize a factor of two smaller
-                smallspec = outspec;
-                smallspec.width = big->spec().width;
-                smallspec.height = big->spec().height;
-                smallspec.depth = big->spec().depth;
-                if (smallspec.width > 1)
-                    smallspec.width /= 2;
-                if (smallspec.height > 1)
-                    smallspec.height /= 2;
-                smallspec.full_width = smallspec.width;
-                smallspec.full_height = smallspec.height;
-                smallspec.full_depth = smallspec.depth;
-                smallspec.set_format (TypeDesc::FLOAT);
-
-                // Trick: to get the resize working properly, we reset
-                // both display and pixel windows to match, and have 0
-                // offset, AND doctor the big image to have its display
-                // and pixel windows match.  Don't worry, the texture
-                // engine doesn't care what the upper MIP levels have
-                // for the window sizes, it uses level 0 to determine
-                // the relatinship between texture 0-1 space (display
-                // window) and the pixels.
-                smallspec.x = 0;
-                smallspec.y = 0;
-                smallspec.full_x = 0;
-                smallspec.full_y = 0;
-                small->alloc (smallspec);  // Realocate with new size
-                big->set_full (big->xbegin(), big->xend(), big->ybegin(),
-                               big->yend(), big->zbegin(), big->zend());
-
-                if (filtername == "box" && filter->width() == 1.0f)
-                    parallel_image (resize_block, small, big,
-                                    small->xbegin(), small->xend(),
-                                    small->ybegin(), small->yend(),
-                                    nthreads);
-                else
-                    parallel_image (resize_block_HQ, small, big,
-                                    small->xbegin(), small->xend(),
-                                    small->ybegin(), small->yend(),
-                                    nthreads);
-            }
-
-            stat_miptime += miptimer();
-            outspec = smallspec;
-            outspec.set_format (outputdatatype);
-            if (envlatlmode && src_samples_border)
-                fix_latl_edges (*small);
-
-            Timer writetimer;
-            // If the format explicitly supports MIP-maps, use that,
-            // otherwise try to simulate MIP-mapping with multi-image.
-            ImageOutput::OpenMode mode = out->supports ("mipmap") ?
-                ImageOutput::AppendMIPLevel : ImageOutput::AppendSubimage;
-            if (! out->open (outputfilename.c_str(), outspec, mode)) {
-                std::cerr << "maketx ERROR: Could not append \"" << outputfilename
-                          << "\" : " << out->geterror() << "\n";
-                exit (EXIT_FAILURE);
-            }
-            if (! small->write (out)) {
-                // ImageBuf::write transfers any errors from the
-                // ImageOutput to the ImageBuf.
-                std::cerr << "maketx ERROR writing \"" << outputfilename
-                          << "\" : " << small->geterror() << "\n";
-                out->close ();
-                exit (EXIT_FAILURE);
-            }
-            stat_writetime += writetimer();
-            if (verbose) {
-                std::cout << "    " << formatres(smallspec) << std::endl;
-            }
-            std::swap (big, small);
-        }
-    }
-
-    if (verbose)
-        std::cout << "  Wrote file: " << outputfilename << std::endl;
-    writetimer.reset ();
-    writetimer.start ();
-    if (! out->close ()) {
-        std::cerr << "maketx ERROR writing \"" << outputfilename
-                  << "\" : " << out->geterror() << "\n";
-        exit (EXIT_FAILURE);
-    }
-    stat_writetime += writetimer ();
-}
-
-
-
-int
-main (int argc, char *argv[])
-{
-    Timer alltimer;
-    getargs (argc, argv);
-
-    OIIO::attribute ("threads", nthreads);
-
-    // N.B. This will apply to the default IC that any ImageBuf's get.
-    ImageCache *ic = ImageCache::create ();  // get the shared one
-    ic->attribute ("forcefloat", 1);   // Force float upon read
-    ic->attribute ("max_memory_MB", 1024.0);  // 1 GB cache
-    ic->attribute ("unassociatedalpha", (int)ignore_unassoc);
-
-    if (mipmapmode) {
-        make_texturemap ("texture map");
-    } else if (shadowmode) {
-        make_texturemap ("shadow map");
-    } else if (shadowcubemode) {
-        std::cerr << "Shadow cubes currently unsupported\n";
-    } else if (volshadowmode) {
-        std::cerr << "Volume shadows currently unsupported\n";
-    } else if (envlatlmode) {
-        make_texturemap ("latlong environment map");
-    } else if (envcubemode) {
-        std::cerr << "Environment cubes currently unsupported\n";
-    } else if (lightprobemode) {
-        std::cerr << "Light probes currently unsupported\n";
-    } else if (vertcrossmode) {
-        std::cerr << "Vertcross currently unsupported\n";
-    } else if (latl2envcubemode) {
-        std::cerr << "Latlong->cube conversion currently unsupported\n";
-    }
+    Filter2D::destroy (filter);
 
-    if (verbose || stats) {
-        std::cout << "maketx Runtime statistics (seconds):\n";
-        double alltime = alltimer();
-        std::cout << Strutil::format ("  total runtime:   %5.2f\n", alltime);
-        std::cout << Strutil::format ("  file read:       %5.2f\n", stat_readtime);
-        std::cout << Strutil::format ("  file write:      %5.2f\n", stat_writetime);
-        std::cout << Strutil::format ("  initial resize:  %5.2f\n", stat_resizetime);
-        std::cout << Strutil::format ("  mip computation: %5.2f\n", stat_miptime);
-        std::cout << Strutil::format ("  color convert:   %5.2f\n", stat_colorconverttime);
-        std::cout << Strutil::format ("  unaccounted:     %5.2f\n",
-                                      alltime-stat_readtime-stat_writetime-stat_resizetime-stat_miptime);
+    if (verbose || configspec.get_int_attribute("maketx:stats")) {
+        double all = alltime();
+        outstream << Strutil::format ("maketx run time (seconds): %5.2f\n", all);;
+
+        outstream << Strutil::format ("  file read:       %5.2f\n", stat_readtime);
+        outstream << Strutil::format ("  file write:      %5.2f\n", stat_writetime);
+        outstream << Strutil::format ("  initial resize:  %5.2f\n", stat_resizetime);
+        outstream << Strutil::format ("  mip computation: %5.2f\n", stat_miptime);
+        outstream << Strutil::format ("  color convert:   %5.2f\n", stat_colorconverttime);
+        outstream << Strutil::format ("  unaccounted:     %5.2f\n",
+                                      all-stat_readtime-stat_writetime-stat_resizetime-stat_miptime);
         size_t kb = Sysutil::memory_used(true) / 1024;
-        std::cout << Strutil::format ("maketx memory used: %5.1f MB\n",
+        outstream << Strutil::format ("maketx memory used: %5.1f MB\n",
                                       (double)kb/1024.0);
     }
 
-    Filter2D::destroy (filter);
-
-    if (stats) {
-        std::cout << "\n" << ic->getstats();
-    }
-
-    return 0;
+    return ok;
 }
diff --git a/src/libtexture/imagecache_pvt.h b/src/libtexture/imagecache_pvt.h
index f4c43cd..5074bff 100644
--- a/src/libtexture/imagecache_pvt.h
+++ b/src/libtexture/imagecache_pvt.h
@@ -513,9 +513,8 @@ private:
     TileID m_id;                  ///< ID of this tile
     std::vector<char> m_pixels;   ///< The pixel data
     bool m_valid;                 ///< Valid pixels
-    atomic_int m_used;            ///< Used recently
     volatile bool m_pixels_ready; ///< The pixels have been read from disk
-    float m_mindepth, m_maxdepth; ///< shadows only: min/max depth of the tile
+    atomic_int m_used;            ///< Used recently
 };
 
 
diff --git a/src/libutil/SHA1.cpp b/src/libutil/SHA1.cpp
index b9e6c8b..c761185 100644
--- a/src/libutil/SHA1.cpp
+++ b/src/libutil/SHA1.cpp
@@ -8,9 +8,9 @@
 
 // If compiling with MFC, you might want to add #include "StdAfx.h"
 
+#include "SHA1.h"
 #include "hash.h"
 #include "dassert.h"
-#include "SHA1.h"
 
 #ifdef SHA1_UTILITY_FUNCTIONS
 #define SHA1_MAX_FILE_BUFFER 8000
diff --git a/src/libutil/tbb_misc.cpp b/src/libutil/tbb_misc.cpp
index a706383..b6a485e 100644
--- a/src/libutil/tbb_misc.cpp
+++ b/src/libutil/tbb_misc.cpp
@@ -30,8 +30,7 @@
 // an executing program.
 
 #include "tbb/tbb_stddef.h"
-// Out-of-line TBB assertion handling routines are instantiated here.
-#include "tbb/tbb_assert_impl.h"
+#include "tbb/tbb_machine.h"
 
 #include "tbb/tbb_misc.h"
 #include <cstdio>
diff --git a/src/maketx/maketx.cpp b/src/maketx/maketx.cpp
index ca17b9d..410cb7c 100644
--- a/src/maketx/maketx.cpp
+++ b/src/maketx/maketx.cpp
@@ -60,6 +60,7 @@ OIIO_NAMESPACE_USING
 // # FIXME: Refactor all statics into a struct
 
 // Basic runtime options
+static bool newmode = false;
 static std::string full_command_line;
 static std::vector<std::string> filenames;
 static std::string outputfilename;
@@ -233,6 +234,8 @@ getargs (int argc, char *argv[])
                   "-o %s", &outputfilename, "Output filename",
                   "--threads %d", &nthreads, "Number of threads (default: #cores)",
                   "-u", &updatemode, "Update mode",
+                  "--new", &newmode, "",
+                  "--old %!", &newmode, "",
                   "--format %s", &fileformatname, "Specify output file format (default: guess from extension)",
                   "--nchannels %d", &nchannels, "Specify the number of output image channels.",
                   "-d %s", &dataformatname, "Set the output data format to one of: "
@@ -1386,6 +1389,218 @@ write_mipmap (ImageBuf &img, const ImageSpec &outspec_template,
 
 
 
+
+static void
+newmode_getargs (int argc, char *argv[], ImageSpec &configspec)
+{
+    bool help = false;
+    // Basic runtime options
+    std::string dataformatname = "";
+    std::string fileformatname = "";
+    std::vector<std::string> mipimages;
+    int tile[3] = { 64, 64, 1 };  // FIXME if we ever support volume MIPmaps
+    std::string compression = "zip";
+    bool updatemode = false;
+    bool checknan = false;
+    std::string fixnan; // none, black, box3
+    bool set_full_to_pixels = false;
+    std::string filtername;
+    // Options controlling file metadata or mipmap creation
+    float fovcot = 0.0f;
+    std::string wrap = "black";
+    std::string swrap;
+    std::string twrap;
+    bool doresize = false;
+    Imath::M44f Mcam(0.0f), Mscr(0.0f);  // Initialize to 0
+    bool separate = false;
+    bool nomipmap = false;
+    bool prman_metadata = false;
+    bool constant_color_detect = false;
+    bool monochrome_detect = false;
+    bool opaque_detect = false;
+    int nchannels = -1;
+    bool prman = false;
+    bool oiio = false;
+    bool ignore_unassoc = false;  // ignore unassociated alpha tags
+    bool unpremult = false;
+    std::string incolorspace;
+    std::string outcolorspace;
+    
+    filenames.clear();
+
+    ArgParse ap;
+    ap.options ("maketx -- convert images to tiled, MIP-mapped textures\n"
+                OIIO_INTRO_STRING "\n"
+                "Usage:  maketx [options] file...",
+                  "%*", parse_files, "",
+                  "--help", &help, "Print help message",
+                  "-v", &verbose, "Verbose status messages",
+                  "-o %s", &outputfilename, "Output filename",
+                  "--new", NULL, "",
+                  "--old", NULL, "Old mode",
+                  "--threads %d", &nthreads, "Number of threads (default: #cores)",
+                  "-u", &updatemode, "Update mode",
+                  "--format %s", &fileformatname, "Specify output file format (default: guess from extension)",
+                  "--nchannels %d", &nchannels, "Specify the number of output image channels.",
+                  "-d %s", &dataformatname, "Set the output data format to one of: "
+                          "uint8, sint8, uint16, sint16, half, float",
+                  "--tile %d %d", &tile[0], &tile[1], "Specify tile size",
+                  "--separate", &separate, "Use planarconfig separate (default: contiguous)",
+                  "--compression %s", &compression, "Set the compression method (default = zip, if possible)",
+                  "--fovcot %f", &fovcot, "Override the frame aspect ratio. Default is width/height.",
+                  "--wrap %s", &wrap, "Specify wrap mode (black, clamp, periodic, mirror)",
+                  "--swrap %s", &swrap, "Specific s wrap mode separately",
+                  "--twrap %s", &twrap, "Specific t wrap mode separately",
+                  "--resize", &doresize, "Resize textures to power of 2 (default: no)",
+                  "--noresize %!", &doresize, "Do not resize textures to power of 2 (deprecated)",
+                  "--filter %s", &filtername, filter_help_string().c_str(),
+                  "--nomipmap", &nomipmap, "Do not make multiple MIP-map levels",
+                  "--checknan", &checknan, "Check for NaN/Inf values (abort if found)",
+                  "--fixnan %s", &fixnan, "Attempt to fix NaN/Inf values in the image (options: none, black, box3)",
+                  "--fullpixels", &set_full_to_pixels, "Set the 'full' image range to be the pixel data window",
+                  "--Mcamera %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f",
+                          &Mcam[0][0], &Mcam[0][1], &Mcam[0][2], &Mcam[0][3], 
+                          &Mcam[1][0], &Mcam[1][1], &Mcam[1][2], &Mcam[1][3], 
+                          &Mcam[2][0], &Mcam[2][1], &Mcam[2][2], &Mcam[2][3], 
+                          &Mcam[3][0], &Mcam[3][1], &Mcam[3][2], &Mcam[3][3], 
+                          "Set the camera matrix",
+                  "--Mscreen %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f",
+                          &Mscr[0][0], &Mscr[0][1], &Mscr[0][2], &Mscr[0][3], 
+                          &Mscr[1][0], &Mscr[1][1], &Mscr[1][2], &Mscr[1][3], 
+                          &Mscr[2][0], &Mscr[2][1], &Mscr[2][2], &Mscr[2][3], 
+                          &Mscr[3][0], &Mscr[3][1], &Mscr[3][2], &Mscr[3][3], 
+                          "Set the screen matrix",
+                  "--hash", NULL, "",
+                  "--prman-metadata", &prman_metadata, "Add prman specific metadata",
+                  "--constant-color-detect", &constant_color_detect, "Create 1-tile textures from constant color inputs",
+                  "--monochrome-detect", &monochrome_detect, "Create 1-channel textures from monochrome inputs",
+                  "--opaque-detect", &opaque_detect, "Drop alpha channel that is always 1.0",
+                  "--ignore-unassoc", &ignore_unassoc, "Ignore unassociated alpha tags in input (don't autoconvert)",
+                  "--stats", &stats, "Print runtime statistics",
+                  "--mipimage %L", &mipimages, "Specify an individual MIP level",
+                  "<SEPARATOR>", "Basic modes (default is plain texture):",
+                  "--shadow", &shadowmode, "Create shadow map",
+                  "--envlatl", &envlatlmode, "Create lat/long environment map",
+//                  "--envcube", &envcubemode, "Create cubic env map (file order: px, nx, py, ny, pz, nz) (UNIMP)",
+                  "<SEPARATOR>", colortitle_help_string().c_str(),
+                  "--colorconvert %s %s", &incolorspace, &outcolorspace,
+                          colorconvert_help_string().c_str(),
+                  "--unpremult", &unpremult, "Unpremultiply before color conversion, then premultiply "
+                          "after the color conversion.  You'll probably want to use this flag "
+                          "if your image contains an alpha channel.",
+                  "<SEPARATOR>", "Configuration Presets",
+                  "--prman", &prman, "Use PRMan-safe settings for tile size, planarconfig, and metadata.",
+                  "--oiio", &oiio, "Use OIIO-optimized settings for tile size, planarconfig, metadata.",
+                  NULL);
+    if (ap.parse (argc, (const char**)argv) < 0) {
+        std::cerr << ap.geterror() << std::endl;
+        ap.usage ();
+        exit (EXIT_FAILURE);
+    }
+    if (help || filenames.empty()) {
+        ap.usage ();
+        exit (EXIT_FAILURE);
+    }
+
+    int optionsum = ((int)shadowmode + (int)envlatlmode + (int)envcubemode);
+    if (optionsum > 1) {
+        std::cerr << "maketx ERROR: At most one of the following options may be set:\n"
+                  << "\t--shadow --envlatl --envcube\n";
+        ap.usage ();
+        exit (EXIT_FAILURE);
+    }
+    if (optionsum == 0)
+        mipmapmode = true;
+    
+    if (prman && oiio) {
+        std::cerr << "maketx ERROR: '--prman' compatibility, and '--oiio' optimizations are mutually exclusive.\n";
+        std::cerr << "\tIf you'd like both prman and oiio compatibility, you should choose --prman\n";
+        std::cerr << "\t(at the expense of oiio-specific optimizations)\n";
+        ap.usage ();
+        exit (EXIT_FAILURE);
+    }
+
+    if (filenames.size() != 1) {
+        std::cerr << "maketx ERROR: requires exactly one input filename\n";
+        exit (EXIT_FAILURE);
+    }
+
+
+//    std::cout << "Converting " << filenames[0] << " to " << outputfilename << "\n";
+
+    // Figure out which data format we want for output
+    if (! dataformatname.empty()) {
+        if (dataformatname == "uint8")
+            configspec.format = TypeDesc::UINT8;
+        else if (dataformatname == "int8" || dataformatname == "sint8")
+            configspec.format = TypeDesc::INT8;
+        else if (dataformatname == "uint16")
+            configspec.format = TypeDesc::UINT16;
+        else if (dataformatname == "int16" || dataformatname == "sint16")
+            configspec.format = TypeDesc::INT16;
+        else if (dataformatname == "half")
+            configspec.format = TypeDesc::HALF;
+        else if (dataformatname == "float")
+            configspec.format = TypeDesc::FLOAT;
+        else if (dataformatname == "double")
+            configspec.format = TypeDesc::DOUBLE;
+    }
+
+    configspec.tile_width  = tile[0];
+    configspec.tile_height = tile[1];
+    configspec.tile_depth  = tile[2];
+    configspec.attribute ("compression", compression);
+    if (fovcot != 0.0f)
+        configspec.attribute ("fovcot", fovcot);
+    configspec.attribute ("planarconfig", separate ? "separate" : "contig");
+    if (Mcam != Imath::M44f(0.0f))
+        configspec.attribute ("worldtocamera", TypeDesc::TypeMatrix, &Mcam);
+    if (Mscr != Imath::M44f(0.0f))
+        configspec.attribute ("worldtoscreen", TypeDesc::TypeMatrix, &Mscr);
+    std::string wrapmodes = (swrap.size() ? swrap : wrap) + ',' + 
+                            (twrap.size() ? twrap : wrap);
+    configspec.attribute ("wrapmodes", wrapmodes);
+
+    configspec.attribute ("maketx:verbose", verbose);
+    configspec.attribute ("maketx:stats", stats);
+    configspec.attribute ("maketx:resize", doresize);
+    configspec.attribute ("maketx:nomipmap", nomipmap);
+    configspec.attribute ("maketx:updatemode", updatemode);
+    configspec.attribute ("maketx:constant_color_detect", constant_color_detect);
+    configspec.attribute ("maketx:monochrome_detect", monochrome_detect);
+    configspec.attribute ("maketx:opaque_detect", opaque_detect);
+    configspec.attribute ("maketx:unpremult", unpremult);
+    configspec.attribute ("maketx:incolorspace", incolorspace);
+    configspec.attribute ("maketx:outcolorspace", outcolorspace);
+    configspec.attribute ("maketx:checknan", checknan);
+    configspec.attribute ("maketx:fixnan", fixnan);
+    configspec.attribute ("maketx:set_full_to_pixels", set_full_to_pixels);
+    if (filtername.size())
+        configspec.attribute ("maketx:filtername", filtername);
+    configspec.attribute ("maketx:nchannels", nchannels);
+    if (fileformatname.size())
+        configspec.attribute ("maketx:fileformatname", fileformatname);
+    configspec.attribute ("maketx:prman_metadata", prman_metadata);
+    configspec.attribute ("maketx:oiio_options", oiio);
+    configspec.attribute ("maketx:prman_options", prman);
+    if (mipimages.size())
+        configspec.attribute ("maketx:mipimages", Strutil::join(mipimages,";"));
+
+    std::string cmdline = Strutil::format ("OpenImageIO %s : %s",
+                                     OIIO_VERSION_STRING, ap.command_line());
+    configspec.attribute ("Software", cmdline);
+    configspec.attribute ("maketx:full_command_line", cmdline);
+
+    if (ignore_unassoc) {
+        configspec.attribute ("maketx:ignore_unassoc", (int)ignore_unassoc);
+        ImageCache *ic = ImageCache::create ();  // get the shared one
+        ic->attribute ("unassociatedalpha", (int)ignore_unassoc);
+    }
+}
+
+
+
+
 int
 main (int argc, char *argv[])
 {
@@ -1400,6 +1615,23 @@ main (int argc, char *argv[])
     ic->attribute ("max_memory_MB", 1024.0);  // 1 GB cache
     ic->attribute ("unassociatedalpha", (int)ignore_unassoc);
 
+    if (newmode) {
+        ImageSpec configspec;
+        newmode_getargs (argc, argv, configspec);
+        ImageBufAlgo::MakeTextureMode mode = ImageBufAlgo::MakeTxTexture;
+        if (shadowmode)
+            mode = ImageBufAlgo::MakeTxShadow;
+        if (envlatlmode)
+            mode = ImageBufAlgo::MakeTxEnvLatl;
+        bool ok = ImageBufAlgo::make_texture (mode, filenames[0],
+                                              outputfilename, configspec,
+                                              &std::cout);
+        if (stats)
+            std::cout << "\n" << ic->getstats();
+        return ok ? 0 : EXIT_FAILURE;
+    }
+
+
     if (mipmapmode) {
         make_texturemap ("texture map");
     } else if (shadowmode) {
diff --git a/src/oiiotool/oiiotool.cpp b/src/oiiotool/oiiotool.cpp
index 0ad6a10..0f8438c 100644
--- a/src/oiiotool/oiiotool.cpp
+++ b/src/oiiotool/oiiotool.cpp
@@ -183,9 +183,13 @@ input_file (int argc, const char *argv[])
             pio.subimages = ot.allsubimages;
             pio.compute_stats = ot.printstats;
             pio.compute_sha1 = ot.hash;
+            pio.metamatch = ot.printinfo_metamatch;
+            pio.nometamatch = ot.printinfo_nometamatch;
             long long totalsize = 0;
             std::string error;
-            OiioTool::print_info (argv[i], pio, totalsize, error);
+            bool ok = OiioTool::print_info (argv[i], pio, totalsize, error);
+            if (! ok)
+                std::cerr << "oiiotool ERROR: " << error << "\n";
         }
         ot.process_pending ();
     }
@@ -1615,11 +1619,8 @@ action_over (int argc, const char *argv[])
     // Create output image specification.
     ImageSpec specR = specA;
     set_roi (specR, roi_union (get_roi(specA), get_roi(specB)));
-    specR.nchannels = std::max (specA.nchannels, specB.nchannels);
-    if (specR.alpha_channel < 0 && specR.nchannels == 4)
-        specR.alpha_channel = 3;
 
-    ot.push (new ImageRec ("irec", specR, ot.imagecache));
+    ot.push (new ImageRec ("over", specR, ot.imagecache));
     ImageBuf &Rib ((*ot.curimg)());
 
     bool ok = ImageBufAlgo::over (Rib, Aib, Bib);
@@ -1631,6 +1632,36 @@ action_over (int argc, const char *argv[])
 
 
 static int
+action_zover (int argc, const char *argv[])
+{
+    if (ot.postpone_callback (2, action_over, argc, argv))
+        return 0;
+
+    ImageRecRef B (ot.pop());
+    ImageRecRef A (ot.pop());
+    ot.read (A);
+    ot.read (B);
+    const ImageBuf &Aib ((*A)());
+    const ImageBuf &Bib ((*B)());
+    const ImageSpec &specA = Aib.spec();
+    const ImageSpec &specB = Bib.spec();
+
+    // Create output image specification.
+    ImageSpec specR = specA;
+    set_roi (specR, roi_union (get_roi(specA), get_roi(specB)));
+
+    ot.push (new ImageRec ("zover", specR, ot.imagecache));
+    ImageBuf &Rib ((*ot.curimg)());
+
+    bool ok = ImageBufAlgo::zover (Rib, Aib, Bib);
+    if (! ok)
+        ot.error (argv[0], Rib.geterror());
+    return 0;
+}
+
+
+
+static int
 action_fill (int argc, const char *argv[])
 {
     if (ot.postpone_callback (1, action_fill, argc, argv))
@@ -1852,6 +1883,10 @@ getargs (int argc, char *argv[])
                 "-q %!", &ot.verbose, "Quiet mode (turn verbose off)",
                 "-a", &ot.allsubimages, "Do operations on all subimages/miplevels",
                 "--info", &ot.printinfo, "Print resolution and metadata on all inputs",
+                "--metamatch %s", &ot.printinfo_metamatch,
+                    "Regex: which metadata is printed with -info -v",
+                "--no-metamatch %s", &ot.printinfo_nometamatch,
+                    "Regex: which metadata is excluded with -info -v",
                 "--stats", &ot.printstats, "Print pixel statistics on all inputs",
                 "--hash", &ot.hash, "Print SHA-1 hash of each input image",
 //                "-u", &ot.updatemode, "Update mode: skip outputs when the file exists and is newer than all inputs",
@@ -1908,6 +1943,7 @@ getargs (int argc, char *argv[])
                 "--sub %@", action_sub, NULL, "Subtract two images",
                 "--abs %@", action_abs, NULL, "Take the absolute value of the image pixels",
                 "--over %@", action_over, NULL, "'Over' composite of two images",
+                "--zover %@", action_zover, NULL, "Depth composite two images with Z channels",
                 "--histogram %@ %s %d", action_histogram, NULL, NULL, "Histogram one channel (args: cumulative=0)",
                 "--flip %@", action_flip, NULL, "Flip the image vertically (top<->bottom)",
                 "--flop %@", action_flop, NULL, "Flop the image horizontally (left<->right)",
diff --git a/src/oiiotool/oiiotool.h b/src/oiiotool/oiiotool.h
index 9db54aa..584f4ae 100644
--- a/src/oiiotool/oiiotool.h
+++ b/src/oiiotool/oiiotool.h
@@ -57,6 +57,8 @@ public:
     bool updatemode;
     int threads;
     std::string full_command_line;
+    std::string printinfo_metamatch;
+    std::string printinfo_nometamatch;
 
     // Output options
     TypeDesc output_dataformat;
@@ -304,6 +306,7 @@ struct print_info_options {
     bool compute_sha1;
     bool compute_stats;
     std::string metamatch;
+    std::string nometamatch;
     size_t namefieldlength;
 
     print_info_options ()
diff --git a/src/oiiotool/printinfo.cpp b/src/oiiotool/printinfo.cpp
index c6179e4..57f4bf0 100644
--- a/src/oiiotool/printinfo.cpp
+++ b/src/oiiotool/printinfo.cpp
@@ -291,7 +291,7 @@ print_stats (const std::string &filename,
 static void
 print_metadata (const ImageSpec &spec, const std::string &filename,
                 const print_info_options &opt,
-                boost::regex &field_re)
+                boost::regex &field_re, boost::regex &field_exclude_re)
 {
     bool printed = false;
     if (opt.metamatch.empty() ||
@@ -370,6 +370,9 @@ print_metadata (const ImageSpec &spec, const std::string &filename,
         if (! opt.metamatch.empty() &&
             ! boost::regex_search (p.name().c_str(), field_re))
             continue;
+        if (! opt.nometamatch.empty() &&
+            boost::regex_search (p.name().c_str(), field_exclude_re))
+            continue;
         std::string s = spec.metadata_val (p, true);
         if (opt.filenameprefix)
             printf ("%s : ", filename.c_str());
@@ -415,17 +418,11 @@ static void
 print_info_subimage (int current_subimage, int max_subimages, ImageSpec &spec,
                      ImageInput *input, const std::string &filename,
                      const print_info_options &opt,
-                     boost::regex &field_re)
+                     boost::regex &field_re, boost::regex &field_exclude_re)
 {
     if ( ! input->seek_subimage (current_subimage, 0, spec) )
         return;
 
-    if (! opt.metamatch.empty() &&
-        ! boost::regex_search ("resolution, width, height, depth, channels, sha-1, stats", field_re)) {
-        // nothing to do here
-        return;
-    }
-
     int nmip = 1;
 
     bool printres = opt.verbose && (opt.metamatch.empty() ||
@@ -466,7 +463,7 @@ print_info_subimage (int current_subimage, int max_subimages, ImageSpec &spec,
     }
 
     if (opt.verbose)
-        print_metadata (spec, filename, opt, field_re);
+        print_metadata (spec, filename, opt, field_re, field_exclude_re);
 
     if (opt.compute_stats && (opt.metamatch.empty() ||
                           boost::regex_search ("stats", field_re))) {
@@ -506,9 +503,27 @@ OiioTool::print_info (const std::string &filename,
     ImageSpec spec = input->spec();
 
     boost::regex field_re;
-    if (! opt.metamatch.empty())
-        field_re.assign (opt.metamatch,
+    boost::regex field_exclude_re;
+    if (! opt.metamatch.empty()) {
+        try {
+            field_re.assign (opt.metamatch,
                          boost::regex::extended | boost::regex_constants::icase);
+        } catch (const std::exception &e) {
+            error = Strutil::format ("Regex error '%s' on metamatch regex \"%s\"",
+                                     e.what(), opt.metamatch);
+            return false;
+        }
+    }
+    if (! opt.nometamatch.empty()) {
+        try {
+            field_exclude_re.assign (opt.nometamatch,
+                         boost::regex::extended | boost::regex_constants::icase);
+        } catch (const std::exception &e) {
+            error = Strutil::format ("Regex error '%s' on metamatch regex \"%s\"",
+                                     e.what(), opt.nometamatch);
+            return false;
+        }
+    }
 
     int padlen = std::max (0, (int)opt.namefieldlength - (int)filename.length());
     std::string padding (padlen, ' ');
@@ -589,7 +604,7 @@ OiioTool::print_info (const std::string &filename,
         num_of_subimages = 1;
     for (int i = 0; i < num_of_subimages; ++i) {
         print_info_subimage (i, num_of_subimages, spec, input,
-                             filename, opt, field_re);
+                             filename, opt, field_re, field_exclude_re);
     }
 
     input->close ();
diff --git a/src/openexr.imageio/exrinput.cpp b/src/openexr.imageio/exrinput.cpp
index 7b242f4..40c914d 100644
--- a/src/openexr.imageio/exrinput.cpp
+++ b/src/openexr.imageio/exrinput.cpp
@@ -79,6 +79,9 @@
 #include "fmath.h"
 #include "filesystem.h"
 
+#include <boost/scoped_array.hpp>
+
+
 OIIO_PLUGIN_NAMESPACE_BEGIN
 
 
@@ -929,12 +932,13 @@ OpenEXRInput::read_native_tiles (int xbegin, int xend, int ybegin, int yend,
     int nytiles = (yend - ybegin + m_spec.tile_height - 1) / m_spec.tile_height;
     int whole_width = nxtiles * m_spec.tile_width;
     int whole_height = nytiles * m_spec.tile_height;
-    std::vector<char> tmpbuf;
+    
+    boost::scoped_array<char> tmpbuf;
     void *origdata = data;
     if (whole_width != (xend-xbegin) || whole_height != (yend-ybegin)) {
         // Deal with the case of reading not a whole number of tiles --
         // OpenEXR will happily overwrite user memory in this case.
-        tmpbuf.resize (nxtiles * nytiles * m_spec.tile_bytes(true));
+        tmpbuf.reset (new char [nxtiles * nytiles * m_spec.tile_bytes(true)]);
         data = &tmpbuf[0];
     }
     char *buf = (char *)data
diff --git a/src/ptex.imageio/ptex/PtexUtils.cpp b/src/ptex.imageio/ptex/PtexUtils.cpp
index 59cc7b6..de0ad2c 100644
--- a/src/ptex.imageio/ptex/PtexUtils.cpp
+++ b/src/ptex.imageio/ptex/PtexUtils.cpp
@@ -45,7 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 const char* Ptex::MeshTypeName(MeshType mt)
 {
     static const char* names[] = { "triangle", "quad" };
-    if (mt < 0 || mt >= int(sizeof(names)/sizeof(const char*)))
+    if (mt < 0 /* || mt >= int(sizeof(names)/sizeof(const char*))*/)
 	return "(invalid mesh type)";
     return names[mt];
 }
@@ -54,7 +54,7 @@ const char* Ptex::MeshTypeName(MeshType mt)
 const char* Ptex::DataTypeName(DataType dt)
 {
     static const char* names[] = { "uint8", "uint16", "float16", "float32" };
-    if (dt < 0 || dt >= int(sizeof(names)/sizeof(const char*)))
+    if (dt < 0 /* || dt >= int(sizeof(names)/sizeof(const char*))*/)
 	return "(invalid data type)";
     return names[dt];
 }
@@ -72,7 +72,7 @@ const char* Ptex::BorderModeName(BorderMode m)
 const char* Ptex::EdgeIdName(EdgeId eid)
 {
     static const char* names[] = { "bottom", "right", "top", "left" };
-    if (eid < 0 || eid >= int(sizeof(names)/sizeof(const char*)))
+    if (eid < 0 /* || eid >= int(sizeof(names)/sizeof(const char*)) */)
 	return "(invalid edge id)";
     return names[eid];
 }
diff --git a/src/tiff.imageio/tiffoutput.cpp b/src/tiff.imageio/tiffoutput.cpp
index 39a3b9c..d66c3c6 100644
--- a/src/tiff.imageio/tiffoutput.cpp
+++ b/src/tiff.imageio/tiffoutput.cpp
@@ -44,6 +44,8 @@
 #include "sysutil.h"
 #include "timer.h"
 
+#include <boost/scoped_array.hpp>
+
 
 OIIO_PLUGIN_NAMESPACE_BEGIN
 
@@ -87,8 +89,7 @@ private:
     }
 
     // Convert planar contiguous to planar separate data format
-    void contig_to_separate (int n, const unsigned char *contig,
-                             unsigned char *separate);
+    void contig_to_separate (int n, const char *contig, char *separate);
     // Add a parameter to the output
     bool put_parameter (const std::string &name, TypeDesc type,
                         const void *data);
@@ -476,8 +477,7 @@ TIFFOutput::close ()
 /// Helper: Convert n pixels from contiguous (RGBRGBRGB) to separate
 /// (RRRGGGBBB) planarconfig.
 void
-TIFFOutput::contig_to_separate (int n, const unsigned char *contig,
-                                unsigned char *separate)
+TIFFOutput::contig_to_separate (int n, const char *contig, char *separate)
 {
     int channelbytes = m_spec.channel_bytes();
     for (int p = 0;  p < n;  ++p)                     // loop over pixels
@@ -504,7 +504,7 @@ TIFFOutput::write_scanline (int y, int z, TypeDesc format,
         std::vector<unsigned char> scratch2 (m_spec.scanline_bytes());
         std::swap (m_scratch, scratch2);
         m_scratch.resize (m_spec.scanline_bytes());
-        contig_to_separate (m_spec.width, (const unsigned char *)data, &m_scratch[0]);
+        contig_to_separate (m_spec.width, (const char *)data, (char *)&m_scratch[0]);
         for (int c = 0;  c < m_spec.nchannels;  ++c) {
             if (TIFFWriteScanline (m_tif, (tdata_t)&m_scratch[plane_bytes*c], y, c) < 0) {
                 error ("TIFFWriteScanline failed");
@@ -562,9 +562,19 @@ TIFFOutput::write_tile (int x, int y, int z,
         imagesize_t plane_bytes = tile_pixels * m_spec.format.size();
         DASSERT (plane_bytes*m_spec.nchannels == m_spec.tile_bytes());
         m_scratch.resize (m_spec.tile_bytes());
-        contig_to_separate (tile_pixels, (const unsigned char *)data, &m_scratch[0]);
+
+        boost::scoped_array<char> separate_heap;
+        char *separate = NULL;
+        imagesize_t separate_size = plane_bytes * m_spec.nchannels;
+        if (separate_size <= (1<<16))
+            separate = ALLOCA (char, separate_size);  // <=64k ? stack
+        else {                                        // >64k ? heap
+            separate_heap.reset (new char [separate_size]); // will auto-free
+            separate = separate_heap.get();
+        }
+        contig_to_separate (tile_pixels, (const char *)data, separate);
         for (int c = 0;  c < m_spec.nchannels;  ++c) {
-            if (TIFFWriteTile (m_tif, (tdata_t)&m_scratch[plane_bytes*c], x, y, z, c) < 0) {
+            if (TIFFWriteTile (m_tif, (tdata_t)&separate[plane_bytes*c], x, y, z, c) < 0) {
                 error ("TIFFWriteTile failed");
                 return false;
             }
diff --git a/testsuite/maketx/ref/out.txt b/testsuite/maketx/ref/out.txt
new file mode 100644
index 0000000..315b616
--- /dev/null
+++ b/testsuite/maketx/ref/out.txt
@@ -0,0 +1,351 @@
+Reading grid.tx
+grid.tx              : 1000 x 1000, 4 channel, uint8 tiff
+    MIP-map levels: 1000x1000 500x500 250x250 125x125 62x62 31x31 15x15 7x7 3x3 1x1
+    SHA-1: 97D6548FF9E9BFD21424B773B1D84FACDF0C1797
+    channel list: R, G, B, A
+    tile size: 64 x 64
+    oiio:BitsPerSample: 8
+    ImageDescription: "SHA-1=8502BD4D9FA89E6449D73510BA008B2AEEC51E86"
+    Orientation: 1 (normal)
+    XResolution: 72
+    YResolution: 72
+    ResolutionUnit: 0
+    DocumentName: "g.tif"
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 8
+    compression: "zip"
+    IPTC:Caption: "SHA-1=8502BD4D9FA89E6449D73510BA008B2AEEC51E86"
+    tiff:PageNumber: "0"
+    tiff:RowsPerStrip: "8"
+Reading grid-resize.tx
+grid-resize.tx       : 1024 x 1024, 4 channel, uint8 tiff
+    MIP-map levels: 1024x1024 512x512 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: C12F91CE509A947ACE16DECA103E73CB62D2AC09
+    channel list: R, G, B, A
+    tile size: 64 x 64
+    oiio:BitsPerSample: 8
+    ImageDescription: "SHA-1=6E041D85002929587707DA67BD1CB10F7F8A6B4D"
+    Orientation: 1 (normal)
+    XResolution: 72
+    YResolution: 72
+    ResolutionUnit: 0
+    DocumentName: "g.tif"
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 8
+    compression: "zip"
+    IPTC:Caption: "SHA-1=6E041D85002929587707DA67BD1CB10F7F8A6B4D"
+    tiff:PageNumber: "0"
+    tiff:RowsPerStrip: "8"
+Reading checker-uint16.tx
+checker-uint16.tx    :  128 x  128, 4 channel, uint16 tiff
+    MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: 75BCACCE72A4275AD11FAF4A5D328C624DFF69AB
+    channel list: R, G, B, A
+    tile size: 64 x 64
+    oiio:BitsPerSample: 16
+    ImageDescription: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    Orientation: 1 (normal)
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 5
+    compression: "zip"
+    IPTC:Caption: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    tiff:RowsPerStrip: "32"
+Reading checker-1chan.tx
+checker-1chan.tx     :  128 x  128, 1 channel, uint8 tiff
+    MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: F7DAC8772B7C6BFABDA884D61ABC84844C949AF1
+    channel list: A
+    tile size: 64 x 64
+    oiio:BitsPerSample: 8
+    ImageDescription: "SHA-1=4226806DDDE5A323C47B930B33702F73DF2EE01B"
+    Orientation: 1 (normal)
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 5
+    compression: "zip"
+    IPTC:Caption: "SHA-1=4226806DDDE5A323C47B930B33702F73DF2EE01B"
+    tiff:RowsPerStrip: "32"
+Reading checker-16x32tile.tx
+checker-16x32tile.tx :  128 x  128, 4 channel, uint8 tiff
+    MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6
+    channel list: R, G, B, A
+    tile size: 16 x 32
+    oiio:BitsPerSample: 8
+    ImageDescription: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    Orientation: 1 (normal)
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 5
+    compression: "zip"
+    IPTC:Caption: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    tiff:RowsPerStrip: "32"
+Reading checker-seplzw.tx
+checker-seplzw.tx    :  128 x  128, 4 channel, uint8 tiff
+    MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6
+    channel list: R, G, B, A
+    tile size: 64 x 64
+    oiio:BitsPerSample: 8
+    ImageDescription: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    Orientation: 1 (normal)
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "separate"
+    tiff:Compression: 5
+    compression: "lzw"
+    IPTC:Caption: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    tiff:RowsPerStrip: "32"
+Reading checker-clamp.tx
+checker-clamp.tx     :  128 x  128, 4 channel, uint8 tiff
+    MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6
+    channel list: R, G, B, A
+    tile size: 64 x 64
+    oiio:BitsPerSample: 8
+    ImageDescription: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    Orientation: 1 (normal)
+    textureformat: "Plain Texture"
+    wrapmodes: "clamp,clamp"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 5
+    compression: "zip"
+    IPTC:Caption: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    tiff:RowsPerStrip: "32"
+Reading checker-permir.tx
+checker-permir.tx    :  128 x  128, 4 channel, uint8 tiff
+    MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6
+    channel list: R, G, B, A
+    tile size: 64 x 64
+    oiio:BitsPerSample: 8
+    ImageDescription: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    Orientation: 1 (normal)
+    textureformat: "Plain Texture"
+    wrapmodes: "periodic,mirror"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 5
+    compression: "zip"
+    IPTC:Caption: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    tiff:RowsPerStrip: "32"
+Reading checker-nomip.tx
+checker-nomip.tx     :  128 x  128, 4 channel, uint8 tiff
+    SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6
+    channel list: R, G, B, A
+    tile size: 64 x 64
+    oiio:BitsPerSample: 8
+    ImageDescription: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    Orientation: 1 (normal)
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 5
+    compression: "zip"
+    IPTC:Caption: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    tiff:RowsPerStrip: "32"
+Reading checker-camera.tx
+checker-camera.tx    :  128 x  128, 4 channel, uint8 tiff
+    MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6
+    channel list: R, G, B, A
+    tile size: 64 x 64
+    oiio:BitsPerSample: 8
+    ImageDescription: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    Orientation: 1 (normal)
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 5
+    compression: "zip"
+    worldtocamera: 1 0 0 0 0 2 0 0 0 0 1 0 0 0 0 1
+    worldtoscreen: 3 0 0 0 0 3 0 0 0 0 3 0 1 2 3 1
+    IPTC:Caption: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    tiff:RowsPerStrip: "32"
+Reading checker-opaque.tx
+checker-opaque.tx    :  128 x  128, 3 channel, uint8 tiff
+    MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: 6827D9ED3A5674C1FAB56F96E5D7D06796D43144
+    channel list: R, G, B
+    tile size: 64 x 64
+    oiio:BitsPerSample: 8
+    ImageDescription: "SHA-1=32F1C7F592C6880B53E690F0F32A67A13936D71B"
+    Orientation: 1 (normal)
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 5
+    compression: "zip"
+    IPTC:Caption: "SHA-1=32F1C7F592C6880B53E690F0F32A67A13936D71B"
+    tiff:RowsPerStrip: "32"
+Reading gray-mono.tx
+gray-mono.tx         :  256 x  256, 1 channel, uint8 tiff
+    MIP-map levels: 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: F5C1F9FA963006D501B14542F5E941AEDB4C5907
+    channel list: A
+    tile size: 64 x 64
+    oiio:BitsPerSample: 8
+    ImageDescription: "SHA-1=F01458A8FF5AEBE45EFB49BD1A2382226D9D838A"
+    Orientation: 1 (normal)
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 5
+    compression: "zip"
+    IPTC:Caption: "SHA-1=F01458A8FF5AEBE45EFB49BD1A2382226D9D838A"
+    tiff:RowsPerStrip: "32"
+Reading pink-mono.tx
+pink-mono.tx         :  256 x  256, 3 channel, uint8 tiff
+    MIP-map levels: 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: D781FABF7CF958AE9D9429255C8591C77BAA9917
+    channel list: R, G, B
+    tile size: 64 x 64
+    oiio:BitsPerSample: 8
+    ImageDescription: "SHA-1=3E6BE27709F0F97E6E0383039233FB66B086C1BA"
+    Orientation: 1 (normal)
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 5
+    compression: "zip"
+    IPTC:Caption: "SHA-1=3E6BE27709F0F97E6E0383039233FB66B086C1BA"
+    tiff:RowsPerStrip: "32"
+Reading checker-prman.tx
+checker-prman.tx     :  128 x  128, 4 channel, int16 tiff
+    MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: DE42A8468161DA333BABF1BEB60811EA0FEF6534
+    channel list: R, G, B, A
+    tile size: 64 x 32
+    oiio:BitsPerSample: 16
+    ImageDescription: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    Orientation: 1 (normal)
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "separate"
+    tiff:Compression: 5
+    compression: "zip"
+    IPTC:Caption: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    tiff:RowsPerStrip: "32"
+Reading nan.exr
+nan.exr              :   64 x   64, 3 channel, half openexr
+    SHA-1: 47A8E8F3E8B2C3B6B032FCC8C39D3C5FC1AAA390
+    channel list: R, G, B
+    tile size: 64 x 64
+    oiio:ColorSpace: "Linear"
+    compression: "zip"
+    ImageDescription: "SHA-1=93D431375F9CC89BC200B8CDDF6C04DB05271363"
+    fovcot: 1
+    openexr:levelmode: 0
+    PixelAspectRatio: 1
+    screenWindowCenter: 0 0
+    screenWindowWidth: 1
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    Stats Min: 0.000000 0.000000 0.000000 (float)
+    Stats Max: 1.000000 1.000000 1.000000 (float)
+    Stats Avg: 0.500000 0.500000 0.500000 (float)
+    Stats StdDev: 0.500000 0.500000 0.500000 (float)
+    Stats NanCount: 0 0 0 
+    Stats InfCount: 0 0 0 
+    Stats FiniteCount: 4096 4096 4096 
+    Constant: No
+    Monochrome: Yes
+Reading checker-exr.pdq
+checker-exr.pdq      :  128 x  128, 4 channel, half openexr
+    MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: FA921281D5C8AF14FC50885DD54E2E17509202EC
+    channel list: R, G, B, A
+    tile size: 64 x 64
+    oiio:ColorSpace: "Linear"
+    openexr:roundingmode: 0
+    textureformat: "Plain Texture"
+    compression: "zip"
+    Orientation: 1 (normal)
+    ImageDescription: "SHA-1=3DCC044E78762699EBEFA0644CD5CEF62000120C"
+    fovcot: 1
+    PixelAspectRatio: 1
+    screenWindowCenter: 0 0
+    screenWindowWidth: 1
+    wrapmodes: "black,black"
+Reading small.tif
+small.tif            :   64 x   64, 3 channel, uint8 tiff
+    SHA-1: BE1D5EA24E907A4C4B3FB3C28EAC872A20F5B414
+    channel list: R, G, B
+    oiio:BitsPerSample: 8
+    ImageDescription: "foo SHA-1=1234abcd ConstantColor=[0.0,0,-0.0] bar"
+    Orientation: 1 (normal)
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 5
+    compression: "lzw"
+    tiff:RowsPerStrip: 32
+    IPTC:Caption: "foo SHA-1=1234abcd ConstantColor=[0.0,0,-0.0] bar"
+Reading small.tx
+small.tx             :   64 x   64, 3 channel, uint8 tiff
+    MIP-map levels: 64x64 32x32 16x16 8x8 4x4 2x2 1x1
+    SHA-1: BE1D5EA24E907A4C4B3FB3C28EAC872A20F5B414
+    channel list: R, G, B
+    tile size: 64 x 64
+    oiio:BitsPerSample: 8
+    ImageDescription: "foo bar SHA-1=FFB354CFB97E56225E1FDA9484B8DE1B04470DAE ConstantColor=[1,0,0]"
+    Orientation: 1 (normal)
+    textureformat: "Plain Texture"
+    wrapmodes: "black,black"
+    fovcot: 1
+    tiff:PhotometricInterpretation: 2
+    tiff:PlanarConfiguration: 1
+    planarconfig: "contig"
+    tiff:Compression: 5
+    compression: "zip"
+    IPTC:Caption: "foo bar SHA-1=FFB354CFB97E56225E1FDA9484B8DE1B04470DAE ConstantColor=[1,0,0]"
+    tiff:RowsPerStrip: "32"
diff --git a/testsuite/maketx/run.py b/testsuite/maketx/run.py
new file mode 100644
index 0000000..e0f04ef
--- /dev/null
+++ b/testsuite/maketx/run.py
@@ -0,0 +1,102 @@
+#!/usr/bin/python 
+
+# location of oiio-images directory
+oiio_images = parent + "/oiio-images/"
+
+# Just for simplicity, make a checkerboard with a solid alpha
+command += (oiio_app("oiiotool") + " --pattern checker 128x128 4 --ch R,G,B,=1.0"
+            + " -d uint8 -o " + oiio_relpath("checker.tif") + " >> out.txt;\n")
+
+# Basic test - recreate the grid texture
+command += maketx_command (oiio_images + "grid.tif", "grid.tx", showinfo=True)
+
+# Test --resize (to power of 2) with the grid, which is 1000x1000
+command += maketx_command (oiio_images + "grid.tif", "grid-resize.tx",
+                           "--resize", showinfo=True)
+
+# Test -d to set output data type
+command += maketx_command ("checker.tif", "checker-uint16.tx",
+                           "-d uint16", showinfo=True)
+
+# Test --nchannels to restrict the number of channels
+command += maketx_command ("checker.tif", "checker-1chan.tx",
+                           "--nchannels 1", showinfo=True)
+
+# Test --tiles to set a non-default tile size
+command += maketx_command ("checker.tif", "checker-16x32tile.tx",
+                           "--tile 16 32", showinfo=True)
+
+# Test --separate and --compression
+command += maketx_command ("checker.tif", "checker-seplzw.tx",
+                           "--separate --compression lzw", showinfo=True)
+
+# Test --wrap
+command += maketx_command ("checker.tif", "checker-clamp.tx",
+                           "--wrap clamp", showinfo=True)
+
+# Test --swrap and --twrap
+command += maketx_command ("checker.tif", "checker-permir.tx",
+                           "--swrap periodic --twrap mirror", showinfo=True)
+
+# Test --nomipmap
+command += maketx_command ("checker.tif", "checker-nomip.tx",
+                           "--nomipmap", showinfo=True)
+
+# Test --Mcamera, --Mscreen
+command += maketx_command ("checker.tif", "checker-camera.tx",
+                           "--Mcamera 1 0 0 0 0 2 0 0 0 0 1 0 0 0 0 1 --Mscreen 3 0 0 0 0 3 0 0 0 0 3 0 1 2 3 1",
+                           showinfo=True)
+
+# Test --opaque-detect (should drop the alpha channel)
+command += maketx_command ("checker.tif", "checker-opaque.tx",
+                           "--opaque-detect", showinfo=True)
+
+# Test --monochrome-detect (first create a monochrome image)
+command += (oiio_app("oiiotool") + " --pattern constant:color=.25,.25,.25 256x256 3 "
+            + " -d uint8 -o " + oiio_relpath("gray.tif") + " >> out.txt;\n")
+command += maketx_command ("gray.tif", "gray-mono.tx",
+                           "--monochrome-detect", showinfo=True)
+
+# Test --monochrome-detect on something that is NOT monochrome
+command += (oiio_app("oiiotool") + " --pattern constant:color=.25,.2,.15 256x256 3 "
+            + " -d uint8 -o " + oiio_relpath("pink.tif") + " >> out.txt;\n")
+command += maketx_command ("pink.tif", "pink-mono.tx",
+                           "--monochrome-detect", showinfo=True)
+
+# Test --prman : should save 'separate' planarconfig, and funny 64x32 tiles
+# since we are specifying 16 bits, and it should save as 'int16' even though
+# we asked for unsigned.
+command += maketx_command ("checker.tif", "checker-prman.tx",
+                           "-d uint16 --prman", showinfo=True)
+
+# Test --fixnan : take advantage of the bad.exr images in 
+# testsuite/oiiotool-fixnan.  (Use --nomipmap to cut down on stats output)
+# FIXME: would also like to test --checknan, but the problem with that is
+# that is actually FAILS if there's a nan.
+command += maketx_command ("../oiiotool-fixnan/bad.exr",
+                           "nan.exr", "--fixnan box3 --nomipmap",
+                           showinfo=True, showinfo_extra="--stats")
+
+# Test --format to force exr even though it can't be deduced from the name.
+command += maketx_command ("checker.tif", "checker-exr.pdq",
+                           "--format exr", showinfo=True)
+
+# Test that we cleanly replace any existing SHA-1 hash and ConstantColor
+# hint in the ImageDescription of the input file.
+command += (oiio_app("oiiotool") + " --pattern constant:color=1,0,0 64x64 3 "
+            + " --caption \"foo SHA-1=1234abcd ConstantColor=[0.0,0,-0.0] bar\""
+            + " -d uint8 -o " + oiio_relpath("small.tif") + " >> out.txt;\n")
+command += info_command ("small.tif", safematch=1);
+command += maketx_command ("small.tif", "small.tx",
+                           "--oiio --constant-color-detect", showinfo=True)
+
+
+outputs = [ "out.txt" ]
+
+
+
+# To do:  --filter --checknan --fullpixels
+#         --prman-metadata --ignore-unassoc
+#         --mipimage 
+#         --envlatl TIFF, --envlatl EXR
+#         --colorconvert --unpremult -u --fovcot
diff --git a/testsuite/oiiotool-fixnan/ref/out.txt b/testsuite/oiiotool-fixnan/ref/out.txt
index 9b45b4b..4b6a46c 100644
--- a/testsuite/oiiotool-fixnan/ref/out.txt
+++ b/testsuite/oiiotool-fixnan/ref/out.txt
@@ -4,7 +4,6 @@ bad.exr              :   64 x   64, 3 channel, half openexr
     channel list: R, G, B
     oiio:ColorSpace: "Linear"
     compression: "zip"
-    DateTime: "2012:02:24 23:11:09"
     PixelAspectRatio: 1
     screenWindowCenter: 0 0
     screenWindowWidth: 1
@@ -23,9 +22,6 @@ black.exr            :   64 x   64, 3 channel, half openexr
     channel list: R, G, B
     oiio:ColorSpace: "Linear"
     compression: "zip"
-    Exif:ImageHistory: "../../oiiotool/oiiotool bad.exr --fixnan black -o black.exr"
-    Software: "OpenImageIO 1.1.2 : ../../oiiotool/oiiotool bad.exr --fixnan black -o black.exr"
-    DateTime: "2012:02:24 23:11:09"
     PixelAspectRatio: 1
     screenWindowCenter: 0 0
     screenWindowWidth: 1
@@ -44,9 +40,6 @@ box3.exr             :   64 x   64, 3 channel, half openexr
     channel list: R, G, B
     oiio:ColorSpace: "Linear"
     compression: "zip"
-    Exif:ImageHistory: "../../oiiotool/oiiotool bad.exr --fixnan box3 -o box3.exr"
-    Software: "OpenImageIO 1.1.2 : ../../oiiotool/oiiotool bad.exr --fixnan box3 -o box3.exr"
-    DateTime: "2012:02:24 23:11:09"
     PixelAspectRatio: 1
     screenWindowCenter: 0 0
     screenWindowWidth: 1
diff --git a/testsuite/oiiotool-fixnan/run.py b/testsuite/oiiotool-fixnan/run.py
index 61f6653..fdf8116 100755
--- a/testsuite/oiiotool-fixnan/run.py
+++ b/testsuite/oiiotool-fixnan/run.py
@@ -4,9 +4,9 @@ command += (oiio_app ("oiiotool") +
                      " bad.exr --fixnan black -o black.exr >> out.txt ;\n")
 command += (oiio_app ("oiiotool") + 
                      " bad.exr --fixnan box3 -o box3.exr >> out.txt ;\n")
-command += info_command ("bad.exr", "--stats")
-command += info_command ("black.exr", "--stats")
-command += info_command ("box3.exr", "--stats")
+command += info_command ("bad.exr", "--stats", safematch=True)
+command += info_command ("black.exr", "--stats", safematch=True)
+command += info_command ("box3.exr", "--stats", safematch=True)
 
 # Outputs to check against references
 outputs = [ "black.exr", "box3.exr", "out.txt" ]
diff --git a/testsuite/runtest.py b/testsuite/runtest.py
index 71826fe..33da090 100755
--- a/testsuite/runtest.py
+++ b/testsuite/runtest.py
@@ -101,8 +101,11 @@ def oiio_app (app):
 
 
 # Construct a command that will compare two images, appending output to
-# the file "out.txt".
-def info_command (file, extraargs="") :
+# the file "out.txt".  If 'safematch' is nonzero, it will exclude printing
+# of fields that tend to change from run to run or release to release.
+def info_command (file, extraargs="", safematch=0) :
+    if safematch :
+        extraargs += " --no-metamatch \"DateTime|Software|OriginatingProgram|ImageHistory\""
     return (oiio_app("oiiotool") + "--info -v -a --hash " + extraargs
             + " " + oiio_relpath(file,tmpdir) + " >> out.txt ;\n")
 
@@ -111,7 +114,7 @@ def info_command (file, extraargs="") :
 # the file "out.txt".  We allow a small number of pixels to have up to
 # 1 LSB (8 bit) error, it's very hard to make different platforms and
 # compilers always match to every last floating point bit.
-def diff_command (fileA, fileB, extraargs="", silent=0, concat=True) :
+def diff_command (fileA, fileB, extraargs="", silent=False, concat=True) :
     command = (oiio_app("idiff") + "-a "
                + "-failpercent 0.01 -hardfail 0.004 -warn 0.004 "
                + extraargs + " " + oiio_relpath(fileA,tmpdir) 
@@ -123,6 +126,25 @@ def diff_command (fileA, fileB, extraargs="", silent=0, concat=True) :
     return command
 
 
+# Construct a command that will create a texture, appending console
+# output to the file "out.txt".
+def maketx_command (infile, outfile, extraargs="",
+                    showinfo=False, showinfo_extra="",
+                    silent=False, concat=True) :
+    command = (oiio_app("maketx") 
+               + " " + oiio_relpath(infile,tmpdir) 
+               + " " + extraargs
+               + " -o " + oiio_relpath(outfile,tmpdir) )
+    if not silent :
+        command += " >> out.txt"
+    if concat:
+        command += " ;\n"
+    if showinfo:
+        command += info_command (outfile, extraargs=showinfo_extra, safematch=1)
+    return command
+
+
+
 # Construct a command that will test the basic ability to read and write
 # an image, appending output to the file "out.txt".  First, iinfo the
 # file, including a hash (VERY unlikely not to match if we've read
diff --git a/testsuite/tiff-misc/ref/check1.tif b/testsuite/tiff-misc/ref/check1.tif
new file mode 100644
index 0000000..0e7cca2
Binary files /dev/null and b/testsuite/tiff-misc/ref/check1.tif differ
diff --git a/testsuite/tiff-misc/run.py b/testsuite/tiff-misc/run.py
new file mode 100755
index 0000000..3bf4be9
--- /dev/null
+++ b/testsuite/tiff-misc/run.py
@@ -0,0 +1,11 @@
+#!/usr/bin/python 
+
+# Miscellaneous TIFF-related tests
+
+
+
+# Regression test -- we once had a bug where 'separate' planarconfig
+# tiled float files would have data corrupted by a buffer overwrite.
+command += (oiio_app("oiiotool") + "--pattern checker 128x128 4 --tile 64 64 --planarconfig separate -d float -o check1.tif")
+
+outputs = [ "check1.tif" ]

-- 
OpenImageIO packaging



More information about the Pkg-phototools-commits mailing list