summaryrefslogtreecommitdiff
path: root/link
diff options
context:
space:
mode:
Diffstat (limited to 'link')
-rw-r--r--link/Jamfile36
-rw-r--r--link/License.txt662
-rw-r--r--link/Makefile.am11
-rw-r--r--link/Readme.txt6
-rw-r--r--link/afiles7
-rw-r--r--link/collink.c3298
-rw-r--r--link/monoplot.c231
-rw-r--r--link/pathplot.c281
8 files changed, 4532 insertions, 0 deletions
diff --git a/link/Jamfile b/link/Jamfile
new file mode 100644
index 0000000..4d4dc24
--- /dev/null
+++ b/link/Jamfile
@@ -0,0 +1,36 @@
+
+# JAM style makefile for collink and friends
+
+#PREF_CCFLAGS += $(CCOPTFLAG) ; # Turn optimisation on
+PREF_CCFLAGS += $(CCDEBUGFLAG) ; # Debugging flags
+#PREF_CCFLAGS += $(CCPROFFLAG) ; # Profile flags
+#PREF_LINKFLAGS += $(LINKPROFFLAG) ; # Profile flags
+#PREF_CCFLAGS += $(CCHEAPDEBUG) ; # Heap Debugging flags
+PREF_LINKFLAGS += $(LINKDEBUGFLAG) ; # Link debugging flags
+
+#Products
+Executables = collink ;
+
+#Install
+InstallBin $(DESTDIR)$(PREFIX)/bin : $(Executables) ;
+
+HDRS = ../h ../icc ../rspl ../plot ../numlib $(TIFFINC) ;
+LINKLIBS = $(TIFFLIB) $(JPEGLIB) ../icc/libicc ../plot/libplot ../numlib/libnum $(LibWin) ;
+
+# K only reprocessor
+#Main icc2ko : icc2ko.c ;
+
+# Monochrome transfer curve plotter
+Main monoplot : monoplot.c ;
+
+HDRS += ../cgats ../xicc ../spectro ../gamut ;
+LINKLIBS = ../xicc/libxicc ../xicc/libxcolorants ../gamut/libgamut.c
+ ../gamut/libgammap ../rspl/librspl ../cgats/libcgats
+ ../plot/libvrml $(LINKLIBS) ;
+
+# ICC linker
+Main collink : collink.c ;
+
+# Device path L curve plotter
+Main pathplot : pathplot.c ;
+
diff --git a/link/License.txt b/link/License.txt
new file mode 100644
index 0000000..a871fcf
--- /dev/null
+++ b/link/License.txt
@@ -0,0 +1,662 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
+
diff --git a/link/Makefile.am b/link/Makefile.am
new file mode 100644
index 0000000..37a3ac9
--- /dev/null
+++ b/link/Makefile.am
@@ -0,0 +1,11 @@
+include $(top_srcdir)/Makefile.shared
+
+LDADD = ../spectro/libinsttypes.la ../xicc/libxicc.la \
+ ../xicc/libxutils.la ../gamut/libgamut.la \
+ ../gamut/libgammap.la ../plot/libplot.la ../plot/libvrml.la \
+ ../rspl/librspl.la $(ICC_LIBS) ../cgats/libcgats.la \
+ ../numlib/libargyllnum.la ../libargyll.la $(X_LIBS) $(TIFF_LIBS)
+
+bin_PROGRAMS = collink pathplot
+
+EXTRA_DIST = License.txt Readme.txt
diff --git a/link/Readme.txt b/link/Readme.txt
new file mode 100644
index 0000000..9e8dacc
--- /dev/null
+++ b/link/Readme.txt
@@ -0,0 +1,6 @@
+This direcory holds the top level icc link code.
+
+collink.exe takes two device ICC profiles, and links
+ then together in a simple fashion, producing
+ a device link ICC profile.
+
diff --git a/link/afiles b/link/afiles
new file mode 100644
index 0000000..7c73bd7
--- /dev/null
+++ b/link/afiles
@@ -0,0 +1,7 @@
+Readme.txt
+License.txt
+afiles
+Jamfile
+collink.c
+monoplot.c
+pathplot.c
diff --git a/link/collink.c b/link/collink.c
new file mode 100644
index 0000000..6c40efd
--- /dev/null
+++ b/link/collink.c
@@ -0,0 +1,3298 @@
+
+/*
+ * collink
+ *
+ * Link two device profiles to create a Device Link Profile
+ *
+ * Author: Graeme W. Gill
+ * Date: 25/11/00
+ * Version: 2.10
+ *
+ * Copyright 2000 - 2005 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/* TTBD:
+ *
+ * Device curve resolution should be taken from the device profiles,
+ * rather than depending on the quality settings (scRGB compression curves)
+ *
+ * Abstract link support intent doesn't work properly for anything
+ * other than absolute. This should really be fixed.
+ */
+
+/* NOTES:
+
+ Normally the device side per channel curves are copied from
+ the source profiles to the link profile on the assumption that
+ the raw linearisation they do is good, and should be maintained
+ for best overall transform accuracy. Since the intermediate
+ link is done in an Lab like space, then this linearisation
+ will even out quantisation error introduced by the Lut
+ in perceptual space. In the case of a Matrix profile with a native
+ XYZ PCS, these assumptions break, since the device curves
+ would generally be linearising the device to Y, not a perceptual
+ space. For this reason we add a Y to L* type curve to the input
+ linearisation curves. For XYZ CLUT based profiles, we just have
+ to hope that the profile has been created well, and that the
+ input curves distribute the indexes reasonably perceptually
+ evenly. For an output (XYZ) Matrix profile, we also
+ use an L* style mixing space.
+
+ In general the per channel curves should be:
+
+ No curve, or
+
+ An optimized curve, or
+
+ The source profile per channel curve plus
+ a Y to L* curve it's a Matrix profile.
+
+
+ Colorspace representations are a bit of a mess. It's hard to know what space
+ color is in at any point, and difficult to transform to match
+ some other element. Putting the different colorspace support within
+ the profile transforms is neat, but may not be flexible enough, since the
+ needed information to do a transform (white point, viewing conditions)
+ might be a bit too closely bound into the profile. An alternative might be
+ to expand the colorspace definition from a tag to include all the
+ other needed information, and create a general colorspace adapter
+ to transform from one to the other, or to limit connections
+ to a cannonical spaces such as XYZ.
+ One big cause of confusion for user and implimentor is how to
+ handle the conflicting intents. What does it mean to link
+ a relative source to absolute abstract to CAM destination ???
+
+ A non-absolute abstract profile is poorly defined (just like
+ the device profiles) if it doesn't define the viewing conditions
+ within which the transform is defined.
+
+ Having separated creating a gamut surface for a raster out
+ (for better modularity), it then creates problems in applying
+ an abstact transform. The abstract transform can't really be
+ applied to a gamut surface, it really needs to be applied
+ to the image data before gamut surface extraction. This is yet
+ another avenue for user & implementor confusion, with regard
+ to intents, even without allowing the user to set the intent
+ of the abstract profile.
+
+ */
+
+#undef USE_MERGE_CLUT_OPT /* [Undef] When using inverse A2B table, merge the output luts */
+ /* with the clut for faster operation, and clipping in */
+ /* Jab space. Turned off because it affects the accuracy too much, */
+ /* and xicc handles Jab clip without this now. */
+
+#define USE_CAM_CLIP_OPT /* [Define] Clip out of gamut in CAM space rather than XYZ or L*a*b* */
+#define ENKHACK /* [Define] Enable K hack code */
+#undef WARN_CLUT_CLIPPING /* [Undef] Print warning if setting clut clips */
+
+#undef DEBUG /* Report values of each sample transformed */
+#undef DEBUGC /* ie "if (tt)" */ /* Debug condition */
+#undef DEBUG_ONE /* test a single value out. Look for DBGNO to set value. */
+#undef NEUTKDEBUG /* print info about neutral L -> K mapping */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include <math.h>
+#include "copyright.h"
+#include "aconfig.h"
+#include "numlib.h"
+#include "icc.h"
+#include "xicc.h"
+#include "gamut.h"
+#include "gammap.h"
+// ~~~99
+#include "vrml.h"
+
+void usage(char *diag, ...) {
+ int i;
+ fprintf(stderr,"Link ICC profiles, Version %s\n",ARGYLL_VERSION_STR);
+ fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
+ if (diag != NULL) {
+ va_list args;
+ fprintf(stderr," Diagnostic: ");
+ va_start(args, diag);
+ vfprintf(stderr, diag, args);
+ va_end(args);
+ fprintf(stderr,"\n");
+ }
+ fprintf(stderr,"usage: collink [options] srcprofile dstprofile linkedprofile\n");
+ fprintf(stderr," -v Verbose\n");
+ fprintf(stderr," -A manufacturer Manufacturer description string\n");
+ fprintf(stderr," -M model Model description string\n");
+ fprintf(stderr," -D description Profile Description string (Default \"inoutfile\")\n");
+ fprintf(stderr," -C copyright Copyright string\n");
+ fprintf(stderr," -V Verify existing profile, rather than link\n");
+ fprintf(stderr," -q lmhu Quality - Low, Medium (def), High, Ultra\n");
+// fprintf(stderr," -q fmsu Speed - Fast, Medium (def), Slow, Ultra Slow\n");
+ fprintf(stderr," -r res Override clut res. set by -q\n");
+ fprintf(stderr," -n Don't preserve device linearization curves in result\n");
+ fprintf(stderr," -f Special :- Force neutral colors to be K only output\n");
+ fprintf(stderr," -fk Special :- Force K only neutral colors to be K only output\n");
+ fprintf(stderr," -F Special :- Force all colors to be K only output\n");
+ fprintf(stderr," -fcmy Special :- Force 100%% C,M or Y only to stay pure \n");
+ fprintf(stderr," -p absprof Include abstract profile in link\n");
+ fprintf(stderr," -s Simple Mode (default)\n");
+ fprintf(stderr," -g [src.gam] Gamut Mapping Mode [optional source image gamut]\n");
+ fprintf(stderr," -G [src.gam] Gamut Mapping Mode using inverse outprofile A2B\n");
+ fprintf(stderr," Simple Mode Options:\n");
+ fprintf(stderr," -i in_intent p = perceptual, r = relative colorimetric,\n");
+ fprintf(stderr," s = saturation, a = absolute colorimetric\n");
+ fprintf(stderr," -o out_intent p = perceptual, r = relative colorimetric,\n");
+ fprintf(stderr," s = saturation, a = absolute colorimetric\n");
+ fprintf(stderr," Gamut Mapping Mode Options:\n");
+ fprintf(stderr," -i intent set linking intent from the following choice:\n");
+ for (i = 0; ; i++) {
+ icxGMappingIntent gmi;
+ if (xicc_enum_gmapintent(&gmi, i, NULL) == icxIllegalGMIntent)
+ break;
+
+ fprintf(stderr," %s\n",gmi.desc);
+ }
+ fprintf(stderr," -w [J,a,b] Use forced whitepoint hack [optional target point]\n");
+// fprintf(stderr," -W J,a,b Forced whitepoint adjustment by delta Jab\n");
+ fprintf(stderr," -c viewcond set source viewing conditions for %s,\n",icxcam_description(cam_default));
+ fprintf(stderr," either an enumerated choice, or a parameter\n");
+ fprintf(stderr," -d viewcond set destination viewing conditions for %s,\n",icxcam_description(cam_default));
+ fprintf(stderr," either an enumerated choice, or parameter:value changes\n");
+ for (i = 0; ; i++) {
+ icxViewCond vc;
+ if (xicc_enum_viewcond(NULL, &vc, i, NULL, 1, NULL) == -999)
+ break;
+
+ fprintf(stderr," %s\n",vc.desc);
+ }
+ fprintf(stderr," s:surround n = auto, a = average, m = dim, d = dark,\n");
+ fprintf(stderr," c = transparency (default average)\n");
+ fprintf(stderr," w:X:Y:Z Adapted white point as XYZ (default media white)\n");
+ fprintf(stderr," w:x:y Adapted white point as x, y\n");
+ fprintf(stderr," a:adaptation Adaptation luminance in cd.m^2 (default 50.0)\n");
+ fprintf(stderr," b:background Background %% of image luminance (default 20)\n");
+ fprintf(stderr," l:scenewhite Scene white in cd.m^2 if surround = auto (default 250)\n");
+ fprintf(stderr," f:flare Flare light %% of image luminance (default 1)\n");
+ fprintf(stderr," f:X:Y:Z Flare color as XYZ (default media white)\n");
+ fprintf(stderr," f:x:y Flare color as x, y\n");
+ fprintf(stderr," -t tlimit set source total ink limit, 0 - 400%% (estimate by default)\n");
+ fprintf(stderr," -T klimit set source black ink limit, 0 - 100%% (estimate by default)\n");
+ fprintf(stderr," Inverse outprofile A2B Options:\n");
+ fprintf(stderr," -k tezhxr CMYK Black generation\n");
+ fprintf(stderr," t = transfer K from source to destination, e = retain K of destination B2A table\n");
+ fprintf(stderr," z = zero K, h = 0.5 K, x = maximum K, r = ramp K (default)\n");
+ fprintf(stderr," -k p stle stpo enpo enle shape\n");
+ fprintf(stderr," p = black target generation curve parameters\n");
+ fprintf(stderr," -k q stle0 stpo0 enpo0 enle0 shape0 stle2 stpo2 enpo2 enle2 shape2\n");
+ fprintf(stderr," q = transfer source K to dual curve limits\n");
+ fprintf(stderr," -K parameters Same as -k, but target is K locus rather than K value itself\n");
+ fprintf(stderr," -l tlimit set destination total ink limit, 0 - 400%% (estimate by default)\n");
+ fprintf(stderr," -L klimit set destination black ink limit, 0 - 100%% (estimate by default)\n");
+ fprintf(stderr," -P Create gamut gammap.wrl diagostic\n");
+ exit(1);
+}
+
+/* ------------------------------------------- */
+/* structures to support icc calbacks */
+
+/* Information needed from a profile */
+struct _profinfo {
+ /* Setup parameters */
+ icRenderingIntent intent; /* Selected ICC rendering intent */
+ icxViewCond vc; /* Viewing Condition for CAM */
+ int inking; /* k inking algorithm, 0 = input, 1 = min, */
+ /* 2 = 0.5, 3 = max, 4 = ramp, 5 = curve, 6 = dual curve */
+ /* 7 = outpupt profile K value */
+ int locus; /* 0 = K target value, 1 = K locus value */
+ icxInk ink; /* Ink parameters */
+
+ /* Operational parameters */
+ icc *c;
+ icmHeader *h;
+ xicc *x;
+ icxLuBase *luo; /* Base XLookup type object */
+ icmLuAlgType alg; /* Type of lookup algorithm */
+ icColorSpaceSignature csp; /* Colorspace */
+ int chan; /* Channels */
+ int nocurve; /* NZ to not use device curve in per channel curve */
+ int lcurve; /* 1 to apply a Y like to L* curve for XYZ Matrix profiles */
+ /* 2 to apply a Y to L* curve for XYZ space */
+ double wp[3]; /* Lab/Jab white point for profile used by wphack & xyzscale */
+ icxLuBase *b2aluo; /* B2A lookup for inking == 7 */
+}; typedef struct _profinfo profinfo;
+
+/* Structure that holds all the color lookup information */
+struct _clink {
+ /* Overall options */
+ int verb;
+ int gamdiag; /* nz, create gammap.wrl diagnostic */
+ int total, count, last; /* Progress count information */
+ int mode; /* 0 = simple mode, 1 = mapping mode, 2 = mapping mode with inverse A2B */
+ int quality; /* 0 = low, 1 = medium, 2 = high, 3 = ultra */
+ int clutres; /* 0 = quality default, !0 = override, then actual during link */
+ int src_kbp; /* nz = Use K only black point as src gamut black point */
+ int dst_kbp; /* nz = Use K only black point as dst gamut black point */
+ int dst_cmymap; /* masks C = 1, M = 2, Y = 4 to force 100% cusp map */
+
+ icColorSpaceSignature pcsor; /* PCS to use between in & out profiles */
+
+ int nhack; /* 0 = off, 1 = hack to map input neutrals to output K only, */
+ /* 2 = map 000K to output K only, 3 = all to K */
+ int cmyhack; /* CMY 100% colorant map though hack, 1 = C, 2 = M, 4 = Y */
+ rspl *pcs2k; /* PCS L to K' lookup for nhack */
+ int wphack; /* 0 = off, 1 = hack to map input wp to output wp, 2 = to hwp[] */
+ double hwp[3]; /* hack destination white point in PCS space */
+ int wphacked; /* Operation flag, set nz if white point was translated */
+ int rel_oride; /* Relative override flag */
+
+ icmFile *abs_fp; /* Abstract profile transform */
+ icRenderingIntent abs_intent;
+ icc *abs_icc;
+ xicc *abs_xicc;
+ icxLuBase *abs_luo; /* NULL if none */
+
+ /* (We current assume that xyzscale can't be used with gmi) */
+ double xyzscale; /* < 1.0 if Y is to be scaled in destination XYZ space */
+ double swxyz[3]; /* Source white point in XYZ */
+
+ icxGMappingIntent gmi;
+ gammap *map; /* Gamut mapping */
+ gammap *Kmap; /* Gamut mapping K in to K out nhack == 2 and K in to K out */
+
+
+ /* Per profile setup information */
+ profinfo in;
+ profinfo out;
+
+}; typedef struct _clink clink;
+
+
+/* ------------------------------------------- */
+//#define YSCALE 1.0
+#define YSCALE (2.0/1.3)
+
+/* Extra non-linearity applied to BtoA XYZ PCS */
+/* This distributes the LUT indexes more evenly in */
+/* perceptual space, greatly improving the B2A accuracy of XYZ LUT */
+/* Since typically XYZ doesn't use the full range of 0-2.0 allowed */
+/* for in the encoding, we scale the cLUT index values to use the 0-1.3 range */
+
+/* (For these functions the encoded XYZ 0.0 - 2.0 range is 0.0 - 1.0 ??) */
+
+/* Y to L* */
+static void y2l_curve(double *out, double *in, int isXYZ) {
+ int i;
+ double val;
+ double isc = 1.0, osc = 1.0;
+
+ /* Scale from 0.0 .. 1.999969 to 0.0 .. 1.0 and back */
+ /* + range adjustment */
+ if (isXYZ) {
+ isc = 32768.0/65535.0 * YSCALE;
+ osc = 65535.0/32768.0;
+ }
+
+ for (i = 0; i < 3; i++) {
+ val = in[i] * isc;
+ if (val > 0.008856451586)
+ val = 1.16 * pow(val,1.0/3.0) - 0.16;
+ else
+ val = 9.032962896 * val;
+ if (val > 1.0)
+ val = 1.0;
+ out[i] = val * osc;
+ }
+}
+
+/* L* to Y */
+static void l2y_curve(double *out, double *in, int isXYZ) {
+ int i;
+ double val;
+ double isc = 1.0, osc = 1.0;
+
+ /* Scale from 0.0 .. 1.999969 to 0.0 .. 1.0 and back */
+ /* + range adjustment */
+ if (isXYZ) {
+ isc = 32768.0/65535.0;
+ osc = 65535.0/32768.0 / YSCALE;
+ }
+
+ /* Use an L* like curve, scaled to the maximum XYZ value */
+ for (i = 0; i < 3; i++) {
+ val = in[i] * isc;
+ if (val > 0.08)
+ val = pow((val + 0.16)/1.16, 3.0);
+ else
+ val = val/9.032962896;
+ out[i] = val * osc;
+ }
+}
+
+/* ------------------------------------------- */
+/* Functions called back in setting up the transform table */
+
+#ifdef DEBUGC
+
+static int tt = 0;
+
+#endif /* DEBUGC */
+
+/* Input table, DevIn -> DevIn' */
+void devi_devip(void *cntx, double *out, double *in) {
+ int rv = 0;
+ clink *p = (clink *)cntx;
+
+#ifdef DEBUGC
+ if (in[0] == 1.0 && in[1] == 1.0 && in[2] == 1.0 && in[3])
+ tt = 1;
+#endif
+
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("DevIn->DevIn' got %f %f %f %f\n",in[0], in[1], in[2], in[3]); fflush(stdout);
+#endif
+
+ if (p->in.nocurve) { /* Don't use profile per channel curves */
+ int i;
+ for (i = 0; i < p->in.chan; i++)
+ out[i] = in[i];
+ } else { /* Use input profile per channel curves */
+ switch(p->in.alg) {
+ case icmMonoFwdType: {
+ icxLuMono *lu = (icxLuMono *)p->in.luo; /* Safe to coerce */
+ rv |= lu->fwd_curve(lu, out, in);
+ break;
+ }
+ case icmMatrixFwdType: {
+ icxLuMatrix *lu = (icxLuMatrix *)p->in.luo; /* Safe to coerce */
+ rv |= lu->fwd_curve(lu, out, in);
+ break;
+ }
+ case icmLutType: {
+ icxLuLut *lu = (icxLuLut *)p->in.luo; /* Safe to coerce */
+ /* Since not PCS, in_abs and matrix cannot be valid, */
+ /* so input curve on own is ok to use. */
+ rv |= lu->input(lu, out, in);
+ break;
+ }
+ default:
+ error("Unexpected algorithm type %d in devi_devip()",p->in.alg);
+ }
+ if (rv >= 2)
+ error("icc lookup failed: %d, %s",p->in.c->errc,p->in.c->err);
+ }
+
+ if (p->in.lcurve) { /* Apply Y to L* */
+//printf("~1 y2l_curve got %f %f %f, isXYZ %d\n",in[0],in[1],in[2],p->in.lcurve == 2);
+ y2l_curve(out, out, p->in.lcurve == 2);
+ }
+
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("DevIn->DevIn' ret %f %f %f %f\n",out[0], out[1], out[2], in[3]); fflush(stdout);
+#endif
+}
+
+
+/* - - - - - - - - - - - - */
+/* clut, DevIn' -> DevOut' */
+void devip_devop(void *cntx, double *out, double *in) {
+ double win[MAX_CHAN]; /* working input values */
+ double pcsv[MAX_CHAN]; /* PCS intermediate value, pre-gamut map */
+ double pcsvm[MAX_CHAN]; /* PCS intermediate value, post-gamut map */
+ double locus[MAX_CHAN]; /* Auxiliary locus values */
+ int wptrig = 0; /* White point hack triggered */
+ double konlyness = 0.0; /* Degree of K onlyness */
+ int ntrig = 0; /* K only output hack triggered */
+ int cmytrig = 0; /* CMY output hack triggered */
+ int i, rv = 0;
+ clink *p = (clink *)cntx;
+
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("DevIn'->DevOut' got %f %f %f %f\n",in[0], in[1], in[2], in[3]); fflush(stdout);
+#endif
+
+#ifdef ENKHACK
+ /* Handle neutral recognition/output K only hack */
+ /* (see discussion at top of file for generalization of this idea) */
+ if (p->nhack == 1 || p->nhack == 2) {
+ double thr = (0.5)/(p->clutres-1.0); /* Match threshold */
+
+ if (p->nhack == 1) {
+ /* We want to see if the input colors are equal (Or a=b= 0.0 ??) */
+ /* li.nhack should have set p->in.nocurve, so we should be getting raw */
+ /* input space device values here. It also made sure that there are at */
+ /* least 3 input channels. */
+
+ if (fabs(in[0] - in[1]) < thr
+ && fabs(in[1] - in[2]) < thr
+ && fabs(in[2] - in[0]) < thr)
+ ntrig = 1; /* K only output triggered flag */
+
+ } else if (p->nhack == 2) {
+ double maxcmy; /* Comute a degree of source "K onlyness" */
+ double maxcmyk;
+
+ maxcmy = in[0]; /* Compute minimum of CMY */
+ if (in[1] > maxcmy)
+ maxcmy = in[1];
+ if (in[2] > maxcmy)
+ maxcmy = in[2];
+
+ maxcmyk = maxcmy; /* Compute minimum of all inks */
+ if (in[3] > maxcmyk)
+ maxcmyk = in[3];
+
+//printf("~1 maxcmy = %f, maxcmyk = %f, in[3] = %f\n",maxcmy,maxcmyk,in[3]);
+ if (in[3] <= 0.0 || maxcmy > in[3]) {
+ konlyness = 0.0;
+ } else {
+ konlyness = (in[3] - maxcmy)/in[3];
+ }
+
+ /* As we approach no colorant, blend towards no Konlyness */
+ if (maxcmyk < 0.2)
+ konlyness *= maxcmyk/0.2;
+
+ /* We want to see if the input colors are exactly K only. */
+ if (in[0] < thr
+ && in[1] < thr
+ && in[2] < thr)
+ ntrig = 1; /* K only output triggered flag */
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("konlyness set to %f\n",konlyness);
+#endif
+
+ }
+ }
+ /* Handle 100% CMY hack */
+ if (p->cmyhack != 0) {
+ double thr = (0.5)/(p->clutres-1.0); /* Match threshold */
+
+ if (p->cmyhack & 1) {
+ if (in[0] > (1.0 - thr)
+ && in[1] < thr
+ && in[2] < thr
+ && (p->in.chan < 4 || in[3] < thr))
+ cmytrig |= 1;
+ }
+ if (p->cmyhack & 2) {
+ if (in[0] < thr
+ && in[1] > (1.0 - thr)
+ && in[2] < thr
+ && (p->in.chan < 4 || in[3] < thr))
+ cmytrig |= 2;
+ }
+ if (p->cmyhack & 4) {
+ if (in[0] < thr
+ && in[1] < thr
+ && in[2] > (1.0 - thr)
+ && (p->in.chan < 4 || in[3] < thr))
+ cmytrig |= 4;
+ }
+ }
+#endif /* ENKHACK */
+
+ if (p->in.lcurve) { /* Apply L* to Y */
+ l2y_curve(win, in, p->in.lcurve == 2);
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("win[] set to L* value %f %f %f %f\n",win[0], win[1], win[2], win[3]); fflush(stdout);
+#endif
+
+ } else {
+ for (i = 0; i < p->in.chan; i++)
+ win[i] = in[i];
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("win[] set to in[] value %f %f %f %f\n",win[0], win[1], win[2], win[3]); fflush(stdout);
+#endif
+ }
+
+ /* Do DevIn' -> PCS */
+ switch(p->in.alg) {
+ case icmMonoFwdType: {
+ icxLuMono *lu = (icxLuMono *)p->in.luo; /* Safe to coerce */
+
+ if (p->in.nocurve) { /* No explicit curve, so do implicit here */
+ rv |= lu->fwd_curve(lu, pcsv, win);
+ rv |= lu->fwd_map(lu, pcsv, pcsv);
+ } else {
+ rv |= lu->fwd_map(lu, pcsv, win);
+ }
+ rv |= lu->fwd_abs(lu, pcsv, pcsv);
+ break;
+ }
+ case icmMatrixFwdType: {
+ icxLuMatrix *lu = (icxLuMatrix *)p->in.luo; /* Safe to coerce */
+
+ if (p->in.nocurve) { /* No explicit curve, so do implicit here */
+ rv |= lu->fwd_curve(lu, pcsv, win);
+ rv |= lu->fwd_matrix(lu, pcsv, pcsv);
+ } else {
+ rv |= lu->fwd_matrix(lu, pcsv, win);
+ }
+ rv |= lu->fwd_abs(lu, pcsv, pcsv);
+ break;
+ }
+ case icmLutType: {
+ icxLuLut *lu = (icxLuLut *)p->in.luo; /* Safe to coerce */
+ if (p->in.nocurve) { /* No explicit curve, so we've got Dev */
+ /* Since not PCS, in_abs and matrix cannot be valid, */
+ /* so input curve on own is ok to use. */
+ rv |= lu->input(lu, pcsv, win); /* Dev -> Dev' */
+ rv |= lu->clut(lu, pcsv, pcsv); /* Dev' -> PCS' */
+ } else { /* We've got Dev' */
+ rv |= lu->clut(lu, pcsv, win); /* Dev' -> PCS' */
+ }
+ /* We've got the input profile PCS' at this point. */
+
+ /* If we're transfering the K value from the input profile to the */
+ /* output, copy it into locus[], which will be given to the inverse */
+ /* lookup function, else the inverse lookup will generate a K using */
+ /* the curve parameters. */
+//printf("~1 out.inking = %d\n",p->out.inking);
+ if (p->out.inking == 0 || p->out.inking == 6) {
+ if (p->out.locus) {
+ /* Converts PCS' to K locus proportion */
+ lu->clut_locus(lu, locus, pcsv, win); /* Compute possible locus values */
+//printf("~1 looked up locus value\n");
+ } else {
+ for (i = 0; i < p->in.chan; i++) /* Target is K input value */
+ locus[i] = win[i];
+ /* Convert K' to K value ready for aux target */
+ if (!p->in.nocurve) { /* we have an input curve, so convert Dev' -> Dev */
+ lu->inv_input(lu, locus, locus);
+ }
+//printf("~1 copied win to locus\n");
+ }
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("Got possible K %s of %f %f %f %f\n",p->out.locus ? "locus" : "value", locus[0],locus[1],locus[2],locus[3]);
+#endif
+ }
+ rv |= lu->output(lu, pcsv, pcsv); /* PCS' -> */
+ rv |= lu->out_abs(lu, pcsv, pcsv); /* PCS */
+ break;
+ }
+ default:
+ error("Unexpected algorithm type %d in devip of devip_devop()",p->in.alg);
+ }
+
+ /* At this point, the PCS is:
+ *
+ * If not gamut mapped:
+ * Lab in the intent selected for the source profile
+ * If gamut mapped:
+ * either
+ * Absolute Lab
+ * or
+ * Jab derived from absolute XYZ via the in/out viewing conditions
+ *
+ * and locus[] contains any auxiliar target values if the
+ * auxiliary is not being created by a rule applied to the PCS.
+ */
+
+ /*
+ * The order to do this intermediate processing is hard to figure out,
+ * as is the interaction between such elements. How should the
+ * abstract profile be properly handled ?
+ * what should we do if the wphack is on and Y scaling is on ?
+ */
+
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("PCS before map %f %f %f\n",pcsv[0], pcsv[1], pcsv[2]); fflush(stdout);
+#endif
+
+ if (p->wphack) {
+ int e;
+ double dd = 0.0;
+ for (e = 0; e < 3; e++) { /* Does this match the input white point ? */
+ double tt;
+ tt = pcsv[e] - p->in.wp[e];
+ dd += tt * tt;
+ }
+ dd = sqrt(dd);
+
+ if (dd < 1.0) { /* Triggered withing 1 delta E */
+ p->wphacked++;
+ wptrig = 1;
+ if (p->wphack == 2) {
+ for (e = 0; e < 3; e++) /* Map input white to given white */
+ pcsv[e] = p->hwp[e];
+ } else {
+ for (e = 0; e < 3; e++) /* Map input white to output white */
+ pcsv[e] = p->out.wp[e];
+ }
+
+#ifndef DEBUG
+ if (p->verb)
+#endif
+ {
+ printf("White point hack mapped %f %f %f to %f %f %f, hit withing %f\n",
+ p->in.wp[0],p->in.wp[1],p->in.wp[2],pcsv[0], pcsv[1], pcsv[2],dd);
+ fflush(stdout);
+ }
+ }
+ }
+
+ /* Do luminence scaling if requested */
+ if (wptrig == 0 && p->xyzscale < 1.0) {
+ double xyz[3];
+
+//printf("~1 got xyzscale = %f\n",p->xyzscale);
+//printf("PCS %f %f %f\n",pcsv[0], pcsv[1], pcsv[2]);
+
+ /* Convert our PCS to XYZ */
+ if (p->pcsor == icxSigJabData) {
+ /* We're being bad in delving inside the xluo, but we'll fix it latter */
+ p->out.luo->cam->cam_to_XYZ(p->out.luo->cam, xyz, pcsv);
+ } else
+ error("Internal :- not setup to handle Y scaling and non-Jab PCS");
+
+//printf("XYZ %f %f %f\n",xyz[0], xyz[1], xyz[2]);
+ /* Scale it */
+ xyz[0] *= p->xyzscale;
+ xyz[1] *= p->xyzscale;
+ xyz[2] *= p->xyzscale;
+
+//printf("scaled XYZ %f %f %f\n",xyz[0], xyz[1], xyz[2]);
+ /* Convert back to PCS */
+ if (p->pcsor == icxSigJabData) {
+ /* We're being bad in delving inside the xluo, but we'll fix it latter */
+ p->out.luo->cam->XYZ_to_cam(p->out.luo->cam, pcsv, xyz);
+ } else
+ error("Internal :- not setup to handle Y scaling and non-Jab PCS");
+
+//printf("scaled PCS %f %f %f\n",pcsv[0], pcsv[1], pcsv[2]);
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("PCS after Y scale %f %f %f\n",pcsv[0], pcsv[1], pcsv[2]); fflush(stdout);
+#endif
+ }
+
+
+ /* Do gamut mapping */
+ if (wptrig == 0 && p->mode > 0 && p->gmi.usemap) {
+ /* We've used pcsor to ensure PCS space is appropriate */
+
+ /* Doing XXXK -> XXXK */
+ if (p->nhack == 2) {
+ /* Ideally we would create a 4D PCSK -> PCSK gamut mapping */
+ /* to smoothly and accurately cope with the changing source */
+ /* and destination gamuts acording to their degree of "K onlyness". */
+ /* In practice we're going to simply interpolated between */
+ /* two extremes: unrestricted gamut and K only black gamut. */
+ double map0[3], map1[3];
+
+ /* Compute blend of normal gamut map and Konly to Konly gamut map */
+ {
+ p->map->domap(p->map, map0, pcsv);
+ p->Kmap->domap(p->Kmap, map1, pcsv);
+ icmBlend3(pcsvm, map0, map1, konlyness);
+ }
+
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("PCS after map0 %f %f %f map1 %f %f %f\n", map0[0], map0[1], map0[2], map1[0], map1[1], map1[2]);
+#endif
+
+ /* Normal gamut mapping */
+ } else {
+ {
+ p->map->domap(p->map, pcsvm, pcsv);
+ }
+ }
+
+
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("PCS after map %f %f %f\n",pcsvm[0], pcsvm[1], pcsvm[2]); fflush(stdout);
+#endif
+ } else {
+ pcsvm[0] = pcsv[0];
+ pcsvm[1] = pcsv[1];
+ pcsvm[2] = pcsv[2];
+ }
+
+ /* Gamut mapped PCS value is now in pcsvm[] */
+
+ /* Abstract profile transform, PCS -> PCS */
+ /* pcsor -> abstract -> pcsor conversion */
+ /* We're applying any abstract profile after gamut mapping, */
+ /* on the assumption is is primarily being used to "correct" the */
+ /* output device. Ideally the gamut mapping should take the change */
+ /* the abstract profile has on the output device into account, but */
+ /* currently we're not doing this.. */
+ if (wptrig == 0 && p->abs_luo != NULL) {
+ /* Abstract profile is either absolute or relative. */
+ /* We need to convert the current PCS into something compatible. */
+ /* This is more ugly than it really should be, so we're ignoring it. */
+ /* We should really run the source through the abstract profile before */
+ /* creating the gamut mapping, to be able to use abstract with gamut */
+ /* mapping properly. */
+ p->abs_luo->lookup(p->abs_luo, pcsvm, pcsvm);
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("PCS after abstract %f %f %f\n",pcsvm[0], pcsvm[1], pcsvm[2]); fflush(stdout);
+#endif
+ }
+
+ /* If we're using the existing B2A inking to determine K, */
+ /* lookup the output profiles K value for this PCS */
+ if (p->mode >= 2 && p->out.inking == 7) {
+ double tdevv[MAX_CHAN];
+
+//printf("~1 dealing with out.inking = %d\n",p->out.inking);
+ if (p->out.alg != icmLutType || p->out.c->header->colorSpace != icSigCmykData)
+ error ("Attempting to use non-CMYK output profile to determine K inking");
+
+ /* Lookup PCS in B2A of output profile to get target K value */
+//printf("~1 looking up pcs %f %f %f in B2A\n", pcsvm[0], pcsvm[1], pcsvm[2]);
+ p->out.b2aluo->lookup(p->out.b2aluo, tdevv, pcsvm);
+//printf("~1 resulting dev %f %f %f %f\n", tdevv[0], tdevv[1], tdevv[2], tdevv[3]);
+
+ if (p->out.locus) {
+ double tpcsv[MAX_CHAN];
+ icxLuLut *lu = (icxLuLut *)p->out.luo; /* Safe to coerce */
+
+ /* Convert PCS to PCS' ready for locus lookup */
+ lu->in_abs(lu, tpcsv, pcsvm);
+ lu->matrix(lu, tpcsv, tpcsv);
+ lu->input(lu, tpcsv, tpcsv);
+ lu->clut_locus(lu, locus, tpcsv, tdevv); /* Compute locus values */
+ } else {
+ for (i = 0; i < p->out.chan; i++) /* Target is K value */
+ locus[i] = tdevv[i];
+ }
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("Got possible K %s of %f %f %f %f\n",p->out.locus ? "locus" : "value", locus[0],locus[1],locus[2],locus[3]);
+#endif
+ }
+
+ /* Do PCS -> DevOut' */
+ if (p->nhack == 3 /* All to K only */
+ || ntrig /* Neutral or K only to K only hack has triggered */
+ || cmytrig) { /* 100% CMY rough hack has triggered */
+
+ if (p->nhack == 3 || ntrig) { /* Neutral to K only hack has triggered */
+ co pp;
+ pp.p[0] = pcsvm[0]; /* Input L value */
+ p->pcs2k->interp(p->pcs2k, &pp); /* L -> K' */
+ if (pp.v[0] < 0.0) /* rspl might have extrapolated */
+ pp.v[0] = 0.0;
+ else if (pp.v[0] > 1.0)
+ pp.v[0] = 1.0;
+ out[0] = out[1] = out[2] = 0.0; /* We know output is CMYK' */
+ out[3] = pp.v[0];
+
+#ifndef DEBUG
+ if (p->verb)
+#endif
+ if (ntrig) {
+ printf("Neutral hack mapped %s to 0 0 0 %f\n", icmPdv(p->in.chan,win), out[3]);
+ fflush(stdout);
+ }
+ } else if (cmytrig) { /* 100% CMY rough hack has triggered */
+ if (cmytrig & 1) {
+ out[0] = 1.0;
+ out[1] = out[2] = out[3] = 0.0;
+ }
+ if (cmytrig & 2) {
+ out[1] = 1.0;
+ out[0] = out[2] = out[3] = 0.0;
+ }
+ if (cmytrig & 4) {
+ out[2] = 1.0;
+ out[0] = out[1] = out[3] = 0.0;
+ }
+
+#ifndef DEBUG
+ if (p->verb)
+#endif
+ if (cmytrig != 0) {
+ if (p->in.chan == 4)
+ printf("CMY hack mapped %s to %s\n",icmPdv(p->in.chan, win), icmPdv(p->out.chan, out));
+ fflush(stdout);
+ }
+ }
+ } else { /* Neutral to K hack has NOT triggered */
+ switch(p->out.alg) {
+ case icmMonoBwdType: {
+ icxLuMono *lu = (icxLuMono *)p->out.luo; /* Safe to coerce */
+
+ rv |= lu->bwd_abs(lu, pcsvm, pcsvm);
+ rv |= lu->bwd_map(lu, out, pcsvm);
+ if (p->out.nocurve) { /* No explicit curve, so do implicit here */
+ rv |= lu->bwd_curve(lu, out, out);
+ }
+ break;
+ }
+ case icmMatrixBwdType: {
+ icxLuMatrix *lu = (icxLuMatrix *)p->out.luo; /* Safe to coerce */
+
+ rv |= lu->bwd_abs(lu, pcsvm, pcsvm);
+ rv |= lu->bwd_matrix(lu, out, pcsvm);
+ if (p->out.nocurve) { /* No explicit curve, so do implicit here */
+ rv |= lu->bwd_curve(lu, out, out);
+ }
+ break;
+ }
+ case icmLutType: {
+ icxLuLut *lu = (icxLuLut *)p->out.luo; /* Safe to coerce */
+
+ if (p->mode < 2) { /* Using B2A table */
+ rv |= lu->in_abs(lu, pcsvm, pcsvm);
+ rv |= lu->matrix(lu, pcsvm, pcsvm);
+ rv |= lu->input(lu, pcsvm, pcsvm);
+ rv |= lu->clut(lu, out, pcsvm);
+ if (p->out.nocurve) { /* No explicit curve, so do implicit here */
+ rv |= lu->output(lu, out, out);
+ }
+
+ } else { /* Use inverse A2B table */
+ int i;
+#ifdef USE_MERGE_CLUT_OPT
+ /* Because we have used the ICX_MERGE_CLUT flag, we don't need */
+ /* to call inv_out_abs() and inv_output() */
+#else
+ rv |= lu->inv_out_abs(lu, pcsvm, pcsvm);
+ rv |= lu->inv_output(lu, pcsvm, pcsvm);
+#endif
+
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("Calling inv_clut with K aux targets %f %f %f %f and pcsvm %f %f %f %f\n",
+ locus[0],locus[1],locus[2],locus[3],pcsvm[0],pcsvm[1],pcsvm[2],pcsvm[3]);
+#endif
+
+ /* locus[] contains possible K target or locus value, */
+ /* so copy it to out[] so that inv_clut will use it. */
+ for (i = 0; i < p->out.chan; i++)
+ out[i] = locus[i];
+
+ rv |= lu->inv_clut(lu, out, pcsvm);
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("Got result %f %f %f %f\n", out[0],out[1],out[2],out[3]);
+#endif
+
+
+ if (p->out.nocurve) { /* No explicit curve, so do implicit here */
+ rv |= lu->inv_input(lu, out, out);
+ }
+ }
+ break;
+ }
+
+ default:
+ error("Unexpected algorithm type %d in devop of devip_devop()",p->out.alg);
+ }
+ if (rv >= 2)
+ error("icc lookup failed: %d, %s",p->in.c->errc,p->in.c->err);
+ }
+
+ if (p->out.lcurve) /* Apply Y to L* */
+ y2l_curve(out, out, p->out.lcurve == 2);
+
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("DevIn'->DevOut' ret %f %f %f %f\n\n",out[0], out[1], out[2], out[3]); fflush(stdout);
+#endif
+
+
+ if (p->verb) { /* Output percent intervals */
+ int pc;
+ p->count++;
+ pc = (int)(p->count * 100.0/p->total + 0.5);
+ if (pc != p->last) {
+ printf("%c%2d%%",cr_char,pc); fflush(stdout);
+ p->last = pc;
+ }
+ }
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Output table, DevOut' -> DevOut */
+void devop_devo(void *cntx, double *out, double *in) {
+ int rv = 0;
+ clink *p = (clink *)cntx;
+ int i;
+
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("DevOut'->DevOut got %f %f %f %f\n",in[0], in[1], in[2], in[4]); fflush(stdout);
+#endif
+
+ for (i = 0; i < p->out.chan; i++)
+ out[i] = in[i];
+
+ if (p->out.lcurve) /* Apply L* to Y */
+ l2y_curve(out, out, p->out.lcurve == 2);
+
+ if (p->out.nocurve == 0) { /* Using per channel curves */
+
+ switch(p->out.alg) {
+ case icmMonoBwdType: {
+ icxLuMono *lu = (icxLuMono *)p->out.luo; /* Safe to coerce */
+ rv |= lu->bwd_curve(lu, out, out);
+ break;
+ }
+ case icmMatrixBwdType: {
+ icxLuMatrix *lu = (icxLuMatrix *)p->out.luo; /* Safe to coerce */
+ rv |= lu->bwd_curve(lu, out, out);
+ break;
+ }
+ case icmLutType: {
+ if (p->mode < 2) { /* Using B2A table */
+ icxLuLut *lu = (icxLuLut *)p->out.luo; /* Safe to coerce */
+ rv |= lu->output(lu, out, out);
+ /* Since not PCS, out_abs is never used */
+ break;
+ } else { /* Use inverse A2B table */
+ icxLuLut *lu = (icxLuLut *)p->out.luo; /* Safe to coerce */
+ rv |= lu->inv_input(lu, out, out);
+ /* Since not PCS, inv_matrix and inv_in_abs is never used */
+ break;
+ }
+ }
+ default:
+ error("Unexpected algorithm type in devop_devo()");
+ }
+ if (rv >= 2)
+ error("icc lookup failed: %d, %s",p->in.c->errc,p->in.c->err);
+ }
+#ifdef DEBUG
+#ifdef DEBUGC
+ DEBUGC
+#endif
+ printf("DevOut'->DevOut ret %f %f %f %f\n",out[0], out[1], out[2], out[3]); fflush(stdout);
+#endif
+#ifdef DEBUGC
+ tt = 0;
+#endif
+}
+
+/* ------------------------------------------- */
+/* Fixup L -> K only lookup table white and black values, */
+/* to compensate for inexact rspl fitting */
+
+/* Context for fixup */
+typedef struct {
+ double kmax;
+ double kmin;
+} pcs2k_ctx;
+
+/* Function to pass to rspl to re-set output values, */
+/* to make them relative to the white and black points */
+static void
+fix_pcs2k_white(
+ void *pp, /* relativectx structure */
+ double *out, /* output value */
+ double *in /* input value */
+) {
+ double f;
+ pcs2k_ctx *p = (pcs2k_ctx *)pp;
+
+ /* Scale so that kmin->hmax becomes 0 to 1 */
+ f = (out[0] - p->kmin)/(p->kmax - p->kmin);
+
+ out[0] = f;
+}
+
+/* ------------------------------------------- */
+/* powell() callback to set XYZ scaling factor */
+
+static double xyzoptfunc(void *cntx, double *v) {
+ clink *p = (clink *)cntx;
+ double swxyz[3], jab[3], dev[MAX_CHAN];
+ double rv;
+ int rc = 0;
+
+ rv = 2.0 - v[0]; /* Make Y as large as possible */
+
+ /* If we wanted to use this function to maximise the brightness */
+ /* we would not limit the scale to 1.0 */
+ if (v[0] > 1.0) {
+ rv += 1000.0;
+ return rv;
+ }
+ if (v[0] < 0.0) {
+ rv += 100.0;
+ return rv;
+ }
+ swxyz[0] = v[0] * p->swxyz[0];
+ swxyz[1] = v[0] * p->swxyz[1];
+ swxyz[2] = v[0] * p->swxyz[2];
+
+//printf("~1 scaled white XYZ = %f %f %f\n", swxyz[0], swxyz[1], swxyz[2]);
+
+ if (p->pcsor == icxSigJabData) {
+ /* We're being bad in delving inside the xluo, but we'll fix it latter */
+ p->out.luo->cam->XYZ_to_cam(p->out.luo->cam, jab, swxyz);
+ } else
+ error("Internal :- not setup to handle Y scaling and non-Jab PCS");
+
+//printf("~1 scaled white Jab = %f %f %f\n", jab[0], jab[1], jab[2]);
+
+ /* Run the target PCS backwards through the output space to see if it clips */
+ switch(p->out.alg) {
+ case icmMonoBwdType: {
+ icxLuMono *lu = (icxLuMono *)p->out.luo; /* Safe to coerce */
+
+ rc = lu->bwd_lookup(p->out.luo, dev, jab);
+ break;
+ }
+ case icmMatrixBwdType: {
+ icxLuMatrix *lu = (icxLuMatrix *)p->out.luo; /* Safe to coerce */
+
+ rc = lu->bwd_lookup(p->out.luo, dev, jab);
+ break;
+ }
+ case icmLutType: {
+ icxLuLut *lu = (icxLuLut *)p->out.luo; /* Safe to coerce */
+
+ if (p->mode < 2) /* Using B2A table */
+ rc = lu->lookup(p->out.luo, dev, jab);
+ else /* Use inverse A2B table */
+ rc = lu->inv_lookup(p->out.luo, dev, jab);
+ break;
+ }
+ default:
+ error("Unexpected algorithm type %d in devop of devip_devop()",p->out.alg);
+ }
+//printf("~1 device = %f %f %f, rc = %d\n", dev[0], dev[1], dev[2], rc);
+ if (rc != 0)
+ rv += 500.0;
+
+//printf("~1 xyzoptfunc rv %f from xyzscale %f\n\n",rv,v[0]);
+ return rv;
+}
+
+/* ------------------------------------------- */
+
+int
+main(int argc, char *argv[]) {
+ int fa, nfa, mfa; /* argument we're looking at */
+ char in_name[MAXNAMEL+1];
+ char sgam_name[MAXNAMEL+1] = "\000"; /* Source gamut name */
+ char abs_name[MAXNAMEL+1] = "\000"; /* Abstract profile name */
+ char out_name[MAXNAMEL+1];
+ char link_name[MAXNAMEL+1];
+ int verify = 0; /* Do verify pass */
+ int outinkset = 0; /* The user specfied an output inking */
+ int intentset = 0; /* The user specified an intent */
+ int vcset = 0; /* Viewing conditions were set by user */
+ int modeset = 0; /* The gamut mapping mode was set by the user */
+ int rv = 0;
+ icxViewCond ivc, ovc; /* Viewing Condition Overrides for in and out profiles */
+ int ivc_e = -1, ovc_e = -1; /* Enumerated viewing condition */
+ clink li; /* Linking information structure */
+ int isJab = 0; /* (Derived from li.mode & li.gmi) NZ if Jab link space */
+ int in_curve_res = 0; /* Input profile A2B input curve resolution (if known) */
+ int out_curve_res = 0; /* Output profile B2A output curve resolution (if known) */
+ profxinf xpi; /* Extra profile information */
+ int i;
+
+ error_program = argv[0];
+ check_if_not_interactive();
+ memset((void *)&xpi, 0, sizeof(profxinf)); /* Init extra profile info to defaults */
+ memset((void *)&li, 0, sizeof(clink));
+
+ /* Set defaults */
+ li.verb = 0;
+ li.count = 0;
+ li.last = -1;
+ li.mode = 0; /* Default simple link mode */
+ li.quality = 1; /* Medium quality */
+ li.clutres = 0; /* No resolution override */
+ li.nhack = 0;
+ li.cmyhack = 0; /* Mask for 100% purity through mapping of CMY */
+ li.pcs2k = NULL;
+ li.wphack = 0;
+ li.wphacked = 0;
+ li.abs_luo = NULL; /* No abstract */
+ li.xyzscale = 1.0; /* No XYZ scaling */
+ li.hwp[0] = li.hwp[1] = li.hwp[2] = 0.0;
+ li.map = NULL;
+ li.Kmap = NULL;
+ li.in.intent = icmDefaultIntent; /* Default */
+ li.in.ink.tlimit = -1.0; /* Default no total limit */
+ li.in.ink.klimit = -1.0; /* Default no black limit */
+ li.in.inking = 4; /* Inking algorithm default = ramp */
+ li.in.locus = 0; /* Default K value target */
+ li.in.nocurve = 0; /* Preserve device linearisation curve */
+ li.in.lcurve = 0; /* Don't apply a Y to L* curve after device curve */
+ li.out.intent = icmDefaultIntent; /* Default */
+ li.out.ink.tlimit = -1.0; /* Default no total limit */
+ li.out.ink.klimit = -1.0; /* Default no black limit */
+ li.out.ink.KonlyLmin = 0; /* Use normal black Lmin for locus */
+ li.out.ink.c.Ksmth = ICXINKDEFSMTH; /* default curve smoothing */
+ li.out.ink.c.Kskew = ICXINKDEFSKEW; /* default curve skew */
+ li.out.ink.x.Ksmth = ICXINKDEFSMTH;
+ li.out.ink.x.Kskew = ICXINKDEFSKEW;
+ li.out.inking = 4; /* Default ramp K */
+ li.out.locus = 0; /* Default K value target */
+ li.out.nocurve = 0; /* Preserve device linearisation curve */
+ li.out.lcurve = 0; /* Don't apply an L* to Y curve before device curve */
+ li.out.b2aluo = NULL; /* B2A lookup for inking == 7 */
+
+ xicc_enum_gmapintent(&li.gmi, icxDefaultGMIntent, NULL); /* Set default overall intent */
+
+ /* Init VC overrides so that we know when the've been set */
+ ivc.Ev = -1;
+ ivc.Wxyz[0] = -1.0; ivc.Wxyz[1] = -1.0; ivc.Wxyz[2] = -1.0;
+ ivc.La = -1.0;
+ ivc.Yb = -1.0;
+ ivc.Lv = -1.0;
+ ivc.Yf = -1.0;
+ ivc.Fxyz[0] = -1.0; ivc.Fxyz[1] = -1.0; ivc.Fxyz[2] = -1.0;
+
+ ovc.Ev = -1;
+ ovc.Wxyz[0] = -1.0; ovc.Wxyz[1] = -1.0; ovc.Wxyz[2] = -1.0;
+ ovc.La = -1.0;
+ ovc.Yb = -1.0;
+ ovc.Lv = -1.0;
+ ovc.Yf = -1.0;
+ ovc.Fxyz[0] = -1.0; ovc.Fxyz[1] = -1.0; ovc.Fxyz[2] = -1.0;
+
+ if (argc < 4)
+ usage("Too few arguments, got %d expect at least 3",argc-1);
+
+ /* Process the arguments */
+ mfa = 3; /* Minimum final arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1+mfa) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?')
+ usage("Requested usage");
+
+ /* Verbosity */
+ else if (argv[fa][1] == 'v') {
+ li.verb = 1;
+ }
+
+ /* Manufacturer description string */
+ else if (argv[fa][1] == 'A') {
+ fa = nfa;
+ if (na == NULL) usage("Expect argument to manufacturer description flag -A");
+ xpi.deviceMfgDesc = na;
+ }
+
+ /* Model description string */
+ else if (argv[fa][1] == 'M') {
+ fa = nfa;
+ if (na == NULL) usage("Expect argument to model description flag -M");
+ xpi.modelDesc = na;
+ }
+
+ /* Profile Description */
+ else if (argv[fa][1] == 'D') {
+ fa = nfa;
+ if (na == NULL) usage("Expect argument to profile description flag -D");
+ xpi.profDesc = na;
+ }
+
+ /* Copyright string */
+ else if (argv[fa][1] == 'C') {
+ fa = nfa;
+ if (na == NULL) usage("Expect argument to copyright flag -C");
+ xpi.copyright = na;
+ }
+
+ /* Verify rather than link */
+ else if (argv[fa][1] == 'V')
+ verify = 1;
+
+ /* Disable profile per channel curve use in device link output */
+ else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') {
+ li.in.nocurve = 1;
+ li.out.nocurve = 1;
+ }
+
+ /* Hack to force input neutrals to K only output */
+ else if (argv[fa][1] == 'f' || argv[fa][1] == 'F') {
+
+ if (argv[fa][1] == 'f') {
+ if (na != NULL) { /* XXXK -> XXXK hack */
+ int j;
+ fa = nfa;
+ for (j = 0; ; j++) {
+ if (na[j] == '\000')
+ break;
+ if (na[j] == 'k' || na[j] == 'K')
+ li.nhack = 2;
+ else if (na[j] == 'c' || na[j] == 'C')
+ li.cmyhack |= 0x1;
+ else if (na[j] == 'm' || na[j] == 'M')
+ li.cmyhack |= 0x2;
+ else if (na[j] == 'y' || na[j] == 'Y')
+ li.cmyhack |= 0x4;
+ else
+ usage("Unexpected argument '%c' to -f flag",na[j]);
+ }
+
+ } else { /* Neutral -> 000K hack */
+ li.nhack = 1;
+ li.in.nocurve = 1; /* Disable input curve to preserve input equality */
+ }
+ } else {
+ li.nhack = 3; /* All -> 000K Hack */
+ }
+ }
+
+ /* Quality */
+ else if (argv[fa][1] == 'q' || argv[fa][1] == 'Q') {
+ fa = nfa;
+ if (na == NULL) usage("Quality flag (-q) needs an argument");
+ switch (na[0]) {
+ case 'f': /* fast */
+ case 'l':
+ case 'L':
+ li.quality = 0;
+ break;
+ case 'm': /* medium */
+ case 'M':
+ li.quality = 1;
+ break;
+ case 's': /* slow */
+ case 'h':
+ case 'H':
+ li.quality = 2;
+ break;
+ case 'u': /* ultra slow */
+ case 'U':
+ li.quality = 3;
+ break;
+ default:
+ usage("Unrecognised quality flag (-q) argument '%c'",na[0]);
+// usage("Unrecognised speed flag (-q) argument '%c'",na[0]);
+ }
+ }
+
+ /* CLUT resolution override */
+ else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') {
+ int rr;
+ fa = nfa;
+ if (na == NULL) usage("Resolution flag (-r) needs an argument");
+ rr = atoi(na);
+ if (rr < 1 || rr > 255) usage("Resolution flag (-r) argument out of range (%d)",rr);
+ li.clutres = rr;
+ }
+
+ /* Abstract profile */
+ else if (argv[fa][1] == 'p') {
+ if (na == NULL) usage("Expected abstract profile filename after -a");
+ fa = nfa;
+ strncpy(abs_name,na,MAXNAMEL); abs_name[MAXNAMEL] = '\000';
+ }
+
+ /* Simple mode */
+ else if (argv[fa][1] == 's') {
+ li.mode = 0;
+ modeset = 1;
+ }
+
+ /* Maping mode */
+ else if (argv[fa][1] == 'g' || argv[fa][1] == 'G') {
+ li.mode = 1;
+ if (argv[fa][1] == 'G') {
+ li.mode = 2;
+ }
+
+ if (na != NULL) { /* Found an optional source gamut */
+ fa = nfa;
+ strncpy(sgam_name,na,MAXNAMEL); sgam_name[MAXNAMEL] = '\000';
+ }
+ modeset = 1;
+ }
+
+ /* White point hack */
+ else if (argv[fa][1] == 'w' || argv[fa][1] == 'W') {
+ li.wphack = 1;
+ if (na != NULL) { // To a particular white point
+ fa = nfa;
+ if (sscanf(na, " %lf , %lf , %lf ",&li.hwp[0], &li.hwp[1], &li.hwp[2]) == 3) {
+ li.wphack = 2;
+ } else
+ usage("Couldn't parse hack white point (-w) value '%s'",na);
+ }
+ }
+ /* Input profile Intent or Mapping mode intent */
+ else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') {
+ fa = nfa;
+ if (na == NULL) usage("Input intent flag (-i) needs an argument");
+ /* Record it for simple mode */
+ switch (na[0]) {
+ case 'p':
+ case 'P':
+ li.in.intent = icPerceptual;
+ break;
+ case 'r':
+ case 'R':
+ li.in.intent = icRelativeColorimetric;
+ break;
+ case 's':
+ case 'S':
+ li.in.intent = icSaturation;
+ break;
+ case 'a':
+ case 'A':
+ li.in.intent = icAbsoluteColorimetric;
+ break;
+ default:
+ li.in.intent = icMaxEnumIntent; /* Detect error later */
+ }
+ /* Record it for gamut mapping mode */
+ if (xicc_enum_gmapintent(&li.gmi, icxNoGMIntent, na) == -999)
+ usage("Input intent (-i) argument '%s' isn't recognised",na);
+ intentset = 1;
+ }
+
+ /* Output profile Intent */
+ else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') {
+ fa = nfa;
+ if (na == NULL) usage("Output intent flag (-o) needs an argument");
+ switch (na[0]) {
+ case 'p':
+ case 'P':
+ li.out.intent = icPerceptual;
+ break;
+ case 'r':
+ case 'R':
+ li.out.intent = icRelativeColorimetric;
+ break;
+ case 's':
+ case 'S':
+ li.out.intent = icSaturation;
+ break;
+ case 'a':
+ case 'A':
+ li.out.intent = icAbsoluteColorimetric;
+ break;
+ default:
+ usage("Output intent (-o) argument '%s' not recognised",na);
+ }
+ }
+
+ /* Viewing conditions */
+ else if (argv[fa][1] == 'c' || argv[fa][1] == 'C'
+ || argv[fa][1] == 'd' || argv[fa][1] == 'D') {
+ icxViewCond *vc;
+
+ if (argv[fa][1] == 'c' || argv[fa][1] == 'C') {
+ vc = &ivc;
+ } else {
+ vc = &ovc;
+ }
+
+ fa = nfa;
+ if (na == NULL) usage("Viewing conditions flag (-[cd]) needs an argument");
+#ifdef NEVER
+ if (na[0] >= '0' && na[0] <= '9') {
+ if (vc == &ivc)
+ ivc_e = atoi(na);
+ else
+ ovc_e = atoi(na);
+ } else
+#endif
+ if (na[1] != ':') {
+ /* Enumerated condition index */
+ if (vc == &ivc) {
+ if ((ivc_e = xicc_enum_viewcond(NULL, NULL, -2, na, 1, NULL)) == -999)
+ usage("Unrecognised viewing condition enumeration '%s'",na);
+ } else {
+ if ((ovc_e = xicc_enum_viewcond(NULL, NULL, -2, na, 1, NULL)) == -999)
+ usage("Unrecognised viewing condition enumeration '%s'",na);
+ }
+ } else if (na[0] == 's' || na[0] == 'S') {
+ if (na[1] != ':')
+ usage("Viewing conditions (-[cd]s) missing ':'");
+ if (na[2] == 'n' || na[2] == 'N') {
+ vc->Ev = vc_none; /* Automatic */
+ } else if (na[2] == 'a' || na[2] == 'A') {
+ vc->Ev = vc_average;
+ } else if (na[2] == 'm' || na[2] == 'M') {
+ vc->Ev = vc_dim;
+ } else if (na[2] == 'd' || na[2] == 'D') {
+ vc->Ev = vc_dark;
+ } else if (na[2] == 'c' || na[2] == 'C') {
+ vc->Ev = vc_cut_sheet;
+ } else
+ usage("Viewing condition (-[cd]) unrecognised surround '%c'",na[2]);
+ } else if (na[0] == 'w' || na[0] == 'W') {
+ double x, y, z;
+ if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) {
+ vc->Wxyz[0] = x; vc->Wxyz[1] = y; vc->Wxyz[2] = z;
+ } else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) {
+ vc->Wxyz[0] = x; vc->Wxyz[1] = y;
+ } else
+ usage("Viewing condition (-[cd]w) unrecognised white point '%s'",na+1);
+ } else if (na[0] == 'a' || na[0] == 'A') {
+ if (na[1] != ':')
+ usage("Viewing conditions (-[cd]a) missing ':'");
+ vc->La = atof(na+2);
+ } else if (na[0] == 'b' || na[0] == 'B') {
+ if (na[1] != ':')
+ usage("Viewing conditions (-[cd]b) missing ':'");
+ vc->Yb = atof(na+2)/100.0;
+ } else if (na[0] == 'l' || na[0] == 'L') {
+ if (na[1] != ':')
+ usage("Viewing conditions (-[cd]l) missing ':'");
+ vc->Lv = atof(na+2);
+ } else if (na[0] == 'f' || na[0] == 'F') {
+ double x, y, z;
+ if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) {
+ vc->Fxyz[0] = x; vc->Fxyz[1] = y; vc->Fxyz[2] = z;
+ } else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) {
+ vc->Fxyz[0] = x; vc->Fxyz[1] = y;
+ } else if (sscanf(na+1,":%lf",&x) == 1) {
+ vc->Yf = x/100.0;
+ } else
+ usage("Viewing condition (-[cd]f) unrecognised flare '%s'",na+1);
+ } else
+ usage("Viewing condition (-[cd]) unrecognised sub flag '%c'",na[0]);
+ vcset = 1; /* Viewing conditions were set by user */
+ }
+
+ /* Inking rule */
+ else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') {
+ fa = nfa;
+ if (na == NULL) usage("Inking rule flag (-k) needs an argument");
+ if (argv[fa][1] == 'k')
+ li.out.locus = 0; /* Use K value target */
+ else
+ li.out.locus = 1; /* Use K locus target */
+ switch (na[0]) {
+ case 't':
+ case 'T':
+ li.out.inking = 0; /* Use input K value for output */
+ break;
+ case 'e':
+ case 'E':
+ li.out.inking = 7; /* Use output K value as guide */
+ break;
+ case 'z':
+ case 'Z':
+ li.out.inking = 1; /* Use minimum k */
+ break;
+ case 'h':
+ case 'H':
+ li.out.inking = 2; /* Use half k */
+ break;
+ case 'x':
+ case 'X':
+ li.out.inking = 3; /* Use maximum k */
+ break;
+ case 'r':
+ case 'R':
+ li.out.inking = 4; /* Use ramp k */
+ break;
+ case 'p':
+ case 'P':
+ case 'q':
+ case 'Q':
+ li.out.inking = 5; /* Use curve parameter */
+
+ ++fa;
+ if (fa >= argc) usage("Inking rule (-kp) expects more parameters");
+ li.out.ink.c.Kstle = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc) usage("Inking rule (-kp) expects more parameters");
+ li.out.ink.c.Kstpo = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kp) expects more parameters");
+ li.out.ink.c.Kenpo = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kp) expects more parameters");
+ li.out.ink.c.Kenle = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kp) expects more parameters");
+ li.out.ink.c.Kshap = atof(argv[fa]);
+
+ if (na[0] == 'q' || na[0] == 'Q') {
+ li.out.inking = 6; /* Use transfer to dual curve parameter */
+
+ ++fa;
+ if (fa >= argc) usage("Inking rule (-kq) expects more parameters");
+ li.out.ink.x.Kstle = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc) usage("Inking rule (-kq) expects more parameters");
+ li.out.ink.x.Kstpo = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kq) expects more parameters");
+ li.out.ink.x.Kenpo = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc) usage("Inking rule (-kq) expects more parameters");
+ li.out.ink.x.Kenle = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kq) expects more parameters");
+ li.out.ink.x.Kshap = atof(argv[fa]);
+
+ }
+ break;
+ default:
+ usage("Inking rule (-k) unknown sub flag '%c'",na[0]);
+ }
+ outinkset = 1; /* The user set an inking */
+ }
+ /* Input ink limits */
+ else if (argv[fa][1] == 't') {
+ int tlimit;
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -t");
+ tlimit = atoi(na);
+ if (tlimit >= 0)
+ li.in.ink.tlimit = tlimit/100.0;
+ else
+ li.in.ink.tlimit = -1.0;
+ }
+ else if (argv[fa][1] == 'T') {
+ int klimit;
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -T");
+ klimit = atoi(na);
+ if (klimit >= 0)
+ li.in.ink.klimit = klimit/100.0;
+ else
+ li.in.ink.klimit = -1.0;
+ }
+ /* Output ink limits */
+ else if (argv[fa][1] == 'l') {
+ int tlimit;
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -l");
+ tlimit = atoi(na);
+ if (tlimit >= 0)
+ li.out.ink.tlimit = tlimit/100.0;
+ else
+ li.out.ink.tlimit = -1.0;
+ if (li.mode < 2) /* Set minimum link mode */
+ li.mode = 2;
+ }
+ else if (argv[fa][1] == 'L') {
+ int klimit;
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -L");
+ klimit = atoi(na);
+ if (klimit >= 0)
+ li.out.ink.klimit = klimit/100.0;
+ else
+ li.out.ink.klimit = -1.0;
+ if (li.mode < 2) /* Set minimum link mode */
+ li.mode = 2;
+ }
+
+ /* Gammut mapping diagnostic plots */
+ else if (argv[fa][1] == 'P')
+ li.gamdiag = 1;
+
+ else
+ usage("Unknown flag '%c'",argv[fa][1]);
+ } else
+ break;
+ }
+
+ if (fa >= argc || argv[fa][0] == '-') usage("Missing input profile");
+ strncpy(in_name,argv[fa++],MAXNAMEL); in_name[MAXNAMEL] = '\000';
+
+ if (fa >= argc || argv[fa][0] == '-') usage("Missing output profile");
+ strncpy(out_name,argv[fa++],MAXNAMEL); out_name[MAXNAMEL] = '\000';
+
+ if (fa >= argc || argv[fa][0] == '-') usage("Missing result profile");
+ strncpy(link_name,argv[fa++],MAXNAMEL); link_name[MAXNAMEL] = '\000';
+
+ if (xpi.profDesc == NULL)
+ xpi.profDesc = link_name; /* Default description */
+
+ if (li.verb)
+ printf("Got options\n");
+
+ /* - - - - - - - - - - - - - - - - - - - */
+#ifndef ENKHACK /* Enable K hack code */
+ warning("!!!!!! linkl/collink.c ENKHACK not enabled !!!!!!");
+#endif
+ /* - - - - - - - - - - - - - - - - - - - */
+ /* Sanity checking/defaulting of options */
+
+ /* Deal with options that need link mode -g */
+ if (li.mode < 1
+ && (li.in.intent == icMaxEnumIntent /* User set a smart linking intent */
+ || vcset /* Viewing conditions were set by user */
+ || li.wphack)) {
+ if (modeset) {
+ if (li.in.intent == icMaxEnumIntent)
+ warning("Complex intent can't work with -s linking mode");
+ else if (vcset)
+ warning("Viewing conditions are ignored with -s linking mode");
+ else if (li.wphack)
+ warning("White point hack is ignored with -s linking mode");
+ } else {
+ if (li.verb) {
+ if (li.in.intent == icMaxEnumIntent)
+ printf("Setting -g to enable Gamut Mapping mode intent\n");
+ else if (vcset)
+ printf("Setting -g to enable viewing conditions\n");
+ else if (li.wphack)
+ printf("Setting -g to enable white point hack\n");
+ }
+ li.mode = 1;
+ }
+ }
+
+ /* Deal with options that need link mode -G */
+ if (li.mode < 2
+ && (outinkset /* The user set a K inking rule */
+ || li.out.ink.tlimit >= 0.0 /* The user set an output total limit */
+ || li.out.ink.klimit >= 0.0)) { /* The user set an output black limit */
+ if (modeset) {
+ if (outinkset)
+ warning("Black inking can't work with -s or -g linking mode");
+ else if (li.out.ink.tlimit >= 0.0 || li.out.ink.klimit >= 0.0)
+ warning("Ink limiting can't work with -s linking mode");
+ } else {
+ if (li.verb) {
+ if (outinkset)
+ printf("Setting -G to enable black inking\n");
+ else if (li.out.ink.tlimit >= 0.0 || li.out.ink.klimit >= 0.0)
+ printf("Setting -G to enable ink limiting\n");
+ }
+ li.mode = 2;
+ }
+ }
+
+ /* Deal with options that complement -f -F */
+ if (li.nhack || li.cmyhack) {
+
+ /* Ideally we need to set K inking and map to K only black point, which require -G mode */
+ if (li.mode < 2) {
+ if (li.nhack == 1) { /* All neutrals to K only */
+ if (modeset) {
+ warning("-f will give best result with -G mode");
+ } else {
+ if (li.verb)
+ printf("Setting -G mode to complement -f option\n");
+ li.mode = 2;
+ }
+ } else if (li.nhack == 2) { /* K only in to K only out */
+ if (modeset) {
+ warning("For better results use -G mode with -fk option");
+ } else {
+ if (li.verb)
+ printf("Setting -G mode to complement -fk option\n");
+ li.mode = 2;
+ }
+ } else if (li.nhack == 3) { /* All to K only out */
+ if (modeset) {
+ warning("For better results use -G mode with -F option");
+ } else {
+ if (li.verb)
+ printf("Setting -G mode to complement -F option\n");
+ li.mode = 2;
+ }
+ }
+ if (li.cmyhack != 0) { /* Map pure 100% CMY to pure CMY */
+ if (modeset) {
+ warning("For better results use -G mode with -fcmy options");
+ } else {
+ if (li.verb)
+ printf("Setting -G mode to complement -fcmy options\n");
+ li.mode = 2;
+ }
+ }
+ }
+
+ /* Ideally we should use an appropriate K inking */
+ if (li.mode >= 2) { /* Gammut mapping mode */
+ if (li.nhack == 1 && li.out.inking != 3) { /* All neutrals to K only */
+ if (outinkset) {
+ warning("For better results use -kx with -f option");
+ } else {
+ if (li.verb)
+ printf("Setting -kx to complement -f option\n");
+ li.out.inking = 3; /* Use maximum K */
+ }
+ } else if (li.nhack == 2 && li.out.inking != 0) { /* K only in to K only out */
+ if (outinkset) {
+ warning("For better results use -kt with -fk option");
+ } else {
+ if (li.verb)
+ printf("Setting -kt to complement -fk option\n");
+ li.out.inking = 0; /* Use input K value for output */
+ }
+ } else if (li.nhack == 3 && li.out.inking != 3) { /* All colors to K only */
+ if (modeset) {
+ warning("For better results use -kx with -F option");
+ } else {
+ if (li.verb)
+ printf("Setting -kx to complement -f option\n");
+ li.out.inking = 3; /* Use maximum K */
+ }
+ }
+ }
+
+ /* Ideally we should use an appropriate gamut mapping */
+ if (li.mode >= 1) { /* Gammut mapping mode */
+
+ if (li.gmi.usemap == 0 || li.gmi.greymf < 1.0 /* Not mapping black point */
+ || li.gmi.glumbcpf < 1.0 || li.gmi.glumbexf < 1.0) {
+ if (li.nhack == 1) { /* All neutrals to K only */
+ if (intentset) {
+ warning("For better results use an intent that maps black point with -f option");
+ } else {
+ if (li.verb)
+ printf("Setting -ip intent to complement -f option\n");
+ if (xicc_enum_gmapintent(&li.gmi, icxNoGMIntent, "p") == -999)
+ usage("Internal, intent 'p' isn't recognised");
+ li.dst_kbp = 1; /* Map to K only black point */
+ li.out.ink.KonlyLmin = 1; /* Use K only black Lmin for locus */
+ }
+ } else if (li.nhack == 3) { /* All to K only out */
+ if (intentset) {
+ warning("For better results use an intent that maps black point with -F option");
+ } else {
+ if (li.verb)
+ printf("Setting -ip intent to complement -F option\n");
+ if (xicc_enum_gmapintent(&li.gmi, icxNoGMIntent, "p") == -999)
+ usage("Internal, intent 'p' isn't recognised");
+ li.dst_kbp = 1; /* Map to K only black point */
+ li.out.ink.KonlyLmin = 1; /* Use K only black Lmin for locus */
+ }
+ }
+
+ /* Got an appropriate intent, so set mapping to K only black point */
+ } else if (li.nhack == 1 || li.nhack == 3) {
+ li.dst_kbp = 1; /* Map to K only black point */
+ li.out.ink.KonlyLmin = 1; /* Use K only black Lmin for locus */
+ }
+ if (li.cmyhack != 0) { /* Map pure 100% CMY to pure CMY */
+ if (intentset) {
+ if (strcmp(li.gmi.as, "s") != 0)
+ warning("For better results use -is with -fcmy options");
+ } else {
+ if (li.verb)
+ printf("Setting -is intent to complement -fcmy options\n");
+ if (xicc_enum_gmapintent(&li.gmi, icxNoGMIntent, "s") == -999)
+ usage("Internal, intent 's' isn't recognised");
+ li.dst_cmymap = li.cmyhack;
+ }
+ }
+ }
+ }
+
+ if (li.mode == 0) {
+ if (li.in.intent == icMaxEnumIntent)
+ usage("Input intent (-i) argument isn't recognised for simple mapping mode");
+ }
+
+ if (li.wphack && (li.gmi.usecas & 0x100) != 0)
+ usage("Can't use 'white point hack' and Luminence scaling intent together");
+
+ /* - - - - - - - - - - - - - - - - - - - */
+ /* Open up the input device profile for reading, and read header etc. */
+ if ((li.in.c = read_embedded_icc(in_name)) == NULL)
+ error ("Can't open file '%s'",in_name);
+ li.in.h = li.in.c->header;
+
+ /* Check that it is a suitable device input icc */
+ if (li.in.h->deviceClass != icSigInputClass
+ && li.in.h->deviceClass != icSigDisplayClass
+ && li.in.h->deviceClass != icSigOutputClass
+ && li.in.h->deviceClass != icSigColorSpaceClass) /* For sRGB etc. */
+ error("Input profile '%s' isn't a device profile",in_name);
+
+ /* Wrap with an expanded icc */
+ if ((li.in.x = new_xicc(li.in.c)) == NULL)
+ error ("Creation of input profile xicc failed");
+
+ /* Set the default ink limits if not set on command line */
+ icxDefaultLimits(li.in.x, &li.in.ink.tlimit, li.in.ink.tlimit, &li.in.ink.klimit, li.in.ink.klimit);
+
+ if (li.verb) {
+ if (li.in.ink.tlimit >= 0.0)
+ printf("Input total ink limit assumed is %3.0f%%\n",100.0 * li.in.ink.tlimit);
+ if (li.in.ink.klimit >= 0.0)
+ printf("Input black ink limit assumed is %3.0f%%\n",100.0 * li.in.ink.klimit);
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - */
+ /* Open up the abstract profile if requested */
+ if (abs_name[0] != '\000') {
+ if ((li.abs_fp = new_icmFileStd_name(abs_name,"r")) == NULL)
+ error ("Can't open abstract profile file '%s'",abs_name);
+
+ if ((li.abs_icc = new_icc()) == NULL)
+ error ("Creation of Abstract profile ICC object failed");
+
+ /* Read header etc. */
+ if ((rv = li.abs_icc->read(li.abs_icc,li.abs_fp,0)) != 0)
+ error ("%d, %s",rv,li.abs_icc->err);
+
+ if (li.abs_icc->header->deviceClass != icSigAbstractClass)
+ error("Abstract profile isn't an abstract profile");
+
+ /* Take intended abstract intent from profile itself */
+ if ((li.abs_intent = li.abs_icc->header->renderingIntent) != icAbsoluteColorimetric)
+ li.abs_intent = icRelativeColorimetric;
+
+ /* Wrap with an expanded icc */
+ if ((li.abs_xicc = new_xicc(li.abs_icc)) == NULL)
+ error ("Creation of abstract profile xicc failed");
+ }
+ /* - - - - - - - - - - - - - - - - - - - */
+ /* Open up the output device output profile for reading, and read header etc. */
+ if ((li.out.c = read_embedded_icc(out_name)) == NULL)
+ error ("Can't open file '%s'",out_name);
+ li.out.h = li.out.c->header;
+
+ if (li.out.h->deviceClass != icSigInputClass
+ && li.out.h->deviceClass != icSigDisplayClass
+ && li.out.h->deviceClass != icSigOutputClass
+ && li.out.h->deviceClass != icSigColorSpaceClass) /* For sRGB etc. */
+ error("Output profile isn't a device profile");
+
+ /* Wrap with an expanded icc */
+ if ((li.out.x = new_xicc(li.out.c)) == NULL)
+ error ("Creation of output profile xicc failed");
+
+ /* Set the default ink limits if not set on command line */
+ icxDefaultLimits(li.out.x, &li.out.ink.tlimit, li.out.ink.tlimit, &li.out.ink.klimit, li.out.ink.klimit);
+
+ if (li.verb) {
+ if (li.out.ink.tlimit >= 0.0)
+ printf("Output total ink limit assumed is %3.0f%%\n",100.0 * li.out.ink.tlimit);
+ if (li.out.ink.klimit >= 0.0)
+ printf("Output black ink limit assumed is %3.0f%%\n",100.0 * li.out.ink.klimit);
+ }
+
+ /* deal with output black generation. */
+ /* Ink limits will have been set in option parsing */
+
+ switch (li.out.inking) {
+ case 0: /* Use input profile K level or locus */
+ /* Sanity check */
+ if (li.in.h->colorSpace != li.out.h->colorSpace)
+ error("Can't transfer black ink in & out unless the same colorspaces");
+ li.out.ink.k_rule = li.out.locus ? icxKlocus : icxKvalue; /* Given as aux parameter in PCS -> Device */
+ break;
+ case 7: /* Use output profile K level or locus */
+ li.out.ink.k_rule = li.out.locus ? icxKlocus : icxKvalue; /* Given as aux parameter in PCS -> Device */
+ break;
+ case 1: /* Minimum K */
+ li.out.ink.k_rule = li.out.locus ? icxKluma5 : icxKluma5k;
+ li.out.ink.c.Kstle = 0.0;
+ li.out.ink.c.Kstpo = 0.0;
+ li.out.ink.c.Kenpo = 1.0;
+ li.out.ink.c.Kenle = 0.0;
+ li.out.ink.c.Kshap = 1.0;
+ break;
+ case 2: /* 0.5 K */
+ li.out.ink.k_rule = li.out.locus ? icxKluma5 : icxKluma5k;
+ li.out.ink.c.Kstle = 0.5;
+ li.out.ink.c.Kstpo = 0.0;
+ li.out.ink.c.Kenpo = 1.0;
+ li.out.ink.c.Kenle = 0.5;
+ li.out.ink.c.Kshap = 1.0;
+ break;
+ case 3: /* Maximum K */
+ li.out.ink.k_rule = li.out.locus ? icxKluma5 : icxKluma5k;
+ li.out.ink.c.Kstle = 1.0;
+ li.out.ink.c.Kstpo = 0.0;
+ li.out.ink.c.Kenpo = 1.0;
+ li.out.ink.c.Kenle = 1.0;
+ li.out.ink.c.Kshap = 1.0;
+ break;
+ case 4: /* Ramp K */
+ li.out.ink.k_rule = li.out.locus ? icxKluma5 : icxKluma5k;
+ li.out.ink.c.Kstle = 0.0;
+ li.out.ink.c.Kstpo = 0.0;
+ li.out.ink.c.Kenpo = 1.0;
+ li.out.ink.c.Kenle = 1.0;
+ li.out.ink.c.Kshap = 1.0;
+ break;
+ case 5: /* Curve */
+ li.out.ink.k_rule = li.out.locus ? icxKluma5 : icxKluma5k;
+ break; /* Other params already set by options */
+ case 6: /* Use input profile K locus + dual curve limits */
+ /* Sanity check */
+ if (li.in.h->colorSpace != li.out.h->colorSpace)
+ error("Can't transfer black ink in & out unless the same colorspaces");
+ li.out.ink.k_rule = li.out.locus ? icxKl5l : icxKl5lk; /* Aux param in PCS -> Device */
+ break; /* Other params already set by options */
+ }
+
+ for (i = 0; i < 2; i++) {
+ xicc *x;
+ icxViewCond *v, *vc;
+ int es;
+
+ if (i == 0) {
+ v = &ivc; /* Override parameters */
+ vc = &li.in.vc; /* Target parameters */
+ es = ivc_e;
+ x = li.in.x; /* xicc */
+ } else {
+ v = &ovc; /* Override parameters */
+ vc = &li.out.vc; /* Target parameters */
+ es = ovc_e;
+ x = li.out.x; /* xicc */
+ }
+
+ /* Set the default viewing conditions */
+ xicc_enum_viewcond(x, vc, -1, NULL, 0, NULL);
+
+ /* Override the default viewing conditions. */
+ /* (?? Could move this code into xicc_enum_viewcond() as an option ??) */
+ /* First any enumerated selection */
+ if (es != -1) {
+ if (xicc_enum_viewcond(x, vc, es, NULL, 0, NULL) == -999)
+ error ("%d, %s",x->errc, x->err);
+ }
+ /* Then any individual paramaters */
+ if (v->Ev >= 0)
+ vc->Ev = v->Ev;
+ if (v->Wxyz[0] >= 0.0 && v->Wxyz[1] > 0.0 && v->Wxyz[2] >= 0.0) {
+ /* Normalise XYZ to current media white */
+ vc->Wxyz[0] = v->Wxyz[0]/v->Wxyz[1] * vc->Wxyz[1];
+ vc->Wxyz[2] = v->Wxyz[2]/v->Wxyz[1] * vc->Wxyz[1];
+ }
+ if (v->Wxyz[0] >= 0.0 && v->Wxyz[1] >= 0.0 && v->Wxyz[2] < 0.0) {
+ /* Convert Yxy to XYZ */
+ double x = v->Wxyz[0];
+ double y = v->Wxyz[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
+ double z = 1.0 - x - y;
+ vc->Wxyz[0] = x/y * vc->Wxyz[1];
+ vc->Wxyz[2] = z/y * vc->Wxyz[1];
+ }
+ if (v->La >= 0.0)
+ vc->La = v->La;
+ if (v->Yb >= 0.0)
+ vc->Yb = v->Yb;
+ if (v->Lv >= 0.0)
+ vc->Lv = v->Lv;
+ if (v->Yf >= 0.0)
+ vc->Yf = v->Yf;
+ if (v->Fxyz[0] >= 0.0 && v->Fxyz[1] > 0.0 && v->Fxyz[2] >= 0.0) {
+ /* Normalise XYZ to current media white */
+ vc->Fxyz[0] = v->Fxyz[0]/v->Fxyz[1] * vc->Fxyz[1];
+ vc->Fxyz[2] = v->Fxyz[2]/v->Fxyz[1] * vc->Fxyz[1];
+ }
+ if (v->Fxyz[0] >= 0.0 && v->Fxyz[1] >= 0.0 && v->Fxyz[2] < 0.0) {
+ /* Convert Yxy to XYZ */
+ double x = v->Fxyz[0];
+ double y = v->Fxyz[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
+ double z = 1.0 - x - y;
+ vc->Fxyz[0] = x/y * vc->Fxyz[1];
+ vc->Fxyz[2] = z/y * vc->Fxyz[1];
+ }
+ }
+
+ if (li.verb)
+ printf("Configured options\n");
+
+ /* - - - - - - - - - - - - - - - - - - - */
+ /* Setup the profile color lookup information */
+ {
+ icmLuAlgType oalg; /* Native output algorithm */
+ icColorSpaceSignature natpcs; /* Underlying native output PCS */
+ int flb = 0, fl = 0; /* luobj flags */
+
+ li.pcsor = icSigLabData; /* Default use Lab as PCS */
+
+ /* If we are using the gamut map mode, then setup */
+ /* the intents and pcsor appropriately. */
+ if (li.mode > 0) {
+
+ if ((li.gmi.usecas & 0xff) != 0) {
+ li.pcsor = icxSigJabData; /* Use CAM as PCS */
+ isJab = 1;
+
+ if ((li.gmi.usecas & 0xff) == 0x2) { /* Absolute Appearance space */
+ double mxw;
+
+ li.in.intent = li.out.intent = li.abs_intent = icxAbsAppearance;
+
+ /* Make absolute common white point average between the two */
+ li.in.vc.Wxyz[0] = 0.5 * (li.in.vc.Wxyz[0] + li.out.vc.Wxyz[0]);
+ li.in.vc.Wxyz[1] = 0.5 * (li.in.vc.Wxyz[1] + li.out.vc.Wxyz[1]);
+ li.in.vc.Wxyz[2] = 0.5 * (li.in.vc.Wxyz[2] + li.out.vc.Wxyz[2]);
+
+ /* And scale it Y to be equal to 1.0 */
+ mxw = 1.0/li.in.vc.Wxyz[1];
+ li.in.vc.Wxyz[0] *= mxw;
+ li.in.vc.Wxyz[1] *= mxw;
+ li.in.vc.Wxyz[2] *= mxw;
+
+ /* Set the output vc to be the same as the input */
+ li.out.vc = li.in.vc; /* Structure copy */
+ } else {
+ /* Not Abs Appearance space */
+ li.in.intent = li.out.intent = li.abs_intent = icxAppearance;
+ }
+ } else {
+ /* Not Appearance space */
+ li.in.intent = li.out.intent = li.abs_intent = icAbsoluteColorimetric;
+ }
+ }
+
+ if (li.verb)
+ printf("Loading input A2B table\n");
+
+ /* default flags for all xicc luobj's */
+ flb = ICX_CLIP_NEAREST;
+ if (li.verb)
+ flb |= ICX_VERBOSE;
+
+ /* Get an input profile xicc conversion object */
+ fl = flb;
+#ifdef USE_MERGE_CLUT_OPT
+ fl |= ICX_MERGE_CLUT;
+#endif
+
+#ifdef NEVER
+ printf("~1 input space flags = 0x%x\n",fl);
+ printf("~1 input space intent = %s\n",icx2str(icmRenderingIntent,li.in.intent));
+ printf("~1 input space pcs = %s\n",icx2str(icmColorSpaceSignature,li.pcsor));
+ printf("~1 input space viewing conditions =\n"); xicc_dump_viewcond(&li.in.vc);
+ printf("~1 input space inking =\n"); xicc_dump_inking(&li.in.ink);
+#endif
+ if ((li.in.luo = li.in.x->get_luobj(li.in.x, fl, icmFwd, li.in.intent,
+ li.pcsor, icmLuOrdNorm, &li.in.vc, &li.in.ink)) == NULL) {
+ error("get xlookup object failed: %d, %s",li.in.x->errc,li.in.x->err);
+ }
+
+ /* Get details of overall conversion */
+ li.in.luo->spaces(li.in.luo, &li.in.csp, &li.in.chan, NULL, NULL, &li.in.alg,
+ NULL, NULL, NULL);
+
+ /* Get the input profile A2B input curve resolution */
+ /* (This is pretty rough - this should work for non LUT types as well!!) */
+ {
+ if (li.in.alg== icmLutType) {
+ icmLut *lut;
+ icxLuLut *luluo = (icxLuLut *)li.in.luo; /* Safe to coerce */
+ luluo->get_info(luluo, &lut, NULL, NULL, NULL); /* Get some details */
+ in_curve_res = lut->inputEnt;
+ }
+ }
+
+ /* Grab the white point in case the wphack or xyzscale needs it */
+ li.in.luo->efv_wh_bk_points(li.in.luo, li.in.wp, NULL, NULL);
+
+ /* Get native PCS space */
+ li.in.luo->lutspaces(li.in.luo, NULL, NULL, &natpcs, NULL, NULL);
+
+ if (li.in.nocurve == 0 && natpcs == icSigXYZData
+ && (li.in.alg == icmMatrixFwdType || li.in.alg == icmMatrixBwdType
+ || li.in.csp == icSigXYZData)) {
+ li.in.lcurve = 1; /* Use Y to L* and L* to Y for input */
+ if (li.in.csp == icSigXYZData) {
+ li.in.lcurve = 2; /* Use real Y to L* and L* to Y for input */
+ li.in.nocurve = 1; /* Don't trust the curve that comes with it */
+ }
+ if (li.verb)
+ printf("Using Y to L* and L* to Y curves for input\n");
+ }
+
+ /* Setup any abstract profile to match the chosen PCS */
+ /* We aren't checking whether the input/abstract/output profile */
+ /* intents really make any sense. It's assumed at the moment */
+ /* that the user knows what they're doing! */
+ if (abs_name[0] != '\000') {
+
+ if ((li.abs_luo = li.abs_xicc->get_luobj(li.abs_xicc, flb, icmFwd, li.abs_intent,
+ li.pcsor, icmLuOrdNorm, &li.out.vc, NULL)) == NULL)
+ error ("%d, %s",li.abs_icc->errc, li.abs_icc->err);
+ }
+
+ // Figure out whether the output profile is a Lut profile or not */
+ {
+ icmLuBase *plu;
+
+ /* Get temporary icm lookup object */
+ /* (Use Fwd just in case profile is missing B2A !!!!) */
+ if ((plu = li.out.c->get_luobj(li.out.c, icmFwd, icmDefaultIntent, icmSigDefaultData,
+ icmLuOrdNorm)) == NULL) {
+ error("get icm lookup object failed: on '%s' %d, %s",out_name,li.out.c->errc,li.out.c->err);
+ }
+
+ /* Check what the algorithm is */
+ plu->spaces(plu, NULL, NULL, NULL, NULL, &oalg, NULL, NULL, NULL, NULL);
+
+ /* release the icm lookup */
+ plu->del(plu);
+
+ }
+
+ if (oalg != icmLutType || li.mode < 2) { /* Using B2A table or inv. mono/matrix */
+ if (li.verb)
+ printf("Loading output B2A table\n");
+
+ if ((li.out.luo = li.out.x->get_luobj(li.out.x, flb, icmBwd, li.out.intent,
+ li.pcsor, icmLuOrdNorm, &li.out.vc, &li.out.ink)) == NULL) {
+ error("get xlookup object failed: %d, %s",li.out.x->errc,li.out.x->err);
+ }
+ /* Get details of overall conversion */
+ li.out.luo->spaces(li.out.luo, NULL, NULL, NULL, &li.out.chan, &li.out.alg,
+ NULL, NULL, NULL);
+
+ /* Get the output profile B2A output curve resolution */
+ /* (This is pretty rough - this should work for non LUT types as well!!) */
+ {
+ if (li.out.alg== icmLutType) {
+ icmLut *lut;
+ icxLuLut *luluo = (icxLuLut *)li.out.luo; /* Safe to coerce */
+ luluo->get_info(luluo, &lut, NULL, NULL, NULL); /* Get some details */
+ out_curve_res = lut->outputEnt;
+ }
+ }
+
+ /* Grab the white point in case the wphack or xyzscale needs it */
+ li.out.luo->efv_wh_bk_points(li.out.luo, li.out.wp, NULL, NULL);
+
+ /* Get native PCS space */
+ li.out.luo->lutspaces(li.out.luo, &natpcs, NULL, NULL, NULL, NULL);
+
+ } else { /* Using inverse A2B Lut for output conversion */
+
+ fl = flb;
+#ifdef USE_MERGE_CLUT_OPT
+ fl |= ICX_MERGE_CLUT;
+#endif
+#ifdef USE_CAM_CLIP_OPT
+ fl |= ICX_CAM_CLIP;
+#endif
+ if (li.verb)
+ printf("Loading output inverse A2B table\n");
+
+#ifdef NEVER
+ printf("~1 output space flags = 0x%x\n",fl);
+ printf("~1 output space intent = %s\n",icx2str(icmRenderingIntent,li.out.intent));
+ printf("~1 output space pcs = %s\n",icx2str(icmColorSpaceSignature,li.pcsor));
+ printf("~1 output space viewing conditions =\n"); xicc_dump_viewcond(&li.out.vc);
+ printf("~1 output space inking =\n"); xicc_dump_inking(&li.out.ink);
+#endif
+
+ if ((li.out.luo = li.out.x->get_luobj(li.out.x, fl, icmFwd,
+ li.out.intent, li.pcsor, icmLuOrdNorm, &li.out.vc,
+ &li.out.ink)) == NULL) {
+ error("get xlookup object failed: %d, %s",li.out.x->errc,li.out.x->err);
+ }
+
+ /* Get details of overall conversion */
+ li.out.luo->spaces(li.out.luo, &li.out.csp, &li.out.chan, NULL, NULL, &li.out.alg,
+ NULL, NULL, NULL);
+
+ /* Get the output profile A2B input curve resolution */
+ /* (This is pretty rough - this should work for non LUT types as well!!) */
+ {
+ if (li.out.alg== icmLutType) {
+ icmLut *lut;
+ icxLuLut *luluo = (icxLuLut *)li.out.luo; /* Safe to coerce */
+ luluo->get_info(luluo, &lut, NULL, NULL, NULL); /* Get some details */
+ out_curve_res = lut->inputEnt;
+ }
+ }
+
+ /* Grab the white point in case the wphack or xyzscale needs it */
+ li.out.luo->efv_wh_bk_points(li.out.luo, li.out.wp, NULL, NULL);
+
+ /* Get native PCS space */
+ li.out.luo->lutspaces(li.out.luo, NULL, NULL, &natpcs, NULL, NULL);
+
+ /* If we need a B2A lookup to get the existing K */
+ if (li.out.inking == 7) {
+ if ((li.out.b2aluo = li.out.x->get_luobj(li.out.x, flb, icmBwd,
+ li.out.intent, li.pcsor, icmLuOrdNorm, &li.out.vc, NULL)) == NULL) {
+ error("get B2A xlookup object failed: %d, %s",li.out.x->errc,li.out.x->err);
+ }
+ }
+ }
+
+ /* If we need an PCS->K' mapping for the neutral axis to K hack. */
+ /* What we do is lookup the L for K values from 0 to 1, */
+ /* and then invert this to create an L to K lookup. */
+ /* If the gamut mapping is set to map to the K only black point, */
+ /* it should all work well... */
+ if (li.nhack) {
+ icxLuBase *luo; /* Base XLookup type object */
+ icmLuAlgType alg; /* Type of lookup algorithm */
+ co ips[256]; /* Initialisation points */
+ datai glow; /* Grid low scale */
+ datai ghigh; /* Grid high scale */
+ datao vlow; /* Data value low normalize */
+ datao vhigh; /* Data value high normalize */
+ double Lmax, Lmin; /* Max and Min L values that result */
+ int grres;
+ double avgdev[MXDO];
+
+ if (li.out.h->colorSpace != icSigCmykData)
+ error("Neutral Axis K only requested with non CMYK output profile");
+
+ if (li.in.chan < 3)
+ error("Neutral Axis K only requested with input profile with less than 3 channels");
+
+ if (li.nhack == 2 && li.in.h->colorSpace != icSigCmykData)
+ error("Neutral Axis 000K only requested with input profile that is not CMYK");
+
+ if ((li.pcs2k = new_rspl(RSPL_NOFLAGS, 1, 1)) == NULL) {
+ error("Failed to create an rspl object");
+ }
+
+ /* Get a device to PCS lookup object to use to lookup K->PCS */
+ if ((luo = li.out.x->get_luobj(li.out.x, flb,
+ icmFwd, li.out.intent, li.pcsor, icmLuOrdNorm, &li.out.vc,
+ NULL)) == NULL) {
+ error("get xlookup object failed: %d, %s",li.out.x->errc,li.out.x->err);
+ }
+ /* Get details of overall conversion */
+ luo->spaces(luo, NULL, NULL, NULL, NULL, &alg, NULL, NULL, NULL);
+ if (alg != icmLutType)
+ error ("Unexpected algorithm type for CMYK output profile");
+
+ /* Setup the initialisation points */
+ Lmax = -100.0;
+ Lmin = 1000.0;
+ for (i = 0; i < 256; i++) {
+ icxLuLut *lu = (icxLuLut *)luo; /* Safe to coerce */
+ double in[4], pcsv[4];
+ in[0] = in[1] = in[2] = 0.0;
+ in[3] = i/(255.0);
+
+ /* Want to do dev' -> PCS conversion to match the */
+ /* normal inverse PCS-> dev' used in devip_devop() */
+ if (li.out.nocurve) { /* No explicit curve, so do implicit here */
+ /* Since not PCS, in_abs and matrix cannot be valid, */
+ /* so input curve on own is ok to use. */
+ lu->input(lu, pcsv, in);
+ lu->clut(lu, pcsv, pcsv);
+ } else {
+ lu->clut(lu, pcsv, in);
+ }
+ lu->output(lu, pcsv, pcsv);
+ lu->out_abs(lu, pcsv, pcsv);
+
+ /* We force the rspl to be a forward conversion by swapping K and PCS */
+ ips[i].p[0] = pcsv[0]; /* PCS as input */
+ ips[i].v[0] = in[3]; /* K as output */
+#ifdef NEUTKDEBUG
+ printf("L %f -> K' %f\n",pcsv[0], in[3]);
+#endif /* NEUTKDEBUG */
+
+ if (pcsv[0] > Lmax) /* Track min and max L values */
+ Lmax = pcsv[0];
+ if (pcsv[0] < Lmin)
+ Lmin = pcsv[0];
+ }
+
+ glow[0] = 0.0;
+ ghigh[0] = 100.0;
+ vlow[0] = 0.0;
+ vhigh[0] = 1.0;
+ grres = 256;
+ avgdev[0] = 0.005;
+
+ li.pcs2k->fit_rspl(li.pcs2k, 0, ips, 256, glow, ghigh, &grres, vlow, vhigh, 1.0, avgdev, NULL);
+
+ /* Fixup the white and black points for neutral axis to K hack. */
+ /* This is to make sure that they exactly match the fwd mapping */
+ /* after the rspl is fitted. */
+ {
+ pcs2k_ctx cx; /* White point fixup context */
+ co pp; /* Lookup the min and max K values */
+
+ pp.p[0] = Lmax;
+ li.pcs2k->interp(li.pcs2k, &pp);
+ cx.kmin = pp.v[0]; /* Ideally would be 0 */
+ pp.p[0] = Lmin;
+ li.pcs2k->interp(li.pcs2k, &pp);
+ cx.kmax = pp.v[0]; /* Ideally would be 1 */
+
+#ifdef NEUTKDEBUG
+ printf("Before fix: Lmax %f, Lmin %f, Kmin %f, Kmax %f\n",Lmax, Lmin, cx.kmin, cx.kmax);
+#endif /* NEUTKDEBUG */
+
+ li.pcs2k->re_set_rspl(li.pcs2k, 0, (void *)&cx, fix_pcs2k_white);
+#ifdef NEUTKDEBUG
+ pp.p[0] = Lmax;
+ li.pcs2k->interp(li.pcs2k, &pp);
+ cx.kmin = pp.v[0]; /* Ideally would be 0 */
+ pp.p[0] = Lmin;
+ li.pcs2k->interp(li.pcs2k, &pp);
+ cx.kmax = pp.v[0]; /* Ideally would be 1 */
+ printf("After fix: Lmax %f, Lmin %f, Kmin %f, Kmax %f\n",Lmax, Lmin, cx.kmin, cx.kmax);
+#endif /* NEUTKDEBUG */
+ }
+
+ } /* end if neutral axis to K hack */
+
+ if (li.cmyhack != 0) {
+ if (li.in.h->colorSpace != icSigCmyData
+ && li.in.h->colorSpace != icSigCmykData)
+ error("100% CMY mapping requested with non CMY or CMYK input profile");
+
+ if (li.out.h->colorSpace != icSigCmyData
+ && li.out.h->colorSpace != icSigCmykData)
+ error("100% CMY mapping requested with non CMY or CMYK output profile");
+ }
+
+ if (li.out.nocurve == 0 && natpcs == icSigXYZData
+ && (li.out.alg == icmMatrixFwdType || li.out.alg == icmMatrixBwdType
+ || li.out.csp == icSigXYZData)) {
+ li.out.lcurve = 1; /* Use Y to L* and L* to Y for output */
+ if (li.out.csp == icSigXYZData) {
+ li.out.lcurve = 2; /* Use real Y to L* and L* to Y for output */
+ li.out.nocurve = 1; /* Don't trust the curve that comes with it */
+ }
+ if (li.verb)
+ printf("Using Y to L* and L* to Y curves for output\n");
+ }
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - */
+ /* Setup the gamut mapping */
+// ~~~~ need to account for possible abstract profile after source !!!!
+// ~~~~ also need to fix tiffgamut to allow for abstract profile !!!!
+
+ if (li.verb)
+ printf("Gamut mapping mode is '%s'\n",li.mode == 0 ? "Simple" : li.mode == 1 ? "Mapping" : "Mapping inverse A2B");
+ if (li.verb && li.mode > 0)
+ printf("Gamut mapping intent is '%s'\n",li.gmi.desc);
+
+ /* In gamut mapping mode, the PCS used will always be absolute */
+ /* intent from the input and output profiles, and either */
+ /* lab or Jab space, with the given in/out viewing conditions */
+ /* for the latter. The xluo->get_gamut work in the set li.pcsor */
+ /* for each xluo. */
+ if (li.mode > 0 && li.gmi.usemap) {
+ gamut *csgam, *igam, *ogam;
+ double sgres; /* Source gamut surface feature resolution */
+ double dgres; /* Destination gamut surface feature resolution */
+ int mapres; /* Mapping rspl resolution */
+
+ if (li.verb)
+ printf("Creating Gamut Mapping\n");
+
+ /* Gamut mapping will extend given grid res to encompas */
+ /* source gamut by a margin. */
+ if (li.quality == 3) { /* Ultra High */
+ sgres = 7.0;
+ dgres = 7.0;
+ mapres = 41;
+ } else if (li.quality == 2) { /* High */
+ sgres = 8.0;
+ dgres = 8.0;
+ mapres = 33;
+ } else if (li.quality == 1) { /* Medium */
+ sgres = 10.0;
+ dgres = 10.0;
+ mapres = 25;
+ } else { /* Low quality */
+ sgres = 12.0;
+ dgres = 12.0;
+ mapres = 17;
+ }
+
+ /* Creat the source colorspace gamut surface */
+ if (li.verb)
+ printf(" Finding Source Colorspace Gamut with res %f\n",sgres);
+
+ /* Creat the source image gamut surface in the selected li.pcsor space */
+ if ((csgam = li.in.luo->get_gamut(li.in.luo, sgres)) == NULL)
+ error ("%d, %s",li.in.x->errc, li.in.x->err);
+
+ /* Grab a given source image gamut. */
+ if (sgam_name[0] != '\000') { /* Optional source gamut - ie. from an images */
+
+ if (li.verb)
+ printf(" Loading Image Source Gamut '%s'\n",sgam_name);
+
+ igam = new_gamut(sgres, isJab, 0); /* isJab will be overriden by gamut file */
+
+ if (igam->read_gam(igam, sgam_name))
+ error("Reading source gamut '%s' failed",sgam_name);
+
+ if (igam->getisjab(igam) != isJab) {
+ /* Should really convert to/from Jab here! */
+ warning("Image gamut is wrong colorspace for link (Lab != Jab)");
+
+ /* This will actually error in the gamut mapping code */
+ /* Note that we're not checking relative/absolute colorspace here. */
+ /* At the moment it's up to the user to get this right. */
+ }
+
+ } else {
+ igam = NULL; /* NULL signals no source image gamut */
+ }
+
+ /* Creat the destination gamut surface */
+ if (li.verb)
+ printf(" Finding Destination Gamut with res %f\n",dgres);
+
+ if ((ogam = li.out.luo->get_gamut(li.out.luo, dgres)) == NULL)
+ error ("%d, %s",li.out.x->errc, li.out.x->err);
+
+ if (li.verb)
+ printf(" Creating Gamut match\n");
+
+ li.map = new_gammap(li.verb, csgam, igam, ogam, &li.gmi,
+ li.src_kbp, li.dst_kbp, li.cmyhack, li.rel_oride,
+ mapres, NULL, NULL, li.gamdiag ? "gammap.wrl" : NULL
+ );
+ if (li.map == NULL)
+ error ("Failed to make gamut map transform");
+
+ if (li.nhack == 2) {
+ if (li.verb)
+ printf(" Creating K only black to K only black Gamut match\n");
+
+ li.Kmap = new_gammap(li.verb, csgam, igam, ogam, &li.gmi,
+ 1, 1, li.cmyhack, li.rel_oride,
+ mapres, NULL, NULL, li.gamdiag ? "gammap.wrl" : NULL
+ );
+ if (li.Kmap == NULL)
+ error ("Failed to make K only gamut map transform");
+ }
+
+ ogam->del(ogam);
+ if (igam != NULL)
+ igam->del(igam);
+ csgam->del(csgam);
+ }
+
+ /* If we've got a request for Absolute Appearance mode with scaling */
+ /* to avoid clipping the source white point, compute the needed XYZ scaling factor. */
+ /* We assume that the white point hack can't be used at the same time. */
+ if (li.mode > 0 && li.wphack == 0 && (li.gmi.usecas & 0x100) != 0) {
+ double xyzscale[1], sa[1];
+
+ /* We already have the source space white point in li.in.wp[] */
+
+ /* Convert it to destination XYZ */
+ if (li.pcsor == icxSigJabData) {
+ /* We're being bad in delving inside the xluo, but we'll fix it latter */
+ li.out.luo->cam->cam_to_XYZ(li.out.luo->cam, li.swxyz, li.in.wp);
+ } else
+ error("Internal :- not setup to handle Y scaling and non-Jab PCS");
+
+//printf("~1 Source white Jab = %f %f %f\n", li.in.wp[0], li.in.wp[1], li.in.wp[2]);
+//printf("~1 Source white XYZ = %f %f %f\n", li.swxyz[0], li.swxyz[1], li.swxyz[2]);
+
+ /* Compute the bigest scale factor less than or equal to 1.0, */
+ /* that doesn't clip the li.swxyz[] on the destination gamut */
+ sa[0] = 0.1;
+ xyzscale[0] = 0.5;
+ if (powell(NULL, 1, xyzscale, sa, 1e-6, 2000, xyzoptfunc, (void *)&li, NULL, NULL) != 0) {
+ warning("set_icxLuLut: XYZ scale powell failed to converge - set scale to 1.0");
+ } else {
+ li.xyzscale = xyzscale[0];
+ if (li.verb)
+ printf("Set XYZ scale factor to %f\n",li.xyzscale);
+ }
+ }
+
+ /* Create the link profile */
+ if (verify == 0) {
+ icmFile *wr_fp;
+ icc *wr_icc;
+
+ if (li.verb)
+ printf("Creating link profile\n");
+
+ /* Open up the link file for writing */
+ if ((wr_fp = new_icmFileStd_name(link_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",link_name);
+
+ if ((wr_icc = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags required */
+
+ /* The header: */
+ {
+ icmHeader *wh = wr_icc->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigLinkClass; /* We are creating a link ! */
+ wh->colorSpace = li.in.h->colorSpace; /* Input profile device space */
+ wh->pcs = li.out.h->colorSpace; /* Output profile device space */
+ if (li.mode > 0) {
+ wh->renderingIntent = li.gmi.icci; /* Closest ICC intent */
+ } else {
+ wh->renderingIntent = li.out.intent; /* Output intent chosen */
+ }
+
+ /* Values that should be set before writing */
+ if (xpi.manufacturer != 0L)
+ wh->manufacturer = xpi.manufacturer;
+ else
+ wh->manufacturer = icmSigUnknownType;
+
+ if (xpi.model != 0L)
+ wh->model = xpi.model;
+ else
+ wh->model = icmSigUnknownType;
+
+ /* Values that may be set before writing */
+ if (xpi.creator != 0L)
+ wh->creator = xpi.creator;
+
+ wh->attributes.l = 0;
+ wh->flags = 0;
+#ifdef NT
+ wh->platform = icSigMicrosoft;
+#endif
+#ifdef __APPLE__
+ wh->platform = icSigMacintosh;
+#endif
+#if defined(UNIX) && !defined(__APPLE__)
+ wh->platform = icmSig_nix;
+#endif
+ }
+ /* Profile Description Tag: */
+ {
+ icmTextDescription *wo;
+ char *dst, dstm[200]; /* description */
+
+ if (xpi.profDesc != NULL)
+ dst = xpi.profDesc;
+ else {
+ dst = "Device Link profile - See ProfileSequenceDescTag for more information";
+ dst = dstm;
+ }
+
+ if ((wo = (icmTextDescription *)wr_icc->add_tag(
+ wr_icc, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icc->errc,wr_icc->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Copyright Tag: */
+ {
+ icmText *wo;
+ char *crt;
+
+ if (xpi.copyright != NULL)
+ crt = xpi.copyright;
+ else
+ crt = "Copyright, the creator of this profile";
+
+ if ((wo = (icmText *)wr_icc->add_tag(
+ wr_icc, icSigCopyrightTag, icSigTextType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icc->errc,wr_icc->err);
+
+ wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, crt); /* Copy the text in */
+ }
+ /* Device Manufacturers Description Tag: */
+ if (xpi.deviceMfgDesc != NULL) {
+ icmTextDescription *wo;
+ char *dst = xpi.deviceMfgDesc;
+
+ if ((wo = (icmTextDescription *)wr_icc->add_tag(
+ wr_icc, icSigDeviceMfgDescTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icc->errc,wr_icc->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Model Description Tag: */
+ if (xpi.modelDesc != NULL) {
+ icmTextDescription *wo;
+ char *dst = xpi.modelDesc;
+
+ if ((wo = (icmTextDescription *)wr_icc->add_tag(
+ wr_icc, icSigDeviceModelDescTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icc->errc,wr_icc->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* ProfileSequenceDescTag: */
+ {
+ unsigned int i;
+ icmProfileSequenceDesc *wo;
+ if ((wo = (icmProfileSequenceDesc *)wr_icc->add_tag(
+ wr_icc, icSigProfileSequenceDescTag, icSigProfileSequenceDescType)) == NULL)
+ return 1;
+
+ wo->count = 2; /* Number of descriptions in sequence */
+ if (wo->allocate((icmBase *)wo) != 0) /* Allocate space for all the DescStructures */
+ error("allocate failed: %d, %s",wr_icc->errc,wr_icc->err);
+
+ /* Fill in each description structure in sequence */
+
+ /* For each profile in the chain */
+ for (i = 0; i < wo->count; i++) {
+ icc *iccs = NULL;
+ icmHeader *sh = NULL;
+ icmSignature *tsig;
+ icmTextDescription *ddesc;
+ icmTextDescription *mdesc;
+
+ if (i == 0) {
+ iccs = li.in.c; /* Input profile */
+ sh = li.in.h; /* Input profile header */
+ } else if (i == (wo->count-1)) {
+ iccs = li.out.c; /* Output profile */
+ sh = li.out.h; /* Output profile header */
+ } else {
+ error("Abstract profiles in link not implemented yet!");
+ }
+
+ /* Try and read the technology tag */
+ if ((tsig = (icmSignature *)iccs->read_tag(iccs, icSigTechnologyTag)) != NULL) {
+ if (tsig->ttype != icSigSignatureType) /* oops */
+ tsig = NULL;
+ }
+
+ /* Try and read the Device Manufacturers Description Tag */
+ if ((ddesc = (icmTextDescription *)iccs->read_tag(
+ iccs, icSigDeviceMfgDescTag)) != NULL) {
+ if (ddesc->ttype != icSigTextDescriptionType) /* oops */
+ ddesc = NULL;
+ }
+
+ /* Try and read the Model Manufacturers Description Tag */
+ if ((mdesc = (icmTextDescription *)iccs->read_tag(
+ iccs, icSigDeviceModelDescTag)) != NULL) {
+ if (mdesc->ttype != icSigTextDescriptionType) /* oops */
+ mdesc = NULL;
+ }
+
+ /* Header information */
+ wo->data[i].deviceMfg = sh->manufacturer;
+ wo->data[i].deviceModel = sh->model;
+ wo->data[i].attributes = sh->attributes;
+
+ /* Technology signature */
+ if (tsig != NULL)
+ wo->data[i].technology = tsig->sig;
+
+ if (ddesc != NULL) {
+ wo->data[i].device.size = ddesc->size;
+ if (wo->data[i].allocate(&wo->data[i]) != 0) /* Allocate space */
+ error("allocate failed: %d, %s",wr_icc->errc,wr_icc->err);
+ strcpy(wo->data[i].device.desc, ddesc->desc);
+
+ wo->data[i].device.ucLangCode = ddesc->ucLangCode;
+ wo->data[i].device.ucSize = ddesc->ucSize;
+ if (wo->data[i].allocate(&wo->data[i]) != 0) /* Allocate space */
+ error("allocate failed: %d, %s",wr_icc->errc,wr_icc->err);
+ memmove(wo->data[i].device.ucDesc, ddesc->ucDesc, 2 * ddesc->ucSize);
+
+ wo->data[i].device.scCode = ddesc->scCode;
+ wo->data[i].device.scSize = ddesc->scSize;
+ strcpy((char *)wo->data[i].device.scDesc, (char *)ddesc->scDesc);
+ }
+
+ /* model Text description */
+ if (mdesc != NULL) {
+ wo->data[i].model.size = mdesc->size;
+ if (wo->data[i].allocate(&wo->data[i])!= 0) /* Allocate space */
+ error("allocate failed: %d, %s",wr_icc->errc,wr_icc->err);
+ strcpy(wo->data[i].model.desc, mdesc->desc);
+
+ wo->data[i].model.ucLangCode = mdesc->ucLangCode;
+ wo->data[i].model.ucSize = mdesc->ucSize;
+ if (wo->data[i].allocate(&wo->data[i]) != 0) /* Allocate space */
+ error("allocate failed: %d, %s",wr_icc->errc,wr_icc->err);
+ memmove(wo->data[i].model.ucDesc, mdesc->ucDesc, 2 * mdesc->ucSize);
+
+ wo->data[i].model.scCode = mdesc->scCode;
+ wo->data[i].model.scSize = mdesc->scSize;
+ strcpy((char *)wo->data[i].model.scDesc, (char *)mdesc->scDesc);
+ }
+ }
+ }
+ /* ColorantTable: */
+ {
+ int i;
+ unsigned int j;
+ int repclip = 0;
+
+ /* For the first and last profile in the chain */
+ /* (Note that we're assuming that the link output PCS is always Lab) */
+ for (i = 0; i < 2; i++) {
+ icc *iccs;
+ icmHeader *sh;
+ icmColorantTable *ro;
+ icmColorantTable *wo;
+ icTagSignature cts;
+
+ if (i == 0) {
+ iccs = li.in.c; /* Input profile */
+ sh = li.in.h; /* Input profile header */
+ cts = icSigColorantTableTag;
+ } else {
+ iccs = li.out.c; /* Output profile */
+ sh = li.out.h; /* Output profile header */
+ cts = icSigColorantTableOutTag;
+ }
+
+ /* Try and read the input ColorantTable */
+ if ((ro = (icmColorantTable *)iccs->read_tag(
+ iccs, icSigColorantTableTag)) != NULL) {
+
+ /* Create a ColorantTable in the output device link */
+ if ((wo = (icmColorantTable *)wr_icc->add_tag(
+ wr_icc, cts, icSigColorantTableType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icc->errc,wr_icc->err);
+
+ /* Copy everything across */
+ wo->count = ro->count;
+ if (wo->allocate((icmBase *)wo) != 0)
+ error("allocate failed: %d, %s",wr_icc->errc,wr_icc->err);
+
+ for (j = 0; j < wo->count; j++) {
+ strcpy(wo->data[j].name, ro->data[j].name);
+ if (sh->pcs != icSigLabData) {
+ icmXYZ2Lab(&icmD50, wo->data[j].pcsCoords, ro->data[j].pcsCoords);
+ /* For device links the colorant table must be Lab PCS, */
+ /* but embarassingly, XYZ profiles can have colorant values */
+ /* not representable in the Lab PCS range. */
+ if (icmClipLab(wo->data[j].pcsCoords, wo->data[j].pcsCoords)) {
+ if (repclip)
+ warning("Colorant Tag Lab value was clipped");
+ repclip = 1;
+ }
+ } else {
+ icmAry2Ary(wo->data[j].pcsCoords, ro->data[j].pcsCoords);
+ }
+ }
+
+ } else { /* Do this the hard way */
+ icmLuBase *luo;
+ unsigned int count;
+ double dv[MAX_CHAN];
+ double cvals[MAX_CHAN][3];
+ inkmask imask;
+
+ /* Get a lookup to read colorant values */
+ if ((luo = iccs->get_luobj(iccs, icmFwd, icRelativeColorimetric,
+ icSigLabData, icmLuOrdNorm)) == NULL)
+ goto skip_coloranttable;
+
+ count = icmCSSig2nchan(sh->colorSpace);
+ for (j = 0; j < count; j++)
+ dv[j] = 0.0;
+
+ /* Lookup the colorant Lab values the recommended ICC way */
+ for (j = 0; j < count; j++) {
+ dv[j] = 1.0;
+ luo->lookup(luo, cvals[j], dv);
+ /* For device links the colorant table must be Lab PCS, */
+ /* but embarassingly, XYZ profiles can have colorant values */
+ /* not representable in the Lab PCS range. */
+ if (icmClipLab(cvals[j], cvals[j])) {
+ if (repclip)
+ warning("Colorant Tag Lab value was clipped");
+ repclip = 1;
+ }
+ dv[j] = 0.0;
+ }
+ luo->del(luo);
+
+ /* Lookup colorant names */
+ if ((imask = icx_icc_cv_to_colorant_comb(sh->colorSpace, iccs->header->deviceClass, cvals)) == 0)
+ goto skip_coloranttable;
+
+ /* Create a ColorantTable in the output device link */
+ if ((wo = (icmColorantTable *)wr_icc->add_tag(
+ wr_icc, cts, icSigColorantTableType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icc->errc,wr_icc->err);
+
+ wo->count = count;
+ if (wo->allocate((icmBase *)wo) != 0)
+ error("allocate failed: %d, %s",wr_icc->errc,wr_icc->err);
+
+ for (j = 0; j < count; j++) {
+ inkmask iimask; /* Individual ink mask */
+ char *name;
+
+ iimask = icx_index2ink(imask, j);
+ name = icx_ink2string(iimask);
+ if (strlen(name) > 31)
+ error("Internal: colorant name exceeds 31 characters");
+ strcpy(wo->data[j].name, name);
+ wo->data[j].pcsCoords[0] = cvals[j][0];
+ wo->data[j].pcsCoords[1] = cvals[j][1];
+ wo->data[j].pcsCoords[2] = cvals[j][2];
+ }
+ }
+ /* Jump to here if we can't figure out what to put in ColorantTag */
+ skip_coloranttable:;
+ }
+ }
+ /* 16 bit input device -> output device lut: */
+ {
+ int inputEnt, outputEnt, clutPoints;
+ icmLut *wo;
+
+ /* Setup the cLUT resolutions */
+ if (li.quality >= 3)
+ inputEnt = 4096;
+ else if (li.quality == 2)
+ inputEnt = 2048;
+ else
+ inputEnt = 256;
+
+ /* Make sure that we have at least the number of input entries as the */
+ /* input profile. */
+ if (in_curve_res > inputEnt)
+ inputEnt = in_curve_res;
+
+ /* See discussion in imdi/imdi_gen.c for ideal numbers */
+ switch (li.in.chan) {
+ case 0:
+ error ("Illegal number of input chanels");
+ case 1:
+ if (li.quality >= 3)
+ clutPoints = 255;
+ else if (li.quality == 2)
+ clutPoints = 255;
+ else
+ clutPoints = 255;
+ break;
+
+ case 2:
+ if (li.quality >= 2)
+ clutPoints = 255;
+ else
+ clutPoints = 86;
+ break;
+ case 3:
+ if (li.quality >= 3)
+ clutPoints = 52;
+ else if (li.quality == 2)
+ clutPoints = 33;
+ else if (li.quality == 1)
+ clutPoints = 17;
+ else
+ clutPoints = 9;
+ break;
+ case 4:
+ if (li.quality >= 3)
+ clutPoints = 33;
+ else if (li.quality == 2)
+ clutPoints = 18;
+ else if (li.quality == 1)
+ clutPoints = 9;
+ else
+ clutPoints = 6;
+ break;
+ case 5:
+ if (li.quality >= 3)
+ clutPoints = 18;
+ else if (li.quality == 2)
+ clutPoints = 16;
+ else
+ clutPoints = 9;
+ break;
+ case 6:
+ if (li.quality >= 3)
+ clutPoints = 12;
+ else if (li.quality == 2)
+ clutPoints = 9;
+ else
+ clutPoints = 6;
+ break;
+ case 7:
+ if (li.quality >= 3)
+ clutPoints = 8;
+ else if (li.quality == 2)
+ clutPoints = 7;
+ else
+ clutPoints = 6;
+ break;
+ case 8:
+ if (li.quality >= 3)
+ clutPoints = 7;
+ else if (li.quality == 2)
+ clutPoints = 6;
+ else
+ clutPoints = 5;
+ break;
+ default: /* > 8 chan */
+ clutPoints = 3;
+ break;
+ }
+
+ if (li.clutres > 0) /* clut resolution override */
+ clutPoints = li.clutres;
+ li.clutres = clutPoints; /* Actual resolution */
+
+ if (li.quality >= 3)
+ outputEnt = 4096;
+ else if (li.quality == 2)
+ outputEnt = 2048;
+ else
+ outputEnt = 256;
+
+ /* Make sure that we have at least the number of input entries as the */
+ /* output profile. */
+ if (out_curve_res > outputEnt)
+ outputEnt = out_curve_res;
+
+
+ /* Link Lut = AToB0 */
+ if ((wo = (icmLut *)wr_icc->add_tag(
+ wr_icc, icSigAToB0Tag, icSigLut16Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icc->errc,wr_icc->err);
+
+ wo->inputChan = li.in.chan;
+ wo->outputChan = li.out.chan;
+
+ /* Setup the tables resolutions */
+ wo->inputEnt = inputEnt;
+ wo->clutPoints = clutPoints;
+ wo->outputEnt = outputEnt;
+
+ if (wo->allocate((icmBase *)wo) != 0) /* Allocate space */
+ error("allocate failed: %d, %s",wr_icc->errc,wr_icc->err);
+
+ /* Special case if input profile is Lut with matrix */
+ /* (Does this do anything since input is not XYZ ?) */
+ if (li.in.alg == icmLutType && li.in.nocurve == 0) {
+ icxLuLut *lu = (icxLuLut *)li.in.luo;
+ lu->get_matrix(lu, wo->e); /* Copy it across */
+ }
+
+
+
+ if (li.verb)
+ printf("Filling in Lut table\n");
+#ifdef DEBUG_ONE
+#define DBGNO 1 /* Up to 10 */
+
+#ifndef NEVER
+ /* Test a single given rgb/cmyk -> cmyk value */
+ {
+ double in[10][MAX_CHAN];
+ double out[MAX_CHAN];
+ in[0][0] = 1.0;
+ in[0][1] = 1.0;
+ in[0][2] = 1.0;
+ in[0][3] = 0.0;
+
+ in[1][0] = 1.0;
+ in[1][1] = 1.0;
+ in[1][2] = 1.0;
+ in[1][3] = 0.0;
+
+ for (i = 0; i < DBGNO; i++) {
+ printf("Input %f %f %f %f\n",in[i][0], in[i][1], in[i][2], in[i][3]);
+ devi_devip((void *)&li, out, in[i]);
+ devip_devop((void *)&li, out, out);
+ devop_devo((void *)&li, out, out);
+ printf("Output %f %f %f %f\n\n",out[0], out[1], out[2], out[3]);
+ }
+ }
+#endif /* NEVER */
+
+#else /* !DEBUG_ONE */
+ /* Use helper function to do the hard work. */
+ if (li.verb) {
+ unsigned int ui;
+ int itotal;
+ for (itotal = 1, ui = 0; ui < li.in.chan; ui++, itotal *= clutPoints)
+ ;
+ li.total = itotal;
+ /* Allow for extra lookups due to ICM_CLUT_SET_APXLS */
+ for (itotal = 1, ui = 0; ui < li.in.chan; ui++, itotal *= (clutPoints-1))
+ ;
+ li.total += itotal;
+ li.count = 0;
+ printf(" 0%%"); fflush(stdout);
+ }
+ if (icmSetMultiLutTables(
+ 1,
+ &wo,
+ ICM_CLUT_SET_APXLS, /* Use aproximate least squares */
+ &li, /* Context */
+ li.in.h->colorSpace, /* Input color space */
+ li.out.h->colorSpace, /* Output color space */
+ devi_devip, /* Input transfer tables devi->devi' */
+ NULL, NULL, /* Use default input colorspace range */
+ devip_devop, /* devi' -> devo' transfer function */
+ NULL, NULL, /* Default output colorspace range */
+ devop_devo /* Output transfer tables, devo'->devo */
+ ) != 0) {
+ error("Setting 16 bit Lut failed: %d, %s",wr_icc->errc,wr_icc->err);
+ }
+ if (li.verb) {
+ printf("\n");
+ }
+#ifdef WARN_CLUT_CLIPPING
+ if (wr_icc->warnc)
+ warning("Values clipped in setting device link LUT");
+#endif /* WARN_CLUT_CLIPPING */
+
+#endif /* !DEBUG_ONE */
+
+ }
+
+ if (li.verb && li.wphack && li.wphacked == 0)
+ printf("Warning :- white point hack didn't trigger!\n");
+ if (li.verb && li.wphack && li.wphacked > 1)
+ printf("Warning :- white point hack trigger more than once! (%d)\n",li.wphacked);
+
+ if (li.verb)
+ printf("Writing out file\n");
+
+ /* Write the file out */
+ if ((rv = wr_icc->write(wr_icc,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,wr_icc->err);
+
+ wr_icc->del(wr_icc);
+ wr_fp->del(wr_fp);
+
+ /* - - - - - - - - - - - - - - - - - - - */
+ /* Verify the given link, assuming all the options are the same */
+ } else {
+ icmFile *rd_fp;
+ icc *rd_icc;
+ icmLuBase *luo;
+
+ icColorSpaceSignature ins, outs; /* Type of input and output spaces */
+ int inn, outn; /* Number of components */
+ icmLuAlgType alg; /* Type of lookup algorithm */
+
+ /* Lookup parameters */
+ icmLookupFunc func = icmFwd; /* Default */
+ icRenderingIntent intent = icmDefaultIntent; /* Default */
+ icmLookupOrder order = icmLuOrdNorm; /* Default */
+
+ int gc[MAX_CHAN]; /* Grid counter */
+ int vres = 8; //~~9
+ double imin[MAX_CHAN], imax[MAX_CHAN]; /* Range of input values */
+ double omin[MAX_CHAN], omax[MAX_CHAN]; /* Range of output values */
+ double in[MAX_CHAN]; /* Input value */
+ double ref[MAX_CHAN]; /* Reference output value */
+ double out[MAX_CHAN]; /* Output value */
+ double aerr, perr; /* Average, Peak error */
+ double nerr;
+ int count, total;
+ int pc, lastpc;
+ double pin[MAX_CHAN], pref[MAX_CHAN], pout[MAX_CHAN]; /* Peak error values */
+
+ if (li.verb)
+ printf("Setting up to verify the link\n");
+
+ /* Open up the link file for reading */
+ if ((rd_fp = new_icmFileStd_name(link_name,"r")) == NULL)
+ error ("Verify: Can't open file '%s'",link_name);
+
+ if ((rd_icc = new_icc()) == NULL)
+ error ("Verify: Creation of ICC object failed");
+
+ if ((rv = rd_icc->read(rd_icc,rd_fp,0)) != 0)
+ error ("%d, %s",rv,rd_icc->err);
+
+ /* Check that the profile is appropriate */
+ if (rd_icc->header->deviceClass != icSigLinkClass)
+ error("Profile isn't a device link profile");
+
+ /* Get a conversion object */
+ if ((luo = rd_icc->get_luobj(rd_icc, func, intent, icmSigDefaultData, order)) == NULL)
+ error ("%d, %s",rd_icc->errc, rd_icc->err);
+
+ /* Get details of conversion (Arguments may be NULL if info not needed) */
+ luo->spaces(luo, &ins, &inn, &outs, &outn, &alg, NULL, NULL, NULL, NULL);
+
+ if (alg != icmLutType)
+ error ("DeviceLink profile doesn't have Lut !");
+
+ /* Get the icm value ranges */
+ luo->get_ranges(luo, imin, imax, omin, omax);
+
+ /* Init the grid counter */
+ for (i = 0; i < inn; i++)
+ gc[i] = 0;
+
+ for (total = 1, i = 0; i < inn; i++, total *= vres)
+ ;
+
+ count = 0;
+ lastpc = 0;
+ nerr = aerr = perr = 0.0;
+ for(i = 0; i < inn; ) {
+ int j;
+ double err;
+
+ /* Create and scale input */
+ for (j = 0; j < inn; j++) {
+ in[j] = gc[j]/(vres-1.0);
+ in[j] = in[j] * (imax[j] - imin[j]) + imin[j];
+ }
+
+// printf("Input %f %f %f %f\n",in[0], in[1], in[2], in[3]);
+
+ /* Create the reference output value */
+ devi_devip((void *)&li, ref, in);
+ devip_devop((void *)&li, ref, ref);
+ devop_devo((void *)&li, ref, ref);
+
+ /* Lookup the icm output value */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icc->errc,rd_icc->err);
+
+// printf("Output %f %f %f %f\n",out[0], out[1], out[2], out[3]);
+// printf("Ref %f %f %f %f\n",ref[0], ref[1], ref[2], ref[3]);
+// printf("\n");
+
+ /* Unscale output and compare the results */
+ for (err = 0.0, j = 0; j < outn; j++) {
+ double o,r;
+ o = (out[j] - omin[j])/(omax[j] - omin[j]);
+ r = (ref[j] - omin[j])/(omax[j] - omin[j]);
+ err += (o - r) * (o - r);
+ }
+ err = sqrt(err);
+
+ aerr += err;
+ nerr++;
+ if (err > perr) {
+ perr = err;
+ for (j = 0; j < outn; j++) {
+ pin[j] = in[j];
+ pref[j] = ref[j];
+ pout[j] = out[j];
+ }
+ }
+
+ count++;
+ pc = (int)(count * 100.0/total + 0.5);
+ if (pc != lastpc) {
+ printf("%c%2d%%",cr_char,pc); fflush(stdout);
+ lastpc = pc;
+ }
+
+ /* Increment the grid counter */
+ for (i = 0; i < inn; i++) {
+ if (++gc[i] < vres)
+ break; /* No carry */
+ gc[i] = 0; /* Reset digit */
+ }
+ }
+
+ if (li.verb)
+ printf("Finished verfication\n");
+
+ printf("Average error = %f%%, peak error = %f%%\n",aerr * 100.0/nerr, perr * 100.0);
+ printf("Input %f %f %f %f\n",pin[0], pin[1], pin[2], pin[3]);
+ printf("Output %f %f %f %f\n",pout[0], pout[1], pout[2], pout[3]);
+ printf("Ref %f %f %f %f\n",pref[0], pref[1], pref[2], pref[3]);
+
+ luo->del(luo);
+ rd_icc->del(rd_icc);
+ rd_fp->del(rd_fp);
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - */
+ /* Cleanup source profiles and exit */
+
+ if (li.pcs2k != NULL) /* Free up PCS->K lookup for neutral hack */
+ li.pcs2k->del(li.pcs2k);
+
+ if (li.map != NULL)
+ li.map->del(li.map);
+ if (li.Kmap != NULL)
+ li.Kmap->del(li.Kmap);
+
+ if (li.abs_luo != NULL) { /* Free up abstract transform */
+ li.abs_luo->del(li.abs_luo);
+ li.abs_xicc->del(li.abs_xicc);
+ li.abs_icc->del(li.abs_icc);
+ li.abs_fp->del(li.abs_fp);
+ }
+
+ li.in.luo->del(li.in.luo);
+ li.in.x->del(li.in.x);
+ li.in.c->del(li.in.c);
+
+ if (li.out.b2aluo != NULL)
+ li.out.b2aluo->del(li.out.b2aluo);
+ li.out.luo->del(li.out.luo);
+ li.out.x->del(li.out.x);
+ li.out.c->del(li.out.c);
+
+ return 0;
+}
+
+
+
+
+
+
+
diff --git a/link/monoplot.c b/link/monoplot.c
new file mode 100644
index 0000000..781342b
--- /dev/null
+++ b/link/monoplot.c
@@ -0,0 +1,231 @@
+
+/*
+ * Argyll Color Management System
+ *
+ * Plot the monochrome axis transfer curve of a given link.
+ *
+ * Author: Graeme W. Gill
+ * Date: 01/26/8
+ * Version: 2.00
+ *
+ * Copyright 2001 Graeme W. Gill
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/* TTBD:
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include <math.h>
+#include "copyright.h"
+#include "aconfig.h"
+#include "icc.h"
+#include "plot.h"
+
+#define PRES 100
+
+void error(char *fmt, ...), warning(char *fmt, ...);
+
+/* ---------------------------------------- */
+
+void usage(void) {
+ fprintf(stderr,"Plot monochrome axis of ICC link file, Version %s\n",ARGYLL_VERSION_STR);
+ fprintf(stderr,"PCS->DEV ->link-> DEV->PCS\n");
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: monoplot inprof linkprof outprof\n");
+ fprintf(stderr," -v verbose\n");
+ exit(1);
+}
+
+int
+main(
+ int argc,
+ char *argv[]
+) {
+ int fa, nfa; /* argument we're looking at */
+ int verb = 0;
+ char in_name[100];
+ char link_name[100];
+ char out_name[100];
+ icmFile *in_fp, *link_fp, *out_fp;
+ icc *in_icco, *link_icco, *out_icco;
+ icmLuBase *in_lu, *link_lu, *out_lu;
+ int rv = 0;
+
+ if (argc < 4)
+ usage();
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ /* Verbosity */
+ if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
+ verb = 1;
+ }
+ else if (argv[fa][1] == '?')
+ usage();
+ else
+ usage();
+ }
+ else
+ break;
+ }
+
+ if (fa >= argc || argv[fa][0] == '-') usage();
+ strcpy(in_name,argv[fa++]);
+
+ if (fa >= argc || argv[fa][0] == '-') usage();
+ strcpy(link_name,argv[fa++]);
+
+ if (fa >= argc || argv[fa][0] == '-') usage();
+ strcpy(out_name,argv[fa++]);
+
+ /* Open up the files for reading */
+ if ((in_fp = new_icmFileStd_name(in_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",in_name);
+
+ if ((in_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ if ((rv = in_icco->read(in_icco,in_fp,0)) != 0)
+ error ("Read: %d, %s",rv,in_icco->err);
+
+
+ if ((link_fp = new_icmFileStd_name(link_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",link_name);
+
+ if ((link_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ if ((rv = link_icco->read(link_icco,link_fp,0)) != 0)
+ error ("Read: %d, %s",rv,link_icco->err);
+
+
+ if ((out_fp = new_icmFileStd_name(out_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",out_name);
+
+ if ((out_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ if ((rv = out_icco->read(out_icco,out_fp,0)) != 0)
+ error ("Read: %d, %s",rv,out_icco->err);
+
+
+ /* Get a PCS to Device conversion object */
+ if ((in_lu = in_icco->get_luobj(in_icco, icmBwd, icAbsoluteColorimetric, icSigLabData, icmLuOrdNorm)) == NULL) {
+ if ((in_lu = in_icco->get_luobj(in_icco, icmBwd, icmDefaultIntent, icSigLabData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",in_icco->errc, in_icco->err);
+ }
+
+ /* Get a Device to Device conversion object */
+ if ((link_lu = link_icco->get_luobj(link_icco, icmFwd, icmDefaultIntent, icSigLabData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",link_icco->errc, link_icco->err);
+
+ /* Get a Device to PCS conversion object */
+ if ((out_lu = out_icco->get_luobj(out_icco, icmFwd, icAbsoluteColorimetric, icSigLabData, icmLuOrdNorm)) == NULL) {
+ if ((out_lu = out_icco->get_luobj(out_icco, icmFwd, icmDefaultIntent, icSigLabData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",out_icco->errc, out_icco->err);
+ }
+
+ {
+ double xx[100], yy[PRES];
+ int i;
+ double tt[10];
+
+ for (i = 0; i < PRES; i++) {
+
+ tt[0] = 100.0 * i/(PRES-1.0);
+ tt[1] = tt[2] = 0.0;
+
+ xx[i] = tt[0];
+
+ if (verb)
+ printf(" %f %f %f -> ",tt[0],tt[1],tt[2]);
+
+ /* PCS to input device space */
+ if ((rv = in_lu->lookup(in_lu, tt, tt)) > 1)
+ error ("%d, %s",in_icco->errc,in_icco->err);
+
+
+ /* input device space to output device space */
+ if ((rv = link_lu->lookup(link_lu, tt, tt)) > 1)
+ error ("%d, %s",link_icco->errc,link_icco->err);
+
+ /* output device space to PCS */
+ if ((rv = out_lu->lookup(out_lu, tt, tt)) > 1)
+ error ("%d, %s",out_icco->errc,out_icco->err);
+
+ yy[i] = tt[0];
+
+ if (verb)
+ printf("%f %f %f\n",tt[0],tt[1],tt[2]);
+ }
+
+ if (do_plot(xx,yy,NULL,NULL,PRES) < 0)
+ error("do_plot returned -1!\n");
+
+ }
+
+ /* Done with lookup objects */
+ in_lu->del(in_lu);
+ link_lu->del(link_lu);
+ out_lu->del(out_lu);
+
+ in_icco->del(in_icco);
+ in_fp->del(in_fp);
+ link_icco->del(link_icco);
+ link_fp->del(link_fp);
+ out_icco->del(out_icco);
+ out_fp->del(out_fp);
+
+ return 0;
+}
+
+/* ------------------------------------------------ */
+/* Basic printf type error() and warning() routines */
+
+void
+error(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"monoplot: Error - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ exit (-1);
+}
+
+void
+warning(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"monoplot: Warning - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
diff --git a/link/pathplot.c b/link/pathplot.c
new file mode 100644
index 0000000..fc12943
--- /dev/null
+++ b/link/pathplot.c
@@ -0,0 +1,281 @@
+
+/*
+ * Argyll Color Management System
+ *
+ * Plot the L in vs. L out for a path through the
+ * input device space, for a given link.
+ *
+ * We are assuming RGB device input space !!!!
+ *
+ * Author: Graeme W. Gill
+ * Date: 2002/1/28
+ * Version: 1.00
+ *
+ * Copyright 2002 Graeme W. Gill
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/* TTBD:
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include <math.h>
+#include "copyright.h"
+#include "aconfig.h"
+#include "numlib.h"
+#include "icc.h"
+#include "xicc.h"
+#include "plot.h"
+
+#define PRES 100
+
+static double start[13][4] = {
+ { 0.0, 0.0, 0.0, 0.0},
+ { 0.0, 0.0, 0.0, 0.0},
+ { 0.0, 0.0, 0.0, 0.0},
+ { 0.0, 0.0, 0.0, 0.0},
+ { 0.0, 0.0, 0.0, 0.0},
+ { 0.0, 0.0, 0.0, 0.0},
+
+ { 0.0, 0.0, 0.0, 0.0},
+
+ { 1.0, 0.0, 0.0, 0.0},
+ { 0.0, 1.0, 0.0, 0.0},
+ { 0.0, 0.0, 1.0, 0.0},
+ { 0.0, 1.0, 1.0, 0.0},
+ { 1.0, 0.0, 1.0, 0.0},
+ { 1.0, 1.0, 0.0, 0.0}
+};
+
+static double end[13][4] = {
+ { 1.0, 0.0, 0.0, 0.0},
+ { 0.0, 1.0, 0.0, 0.0},
+ { 0.0, 0.0, 1.0, 0.0},
+ { 0.0, 1.0, 1.0, 0.0},
+ { 1.0, 0.0, 1.0, 0.0},
+ { 1.0, 1.0, 0.0, 0.0},
+
+ { 1.0, 1.0, 1.0, 0.0},
+
+ { 1.0, 1.0, 1.0, 0.0},
+ { 1.0, 1.0, 1.0, 0.0},
+ { 1.0, 1.0, 1.0, 0.0},
+ { 1.0, 1.0, 1.0, 0.0},
+ { 1.0, 1.0, 1.0, 0.0},
+ { 1.0, 1.0, 1.0, 0.0}
+};
+
+static char *name[13] = {
+ "Black - Red", "Black - Green", "Black - Blue",
+ "Black - Cyan", "Black - Magenta", "Black - Yellow",
+ "Grey",
+ "Red - White", "Green - White", "Blue - White",
+ "Cyan - White", "Magenta - White", "Yellow - White",
+ };
+
+#define USE_JAB /* Assume CRT in (2), print out (0) */
+
+/* ---------------------------------------- */
+
+void usage(void) {
+ fprintf(stderr,"Plot device space path L in/out curve from an ICC link file, Version %s\n",ARGYLL_VERSION_STR);
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: pathplot inprof linkprof outprof\n");
+ fprintf(stderr," -v verbose\n");
+ exit(1);
+}
+
+int
+main(
+ int argc,
+ char *argv[]
+) {
+ int fa, nfa; /* argument we're looking at */
+ int verb = 0;
+ char in_name[100];
+ char link_name[100];
+ char out_name[100];
+ icmFile *in_fp, *link_fp, *out_fp;
+ icc *in_icco, *link_icco, *out_icco;
+ xicc *in_xicco, *out_xicco;
+ icmLuBase *link_lu;
+ icxLuBase *in_lu, *out_lu;
+ icColorSpaceSignature pcsor; /* PCS to use */
+ icxViewCond ivc[1], ovc[1];
+ int rv = 0;
+
+ error_program = argv[0];
+
+ if (argc < 4)
+ usage();
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ /* Verbosity */
+ if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
+ verb = 1;
+ }
+ else if (argv[fa][1] == '?')
+ usage();
+ else
+ usage();
+ }
+ else
+ break;
+ }
+
+ if (fa >= argc || argv[fa][0] == '-') usage();
+ strcpy(in_name,argv[fa++]);
+
+ if (fa >= argc || argv[fa][0] == '-') usage();
+ strcpy(link_name,argv[fa++]);
+
+ if (fa >= argc || argv[fa][0] == '-') usage();
+ strcpy(out_name,argv[fa++]);
+
+ /* Open up the files for reading */
+ if ((in_fp = new_icmFileStd_name(in_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",in_name);
+
+ if ((in_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ if ((rv = in_icco->read(in_icco,in_fp,0)) != 0)
+ error ("Read: %d, %s",rv,in_icco->err);
+
+ if ((in_xicco = new_xicc(in_icco)) == NULL)
+ error ("Creation of input profile xicc failed");
+
+
+ if ((link_fp = new_icmFileStd_name(link_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",link_name);
+
+ if ((link_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ if ((rv = link_icco->read(link_icco,link_fp,0)) != 0)
+ error ("Read: %d, %s",rv,link_icco->err);
+
+
+ if ((out_fp = new_icmFileStd_name(out_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",out_name);
+
+ if ((out_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ if ((rv = out_icco->read(out_icco,out_fp,0)) != 0)
+ error ("Read: %d, %s",rv,out_icco->err);
+
+ if ((out_xicco = new_xicc(out_icco)) == NULL)
+ error ("Creation of output profile xicc failed");
+
+
+#ifdef USE_JAB
+ pcsor = icxSigJabData; /* Use CIECAM as PCS */
+
+ if (xicc_enum_viewcond(in_xicco, ivc, -2, "mt", 0, NULL) == -999) /* Set input at monitor in typical */
+ error ("%d, %s",in_xicco->errc, in_xicco->err);
+
+ if (xicc_enum_viewcond(out_xicco, ovc, -2, "pp", 0, NULL) == -999) /* Set output at practical reflection print */
+ error ("%d, %s",out_xicco->errc, out_xicco->err);
+
+#else
+ pcsor = icSigLabData; /* Default use Lab as PCS */
+#endif /* !USE_JAB */
+
+ /* Device to PCS conversion object */
+ if ((in_lu = in_xicco->get_luobj(in_xicco, ICX_CLIP_NEAREST, icmFwd, icAbsoluteColorimetric, pcsor, icmLuOrdNorm, ivc, NULL)) == NULL) {
+ if ((in_lu = in_xicco->get_luobj(in_xicco, ICX_CLIP_NEAREST, icmBwd, icmDefaultIntent, pcsor, icmLuOrdNorm, ivc, NULL)) == NULL)
+ error ("%d, %s",in_xicco->errc, in_xicco->err);
+ }
+
+ /* Get a Device to Device conversion object */
+ if ((link_lu = link_icco->get_luobj(link_icco, icmFwd, icmDefaultIntent, pcsor, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",link_icco->errc, link_icco->err);
+
+ /* Get a Device to PCS conversion object */
+ if ((out_lu = out_xicco->get_luobj(out_xicco, ICX_CLIP_NEAREST, icmFwd, icAbsoluteColorimetric, pcsor, icmLuOrdNorm, ovc, NULL)) == NULL) {
+ if ((out_lu = out_xicco->get_luobj(out_xicco, ICX_CLIP_NEAREST, icmFwd, icmDefaultIntent, pcsor, icmLuOrdNorm, ovc, NULL)) == NULL)
+ error ("%d, %s",out_xicco->errc, out_xicco->err);
+ }
+
+ {
+ double xx[PRES], yy[PRES];
+ int k, i, j;
+ double tt[10], tt2[10];
+
+ for (k = 0; k < 13; k++) {
+ printf("Doing %s\n",name[k]);
+
+ for (i = 0; i < PRES; i++) {
+ double frac = i/(PRES-1.0);
+
+ for (j = 0; j < 4; j++)
+ tt[j] = (end[k][j] - start[k][j]) * frac + start[k][j];
+
+ if (verb)
+ printf(" %f %f %f -> ",tt[0],tt[1],tt[2]);
+
+ /* input device space to PCS */
+ if ((rv = in_lu->lookup(in_lu, tt2, tt)) > 1)
+ error ("%d, %s",in_icco->errc,in_icco->err);
+
+ xx[i] = tt2[0]; /* L value */
+
+ /* input device space to output device space */
+ if ((rv = link_lu->lookup(link_lu, tt, tt)) > 1)
+ error ("%d, %s",link_icco->errc,link_icco->err);
+
+ /* output device space to PCS */
+ if ((rv = out_lu->lookup(out_lu, tt, tt)) > 1)
+ error ("%d, %s",out_icco->errc,out_icco->err);
+
+ yy[i] = tt[0]; /* L value */
+
+ if (verb)
+ printf("%f %f %f\n",tt[0],tt[1],tt[2]);
+ }
+
+ if (do_plot(xx,yy,NULL,NULL,PRES) < 0)
+ error("do_plot returned -1!\n");
+ }
+
+ }
+
+ /* Done with lookup objects */
+ in_lu->del(in_lu);
+ link_lu->del(link_lu);
+ out_lu->del(out_lu);
+
+ in_icco->del(in_icco);
+ in_fp->del(in_fp);
+ link_icco->del(link_icco);
+ link_fp->del(link_fp);
+ out_icco->del(out_icco);
+ out_fp->del(out_fp);
+
+ return 0;
+}
+