From 22f703cab05b7cd368f4de9e03991b7664dc5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 13:56:46 +0200 Subject: Initial import of argyll version 1.5.1-8 --- imdi/Jamfile | 90 ++ imdi/License.txt | 662 +++++++++++++++ imdi/Makefile | 66 ++ imdi/Makefile.OSX | 42 + imdi/Makefile.UNIX | 42 + imdi/Makefile.WNT | 42 + imdi/Makefile.am | 40 + imdi/Readme.txt | 114 +++ imdi/afiles | 26 + imdi/cctiff.c | 2320 ++++++++++++++++++++++++++++++++++++++++++++++++++++ imdi/cctiffo.c | 1097 +++++++++++++++++++++++++ imdi/cgen.c | 2150 ++++++++++++++++++++++++++++++++++++++++++++++++ imdi/ctest.c | 156 ++++ imdi/greytiff.c | 575 +++++++++++++ imdi/imdi.c | 605 ++++++++++++++ imdi/imdi.h | 93 +++ imdi/imdi_arch.h | 71 ++ imdi/imdi_gen.c | 319 ++++++++ imdi/imdi_gen.h | 271 ++++++ imdi/imdi_k.h | 910 +++++++++++++++++++++ imdi/imdi_make.c | 514 ++++++++++++ imdi/imdi_tab.c | 803 ++++++++++++++++++ imdi/imdi_tab.h | 170 ++++ imdi/imdi_utl.h | 262 ++++++ imdi/itest.c | 652 +++++++++++++++ imdi/refi.c | 212 +++++ imdi/refi.h | 53 ++ imdi/ssort.c | 322 ++++++++ 28 files changed, 12679 insertions(+) create mode 100644 imdi/Jamfile create mode 100644 imdi/License.txt create mode 100644 imdi/Makefile create mode 100644 imdi/Makefile.OSX create mode 100644 imdi/Makefile.UNIX create mode 100644 imdi/Makefile.WNT create mode 100644 imdi/Makefile.am create mode 100644 imdi/Readme.txt create mode 100644 imdi/afiles create mode 100644 imdi/cctiff.c create mode 100644 imdi/cctiffo.c create mode 100644 imdi/cgen.c create mode 100644 imdi/ctest.c create mode 100644 imdi/greytiff.c create mode 100644 imdi/imdi.c create mode 100644 imdi/imdi.h create mode 100644 imdi/imdi_arch.h create mode 100644 imdi/imdi_gen.c create mode 100644 imdi/imdi_gen.h create mode 100644 imdi/imdi_k.h create mode 100644 imdi/imdi_make.c create mode 100644 imdi/imdi_tab.c create mode 100644 imdi/imdi_tab.h create mode 100644 imdi/imdi_utl.h create mode 100644 imdi/itest.c create mode 100644 imdi/refi.c create mode 100644 imdi/refi.h create mode 100644 imdi/ssort.c (limited to 'imdi') diff --git a/imdi/Jamfile b/imdi/Jamfile new file mode 100644 index 0000000..a2b3796 --- /dev/null +++ b/imdi/Jamfile @@ -0,0 +1,90 @@ + +# JAM style makefile for integer interpolation code, cctiff etc. + +#PREF_CCFLAGS += $(CCOPTFLAG) ; # Turn optimisation on +PREF_CCFLAGS += $(CCDEBUGFLAG) ; # Debugging flags +#PREF_CCFLAGS += $(CCPROFFLAG) ; # Profile flags +PREF_LINKFLAGS += $(LINKDEBUGFLAG) ; # Link debugging flags +#PREF_LINKFLAGS += $(LINKPROFFLAG) ; # Profile flags +#PREF_CCFLAGS += $(CCHEAPDEBUG) ; # Heap Debugging flags + +#Products +Libraries = libimdi ; +Executables = cctiff greytiff ; +Headers = imdi.h ; + +#Install +InstallBin $(DESTDIR)$(PREFIX)/bin : $(Executables) ; +#InstallFile $(DESTDIR)$(PREFIX)/h : $(Headers) ; +#InstallLib $(DESTDIR)$(PREFIX)/lib : $(Libraries) ; + +HDRS = ../h ; + +# Hack! Make cross compile of MingW64 on 32 bit host work. +# (This doesn't work - 32 bit libraries are not provided :-() +#if $(NT) && $(MINGW64) { +# ObjectCcFlags ctest imdi_make imdi_gen cgen : -m32 ; +# MainLinkFlags ctest imdi_make : -m32 -L $(MINGW64_LIB32) ; +#} +if $(NT) && $(MINGW64) { + IMDI_MAKE_OPT = -f ; # Force 64 bits +} + +# imdi low level cgen test code +Main ctest : ctest.c cgen.c ; + +# make imdi code program +Main imdi_make : imdi_make.c imdi_gen.c cgen.c ; + +HDRS = ../h ../numlib ; +LINKLIBS = ../numlib/libnum ; + +# GenFile source.c : program args ; make custom file +# Generate all the kernel files +GenFileND imdi_k.h : imdi_make $(IMDI_MAKE_OPT) -d [ NormPaths $(DOT) ] ; + +# imdi library +Library libimdi : imdi.c imdi_tab.c ; + +HDRS += ../icc ../rspl ../gamut ../cgats ../spectro ; +LINKLIBS = $(LINKLIBS) libimdi ../icc/libicc ../numlib/libnum ; + +# imdi test code +Main itest : itest.c refi.c : : : ../rspl : : ../rspl/librspl ../plot/libvrml ; + +# TIFF file color correction utlity +Main cctiff : cctiff.c : : : ../xicc $(TIFFINC) $(JPEGINC) : : ../xicc/libxicc ../rspl/librspl ../cgats/libcgats ../plot/libvrml $(TIFFLIB) $(JPEGLIB) ; + +# Old TIFF file color correction utlity +#Main cctiffo : cctiffo.c : : : $(TIFFINC) : : $(TIFFLIB) ; + +# TIFF file monochrome conversion utlity +#Main greytiff : greytiff.c ; +Main greytiff : greytiff.c : : : ../spectro ../xicc ../gamut ../rspl ../cgats $(TIFFINC) + : : ../xicc/libxicc ../gamut/libgamut ../rspl/librspl ../cgats/libcgats + ../plot/libplot ../plot/libvrml $(TIFFLIB) $(JPEGLIB) ; + +# ssort generation code +#Main ssort : ssort.c ; + +#Main shsort : shsort.c ; + +# code generated by shsort +#Main ttt : ttt.c ; + +if $(BUILD_JUNK) { + + Main f2test : f2test.c : : : ../spectro ../xicc ../gamut ../rspl ../cgats $(TIFFINC) + : : ../xicc/libxicc ../gamut/libgamut ../rspl/librspl ../cgats/libcgats + ../plot/libplot ../plot/libvrml $(TIFFLIB) $(JPEGLIB) ; + + + CCFLAGS += -msse3 ; + + Main tvec : tvec.c ; + Main tvec2 : tvec2.c ; + + # test code + Main tsort : tsort.c ; +} + diff --git a/imdi/License.txt b/imdi/License.txt new file mode 100644 index 0000000..a871fcf --- /dev/null +++ b/imdi/License.txt @@ -0,0 +1,662 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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 +. + diff --git a/imdi/Makefile b/imdi/Makefile new file mode 100644 index 0000000..5523893 --- /dev/null +++ b/imdi/Makefile @@ -0,0 +1,66 @@ + +# Boilerplate Makefile for compiling imdi + +# Copyright 2000 - 2007 Graeme W. Gill +# This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- +# see the License.txt file for licencing details. + +# "include" the right environment for your system, +# by uncommenting the appropriate line: + +# Microsoft VC++, WinNT setup +include Makefile.WNT + +# Generic UNIX setup +#include Makefile.UNIX + +# Apple OS X +#include Makefile.OSX + + +############################### + +#Set optimisation on +CCFLAGS = $(CCFLAGSDEF) $(CCOPTFLAG) $(CCDEFINES) $(BCONFIG) + +#Set debugging on +#CCFLAGS = $(CCFLAGSDEF) $(CCDEBUGFLAG) $(CCDEFINES) $(BCONFIG) +# debugging #define +#CCFLAGS = $(CCFLAGSDEF) $(CCDEBUGFLAG) $(CCDEFINES) $(DEFFLAG)DEBUG +LINKFLAGS = $(LINKFLAGSDEF) $(LINKDEBUGFLAG) + +STDHDRS = $(INCFLAG)$(STDHDRSDEF) + +all:: libimdi$(SUFLIB) + +# Used by both code generator and runtime +imdi_make$(SUFEXE): imdi_make$(SUFOBJ) imdi_gen$(SUFOBJ) cgen$(SUFOBJ) + $(LINK) $(LINKOF)imdi_make$(SUFEXE) imdi_make$(SUFOBJ) imdi_gen$(SUFOBJ) cgen$(SUFOBJ) + + +# The code generator program + +cgen$(SUFOBJ): cgen.c imdi_utl.h imdi_arch.h imdi_gen.h imdi_tab.h + $(CC) cgen.c + +imdi_gen$(SUFOBJ): imdi_gen.c imdi_utl.h imdi_arch.h imdi_gen.h + $(CC) imdi_gen.c + +# Generate the kernel files +imdi_k.h imdi_k.c : imdi_make$(SUFEXE) + .$(SLASH)imdi_make$(SUFEXE) + + +# imdi runtime library + +imdi$(SUFOBJ): imdi.c imdi.h imdi_tab.h imdi_k.h imdi_k.c + $(CC) imdi.c + +libimdi$(SUFLIB): imdi$(SUFOBJ) imdi_tab$(SUFOBJ) + $(LIBU) $(LIBOF)$@ imdi$(SUFOBJ) imdi_tab$(SUFOBJ) + $(RANLIB) libimdi$(SUFLIB) + + + + + diff --git a/imdi/Makefile.OSX b/imdi/Makefile.OSX new file mode 100644 index 0000000..1e03a43 --- /dev/null +++ b/imdi/Makefile.OSX @@ -0,0 +1,42 @@ +# MAC OSX, derived from UNIX setup + +# Copyright 2000 - 2007 Graeme W. Gill +# This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- +# see the License.txt file for licencing details. + +SLASH = / +SUFLIB = .a +SUFOBJ = .o +SUFEXE = +CMDSEP = ; + +INCFLAG = -I +DEFFLAG = -D +UNDEFFLAG = -U +CCOPTFLAG = -O +CCDEBUGFLAG = -g +CCPROFFLAG = +LINKDEBUGFLAG = +LINKPROFFLAG = + +STDHDRSDEF = /usr/include + +MAKEU = make +LIBU = ar -r +LIBOF = +RANLIB = ranlib +AS = as +CCFLAGSDEF = -DUNIX -c +CC = cc $(CCFLAGS) $(STDHDRS) +CCOF = -o +LINKFLAGSDEF = -lm +LINKLIBS = +LINK = cc $(LINKFLAGS) $(LINKLIBS) +LINKOF = -o + +.SUFFIXES: +.SUFFIXES: .c $(SUFLIB) $(SUFOBJ) $(SUFEXE) + +.c$(SUFOBJ): + $(CC) $(CCOF)$*$(SUFOBJ) $< + diff --git a/imdi/Makefile.UNIX b/imdi/Makefile.UNIX new file mode 100644 index 0000000..37aab1a --- /dev/null +++ b/imdi/Makefile.UNIX @@ -0,0 +1,42 @@ +# Generic UNIX setup + +# Copyright 2000 - 2007 Graeme W. Gill +# This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- +# see the License.txt file for licencing details. + +SLASH = / +SUFLIB = .a +SUFOBJ = .o +SUFEXE = +CMDSEP = ; + +INCFLAG = -I +DEFFLAG = -D +UNDEFFLAG = -U +CCOPTFLAG = -O +CCDEBUGFLAG = -g +CCPROFFLAG = +LINKDEBUGFLAG = +LINKPROFFLAG = + +STDHDRSDEF = /usr/include + +MAKEU = make +LIBU = ar -r +LIBOF = +RANLIB = echo +AS = as +CCFLAGSDEF = -DUNIX -c +CC = cc $(CCFLAGS) $(STDHDRS) +CCOF = -o +LINKFLAGSDEF = -lm +LINKLIBS = +LINK = cc $(LINKFLAGS) $(LINKLIBS) +LINKOF = -o + +.SUFFIXES: +.SUFFIXES: .c $(SUFLIB) $(SUFOBJ) $(SUFEXE) + +.c$(SUFOBJ): + $(CC) $(CCOF)$*$(SUFOBJ) $< + diff --git a/imdi/Makefile.WNT b/imdi/Makefile.WNT new file mode 100644 index 0000000..601e1f6 --- /dev/null +++ b/imdi/Makefile.WNT @@ -0,0 +1,42 @@ +# Microsoft VC++, WinNT setup + +# Copyright 2000 - 2007 Graeme W. Gill +# This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- +# see the License.txt file for licencing details. + +SLASH = \ +SUFLIB = .lib +SUFOBJ = .obj +SUFEXE = .exe +CMDSEP = & + +INCFLAG = /I +DEFFLAG = /D +UNDEFFLAG = /U +CCOPTFLAG = /Ox /GB +CCDEBUGFLAG = /Z7 /Od +CCPROFFLAG = /Z7 +LINKDEBUGFLAG = /DEBUG +LINKPROFFLAG = /PROFILE + +STDHDRSDEF = $(MSVCNT)\include + +MAKEU = nmake +LIBU = lib +LIBOF = /OUT: +RANLIB = rem +AS = masm386 +CCFLAGSDEF = /DNT /c +CC = cl /nologo $(CCFLAGS) $(STDHDRS) +CCOF = /Fo +LINKLIBS = $(MSVCNT)/lib/user32.lib $(MSVCNT)/lib/gdi32.lib +LINKFLAGSDEF = /link /INCREMENTAL:NO +LINK = link $(LINKFLAGS) +LINKOF = /OUT: + +.SUFFIXES: +.SUFFIXES: .c $(SUFLIB) $(SUFOBJ) $(SUFEXE) + +.c$(SUFOBJ): + $(CC) $(CCOF)$*$(SUFOBJ) $< + diff --git a/imdi/Makefile.am b/imdi/Makefile.am new file mode 100644 index 0000000..94ca112 --- /dev/null +++ b/imdi/Makefile.am @@ -0,0 +1,40 @@ +include $(top_srcdir)/Makefile.shared + +lib_LTLIBRARIES = libimdi.la + +LIBIMDI_VERSION=0.0 + +libimdi_la_SOURCES = imdi.c imdi_tab.c imdi_arch.h imdi_gen.h imdi.h \ + imdi_tab.h imdi_utl.h refi.h imdi_k.h +libimdi_la_LDFLAGS = -version $(shell echo $(LIBIMDI_VERSION) | tr . :):0 + +include_HEADERS = imdi_arch.h imdi_gen.h imdi.h imdi_tab.h \ + imdi_utl.h refi.h + +bin_PROGRAMS = cctiff greytiff + +BINLDADD = ./libimdi.la ../spectro/libinsttypes.la ../plot/libvrml.la \ + ../xicc/libxicc.la ../xicc/libxutils.la ../gamut/libgamut.la \ + ../gamut/libgammap.la ../rspl/librspl.la $(ICC_LIBS) \ + ../cgats/libcgats.la ../numlib/libargyllnum.la ../libargyll.la \ + $(TIFF_LIBS) -ljpeg + +cctiff_LDADD = $(BINLDADD) +greytiff_LDADD = $(BINLDADD) + +check_PROGRAMS = ctest + +ctest_SOURCES = ctest.c cgen.c + +noinst_PROGRAMS = imdi_make + +imdi_make_SOURCES = imdi_make.c imdi_gen.c cgen.c +##imdi_make_CFLAGS = $(CFLAGS) -O + +BUILT_SOURCES = imdi_k.h + +imdi.c: imdi_k.h +imdi_k.h: imdi_make + ./imdi_make + +EXTRA_DIST = License.txt Readme.txt diff --git a/imdi/Readme.txt b/imdi/Readme.txt new file mode 100644 index 0000000..a12d649 --- /dev/null +++ b/imdi/Readme.txt @@ -0,0 +1,114 @@ + +This is the development area for IMDI, the +Integer Multi-Dimensional Interpolation routines. + +They provide a flexible and high performance +system for applying color transforms to typical +raster pixel data. Because they provide a means of +applying arbitrary combination dependent mappings +of multi-channel pixel data, there are many other +possible uses for these sorts of routines as well, +including high quality matting/compositing. For instance, +one could create a smooth, proportional "chroma key" +type of matt for matting one RGB image onto another +by creating a 6 channel to 3 dimensional transform, +that its applied to each pair of pixels from the +source images and produces one combined output pixel. +Additional input or output alpha channels are easy +to add by simply adding more input and/or output +dimensions. The matting calculatons can be almost +arbitrarily complex, and the imdi will apply them +to the pixel data at very high speed. + +The system has two parts, one that generates taylored, +optimised source code for the transformation kernels, +and the run time code that matches a transform request +to a compiled kernel, and initialises the appropriate +run time lookup tables. + +The kernel source generator is intended to accomodate +various optimisations, such as assembly code, vector +instruction set (ie. MMX, AltiVec etc.) versions, but +at present only generates the more portable 'C' code +kernels. + +Both 8 bit per component and 16 bit per component +pixel data is handled, up to 8 input and output +dimensions (but this limit could be trivially raised). + +imdi_make.exe is the module that triggers the generation of + optimised source code as configured for the color spaces + and pixel formats selected. By default creates + a single imdi_k.c and imdi_k.h file, but if + given the -i flag, creates a separate file + for each kernel variant. + +cgen.c C code generator module. + +itest.c regresion test routine. + Normally runs speed and accuracy tests for + all configured kernel variants. + The -q flag makes it run quicker, + but makes the benchmarking inacurate, + the -s flag will cause it to stop + if any routine has unexpectedly low + accuracy. + +cctiff.c is the utility that takes an ICC device + profile link, and converts a TIFF file + from the input colorspace to the output + space. Both 8 bit and 16 bit TIFF files + are handled, as well as colorspaces up to + 8 channels in and out. + This accepts either a device link ICC profile, + or links an input and output devce ICC profile + to define the color transform. + + +greytiff.c is a utility similar to cctiff, that + is an example of how to colorimetrically + convert an RGB file into a monochrome RGB file. + + + +Misc. Notes +----------- + + ITU-T Rec. T.42 specifies the ITULAB encoding in terms of a range + and offset for each component, which are related to the minimum and + maximum values as follows: + + minimum = - (range x offset) / 2^n - 1 + maximum = minimum + range + + The Decode field default values depend on the color space. For the + ITULAB color space encoding, the default values correspond to the + base range and offset, as specified in ITU-T Rec. T.42 [T.42]. The + following table gives the base range and offset values for + BitsPerSample=8 and 12, and the corresponding default minimum and + maximum default values for the Decode field, calculated using the + equations above when PhotometricInterpetation=10. + + +-----------------------------------------------+ + | ITU-T Rec. T.42 | Decode | + +---------+-----------| base values | default values | + | BitsPer + Component +------------------+----------------------------+ + | -Sample | | Range | Offset | Min | Max | + +---------+-----------+--------+---------+--------------+-------------+ + | 8 | L* | 100 | 0 | 0 | 100 | + | +-----------+--------+---------+--------------+-------------+ + | | a* | 170 | 128 | -21760/255 | 21590/255 | + | +-----------+--------+---------+--------------+-------------+ + | | b* | 200 | 96 | -19200/255 | 31800/255 | + +---------+-----------+--------+---------+--------------+-------------+ + | 12 | L* | 100 | 0 | 0 | 100 | + | +-----------+--------+---------+--------------+-------------+ + | | a* | 170 | 2048 | -348160/4095 | 347990/4095 | + | +-----------+--------+---------+--------------+-------------+ + | | b* | 200 | 1536 | -307200/4095 | 511800/4095 | + +---------+-----------+--------+---------+--------------+-------------+ + + For example, when PhotometricInterpretation=10 and BitsPerSample=8, + the default value for Decode is (0, 100, -21760/255, 21590/255, + -19200/255, 31800/255). + diff --git a/imdi/afiles b/imdi/afiles new file mode 100644 index 0000000..81f6220 --- /dev/null +++ b/imdi/afiles @@ -0,0 +1,26 @@ +Readme.txt +License.txt +afiles +Jamfile +Makefile +Makefile.OSX +Makefile.UNIX +Makefile.WNT +cctiff.c +cctiffo.c +greytiff.c +cgen.c +ctest.c +imdi.c +imdi.h +imdi_make.c +imdi_arch.h +imdi_gen.h +imdi_gen.c +imdi_utl.h +imdi_tab.c +imdi_tab.h +itest.c +refi.c +refi.h +ssort.c diff --git a/imdi/cctiff.c b/imdi/cctiff.c new file mode 100644 index 0000000..1f3eb9e --- /dev/null +++ b/imdi/cctiff.c @@ -0,0 +1,2320 @@ + +/* + * Color Correct a TIFF or JPEG file, using an ICC Device link profile. + * Version #2, that allows an arbitrary string of profiles, and + * copes with TIFF L*a*b* input and output. + * + * Author: Graeme W. Gill + * Date: 29/5/2004 + * Version: 2.00 + * + * Copyright 2000 - 2006, 2012 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: + + Should make -d option default to the last device + profile. + + Should add support for getting TIFF ink names from + the ICC profile colorantTable if it exists, + or guessing them if they don't, and + then matching them to the incoming TIFF, and + embedding them in the outgoing TIFF. + Add flag to ignore inkname mismatches. + + + Should add support for transfering any extra alpha + planes from input to output, rather than simply ignoring them. + + + Question: Should this be changed to also function as + a dedicated simple linker, capable of outputing + a device link formed from a sequence ? + If argyll functionality was properly modularized, + it would be possible to have a single arbitrary + smart link sequence for both purposes. + + + There's the sugggestion that the CIELab and ICCLab encodings + have different white points (D65 and D50 respecively - + see ). Should + we convert the white point to D65 for CIELab, or make this + an option ? Probably a bad idea if we regard the Lab in/out + as relative colorimetric representation ?? + + Ideally should automatically generate optimized per channel + input and output curves, rather than depending on + reasonable behaviour from the profiles. + + I don't think that the idea of an XYZ input/output space + has been properly implemented, since the TIFF format + doesn't have an encoding for it, and hence the l2y_curve + and u2l_curve scaling is probably incorrect. + + Forcing XYZ or Lab input & output spaces to be [io]combined + is also not actually necessary, if the necessary profile + curves were applied and the colorspace ranges properly allowed for. + + JPEG FAX encoding is not currently implemented. + + */ + +/* + This program is a framework that exercises the + IMDI code, as well as a demonstration of profile linking. + It can also do the raster data conversion using the + floating point code in ICCLIB as a reference. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#include "numlib.h" +#include "tiffio.h" +#include "jpeglib.h" +#include "iccjpeg.h" +#include "icc.h" +#include "xicc.h" +#include "imdi.h" + +#undef DEBUG /* Print detailed debug info */ + + +#if !defined(O_CREAT) && !defined(_O_CREAT) +# error "Need to #include fcntl.h!" +#endif + +#define DEFJPGQ 80 /* Default JPEG quality */ + +void usage(char *diag, ...) { + fprintf(stderr,"Color Correct a TIFF or JPEG file using any sequence of ICC profiles or Calibrations, V%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: cctiff [-options] { [-i intent] profile%s | calbrtn.cal ...} infile.tif/jpg outfile.tif/jpg\n",ICC_FILE_EXT); + fprintf(stderr," -v Verbose.\n"); + fprintf(stderr," -c Combine linearisation curves into one transform.\n"); + fprintf(stderr," -p Use slow precise correction.\n"); + fprintf(stderr," -k Check fast result against precise, and report.\n"); + fprintf(stderr," -r n Override the default CLUT resolution\n"); + fprintf(stderr," -t n Choose output encoding from 1..n\n"); + fprintf(stderr," -f [T|J] Set output format to Tiff or Jpeg (Default is same as input)\n"); + fprintf(stderr," -q quality Set JPEG quality 1..100 (Default %d)\n",DEFJPGQ); + fprintf(stderr," -a Read and Write planes > 4 as alpha planes\n"); + fprintf(stderr," -I Ignore any file or profile colorspace mismatches\n"); + fprintf(stderr," -D Don't append or set the output TIFF or JPEG description\n"); + fprintf(stderr," -e profile.[%s | tiff | jpg] Optionally embed a profile in the destination TIFF or JPEG file.\n",ICC_FILE_EXT_ND); + fprintf(stderr,"\n"); + fprintf(stderr," Then for each profile in sequence:\n"); + fprintf(stderr," -i intent p = perceptual, r = relative colorimetric,\n"); + fprintf(stderr," s = saturation, a = absolute colorimetric\n"); + fprintf(stderr," -o order n = normal (priority: lut > matrix > monochrome)\n"); + fprintf(stderr," r = reverse (priority: monochrome > matrix > lut)\n"); + fprintf(stderr," profile.[%s | tiff] Device, Link or Abstract profile\n",ICC_FILE_EXT_ND); + fprintf(stderr," ( May be embedded profile in TIFF/JPEG file)\n"); + fprintf(stderr," or each calibration file in sequence:\n"); + fprintf(stderr," -d dir f = forward cal. (default), b = backwards cal.\n"); + fprintf(stderr," calbrtn.cal Device calibration file.\n"); + fprintf(stderr,"\n"); + fprintf(stderr," infile.tif/jpg Input TIFF/JPEG file in appropriate color space\n"); + fprintf(stderr," outfile.tif/jpg Output TIFF/JPEG file\n"); + exit(1); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Conversion functions from direct binary 0..n^2-1 == 0.0 .. 1.0 range */ +/* to ICC luo input range, and the reverse. */ +/* Note that all these functions are per-component, */ +/* so the can be included in per-component input or output curves, */ +/* if they PCS values were rescaled to be within the range 0.0 .. 1.0. */ +/* Since we're not currently doing this, we always set i/ocpmbine */ +/* if the input/output is PCS, so that real PCS values don't */ +/* appear in the input/output curves. */ + +/* TIFF 8 bit CIELAB to standard L*a*b* */ +/* Assume that a & b have been converted from signed to offset */ +static void cvt_CIELAB8_to_Lab(double *out, double *in) { + out[0] = in[0] * 100.0; + out[1] = in[1] * 255.0 - 128.0; + out[2] = in[2] * 255.0 - 128.0; +} + +/* Standard L*a*b* to TIFF 8 bit CIELAB */ +/* Assume that a & b will be converted from offset to signed */ +static void cvt_Lab_to_CIELAB8(double *out, double *in) { + out[0] = in[0] / 100.0; + out[1] = (in[1] + 128.0) * 1.0/255.0; + out[2] = (in[2] + 128.0) * 1.0/255.0; +} + + +/* TIFF 16 bit CIELAB to standard L*a*b* */ +/* Assume that a & b have been converted from signed to offset */ +static void cvt_CIELAB16_to_Lab(double *out, double *in) { + out[0] = in[0] * 100.0; + out[1] = (in[1] - 32768.0/65535.0) * 256.0; + out[2] = (in[2] - 32768.0/65535.0) * 256.0; +} + +/* Standard L*a*b* to TIFF 16 bit CIELAB */ +/* Assume that a & b will be converted from offset to signed */ +static void cvt_Lab_to_CIELAB16(double *out, double *in) { + out[0] = in[0] / 100.0; + out[1] = in[1]/256.0 + 32768.0/65535.0; + out[2] = in[2]/256.0 + 32768.0/65535.0; +} + + +/* TIFF 8 bit ICCLAB to standard L*a*b* */ +static void cvt_ICCLAB8_to_Lab(double *out, double *in) { + out[0] = in[0] * 100.0; + out[1] = (in[1] * 255.0) - 128.0; + out[2] = (in[2] * 255.0) - 128.0; +} + +/* Standard L*a*b* to TIFF 8 bit ICCLAB */ +static void cvt_Lab_to_ICCLAB8(double *out, double *in) { + out[0] = in[0] * 1.0/100.0; + out[1] = (in[1] + 128.0) * 1.0/255.0; + out[2] = (in[2] + 128.0) * 1.0/255.0; +} + + +/* TIFF 16 bit ICCLAB to standard L*a*b* */ +static void cvt_ICCLAB16_to_Lab(double *out, double *in) { + out[0] = in[0] * (100.0 * 65535.0)/65280.0; + out[1] = (in[1] * (255.0 * 65535.0)/65280) - 128.0; + out[2] = (in[2] * (255.0 * 65535.0)/65280) - 128.0; +} + +/* Standard L*a*b* to TIFF 16 bit ICCLAB */ +static void cvt_Lab_to_ICCLAB16(double *out, double *in) { + out[0] = in[0] * 65280.0/(100.0 * 65535.0); + out[1] = (in[1] + 128.0) * 65280.0/(255.0 * 65535.0); + out[2] = (in[2] + 128.0) * 65280.0/(255.0 * 65535.0); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Convert a TIFF Photometric tag to an ICC colorspace. */ +/* return 0 if not possible or applicable. */ +icColorSpaceSignature +TiffPhotometric2ColorSpaceSignature( +void (**ocvt)(double *out, double *in), /* Return write conversion function, NULL if none */ +void (**icvt)(double *out, double *in), /* Return read conversion function, NULL if none */ +int *smsk, /* Return signed handling mask, 0x0 if none */ +int pmtc, /* Input TIFF photometric */ +int bps, /* Input Bits per sample */ +int spp, /* Input Samples per pixel */ +int extra /* Extra Samples per pixel, if any */ +) { + if (icvt != NULL) + *icvt = NULL; /* Default return values */ + if (ocvt != NULL) + *ocvt = NULL; /* Default return values */ + if (smsk != NULL) + *smsk = 0x0; + +// if (extra > 0 && pmtc != PHOTOMETRIC_SEPARATED) +// return 0x0; /* We don't handle this */ + + switch (pmtc) { + case PHOTOMETRIC_MINISWHITE: /* Subtractive Gray */ + return icSigGrayData; + + case PHOTOMETRIC_MINISBLACK: /* Additive Gray */ + return icSigGrayData; + + case PHOTOMETRIC_RGB: + return icSigRgbData; + + case PHOTOMETRIC_PALETTE: + return 0x0; + + case PHOTOMETRIC_MASK: + return 0x0; + + case PHOTOMETRIC_SEPARATED: + /* Should look at the colorant names to figure out if this is CMY, CMYK */ + /* Should at least return both Cmy/3 or Cmyk/4 ! */ + switch(spp) { + case 2: + return icSig2colorData; + case 3: +// return icSig3colorData; + return icSigCmyData; + case 4: +// return icSig4colorData; + return icSigCmykData; + case 5: + return icSig5colorData; + case 6: + return icSig6colorData; + case 7: + return icSig7colorData; + case 8: + return icSig8colorData; + case 9: + return icSig9colorData; + case 10: + return icSig10colorData; + case 11: + return icSig11colorData; + case 12: + return icSig12colorData; + case 13: + return icSig13colorData; + case 14: + return icSig14colorData; + case 15: + return icSig15colorData; + } + + case PHOTOMETRIC_YCBCR: + return icSigYCbCrData; + + case PHOTOMETRIC_CIELAB: + if (bps == 8) { + if (icvt != NULL) + *icvt = cvt_CIELAB8_to_Lab; + if (ocvt != NULL) + *ocvt = cvt_Lab_to_CIELAB8; + } else { + if (icvt != NULL) + *icvt = cvt_CIELAB16_to_Lab; + if (ocvt != NULL) + *ocvt = cvt_Lab_to_CIELAB16; + } + *smsk = 0x6; /* Treat a & b as signed */ + return icSigLabData; + + case PHOTOMETRIC_ICCLAB: + if (bps == 8) { + if (icvt != NULL) + *icvt = cvt_ICCLAB8_to_Lab; + if (ocvt != NULL) + *ocvt = cvt_Lab_to_ICCLAB8; + } else { + if (icvt != NULL) + *icvt = cvt_ICCLAB16_to_Lab; + if (ocvt != NULL) + *ocvt = cvt_Lab_to_ICCLAB16; + } + return icSigLabData; + + case PHOTOMETRIC_ITULAB: + return 0x0; /* Could add this with a conversion function */ + /* but have to allow for variable ITU gamut */ + /* (Tag 433, "Decode") */ + + case PHOTOMETRIC_LOGL: + return 0x0; /* Could add this with a conversion function */ + + case PHOTOMETRIC_LOGLUV: + return 0x0; /* Could add this with a conversion function */ + } + return 0x0; +} + + +/* Convert an ICC colorspace to the corresponding possible TIFF Photometric tags. */ +/* Return the number of matching tags, and 0 if there is no corresponding tag. */ +int +ColorSpaceSignature2TiffPhotometric( +uint16 tags[10], /* Pointer to return array, up to 10 */ +icColorSpaceSignature cspace /* Input ICC colorspace */ +) { + switch(cspace) { + case icSigGrayData: + tags[0] = PHOTOMETRIC_MINISBLACK; + return 1; + case icSigRgbData: + tags[0] = PHOTOMETRIC_RGB; + return 1; + case icSigCmyData: + case icSigCmykData: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + case icSigYCbCrData: + tags[0] = PHOTOMETRIC_YCBCR; + return 1; + case icSigXYZData: + case icSigLabData: + tags[0] = PHOTOMETRIC_CIELAB; + tags[1] = PHOTOMETRIC_ICCLAB; +#ifdef NEVER + tags[2] = PHOTOMETRIC_ITULAB; +#endif + return 2; + + case icSigLuvData: + case icSigYxyData: /* Could handle this with a conversion ?? */ + case icSigHsvData: + case icSigHlsData: + return 0; + + case icSig2colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + case icSig3colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + case icSig4colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + case icSig5colorData: + case icSigMch5Data: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + case icSig6colorData: + case icSigMch6Data: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + case icSig7colorData: + case icSigMch7Data: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + case icSig8colorData: + case icSigMch8Data: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + case icSig9colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + case icSig10colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + case icSig11colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + case icSig12colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + case icSig13colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + case icSig14colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + case icSig15colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + + default: + return 0; + } + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Compute the length of a double nul terminated string, including */ +/* the nuls. */ +static int zzstrlen(char *s) { + int i; + for (i = 0;; i++) { + if (s[i] == '\000' && s[i+1] == '\000') + return i+2; + } + return 0; +} + +/* Convert an ICC colorspace to the corresponding TIFF Inkset tag */ +/* return 0xffff if not possible or applicable. */ +static int +ColorSpaceSignature2TiffInkset( +icColorSpaceSignature cspace, +int *len, /* Return length of ASCII inknames */ +char **inknames /* Return ASCII inknames if non NULL */ +) { + switch(cspace) { + case icSigCmyData: + return 0xffff; + if (inknames != NULL) { + *inknames = "cyan\000magenta\000yellow\000\000"; + *len = zzstrlen(*inknames); + } + return 0; /* Not CMYK */ + case icSigCmykData: + if (inknames != NULL) { + *inknames = NULL; /* No inknames */ + *len = 0; + } + return INKSET_CMYK; + + case icSigGrayData: + case icSigRgbData: + case icSigYCbCrData: + case icSigLabData: + case icSigXYZData: + case icSigLuvData: + case icSigYxyData: + case icSigHsvData: + case icSigHlsData: + case icSig2colorData: + case icSig3colorData: + case icSig4colorData: + case icSig5colorData: + case icSigMch5Data: + return 0xffff; + + case icSig6colorData: + case icSigMch6Data: + /* This is a cheat and a hack. Should really make use of the */ + /* ColorantTable to determine the colorant names. */ + /* allowing cctiff to read it. */ + if (inknames != NULL) { + *inknames = "cyan\000magenta\000yellow\000black\000orange\000green\000\000"; + *len = zzstrlen(*inknames); + } + return INKSET_MULTIINK; + + case icSig7colorData: + case icSigMch7Data: + return 0xffff; + + case icSig8colorData: + case icSigMch8Data: + /* This is a cheat and a hack. Should really make use of the */ + /* ColorantTable to determine the colorant names. */ + /* allowing cctiff to read it. */ + if (inknames != NULL) { + *inknames = "cyan\000magenta\000yellow\000black\000orange\000green\000lightcyan\000lightmagenta\000\000"; + *len = zzstrlen(*inknames); + } + return INKSET_MULTIINK; + case icSig9colorData: + case icSig10colorData: + case icSig11colorData: + case icSig12colorData: + case icSig13colorData: + case icSig14colorData: + case icSig15colorData: + default: + return 0xffff; + } + return 0xffff; +} + +static char * +Photometric2str( +int pmtc +) { + static char buf[80]; + switch (pmtc) { + case PHOTOMETRIC_MINISWHITE: + return "Subtractive Gray"; + case PHOTOMETRIC_MINISBLACK: + return "Additive Gray"; + case PHOTOMETRIC_RGB: + return "RGB"; + case PHOTOMETRIC_PALETTE: + return "Indexed"; + case PHOTOMETRIC_MASK: + return "Transparency Mask"; + case PHOTOMETRIC_SEPARATED: + return "Separated"; + case PHOTOMETRIC_YCBCR: + return "YCbCr"; + case PHOTOMETRIC_CIELAB: + return "CIELab"; + case PHOTOMETRIC_ICCLAB: + return "ICCLab"; + case PHOTOMETRIC_ITULAB: + return "ITULab"; + case PHOTOMETRIC_LOGL: + return "CIELog2L"; + case PHOTOMETRIC_LOGLUV: + return "CIELog2Luv"; + } + sprintf(buf,"Unknown Photometric Tag %d",pmtc); + return buf; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#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; + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Callbacks used to initialise imdi */ + + +/* Information needed from a single profile */ +struct _profinfo { + char name[MAXNAMEL+1]; + icc *c; /* If non-NULL, ICC profile. */ + xcal *cal; /* if non-NULL, xcal rather than profile */ + + /* Valid for both icc and xcal: */ + icColorSpaceSignature ins, outs; /* Colorspace of conversion */ + int id, od; /* Dimensions of conversion */ + + /* Valid only for icc: */ + icmHeader *h; + icRenderingIntent intent; /* Rendering intent chosen */ + icmLookupFunc func; /* Type of function to use in lookup */ + icmLookupOrder order; /* tag search order to use */ + icmLuAlgType alg; /* Type of lookup algorithm used */ + int clutres; /* If this profile uses a clut, what's it's res. ? */ + icColorSpaceSignature natpcs; /* Underlying natural PCS */ + icmLuBase *luo; /* Base Lookup type object */ + +}; typedef struct _profinfo profinfo; + +/* Context for imdi setup callbacks */ +typedef struct { + /* Overall parameters */ + int verb; /* Non-zero if verbose */ + icColorSpaceSignature ins, outs; /* Input/Output spaces */ + int iinv, oinv; /* Space inversion */ + int id, od, md; /* Input/Output dimensions and max(id,od) */ + int width, height; /* Width and heigh of raster */ + int isign_mask; /* Input sign mask */ + int osign_mask; /* Output sign mask */ + int icombine; /* Non-zero if input curves are to be combined */ + int ocombine; /* Non-zero if output curves are to be combined */ + int ilcurve; /* 1 if input curves are to be concatenated with Y like ->L* curve */ + /* (2 if input curves are to be concatenated with Y->L* curve) */ + int olcurve; /* 1 if output curves are to be concatenated with L*->Y like curve */ + /* (2 if output curves are to be concatenated with L*->Y curve) */ + void (*icvt)(double *out, double *in); /* If non-NULL, Input format conversion */ + void (*ocvt)(double *out, double *in); /* If non-NULL, Output format conversion */ + + int nprofs; /* Number of profiles in the sequence */ + int first, last; /* index of first and last profiles/cals, 0 and nprofs-1 */ + int fclut, lclut; /* first and last profiles/cals that are part of multi-d table */ + profinfo *profs; /* Profile information */ +} sucntx; + + +/* Input curve function */ +static void input_curves( + void *cntx, + double *out_vals, + double *in_vals +) { + int i; + sucntx *rx = (sucntx *)cntx; + +//printf("~1 incurve in %f %f %f %f\n",in_vals[0],in_vals[1],in_vals[2],in_vals[3]); + + for (i = 0; i < rx->id; i++) + out_vals[i] = in_vals[i]; /* Default to nothing */ + + if (rx->icombine == 0) { /* Not combined into multi-d table */ + + /* TIFF input format conversion */ + if (rx->icvt != NULL) { /* (Never used because PCS < 0.0 > 1.0) */ + rx->icvt(out_vals, out_vals); + } + + /* Any concatinated input calibrations */ + for (i = rx->first; i < rx->fclut; i++) { + if (rx->profs[i].func == icmFwd) + rx->profs[i].cal->interp(rx->profs[i].cal, out_vals, out_vals); + else + rx->profs[i].cal->inv_interp(rx->profs[i].cal, out_vals, out_vals); + } + + /* The input table of the first profile */ + /* (icombine is set if input is PCS) */ + if (rx->fclut <= rx->lclut) + rx->profs[rx->fclut].luo->lookup_in(rx->profs[rx->fclut].luo, out_vals, out_vals); + } + + /* If input curve converts to Y like space, apply Y->L* curve */ + /* so as to index CLUT perceptually. */ + if (rx->ilcurve != 0) { + y2l_curve(out_vals, out_vals, rx->ilcurve == 2); + } +//printf("~1 incurve out %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]); +} + +/* Multi-dim table function */ +static void md_table( +void *cntx, +double *out_vals, +double *in_vals +) { + sucntx *rx = (sucntx *)cntx; + double vals[MAX_CHAN]; + icColorSpaceSignature prs; /* Previous colorspace */ + int i, j; + + for (i = 0; i < rx->id; i++) + vals[i] = in_vals[i]; /* default is do nothing */ + +//printf("~1 md_table in %f %f %f %f\n",vals[0],vals[1],vals[2],vals[3]); + + if (rx->ilcurve) { + /* Apply L*->Y curve to compensate for curve applied after input curve */ + l2y_curve(vals, vals, rx->ilcurve == 2); + } + + prs = rx->ins; + + /* If the input curves are being combined into clut: */ + if (rx->icombine != 0) { + + /* Any needed TIFF file format conversion */ + if (rx->icvt != NULL) { + rx->icvt(vals, vals); +//printf("~1 md_table after icvt %f %f %f %f\n",vals[0],vals[1],vals[2],vals[3]); + } + + /* Any concatinated input calibrations */ + for (j = rx->first; j < rx->fclut; j++) { + if (rx->profs[j].func == icmFwd) + rx->profs[j].cal->interp(rx->profs[j].cal, vals, vals); + else + rx->profs[j].cal->inv_interp(rx->profs[j].cal, vals, vals); + } +//printf("~1 md_table after in cals %f %f %f %f\n",vals[0],vals[1],vals[2],vals[3]); + } + + /* Do all the profile links in-between (if any) */ + for (j = rx->fclut; j <= rx->lclut; j++) { + + /* If it's a calibration */ + if (rx->profs[j].cal != NULL) { + if (rx->profs[j].func == icmFwd) + rx->profs[j].cal->interp(rx->profs[j].cal, vals, vals); + else + rx->profs[j].cal->inv_interp(rx->profs[j].cal, vals, vals); + + /* Else it's a profile */ + } else { + /* Convert PCS for this profile */ + if (prs == icSigXYZData && rx->profs[j].ins == icSigLabData) { + icmXYZ2Lab(&icmD50, vals, vals); +//printf("~1 md_table after XYZ2Lab %f %f %f %f\n",vals[0],vals[1],vals[2],vals[3]); + } else if (prs == icSigLabData && rx->profs[j].ins == icSigXYZData) { + icmLab2XYZ(&icmD50, vals, vals); +//printf("~1 md_table after Lab2XYZ %f %f %f %f\n",vals[0],vals[1],vals[2],vals[3]); + } + + /* If first or last profile */ + if (j == rx->fclut || j == rx->lclut) { + if (j != rx->fclut || rx->icombine) { + rx->profs[j].luo->lookup_in(rx->profs[j].luo, vals, vals); +//printf("~1 md_table after input curve %f %f %f %f\n",vals[0],vals[1],vals[2],vals[3]); + } + rx->profs[j].luo->lookup_core(rx->profs[j].luo, vals, vals); +//printf("~1 md_table after core %f %f %f %f\n",vals[0],vals[1],vals[2],vals[3]); + if (j != rx->lclut || rx->ocombine) { + rx->profs[j].luo->lookup_out(rx->profs[j].luo, vals, vals); + } +//printf("~1 md_table after output curve %f %f %f %f\n",vals[0],vals[1],vals[2],vals[3]); + /* Middle of chain */ + } else { + rx->profs[j].luo->lookup(rx->profs[j].luo, vals, vals); +//printf("~1 md_table after middle %f %f %f %f\n",vals[0],vals[1],vals[2],vals[3]); + } + } + prs = rx->profs[j].outs; + } + + /* convert last PCS to rx->outs PCS if needed */ + if (prs == icSigXYZData + && rx->outs == icSigLabData) { + icmXYZ2Lab(&icmD50, vals, vals); +//printf("~1 md_table after out XYZ2Lab %f %f %f %f\n",vals[0],vals[1],vals[2],vals[3]); + } else if (prs == icSigLabData + && rx->outs == icSigXYZData) { + icmLab2XYZ(&icmD50, vals, vals); +//printf("~1 md_table after out Lab2XYZ %f %f %f %f\n",vals[0],vals[1],vals[2],vals[3]); + } + + /* If the output curves are being combined into clut: */ + if (rx->ocombine != 0) { + + /* Any concatinated output calibrations */ + for (j = rx->lclut+1; j <= rx->last; j++) { + if (rx->profs[j].func == icmFwd) + rx->profs[j].cal->interp(rx->profs[j].cal, vals, vals); + else + rx->profs[j].cal->inv_interp(rx->profs[j].cal, vals, vals); + } +//printf("~1 md_table after out cals %f %f %f %f\n",vals[0],vals[1],vals[2],vals[3]); + + /* Any needed TIFF file format conversion */ + if (rx->ocvt != NULL) { + rx->ocvt(vals, vals); +//printf("~1 md_table after out ocvt %f %f %f %f\n",vals[0],vals[1],vals[2],vals[3]); + } + + } + + if (rx->olcurve) { + /* Add Y->L* curve to cause interpolation in perceptual space */ + y2l_curve(vals, vals, rx->olcurve == 2); + } + + for (i = 0; i < rx->od; i++) + out_vals[i] = vals[i]; +//printf("~1 md_table returns %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]); +} + +/* Output curve function */ +static void output_curves( + void *cntx, + double *out_vals, + double *in_vals +) { + sucntx *rx = (sucntx *)cntx; + int i; + +//printf("~1 outurve in %f %f %f %f\n",in_vals[0],in_vals[1],in_vals[2],in_vals[3]); + for (i = 0; i < rx->od; i++) + out_vals[i] = in_vals[i]; + + /* Apply L* -> Y curve to undo curve applied at CLUT output. */ + if (rx->olcurve != 0) { + l2y_curve(out_vals, out_vals, rx->olcurve == 2); + } + + if (rx->ocombine == 0) { /* Not combined into multi-d table */ + + /* The output table of the last profile */ + /* (ocombine is set if output is PCS) */ + if (rx->fclut <= rx->lclut) { + rx->profs[rx->lclut].luo->lookup_out(rx->profs[rx->lclut].luo, out_vals, out_vals); +//printf("~1 md_table after out curve %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]); + } + + /* Any concatinated output calibrations */ + for (i = rx->lclut+1; i <= rx->last; i++) { + if (rx->profs[i].func == icmFwd) + rx->profs[i].cal->interp(rx->profs[i].cal, out_vals, out_vals); + else + rx->profs[i].cal->inv_interp(rx->profs[i].cal, out_vals, out_vals); + } + + /* Any needed file format conversion */ + if (rx->ocvt != NULL) { /* (Never used because PCS < 0.0 > 1.0) */ + rx->ocvt(out_vals, out_vals); +//printf("~1 md_table after out ocvt %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]); + } + } +//printf("~1 outurve out %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +/* Check whether two colorspaces appear compatible */ +/* return NZ if they match, Z if they don't. */ +/* Compatible means any PCS == any PCS, or exact match */ +int CSMatch(icColorSpaceSignature s1, icColorSpaceSignature s2) { + if (s1 == s2) + return 1; + + if ((s1 == icSigXYZData || s1 == icSigLabData) + && (s2 == icSigXYZData || s2 == icSigLabData)) + return 1; + + if ((s1 == icSig5colorData || s1 == icSigMch5Data) + && (s2 == icSig5colorData || s2 == icSigMch5Data)) + return 1; + + if ((s1 == icSig6colorData || s1 == icSigMch6Data) + && (s2 == icSig6colorData || s2 == icSigMch6Data)) + return 1; + + if ((s1 == icSig7colorData || s1 == icSigMch7Data) + && (s2 == icSig7colorData || s2 == icSigMch7Data)) + return 1; + + if ((s1 == icSig8colorData || s1 == icSigMch8Data) + && (s2 == icSig8colorData || s2 == icSigMch8Data)) + return 1; + + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* JPEG error information */ +typedef struct { + jmp_buf env; /* setjmp/longjmp environment */ + char message[JMSG_LENGTH_MAX]; +} jpegerrorinfo; + +/* JPEG error handler */ +static void jpeg_error(j_common_ptr cinfo) { + jpegerrorinfo *p = (jpegerrorinfo *)cinfo->client_data; + (*cinfo->err->format_message) (cinfo, p->message); + longjmp(p->env, 1); +} + +static char * +JPEG_cspace2str( +J_COLOR_SPACE cspace +) { + static char buf[80]; + switch (cspace) { + case JCS_UNKNOWN: + return "Unknown"; + case JCS_GRAYSCALE: + return "Monochrome"; + case JCS_RGB: + return "RGB"; + case JCS_YCbCr: + return "YCbCr"; + case JCS_CMYK: + return "CMYK"; + case JCS_YCCK: + return "YCCK"; + } + sprintf(buf,"Unknown JPEG colorspace %d",cspace); + return buf; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +int +main(int argc, char *argv[]) { + int fa, nfa; /* argument we're looking at */ + char in_name[MAXNAMEL+1] = ""; /* Input raster file name */ + char out_name[MAXNAMEL+1] = ""; /* Output raster file name */ + char dst_pname[MAXNAMEL+1] = ""; /* Destination embedded profile file name */ + icc *deicc = NULL; /* Destination embedded profile (if any) */ + icRenderingIntent next_intent; /* Rendering intent for next profile */ + icmLookupOrder next_order; /* tag search order for next profile */ + icmLookupFunc next_func; /* Direction for next calibration */ + int last_dim; /* Next dimentionality between conversions */ + icColorSpaceSignature last_colorspace; /* Next colorspace between conversions */ + char *last_cs_file; /* Name of the file the last colorspace came from */ + int dojpg = -1; /* 0 = tiff, 1 = jpg, -1 = same as input */ + int jpgq = -1; /* Jpeg quality, default DEFJPGQ */ + int doimdi = 1; /* Use the fast overall integer conversion */ + int dofloat = 0; /* Use the slow precice (float). */ + int check = 0; /* Check fast (int) against slow (float) */ + int ochoice = 0; /* Output encoding choice 1..n */ + int alpha = 0; /* Use alpha for extra planes */ + int ignoremm = 0; /* Ignore any colorspace mismatches */ + int nodesc = 0; /* Don't append or set the description */ + int i, j, rv = 0; + + /* TIFF file info */ + TIFFErrorHandler olderrh, oldwarnh; + TIFFErrorHandlerExt olderrhx, oldwarnhx; + TIFF *rh = NULL, *wh = NULL; + + int x, y, width, height; /* Common size of image */ + uint16 bitspersample; /* Bits per sample */ + uint16 resunits; + float resx, resy; + uint16 pconfig; /* Planar configuration */ + + uint16 rsamplesperpixel, wsamplesperpixel; /* Channels per sample */ + uint16 rphotometric, wphotometric; /* Photometrics */ + uint16 rextrasamples, wextrasamples; /* Extra "alpha" samples */ + uint16 *rextrainfo, wextrainfo[MAX_CHAN]; /* Info about extra samples */ + char *rdesc = NULL; /* Existing description */ + char *ddesc = "[ Color corrected by ArgyllCMS ]"; /* Default description */ + char *wdesc = NULL; /* Written desciption */ + + tdata_t *inbuf = NULL, *outbuf = NULL, *hprecbuf = NULL; + int inbpix, outbpix; /* Number of pixels in jpeg in/out buf */ + + /* JPEG file info */ + jpegerrorinfo jpeg_rerr, jpeg_werr; + FILE *rf = NULL, *wf = NULL; + struct jpeg_decompress_struct rj; + struct jpeg_compress_struct wj; + struct jpeg_error_mgr jerr; + + /* IMDI */ + imdi *s = NULL; + sucntx su; /* Setup context */ + unsigned char *inp[MAX_CHAN]; + unsigned char *outp[MAX_CHAN]; + int clutres = 0; /* Default */ + + /* Error check */ + int mxerr = 0; + double avgerr = 0.0; + double avgcount = 0.0; + + error_program = "cctiff"; + if (argc < 2) + usage("Too few arguments"); + + /* Set defaults */ + memset((void *)&su, 0, sizeof(sucntx)); + next_intent = icmDefaultIntent; + next_func = icmFwd; + next_order = icmLuOrdNorm; + + /* JPEG */ + jpeg_std_error(&jerr); + jerr.error_exit = jpeg_error; + + /* 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 */ + } + } + } + + if (argv[fa][1] == '?') + usage("Usage requested"); + + /* Slow, Precise, not integer */ + else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') { + doimdi = 0; + dofloat = 1; + } + + /* Combine per channel curves */ + else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') { + su.icombine = 1; + su.ocombine = 1; + } + + /* Check curves */ + else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') { + doimdi = 1; + dofloat = 1; + check = 1; + } + + /* Use alpha planes for any over 4 */ + else if (argv[fa][1] == 'a' || argv[fa][1] == 'A') { + alpha = 1; + } + + /* CLUT resolution */ + else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') { + fa = nfa; + if (na == NULL) usage("Expect argument to -r flag"); + clutres = atoi(na); + if (clutres < 2) + usage("-r argument must be >= 2"); + } + + /* Output file encoding choice */ + else if (argv[fa][1] == 't' || argv[fa][1] == 'T') { + fa = nfa; + if (na == NULL) usage("Expect argument to -t flag"); + ochoice = atoi(na); + } + + /* Output file format override */ + else if (argv[fa][1] == 'f') { + fa = nfa; + if (na == NULL) usage("Missing argument to -f flag"); + switch (na[0]) { + case 't': + case 'T': + dojpg = 0; + break; + case 'j': + case 'J': + dojpg = 1; + break; + default: + usage("Unknown argument '%c' to -f flag",na[0]); + } + } + + /* JPEG quality */ + else if (argv[fa][1] == 'q') { + fa = nfa; + if (na == NULL) usage("Expect argument to -q flag"); + jpgq = atoi(na); + if (jpgq < 1 || jpgq > 100) + usage("-q argument must 1..100"); + } + + /* Destination TIFF embedded profile */ + else if (argv[fa][1] == 'e' || argv[fa][1] == 'E') { + fa = nfa; + if (na == NULL) usage("Expect profile name argument to -e flag"); + strncpy(dst_pname,na, MAXNAMEL); dst_pname[MAXNAMEL] = '\000'; + } + + /* Next profile Intent */ + else if (argv[fa][1] == 'i') { + fa = nfa; + if (na == NULL) usage("Missing argument to -i flag"); + switch (na[0]) { + case 'p': + case 'P': + next_intent = icPerceptual; + break; + case 'r': + case 'R': + next_intent = icRelativeColorimetric; + break; + case 's': + case 'S': + next_intent = icSaturation; + break; + case 'a': + case 'A': + next_intent = icAbsoluteColorimetric; + break; + default: + usage("Unknown argument '%c' to -i flag",na[0]); + } + } + + /* Next profile search order */ + else if (argv[fa][1] == 'o') { + fa = nfa; + if (na == NULL) usage("Missing argument to -o flag"); + switch (na[0]) { + case 'n': + case 'N': + next_order = icmLuOrdNorm; + break; + case 'r': + case 'R': + next_order = icmLuOrdRev; + break; + default: + usage("Unknown argument '%c' to -o flag",na[0]); + } + } + + /* Next calibraton direction */ + else if (argv[fa][1] == 'd') { + fa = nfa; + if (na == NULL) usage("Missing argument to -i flag"); + switch (na[0]) { + case 'f': + case 'F': + next_func = icmFwd; + break; + case 'b': + case 'B': + next_func = icmBwd; + break; + default: + usage("Unknown argument '%c' to -d flag",na[0]); + } + } + + else if (argv[fa][1] == 'I') + ignoremm = 1; + + else if (argv[fa][1] == 'D') + nodesc = 1; + + /* Verbosity */ + else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') { + su.verb = 1; + } + + else { + usage("Unknown flag '%c'",argv[fa][1]); + } + + } else if (argv[fa][0] != '\000') { + /* Get the next filename */ + + if (su.nprofs == 0) + su.profs = (profinfo *)malloc(sizeof(profinfo)); + else + su.profs = (profinfo *)realloc(su.profs, (su.nprofs+1) * sizeof(profinfo)); + if (su.profs == NULL) + error("Malloc failed in allocating space for profile info."); + + memset((void *)&su.profs[su.nprofs], 0, sizeof(profinfo)); + strncpy(su.profs[su.nprofs].name,argv[fa],MAXNAMEL); + su.profs[su.nprofs].name[MAXNAMEL] = '\000'; + su.profs[su.nprofs].intent = next_intent; + su.profs[su.nprofs].func = next_func; + su.profs[su.nprofs].order = next_order; + + su.nprofs++; + next_intent = icmDefaultIntent; + next_func = icmFwd; + next_order = icmLuOrdNorm; + } else { + break; + } + } + + /* The last two "profiles" are actually the input and output TIFF filenames */ + /* Unwind them */ + if (su.nprofs < 2) + usage("Not enough arguments to specify input and output TIFF files"); + + strncpy(out_name,su.profs[--su.nprofs].name, MAXNAMEL); out_name[MAXNAMEL] = '\000'; + strncpy(in_name,su.profs[--su.nprofs].name, MAXNAMEL); in_name[MAXNAMEL] = '\000'; + + su.fclut = su.first = 0; + su.lclut = su.last = su.nprofs-1; + + if (check && (!doimdi || !dofloat)) + error("Can't do check unless both integera and float processing are enabled"); + +/* + + Logic required: + + Discover input TIFF colorspace and set as (ICC) "next_space" + Set any special input space encoding transform (ie. device, Lab flavour) + + For each profile: + + case abstract: + set dir = fwd, intent = default + check next_space == CIE + next_space = CIE + + case dev link: + set dir = fwd, intent = default + check next_space == profile.in_devspace + next_space = profile.out_devspace + + case cal file: + check next_space == cal.devspace + next_space = cal.devspace + + case colorspace/input/display/output: + if colorspace + set intent = default + + if next_space == CIE + set dir = fwd + next_space = profile.devspace + else + set dir = bwd + check next_space == profile.devspace + next_space = CIE + + create luo + + Make output TIFF colorspace match next_space + + Figure out how many calibrations can be concatinated into the input + and output curves. + + Set any special output space encoding transform (ie. device, Lab flavour) + +*/ + /* - - - - - - - - - - - - - - - */ + /* Open up input tiff file ready for reading */ + /* Discover input TIFF colorspace and set as (ICC) "last_colorspace" */ + /* Set any special input space encoding transform (ie. device, Lab flavour) */ + + /* Supress TIFF messages */ + olderrh = TIFFSetErrorHandler(NULL); + oldwarnh = TIFFSetWarningHandler(NULL); + olderrhx = TIFFSetErrorHandlerExt(NULL); + oldwarnhx = TIFFSetWarningHandlerExt(NULL); + + if ((rh = TIFFOpen(in_name, "r")) != NULL) { + + TIFFSetErrorHandler(olderrh); + TIFFSetWarningHandler(oldwarnh); + TIFFSetErrorHandlerExt(olderrhx); + TIFFSetWarningHandlerExt(oldwarnhx); + + TIFFGetField(rh, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetField(rh, TIFFTAG_IMAGELENGTH, &height); + + TIFFGetField(rh, TIFFTAG_BITSPERSAMPLE, &bitspersample); + if (bitspersample != 8 && bitspersample != 16) { + error("TIFF Input file must be 8 or 16 bits/channel"); + } + + TIFFGetFieldDefaulted(rh, TIFFTAG_EXTRASAMPLES, &rextrasamples, &rextrainfo); +// if (rextrasamples > 0 && alpha == 0) +// error("TIFF Input file has extra samples per pixel - cctiff can't handle that"); + + TIFFGetField(rh, TIFFTAG_PHOTOMETRIC, &rphotometric); + TIFFGetField(rh, TIFFTAG_SAMPLESPERPIXEL, &rsamplesperpixel); + + /* Figure out how to handle the input TIFF colorspace */ + if ((su.ins = TiffPhotometric2ColorSpaceSignature(NULL, &su.icvt, &su.isign_mask, rphotometric, + bitspersample, rsamplesperpixel, rextrasamples)) == 0) + error("Can't handle TIFF file photometric %s", Photometric2str(rphotometric)); + su.iinv = 0; + su.id = rsamplesperpixel; + + TIFFGetField(rh, TIFFTAG_PLANARCONFIG, &pconfig); + if (pconfig != PLANARCONFIG_CONTIG) + error ("TIFF Input file must be planar"); + + TIFFGetField(rh, TIFFTAG_RESOLUTIONUNIT, &resunits); + TIFFGetField(rh, TIFFTAG_XRESOLUTION, &resx); + TIFFGetField(rh, TIFFTAG_YRESOLUTION, &resy); + + last_dim = su.id; + last_colorspace = su.ins; + last_cs_file = in_name; + + su.width = width; + su.height = height; + + if (TIFFGetField(rh, TIFFTAG_IMAGEDESCRIPTION, &rdesc) != 0) { + if ((rdesc = strdup(rdesc)) == NULL) + error("Malloc of input file description string failed"); + } else + rdesc = NULL; + + if (dojpg < 0) + dojpg = 0; + + /* See if it is a JPEG File */ + } else { + jpeg_saved_marker_ptr mlp; + + TIFFSetErrorHandler(olderrh); + TIFFSetWarningHandler(oldwarnh); + TIFFSetErrorHandlerExt(olderrhx); + TIFFSetWarningHandlerExt(oldwarnhx); + +//printf("~1 TIFFOpen failed on '%s'\n",in_name); + + /* We cope with the horrible ijg jpeg library error handling */ + /* by using a setjmp/longjmp. */ + if (setjmp(jpeg_rerr.env)) { + /* Something went wrong with opening the file */ + jpeg_destroy_decompress(&rj); + error("error opening read file '%s' [%s]",in_name,jpeg_rerr.message); + } + + rj.err = &jerr; + rj.client_data = &jpeg_rerr; + jpeg_create_decompress(&rj); + +#if defined(O_BINARY) || defined(_O_BINARY) + if ((rf = fopen(in_name,"rb")) == NULL) +#else + if ((rf = fopen(in_name,"r")) == NULL) +#endif + { + jpeg_destroy_decompress(&rj); + error("error opening read file '%s'",in_name); + } + + jpeg_stdio_src(&rj, rf); + jpeg_save_markers(&rj, JPEG_COM, 0xFFFF); + + /* we'll longjmp on error */ + jpeg_read_header(&rj, TRUE); + + bitspersample = rj.data_precision; + if (bitspersample != 8 && bitspersample != 16) { + error("JPEG Input file must be 8 or 16 bit/channel"); + } + + /* No extra samples */ + rextrasamples = 0; + su.iinv = 0; + + switch (rj.jpeg_color_space) { + case JCS_GRAYSCALE: + rj.out_color_space = JCS_GRAYSCALE; + su.ins = icSigGrayData; + su.id = 1; + break; + + case JCS_YCbCr: /* get libjpg to convert to RGB */ + rj.out_color_space = JCS_RGB; + su.ins = icSigRgbData; + su.id = 3; + if (ochoice == 0) + ochoice = 1; + break; + + case JCS_RGB: + rj.out_color_space = JCS_RGB; + su.ins = icSigRgbData; + su.id = 3; + if (ochoice == 0) + ochoice = 2; + break; + + case JCS_YCCK: /* libjpg to convert to CMYK */ + rj.out_color_space = JCS_CMYK; + su.ins = icSigCmykData; + su.id = 4; + if (rj.saw_Adobe_marker) + su.iinv = 1; + if (ochoice == 0) + ochoice = 1; + break; + + case JCS_CMYK: + rj.out_color_space = JCS_CMYK; + su.ins = icSigCmykData; + su.id = 4; + if (rj.saw_Adobe_marker) /* Adobe inverts CMYK */ + su.iinv = 1; + if (ochoice == 0) + ochoice = 2; + break; + + default: + error("Can't handle JPEG file colorspace 0x%x", rj.jpeg_color_space); + } + + if (rj.density_unit == 1) + resunits = RESUNIT_INCH; + else if (rj.density_unit == 2) + resunits = RESUNIT_CENTIMETER; + else + resunits = RESUNIT_NONE; + resx = rj.X_density; + resy = rj.Y_density; + + last_dim = su.id; + last_colorspace = su.ins; + last_cs_file = in_name; + + jpeg_calc_output_dimensions(&rj); + su.width = width = rj.output_width; + su.height = height = rj.output_height; + + /* Locate any comment */ + rdesc = NULL; + for (mlp = rj.marker_list; mlp != NULL; mlp = mlp->next) { + if (mlp->marker == JPEG_COM && mlp->data_length > 0) { + if ((rdesc = malloc(mlp->data_length+1)) == NULL) + error("Malloc of input file description string failed"); + memcpy(rdesc, mlp->data, mlp->data_length-1); + rdesc[mlp->data_length] = '\000'; + break; + } + } + + if (dojpg < 0) + dojpg = 1; + + /* ~~ Should determine deafult jpgq from tables of this file */ + + jpeg_start_decompress(&rj); + } + + + /* - - - - - - - - - - - - - - - */ + /* Check and setup the sequence of ICC profiles */ + + /* For each profile in the sequence, configure it to transform the color */ + /* appropriately */ + for (i = su.first; i <= su.last; i++) { + + /* First see if it's a calibration file */ + if ((su.profs[i].cal = new_xcal()) == NULL) + error("new_xcal failed"); + + if ((su.profs[i].cal->read(su.profs[i].cal, su.profs[i].name)) == 0) { + + su.profs[i].ins = su.profs[i].outs = icx_colorant_comb_to_icc(su.profs[i].cal->devmask); + if (su.profs[i].outs == 0) + error ("Calibration file '%s' has unhandled device mask %s",su.profs[i].name,icx_inkmask2char(su.profs[i].cal->devmask,1)); + su.profs[i].id = su.profs[i].od = su.profs[i].cal->devchan; + /* We use the user provided direction */ + + /* else see if it's an ICC or embedded ICC */ + } else { + + su.profs[i].cal->del(su.profs[i].cal); /* Clean up */ + su.profs[i].cal = NULL; + + if ((su.profs[i].c = read_embedded_icc(su.profs[i].name)) == NULL) + error ("Can't read profile or calibration from file '%s'",su.profs[i].name); + + su.profs[i].h = su.profs[i].c->header; + + /* Deal with different profile classes, */ + /* and set the profile function and intent. */ + switch (su.profs[i].h->deviceClass) { + case icSigAbstractClass: + case icSigLinkClass: + su.profs[i].func = icmFwd; + su.profs[i].intent = icmDefaultIntent; + break; + + case icSigColorSpaceClass: + su.profs[i].func = icmFwd; + su.profs[i].intent = icmDefaultIntent; + /* Fall through */ + + case icSigInputClass: + case icSigDisplayClass: + case icSigOutputClass: + /* Note we don't handle an ambigious (both directions match) case. */ + /* We would need direction from the user to resolve this. */ + if (CSMatch(last_colorspace, su.profs[i].h->colorSpace)) { + su.profs[i].func = icmFwd; + } else { + su.profs[i].func = icmBwd; /* PCS -> Device */ + } + break; + /* Use the user provided intent */ + + default: + error("Can't handle deviceClass %s from file '%s'", + icm2str(icmProfileClassSignature,su.profs[i].h->deviceClass), + su.profs[i].c->err,su.profs[i].name); + } + + /* Get a conversion object */ + if ((su.profs[i].luo = su.profs[i].c->get_luobj(su.profs[i].c, su.profs[i].func, + su.profs[i].intent, icmSigDefaultData, su.profs[i].order)) == NULL) + error ("%d, %s from '%s'",su.profs[i].c->errc, su.profs[i].c->err, su.profs[i].name); + + /* Get details of conversion */ + su.profs[i].luo->spaces(su.profs[i].luo, &su.profs[i].ins, &su.profs[i].id, + &su.profs[i].outs, &su.profs[i].od, &su.profs[i].alg, NULL, NULL, NULL, NULL); + + /* Get native PCS space */ + su.profs[i].luo->lutspaces(su.profs[i].luo, NULL, NULL, NULL, NULL, &su.profs[i].natpcs); + + /* If this is a lut transform, find out its resolution */ + if (su.profs[i].alg == icmLutType) { + icmLut *lut; + icmLuLut *luluo = (icmLuLut *)su.profs[i].luo; /* Safe to coerce */ + luluo->get_info(luluo, &lut, NULL, NULL, NULL); /* Get some details */ + su.profs[i].clutres = lut->clutPoints; /* Desired table resolution */ + } else + su.profs[i].clutres = 0; + + } + + /* Check that we can join to previous correctly */ + if (!ignoremm && !CSMatch(last_colorspace, su.profs[i].ins)) + error("Last colorspace %s from file '%s' doesn't match input space %s of profile %s", + icm2str(icmColorSpaceSignature,last_colorspace), + last_cs_file, + icm2str(icmColorSpaceSignature,su.profs[i].h->colorSpace), + su.profs[i].name); + + last_dim = icmCSSig2nchan(su.profs[i].outs); + last_colorspace = su.profs[i].outs; + last_cs_file = su.profs[i].name; + } + + su.od = last_dim; + su.oinv = 0; + su.outs = last_colorspace; + + /* Go though the sequence again, and count the number of leading and */ + /* trailing calibrations that can be combined into the input and output */ + /* lookup curves */ + for (i = su.first; ; i++) { + if (i > su.last || su.profs[i].c != NULL) { + su.fclut = i; + break; + } + } + for (i = su.last; ; i--) { + if (i < su.first || su.profs[i].c != NULL) { + su.lclut = i; + break; + } + } + + if (su.fclut > su.lclut) { /* Hmm. All calibs, no profiles */ + su.fclut = su.first; /* None at start */ + su.lclut = su.first-1; /* All at the end */ + } + +//printf("~1 first = %d, fclut = %d, lclut = %d, last = %d\n", su.first, su.fclut, su.lclut, su.last); + + su.md = su.id > su.od ? su.id : su.od; + + /* - - - - - - - - - - - - - - - */ + /* Create a TIFF file */ + if (dojpg == 0) { + /* Open up the output TIFF file for writing */ + if ((wh = TIFFOpen(out_name, "w")) == NULL) + error("Can\'t create TIFF file '%s'!",out_name); + + wsamplesperpixel = su.od; + + wextrasamples = 0; + if (alpha && wsamplesperpixel > 4) { + wextrasamples = wsamplesperpixel - 4; /* Call samples > 4 "alpha" samples */ + for (j = 0; j < wextrasamples; j++) + wextrainfo[j] = EXTRASAMPLE_UNASSALPHA; + } + + /* Configure the output TIFF file appropriately */ + TIFFSetField(wh, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(wh, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(wh, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(wh, TIFFTAG_SAMPLESPERPIXEL, wsamplesperpixel); + TIFFSetField(wh, TIFFTAG_BITSPERSAMPLE, bitspersample); + TIFFSetField(wh, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + + TIFFSetField(wh, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + if (resunits) { + TIFFSetField(wh, TIFFTAG_RESOLUTIONUNIT, resunits); + TIFFSetField(wh, TIFFTAG_XRESOLUTION, resx); + TIFFSetField(wh, TIFFTAG_YRESOLUTION, resy); + } + /* Perhaps the description could be more informative ? */ + if (rdesc != NULL) { + if ((wdesc = malloc(sizeof(char) * (strlen(rdesc) + strlen(ddesc) + 2))) == NULL) + error("malloc failed on new desciption string"); + + strcpy(wdesc, rdesc); + if (nodesc == 0 && su.nprofs > 0) { + strcat(wdesc, " "); + strcat(wdesc, ddesc); + } + TIFFSetField(wh, TIFFTAG_IMAGEDESCRIPTION, wdesc); + } else if (nodesc == 0 && su.nprofs > 0) { + if ((wdesc = strdup(ddesc)) == NULL) + error("malloc failed on new desciption string"); + TIFFSetField(wh, TIFFTAG_IMAGEDESCRIPTION, ddesc); + } + + /* Lookup and decide what TIFF photometric suites the output colorspace */ + { + int no_pmtc; /* Number of possible photometrics */ + uint16 pmtc[10]; /* Photometrics of output file */ + if ((no_pmtc = ColorSpaceSignature2TiffPhotometric(pmtc, + last_colorspace)) == 0) + error("TIFF file can't handle output colorspace '%s'!", + icm2str(icmColorSpaceSignature, last_colorspace)); + + if (no_pmtc > 1) { /* Need to choose a photometric */ + if (ochoice < 1 || ochoice > no_pmtc ) { + printf("Possible Output Encodings for output colorspace %s are:\n", + icm2str(icmColorSpaceSignature,last_colorspace)); + for (i = 0; i < no_pmtc; i++) + printf("%d: %s%s\n",i+1, Photometric2str(pmtc[i]), i == 0 ? " (Default)" : ""); + ochoice = 1; + } + wphotometric = pmtc[ochoice-1]; + } else { + wphotometric = pmtc[0]; + } + } + + /* Lookup what we need to handle this. */ + if ((su.outs = TiffPhotometric2ColorSpaceSignature(&su.ocvt, NULL, &su.osign_mask, wphotometric, + bitspersample, wsamplesperpixel, wextrasamples)) == 0) + error("Can't handle TIFF file photometric %s", Photometric2str(wphotometric)); + TIFFSetField(wh, TIFFTAG_PHOTOMETRIC, wphotometric); + + if (alpha && wextrasamples > 0) { + TIFFSetField(wh, TIFFTAG_EXTRASAMPLES, wextrasamples, wextrainfo); + + } else { + + if (wphotometric == PHOTOMETRIC_SEPARATED) { + icc *c = su.profs[su.lclut].c; + icmColorantTable *ct; + int iset; + int inlen; + char *inames = NULL; + + if (c == NULL + || ((ct = (icmColorantTable *)c->read_tag(c, icSigColorantTableOutTag)) == NULL + && (ct = (icmColorantTable *)c->read_tag(c, icSigColorantTableTag)) == NULL) + || ct->count != wsamplesperpixel + ) { + iset = ColorSpaceSignature2TiffInkset(su.outs, &inlen, &inames); + } else { + int i; + char *cp; + inlen = 0; + for (i = 0; i < ct->count; i++) + inlen += strlen(ct->data[i].name) + 1; + inlen += 1; + if ((inames = malloc(inlen)) == NULL) + error("malloc failed on inknames string"); + cp = inames; + for (i = 0; i < ct->count; i++) { + int slen = strlen(ct->data[i].name) + 1; + memcpy(cp, ct->data[i].name, slen); + cp += slen; + } + *cp = '\000'; + iset = INKSET_MULTIINK; + } + if (iset != 0xffff && inlen > 0 && inames != NULL) { + TIFFSetField(wh, TIFFTAG_INKSET, iset); + if (inames != NULL) { + TIFFSetField(wh, TIFFTAG_INKNAMES, inlen, inames); + } + } + } + } + + /* Create JPEG file */ + } else { + jpeg_saved_marker_ptr mlp; + int jpeg_color_space; + + /* We cope with the horrible ijg jpeg library error handling */ + /* by using a setjmp/longjmp. */ + if (setjmp(jpeg_werr.env)) { + /* Something went wrong with opening the file */ + jpeg_destroy_compress(&wj); + error("Can\'t create JPEG file '%s'! [%s]",out_name, jpeg_werr.message); + } + + wj.err = &jerr; + wj.client_data = &jpeg_werr; + jpeg_create_compress(&wj); + +#if defined(O_BINARY) || defined(_O_BINARY) + if ((wf = fopen(out_name,"wb")) == NULL) +#else + if ((wf = fopen(out_name,"w")) == NULL) +#endif + { + jpeg_destroy_compress(&wj); + error("Can\'t create JPEG file '%s'!",out_name); + } + + jpeg_stdio_dest(&wj, wf); + + wj.image_width = width; + wj.image_height = height; + wj.input_components = su.od; + + switch (last_colorspace) { + case icSigGrayData: + wj.in_color_space = JCS_GRAYSCALE; + jpeg_color_space = JCS_GRAYSCALE; + break; + + case icSigRgbData: + wj.in_color_space = JCS_RGB; + if (ochoice < 0 || ochoice > 2) { + printf("Possible JPEG Output Encodings for output colorspace icSigRgbData are\n" + "1: YCbCr (Default)\n" "2: RGB\n"); + ochoice = 1; + } + if (ochoice == 2) + jpeg_color_space = JCS_RGB; + else + jpeg_color_space = JCS_YCbCr; + break; + + case icSigCmykData: + wj.in_color_space = JCS_CMYK; + if (ochoice < 0 || ochoice > 2) { + printf("Possible JPEG Output Encodings for output colorspace icSigCmykData are\n" + "1: YCCK (Default)\n" "2: CMYK\n"); + ochoice = 1; + } + if (ochoice == 2) + jpeg_color_space = JCS_CMYK; + else + jpeg_color_space = JCS_YCCK; + break; + + default: + error("JPEG file can't handle output colorspace '%s'!", + icm2str(icmColorSpaceSignature, last_colorspace)); + } + + if (resunits != RESUNIT_NONE) { + if (resunits == RESUNIT_INCH) + wj.density_unit = 1; + else if (resunits == RESUNIT_CENTIMETER) + wj.density_unit = 2; + wj.X_density = resx; + wj.Y_density = resy; + } + + jpeg_set_defaults(&wj); + jpeg_set_colorspace(&wj, jpeg_color_space); + + if (jpgq < 0) + jpgq = DEFJPGQ; + jpeg_set_quality(&wj, jpgq, TRUE); + + /* The default sub-sampling sub-samples the CC and K of YCC & YCCK */ + /* while not sub-sampling RGB or CMYK */ + + if (wj.write_Adobe_marker) + su.oinv = 1; + + jpeg_start_compress(&wj, TRUE); + + /* Perhaps the description could be more informative ? */ + if (rdesc != NULL) { + if ((wdesc = malloc(sizeof(char) * (strlen(rdesc) + strlen(ddesc) + 2))) == NULL) + error("malloc failed on new desciption string"); + + strcpy(wdesc, rdesc); + if (nodesc == 0 && su.nprofs > 0) { + strcat(wdesc, " "); + strcat(wdesc, ddesc); + } + jpeg_write_marker(&wj, JPEG_COM, (const JOCTET *)wdesc, strlen(wdesc)+1); + } else if (nodesc == 0 && su.nprofs > 0) { + if ((wdesc = strdup(ddesc)) == NULL) + error("malloc failed on new desciption string"); + jpeg_write_marker(&wj, JPEG_COM, (const JOCTET *)wdesc, strlen(wdesc)+1); + } + } + + /* - - - - - - - - - - - - - - - */ + /* Setup any destination embedded profile */ + if (dst_pname[0] != '\000') { + icmFile *fp; /* Read fp for the profile */ + unsigned char *buf; + int size; + + if ((deicc = read_embedded_icc(dst_pname)) == NULL) + error("Unable to open profile for destination embedding '%s'",dst_pname); + + /* Check that it is compatible with the destination raster file */ + if (deicc->header->deviceClass != icSigColorSpaceClass + && deicc->header->deviceClass != icSigInputClass + && deicc->header->deviceClass != icSigDisplayClass + && deicc->header->deviceClass != icSigOutputClass) { + error("Destination embedded profile is wrong device class for embedding"); + } + + if (deicc->header->colorSpace != su.outs + || (deicc->header->pcs != icSigXYZData + && deicc->header->pcs != icSigLabData)) { + error("Destination embedded profile colorspaces don't match TIFF"); + } + + if ((fp = deicc->get_rfp(deicc)) == NULL) + error("Failed to be able to read destination embedded profile"); + + if ((size = fp->get_size(fp)) == 0) + error("Failed to be able to get size of destination embedded profile"); + + if ((buf = malloc(size)) == NULL) + error("malloc failed on destination embedded profile size %d",size); + + if (fp->seek(fp,0)) + error("rewind on destination embedded profile failed"); + + if (fp->read(fp, buf, 1, size) != size) + error("reading destination embedded profile failed"); + + /* (For iccv4 we would now fp->del(fp) because we got a reference) */ + + if (wh != NULL) { + if (TIFFSetField(wh, TIFFTAG_ICCPROFILE, size, buf) == 0) + error("setting TIFF embedded ICC profile field failed"); + } else { + if (setjmp(jpeg_werr.env)) { + jpeg_destroy_compress(&wj); + error("setting JPEG embedded ICC profile marker failed"); + } + write_icc_profile(&wj, buf, size); + } + + free(buf); + deicc->del(deicc); + } + + /* - - - - - - - - - - - - - - - */ + if ((su.fclut <= su.lclut + && (su.profs[su.fclut].natpcs == icSigXYZData) && (su.profs[su.fclut].alg == icmMatrixFwdType)) + || su.profs[su.fclut].ins == icSigXYZData) { + su.ilcurve = 1; /* Index CLUT with L* curve rather than Y */ + } + + /* Setup input/output curve use. */ + if (su.ins == icSigLabData || su.ins == icSigXYZData) { + su.icombine = 1; /* CIE can't be conveyed through 0..1 domain lookup */ + } + + if ((su.fclut <= su.lclut + && (su.profs[su.lclut].natpcs == icSigXYZData && su.profs[su.lclut].alg == icmMatrixBwdType)) + || (su.profs[su.lclut].outs == icSigXYZData)) { + su.olcurve = 1; /* Interpolate in L* space rather than Y */ + } + + if (su.outs == icSigLabData || su.outs == icSigXYZData) { + su.ocombine = 1; /* CIE can't be conveyed through 0..1 domain lookup */ + } + + /* - - - - - - - - - - - - - - - */ + /* Report the connection sequence details */ + + if (su.verb) { + + if (rh) { + printf("Input raster file '%s' is TIFF\n",in_name); + printf("Input TIFF file photometric is %s\n",Photometric2str(rphotometric)); + } else { + printf("Input raster file '%s' is JPEG\n",in_name); + printf("Input JPEG file original colorspace is %s\n",JPEG_cspace2str(rj.jpeg_color_space)); + } + printf("Input raster file ICC colorspace is %s\n",icm2str(icmColorSpaceSignature,su.ins)); + printf("Input raster file is %d x %d pixels\n",su.width, su.height); + if (rdesc != NULL) + printf("Input raster file description: '%s'\n",rdesc); + printf("\n"); + + printf("There are %d profiles/calibrations in the sequence:\n\n",su.nprofs); + + for (i = su.first; i <= su.last; i++) { + if (su.profs[i].c != NULL) { + icmFile *op; + if ((op = new_icmFileStd_fp(stdout)) == NULL) + error ("Can't open stdout"); + printf("Profile %d '%s':\n",i,su.profs[i].name); + su.profs[i].h->dump(su.profs[i].h, op, 1); + op->del(op); + printf("Direction = %s\n",icm2str(icmTransformLookupFunc, su.profs[i].func)); + printf("Intent = %s\n",icm2str(icmRenderingIntent, su.profs[i].intent)); + printf("Algorithm = %s\n",icm2str(icmLuAlg, su.profs[i].alg)); + } else { + printf("Calibration %d '%s':\n",i,su.profs[i].name); + printf("Direction = %s\n",icm2str(icmTransformLookupFunc, su.profs[i].func)); + if (su.profs[i].cal->xpi.deviceMfgDesc != NULL) + printf("Manufacturer: '%s'\n",su.profs[i].cal->xpi.deviceMfgDesc); + if (su.profs[i].cal->xpi.modelDesc != NULL) + printf("Model: '%s'\n",su.profs[i].cal->xpi.modelDesc); + if (su.profs[i].cal->xpi.profDesc != NULL) + printf("Description: '%s'\n",su.profs[i].cal->xpi.profDesc); + if (su.profs[i].cal->xpi.copyright != NULL) + printf("Copyright: '%s'\n",su.profs[i].cal->xpi.copyright); + } + + if (i == 0 && su.icombine) + printf("Input curves being combined\n"); + if (i == 0 && su.ilcurve) + printf("Input curves being post-converted to L*\n"); + printf("Input space = %s\n",icm2str(icmColorSpaceSignature, su.profs[i].ins)); + printf("Output space = %s\n",icm2str(icmColorSpaceSignature, su.profs[i].outs)); + if (i == (su.last) && su.olcurve) + printf("Output curves being pre-converted from L*\n"); + if (i == (su.last) && su.ocombine) + printf("Output curves being combined\n"); + printf("\n"); + } + + if (wh != NULL) { + printf("Output TIFF file '%s'\n",out_name); + printf("Ouput raster file ICC colorspace is %s\n",icm2str(icmColorSpaceSignature,su.outs)); + printf("Output TIFF file photometric is %s\n",Photometric2str(wphotometric)); + } else { + printf("Output JPEG file '%s'\n",out_name); + printf("Ouput raster file ICC colorspace is %s\n",icm2str(icmColorSpaceSignature,su.outs)); + printf("Output JPEG file colorspace is %s\n",JPEG_cspace2str(wj.jpeg_color_space)); + if (wdesc != NULL) + printf("Output raster file description: '%s'\n",wdesc); + } + printf("\n"); + } + + /* - - - - - - - - - - - - - - - */ + /* Setup the imdi */ + + if (check) + doimdi = dofloat = 1; + + if (doimdi && su.nprofs > 0) { + int aclutres = 0; /* Automatically set res */ + imdi_options opts = opts_none; + + if (rextrasamples > 0) { /* We need to skip the alpha */ + opts |= opts_istride; + } + + /* Setup the imdi resolution */ + /* Choose the resolution from the highest lut resolution in the sequence, */ + /* or choose a default. */ + for (i = su.first; i <= su.last; i++) { + if (su.profs[i].c != NULL + && su.profs[i].clutres > aclutres) + aclutres = su.profs[i].clutres; + } + if (aclutres == 0) { + aclutres = dim_to_clutres(su.id, 2); /* High quality */ + + } else if (aclutres < dim_to_clutres(su.id, 1)) { /* Worse than medium */ + aclutres = dim_to_clutres(su.id, 1); + } + + if (clutres == 0) + clutres = aclutres; + + if (su.verb) + printf("Using CLUT resolution %d\n",clutres); + + s = new_imdi( + su.id, /* Number of input dimensions */ + su.od, /* Number of output dimensions */ + /* Input pixel representation */ + bitspersample == 8 ? pixint8 : pixint16, + /* Output pixel representation */ + su.isign_mask, /* Treat appropriate channels as signed */ + NULL, /* No raster to callback channel mapping */ + prec_min, /* Minimum of input and output precision */ + bitspersample == 8 ? pixint8 : pixint16, + su.osign_mask, /* Treat appropriate channels as signed */ + NULL, /* No raster to callback channel mapping */ + clutres, /* Desired table resolution */ + oopts_none, /* Desired per channel output options */ + NULL, /* Output channel check values */ + opts, /* Desired processing direction and stride support */ + input_curves, /* Callback functions */ + md_table, + output_curves, + (void *)&su /* Context to callbacks */ + ); + + if (s == NULL) { + #ifdef NEVER + printf("id = %d\n",su.id); + printf("od = %d\n",su.od); + printf("in bps = %d\n",bitspersample); + printf("out bps = %d\n",bitspersample); + printf("in signs = %d\n",su.isign_mask); + printf("out signs = %d\n",su.osign_mask); + printf("clutres = %d\n",clutres); + #endif + error("new_imdi failed"); + } + } + + if (rh != NULL) + inbuf = _TIFFmalloc(TIFFScanlineSize(rh)); + else { + inbpix = rj.output_width * rj.num_components; + if ((inbuf = (tdata_t *)malloc(inbpix)) == NULL) + error("Malloc failed on input line buffer"); + } + + if (wh != NULL) + outbuf = _TIFFmalloc(TIFFScanlineSize(wh)); + else { + outbpix = wj.image_width * wj.input_components; + if ((outbuf = (tdata_t *)malloc(outbpix)) == NULL) + error("Malloc failed on output line buffer"); + } + + inp[0] = (unsigned char *)inbuf; + outp[0] = (unsigned char *)outbuf; + + if (dofloat || su.nprofs == 0) { + if (wh != NULL) + hprecbuf = _TIFFmalloc(TIFFScanlineSize(wh)); + else { + if ((hprecbuf = (tdata_t *)malloc(outbpix)) == NULL) + error("Malloc failed on high precision line buffer"); + } + } + + if (rh == NULL) { + if (setjmp(jpeg_rerr.env)) { + /* Something went wrong with reading the file */ + jpeg_destroy_decompress(&rj); + error("failed to read JPEG line [%s]",jpeg_rerr.message); + } + } + + if (wh == NULL) { + if (setjmp(jpeg_werr.env)) { + /* Something went wrong with writing the file */ + jpeg_destroy_compress(&wj); + error("failed to write JPEG line [%s]", jpeg_werr.message); + } + } + + + /* - - - - - - - - - - - - - - - */ + /* Process colors to translate */ + /* (Should fix this to process a group of lines at a time ?) */ + + for (y = 0; y < height; y++) { + tdata_t *obuf; + + /* Read in the next line */ + if (rh) { + if (TIFFReadScanline(rh, inbuf, y, 0) < 0) + error ("Failed to read TIFF line %d",y); + } else { + jpeg_read_scanlines(&rj, (JSAMPARRAY)&inbuf, 1); + if (su.iinv) { + unsigned char *cp, *ep = (unsigned char *)inbuf + inbpix; + for (cp = (unsigned char *)inbuf; cp < ep; cp++) + *cp = ~*cp; + } + } + + if (doimdi && su.nprofs > 0) { + /* Do fast conversion */ + s->interp(s, (void **)outp, 0, (void **)inp, su.id, width); + } + + if (dofloat || su.nprofs == 0) { + /* Do floating point conversion into the hprecbuf[] */ + for (x = 0; x < width; x++) { + int i; + double in[MAX_CHAN], out[MAX_CHAN]; + +//printf("\n"); + if (bitspersample == 8) { + for (i = 0; i < su.id; i++) { + int v = ((unsigned char *)inbuf)[x * su.id + i]; +//printf("~1 8 bit pixel value chan %d = %d\n",i,v); + if (su.isign_mask & (1 << i)) /* Treat input as signed */ + v = (v & 0x80) ? v - 0x80 : v + 0x80; +//printf("~1 8 bit after treat as signed chan %d = %d\n",i,v); + in[i] = v/255.0; +//printf("~1 8 bit fp chan %d value = %f\n",i,in[i]); + } + } else { + for (i = 0; i < su.id; i++) { + int v = ((unsigned short *)inbuf)[x * su.id + i]; +//printf("~1 16 bit pixel value chan %d = %d\n",i,v); + if (su.isign_mask & (1 << i)) /* Treat input as signed */ + v = (v & 0x8000) ? v - 0x8000 : v + 0x8000; +//printf("~1 16 bit after treat as signed chan %d = %d\n",i,v); + in[i] = v/65535.0; +//printf("~1 16 bit fp chan %d value = %f\n",i,in[i]); + } + } + + if (su.nprofs > 0) { + /* Apply the reference conversion */ + input_curves((void *)&su, out, in); +//for (i = 0; i < su.id; i++) printf("~1 after input curve chan %d = %f\n",i,out[i]); + md_table((void *)&su, out, out); +//for (i = 0; i < su.od; i++) printf("~1 after md table chan %d = %f\n",i,out[i]); + output_curves((void *)&su, out, out); +//for (i = 0; i < su.od; i++) printf("~1 after output curve chan %d = %f\n",i,out[i]); + } else { + for (i = 0; i < su.od; i++) + out[i] = in[i]; + } + + if (bitspersample == 8) { + for (i = 0; i < su.od; i++) { + int v = (int)(out[i] * 255.0 + 0.5); +//printf("~1 8 bit chan %d = %d\n",i,v); + if (v < 0) + v = 0; + else if (v > 255) + v = 255; +//printf("~1 8 bit after clip curve chan %d = %d\n",i,v); + if (su.osign_mask & (1 << i)) /* Treat input as offset */ + v = (v & 0x80) ? v - 0x80 : v + 0x80; +//printf("~1 8 bit after treat as offset chan %d = %d\n",i,v); + ((unsigned char *)hprecbuf)[x * su.od + i] = v; + } + } else { + for (i = 0; i < su.od; i++) { + int v = (int)(out[i] * 65535.0 + 0.5); +//printf("~1 16 bit chan %d = %d\n",i,v); + if (v < 0) + v = 0; + else if (v > 65535) + v = 65535; +//printf("~1 16 bit after clip curve chan %d = %d\n",i,v); + if (su.osign_mask & (1 << i)) /* Treat input as offset */ + v = (v & 0x8000) ? v - 0x8000 : v + 0x8000; +//printf("~1 16 bit after treat as offset chan %d = %d\n",i,v); + ((unsigned short *)hprecbuf)[x * su.od + i] = v; + } + } + } + + if (check) { + /* Compute the errors */ + for (x = 0; x < (width * su.od); x++) { + int err; + if (bitspersample == 8) + err = ((unsigned char *)outbuf)[x] - ((unsigned char *)hprecbuf)[x]; + else + err = ((unsigned short *)outbuf)[x] - ((unsigned short *)hprecbuf)[x]; + if (err < 0) + err = -err; + if (err > mxerr) + mxerr = err; + avgerr += (double)err; + avgcount++; + } + } + } + + if (dofloat || su.nprofs == 0) /* Use the results of the f.p. conversion */ + obuf = hprecbuf; + else + obuf = outbuf; + + if (wh != NULL) { + if (TIFFWriteScanline(wh, obuf, y, 0) < 0) + error ("Failed to write TIFF line %d",y); + } else { + if (su.oinv) { + unsigned char *cp, *ep = (unsigned char *)obuf + outbpix; + for (cp = (unsigned char *)obuf; cp < ep; cp++) + *cp = ~(*cp); + } + jpeg_write_scanlines(&wj, (JSAMPARRAY)&obuf, 1); + } + } + + if (check) { + printf("Worst error = %d bits, average error = %f bits\n", mxerr, avgerr/avgcount); + if (bitspersample == 8) + printf("Worst error = %f%%, average error = %f%%\n", + mxerr/2.55, avgerr/(2.55 * avgcount)); + else + printf("Worst error = %f%%, average error = %f%%\n", + mxerr/655.35, avgerr/(655.35 * avgcount)); + } + + + /* Release buffers and close files */ + if (rh != NULL) { + if (inbuf != NULL) + _TIFFfree(inbuf); + TIFFClose(rh); /* Close Input file */ + } else { + jpeg_finish_decompress(&rj); + jpeg_destroy_decompress(&rj); + if (inbuf != NULL) + free(inbuf); + if (fclose(rf)) + error("Error closing JPEG input file '%s'\n",in_name); + } + + if (wh != NULL) { + if (outbuf != NULL) + _TIFFfree(outbuf); + if (hprecbuf != NULL) + _TIFFfree(hprecbuf); + TIFFClose(wh); + } else { + jpeg_finish_compress(&wj); + jpeg_destroy_compress(&wj); + if (outbuf != NULL) + free(outbuf); + if (hprecbuf != NULL) + free(hprecbuf); + if (fclose(wf)) + error("Error closing output file '%s'\n",out_name); + } + + /* Done with lookup object */ + if (s != NULL) + s->del(s); + + /* Free up all the profiles etc. in the sequence. */ + for (i = 0; i < su.nprofs; i++) { + if (su.profs[i].c != NULL) { /* Has an ICC profile */ + su.profs[i].luo->del(su.profs[i].luo); /* Lookup */ + su.profs[i].c->del(su.profs[i].c); + } else { + su.profs[i].cal->del(su.profs[i].cal); /* Calibration */ + } + } + + if (rdesc != NULL) + free(rdesc); + if (wdesc != NULL) + free(wdesc); + + return 0; +} + diff --git a/imdi/cctiffo.c b/imdi/cctiffo.c new file mode 100644 index 0000000..c155e03 --- /dev/null +++ b/imdi/cctiffo.c @@ -0,0 +1,1097 @@ + +/* + * Color Correct a TIFF file, using an ICC Device link profile. + * + * Author: Graeme W. Gill + * Date: 00/3/8 + * Version: 1.30 + * + * Copyright 2000 - 2004 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. + */ + +/* + * Thanks to Neil Okamoto for the 16 bit TIFF mods. + */ + +/* TTBD: + */ + +/* + This program is a framework that exercises the + IMDI code, as well as a demonstration of simple + profile linking. It can also do the conversion using the + floating point code in ICCLIB as a reference. + + */ + +#include +#include +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#include "tiffio.h" +#include "icc.h" +#include "imdi.h" + +#undef TREAT_CMY_AS_RGB + +void error(char *fmt, ...), warning(char *fmt, ...); + +void usage(void) { + fprintf(stderr,"Color Correct a TIFF file using an ICC device link profile, V%s\n",ARGYLL_VERSION_STR); + fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); + fprintf(stderr,"usage: cctiff [-options] devlinkprofile.icm infile.tif outfile.tif\n"); + fprintf(stderr,"usage: cctiff [-options] -l inprofile.icm outprofile.icm infile.tif outfile.tif\n"); + fprintf(stderr," -v Verbose\n"); + fprintf(stderr," -c Combine linearisation curves into one transform\n"); + fprintf(stderr," -p Use slow precise correction\n"); + fprintf(stderr," -k Check fast result against precise, and report\n"); + fprintf(stderr," -l Link input and output profiles\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"); + exit(1); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Convert an ICC colorspace to the corresponding possible TIFF Photometric tags. */ +/* Return the number of matching tags, and 0 if there is no corresponding tag. */ +int +ColorSpaceSignature2TiffPhotometric( +uint16 tags[10], /* Pointer to return array, up to 10 */ +icColorSpaceSignature cspace /* Input ICC colorspace */ +) { + switch(cspace) { + case icSigGrayData: + tags[0] = PHOTOMETRIC_MINISBLACK; + return 1; + case icSigRgbData: +#ifdef TREAT_CMY_AS_RGB + case icSigCmyData: +#endif + tags[0] = PHOTOMETRIC_RGB; + return 1; +#ifndef TREAT_CMY_AS_RGB + case icSigCmyData: +#endif + case icSigCmykData: + tags[0] = PHOTOMETRIC_SEPARATED; + return 1; + case icSigYCbCrData: + tags[0] = PHOTOMETRIC_YCBCR; + return 1; + case icSigLabData: + tags[0] = PHOTOMETRIC_CIELAB; +#ifdef PHOTOMETRIC_ICCLAB + tags[1] = PHOTOMETRIC_ICCLAB; + tags[2] = PHOTOMETRIC_ITULAB; +#endif + return 3; + + case icSigXYZData: + case icSigLuvData: + case icSigYxyData: + case icSigHsvData: + case icSigHlsData: + return 0; + + case icSig2colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 2; /* Cheat */ + return 1; + + case icSig3colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 3; /* Cheat */ + return 1; + + case icSig4colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 4; /* Cheat */ + return 1; + + case icSig5colorData: + case icSigMch5Data: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 5; /* Cheat */ + return 1; + + case icSig6colorData: + case icSigMch6Data: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 6; /* Cheat */ + return 1; + + case icSig7colorData: + case icSigMch7Data: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 7; /* Cheat */ + return 1; + + case icSig8colorData: + case icSigMch8Data: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 8; /* Cheat */ + return 1; + + case icSig9colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 9; /* Cheat */ + return 1; + + case icSig10colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 10; /* Cheat */ + return 1; + + case icSig11colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 11; /* Cheat */ + return 1; + + case icSig12colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 12; /* Cheat */ + return 1; + + case icSig13colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 13; /* Cheat */ + return 1; + + case icSig14colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 14; /* Cheat */ + return 1; + + case icSig15colorData: + tags[0] = PHOTOMETRIC_SEPARATED; + tags[1] = 15; /* Cheat */ + return 1; + + default: + return 0; + } + return 0; +} + + +/* Compute the length of a double nul terminated string, including */ +/* the nuls. */ +static int zzstrlen(char *s) { + int i; + for (i = 0;; i++) { + if (s[i] == '\000' && s[i+1] == '\000') + return i+2; + } + return 0; +} + +/* Convert an ICC colorspace to the corresponding TIFF Inkset tag */ +/* return 0xffff if not possible or applicable. */ + +int +ColorSpaceSignature2TiffInkset( +icColorSpaceSignature cspace, +int *len, /* Return length of ASCII inknames */ +char **inknames /* Return ASCII inknames if non NULL */ +) { + switch(cspace) { + case icSigCmyData: + return 0xffff; // ~~9999 + if (inknames != NULL) { + *inknames = "cyan\000magenta\000yellow\000\000"; + *len = zzstrlen(*inknames); + } + return 0; /* Not CMYK */ + case icSigCmykData: + if (inknames != NULL) { + *inknames = NULL; /* No inknames */ + *len = 0; + } + return INKSET_CMYK; + + case icSigGrayData: + case icSigRgbData: + case icSigYCbCrData: + case icSigLabData: + case icSigXYZData: + case icSigLuvData: + case icSigYxyData: + case icSigHsvData: + case icSigHlsData: + case icSig2colorData: + case icSig3colorData: + case icSig4colorData: + case icSig5colorData: + case icSigMch5Data: + return 0xffff; + + case icSig6colorData: + case icSigMch6Data: + /* This is a cheat and a hack. Should really make use of the */ + /* ColorantTable to determine the colorant names. */ + /* allowing cctiff to read it. */ + if (inknames != NULL) { + *inknames = "cyan\000magenta\000yellow\000black\000orange\000green\000\000"; + *len = zzstrlen(*inknames); + } + return 0; /* Not CMYK */ + + case icSig7colorData: + case icSigMch7Data: + return 0xffff; + + case icSig8colorData: + case icSigMch8Data: + /* This is a cheat and a hack. Should really make use of the */ + /* ColorantTable to determine the colorant names. */ + /* allowing cctiff to read it. */ + if (inknames != NULL) { + *inknames = "cyan\000magenta\000yellow\000black\000orange\000green\000lightcyan\000lightmagenta\000\000"; + *len = zzstrlen(*inknames); + } + return 0; /* Not CMYK */ + case icSig9colorData: + case icSig10colorData: + case icSig11colorData: + case icSig12colorData: + case icSig13colorData: + case icSig14colorData: + case icSig15colorData: + default: + return 0xffff; + } + return 0xffff; +} + +char * +Photometric2str( +int pmtc +) { + static char buf[80]; + switch (pmtc) { + case PHOTOMETRIC_MINISWHITE: + return "Subtractive Gray"; + case PHOTOMETRIC_MINISBLACK: + return "Additive Gray"; + case PHOTOMETRIC_RGB: + return "RGB"; + case PHOTOMETRIC_PALETTE: + return "Indexed"; + case PHOTOMETRIC_MASK: + return "Transparency Mask"; + case PHOTOMETRIC_SEPARATED: + return "Separated"; + case PHOTOMETRIC_YCBCR: + return "YCbCr"; + case PHOTOMETRIC_CIELAB: + return "CIELab"; +#ifdef PHOTOMETRIC_ICCLAB + case PHOTOMETRIC_ICCLAB: + return "ICCLab"; + case PHOTOMETRIC_ITULAB: + return "ITULab"; +#endif + case PHOTOMETRIC_LOGL: + return "CIELog2L"; + case PHOTOMETRIC_LOGLUV: + return "CIELog2Luv"; + } + sprintf(buf,"Unknonw Tag %d",pmtc); + return buf; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Callbacks used to initialise imdi */ + +/* Information needed from a profile */ +struct _profinfo { + char name[100]; + icmFile *fp; + icc *c; + icmHeader *h; + icRenderingIntent intent; + icmLuBase *luo; /* Base Lookup type object */ + icmLuAlgType alg; /* Type of lookup algorithm */ + int chan; /* Device channels */ +}; typedef struct _profinfo profinfo; + +/* Context for imdi setup callbacks */ +typedef struct { + /* Overall parameters */ + int verb; /* Non-zero if verbose */ + icColorSpaceSignature ins, outs; /* Input/Output spaces */ + int id, od; /* Input/Output dimensions */ + int icombine; /* Non-zero if input curves are to be combined */ + int ocombine; /* Non-zero if output curves are to be combined */ + int dolink; /* Non-zero if input and output profiles are to be linked */ + + profinfo dev; /* Device link profile */ + profinfo in; /* Device to PCS profile */ + profinfo out; /* PCS to Device profile */ +} sucntx; + +/* Input curve function */ +static void input_curves( + void *cntx, + double *out_vals, + double *in_vals +) { + sucntx *rx = (sucntx *)cntx; + +//printf("~1 incurve in %f %f %f %f\n",in_vals[0],in_vals[1],in_vals[2],in_vals[3]); + if (rx->icombine) { + int i; + for (i = 0; i < rx->id; i++) + out_vals[i] = in_vals[i]; + } else { + if (rx->dolink) { /* Two ICC profiles */ + rx->in.luo->lookup_in(rx->in.luo, out_vals, in_vals); + } else { /* Device link */ + rx->dev.luo->lookup_in(rx->dev.luo, out_vals, in_vals); + } + } +//printf("~1 incurve out %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]); +} + +/* Multi-dim table function */ +static void md_table( +void *cntx, +double *out_vals, +double *in_vals +) { + sucntx *rx = (sucntx *)cntx; + double vals[MAX_CHAN]; + +//printf("~1 md_table in %f %f %f %f\n",in_vals[0],in_vals[1],in_vals[2]); + if (rx->dolink) { /* Two ICC profiles */ + + if (rx->icombine) { + rx->in.luo->lookup_in(rx->in.luo, vals, in_vals); + rx->in.luo->lookup_core(rx->in.luo, vals, vals); + } else { + rx->in.luo->lookup_core(rx->in.luo, vals, in_vals); + } + rx->in.luo->lookup_out(rx->in.luo, vals, vals); + rx->out.luo->lookup_in(rx->out.luo, vals, vals); + rx->out.luo->lookup_core(rx->out.luo, out_vals, vals); + if (rx->ocombine) + rx->out.luo->lookup_out(rx->out.luo, out_vals, out_vals); + + } else { /* Device link */ + + if (rx->icombine) { + rx->dev.luo->lookup_in(rx->dev.luo, vals, in_vals); + rx->dev.luo->lookup_core(rx->dev.luo, out_vals, vals); + } else { + rx->dev.luo->lookup_core(rx->dev.luo, out_vals, in_vals); + } + if (rx->ocombine) + rx->dev.luo->lookup_out(rx->dev.luo, out_vals, out_vals); + } +//printf("~1 md_table returns %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]); +} + +/* Output curve function */ +static void output_curves( + void *cntx, + double *out_vals, + double *in_vals +) { + sucntx *rx = (sucntx *)cntx; + +//printf("~1 outurve in %f %f %f %f\n",in_vals[0],in_vals[1],in_vals[2],in_vals[3]); + if (rx->ocombine) { + int i; + for (i = 0; i < rx->od; i++) + out_vals[i] = in_vals[i]; + } else { + if (rx->dolink) { /* Two ICC profiles */ + rx->out.luo->lookup_out(rx->out.luo, out_vals, in_vals); + } else { /* Device link */ + rx->dev.luo->lookup_out(rx->dev.luo, out_vals, in_vals); + } + } +//printf("~1 outurve out %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +int +main(int argc, char *argv[]) { + int fa,nfa; /* argument we're looking at */ + char in_name[100]; /* Raster file name */ + char out_name[100]; /* Raster file name */ + int slow = 0; + int check = 0; + int i, rv = 0; + + TIFF *rh = NULL, *wh = NULL; + int x, y, width, height; /* Size of image */ + uint16 samplesperpixel, bitspersample; + int no_pmtc; /* Number of input photometrics */ + uint16 photometric, pmtc[10]; /* Photometrics of input file, and input profile */ + uint16 pconfig; /* Planar configuration */ + uint16 resunits; + float resx, resy; + tdata_t *inbuf, *outbuf, *checkbuf = NULL; + + /* IMDI */ + imdi *s = NULL; + sucntx su; /* Setup context */ + unsigned char *inp[MAX_CHAN]; + unsigned char *outp[MAX_CHAN]; + int clutres = 33; + + /* Error check */ + int mxerr = 0; + double avgerr = 0.0; + double avgcount = 0.0; + + if (argc < 2) + usage(); + + su.verb = 0; + su.icombine = 0; + su.ocombine = 0; + su.dolink = 0; + su.in.intent = icmDefaultIntent; + su.out.intent = icmDefaultIntent; + + /* 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 */ + } + } + } + + if (argv[fa][1] == '?') + usage(); + + /* Slow, Precise */ + else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') { + slow = 1; + } + + /* Combine per channel curves */ + else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') { + su.icombine = 1; + su.ocombine = 1; + } + + /* Check curves */ + else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') { + check = 1; + } + + /* Link profiles */ + else if (argv[fa][1] == 'l' || argv[fa][1] == 'L') { + su.dolink = 1; + } + + /* Input profile Intent */ + else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') { + fa = nfa; + if (na == NULL) usage(); + switch (na[0]) { + case 'p': + case 'P': + su.in.intent = icPerceptual; + break; + case 'r': + case 'R': + su.in.intent = icRelativeColorimetric; + break; + case 's': + case 'S': + su.in.intent = icSaturation; + break; + case 'a': + case 'A': + su.in.intent = icAbsoluteColorimetric; + break; + default: + usage(); + } + } + + /* Output profile Intent */ + else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') { + fa = nfa; + if (na == NULL) usage(); + switch (na[0]) { + case 'p': + case 'P': + su.out.intent = icPerceptual; + break; + case 'r': + case 'R': + su.out.intent = icRelativeColorimetric; + break; + case 's': + case 'S': + su.out.intent = icSaturation; + break; + case 'a': + case 'A': + su.out.intent = icAbsoluteColorimetric; + break; + default: + usage(); + } + } + + /* Verbosity */ + else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') { + su.verb = 1; + } + + else + usage(); + } else + break; + } + + if (su.dolink) { + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(su.in.name,argv[fa++]); + + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(su.out.name,argv[fa++]); + } else { + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(su.dev.name,argv[fa++]); + } + + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(in_name,argv[fa++]); + + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(out_name,argv[fa++]); + + /* - - - - - - - - - - - - - - - - */ + + if (su.dolink) { + icColorSpaceSignature natpcs; + + /* Open up the input device profile for reading */ + if ((su.in.fp = new_icmFileStd_name(su.in.name,"r")) == NULL) + error ("Can't open file '%s'",su.in.name); + + if ((su.in.c = new_icc()) == NULL) + error ("Creation of Input profile ICC object failed"); + + /* Read header etc. */ + if ((rv = su.in.c->read(su.in.c,su.in.fp,0)) != 0) + error ("%d, %s on file '%s'",rv,su.in.c->err,su.in.name); + su.in.h = su.in.c->header; + + /* Check that it is a suitable device input icc */ + if (su.in.h->deviceClass != icSigInputClass + && su.in.h->deviceClass != icSigDisplayClass + && su.in.h->deviceClass != icSigOutputClass + && su.in.h->deviceClass != icSigColorSpaceClass) /* For sRGB etc. */ + error("Input profile isn't a device profile"); + + /* Get a conversion object */ + if ((su.in.luo = su.in.c->get_luobj(su.in.c, icmFwd, su.in.intent, + icSigLabData, icmLuOrdNorm)) == NULL) + error ("%d, %s for profile '%s'",su.in.c->errc, su.in.c->err, su.in.name); + + /* Get details of conversion (Arguments may be NULL if info not needed) */ + su.in.luo->spaces(su.in.luo, &su.ins, &su.id, NULL, NULL, &su.in.alg, NULL, NULL, NULL); + + /* Get native PCS space */ + su.in.luo->lutspaces(su.in.luo, NULL, NULL, NULL, NULL, &natpcs); + + if (natpcs == icSigXYZData) { + su.icombine = 1; /* XYZ is to non-linear to be a benefit */ + } + + /* Open up the output device profile for reading */ + if ((su.out.fp = new_icmFileStd_name(su.out.name,"r")) == NULL) + error ("Can't open file '%s'",su.out.name); + + if ((su.out.c = new_icc()) == NULL) + error ("Creation of Output profile ICC object failed"); + + /* Read header etc. */ + if ((rv = su.out.c->read(su.out.c,su.out.fp,0)) != 0) + error ("%d, %s on file '%s'",rv,su.out.c->err,su.out.name); + su.out.h = su.out.c->header; + + /* Check that it is a suitable device output icc */ + if (su.out.h->deviceClass != icSigInputClass + && su.out.h->deviceClass != icSigDisplayClass + && su.out.h->deviceClass != icSigOutputClass + && su.out.h->deviceClass != icSigColorSpaceClass) /* For sRGB etc. */ + error("Output profile isn't a device profile"); + + /* Get a conversion object */ + if ((su.out.luo = su.out.c->get_luobj(su.out.c, icmBwd, su.out.intent, + icSigLabData, icmLuOrdNorm)) == NULL) + error ("%d, %s for profile '%s'",su.out.c->errc, su.out.c->err, su.out.name); + + /* Get details of conversion (Arguments may be NULL if info not needed) */ + su.out.luo->spaces(su.out.luo, NULL, NULL, &su.outs, &su.od, &su.out.alg, NULL, NULL, NULL); + + /* Get native PCS space */ + su.out.luo->lutspaces(su.out.luo, NULL, NULL, NULL, NULL, &natpcs); + + if (natpcs == icSigXYZData) { + su.ocombine = 1; /* XYZ is to non-linear to be a benefit */ + } + + /* See discussion in imdi/imdi_gen.c for ideal numbers */ + /* Use "high quality" resolution numbers */ + switch (su.id) { + case 0: + error ("Illegal number of input chanels"); + case 1: + clutres = 256; + break; + case 2: + clutres = 256; + break; + case 3: + clutres = 33; + break; + case 4: + clutres = 18; + break; + case 5: + clutres = 16; + break; + case 6: + clutres = 9; + break; + case 7: + clutres = 7; + break; + case 8: + clutres = 6; + break; + default: /* > 8 chan */ + clutres = 3; + break; + } + + } else { + icmLut *lut; /* ICC LUT table */ + icmLuLut *luluo; /* LUT lookup object */ + + /* Open up the device link profile for reading */ + if ((su.dev.fp = new_icmFileStd_name(su.dev.name,"r")) == NULL) + error ("Can't open file '%s'",su.dev.name); + + if ((su.dev.c = new_icc()) == NULL) + error ("Creation of ICC object failed"); + + if ((rv = su.dev.c->read(su.dev.c, su.dev.fp, 0)) != 0) + error ("%d, %s",rv,su.dev.c->err); + su.dev.h = su.dev.c->header; + + if (su.verb) { + icmFile *op; + if ((op = new_icmFileStd_fp(stdout)) == NULL) + error ("Can't open stdout"); + su.dev.h->dump(su.dev.h, op, 1); + op->del(op); + } + + /* Check that the profile is appropriate */ + if (su.dev.h->deviceClass != icSigLinkClass) + error("Profile isn't a device link profile"); + + /* Get a conversion object */ + if ((su.dev.luo = su.dev.c->get_luobj(su.dev.c, icmFwd, icmDefaultIntent, + icmSigDefaultData, icmLuOrdNorm)) == NULL) + error ("%d, %s",su.dev.c->errc, su.dev.c->err); + + /* Get details of conversion (Arguments may be NULL if info not needed) */ + su.dev.luo->spaces(su.dev.luo, &su.ins, &su.id, &su.outs, &su.od, &su.dev.alg, NULL, NULL, NULL); + + if (su.dev.alg != icmLutType) + error ("DeviceLink profile doesn't have Lut !"); + + luluo = (icmLuLut *)su.dev.luo; /* Safe to coerce */ + luluo->get_info(luluo, &lut, NULL, NULL, NULL); /* Get some details */ + clutres = lut->clutPoints; /* Desired table resolution */ + } + + /* - - - - - - - - - - - - - - - */ + /* Open up input tiff file ready for reading */ + /* Got arguments, so setup to process the file */ + if ((rh = TIFFOpen(in_name, "r")) == NULL) + error("error opening read file '%s'",in_name); + + TIFFGetField(rh, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetField(rh, TIFFTAG_IMAGELENGTH, &height); + + TIFFGetField(rh, TIFFTAG_BITSPERSAMPLE, &bitspersample); + if (bitspersample != 8 && bitspersample != 16) { + error("TIFF Input file must be 8 or 16 bit/channel"); + } + + TIFFGetField(rh, TIFFTAG_PHOTOMETRIC, &photometric); + if ((no_pmtc = ColorSpaceSignature2TiffPhotometric(pmtc, su.ins)) == 0) + error("ICC input colorspace '%s' can't be handled by a TIFF file!", + icm2str(icmColorSpaceSignature, su.ins)); + for (i = 0; i < no_pmtc; i++) { + if (pmtc[i] == photometric) + break; /* Matches */ + } + if (i >= no_pmtc) { + /* These error reports are a bit sloppy */ + switch (no_pmtc) { + case 1: + error("TIFF colorspace '%s' doesn't match ICC colorspace '%s'!", + Photometric2str(photometric), Photometric2str(pmtc[0])); + case 2: + error("TIFF colorspace '%s' doesn't match ICC colorspace '%s' or '%s'!", + Photometric2str(photometric), Photometric2str(pmtc[0]), + Photometric2str(pmtc[1])); + default: + error("TIFF colorspace '%s' doesn't match ICC colorspace '%s', '%s' or '%s'!", + Photometric2str(photometric), Photometric2str(pmtc[0]), + Photometric2str(pmtc[1]), Photometric2str(pmtc[2])); + } + } + + TIFFGetField(rh, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel); + if (su.id != samplesperpixel) + error ("TIFF Input file has %d input channels mismatched to colorspace '%s'", + samplesperpixel, icm2str(icmColorSpaceSignature, su.ins)); + + TIFFGetField(rh, TIFFTAG_PLANARCONFIG, &pconfig); + if (pconfig != PLANARCONFIG_CONTIG) + error ("TIFF Input file must be planar"); + + TIFFGetField(rh, TIFFTAG_RESOLUTIONUNIT, &resunits); + TIFFGetField(rh, TIFFTAG_XRESOLUTION, &resx); + TIFFGetField(rh, TIFFTAG_YRESOLUTION, &resy); + + /* - - - - - - - - - - - - - - - */ + if ((wh = TIFFOpen(out_name, "w")) == NULL) + error("Can\'t create TIFF file '%s'!",out_name); + + TIFFSetField(wh, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(wh, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(wh, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(wh, TIFFTAG_SAMPLESPERPIXEL, su.od); + TIFFSetField(wh, TIFFTAG_BITSPERSAMPLE, bitspersample); + TIFFSetField(wh, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + if ((no_pmtc = ColorSpaceSignature2TiffPhotometric(pmtc, su.outs)) == 0) + error("TIFF file can't handle output colorspace '%s'!", + icm2str(icmColorSpaceSignature, su.outs)); + TIFFSetField(wh, TIFFTAG_PHOTOMETRIC, pmtc[0]); /* Use first returned */ + if (pmtc[0] == PHOTOMETRIC_SEPARATED) { + int iset; + int inlen; + char *inames; + iset = ColorSpaceSignature2TiffInkset(su.outs, &inlen, &inames); + if (iset != 0xffff && inlen > 0 && inames != NULL) { + TIFFSetField(wh, TIFFTAG_INKSET, iset); + if (inames != NULL) { + TIFFSetField(wh, TIFFTAG_INKNAMES, inlen, inames); + } + } + } + TIFFSetField(wh, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + if (resunits) { + TIFFSetField(wh, TIFFTAG_RESOLUTIONUNIT, resunits); + TIFFSetField(wh, TIFFTAG_XRESOLUTION, resx); + TIFFSetField(wh, TIFFTAG_YRESOLUTION, resy); + } + TIFFSetField(wh, TIFFTAG_IMAGEDESCRIPTION, "Color corrected by Argyll"); + + /* - - - - - - - - - - - - - - - */ + /* Setup the imdi */ + + if (!slow) { + s = new_imdi( + su.id, /* Number of input dimensions */ + su.od, /* Number of output dimensions */ + /* Input pixel representation */ + bitspersample == 8 ? pixint8 : pixint16, + /* Output pixel representation */ + 0x0, /* Treat every channel as unsigned */ + NULL, /* No raster to callback mapping */ + prec_min, /* Minimum of input and output precision */ + bitspersample == 8 ? pixint8 : pixint16, + 0x0, /* Treat every channel as unsigned */ + NULL, /* No raster to callback mapping */ + clutres, /* Desired table resolution */ + oopts_none, /* Desired per channel output options */ + NULL, /* Output channel check values */ + opts_none, /* Desired processing direction and stride support */ + input_curves, /* Callback functions */ + md_table, + output_curves, + (void *)&su /* Context to callbacks */ + ); + + if (s == NULL) + error("new_imdi failed"); + } + + /* - - - - - - - - - - - - - - - */ + /* Process colors to translate */ + /* (Should fix this to process a group of lines at a time ?) */ + + inbuf = _TIFFmalloc(TIFFScanlineSize(rh)); + outbuf = _TIFFmalloc(TIFFScanlineSize(wh)); + if (check) + checkbuf = _TIFFmalloc(TIFFScanlineSize(wh)); + + inp[0] = (unsigned char *)inbuf; + outp[0] = (unsigned char *)outbuf; + + if (!slow) { /* Fast */ + for (y = 0; y < height; y++) { + + /* Read in the next line */ + if (TIFFReadScanline(rh, inbuf, y, 0) < 0) + error ("Failed to read TIFF line %d",y); + + /* Do fast conversion */ + s->interp(s, (void **)outp, 0, (void **)inp, 0, width); + + if (check) { + /* Do floating point conversion */ + for (x = 0; x < width; x++) { + int i; + double in[MAX_CHAN], out[MAX_CHAN]; + + if (bitspersample == 8) + for (i = 0; i < su.id; i++) + in[i] = ((unsigned char *)inbuf)[x * su.id + i]/255.0; + else + for (i = 0; i < su.id; i++) + in[i] = ((unsigned short *)inbuf)[x * su.id + i]/65535.0; + +#ifdef NEVER + if (su.dolink) { + if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1) + error ("%d, %s",su.dev.c->errc,su.dev.c->err); + if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1) + error ("%d, %s",su.dev.c->errc,su.dev.c->err); + } else { + if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1) + error ("%d, %s",su.dev.c->errc,su.dev.c->err); + } +#else + /* Apply the reference conversion */ + input_curves((void *)&su, out, in); + md_table((void *)&su, out, out); + output_curves((void *)&su, out, out); +#endif + if (bitspersample == 8) + for (i = 0; i < su.od; i++) + ((unsigned char *)checkbuf)[x * su.od + i] = (int)(out[i] * 255.0 + 0.5); + else + for (i = 0; i < su.od; i++) + ((unsigned short *)checkbuf)[x * su.od + i] = (int)(out[i] * 65535.0 + 0.5); + } + /* Compute the errors */ + for (x = 0; x < (width * su.od); x++) { + int err; + if (bitspersample == 8) + err = ((unsigned char *)outbuf)[x] - ((unsigned char *)checkbuf)[x]; + else + err = ((unsigned short *)outbuf)[x] - ((unsigned short *)checkbuf)[x]; + if (err < 0) + err = -err; + if (err > mxerr) + mxerr = err; + avgerr += (double)err; + avgcount++; + } + } + + if (TIFFWriteScanline(wh, outbuf, y, 0) < 0) + error ("Failed to write TIFF line %d",y); + + } + + } else { /* Slow but precise */ + if (bitspersample == 8) { + for (y = 0; y < height; y++) { + + /* Read in the next line */ + if (TIFFReadScanline(rh, inbuf, y, 0) < 0) + error ("Failed to read TIFF line %d",y); + + /* Do floating point conversion */ + for (x = 0; x < width; x++) { + int i; + double in[MAX_CHAN], out[MAX_CHAN]; + + for (i = 0; i < su.id; i++) { + in[i] = ((unsigned char *)inbuf)[x * su.id + i]/255.0; + } + +#ifdef NEVER + if (su.dolink) { + if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1) + error ("%d, %s",su.dev.c->errc,su.dev.c->err); + if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1) + error ("%d, %s",su.dev.c->errc,su.dev.c->err); + } else { + if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1) + error ("%d, %s",su.dev.c->errc,su.dev.c->err); + } +#else + /* Apply the reference conversion */ + input_curves((void *)&su, out, in); + md_table((void *)&su, out, out); + output_curves((void *)&su, out, out); +#endif + + for (i = 0; i < su.od; i++) { + double outi = out[i]; + if (outi < 0.0) /* Protect against sillies */ + outi = 0.0; + else if (outi > 1.0) + outi = 1.0; + ((unsigned char *)outbuf)[x * su.od + i] = (int)(outi * 255.0 + 0.5); + } + } + if (TIFFWriteScanline(wh, outbuf, y, 0) < 0) + error ("Failed to write TIFF line %d",y); + } + } else if (bitspersample == 16) { + for (y = 0; y < height; y++) { + + /* Read in the next line */ + if (TIFFReadScanline(rh, inbuf, y, 0) < 0) + error ("Failed to read TIFF line %d",y); + + /* Do floating point conversion */ + for (x = 0; x < width; x++) { + int i; + double in[MAX_CHAN], out[MAX_CHAN]; + + for (i = 0; i < su.id; i++) { + in[i] = ((unsigned short *)inbuf)[x * su.id + i]/65535.0; + } + +#ifdef NEVER + if (su.dolink) { + if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1) + error ("%d, %s",su.dev.c->errc,su.dev.c->err); + if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1) + error ("%d, %s",su.dev.c->errc,su.dev.c->err); + } else { + if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1) + error ("%d, %s",su.dev.c->errc,su.dev.c->err); + } +#else + /* Apply the reference conversion */ + input_curves((void *)&su, out, in); + md_table((void *)&su, out, out); + output_curves((void *)&su, out, out); +#endif + + for (i = 0; i < su.od; i++) { + double outi = out[i]; + if (outi < 0.0) /* Protect against sillies */ + outi = 0.0; + else if (outi > 1.0) + outi = 1.0; + ((unsigned short *)outbuf)[x * su.od + i] = (int)(outi * 65535.0 + 0.5); + } + } + if (TIFFWriteScanline(wh, outbuf, y, 0) < 0) + error ("Failed to write TIFF line %d",y); + } + } + } + + if (check) { + printf("Worst error = %d bits, average error = %f bits\n", mxerr, avgerr/avgcount); + if (bitspersample == 8) + printf("Worst error = %f%%, average error = %f%%\n", + mxerr/2.55, avgerr/(2.55 * avgcount)); + else + printf("Worst error = %f%%, average error = %f%%\n", + mxerr/655.35, avgerr/(655.35 * avgcount)); + } + + /* Done with lookup object */ + if (s != NULL) + s->del(s); + + if (su.dolink) { + su.in.luo->del(su.in.luo); + su.in.c->del(su.in.c); + su.in.fp->del(su.in.fp); + su.out.luo->del(su.out.luo); + su.out.c->del(su.out.c); + su.out.fp->del(su.out.fp); + } else { + su.dev.luo->del(su.dev.luo); + su.dev.c->del(su.dev.c); + su.dev.fp->del(su.dev.fp); + } + + _TIFFfree(inbuf); + _TIFFfree(outbuf); + if (check) + _TIFFfree(checkbuf); + + TIFFClose(rh); /* Close Input file */ + TIFFClose(wh); /* Close Output file */ + + return 0; +} + + +/* Basic printf type error() and warning() routines */ + +void +error(char *fmt, ...) +{ + va_list args; + + fprintf(stderr,"cctiff: 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,"cctiff: Warning - "); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); +} diff --git a/imdi/cgen.c b/imdi/cgen.c new file mode 100644 index 0000000..b186dd6 --- /dev/null +++ b/imdi/cgen.c @@ -0,0 +1,2150 @@ + +/* Integer Multi-Dimensional Interpolation */ + +/* + * Copyright 2000 - 2007 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. + */ + +/* 'C' code color transform kernel code generator. */ + +/* + This module generates C code routines which implement + an integer multi-channel transform. The input values + are read, passed through per channel lookup tables, + a multi-dimentional interpolation table, and then + a per channel output lookup table, before being written. +*/ + + +#include +#include +#include +#include +#include + +#include "imdi.h" +#include "imdi_tab.h" + +#undef VERBOSE +#define INSTHRESH 4 /* Use inserion sort of di >= INSTHRESH for best performance. */ +#undef ROUND /* Round the division after accumulation */ + /* Improves accuracy at the cost of a little speed */ + +/* ------------------------------------ */ +/* Generator context */ +typedef struct { + FILE *of; /* Output file */ + int indt; /* Indent */ + + /* Other info */ + genspec *g; /* Generation specifications */ + tabspec *t; /* Table setup data */ + mach_arch *a; /* Machine architecture and tuning data */ + + /* Code generation information */ + /* if() conditions are for entry usage */ + + /* Pixel read information */ + int ipt[IXDI]; /* Input pointer types */ + int nip; /* Actual number of input pointers, accounting for pint */ + int chv_bits; /* Bits in chv temp variable ?? */ + + /* Input table entry */ + int itet; /* Input table entry type */ + int itvt; /* Input table variable type */ + int itmnb; /* Input table minimum bits (actual is it_ab) */ + + /* Interpolation index */ + int ixet; /* Interpolation index entry type */ + int ixvt; /* Interpolation index variable type */ + int ixmnb; /* Interpolation index minimum bits (actual is ix_ab ???) */ + int ixmxres; /* Interpolation table maximum resolution */ + + /* Simplex index: if(!sort && it_xs) */ + int sxet; /* Simplex index entry type */ + int sxvt; /* Simplex index variable type */ + int sxmnb; /* Simplex index bits minimum (actual is sx_ab) */ + int sxmxres; /* Simplex table maximum resolution (0 if sort) */ + + /* Combination Weighting + Vertex offset values: if(it_xs && !wo_xs) */ + int woet; /* Weighting+offset entry type */ + int wovt; /* Weighting+offset variable type */ + int womnb; /* Weighting+offset index bits minimum (actual is wo_ab) */ + + /* Weighting value: if(it_xs && wo_xs) */ + int weet; /* Weighting entry type */ + int wevt; /* Weighting variable type */ + int wemnb; /* Weighting index bits minimum (actual is we_ab) */ + + /* Vertex offset value: if(it_xs && wo_xs) */ + int voet; /* Vertex offset entry type */ + int vovt; /* Vertex offset variable type */ + int vomnb; /* Vertex offset index bits minimum (actual is vo_ab) */ + + /* Interpolation table entry: */ + int imovb; /* Interpolation output value bits per channel required */ + int imfvt; /* Interpolation full entry & variable type */ + int impvt; /* Interpolation partial entry variable type */ + + /* Interpolation accumulators: */ + int iaovb; /* Interpolation output value bits per channel required */ + int iafvt; /* Interpolation full entry & variable type */ + int iapvt; /* Interpolation partial entry variable type */ + int ian; /* Total number of accumulators */ + + /* Output table lookup */ + int otit; /* Output table index type */ + int otvt; /* Output table value type (size is ot_ts bytes) */ + + /* Write information */ + int opt[IXDO]; /* Output pointer types */ + int nop; /* Actual number of output pointers, accounting for pint */ + +} fileo; + +void line(fileo *f, char *fmt, ...); /* Output one line */ +void sline(fileo *f, char *fmt, ...); /* Output start of line */ +void mline(fileo *f, char *fmt, ...); /* Output middle of line */ +void eline(fileo *f, char *fmt, ...); /* Output end of line */ +void niline(fileo *f, char *fmt, ...); /* Output one line, no indent */ +void cr(fileo *f) { line(f,""); } /* Output a blank line */ +void inc(fileo *f) { f->indt++; } /* Increment the indent level */ +void dec(fileo *f) { f->indt--; } /* Decrement the indent level */ +void lineinc(fileo *f, char *fmt, ...); /* Output one line and increment indent */ +void decline(fileo *f, char *fmt, ...); /* Decrement indent and output one line */ +/* ------------------------------------ */ + +int findord(fileo *f, int bits); /* Find ordinal with bits or more */ +int nord(fileo *f, int ov); /* Round ordinal type up to natural size */ +int findnord(fileo *f, int bits); /* Find ordinal with bits, or natural larger */ +int findint(fileo *f, int bits); /* Find integer with bits or more */ +int nint(fileo *f, int iv); /* Round integer type up to natural size */ +int findnint(fileo *f, int bits); /* Find integer with bits, or natural larger */ +static void doheader(fileo *f); + +static int calc_bits(int dim, int res); +static int calc_res(int dim, int bits); +static int calc_obits(int dim, int res, int esize); +static int calc_ores(int dim, int bits, int esize); + + +/* return a hexadecimal mask string */ +/* take care of the case when bits >= 32 */ +char *hmask(int bits) { + static char buf[20]; + + if (bits < 32) { + sprintf(buf, "0x%x",(1 << bits)-1); + } else if (bits == 32) { + return "0xffffffff"; + } else if (bits == 64) { + return "0xffffffffffffffff"; + } else { /* Bits > 32 */ + sprintf(buf, "0x%xffffffff",(1 << (bits-32))-1); + } + return buf; +} + +/* Generate a source file to implement the specified */ +/* interpolation kernel. Fill in return values and return 0 if OK. */ +/* g->opt should be set to opts_splx_sort or opts_sort_splx if both */ +/* are being generated, but opts_splx is what actually chooses simplex */ +/* when available, and is not recorded in the resulting table. */ +/* Return 1 if this kernel could be generated with a simplex table algorithm, */ +/* and some other non-zero on another error. */ +int gen_c_kernel( + genspec *g, /* Specification of what to generate */ + tabspec *t, /* Tablspec that will be filled in */ + mach_arch *a, + FILE *fp, /* File to write to */ + int index, /* Identification index, 1 = first */ + genspec *og, /* Previous tables genspec (for diff) */ + tabspec *ot /* Previous tables tabspec (for diff) */ +) { + int frv = 0; /* Function return value */ + unsigned char kk[] = { 0x43, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, + 0x74, 0x20, 0x32, 0x30, 0x30, 0x34, 0x20, 0x47, + 0x72, 0x61, 0x65, 0x6D, 0x65, 0x20, 0x57, 0x2E, + 0x20, 0x47, 0x69, 0x6C, 0x6C, 0x00 }; + fileo f[1]; + int e, i; + int timp = 0; /* Flag to use temporary imp pointer. */ + /* Seem to make x86 MSVC++ slower */ + /* Has no effect on x86 IBMCC */ + + sprintf(g->kname, "imdi_k%d",index); /* Kernel routine base name */ + strcpy(g->kkeys, (char *)kk); /* Kernel keys for this session */ + + /* Setup the file output context */ + f->of = fp; + f->indt = 0; /* Start with no indentation */ + f->g = g; + f->t = t; + f->a = a; + + /* (prec is currently permitted to be only 8 or 16) */ + if (g->prec == 8) { + if (g->id <= 4) { /* Simplex table can be used */ + frv = 1; /* Signal caller that simplex is possible */ + if (g->opt & opts_splx) + t->sort = 0; /* Implicit sort using simplex table lookup */ + else + t->sort = 1; /* Explicit sort */ + } else { + t->sort = 1; /* Explicit sort */ + } + + } else if (g->prec == 16) { + t->sort = 1; /* Explit sort, no simplex table */ + + } else { + fprintf(stderr,"Can't cope with requested precision of %d bits\n",g->prec); + exit(-1); + } + + /* Compute input read and input table lookup stuff */ + + /* Compute number of input pointers */ + if (g->in.pint != 0) /* Pixel interleaved */ + f->nip = 1; + else + f->nip = g->id; + + /* Figure out the input pointer types */ + for (e = 0; e < f->nip; e++) { + if ((f->ipt[e] = findord(f, g->in.bpch[e])) < 0) { + fprintf(stderr,"Input channel size can't be handled\n"); + exit(-1); + } + } + + /* Do the rest of the input table size calculations after figuring */ + /* out simplex and interpolation table sizes. */ + + /* Figure out the interpolation multi-dimentional table structure */ + /* and output accumulation variable sizes. Note that the accumulator */ + /* size needs to be greater than the basic precision by soem factor, */ + /* if we are not to get rounding errors due to each value being the sum */ + /* of di+1 parts with weighting that sum to 1.0. It's convenient in */ + /* C code case to simply double the basic precision size. */ + if (g->prec == 8 + || (g->prec == 16 && a->ords[a->nords-1].bits >= (g->prec * 4))) { + int tiby; /* Total interpolation bytes needed */ + + /* We assume that we can normally compute more than one */ + /* output value at a time, so we need to hold the interpolation */ + /* output data in the expanded fixed point format in both the */ + /* table and accumulator. */ + t->im_cd = 1; + f->imovb = g->prec * 2; /* 16 bits needed for 8 bit precision, */ + f->iaovb = g->prec * 2; /* 32 bits needed for 16 bit precision */ + f->imfvt = a->nords-1; /* Full variable entry type is biggest available */ + f->iafvt = a->nords-1; /* Full variable accum. type is same */ + + if (a->ords[f->imfvt].bits < f->imovb) { + fprintf(stderr,"Interpolation table entry size can't be handled\n"); + exit(-1); + } + + /* Compute details of table entry sizes, number */ + tiby = (f->imovb * g->od)/8; /* Total table bytes needed */ + t->im_fs = a->ords[f->imfvt].bits/8; /* Full entry bytes */ + t->im_fv = (t->im_fs * 8)/f->imovb; /* output values per full entry . */ + t->im_fn = tiby/t->im_fs; /* Number of full entries (may be 0) */ + t->im_ts = t->im_fn * t->im_fs; /* Structure size so far */ + tiby -= t->im_fn * t->im_fs; /* Remaining bytes */ + + if (tiby <= 0) { + t->im_pn = 0; /* No partials */ + t->im_ps = 0; + t->im_pv = 0; + f->impvt = 0; + f->iapvt = 0; + + } else { + t->im_pn = 1; /* Must be just 1 partial */ + t->im_pv = (tiby * 8)/f->imovb; /* Partial holds remaining entries */ + +#ifdef NEVER /* For better performance ??? */ + if ((f->impvt = findnord(f, tiby * 8)) < 0) { +#else /* Better memory footprint - minimise multi-D entry sizes */ + /* (but only if structure is alowed to be mis-aligned!) */ + if ((f->impvt = findord(f, tiby * 8)) < 0) { +#endif + fprintf(stderr,"Can't find partial interp table entry variable size\n"); + exit(-1); + } + f->iapvt = f->impvt; + t->im_ps = a->ords[f->impvt].bits/8;/* Partial entry bytes */ + + if (a->ords[f->imfvt].align) /* If full entry's need to be aligned */ + t->im_ts += t->im_fs; /* Round out struct size by full entry */ + else + t->im_ts += t->im_ps; /* Round out to natural size */ + } + + } else { + /* One 16 bit output value per entry + 32 bit accumulator. */ + /* We can conserve table space by not holding the table data in expanded */ + /* fixed point format, but expanding it when it is read. */ + /* Without resorting to compicated code, this restricts us */ + /* to only computing one output value per accumulator. */ + t->im_cd = 0; + f->imovb = g->prec; /* Table holds 16 bit entries with no fractions */ + f->iaovb = g->prec * 2; /* 32 bits needed for 16 bit precision in comp. */ + + if ((f->imfvt = findord(f, f->imovb)) < 0) { + fprintf(stderr,"Interpolation table entry size can't be handled\n"); + exit(-1); + } + if ((f->iafvt = findord(f, f->iaovb)) < 0) { + fprintf(stderr,"Interpolation accumulator size can't be handled\n"); + exit(-1); + } + + /* Compute details of table entry sizes, number */ + t->im_fs = a->ords[f->imfvt].bits/8; /* Full entry bytes */ + t->im_fv = 1; /* output values per full entry . */ + t->im_fn = g->od; /* Number of full entries */ + t->im_ts = t->im_fn * t->im_fs; /* Total structure size */ + + t->im_pn = 0; /* No partials */ + t->im_ps = 0; + t->im_pv = 0; + f->impvt = 0; + f->iapvt = 0; + } + f->ian = t->im_fn + t->im_pn; /* Total number of output accumulators */ + + /* Figure out how much of the interpolation entry offset to put in the */ + /* vertex offset value, and how much to make explicit in accessing the */ + /* interpolation table enty. */ + if (a->oscale > 0) { /* We have a scaled index mode */ + /* Use as much of the scaled index mode as possible */ + /* and then do the balance by scaling the simplex index entry. */ + for (t->im_oc = a->oscale; ; t->im_oc >>= 1) { + t->vo_om = t->im_ts/t->im_oc; /* Simplex index multiplier */ + if ((t->vo_om * t->im_oc) == t->im_ts) + break; /* Got appropriate offset scale */ + } + } else if (a->smmul) { /* Architecure supports fast small multiply */ + t->im_oc = t->im_ts; /* Do scale by structure size explicitly */ + t->vo_om = 1; /* Do none in the Simplex index */ + } else { /* We have no fast tricks */ + t->im_oc = 1; /* Do none explicitly */ + t->vo_om = t->im_ts; /* Do all in Simplex index */ + } + + /* Compute the number of bits needed to hold an index into */ + /* the interpolation table (index is in terms of table entry size). */ + /* This value is used to figure out the room needed in the input */ + /* table to accumulate the interpolation cube base offset value. (IM_O macro) */ + f->ixmnb = calc_bits(g->id, g->itres); + +#ifdef VERBOSE + /* Summarise the interpolation table arrangements */ + printf("\n"); + printf("Interpolation table structure:\n"); + printf(" Minimum bits needed to index table %d\n", f->ixmnb); + printf(" Entry total size %d bytes\n", t->im_ts); + printf(" Simplex entry offset scale %d\n", t->vo_om); + printf(" Explicit entry offset scale %d\n", t->im_oc); + printf(" %d full entries, size %d bytes\n", t->im_fn, t->im_fs); + printf(" %d partial entries, size %d bytes\n", t->im_pn, t->im_ps); + printf(" to hold %d output values of %d bits\n", g->od, f->imovb); + +#endif /* VERBOSE */ + + /* Number of bits needed for the weighting value */ + f->wemnb = g->prec+1; /* Need to hold a weighting factor of 0 - 256 for 8 bits */ + /* Need to hold a weighting factor of 0 - 65536 for 16 bits */ + + /* Variable that would be used to hold it */ + if ((f->wevt = findnord(f, f->wemnb)) < 0) { + fprintf(stderr,"Can't find entry size to hold weighting variable\n"); + exit(-1); + } + + /* Number of bits needed for vertex offset value */ + f->vomnb = calc_obits(g->id, g->itres, t->vo_om); + + /* Variable that would be used to hold it */ + if ((f->vovt = findnord(f, f->vomnb)) < 0) { + fprintf(stderr,"Can't find entry size to hold vertex offset variable\n"); + exit(-1); + } + + if (t->sort) { + /* If we are using an explicit sort, we need to figure how many */ + /* separate entries we need to use to hold the interpolation index, */ + /* weighting factor and vertex offset values in the input table. */ + + /* First try all three in one entry */ + if ((f->itet = findord(f, f->ixmnb + f->wemnb + f->vomnb)) >= 0) {/* size to read */ + int rem; /* Remainder bits */ + + t->it_xs = 0; /* Combined interp+weight+offset */ + t->wo_xs = 0; + t->it_ab = a->ords[f->itet].bits; /* Bits in combined input entry */ + rem = t->it_ab - f->ixmnb - f->wemnb - f->vomnb; /* Spair bits */ + t->we_ab = f->wemnb; /* Get minimum weight bits */ + t->vo_ab = f->vomnb + rem/2; /* vertex offset index bits actually available */ + t->ix_ab = t->it_ab - t->vo_ab - t->we_ab; /* interp index bits actually available */ + t->wo_ab = t->we_ab + t->vo_ab; /* Weight & offset total bits */ + t->it_ts = a->ords[f->itet].bits/8; /* total size in bytes */ + f->itvt = nord(f, f->itet); /* Variable type */ + + if ((f->wovt = findnord(f, t->we_ab + t->vo_ab)) < 0) { + fprintf(stderr,"Can't find variable size to hold weight/offset\n"); + exit(-1); + } + if ((f->wevt = findnord(f, t->we_ab)) < 0) { + fprintf(stderr,"Can't find variable size to hold weighting factor\n"); + exit(-1); + } + if ((f->vovt = findnord(f, t->vo_ab)) < 0) { + fprintf(stderr,"Can't find variable size to hold vertex offset index\n"); + exit(-1); + } + if ((f->ixvt = findnord(f, t->ix_ab)) < 0) { + fprintf(stderr,"Interp index variable size can't be handled\n"); + exit(-1); + } + } else { /* Interp index will be a separate entry */ + int wit, oft, bigt; /* weighting type, offset type, biggest type */ + int combt; /* Combined type */ + int sepbits, combits; /* Total separate, combined bits */ + + t->it_xs = 1; /* Separate interp index and weighting+offset */ + if ((f->ixet = findord(f, f->ixmnb)) < 0) { + fprintf(stderr,"Interp index entry size can't be handled\n"); + exit(-1); + } + f->ixvt = nord(f, f->ixet); /* Variable type */ + t->ix_ab = a->ords[f->ixet].bits; + t->ix_es = t->ix_ab/8; + t->ix_eo = 0; + t->it_ts = t->ix_es; /* Input table size so far */ + + /* Now figure weighting and vertex offset */ + + /* See if we can fit them into separately readable entries, or whether */ + /* they should be combined to minimise overall table size. */ + + if ((wit = findord(f, f->wemnb)) < 0) { + fprintf(stderr,"Can't find entry size to hold weighting factor\n"); + exit(-1); + } + if ((oft = findord(f, f->vomnb)) < 0) { + fprintf(stderr,"Can't find entry size to hold vertex offset index\n"); + exit(-1); + } + bigt = wit > oft ? wit : oft; /* Bigest separate type */ + + if ((combt = findord(f, f->wemnb + f->vomnb)) < 0) {/* Combined isn't possible */ + sepbits = 2 * a->ords[bigt].bits; /* Total separate bits */ + combits = sepbits; /* Force separate entries */ + } else { + sepbits = 2 * a->ords[bigt].bits; /* Total separate bits */ + combits = a->ords[combt].bits; /* Total combined bits */ + } + + if (sepbits <= combits) { /* We will use separate entries */ + t->wo_xs = 1; + t->we_es = a->ords[bigt].bits/8; /* size in bytes for weighting entry */ + t->we_ab = a->ords[bigt].bits; /* bits available for weighting */ + t->we_eo = t->ix_es; /* Entry offset in input table */ + t->vo_es = a->ords[bigt].bits/8; /* size in bytes for vertex offset entry */ + t->vo_ab = a->ords[bigt].bits; /* bits available for vertex offset */ + t->vo_eo = t->ix_es + t->we_es; /* Entry offset in input table */ + t->wo_es = t->we_es + t->vo_es; /* Total entry size for each vertex */ + t->it_ts += t->we_es + t->vo_es; /* Total input entry size in bytes */ + + f->weet = bigt; /* Variable type for accessing weighting entry */ + f->voet = bigt; /* Variable type for accessing vertex offset entry */ + f->wevt = nord(f, wit); /* Variable type for holding weight value */ + f->vovt = nord(f, oft); /* Variable type for holding offset value */ + + } else { /* We will combine the two entries */ + t->wo_xs = 0; + t->wo_es = a->ords[combt].bits/8; /* entry size in bytes for each entry */ + t->wo_ab = a->ords[combt].bits; /* bits in weightig + offset */ + t->we_ab = f->wemnb; /* bits available for weighting */ + t->vo_ab = t->wo_ab - t->we_ab; /* Allow all spare bits to vertex offset */ + t->wo_eo = t->ix_es; /* entry offset in input table */ + t->it_ts += t->wo_es; /* Final input table size */ + + f->woet = combt; /* Variable type for accessing combined entry */ + f->wovt = nord(f, combt); /* Variable type holding weight/offset read value */ + + if ((f->wevt = findnord(f, t->we_ab)) < 0) { + fprintf(stderr,"Can't find variable size to hold weighting factor\n"); + exit(-1); + } + if ((f->vovt = findnord(f, t->vo_ab)) < 0) { + fprintf(stderr,"Can't find variable size to hold vertex offset index\n"); + exit(-1); + } + } + } +#ifdef VERBOSE + /* Summarise the input table arrangements */ + printf("\n"); + printf("Input table structure:\n"); + printf(" Input table entry size = %d bytes\n",t->it_ts); + if (t->it_ix) { + printf(" Input table extracts value from read values\n"); + if (t->wo_xs) { + printf(" Separate Interp., Weighting and Offset values\n"); + printf(" Interp. index is at offset %d, size %d bytes\n",t->ix_eo, t->ix_es); + printf(" Weighting is at offset %d, size %d bytes\n",t->we_eo, t->we_es); + printf(" Vertex offset is at offset %d, size %d bytes\n",t->vo_eo, t->vo_es); + } else { + printf(" Separate Interp. index and Weightint+Offset value\n"); + printf(" Interp. index is at offset %d, size %d bytes\n",t->ix_eo, t->ix_es); + printf(" Weighting+Offset is at offset %d, size %d bytes\n",t->wo_eo, t->wo_es); + printf(" Weighting = %d bits\n",t->we_ab); + printf(" Vertex offset = %d bits\n",t->vo_ab); + } + } else { + printf(" Combined InterpIndex+Weighting+Voffset values\n"); + printf(" Values are stored in size %d bytes\n",t->it_ts); + printf(" Interp. index = %d bits\n",t->ix_ab); + printf(" Weighting = %d bits\n",t->we_ab); + printf(" Vertex offset = %d bits\n",t->vo_ab); + } +#endif /* VERBOSE */ + + } else { /* Simplex table */ + /* If we are going to use a simplex table, figure out how we */ + /* will store the weighting value and vertex offset values in it, */ + /* as well as the size of index we'll need to address it. */ + int wit, oft, bigt; /* weighting type, offset type, biggest type */ + int combt; /* Combined type */ + int sepbits, combits; /* Total separate, combined bits */ + + /* See if we can fit them into separately readable entries, or whether */ + /* they should be combined to minimise overall table size. */ + + if ((wit = findord(f, f->wemnb)) < 0) { + fprintf(stderr,"Can't find entry size to hold weighting factor\n"); + exit(-1); + } + if ((oft = findord(f, f->vomnb)) < 0) { + fprintf(stderr,"Can't find entry size to hold vertex offset index\n"); + exit(-1); + } + bigt = wit > oft ? wit : oft; /* Bigest separate type */ + + if ((combt = findord(f, f->wemnb + f->vomnb)) < 0) {/* Combined isn't possible */ + sepbits = 2 * a->ords[bigt].bits; /* Total separate bits */ + combits = sepbits; /* Force separate entries */ + } else { + sepbits = 2 * a->ords[bigt].bits; /* Total separate bits */ + combits = a->ords[combt].bits; /* Total combined bits */ + } + + if (sepbits <= combits) { /* We will use separate entries */ + t->wo_xs = 1; + t->we_es = a->ords[bigt].bits/8; /* size in bytes for weighting entry */ + t->we_ab = a->ords[bigt].bits; /* bits available for weighting */ + t->we_eo = 0; /* Entry offset in simplex table */ + t->vo_es = a->ords[bigt].bits/8; /* size in bytes for vertex offset entry */ + t->vo_ab = a->ords[bigt].bits; /* bits available for vertex offset */ + t->vo_eo = t->we_es; /* Entry offset in simplex table */ + t->wo_es = t->we_es + t->vo_es; /* Total entry size for each vertex */ + t->sm_ts = (g->id + 1) * (t->we_es + t->vo_es) ; /* Total size in bytes */ + + f->weet = bigt; /* Variable type for accessing weighting entry */ + f->voet = bigt; /* Variable type for accessing vertex offset entry */ + f->wevt = nord(f, wit); /* Variable type for holding weight value */ + f->vovt = nord(f, oft); /* Variable type for holding offset value */ + + } else { /* We will combine the two entries */ + t->wo_xs = 0; + t->wo_es = a->ords[combt].bits/8; /* entry size in bytes for each entry */ + t->wo_ab = a->ords[combt].bits; /* bits in weightig + offset */ + t->we_ab = f->wemnb; /* bits available for weighting */ + t->vo_ab = t->wo_ab - t->we_ab; /* Allow all spare bits to vertex offset */ + t->wo_eo = 0; /* entry offset in simplex table */ + t->sm_ts = (g->id + 1) * t->wo_es; /* Total size in bytes */ + + f->woet = combt; /* Variable type for accessing combined entry */ + f->wovt = nord(f, combt); /* Variable type holding weight/offset read value */ + + if ((f->wevt = findnord(f, t->we_ab)) < 0) { + fprintf(stderr,"Can't find variable size to hold weighting factor\n"); + exit(-1); + } + if ((f->vovt = findnord(f, t->vo_ab)) < 0) { + fprintf(stderr,"Can't find variable size to hold vertex offset index\n"); + exit(-1); + } + } + + /* Compute the number of bits needed to hold an index into */ + /* the simplex table (index is in terms of table entry size). */ + /* This value is used to figure out the room needed in the input */ + /* table to accumulate the simplex cube base offset value. (SW_O macro) */ + f->sxmnb = calc_bits(g->id, g->stres); + +#ifdef VERBOSE + /* Summarise the simplex table arrangements */ + printf("\n"); + printf("Simplex table structure:\n"); + printf(" Minimum bits needed to index table %d\n", f->sxmnb); + printf(" Total simplex entry size %d bytes to hold %d entries\n",t->sm_ts, g->id+1); + if (t->wo_xs) { + printf(" Separate entries for offset and weight\n"); + printf(" Weighting entry size %d bytes\n",t->we_es); + printf(" Offset entry size %d bytes\n",t->vo_es); + } else { + printf(" Combined offset and weight entries in %d bytes\n",t->wo_es); + printf(" Weighting entry size %d bits\n",t->we_ab); + printf(" Offset entry size %d bits\n",t->vo_ab); + } + printf(" Vertex offset scale factor %d\n", t->vo_om); +#endif /* VERBOSE */ + + /* We known how big the interpolation and simplex */ + /* tables indexes are going to be, so complete figuring out */ + /* how big the input table entries have to be. */ + if ((f->itet = findord(f, f->sxmnb + f->ixmnb)) >= 0) {/* size to read */ + int rem; /* Remainder bits */ + + t->it_xs = 0; /* Combined simplex+interp index */ + + t->it_ab = a->ords[f->itet].bits; /* Bits in combined input entry */ + rem = t->it_ab - f->sxmnb - f->ixmnb; + t->sx_ab = f->sxmnb + rem/2; /* simplex index bits actually available */ + t->ix_ab = t->it_ab - t->sx_ab; /* interp index bits actually available */ + t->it_ts = a->ords[f->itet].bits/8; /* total size in bytes */ + f->itvt = nord(f, f->itet); /* Variable type */ + + if ((f->sxvt = findnord(f, t->sx_ab)) < 0) { + fprintf(stderr,"Simplex index variable size can't be handled\n"); + exit(-1); + } + if ((f->ixvt = findnord(f, t->ix_ab)) < 0) { + fprintf(stderr,"Interp index variable size can't be handled\n"); + exit(-1); + } + } else { /* Separate entries */ + int bbits; /* Largest number of bits needed */ + + t->it_xs = 1; /* Separate simplex+interp indexes */ + bbits = f->sxmnb > f->ixmnb ? f->sxmnb : f->ixmnb; + + /* Allocate same size for both so that total structure size is power of 2 */ + if ((f->sxet = f->ixet = findord(f, bbits)) < 0) { + fprintf(stderr,"Interp/Simplex index entry size can't be handled\n"); + exit(-1); + } + + t->sx_ab = a->ords[f->sxet].bits; /* Actual bits available */ + t->sx_es = t->sx_ab/8; /* Entry size in bytes */ + t->ix_ab = a->ords[f->ixet].bits; + t->ix_es = t->sx_ab/8; + t->it_ts = t->sx_es + t->ix_es; /* total size in bytes */ + t->sx_eo = 0; /* simplex index offset in bytes */ + t->ix_eo = t->sx_es; /* interp. index offset in bytes */ + f->sxvt = nord(f, f->sxet); /* Variable type */ + f->ixvt = nord(f, f->ixet); /* Variable type */ + } + +#ifdef VERBOSE + /* Summarise the input table arrangements */ + printf("\n"); + printf("Input table structure:\n"); + if (t->it_ix) { + printf(" Input table extracts value from read values\n"); + } else { + printf(" Value extraction read values is explicit\n"); + } + printf(" Input table entry size = %d bytes\n",t->it_ts); + if (t->it_xs) { + printf(" Separate Interp. and Simplex index values\n"); + printf(" Interp. index is at offset %d, size %d bytes\n",t->ix_eo, t->ix_es); + printf(" Simplex index is at offset %d, size %d bytes\n",t->sx_eo, t->sx_es); + } else { + printf(" Combined Interp. and Simplex index values\n"); + printf(" Values are size %d bytes\n",t->it_ts); + printf(" Interp. index = %d bits\n",t->ix_ab); + printf(" Simplex index = %d bits\n",t->sx_ab); + } +#endif /* VERBOSE */ + } + + /* Figure out output table stuff */ + { + /* A variable to hold the index into an output table */ + if ((f->otit = findord(f, g->prec)) < 0) { + fprintf(stderr,"Can't find output table index size\n"); + exit(-1); + } + f->otit = nord(f,f->otit); /* Make temp variable natural size */ + + if (g->out.pint != 0) /* Pixel interleaved */ + f->nop = 1; /* Use same pointers for every pixel */ + else + f->nop = g->od; /* Use a separate pointer for each output value */ + + /* Figure out the output pointer types */ + f->otvt = 0; /* Output table value type */ + for (e = 0; e < f->nop; e++) { + if ((f->opt[e] = findord(f, g->out.bpch[e])) < 0) { + fprintf(stderr,"Output channel size can't be handled\n"); + exit(-1); + } + if (f->opt[e] > f->otvt) + f->otvt = f->opt[e]; /* Make value type big enough for any channel size */ + } + t->ot_ts = a->ords[f->otvt].bits/8; /* Output table entry size in bytes */ + + /* Setup information on data placement in output table entries */ + for (e = 0; e < g->od; e++) { + t->ot_off[e] = g->out.bov[e]; /* Transfer info from generation spec. */ + t->ot_bits[e] = g->out.bpv[e]; + } + } + +#ifdef VERBOSE + /* Summarise the output table arrangements */ + printf("Output table structure:\n"); + printf(" Entry size = %d bytes\n",t->ot_ts); + printf(" Output value placement within each enry is:\n"); + for (e = 0; e < f->nop; e++) { + printf(" %d: Offset %d bits, size %d bits\n", e, t->ot_off[e], t->ot_bits[e]); + } +#endif /* VERBOSE */ + + /* Compute the maximum interpolation table resolution we will be able to handle */ + { + int res, ores; + + res = calc_res(g->id, t->ix_ab); + ores = calc_ores(g->id, t->vo_ab, t->vo_om); + f->ixmxres = res < ores ? res : ores; + } + + /* Compute the maximum simplex table resolution we will be able to handle */ + if (t->sort) { + f->sxmxres = 0; + } else { + f->sxmxres = calc_res(g->id, t->sx_ab); + } + +#ifdef VERBOSE + printf("Emitting introductory code\n"); fflush(stdout); +#endif /* VERBOSE */ + + /* Start of code generation */ + doheader(f); /* Output the header comments */ + + /* We need an include file */ + line(f,"#ifndef IMDI_INCLUDED"); + line(f,"#include "); + line(f,"#include \"imdi_utl.h\""); + line(f,"#define IMDI_INCLUDED"); + line(f,"#endif /* IMDI_INCLUDED */"); + cr(f); + + /* Declare our explicit pointer type */ + line(f,"#ifndef DEFINED_pointer"); + line(f,"#define DEFINED_pointer"); + line(f,"typedef unsigned char * pointer;"); + line(f,"#endif"); + cr(f); + + /* Declare our explicit structure access macros */ + +#ifdef VERBOSE + printf("Declaring macros\n"); fflush(stdout); +#endif /* VERBOSE */ + + /* Macros for accessing input table entries */ + if (t->sort) { + if (t->it_xs) { + line(f,"/* Input table interp. index */"); + line(f,"#define IT_IX(p, off) *((%s *)((p) + %d + (off) * %d))", + a->ords[f->ixet].name, t->ix_eo, t->it_ts); + cr(f); + if (t->wo_xs) { + line(f,"/* Input table input weighting enty */"); + line(f,"#define IT_WE(p, off) *((%s *)((p) + %d + (off) * %d))", + a->ords[f->weet].name, t->we_eo, t->it_ts); + cr(f); + line(f,"/* Input table input offset value enty */"); + line(f,"#define IT_VO(p, off) *((%s *)((p) + %d + (off) * %d))", + a->ords[f->voet].name, t->vo_eo, t->it_ts); + cr(f); + } else { + line(f,"/* Input table input weighting/offset value enty */"); + line(f,"#define IT_WO(p, off) *((%s *)((p) + %d + (off) * %d))", + a->ords[f->woet].name, t->wo_eo, t->it_ts); + cr(f); + } + } else { + line(f,"/* Input table interp index, weighting and vertex offset */"); + line(f,"#define IT_IT(p, off) *((%s *)((p) + %d + (off) * %d))", + a->ords[f->itet].name, 0, t->it_ts); + cr(f); + } + + /* Sort primitive macro's */ + line(f,"/* Sorting macros */"); + if (t->wo_xs) { + line(f,"#define XFR(A, AA, B, BB) A = B; AA = BB;"); + line(f,"#define CEX(A, AA, B, BB) if (A < B) { \\"); + line(f," A ^= B; B ^= A; A ^= B; AA ^= BB; BB ^= AA; AA ^= BB; }"); + line(f,"#define CXJ(A, B, BB, D, DD, L) if (A >= B) { D = B; DD = BB; goto L; }"); + } else { + line(f,"#define XFR(A, B) A = B;"); + line(f,"#define CEX(A, B) if (A < B) { A ^= B; B ^= A; A ^= B; }"); + line(f,"#define CXJ(A, B, D, L) if (A >= B) { D = B; goto L; }"); + } + line(f,"#define CJ(A, B, L) if (A >= B) goto L;"); + cr(f); + + } else { /* Simplex table */ + if (t->it_xs) { + line(f,"/* Input table interp. index */"); + line(f,"#define IT_IX(p, off) *((%s *)((p) + %d + (off) * %d))", + a->ords[f->ixet].name, t->ix_eo, t->it_ts); + cr(f); + line(f,"/* Input table simplex index enty */"); + line(f,"#define IT_SX(p, off) *((%s *)((p) + %d + (off) * %d))", + a->ords[f->sxet].name, t->sx_eo, t->it_ts); + cr(f); + } else { + line(f,"/* Input table inter & simplex indexes */"); + line(f,"#define IT_IT(p, off) *((%s *)((p) + %d + (off) * %d))", + a->ords[f->itet].name, 0, t->it_ts); + cr(f); + } + } + + if (!t->sort) { + /* Macro for computing a simplex table entry */ + line(f,"/* Simplex weighting table access */"); + line(f,"#define SW_O(off) ((off) * %d)", t->sm_ts); + cr(f); + + /* Macros for accessing the contents of the simplex table */ + if (t->wo_xs) { /* If separate */ + line(f,"/* Simplex table - get weighting value */"); + line(f,"#define SX_WE(p, v) *((%s *)((p) + (v) * %d + %d))", + a->ords[f->weet].name, t->wo_es, t->we_eo); + cr(f); + + line(f,"/* Simplex table - get offset value */"); + line(f,"#define SX_VO(p, v) *((%s *)((p) + (v) * %d + %d))", + a->ords[f->voet].name, t->wo_es, t->vo_eo); + cr(f); + + } else { /* Combined */ + line(f,"/* Simplex table - get weighting/offset value */"); + line(f,"#define SX_WO(p, v) *((%s *)((p) + (v) * %d))", + a->ords[f->woet].name, t->wo_es); + cr(f); + } + } + + /* Macro for computing an interpolation table entry */ + line(f,"/* Interpolation multi-dim. table access */"); + line(f,"#define IM_O(off) ((off) * %d)", t->im_ts); + cr(f); + + /* Macro for accessing an entry in the interpolation table */ + line(f,"/* Interpolation table - get vertex values */"); + + if (t->im_fn > 0) { + /* Arguments to macro are cell base address, vertex offset, data offset */ + + if (f->imfvt == f->iafvt) { /* Table and accumulator are the same size */ + if (!timp || t->im_fn == 1) + line(f,"#define IM_FE(p, v, c) *((%s *)((p) + (v) * %d + (c) * %d))", + a->ords[f->imfvt].name, t->im_oc, t->im_fs); + else { + line(f,"#define IM_TP(p, v) ((p) + (v) * %d)", t->im_oc); + line(f,"#define IM_FE(p, c) *((%s *)((p) + (c) * %d))", + a->ords[f->imfvt].name, t->im_fs); + } + } else { /* Expand single table entry to accumulator size */ + if (!timp || t->im_fn == 1) + line(f,"#define IM_FE(p, v, c) ((%s)*((%s *)((p) + (v) * %d + (c) * %d)))", + a->ords[f->iafvt].name, + a->ords[f->imfvt].name, t->im_oc, t->im_fs); + else { + line(f,"#define IM_TP(p, v) ((p) + (v) * %d)", t->im_oc); + line(f,"#define IM_FE(p, c) ((%s)*((%s *)((p) + (c) * %d)))", + a->ords[f->iafvt].name, + a->ords[f->imfvt].name, t->im_fs); + } + } + } + if (t->im_pn > 0) { + /* Arguments to macro are cell base address, vertex offset */ + /* There is no data offset since there can be only be one partial entry */ + + if (f->imfvt == f->iafvt) /* Table and accumulator are the same size */ + line(f,"#define IM_PE(p, v) *((%s *)((p) + %d + (v) * %d))", + a->ords[f->impvt].name, t->im_fn * t->im_fs, t->im_oc); + else /* Expand single table entry to accumulator size */ + line(f,"#define IM_PE(p, v) ((%s)*((%s *)((p) + %d + (v) * %d)))", + a->ords[f->iafvt].name, + a->ords[f->impvt].name, t->im_fn * t->im_fs, t->im_oc); + } + cr(f); + + /* Macro for accessing an output table entry */ + line(f,"/* Output table indexes */"); + line(f,"#define OT_E(p, off) *((%s *)((p) + (off) * %d))", + a->ords[f->otvt].name, t->ot_ts); + cr(f); + + /* =============================================== */ + +#ifdef VERBOSE + printf("Starting interpolation function\n"); fflush(stdout); +#endif /* VERBOSE */ + + /* Declare the function */ + line(f,"void"); + line(f, "imdi_k%d(",index); + line(f, "imdi *s, /* imdi context */"); + line(f, "void **outp, /* pointer to output pointers */"); + line(f, "int ostride, /* optional input component stride */"); + line(f, "void **inp, /* pointer to input pointers */"); + line(f, "int istride, /* optional input component stride */"); + line(f, "unsigned int npix /* Number of pixels to process */"); + line(f, ") {"); + inc(f); + + /* We need access to the imdi_imp */ + line(f, "imdi_imp *p = (imdi_imp *)(s->impl);"); + + /* Declare the input pointers and init them */ + for (e = 0; e < f->nip; e++) { + if (g->opt & opts_bwd) { + if (g->opt & opts_istride) + line(f, "%s *ip%d = (%s *)inp[%d] + (npix-1) * istride;", + a->ords[f->ipt[e]].name, e, + a->ords[f->ipt[e]].name, e); + else + line(f, "%s *ip%d = (%s *)inp[%d] + (npix-1) * %d;", + a->ords[f->ipt[e]].name, e, + a->ords[f->ipt[e]].name, e, + g->in.chi[e]); + } else { + g->opt |= opts_fwd; /* Make sure it's marked for what it is */ + line(f, "%s *ip%d = (%s *)inp[%d];", + a->ords[f->ipt[e]].name, e, a->ords[f->ipt[e]].name, e); + } + } + + /* Declare the output pointers and init them */ + for (e = 0; e < f->nop; e++) { + if (g->opt & opts_bwd) { + if (g->opt & opts_ostride) + line(f, "%s *op%d = (%s *)outp[%d] + (npix-1) * ostride;", + a->ords[f->opt[e]].name, e, + a->ords[f->opt[e]].name, e); + else + line(f, "%s *op%d = (%s *)outp[%d] + (npix-1) * %d;", + a->ords[f->opt[e]].name, e, + a->ords[f->opt[e]].name, e, + g->out.chi[e]); + } else { + line(f, "%s *op%d = (%s *)outp[%d];", + a->ords[f->opt[e]].name, e, a->ords[f->opt[e]].name, e); + } + } + + /* Declare and intialise the end pointer */ + if (g->opt & opts_bwd) { + if (g->opt & opts_istride) + line(f, "%s *ep = (%s *)inp[0] - istride ;", + a->ords[f->ipt[0]].name, + a->ords[f->ipt[0]].name); + else + line(f, "%s *ep = (%s *)inp[0] - %d ;", + a->ords[f->ipt[0]].name, + a->ords[f->ipt[0]].name, g->in.chi[0]); + } else { + if (g->opt & opts_istride) + line(f, "%s *ep = (%s *)inp[0] + npix * istride ;", + a->ords[f->ipt[0]].name, + a->ords[f->ipt[0]].name); + else + line(f, "%s *ep = (%s *)inp[0] + npix * %d ;", + a->ords[f->ipt[0]].name, + a->ords[f->ipt[0]].name, g->in.chi[0]); + } + + /* Declare and initialise the input table pointers */ + for (e = 0; e < g->id; e++) + line(f,"pointer it%d = (pointer)p->in_tables[%d];",e,e); + + /* Declare and initialise the output table pointers */ + for (e = 0; e < g->od; e++) + line(f,"pointer ot%d = (pointer)p->out_tables[%d];",e,e); + + if (!t->sort) { + /* Declare and initialise the Simplex weighting base pointer */ + line(f,"pointer sw_base = (pointer)p->sw_table;"); + } + + /* Declare and initialise the Interpolation multidim base pointer */ + line(f,"pointer im_base = (pointer)p->im_table;"); + + /* Figure out whether input channel reads can be used directly as table offsets */ + t->it_ix = 1; /* Default use input table lookup to extract value */ + + if (g->in.packed != 0) + t->it_ix = 0; /* Extract will be done explicitly */ + + for (e = 0; e < g->id; e++) { + int ee = (g->in.pint != 0) ? 0 : e; /* bpch index */ + + if ((g->in.bov[e] + g->in.bpv[e]) <= 12) + continue; /* Table can do extract */ + + if (g->in.bov[e] != 0 || g->in.bpv[e] != g->in.bpch[ee]) { + t->it_ix = 0; /* Extract will be done explicitly */ + break; + } + } + + /* ------------------------------- */ +#ifdef VERBOSE + printf("Starting pixel processing loop\n"); fflush(stdout); +#endif /* VERBOSE */ + + /* Start the pixel processing loop */ + cr(f); + if (g->opt & opts_bwd) { + sline(f, "for(;ip0 != ep;"); + + if (g->opt & opts_istride) + for (e = 0; e < f->nip; e++) + mline(f, " ip%d -= istride,", e); + else + for (e = 0; e < f->nip; e++) + mline(f, " ip%d -= %d,", e, g->in.chi[e]); + + if (g->opt & opts_ostride) + for (e = 0; e < f->nop; e++) + mline(f, " op%d -= ostride%s", e, ((e+1) < f->nop) ? "," : ""); + else + for (e = 0; e < f->nop; e++) + mline(f, " op%d -= %d%s", e, g->out.chi[e], ((e+1) < f->nop) ? "," : ""); + } else { + sline(f, "for(;ip0 != ep;"); + + if (g->opt & opts_istride) + for (e = 0; e < f->nip; e++) + mline(f, " ip%d += istride,", e); + else + for (e = 0; e < f->nip; e++) + mline(f, " ip%d += %d,", e, g->in.chi[e]); + + if (g->opt & opts_ostride) + for (e = 0; e < f->nop; e++) + mline(f, " op%d += ostride%s", e, ((e+1) < f->nop) ? "," : ""); + else + for (e = 0; e < f->nop; e++) + mline(f, " op%d += %d%s", e, g->out.chi[e], ((e+1) < f->nop) ? "," : ""); + } + eline(f, ") {"); + inc(f); + + /* Declare output value accumulator(s) */ + for (i = 0; i < t->im_fn; i++) { + line(f,"%s ova%d; /* Output value accumulator */",a->ords[f->iafvt].name,i); + } + for (; i < f->ian; i++) { + line(f,"%s ova%d; /* Output value partial accumulator */",a->ords[f->iapvt].name,i); + } + + /* Context around interp/Simplex table lookup */ + line(f, "{"); + inc(f); + + if (!t->sort) + line(f,"pointer swp;"); /* Declare Simplex weighting pointer */ + line(f,"pointer imp;"); /* Declare Interpolation multidim pointer */ + + /* Declare the input weighting/vertex offset variables */ + if (t->sort) { + for (e = 0; e < g->id; e++) { + if (t->wo_xs) { + line(f,"%s we%d; /* Weighting value variable */", + a->ords[f->wevt].name, e); + line(f,"%s vo%d; /* Vertex offset variable */", + a->ords[f->vovt].name, e); + } else { + line(f,"%s wo%d; /* Weighting value and vertex offset variable */", + a->ords[f->wovt].name, e); + } + } + } + + /* Context around input table processing */ + line(f, "{"); + inc(f); + + /* Declare the table index variables/input weighting/vertex offset variables */ + if (t->sort) { + if (!t->it_xs) + line(f,"%s ti; /* Input table entry variable */",a->ords[f->itvt].name); + line(f,"%s ti_i; /* Interpolation index variable */",a->ords[f->ixvt].name); + } else { + if (t->it_xs) { + line(f,"%s ti_s; /* Simplex index variable */",a->ords[f->sxvt].name); + line(f,"%s ti_i; /* Interpolation index variable */",a->ords[f->ixvt].name); + } else { + line(f,"%s ti; /* Simplex+Interpolation index variable */",a->ords[f->itvt].name); + } + } + + if (g->in.packed != 0) /* We need to unpack from a single read */ + line(f,"%s rdv; /* Read value */",a->ords[f->ipt[0]].name); + + if (t->it_ix == 0) { + int bv = 0; + for (e = 0; e < f->nip; e++) { /* Find largest input type */ + if (f->ipt[e] > bv) + bv = f->ipt[e]; + } + bv = nord(f, bv); + line(f,"%s chv; /* Channel value */",a->ords[bv].name); + f->chv_bits = a->ords[bv].bits; + } + cr(f); + +#ifdef VERBOSE + printf("Read code\n"); fflush(stdout); +#endif /* VERBOSE */ + + /* For all the input channels */ + for (e = 0; e < g->id; e++) { + char rde[50]; /* Read expression */ + char toff[50]; /* Table offset expression */ + int ee = (g->in.pint != 0) ? 0 : e; /* bpch index */ + + if (g->in.pint != 0) /* Pixel interleaved */ + sprintf(rde,"ip0[%d]",e); /* Offset from single pointer */ + else + sprintf(rde,"*ip%d",e); /* Pointer per channel */ + + if (g->in.packed != 0) { + if (e == 0) + line(f,"rdv = %s;",rde); /* Do single read */ + sprintf(rde,"rdv"); /* Use read value for extraction */ + } + + if (t->it_ix == 0) { + if (g->in.bov[e] == 0 ) { /* No offset */ + if (g->in.bpv[e] == g->in.bpch[ee]) /* No mask */ + line(f,"chv = %s;",rde); + else /* Just mask */ + line(f,"chv = (%s & %s);",rde, hmask(g->in.bpv[e])); + } else { /* Offset */ + if ((g->in.bov[e] + g->in.bpv[e]) == g->in.bpch[ee]) + line(f,"chv = (%s >> %d);",rde, g->in.bov[e]); + else { /* Offset and mask */ + if (a->shfm || g->in.bpv[e] > 32) { + /* Extract using just shifts */ + line(f,"chv = ((%s << %d) >> %d);", rde, + f->chv_bits - g->in.bpv[e] - g->in.bov[e], + f->chv_bits - g->in.bpv[e]); + } else { + /* Extract using shift and mask */ + line(f,"chv = ((%s >> %d) & %s);", + rde, g->in.bov[e], hmask(g->in.bpv[e])); + } + } + } + sprintf(toff,"chv"); + } else { /* No extraction */ + sprintf(toff,"%s",rde); + } + + if (t->sort) { + if (t->it_xs) { + line(f,"ti_i %s= IT_IX(it%d, %s);", e ? "+" : " ", e, toff); + if (t->wo_xs) { + line(f,"we%d = IT_WE(it%d, %s);", e, e, toff); + line(f,"vo%d = IT_VO(it%d, %s);", e, e, toff); + } else { + line(f,"wo%d = IT_WO(it%d, %s);", e, e, toff); + } + } else { /* All three combined */ + line(f,"ti = IT_IT(it%d, %s);", e, toff); + if (a->shfm || t->wo_ab > 32) { + /* Extract using just shifts */ + line(f,"wo%d = ((ti << %d) >> %d); " + "/* Extract weighting/vertex offset value */", + e, a->ords[f->wovt].bits - t->wo_ab, a->ords[f->wovt].bits - t->wo_ab); + line(f,"ti_i %s= (ti >> %d); " + "/* Extract interpolation table value */", + e ? "+" : " ", t->wo_ab); + } else { + /* Extract using shift and mask */ + line(f,"wo%d = (ti & %s); " + "/* Extract weighting/vertex offset value */", + e, hmask(t->wo_ab)); + line(f,"ti_i %s= (ti >> %d); " + "/* Extract interpolation table value */", + e ? "+" : " ", t->wo_ab); + } + } + + } else { /* Simplex */ + if (t->it_xs) { + /* ~~~~ should toff be forced to be a temp variable ?? */ + /* (ie. force use of rde (above) if t->it_xs is nonz) */ + line(f,"ti_i %s= IT_IX(it%d, %s);", e ? "+" : " ", e, toff); + line(f,"ti_s %s= IT_SX(it%d, %s);", e ? "+" : " ", e, toff); + } else { + line(f,"ti %s= IT_IT(it%d, %s);", e ? "+" : " ", e, toff); + } + } + } + +#ifdef VERBOSE + printf("Index extraction code\n"); fflush(stdout); +#endif /* VERBOSE */ + + cr(f); + + if (t->sort) { + /* Extract Simplex and Interpolation indexes from accumulator */ + line(f,"imp = im_base + IM_O(ti_i); /* Compute interp. table entry pointer */"); + } else { + if (t->it_xs) { /* Extract Simplex and Interpolation indexes from accumulator */ + line(f,"swp = sw_base + SW_O(ti_s); /* Compute simplex table entry pointer */"); + line(f,"imp = im_base + IM_O(ti_i); /* Compute interp. table entry pointer */"); + } else { + line(f,"imp = im_base + IM_O(ti >> %d); " + "/* Extract interp. index and comp. entry */", + t->sx_ab); + if (a->shfm || t->sx_ab > 32) { + /* Extract using just shifts */ + line(f,"swp = sw_base + SW_O((ti << %d) >> %d); " + "/* Extract simplex index & comp. entry */", + a->ords[f->itvt].bits - t->sx_ab, a->ords[f->itvt].bits - t->sx_ab); + } else { + /* Extract using shift and mask */ + line(f,"swp = sw_base + SW_O(ti & %s); " + "/* Extract simplex index and comp. entry */", + hmask(t->sx_ab)); + } + } + } + + /* Do the explicit sort now */ + if (t->sort) { + cr(f); + /* Sort from largest to smallest */ + /* We can use a selection sort, or an insertions sort. */ + + line(f,"/* Sort weighting values and vertex offset values */"); + + if (g->id >= INSTHRESH) { + /* We do an insertion sort */ + lineinc(f,"{"); + if (t->wo_xs) { + line(f,"%s wet; /* Sort temporary */", a->ords[f->wevt].name); + line(f,"%s vot; /* Sort temporary */", a->ords[f->vovt].name); + } else + line(f,"%s wot; /* Sort temp variable */", a->ords[f->wovt].name); + cr(f); + + for (i = 1; i < g->id; i++) { + int j; + + j = i; + if (j < 2) { /* Only test & exchange needed */ + if (t->wo_xs) + line(f,"CEX(we%d, vo%d, we%d, vo%d);",j-1,j-1,j,j); + else + line(f,"CEX(wo%d, wo%d);",j-1,j); + + } else { + if (t->wo_xs) + line(f,"XFR(wet, vot, we%d, vo%d);",j,j); + else + line(f,"XFR(wot, wo%d);",j); + while (j > 0) { + if (j == i) { /* First test from i */ + if (t->wo_xs) + line(f,"CJ(we%d, wet, shs%d);",j-1,i); + else + line(f,"CJ(wo%d, wot, shs%d);",j-1,i); + if (t->wo_xs) + line(f,"XFR(we%d, vo%d, we%d, vo%d);",j,j,j-1,j-1); + else + line(f,"XFR(wo%d, wo%d);",j,j-1); + } else { + if (t->wo_xs) + line(f,"CXJ(we%d, wet, vot, we%d, vo%d, shs%d);",j-1,j,j,i); + else + line(f,"CXJ(wo%d, wot, wo%d, shs%d);",j-1,j,i); + if (t->wo_xs) + line(f,"XFR(we%d, vo%d, we%d, vo%d);",j,j,j-1,j-1); + else + line(f,"XFR(wo%d, wo%d);",j,j-1); + } + j--; + } + if (t->wo_xs) + line(f,"XFR(we%d, vo%d, wet, vot);",j,j); + else + line(f,"XFR(wo%d, wot);",j); + niline(f,"shs%d:;",i); + } + } + decline(f,"}"); + + } else { + /* Use a selection sort */ + for (i = 0; i < (g->id-1); i++) { + for (e = i+1; e < g->id; e++) { + if (t->wo_xs) + line(f,"CEX(we%d, vo%d, we%d, vo%d);",i,i,e,e); + else + line(f,"CEX(wo%d, wo%d);",i,e); + } + } + } + } + + /* End of input table processing context */ + dec(f); + line(f,"}"); + + line(f,"{"); /* Context around vertex lookup and accumulation */ + inc(f); + + /* Declare vertex offset and weight variables */ + if (t->sort && t->wo_xs == 0) { + line(f,"%s nvof; /* Next vertex offset value */",a->ords[f->vovt].name); + } else { + if (!t->wo_xs) /* If combined in table */ + line(f,"%s vowr; /* Vertex offset/weight value */",a->ords[f->wovt].name); + } + line(f,"%s vof; /* Vertex offset value */",a->ords[f->vovt].name); + line(f,"%s vwe; /* Vertex weighting */",a->ords[f->wevt].name); + if (timp && t->im_fn > 1) + line(f,"pointer timp; /* Temporary interpolation table pointer */"); + cr(f); + +#ifdef VERBOSE + printf("Vertex offset and weight code\n"); fflush(stdout); +#endif /* VERBOSE */ + + /* For each vertex in the simplex */ + for (e = 0; e < (g->id +1); e++) { + + if (t->sort) { + + if (e == 0) { + line(f,"vof = 0; /* First vertex offset is 0 */"); + } else { + if (t->wo_xs) + line(f,"vof += vo%d; /* Move to next vertex */",e-1); + else + line(f,"vof += nvof; /* Move to next vertex */"); + } + + /* Extract the vertex offset and weight values from the sorted input values */ + if (e < g->id && !t->wo_xs) { + if (a->shfm || t->vo_ab > 32) { + /* Extract using just shifts */ + line(f,"nvof = ((wo%d << %d) >> %d); " + "/* Extract offset value */", + e, a->ords[f->vovt].bits - t->vo_ab, a->ords[f->vovt].bits - t->vo_ab); + line(f,"wo%d = (wo%d >> %d); " + " /* Extract weighting value */", + e, e, t->vo_ab); + } else { + /* Extract using shift and mask */ + line(f,"nvof = (wo%d & %s); " + "/* Extract offset value */", + e, hmask(t->vo_ab)); + line(f,"wo%d = (wo%d >> %d); " + " /* Extract weighting value */", + e, e, t->vo_ab); + } + } + /* Compute the weighting value */ + if (!t->wo_xs) { + if (e == 0) { + line(f,"vwe = %d - wo%d; /* Baricentric weighting */", 1 << g->prec, e); + } else if (e < g->id) { + line(f,"vwe = wo%d - wo%d; /* Baricentric weighting */", e-1, e); + } else { + line(f,"vwe = wo%d; /* Baricentric weighting */", e-1); + } + } else { + if (e == 0) { + line(f,"vwe = %d - we%d; /* Baricentric weighting */", 1 << g->prec, e); + } else if (e < g->id) { + line(f,"vwe = we%d - we%d; /* Baricentric weighting */", e-1, e); + } else { + line(f,"vwe = we%d; /* Baricentric weighting */", e-1); + } + } + + } else { /* Not sort */ + /* Read the vertex offset and weight values from the simplex table */ + if (t->wo_xs) { /* If separate */ + line(f,"vof = SX_VO(swp, %d); /* Read vertex offset value */", e); + line(f,"vwe = SX_WE(swp, %d); /* Read vertex weighting value */", e); + } else { /* If combined in table */ + line(f,"vowr = SX_WO(swp, %d); /* Read vertex offset+weighting values */", e); + if (a->shfm || t->vo_ab > 32) { + /* Extract using just shifts */ + line(f,"vof = ((vowr << %d) >> %d); " + "/* Extract offset value */", + a->ords[f->vovt].bits - t->vo_ab, a->ords[f->vovt].bits - t->vo_ab); + line(f,"vwe = (vowr >> %d); " + "/* Extract weighting value */", + t->vo_ab); + } else { + /* Extract using shift and mask */ + line(f,"vof = (vowr & %s); " + "/* Extract offset value */", + hmask(t->vo_ab)); + line(f,"vwe = (vowr >> %d); " + "/* Extract weighting value */", + t->vo_ab); + } + } + } + + /* Lookup the vertex value, weight it, and accumulate it into output value */ + if (timp && t->im_fn > 1) + line(f,"timp = IM_TP(imp, vof); /* Vertex address */"); + for (i = 0; i < f->ian; i++) { /* For each output accumulation chunk */ + if (i < t->im_fn) { /* Full entry */ + if (!timp || t->im_fn == 1) + line(f,"ova%d %s= IM_FE(imp, vof, %d) * vwe; " + "/* Accumulate weighted output values */", + i, e ? "+" : " ", i); + else + line(f,"ova%d %s= IM_FE(timp, %d) * vwe; " + "/* Accumulate weighted output values */", + i, e ? "+" : " ", i); + } else /* One partial entry */ + line(f,"ova%d %s= IM_PE(imp, vof) * vwe; " + "/* Accumulate last weighted output values */", + i, e ? "+" : " "); + } + } + + dec(f); + line(f, "}"); /* End of output value lookup context */ + + dec(f); + line(f, "}"); /* End of output value accumulation context */ + + /* Start of output lookup and write */ + line(f,"{"); + inc(f); + +#ifdef VERBOSE + printf("Output table code\n"); fflush(stdout); +#endif /* VERBOSE */ + + { + char wre[50]; /* Write destination expression */ + + if (g->out.packed != 0) /* We need to pack results into a single write */ + line(f,"%s wrv; /* Write value */",a->ords[f->ipt[0]].name); + + /* Declare temporary to hold index into output lookup table */ + line(f,"%s oti; /* Vertex offset value */",a->ords[f->otit].name); + if (g->oopt & OOPTS_CHECK) + line(f,"%s otv; /* Output temporary value */",a->ords[f->otvt].name); + + /* For each accumulator value */ + /* (Assume they are in output order for the moment ?) */ + for (e = i = 0; i < f->ian; i++) { /* For each output accumulation chunk */ + int vpa = i < t->im_fn ? t->im_fv : t->im_pv; /* Chanel values per accumulator */ + int oat = i < t->im_fn ? f->iafvt : f->iapvt; /* Output accumulator type */ + int ee; /* Relative e to this accumulator */ + + /* For each output value in this accumulator */ + for (ee = 0; ee < vpa && e < g->od; ee++, e++) { + int off, size; /* Bits to be extracted */ + + /* Extract wanted 8 bits from the 8.8 bit result in accumulator */ + /* (or 16 bits from 16.16) */ + off = ee * f->iaovb + (f->iaovb - g->prec); + size = g->prec; + + if (e == 0 || g->out.packed == 0) { + if (g->out.pint != 0) /* Pixel interleaved */ + sprintf(wre,"op0[%d]",e); /* Offset from single pointer */ + else + sprintf(wre,"*op%d",e); /* Pointer per channel */ + } + + if (a->shfm || size > 32) { + /* Extract using just shifts */ +#ifdef ROUND + line(f,"oti = (((ova%d + (1 << %d)) << %d) >> %d); " + "/* Extract integer part of result */", + i, off-1, a->ords[oat].bits - off - size, a->ords[oat].bits - size); +#else + line(f,"oti = ((ova%d << %d) >> %d); " + "/* Extract integer part of result */", + i, a->ords[oat].bits - off - size, a->ords[oat].bits - size); +#endif + } else { + /* Extract using shift and mask */ +#ifdef ROUND + line(f,"oti = (((ova%d + 0x%x) >> %d) & %s); " + "/* Extract integer part of result */", + i, (1 << off-1), off, hmask(size)); +#else + line(f,"oti = ((ova%d >> %d) & %s); " + "/* Extract integer part of result */", + i, off, hmask(size)); +#endif + } + + if (g->oopt & OOPT(oopts_check,e)) { /* Lookup with check */ + line(f,"otv = OT_E(ot%d, oti); /* Fetch result */", e); + line(f,"if (otv != p->checkv[%d]) /* Do output value check */", e); + line(f," p->checkf |= (1 << %d); /* Set check flag */", e); + if (g->out.packed != 0) { + if (g->oopt & OOPT(oopts_skip,e)) + return 2; /* Error, can't skip on pixel interleaved */ + line(f,"wrv %s= otv;", e ? "+" : "", e); + } else { + if (g->oopt & OOPT(oopts_skip,e)) { + line(f,"if ((p->skipf & (1 << %d)) == 0) /* If not being skipped */", e); + line(f," %s = otv; /* Write result */", wre); + } else + line(f,"%s = otv; /* Write result */", wre); + } + } else { /* Normal lookup output table */ + /* Lookup in output table and write to destination */ + if (g->out.packed != 0) { + if (g->oopt & OOPT(oopts_skip,e)) + return 2; /* Error, can't skip on pixel interleaved */ + line(f,"wrv %s= OT_E(ot%d, oti);", e ? "+" : "", e); + } else { + if (g->oopt & OOPT(oopts_skip,e)) { + line(f,"if ((p->skipf & (1 << %d)) == 0) /* If not being skipped */", e); + line(f," %s = OT_E(ot%d, oti); /* Write result */", wre, e); + } else + line(f,"%s = OT_E(ot%d, oti); /* Write result */", wre, e); + } + } + } + } + + if (g->out.packed != 0) { /* Write out the accumulated value */ + line(f,"%s = wrv; /* Write result */", wre); + } + } + + /* The end of the output lookup and write */ + dec(f); + line(f, "}"); + + /* The end of the pixel processing loop */ + dec(f); + line(f, "}"); + + /* The end of the function */ + dec(f); + line(f, "}"); + + /* Undefine all the macros */ + if (t->sort) { + if (t->it_xs) { + if (t->wo_xs) { + line(f,"#undef IT_WE"); + line(f,"#undef IT_VO"); + } else + line(f,"#undef IT_WO"); + line(f,"#undef IT_IX"); + } else { + line(f,"#undef IT_IT"); + } + line(f,"#undef CXJ"); + line(f,"#undef CJ"); + line(f,"#undef XFR"); + line(f,"#undef CEX"); + } else { + if (t->it_xs) { + line(f,"#undef IT_IX"); + line(f,"#undef IT_SX"); + } else { + line(f,"#undef IT_IT"); + } + + line(f,"#undef SW_O"); + if (t->wo_xs) { + line(f,"#undef SX_WE"); + line(f,"#undef SX_VO"); + } else { + line(f,"#undef SX_WO"); + } + } + line(f,"#undef IM_O"); + if (t->im_fn > 0) { + if (timp && t->im_fn > 1) + line(f,"#undef IM_TP"); + line(f,"#undef IM_FE"); + } + if (t->im_pn > 0) { + line(f,"#undef IM_PE"); + } + line(f,"#undef OT_E"); + + /* =============================================== */ +#ifdef VERBOSE + printf("Done interpolation code\n"); fflush(stdout); +#endif /* VERBOSE */ + + /* =============================================== */ + + /* !genspec and tabspec delta code! */ + /* We generate code that updates any entries in the genspec and */ + /* tabpsec strucures that are different for this kernel, */ + /* compared to the previously generated kernel. */ + /* In this way, we save a lot of space, at the price */ + /* of having to access the table of kernels sequentially. */ + + /* If the genspec of tabspec structures are modified, */ + /* then corresponding changes need to be made to the code here. */ + { + int i; + int s_stres, s_itres; /* Save values */ + imdi_options s_opt; + + s_stres = g->stres; + s_itres = g->itres; + s_opt = g->opt; + g->stres = f->sxmxres; /* Set maximum values */ + g->itres = f->ixmxres; + g->opt &= ~opts_splx; /* Don't care about this, only about opts_splx/sort */ + if (frv == 0) { /* Simplex algorithm wasn't possible */ + g->opt &= ~opts_splx_sort; /* Therefore we don't care about preference */ + g->opt &= ~opts_sort_splx; + } + + /* Declare the genspec & tabspec update function */ + cr(f); + line(f,"void"); + line(f, "imdi_k%d_gentab(",index); + line(f, "genspec *g, /* structure to be updated */"); + line(f, "tabspec *t /* structure to be updated */"); + line(f, ") {"); + inc(f); + +#define GSET_ENTRY(KEY) if (g->KEY != og->KEY) line(f, "g->%s = %d;",#KEY,g->KEY) +#define GSET_ARRAY(KEY,IX) if (g->KEY[IX] != og->KEY[IX]) line(f, "g->%s[%d] = %d;",#KEY,IX,g->KEY[IX]) +#define TSET_ENTRY(KEY) if (t->KEY != ot->KEY) line(f, "t->%s = %d;",#KEY,t->KEY) +#define TSET_ARRAY(KEY,IX) if (t->KEY[IX] != ot->KEY[IX]) line(f, "t->%s[%d] = %d;",#KEY,IX,t->KEY[IX]) + + /* Create code that updates the genspec structure from og to g */ + GSET_ENTRY(prec); + GSET_ENTRY(id); + GSET_ENTRY(od); + GSET_ENTRY(irep); + GSET_ENTRY(orep); + GSET_ENTRY(in_signed); + GSET_ENTRY(out_signed); + + /* pixlayout structure */ + for (i = 0; i < IXDIDO; i++) { + GSET_ARRAY(in.bpch,i); + GSET_ARRAY(in.chi,i); + GSET_ARRAY(in.bov,i); + GSET_ARRAY(in.bpv,i); + } + GSET_ENTRY(in.pint); + GSET_ENTRY(in.packed); + + /* pixlayout structure */ + for (i = 0; i < IXDIDO; i++) { + GSET_ARRAY(out.bpch,i); + GSET_ARRAY(out.chi,i); + GSET_ARRAY(out.bov,i); + GSET_ARRAY(out.bpv,i); + } + GSET_ENTRY(out.pint); + GSET_ENTRY(out.packed); + + GSET_ENTRY(oopt); + GSET_ENTRY(opt); + GSET_ENTRY(itres); + GSET_ENTRY(stres); + + for (i = 0; i < 100; i++) { + GSET_ARRAY(kkeys,i); + } + for (i = 0; i < 100; i++) { + GSET_ARRAY(kdesc,i); + } + for (i = 0; i < 100; i++) { + GSET_ARRAY(kname,i); + } + + /* Create code that updates the tabspec structure from og to g */ + TSET_ENTRY(sort); + TSET_ENTRY(it_xs); + TSET_ENTRY(wo_xs); + TSET_ENTRY(it_ix); + TSET_ENTRY(it_ab); + TSET_ENTRY(it_ts); + TSET_ENTRY(ix_ab); + TSET_ENTRY(ix_es); + TSET_ENTRY(ix_eo); + TSET_ENTRY(sx_ab); + TSET_ENTRY(sx_es); + TSET_ENTRY(sx_eo); + TSET_ENTRY(sm_ts); + TSET_ENTRY(wo_ab); + TSET_ENTRY(wo_es); + TSET_ENTRY(wo_eo); + TSET_ENTRY(we_ab); + TSET_ENTRY(we_es); + TSET_ENTRY(we_eo); + TSET_ENTRY(vo_ab); + TSET_ENTRY(vo_es); + TSET_ENTRY(vo_eo); + TSET_ENTRY(vo_om); + TSET_ENTRY(im_cd); + TSET_ENTRY(im_ts); + TSET_ENTRY(im_oc); + TSET_ENTRY(im_fs); + TSET_ENTRY(im_fn); + TSET_ENTRY(im_fv); + TSET_ENTRY(im_ps); + TSET_ENTRY(im_pn); + TSET_ENTRY(im_pv); + TSET_ENTRY(ot_ts); + for (i = 0; i < IXDO; i++) { + TSET_ARRAY(ot_off, i); + } + for (i = 0; i < IXDO; i++) { + TSET_ARRAY(ot_bits,i); + } + +#undef GSET_ENTRY +#undef GSET_ARRAY +#undef TSET_ENTRY +#undef TSET_ARRAY + + /* The end of the function */ + dec(f); + line(f, "}"); + + g->opt = s_opt; /* Restore entry values */ + g->stres = s_stres; + g->itres = s_itres; + } + + /* =============================================== */ + + cr(f); cr(f); cr(f); cr(f); cr(f); cr(f); + + return frv; +} + + +/* Return bits needed to store index into table of */ +/* given resolution and dimensionality. */ +static int +calc_bits( +int dim, +int res) { + + return (int)ceil(log((double)res) * (double)dim/log(2.0) - 1e-14); +} + +/* Return maximum resolution possible given dimensionality */ +/* and number of index bits. */ +static int +calc_res( +int dim, +int bits) { + double fres; + + fres = log(2.0) * (double)bits/(double)dim; + if (fres > 12 || (fres = exp(fres)) > 65536.0) + fres = 65536.0; /* Limit to a sane value */ + return (int)(fres + 1e-14); +} + +/* Return bits needed to store a relative offset of 1, */ +/* into a table of given resolution, dimensionality , and */ +/* entry size. */ +static int +calc_obits( +int dim, +int res, +int esize) { + double off; /* Maximum diagonal offset value */ + int bits; + + if (res == 0 || res == 1) + return 0; + if (dim == 1) + off = esize; + else { + off = (double)esize * floor(exp(log((double)res) * dim - log(res-1.0))); + } + + bits = (int)ceil(log(off)/log(2.0) - 1e-14); + return bits; +} + +/* Return maximum resolution possible given dimensionality */ +/* number of index bits, and entry size */ +static int +calc_ores( +int dim, +int bits, +int esize) { + int res; + + /* Find resolution. Stop at arbitrary 65536 */ + for (res = 1; res < 65537; res++) { + int bn; + bn = calc_obits(dim, res, esize); + if (bn > bits) { + return res-1; + } + } + return res-1; +} + + + +/* Output the introductory comments */ +static void +doheader( + fileo *f +) { + genspec *g = f->g; + tabspec *t = f->t; + mach_arch *a = f->a; + int e; + + /* - - - - - - - - - - - - */ + /* Output file title block */ + line(f,"/* Integer Multi-Dimensional Interpolation */"); + line(f,"/* Interpolation Kernel Code */"); + line(f,"/* Generated by cgen */"); + line(f,"/* Copyright 2000 - 2007 Graeme W. Gill */"); + line(f,"/* All rights reserved. */"); + line(f,"/* This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- */\n"); + line(f,"/* see the License.txt file for licencing details.*/\n"); + cr(f); + + /* - - - - - - - - - - - - */ + /* Output the specification */ + line(f,"/*"); + line(f," Interpolation kernel specs:"); + cr(f); + line(f," Input channels per pixel = %d",g->id); + for (e = 0; e < g->id; e++) { + line(f," Input channel %d bits = %d",e, g->in.bpch[e]); + line(f," Input channel %d increment = %d",e, g->in.chi[e]); + } + if (g->in.pint != 0) + line(f," Input is channel interleaved"); + else + line(f," Input is plane interleaved"); + + if (g->in.packed != 0) + line(f," Input channels are packed into one word"); + else + line(f," Input channels are separate words"); + + if (t->it_ix) + line(f," Input value extraction is done in input table lookup"); + cr(f); + + line(f," Output channels per pixel = %d",g->od); + for (e = 0; e < g->od; e++) { + line(f," Output channel %d bits = %d",e, g->out.bpch[e]); + line(f," Output channel %d increment = %d",e, g->out.chi[e]); + if (g->oopt & OOPT(oopts_check,e)) + line(f," Output channel %d has value check",e); + if (g->oopt & OOPT(oopts_skip,e)) + line(f," Output channel %d has skip available",e); + } + if (g->out.pint != 0) + line(f," Output is channel interleaved"); + else + line(f," Output is plane interleaved"); + if (g->out.packed != 0) + line(f," Output channels are packed into one word"); + else + line(f," Output channels are separate words"); + cr(f); + + line(f," Basic Internal precision bits = %d",g->prec); + if (t->sort) + line(f," Weight+voffset bits = %d",t->sx_ab); + else + line(f," Simplex table index bits = %d",t->sx_ab); + line(f," Interpolation table index bits = %d",t->ix_ab); + if (!t->sort) + line(f," Simplex table max resolution = %d",f->sxmxres); + line(f," Interpolation table max resolution = %d",f->ixmxres); + cr(f); + line(f," Processing direction is %s",g->opt & opts_bwd ? "backwards" : "forwards" ); + line(f," Input stride is %ssupported",g->opt & opts_istride ? "" : "not " ); + line(f," Output stride is %ssupported",g->opt & opts_ostride ? "" : "not " ); + if (g->opt & opts_splx_sort) + line(f," Prefer simplex over sort algorithm"); + if (g->opt & opts_sort_splx) + line(f," Prefer sort over simplex"); + line(f," */"); + cr(f); + + /* - - - - - - - - - - - - */ + line(f,"/*"); + line(f," Machine architecture specs:"); + cr(f); + if (a->bigend != 0) + line(f," Big Endian"); + else + line(f," Little endian"); + + if (a->uwa != 0) + line(f," Using maximum sized memory accesses where possible"); + else + line(f," Reading and writing pixel values separately"); + + line(f," Pointer size = %d bits",a->pbits); + cr(f); + + for (e = 0; e < a->nords; e++) { + line(f," Ordinal size %2d bits is known as '%s'", + a->ords[e].bits,a->ords[e].name); + } + line(f," Natural ordinal is '%s'", a->ords[a->natord].name); + cr(f); + + for (e = 0; e < a->nints; e++) { + line(f," Integer size %2d bits is known as '%s'", + a->ints[e].bits,a->ints[e].name); + } + line(f," Natural integer is '%s'", a->ints[a->natint].name); + cr(f); + + line(f," */"); + cr(f); +} + + +/* ---------------------------------------- */ +/* Architecture support */ +/* Find an ordinal with at least bits size */ +/* Return -1 if failed */ +int findord( +fileo *f, +int bits +) { + mach_arch *a = f->a; + int i; + + for (i = 0; i < a->nords; i++) { + if (a->ords[i].bits >= bits) + return i; + } + return -1; +} + +/* Round ordinal type up to natural size */ +int nord( + fileo *f, + int ov +) { + if (ov >= 0 && ov < f->a->natord) + ov = f->a->natord; + return ov; +} + +/* Find an ordinal with at least bits size, */ +/* or natural size, whichever is greater. */ +/* Return -1 if failed */ +int findnord( + fileo *f, + int bits +) { + int ov; + + ov = findord(f, bits); + ov = nord(f, ov); + return ov; +} + +/* Find an integer with at least bits size */ +/* Return -1 if failed */ +int findint( + fileo *f, + int bits +) { + mach_arch *a = f->a; + int i; + + for (i = 0; i < a->nints; i++) { + if (a->ints[i].bits >= bits) + return i; + } + return -1; +} + +/* Round integer type up to natural size */ +int nint( + fileo *f, + int iv +) { + if (iv >= 0 && iv < f->a->natint) + iv = f->a->natint; + return iv; +} + +/* Find an interger with at least bits size, */ +/* or natural size, whichever is greater. */ +/* Return -1 if failed */ +int findnint( + fileo *f, + int bits +) { + int iv; + + iv = findint(f, bits); + iv = nint(f, iv); + return iv; +} + + +/* ------------------------------------ */ +/* File output support */ + +/* Output a line to the file (including trailing \n) */ +void +line(fileo *f, char *fmt, ...) +{ + int i; + va_list args; + + /* Indent to the correct level */ + for (i = 0; i < f->indt; i++) + fprintf(f->of," "); + + va_start(args, fmt); + vfprintf(f->of, fmt, args); + va_end(args); + fprintf(f->of, "\n"); +} + +/* Output the start of a line to the file) */ +void +sline(fileo *f, char *fmt, ...) +{ + int i; + va_list args; + + /* Indent to the correct level */ + for (i = 0; i < f->indt; i++) + fprintf(f->of," "); + + va_start(args, fmt); + vfprintf(f->of, fmt, args); + va_end(args); +} + +/* Output the middle of a line to the file) */ +void +mline(fileo *f, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(f->of, fmt, args); + va_end(args); +} + +/* Output the end of a line to the file (including trailing \n) */ +void +eline(fileo *f, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(f->of, fmt, args); + va_end(args); + fprintf(f->of, "\n"); +} + +/* Output a line to the file (including trailing \n) */ +/* No indent */ +void +niline(fileo *f, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(f->of, fmt, args); + va_end(args); + fprintf(f->of, "\n"); +} + +/* Output one line and increment indent */ +void lineinc(fileo *f, char *fmt, ...) { + int i; + va_list args; + + /* Indent to the correct level */ + for (i = 0; i < f->indt; i++) + fprintf(f->of," "); + + va_start(args, fmt); + vfprintf(f->of, fmt, args); + va_end(args); + fprintf(f->of, "\n"); + f->indt++; +} + +/* Decrement indent and output one line */ +void decline(fileo *f, char *fmt, ...) { + int i; + va_list args; + + f->indt--; + /* Indent to the correct level */ + for (i = 0; i < f->indt; i++) + fprintf(f->of," "); + + va_start(args, fmt); + vfprintf(f->of, fmt, args); + va_end(args); + fprintf(f->of, "\n"); +} + + +/* ------------------------------------ */ + + + + diff --git a/imdi/ctest.c b/imdi/ctest.c new file mode 100644 index 0000000..caf0692 --- /dev/null +++ b/imdi/ctest.c @@ -0,0 +1,156 @@ + +#include +#include +#include + +/* Test frame for cgen.c IMDI generation code */ +/* + * Copyright 2000 - 2006 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. + */ + +#include "imdi.h" +#include "imdi_tab.h" + + +int +main(void) { + int rv; + genspec gs, ogs; + tabspec ts, ots; + mach_arch ar; + FILE *kcode; + + /* Zero out the gen and tabspecs, to give diff a place to start */ + memset((void *)&ogs, 0, sizeof(genspec)); + memset((void *)&gs, 0, sizeof(genspec)); + memset((void *)&ots, 0, sizeof(tabspec)); + memset((void *)&ts, 0, sizeof(tabspec)); + + printf("Testing gen_c_kernel\n"); + + if ((kcode = fopen("imdi_k99.c","w")) == NULL) { + printf("Couldn't open 'imdi_k99.c'\n"); + return -1; + } + + /* Setup an interpolation kernel specification */ + gs.id = 4; /* Number of input dimensions */ + + gs.in.pint = 1; /* Flag - nonz if pixel interleaved */ + gs.in.packed = 0; /* Flag - nonz if channels packed into one read */ + + gs.in.bpch[0] = 8; /* Bits per channel */ + gs.in.chi[0] = 4; /* Channel increment */ + gs.in.bov[0] = 0; /* Bit offset to value within channel */ + gs.in.bpv[0] = 8; /* Bits per value within channel */ + + gs.in.bpch[1] = 8; /* Bits per channel */ + gs.in.chi[1] = 4; /* Channel increment */ + gs.in.bov[1] = 0; /* Bit offset to value within channel */ + gs.in.bpv[1] = 8; /* Bits per value within channel */ + + gs.in.bpch[2] = 8; /* Bits per channel */ + gs.in.chi[2] = 4; /* Channel increment */ + gs.in.bov[2] = 0; /* Bit offset to value within channel */ + gs.in.bpv[2] = 8; /* Bits per value within channel */ + + gs.in.bpch[3] = 8; /* Bits per channel */ + gs.in.chi[3] = 4; /* Channel increment */ + gs.in.bov[3] = 0; /* Bit offset to value within channel */ + gs.in.bpv[3] = 8; /* Bits per value within channel */ + + gs.od = 1; /* Number of output dimensions */ + + gs.out.pint = 1; /* Flag - nonz if pixel interleaved */ + gs.out.packed = 0; /* Flag - nonz if channels packed into one write */ + + gs.out.bpch[0] = 8; /* Bits per channel */ + gs.out.chi[0] = 1; /* Channel increment */ + gs.out.bov[0] = 0; /* Bit offset to value within channel */ + gs.out.bpv[0] = 8; /* Bits per value within channel */ + + gs.out.bpch[1] = 8; /* Bits per channel */ + gs.out.chi[1] = 4; /* Channel increment */ + gs.out.bov[1] = 0; /* Bit offset to value within channel */ + gs.out.bpv[1] = 8; /* Bits per value within channel */ + + gs.out.bpch[2] = 8; /* Bits per channel */ + gs.out.chi[2] = 4; /* Channel increment */ + gs.out.bov[2] = 0; /* Bit offset to value within channel */ + gs.out.bpv[2] = 8; /* Bits per value within channel */ + + gs.out.bpch[3] = 8; /* Bits per channel */ + gs.out.chi[3] = 4; /* Channel increment */ + gs.out.bov[3] = 0; /* Bit offset to value within channel */ + gs.out.bpv[3] = 8; /* Bits per value within channel */ + + gs.out.bpch[4] = 8; /* Bits per channel */ + gs.out.chi[4] = 4; /* Channel increment */ + gs.out.bov[4] = 0; /* Bit offset to value within channel */ + gs.out.bpv[4] = 8; /* Bits per value within channel */ + + gs.out.bpch[5] = 8; /* Bits per channel */ + gs.out.chi[5] = 4; /* Channel increment */ + gs.out.bov[5] = 0; /* Bit offset to value within channel */ + gs.out.bpv[5] = 8; /* Bits per value within channel */ + + gs.opt = opts_none; /* Direction and stride options */ + + gs.prec = 8; /* Precsision needed */ + gs.itres = 16; /* Interpolation table resolution */ + gs.stres = 17; /* Simplex table resolution */ + + /* Setup a machine architecture */ + + ar.bigend = 0; /* Non-zero if this is a bigendian architecture */ + ar.uwa = 0; /* Use wide memory access */ + ar.shfm = 0; /* Use shifts to mask values */ + ar.oscale = 8; /* Has scaled indexing up to * 8 */ + ar.smmul = 0; /* Doesn't have fast small multiply for index scaling */ + + ar.pbits = 32; /* Number of bits in a pointer */ + + ar.nords = 3; /* Number of ord types */ + ar.ords[0].bits = 8; + ar.ords[0].name = "unsigned char"; + ar.ords[0].align = 1; + ar.ords[1].bits = 16; + ar.ords[1].name = "unsigned short"; + ar.ords[1].align = 1; + ar.ords[2].bits = 32; + ar.ords[2].name = "unsigned int"; + ar.ords[2].align = 1; +#ifdef ALLOW64 + ar.ords[3].bits = 64; + ar.ords[3].name = "unsigned longlong"; + ar.ords[3].align = 0; +#endif /* ALLOW64 */ + ar.natord = 2; /* Most natural type */ + + ar.nints = 3; /* Number of int types */ + ar.ints[0].bits = 8; + ar.ints[0].name = "signed char"; + ar.ints[0].align = 1; + ar.ints[1].bits = 16; + ar.ints[1].name = "short"; + ar.ints[1].align = 1; + ar.ints[2].bits = 32; + ar.ints[2].name = "int"; + ar.ints[2].align = 1; +#ifdef ALLOW64 + ar.ints[3].bits = 64; + ar.ints[3].name = "longlong"; + ar.ints[3].align = 0; +#endif /* ALLOW64 */ + ar.natint = 2; /* Most natural type */ + + rv = gen_c_kernel(&gs, &ts, &ar, kcode, 99, &ogs, &ots); + + fclose(kcode); + + return 0; +} diff --git a/imdi/greytiff.c b/imdi/greytiff.c new file mode 100644 index 0000000..af52013 --- /dev/null +++ b/imdi/greytiff.c @@ -0,0 +1,575 @@ + +/* + * Convert a TIFF to monochrome in a colorimetrically correct way. + * + * Author: Graeme W. Gill + * Date: 01/8/29 + * Version: 1.00 + * + * Copyright 2000, 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. + */ + +/* + * Thanks to Neil Okamoto for the 16 bit TIFF mods. + */ + +/* TTBD: + * + */ + +/* + + This program is a framework that exercises the + IMDI code. It can also do the conversion using the + floating point code in ICCLIB as a reference. + + */ + +#include +#include +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#include "tiffio.h" +#include "icc.h" +#include "numlib.h" +#include "xicc.h" +#include "imdi.h" + +#undef DO_CHECK /* Do floating point check */ + +void usage(void) { + fprintf(stderr,"Convert a TIFF file to monochrome using an ICC device profile, V%s\n",ARGYLL_VERSION_STR); + fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); + fprintf(stderr,"usage: greytiff [-v level] profile.icm infile.tif outfile.tif\n"); + fprintf(stderr," -v Verbose\n"); + fprintf(stderr," -p Use slow precise correction\n"); + fprintf(stderr," -j Use CIECAM02\n"); + exit(1); +} + +/* Convert an ICC colorspace to the corresponding TIFF Photometric tag */ +/* return 0xffff if not possible. */ + +int +ColorSpaceSignature2TiffPhotometric( +icColorSpaceSignature cspace +) { + switch(cspace) { + case icSigGrayData: + return PHOTOMETRIC_MINISBLACK; + case icSigRgbData: + return PHOTOMETRIC_RGB; + case icSigCmykData: + return PHOTOMETRIC_SEPARATED; + case icSigYCbCrData: + return PHOTOMETRIC_YCBCR; + case icSigLabData: + return PHOTOMETRIC_CIELAB; + + case icSigXYZData: + case icSigLuvData: + case icSigYxyData: + case icSigHsvData: + case icSigHlsData: + case icSigCmyData: + case icSig2colorData: + case icSig3colorData: + case icSig4colorData: + case icSig5colorData: + case icSigMch5Data: + case icSig6colorData: + case icSigMch6Data: + case icSig7colorData: + case icSigMch7Data: + case icSig8colorData: + case icSigMch8Data: + case icSig9colorData: + case icSig10colorData: + case icSig11colorData: + case icSig12colorData: + case icSig13colorData: + case icSig14colorData: + case icSig15colorData: + default: + return 0xffff; + } + return 0xffff; +} + +char * +Photometric2str( +int pmtc +) { + static char buf[80]; + switch (pmtc) { + case PHOTOMETRIC_MINISWHITE: + return "Subtractive Gray"; + case PHOTOMETRIC_MINISBLACK: + return "Additive Gray"; + case PHOTOMETRIC_RGB: + return "RGB"; + case PHOTOMETRIC_PALETTE: + return "Indexed"; + case PHOTOMETRIC_MASK: + return "Transparency Mask"; + case PHOTOMETRIC_SEPARATED: + return "CMYK"; + case PHOTOMETRIC_YCBCR: + return "YCbCr"; + case PHOTOMETRIC_CIELAB: + return "CIELab"; + case PHOTOMETRIC_LOGL: + return "CIELog2L"; + case PHOTOMETRIC_LOGLUV: + return "CIELog2Luv"; + } + sprintf(buf,"Unknonw Tag %d",pmtc); + return buf; +} + +/* Callbacks used to initialise imdi */ + +/* Context for imdi setup callbacks */ +typedef struct { + int id, od; + icxLuBase *flu; /* Device -> Jab/Lab */ + icxLuBase *blu; /* Jab/Lab -> Device */ +} sucntx; + +/* Input curve function */ +void input_curve( + void *cntx, + double *out_vals, + double *in_vals +) { + sucntx *rx = (sucntx *)cntx; + int e; + + for (e = 0; e < rx->id; e++) + out_vals[e] = in_vals[e]; +} + +/* Multi-dim table function */ +void md_table( +void *cntx, +double *out_vals, +double *in_vals +) { + sucntx *rx = (sucntx *)cntx; + double Lab[3]; + + rx->flu->lookup(rx->flu, Lab, in_vals); + Lab[1] = Lab[2] = 0.0; + rx->blu->lookup(rx->blu, out_vals, Lab); +} + + +/* Output curve function */ +void output_curve( + void *cntx, + double *out_vals, + double *in_vals +) { + sucntx *rx = (sucntx *)cntx; + int e; + + for (e = 0; e < rx->od; e++) + out_vals[e] = in_vals[e]; +} + +int +main(int argc, char *argv[]) { + int fa,nfa; /* argument we're looking at */ + char prof_name[100]; + char in_name[100]; + char out_name[100]; + + icmFile *p_fp; + icc *icco; + xicc *xicco; + int verb = 0; + int slow = 0; + int rv = 0; + icColorSpaceSignature pcsor = icSigLabData; + + TIFF *rh = NULL, *wh = NULL; + int x, y, width, height; /* Size of image */ + uint16 samplesperpixel, bitspersample; + uint16 pconfig, photometric, pmtc; + uint16 resunits; + float resx, resy; + tdata_t *inbuf, *outbuf, *checkbuf; + + icColorSpaceSignature ins; /* Type of input spaces */ + int inn; /* Number of device components */ + + /* IMDI */ + imdi *s = NULL; + sucntx su; /* Setup context */ + unsigned char *inp[MAX_CHAN]; + unsigned char *outp[MAX_CHAN]; + +#ifdef DO_CHECK + /* Error check */ + int mxerr = 0; + double avgerr = 0.0; + double avgcount = 0.0; +#endif /* DO_CHECK */ + + if (argc < 2) + 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 */ + } + } + } + + if (argv[fa][1] == '?') + usage(); + + /* Slow, Precise */ + else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') { + slow = 1; + } + + /* Use CIECAM02 */ + else if (argv[fa][1] == 'j' || argv[fa][1] == 'J') { + pcsor = icxSigJabData; + } + + /* Verbosity */ + else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') { + verb = 1; + } + + else + usage(); + } else + break; + } + + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(prof_name,argv[fa++]); + + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(in_name,argv[fa++]); + + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(out_name,argv[fa++]); + + /* - - - - - - - - - - - - - - - - */ + /* Open up the profile for reading */ + if ((p_fp = new_icmFileStd_name(prof_name,"r")) == NULL) + error ("Can't open file '%s'",prof_name); + + if ((icco = new_icc()) == NULL) + error ("Creation of ICC object failed"); + + /* Wrap with an expanded icc */ + if ((xicco = new_xicc(icco)) == NULL) + error ("Creation of xicc failed"); + + if ((rv = icco->read(icco,p_fp,0)) != 0) + error ("%d, %s",rv,icco->err); + + if (verb) { + icmFile *op; + if ((op = new_icmFileStd_fp(stdout)) == NULL) + error ("Can't open stdout"); + icco->header->dump(icco->header, op, 1); + op->del(op); + } + + /* Check that the profile is appropriate */ + if (icco->header->deviceClass != icSigInputClass + && icco->header->deviceClass != icSigDisplayClass + && icco->header->deviceClass != icSigOutputClass + && icco->header->deviceClass != icSigColorSpaceClass) /* For sRGB etc. */ + error("Profile isn't a device profile"); + + /* Get a expanded color conversion object */ + if ((su.flu = xicco->get_luobj(xicco, ICX_CLIP_NEAREST, icmFwd, icRelativeColorimetric, pcsor, icmLuOrdNorm, NULL, NULL)) == NULL) + error ("%d, %s",xicco->errc, xicco->err); + + /* Get details of conversion (Arguments may be NULL if info not needed) */ + su.flu->spaces(su.flu, &ins, &inn, NULL, NULL, NULL, NULL, NULL, NULL); + + su.id = inn; + su.od = inn; + + /* Get a bwd conversion object */ + if ((su.blu = xicco->get_luobj(xicco, ICX_CLIP_NEAREST, icmBwd, icRelativeColorimetric, pcsor, icmLuOrdNorm, NULL, NULL)) == NULL) + error ("%d, %s",xicco->errc, xicco->err); + + /* - - - - - - - - - - - - - - - */ + /* Open up input tiff file ready for reading */ + /* Got arguments, so setup to process the file */ + if ((rh = TIFFOpen(in_name, "r")) == NULL) + error("error opening read file '%s'",in_name); + + TIFFGetField(rh, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetField(rh, TIFFTAG_IMAGELENGTH, &height); + + TIFFGetField(rh, TIFFTAG_BITSPERSAMPLE, &bitspersample); + if (bitspersample != 8 && bitspersample != 16) { + error("TIFF Input file must be 8 or 16 bit/channel"); + } + + TIFFGetField(rh, TIFFTAG_PHOTOMETRIC, &photometric); + if ((pmtc = ColorSpaceSignature2TiffPhotometric(ins)) == 0xffff) + error("ICC input colorspace '%s' can't be handled by a TIFF file!", + icm2str(icmColorSpaceSignature, ins)); + if (pmtc != photometric) + error("ICC input colorspace '%s' doesn't match TIFF photometric '%s'!", + icm2str(icmColorSpaceSignature, ins), Photometric2str(photometric)); + + TIFFGetField(rh, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel); + if (inn != samplesperpixel) + error ("TIFF Input file has %d input channels mismatched to colorspace '%s'", + samplesperpixel, icm2str(icmColorSpaceSignature, ins)); + + TIFFGetField(rh, TIFFTAG_PLANARCONFIG, &pconfig); + if (pconfig != PLANARCONFIG_CONTIG) + error ("TIFF Input file must be planar"); + + TIFFGetField(rh, TIFFTAG_RESOLUTIONUNIT, &resunits); + TIFFGetField(rh, TIFFTAG_XRESOLUTION, &resx); + TIFFGetField(rh, TIFFTAG_YRESOLUTION, &resy); + + /* - - - - - - - - - - - - - - - */ + if ((wh = TIFFOpen(out_name, "w")) == NULL) + error("Can\'t create TIFF file '%s'!",out_name); + + TIFFSetField(wh, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(wh, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(wh, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(wh, TIFFTAG_SAMPLESPERPIXEL, inn); + TIFFSetField(wh, TIFFTAG_BITSPERSAMPLE, bitspersample); + TIFFSetField(wh, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + if ((pmtc = ColorSpaceSignature2TiffPhotometric(ins)) == 0xffff) + error("TIFF file can't handle output colorspace '%s'!", + icm2str(icmColorSpaceSignature, ins)); + TIFFSetField(wh, TIFFTAG_PHOTOMETRIC, pmtc); + TIFFSetField(wh, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + if (resunits) { + TIFFSetField(wh, TIFFTAG_RESOLUTIONUNIT, resunits); + TIFFSetField(wh, TIFFTAG_XRESOLUTION, resx); + TIFFSetField(wh, TIFFTAG_YRESOLUTION, resy); + } + TIFFSetField(wh, TIFFTAG_IMAGEDESCRIPTION, "Color corrected by Argyll"); + + /* - - - - - - - - - - - - - - - */ + /* Setup the imdi */ + + if (!slow) { + s = new_imdi( + inn, /* Number of input dimensions */ + inn, /* Number of output dimensions */ + bitspersample == 8 ? pixint8 : pixint16, + /* Output pixel representation */ + 0x0, /* Treat every channel as unsigned */ + NULL, /* No raster to callback channel mapping */ + prec_min, /* Minimum of input and output precision */ + bitspersample == 8 ? pixint8 : pixint16, + 0x0, /* Treat every channel as unsigned */ + NULL, /* No raster to callback channel mapping */ + 17, /* Desired table resolution. 33 is also a good number */ + oopts_none, /* Desired per channel output options */ + NULL, /* Output channel check values */ + opts_none, /* Desired processing direction and stride support */ + input_curve, /* Callback functions */ + md_table, + output_curve, + (void *)&su /* Context to callbacks */ + ); + + if (s == NULL) + error("new_imdi failed"); + } + + /* - - - - - - - - - - - - - - - */ + /* Process colors to translate */ + /* (Should fix this to process a group of lines at a time ?) */ + + inbuf = _TIFFmalloc(TIFFScanlineSize(rh)); + outbuf = _TIFFmalloc(TIFFScanlineSize(wh)); + checkbuf = _TIFFmalloc(TIFFScanlineSize(wh)); + + inp[0] = (unsigned char *)inbuf; + outp[0] = (unsigned char *)outbuf; + + if (!slow) { /* Fast */ + for (y = 0; y < height; y++) { + + /* Read in the next line */ + if (TIFFReadScanline(rh, inbuf, y, 0) < 0) + error ("Failed to read TIFF line %d",y); + + /* Do fast conversion */ + s->interp(s, (void **)outp, 0, (void **)inp, 0, width); + +#ifdef DO_CHECK + /* Do floating point conversion */ + for (x = 0; x < width; x++) { + int i; + double in[MAX_CHAN], out[MAX_CHAN]; + double Lab[3]; + + if (bitspersample == 8) + for (i = 0; i < inn; i++) + in[i] = ((unsigned char *)inbuf)[x * inn + i]/255.0; + else + for (i = 0; i < inn; i++) + in[i] = ((unsigned short *)inbuf)[x * inn + i]/65535.0; + + if ((rv = su.flu->lookup(su.flu, Lab, in)) > 1) + error ("%d, %s",icco->errc,icco->err); + + Lab[1] = Lab[2] = 0.0; + + if ((rv = su.blu->lookup(su.blu, out, Lab)) > 1) + error ("%d, %s",icco->errc,icco->err); + + if (bitspersample == 8) + for (i = 0; i < inn; i++) + ((unsigned char *)checkbuf)[x * inn + i] = (int)(out[i] * 255.0 + 0.5); + else + for (i = 0; i < inn; i++) + ((unsigned short *)checkbuf)[x * inn + i] = (int)(out[i] * 65535.0 + 0.5); + } + /* Compute the errors */ + for (x = 0; x < (width * inn); x++) { + int err; + + if (bitspersample == 8) + err = ((unsigned char *)outbuf)[x] - ((unsigned char *)checkbuf)[x]; + else + err = ((unsigned short *)outbuf)[x] - ((unsigned short *)checkbuf)[x]; + if (err < 0) + err = -err; + if (err > mxerr) + mxerr = err; + avgerr += (double)err; + avgcount++; + } +#endif /* DO_CHECK */ + + if (TIFFWriteScanline(wh, outbuf, y, 0) < 0) + error ("Failed to write TIFF line %d",y); + + } + + } else { /* Slow but precise */ + if (bitspersample == 8) { + for (y = 0; y < height; y++) { + + /* Read in the next line */ + if (TIFFReadScanline(rh, inbuf, y, 0) < 0) + error ("Failed to read TIFF line %d",y); + + /* Do floating point conversion */ + for (x = 0; x < width; x++) { + int i; + double in[MAX_CHAN], out[MAX_CHAN]; + double Lab[3]; + + for (i = 0; i < inn; i++) { + in[i] = ((unsigned char *)inbuf)[x * inn + i]/255.0; + } + + if ((rv = su.flu->lookup(su.flu, Lab, in)) > 1) + error ("%d, %s",icco->errc,icco->err); + + Lab[1] = Lab[2] = 0.0; + + if ((rv = su.blu->lookup(su.blu, out, Lab)) > 1) + error ("%d, %s",icco->errc,icco->err); + + for (i = 0; i < inn; i++) { + ((unsigned char *)outbuf)[x * inn + i] = (int)(out[i] * 255.0 + 0.5); + } + } + if (TIFFWriteScanline(wh, outbuf, y, 0) < 0) + error ("Failed to write TIFF line %d",y); + } + } else if (bitspersample == 16) { + for (y = 0; y < height; y++) { + + /* Read in the next line */ + if (TIFFReadScanline(rh, inbuf, y, 0) < 0) + error ("Failed to read TIFF line %d",y); + + /* Do floating point conversion */ + for (x = 0; x < width; x++) { + int i; + double in[MAX_CHAN], out[MAX_CHAN]; + double Lab[3]; + + for (i = 0; i < inn; i++) { + in[i] = ((unsigned short *)inbuf)[x * inn + i]/65535.0; + } + + if ((rv = su.flu->lookup(su.flu, Lab, in)) > 1) + error ("%d, %s",icco->errc,icco->err); + + Lab[1] = Lab[2] = 0.0; + + if ((rv = su.blu->lookup(su.blu, out, Lab)) > 1) + error ("%d, %s",icco->errc,icco->err); + + for (i = 0; i < inn; i++) { + ((unsigned short *)outbuf)[x * inn + i] = (int)(out[i] * 65535.0 + 0.5); + } + } + if (TIFFWriteScanline(wh, outbuf, y, 0) < 0) + error ("Failed to write TIFF line %d",y); + } + } + } + + +#ifdef DO_CHECK + printf("Worst error = %d bits, average error = %f bits\n", mxerr, avgerr/avgcount); + if (bitspersample == 8) + printf("Worst error = %f%%, average error = %f%%\n", + mxerr/2.55, avgerr/(2.55 * avgcount)); + else + printf("Worst error = %f%%, average error = %f%%\n", + mxerr/655.35, avgerr/(655.35 * avgcount)); +#endif /* DO_CHECK */ + + /* Done with lookup object */ + if (s != NULL) + s->del(s); + su.flu->del(su.flu); + su.blu->del(su.blu); + xicco->del(xicco); + icco->del(icco); + p_fp->del(p_fp); + + TIFFClose(rh); /* Close Input file */ + TIFFClose(wh); /* Close Output file */ + + return 0; +} + diff --git a/imdi/imdi.c b/imdi/imdi.c new file mode 100644 index 0000000..8174c8e --- /dev/null +++ b/imdi/imdi.c @@ -0,0 +1,605 @@ + +/* Integer Multi-Dimensional Interpolation */ + +/* + * Copyright 2000 - 2007 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. + */ + +/* + * This is the implementation of the run time code. + */ + + +#include +#include +#include +#include +#include + +#include "imdi.h" +#include "imdi_tab.h" +#include "imdi_k.h" /* Declaration of all the kernel functions */ + +#undef VERBOSE +#undef VVERBOSE + +static unsigned int imdi_get_check(imdi *im); +static void imdi_reset_check(imdi *im); +static void imdi_info(imdi *s, unsigned long *size, int *gres, int *sres); +static void imdi_del(imdi *im); +static void interp_match(imdi *s, void **outp, int outst, void **inp, int inst, + unsigned int npixels); + + +/* Create a new imdi */ +/* Return NULL if request is not supported */ +/* Note that we use the high level pixel layout description to locate */ +/* a suitable run-time. */ +imdi *new_imdi( + int id, /* Number of input dimensions */ + int od, /* Number of output lookup dimensions */ + /* Number of output channels written = od - no. of oopt skip flags */ + imdi_pixrep in, /* Input pixel representation */ + int in_signed, /* Bit flag per channel, NZ if treat as signed */ + int *inm, /* Input raster channel to callback channel mapping, NULL for none. */ + imdi_iprec iprec, /* Internal processing precision */ + imdi_pixrep out, /* Output pixel representation */ + int out_signed, /* Bit flag per channel, NZ if treat as signed */ + int *outm, /* Output raster channel to callback channel mapping, NULL for none. */ + /* Mapping must include skipped channels. */ + int res, /* Desired table resolution */ + imdi_ooptions oopt, /* Output per channel options (by callback channel) */ + unsigned int *checkv, /* Output channel check values (by callback channel, NULL == 0) */ + imdi_options opt, /* Direction and stride options */ + + /* Callbacks to lookup the imdi table values. */ + /* (Skip output channels are looked up) */ + void (*input_curves) (void *cntx, double *out_vals, double *in_vals), + void (*md_table) (void *cntx, double *out_vals, double *in_vals), + void (*output_curves)(void *cntx, double *out_vals, double *in_vals), + void *cntx /* Context to callbacks */ +) { + int i; + int prec; /* Target internal precision */ + int bk = -1; /* Best table */ + int bfig = 0x7fffffff; /* Best tables figure of merit */ + int bstres = 0; /* Best tables target stres */ + genspec gs; /* Generation specification */ + tabspec ts; /* Table specifications */ + genspec bgs; /* Best gen spec */ + tabspec bts; /* Best tab spec */ + imdi_conv bcnv = conv_none; /* Best tables conversion flags */ + imdi_ooptions Ooopt; /* oopt re-aranged to correspond to output channel index */ + + imdi *im; + + /* Compute the Output channel index oopt mask */ + if (outm == NULL) + Ooopt = oopt; + else { + int ff; + Ooopt = 0; + for (i = 0; i < od; i++) { + ff = OOPTX(oopt,outm[i]); + Ooopt |= OOPT(ff,i); + } + } + +#ifdef VERBOSE + printf("new_imdi called with id %d, od %d, res %d Ooopt 0x%x, opt 0x%x\n", id, od, res, Ooopt, opt); + printf("about to checking %d kernels\n", no_kfuncs); +#endif + + /* Figure out the internal precision requested */ + COMPUTE_IPREC(prec, in, iprec, out) + + /* Zero out the gen and tabspecs, to give diff a place to start */ + memset((void *)&gs, 0, sizeof(genspec)); + memset((void *)&ts, 0, sizeof(tabspec)); + + /* The first thing to do is see if there is an available kernel function */ + for (i = 0; i < no_kfuncs; i++) { + int stres; /* Computed stres needed */ + imdi_conv cnv = conv_none; /* Conversions needed for this choice */ + int fig; /* Figure of merit - smaller is better */ + ktable[i].gentab(&gs, &ts); /* Udate the kernel functions genspec and tabspec */ + +#ifdef VERBOSE + printf("\n"); + printf("kernel %d has id %d, od %d, irep %d orep %d oopt 0x%x, opt 0x%x\n", + i, gs.id, gs.od, gs.irep, gs.orep, gs.oopt, gs.opt); + printf("Input req is id %d, od %d, irep %d orep %d, oopt 0x%x, opt 0x%x\n", + id, od, in, out, Ooopt, opt); +#endif + /* First check mandatory things */ + if (!( + id == gs.id /* Input dimension */ + && od == gs.od /* Output dimension */ + )) { +#ifdef VVERBOSE + printf(" Input or Output dimension mismatch\n"); +#endif + continue; + } + + /* Check if we have an exact or conversion match for input stride */ + if (!( + (opt & opts_istride) == (gs.opt & opts_istride) + || (!(opt & opts_istride) && (gs.opt & opts_istride)) + )) { +#ifdef VVERBOSE + printf(" Input stride mismatch\n"); +#endif + continue; + } + + /* Check if we have an exact or conversion match for output stride */ + if (!( + (opt & opts_ostride) == (gs.opt & opts_ostride) + || (!(opt & opts_ostride) && (gs.opt & opts_ostride)) + )) { +#ifdef VVERBOSE + printf(" Output stride mismatch\n"); +#endif + continue; + } + + /* Check if we have an exact or conversion match for input representation */ + if (!( + in == gs.irep + || (in == pixint8 && gs.irep == planeint8 && (gs.opt & opts_istride)) + || (in == pixint16 && gs.irep == planeint16 && (gs.opt & opts_istride)) + )) { +#ifdef VVERBOSE + printf(" Input representation mismatch\n"); +#endif + continue; + } + + /* Check if we have an exact or conversion match for output representation */ + if (!( + out == gs.orep + || (out == pixint8 && gs.orep == planeint8 && (gs.opt & opts_ostride)) + || (out == pixint16 && gs.orep == planeint16 && (gs.opt & opts_ostride)) + )) { +#ifdef VVERBOSE + printf(" Output representation mismatch\n"); +#endif + continue; + } + + /* See if we have the output per channel options needed */ + if (!((Ooopt & gs.oopt) == Ooopt)) { +#ifdef VVERBOSE + printf(" Output per channel options mismatch\n"); +#endif + continue; + } + + /* See if we have an exact or conversion match for reverse direction */ + if (!( + ((!(opt & opts_bwd) && !(opt & opts_fwd))) /* Don't care */ + || (((opt & opts_bwd) && (gs.opt & opts_bwd))) /* Match */ + || (((opt & opts_fwd) && (gs.opt & opts_fwd))) /* Match */ + || ((gs.opt & opts_istride) && (gs.opt & opts_ostride)) /* Can convert */ + )) { +#ifdef VVERBOSE + printf(" Direction mismatch\n"); +#endif + continue; + } + +#ifdef VERBOSE + printf(" found match\n"); +#endif + fig = 0; + + /* Apply penalty if we will have to do a conversion match */ + if ((opt & opts_istride) != (gs.opt & opts_istride)) { + cnv |= conv_istr; + fig += 1000; +#ifdef VERBOSE + printf(" needs input stride conversion though\n"); +#endif + } + + if ((opt & opts_ostride) != (gs.opt & opts_ostride)) { + cnv |= conv_ostr; + fig += 1000; +#ifdef VERBOSE + printf(" needs output stride conversion though\n"); +#endif + } + + if (in != gs.irep) { + cnv |= conv_irep; + fig += 5000; +#ifdef VERBOSE + printf(" needs input representation conversion though\n"); +#endif + } + + if (out != gs.orep) { + cnv |= conv_orep; + fig += 5000; +#ifdef VERBOSE + printf(" needs output representation conversion though\n"); +#endif + } + + /* If we don't need output checking or skipping, but we've got it */ + if ((~Ooopt & gs.oopt) != 0) { + unsigned int tmp = (~Ooopt & gs.oopt); + /* Count number of bits set that we don't need */ + /* (From HACKMEM 169) */ + tmp = tmp - ((tmp >> 1) & 033333333333) - ((tmp >> 2) & 011111111111); + tmp = ((tmp + (tmp >> 3)) & 030707070707) % 63; + fig += tmp; +#ifdef VERBOSE + printf(" has output options we don't need though\n"); +#endif + } + + /* If we've got output skipping, we need to skip pointers in interp. */ + if ((Ooopt & OOPTS_SKIP) != 0) { + cnv |= conv_skip; + } + + if (((opt & opts_fwd) && (gs.opt & opts_bwd)) + || ((opt & opts_bwd) && (gs.opt & opts_fwd))) { + cnv |= conv_rev; + fig += 1000; +#ifdef VERBOSE + printf(" needs direction conversion though\n"); +#endif + } + + if (prec != gs.prec) { /* Internal precision mismatch */ + fig += 100000; /* Will have a major effect, so discourage using it */ +#ifdef VERBOSE + printf(" internal precision doesn't match though (want %d, got %d)\n",prec,gs.prec); +#endif + } + + /* If we've got a choice of algorithm possible */ + if (gs.opt & (opts_splx_sort | opts_sort_splx)) { + + /* If we've got a preference of algorithm, */ + if ((opt & (opts_splx_sort | opts_sort_splx))) { + + /* and there is a mismatch */ + if (((opt & opts_splx_sort) && ts.sort != 0) + || ((opt & opts_sort_splx) && ts.sort == 0)) { + fig += 10000; /* Will have a great effect, so discourage using it */ +#ifdef VERBOSE + printf(" sort/simplex algorithm mismatch\n"); +#endif + } + + } else { /* There is no preference chosen at run time, */ + /* and there is a mismatch to the compile time preference */ + if (((gs.opt & opts_splx_sort) && ts.sort != 0) + || ((gs.opt & opts_sort_splx) && ts.sort == 0)) { + fig += 10000; /* Will have a great effect, so discourage using it */ +#ifdef VERBOSE + printf(" sort/simplex algorithm mismatch\n"); +#endif + } + } + } + + if (ts.sort) { + stres = 0; +#ifdef VERBOSE + printf("gres = %d, gs.gres = %d\n",res,gs.itres); +#endif + /* We want one that is equals or exceeds the desired */ + /* resolution, but doesn't exceed it too much, or the code */ + /* will be inefficient. */ + /* If there are no routines that can meet the desired precision, */ + /* then it is ok to use the one closest to the desired precision. */ + if (gs.itres >= res) { + fig += 10 * (gs.itres - res); + } else { + fig += 10000 + 10 * (res - gs.itres); + } + } else { + /* compute the needed stres (Assuming not sort) */ + stres = ((1 << gs.prec)-1 + res-2)/(res-1); + +#ifdef VERBOSE + printf("gres = %d, sres = %d, gs.gres = %d, gs.sres = %d\n",res,stres,gs.itres,gs.stres); +#endif + /* We want one that is equals or exceeds the desired */ + /* resolution, but doesn't exceed it too much, or the code */ + /* will be inefficient. */ + /* If there are no routines that can meet the desired precision, */ + /* then it is ok to use the one closest to the desired precision. */ + if (gs.itres >= res && gs.stres >= stres) { + fig += 10 * (gs.itres - res) + (gs.stres - stres); + } { + if (gs.itres < res) { + fig += 10000 + 10 * (res - gs.itres); + } + if (gs.stres < stres) { + fig += 1000 + 10 * (stres - gs.stres); + } + } + } + +#ifdef VERBOSE + printf(" figure of merit %d\n",fig); +#endif + /* Is this the best one so far ? */ + if (fig < bfig) { + bfig = fig; + bk = i; + bstres = stres; + bgs = gs; /* Structure copy */ + bts = ts; /* Structure copy */ + bcnv = cnv; +#ifdef VERBOSE + printf(" best so far\n"); +#endif + } + } + + if (bk < 0) { +#ifdef VERBOSE + printf("new_imdi failed - dimensionality or representations couldn't be matched\n"); +#endif + return NULL; /* Nothing matches */ + } + + if ((im = (imdi *)calloc(1, sizeof(imdi))) == NULL) { +#ifdef VERBOSE + printf("new_imdi malloc imdi failed\n"); +#endif + /* Should we return an error somehow ? */ + return NULL; + } + + /* We've decided kernel function bk is going to be the best, */ + /* so now setup the appropriate tables to use with it. */ + if (bgs.itres > res) + bgs.itres = res; /* Tell table create what the res is */ + if (bgs.stres > bstres) + bgs.stres = bstres; + + /* Tel table setup how to treat integer input in per channel lookups */ + bgs.in_signed = in_signed; + bgs.out_signed = out_signed; + +#ifdef VERBOSE + if (!bts.sort) { + if ((bgs.stres * (bgs.itres-1)) < ((1 << bgs.prec)-1)) { + printf("Warning: table chosen doesn't reach desired precision!\n"); + printf("Wanted %d, got %d\n", ((1 << bgs.prec)-1), (bgs.stres * (bgs.itres-1))); + } + } +#endif + + /* Allocate and initialise the appropriate tables */ + im->impl = (void *)imdi_tab(&bgs, &bts, bcnv, in, out, ktable[bk].interp, + inm, outm, oopt, checkv, input_curves, md_table, + output_curves, cntx); + + if (im->impl == NULL) { +#ifdef VERBOSE + printf("imdi_tab failed\n"); +#endif + imdi_del(im); + return NULL; + } + +#ifdef VERBOSE + if (bcnv != conv_none) + printf("imdi_tab: using a runtime match, cnv flags 0x%x\n",bcnv); +#endif + + if (bcnv == conv_none) /* No runtime match conversion needed */ + im->interp = ktable[bk].interp; + else + im->interp = interp_match; + im->get_check = imdi_get_check; + im->reset_check = imdi_reset_check; + im->info = imdi_info; + im->del = imdi_del; + +#ifdef VERBOSE + printf("new_imdi returning 0x%x\n", im); +#endif + return im; +} + +/* Runtime matching adapter */ +/* We can emulate many combinations of interpolation function */ +/* by converting to a routine that supports stride, or one that */ +/* supports plane interleaved and stride. */ +/* This also lets us emulate functions that can convert between */ +/* pixel and plane interleaved at the same time as doing the */ +/* color interpolation. Warp is in components (NOT bytes) */ +/* We cope with skipping writes to output channels by */ +/* only implementing that support in plane interleaved, */ +/* and depending on that being used for pixel interleaved. */ +static void interp_match( +imdi *s, +void **outp, int outst, /* Output pointers and stride */ +void **inp, int inst, /* Input pointers and stride */ +unsigned int npixels /* Number of pixels */ +) { + int j, i; + void *minp[IXDI]; + void *moutp[IXDI]; + imdi_imp *impl = (imdi_imp *)s->impl; + imdi_conv cnv = impl->cnv; + + /* Need to supply default stride. */ + /* This is simply the input dimension for pixel interleaved, */ + /* and 1 for plane interleaved. */ + if (cnv & conv_istr) { + if (impl->cirep == pixint8 || impl->cirep == pixint16) + inst = impl->id; + else + inst = 1; + } + if (cnv & conv_ostr) { + if (impl->corep == pixint8 || impl->corep == pixint16) + outst = impl->wod; + else + outst = 1; + } + + /* Convert from pixel to plane by setting the plane pointers */ + /* to each pixel component, and using the pixel interleaved stride */ + if (cnv & conv_irep) { + if (impl->cirep == pixint8) { /* Convert from pix8 to plane8 */ + for (j = 0; j < impl->id; j++) + minp[j] = (void *)((char *)inp[0] + j); + } else if (impl->cirep == pixint16) { /* Convert from pix16 to plane16 */ + for (j = 0; j < impl->id; j++) + minp[j] = (void *)((char *)inp[0] + 2 * j); + } + } else { /* Copy pointers, because we call with them. */ + if (impl->cirep == pixint8 || impl->cirep == pixint16) { + minp[0] = inp[0]; + } else { + for (j = 0; j < impl->id; j++) + minp[j] = inp[j]; + } + } + if (cnv & conv_orep) { + if (impl->corep == pixint8) { /* Convert from pix8 to plane8 */ + for (j = i = 0; j < impl->od; j++) { + if ((impl->skipf & (1 << j)) == 0) /* If not skipped */ + moutp[j] = (void *)((char *)outp[0] + i++); + else + moutp[j] = NULL; + } + } else if (impl->corep == pixint16) { /* Convert from pix16 to plane16 */ + for (j = i = 0; j < impl->od; j++) + if ((impl->skipf & (1 << j)) == 0) /* If not skipped */ + moutp[j] = (void *)((char *)outp[0] + 2 * i++); + else + moutp[j] = NULL; + } + } else { /* Copy pointers, because we call with them. */ + if (impl->corep == pixint8 || impl->corep == pixint16) { + moutp[0] = outp[0]; + } else { /* Plane interleaved */ + for (j = i = 0; j < impl->od; j++) { + if ((impl->skipf & (1 << j)) == 0) /* If not skipped */ + moutp[j] = outp[i++]; + else + moutp[j] = NULL; + } + } + } + + /* Convert fwd function to a backwards one, or visa-versa. */ + /* Move the pointers to the last pixel, and reverse the stride */ + if (cnv & conv_rev) { + if (impl->firep == pixint8) { + minp[0] = (void *)((char *)minp[0] + inst * (npixels - 1)); + } else if (impl->firep == pixint16) { + minp[0] = (void *)((char *)minp[0] + inst * 2 * (npixels - 1)); + } else if (impl->firep == planeint8) { + for (j = 0; j < impl->id; j++) + minp[j] = (void *)((char *)minp[j] + inst * (npixels - 1)); + } else if (impl->firep == planeint16) { + for (j = 0; j < impl->id; j++) + minp[j] = (void *)((char *)minp[j] + inst * 2 * (npixels - 1)); + } + inst = -inst; + if (impl->forep == pixint8) { + moutp[0] = (void *)((char *)moutp[0] + outst * (npixels - 1)); + } else if (impl->forep == pixint16) { + moutp[0] = (void *)((char *)moutp[0] + outst * 2 * (npixels - 1)); + } else if (impl->forep == planeint8) { + for (j = 0; j < impl->od; j++) + moutp[j] = (void *)((char *)moutp[j] + outst * (npixels - 1)); + } else if (impl->forep == planeint16) { + for (j = 0; j < impl->od; j++) + moutp[j] = (void *)((char *)moutp[j] + outst * 2 * (npixels - 1)); + } + outst = -outst; + } + + /* Call the conversion routine we have */ + impl->interp(s, moutp, outst, minp, inst, npixels); +} + +/* Get the per output channel check flags - bit corresponds to output interpolation channel */ +static unsigned int imdi_get_check(imdi *im) { + imdi_imp *impl = (imdi_imp *)im->impl; + unsigned int rv; + int i; + + /* Convert from output channel index to callback index */ + for (rv = 0, i = 0; i < impl->od; i++) { + unsigned int b; + b = 1 & (impl->checkf >> i); /* Output index flag */ + rv |= (b << impl->im_map[i]); /* to callback index */ + } + return rv; +} + +/* Reset the output check flags (not reset by interp) */ +static void imdi_reset_check(imdi *im) { + imdi_imp *impl = (imdi_imp *)im->impl; + + impl->checkf = 0; +} + +/* Return some information about the imdi */ +static void imdi_info(imdi *im, unsigned long *psize, int *pgres, int *psres) { + imdi_imp *impl = (imdi_imp *)im->impl; + unsigned long size; + + size = sizeof(imdi); + + if (impl != NULL) { + size += impl->size; + if (psize != NULL) + *psize = size; + + if (pgres != NULL) + *pgres = impl->gres; + + if (psres != NULL) + *psres = impl->sres; + } +} + + +/* Delete the object */ +static void imdi_del(imdi *im) { + /* Free all the allocated tables */ + if (im->impl != NULL) + imdi_tab_free((imdi_imp *)im->impl); + + /* Free this structure */ + free(im); +} + + + + + + + + + + + + + + + + + + diff --git a/imdi/imdi.h b/imdi/imdi.h new file mode 100644 index 0000000..1e72b8c --- /dev/null +++ b/imdi/imdi.h @@ -0,0 +1,93 @@ +#ifndef IMDI_H +#define IMDI_H + +/* Integer Multi-Dimensional Interpolation */ + +/* + * Copyright 2000 - 2007 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. + */ + +/* + * This file provides the common definitions for IMDI, and + * in particular, the runtime conversion object. + * Actual runtime details are kept opaque. + */ + +#include "imdi_utl.h" +#include "imdi_arch.h" +#include "imdi_gen.h" + +/* IMDI Object */ +struct _imdi { + void *impl; /* Opaque pointer to implementation information (type imdi_imp *) */ + + /* Do the interpolation. */ + + /* Each pointer corresponds to the colors plane for plane interleaved. */ + /* pointer[0] is used for pixel interleave. */ + + /* Stride is only obeyed if the appropriate option flag was set */ + /* in new_imdi, and is in pixel components, and effectively defaults */ + /* to 1 for plane interleaved, and id and od (adjusted for skip) for pixel interleave, */ + /* so stride is in color components (NOT bytes) */ + /* Output pointers and data must only reference non-skipped output channels. */ + + /* Note that once an imdi is created, multiple can call interp() without */ + /* interfering with each other, allowing parallel execution. */ + void (*interp)(struct _imdi *s, void **outp, int outst, /* Ouput pointers and stride */ + void **inp, int inst, /* Input pointers and stride */ + unsigned int npixels); /* Number of pixels */ + + /* Return some information about the imdi */ + void (*info)(struct _imdi *s, unsigned long *size, int *gres, int *sres); + + /* Get the per output channel check flags (bit is indexed by callback channel) */ + /* Flag gets set if output != checkv */ + unsigned int (*get_check)(struct _imdi *s); + + /* Reset the output check flags (flag is not reset by interp) */ + void (*reset_check)(struct _imdi *s); + + /* Delete this object */ + void (*del)(struct _imdi *s); + +}; typedef struct _imdi imdi; + +/* Create a new imdi. */ +/* Return NULL if request is not supported */ +imdi *new_imdi( + int id, /* Number of input dimensions */ + int od, /* Number of output lookup dimensions */ + /* Number of output channels written = od - no. of oopt skip flags */ + imdi_pixrep in, /* Input pixel representation */ + int in_signed, /* Bit flag per channel, NZ if treat as signed */ + int *inm, /* Input raster channel to callback channel mapping, NULL for none. */ + imdi_iprec iprec, /* Internal processing precision */ + imdi_pixrep out, /* Output pixel representation */ + int out_signed, /* Bit flag per channel, NZ if treat as signed */ + int *outm, /* Output raster channel to callback channel mapping, NULL for none. */ + /* Mapping must include skipped channels. */ + int res, /* Desired table resolution */ + imdi_ooptions oopt, /* Output per channel options (by callback channel) */ + unsigned int *checkv, /* Output channel check values (by callback channel, NULL == 0) */ + imdi_options opt, /* Direction and stride options */ + + /* Callbacks to lookup the imdi table values. */ + /* (Skip output channels are looked up) */ + void (*input_curves) (void *cntx, double *out_vals, double *in_vals), + void (*md_table) (void *cntx, double *out_vals, double *in_vals), + void (*output_curves)(void *cntx, double *out_vals, double *in_vals), + void *cntx /* Context to callbacks */ +); + +#endif /* IMDI_H */ + + + + + + diff --git a/imdi/imdi_arch.h b/imdi/imdi_arch.h new file mode 100644 index 0000000..afceb73 --- /dev/null +++ b/imdi/imdi_arch.h @@ -0,0 +1,71 @@ +#ifndef IMDI_ARCH_H +#define IMDI_ARCH_H + +/* Integer Multi-Dimensional Interpolation */ + +/* + * Copyright 2000 - 2007 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. + */ + +/* + * Contained here is any architecture/platform specific facilities, + * used by the runtime code, and used by the code generation code + * in generating the runtime code (used in the generation code itself + * only in discovering the architecture automatically), + * and the architecture table that describes the architecture to the + * code generation. + * + * The mach_arch structure is not used by the runtime, since it is implicit + * that the runtime is setup for the described architecture. + * + */ + +#ifdef ALLOW64 + +#define STR_DEF(def) #def + +/* Detect machine/compiler specifics here */ +#if defined(NT) +#define longlong __int64 +#else /* !NT, assume standard */ +#define longlong long long +#endif /* !NT */ +#define str_longlong STR_DEF(longlong) + +#endif /* ALLOW64 */ + +/* Machine/Language architectural specifications */ +typedef struct { + int bits; /* Bits in this data type */ + char *name; /* Name used to specify this type */ + int align; /* Non-zero if this type should be accessed aligned */ +} dtypes; + +#define MXDTYPES 6 + +typedef struct { + int bigend; /* Non-zero if this is a bigendian architecture */ + int uwa; /* Use wide memory access */ + + int pbits; /* Number of bits in a pointer */ + + int nords; /* Number of ord types */ + dtypes ords[MXDTYPES]; /* Ordinal types, in size order */ + int natord; /* Index of natural machine ordinal */ + + int nints; /* Number of int types */ + dtypes ints[MXDTYPES]; /* Integer types, in size order */ + int natint; /* Index of natural machine integer */ + + /* Optimisation settings */ + int shfm; /* Non-zero to use shifts for masking */ + int oscale; /* Maximum power of 2 scaled indexing mode, 0 for none. */ + int smmul; /* Has fast small multiply for index scaling */ + +} mach_arch; + +#endif /* IMDI_ARCH_H */ diff --git a/imdi/imdi_gen.c b/imdi/imdi_gen.c new file mode 100644 index 0000000..ab17ad3 --- /dev/null +++ b/imdi/imdi_gen.c @@ -0,0 +1,319 @@ + +/* Integer Multi-Dimensional Interpolation */ + +/* + * Copyright 2000 - 2007 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. + */ + +/* + * Code generation support that translates between the higher level + * gendesc, and the low level genspec. + */ + +#undef VERBOSE + +/* + Ideal grid resolutions for 8/16 bit precision calculations: + + There are a limited number of grid resolution and grid cells + sub resolution values that work perfectly, and allow every possible + input value to map to a precise interpolation value. For any + system that deals with Lab data, it is also better if there is + a grid point near a,b == 0, meaning that an odd grid size is + preferable for 3 dimensional Lab input conversions. Reasonable memory + usage is usually somewhere between 100K and 1M entries for + the grid table, limiting the maximum grid resolution. + The following lists the some of the possible grid and sub grid + resolutions in order of best to worst. (Fewer jumps is better). + + + Grid Sub8 Round8 Sub16 Round16 Jumps + 4 85 85 21845 21845 0 + 6 51 51 13107 13107 0 + 16 17 17 4369 4369 0 + 18 15 15 3855 3855 0 + 52 5 5 1285 1285 0 + 86 3 3 771 771 0 + 256 1 1 257 257 0 + 258 255 255 0 + 772 85 85 0 + 1286 51 51 0 + 3856 17 17 0 + 4370 15 15 0 + 13108 5 5 0 + 21846 3 3 0 + 65536 1 1 0 + 3 127.5 128 1 + 5 63.75 64 1 + 9 31.875 32 1 + 17 15.937 16 1 + 33 7.968 8 1 + 65 3.984 4 1 + 128 2.007 2 1 + 129 1.992 2 1 + 255 1.003 1 1 + 12 23.188 23 2 + 24 11.086 11 2 + 254 1.007 1 2 + 7 42.5 43 3 + 8 36.428 36 3 + 10 28.333 28 3 + 13 21.25 21 3 + 15 18.214 18 3 + 19 14.166 14 3 + 22 12.142 12 3 + 29 9.107 9 3 + 37 7.083 7 3 + 43 6.071 6 3 + 44 5.930 6 3 + 64 4.047 4 3 + 85 3.035 3 3 + 87 2.965 3 3 + 127 2.023 2 3 + 130 1.976 2 3 + 253 1.011 1 3 + + [8 bit: sub = 255/(grid-1), jumps = abs((grid-1)*round(sub)-255)] + [16 bit: sub = 65535/(grid-1), jumps = abs((grid-1)*round(sub)-65535)] + + The above takes into consideration the mapping of the sub-cell or + simplex table resolution, but doesn't take into account the quantizing + of the sub-cell weighting values into the range 0..256 or 0..65536. + + This will be best when round(sub) divides evenly into 256 or 65536, + so grid resolutions of 3, 5, 9, 17, 33, 65, 128, 129, 255 may be the + best choice for sort algorithm grid resolutions. + + */ + +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" + +#include "imdi_utl.h" +#include "imdi_arch.h" +#include "imdi_gen.h" + +static void translate_pixrep(pixlayout *pl, char **desc, int *prec, imdi_pixrep rep, int dim, mach_arch *a); + +/* Translate between a gendesc and genspec */ +int /* Return number of combinations possible */ +set_genspec( +genspec *gs, /* Return specification */ +gendesc *gd, /* Input description */ +int comb, /* Combination index */ +mach_arch *a /* Machine architecture */ +) { + int nc = 1; /* Number of combinations */ + int nidc, id; /* Number of input dimension combinations, current index */ + int nodc, od; /* Number of output dimension combinations, current index */ + int nirc, ir; /* Number of input, prec. & out representations combinations, cnt index */ + int pc, or; /* Precision combination index, Output combination index */ + int ndc, opt; /* Number of direction stride ombinations, current index */ + int tt; + + /* Figure out how many combinations there are */ + for (nidc = 0; gd->idcombs[nidc] != 0; nidc++) /* Input dimension */ + ; + nc *= nidc; + for (nodc = 0; gd->odcombs[nodc] != 0; nodc++) /* Output dimension */ + ; + nc *= nodc; + for (nirc = 0; gd->incombs[nirc] != 0; nirc++) /* Input/Precision/Output representation */ + ; + nc *= nirc; + for (tt = 0; gd->iprecs[tt] != 0; tt++) /* Internal precision representation */ + ; + if (nirc != tt) { + fprintf(stderr,"imdi_gen: Must be equal numberof input and precision representations\n"); + exit(-1); + } + for (tt = 0; gd->outcombs[tt] != 0; tt++) /* Output representation */ + ; + if (nirc != tt) { + fprintf(stderr,"imdi_gen: Must be equal numberof input and output representations\n"); + exit(-1); + } + + for (ndc = 0; gd->optcombs[ndc] != opts_end; ndc++) /* Direction and stride options */ + ; + nc *= ndc; + + if (nc <= 0) { + fprintf(stderr,"imdi_gen: no valid combinations specified for kernel generation\n"); + exit(-1); + } + if (comb < nc) { /* If we are within a legal combination */ + int iprec, oprec; + char *idesc, *odesc; /* Representation descriptions */ + char *ddesc; /* Direction description */ + + id = comb % nidc; + comb /= nidc; + od = comb % nodc; + comb /= nodc; + ir = comb % nirc; + comb /= nirc; + pc = ir; /* In and precision combs track together */ + or = ir; /* In and out combs track together */ + opt = comb % ndc; + comb /= ndc; +#ifdef VERBOSE + printf("Combination id = %d, od = %d, ir = %d, pc = %d, or = %d, opt = %d\n", + id,od,ir,pc,or,opt); +#endif /* VERBOSE */ + + gs->id = gd->idcombs[id]; /* Input dimensions */ + gs->itres = gd->itres[id]; /* Interpolation table resolution */ + gs->stres = gd->stres[id]; /* Simplex table resolution */ + gs->od = gd->odcombs[od]; /* Output dimensions */ + gs->oopt = gd->ooptcombs[od]; /* Output per channel options */ + + if (gs->id > IXDI) { + fprintf(stderr,"imdi_gen: Input dimension %d exceeds limit %d\n",gs->id,IXDI); + exit(-1); + } + if (gs->od > IXDO) { + fprintf(stderr,"imdi_gen: Output dimension %d exceeds limit %d\n",gs->od,IXDO); + exit(-1); + } + /* Input representation */ + gs->irep = gd->incombs[ir]; /* Keep a copy of this */ + translate_pixrep(&gs->in, &idesc, &iprec, gd->incombs[ir], gs->id, a); + gs->in_signed = 0x0; /* Not used during generation, used at runtime setup */ + + /* Output representation */ + gs->orep = gd->outcombs[or]; /* Keep a copy of this */ + translate_pixrep(&gs->out, &odesc, &oprec, gd->outcombs[or], gs->od, a); + gs->out_signed = 0x0; /* Not used during generation, used at runtime setup */ + + COMPUTE_IPREC(gs->prec, gs->irep, gd->iprecs[pc], gs->orep) + + gs->opt = gd->optcombs[opt]; /* Direction and stride options */ + + if (((gs->opt & opts_splx_sort) && (gs->opt & opts_sort_splx)) + || ((gs->opt & opts_splx_sort) && (gs->opt & opts_splx)) + || ((gs->opt & opts_sort_splx) && (gs->opt & opts_splx))) { + fprintf(stderr,"imdi_gen: Conflict in simplex/sort preferences\n"); + exit(-1); + } + + ddesc = gs->opt & opts_bwd ? "b" : "f"; /* Direction description */ + +#ifdef VERBOSE + printf("translates to prec = %d, id = %d, od = %d, itres %d, stdres %d\n", + gs->prec,gs->id,gs->od,gs->itres,gs->stres); +#endif /* VERBOSE */ + + /* Create a concise description string */ + sprintf(gs->kdesc,"%d_%d_%s_%s_%s", gs->id, gs->od, idesc, odesc, ddesc); + } + return nc; +} + +/* Convert the high level pixrep into the lower level pixel layout */ +static void +translate_pixrep( +pixlayout *pl, /* pixlayout to initialise */ +char **desc, /* Return description identifier (may be NULL) */ +int *prec, /* Return basic precision specifier (may be NULL) */ +imdi_pixrep rep, /* Representation to be translated */ +int dim, /* Number of dimensions (values/pixel) */ +mach_arch *a /* Machine architecture */ +) { + switch (rep) { + + case pixint8: { /* 8 Bits per value, pixel interleaved, no padding */ + int i; + + /* Could optimise this to packed for dim == 4 ~~~~ */ + + pl->pint = 1; /* pixel interleaved */ + pl->packed = 0; /* Not packed */ + + for (i = 0; i < dim; i++) { + pl->bpch[i] = 8; /* Bits per channel */ + pl->chi[i] = dim; /* Channel increment */ + pl->bov[i] = 0; /* Bit offset to value within channel */ + pl->bpv[i] = 8; /* Bits per value within channel */ + } + + if (prec != NULL) + *prec = 8; /* Basic 8 bit precision */ + if (desc != NULL) + *desc = "i8"; /* Interleaved 8 bit */ + } break; + + case planeint8: { /* 8 bits per value, plane interleaved */ + int i; + + pl->pint = 0; /* Not pixel interleaved */ + pl->packed = 0; /* Not packed */ + + for (i = 0; i < dim; i++) { + pl->bpch[i] = 8; /* Bits per channel */ + pl->chi[i] = 1; /* Channel increment */ + pl->bov[i] = 0; /* Bit offset to value within channel */ + pl->bpv[i] = 8; /* Bits per value within channel */ + } + + if (prec != NULL) + *prec = 8; /* Basic 8 bit precision */ + if (desc != NULL) + *desc = "p8"; /* Planar 8 bit */ + + } break; + + case pixint16: { /* 16 Bits per value, pixel interleaved, no padding */ + int i; + + /* Could optimise this to packed for dim == 4 ~~~~ */ + + pl->pint = 1; /* pixel interleaved */ + pl->packed = 0; /* Not packed */ + + for (i = 0; i < dim; i++) { + pl->bpch[i] = 16; /* Bits per channel */ + pl->chi[i] = dim; /* Channel increment */ + pl->bov[i] = 0; /* Bit offset to value within channel */ + pl->bpv[i] = 16; /* Bits per value within channel */ + } + + if (prec != NULL) + *prec = 16; /* Basic 8 bit precision */ + if (desc != NULL) + *desc = "i16"; /* Interleaved 16 bit */ + } break; + + case planeint16: { /* 16 bits per value, plane interleaved */ + int i; + + pl->pint = 0; /* Not pixel interleaved */ + pl->packed = 0; /* Not packed */ + + for (i = 0; i < dim; i++) { + pl->bpch[i] = 16; /* Bits per channel */ + pl->chi[i] = 1; /* Channel increment */ + pl->bov[i] = 0; /* Bit offset to value within channel */ + pl->bpv[i] = 16; /* Bits per value within channel */ + } + + if (prec != NULL) + *prec = 16; /* Basic 8 bit precision */ + + if (desc != NULL) + *desc = "p16"; /* Planar 8 bit */ + } break; + + default: { + fprintf(stderr,"Warning: Unknown pixel representation %d\n",rep); + } break; + } +} + diff --git a/imdi/imdi_gen.h b/imdi/imdi_gen.h new file mode 100644 index 0000000..8a7ced9 --- /dev/null +++ b/imdi/imdi_gen.h @@ -0,0 +1,271 @@ +#ifndef IMDI_GEN_H +#define IMDI_GEN_H + +/* Integer Multi-Dimensional Interpolation */ + +/* + * Copyright 2000 - 2007 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. + */ + +/* + * Pixel code generation definitions. + * + * This defines a particular combination of pixel layout, + * number of channels, and other runtime requestable features. + * This is used by the code generation to setup anticipated + * support for these requests, used by the runtime to request + * the features, and by the runtime to identify the correct + * generated module to use. + * + */ + + +/* -------------------------------------------------- */ +/* High level kernel generation description */ + + +/* This is a high level macro desciption of the pixel layout. */ +/* It can be expanded by adding a new enumeration, and then */ +/* implementing the code in imdi_gen to translate the enumeration */ +/* into the exact pixlayout structure details. */ + +typedef enum { + invalid_rep = 0x00, + pixint8 = 0x01, /* 8 Bits per value, pixel interleaved, no padding */ + planeint8 = 0x02, /* 8 bits per value, plane interleaved */ + pixint16 = 0x03, /* 16 Bits per value, pixel interleaved, no padding */ + planeint16 = 0x04 /* 16 bits per value, plane interleaved */ +} imdi_pixrep; + +/* The internal processing precision */ +typedef enum { + prec_min = 0, /* Minimum of input and output precision */ + prec_max = 1, /* Maximum of input and output precision */ + prec_in = 2, /* Same as input */ + prec_out = 3, /* Same as output */ + prec_p8 = 4, /* 8 Bits precision */ + prec_p16 = 5 /* 16 Bits precision */ +} imdi_iprec; + +/* Output per channel processing options. For the compiled */ +/* kernels, this is indexed by physical output channel, */ +/* (not to be confused with the runtime oopt which is indexed */ +/* by callback channel). */ +/* - shift left by channel no * 2 to access option. */ +/* Note that oopts_skip valid only for plane interleaved, */ +/* and doesn't change the number output channels looked up, */ +/* it changes the number of output channels written, hence */ +/* must be matched by the arguments to interp() (number of */ +/* output pointers, stride etc.) */ +typedef enum { + oopts_none = 0x0, /* No extra options */ + oopts_check = 0x1, /* Test channel value against trigger */ + oopts_skip = 0x2, /* Don't write channel value */ + oopts_chskp = 0x3 /* oopts_check + oopts_skip */ +} imdi_ooptions; + +/* Macro to create an instance of imdi_ooptions for a particular channel */ +#define OOPT(flag,chan) ((flag) << (chan * 2)) + +/* Macro to extract the flags particular channel */ +#define OOPTX(val,chan) (0x3 & ((val) >> (chan * 2))) + +/* Value of no oopt flags set */ +#define OOPTS_NONE 0x00000000 + +/* Mask to detect any oopts_check flags that are set */ +#define OOPTS_CHECK 0x55555555 + +/* Mask to ignore any oopts_check flags that are set */ +#define OOPTS_NOT_CHECK 0xAAAAAAAA + +/* Mask to detect any oopts_skip flags that are set */ +#define OOPTS_SKIP 0xAAAAAAAA + +/* Mask to ignore any oopts_skip flags that are set */ +#define OOPTS_NOT_SKIP 0x55555555 + +/* Processing options */ +/* Note that there will be a choice between simplex lookup or sort algoritm for */ +/* only a subset of kernels (8 bits processing precision, <= 4 input channels) */ +typedef enum { + opts_none = 0x00, /* Forward direction, no stride */ + opts_fwd = 0x01, /* Forwards direction (default is gen fwd/run don't care) */ + opts_bwd = 0x02, /* Backwards direction (default is gen fwd/run don't care) */ + opts_istride = 0x04, /* Stride on input */ + opts_ostride = 0x08, /* Stride on output */ + + opts_splx_sort = 0x10, /* Prefer simplex over sort algorithm (generate both) */ + opts_sort_splx = 0x20, /* Force sort algorithm, rather than simplex table (generate both) */ + opts_splx = 0x40, /* Generate simplex only (when possible), default is sort only. */ + + opts_end = 0x80 /* End marker */ +} imdi_options; + +/* This sructure allows a series of related kernels to be generated */ +#define MX_GDCS 20 + /* * means multiplies combination */ + /* + means lockstep with previous line */ +typedef struct { + int idcombs[MX_GDCS]; /* * Input dimension combinations (0 at end) */ + int itres[MX_GDCS]; /* + Interpolation table resolutions for */ + int stres[MX_GDCS]; /* + Simplex table resolutions */ + + int odcombs[MX_GDCS]; /* * Output dimensions combinations (0 at end) */ + imdi_ooptions ooptcombs[MX_GDCS]; /* + output per channel options */ + + imdi_pixrep incombs[MX_GDCS]; /* * Input pixel representation */ + imdi_iprec iprecs[MX_GDCS]; /* + Internal precision */ + imdi_pixrep outcombs[MX_GDCS]; /* + Output pixel representation */ + + imdi_options optcombs[MX_GDCS]; /* * Direction and stride options */ +} gendesc; + +/* -------------------------------------------------- */ +/* Detailed level of generation specification */ + +/* Pixel layout: */ +/* Each channel value is assumed to be read with a */ +/* native machine single read of size bpch, */ +/* and bov and bpv being bit indexes into that value. */ +/* */ +/* If pint == 0, then each read will be of size bpch[], and will */ +/* have its own pointer, will be incremented by chi[]. */ +/* If pint != 0, then the reads will be size bpch[0], from successive */ +/* locations, chi[] apart. */ +/* */ +/* If packed == 0, then separate reads are needed for each input */ +/* channel. */ +/* If packed != 0, then the channel values will be extracted */ +/* from a single read of size bpch[0] */ +/* */ +/* Note that at all times the bit offset and size values */ +/* will be obeyed for each input value. */ + +/* NOTE :- if you change this, you need to change the code in cgen.c */ +/* labeled !genspec and tabspec delta code! */ +typedef struct { + int bpch[IXDIDO]; /* Bits per channel read (must be divisible by 8) */ + int chi[IXDIDO]; /* channel increment in multiples of bpch[] (0 == dimensionality) */ + int bov[IXDIDO]; /* Bit offset to value within channel */ + int bpv[IXDIDO]; /* Bits per value within channel */ + int pint; /* Flag - nonz if pixel interleaved (ie. reads from successice locations) */ + int packed; /* Flag - nonz if all channels are packed into a single read */ +} pixlayout; + +/* Structure that specifies the configuration of a generated interpolation kernel. */ +/* NOTE :- if you change this, you need to change the code in cgen.c */ +/* labeled !genspec and tabspec delta code! */ +typedef struct { + /* Input to code generator */ + int prec; /* Internal precision:- either 8 or 16 bits */ + int id; /* Number of input dimensions used for interpolation */ + int od; /* Number of output dimensions created by interpolation */ + imdi_pixrep irep; /* High level input pixel representation */ + imdi_pixrep orep; /* High level output pixel representation */ + int in_signed; /* Bit flag per channel, NZ if treat as signed (runtime setup) */ + int out_signed; /* Bit flag per channel, NZ if treat as signed (runtime setup) */ + pixlayout in; /* Input pixel layout */ + pixlayout out; /* Output pixel layout */ + imdi_ooptions oopt; /* output per channel options */ + imdi_options opt; /* Direction and stride options */ + int itres; /* Interpolation table resolution */ + int stres; /* Simplex table resolution */ + + /* Returned value */ + char kkeys[100]; /* Kernel keys */ + char kdesc[100]; /* At genspec time */ + char kname[100]; /* At generation time */ +} genspec; + +/* - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Translate between high level and low level generation specification */ +int set_genspec(genspec *gs, gendesc *gd, int comb, mach_arch *a); + +/* - - - - - - - - - - - - - - - - - - - - - - - */ +/* Convert the input and output pixel representation and the internal */ +/* precision choice into a precision in bits */ +/* (Used at code gen and code execution, hence defined as a macro) */ +#define COMPUTE_IPREC(PRECOUT, INREP, IPREC, OUTREP) \ +{ \ + int _iprec, _oprec; \ + \ + switch (INREP) { \ + default: \ + case pixint8: \ + case planeint8: \ + _iprec = 8; \ + break; \ + case pixint16: \ + case planeint16: \ + _iprec = 16; \ + break; \ + } \ + switch (OUTREP) { \ + default: \ + case pixint8: \ + case planeint8: \ + _oprec = 8; \ + break; \ + case pixint16: \ + case planeint16: \ + _oprec = 16; \ + break; \ + } \ + switch (IPREC) { \ + default: \ + case prec_min: \ + PRECOUT = _iprec < _oprec ? _iprec : _oprec; \ + break; \ + case prec_max: \ + PRECOUT = _iprec > _oprec ? _iprec : _oprec; \ + break; \ + case prec_in: \ + PRECOUT = _iprec; \ + break; \ + case prec_out: \ + PRECOUT = _oprec; \ + break; \ + case prec_p8: \ + PRECOUT = 8; \ + break; \ + case prec_p16: \ + PRECOUT = 16; \ + break; \ + } \ +} \ + + +/* - - - - - - - - - - - - - - - - - - - - - - - */ + +struct _tabspec; + +/* Supported code generators: */ + +/* The 'C' code generator */ +/* Generate a source file to implement the specified */ +/* interpolation kernel. Fill in return values and return 0 if OK. */ +/* Return 1 if this kernel should be skipped (ie. force sort and sort not forced) */ +/* and some other anon-zero on error. */ +int gen_c_kernel(genspec *g, struct _tabspec *t, mach_arch *a, + FILE *fp, int index, genspec *og, struct _tabspec *ot); + +/* asm, MMX, etc. generators declarations go here ! */ + +#endif /* IMDI_GEN_H */ + + + + + + + + + + + diff --git a/imdi/imdi_k.h b/imdi/imdi_k.h new file mode 100644 index 0000000..79f60d5 --- /dev/null +++ b/imdi/imdi_k.h @@ -0,0 +1,910 @@ +/* Integer Multi-Dimensional Interpolation */ +/* Declarations for all the generated kernel functions */ +/* This file is generated by imdi_make */ + +/* Copyright 2000 - 2007 Graeme W. Gill */ +/* All rights reserved. */ +/* This material is licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- */ +/* see the License.txt file for licensing details.*/ + +#include "imdi_k.c" /* All the kernel code */ + +struct { + void (*interp)(imdi *s, void **outp, int ostride, void **inp, int istride, unsigned int npix); + void (*gentab)(genspec *g, tabspec *t); +} ktable[891] = { + { imdi_k1, imdi_k1_gentab }, + { imdi_k2, imdi_k2_gentab }, + { imdi_k3, imdi_k3_gentab }, + { imdi_k4, imdi_k4_gentab }, + { imdi_k5, imdi_k5_gentab }, + { imdi_k6, imdi_k6_gentab }, + { imdi_k7, imdi_k7_gentab }, + { imdi_k8, imdi_k8_gentab }, + { imdi_k9, imdi_k9_gentab }, + { imdi_k10, imdi_k10_gentab }, + { imdi_k11, imdi_k11_gentab }, + { imdi_k12, imdi_k12_gentab }, + { imdi_k13, imdi_k13_gentab }, + { imdi_k14, imdi_k14_gentab }, + { imdi_k15, imdi_k15_gentab }, + { imdi_k16, imdi_k16_gentab }, + { imdi_k17, imdi_k17_gentab }, + { imdi_k18, imdi_k18_gentab }, + { imdi_k19, imdi_k19_gentab }, + { imdi_k20, imdi_k20_gentab }, + { imdi_k21, imdi_k21_gentab }, + { imdi_k22, imdi_k22_gentab }, + { imdi_k23, imdi_k23_gentab }, + { imdi_k24, imdi_k24_gentab }, + { imdi_k25, imdi_k25_gentab }, + { imdi_k26, imdi_k26_gentab }, + { imdi_k27, imdi_k27_gentab }, + { imdi_k28, imdi_k28_gentab }, + { imdi_k29, imdi_k29_gentab }, + { imdi_k30, imdi_k30_gentab }, + { imdi_k31, imdi_k31_gentab }, + { imdi_k32, imdi_k32_gentab }, + { imdi_k33, imdi_k33_gentab }, + { imdi_k34, imdi_k34_gentab }, + { imdi_k35, imdi_k35_gentab }, + { imdi_k36, imdi_k36_gentab }, + { imdi_k37, imdi_k37_gentab }, + { imdi_k38, imdi_k38_gentab }, + { imdi_k39, imdi_k39_gentab }, + { imdi_k40, imdi_k40_gentab }, + { imdi_k41, imdi_k41_gentab }, + { imdi_k42, imdi_k42_gentab }, + { imdi_k43, imdi_k43_gentab }, + { imdi_k44, imdi_k44_gentab }, + { imdi_k45, imdi_k45_gentab }, + { imdi_k46, imdi_k46_gentab }, + { imdi_k47, imdi_k47_gentab }, + { imdi_k48, imdi_k48_gentab }, + { imdi_k49, imdi_k49_gentab }, + { imdi_k50, imdi_k50_gentab }, + { imdi_k51, imdi_k51_gentab }, + { imdi_k52, imdi_k52_gentab }, + { imdi_k53, imdi_k53_gentab }, + { imdi_k54, imdi_k54_gentab }, + { imdi_k55, imdi_k55_gentab }, + { imdi_k56, imdi_k56_gentab }, + { imdi_k57, imdi_k57_gentab }, + { imdi_k58, imdi_k58_gentab }, + { imdi_k59, imdi_k59_gentab }, + { imdi_k60, imdi_k60_gentab }, + { imdi_k61, imdi_k61_gentab }, + { imdi_k62, imdi_k62_gentab }, + { imdi_k63, imdi_k63_gentab }, + { imdi_k64, imdi_k64_gentab }, + { imdi_k65, imdi_k65_gentab }, + { imdi_k66, imdi_k66_gentab }, + { imdi_k67, imdi_k67_gentab }, + { imdi_k68, imdi_k68_gentab }, + { imdi_k69, imdi_k69_gentab }, + { imdi_k70, imdi_k70_gentab }, + { imdi_k71, imdi_k71_gentab }, + { imdi_k72, imdi_k72_gentab }, + { imdi_k73, imdi_k73_gentab }, + { imdi_k74, imdi_k74_gentab }, + { imdi_k75, imdi_k75_gentab }, + { imdi_k76, imdi_k76_gentab }, + { imdi_k77, imdi_k77_gentab }, + { imdi_k78, imdi_k78_gentab }, + { imdi_k79, imdi_k79_gentab }, + { imdi_k80, imdi_k80_gentab }, + { imdi_k81, imdi_k81_gentab }, + { imdi_k82, imdi_k82_gentab }, + { imdi_k83, imdi_k83_gentab }, + { imdi_k84, imdi_k84_gentab }, + { imdi_k85, imdi_k85_gentab }, + { imdi_k86, imdi_k86_gentab }, + { imdi_k87, imdi_k87_gentab }, + { imdi_k88, imdi_k88_gentab }, + { imdi_k89, imdi_k89_gentab }, + { imdi_k90, imdi_k90_gentab }, + { imdi_k91, imdi_k91_gentab }, + { imdi_k92, imdi_k92_gentab }, + { imdi_k93, imdi_k93_gentab }, + { imdi_k94, imdi_k94_gentab }, + { imdi_k95, imdi_k95_gentab }, + { imdi_k96, imdi_k96_gentab }, + { imdi_k97, imdi_k97_gentab }, + { imdi_k98, imdi_k98_gentab }, + { imdi_k99, imdi_k99_gentab }, + { imdi_k100, imdi_k100_gentab }, + { imdi_k101, imdi_k101_gentab }, + { imdi_k102, imdi_k102_gentab }, + { imdi_k103, imdi_k103_gentab }, + { imdi_k104, imdi_k104_gentab }, + { imdi_k105, imdi_k105_gentab }, + { imdi_k106, imdi_k106_gentab }, + { imdi_k107, imdi_k107_gentab }, + { imdi_k108, imdi_k108_gentab }, + { imdi_k109, imdi_k109_gentab }, + { imdi_k110, imdi_k110_gentab }, + { imdi_k111, imdi_k111_gentab }, + { imdi_k112, imdi_k112_gentab }, + { imdi_k113, imdi_k113_gentab }, + { imdi_k114, imdi_k114_gentab }, + { imdi_k115, imdi_k115_gentab }, + { imdi_k116, imdi_k116_gentab }, + { imdi_k117, imdi_k117_gentab }, + { imdi_k118, imdi_k118_gentab }, + { imdi_k119, imdi_k119_gentab }, + { imdi_k120, imdi_k120_gentab }, + { imdi_k121, imdi_k121_gentab }, + { imdi_k122, imdi_k122_gentab }, + { imdi_k123, imdi_k123_gentab }, + { imdi_k124, imdi_k124_gentab }, + { imdi_k125, imdi_k125_gentab }, + { imdi_k126, imdi_k126_gentab }, + { imdi_k127, imdi_k127_gentab }, + { imdi_k128, imdi_k128_gentab }, + { imdi_k129, imdi_k129_gentab }, + { imdi_k130, imdi_k130_gentab }, + { imdi_k131, imdi_k131_gentab }, + { imdi_k132, imdi_k132_gentab }, + { imdi_k133, imdi_k133_gentab }, + { imdi_k134, imdi_k134_gentab }, + { imdi_k135, imdi_k135_gentab }, + { imdi_k136, imdi_k136_gentab }, + { imdi_k137, imdi_k137_gentab }, + { imdi_k138, imdi_k138_gentab }, + { imdi_k139, imdi_k139_gentab }, + { imdi_k140, imdi_k140_gentab }, + { imdi_k141, imdi_k141_gentab }, + { imdi_k142, imdi_k142_gentab }, + { imdi_k143, imdi_k143_gentab }, + { imdi_k144, imdi_k144_gentab }, + { imdi_k145, imdi_k145_gentab }, + { imdi_k146, imdi_k146_gentab }, + { imdi_k147, imdi_k147_gentab }, + { imdi_k148, imdi_k148_gentab }, + { imdi_k149, imdi_k149_gentab }, + { imdi_k150, imdi_k150_gentab }, + { imdi_k151, imdi_k151_gentab }, + { imdi_k152, imdi_k152_gentab }, + { imdi_k153, imdi_k153_gentab }, + { imdi_k154, imdi_k154_gentab }, + { imdi_k155, imdi_k155_gentab }, + { imdi_k156, imdi_k156_gentab }, + { imdi_k157, imdi_k157_gentab }, + { imdi_k158, imdi_k158_gentab }, + { imdi_k159, imdi_k159_gentab }, + { imdi_k160, imdi_k160_gentab }, + { imdi_k161, imdi_k161_gentab }, + { imdi_k162, imdi_k162_gentab }, + { imdi_k163, imdi_k163_gentab }, + { imdi_k164, imdi_k164_gentab }, + { imdi_k165, imdi_k165_gentab }, + { imdi_k166, imdi_k166_gentab }, + { imdi_k167, imdi_k167_gentab }, + { imdi_k168, imdi_k168_gentab }, + { imdi_k169, imdi_k169_gentab }, + { imdi_k170, imdi_k170_gentab }, + { imdi_k171, imdi_k171_gentab }, + { imdi_k172, imdi_k172_gentab }, + { imdi_k173, imdi_k173_gentab }, + { imdi_k174, imdi_k174_gentab }, + { imdi_k175, imdi_k175_gentab }, + { imdi_k176, imdi_k176_gentab }, + { imdi_k177, imdi_k177_gentab }, + { imdi_k178, imdi_k178_gentab }, + { imdi_k179, imdi_k179_gentab }, + { imdi_k180, imdi_k180_gentab }, + { imdi_k181, imdi_k181_gentab }, + { imdi_k182, imdi_k182_gentab }, + { imdi_k183, imdi_k183_gentab }, + { imdi_k184, imdi_k184_gentab }, + { imdi_k185, imdi_k185_gentab }, + { imdi_k186, imdi_k186_gentab }, + { imdi_k187, imdi_k187_gentab }, + { imdi_k188, imdi_k188_gentab }, + { imdi_k189, imdi_k189_gentab }, + { imdi_k190, imdi_k190_gentab }, + { imdi_k191, imdi_k191_gentab }, + { imdi_k192, imdi_k192_gentab }, + { imdi_k193, imdi_k193_gentab }, + { imdi_k194, imdi_k194_gentab }, + { imdi_k195, imdi_k195_gentab }, + { imdi_k196, imdi_k196_gentab }, + { imdi_k197, imdi_k197_gentab }, + { imdi_k198, imdi_k198_gentab }, + { imdi_k199, imdi_k199_gentab }, + { imdi_k200, imdi_k200_gentab }, + { imdi_k201, imdi_k201_gentab }, + { imdi_k202, imdi_k202_gentab }, + { imdi_k203, imdi_k203_gentab }, + { imdi_k204, imdi_k204_gentab }, + { imdi_k205, imdi_k205_gentab }, + { imdi_k206, imdi_k206_gentab }, + { imdi_k207, imdi_k207_gentab }, + { imdi_k208, imdi_k208_gentab }, + { imdi_k209, imdi_k209_gentab }, + { imdi_k210, imdi_k210_gentab }, + { imdi_k211, imdi_k211_gentab }, + { imdi_k212, imdi_k212_gentab }, + { imdi_k213, imdi_k213_gentab }, + { imdi_k214, imdi_k214_gentab }, + { imdi_k215, imdi_k215_gentab }, + { imdi_k216, imdi_k216_gentab }, + { imdi_k217, imdi_k217_gentab }, + { imdi_k218, imdi_k218_gentab }, + { imdi_k219, imdi_k219_gentab }, + { imdi_k220, imdi_k220_gentab }, + { imdi_k221, imdi_k221_gentab }, + { imdi_k222, imdi_k222_gentab }, + { imdi_k223, imdi_k223_gentab }, + { imdi_k224, imdi_k224_gentab }, + { imdi_k225, imdi_k225_gentab }, + { imdi_k226, imdi_k226_gentab }, + { imdi_k227, imdi_k227_gentab }, + { imdi_k228, imdi_k228_gentab }, + { imdi_k229, imdi_k229_gentab }, + { imdi_k230, imdi_k230_gentab }, + { imdi_k231, imdi_k231_gentab }, + { imdi_k232, imdi_k232_gentab }, + { imdi_k233, imdi_k233_gentab }, + { imdi_k234, imdi_k234_gentab }, + { imdi_k235, imdi_k235_gentab }, + { imdi_k236, imdi_k236_gentab }, + { imdi_k237, imdi_k237_gentab }, + { imdi_k238, imdi_k238_gentab }, + { imdi_k239, imdi_k239_gentab }, + { imdi_k240, imdi_k240_gentab }, + { imdi_k241, imdi_k241_gentab }, + { imdi_k242, imdi_k242_gentab }, + { imdi_k243, imdi_k243_gentab }, + { imdi_k244, imdi_k244_gentab }, + { imdi_k245, imdi_k245_gentab }, + { imdi_k246, imdi_k246_gentab }, + { imdi_k247, imdi_k247_gentab }, + { imdi_k248, imdi_k248_gentab }, + { imdi_k249, imdi_k249_gentab }, + { imdi_k250, imdi_k250_gentab }, + { imdi_k251, imdi_k251_gentab }, + { imdi_k252, imdi_k252_gentab }, + { imdi_k253, imdi_k253_gentab }, + { imdi_k254, imdi_k254_gentab }, + { imdi_k255, imdi_k255_gentab }, + { imdi_k256, imdi_k256_gentab }, + { imdi_k257, imdi_k257_gentab }, + { imdi_k258, imdi_k258_gentab }, + { imdi_k259, imdi_k259_gentab }, + { imdi_k260, imdi_k260_gentab }, + { imdi_k261, imdi_k261_gentab }, + { imdi_k262, imdi_k262_gentab }, + { imdi_k263, imdi_k263_gentab }, + { imdi_k264, imdi_k264_gentab }, + { imdi_k265, imdi_k265_gentab }, + { imdi_k266, imdi_k266_gentab }, + { imdi_k267, imdi_k267_gentab }, + { imdi_k268, imdi_k268_gentab }, + { imdi_k269, imdi_k269_gentab }, + { imdi_k270, imdi_k270_gentab }, + { imdi_k271, imdi_k271_gentab }, + { imdi_k272, imdi_k272_gentab }, + { imdi_k273, imdi_k273_gentab }, + { imdi_k274, imdi_k274_gentab }, + { imdi_k275, imdi_k275_gentab }, + { imdi_k276, imdi_k276_gentab }, + { imdi_k277, imdi_k277_gentab }, + { imdi_k278, imdi_k278_gentab }, + { imdi_k279, imdi_k279_gentab }, + { imdi_k280, imdi_k280_gentab }, + { imdi_k281, imdi_k281_gentab }, + { imdi_k282, imdi_k282_gentab }, + { imdi_k283, imdi_k283_gentab }, + { imdi_k284, imdi_k284_gentab }, + { imdi_k285, imdi_k285_gentab }, + { imdi_k286, imdi_k286_gentab }, + { imdi_k287, imdi_k287_gentab }, + { imdi_k288, imdi_k288_gentab }, + { imdi_k289, imdi_k289_gentab }, + { imdi_k290, imdi_k290_gentab }, + { imdi_k291, imdi_k291_gentab }, + { imdi_k292, imdi_k292_gentab }, + { imdi_k293, imdi_k293_gentab }, + { imdi_k294, imdi_k294_gentab }, + { imdi_k295, imdi_k295_gentab }, + { imdi_k296, imdi_k296_gentab }, + { imdi_k297, imdi_k297_gentab }, + { imdi_k298, imdi_k298_gentab }, + { imdi_k299, imdi_k299_gentab }, + { imdi_k300, imdi_k300_gentab }, + { imdi_k301, imdi_k301_gentab }, + { imdi_k302, imdi_k302_gentab }, + { imdi_k303, imdi_k303_gentab }, + { imdi_k304, imdi_k304_gentab }, + { imdi_k305, imdi_k305_gentab }, + { imdi_k306, imdi_k306_gentab }, + { imdi_k307, imdi_k307_gentab }, + { imdi_k308, imdi_k308_gentab }, + { imdi_k309, imdi_k309_gentab }, + { imdi_k310, imdi_k310_gentab }, + { imdi_k311, imdi_k311_gentab }, + { imdi_k312, imdi_k312_gentab }, + { imdi_k313, imdi_k313_gentab }, + { imdi_k314, imdi_k314_gentab }, + { imdi_k315, imdi_k315_gentab }, + { imdi_k316, imdi_k316_gentab }, + { imdi_k317, imdi_k317_gentab }, + { imdi_k318, imdi_k318_gentab }, + { imdi_k319, imdi_k319_gentab }, + { imdi_k320, imdi_k320_gentab }, + { imdi_k321, imdi_k321_gentab }, + { imdi_k322, imdi_k322_gentab }, + { imdi_k323, imdi_k323_gentab }, + { imdi_k324, imdi_k324_gentab }, + { imdi_k325, imdi_k325_gentab }, + { imdi_k326, imdi_k326_gentab }, + { imdi_k327, imdi_k327_gentab }, + { imdi_k328, imdi_k328_gentab }, + { imdi_k329, imdi_k329_gentab }, + { imdi_k330, imdi_k330_gentab }, + { imdi_k331, imdi_k331_gentab }, + { imdi_k332, imdi_k332_gentab }, + { imdi_k333, imdi_k333_gentab }, + { imdi_k334, imdi_k334_gentab }, + { imdi_k335, imdi_k335_gentab }, + { imdi_k336, imdi_k336_gentab }, + { imdi_k337, imdi_k337_gentab }, + { imdi_k338, imdi_k338_gentab }, + { imdi_k339, imdi_k339_gentab }, + { imdi_k340, imdi_k340_gentab }, + { imdi_k341, imdi_k341_gentab }, + { imdi_k342, imdi_k342_gentab }, + { imdi_k343, imdi_k343_gentab }, + { imdi_k344, imdi_k344_gentab }, + { imdi_k345, imdi_k345_gentab }, + { imdi_k346, imdi_k346_gentab }, + { imdi_k347, imdi_k347_gentab }, + { imdi_k348, imdi_k348_gentab }, + { imdi_k349, imdi_k349_gentab }, + { imdi_k350, imdi_k350_gentab }, + { imdi_k351, imdi_k351_gentab }, + { imdi_k352, imdi_k352_gentab }, + { imdi_k353, imdi_k353_gentab }, + { imdi_k354, imdi_k354_gentab }, + { imdi_k355, imdi_k355_gentab }, + { imdi_k356, imdi_k356_gentab }, + { imdi_k357, imdi_k357_gentab }, + { imdi_k358, imdi_k358_gentab }, + { imdi_k359, imdi_k359_gentab }, + { imdi_k360, imdi_k360_gentab }, + { imdi_k361, imdi_k361_gentab }, + { imdi_k362, imdi_k362_gentab }, + { imdi_k363, imdi_k363_gentab }, + { imdi_k364, imdi_k364_gentab }, + { imdi_k365, imdi_k365_gentab }, + { imdi_k366, imdi_k366_gentab }, + { imdi_k367, imdi_k367_gentab }, + { imdi_k368, imdi_k368_gentab }, + { imdi_k369, imdi_k369_gentab }, + { imdi_k370, imdi_k370_gentab }, + { imdi_k371, imdi_k371_gentab }, + { imdi_k372, imdi_k372_gentab }, + { imdi_k373, imdi_k373_gentab }, + { imdi_k374, imdi_k374_gentab }, + { imdi_k375, imdi_k375_gentab }, + { imdi_k376, imdi_k376_gentab }, + { imdi_k377, imdi_k377_gentab }, + { imdi_k378, imdi_k378_gentab }, + { imdi_k379, imdi_k379_gentab }, + { imdi_k380, imdi_k380_gentab }, + { imdi_k381, imdi_k381_gentab }, + { imdi_k382, imdi_k382_gentab }, + { imdi_k383, imdi_k383_gentab }, + { imdi_k384, imdi_k384_gentab }, + { imdi_k385, imdi_k385_gentab }, + { imdi_k386, imdi_k386_gentab }, + { imdi_k387, imdi_k387_gentab }, + { imdi_k388, imdi_k388_gentab }, + { imdi_k389, imdi_k389_gentab }, + { imdi_k390, imdi_k390_gentab }, + { imdi_k391, imdi_k391_gentab }, + { imdi_k392, imdi_k392_gentab }, + { imdi_k393, imdi_k393_gentab }, + { imdi_k394, imdi_k394_gentab }, + { imdi_k395, imdi_k395_gentab }, + { imdi_k396, imdi_k396_gentab }, + { imdi_k397, imdi_k397_gentab }, + { imdi_k398, imdi_k398_gentab }, + { imdi_k399, imdi_k399_gentab }, + { imdi_k400, imdi_k400_gentab }, + { imdi_k401, imdi_k401_gentab }, + { imdi_k402, imdi_k402_gentab }, + { imdi_k403, imdi_k403_gentab }, + { imdi_k404, imdi_k404_gentab }, + { imdi_k405, imdi_k405_gentab }, + { imdi_k406, imdi_k406_gentab }, + { imdi_k407, imdi_k407_gentab }, + { imdi_k408, imdi_k408_gentab }, + { imdi_k409, imdi_k409_gentab }, + { imdi_k410, imdi_k410_gentab }, + { imdi_k411, imdi_k411_gentab }, + { imdi_k412, imdi_k412_gentab }, + { imdi_k413, imdi_k413_gentab }, + { imdi_k414, imdi_k414_gentab }, + { imdi_k415, imdi_k415_gentab }, + { imdi_k416, imdi_k416_gentab }, + { imdi_k417, imdi_k417_gentab }, + { imdi_k418, imdi_k418_gentab }, + { imdi_k419, imdi_k419_gentab }, + { imdi_k420, imdi_k420_gentab }, + { imdi_k421, imdi_k421_gentab }, + { imdi_k422, imdi_k422_gentab }, + { imdi_k423, imdi_k423_gentab }, + { imdi_k424, imdi_k424_gentab }, + { imdi_k425, imdi_k425_gentab }, + { imdi_k426, imdi_k426_gentab }, + { imdi_k427, imdi_k427_gentab }, + { imdi_k428, imdi_k428_gentab }, + { imdi_k429, imdi_k429_gentab }, + { imdi_k430, imdi_k430_gentab }, + { imdi_k431, imdi_k431_gentab }, + { imdi_k432, imdi_k432_gentab }, + { imdi_k433, imdi_k433_gentab }, + { imdi_k434, imdi_k434_gentab }, + { imdi_k435, imdi_k435_gentab }, + { imdi_k436, imdi_k436_gentab }, + { imdi_k437, imdi_k437_gentab }, + { imdi_k438, imdi_k438_gentab }, + { imdi_k439, imdi_k439_gentab }, + { imdi_k440, imdi_k440_gentab }, + { imdi_k441, imdi_k441_gentab }, + { imdi_k442, imdi_k442_gentab }, + { imdi_k443, imdi_k443_gentab }, + { imdi_k444, imdi_k444_gentab }, + { imdi_k445, imdi_k445_gentab }, + { imdi_k446, imdi_k446_gentab }, + { imdi_k447, imdi_k447_gentab }, + { imdi_k448, imdi_k448_gentab }, + { imdi_k449, imdi_k449_gentab }, + { imdi_k450, imdi_k450_gentab }, + { imdi_k451, imdi_k451_gentab }, + { imdi_k452, imdi_k452_gentab }, + { imdi_k453, imdi_k453_gentab }, + { imdi_k454, imdi_k454_gentab }, + { imdi_k455, imdi_k455_gentab }, + { imdi_k456, imdi_k456_gentab }, + { imdi_k457, imdi_k457_gentab }, + { imdi_k458, imdi_k458_gentab }, + { imdi_k459, imdi_k459_gentab }, + { imdi_k460, imdi_k460_gentab }, + { imdi_k461, imdi_k461_gentab }, + { imdi_k462, imdi_k462_gentab }, + { imdi_k463, imdi_k463_gentab }, + { imdi_k464, imdi_k464_gentab }, + { imdi_k465, imdi_k465_gentab }, + { imdi_k466, imdi_k466_gentab }, + { imdi_k467, imdi_k467_gentab }, + { imdi_k468, imdi_k468_gentab }, + { imdi_k469, imdi_k469_gentab }, + { imdi_k470, imdi_k470_gentab }, + { imdi_k471, imdi_k471_gentab }, + { imdi_k472, imdi_k472_gentab }, + { imdi_k473, imdi_k473_gentab }, + { imdi_k474, imdi_k474_gentab }, + { imdi_k475, imdi_k475_gentab }, + { imdi_k476, imdi_k476_gentab }, + { imdi_k477, imdi_k477_gentab }, + { imdi_k478, imdi_k478_gentab }, + { imdi_k479, imdi_k479_gentab }, + { imdi_k480, imdi_k480_gentab }, + { imdi_k481, imdi_k481_gentab }, + { imdi_k482, imdi_k482_gentab }, + { imdi_k483, imdi_k483_gentab }, + { imdi_k484, imdi_k484_gentab }, + { imdi_k485, imdi_k485_gentab }, + { imdi_k486, imdi_k486_gentab }, + { imdi_k487, imdi_k487_gentab }, + { imdi_k488, imdi_k488_gentab }, + { imdi_k489, imdi_k489_gentab }, + { imdi_k490, imdi_k490_gentab }, + { imdi_k491, imdi_k491_gentab }, + { imdi_k492, imdi_k492_gentab }, + { imdi_k493, imdi_k493_gentab }, + { imdi_k494, imdi_k494_gentab }, + { imdi_k495, imdi_k495_gentab }, + { imdi_k496, imdi_k496_gentab }, + { imdi_k497, imdi_k497_gentab }, + { imdi_k498, imdi_k498_gentab }, + { imdi_k499, imdi_k499_gentab }, + { imdi_k500, imdi_k500_gentab }, + { imdi_k501, imdi_k501_gentab }, + { imdi_k502, imdi_k502_gentab }, + { imdi_k503, imdi_k503_gentab }, + { imdi_k504, imdi_k504_gentab }, + { imdi_k505, imdi_k505_gentab }, + { imdi_k506, imdi_k506_gentab }, + { imdi_k507, imdi_k507_gentab }, + { imdi_k508, imdi_k508_gentab }, + { imdi_k509, imdi_k509_gentab }, + { imdi_k510, imdi_k510_gentab }, + { imdi_k511, imdi_k511_gentab }, + { imdi_k512, imdi_k512_gentab }, + { imdi_k513, imdi_k513_gentab }, + { imdi_k514, imdi_k514_gentab }, + { imdi_k515, imdi_k515_gentab }, + { imdi_k516, imdi_k516_gentab }, + { imdi_k517, imdi_k517_gentab }, + { imdi_k518, imdi_k518_gentab }, + { imdi_k519, imdi_k519_gentab }, + { imdi_k520, imdi_k520_gentab }, + { imdi_k521, imdi_k521_gentab }, + { imdi_k522, imdi_k522_gentab }, + { imdi_k523, imdi_k523_gentab }, + { imdi_k524, imdi_k524_gentab }, + { imdi_k525, imdi_k525_gentab }, + { imdi_k526, imdi_k526_gentab }, + { imdi_k527, imdi_k527_gentab }, + { imdi_k528, imdi_k528_gentab }, + { imdi_k529, imdi_k529_gentab }, + { imdi_k530, imdi_k530_gentab }, + { imdi_k531, imdi_k531_gentab }, + { imdi_k532, imdi_k532_gentab }, + { imdi_k533, imdi_k533_gentab }, + { imdi_k534, imdi_k534_gentab }, + { imdi_k535, imdi_k535_gentab }, + { imdi_k536, imdi_k536_gentab }, + { imdi_k537, imdi_k537_gentab }, + { imdi_k538, imdi_k538_gentab }, + { imdi_k539, imdi_k539_gentab }, + { imdi_k540, imdi_k540_gentab }, + { imdi_k541, imdi_k541_gentab }, + { imdi_k542, imdi_k542_gentab }, + { imdi_k543, imdi_k543_gentab }, + { imdi_k544, imdi_k544_gentab }, + { imdi_k545, imdi_k545_gentab }, + { imdi_k546, imdi_k546_gentab }, + { imdi_k547, imdi_k547_gentab }, + { imdi_k548, imdi_k548_gentab }, + { imdi_k549, imdi_k549_gentab }, + { imdi_k550, imdi_k550_gentab }, + { imdi_k551, imdi_k551_gentab }, + { imdi_k552, imdi_k552_gentab }, + { imdi_k553, imdi_k553_gentab }, + { imdi_k554, imdi_k554_gentab }, + { imdi_k555, imdi_k555_gentab }, + { imdi_k556, imdi_k556_gentab }, + { imdi_k557, imdi_k557_gentab }, + { imdi_k558, imdi_k558_gentab }, + { imdi_k559, imdi_k559_gentab }, + { imdi_k560, imdi_k560_gentab }, + { imdi_k561, imdi_k561_gentab }, + { imdi_k562, imdi_k562_gentab }, + { imdi_k563, imdi_k563_gentab }, + { imdi_k564, imdi_k564_gentab }, + { imdi_k565, imdi_k565_gentab }, + { imdi_k566, imdi_k566_gentab }, + { imdi_k567, imdi_k567_gentab }, + { imdi_k568, imdi_k568_gentab }, + { imdi_k569, imdi_k569_gentab }, + { imdi_k570, imdi_k570_gentab }, + { imdi_k571, imdi_k571_gentab }, + { imdi_k572, imdi_k572_gentab }, + { imdi_k573, imdi_k573_gentab }, + { imdi_k574, imdi_k574_gentab }, + { imdi_k575, imdi_k575_gentab }, + { imdi_k576, imdi_k576_gentab }, + { imdi_k577, imdi_k577_gentab }, + { imdi_k578, imdi_k578_gentab }, + { imdi_k579, imdi_k579_gentab }, + { imdi_k580, imdi_k580_gentab }, + { imdi_k581, imdi_k581_gentab }, + { imdi_k582, imdi_k582_gentab }, + { imdi_k583, imdi_k583_gentab }, + { imdi_k584, imdi_k584_gentab }, + { imdi_k585, imdi_k585_gentab }, + { imdi_k586, imdi_k586_gentab }, + { imdi_k587, imdi_k587_gentab }, + { imdi_k588, imdi_k588_gentab }, + { imdi_k589, imdi_k589_gentab }, + { imdi_k590, imdi_k590_gentab }, + { imdi_k591, imdi_k591_gentab }, + { imdi_k592, imdi_k592_gentab }, + { imdi_k593, imdi_k593_gentab }, + { imdi_k594, imdi_k594_gentab }, + { imdi_k595, imdi_k595_gentab }, + { imdi_k596, imdi_k596_gentab }, + { imdi_k597, imdi_k597_gentab }, + { imdi_k598, imdi_k598_gentab }, + { imdi_k599, imdi_k599_gentab }, + { imdi_k600, imdi_k600_gentab }, + { imdi_k601, imdi_k601_gentab }, + { imdi_k602, imdi_k602_gentab }, + { imdi_k603, imdi_k603_gentab }, + { imdi_k604, imdi_k604_gentab }, + { imdi_k605, imdi_k605_gentab }, + { imdi_k606, imdi_k606_gentab }, + { imdi_k607, imdi_k607_gentab }, + { imdi_k608, imdi_k608_gentab }, + { imdi_k609, imdi_k609_gentab }, + { imdi_k610, imdi_k610_gentab }, + { imdi_k611, imdi_k611_gentab }, + { imdi_k612, imdi_k612_gentab }, + { imdi_k613, imdi_k613_gentab }, + { imdi_k614, imdi_k614_gentab }, + { imdi_k615, imdi_k615_gentab }, + { imdi_k616, imdi_k616_gentab }, + { imdi_k617, imdi_k617_gentab }, + { imdi_k618, imdi_k618_gentab }, + { imdi_k619, imdi_k619_gentab }, + { imdi_k620, imdi_k620_gentab }, + { imdi_k621, imdi_k621_gentab }, + { imdi_k622, imdi_k622_gentab }, + { imdi_k623, imdi_k623_gentab }, + { imdi_k624, imdi_k624_gentab }, + { imdi_k625, imdi_k625_gentab }, + { imdi_k626, imdi_k626_gentab }, + { imdi_k627, imdi_k627_gentab }, + { imdi_k628, imdi_k628_gentab }, + { imdi_k629, imdi_k629_gentab }, + { imdi_k630, imdi_k630_gentab }, + { imdi_k631, imdi_k631_gentab }, + { imdi_k632, imdi_k632_gentab }, + { imdi_k633, imdi_k633_gentab }, + { imdi_k634, imdi_k634_gentab }, + { imdi_k635, imdi_k635_gentab }, + { imdi_k636, imdi_k636_gentab }, + { imdi_k637, imdi_k637_gentab }, + { imdi_k638, imdi_k638_gentab }, + { imdi_k639, imdi_k639_gentab }, + { imdi_k640, imdi_k640_gentab }, + { imdi_k641, imdi_k641_gentab }, + { imdi_k642, imdi_k642_gentab }, + { imdi_k643, imdi_k643_gentab }, + { imdi_k644, imdi_k644_gentab }, + { imdi_k645, imdi_k645_gentab }, + { imdi_k646, imdi_k646_gentab }, + { imdi_k647, imdi_k647_gentab }, + { imdi_k648, imdi_k648_gentab }, + { imdi_k649, imdi_k649_gentab }, + { imdi_k650, imdi_k650_gentab }, + { imdi_k651, imdi_k651_gentab }, + { imdi_k652, imdi_k652_gentab }, + { imdi_k653, imdi_k653_gentab }, + { imdi_k654, imdi_k654_gentab }, + { imdi_k655, imdi_k655_gentab }, + { imdi_k656, imdi_k656_gentab }, + { imdi_k657, imdi_k657_gentab }, + { imdi_k658, imdi_k658_gentab }, + { imdi_k659, imdi_k659_gentab }, + { imdi_k660, imdi_k660_gentab }, + { imdi_k661, imdi_k661_gentab }, + { imdi_k662, imdi_k662_gentab }, + { imdi_k663, imdi_k663_gentab }, + { imdi_k664, imdi_k664_gentab }, + { imdi_k665, imdi_k665_gentab }, + { imdi_k666, imdi_k666_gentab }, + { imdi_k667, imdi_k667_gentab }, + { imdi_k668, imdi_k668_gentab }, + { imdi_k669, imdi_k669_gentab }, + { imdi_k670, imdi_k670_gentab }, + { imdi_k671, imdi_k671_gentab }, + { imdi_k672, imdi_k672_gentab }, + { imdi_k673, imdi_k673_gentab }, + { imdi_k674, imdi_k674_gentab }, + { imdi_k675, imdi_k675_gentab }, + { imdi_k676, imdi_k676_gentab }, + { imdi_k677, imdi_k677_gentab }, + { imdi_k678, imdi_k678_gentab }, + { imdi_k679, imdi_k679_gentab }, + { imdi_k680, imdi_k680_gentab }, + { imdi_k681, imdi_k681_gentab }, + { imdi_k682, imdi_k682_gentab }, + { imdi_k683, imdi_k683_gentab }, + { imdi_k684, imdi_k684_gentab }, + { imdi_k685, imdi_k685_gentab }, + { imdi_k686, imdi_k686_gentab }, + { imdi_k687, imdi_k687_gentab }, + { imdi_k688, imdi_k688_gentab }, + { imdi_k689, imdi_k689_gentab }, + { imdi_k690, imdi_k690_gentab }, + { imdi_k691, imdi_k691_gentab }, + { imdi_k692, imdi_k692_gentab }, + { imdi_k693, imdi_k693_gentab }, + { imdi_k694, imdi_k694_gentab }, + { imdi_k695, imdi_k695_gentab }, + { imdi_k696, imdi_k696_gentab }, + { imdi_k697, imdi_k697_gentab }, + { imdi_k698, imdi_k698_gentab }, + { imdi_k699, imdi_k699_gentab }, + { imdi_k700, imdi_k700_gentab }, + { imdi_k701, imdi_k701_gentab }, + { imdi_k702, imdi_k702_gentab }, + { imdi_k703, imdi_k703_gentab }, + { imdi_k704, imdi_k704_gentab }, + { imdi_k705, imdi_k705_gentab }, + { imdi_k706, imdi_k706_gentab }, + { imdi_k707, imdi_k707_gentab }, + { imdi_k708, imdi_k708_gentab }, + { imdi_k709, imdi_k709_gentab }, + { imdi_k710, imdi_k710_gentab }, + { imdi_k711, imdi_k711_gentab }, + { imdi_k712, imdi_k712_gentab }, + { imdi_k713, imdi_k713_gentab }, + { imdi_k714, imdi_k714_gentab }, + { imdi_k715, imdi_k715_gentab }, + { imdi_k716, imdi_k716_gentab }, + { imdi_k717, imdi_k717_gentab }, + { imdi_k718, imdi_k718_gentab }, + { imdi_k719, imdi_k719_gentab }, + { imdi_k720, imdi_k720_gentab }, + { imdi_k721, imdi_k721_gentab }, + { imdi_k722, imdi_k722_gentab }, + { imdi_k723, imdi_k723_gentab }, + { imdi_k724, imdi_k724_gentab }, + { imdi_k725, imdi_k725_gentab }, + { imdi_k726, imdi_k726_gentab }, + { imdi_k727, imdi_k727_gentab }, + { imdi_k728, imdi_k728_gentab }, + { imdi_k729, imdi_k729_gentab }, + { imdi_k730, imdi_k730_gentab }, + { imdi_k731, imdi_k731_gentab }, + { imdi_k732, imdi_k732_gentab }, + { imdi_k733, imdi_k733_gentab }, + { imdi_k734, imdi_k734_gentab }, + { imdi_k735, imdi_k735_gentab }, + { imdi_k736, imdi_k736_gentab }, + { imdi_k737, imdi_k737_gentab }, + { imdi_k738, imdi_k738_gentab }, + { imdi_k739, imdi_k739_gentab }, + { imdi_k740, imdi_k740_gentab }, + { imdi_k741, imdi_k741_gentab }, + { imdi_k742, imdi_k742_gentab }, + { imdi_k743, imdi_k743_gentab }, + { imdi_k744, imdi_k744_gentab }, + { imdi_k745, imdi_k745_gentab }, + { imdi_k746, imdi_k746_gentab }, + { imdi_k747, imdi_k747_gentab }, + { imdi_k748, imdi_k748_gentab }, + { imdi_k749, imdi_k749_gentab }, + { imdi_k750, imdi_k750_gentab }, + { imdi_k751, imdi_k751_gentab }, + { imdi_k752, imdi_k752_gentab }, + { imdi_k753, imdi_k753_gentab }, + { imdi_k754, imdi_k754_gentab }, + { imdi_k755, imdi_k755_gentab }, + { imdi_k756, imdi_k756_gentab }, + { imdi_k757, imdi_k757_gentab }, + { imdi_k758, imdi_k758_gentab }, + { imdi_k759, imdi_k759_gentab }, + { imdi_k760, imdi_k760_gentab }, + { imdi_k761, imdi_k761_gentab }, + { imdi_k762, imdi_k762_gentab }, + { imdi_k763, imdi_k763_gentab }, + { imdi_k764, imdi_k764_gentab }, + { imdi_k765, imdi_k765_gentab }, + { imdi_k766, imdi_k766_gentab }, + { imdi_k767, imdi_k767_gentab }, + { imdi_k768, imdi_k768_gentab }, + { imdi_k769, imdi_k769_gentab }, + { imdi_k770, imdi_k770_gentab }, + { imdi_k771, imdi_k771_gentab }, + { imdi_k772, imdi_k772_gentab }, + { imdi_k773, imdi_k773_gentab }, + { imdi_k774, imdi_k774_gentab }, + { imdi_k775, imdi_k775_gentab }, + { imdi_k776, imdi_k776_gentab }, + { imdi_k777, imdi_k777_gentab }, + { imdi_k778, imdi_k778_gentab }, + { imdi_k779, imdi_k779_gentab }, + { imdi_k780, imdi_k780_gentab }, + { imdi_k781, imdi_k781_gentab }, + { imdi_k782, imdi_k782_gentab }, + { imdi_k783, imdi_k783_gentab }, + { imdi_k784, imdi_k784_gentab }, + { imdi_k785, imdi_k785_gentab }, + { imdi_k786, imdi_k786_gentab }, + { imdi_k787, imdi_k787_gentab }, + { imdi_k788, imdi_k788_gentab }, + { imdi_k789, imdi_k789_gentab }, + { imdi_k790, imdi_k790_gentab }, + { imdi_k791, imdi_k791_gentab }, + { imdi_k792, imdi_k792_gentab }, + { imdi_k793, imdi_k793_gentab }, + { imdi_k794, imdi_k794_gentab }, + { imdi_k795, imdi_k795_gentab }, + { imdi_k796, imdi_k796_gentab }, + { imdi_k797, imdi_k797_gentab }, + { imdi_k798, imdi_k798_gentab }, + { imdi_k799, imdi_k799_gentab }, + { imdi_k800, imdi_k800_gentab }, + { imdi_k801, imdi_k801_gentab }, + { imdi_k802, imdi_k802_gentab }, + { imdi_k803, imdi_k803_gentab }, + { imdi_k804, imdi_k804_gentab }, + { imdi_k805, imdi_k805_gentab }, + { imdi_k806, imdi_k806_gentab }, + { imdi_k807, imdi_k807_gentab }, + { imdi_k808, imdi_k808_gentab }, + { imdi_k809, imdi_k809_gentab }, + { imdi_k810, imdi_k810_gentab }, + { imdi_k811, imdi_k811_gentab }, + { imdi_k812, imdi_k812_gentab }, + { imdi_k813, imdi_k813_gentab }, + { imdi_k814, imdi_k814_gentab }, + { imdi_k815, imdi_k815_gentab }, + { imdi_k816, imdi_k816_gentab }, + { imdi_k817, imdi_k817_gentab }, + { imdi_k818, imdi_k818_gentab }, + { imdi_k819, imdi_k819_gentab }, + { imdi_k820, imdi_k820_gentab }, + { imdi_k821, imdi_k821_gentab }, + { imdi_k822, imdi_k822_gentab }, + { imdi_k823, imdi_k823_gentab }, + { imdi_k824, imdi_k824_gentab }, + { imdi_k825, imdi_k825_gentab }, + { imdi_k826, imdi_k826_gentab }, + { imdi_k827, imdi_k827_gentab }, + { imdi_k828, imdi_k828_gentab }, + { imdi_k829, imdi_k829_gentab }, + { imdi_k830, imdi_k830_gentab }, + { imdi_k831, imdi_k831_gentab }, + { imdi_k832, imdi_k832_gentab }, + { imdi_k833, imdi_k833_gentab }, + { imdi_k834, imdi_k834_gentab }, + { imdi_k835, imdi_k835_gentab }, + { imdi_k836, imdi_k836_gentab }, + { imdi_k837, imdi_k837_gentab }, + { imdi_k838, imdi_k838_gentab }, + { imdi_k839, imdi_k839_gentab }, + { imdi_k840, imdi_k840_gentab }, + { imdi_k841, imdi_k841_gentab }, + { imdi_k842, imdi_k842_gentab }, + { imdi_k843, imdi_k843_gentab }, + { imdi_k844, imdi_k844_gentab }, + { imdi_k845, imdi_k845_gentab }, + { imdi_k846, imdi_k846_gentab }, + { imdi_k847, imdi_k847_gentab }, + { imdi_k848, imdi_k848_gentab }, + { imdi_k849, imdi_k849_gentab }, + { imdi_k850, imdi_k850_gentab }, + { imdi_k851, imdi_k851_gentab }, + { imdi_k852, imdi_k852_gentab }, + { imdi_k853, imdi_k853_gentab }, + { imdi_k854, imdi_k854_gentab }, + { imdi_k855, imdi_k855_gentab }, + { imdi_k856, imdi_k856_gentab }, + { imdi_k857, imdi_k857_gentab }, + { imdi_k858, imdi_k858_gentab }, + { imdi_k859, imdi_k859_gentab }, + { imdi_k860, imdi_k860_gentab }, + { imdi_k861, imdi_k861_gentab }, + { imdi_k862, imdi_k862_gentab }, + { imdi_k863, imdi_k863_gentab }, + { imdi_k864, imdi_k864_gentab }, + { imdi_k865, imdi_k865_gentab }, + { imdi_k866, imdi_k866_gentab }, + { imdi_k867, imdi_k867_gentab }, + { imdi_k868, imdi_k868_gentab }, + { imdi_k869, imdi_k869_gentab }, + { imdi_k870, imdi_k870_gentab }, + { imdi_k871, imdi_k871_gentab }, + { imdi_k872, imdi_k872_gentab }, + { imdi_k873, imdi_k873_gentab }, + { imdi_k874, imdi_k874_gentab }, + { imdi_k875, imdi_k875_gentab }, + { imdi_k876, imdi_k876_gentab }, + { imdi_k877, imdi_k877_gentab }, + { imdi_k878, imdi_k878_gentab }, + { imdi_k879, imdi_k879_gentab }, + { imdi_k880, imdi_k880_gentab }, + { imdi_k881, imdi_k881_gentab }, + { imdi_k882, imdi_k882_gentab }, + { imdi_k883, imdi_k883_gentab }, + { imdi_k884, imdi_k884_gentab }, + { imdi_k885, imdi_k885_gentab }, + { imdi_k886, imdi_k886_gentab }, + { imdi_k887, imdi_k887_gentab }, + { imdi_k888, imdi_k888_gentab }, + { imdi_k889, imdi_k889_gentab }, + { imdi_k890, imdi_k890_gentab }, + { imdi_k891, imdi_k891_gentab } +}; + +int no_kfuncs = 891; + diff --git a/imdi/imdi_make.c b/imdi/imdi_make.c new file mode 100644 index 0000000..7990f53 --- /dev/null +++ b/imdi/imdi_make.c @@ -0,0 +1,514 @@ + +/* Integer Multi-Dimensional Interpolation */ + +/* + * Copyright 2000 - 2007 Graeme W. Gill + * All rights reserved. + * + * This material is licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License.txt file for licencing details. + */ + +/* + * Top level kernel code generator + * + * This module is invoked from the make system, + * and generates all the versions and configurations of + * the IMDI kernel code. It includes all the generated + * files in imdi_k.h, which also contains a table + * so that the run time code knows what kernels + * are available. +*/ + +#include +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" + +#include "imdi.h" +#include "imdi_tab.h" + +#ifndef MAXNAMEL +# define MAXNAMEL 512 /* Maximum command line filename lengths */ +#endif + +#undef VERBOSE +#undef TEST1 /* Generate one test case */ + +/* + Ideal grid resolutions for 8 bit precision calculations. + See imdi_gen.c for a more detailed list. + + Grid Jumps + 4 0 + 6 0 + 16 0 + 18 0 + 52 0 + 86 0 + 256 0 + + 3 1 + 5 1 + 9 1 + 17 1 + 33 1 + 65 1 + 128 1 + 129 1 + 255 1 + + */ + +/* The following structure initialisations define what kernel routines should be built */ +static +gendesc descs[] = { +#ifdef TEST1 + { + { 3, 0 }, /* * Input dimension combinations */ + { 33, 0 }, /* + Interpolation table resolutions */ + { 8, 0 }, /* + Min Simplex table resolutions */ + { 4, 0 }, /* * Output dimension combinations */ + {OOPT(oopts_check,3), oopts_none}, /* + Output channel options */ + {pixint8, 0 }, /* * Input pixel representation */ + {prec_p16, 0}, /* + Internal precision */ + {pixint8, 0}, /* + Output pixel representation */ + {opts_sort_splx, opts_end} /* * Direction & stride combinations */ +// {opts_splx_sort, opts_end} /* * Direction & stride combinations */ + } +#else + /* A reasonably full set of combinations */ + /* * means multiplies combination */ + /* + means lockstep with previous line */ + { + { 1, 3, 4, 5, 6, 7, 8, 9, 10, 0 }, /* * Input dimension combinations */ + { 256, 33, 18, 16, 12, 8, 7, 6, 5, 0 }, /* + Min Interpolation table resolutions */ + { 1, 8, 17, 1, 1, 1, 1, 1, 1, 0 }, /* + Min Simplex table resolutions */ + + { 1, 3, 4, 5, 6, 7, 8, 9, 10, 0 }, /* * Output dimension combinations */ + {oopts_none, oopts_none, oopts_none, oopts_none, oopts_none, oopts_none, + oopts_none, oopts_none, oopts_none, oopts_none, oopts_none, oopts_none}, + /* + Output channel options */ + + {pixint8, pixint16, pixint8, pixint16, pixint16, 0 }, /* * Input pixel representation */ + {prec_p8, prec_p8, prec_p8, prec_p16, prec_p16, 0 }, /* + Internal precision */ + {pixint8, pixint8, pixint16, pixint16, pixint16, 0 }, /* + Output pixel representation */ + + { + opts_splx_sort, /* (both, but default to simple alg, no stride) */ + opts_istride | opts_ostride, /* + (Sort only with stride) */ + opts_end } /* * Direction & stride combinations */ + } +#endif /* !TEST1 */ +}; + +void set_architecture(mach_arch *ar, int use64); + +struct _knamestr { + char name[100]; + char desc[100]; + struct _knamestr *next; +}; typedef struct _knamestr knamestr; + +knamestr * +new_knamestr(char *name, char *desc) { + knamestr *kn; + + if ((kn = (knamestr *)malloc(sizeof(knamestr))) == NULL) { + fprintf(stderr,"new_knamestr malloc failed\n"); + exit(-1); + } + strcpy(kn->name, name); + strcpy(kn->desc, desc); + kn->next = NULL; + return kn; +} + +void usage(void) { + fprintf(stderr,"Make imdi kernel code Version %s\n",ARGYLL_VERSION_STR); + fprintf(stderr,"usage: imdi_make [-i]\n"); + fprintf(stderr," -d dir Directory to create them in (default .)\n"); + fprintf(stderr," -i Individial Files\n"); + fprintf(stderr," -f Force 64 bit\n"); + exit(1); +} + +int +main(int argc, char *argv[]) { + int fa,nfa; /* argument we're looking at */ + int indiv = 0; /* Individual files */ + int rv; + int dn, tnd; + genspec gs, ogs; + tabspec ts, ots; + mach_arch ar; + int ix = 1; /* kernel index */ + knamestr *list = NULL, *lp = NULL; +#if defined(ALLOW64) && defined(USE64) + int use64 = 1; +#else + int use64 = 0; +#endif + char dirname[MAXNAMEL+1+1] = ""; /* Output directory name */ + char temp[MAXNAMEL+100+1]; /* Buffer to compose filenames in */ + FILE *kcode = NULL; /* Kernel routine code file */ + FILE *kheader; /* Kernel routine header file */ + + /* Zero out the gen and tabspecs, to give diff a place to start */ + memset((void *)&ogs, 0, sizeof(genspec)); + memset((void *)&gs, 0, sizeof(genspec)); + memset((void *)&ots, 0, sizeof(tabspec)); + memset((void *)&ts, 0, sizeof(tabspec)); + + /* 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 */ + } + } + } + + if (argv[fa][1] == '?') { + usage(); + + } + /* Destination directory */ + else if (argv[fa][1] == 'd' || argv[fa][1] == 'D') { + int len; + fa = nfa; + if (na == NULL) usage(); + strncpy(dirname,na,MAXNAMEL); dirname[MAXNAMEL] = '\000'; + len = strlen(dirname); + if (len > 0) { + if (dirname[len-1] != '/' && dirname[len-1] != '\\') + strcat(dirname, "/"); + } + } + /* Individual files */ + else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') { + indiv = 1; + } + /* Force 64 bit */ + else if (argv[fa][1] == 'f' || argv[fa][1] == 'F') { +#ifdef ALLOW64 + use64 = 1; +#else + fprintf(stderr,"ALLOW64 bits is undefined\n"); + usage(); +#endif + } + else { + usage(); + } + } else + break; + } + + set_architecture(&ar, use64); + + /* Open the file for kernel routine declaration header */ + sprintf(temp, "%simdi_k.h",dirname); + if ((kheader = fopen(temp, "w")) == NULL) { + fprintf(stderr,"imdi_make: unable to open file '%s'\n",temp); + exit(-1); + } + + if (!indiv) { + sprintf(temp, "%simdi_k.c",dirname); + if ((kcode = fopen(temp, "w")) == NULL) { + fprintf(stderr,"imdi_make: unable to open file '%s'\n",temp); + exit(-1); + } + } + + tnd = sizeof(descs)/sizeof(gendesc); /* Total number of descriptions */ +#ifdef VERBOSE + printf("Number of descriptions = %d\n",tnd); +#endif /* VERBOSE */ + + fprintf(kheader,"/* Integer Multi-Dimensional Interpolation */\n"); + fprintf(kheader,"/* Declarations for all the generated kernel functions */\n"); + fprintf(kheader,"/* This file is generated by imdi_make */\n\n"); + fprintf(kheader,"/* Copyright 2000 - 2007 Graeme W. Gill */\n"); + fprintf(kheader,"/* All rights reserved. */\n"); + fprintf(kheader,"/* This material is licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- */\n"); + fprintf(kheader,"/* see the License.txt file for licensing details.*/\n"); + + fprintf(kheader,"\n"); + + /* For all the descriptions */ + for (dn = 0; dn < tnd; dn++) { + int cb, ncb; + + /* Do all combinations for that description */ + for (cb = 0, ncb = 1; cb < ncb; cb++) { + int nalg, alg; + char ofname[100]; + + /* Compute generate spec. and number of combinations */ + ncb = set_genspec(&gs, &descs[dn], cb, &ar); + + if (indiv) { + sprintf(temp, "%s%s.c",dirname,ofname); + if ((kcode = fopen(temp, "w")) == NULL) { + fprintf(stderr,"imdi_make: unable to open file '%s'\n",temp); + exit(-1); + } + } + + nalg = 2; /* By default generate just sort algorithm */ + alg = 1; + + if ((gs.opt & opts_splx_sort) + || (gs.opt & opts_sort_splx)) { + alg = 0; /* Generate both simplex and sort algorithms */ + } + if (gs.opt & opts_splx) { + nalg = 1; /* Generate just simplex algorithm */ + alg = 0; + } + + for (; alg < nalg; alg++) { + + if (alg == 0) + gs.opt |= opts_splx; + else + gs.opt &= ~opts_splx; + + /* Generate it */ + rv = gen_c_kernel(&gs, &ts, &ar, kcode, ix, &ogs, &ots); + if (rv != 0 && rv != 1) { + fprintf(stderr,"imdi_make: gen_c_kernel returned a err %d\n",rv); + exit(-1); + } + + /* Add the name to the list */ + if (list == NULL) + lp = list = new_knamestr(gs.kname, gs.kdesc); + else { + lp->next = new_knamestr(gs.kname, gs.kdesc); + lp = lp->next; + } + if (indiv) { + if (fclose(kcode) != 0) { + fprintf(stderr,"imdi_make: unable to close file '%s'\n",ofname); + exit(-1); + } + } + ogs = gs; /* Structure copy */ + ots = ts; + ix++; + + if (rv == 0) + break; /* there was only sort available */ + } + } + } + + /* Include the kernel functions in the header file */ + if (indiv) { + for(lp = list; lp != NULL; lp = lp->next) { + fprintf(kheader,"#include \"%s_%s.c\"\n",lp->name,lp->desc); + } + } else { + fprintf(kheader,"#include \"imdi_k.c\" /* All the kernel code */\n"); + } + fprintf(kheader,"\n"); + + /* Output function table */ + + fprintf(kheader, + "struct {\n" + " void (*interp)(imdi *s, void **outp, int ostride, void **inp, int istride, unsigned int npix);\n" + " void (*gentab)(genspec *g, tabspec *t);\n" + "} ktable[%d] = {\n",ix-1); + + for(lp = list; lp != NULL; lp = lp->next) { + fprintf(kheader,"\t{ %s, %s_gentab }%s\n", lp->name, lp->name, + lp->next != NULL ? "," : ""); + } + fprintf(kheader,"};\n"); + fprintf(kheader,"\n"); + fprintf(kheader,"int no_kfuncs = %d;\n",ix-1); + fprintf(kheader,"\n"); + + if (!indiv) { + if (fclose(kcode) != 0) { + fprintf(stderr,"imdi_make: unable to close file 'imdi_k.c'\n"); + exit(-1); + } + } + + if (fclose(kheader) != 0) { + fprintf(stderr,"imdi_make: unable to close file 'imdi_k.h'\n"); + exit(-1); + } + + /* Free the kname list */ + for(lp = list; lp != NULL;) { + char *p = (char *)lp; + lp = lp->next; + free(p); + } + + return 0; +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - */ +/* Initialse the architecture structure properly. */ +/* We're doing this purely at run time, on the assumption */ +/* that the target machine is the one we're running on. */ +/* We would have to do this differently in a cross development */ +/* environment. */ +void +set_architecture( +mach_arch *ar, +int use64 +) { + unsigned long etest = 0xff; + char *machtype; /* Environment value */ + + if (*((unsigned char *)&etest) == 0xff) { + ar->bigend = 0; /* Little endian */ + } else { + ar->bigend = 1; /* Big endian endian */ + } + + machtype = getenv("MACHTYPE"); + + /* Unfortunetaly many environments don't export MACHTYPE :-( */ + /* so we implement a fall back */ + if (machtype == NULL) { +#ifdef __ppc__ + machtype = "powerpc"; +#endif + } + + if (machtype != NULL && strcmp(machtype, "powerpc") == 0) { + + /* Section tunable for PowerPC */ + + ar->uwa = 0; /* Use wide memory access */ + ar->shfm = 0; /* Use shifts to mask values */ + ar->oscale = 8; /* Has scaled indexing up to * 8 */ + ar->smmul = 0; /* Doesn't have fast small multiply for index scaling */ + if (use64) { + ar->nords = 4; /* Number of ord types */ + ar->nints = 4; /* Number of int types */ + } else { + ar->nords = 3; /* Number of ord types */ + ar->nints = 3; /* Number of int types */ + } + ar->natord = 2; /* Most natural type (assume unsigned int) */ + ar->natint = 2; /* Most natural type (assume int) */ + + ar->pbits = sizeof(void *) * 8; /* Number of bits in a pointer */ + + ar->ords[0].bits = 8 * sizeof(unsigned char); + ar->ords[0].name = "unsigned char"; + ar->ords[0].align = 1; + + ar->ords[1].bits = 8 * sizeof(unsigned short); + ar->ords[1].name = "unsigned short"; + ar->ords[1].align = 1; + + ar->ords[2].bits = 8 * sizeof(unsigned int); + ar->ords[2].name = "unsigned int"; + ar->ords[2].align = 1; + +#ifdef ALLOW64 + ar->ords[3].bits = 8 * sizeof(unsigned longlong); + ar->ords[3].name = "unsigned " str_longlong ; + ar->ords[3].align = 0; +#endif /* ALLOW64 */ + + ar->ints[0].bits = 8 * sizeof(signed char); + ar->ints[0].name = "signed char"; + ar->ints[0].align = 1; + + ar->ints[1].bits = 8 * sizeof(short); + ar->ints[1].name = "short"; + ar->ints[1].align = 1; + + ar->ints[2].bits = 8 * sizeof(int); + ar->ints[2].name = "int"; + ar->ints[2].align = 1; + +#ifdef ALLOW64 + ar->ints[3].bits = 8 * sizeof(longlong); + ar->ints[3].name = str_longlong ; + ar->ints[3].align = 0; +#endif /* ALLOW64 */ + + } else { + + /* Currently assume x86 type */ + + ar->uwa = 0; /* Use wide memory access */ + ar->shfm = 0; /* Use shifts to mask values */ + ar->oscale = 8; /* Has scaled indexing up to * 8 */ + ar->smmul = 0; /* Doesn't have fast small multiply for index scaling */ + if (use64) { + ar->nords = 4; /* Number of ord types */ + ar->nints = 4; /* Number of int types */ + } else { + ar->nords = 3; /* Number of ord types */ + ar->nints = 3; /* Number of int types */ + } + ar->natord = 2; /* Most natural type (assume unsigned int) */ + ar->natint = 2; /* Most natural type (assume int) */ + + ar->pbits = sizeof(void *) * 8; /* Number of bits in a pointer */ + + ar->ords[0].bits = 8 * sizeof(unsigned char); + ar->ords[0].name = "unsigned char"; + ar->ords[0].align = 1; + + ar->ords[1].bits = 8 * sizeof(unsigned short); + ar->ords[1].name = "unsigned short"; + ar->ords[1].align = 1; + + ar->ords[2].bits = 8 * sizeof(unsigned int); + ar->ords[2].name = "unsigned int"; + ar->ords[2].align = 1; + +#ifdef ALLOW64 + ar->ords[3].bits = 8 * sizeof(unsigned longlong); + ar->ords[3].name = "unsigned " str_longlong ; + ar->ords[3].align = 0; +#endif /* ALLOW64 */ + + ar->ints[0].bits = 8 * sizeof(signed char); + ar->ints[0].name = "signed char"; + ar->ints[0].align = 1; + + ar->ints[1].bits = 8 * sizeof(short); + ar->ints[1].name = "short"; + ar->ints[1].align = 1; + + ar->ints[2].bits = 8 * sizeof(int); + ar->ints[2].name = "int"; + ar->ints[2].align = 1; + +#ifdef ALLOW64 + ar->ints[3].bits = 8 * sizeof(longlong); + ar->ints[3].name = str_longlong ; + ar->ints[3].align = 0; +#endif /* ALLOW64 */ + } +} + + diff --git a/imdi/imdi_tab.c b/imdi/imdi_tab.c new file mode 100644 index 0000000..4f448f0 --- /dev/null +++ b/imdi/imdi_tab.c @@ -0,0 +1,803 @@ + +/* Integer Multi-Dimensional Interpolation */ + +/* + * Copyright 2000 - 2007 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. + */ + +/* + * Run time table allocater and initialiser + * + * The function here that knows how to create the + * appropriate run time tables for our chosen kernel, + * and the type color mapping we want to perform. + */ + +#include +#include +#include +#include +#include + +#include "imdi.h" +#include "imdi_tab.h" + +#undef VERBOSE +#undef ASSERTS /* Check asserts */ + +#ifdef ASSERTS +#include +#endif + + + +typedef unsigned char byte; + +/* Left shift, handles >= 32 properly */ +#define LSHIFT(aa, bb) ((bb) <= 31 ? ((aa) << (bb)) : (((aa) << 31) << ((bb)-31))) + +/* The big value type used to represent table entries */ +#ifdef ALLOW64 +typedef unsigned longlong bvt; +#else +typedef unsigned long bvt; +#endif + +/* Specific entry size write routine */ + +void write_uchar( +byte *p, +bvt v +) { + *((unsigned char *)p) = (unsigned char)v; +} + +void write_ushort( +byte *p, +bvt v +) { + *((unsigned short *)p) = (unsigned short)v; +} + +void write_uint( +byte *p, +bvt v +) { + *((unsigned int *)p) = (unsigned int)v; +} + +void write_ulong( +byte *p, +bvt v +) { + *((unsigned long *)p) = (unsigned long)v; +} + +#ifdef ALLOW64 +void write_ulonglong( +byte *p, +bvt v +) { + *((unsigned longlong *)p) = (unsigned longlong)v; +} +#endif /* ALLOW64 */ + +void write_default( +byte *p, +bvt v +) { + fprintf(stderr,"imdi_tabl: internal failure - unexpected write size!\n"); + exit(-1); +} + +/* Array of write routines */ +void (*write_entry[16])(byte *p, bvt v); + +static void +init_write_tab(void) { + int i; + + for (i = 0; i < 16; i++) + write_entry[i] = write_default; /* Make sure any un-inited access bombs */ + + write_entry[sizeof(unsigned char)] = write_uchar; + write_entry[sizeof(unsigned short)] = write_ushort; + write_entry[sizeof(unsigned int)] = write_uint; + write_entry[sizeof(unsigned long)] = write_ulong; +#ifdef ALLOW64 + write_entry[sizeof(unsigned longlong)] = write_ulonglong; +#endif /* ALLOW64 */ +} + + +/* Input offset adjustment table */ +double in_adj[] = { + 8.0324820232182659e+281, 1.3051220361353854e+214, 1.5654418860154115e-076, + 6.6912978722165055e+281, 1.2369092402930559e+277, 1.4097588049607207e-308, + 7.7791723264456369e-260, 3.6184161952648606e+238, 5.8235640814908141e+180, + 9.1271554315814989e-072, 5.4310198502711138e+241, 2.7935452404894958e+275, + -2.9408705449902027e+003 +}; + + +/* Table creation function */ +imdi_imp * +imdi_tab( + genspec *gs, /* Pointer to gen spec */ + tabspec *ts, /* Pointer to table spec */ + imdi_conv cnv, /* Runtime argument conversion needed */ + imdi_pixrep irep, /* High level input pixel representation to match */ + imdi_pixrep orep, /* High level output pixel representation to match */ + void (*interp)(struct _imdi *s, void **outp, int outst, /* Underlying conversion function */ + void **inp, int inst, + unsigned int npixels), + + int *inm, /* Input raster channel to callback channel mapping, NULL for none. */ + int *outm, /* Output raster channel to callback channel mapping, NULL for none. */ + imdi_ooptions oopt, /* Output per channel options (Callback channel, NOT output channel) */ + unsigned int *checkv, /* Output channel check values (Callback channel, NULL for none == 0. */ + + /* Callbacks to lookup the mdi table values */ + void (*input_curves) (void *cntx, double *out_vals, double *in_vals), + void (*md_table) (void *cntx, double *out_vals, double *in_vals), + void (*output_curves)(void *cntx, double *out_vals, double *in_vals), + void *cntx /* Context to callbacks */ +) { + static int inited = 0; + static int bigend = 0; + int i, e, f; + imdi_imp *it; + unsigned long etest = 0xff; + int idinc[IXDI+1]; /* Increment for each dimension of interp table. */ + int ibdinc[IXDI+1]; /* idinc[] in bytes */ + int sdinc[IXDI+1]; /* Increment for each dimension of simplex table. */ + int sbdinc[IXDI+1]; /* sdinc[] in bytes */ + +#ifdef VERBOSE + printf("imdi_tab called\n"); +#endif + + if (inited == 0) { + init_write_tab(); + if (*((unsigned char *)&etest) == 0xff) + bigend = 0; /* Little endian */ + else + bigend = 1; /* Big endian */ + inited = 1; + } + + if ((it = (imdi_imp *)calloc(1, sizeof(imdi_imp))) == NULL) { +#ifdef VERBOSE + printf("malloc imdi_imp size %d failed\n",sizeof(imdi_imp)); +#endif + return NULL; /* Should we signal error ? How ? */ + } + + it->size = sizeof(imdi_imp); +#ifdef VERBOSE + printf("Allocated imdi_imp structure size %u\n",it->size); +#endif /* VERBOSE */ + + /* Set runtime matching conversion provided */ + it->cnv = cnv; + it->id = gs->id; + it->od = gs->od; + it->cirep = irep; /* Pixel representation interp is called with */ + it->corep = orep; + it->firep = gs->irep; /* Pixel representation of function we are going to use */ + it->forep = gs->orep; + it->interp = interp; + it->checkf = 0; + + /* Compute number of written channels (allow for skip) */ + it->wod = it->od; + for (i = 0; i < it->od; i++) { + if ((oopt & OOPT(oopts_skip,i)) != 0) + it->wod--; + } + + /* Setup the raster to callback channel mappings */ + if (inm != NULL) { + for (e = 0; e < it->id; e++) + it->it_map[e] = inm[e]; /* Copy input */ + } else { + for (e = 0; e < it->id; e++) + it->it_map[e] = e; /* Direct mapping */ + } + if (outm != NULL) { + for (e = 0; e < it->od; e++) + it->im_map[e] = outm[e]; /* Copy input */ + } else { + for (e = 0; e < it->od; e++) + it->im_map[e] = e; /* Direct mapping */ + } + if (checkv != NULL) { + for (e = 0; e < it->od; e++) + it->checkv[e] = checkv[it->im_map[e]]; /* Copy input and convert to Output index */ + } else { + for (e = 0; e < it->od; e++) + it->checkv[e] = 0; /* Set to zero */ + } + + /* Compute interp and simplex table dimension increments & total sizes */ + idinc[0] = 1; + ibdinc[0] = ts->im_ts; + for (e = 1; e <= it->id; e++) { + idinc[e] = idinc[e-1] * gs->itres; + ibdinc[e] = ibdinc[e-1] * gs->itres; + } + + if (!ts->sort) { + sdinc[0] = 1; + sbdinc[0] = ts->sm_ts; + for (e = 1; e <= it->id; e++) { + sdinc[e] = sdinc[e-1] * gs->stres; + sbdinc[e] = sbdinc[e-1] * gs->stres; + } + } + + /* First we setup the input tables */ + for (e = 0; e < it->id; e++) { + byte *t, *p; /* Pointer to input table, entry pointer */ + int ne; /* Number of entries */ + int ex; /* Entry index */ + double iaf; + int ix = 0; /* Extract flag */ + + /* Compute number of entries */ + if (ts->it_ix && !gs->in.packed) { /* Input is the whole bpch[] size */ + ix = 1; /* Need to do extraction in lookup */ + if (gs->in.pint) { + ne = (1 << (gs->in.bpch[0])); /* Same size used for all input tables */ + } else { + ne = (1 << (gs->in.bpch[e])); /* This input channels size */ + } + } else { /* Input is the value size */ + ne = (1 << (gs->in.bpv[e])); /* This input values size */ + } + + /* Allocate the table */ + if ((t = (byte *)malloc(ts->it_ts * ne)) == NULL) { +#ifdef VERBOSE + printf("malloc imdi input table size %d failed\n",ts->it_ts * ne); +#endif + return NULL; /* Should we signal error ? How ? */ + } + it->size += ts->it_ts * ne; +#ifdef VERBOSE + printf("Allocated input table %d size %u = %u * %u\n",e, ts->it_ts * ne,ts->it_ts,ne); +#endif /* VERBOSE */ + + /* Comput input adjustment factor */ + for (iaf = 0.0, i = 0; i < (sizeof(in_adj)/sizeof(double)-1); i++) + iaf += log(in_adj[i]); + iaf += in_adj[i]; + + /* For each possible input value, compute the entry value */ + for (ex = 0, p = t; ex < ne; ex++, p += ts->it_ts) { + int iiv; /* Integer input value */ + int ivr; /* Input value range */ + int isb; /* Input sign bit/signed to offset displacement */ + double riv; /* Real input value, 0.0 - 1.0 */ + double rtv; /* Real transformed value, 0.0 - 1.0 */ + double rmi; /* Real interpolation table index */ + double rsi; /* Real simplex index */ + int imi; /* Interpiolation table index */ + int isi = 0; /* Integer simplex index */ + int iwe = 0; /* Integer weighting value */ + int vo = 0; /* Vertex offset value */ + + if (ix) { /* Extract value from index */ + ivr = ((1 << (gs->in.bpv[e])) -1); + iiv = (ex >> gs->in.bov[e]) & ((1 << (gs->in.bpv[e])) -1); + } else { + ivr = (ne - 1); /* (Should be bpv[e], but take no chances!) */ + iiv = ex; /* Input value is simply index */ + } + isb = ivr & ~(((unsigned int)ivr) >> 1); /* Top bit */ + if (gs->in_signed & (1 << e)) /* Treat input as signed */ + iiv = (iiv & isb) ? iiv - isb : iiv + isb; /* Convert to offset from signed */ + riv = (double) iiv / (double)ivr; /* Compute floating point */ + { + double civ[IXDI], cov[IXDI]; + for (f = 0; f < it->id; f++) + civ[f] = riv; + input_curves(cntx, cov, civ); /* Lookup the input table transform */ + rtv = iaf * cov[it->it_map[e]]; + } + if (rtv < 0.0) /* Guard against sillies */ + rtv = 0.0; + else if (rtv > 1.0) + rtv = 1.0; + + /* divide into interp base and cube sub index */ + rmi = rtv * (gs->itres - 1); + imi = (int)floor(rmi); /* Interp. entry coordinate */ + if (imi >= (gs->itres-1)) /* Keep cube base one row back from far edge */ + imi = gs->itres-2; + rsi = rmi - (double)imi; /* offset into entry cube */ + if (ts->sort) { + iwe = (int)((rsi * (1 << gs->prec)) + 0.5); /* Weighting scale */ + vo = idinc[e] * ts->vo_om; /* Vertex offset */ + } else { + isi = (int)((rsi * gs->stres) + 0.5); + if (isi == gs->stres) { /* Keep simplex index within table */ + isi = 0; + imi++; /* Move to next interp. lattice */ + } + isi *= sdinc[e]; /* Convert the raw indexes into offset in this dim */ + } + imi *= idinc[e]; /* Convert the raw indexes into offset in this dim */ + +#ifdef ASSERTS + /* ~~~ needs fixing for sort ~~~~ */ + if ((imi & (LSHIFT(1,ts->it_ab)-1)) != imi) + error("imdi_tab assert: (imi & ((1 << ts->it_ab)-1)) != imi, imi = 0x%x, it_ab = 0x%x\n",imi,ts->it_ab); + if (imi >= idinc[it->id]) + error("imdi_tab assert: imi >= idinc[it->id]\n"); + if ((isi & (LSHIFT(1,ts->sx_ab)-1)) != isi) + error("imdi_tab assert: (isi & ((1 << ts->sx_ab)-1)) != isi, isi = 0x%x, sx_ab = 0x%x\n",isi,ts->sx_ab); + if (!ts->sort && isi >= sdinc[it->id]) + error("imdi_tab assert: isi >= sdinc[it->id]\n"); +#endif + + /* Now stuff them into the table entry */ + if (ts->sort) { + if (ts->it_xs) { /* Separate interp index and weight/offset*/ + if (ts->wo_xs) { /* All 3 are separate */ + write_entry[ts->ix_es](p + ts->ix_eo, imi); + write_entry[ts->we_es](p + ts->we_eo, iwe); + write_entry[ts->vo_es](p + ts->vo_eo, vo); + } else { + bvt iwo; + + iwo = ((bvt)iwe << ts->vo_ab) | vo; /* Combined weight+vertex offset */ + write_entry[ts->ix_es](p + ts->ix_eo, imi); + write_entry[ts->wo_es](p + ts->wo_eo, iwo); + } + } else { /* All 3 are combined */ + bvt iit; + + iit = ((((bvt)imi << ts->we_ab) | (bvt)iwe) << ts->vo_ab) | vo; + write_entry[ts->it_ts](p, iit); + } + } else { + if (ts->it_xs) { /* Separate interp index and weight/offset*/ + write_entry[ts->ix_es](p + ts->ix_eo, imi); + write_entry[ts->sx_es](p + ts->sx_eo, isi); + } else { + bvt iit; + + iit = ((bvt)imi << ts->sx_ab) | isi; /* Combine interp and simplex indexes */ + write_entry[ts->it_ts](p, iit); + } + } + } + + /* Put table into place */ + it->in_tables[e] = (void *)t; + } + it->nintabs = e; + + /* Setup the interpolation table */ + { + byte *t, *p; /* Pointer to interp table, pointer to total entry */ + PHILBERT(phc) /* Pseudo Hilbert counter */ + double vscale; /* Value scale for fixed point */ + int vsize; /* Fixed point storage size */ + + if (ts->im_cd) + vsize = (gs->prec * 2)/8; /* Fixed point entry & computation size */ + else + vsize = gs->prec/8; /* Fixed point entry size */ + vscale = (1 << gs->prec) -0.50000001; + /* Value scale for fixed point padding */ + /* -0.5 is to prevent carry/rollover after accumulation */ + /* Could get better accuracy with saturation arithmatic */ + + /* Allocate the table */ + if ((t = (byte *)malloc(ibdinc[it->id])) == NULL) { +#ifdef VERBOSE + printf("malloc imdi interpolation table size %d failed\n",ibdinc[it->id]); +#endif + return NULL; /* Should we signal error ? How ? */ + } + it->size += ibdinc[it->id]; +#ifdef VERBOSE + printf("Allocated grid table = %u bytes, composed of %d dim of res %d entry %d\n",ibdinc[it->id], it->id, gs->itres, ts->im_ts); +#endif /* VERBOSE */ + + /* Get ready to access all the entries in the table */ + p = t; + PH_INIT(phc, it->id, gs->itres) + + /* Create all the interpolation table entry values */ + do { + int ee, ff; + double riv[IXDI]; /* Real input values */ + double rev[IXDO]; /* Real entry values */ + unsigned long iev; + byte *pp; /* Pointer to sub-entry */ + + for (e = 0, p = t; e < it->id; e++) { + riv[e] = ((double)phc[e]) / (gs->itres - 1.0); + p += phc[e] * ibdinc[e]; /* Compute pointer to entry value */ + } + + /* Lookup this verticies value */ + { + double mriv[IXDI]; /* Channel mapped real input values */ + double mrev[IXDO]; /* Channel mapped real entry values */ + for (e = 0; e < it->id; e++) + mriv[it->it_map[e]] = riv[e]; + md_table(cntx, mrev, mriv); + for (e = 0; e < it->od; e++) + rev[e] = mrev[it->im_map[e]]; + } + + /* Create all the output values */ + + /* I'm trying to avoid having to declare the actual entry sized */ + /* variables, since it is difficult dynamically. */ + + /* For all the full entries */ + ff = 0; + pp = p; + for (e = 0; e < ts->im_fn; e++, pp += ts->im_fs) { + /* For all channels within full entry */ + for (ee = 0; ee < ts->im_fv; ee++, ff++) { + double revf = rev[ff]; + if (revf < 0.0) /* Guard against sillies */ + revf = 0.0; + else if (revf > 1.0) + revf = 1.0; + iev = (unsigned long)(revf * vscale + 0.5); + + if (bigend) { + write_entry[vsize](pp + (ts->im_fs - (ee+1) * vsize), iev); + } else { + write_entry[vsize](pp + ee * vsize, iev); + } + } + } + + /* For all the 0 or 1 partial entry */ + for (e = 0; e < ts->im_pn; e++) { + /* For all channels within partial entry */ + for (ee = 0; ee < ts->im_pv; ee++, ff++) { + double revf = rev[ff]; + if (revf < 0.0) /* Guard against sillies */ + revf = 0.0; + else if (revf > 1.0) + revf = 1.0; + iev = (unsigned long)(revf * vscale + 0.5); + + if (bigend) { + write_entry[vsize](pp + (ts->im_ps - (ee+1) * vsize), iev); + } else { + write_entry[vsize](pp + ee * vsize, iev); + } + } + } +#ifdef ASSERTS + if (f != it->od) + fprintf(stderr,"imdi_tab assert: f == it->od\n"); +#endif + + PH_INC(phc) + + } while (!PH_LOOPED(phc)); + + /* Put table into place */ + it->im_table = (void *)t; + } + + /* Setup the simplex table */ + if (ts->sort) { + it->sw_table = (void *)NULL; + + } else { + byte *t, *p; /* Pointer to input table, pointer to total entry */ + int nsplx; /* Total number of simplexes */ + XCOMBO(vcmb, it->id+1, 1 << it->id);/* Simplex dimension id out of cube dimention id */ + int comb[24][IXDI]; /* Parameter[id]->Absolute[id] coordinate index */ + int ps[IXDI+1]; /* Base simplex parameter space counter */ + int pse; /* Base simplex parameter space counter index */ + int idioff; /* Interpolation table diagonal offset value */ + + if (it->id > 4) { + fprintf(stderr,"imdi_tabl: internal failure - trying to create simplex table with di > 4!\n"); + exit(-1); + } + + /* Allocate the table */ + if ((t = (byte *)malloc(sbdinc[it->id])) == NULL) { +#ifdef VERBOSE + printf("malloc imdi simplex table size %d failed\n",sbdinc[it->id]); +#endif + return NULL; /* Should we signal error ? How ? */ + } + it->size += sbdinc[it->id]; +#ifdef VERBOSE + printf("Allocated simplex table = %u bytes, composed of %d dim of res %d entry %d\n",sbdinc[it->id], it->id, gs->stres, ts->sm_ts); +#endif /* VERBOSE */ + + /* Compute the interp table offset to the diagonal vertex */ + for (idioff = 0, e = 0; e < it->id; e++) + idioff += idinc[e]; /* Sum one offset in each dimension */ + + /* Figure out how many simplexes fit into this dimension cube, */ + /* and how to map from the base simplex to each actual simplex. */ + XCB_INIT(vcmb); + for (nsplx = 0; ;) { + int i; + + /* XCOMB generates verticies in order from max to min offest */ + + /* Compute Absolute -> Parameter mapping */ + for (e = 0; e < it->id; e++) { /* For each absolute axis */ + for (i = 0; i < it->id; i++) { /* For each verticy, order large to small */ + if ((vcmb[i] & (1< Abs = "); +//for (e = 0; e < it->id; e++) +// printf("%d ",comb[nsplx][e]); +//printf("\n"); + + /* Increment the counter value */ + XCB_INC(vcmb); + nsplx++; + if (XCB_DONE(vcmb)) + break; + } + + /* Now generate the contents of the base simplex, */ + /* and map it to all the symetrical simplexes */ + + /* Init parameter space counter. */ + /* Note that ps[id-1] >= ps[id-2] >= ... >= ps[1] >= ps[0] */ + for (pse = 0; pse < it->id; pse++) + ps[pse] = 0; + ps[pse] = gs->stres-1; + + /* Itterate through the simplex parameter space */ + for (pse = 0; pse < it->id;) { + int qps[IXDI]; /* Quantized parameter values */ + int we[IXDI+1]; /* Baricentric coords/vertex weighting */ + double wvscale = (1 << gs->prec); /* Weighting value scale */ + int sx; /* Simplex */ + +//printf("Param coord ="); +//for (e = it->id-1; e >= 0; e--) { +// printf(" %d",ps[e]); +//} +//printf("\n"); + + for (e = 0; e < it->id; e++) { + /* (Should try wvscale + 0.49999999, or something ?) */ + double tt = (wvscale * (double)ps[e])/((double)gs->stres); + qps[e] = (int)(tt + 0.5); + } + + /* Convert quantized parameter values into weighting values */ + we[it->id] = (1 << gs->prec) - qps[it->id-1]; + for (e = it->id-1; e > 0; e--) + we[e] = qps[e] - qps[e-1]; + we[0] = qps[0]; + +#ifdef ASSERTS + { + int sow = 0; + for (e = it->id; e >= 0; e--) + sow += we[e]; + + if (sow != (1 << gs->prec)) + fprintf(stderr,"imdi_tab assert: sum weights == (1 << gs->prec)\n"); + } +#endif + +//printf("Baricentric coord ="); +//for (e = it->id; e >= 0; e--) { +// printf(" %d",we[e]); +//} +//printf("\n"); + + /* For each simplex, compute the interp. and */ + /* and entry offsets, and write the entry. */ + for (sx = 0; sx < nsplx; sx++ ) { + int v; /* Vertex index */ + byte *pp; /* Pointer to sub-entry */ + unsigned long vofb = 0; /* Vertex offset, base */ + unsigned long vwe; /* Vertex weight */ + + for (e = 0, p = t; e < it->id; e++) { + int ee = comb[sx][e]; /* Absolute coord index */ + p += ps[e] * sbdinc[ee]; /* Pointer to entry */ + } + + /* For each vertex entry */ + for (v = 0, pp = p; v <= it->id; v++) { + unsigned long vof; + if (v == 0) { + vofb = idioff; /* Start at diagonal offset */ + } else { + vofb -= idinc[comb[sx][v-1]];/* Move to next vertex */ + } + vwe = we[v]; /* Weight for this vertex */ + + if (vwe == 0) + vof = 0; /* Use zero offset if weight is zero */ + else + vof = vofb * ts->vo_om; /* Strength reduce kernel scaling */ + + /* Write vwe and vof to entry */ + if (ts->wo_xs) { /* Separate entries */ + write_entry[ts->we_es](pp + ts->we_eo, vwe); + write_entry[ts->vo_es](pp + ts->vo_eo, vof); + pp += ts->wo_es; + } else { /* Combined entries */ + bvt iwo; + + iwo = ((bvt)vwe << ts->vo_ab) | vof; /* Combined weight+vertex offset */ + write_entry[ts->wo_es](pp + ts->wo_eo, iwo); + pp += ts->wo_es; + } + } + + /* Assert vofb == 0 */ +#ifdef ASSERTS + if (vofb != 0) + fprintf(stderr,"imdi_tab assert: vofb == 0\n"); +#endif + } /* Next simplex */ + + /* Increment the parameter coords */ + for (pse = 0; pse < it->id; pse++) { + ps[pse]++; + if (ps[pse] <= ps[pse+1]) + break; /* No carry */ + ps[pse] = 0; + } + } + + /* Put table into place */ + it->sw_table = (void *)t; + } + + /* Last, setup the output tables */ + for (e = 0; e < it->od; e++) { + byte *t, *p; /* Pointer to output table, entry pointer */ + int ne; /* Number of entries */ + int iiv; /* Integer input value */ + double ivr = (double)((1 << gs->prec)-1); /* Input value range */ + double ovr = (double)((1 << ts->ot_bits[e])-1); /* Output value range */ + int osb = (1 << (ts->ot_bits[e]-1)); /* Output offset to signed displacement */ + int ooff = ts->ot_off[e]; /* Output value bit offset */ + + ne = (1 << gs->prec); /* Output of clut is prec bits */ + + /* Allocate the table */ + if ((t = (byte *)malloc(ts->ot_ts * ne)) == NULL) { +#ifdef VERBOSE + printf("malloc imdi output table size %d failed\n",ts->ot_ts * ne); +#endif + return NULL; /* Should we signal error ? How ? */ + } + it->size += ts->ot_ts * ne; +#ifdef VERBOSE + printf("Allocated output table %d size %u = %u * %u\n",e, ts->ot_ts * ne,ts->ot_ts,ne); +#endif /* VERBOSE */ + + /* For each possible output value, compute the entry value */ + for (iiv = 0, p = t; iiv < ne; iiv++, p += ts->ot_ts) { + double riv; /* Real input value, 0.0 - 1.0 */ + double rtv; /* Real transformed value, 0.0 - 1.0 */ + unsigned long iov; /* Integer output value */ + + riv = (double) iiv / ivr; /* Compute floating point */ + { + double civ[IXDO], cov[IXDO]; + for (f = 0; f < it->od; f++) + civ[f] = riv; + output_curves(cntx, cov, civ); /* Lookup the input table transform */ + rtv = cov[it->im_map[e]]; + } + if (rtv < 0.0) /* Guard against sillies */ + rtv = 0.0; + else if (rtv > 1.0) + rtv = 1.0; + iov = (unsigned long)(rtv * ovr + 0.5); /* output value */ + if (gs->out_signed & (1 << e)) /* Treat output as signed */ + iov = (iov & osb) ? iov - osb : iov + osb; /* Convert to signed from offset */ + iov <<= ooff; /* Aligned for output */ + + write_entry[ts->ot_ts](p, iov); /* Write entry */ + } + + /* Put table into place */ + it->out_tables[e] = (void *)t; + } + it->nouttabs = e; + + /* Adjust the check values for output value shift */ + for (e = 0; e < it->od; e++) { + int ooff = ts->ot_off[e]; /* Output value bit offset */ + it->checkv[e] <<= ooff; /* Aligned for output */ + } + + /* Setup the appropriate skip flags, indexed by Output channel */ + it->skipf = 0; + if ((oopt & OOPTS_SKIP) != 0) { + int i; + for (i = 0; i < it->od; i++) { + if (oopt & OOPT(oopts_skip,it->im_map[i])) { /* Skip flag for this output chan */ + it->skipf |= (1 << i); + } + } + } + + /* Fill in some report information */ + it->gres = gs->itres; + if (!ts->sort) { + it->sres = gs->stres; + } else { + it->sres = 0; + } + +#ifdef VERBOSE + printf("imdi_tabl returning OK\n"); +#endif + return it; +} + +/* Free up the data allocated */ +void +imdi_tab_free( +imdi_imp *it +) { + int e; + + for (e = 0; e < it->nintabs; e++) + free(it->in_tables[e]); + + if (it->sw_table != NULL) + free(it->sw_table); + if (it->im_table != NULL) + free(it->im_table); + + for (e = 0; e < it->nouttabs; e++) + free(it->out_tables[e]); + + free(it); + +} + + + + + + + + + + + + + + + diff --git a/imdi/imdi_tab.h b/imdi/imdi_tab.h new file mode 100644 index 0000000..403fcaa --- /dev/null +++ b/imdi/imdi_tab.h @@ -0,0 +1,170 @@ +#ifndef IMDI_TAB_H +#define IMDI_TAB_H + +/* Integer Multi-Dimensional Interpolation */ + +/* + * Copyright 2000 - 2007 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. + */ + +/* + * Implementation details needed for table initialisation for a particular + * kernel. This is private implementation for imdi.[ch] + * + * The tabspec structure holds detailed information on the algorithms used + * by the runtime code, and (implicit in this) the layout of the runtime + * tables needed to match the algorithm. There are also implicit dependencies on + * the genspec structure, since this determines the overall features + * supported by a particular pixel kernel module. + * + * This is effectively the product of the genspec, the architechure, + * and the coding choices made by the code generator + * (ie. gen_c_kernel() in cgen.c) + * + */ + +/* entries marked with '#' are not currently used by imdi_tab() */ +/* NOTE :- if you change this, you need to change the code in cgen.c */ +/* labeled !genspec and tabspec delta code! */ +struct _tabspec { + + int sort; /* NZ for explicit sort rather than simplex table lookup */ + int it_xs; /* NZ if separate interp index and simplex index/Weighting+Offset values */ + int wo_xs; /* NZ if separate weighting and vertex offset entries are to be used */ + + int it_ix; /* Non-zero if input value extraction should be done in input table */ + int it_ab; /* Input table entry size in bits */ + int it_ts; /* Input table :- total input table entry size in bytes */ + /* Bit packing order is (ms to ls) : + sort: ix, we, vo + sort: ix, wo + !sort: ix, sx + */ + + /* Interpolation index is always in the input table */ + int ix_ab; /* # Interpolation index entry size in bits */ + int ix_es; /* Interpolation index entry size in bytes */ + int ix_eo; /* Interpolation index entry offset in bytes */ + + /* Simplex Index is always in the input table */ + int sx_ab; /* Simplex Index entry size in bits */ + int sx_es; /* Simplex Index entry size in bytes */ + int sx_eo; /* Simplex Index entry offset in bytes */ + + int sm_ts; /* Simplex table entry total size in bytes */ + /* Bit packing order is (ms to ls) : we, vo */ + + /* Combined Weighting + Offset may be in input table or Simplex entry */ + int wo_ab; /* Combined Weighting + Offset entry size in bits */ + int wo_es; /* Combined Weighting + Offset entry size in bytes */ + int wo_eo; /* Combined Weighting + Offset entry offset in bytes */ + + /* Weighting may be in input table or Simplex entry */ + int we_ab; /* # Weighting entry size in bits */ + int we_es; /* Weighting entry size in bytes */ + int we_eo; /* Weighting entry offset in bytes */ + + /* Vertex offset may be in input table or Simplex entry */ + int vo_ab; /* Vertex Offset entry size in bits */ + int vo_es; /* Vertex Offset entry size in bytes */ + int vo_eo; /* Vertex Offset entry offset in bytes */ + int vo_om; /* Vertex Offset scaling multiplier */ + + int im_cd; /* Non-zero if interpolation table entries are padded with fraction */ + int im_ts; /* Interp. multidim :- total interp table entry size in bytes */ + int im_oc; /* # Interp. multidim :- offset scale to apply to index into interp entry */ + int im_fs; /* Interp. multidim :- full table entry size in bytes */ + int im_fn; /* Interp. multidim :- number of full entries */ + int im_fv; /* Interp. multidim :- output values per full entry . */ + int im_ps; /* Interp. multidim :- partial table entry size in bytes, used & unsused */ + int im_pn; /* Interp. multidim :- number of partial entries - must be 0 or 1 */ + int im_pv; /* Interp. multidim :- used output values per partial entry . */ + + int ot_ts; /* Output table :- total entry size in bytes of every table */ + int ot_off[IXDO]; /* Offset for each output value within the output word needed */ + int ot_bits[IXDO]; /* Number of bits for value within the output word needed */ + + /* Associated interpolation function */ + void (*interp)(struct _imdi *s, void **inp, void **outp, unsigned int npix); /* At run time */ +}; typedef struct _tabspec tabspec; + +/* Runtime conversion needed */ +typedef enum { + conv_none = 0x00, /* No conversion needed */ + conv_istr = 0x01, /* Input stride conversion */ + conv_ostr = 0x02, /* Output stride conversion */ + conv_irep = 0x04, /* Input representation conversion */ + conv_orep = 0x08, /* Output representation conversion */ + conv_rev = 0x10, /* Reverse direction conversion */ + conv_skip = 0x20 /* Skip output channel write conversion */ +} imdi_conv; + +/* The actual run time table that tabspec describes */ +typedef struct { + /* Runtime setup */ + int id; /* Number of input dimensions */ + int od; /* Number of output dimensions (including skip channels) */ + int wod; /* Number of written output dimensions ( < od if skipf != 0) */ + int it_map[IXDI]; /* Mapping from input raster channels to callback channels. */ + int im_map[IXDO]; /* Mapping from output raster channels to callback channels. */ + imdi_pixrep cirep; /* High level input pixel representation called with */ + imdi_pixrep corep; /* High level output pixel representation called with */ + imdi_pixrep firep; /* High level input pixel representation of interp func. */ + imdi_pixrep forep; /* High level output pixel representation of interp func. */ + imdi_conv cnv; /* Runtime argument conversion needed */ + void (*interp)(struct _imdi *s, void **outp, int outst, /* Underlying conversion function */ + void **inp, int inst, + unsigned int npixels); + /* Output channel check data */ + unsigned long checkv[IXDO]; /* Output per channel check values. Set flag if != checkv */ + unsigned int checkf; /* Output per channel check flags (one per bit) */ + unsigned int skipf; /* Output per channel skip flags (one per bit) */ + + /* Table data */ + void *in_tables[IXDI]; /* Input dimension input lookup tables */ + void *sw_table; /* Simplex weighting lookup table */ + void *im_table; /* Interpolation Multi-dimensional lookup table */ + void *out_tables[IXDO]; /* Output dimension output lookup tables */ + int nintabs; /* Number of input tables */ + int nouttabs; /* Number of output tables */ + + /* Extra reporting data */ + unsigned long size; /* Number of bytes allocated to imdi_imp */ + unsigned int gres, sres; /* Grid and simplex table resolutions. sres = 0 = sort */ +} imdi_imp; + +/* + * The runtime function that knows how to setup an imdi_imp + * table for for our chosen kernel and the color mapping we + * want to perform. + */ + +imdi_imp * +imdi_tab( + genspec *gs, /* Pointer to gen spec */ + tabspec *ts, /* Pointer to table spec */ + imdi_conv cnv, /* Runtime argument conversion needed */ + imdi_pixrep irep, /* High level input pixel representation to match */ + imdi_pixrep orep, /* High level output pixel representation to match */ + void (*interp)(struct _imdi *s, void **outp, int outst, /* Underlying conversion function */ + void **inp, int inst, + unsigned int npixels), + int *inm, /* Input raster channel to callback channel mapping, NULL for none. */ + int *outm, /* Output raster channel to callback channel mapping, NULL for none. */ + imdi_ooptions oopt, /* Output per channel options (Callback channel, NOT written channel) */ + unsigned int *checkv, /* Output channel check values (Callback channel, NULL for none == 0. */ + + /* Callbacks to initialse the imdi table values */ + void (*input_curves) (void *cntx, double *out_vals, double *in_vals), + void (*md_table) (void *cntx, double *out_vals, double *in_vals), + void (*output_curves)(void *cntx, double *out_vals, double *in_vals), + void *cntx /* Context of callbacks */ +); + +void imdi_tab_free(imdi_imp *it); + +#endif /* IMDI_TAB_H */ diff --git a/imdi/imdi_utl.h b/imdi/imdi_utl.h new file mode 100644 index 0000000..f1a3ff5 --- /dev/null +++ b/imdi/imdi_utl.h @@ -0,0 +1,262 @@ +#ifndef IMDI_UTL_H +#define IMDI_UTL_H + +/* Integer Multi-Dimensional Interpolation */ + +/* + * Copyright 2000 - 2007 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. + */ + +#include + +/* Common utility definitions used at both generation and runtime. */ + +#define IXDI 10 /* maximum input channels/dimensions allowed */ +#define IXDO 10 /* maximum output channels/dimensions allowed */ + +#if IXDI > IXDO /* Maximum of either DI or DO */ +# define IXDIDO IXDI +#else +# define IXDIDO IXDO +#endif + +#define ALLOW64 /* Allow declarations but not use of 64 bit types */ + +#ifndef FORCE64 +# undef FORCE64 /* Use 64 bit, even on architectures where it's */ + /* not a native size. ALLOW64 must be defined */ +#endif + +/* ------------------------------------------------------ */ + +#if defined(ALLOW64) && (ULONG_MAX == 0xffffffffffffffffUL || defined(FORCE64)) +#ifndef USE64 +#pragma message("Using 64 bit integer color kernel") +#endif /* USE64 */ +#define USE64 /* Use 64 bits if it's natural or forced */ +#endif + +/* ------------------------------------------------------- */ +/* Macros combination counter */ +/* Declare the counter name nn, combinations out of total */ +/* Maximum combinations is DI+2 */ + +#define COMBO(nn, comb, total) \ + int nn[IXDI+2]; /* counter value */ \ + int nn##_cmb = (comb); /* number of combinations*/ \ + int nn##_tot = (total); /* out of total possible */ \ + int nn##_e /* dimension index */ + +/* Set total to new setting */ +#define CB_SETT(nn, total) \ + nn##_tot = (total) /* total possible */ + +/* Set combinations to new setting */ +#define CB_SETC(nn, comb) \ + nn##_cmb = (comb) /* number of combinations*/ + +/* Set the counter to its initial value */ +#define CB_INIT(nn) \ +{ \ + for (nn##_e = 0; nn##_e < nn##_cmb; nn##_e++) \ + nn[nn##_e] = nn##_cmb-nn##_e-1; \ + nn##_e = 0; \ +} + +/* Increment the counter value */ +#define CB_INC(nn) \ +{ \ + for (nn##_e = 0; nn##_e < nn##_cmb; nn##_e++) { \ + nn[nn##_e]++; \ + if (nn[nn##_e] < (nn##_tot-nn##_e)) { \ + int nn##_ee; /* No carry */ \ + for (nn##_ee = nn##_e-1; nn##_ee >= 0; nn##_ee--) \ + nn[nn##_ee] = nn[nn##_ee+1] + 1; \ + break; \ + } \ + } \ +} + +/* After increment, expression is TRUE if counter is done */ +#define CB_DONE(nn) \ + (nn##_e >= nn##_cmb) + + +/* ------------------------------------------------------- */ +/* Macros simplex combination counter. */ +/* Based on COMBO, but skips invalid simplex combinations */ + +#define XCOMBO(nn, comb, total) \ + COMBO(nn, comb, total) + +/* Set total to new setting */ +#define XCB_SETT(nn, total) \ + CB_SETT(nn, total) + +/* Set combinations to new setting */ +#define XCB_SETC(nn, comb) \ + CB_SETC(nn, comb) + + +/* Set the counter to its initial value */ +#define XCB_INIT(nn) \ +{ \ + int nn##_ii; \ + \ + for (nn##_e = 0; nn##_e < nn##_cmb; nn##_e++) \ + nn[nn##_e] = nn##_cmb-nn##_e-1; \ + for (nn##_ii = 1; nn##_ii < nn##_cmb; nn##_ii++) { \ + if ((nn[nn##_ii-1] ^ nn[nn##_ii]) & nn[nn##_ii])\ + break; /* Went from 0 to 1 */ \ + } \ + if (nn##_ii < nn##_cmb) { /* Fix invalid combination */ \ + XCB_INC(nn); \ + } \ + nn##_e = 0; \ +} + +/* Increment the counter value */ +#define XCB_INC(nn) \ +{ \ + int nn##_ii = 0; \ + \ + while (nn##_ii < nn##_cmb) { \ + for (nn##_e = 0; nn##_e < nn##_cmb; nn##_e++) { \ + nn[nn##_e]++; \ + if (nn[nn##_e] < (nn##_tot-nn##_e)) { \ + int nn##_ee; /* No carry */ \ + for (nn##_ee = nn##_e-1; nn##_ee >= 0; nn##_ee--) \ + nn[nn##_ee] = nn[nn##_ee+1] + 1; \ + break; \ + } \ + } \ + if (nn##_e >= nn##_cmb) \ + break; /* Done */ \ + \ + /* Reject invalid combinations */ \ + for (nn##_ii = 1; nn##_ii < nn##_cmb; nn##_ii++) { \ + if ((nn[nn##_ii-1] ^ nn[nn##_ii]) & nn[nn##_ii]) \ + break; /* Went from 0 to 1 */ \ + } \ + } \ +} + +/* After increment, expression is TRUE if counter is done */ +#define XCB_DONE(nn) \ + CB_DONE(nn) + +/* ------------------------------------------------------- */ +/* Macro pseudo-hilbert counter */ +/* This multi-dimensional count sequence is a distributed */ +/* Gray code sequence, with direction reversal on every */ +/* alternate power of 2 scale. */ +/* It is intended to aid cache coherence in multi-dimensional */ +/* regular sampling. It approximates the Hilbert curve sequence. */ + +#define PHILBERT(nn) \ + int nn[IXDIDO];/* counter value */ \ + int nn##di; /* Dimensionality */ \ + unsigned nn##res; /* Resolution per coordinate */ \ + unsigned nn##bits; /* Bits per coordinate */ \ + unsigned nn##ix; /* Current binary index */ \ + unsigned nn##tmask; /* Total 2^n count mask */ \ + unsigned nn##count; /* Usable count */ + +/* Init counter for dimenion di, resolution res */ +#define PH_INIT(nn, pdi, pres) \ +{ \ + int nn##e; \ + \ + nn##di = pdi; \ + nn##res = (unsigned)pres; \ + \ + /* Compute bits */ \ + for (nn##bits = 0; (1u << nn##bits) < nn##res; nn##bits++) \ + ; \ + \ + /* Compute the total count mask */ \ + nn##tmask = ((1u << (nn##bits * nn##di))-1); \ + \ + /* Compute usable count */ \ + nn##count = 1; \ + for (nn##e = 0; nn##e < nn##di; nn##e++) \ + nn##count *= nn##res; \ + \ + nn##ix = 0; \ + for (nn##e = 0; nn##e < nn##di; nn##e++) \ + nn[nn##e] = 0; \ +} + +/* Increment the counter value */ +#define PH_INC(nn) \ +{ \ + int nn##e; \ + do { \ + unsigned int nn##b; \ + int nn##gix; /* Gray code index */ \ + \ + nn##ix = (nn##ix + 1) & nn##tmask; \ + \ + /* Convert to gray code index */ \ + nn##gix = nn##ix ^ (nn##ix >> 1); \ + \ + for (nn##e = 0; nn##e < nn##di; nn##e++) \ + nn[nn##e] = 0; \ + \ + /* Distribute bits */ \ + for (nn##b = 0; nn##b < nn##bits; nn##b++) { \ + if (nn##b & 1) { /* In reverse order */ \ + for (nn##e = nn##di-1; nn##e >= 0; nn##e--) { \ + nn[nn##e] |= (nn##gix & 1) << nn##b; \ + nn##gix >>= 1; \ + } \ + } else { /* In normal order */ \ + for (nn##e = 0; nn##e < nn##di; nn##e++) { \ + nn[nn##e] |= (nn##gix & 1) << nn##b; \ + nn##gix >>= 1; \ + } \ + } \ + } \ + \ + /* Convert from Gray to binary coordinates */ \ + for (nn##e = 0; nn##e < nn##di; nn##e++) { \ + unsigned nn##sh, nn##tv; \ + \ + for(nn##sh = 1, nn##tv = nn[nn##e];; nn##sh <<= 1) { \ + unsigned nn##ptv = nn##tv; \ + nn##tv ^= (nn##tv >> nn##sh); \ + if (nn##ptv <= 1 || nn##sh == 16) \ + break; \ + } \ + /* Filter - increment again if outside cube range */ \ + if (nn##tv >= nn##res) \ + break; \ + nn[nn##e] = nn##tv; \ + } \ + \ + } while (nn##e < nn##di); \ + \ +} + +/* After increment, expression is TRUE if counter has looped back to start. */ +#define PH_LOOPED(nn) \ + (nn##ix == 0) \ + +/* ------------------------------------------------------- */ + +#endif /* IMDI_UTL_H */ + + + + + + + + + + + diff --git a/imdi/itest.c b/imdi/itest.c new file mode 100644 index 0000000..8edcb8b --- /dev/null +++ b/imdi/itest.c @@ -0,0 +1,652 @@ + +/* Verify and benchmark the imdi code */ +/* + * Copyright 2000 - 2006 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. + */ + +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#include "numlib.h" +#include "imdi.h" +#include "refi.h" + +/* Test parameters */ +#undef TEST1 /* Test just one combination */ +#define FULL /* Test full range */ +#undef VERBOSE /* Report every conversion */ +#undef REPORT_ERRORS /* Report conversion with large errors */ +#define TRAND /* Test random in/out table contents */ +#define TCRAND /* Test random clut table contents */ +#undef QUANTIZE /* Quantize the target table values */ +#define TBUFSIZE (2 * 1024 * 1024) /* Default number of input bytes to test */ +#define ITERS 10 /* Itterations */ + +double trans1(double in, double t); +double trans2(double in, double t); + +/* Context for refi setup callbacks */ +typedef struct { + int id; + int od; + double icurve[MXDI]; /* Input curve factors */ + double clut[MXDO][MXDI]; /* clut matrix factors */ + double ocurve[MXDO]; /* Output curve factors */ +} rcntx; + +/* Input curve function */ +static void input_curves( + void *cntx, + double *out_vals, + double *in_vals +) { + int i; + rcntx *rx = (rcntx *)cntx; + + for (i = 0; i < rx->id; i++) { + double val = in_vals[i]; +#ifdef TRAND + val = trans1(val, rx->icurve[i]); +#ifdef QUANTIZE + val = ((int)(val * 255.0 + 0.5))/255.0; /* Quantize to 8 bits */ +#endif +#endif + out_vals[i] = val; + } +} + +/* Multi-dim table function */ +static void md_table( + void *cntx, + double *out_vals, + double *in_vals +) { + rcntx *rx = (rcntx *)cntx; + int i, j; + +#ifdef TCRAND + for (j = 0; j < rx->od; j++) { + double val = 0.0; + for (i = 0; i < rx->id; i++) { + val += rx->clut[j][i] * in_vals[i]; + } +#ifdef QUANTIZE + val = ((int)(val * 255.0 + 0.5))/255.0; /* Quantize to 8 bits */ +#endif + out_vals[j] = val; + } +#else + for (j = 0; j < rx->od; j++) + out_vals[j] = in_vals[j % rx->id]; +#endif +//printf("~1 out %f %f %f %f from %f\n", out_vals[0], out_vals[1], out_vals[2], out_vals[3], in_vals[0]); +} + +/* Output curve function */ +static void output_curves( + void *cntx, + double *out_vals, + double *in_vals +) { + int i; + rcntx *rx = (rcntx *)cntx; + + for (i = 0; i < rx->od; i++) { + double val = in_vals[i]; +#ifdef TRAND + val = trans1(val, rx->ocurve[i]); +#ifdef QUANTIZE + val = ((int)(val * 255.0 + 0.5))/255.0; /* Quantize to 8 bits */ +#endif +#endif + out_vals[i] = val; + } +} + +/* Complete reference interpolation */ +void refi_interp(refi *r, double *out_vals, double *in_vals); + +void usage(void) { + fprintf(stderr,"Regression test imdi code Version %s\n",ARGYLL_VERSION_STR); + fprintf(stderr,"usage: itest [-q] [-s]\n"); + fprintf(stderr," -q Quick test\n"); + fprintf(stderr," -s Stop on error\n"); + fprintf(stderr," -r bits Specify bits of randomness in input data\n"); + exit(1); +} + + +int +main(int argc, char *argv[]) { + int fa,nfa; /* argument we're looking at */ + int quick = 0; + int stop = 0; + int rbits = 16; + int n, i, j, e; + int pix, iix, oix; + int ip, op, id, od; + int ires, cres, ores; + clock_t stime, ttime; /* Start and total times */ + double xtime; /* Total execution time in seconds */ + double npixels; /* Number of pixels processed */ + rcntx rx; + + refi *r; + double ribuf[MXDI]; + double robuf[MXDO]; + + imdi *s; + int iters; + unsigned long tbufsize; + unsigned char *ibuf; + unsigned char *obuf; + unsigned char *inp[MXDI]; + unsigned char *outp[MXDO]; + unsigned short *ibuf2; /* 16 bit references */ + unsigned short *obuf2; + + double omxerr = 0; /* Overall max error /1.0 */ + + /* Define combinations to test */ +#ifdef TEST1 +#pragma message("!!!!!!!!!!!!!!!!! TEST1 is defined !!!!!!!!!!!!!!!1") + int ids[] = { 3, 0 }; /* Input dimensions */ + int ods[] = { 3, 0 }; /* Output dimensions */ + int iprs[] = { 16, 0 }; + int oprs[] = { 16, 0 }; +#else +#ifndef FULL + int ids[] = { 1, 3, 4, 8, 0 }; + int ods[] = { 1, 3, 4, 8, 0 }; + int iprs[] = { 8, 8, 16, 0}; + int oprs[] = { 8, 16, 16, 0}; +#else + int ids[] = { 1, 3, 4, 5, 6, 7, /* 8, */ 0 }; + int ods[] = { 1, 3, 4, 5, 6, 7, /* 8, */ 0 }; + int iprs[] = { 8, 8, 16, 0}; + int oprs[] = { 8, 16, 16, 0}; +#endif +#endif + + /* 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 */ + } + } + } + + if (argv[fa][1] == '?') + usage(); + + /* Quick test */ + else if (argv[fa][1] == 'q' || argv[fa][1] == 'Q') { + quick = 1; + } + + /* Stop on error */ + else if (argv[fa][1] == 's' || argv[fa][1] == 'S') { + stop = 1; + } + + /* Degree of data randomness */ + else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') { + fa = nfa; + if (na == NULL) usage(); + rbits = atoi(na); + if (rbits < 0) + rbits = 1; + else if (rbits > 16) + rbits = 16; + } + else + usage(); + } else + break; + } + + for (iix = 0; ids[iix] > 0; iix++) { /* All input dimensions */ + for (oix = 0; ods[oix] > 0; oix++) { /* All output dimensions */ + for (pix = 0; iprs[pix] > 0; pix++) { /* All precisions */ + int rmask = 0; + ip = iprs[pix]; /* Input precision */ + op = oprs[pix]; /* Output precision */ + id = ids[iix]; /* Input dimensions */ + od = ods[oix]; /* Outpu dtimensions */ + + if (ip != 8 && ip != 16) + error("Can't handle input precision of %d in testing\n",ip); + if (op != 8 && op != 16) + error("Can't handle output precision of %d in testing\n",op); + if (id > MXDI) + error("Can't handle id greater than %d in testing\n",MXDI); + if (od > MXDO) + error("Can't handle od greater than %d in testing\n",MXDO); + + printf("Testing id = %d, od = %d, ip = %d, op = %d\n",id,od,ip,op); + + ires = 256; + + switch (id) { + case 1: + cres = 257; + break; + case 2: + case 3: + cres = 33; + break; + case 4: + cres = 17; + break; + case 5: + cres = 7; + break; + case 6: + cres = 7; + break; + case 7: + cres = 5; + break; + case 8: + cres = 5; + break; + default: + cres = 3; + } + ores = 256; + + /* Setup error messages */ + error_program = "itest"; + + /* Create the reference first */ + printf("About to create refi\n"); + + /* Create test curves */ + rx.id = id; + rx.od = od; + + rand32(0x1234); + for (i = 0; i < id; i++) { + rx.icurve[i] = d_rand(-1.0, 1.0); + } + +//printf("Matrix params =\n"); + rand32(0x2345); + for (j = 0; j < od; j++) { + double s = 0.0; + for (i = 0; i < id; i++) { + double v = d_rand(1.0, 0.0); + rx.clut[j][i] = v; /* Make them sum to 1.0 */ + s += v; + } + for (i = 0; i < id; i++) { + rx.clut[j][i] /= s; +//printf(" %f",rx.clut[j][i]); + } +//printf("\n"); + } + + rand32(0x3456); + for (j = 0; j < od; j++) { + rx.ocurve[j] = d_rand(-1.0, 1.0); + } + + r = new_refi( + id, /* Number of input dimensions */ + od, /* Number of output dimensions */ + ires, /* Input table resolution */ + cres, /* clut resolution */ + ores, /* Output table resolution */ + input_curves, /* Callback functions */ + md_table, + output_curves, + (void *)&rx /* Context to callbacks */ + ); + + if (r == NULL) { + error("new_refi failed"); + } + + /* Now crate the imdi we want to test, using the */ + /* reference as a template */ + printf("About to create imdi\n"); + + s = new_imdi( + id, /* Number of input dimensions */ + od, /* Number of output dimensions */ + ip == 8 ? pixint8 : pixint16, /* Input pixel representation */ + 0x0, /* Treat every channel as unsigned */ + NULL, /* No raster to callback mapping */ + prec_min, /* Minimum of input and output precision */ + op == 8 ? pixint8 : pixint16, /* Output pixel representation */ + 0x0, /* Treat every channel as unsigned */ + NULL, /* No raster to callback mapping */ + cres, /* Desired table resolution */ + oopts_none, /* Desired per channel output options */ + NULL, /* Output channel check values */ + opts_none, /* Desired processing direction and stride support */ + refi_input, /* Callback functions */ + refi_clut, + refi_output, + (void *)r /* Context to callbacks */ + ); + + if (s == NULL) { + error("new_imdi failed"); + } + + if (quick) { + iters = 1; + tbufsize = 4096/id; + } else { + iters = ITERS; + tbufsize = TBUFSIZE/id; + } + + /* Allocate the test buffers */ + if ((ibuf = malloc(sizeof(unsigned char) * ip/8 * id * tbufsize)) == NULL) + error("Malloc of input buffer failed"); + ibuf2 = (unsigned short *)ibuf; + + if ((obuf = malloc(sizeof(unsigned char) * op/8 * od * tbufsize)) == NULL) + error("Malloc of output buffer failed"); + obuf2 = (unsigned short *)obuf; + + /* Initialise the input buffer contents */ + rand32(0x12345678); + if (ip == 8) { + unsigned ui; + int rr = rbits; + if (rr > 8) + rr = 8; + rmask = ((1 << rr) -1) << (8-rr); + for (ui = 0; ui < tbufsize; ui += id) { + for (e = 0; e < id; e++) { + unsigned long ran = rand32(0); + ibuf[ui + e] = (unsigned char)(ran & rmask); + } + } + } else { + unsigned ui; + rmask = ((1 << rbits) -1) << (16-rbits); + for (ui = 0; ui < tbufsize; ui += id) { + for (e = 0; e < id; e++) { + unsigned long ran = rand32(0); + ibuf2[ui + e] = (unsigned short)(ran & rmask); + } + } + } + + /* We are assuming packed pixel interleaved */ + inp[0] = ibuf; + outp[0] = obuf; + + /* Benchmark it */ + stime = clock(); + for (n = 0; n < iters; n++) { + s->interp(s, (void **)outp, 0, (void **)inp, 0, tbufsize); + } + ttime = clock() - stime; + xtime = (double)ttime/(double)CLOCKS_PER_SEC; + npixels = (double)iters * (double)tbufsize; + + if (xtime > 0.0) + printf("Speed = rate = %f Mpix/sec\n",1e-6 * npixels / xtime); + else + printf("Speed - too fast!\n"); + + { + unsigned ui; + double mxerr = 0.0; + double avgerr = 0.0; + + /* Verify the accuracy against refi of each sample */ + for (ui = j = 0; ui < tbufsize; ui += id, j += od) { + int mxserr; + + if (ip == 8) { + for (e = 0; e < id; e++) { + ribuf[e] = ibuf[ui + e]/255.0; + } + } else { + for (e = 0; e < id; e++) { + ribuf[e] = ibuf2[ui + e]/65535.0; + } + } + refi_interp(r, robuf, ribuf); + + mxserr = 0; + + if (op == 8) { + for (e = 0; e < od; e++) { + double err = robuf[e] * 255.0 - obuf[j + e]; + if (err < 0) + err = -err; + if (err > mxerr) + mxerr = err; + if (err > mxserr) + mxserr = err; + avgerr += err; + } + } else { + for (e = 0; e < od; e++) { + double err = robuf[e] * 65535.0 - obuf2[j + e]; + if (err < 0) + err = -err; + if (err > mxerr) + mxerr = err; + if (err > mxserr) + mxserr = err; + avgerr += err; + } + } +#if defined(REPORT_ERRORS) || defined(VERBOSE) + if (mxserr >= 37) { + if (ip == 8) { + if (id == 1) + printf("in %d, ", ibuf[ui+0]); + if (id == 2) + printf("in %d %d, ", ibuf[ui+0], ibuf[ui+1]); + if (id == 3) + printf("in %d %d %d, ", ibuf[ui+0], ibuf[ui+1], ibuf[ui+2]); + if (id == 4) + printf("in %d %d %d, ", + ibuf[ui+0], ibuf[ui+1], ibuf[ui+2], ibuf[ui+3]); + } else { + if (id == 1) + printf("in %d, ", ibuf2[ui+0]); + if (id == 2) + printf("in %d %d, ", ibuf2[ui+0], ibuf2[ui+1]); + if (id == 3) + printf("~in %d %d %d, ", ibuf2[ui+0], ibuf2[ui+1], ibuf2[ui+2]); + if (id == 4) + printf("in %d %d %d, ", + ibuf2[ui+0], ibuf2[ui+1], ibuf2[ui+2], ibuf2[ui+3]); + } + if (ip == 8) { + if (od == 1) + printf("is %d, should be %d\n", + obuf[j+0], + (int)(robuf[0] * 255.0 + 0.5)); + if (od == 2) + printf("is %d %d, should be %d %d\n", + obuf[j+0], obuf[j+1], + (int)(robuf[0] * 255.0 + 0.5), + (int)(robuf[1] * 255.0 + 0.5)); + if (od == 3) + printf("is %d %d %d, should be %d %d %d\n", + obuf[j+0], obuf[j+1], obuf[j+2], + (int)(robuf[0] * 255.0 + 0.5), + (int)(robuf[1] * 255.0 + 0.5), + (int)(robuf[2] * 255.0 + 0.5)); + if (od == 4) + printf("is %d %d %d %d, should be %d %d %d %d\n", + obuf[j+0], obuf[j+1], obuf[j+2], obuf[j+3], + (int)(robuf[0] * 255.0 + 0.5), + (int)(robuf[1] * 255.0 + 0.5), + (int)(robuf[2] * 255.0 + 0.5), + (int)(robuf[3] * 255.0 + 0.5)); + } else { + if (od == 1) + printf("is %d, should be %d\n", + obuf2[j+0], + (int)(robuf[0] * 65535.0 + 0.5)); + if (od == 2) + printf("is %d %d, should be %d %d\n", + obuf2[j+0], obuf2[j+1], + (int)(robuf[0] * 65535.0 + 0.5), + (int)(robuf[1] * 65535.0 + 0.5)); + if (od == 3) + printf("is %d %d %d, should be %d %d %d\n", + obuf2[j+0], obuf2[j+1], obuf2[j+2], + (int)(robuf[0] * 65535.0 + 0.5), + (int)(robuf[1] * 65535.0 + 0.5), + (int)(robuf[2] * 65535.0 + 0.5)); + if (od == 4) + printf("is %d %d %d %d, should be %d %d %d %d\n", + obuf2[j+0], obuf2[j+1], obuf2[j+2], obuf2[j+3], + (int)(robuf[0] * 65535.0 + 0.5), + (int)(robuf[1] * 65535.0 + 0.5), + (int)(robuf[2] * 65535.0 + 0.5), + (int)(robuf[3] * 65535.0 + 0.5)); + } + } + +#endif /* VERBOSE || REPORT+ERRORS */ + } + avgerr /= ((double)tbufsize * od); + + { + double fmxerr; /* Relative maximum error */ + double favgerr; /* Relative average error */ + + /* Always relative to basic precision */ + fmxerr = mxerr / ((1 << op) - 1.0); + favgerr = avgerr / ((1 << op) - 1.0); + + printf("Worst error = %f = %f%%, average error = %f%%\n", + mxerr, 100.0 * fmxerr, 100.0 * favgerr); + printf("\n"); + + if (fmxerr > omxerr) + omxerr = fmxerr; + + } + } + /* Free everything up */ + free(ibuf); + free(obuf); + refi_free(r); + s->del(s); + + if (stop && (100.0 * omxerr) > 1.2) { + goto quit; + } + } + } + } + + quit:; + printf("Overall worst error = %f%%\n", 100.0 * omxerr); + + return 0; +} + + + + +/* ------------------------------------------------- */ +/* Support */ + +/* Run an interpolation though the whole refi */ +void refi_interp( +refi *r, +double *out_vals, +double *in_vals +) { +#ifdef QUANTIZE + int e; +#endif + double ivals[MXDI]; + double ovals[MXDO]; + + refi_input((void *)r, ivals, in_vals); +#ifdef QUANTIZE + for (e = 0; e < r->id; e++) + ivals[e] = ((int)(ivals[e] * 255.0 + 0.5))/255.0; /* Quantize to 8 bits */ +#endif + + refi_clut((void *)r, ovals, ivals); + +#ifdef QUANTIZE + for (e = 0; e < r->od; e++) + ovals[e] = ((int)(ovals[e] * 255.0 + 0.5))/255.0; /* Quantize to 8 bits */ +#endif + refi_output((void *)r, out_vals, ovals); +} + + +/* ------------------------------------------------- */ +/* Some test functions */ + +/* Single bump version */ +double +trans1( +double in, +double t /* Non-linearity factor. 0.0 to +/-1.0 */ +) { + double out; + + out = in + t * in * (1.0 - in); + return out; +} + +/* Double bump version */ +double +trans2( +double in, +double t /* Non-linearity factor. 0.0 to +/-1.0 */ +) { + double nf, out; + + if (in >= 0.5) { + nf = -2.0 * (in - 0.5) * (1.0 - in); + } else { + nf = 2.0 * in * (0.5 - in); + } + out = in + t * nf; + return out; +} + + + + + + + + + + + + + + + + + + diff --git a/imdi/refi.c b/imdi/refi.c new file mode 100644 index 0000000..55fe449 --- /dev/null +++ b/imdi/refi.c @@ -0,0 +1,212 @@ +/* Test support code */ +/* + * Copyright 2000 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. + */ + +#include "stdio.h" +#include "stdlib.h" +#include "refi.h" + +/* Callbackes used to setup rspl's */ +static void inputlu( +void *cbctx, +double *out, +double *in +) { + int i; + refi *r = (refi *)cbctx; + double ov[MXDI], iv[MXDI]; + + for (i = 0; i < r->id; i++) + iv[i] = *in; + r->input_curves(r->cntx, ov, iv); + + *out = ov[r->chan]; +} + +static void clutlu( +void *cbctx, +double *out, +double *in +) { + refi *r = (refi *)cbctx; + + r->md_table(r->cntx, out, in); +} + +static void outputlu( +void *cbctx, +double *out, +double *in +) { + int i; + refi *r = (refi *)cbctx; + double ov[MXDO], iv[MXDO]; + + for (i = 0; i < r->od; i++) + iv[i] = *in; + r->output_curves(r->cntx, ov, iv); + + *out = ov[r->chan]; +} + + +refi *new_refi( +int id, /* Number of input dimensions */ +int od, /* Number of output dimensions */ +int inres, /* Desired input table resolution */ +int clutres, /* Desired clut table resolution */ +int outres, /* Desired output table resolution */ + +/* Callbacks to lookup the table values */ +void (*input_curves) (void *cntx, double *out_vals, double *in_vals), +void (*md_table) (void *cntx, double *out_vals, double *in_vals), +void (*output_curves)(void *cntx, double *out_vals, double *in_vals), +void *cntx /* Context to callbacks */ +) { + refi *r; + int e; + int gres[MXDI]; + + if ((r = (refi *)malloc(sizeof(refi))) == NULL) { + fprintf(stderr,"Malloc of refi failed\n"); + exit (-1); + } + + r->id = id; + r->od = od; + r->inres = inres; + r->clutres = clutres; + r->outres = outres; + r->input_curves = input_curves; + r->md_table = md_table; + r->output_curves = output_curves; + r->cntx = cntx; + + /* Create some input interpolations */ + for (e = 0; e < id; e++) { + if ((r->in[e] = new_rspl(RSPL_NOFLAGS, 1, 1)) == NULL) { + fprintf(stderr,"new_rspl failed\n"); + exit (-1); + } + r->chan = e; + r->in[e]->set_rspl(r->in[e], 0, (void *)r, inputlu, NULL, NULL, &inres, NULL, NULL); + } + + /* Clut */ + if ((r->clut = new_rspl(RSPL_NOFLAGS, id, od)) == NULL) { + fprintf(stderr,"new_rspl failed\n"); + exit (-1); + } + for (e = 0; e < id; e++) + gres[e] = clutres; + r->clut->set_rspl(r->clut, 0, (void *)r, clutlu, NULL, NULL, gres, NULL, NULL); + + /* Create some output interpolations */ + for (e = 0; e < od; e++) { + if ((r->out[e] = new_rspl(RSPL_NOFLAGS, 1, 1)) == NULL) { + fprintf(stderr,"new_rspl failed\n"); + exit (-1); + } + r->chan = e; + r->out[e]->set_rspl(r->out[e], 0, (void *)r, outputlu, NULL, NULL, &outres, NULL, NULL); + } + + return r; +} + +/* Run an interpolation through an input table */ +void refi_input( +void *cntx, +double *out_vals, +double *in_vals +) { + refi *r = (refi *)cntx; + int e; + co vals; /* Input and output values */ + + for (e = 0; e < r->id; e++) { + vals.p[0] = in_vals[e]; + r->in[e]->interp(r->in[e], &vals); + out_vals[e] = vals.v[0]; + } +} + +/* Run an interpolation through an clut table */ +void refi_clut( +void *cntx, +double *out_vals, +double *in_vals +) { + refi *r = (refi *)cntx; + int e; + co vals; /* Input and output values */ + + for (e = 0; e < r->id; e++) + vals.p[e] = in_vals[e]; + r->clut->interp(r->clut, &vals); + for (e = 0; e < r->od; e++) + out_vals[e] = vals.v[e]; +} + +/* Run an interpolation through an output table */ +void refi_output( +void *cntx, +double *out_vals, +double *in_vals +) { + refi *r = (refi *)cntx; + int e; + co vals; /* Input and output values */ + + for (e = 0; e < r->od; e++) { + vals.p[0] = in_vals[e]; + r->out[e]->interp(r->out[e], &vals); + out_vals[e] = vals.v[0]; + } +} + +void +refi_free( +refi *r +) { + int e; + + for (e = 0; e < r->id; e++) { + r->in[e]->del(r->in[e]); + } + + r->clut->del(r->clut); + + for (e = 0; e < r->od; e++) { + r->out[e]->del(r->out[e]); + } +} + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/imdi/refi.h b/imdi/refi.h new file mode 100644 index 0000000..26570c4 --- /dev/null +++ b/imdi/refi.h @@ -0,0 +1,53 @@ + +/* Reference floating point interpolator constructed out of rspl's */ +/* This provides imdi functionality in floating point */ +/* + * Copyright 2000 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. + */ + +#include "../rspl/rspl.h" + +/* ------------------------------------------------ */ + +typedef struct { + int id, od; /* Input and output dimensions */ + int inres; /* Desired input table resolution */ + int clutres; /* Desired clut table resolution */ + int outres; /* Desired output table resolution */ + rspl *in[MXDI]; + rspl *clut; + rspl *out[MXDO]; + + void (*input_curves) (void *cntx, double *out_vals, double *in_vals); + void (*md_table) (void *cntx, double *out_vals, double *in_vals); + void (*output_curves)(void *cntx, double *out_vals, double *in_vals); + void *cntx; /* Context to callbacks */ + int chan; /* Current callback channel */ +} refi; + +refi *new_refi( + int id, /* Number of input dimensions */ + int od, /* Number of output dimensions */ + int inres, /* Desired input table resolution */ + int clutres, /* Desired clut table resolution */ + int outres, /* Desired output table resolution */ + + /* Callbacks to lookup the table values */ + void (*input_curves) (void *cntx, double *out_vals, double *in_vals), + void (*md_table) (void *cntx, double *out_vals, double *in_vals), + void (*output_curves)(void *cntx, double *out_vals, double *in_vals), + void *cntx /* Context to callbacks */ +); + +void refi_free(refi *r); + + +/* Component interpolations */ +void refi_input(void *cntx, double *out_vals, double *in_vals); +void refi_clut(void *cntx, double *out_vals, double *in_vals); +void refi_output(void *cntx, double *out_vals, double *in_vals); + diff --git a/imdi/ssort.c b/imdi/ssort.c new file mode 100644 index 0000000..f9a25fd --- /dev/null +++ b/imdi/ssort.c @@ -0,0 +1,322 @@ +/* Test code that generates optimised C sorting */ +/* code, suitable for classifying an input vector */ +/* into a particular simplex. */ + +/* + * Copyright 2000 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. + */ + +/* Intended for inclusion in IMDI for 16 bit support */ + + +#include +#include +#include + +/* Sorting data */ +#define MAXSV 50 /* Maximum sort variables */ +typedef struct { + int bot; /* 0 if we are doing ==, 1 if >, 2 if else */ + int dval; /* Difference value to apply */ + int vals[MAXSV]; /* Variable values */ + int sort[MAXSV]; /* Sorted index */ +} sortd; + +/* Context */ +typedef struct { + FILE *of; /* Output file */ + int indt; /* Indent */ + + /* Sort data */ + int nv; /* Number of variables to sort */ + int sl; /* Non-zero to generate code for smallest to largest */ + int eq; /* Non-zero to generate code for equal case */ + int la; /* Non-zero to generate labels */ + int rl; /* Recursion level */ + sortd sd[MAXSV]; /* Data at each recursion level */ + int ln; /* Label number */ + +} fileo; + +void line(fileo *f, char *fmt, ...); /* Output one line */ +void niline(fileo *f, char *fmt, ...); /* Output one line, no indent */ +void sline(fileo *f, char *fmt, ...); /* Output start of line line */ +void mline(fileo *f, char *fmt, ...); /* Output middle of line */ +void eline(fileo *f, char *fmt, ...); /* Output end of line */ + +void cr(fileo *f) { line(f,""); } /* Output a blank line */ + +void inc(fileo *f) { f->indt++; } /* Increment the indent level */ + +void dec(fileo *f) { f->indt--; } /* Decrement the indent level */ + +int +main(void) { + fileo f[1]; + + f->of = fopen("ttt.c","w"); + f->indt = 0; + + line(f,"/* This is the top */"); + cr(f); + line(f,"#include "); + cr(f); + line(f,"void main(void) {"); + inc(f); + line(f,"int v0, v1, v2, v3, v4;"); + line(f,"printf(\"Hi there!\\n\");"); + + line(f,"v0 = 1;"); + line(f,"v1 = 9;"); + line(f,"v2 = 3;"); + line(f,"v3 = 2;"); + line(f,"v4 = 0;"); + + /* ================ */ + /* Sort development */ + { +int xx; + int i, j; + + f->nv = 6; /* No variables */ + f->sl = 1; /* Smallest to largest */ + f->eq = 0; /* No equals case */ + f->la = 0; /* No labels */ + f->rl = 0; /* Top recursion level */ + f->ln = 0; /* Start label number */ + f->sd[f->rl].bot = f->eq ? 0 : 1; + f->sd[f->rl].dval = 256; + for (i = 0; i < f->nv; i++) { + f->sd[f->rl].vals[i] = -99999; /* Initial sort value = unknown */ + f->sd[f->rl].sort[i] = i; /* Initial sort order */ + } + f->sd[f->rl].vals[0] = 0; /* Make sure one is known */ + + /* First label is for no known sort */ + if (f->la) + niline(f," label%d:;",f->ln++); + + /* Until we have done every sort condition */ + for (;f->rl >= 0;) { + int ix, nix; /* Variable index that needs comparison */ + int six, snix; /* Sorted references to ix, nix */ + +//printf("\n~~recursion level %d\n",f->rl); +//for (i = 0; i < f->nv; i++) { +//xx = f->sd[f->rl].sort[i]; +//printf("Current = %d (index %d)\n",f->sd[f->rl].vals[xx],xx); +//} + /* see if we have resolved a sort */ + for (i = 1; i < f->nv; i++) { + six = i-1; + ix = f->sd[f->rl].sort[i-1]; + snix = i; + nix = f->sd[f->rl].sort[i]; + if (f->sd[f->rl].vals[ix] == f->sd[f->rl].vals[nix] + || f->sd[f->rl].vals[nix] == -99999) { + break; /* Not distinguishable or not known */ + } + } +//if (i < f->nv) +//printf("~~Unresolved sort indexes %d %d\n",ix,nix); +//else +//printf("~~Resolved sort\n"); + + if (i < f->nv) { + /* We haven't fully sorted the variables yet. */ + /* Compare ix to nix */ + if (f->sd[f->rl].bot == 0) { +//printf("~~ Unresolved at level %d, doing comparison for equals\n",f->rl); + line(f,"if (v%d == v%d) {",ix,nix); /* } */ + inc(f); + f->sd[f->rl+1] = f->sd[f->rl]; /* Structure copy */ + f->sd[f->rl+1].bot = 0; + f->sd[f->rl].bot = 1; + f->rl++; +// ~~~~~9 + /* Find next lowest value from nix */ + for (i = snix+1; i < f->nv; i++) { + int si = f->sd[f->rl].sort[i]; + if (f->sd[f->rl].vals[si] == -99999) + continue; + if (f->sd[f->rl].vals[si] < f->sd[f->rl].vals[nix]) { + f->sd[f->rl].vals[nix] = + (f->sd[f->rl].vals[si] + f->sd[f->rl].vals[nix])/2; + break; + } + } + if (i >= f->nv) { /* Not between next lowest */ + f->sd[f->rl].vals[nix] = f->sd[f->rl].vals[ix] -256; + } + } else if (f->sd[f->rl].bot == 1) { /* { */ +//printf("~~ Unresolved at level %d, doing comparison for greater\n",f->rl); + if (f->eq) { + dec(f); + line(f,"} else if (v%d %c v%d) {",ix, f->sl ? '<':'>',nix); /* } */ + } else + line(f,"if (v%d %c v%d) {",ix,f->sl ? '<':'>', nix); /* } */ + inc(f); + if (f->la) + niline(f," label%d:;",f->ln++); + f->sd[f->rl+1] = f->sd[f->rl]; /* Structure copy */ + f->sd[f->rl+1].bot = f->eq ? 0 : 1; + f->sd[f->rl].bot = 2; + f->rl++; + /* Find next lowest value from nix */ + for (i = snix+1; i < f->nv; i++) { + int si = f->sd[f->rl].sort[i]; + if (f->sd[f->rl].vals[si] == -99999) + continue; + if (f->sd[f->rl].vals[si] < f->sd[f->rl].vals[nix]) { + f->sd[f->rl].vals[nix] = + (f->sd[f->rl].vals[si] + f->sd[f->rl].vals[nix])/2; + break; + } + } + if (i >= f->nv) { /* Not between next lowest */ + f->sd[f->rl].vals[nix] = f->sd[f->rl].vals[ix] -256; + } + } else if (f->sd[f->rl].bot == 2) { /* { */ +//printf("~~ Unresolved at level %d, doing else\n",f->rl); + dec(f); + line(f,"} else { /* v%d %c v%d */",ix,f->sl ? '>':'<', nix); /* } */ + inc(f); + if (f->la) + niline(f," label%d:;",f->ln++); + f->sd[f->rl+1] = f->sd[f->rl]; /* Structure copy */ + f->sd[f->rl+1].bot = f->eq ? 0 : 1; + f->sd[f->rl].bot = 3; + f->rl++; + if (six-1 >= 0) { /* If there is a higher value */ + int si = f->sd[f->rl].sort[six-1]; + /* Make equal to next highest, since never been compared */ + f->sd[f->rl].vals[nix] = f->sd[f->rl].vals[si]; + } else { /* Nothing above */ + f->sd[f->rl].vals[nix] = f->sd[f->rl].vals[ix] + 256; + } + /* sort nix above ix */ + j = f->sd[f->rl].sort[six]; + f->sd[f->rl].sort[six] = f->sd[f->rl].sort[snix]; + f->sd[f->rl].sort[snix] = j; + } else { /* We've done both cases - return from recursion */ +//printf("~~ Unresolved at level %d, going up a level\n",f->rl); + dec(f); /* { */ + line(f,"}"); + f->rl--; /* Back a recursion level */ + } + } else { +//printf("~~ Resolved at level %d, outputing sort code, up a level\n",f->rl); + /* We have a resolved sort */ + /* So output the code for this sort combination */ + sline(f,"/* Sorted "); + for (i = 0; i < f->nv; i++) { + mline(f," %d",f->sd[f->rl].sort[i]); + } + eline(f," */"); + +#ifdef NEVER + sline(f,"printf(\"Sorted = %%d %%d %%d %%d %%d\\n\""); + for (i = 0; i < f->nv; i++) { + mline(f,",v%d",f->sd[f->rl].sort[i]); + } + eline(f,");"); +#else + for (i = 0; i < f->nv; i++) { + sline(f,""); + mline(f,"op[%d] = v%d",i,f->sd[f->rl].sort[i]); + eline(f,";"); + } + line(f,"ip += %d;",f->nv); + line(f,"op += %d;",f->nv); + line(f,"continue;"); +#endif + f->rl--; /* Back a recursion level */ + } + } + } + /* ================ */ + dec(f); + line(f,"}"); + + fclose(f->of); + + return 0; +} + + +/* Output a line to the file (including trailing \n) */ +void +line(fileo *f, char *fmt, ...) +{ + int i; + va_list args; + + /* Indent to the correct level */ + for (i = 0; i < f->indt; i++) + fprintf(f->of," "); + + va_start(args, fmt); + vfprintf(f->of, fmt, args); + va_end(args); + fprintf(f->of, "\n"); +} + +/* Output a line to the file (including trailing \n) */ +/* No indent */ +void +niline(fileo *f, char *fmt, ...) +{ + int i; + va_list args; + + va_start(args, fmt); + vfprintf(f->of, fmt, args); + va_end(args); + fprintf(f->of, "\n"); +} + +/* Output the start of a line to the file) */ +void +sline(fileo *f, char *fmt, ...) +{ + int i; + va_list args; + + /* Indent to the correct level */ + for (i = 0; i < f->indt; i++) + fprintf(f->of," "); + + va_start(args, fmt); + vfprintf(f->of, fmt, args); + va_end(args); +} + +/* Output the middle of a line to the file) */ +void +mline(fileo *f, char *fmt, ...) +{ + int i; + va_list args; + + va_start(args, fmt); + vfprintf(f->of, fmt, args); + va_end(args); +} + +/* Output the end of a line to the file (including trailing \n) */ +void +eline(fileo *f, char *fmt, ...) +{ + int i; + va_list args; + + va_start(args, fmt); + vfprintf(f->of, fmt, args); + va_end(args); + fprintf(f->of, "\n"); +} + -- cgit v1.2.3