From f52987d28426f6da69907bdd190d2c379ce9077d Mon Sep 17 00:00:00 2001 From: jhanika Date: Tue, 7 Apr 2009 07:13:23 +0000 Subject: [PATCH] initial proof-of-concept darktable checkin. basic functionality is there. git-svn-id: https://darktable.svn.sourceforge.net/svnroot/darktable@1 2165daf7-ed08-4abd-b3da-b2de91a31a1b --- LICENSE | 674 ++ Makefile.am | 2 + README | 29 + TODO | 95 + configure.ac | 70 + darktable.glade | 717 ++ darktable.gtkrc | 19 + src/LibRaw/Makefile.am | 5 + src/LibRaw/internal/dcraw_common.cpp | 8244 +++++++++++++++++++ src/LibRaw/internal/dcraw_fileio.cpp | 215 + src/LibRaw/internal/defines.h | 134 + src/LibRaw/internal/foveon.cpp | 817 ++ src/LibRaw/internal/libraw_internal_funcs.h | 200 + src/LibRaw/internal/var_defines.h | 180 + src/LibRaw/libraw/libraw.h | 229 + src/LibRaw/libraw/libraw_alloc.h | 98 + src/LibRaw/libraw/libraw_const.h | 160 + src/LibRaw/libraw/libraw_datastream.h | 296 + src/LibRaw/libraw/libraw_internal.h | 241 + src/LibRaw/libraw/libraw_types.h | 291 + src/LibRaw/libraw/libraw_version.h | 47 + src/LibRaw/src/libraw_c_api.cpp | 149 + src/LibRaw/src/libraw_cxx.cpp | 1935 +++++ src/Makefile.am | 8 + src/common/darktable.c | 131 + src/common/darktable.h | 53 + src/common/image.c | 517 ++ src/common/image.h | 112 + src/common/image_cache.c | 136 + src/common/image_cache.h | 48 + src/common/image_compression.c | 130 + src/common/image_compression.h | 8 + src/common/imageio.c | 629 ++ src/common/imageio.h | 28 + src/common/nikon_curve.c | 2183 +++++ src/common/nikon_curve.h | 717 ++ src/control/control.c | 954 +++ src/control/control.h | 124 + src/control/jobs.c | 87 + src/control/jobs.h | 62 + src/control/settings.h | 120 + src/develop/develop.c | 881 ++ src/develop/develop.h | 123 + src/develop/imageop.c | 143 + src/develop/imageop.h | 44 + src/develop/iop_hsb.c | 193 + src/develop/iop_hsb.h | 12 + src/gui/curveeditor.c | 290 + src/gui/curveeditor.h | 30 + src/gui/develop_view.c | 168 + src/gui/gtk.c | 375 + src/gui/gtk.h | 26 + src/gui/histogram.c | 125 + src/gui/histogram.h | 22 + src/gui/navigation.c | 170 + src/gui/navigation.h | 22 + src/library/film.c | 122 + src/library/library.c | 380 + src/library/library.h | 63 + src/main.c | 11 + src/shared.am | 1 + 61 files changed, 24095 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile.am create mode 100644 README create mode 100644 TODO create mode 100644 configure.ac create mode 100644 darktable.glade create mode 100644 darktable.gtkrc create mode 100644 src/LibRaw/Makefile.am create mode 100644 src/LibRaw/internal/dcraw_common.cpp create mode 100644 src/LibRaw/internal/dcraw_fileio.cpp create mode 100644 src/LibRaw/internal/defines.h create mode 100644 src/LibRaw/internal/foveon.cpp create mode 100644 src/LibRaw/internal/libraw_internal_funcs.h create mode 100644 src/LibRaw/internal/var_defines.h create mode 100644 src/LibRaw/libraw/libraw.h create mode 100644 src/LibRaw/libraw/libraw_alloc.h create mode 100644 src/LibRaw/libraw/libraw_const.h create mode 100644 src/LibRaw/libraw/libraw_datastream.h create mode 100644 src/LibRaw/libraw/libraw_internal.h create mode 100644 src/LibRaw/libraw/libraw_types.h create mode 100644 src/LibRaw/libraw/libraw_version.h create mode 100644 src/LibRaw/src/libraw_c_api.cpp create mode 100644 src/LibRaw/src/libraw_cxx.cpp create mode 100644 src/Makefile.am create mode 100644 src/common/darktable.c create mode 100644 src/common/darktable.h create mode 100644 src/common/image.c create mode 100644 src/common/image.h create mode 100644 src/common/image_cache.c create mode 100644 src/common/image_cache.h create mode 100644 src/common/image_compression.c create mode 100644 src/common/image_compression.h create mode 100644 src/common/imageio.c create mode 100644 src/common/imageio.h create mode 100644 src/common/nikon_curve.c create mode 100644 src/common/nikon_curve.h create mode 100644 src/control/control.c create mode 100644 src/control/control.h create mode 100644 src/control/jobs.c create mode 100644 src/control/jobs.h create mode 100644 src/control/settings.h create mode 100644 src/develop/develop.c create mode 100644 src/develop/develop.h create mode 100644 src/develop/imageop.c create mode 100644 src/develop/imageop.h create mode 100644 src/develop/iop_hsb.c create mode 100644 src/develop/iop_hsb.h create mode 100644 src/gui/curveeditor.c create mode 100644 src/gui/curveeditor.h create mode 100644 src/gui/develop_view.c create mode 100644 src/gui/gtk.c create mode 100644 src/gui/gtk.h create mode 100644 src/gui/histogram.c create mode 100644 src/gui/histogram.h create mode 100644 src/gui/navigation.c create mode 100644 src/gui/navigation.h create mode 100644 src/library/film.c create mode 100644 src/library/library.c create mode 100644 src/library/library.h create mode 100644 src/main.c create mode 100644 src/shared.am diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000000..94a9ed024d38 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 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 General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is 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. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + 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. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + 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 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. Use with the GNU Affero General Public License. + + 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 Affero 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 special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 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 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 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + 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 GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 000000000000..9b5dbebc2ad8 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS=src +EXTRA_DIST=darktable.gtkrc darktable.glade diff --git a/README b/README new file mode 100644 index 000000000000..f3d930458aa8 --- /dev/null +++ b/README @@ -0,0 +1,29 @@ +this is darktable, a free raw photo viewer and organiser. +darktable is not a light table. it's digital. + +to build it, you need: + + - lcms + - gtk+-2.0 + - libglade-2.0 + - libgthread-2.0 + - cairo + - MagickCore + +other used packages (supplied in the source tree): + + - LibRaw + - nikon_curve (taken from ufraw) + +then, type: + + $ autoreconf --install + $ ./configure [--enable-qwerty] [--enable-debug] + $ make + $ src/darkroom + +(the first step is only necessary when building from svn for the first time) +and enjoy! + +send any bugreports to me: hanatos@gmail.com + diff --git a/TODO b/TODO new file mode 100644 index 000000000000..4345d04f6bf8 --- /dev/null +++ b/TODO @@ -0,0 +1,95 @@ +new bugs: + - exif tag in libraw not really handled well (aperture+time missing) + - JPEG loading needs shrink + orientation + - dev switch resolution AFTER setting gui stuff. + control->width is updated in expose, and configure + is called in dev_enter. (just before) :( + - image_load_job should take img id and get it from cache himself! + (this is a serious problem if not all images fit into cache) + +broken since sqlite backend: + - remove last film in favour of film table with timestamp. + +feature requests: + - GMM for color model using EM in opencv! + - img table + date inserted/modified + - faster (unsafe) sql option: PRAGMA synchronous=OFF + - cache auf platte, echte bilder irgendwo auf dvd/platte extern/kamera chip etc + - moeglicherweise ECHTE bilder (dias im schrank) + - bilder sortieren, evtl manuell (mehrere kameras in einen film usw) + - zoom in in 3 steps? + - hide gui triangles andere form und switch in/out + - inotify dir watching + - execute iop: pass global image window! + - zoom in dev mode via mouse wheel. + - dt_film_sort with global settings + - tags and some tag search facility + - brightness/exposure highlight histogram stuff + +bugs: + - strange G_OBJECT cast errors when loading pfm/exr/pdf? + - control add history item > 10 (in gui representation. core can do)! + - import folder file chooser sometimes not sized appropriately + - histogram updates in fixed pipeline come too early/uselessly? + - export muss erst dev mode haben, sonst kaputt. + +priority list: + - selection behaviour (ctl shift etc) + - magick + exif (read)! + - clean up dev events: add history-> cache up -> fixed pipeline up + - modular one-file plugin support: + src/imageio/saturation.{c,h}: + - dr_gui_XX_reset -> reset op to neutral/default params + dr_gui_XX_init -> init gui elements and callbacks + dr_iop_XX -> transform buffers + - add expander to right vbox, + init gui with expander->child + expose: if op !=, gui_reset + reserve imageop, need string hash? + - image properties when zoomed in a lot + - clean up namings/modules. + - move expose to ctl or gui? + - split ctl and sched? + - control panel with buttons for lib->dev->lib + +image ops: + - white balance + - exposure + - luma/chroma denoising + - crop + +usability: + - aoe, keys to change +-= table_cols/1 resp. + - write out image_t at quit (exif, filenames, selected flags) + - navigation: buttons for zoom levels + - closeup in 3:1 5:1 or 2:1 ? + - disable dev controls/make invisible in lib? + - make directory loading interruptible + - adjust navigation preview in dev on the fly? + - image loading: load thumbnails first, if any. + - navigation widget for lib? + - curve widget presets + - x-axis adjusters + - active live histogram (exposure etc) + - all functions mouse-accessible + - library graph with keyword edges. + - expander with exif data (+filename of saved image) + +look: + - dev img border + - border between center and outer panels? + - adjust .gtkrc to have consistently cool look even in filechooser + - end markers :) + - dev full res image possibly smaller as lo-res (paint border with bg) + +cleanup: + - QWERTZ/dvorak keyboard shortcuts run-time switching + +cache: + - user profile guided prefetching for full raw load! + - load cache from disk only if md5sum matches + +build/packaging: + - consistent prefix naming: dev, lib, ctl, gui + - doxygen documentation + diff --git a/configure.ac b/configure.ac new file mode 100644 index 000000000000..04d819f116e6 --- /dev/null +++ b/configure.ac @@ -0,0 +1,70 @@ +AC_INIT([darktable], [0.1], [hanatos@gmail.com]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign dist-bzip2]) +AC_PROG_CC +AC_PROG_CXX +AC_PROG_RANLIB +PKG_CHECK_MODULES(GTK, gtk+-2.0 >= 2.14) +PKG_CHECK_MODULES(CAIRO, [cairo]) +PKG_CHECK_MODULES(GTHREAD, [gthread-2.0]) +PKG_CHECK_MODULES(GLADE, [libglade-2.0]) +PKG_CHECK_MODULES(SQLITE, [sqlite3]) + +dnl *** check for MagickCore *** +AC_CHECK_PROGS(MAGICKCONFIG, [Magick-config GraphicsMagick-config]) +usemagick=0 +if test -n "$MAGICKCONFIG"; then + ac_save_CPPFLAGS="$CPPFLAGS" + ac_save_LIBS="$LIBS" + MAGICK_CPPFLAGS="`$MAGICKCONFIG --cppflags`" + MAGICK_LIBS="`$MAGICKCONFIG --ldflags` `$MAGICKCONFIG --libs`" + CPPFLAGS="$CPPFLAGS $MAGICK_CPPFLAGS" + LIBS="$MAGICK_LIBS $LIBS" + AC_CHECK_FUNC(ExportImagePixels, usemagick=1, AC_MSG_NOTICE([ImageMagick/GraphicsMagick does not support the function + ExportImagePixels. Please upgrade to ImageMagick 5.5.7 or newer (or + the corresponding GraphicsMagick version)])) + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" +fi + +if test "$usemagick" != 1; then + AC_CHECK_LIB(z, zlibVersion, test, AC_MSG_ERROR([You must have libz(-devel) installed])) + + AC_CHECK_LIB(png, png_sig_cmp, test, AC_MSG_ERROR([You must have libpng(-devel) installed]), -lz -lm) + MAGICK_CPPFLAGS= + MAGICK_LIBS="-lpng -lz -lm" +else + AC_DEFINE(HAVE_MAGICK, 1, [Whether the ImageMagick or GraphicsMagick libraries are available]) +fi +AC_SUBST(MAGICK_CPPFLAGS) +AC_SUBST(MAGICK_LIBS) + +dnl *** Check for LittleCMS *** +AC_CHECK_LIB(lcms, cmsOpenProfileFromFile, + [AC_DEFINE(HAVE_LCMS, 1, [Define if you have the LittleCMS development environment]) + LCMS_LIBS="-llcms"]) +AC_SUBST(LCMS_LIBS) + +dnl *** check for lamer keyboard *** +AC_ARG_ENABLE(qwerty, [ --enable-qwerty Enable QWERTY key accels], [if test "$enableval" = yes; then + AC_DEFINE(HAVE_QWERTY, 1, [Use to switch from default dvorak key accels to qwerty]) + fi + ]) + +AC_MSG_CHECKING([whether to enable debugging]) +AC_ARG_ENABLE(debug, + AC_HELP_STRING([--enable-debug], [build debug executable])) +if test "$enable_debug" = "yes"; then + CFLAGS="$CFLAGS -Wall -O0 -g -D_DEBUG" + AC_MSG_RESULT([yes]) +else + CFLAGS="$CFLAGS -Wall -O4" + AC_MSG_RESULT([no]) +fi + +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_FILES([ + Makefile + src/Makefile + src/LibRaw/Makefile + ]) +AC_OUTPUT diff --git a/darktable.glade b/darktable.glade new file mode 100644 index 000000000000..9490814af2ba --- /dev/null +++ b/darktable.glade @@ -0,0 +1,717 @@ + + + + + + 640 + 480 + + + True + 3 + + + 200 + True + True + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + + + True + GTK_RESIZE_QUEUE + + + True + 10 + + + True + True + True + + + 200 + 133 + True + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK + + + + + True + histogram + + + label_item + + + + + False + False + + + + + True + True + True + + + True + + + 200 + 200 + True + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK + + + + + True + + + True + presets + + + False + False + 5 + 1 + + + + + True + False + False + linear +med contrast +high contrast + + + False + False + 5 + GTK_PACK_END + 1 + + + + + False + False + 1 + + + + + + + True + tone curve + + + label_item + + + + + False + False + 1 + + + + + True + True + 10 + + + True + + + True + + + True + 0 + linear + True + + + + + True + 0 + gamma + True + + + 1 + + + + + False + False + 5 + + + + + True + + + True + True + 0.10000000000000001 0 1 0.01 0.10000000000000001 0 + 2 + GTK_POS_LEFT + + + + + True + True + 0.45000000000000001 0 1 0.01 0.10000000000000001 0 + 2 + GTK_POS_LEFT + + + 1 + + + + + 5 + 1 + + + + + + + True + gamma correction + + + label_item + + + + + False + False + 2 + + + + + True + True + 10 + + + True + + + True + + + True + 0 + hue + + + + + True + 0 + saturation + + + 1 + + + + + True + 0 + brightness + + + 2 + + + + + False + False + 5 + + + + + True + + + True + True + 1 0 2 0.01 0.10000000000000001 0 + 2 + GTK_POS_LEFT + + + False + False + + + + + True + True + 1 0 2 0.01 0.10000000000000001 0 + 2 + GTK_POS_LEFT + + + False + False + 1 + + + + + True + True + 1 0 2 0.01 0.10000000000000001 0 + 2 + GTK_POS_LEFT + + + False + False + 2 + + + + + 5 + 1 + + + + + + + True + saturation + + + label_item + + + + + False + False + 3 + + + + + True + True + 10 + + + True + + + True + float pfm +16-bit ppm +8-bit jpg +8-bit png + + + 5 + + + + + True + True + True + export + 0 + + + False + False + 5 + 1 + + + + + + + True + export selected + + + label_item + + + + + False + False + 4 + + + + + + + + + 2 + 3 + GTK_SHRINK + GTK_EXPAND | GTK_SHRINK | GTK_FILL + + + + + True + + + True + TODO: lib: sorting, dev: file op/save + + + False + False + + + + + 500 + 500 + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK + + + 1 + + + + + True + + + True + True + 13 1 13 1 1 0 + 0 + GTK_POS_RIGHT + + + GTK_PACK_END + -1 + + + + + False + False + 2 + + + + + 1 + 2 + + + + + 200 + True + 10 + + + True + True + True + + + 200 + 133 + True + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK + + + + + True + navigation + + + label_item + + + + + False + False + + + + + True + True + True + 5 + + + True + + + True + True + True + import folder + 0 + + + False + False + + + + + True + True + + + 1 + + + + + + + True + library + + + label_item + + + + + False + False + 1 + + + + + True + True + + + True + TODO: store hist stacks + + + + + True + snapshots + + + label_item + + + + + False + False + 2 + + + + + True + True + True + + + True + + + True + True + button + 0 + + + False + False + + + + + True + True + button + 0 + + + False + False + 1 + + + + + True + True + button + 0 + + + False + False + 2 + + + + + True + True + button + 0 + + + False + False + 3 + + + + + True + True + button + 0 + + + False + False + 4 + + + + + True + True + button + 0 + + + False + False + 5 + + + + + True + True + button + 0 + + + False + False + 6 + + + + + True + True + button + 0 + + + False + False + 7 + + + + + True + True + button + 0 + + + False + False + 8 + + + + + True + True + original + 0 + + + False + False + 9 + + + + + + + True + history + + + label_item + + + + + False + False + 3 + + + + + GTK_SHRINK + GTK_EXPAND | GTK_SHRINK | GTK_FILL + + + + + + diff --git a/darktable.gtkrc b/darktable.gtkrc new file mode 100644 index 000000000000..945197f1df7a --- /dev/null +++ b/darktable.gtkrc @@ -0,0 +1,19 @@ +style "default"{ + +#fonts +fg[NORMAL] = "#F0F0F0" +fg[PRELIGHT] = "#000000" +fg[SELECTED] = "#000000" + +#selected areas +base[SELECTED] = "#8a8aa0" +base[ACTIVE] = "#e3e3e3" +base[PRELIGHT] = "#E5E5F7" +base[INSENSITIVE]= "#ced0d7" +bg[SELECTED]= "#828aa4" + +#app backgrouds +bg[NORMAL] = "#333333" +# bg_pixmap[NORMAL] = "lines_background.png" +# bg_pixmap[PRELIGHT] = "lines_background_blue.png" +}class "GtkWidget" style "default" diff --git a/src/LibRaw/Makefile.am b/src/LibRaw/Makefile.am new file mode 100644 index 000000000000..eb98ba992842 --- /dev/null +++ b/src/LibRaw/Makefile.am @@ -0,0 +1,5 @@ +AM_CFLAGS=-O4 -I$(srcdir) -Wall -fopenmp -pthread +LDADD=$(LCMS_LIBS) +noinst_LIBRARIES=libraw_r.a +libraw_r_a_SOURCES=internal/dcraw_common.cpp src/libraw_cxx.cpp internal/dcraw_fileio.cpp internal/foveon.cpp src/libraw_c_api.cpp +noinst_HEADERS=internal/defines.h internal/libraw_internal_funcs.h internal/var_defines.h libraw/libraw_internal.h libraw/libraw_alloc.h libraw/libraw_types.h libraw/libraw.h libraw/libraw_version.h libraw/libraw_const.h libraw/libraw_datastream.h diff --git a/src/LibRaw/internal/dcraw_common.cpp b/src/LibRaw/internal/dcraw_common.cpp new file mode 100644 index 000000000000..c55184ef5f72 --- /dev/null +++ b/src/LibRaw/internal/dcraw_common.cpp @@ -0,0 +1,8244 @@ +/* + GENERATED FILE, DO NOT EDIT + Generated from dcraw/dcraw.c at Sat Feb 7 20:23:43 2009 + Look into original file (probably http://cybercom.net/~dcoffin/dcraw/dcraw.c) + for copyright information. +*/ + +#line 259 "dcraw/dcraw.c" +#define CLASS LibRaw:: +#include "libraw/libraw_types.h" +#define LIBRAW_LIBRARY_BUILD +#define LIBRAW_IO_REDEFINED +#include "libraw/libraw.h" +#include "internal/defines.h" +#include "internal/var_defines.h" + +#line 269 "dcraw/dcraw.c" + +#ifndef __GLIBC__ +char *my_memmem (char *haystack, size_t haystacklen, + char *needle, size_t needlelen) +{ + char *c; + for (c = haystack; c <= haystack + haystacklen - needlelen; c++) + if (!memcmp (c, needle, needlelen)) + return c; + return 0; +} +#define memmem my_memmem +#endif + +#line 303 "dcraw/dcraw.c" + +ushort CLASS sget2 (uchar *s) +{ + if (order == 0x4949) /* "II" means little-endian */ + return s[0] | s[1] << 8; + else /* "MM" means big-endian */ + return s[0] << 8 | s[1]; +} + +ushort CLASS get2() +{ + uchar str[2] = { 0xff,0xff }; + fread (str, 1, 2, ifp); + return sget2(str); +} + +unsigned CLASS sget4 (uchar *s) +{ + if (order == 0x4949) + return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24; + else + return s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]; +} +#define sget4(s) sget4((uchar *)s) + +unsigned CLASS get4() +{ + uchar str[4] = { 0xff,0xff,0xff,0xff }; + fread (str, 1, 4, ifp); + return sget4(str); +} + +unsigned CLASS getint (int type) +{ + return type == 3 ? get2() : get4(); +} + +float CLASS int_to_float (int i) +{ + union { int i; float f; } u; + u.i = i; + return u.f; +} + +double CLASS getreal (int type) +{ + union { char c[8]; double d; } u; + int i, rev; + + switch (type) { + case 3: return (unsigned short) get2(); + case 4: return (unsigned int) get4(); + case 5: u.d = (unsigned int) get4(); + return u.d / (unsigned int) get4(); + case 8: return (signed short) get2(); + case 9: return (signed int) get4(); + case 10: u.d = (signed int) get4(); + return u.d / (signed int) get4(); + case 11: return int_to_float (get4()); + case 12: + rev = 7 * ((order == 0x4949) == (ntohs(0x1234) == 0x1234)); + for (i=0; i < 8; i++) + u.c[i ^ rev] = fgetc(ifp); + return u.d; + default: return fgetc(ifp); + } +} + +void CLASS read_shorts (ushort *pixel, int count) +{ + if (fread (pixel, 2, count, ifp) < count) derror(); + if ((order == 0x4949) == (ntohs(0x1234) == 0x1234)) + swab ((char*)pixel, (char*)pixel, count*2); +} +#line 380 "dcraw/dcraw.c" +void CLASS canon_black (double dark[2]) +{ + int c, diff, row, col; + + if (raw_width < width+4) return; + FORC(2) dark[c] /= (raw_width-width-2) * height >> 1; + if ((diff = dark[0] - dark[1])) + for (row=0; row < height; row++) + for (col=1; col < width; col+=2) + BAYER(row,col) += diff; + dark[1] += diff; + black = (dark[0] + dark[1] + 1) / 2; +} + +void CLASS canon_600_fixed_wb (int temp) +{ + static const short mul[4][5] = { + { 667, 358,397,565,452 }, + { 731, 390,367,499,517 }, + { 1119, 396,348,448,537 }, + { 1399, 485,431,508,688 } }; + int lo, hi, i; + float frac=0; + + for (lo=4; --lo; ) + if (*mul[lo] <= temp) break; + for (hi=0; hi < 3; hi++) + if (*mul[hi] >= temp) break; + if (lo != hi) + frac = (float) (temp - *mul[lo]) / (*mul[hi] - *mul[lo]); + for (i=1; i < 5; i++) + pre_mul[i-1] = 1 / (frac * mul[hi][i] + (1-frac) * mul[lo][i]); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif +} + +/* Return values: 0 = white 1 = near white 2 = not white */ +int CLASS canon_600_color (int ratio[2], int mar) +{ + int clipped=0, target, miss; + + if (flash_used) { + if (ratio[1] < -104) + { ratio[1] = -104; clipped = 1; } + if (ratio[1] > 12) + { ratio[1] = 12; clipped = 1; } + } else { + if (ratio[1] < -264 || ratio[1] > 461) return 2; + if (ratio[1] < -50) + { ratio[1] = -50; clipped = 1; } + if (ratio[1] > 307) + { ratio[1] = 307; clipped = 1; } + } + target = flash_used || ratio[1] < 197 + ? -38 - (398 * ratio[1] >> 10) + : -123 + (48 * ratio[1] >> 10); + if (target - mar <= ratio[0] && + target + 20 >= ratio[0] && !clipped) return 0; + miss = target - ratio[0]; + if (abs(miss) >= mar*4) return 2; + if (miss < -20) miss = -20; + if (miss > mar) miss = mar; + ratio[0] = target - miss; + return 1; +} + +void CLASS canon_600_auto_wb() +{ + int mar, row, col, i, j, st, count[] = { 0,0 }; + int test[8], total[2][8], ratio[2][2], stat[2]; + + memset (&total, 0, sizeof total); + i = canon_ev + 0.5; + if (i < 10) mar = 150; + else if (i > 12) mar = 20; + else mar = 280 - 20 * i; + if (flash_used) mar = 80; + for (row=14; row < height-14; row+=4) + for (col=10; col < width; col+=2) { + for (i=0; i < 8; i++) + test[(i & 4) + FC(row+(i >> 1),col+(i & 1))] = + BAYER(row+(i >> 1),col+(i & 1)); + for (i=0; i < 8; i++) + if (test[i] < 150 || test[i] > 1500) goto next; + for (i=0; i < 4; i++) + if (abs(test[i] - test[i+4]) > 50) goto next; + for (i=0; i < 2; i++) { + for (j=0; j < 4; j+=2) + ratio[i][j >> 1] = ((test[i*4+j+1]-test[i*4+j]) << 10) / test[i*4+j]; + stat[i] = canon_600_color (ratio[i], mar); + } + if ((st = stat[0] | stat[1]) > 1) goto next; + for (i=0; i < 2; i++) + if (stat[i]) + for (j=0; j < 2; j++) + test[i*4+j*2+1] = test[i*4+j*2] * (0x400 + ratio[i][j]) >> 10; + for (i=0; i < 8; i++) + total[st][i] += test[i]; + count[st]++; +next: ; + } + if (count[0] | count[1]) { + st = count[0]*200 < count[1]; + for (i=0; i < 4; i++) + pre_mul[i] = 1.0 / (total[st][i] + total[st][i+4]); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CALCULATED; +#endif + } +} + +void CLASS canon_600_coeff() +{ + static const short table[6][12] = { + { -190,702,-1878,2390, 1861,-1349,905,-393, -432,944,2617,-2105 }, + { -1203,1715,-1136,1648, 1388,-876,267,245, -1641,2153,3921,-3409 }, + { -615,1127,-1563,2075, 1437,-925,509,3, -756,1268,2519,-2007 }, + { -190,702,-1886,2398, 2153,-1641,763,-251, -452,964,3040,-2528 }, + { -190,702,-1878,2390, 1861,-1349,905,-393, -432,944,2617,-2105 }, + { -807,1319,-1785,2297, 1388,-876,769,-257, -230,742,2067,-1555 } }; + int t=0, i, c; + float mc, yc; + + mc = pre_mul[1] / pre_mul[2]; + yc = pre_mul[3] / pre_mul[2]; + if (mc > 1 && mc <= 1.28 && yc < 0.8789) t=1; + if (mc > 1.28 && mc <= 2) { + if (yc < 0.8789) t=3; + else if (yc <= 2) t=4; + } + if (flash_used) t=5; + for (raw_color = i=0; i < 3; i++) + FORCC rgb_cam[i][c] = table[t][i*4 + c] / 1024.0; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.rgb_cam_state = LIBRAW_COLORSTATE_CALCULATED; +#endif +} + +void CLASS canon_600_load_raw() +{ + uchar data[1120], *dp; + ushort pixel[896], *pix; + int irow, row, col, val; + static const short mul[4][2] = + { { 1141,1145 }, { 1128,1109 }, { 1178,1149 }, { 1128,1109 } }; + + for (irow=row=0; irow < height; irow++) { + if (fread (data, 1, raw_width*5/4, ifp) < raw_width*5/4) derror(); + for (dp=data, pix=pixel; dp < data+1120; dp+=10, pix+=8) { + pix[0] = (dp[0] << 2) + (dp[1] >> 6 ); + pix[1] = (dp[2] << 2) + (dp[1] >> 4 & 3); + pix[2] = (dp[3] << 2) + (dp[1] >> 2 & 3); + pix[3] = (dp[4] << 2) + (dp[1] & 3); + pix[4] = (dp[5] << 2) + (dp[9] & 3); + pix[5] = (dp[6] << 2) + (dp[9] >> 2 & 3); + pix[6] = (dp[7] << 2) + (dp[9] >> 4 & 3); + pix[7] = (dp[8] << 2) + (dp[9] >> 6 ); + } + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col]; + for (col=width; col < raw_width; col++) + { + black += pixel[col]; +#ifdef LIBRAW_LIBRARY_BUILD + ushort *dfp = get_masked_pointer(row,col); + if(dfp) *dfp = pixel[col]; +#endif + } + if ((row+=2) > height) row = 1; + } + if (raw_width > width) + black = black / ((raw_width - width) * height) - 4; + for (row=0; row < height; row++) + for (col=0; col < width; col++) { +#ifdef LIBRAW_LIBRARY_BUILD + if( filtering_mode & LIBRAW_FILTERING_NOBLACKS) + val = BAYER(row,col); + else +#endif + if ((val = BAYER(row,col) - black) < 0) val = 0; + val = val * mul[row & 3][col & 1] >> 9; + BAYER(row,col) = val; + } + canon_600_fixed_wb(1311); + canon_600_auto_wb(); + canon_600_coeff(); + maximum = (0x3ff - black) * 1109 >> 9; + black = 0; +} + +void CLASS remove_zeroes() +{ + unsigned row, col, tot, n, r, c; + +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_REMOVE_ZEROES,0,2); +#endif + for (row=0; row < height; row++) + for (col=0; col < width; col++) + if (BAYER(row,col) == 0) { + tot = n = 0; + for (r = row-2; r <= row+2; r++) + for (c = col-2; c <= col+2; c++) + if (r < height && c < width && + FC(r,c) == FC(row,col) && BAYER(r,c)) + tot += (n++,BAYER(r,c)); + if (n) BAYER(row,col) = tot/n; + } +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_REMOVE_ZEROES,1,2); +#endif +} + +int CLASS canon_s2is() +{ + unsigned row; + + for (row=0; row < 100; row++) { + fseek (ifp, row*3340 + 3284, SEEK_SET); + if (getc(ifp) > 15) return 1; + } + return 0; +} + +void CLASS canon_a5_load_raw() +{ + ushort data[2565], *dp, pixel; + int vbits=0, buf=0, row, col, bc=0; + + order = 0x4949; + for (row=-top_margin; row < raw_height-top_margin; row++) { + read_shorts (dp=data, raw_width * 10 / 16); + for (col=-left_margin; col < raw_width-left_margin; col++) { + if ((vbits -= 10) < 0) + buf = (vbits += 16, (buf << 16) + *dp++); + pixel = buf >> vbits & 0x3ff; +#ifdef LIBRAW_LIBRARY_BUILD + ushort *dfp = get_masked_pointer(row+top_margin,col+left_margin); + if(dfp) *dfp = pixel; +#endif + if ((unsigned) row < height && (unsigned) col < width) + BAYER(row,col) = pixel; + else if (col > 1-left_margin && col != width) + black += (bc++,pixel); + } + } + if (bc) black /= bc; + maximum = 0x3ff; + +#ifdef LIBRAW_LIBRARY_BUILD + if(!(filtering_mode & LIBRAW_FILTERING_NOZEROES)) +#endif + if (raw_width > 1600) remove_zeroes(); +} + +/* + getbits(-1) initializes the buffer + getbits(n) where 0 <= n <= 25 returns an n-bit integer + */ +unsigned CLASS getbits (int nbits) +{ +#ifdef LIBRAW_NOTHREADS + static unsigned bitbuf=0; + static int vbits=0, reset=0; +#else +#define bitbuf tls->getbits.bitbuf +#define vbits tls->getbits.vbits +#define reset tls->getbits.reset +#endif + unsigned c; + + if (nbits == -1) + return bitbuf = vbits = reset = 0; + if (nbits == 0 || reset) return 0; + while (vbits < nbits) { + if ((c = fgetc(ifp)) == EOF) derror(); + if ((reset = zero_after_ff && c == 0xff && fgetc(ifp))) return 0; + bitbuf = (bitbuf << 8) + (uchar) c; + vbits += 8; + } + vbits -= nbits; + return bitbuf << (32-nbits-vbits) >> (32-nbits); +#ifndef LIBRAW_NOTHREADS +#undef bitbuf +#undef vbits +#undef reset +#endif +} + +void CLASS init_decoder() +{ + memset (first_decode, 0, sizeof first_decode); + free_decode = first_decode; +} + +/* + Construct a decode tree according to the specification in *source. + The first 16 bytes specify how many codes should be 1-bit, 2-bit + 3-bit, etc. Bytes after that are the leaf values. + + For example, if the source is + + { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0, + 0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff }, + + then the code is + + 00 0x04 + 010 0x03 + 011 0x05 + 100 0x06 + 101 0x02 + 1100 0x07 + 1101 0x01 + 11100 0x08 + 11101 0x09 + 11110 0x00 + 111110 0x0a + 1111110 0x0b + 1111111 0xff + */ +uchar * CLASS make_decoder (const uchar *source, int level) +{ + struct decode *cur; +#ifndef LIBRAW_NOTHREADS +#define t_leaf tls->make_decoder_leaf +#else + static int t_leaf; +#endif + int i, next; + + if (level==0) t_leaf=0; + cur = free_decode++; + if (free_decode > first_decode+2048) { +#ifdef LIBRAW_LIBRARY_BUILD + throw LIBRAW_EXCEPTION_DECODE_RAW; +#else + fprintf (stderr,_("%s: decoder table overflow\n"), ifname); + longjmp (failure, 2); +#endif + } + for (i=next=0; i <= t_leaf && next < 16; ) + i += source[next++]; + if (i > t_leaf) { + if (level < next) { + cur->branch[0] = free_decode; + make_decoder (source, level+1); + cur->branch[1] = free_decode; + make_decoder (source, level+1); + } else + cur->leaf = source[16 + t_leaf++]; + } + return (uchar *) source + 16 + t_leaf; +#ifndef LIBRAW_NOTHREADS +#undef t_leaf +#endif +} + +void CLASS crw_init_tables (unsigned table) +{ + static const uchar first_tree[3][29] = { + { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0, + 0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff }, + { 0,2,2,3,1,1,1,1,2,0,0,0,0,0,0,0, + 0x03,0x02,0x04,0x01,0x05,0x00,0x06,0x07,0x09,0x08,0x0a,0x0b,0xff }, + { 0,0,6,3,1,1,2,0,0,0,0,0,0,0,0,0, + 0x06,0x05,0x07,0x04,0x08,0x03,0x09,0x02,0x00,0x0a,0x01,0x0b,0xff }, + }; + static const uchar second_tree[3][180] = { + { 0,2,2,2,1,4,2,1,2,5,1,1,0,0,0,139, + 0x03,0x04,0x02,0x05,0x01,0x06,0x07,0x08, + 0x12,0x13,0x11,0x14,0x09,0x15,0x22,0x00,0x21,0x16,0x0a,0xf0, + 0x23,0x17,0x24,0x31,0x32,0x18,0x19,0x33,0x25,0x41,0x34,0x42, + 0x35,0x51,0x36,0x37,0x38,0x29,0x79,0x26,0x1a,0x39,0x56,0x57, + 0x28,0x27,0x52,0x55,0x58,0x43,0x76,0x59,0x77,0x54,0x61,0xf9, + 0x71,0x78,0x75,0x96,0x97,0x49,0xb7,0x53,0xd7,0x74,0xb6,0x98, + 0x47,0x48,0x95,0x69,0x99,0x91,0xfa,0xb8,0x68,0xb5,0xb9,0xd6, + 0xf7,0xd8,0x67,0x46,0x45,0x94,0x89,0xf8,0x81,0xd5,0xf6,0xb4, + 0x88,0xb1,0x2a,0x44,0x72,0xd9,0x87,0x66,0xd4,0xf5,0x3a,0xa7, + 0x73,0xa9,0xa8,0x86,0x62,0xc7,0x65,0xc8,0xc9,0xa1,0xf4,0xd1, + 0xe9,0x5a,0x92,0x85,0xa6,0xe7,0x93,0xe8,0xc1,0xc6,0x7a,0x64, + 0xe1,0x4a,0x6a,0xe6,0xb3,0xf1,0xd3,0xa5,0x8a,0xb2,0x9a,0xba, + 0x84,0xa4,0x63,0xe5,0xc5,0xf3,0xd2,0xc4,0x82,0xaa,0xda,0xe4, + 0xf2,0xca,0x83,0xa3,0xa2,0xc3,0xea,0xc2,0xe2,0xe3,0xff,0xff }, + { 0,2,2,1,4,1,4,1,3,3,1,0,0,0,0,140, + 0x02,0x03,0x01,0x04,0x05,0x12,0x11,0x06, + 0x13,0x07,0x08,0x14,0x22,0x09,0x21,0x00,0x23,0x15,0x31,0x32, + 0x0a,0x16,0xf0,0x24,0x33,0x41,0x42,0x19,0x17,0x25,0x18,0x51, + 0x34,0x43,0x52,0x29,0x35,0x61,0x39,0x71,0x62,0x36,0x53,0x26, + 0x38,0x1a,0x37,0x81,0x27,0x91,0x79,0x55,0x45,0x28,0x72,0x59, + 0xa1,0xb1,0x44,0x69,0x54,0x58,0xd1,0xfa,0x57,0xe1,0xf1,0xb9, + 0x49,0x47,0x63,0x6a,0xf9,0x56,0x46,0xa8,0x2a,0x4a,0x78,0x99, + 0x3a,0x75,0x74,0x86,0x65,0xc1,0x76,0xb6,0x96,0xd6,0x89,0x85, + 0xc9,0xf5,0x95,0xb4,0xc7,0xf7,0x8a,0x97,0xb8,0x73,0xb7,0xd8, + 0xd9,0x87,0xa7,0x7a,0x48,0x82,0x84,0xea,0xf4,0xa6,0xc5,0x5a, + 0x94,0xa4,0xc6,0x92,0xc3,0x68,0xb5,0xc8,0xe4,0xe5,0xe6,0xe9, + 0xa2,0xa3,0xe3,0xc2,0x66,0x67,0x93,0xaa,0xd4,0xd5,0xe7,0xf8, + 0x88,0x9a,0xd7,0x77,0xc4,0x64,0xe2,0x98,0xa5,0xca,0xda,0xe8, + 0xf3,0xf6,0xa9,0xb2,0xb3,0xf2,0xd2,0x83,0xba,0xd3,0xff,0xff }, + { 0,0,6,2,1,3,3,2,5,1,2,2,8,10,0,117, + 0x04,0x05,0x03,0x06,0x02,0x07,0x01,0x08, + 0x09,0x12,0x13,0x14,0x11,0x15,0x0a,0x16,0x17,0xf0,0x00,0x22, + 0x21,0x18,0x23,0x19,0x24,0x32,0x31,0x25,0x33,0x38,0x37,0x34, + 0x35,0x36,0x39,0x79,0x57,0x58,0x59,0x28,0x56,0x78,0x27,0x41, + 0x29,0x77,0x26,0x42,0x76,0x99,0x1a,0x55,0x98,0x97,0xf9,0x48, + 0x54,0x96,0x89,0x47,0xb7,0x49,0xfa,0x75,0x68,0xb6,0x67,0x69, + 0xb9,0xb8,0xd8,0x52,0xd7,0x88,0xb5,0x74,0x51,0x46,0xd9,0xf8, + 0x3a,0xd6,0x87,0x45,0x7a,0x95,0xd5,0xf6,0x86,0xb4,0xa9,0x94, + 0x53,0x2a,0xa8,0x43,0xf5,0xf7,0xd4,0x66,0xa7,0x5a,0x44,0x8a, + 0xc9,0xe8,0xc8,0xe7,0x9a,0x6a,0x73,0x4a,0x61,0xc7,0xf4,0xc6, + 0x65,0xe9,0x72,0xe6,0x71,0x91,0x93,0xa6,0xda,0x92,0x85,0x62, + 0xf3,0xc5,0xb2,0xa4,0x84,0xba,0x64,0xa5,0xb3,0xd2,0x81,0xe5, + 0xd3,0xaa,0xc4,0xca,0xf2,0xb1,0xe4,0xd1,0x83,0x63,0xea,0xc3, + 0xe2,0x82,0xf1,0xa3,0xc2,0xa1,0xc1,0xe3,0xa2,0xe1,0xff,0xff } + }; + if (table > 2) table = 2; + init_decoder(); + make_decoder ( first_tree[table], 0); + second_decode = free_decode; + make_decoder (second_tree[table], 0); +} + +/* + Return 0 if the image starts with compressed data, + 1 if it starts with uncompressed low-order bits. + + In Canon compressed data, 0xff is always followed by 0x00. + */ +int CLASS canon_has_lowbits() +{ + uchar test[0x4000]; + int ret=1, i; + + fseek (ifp, 0, SEEK_SET); + fread (test, 1, sizeof test, ifp); + for (i=540; i < sizeof test - 1; i++) + if (test[i] == 0xff) { + if (test[i+1]) return 1; + ret=0; + } + return ret; +} + +void CLASS canon_compressed_load_raw() +{ + ushort *pixel, *prow; + int nblocks, lowbits, i, row, r, col, save, val; + unsigned irow, icol; + struct decode *decode, *dindex; + int block, diffbuf[64], leaf, len, diff, carry=0, pnum=0, base[2]; + double dark[2] = { 0,0 }; + uchar c; + + crw_init_tables (tiff_compress); + pixel = (ushort *) calloc (raw_width*8, sizeof *pixel); + merror (pixel, "canon_compressed_load_raw()"); + lowbits = canon_has_lowbits(); + if (!lowbits) maximum = 0x3ff; + fseek (ifp, 540 + lowbits*raw_height*raw_width/4, SEEK_SET); + zero_after_ff = 1; + getbits(-1); + for (row=0; row < raw_height; row+=8) { + nblocks = MIN (8, raw_height-row) * raw_width >> 6; + for (block=0; block < nblocks; block++) { + memset (diffbuf, 0, sizeof diffbuf); + decode = first_decode; + for (i=0; i < 64; i++ ) { + for (dindex=decode; dindex->branch[0]; ) + dindex = dindex->branch[getbits(1)]; + leaf = dindex->leaf; + decode = second_decode; + if (leaf == 0 && i) break; + if (leaf == 0xff) continue; + i += leaf >> 4; + len = leaf & 15; + if (len == 0) continue; + diff = getbits(len); + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + if (i < 64) diffbuf[i] = diff; + } + diffbuf[0] += carry; + carry = diffbuf[0]; + for (i=0; i < 64; i++ ) { + if (pnum++ % raw_width == 0) + base[0] = base[1] = 512; + if ((pixel[(block << 6) + i] = base[i & 1] += diffbuf[i]) >> 10) + derror(); + } + } + if (lowbits) { + save = ftell(ifp); + fseek (ifp, 26 + row*raw_width/4, SEEK_SET); + for (prow=pixel, i=0; i < raw_width*2; i++) { + c = fgetc(ifp); + for (r=0; r < 8; r+=2, prow++) { + val = (*prow << 2) + ((c >> r) & 3); + if (raw_width == 2672 && val < 512) val += 2; + *prow = val; + } + } + fseek (ifp, save, SEEK_SET); + } + for (r=0; r < 8; r++) { + irow = row - top_margin + r; +#ifndef LIBRAW_LIBRARY_BUILD + if (irow >= height) continue; +#endif + for (col=0; col < raw_width; col++) { +#ifdef LIBRAW_LIBRARY_BUILD + ushort *dfp = get_masked_pointer(row+r,col); + if(dfp) *dfp = pixel[r*raw_width+col]; + if (irow >= height) continue; // skip for top/bottom rows +#endif + icol = col - left_margin; + if (icol < width) + BAYER(irow,icol) = pixel[r*raw_width+col]; + else if (col > 1) + dark[icol & 1] += pixel[r*raw_width+col]; + } + } + } + free (pixel); +#ifdef LIBRAW_LIBRARY_BUILD + if(!( filtering_mode & LIBRAW_FILTERING_NOBLACKS) ) +#endif + canon_black (dark); +} + +#line 922 "dcraw/dcraw.c" +int CLASS ljpeg_start (struct jhead *jh, int info_only) +{ + int c, tag, len; + uchar data[0x10000], *dp; + + init_decoder(); + memset (jh, 0, sizeof *jh); + FORC(6) jh->huff[c] = free_decode; + jh->restart = INT_MAX; + fread (data, 2, 1, ifp); + if (data[1] != 0xd8) return 0; + do { + fread (data, 2, 2, ifp); + tag = data[0] << 8 | data[1]; + len = (data[2] << 8 | data[3]) - 2; + if (tag <= 0xff00) return 0; + fread (data, 1, len, ifp); + switch (tag) { + case 0xffc3: + jh->sraw = ((data[7] >> 4) * (data[7] & 15) - 1) & 3; + case 0xffc0: + jh->bits = data[0]; + jh->high = data[1] << 8 | data[2]; + jh->wide = data[3] << 8 | data[4]; + jh->clrs = data[5] + jh->sraw; + if (len == 9 && !dng_version) getc(ifp); + break; + case 0xffc4: + if (info_only) break; + for (dp = data; dp < data+len && *dp < 4; ) { + jh->huff[*dp] = free_decode; + dp = make_decoder (++dp, 0); + } + break; + case 0xffda: + jh->psv = data[1+data[0]*2]; + jh->bits -= data[3+data[0]*2] & 15; + break; + case 0xffdd: + jh->restart = data[0] << 8 | data[1]; + } + } while (tag != 0xffda); + if (info_only) return 1; + if (jh->sraw) { + FORC(4) jh->huff[2+c] = jh->huff[1]; + FORC(jh->sraw) jh->huff[1+c] = jh->huff[0]; + } + jh->row = (ushort *) calloc (jh->wide*jh->clrs, 4); + merror (jh->row, "ljpeg_start()"); + return zero_after_ff = 1; +} + +int CLASS ljpeg_diff (struct decode *dindex) +{ + int len, diff; + + while (dindex->branch[0]) + dindex = dindex->branch[getbits(1)]; + len = dindex->leaf; + if (len == 16 && (!dng_version || dng_version >= 0x1010000)) + return -32768; + diff = getbits(len); + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + return diff; +} + +ushort * CLASS ljpeg_row (int jrow, struct jhead *jh) +{ + int col, c, diff, pred, spred=0; + ushort mark=0, *row[3]; + + if (jrow * jh->wide % jh->restart == 0) { + FORC(6) jh->vpred[c] = 1 << (jh->bits-1); + if (jrow) + do mark = (mark << 8) + (c = fgetc(ifp)); + while (c != EOF && mark >> 4 != 0xffd); + getbits(-1); + } + FORC3 row[c] = jh->row + jh->wide*jh->clrs*((jrow+c) & 1); + for (col=0; col < jh->wide; col++) + FORC(jh->clrs) { + diff = ljpeg_diff (jh->huff[c]); + if (jh->sraw && c <= jh->sraw && (col | c)) + pred = spred; + else if (col) pred = row[0][-jh->clrs]; + else pred = (jh->vpred[c] += diff) - diff; + if (jrow && col) switch (jh->psv) { + case 1: break; + case 2: pred = row[1][0]; break; + case 3: pred = row[1][-jh->clrs]; break; + case 4: pred = pred + row[1][0] - row[1][-jh->clrs]; break; + case 5: pred = pred + ((row[1][0] - row[1][-jh->clrs]) >> 1); break; + case 6: pred = row[1][0] + ((pred - row[1][-jh->clrs]) >> 1); break; + case 7: pred = (pred + row[1][0]) >> 1; break; + default: pred = 0; + } + if ((**row = pred + diff) >> jh->bits) derror(); + if (c <= jh->sraw) spred = **row; + row[0]++; row[1]++; + } + return row[2]; +} + +void CLASS lossless_jpeg_load_raw() +{ + int jwide, jrow, jcol, val, jidx, i, j, row=0, col=0; + double dark[2] = { 0,0 }; + struct jhead jh; + int min=INT_MAX; + ushort *rp; + + if (!ljpeg_start (&jh, 0)) return; + jwide = jh.wide * jh.clrs; + + for (jrow=0; jrow < jh.high; jrow++) { + rp = ljpeg_row (jrow, &jh); + for (jcol=0; jcol < jwide; jcol++) { + val = *rp++; + if (jh.bits <= 12) +#ifdef LIBRAW_LIBRARY_BUILD + if( !(filtering_mode & LIBRAW_FILTERING_NORAWCURVE)) +#endif + val = curve[val & 0xfff]; + if (cr2_slice[0]) { + jidx = jrow*jwide + jcol; + i = jidx / (cr2_slice[1]*jh.high); + if ((j = i >= cr2_slice[0])) + i = cr2_slice[0]; + jidx -= i * (cr2_slice[1]*jh.high); + row = jidx / cr2_slice[1+j]; + col = jidx % cr2_slice[1+j] + i*cr2_slice[1]; + } + if (raw_width == 3984 && (col -= 2) < 0) + col += (row--,raw_width); +#ifdef LIBRAW_LIBRARY_BUILD + ushort *dfp = get_masked_pointer(row,col); + if(dfp) *dfp = val; +#endif + if ((unsigned) (row-top_margin) < height) { + if ((unsigned) (col-left_margin) < width) { + BAYER(row-top_margin,col-left_margin) = val; + if (min > val) min = val; + } else if (col > 1) + dark[(col-left_margin) & 1] += val; + } + if (++col >= raw_width) + col = (row++,0); + } + } + free (jh.row); +#ifdef LIBRAW_LIBRARY_BUILD + if(!(filtering_mode & LIBRAW_FILTERING_NOBLACKS)) +#endif + canon_black (dark); + if (!strcasecmp(make,"KODAK")) + black = min; +} + +void CLASS canon_sraw_load_raw() +{ + struct jhead jh; + short *rp=0, (*ip)[4]; + int jwide, slice, scol, ecol, row, col, jrow=0, jcol=0, pix[3], c; + + if (!ljpeg_start (&jh, 0)) return; + jwide = (jh.wide >>= 1) * jh.clrs; + + for (ecol=slice=0; slice <= cr2_slice[0]; slice++) { + scol = ecol; + ecol += cr2_slice[1] * 2 / jh.clrs; + if (!cr2_slice[0] || ecol > raw_width-1) ecol = raw_width & -2; + for (row=0; row < height; row += (jh.clrs >> 1) - 1) { + ip = (short (*)[4]) image + row*width; + for (col=scol; col < ecol; col+=2, jcol+=jh.clrs) { + if ((jcol %= jwide) == 0) + rp = (short *) ljpeg_row (jrow++, &jh); + if (col >= width) continue; + FORC (jh.clrs-2) + ip[col + (c >> 1)*width + (c & 1)][0] = rp[jcol+c]; + ip[col][1] = rp[jcol+jh.clrs-2] - 16384; + ip[col][2] = rp[jcol+jh.clrs-1] - 16384; + } + } + } + ip = (short (*)[4]) image; + rp = ip[0]; + for (row=0; row < height; row++, ip+=width) { + if (row & (jh.sraw >> 1)) + for (col=0; col < width; col+=2) + for (c=1; c < 3; c++) + if (row == height-1) + ip[col][c] = ip[col-width][c]; + else ip[col][c] = (ip[col-width][c] + ip[col+width][c] + 1) >> 1; + for (col=1; col < width; col+=2) + for (c=1; c < 3; c++) + if (col == width-1) + ip[col][c] = ip[col-1][c]; + else ip[col][c] = (ip[col-1][c] + ip[col+1][c] + 1) >> 1; + } + for ( ; rp < ip[0]; rp+=4) { + if (unique_id < 0x80000200) { + pix[0] = rp[0] + rp[2] - 512; + pix[2] = rp[0] + rp[1] - 512; + pix[1] = rp[0] + ((-778*rp[1] - (rp[2] << 11)) >> 12) - 512; + } else { + rp[1] += jh.sraw+1; + rp[2] += jh.sraw+1; + pix[0] = rp[0] + (( 200*rp[1] + 22929*rp[2]) >> 12); + pix[1] = rp[0] + ((-5640*rp[1] - 11751*rp[2]) >> 12); + pix[2] = rp[0] + ((29040*rp[1] - 101*rp[2]) >> 12); + } + FORC3 rp[c] = CLIP(pix[c] * sraw_mul[c] >> 10); + } + free (jh.row); + maximum = 0x3fff; +} + +void CLASS adobe_copy_pixel (int row, int col, ushort **rp) +{ + unsigned r, c; + + r = row -= top_margin; + c = col -= left_margin; + if (is_raw == 2 && shot_select) (*rp)++; + if (filters) { +#ifndef LIBRAW_LIBRARY_BUILD + if (fuji_width) { + r = row + fuji_width - 1 - (col >> 1); + c = row + ((col+1) >> 1); + } +#endif +#ifdef LIBRAW_LIBRARY_BUILD + ushort val = **rp; + if(!(filtering_mode & LIBRAW_FILTERING_NORAWCURVE)) + val = **rp < 0x1000 ? curve[**rp] : **rp; + if (r < height && c < width) + BAYER(r,c) = val; + else + { + ushort *dfp = get_masked_pointer(row+top_margin,col+left_margin); + if(dfp) *dfp = val; + } +#else + if (r < height && c < width) + BAYER(r,c) = **rp < 0x1000 ? curve[**rp] : **rp; +#endif + *rp += is_raw; + } else { + if (r < height && c < width) + FORC(tiff_samples) + image[row*width+col][c] = (*rp)[c] < 0x1000 ? curve[(*rp)[c]]:(*rp)[c]; + *rp += tiff_samples; + } + if (is_raw == 2 && shot_select) (*rp)--; +} + +void CLASS adobe_dng_load_raw_lj() +{ + unsigned save, trow=0, tcol=0, jwide, jrow, jcol, row, col; + struct jhead jh; + ushort *rp; + + while (trow < raw_height) { + save = ftell(ifp); + if (tile_length < INT_MAX) + fseek (ifp, get4(), SEEK_SET); + if (!ljpeg_start (&jh, 0)) break; + jwide = jh.wide; + if (filters) jwide *= jh.clrs; + jwide /= is_raw; + for (row=col=jrow=0; jrow < jh.high; jrow++) { + rp = ljpeg_row (jrow, &jh); + for (jcol=0; jcol < jwide; jcol++) { + adobe_copy_pixel (trow+row, tcol+col, &rp); + if (++col >= tile_width || col >= raw_width) + row += 1 + (col = 0); + } + } + fseek (ifp, save+4, SEEK_SET); + if ((tcol += tile_width) >= raw_width) + trow += tile_length + (tcol = 0); + free (jh.row); + } +} + +void CLASS adobe_dng_load_raw_nc() +{ + ushort *pixel, *rp; + int row, col; + + pixel = (ushort *) calloc (raw_width * tiff_samples, sizeof *pixel); + merror (pixel, "adobe_dng_load_raw_nc()"); + for (row=0; row < raw_height; row++) { + if (tiff_bps == 16) + read_shorts (pixel, raw_width * tiff_samples); + else { + getbits(-1); + for (col=0; col < raw_width * tiff_samples; col++) + pixel[col] = getbits(tiff_bps); + } + for (rp=pixel, col=0; col < raw_width; col++) + adobe_copy_pixel (row, col, &rp); + } + free (pixel); +} + +void CLASS pentax_k10_load_raw() +{ + static const uchar pentax_tree[] = + { 0,2,3,1,1,1,1,1,1,2,0,0,0,0,0,0, + 3,4,2,5,1,6,0,7,8,9,10,11,12 }; + int row, col, diff; + ushort vpred[2][2] = {{0,0},{0,0}}, hpred[2]; + + init_decoder(); + make_decoder (pentax_tree, 0); + getbits(-1); + for (row=0; row < raw_height; row++) + { +#ifndef LIBRAW_LIBRARY_BUILD + if(row >= height) break; +#endif + for (col=0; col < raw_width; col++) { + diff = ljpeg_diff (first_decode); + if (col < 2) hpred[col] = vpred[row & 1][col] += diff; + else hpred[col & 1] += diff; + if (col < width && row < height) + BAYER(row,col) = hpred[col & 1]; +#ifdef LIBRAW_LIBRARY_BUILD + else + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) *dfp = hpred[col & 1]; + } + + if (col < width && row < height) +#endif + if (hpred[col & 1] >> 12) derror(); + } + } +} + +void CLASS nikon_compressed_load_raw() +{ + static const uchar nikon_tree[][32] = { + { 0,1,5,1,1,1,1,1,1,2,0,0,0,0,0,0, /* 12-bit lossy */ + 5,4,3,6,2,7,1,0,8,9,11,10,12 }, + { 0,1,5,1,1,1,1,1,1,2,0,0,0,0,0,0, /* 12-bit lossy after split */ + 0x39,0x5a,0x38,0x27,0x16,5,4,3,2,1,0,11,12,12 }, + { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0, /* 12-bit lossless */ + 5,4,6,3,7,2,8,1,9,0,10,11,12 }, + { 0,1,4,3,1,1,1,1,1,2,0,0,0,0,0,0, /* 14-bit lossy */ + 5,6,4,7,8,3,9,2,1,0,10,11,12,13,14 }, + { 0,1,5,1,1,1,1,1,1,1,2,0,0,0,0,0, /* 14-bit lossy after split */ + 8,0x5c,0x4b,0x3a,0x29,7,6,5,4,3,2,1,0,13,14 }, + { 0,1,4,2,2,3,1,2,0,0,0,0,0,0,0,0, /* 14-bit lossless */ + 7,6,8,5,9,4,10,3,11,12,2,0,1,13,14 } }; + struct decode *dindex; + ushort ver0, ver1, vpred[2][2], hpred[2], csize; + int i, min, max, step=0, huff=0, split=0, row, col, len, shl, diff; + + fseek (ifp, meta_offset, SEEK_SET); + ver0 = fgetc(ifp); + ver1 = fgetc(ifp); + if (ver0 == 0x49 || ver1 == 0x58) + fseek (ifp, 2110, SEEK_CUR); + if (ver0 == 0x46) huff = 2; + if (tiff_bps == 14) huff += 3; + read_shorts (vpred[0], 4); + max = 1 << tiff_bps & 0x7fff; + if ((csize = get2()) > 1) + step = max / (csize-1); + if (ver0 == 0x44 && ver1 == 0x20 && step > 0) { + for (i=0; i < csize; i++) + curve[i*step] = get2(); + for (i=0; i < max; i++) + curve[i] = ( curve[i-i%step]*(step-i%step) + + curve[i-i%step+step]*(i%step) ) / step; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.curve_state = LIBRAW_COLORSTATE_LOADED; +#endif + fseek (ifp, meta_offset+562, SEEK_SET); + split = get2(); + } else if (ver0 != 0x46 && csize <= 0x4001) + { + read_shorts (curve, max=csize); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.curve_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + while (curve[max-2] == curve[max-1]) max--; + init_decoder(); + make_decoder (nikon_tree[huff], 0); + fseek (ifp, data_offset, SEEK_SET); + getbits(-1); + for (min=row=0; row < height; row++) { + if (split && row == split) { + init_decoder(); + make_decoder (nikon_tree[huff+1], 0); + max += (min = 16) << 1; + } + for (col=0; col < raw_width; col++) { + for (dindex=first_decode; dindex->branch[0]; ) + dindex = dindex->branch[getbits(1)]; + len = dindex->leaf & 15; + shl = dindex->leaf >> 4; + diff = ((getbits(len-shl) << 1) + 1) << shl >> 1; + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - !shl; + if (col < 2) hpred[col] = vpred[row & 1][col] += diff; + else hpred[col & 1] += diff; + if ((ushort)(hpred[col & 1] + min) >= max) derror(); +#ifndef LIBRAW_LIBRARY_BUILD + if ((unsigned) (col-left_margin) < width) + BAYER(row,col-left_margin) = curve[LIM((short)hpred[col & 1],0,0x3fff)]; +#else + ushort xval = hpred[col & 1]; + if(!(filtering_mode & LIBRAW_FILTERING_NORAWCURVE)) + xval = curve[LIM((short)xval,0,0x3fff)]; + if ((unsigned) (col-left_margin) < width) + { + BAYER(row,col-left_margin) = xval; + } + else + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) *dfp = xval; + } +#endif + + } + } +} + +/* + Figure out if a NEF file is compressed. These fancy heuristics + are only needed for the D100, thanks to a bug in some cameras + that tags all images as "compressed". + */ +int CLASS nikon_is_compressed() +{ + uchar test[256]; + int i; + + fseek (ifp, data_offset, SEEK_SET); + fread (test, 1, 256, ifp); + for (i=15; i < 256; i+=16) + if (test[i]) return 1; + return 0; +} + +/* + Returns 1 for a Coolpix 995, 0 for anything else. + */ +int CLASS nikon_e995() +{ + int i, histo[256]; + const uchar often[] = { 0x00, 0x55, 0xaa, 0xff }; + + memset (histo, 0, sizeof histo); + fseek (ifp, -2000, SEEK_END); + for (i=0; i < 2000; i++) + histo[fgetc(ifp)]++; + for (i=0; i < 4; i++) + if (histo[often[i]] < 200) + return 0; + return 1; +} + +/* + Returns 1 for a Coolpix 2100, 0 for anything else. + */ +int CLASS nikon_e2100() +{ + uchar t[12]; + int i; + + fseek (ifp, 0, SEEK_SET); + for (i=0; i < 1024; i++) { + fread (t, 1, 12, ifp); + if (((t[2] & t[4] & t[7] & t[9]) >> 4 + & t[1] & t[6] & t[8] & t[11] & 3) != 3) + return 0; + } + return 1; +} + +void CLASS nikon_3700() +{ + int bits, i; + uchar dp[24]; + static const struct { + int bits; + char t_make[12], t_model[15]; + } table[] = { + { 0x00, "PENTAX", "Optio 33WR" }, + { 0x03, "NIKON", "E3200" }, + { 0x32, "NIKON", "E3700" }, + { 0x33, "OLYMPUS", "C740UZ" } }; + + fseek (ifp, 3072, SEEK_SET); + fread (dp, 1, 24, ifp); + bits = (dp[8] & 3) << 4 | (dp[20] & 3); + for (i=0; i < sizeof table / sizeof *table; i++) + if (bits == table[i].bits) { + strcpy (make, table[i].t_make ); + strcpy (model, table[i].t_model); + } +} + +/* + Separates a Minolta DiMAGE Z2 from a Nikon E4300. + */ +int CLASS minolta_z2() +{ + int i, nz; + char tail[424]; + + fseek (ifp, -sizeof tail, SEEK_END); + fread (tail, 1, sizeof tail, ifp); + for (nz=i=0; i < sizeof tail; i++) + if (tail[i]) nz++; + return nz > 20; +} + +/* Here raw_width is in bytes, not pixels. */ +void CLASS nikon_e900_load_raw() +{ + int offset=0, irow, row, col; + + for (irow=0; irow < height; irow++) { + row = irow * 2 % height; + if (row == 1) + offset = - (-offset & -4096); + fseek (ifp, offset, SEEK_SET); + offset += raw_width; + getbits(-1); + for (col=0; col < width; col++) + BAYER(row,col) = getbits(10); + } +} + +/* + The Fuji Super CCD is just a Bayer grid rotated 45 degrees. + */ +void CLASS fuji_load_raw() +{ + ushort *pixel; +#ifndef LIBRAW_LIBRARY_BUILD + int wide, row, col, r, c; + + fseek (ifp, (top_margin*raw_width + left_margin) * 2, SEEK_CUR); + wide = fuji_width << !fuji_layout; + pixel = (ushort *) calloc (wide, sizeof *pixel); + merror (pixel, "fuji_load_raw()"); + for (row=0; row < raw_height; row++) { + read_shorts (pixel, wide); + fseek (ifp, 2*(raw_width - wide), SEEK_CUR); + for (col=0; col < wide; col++) { + if (fuji_layout) { + r = fuji_width - 1 - col + (row >> 1); + c = col + ((row+1) >> 1); + } else { + r = fuji_width - 1 + row - (col >> 1); + c = row + ((col+1) >> 1); + } + BAYER(r,c) = pixel[col]; + } + } + free (pixel); +#else + int row,col; + pixel = (ushort *) calloc (raw_width, sizeof *pixel); + merror (pixel, "fuji_load_raw()"); + for (row=0; row < raw_height; row++) { + read_shorts (pixel, raw_width); + for (col=0; col < raw_width; col++) { + if(col >= left_margin && col < width+left_margin + && row >= top_margin && row < height+top_margin) + { + BAYER(row-top_margin,col-left_margin) = pixel[col]; + } + else + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) *dfp = pixel[col]; + } + } + } + free (pixel); +#endif +} +#line 1520 "dcraw/dcraw.c" +void CLASS ppm_thumb (FILE *tfp) +{ + char *thumb; + thumb_length = thumb_width*thumb_height*3; + thumb = (char *) malloc (thumb_length); + merror (thumb, "ppm_thumb()"); + fprintf (tfp, "P6\n%d %d\n255\n", thumb_width, thumb_height); + fread (thumb, 1, thumb_length, ifp); + fwrite (thumb, 1, thumb_length, tfp); + free (thumb); +} + +void CLASS layer_thumb (FILE *tfp) +{ + int i, c; + char *thumb, map[][4] = { "012","102" }; + + colors = thumb_misc >> 5 & 7; + thumb_length = thumb_width*thumb_height; + thumb = (char *) calloc (colors, thumb_length); + merror (thumb, "layer_thumb()"); + fprintf (tfp, "P%d\n%d %d\n255\n", + 5 + (colors >> 1), thumb_width, thumb_height); + fread (thumb, thumb_length, colors, ifp); + for (i=0; i < thumb_length; i++) + FORCC putc (thumb[i+thumb_length*(map[thumb_misc >> 8][c]-'0')], tfp); + free (thumb); +} + +void CLASS rollei_thumb (FILE *tfp) +{ + unsigned i; + ushort *thumb; + + thumb_length = thumb_width * thumb_height; + thumb = (ushort *) calloc (thumb_length, 2); + merror (thumb, "rollei_thumb()"); + fprintf (tfp, "P6\n%d %d\n255\n", thumb_width, thumb_height); + read_shorts (thumb, thumb_length); + for (i=0; i < thumb_length; i++) { + putc (thumb[i] << 3, tfp); + putc (thumb[i] >> 5 << 2, tfp); + putc (thumb[i] >> 11 << 3, tfp); + } + free (thumb); +} + +void CLASS rollei_load_raw() +{ + uchar pixel[10]; + unsigned iten=0, isix, i, buffer=0, row, col, todo[16]; + + isix = raw_width * raw_height * 5 / 8; + while (fread (pixel, 1, 10, ifp) == 10) { + for (i=0; i < 10; i+=2) { + todo[i] = iten++; + todo[i+1] = pixel[i] << 8 | pixel[i+1]; + buffer = pixel[i] >> 2 | buffer << 6; + } + for ( ; i < 16; i+=2) { + todo[i] = isix++; + todo[i+1] = buffer >> (14-i)*5; + } + for (i=0; i < 16; i+=2) { + row = todo[i] / raw_width - top_margin; + col = todo[i] % raw_width - left_margin; + if (row < height && col < width) + BAYER(row,col) = (todo[i+1] & 0x3ff); +#ifdef LIBRAW_LIBRARY_BUILD + else + { + ushort *dfp = get_masked_pointer(todo[i] / raw_width,todo[i] % raw_width); + if(dfp) *dfp = (todo[i+1] & 0x3ff); + } +#endif + } + } + maximum = 0x3ff; +} + +int CLASS bayer (unsigned row, unsigned col) +{ + return (row < height && col < width) ? BAYER(row,col) : 0; +} + +void CLASS phase_one_flat_field (int is_float, int nc) +{ + ushort head[8]; + unsigned wide, y, x, c, rend, cend, row, col; + float *mrow, num, mult[4]; + + read_shorts (head, 8); + wide = head[2] / head[4]; + mrow = (float *) calloc (nc*wide, sizeof *mrow); + merror (mrow, "phase_one_flat_field()"); + for (y=0; y < head[3] / head[5]; y++) { + for (x=0; x < wide; x++) + for (c=0; c < nc; c+=2) { + num = is_float ? getreal(11) : get2()/32768.0; + if (y==0) mrow[c*wide+x] = num; + else mrow[(c+1)*wide+x] = (num - mrow[c*wide+x]) / head[5]; + } + if (y==0) continue; + rend = head[1]-top_margin + y*head[5]; + for (row = rend-head[5]; row < height && row < rend; row++) { + for (x=1; x < wide; x++) { + for (c=0; c < nc; c+=2) { + mult[c] = mrow[c*wide+x-1]; + mult[c+1] = (mrow[c*wide+x] - mult[c]) / head[4]; + } + cend = head[0]-left_margin + x*head[4]; + for (col = cend-head[4]; col < width && col < cend; col++) { + c = nc > 2 ? FC(row,col) : 0; + if (!(c & 1)) { + c = BAYER(row,col) * mult[c]; + BAYER(row,col) = LIM(c,0,65535); + } + for (c=0; c < nc; c+=2) + mult[c] += mult[c+1]; + } + } + for (x=0; x < wide; x++) + for (c=0; c < nc; c+=2) + mrow[c*wide+x] += mrow[(c+1)*wide+x]; + } + } + free (mrow); +} + +void CLASS phase_one_correct() +{ + unsigned entries, tag, data, save, col, row, type; + int len, i, j, k, cip, val[4], dev[4], sum, max; + int head[9], diff, mindiff=INT_MAX, off_412=0; + static const signed char dir[12][2] = + { {-1,-1}, {-1,1}, {1,-1}, {1,1}, {-2,0}, {0,-2}, {0,2}, {2,0}, + {-2,-2}, {-2,2}, {2,-2}, {2,2} }; + float poly[8], num, cfrac, frac, mult[2], *yval[2]; + ushort t_curve[0x10000], *xval[2]; + + if (half_size || !meta_length) return; +#ifdef DCRAW_VERBOSE + if (verbose) fprintf (stderr,_("Phase One correction...\n")); +#endif + fseek (ifp, meta_offset, SEEK_SET); + order = get2(); + fseek (ifp, 6, SEEK_CUR); + fseek (ifp, meta_offset+get4(), SEEK_SET); + entries = get4(); get4(); + while (entries--) { + tag = get4(); + len = get4(); + data = get4(); + save = ftell(ifp); + fseek (ifp, meta_offset+data, SEEK_SET); + if (tag == 0x419) { /* Polynomial curve */ + for (get4(), i=0; i < 8; i++) + poly[i] = getreal(11); + poly[3] += (ph1.tag_210 - poly[7]) * poly[6] + 1; + for (i=0; i < 0x10000; i++) { + num = (poly[5]*i + poly[3])*i + poly[1]; + t_curve[i] = LIM(num,0,65535); + } goto apply; /* apply to right half */ + } else if (tag == 0x41a) { /* Polynomial curve */ + for (i=0; i < 4; i++) + poly[i] = getreal(11); + for (i=0; i < 0x10000; i++) { + for (num=0, j=4; j--; ) + num = num * i + poly[j]; + t_curve[i] = LIM(num+i,0,65535); + } apply: /* apply to whole image */ + for (row=0; row < height; row++) + for (col = (tag & 1)*ph1.split_col; col < width; col++) + BAYER(row,col) = t_curve[BAYER(row,col)]; + } else if (tag == 0x400) { /* Sensor defects */ + while ((len -= 8) >= 0) { + col = get2() - left_margin; + row = get2() - top_margin; + type = get2(); get2(); + if (col >= width) continue; + if (type == 131) /* Bad column */ + for (row=0; row < height; row++) + if (FC(row,col) == 1) { + for (sum=i=0; i < 4; i++) + sum += val[i] = bayer (row+dir[i][0], col+dir[i][1]); + for (max=i=0; i < 4; i++) { + dev[i] = abs((val[i] << 2) - sum); + if (dev[max] < dev[i]) max = i; + } + BAYER(row,col) = (sum - val[max])/3.0 + 0.5; + } else { + for (sum=0, i=8; i < 12; i++) + sum += bayer (row+dir[i][0], col+dir[i][1]); + BAYER(row,col) = 0.5 + sum * 0.0732233 + + (bayer(row,col-2) + bayer(row,col+2)) * 0.3535534; + } + else if (type == 129) { /* Bad pixel */ + if (row >= height) continue; + j = (FC(row,col) != 1) * 4; + for (sum=0, i=j; i < j+8; i++) + sum += bayer (row+dir[i][0], col+dir[i][1]); + BAYER(row,col) = (sum + 4) >> 3; + } + } + } else if (tag == 0x401) { /* All-color flat fields */ + phase_one_flat_field (1, 2); + } else if (tag == 0x416 || tag == 0x410) { + phase_one_flat_field (0, 2); + } else if (tag == 0x40b) { /* Red+blue flat field */ + phase_one_flat_field (0, 4); + } else if (tag == 0x412) { + fseek (ifp, 36, SEEK_CUR); + diff = abs (get2() - ph1.tag_21a); + if (mindiff > diff) { + mindiff = diff; + off_412 = ftell(ifp) - 38; + } + } + fseek (ifp, save, SEEK_SET); + } + if (off_412) { + fseek (ifp, off_412, SEEK_SET); + for (i=0; i < 9; i++) head[i] = get4() & 0x7fff; + yval[0] = (float *) calloc (head[1]*head[3] + head[2]*head[4], 6); + merror (yval[0], "phase_one_correct()"); + yval[1] = (float *) (yval[0] + head[1]*head[3]); + xval[0] = (ushort *) (yval[1] + head[2]*head[4]); + xval[1] = (ushort *) (xval[0] + head[1]*head[3]); + get2(); + for (i=0; i < 2; i++) + for (j=0; j < head[i+1]*head[i+3]; j++) + yval[i][j] = getreal(11); + for (i=0; i < 2; i++) + for (j=0; j < head[i+1]*head[i+3]; j++) + xval[i][j] = get2(); + for (row=0; row < height; row++) + for (col=0; col < width; col++) { + cfrac = (float) col * head[3] / raw_width; + cfrac -= cip = cfrac; + num = BAYER(row,col) * 0.5; + for (i=cip; i < cip+2; i++) { + for (k=j=0; j < head[1]; j++) + if (num < xval[0][k = head[1]*i+j]) break; + frac = (j == 0 || j == head[1]) ? 0 : + (xval[0][k] - num) / (xval[0][k] - xval[0][k-1]); + mult[i-cip] = yval[0][k-1] * frac + yval[0][k] * (1-frac); + } + i = ((mult[0] * (1-cfrac) + mult[1] * cfrac) + * (row + top_margin) + num) * 2; + BAYER(row,col) = LIM(i,0,65535); + } + free (yval[0]); + } +} + +void CLASS phase_one_load_raw() +{ + int row, col, a, b; + ushort *pixel, akey, bkey, mask; + + fseek (ifp, ph1.key_off, SEEK_SET); + akey = get2(); + bkey = get2(); + mask = ph1.format == 1 ? 0x5555:0x1354; +#ifndef LIBRAW_LIBRARY_BUILD + fseek (ifp, data_offset + top_margin*raw_width*2, SEEK_SET); + pixel = (ushort *) calloc (raw_width, sizeof *pixel); + merror (pixel, "phase_one_load_raw()"); + for (row=0; row < height; row++) { + read_shorts (pixel, raw_width); + for (col=0; col < raw_width; col+=2) { + a = pixel[col+0] ^ akey; + b = pixel[col+1] ^ bkey; + pixel[col+0] = (a & mask) | (b & ~mask); + pixel[col+1] = (b & mask) | (a & ~mask); + } + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col+left_margin]; + } + free (pixel); +#else + fseek (ifp, data_offset, SEEK_SET); + pixel = (ushort *) calloc (raw_width, sizeof *pixel); + merror (pixel, "phase_one_load_raw()"); + for (row=0; row < raw_height; row++) { + read_shorts (pixel, raw_width); + for (col=0; col < raw_width; col+=2) { + a = pixel[col+0] ^ akey; + b = pixel[col+1] ^ bkey; + pixel[col+0] = (a & mask) | (b & ~mask); + pixel[col+1] = (b & mask) | (a & ~mask); + } + for (col=0; col < raw_width; col++) + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) + *dfp = pixel[col]; + else + BAYER(row,col-left_margin) = pixel[col]; + } + } + free (pixel); + if(!( filtering_mode & LIBRAW_FILTERING_NORAWCURVE) ) +#endif + phase_one_correct(); +} + +unsigned CLASS ph1_bits (int nbits) +{ +#ifndef LIBRAW_NOTHREADS +#define bitbuf tls->ph1_bits.bitbuf +#define vbits tls->ph1_bits.vbits +#else + static UINT64 bitbuf=0; + static int vbits=0; +#endif + if (nbits == -1) + return bitbuf = vbits = 0; + if (nbits == 0) return 0; + if ((vbits -= nbits) < 0) { + bitbuf = bitbuf << 32 | get4(); + vbits += 32; + } + return bitbuf << (64-nbits-vbits) >> (64-nbits); +#ifndef LIBRAW_NOTHREADS +#undef bitbuf +#undef vbits +#endif +} + +void CLASS phase_one_load_raw_c() +{ + static const int length[] = { 8,7,6,9,11,10,5,12,14,13 }; + int *offset, len[2], pred[2], row, col, i, j; + ushort *pixel; + short (*t_black)[2]; + + pixel = (ushort *) calloc (raw_width + raw_height*4, 2); + merror (pixel, "phase_one_load_raw_c()"); + offset = (int *) (pixel + raw_width); + fseek (ifp, strip_offset, SEEK_SET); + for (row=0; row < raw_height; row++) + offset[row] = get4(); + t_black = (short (*)[2]) offset + raw_height; + fseek (ifp, ph1.black_off, SEEK_SET); + if (ph1.black_off) + { + read_shorts ((ushort *) t_black[0], raw_height*2); +#ifdef LIBRAW_LIBRARY_BUILD + imgdata.masked_pixels.ph1_black = (ushort (*)[2])calloc(raw_height*2,sizeof(ushort)); + merror (imgdata.masked_pixels.ph1_black, "phase_one_load_raw_c()"); + memmove(imgdata.masked_pixels.ph1_black,(ushort *) t_black[0],raw_height*2*sizeof(ushort)); +#endif + } + for (i=0; i < 256; i++) + curve[i] = i*i / 3.969 + 0.5; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.curve_state = LIBRAW_COLORSTATE_CALCULATED; +#endif + for (row=0; row < raw_height; row++) { + fseek (ifp, data_offset + offset[row], SEEK_SET); + ph1_bits(-1); + pred[0] = pred[1] = 0; + for (col=0; col < raw_width; col++) { + if (col >= (raw_width & -8)) + len[0] = len[1] = 14; + else if ((col & 7) == 0) + for (i=0; i < 2; i++) { + for (j=0; j < 5 && !ph1_bits(1); j++); + if (j--) len[i] = length[j*2 + ph1_bits(1)]; + } + if ((i = len[col & 1]) == 14) + pixel[col] = pred[col & 1] = ph1_bits(16); + else + pixel[col] = pred[col & 1] += ph1_bits(i) + 1 - (1 << (i - 1)); + if (pred[col & 1] >> 16) derror(); +#ifdef LIBRAW_LIBRARY_BUILD + if(!( filtering_mode & LIBRAW_FILTERING_NORAWCURVE) ) +#endif + if (ph1.format == 5 && pixel[col] < 256) + pixel[col] = curve[pixel[col]]; + } + if ((unsigned) (row-top_margin) < height) +#ifndef LIBRAW_LIBRARY_BUILD + for (col=0; col < width; col++) { + i = (pixel[col+left_margin] << 2) + - ph1.t_black + t_black[row][col >= ph1.split_col]; + if (i > 0) BAYER(row-top_margin,col) = i; + } +#else + { + for (col=0; col < raw_width; col++) { + if( filtering_mode & LIBRAW_FILTERING_NOBLACKS) + i = (pixel[col] << 2); + else + i = (pixel[col] << 2) + - ph1.t_black + t_black[row][(col /* - left_margin */) >= ph1.split_col]; // changed to fix Coffin's bug! + if(col >= left_margin && col < width+left_margin) + { + if (i > 0) BAYER(row-top_margin,col-left_margin) = i; + } + else + { + ushort *dfp = get_masked_pointer(row,col); + if(i>0 && dfp) *dfp = i; + } + } + } + else + { + // top-bottom fields + for (col=0; col < raw_width; col++) { + i = (pixel[col] << 2) + - ph1.t_black + t_black[row][(col+left_margin) >= ph1.split_col]; + if (i > 0) + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) *dfp = i; + } + } + } +#endif + } + free (pixel); +#ifdef LIBRAW_LIBRARY_BUILD + if(!( filtering_mode & LIBRAW_FILTERING_NORAWCURVE) ) +#endif + phase_one_correct(); + maximum = 0xfffc - ph1.t_black; +} + +void CLASS hasselblad_load_raw() +{ + struct jhead jh; + struct decode *dindex; + int row, col, pred[2], len[2], diff, i; + + if (!ljpeg_start (&jh, 0)) return; + free (jh.row); + order = 0x4949; + ph1_bits(-1); + for (row=-top_margin; row < raw_height-top_margin; row++) { + pred[0] = pred[1] = 0x8000; + for (col=-left_margin; col < raw_width-left_margin; col+=2) { + for (i=0; i < 2; i++) { + for (dindex=jh.huff[0]; dindex->branch[0]; ) + dindex = dindex->branch[ph1_bits(1)]; + len[i] = dindex->leaf; + } + for (i=0; i < 2; i++) { + diff = ph1_bits(len[i]); + if ((diff & (1 << (len[i]-1))) == 0) + diff -= (1 << len[i]) - 1; + if (diff == 65535) diff = -32768; + pred[i] += diff; + if (row >= 0 && row < height && (unsigned)(col+i) < width) + BAYER(row,col+i) = pred[i]; +#ifdef LIBRAW_LIBRARY_BUILD + else + { + ushort *dfp = get_masked_pointer(row+top_margin,col+left_margin); + if(dfp) *dfp = pred[i]; + } +#endif + } + } + } + maximum = 0xffff; +} + +void CLASS leaf_hdr_load_raw() +{ + ushort *pixel; + unsigned tile=0, r, c, row, col; + + pixel = (ushort *) calloc (raw_width, sizeof *pixel); + merror (pixel, "leaf_hdr_load_raw()"); + FORC(tiff_samples) + for (r=0; r < raw_height; r++) { + if (r % tile_length == 0) { + fseek (ifp, data_offset + 4*tile++, SEEK_SET); + fseek (ifp, get4() + 2*left_margin, SEEK_SET); + } + if (filters && c != shot_select) continue; + read_shorts (pixel, raw_width); + if ((row = r - top_margin) >= height) continue; + for (col=0; col < width; col++) + if (filters) BAYER(row,col) = pixel[col]; + else image[row*width+col][c] = pixel[col]; + } + free (pixel); + if (!filters) { + maximum = 0xffff; + raw_color = 1; + } +} + +#line 2021 "dcraw/dcraw.c" +void CLASS sinar_4shot_load_raw() +{ + ushort *pixel; + unsigned shot, row, col, r, c; + + if ((shot = shot_select) || half_size) { + if (shot) shot--; + if (shot > 3) shot = 3; + fseek (ifp, data_offset + shot*4, SEEK_SET); + fseek (ifp, get4(), SEEK_SET); + unpacked_load_raw(); + return; + } + free (image); + image = (ushort (*)[4]) + calloc ((iheight=height)*(iwidth=width), sizeof *image); + merror (image, "sinar_4shot_load_raw()"); + pixel = (ushort *) calloc (raw_width, sizeof *pixel); + merror (pixel, "sinar_4shot_load_raw()"); + for (shot=0; shot < 4; shot++) { + fseek (ifp, data_offset + shot*4, SEEK_SET); + fseek (ifp, get4(), SEEK_SET); + for (row=0; row < raw_height; row++) { + read_shorts (pixel, raw_width); + if ((r = row-top_margin - (shot >> 1 & 1)) >= height) continue; + for (col=0; col < raw_width; col++) { + if ((c = col-left_margin - (shot & 1)) >= width) continue; + image[r*width+c][FC(row,col)] = pixel[col]; + } + } + } + free (pixel); + shrink = filters = 0; +} + +void CLASS imacon_full_load_raw() +{ + int row, col; + + for (row=0; row < height; row++) + for (col=0; col < width; col++) + read_shorts (image[row*width+col], 3); +} + +void CLASS packed_12_load_raw() +{ + int vbits=0, rbits=0, irow, row, col; + UINT64 bitbuf=0; + + if (raw_width * 2 >= width * 3) { /* If raw_width is in bytes, */ + rbits = raw_width * 8; + raw_width = raw_width * 2 / 3; /* convert it to pixels and */ + rbits -= raw_width * 12; /* save the remainder. */ + } + order = load_flags & 1 ? 0x4949 : 0x4d4d; + for (irow=0; irow < height; irow++) { + row = irow; + if (load_flags & 2 && + (row = irow * 2 % height + irow / (height/2)) == 1 && + load_flags & 4) { + if (vbits=0, tiff_compress) + fseek (ifp, data_offset - (-width*height*3/4 & -2048), SEEK_SET); + else { + fseek (ifp, 0, SEEK_END); + fseek (ifp, ftell(ifp)/2, SEEK_SET); + } + } + for (col=0; col < raw_width; col++) { + if ((vbits -= 12) < 0) { + bitbuf = bitbuf << 32 | get4(); + vbits += 32; + } + if ((unsigned) (col-left_margin) < width) + BAYER(row,col-left_margin) = bitbuf << (52-vbits) >> 52; +#ifdef LIBRAW_LIBRARY_BUILD + else + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) *dfp = bitbuf << (52-vbits) >> 52; + } +#endif + if (load_flags & 8 && (col % 10) == 9) + if (vbits=0, bitbuf & 255) derror(); + } + vbits -= rbits; + } + if (!strcmp(make,"OLYMPUS")) black >>= 4; +} + +void CLASS unpacked_load_raw() +{ + ushort *pixel; + int row, col, bits=0; + + while (1 << ++bits < maximum); +#ifndef LIBRAW_LIBRARY_BUILD + fseek (ifp, (top_margin*raw_width + left_margin) * 2, SEEK_CUR); + pixel = (ushort *) calloc (width, sizeof *pixel); + merror (pixel, "unpacked_load_raw()"); + for (row=0; row < height; row++) { + read_shorts (pixel, width); + fseek (ifp, 2*(raw_width - width), SEEK_CUR); + for (col=0; col < width; col++) + if ((BAYER2(row,col) = pixel[col]) >> bits) derror(); + } + free (pixel); +#else + // fseek (ifp, (top_margin*raw_width + left_margin) * 2, SEEK_CUR); + pixel = (ushort *) calloc (raw_width, sizeof *pixel); + merror (pixel, "unpacked_load_raw()"); + for (row=0; row < raw_height; row++) { + read_shorts (pixel, raw_width); + //fseek (ifp, 2*(raw_width - width), SEEK_CUR); + for (col=0; col < raw_width; col++) + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) + *dfp = pixel[col]; + else + { + if ((BAYER2(row-top_margin,col-left_margin) = pixel[col]) >> bits) derror(); + } + } + } + free (pixel); +#endif +} + +void CLASS nokia_load_raw() +{ + uchar *data, *dp; + ushort *pixel, *pix; + int dwide, row, c; + + dwide = raw_width * 5 / 4; + data = (uchar *) malloc (dwide + raw_width*2); + merror (data, "nokia_load_raw()"); + pixel = (ushort *) (data + dwide); + for (row=0; row < raw_height; row++) { + if (fread (data, 1, dwide, ifp) < dwide) derror(); + for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=5, pix+=4) + FORC4 pix[c] = (dp[c] << 2) | (dp[4] >> (c << 1) & 3); + if (row < top_margin) +#ifdef LIBRAW_LIBRARY_BUILD + { + int col; + for(col=0;colpana_bits.buf +#define vbits tls->pana_bits.vbits +#else + static uchar buf[0x4000]; + static int vbits; +#endif + int byte; + + if (!nbits) return vbits=0; + if (!vbits) { + fread (buf+load_flags, 1, 0x4000-load_flags, ifp); + fread (buf, 1, load_flags, ifp); + } + vbits = (vbits - nbits) & 0x1ffff; + byte = vbits >> 3 ^ 0x3ff0; + return (buf[byte] | buf[byte+1] << 8) >> (vbits & 7) & ~(-1 << nbits); +#ifndef LIBRAW_NOTHREADS +#undef buf +#undef vbits +#endif +} + +void CLASS panasonic_load_raw() +{ + int row, col, i, j, sh=0, pred[2], nonz[2]; + + pana_bits(0); + for (row=0; row < height; row++) + for (col=0; col < raw_width; col++) { + if ((i = col % 14) == 0) + pred[0] = pred[1] = nonz[0] = nonz[1] = 0; + if (i % 3 == 2) sh = 4 >> (3 - pana_bits(2)); + if (nonz[i & 1]) { + if ((j = pana_bits(8))) { + if ((pred[i & 1] -= 0x80 << sh) < 0 || sh == 4) + pred[i & 1] &= ~(-1 << sh); + pred[i & 1] += j << sh; + } + } else if ((nonz[i & 1] = pana_bits(8)) || i > 11) + pred[i & 1] = nonz[i & 1] << 4 | pana_bits(4); + if (col < width) + if ((BAYER(row,col) = pred[col & 1]) > 4098) derror(); +#ifdef LIBRAW_LIBRARY_BUILD + if(col>=width) + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp)*dfp = pred[col & 1]; + } +#endif + } +} + +void CLASS olympus_e300_load_raw() +{ + uchar *data, *dp; + ushort *pixel, *pix; + int dwide, row, col; + + dwide = raw_width * 16 / 10; +#ifndef LIBRAW_LIBRARY_BUILD + fseek (ifp, dwide*top_margin, SEEK_CUR); +#endif + data = (uchar *) malloc (dwide + raw_width*2); + merror (data, "olympus_e300_load_raw()"); + pixel = (ushort *) (data + dwide); +#ifndef LIBRAW_LIBRARY_BUILD + for (row=0; row < height; row++) +#else + for (row=0; row < raw_height; row++) +#endif + { + if (fread (data, 1, dwide, ifp) < dwide) derror(); + for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=3, pix+=2) { + if (((dp-data) & 15) == 15) + if (*dp++ && pix < pixel+width+left_margin) derror(); + pix[0] = dp[1] << 8 | dp[0]; + pix[1] = dp[2] << 4 | dp[1] >> 4; + } +#ifndef LIBRAW_LIBRARY_BUILD + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col+left_margin] & 0xfff); +#else + for (col=0; col < raw_width; col++) + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) + *dfp = (pixel[col] & 0xfff); + else + BAYER(row-top_margin,col-left_margin) = (pixel[col] & 0xfff); + } + +#endif + } + free (data); + maximum >>= 4; + black >>= 4; +} + +void CLASS olympus_e410_load_raw() +{ + int row, col, nbits, sign, low, high, i, w, n, nw; + int acarry[2][3], *carry, pred, diff; + + fseek (ifp, 7, SEEK_CUR); + getbits(-1); + for (row=0; row < height; row++) { + memset (acarry, 0, sizeof acarry); + for (col=0; col < width; col++) { + carry = acarry[col & 1]; + i = 2 * (carry[2] < 3); + for (nbits=2+i; (ushort) carry[0] >> (nbits+i); nbits++); + sign = getbits(1) * -1; + low = getbits(2); + for (high=0; high < 12; high++) + if (getbits(1)) break; + if (high == 12) + high = getbits(16-nbits) >> 1; + carry[0] = (high << nbits) | getbits(nbits); + diff = (carry[0] ^ sign) + carry[1]; + carry[1] = (diff*3 + carry[1]) >> 5; + carry[2] = carry[0] > 16 ? 0 : carry[2]+1; + if (row < 2 && col < 2) pred = 0; + else if (row < 2) pred = BAYER(row,col-2); + else if (col < 2) pred = BAYER(row-2,col); + else { + w = BAYER(row,col-2); + n = BAYER(row-2,col); + nw = BAYER(row-2,col-2); + if ((w < nw && nw < n) || (n < nw && nw < w)) { + if (ABS(w-nw) > 32 || ABS(n-nw) > 32) + pred = w + n - nw; + else pred = (w + n) >> 1; + } else pred = ABS(w-nw) > ABS(n-nw) ? w : n; + } + if ((BAYER(row,col) = pred + ((diff << 2) | low)) >> 12) derror(); + } + } +} + +void CLASS minolta_rd175_load_raw() +{ + uchar pixel[768]; + unsigned irow, box, row, col; + + for (irow=0; irow < 1481; irow++) { + if (fread (pixel, 1, 768, ifp) < 768) derror(); + box = irow / 82; + row = irow % 82 * 12 + ((box < 12) ? box | 1 : (box-12)*2); + switch (irow) { + case 1477: case 1479: continue; + case 1476: row = 984; break; + case 1480: row = 985; break; + case 1478: row = 985; box = 1; + } + if ((box < 12) && (box & 1)) { + for (col=0; col < 1533; col++, row ^= 1) + if (col != 1) BAYER(row,col) = (col+1) & 2 ? + pixel[col/2-1] + pixel[col/2+1] : pixel[col/2] << 1; + BAYER(row,1) = pixel[1] << 1; + BAYER(row,1533) = pixel[765] << 1; + } else + for (col=row & 1; col < 1534; col+=2) + BAYER(row,col) = pixel[col/2] << 1; + } + maximum = 0xff << 1; +} + +void CLASS casio_qv5700_load_raw() +{ + uchar data[3232], *dp; + ushort pixel[2576], *pix; + int row, col; + + for (row=0; row < height; row++) { + fread (data, 1, 3232, ifp); + for (dp=data, pix=pixel; dp < data+3220; dp+=5, pix+=4) { + pix[0] = (dp[0] << 2) + (dp[1] >> 6); + pix[1] = (dp[1] << 4) + (dp[2] >> 4); + pix[2] = (dp[2] << 6) + (dp[3] >> 2); + pix[3] = (dp[3] << 8) + (dp[4] ); + } + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col] & 0x3ff); + } + maximum = 0x3fc; +} + +void CLASS quicktake_100_load_raw() +{ + uchar pixel[484][644]; + static const short gstep[16] = + { -89,-60,-44,-32,-22,-15,-8,-2,2,8,15,22,32,44,60,89 }; + static const short rstep[6][4] = + { { -3,-1,1,3 }, { -5,-1,1,5 }, { -8,-2,2,8 }, + { -13,-3,3,13 }, { -19,-4,4,19 }, { -28,-6,6,28 } }; + static const short t_curve[256] = + { 0,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27, + 28,29,30,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,53, + 54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,74,75,76,77,78, + 79,80,81,82,83,84,86,88,90,92,94,97,99,101,103,105,107,110,112,114,116, + 118,120,123,125,127,129,131,134,136,138,140,142,144,147,149,151,153,155, + 158,160,162,164,166,168,171,173,175,177,179,181,184,186,188,190,192,195, + 197,199,201,203,205,208,210,212,214,216,218,221,223,226,230,235,239,244, + 248,252,257,261,265,270,274,278,283,287,291,296,300,305,309,313,318,322, + 326,331,335,339,344,348,352,357,361,365,370,374,379,383,387,392,396,400, + 405,409,413,418,422,426,431,435,440,444,448,453,457,461,466,470,474,479, + 483,487,492,496,500,508,519,531,542,553,564,575,587,598,609,620,631,643, + 654,665,676,687,698,710,721,732,743,754,766,777,788,799,810,822,833,844, + 855,866,878,889,900,911,922,933,945,956,967,978,989,1001,1012,1023 }; + int rb, row, col, sharp, val=0; + + getbits(-1); + memset (pixel, 0x80, sizeof pixel); + for (row=2; row < height+2; row++) { + for (col=2+(row & 1); col < width+2; col+=2) { + val = ((pixel[row-1][col-1] + 2*pixel[row-1][col+1] + + pixel[row][col-2]) >> 2) + gstep[getbits(4)]; + pixel[row][col] = val = LIM(val,0,255); + if (col < 4) + pixel[row][col-2] = pixel[row+1][~row & 1] = val; + if (row == 2) + pixel[row-1][col+1] = pixel[row-1][col+3] = val; + } + pixel[row][col] = val; + } + for (rb=0; rb < 2; rb++) + for (row=2+rb; row < height+2; row+=2) + for (col=3-(row & 1); col < width+2; col+=2) { + if (row < 4 || col < 4) sharp = 2; + else { + val = ABS(pixel[row-2][col] - pixel[row][col-2]) + + ABS(pixel[row-2][col] - pixel[row-2][col-2]) + + ABS(pixel[row][col-2] - pixel[row-2][col-2]); + sharp = val < 4 ? 0 : val < 8 ? 1 : val < 16 ? 2 : + val < 32 ? 3 : val < 48 ? 4 : 5; + } + val = ((pixel[row-2][col] + pixel[row][col-2]) >> 1) + + rstep[sharp][getbits(2)]; + pixel[row][col] = val = LIM(val,0,255); + if (row < 4) pixel[row-2][col+2] = val; + if (col < 4) pixel[row+2][col-2] = val; + } + for (row=2; row < height+2; row++) + for (col=3-(row & 1); col < width+2; col+=2) { + val = ((pixel[row][col-1] + (pixel[row][col] << 2) + + pixel[row][col+1]) >> 1) - 0x100; + pixel[row][col] = LIM(val,0,255); + } + for (row=0; row < height; row++) + for (col=0; col < width; col++) + BAYER(row,col) = t_curve[pixel[row+2][col+2]]; + maximum = 0x3ff; +} + +const int * CLASS make_decoder_int (const int *source, int level) +{ + struct decode *cur; + + cur = free_decode++; + if (level < source[0]) { + cur->branch[0] = free_decode; + source = make_decoder_int (source, level+1); + cur->branch[1] = free_decode; + source = make_decoder_int (source, level+1); + } else { + cur->leaf = source[1]; + source += 2; + } + return source; +} + +int CLASS radc_token (int tree) +{ + int t; +#ifndef LIBRAW_NOTHREADS +#define dstart tls->radc_token.dstart +#define dindex tls->radc_token.dindex +#define s tls->radc_token.s + + static const int source[] = { +#else + static struct decode *dstart[18], *dindex; + static const int *s, source[] = { +#endif + 1,1, 2,3, 3,4, 4,2, 5,7, 6,5, 7,6, 7,8, + 1,0, 2,1, 3,3, 4,4, 5,2, 6,7, 7,6, 8,5, 8,8, + 2,1, 2,3, 3,0, 3,2, 3,4, 4,6, 5,5, 6,7, 6,8, + 2,0, 2,1, 2,3, 3,2, 4,4, 5,6, 6,7, 7,5, 7,8, + 2,1, 2,4, 3,0, 3,2, 3,3, 4,7, 5,5, 6,6, 6,8, + 2,3, 3,1, 3,2, 3,4, 3,5, 3,6, 4,7, 5,0, 5,8, + 2,3, 2,6, 3,0, 3,1, 4,4, 4,5, 4,7, 5,2, 5,8, + 2,4, 2,7, 3,3, 3,6, 4,1, 4,2, 4,5, 5,0, 5,8, + 2,6, 3,1, 3,3, 3,5, 3,7, 3,8, 4,0, 5,2, 5,4, + 2,0, 2,1, 3,2, 3,3, 4,4, 4,5, 5,6, 5,7, 4,8, + 1,0, 2,2, 2,-2, + 1,-3, 1,3, + 2,-17, 2,-5, 2,5, 2,17, + 2,-7, 2,2, 2,9, 2,18, + 2,-18, 2,-9, 2,-2, 2,7, + 2,-28, 2,28, 3,-49, 3,-9, 3,9, 4,49, 5,-79, 5,79, + 2,-1, 2,13, 2,26, 3,39, 4,-16, 5,55, 6,-37, 6,76, + 2,-26, 2,-13, 2,1, 3,-39, 4,16, 5,-55, 6,-76, 6,37 + }; + + if (free_decode == first_decode) + for (s=source, t=0; t < 18; t++) { + dstart[t] = free_decode; + s = make_decoder_int (s, 0); + } + if (tree == 18) { + if (kodak_cbpp == 243) + return (getbits(6) << 2) + 2; /* most DC50 photos */ + else + return (getbits(5) << 3) + 4; /* DC40, Fotoman Pixtura */ + } + for (dindex = dstart[tree]; dindex->branch[0]; ) + dindex = dindex->branch[getbits(1)]; + return dindex->leaf; + +#ifndef LIBRAW_NOTHREADS +#undef dstart +#undef dindex +#undef s +#endif +} + +#define FORYX for (y=1; y < 3; y++) for (x=col+1; x >= col; x--) + +#define PREDICTOR (c ? (buf[c][y-1][x] + buf[c][y][x+1]) / 2 \ +: (buf[c][y-1][x+1] + 2*buf[c][y-1][x] + buf[c][y][x+1]) / 4) + +void CLASS kodak_radc_load_raw() +{ + int row, col, tree, nreps, rep, step, i, c, s, r, x, y, val; + short last[3] = { 16,16,16 }, mul[3], buf[3][3][386]; + + init_decoder(); + getbits(-1); + for (i=0; i < sizeof(buf)/sizeof(short); i++) + buf[0][0][i] = 2048; + for (row=0; row < height; row+=4) { + FORC3 mul[c] = getbits(6); + FORC3 { + val = ((0x1000000/last[c] + 0x7ff) >> 12) * mul[c]; + s = val > 65564 ? 10:12; + x = ~(-1 << (s-1)); + val <<= 12-s; + for (i=0; i < sizeof(buf[0])/sizeof(short); i++) + buf[c][0][i] = (buf[c][0][i] * val + x) >> s; + last[c] = mul[c]; + for (r=0; r <= !c; r++) { + buf[c][1][width/2] = buf[c][2][width/2] = mul[c] << 7; + for (tree=1, col=width/2; col > 0; ) { + if ((tree = radc_token(tree))) { + col -= 2; + if (tree == 8) + FORYX buf[c][y][x] = radc_token(tree+10) * mul[c]; + else + FORYX buf[c][y][x] = radc_token(tree+10) * 16 + PREDICTOR; + } else + do { + nreps = (col > 2) ? radc_token(9) + 1 : 1; + for (rep=0; rep < 8 && rep < nreps && col > 0; rep++) { + col -= 2; + FORYX buf[c][y][x] = PREDICTOR; + if (rep & 1) { + step = radc_token(10) << 4; + FORYX buf[c][y][x] += step; + } + } + } while (nreps == 9); + } + for (y=0; y < 2; y++) + for (x=0; x < width/2; x++) { + val = (buf[c][y+1][x] << 4) / mul[c]; + if (val < 0) val = 0; + if (c) BAYER(row+y*2+c-1,x*2+2-c) = val; + else BAYER(row+r*2+y,x*2+y) = val; + } + memcpy (buf[c][0]+!c, buf[c][2], sizeof buf[c][0]-2*!c); + } + } + for (y=row; y < row+4; y++) + for (x=0; x < width; x++) + if ((x+y) & 1) { + r = x ? x-1 : x+1; + s = x+1 < width ? x+1 : x-1; + val = (BAYER(y,x)-2048)*2 + (BAYER(y,r)+BAYER(y,s))/2; + if (val < 0) val = 0; + BAYER(y,x) = val; + } + } + maximum = 0xfff; + use_gamma = 0; +} + +#undef FORYX +#undef PREDICTOR + +#ifdef NO_JPEG +void CLASS kodak_jpeg_load_raw() {} +#else + +METHODDEF(boolean) +fill_input_buffer (j_decompress_ptr cinfo) +{ +#ifndef LIBRAW_NOTHREADS +#define jpeg_buffer tls->jpeg_buffer +#else + static uchar jpeg_buffer[4096]; +#endif + size_t nbytes; + + nbytes = fread (jpeg_buffer, 1, 4096, ifp); + swab (jpeg_buffer, jpeg_buffer, nbytes); + cinfo->src->next_input_byte = jpeg_buffer; + cinfo->src->bytes_in_buffer = nbytes; + return TRUE; +#ifndef LIBRAW_NOTHREADS +#undef jpeg_buffer +#endif +} + +void CLASS kodak_jpeg_load_raw() +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPARRAY buf; + JSAMPLE (*pixel)[3]; + int row, col; + + cinfo.err = jpeg_std_error (&jerr); + jpeg_create_decompress (&cinfo); + jpeg_stdio_src (&cinfo, ifp); + cinfo.src->fill_input_buffer = fill_input_buffer; + jpeg_read_header (&cinfo, TRUE); + jpeg_start_decompress (&cinfo); + if ((cinfo.output_width != width ) || + (cinfo.output_height*2 != height ) || + (cinfo.output_components != 3 )) { +#ifdef DCRAW_VERBOSE + fprintf (stderr,_("%s: incorrect JPEG dimensions\n"), ifname); +#endif + jpeg_destroy_decompress (&cinfo); +#ifdef LIBRAW_LIBRARY_BUILD + throw LIBRAW_EXCEPTION_DECODE_JPEG; +#else + longjmp (failure, 3); +#endif + } + buf = (*cinfo.mem->alloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, width*3, 1); + + while (cinfo.output_scanline < cinfo.output_height) { + row = cinfo.output_scanline * 2; + jpeg_read_scanlines (&cinfo, buf, 1); + pixel = (JSAMPLE (*)[3]) buf[0]; + for (col=0; col < width; col+=2) { + BAYER(row+0,col+0) = pixel[col+0][1] << 1; + BAYER(row+1,col+1) = pixel[col+1][1] << 1; + BAYER(row+0,col+1) = pixel[col][0] + pixel[col+1][0]; + BAYER(row+1,col+0) = pixel[col][2] + pixel[col+1][2]; + } + } + jpeg_finish_decompress (&cinfo); + jpeg_destroy_decompress (&cinfo); + maximum = 0xff << 1; +} +#endif + +void CLASS kodak_dc120_load_raw() +{ + static const int mul[4] = { 162, 192, 187, 92 }; + static const int add[4] = { 0, 636, 424, 212 }; + uchar pixel[848]; + int row, shift, col; + + for (row=0; row < height; row++) { + if (fread (pixel, 1, 848, ifp) < 848) derror(); + shift = row * mul[row & 3] + add[row & 3]; + for (col=0; col < width; col++) + BAYER(row,col) = (ushort) pixel[(col + shift) % 848]; + } + maximum = 0xff; +} + +void CLASS eight_bit_load_raw() +{ + uchar *pixel; + unsigned row, col, val, lblack=0; + + pixel = (uchar *) calloc (raw_width, sizeof *pixel); + merror (pixel, "eight_bit_load_raw()"); +#ifndef LIBRAW_LIBRARY_BUILD + fseek (ifp, top_margin*raw_width, SEEK_CUR); + for (row=0; row < height; row++) { + if (fread (pixel, 1, raw_width, ifp) < raw_width) derror(); + for (col=0; col < raw_width; col++) { + val = curve[pixel[col]]; + if ((unsigned) (col-left_margin) < width) + BAYER(row,col-left_margin) = val; + else lblack += val; + } + } +#else + for (row=0; row < raw_height; row++) { + if (fread (pixel, 1, raw_width, ifp) < raw_width) derror(); + for (col=0; col < raw_width; col++) { + if(filtering_mode & LIBRAW_FILTERING_NORAWCURVE) + { + val = pixel[col]; + if(val>maximum) maximum = val; + } + else + val = curve[pixel[col]]; + if((unsigned) (row-top_margin)< height) + { + if ((unsigned) (col-left_margin) < width) + BAYER(row,col-left_margin) = val; + else + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) *dfp = val; + lblack += val; + } + } + else // top/bottom margins + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) *dfp = val; + } + } + } +#endif + + free (pixel); + if (raw_width > width+1) + black = lblack / ((raw_width - width) * height); + if (!strncmp(model,"DC2",3)) + black = 0; +#ifdef LIBRAW_LIBRARY_BUILD + if(!(filtering_mode & LIBRAW_FILTERING_NORAWCURVE)) +#endif + maximum = curve[0xff]; +} + +void CLASS kodak_yrgb_load_raw() +{ + uchar *pixel; + int row, col, y, cb, cr, rgb[3], c; + + pixel = (uchar *) calloc (raw_width, 3*sizeof *pixel); + merror (pixel, "kodak_yrgb_load_raw()"); + for (row=0; row < height; row++) { + if (~row & 1) + if (fread (pixel, raw_width, 3, ifp) < 3) derror(); + for (col=0; col < raw_width; col++) { + y = pixel[width*2*(row & 1) + col]; + cb = pixel[width + (col & -2)] - 128; + cr = pixel[width + (col & -2)+1] - 128; + rgb[1] = y-((cb + cr + 2) >> 2); + rgb[2] = rgb[1] + cb; + rgb[0] = rgb[1] + cr; + FORC3 image[row*width+col][c] = LIM(rgb[c],0,255); + } + } + free (pixel); + use_gamma = 0; +} + +void CLASS kodak_262_load_raw() +{ + static const uchar kodak_tree[2][26] = + { { 0,1,5,1,1,2,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9 }, + { 0,3,1,1,1,1,1,2,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9 } }; + struct decode *decode[2]; + uchar *pixel; + int *strip, ns, i, row, col, chess, pi=0, pi1, pi2, pred, val; + + init_decoder(); + for (i=0; i < 2; i++) { + decode[i] = free_decode; + make_decoder (kodak_tree[i], 0); + } + ns = (raw_height+63) >> 5; + pixel = (uchar *) malloc (raw_width*32 + ns*4); + merror (pixel, "kodak_262_load_raw()"); + strip = (int *) (pixel + raw_width*32); + order = 0x4d4d; + for (i=0; i < ns; i++) + strip[i] = get4(); + for (row=0; row < raw_height; row++) { + if ((row & 31) == 0) { + fseek (ifp, strip[row >> 5], SEEK_SET); + getbits(-1); + pi = 0; + } + for (col=0; col < raw_width; col++) { + chess = (row + col) & 1; + pi1 = chess ? pi-2 : pi-raw_width-1; + pi2 = chess ? pi-2*raw_width : pi-raw_width+1; + if (col <= chess) pi1 = -1; + if (pi1 < 0) pi1 = pi2; + if (pi2 < 0) pi2 = pi1; + if (pi1 < 0 && col > 1) pi1 = pi2 = pi-2; + pred = (pi1 < 0) ? 0 : (pixel[pi1] + pixel[pi2]) >> 1; + pixel[pi] = val = pred + ljpeg_diff (decode[chess]); + if (val >> 8) derror(); +#ifdef LIBRAW_LIBRARY_BUILD + if(filtering_mode & LIBRAW_FILTERING_NORAWCURVE) + val = pixel[pi++]; + else + val = curve[pixel[pi++]]; +#else + val = curve[pixel[pi++]]; +#endif + if ((unsigned) (col-left_margin) < width) + BAYER(row,col-left_margin) = val; + else +#ifndef LIBRAW_LIBRARY_BUILD + black += val; +#else + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) *dfp = val; + black += val; + } +#endif + } + } + free (pixel); + if (raw_width > width) + black /= (raw_width - width) * height; +} + +int CLASS kodak_65000_decode (short *out, int bsize) +{ + uchar c, blen[768]; + ushort raw[6]; + INT64 bitbuf=0; + int save, bits=0, i, j, len, diff; + + save = ftell(ifp); + bsize = (bsize + 3) & -4; + for (i=0; i < bsize; i+=2) { + c = fgetc(ifp); + if ((blen[i ] = c & 15) > 12 || + (blen[i+1] = c >> 4) > 12 ) { + fseek (ifp, save, SEEK_SET); + for (i=0; i < bsize; i+=8) { + read_shorts (raw, 6); + out[i ] = raw[0] >> 12 << 8 | raw[2] >> 12 << 4 | raw[4] >> 12; + out[i+1] = raw[1] >> 12 << 8 | raw[3] >> 12 << 4 | raw[5] >> 12; + for (j=0; j < 6; j++) + out[i+2+j] = raw[j] & 0xfff; + } + return 1; + } + } + if ((bsize & 7) == 4) { + bitbuf = fgetc(ifp) << 8; + bitbuf += fgetc(ifp); + bits = 16; + } + for (i=0; i < bsize; i++) { + len = blen[i]; + if (bits < len) { + for (j=0; j < 32; j+=8) + bitbuf += (INT64) fgetc(ifp) << (bits+(j^8)); + bits += 32; + } + diff = bitbuf & (0xffff >> (16-len)); + bitbuf >>= len; + bits -= len; + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + out[i] = diff; + } + return 0; +} + +void CLASS kodak_65000_load_raw() +{ + short buf[256]; + int row, col, len, pred[2], ret, i; + + for (row=0; row < height; row++) + for (col=0; col < width; col+=256) { + pred[0] = pred[1] = 0; + len = MIN (256, width-col); + ret = kodak_65000_decode (buf, len); + for (i=0; i < len; i++) +#ifndef LIBRAW_LIBRARY_BUILD + if ((BAYER(row,col+i) = curve[ret ? buf[i] : + (pred[i & 1] += buf[i])]) >> 12) derror(); +#else + { + ushort val = ret ? buf[i] : (pred[i & 1] += buf[i]); + if(!(filtering_mode & LIBRAW_FILTERING_NORAWCURVE)) + val = curve[val]; + BAYER(row,col+i)=val; + if(curve[val]>>12) derror(); + } +#endif + } +} + +void CLASS kodak_ycbcr_load_raw() +{ + short buf[384], *bp; + int row, col, len, c, i, j, k, y[2][2], cb, cr, rgb[3]; + ushort *ip; + + for (row=0; row < height; row+=2) + for (col=0; col < width; col+=128) { + len = MIN (128, width-col); + kodak_65000_decode (buf, len*3); + y[0][1] = y[1][1] = cb = cr = 0; + for (bp=buf, i=0; i < len; i+=2, bp+=2) { + cb += bp[4]; + cr += bp[5]; + rgb[1] = -((cb + cr + 2) >> 2); + rgb[2] = rgb[1] + cb; + rgb[0] = rgb[1] + cr; + for (j=0; j < 2; j++) + for (k=0; k < 2; k++) { + if ((y[j][k] = y[j][k^1] + *bp++) >> 10) derror(); + ip = image[(row+j)*width + col+i+k]; +#ifndef LIBRAW_LIBRARY_BUILD + FORC3 ip[c] = curve[LIM(y[j][k]+rgb[c], 0, 0xfff)]; +#else + if(!(filtering_mode & LIBRAW_FILTERING_NORAWCURVE)) + FORC3 ip[c] = curve[LIM(y[j][k]+rgb[c], 0, 0xfff)]; + else + FORC3 ip[c] = y[j][k]+rgb[c];; +#endif + } + } + } +} + +void CLASS kodak_rgb_load_raw() +{ + short buf[768], *bp; + int row, col, len, c, i, rgb[3]; + ushort *ip=image[0]; + + for (row=0; row < height; row++) + for (col=0; col < width; col+=256) { + len = MIN (256, width-col); + kodak_65000_decode (buf, len*3); + memset (rgb, 0, sizeof rgb); + for (bp=buf, i=0; i < len; i++, ip+=4) + FORC3 if ((ip[c] = rgb[c] += *bp++) >> 12) derror(); + } +} + +void CLASS kodak_thumb_load_raw() +{ + int row, col; + colors = thumb_misc >> 5; + for (row=0; row < height; row++) + for (col=0; col < width; col++) + read_shorts (image[row*width+col], colors); + maximum = (1 << (thumb_misc & 31)) - 1; +} + +void CLASS sony_decrypt (unsigned *data, int len, int start, int key) +{ +#ifndef LIBRAW_NOTHREADS +#define pad tls->sony_decrypt.pad +#define p tls->sony_decrypt.p +#else + static unsigned pad[128], p; +#endif + + if (start) { + for (p=0; p < 4; p++) + pad[p] = key = key * 48828125 + 1; + pad[3] = pad[3] << 1 | (pad[0]^pad[2]) >> 31; + for (p=4; p < 127; p++) + pad[p] = (pad[p-4]^pad[p-2]) << 1 | (pad[p-3]^pad[p-1]) >> 31; + for (p=0; p < 127; p++) + pad[p] = htonl(pad[p]); + } + while (len--) + *data++ ^= pad[p++ & 127] = pad[(p+1) & 127] ^ pad[(p+65) & 127]; +#ifndef LIBRAW_NOTHREADS +#undef pad +#undef p +#endif +} + +void CLASS sony_load_raw() +{ + uchar head[40]; + ushort *pixel; + unsigned i, key, row, col; + + fseek (ifp, 200896, SEEK_SET); + fseek (ifp, (unsigned) fgetc(ifp)*4 - 1, SEEK_CUR); + order = 0x4d4d; + key = get4(); + fseek (ifp, 164600, SEEK_SET); + fread (head, 1, 40, ifp); + sony_decrypt ((unsigned int *) head, 10, 1, key); + for (i=26; i-- > 22; ) + key = key << 8 | head[i]; + fseek (ifp, data_offset, SEEK_SET); + pixel = (ushort *) calloc (raw_width, sizeof *pixel); + merror (pixel, "sony_load_raw()"); + for (row=0; row < height; row++) { + if (fread (pixel, 2, raw_width, ifp) < raw_width) derror(); + sony_decrypt ((unsigned int *) pixel, raw_width/2, !row, key); +#ifdef LIBRAW_LIBRARY_BUILD + for (col=0; col < left_margin; col++) + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) *dfp = ntohs(pixel[col]); + } + for (col=left_margin+width; col < raw_width; col++) + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) *dfp = ntohs(pixel[col]); + } +#endif + for (col=9; col < left_margin; col++) + black += ntohs(pixel[col]); + for (col=0; col < width; col++) + if ((BAYER(row,col) = ntohs(pixel[col+left_margin])) >> 14) + derror(); + } + free (pixel); + if (left_margin > 9) + black /= (left_margin-9) * height; + maximum = 0x3ff0; +} + +void CLASS sony_arw_load_raw() +{ + int col, row, len, diff, sum=0; + + getbits(-1); + for (col = raw_width; col--; ) + for (row=0; row < raw_height+1; row+=2) { + if (row == raw_height) row = 1; + len = 4 - getbits(2); + if (len == 3 && getbits(1)) len = 0; + if (len == 4) + while (len < 17 && !getbits(1)) len++; + diff = getbits(len); + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + if ((sum += diff) >> 12) derror(); + if (row < height) BAYER(row,col) = sum; +#ifdef LIBRAW_LIBRARY_BUILD + else + { + ushort *dfp = get_masked_pointer(row,col); + if(dfp) *dfp = sum; + } +#endif + } +} + +void CLASS sony_arw2_load_raw() +{ + uchar *data, *dp; + ushort pix[16]; + int row, col, val, max, min, imax, imin, sh, bit, i; + + data = (uchar *) malloc (raw_width*tiff_bps >> 3); + merror (data, "sony_arw2_load_raw()"); + for (row=0; row < height; row++) { + fread (data, 1, raw_width*tiff_bps >> 3, ifp); + if (tiff_bps == 8) { + for (dp=data, col=0; col < width-30; dp+=16) { + max = 0x7ff & (val = sget4(dp)); + min = 0x7ff & val >> 11; + imax = 0x0f & val >> 22; + imin = 0x0f & val >> 26; + for (sh=0; sh < 4 && 0x80 << sh <= max-min; sh++); + for (bit=30, i=0; i < 16; i++) + if (i == imax) pix[i] = max; + else if (i == imin) pix[i] = min; + else { + pix[i] = ((sget2(dp+(bit >> 3)) >> (bit & 7) & 0x7f) << sh) + min; + if (pix[i] > 0x7ff) pix[i] = 0x7ff; + bit += 7; + } + for (i=0; i < 16; i++, col+=2) +#ifndef LIBRAW_LIBRARY_BUILD + BAYER(row,col) = curve[pix[i] << 1] >> 1; +#else + { + ushort val = pix[i]; + if(!(filtering_mode & LIBRAW_FILTERING_NORAWCURVE)) + val = curve[val<<1]>>1; + BAYER(row,col)=val; + } +#endif + col -= col & 1 ? 1:31; + } + } else if (tiff_bps == 12) + for (dp=data, col=0; col < width; dp+=3, col+=2) { + BAYER(row,col) = ((dp[1] << 8 | dp[0]) & 0xfff) << 1; + BAYER(row,col+1) = (dp[2] << 4 | dp[1] >> 4) << 1; + } + } + free (data); +} + +#define HOLE(row) ((holes >> (((row) - raw_height) & 7)) & 1) + +/* Kudos to Rich Taylor for figuring out SMaL's compression algorithm. */ +void CLASS smal_decode_segment (unsigned seg[2][2], int holes) +{ + uchar hist[3][13] = { + { 7, 7, 0, 0, 63, 55, 47, 39, 31, 23, 15, 7, 0 }, + { 7, 7, 0, 0, 63, 55, 47, 39, 31, 23, 15, 7, 0 }, + { 3, 3, 0, 0, 63, 47, 31, 15, 0 } }; + int low, high=0xff, carry=0, nbits=8; + int s, count, bin, next, i, sym[3]; + uchar diff, pred[]={0,0}; + ushort data=0, range=0; + unsigned pix, row, col; + + fseek (ifp, seg[0][1]+1, SEEK_SET); + getbits(-1); + for (pix=seg[0][0]; pix < seg[1][0]; pix++) { + for (s=0; s < 3; s++) { + data = data << nbits | getbits(nbits); + if (carry < 0) + carry = (nbits += carry+1) < 1 ? nbits-1 : 0; + while (--nbits >= 0) + if ((data >> nbits & 0xff) == 0xff) break; + if (nbits > 0) + data = ((data & ((1 << (nbits-1)) - 1)) << 1) | + ((data + (((data & (1 << (nbits-1)))) << 1)) & (-1 << nbits)); + if (nbits >= 0) { + data += getbits(1); + carry = nbits - 8; + } + count = ((((data-range+1) & 0xffff) << 2) - 1) / (high >> 4); + for (bin=0; hist[s][bin+5] > count; bin++); + low = hist[s][bin+5] * (high >> 4) >> 2; + if (bin) high = hist[s][bin+4] * (high >> 4) >> 2; + high -= low; + for (nbits=0; high << nbits < 128; nbits++); + range = (range+low) << nbits; + high <<= nbits; + next = hist[s][1]; + if (++hist[s][2] > hist[s][3]) { + next = (next+1) & hist[s][0]; + hist[s][3] = (hist[s][next+4] - hist[s][next+5]) >> 2; + hist[s][2] = 1; + } + if (hist[s][hist[s][1]+4] - hist[s][hist[s][1]+5] > 1) { + if (bin < hist[s][1]) + for (i=bin; i < hist[s][1]; i++) hist[s][i+5]--; + else if (next <= bin) + for (i=hist[s][1]; i < bin; i++) hist[s][i+5]++; + } + hist[s][1] = next; + sym[s] = bin; + } + diff = sym[2] << 5 | sym[1] << 2 | (sym[0] & 3); + if (sym[0] & 4) + diff = diff ? -diff : 0x80; + if (ftell(ifp) + 12 >= seg[1][1]) + diff = 0; + pred[pix & 1] += diff; + row = pix / raw_width - top_margin; + col = pix % raw_width - left_margin; + if (row < height && col < width) + BAYER(row,col) = pred[pix & 1]; +#ifdef LIBRAW_LIBRARY_BUILD + else + { + ushort *dfp = get_masked_pointer(row+top_margin,col+left_margin); + if(dfp) *dfp = pred[pix &1]; + } +#endif + if (!(pix & 1) && HOLE(row)) pix += 2; + } + maximum = 0xff; +} + +void CLASS smal_v6_load_raw() +{ + unsigned seg[2][2]; + + fseek (ifp, 16, SEEK_SET); + seg[0][0] = 0; + seg[0][1] = get2(); + seg[1][0] = raw_width * raw_height; + seg[1][1] = INT_MAX; + smal_decode_segment (seg, 0); + use_gamma = 0; +} + +int CLASS median4 (int *p) +{ + int min, max, sum, i; + + min = max = sum = p[0]; + for (i=1; i < 4; i++) { + sum += p[i]; + if (min > p[i]) min = p[i]; + if (max < p[i]) max = p[i]; + } + return (sum - min - max) >> 1; +} + +void CLASS fill_holes (int holes) +{ + int row, col, val[4]; + + for (row=2; row < height-2; row++) { + if (!HOLE(row)) continue; + for (col=1; col < width-1; col+=4) { + val[0] = BAYER(row-1,col-1); + val[1] = BAYER(row-1,col+1); + val[2] = BAYER(row+1,col-1); + val[3] = BAYER(row+1,col+1); + BAYER(row,col) = median4(val); + } + for (col=2; col < width-2; col+=4) + if (HOLE(row-2) || HOLE(row+2)) + BAYER(row,col) = (BAYER(row,col-2) + BAYER(row,col+2)) >> 1; + else { + val[0] = BAYER(row,col-2); + val[1] = BAYER(row,col+2); + val[2] = BAYER(row-2,col); + val[3] = BAYER(row+2,col); + BAYER(row,col) = median4(val); + } + } +} + +void CLASS smal_v9_load_raw() +{ + unsigned seg[256][2], offset, nseg, holes, i; + + fseek (ifp, 67, SEEK_SET); + offset = get4(); + nseg = fgetc(ifp); + fseek (ifp, offset, SEEK_SET); + for (i=0; i < nseg*2; i++) + seg[0][i] = get4() + data_offset*(i & 1); + fseek (ifp, 78, SEEK_SET); + holes = fgetc(ifp); + fseek (ifp, 88, SEEK_SET); + seg[nseg][0] = raw_height * raw_width; + seg[nseg][1] = get4() + data_offset; + for (i=0; i < nseg; i++) + smal_decode_segment (seg+i, holes); + if (holes) fill_holes (holes); +} +#line 4122 "dcraw/dcraw.c" + +void CLASS pseudoinverse (double (*in)[3], double (*out)[3], int size) +{ + double work[3][6], num; + int i, j, k; + + for (i=0; i < 3; i++) { + for (j=0; j < 6; j++) + work[i][j] = j == i+3; + for (j=0; j < 3; j++) + for (k=0; k < size; k++) + work[i][j] += in[k][i] * in[k][j]; + } + for (i=0; i < 3; i++) { + num = work[i][i]; + for (j=0; j < 6; j++) + work[i][j] /= num; + for (k=0; k < 3; k++) { + if (k==i) continue; + num = work[k][i]; + for (j=0; j < 6; j++) + work[k][j] -= work[i][j] * num; + } + } + for (i=0; i < size; i++) + for (j=0; j < 3; j++) + for (out[i][j]=k=0; k < 3; k++) + out[i][j] += work[j][k+3] * in[i][k]; +} + +void CLASS cam_xyz_coeff (double cam_xyz[4][3]) +{ + double cam_rgb[4][3], inverse[4][3], num; + int i, j, k; + + for (i=0; i < colors; i++) /* Multiply out XYZ colorspace */ + for (j=0; j < 3; j++) + for (cam_rgb[i][j] = k=0; k < 3; k++) + cam_rgb[i][j] += cam_xyz[i][k] * xyz_rgb[k][j]; + + for (i=0; i < colors; i++) { /* Normalize cam_rgb so that */ + for (num=j=0; j < 3; j++) /* cam_rgb * (1,1,1) is (1,1,1,1) */ + num += cam_rgb[i][j]; + for (j=0; j < 3; j++) + cam_rgb[i][j] /= num; + pre_mul[i] = 1 / num; + } + pseudoinverse (cam_rgb, inverse, colors); + for (raw_color = i=0; i < 3; i++) + for (j=0; j < colors; j++) + rgb_cam[i][j] = inverse[j][i]; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; + color_flags.rgb_cam_state = LIBRAW_COLORSTATE_CONST; +#endif +} + +#ifdef COLORCHECK +void CLASS colorcheck() +{ +#define NSQ 24 +// Coordinates of the GretagMacbeth ColorChecker squares +// width, height, 1st_column, 1st_row + static const int cut[NSQ][4] = { + { 241, 231, 234, 274 }, + { 251, 235, 534, 274 }, + { 255, 239, 838, 272 }, + { 255, 240, 1146, 274 }, + { 251, 237, 1452, 278 }, + { 243, 238, 1758, 288 }, + { 253, 253, 218, 558 }, + { 255, 249, 524, 562 }, + { 261, 253, 830, 562 }, + { 260, 255, 1144, 564 }, + { 261, 255, 1450, 566 }, + { 247, 247, 1764, 576 }, + { 255, 251, 212, 862 }, + { 259, 259, 518, 862 }, + { 263, 261, 826, 864 }, + { 265, 263, 1138, 866 }, + { 265, 257, 1450, 872 }, + { 257, 255, 1762, 874 }, + { 257, 253, 212, 1164 }, + { 262, 251, 516, 1172 }, + { 263, 257, 826, 1172 }, + { 263, 255, 1136, 1176 }, + { 255, 252, 1452, 1182 }, + { 257, 253, 1760, 1180 } }; +// ColorChecker Chart under 6500-kelvin illumination + static const double gmb_xyY[NSQ][3] = { + { 0.400, 0.350, 10.1 }, // Dark Skin + { 0.377, 0.345, 35.8 }, // Light Skin + { 0.247, 0.251, 19.3 }, // Blue Sky + { 0.337, 0.422, 13.3 }, // Foliage + { 0.265, 0.240, 24.3 }, // Blue Flower + { 0.261, 0.343, 43.1 }, // Bluish Green + { 0.506, 0.407, 30.1 }, // Orange + { 0.211, 0.175, 12.0 }, // Purplish Blue + { 0.453, 0.306, 19.8 }, // Moderate Red + { 0.285, 0.202, 6.6 }, // Purple + { 0.380, 0.489, 44.3 }, // Yellow Green + { 0.473, 0.438, 43.1 }, // Orange Yellow + { 0.187, 0.129, 6.1 }, // Blue + { 0.305, 0.478, 23.4 }, // Green + { 0.539, 0.313, 12.0 }, // Red + { 0.448, 0.470, 59.1 }, // Yellow + { 0.364, 0.233, 19.8 }, // Magenta + { 0.196, 0.252, 19.8 }, // Cyan + { 0.310, 0.316, 90.0 }, // White + { 0.310, 0.316, 59.1 }, // Neutral 8 + { 0.310, 0.316, 36.2 }, // Neutral 6.5 + { 0.310, 0.316, 19.8 }, // Neutral 5 + { 0.310, 0.316, 9.0 }, // Neutral 3.5 + { 0.310, 0.316, 3.1 } }; // Black + double gmb_cam[NSQ][4], gmb_xyz[NSQ][3]; + double inverse[NSQ][3], cam_xyz[4][3], num; + int c, i, j, k, sq, row, col, count[4]; + + memset (gmb_cam, 0, sizeof gmb_cam); + for (sq=0; sq < NSQ; sq++) { + FORCC count[c] = 0; + for (row=cut[sq][3]; row < cut[sq][3]+cut[sq][1]; row++) + for (col=cut[sq][2]; col < cut[sq][2]+cut[sq][0]; col++) { + c = FC(row,col); + if (c >= colors) c -= 2; + gmb_cam[sq][c] += BAYER(row,col); + count[c]++; + } + FORCC gmb_cam[sq][c] = gmb_cam[sq][c]/count[c] - black; + gmb_xyz[sq][0] = gmb_xyY[sq][2] * gmb_xyY[sq][0] / gmb_xyY[sq][1]; + gmb_xyz[sq][1] = gmb_xyY[sq][2]; + gmb_xyz[sq][2] = gmb_xyY[sq][2] * + (1 - gmb_xyY[sq][0] - gmb_xyY[sq][1]) / gmb_xyY[sq][1]; + } + pseudoinverse (gmb_xyz, inverse, NSQ); + for (i=0; i < colors; i++) + for (j=0; j < 3; j++) + for (cam_xyz[i][j] = k=0; k < NSQ; k++) + cam_xyz[i][j] += gmb_cam[k][i] * inverse[k][j]; + cam_xyz_coeff (cam_xyz); +#ifdef DCRAW_VERBOSE + if (verbose) { + printf (" { \"%s %s\", %d,\n\t{", make, model, black); + num = 10000 / (cam_xyz[1][0] + cam_xyz[1][1] + cam_xyz[1][2]); + FORCC for (j=0; j < 3; j++) + printf ("%c%d", (c | j) ? ',':' ', (int) (cam_xyz[c][j] * num + 0.5)); + puts (" } },"); + } +#endif +#undef NSQ +} +#endif + +void CLASS hat_transform (float *temp, float *base, int st, int size, int sc) +{ + int i; + for (i=0; i < sc; i++) + temp[i] = 2*base[st*i] + base[st*(sc-i)] + base[st*(i+sc)]; + for (; i+sc < size; i++) + temp[i] = 2*base[st*i] + base[st*(i-sc)] + base[st*(i+sc)]; + for (; i < size; i++) + temp[i] = 2*base[st*i] + base[st*(i-sc)] + base[st*(2*size-2-(i+sc))]; +} + +#ifndef _OPENMP +void CLASS wavelet_denoise() +{ + float *fimg=0, *temp, thold, mul[2], avg, diff; + int scale=1, size, lev, hpass, lpass, row, col, nc, c, i, wlast; + ushort *window[4]; + static const float noise[] = + { 0.8002,0.2735,0.1202,0.0585,0.0291,0.0152,0.0080,0.0044 }; + +#ifdef DCRAW_VERBOSE + if (verbose) fprintf (stderr,_("Wavelet denoising...\n")); +#endif + + while (maximum << scale < 0x10000) scale++; + maximum <<= --scale; + black <<= scale; + if ((size = iheight*iwidth) < 0x15550000) + fimg = (float *) malloc ((size*3 + iheight + iwidth) * sizeof *fimg); + merror (fimg, "wavelet_denoise()"); + temp = fimg + size*3; + if ((nc = colors) == 3 && filters) nc++; + FORC(nc) { /* denoise R,G1,B,G3 individually */ + for (i=0; i < size; i++) + fimg[i] = 256 * sqrt((double)(image[i][c] << scale)); + for (hpass=lev=0; lev < 5; lev++) { + lpass = size*((lev & 1)+1); + for (row=0; row < iheight; row++) { + hat_transform (temp, fimg+hpass+row*iwidth, 1, iwidth, 1 << lev); + for (col=0; col < iwidth; col++) + fimg[lpass + row*iwidth + col] = temp[col] * 0.25; + } + for (col=0; col < iwidth; col++) { + hat_transform (temp, fimg+lpass+col, iwidth, iheight, 1 << lev); + for (row=0; row < iheight; row++) + fimg[lpass + row*iwidth + col] = temp[row] * 0.25; + } + thold = threshold * noise[lev]; + for (i=0; i < size; i++) { + fimg[hpass+i] -= fimg[lpass+i]; + if (fimg[hpass+i] < -thold) fimg[hpass+i] += thold; + else if (fimg[hpass+i] > thold) fimg[hpass+i] -= thold; + else fimg[hpass+i] = 0; + if (hpass) fimg[i] += fimg[hpass+i]; + } + hpass = lpass; + } + for (i=0; i < size; i++) + image[i][c] = CLIP(SQR(fimg[i]+fimg[lpass+i])/0x10000); + } + if (filters && colors == 3) { /* pull G1 and G3 closer together */ + for (row=0; row < 2; row++) + mul[row] = 0.125 * pre_mul[FC(row+1,0) | 1] / pre_mul[FC(row,0) | 1]; + for (i=0; i < 4; i++) + window[i] = (ushort *) fimg + width*i; + for (wlast=-1, row=1; row < height-1; row++) { + while (wlast < row+1) { + for (wlast++, i=0; i < 4; i++) + window[(i+3) & 3] = window[i]; + for (col = FC(wlast,1) & 1; col < width; col+=2) + window[2][col] = BAYER(wlast,col); + } + thold = threshold/512; + for (col = (FC(row,0) & 1)+1; col < width-1; col+=2) { + avg = ( window[0][col-1] + window[0][col+1] + + window[2][col-1] + window[2][col+1] - black*4 ) + * mul[row & 1] + (window[1][col] - black) * 0.5 + black; + avg = avg < 0 ? 0 : sqrt(avg); + diff = sqrt((double)(BAYER(row,col))) - avg; + if (diff < -thold) diff += thold; + else if (diff > thold) diff -= thold; + else diff = 0; + BAYER(row,col) = CLIP(SQR(avg+diff) + 0.5); + } + } + } + free (fimg); +} +#else +void CLASS wavelet_denoise() +{ + float *fimg=0, *temp, thold, mul[2], avg, diff; + int scale=1, size, lev, hpass, lpass, row, col, nc, c, i, wlast; + ushort *window[4]; + static const float noise[] = + { 0.8002,0.2735,0.1202,0.0585,0.0291,0.0152,0.0080,0.0044 }; + +#ifdef DCRAW_VERBOSE + if (verbose) fprintf (stderr,_("Wavelet denoising...\n")); +#endif + + while (maximum << scale < 0x10000) scale++; + maximum <<= --scale; + black <<= scale; + if ((size = iheight*iwidth) < 0x15550000) + fimg = (float *) malloc ((size*3 + iheight + iwidth) * sizeof *fimg); + merror (fimg, "wavelet_denoise()"); + temp = fimg + size*3; + if ((nc = colors) == 3 && filters) nc++; +#ifdef LIBRAW_LIBRARY_BUILD +#pragma omp parallel default(shared) private(i,col,row,thold,lev,lpass,hpass,temp) firstprivate(c,scale,size) +#endif + { + temp = (float*)malloc( (iheight + iwidth) * sizeof *fimg); + FORC(nc) { /* denoise R,G1,B,G3 individually */ +#ifdef LIBRAW_LIBRARY_BUILD +#pragma omp for +#endif + for (i=0; i < size; i++) + fimg[i] = 256 * sqrt((double)(image[i][c] << scale)); + for (hpass=lev=0; lev < 5; lev++) { + lpass = size*((lev & 1)+1); +#ifdef LIBRAW_LIBRARY_BUILD +#pragma omp for +#endif + for (row=0; row < iheight; row++) { + hat_transform (temp, fimg+hpass+row*iwidth, 1, iwidth, 1 << lev); + for (col=0; col < iwidth; col++) + fimg[lpass + row*iwidth + col] = temp[col] * 0.25; + } +#ifdef LIBRAW_LIBRARY_BUILD +#pragma omp for +#endif + for (col=0; col < iwidth; col++) { + hat_transform (temp, fimg+lpass+col, iwidth, iheight, 1 << lev); + for (row=0; row < iheight; row++) + fimg[lpass + row*iwidth + col] = temp[row] * 0.25; + } + thold = threshold * noise[lev]; +#ifdef LIBRAW_LIBRARY_BUILD +#pragma omp for +#endif + for (i=0; i < size; i++) { + fimg[hpass+i] -= fimg[lpass+i]; + if (fimg[hpass+i] < -thold) fimg[hpass+i] += thold; + else if (fimg[hpass+i] > thold) fimg[hpass+i] -= thold; + else fimg[hpass+i] = 0; + if (hpass) fimg[i] += fimg[hpass+i]; + } + hpass = lpass; + } +#ifdef LIBRAW_LIBRARY_BUILD +#pragma omp for +#endif + for (i=0; i < size; i++) + image[i][c] = CLIP(SQR(fimg[i]+fimg[lpass+i])/0x10000); + } + free(temp); + } /* end omp parallel */ +/* the following loops are hard to parallize, no idea yes, + * problem is wlast which is carrying dependency + * second part should be easyer, but did not yet get it right. + */ + if (filters && colors == 3) { /* pull G1 and G3 closer together */ + for (row=0; row < 2; row++) + mul[row] = 0.125 * pre_mul[FC(row+1,0) | 1] / pre_mul[FC(row,0) | 1]; + for (i=0; i < 4; i++) + window[i] = (ushort *) fimg + width*i; + for (wlast=-1, row=1; row < height-1; row++) { + while (wlast < row+1) { + for (wlast++, i=0; i < 4; i++) + window[(i+3) & 3] = window[i]; + for (col = FC(wlast,1) & 1; col < width; col+=2) + window[2][col] = BAYER(wlast,col); + } + thold = threshold/512; + for (col = (FC(row,0) & 1)+1; col < width-1; col+=2) { + avg = ( window[0][col-1] + window[0][col+1] + + window[2][col-1] + window[2][col+1] - black*4 ) + * mul[row & 1] + (window[1][col] - black) * 0.5 + black; + avg = avg < 0 ? 0 : sqrt(avg); + diff = sqrt(BAYER(row,col)) - avg; + if (diff < -thold) diff += thold; + else if (diff > thold) diff -= thold; + else diff = 0; + BAYER(row,col) = CLIP(SQR(avg+diff) + 0.5); + } + } + } + free (fimg); +} + +#endif + +void CLASS scale_colors() +{ + unsigned bottom, right, size, row, col, ur, uc, i, x, y, c, sum[8]; + int val, dark, sat; + double dsum[8], dmin, dmax; + float scale_mul[4], fr, fc; + ushort *img=0, *pix; + +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_SCALE_COLORS,0,2); +#endif + + if (user_mul[0]) + memcpy (pre_mul, user_mul, sizeof pre_mul); + if (use_auto_wb || (use_camera_wb && cam_mul[0] == -1)) { + memset (dsum, 0, sizeof dsum); + bottom = MIN (greybox[1]+greybox[3], height); + right = MIN (greybox[0]+greybox[2], width); + for (row=greybox[1]; row < bottom; row += 8) + for (col=greybox[0]; col < right; col += 8) { + memset (sum, 0, sizeof sum); + for (y=row; y < row+8 && y < bottom; y++) + for (x=col; x < col+8 && x < right; x++) + FORC4 { + if (filters) { + c = FC(y,x); + val = BAYER(y,x); + } else + val = image[y*width+x][c]; + if (val > maximum-25) goto skip_block; + if ((val -= black) < 0) val = 0; + sum[c] += val; + sum[c+4]++; + if (filters) break; + } + FORC(8) dsum[c] += sum[c]; +skip_block: ; + } + FORC4 if (dsum[c]) pre_mul[c] = dsum[c+4] / dsum[c]; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CALCULATED; +#endif + } + if (use_camera_wb && cam_mul[0] != -1) { + memset (sum, 0, sizeof sum); + for (row=0; row < 8; row++) + for (col=0; col < 8; col++) { + c = FC(row,col); + if ((val = white[row][col] - black) > 0) + sum[c] += val; + sum[c+4]++; + } + if (sum[0] && sum[1] && sum[2] && sum[3]) + { + FORC4 pre_mul[c] = (float) sum[c+4] / sum[c]; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CALCULATED; +#endif + } + else if (cam_mul[0] && cam_mul[2]) + { + memcpy (pre_mul, cam_mul, sizeof pre_mul); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state =color_flags.pre_mul_state; +#endif + } + else + { +#ifdef LIBRAW_LIBRARY_BUILD + imgdata.process_warnings |= LIBRAW_WARN_BAD_CAMERA_WB; +#endif +#ifdef DCRAW_VERBOSE + fprintf (stderr,_("%s: Cannot use camera white balance.\n"), ifname); +#endif + } + } + if (pre_mul[3] == 0) pre_mul[3] = colors < 4 ? pre_mul[1] : 1; + dark = black; + sat = maximum; + if (threshold) wavelet_denoise(); + maximum -= black; + for (dmin=DBL_MAX, dmax=c=0; c < 4; c++) { + if (dmin > pre_mul[c]) + dmin = pre_mul[c]; + if (dmax < pre_mul[c]) + dmax = pre_mul[c]; + } + if (!highlight) dmax = dmin; + FORC4 scale_mul[c] = (pre_mul[c] /= dmax) * 65535.0 / maximum; +#ifdef DCRAW_VERBOSE + if (verbose) { + fprintf (stderr, + _("Scaling with darkness %d, saturation %d, and\nmultipliers"), dark, sat); + FORC4 fprintf (stderr, " %f", pre_mul[c]); + fputc ('\n', stderr); + } +#endif + size = iheight*iwidth; + for (i=0; i < size*4; i++) { + val = image[0][i]; + if (!val) continue; + val -= black; + val *= scale_mul[i & 3]; + image[0][i] = CLIP(val); + } + if ((aber[0] != 1 || aber[2] != 1) && colors == 3) { +#ifdef DCRAW_VERBOSE + if (verbose) + fprintf (stderr,_("Correcting chromatic aberration...\n")); +#endif + for (c=0; c < 4; c+=2) { + if (aber[c] == 1) continue; + img = (ushort *) malloc (size * sizeof *img); + merror (img, "scale_colors()"); + for (i=0; i < size; i++) + img[i] = image[i][c]; + for (row=0; row < iheight; row++) { + ur = fr = (row - iheight*0.5) * aber[c] + iheight*0.5; + if (ur > iheight-2) continue; + fr -= ur; + for (col=0; col < iwidth; col++) { + uc = fc = (col - iwidth*0.5) * aber[c] + iwidth*0.5; + if (uc > iwidth-2) continue; + fc -= uc; + pix = img + ur*iwidth + uc; + image[row*iwidth+col][c] = + (pix[ 0]*(1-fc) + pix[ 1]*fc) * (1-fr) + + (pix[iwidth]*(1-fc) + pix[iwidth+1]*fc) * fr; + } + } + free(img); + } + } +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_SCALE_COLORS,1,2); +#endif +} + +void CLASS pre_interpolate() +{ + ushort (*img)[4]; + int row, col, c; + +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_PRE_INTERPOLATE,0,2); +#endif + if (shrink) { + if (half_size) { + height = iheight; + width = iwidth; + } else { + img = (ushort (*)[4]) calloc (height*width, sizeof *img); + merror (img, "pre_interpolate()"); + for (row=0; row < height; row++) + for (col=0; col < width; col++) { + c = fc(row,col); + img[row*width+col][c] = image[(row >> 1)*iwidth+(col >> 1)][c]; + } + free (image); + image = img; + shrink = 0; + } + } + if (filters && colors == 3) { + if ((mix_green = four_color_rgb)) colors++; + else { + for (row = FC(1,0) >> 1; row < height; row+=2) + for (col = FC(row,1) & 1; col < width; col+=2) + image[row*width+col][1] = image[row*width+col][3]; + filters &= ~((filters & 0x55555555) << 1); + } + } + if (half_size) filters = 0; +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_PRE_INTERPOLATE,1,2); +#endif +} + +void CLASS border_interpolate (int border) +{ + unsigned row, col, y, x, f, c, sum[8]; + + for (row=0; row < height; row++) + for (col=0; col < width; col++) { + if (col==border && row >= border && row < height-border) + col = width-border; + memset (sum, 0, sizeof sum); + for (y=row-1; y != row+2; y++) + for (x=col-1; x != col+2; x++) + if (y < height && x < width) { + f = fc(y,x); + sum[f] += image[y*width+x][f]; + sum[f+4]++; + } + f = fc(row,col); + FORCC if (c != f && sum[c+4]) + image[row*width+col][c] = sum[c] / sum[c+4]; + } +} + +void CLASS lin_interpolate() +{ + int code[16][16][32], *ip, sum[4]; + int c, i, x, y, row, col, shift, color; + ushort *pix; + +#ifdef DCRAW_VERBOSE + if (verbose) fprintf (stderr,_("Bilinear interpolation...\n")); +#endif + +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,0,3); +#endif + border_interpolate(1); + for (row=0; row < 16; row++) + for (col=0; col < 16; col++) { + ip = code[row][col]; + memset (sum, 0, sizeof sum); + for (y=-1; y <= 1; y++) + for (x=-1; x <= 1; x++) { + shift = (y==0) + (x==0); + if (shift == 2) continue; + color = fc(row+y,col+x); + *ip++ = (width*y + x)*4 + color; + *ip++ = shift; + *ip++ = color; + sum[color] += 1 << shift; + } + FORCC + if (c != fc(row,col)) { + *ip++ = c; + *ip++ = 256 / sum[c]; + } + } +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,1,3); +#endif + for (row=1; row < height-1; row++) + for (col=1; col < width-1; col++) { + pix = image[row*width+col]; + ip = code[row & 15][col & 15]; + memset (sum, 0, sizeof sum); + for (i=8; i--; ip+=3) + sum[ip[2]] += pix[ip[0]] << ip[1]; + for (i=colors; --i; ip+=2) + pix[ip[0]] = sum[ip[0]] * ip[1] >> 8; + } +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,2,3); +#endif +} + +/* + This algorithm is officially called: + + "Interpolation using a Threshold-based variable number of gradients" + + described in http://scien.stanford.edu/class/psych221/projects/99/tingchen/algodep/vargra.html + + I've extended the basic idea to work with non-Bayer filter arrays. + Gradients are numbered clockwise from NW=0 to W=7. + */ +void CLASS vng_interpolate() +{ + static const signed char *cp, terms[] = { + -2,-2,+0,-1,0,0x01, -2,-2,+0,+0,1,0x01, -2,-1,-1,+0,0,0x01, + -2,-1,+0,-1,0,0x02, -2,-1,+0,+0,0,0x03, -2,-1,+0,+1,1,0x01, + -2,+0,+0,-1,0,0x06, -2,+0,+0,+0,1,0x02, -2,+0,+0,+1,0,0x03, + -2,+1,-1,+0,0,0x04, -2,+1,+0,-1,1,0x04, -2,+1,+0,+0,0,0x06, + -2,+1,+0,+1,0,0x02, -2,+2,+0,+0,1,0x04, -2,+2,+0,+1,0,0x04, + -1,-2,-1,+0,0,0x80, -1,-2,+0,-1,0,0x01, -1,-2,+1,-1,0,0x01, + -1,-2,+1,+0,1,0x01, -1,-1,-1,+1,0,0x88, -1,-1,+1,-2,0,0x40, + -1,-1,+1,-1,0,0x22, -1,-1,+1,+0,0,0x33, -1,-1,+1,+1,1,0x11, + -1,+0,-1,+2,0,0x08, -1,+0,+0,-1,0,0x44, -1,+0,+0,+1,0,0x11, + -1,+0,+1,-2,1,0x40, -1,+0,+1,-1,0,0x66, -1,+0,+1,+0,1,0x22, + -1,+0,+1,+1,0,0x33, -1,+0,+1,+2,1,0x10, -1,+1,+1,-1,1,0x44, + -1,+1,+1,+0,0,0x66, -1,+1,+1,+1,0,0x22, -1,+1,+1,+2,0,0x10, + -1,+2,+0,+1,0,0x04, -1,+2,+1,+0,1,0x04, -1,+2,+1,+1,0,0x04, + +0,-2,+0,+0,1,0x80, +0,-1,+0,+1,1,0x88, +0,-1,+1,-2,0,0x40, + +0,-1,+1,+0,0,0x11, +0,-1,+2,-2,0,0x40, +0,-1,+2,-1,0,0x20, + +0,-1,+2,+0,0,0x30, +0,-1,+2,+1,1,0x10, +0,+0,+0,+2,1,0x08, + +0,+0,+2,-2,1,0x40, +0,+0,+2,-1,0,0x60, +0,+0,+2,+0,1,0x20, + +0,+0,+2,+1,0,0x30, +0,+0,+2,+2,1,0x10, +0,+1,+1,+0,0,0x44, + +0,+1,+1,+2,0,0x10, +0,+1,+2,-1,1,0x40, +0,+1,+2,+0,0,0x60, + +0,+1,+2,+1,0,0x20, +0,+1,+2,+2,0,0x10, +1,-2,+1,+0,0,0x80, + +1,-1,+1,+1,0,0x88, +1,+0,+1,+2,0,0x08, +1,+0,+2,-1,0,0x40, + +1,+0,+2,+1,0,0x10 + }, chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 }; + ushort (*brow[5])[4], *pix; + int prow=7, pcol=1, *ip, *code[16][16], gval[8], gmin, gmax, sum[4]; + int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag; + int g, diff, thold, num, c; + lin_interpolate(); +#ifdef DCRAW_VERBOSE + if (verbose) fprintf (stderr,_("VNG interpolation...\n")); +#endif + + if (filters == 1) prow = pcol = 15; + ip = (int *) calloc ((prow+1)*(pcol+1), 1280); + merror (ip, "vng_interpolate()"); + for (row=0; row <= prow; row++) /* Precalculate for VNG */ + for (col=0; col <= pcol; col++) { + code[row][col] = ip; + for (cp=terms, t=0; t < 64; t++) { + y1 = *cp++; x1 = *cp++; + y2 = *cp++; x2 = *cp++; + weight = *cp++; + grads = *cp++; + color = fc(row+y1,col+x1); + if (fc(row+y2,col+x2) != color) continue; + diag = (fc(row,col+1) == color && fc(row+1,col) == color) ? 2:1; + if (abs(y1-y2) == diag && abs(x1-x2) == diag) continue; + *ip++ = (y1*width + x1)*4 + color; + *ip++ = (y2*width + x2)*4 + color; + *ip++ = weight; + for (g=0; g < 8; g++) + if (grads & 1< gval[g]) gmin = gval[g]; + if (gmax < gval[g]) gmax = gval[g]; + } + if (gmax == 0) { + memcpy (brow[2][col], pix, sizeof *image); + continue; + } + thold = gmin + (gmax >> 1); + memset (sum, 0, sizeof sum); + color = fc(row,col); + for (num=g=0; g < 8; g++,ip+=2) { /* Average the neighbors */ + if (gval[g] <= thold) { + FORCC + if (c == color && ip[1]) + sum[c] += (pix[c] + pix[ip[1]]) >> 1; + else + sum[c] += pix[ip[0] + c]; + num++; + } + } + FORCC { /* Save to buffer */ + t = pix[color]; + if (c != color) + t += (sum[c] - sum[color]) / num; + brow[2][col][c] = CLIP(t); + } + } + if (row > 3) /* Write buffer to image */ + memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image); + for (g=0; g < 4; g++) + brow[(g-1) & 3] = brow[g]; + } + memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image); + memcpy (image[(row-1)*width+2], brow[1]+2, (width-4)*sizeof *image); + free (brow[4]); + free (code[0][0]); +} + +/* + Patterned Pixel Grouping Interpolation by Alain Desbiolles +*/ +void CLASS ppg_interpolate() +{ + int dir[5] = { 1, width, -1, -width, 1 }; + int row, col, diff[2], guess[2], c, d, i; + ushort (*pix)[4]; + + border_interpolate(3); +#ifdef DCRAW_VERBOSE + if (verbose) fprintf (stderr,_("PPG interpolation...\n")); +#endif + +/* Fill in the green layer with gradients and pattern recognition: */ +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,0,3); +#endif + for (row=3; row < height-3; row++) + for (col=3+(FC(row,3) & 1), c=FC(row,col); col < width-3; col+=2) { + pix = image + row*width+col; + for (i=0; (d=dir[i]) > 0; i++) { + guess[i] = (pix[-d][1] + pix[0][c] + pix[d][1]) * 2 + - pix[-2*d][c] - pix[2*d][c]; + diff[i] = ( ABS(pix[-2*d][c] - pix[ 0][c]) + + ABS(pix[ 2*d][c] - pix[ 0][c]) + + ABS(pix[ -d][1] - pix[ d][1]) ) * 3 + + ( ABS(pix[ 3*d][1] - pix[ d][1]) + + ABS(pix[-3*d][1] - pix[-d][1]) ) * 2; + } + d = dir[i = diff[0] > diff[1]]; + pix[0][1] = ULIM(guess[i] >> 2, pix[d][1], pix[-d][1]); + } +/* Calculate red and blue for each green pixel: */ +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,1,3); +#endif + for (row=1; row < height-1; row++) + for (col=1+(FC(row,2) & 1), c=FC(row,col+1); col < width-1; col+=2) { + pix = image + row*width+col; + for (i=0; (d=dir[i]) > 0; c=2-c, i++) + pix[0][c] = CLIP((pix[-d][c] + pix[d][c] + 2*pix[0][1] + - pix[-d][1] - pix[d][1]) >> 1); + } +/* Calculate blue for red pixels and vice versa: */ +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,2,3); +#endif + for (row=1; row < height-1; row++) + for (col=1+(FC(row,1) & 1), c=2-FC(row,col); col < width-1; col+=2) { + pix = image + row*width+col; + for (i=0; (d=dir[i]+dir[i+1]) > 0; i++) { + diff[i] = ABS(pix[-d][c] - pix[d][c]) + + ABS(pix[-d][1] - pix[0][1]) + + ABS(pix[ d][1] - pix[0][1]); + guess[i] = pix[-d][c] + pix[d][c] + 2*pix[0][1] + - pix[-d][1] - pix[d][1]; + } + if (diff[0] != diff[1]) + pix[0][c] = CLIP(guess[diff[0] > diff[1]] >> 1); + else + pix[0][c] = CLIP((guess[0]+guess[1]) >> 2); + } +} + +/* + Adaptive Homogeneity-Directed interpolation is based on + the work of Keigo Hirakawa, Thomas Parks, and Paul Lee. + */ +#define TS 256 /* Tile Size */ +#ifndef _OPENMP +void CLASS ahd_interpolate() +{ + int i, j, k, top, left, row, col, tr, tc, c, d, val, hm[2]; + ushort (*pix)[4], (*rix)[3]; + static const int dir[4] = { -1, 1, -TS, TS }; + unsigned ldiff[2][4], abdiff[2][4], leps, abeps; + float r, cbrt[0x10000], xyz[3], xyz_cam[3][4]; + ushort (*rgb)[TS][TS][3]; + short (*lab)[TS][TS][3], (*lix)[3]; + char (*homo)[TS][TS], *buffer; + +#ifdef DCRAW_VERBOSE + if (verbose) fprintf (stderr,_("AHD interpolation...\n")); +#endif + + for (i=0; i < 0x10000; i++) { + r = i / 65535.0; + cbrt[i] = r > 0.008856 ? pow((double)r,1/3.0) : 7.787*r + 16/116.0; + } + for (i=0; i < 3; i++) + for (j=0; j < colors; j++) + for (xyz_cam[i][j] = k=0; k < 3; k++) + xyz_cam[i][j] += xyz_rgb[i][k] * rgb_cam[k][j] / d65_white[i]; + + border_interpolate(5); + buffer = (char *) malloc (26*TS*TS); /* 1664 kB */ + merror (buffer, "ahd_interpolate()"); + rgb = (ushort(*)[TS][TS][3]) buffer; + lab = (short (*)[TS][TS][3])(buffer + 12*TS*TS); + homo = (char (*)[TS][TS]) (buffer + 24*TS*TS); + + for (top=2; top < height-5; top += TS-6) + { + +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,(top-2)/(TS-6),(height-7)/(TS-6)+1); +#endif + for (left=2; left < width-5; left += TS-6) { + +/* Interpolate green horizontally and vertically: */ + for (row = top; row < top+TS && row < height-2; row++) { + col = left + (FC(row,left) & 1); + for (c = FC(row,col); col < left+TS && col < width-2; col+=2) { + pix = image + row*width+col; + val = ((pix[-1][1] + pix[0][c] + pix[1][1]) * 2 + - pix[-2][c] - pix[2][c]) >> 2; + rgb[0][row-top][col-left][1] = ULIM(val,pix[-1][1],pix[1][1]); + val = ((pix[-width][1] + pix[0][c] + pix[width][1]) * 2 + - pix[-2*width][c] - pix[2*width][c]) >> 2; + rgb[1][row-top][col-left][1] = ULIM(val,pix[-width][1],pix[width][1]); + } + } +/* Interpolate red and blue, and convert to CIELab: */ + for (d=0; d < 2; d++) + for (row=top+1; row < top+TS-1 && row < height-3; row++) + for (col=left+1; col < left+TS-1 && col < width-3; col++) { + pix = image + row*width+col; + rix = &rgb[d][row-top][col-left]; + lix = &lab[d][row-top][col-left]; + if ((c = 2 - FC(row,col)) == 1) { + c = FC(row+1,col); + val = pix[0][1] + (( pix[-1][2-c] + pix[1][2-c] + - rix[-1][1] - rix[1][1] ) >> 1); + rix[0][2-c] = CLIP(val); + val = pix[0][1] + (( pix[-width][c] + pix[width][c] + - rix[-TS][1] - rix[TS][1] ) >> 1); + } else + val = rix[0][1] + (( pix[-width-1][c] + pix[-width+1][c] + + pix[+width-1][c] + pix[+width+1][c] + - rix[-TS-1][1] - rix[-TS+1][1] + - rix[+TS-1][1] - rix[+TS+1][1] + 1) >> 2); + rix[0][c] = CLIP(val); + c = FC(row,col); + rix[0][c] = pix[0][c]; + xyz[0] = xyz[1] = xyz[2] = 0.5; + FORCC { + xyz[0] += xyz_cam[0][c] * rix[0][c]; + xyz[1] += xyz_cam[1][c] * rix[0][c]; + xyz[2] += xyz_cam[2][c] * rix[0][c]; + } + xyz[0] = cbrt[CLIP((int) xyz[0])]; + xyz[1] = cbrt[CLIP((int) xyz[1])]; + xyz[2] = cbrt[CLIP((int) xyz[2])]; + lix[0][0] = 64 * (116 * xyz[1] - 16); + lix[0][1] = 64 * 500 * (xyz[0] - xyz[1]); + lix[0][2] = 64 * 200 * (xyz[1] - xyz[2]); + } +/* Build homogeneity maps from the CIELab images: */ + memset (homo, 0, 2*TS*TS); + for (row=top+2; row < top+TS-2 && row < height-4; row++) { + tr = row-top; + for (col=left+2; col < left+TS-2 && col < width-4; col++) { + tc = col-left; + for (d=0; d < 2; d++) { + lix = &lab[d][tr][tc]; + for (i=0; i < 4; i++) { + ldiff[d][i] = ABS(lix[0][0]-lix[dir[i]][0]); + abdiff[d][i] = SQR(lix[0][1]-lix[dir[i]][1]) + + SQR(lix[0][2]-lix[dir[i]][2]); + } + } + leps = MIN(MAX(ldiff[0][0],ldiff[0][1]), + MAX(ldiff[1][2],ldiff[1][3])); + abeps = MIN(MAX(abdiff[0][0],abdiff[0][1]), + MAX(abdiff[1][2],abdiff[1][3])); + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps) + homo[d][tr][tc]++; + } + } +/* Combine the most homogenous pixels for the final result: */ + for (row=top+3; row < top+TS-3 && row < height-5; row++) { + tr = row-top; + for (col=left+3; col < left+TS-3 && col < width-5; col++) { + tc = col-left; + for (d=0; d < 2; d++) + for (hm[d]=0, i=tr-1; i <= tr+1; i++) + for (j=tc-1; j <= tc+1; j++) + hm[d] += homo[d][i][j]; + if (hm[0] != hm[1]) + FORC3 image[row*width+col][c] = rgb[hm[1] > hm[0]][tr][tc][c]; + else + FORC3 image[row*width+col][c] = + (rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1; + } + } + } + } + free (buffer); +} +#else +void CLASS ahd_interpolate() +{ + int i, j, k, top, left, row, col, tr, tc, c, d, val, hm[2]; + ushort (*pix)[4], (*rix)[3]; + static const int dir[4] = { -1, 1, -TS, TS }; + unsigned ldiff[2][4], abdiff[2][4], leps, abeps; + float r, cbrt[0x10000], xyz[3], xyz_cam[3][4]; + ushort (*rgb)[TS][TS][3]; + short (*lab)[TS][TS][3], (*lix)[3]; + char (*homo)[TS][TS], *buffer; + +#ifdef DCRAW_VERBOSE + if (verbose) fprintf (stderr,_("AHD interpolation...\n")); +#endif + + for (i=0; i < 0x10000; i++) { + r = i / 65535.0; + cbrt[i] = r > 0.008856 ? pow(r,1/3.0) : 7.787*r + 16/116.0; + } + for (i=0; i < 3; i++) + for (j=0; j < colors; j++) + for (xyz_cam[i][j] = k=0; k < 3; k++) + xyz_cam[i][j] += xyz_rgb[i][k] * rgb_cam[k][j] / d65_white[i]; + + border_interpolate(5); + +#ifdef LIBRAW_LIBRARY_BUILD +#pragma omp parallel private(buffer,rgb,lab,homo,top,left,row,c,col,pix,val,d,rix,xyz,lix,tc,tr,ldiff,abdiff,leps,abeps,hm,i,j) firstprivate(cbrt) shared(xyz_cam) +#endif + { + buffer = (char *) malloc (26*TS*TS); /* 1664 kB */ + merror (buffer, "ahd_interpolate()"); + rgb = (ushort(*)[TS][TS][3]) buffer; + lab = (short (*)[TS][TS][3])(buffer + 12*TS*TS); + homo = (char (*)[TS][TS]) (buffer + 24*TS*TS); + +#pragma omp for schedule(dynamic) + for (top=2; top < height-5; top += TS-6){ +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,(top-2)/(TS-6)+1,(height-7)/(TS-6)+1); +#endif + for (left=2; left < width-5; left += TS-6) { + + /* Interpolate green horizontally and vertically: */ + for (row = top; row < top+TS && row < height-2; row++) { + col = left + (FC(row,left) & 1); + for (c = FC(row,col); col < left+TS && col < width-2; col+=2) { + pix = image + row*width+col; + val = ((pix[-1][1] + pix[0][c] + pix[1][1]) * 2 + - pix[-2][c] - pix[2][c]) >> 2; + rgb[0][row-top][col-left][1] = ULIM(val,pix[-1][1],pix[1][1]); + val = ((pix[-width][1] + pix[0][c] + pix[width][1]) * 2 + - pix[-2*width][c] - pix[2*width][c]) >> 2; + rgb[1][row-top][col-left][1] = ULIM(val,pix[-width][1],pix[width][1]); + } + } + /* Interpolate red and blue, and convert to CIELab: */ + for (d=0; d < 2; d++) + for (row=top+1; row < top+TS-1 && row < height-3; row++) + for (col=left+1; col < left+TS-1 && col < width-3; col++) { + pix = image + row*width+col; + rix = &rgb[d][row-top][col-left]; + lix = &lab[d][row-top][col-left]; + if ((c = 2 - FC(row,col)) == 1) { + c = FC(row+1,col); + val = pix[0][1] + (( pix[-1][2-c] + pix[1][2-c] + - rix[-1][1] - rix[1][1] ) >> 1); + rix[0][2-c] = CLIP(val); + val = pix[0][1] + (( pix[-width][c] + pix[width][c] + - rix[-TS][1] - rix[TS][1] ) >> 1); + } else + val = rix[0][1] + (( pix[-width-1][c] + pix[-width+1][c] + + pix[+width-1][c] + pix[+width+1][c] + - rix[-TS-1][1] - rix[-TS+1][1] + - rix[+TS-1][1] - rix[+TS+1][1] + 1) >> 2); + rix[0][c] = CLIP(val); + c = FC(row,col); + rix[0][c] = pix[0][c]; + xyz[0] = xyz[1] = xyz[2] = 0.5; + FORCC { + xyz[0] += xyz_cam[0][c] * rix[0][c]; + xyz[1] += xyz_cam[1][c] * rix[0][c]; + xyz[2] += xyz_cam[2][c] * rix[0][c]; + } + xyz[0] = cbrt[CLIP((int) xyz[0])]; + xyz[1] = cbrt[CLIP((int) xyz[1])]; + xyz[2] = cbrt[CLIP((int) xyz[2])]; + lix[0][0] = 64 * (116 * xyz[1] - 16); + lix[0][1] = 64 * 500 * (xyz[0] - xyz[1]); + lix[0][2] = 64 * 200 * (xyz[1] - xyz[2]); + } + /* Build homogeneity maps from the CIELab images: */ + memset (homo, 0, 2*TS*TS); + for (row=top+2; row < top+TS-2 && row < height-4; row++) { + tr = row-top; + for (col=left+2; col < left+TS-2 && col < width-4; col++) { + tc = col-left; + for (d=0; d < 2; d++) { + lix = &lab[d][tr][tc]; + for (i=0; i < 4; i++) { + ldiff[d][i] = ABS(lix[0][0]-lix[dir[i]][0]); + abdiff[d][i] = SQR(lix[0][1]-lix[dir[i]][1]) + + SQR(lix[0][2]-lix[dir[i]][2]); + } + } + leps = MIN(MAX(ldiff[0][0],ldiff[0][1]), + MAX(ldiff[1][2],ldiff[1][3])); + abeps = MIN(MAX(abdiff[0][0],abdiff[0][1]), + MAX(abdiff[1][2],abdiff[1][3])); + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps) + homo[d][tr][tc]++; + } + } + /* Combine the most homogenous pixels for the final result: */ + for (row=top+3; row < top+TS-3 && row < height-5; row++) { + tr = row-top; + for (col=left+3; col < left+TS-3 && col < width-5; col++) { + tc = col-left; + for (d=0; d < 2; d++) + for (hm[d]=0, i=tr-1; i <= tr+1; i++) + for (j=tc-1; j <= tc+1; j++) + hm[d] += homo[d][i][j]; + if (hm[0] != hm[1]) + FORC3 image[row*width+col][c] = rgb[hm[1] > hm[0]][tr][tc][c]; + else + FORC3 image[row*width+col][c] = + (rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1; + } + } + } + } + free (buffer); + } +} + +#endif +#undef TS + +void CLASS median_filter() +{ + ushort (*pix)[4]; + int pass, c, i, j, k, med[9]; + static const uchar opt[] = /* Optimal 9-element median search */ + { 1,2, 4,5, 7,8, 0,1, 3,4, 6,7, 1,2, 4,5, 7,8, + 0,3, 5,8, 4,7, 3,6, 1,4, 2,5, 4,7, 4,2, 6,4, 4,2 }; + + for (pass=1; pass <= med_passes; pass++) { +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_MEDIAN_FILTER,pass-1,med_passes); +#endif +#ifdef DCRAW_VERBOSE + if (verbose) + fprintf (stderr,_("Median filter pass %d...\n"), pass); +#endif + for (c=0; c < 3; c+=2) { + for (pix = image; pix < image+width*height; pix++) + pix[0][3] = pix[0][c]; + for (pix = image+width; pix < image+width*(height-1); pix++) { + if ((pix-image+1) % width < 2) continue; + for (k=0, i = -width; i <= width; i += width) + for (j = i-1; j <= i+1; j++) + med[k++] = pix[j][3] - pix[j][1]; + for (i=0; i < sizeof opt; i+=2) + if (med[opt[i]] > med[opt[i+1]]) + SWAP (med[opt[i]] , med[opt[i+1]]); + pix[0][c] = CLIP(med[4] + pix[0][1]); + } + } + } +} + +void CLASS blend_highlights() +{ + int clip=INT_MAX, row, col, c, i, j; + static const float trans[2][4][4] = + { { { 1,1,1 }, { 1.7320508,-1.7320508,0 }, { -1,-1,2 } }, + { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } }; + static const float itrans[2][4][4] = + { { { 1,0.8660254,-0.5 }, { 1,-0.8660254,-0.5 }, { 1,0,1 } }, + { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } }; + float cam[2][4], lab[2][4], sum[2], chratio; + + if ((unsigned) (colors-3) > 1) return; +#ifdef DCRAW_VERBOSE + if (verbose) fprintf (stderr,_("Blending highlights...\n")); +#endif + FORCC if (clip > (i = 65535*pre_mul[c])) clip = i; +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS,0,2); +#endif + for (row=0; row < height; row++) + for (col=0; col < width; col++) { + FORCC if (image[row*width+col][c] > clip) break; + if (c == colors) continue; + FORCC { + cam[0][c] = image[row*width+col][c]; + cam[1][c] = MIN(cam[0][c],clip); + } + for (i=0; i < 2; i++) { + FORCC for (lab[i][c]=j=0; j < colors; j++) + lab[i][c] += trans[colors-3][c][j] * cam[i][j]; + for (sum[i]=0,c=1; c < colors; c++) + sum[i] += SQR(lab[i][c]); + } + chratio = sqrt(sum[1]/sum[0]); + for (c=1; c < colors; c++) + lab[0][c] *= chratio; + FORCC for (cam[0][c]=j=0; j < colors; j++) + cam[0][c] += itrans[colors-3][c][j] * lab[0][j]; + FORCC image[row*width+col][c] = cam[0][c] / colors; + } +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS,1,2); +#endif +} + +#define SCALE (4 >> shrink) +void CLASS recover_highlights() +{ + float *map, sum, wgt, grow; + int hsat[4], count, spread, change, val, i; + unsigned high, wide, mrow, mcol, row, col, kc, c, d, y, x; + ushort *pixel; + static const signed char dir[8][2] = + { {-1,-1}, {-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1} }; + +#ifdef DCRAW_VERBOSE + if (verbose) fprintf (stderr,_("Rebuilding highlights...\n")); +#endif + + grow = pow (2.0, 4.0-highlight); + FORCC hsat[c] = 32000 * pre_mul[c]; + for (kc=0, c=1; c < colors; c++) + if (pre_mul[kc] < pre_mul[c]) kc = c; + high = height / SCALE; + wide = width / SCALE; + map = (float *) calloc (high*wide, sizeof *map); + merror (map, "recover_highlights()"); + FORCC if (c != kc) { +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS,c-1,colors-1); +#endif + memset (map, 0, high*wide*sizeof *map); + for (mrow=0; mrow < high; mrow++) + for (mcol=0; mcol < wide; mcol++) { + sum = wgt = count = 0; + for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++) + for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) { + pixel = image[row*width+col]; + if (pixel[c] / hsat[c] == 1 && pixel[kc] > 24000) { + sum += pixel[c]; + wgt += pixel[kc]; + count++; + } + } + if (count == SCALE*SCALE) + map[mrow*wide+mcol] = sum / wgt; + } + for (spread = 32/grow; spread--; ) { + for (mrow=0; mrow < high; mrow++) + for (mcol=0; mcol < wide; mcol++) { + if (map[mrow*wide+mcol]) continue; + sum = count = 0; + for (d=0; d < 8; d++) { + y = mrow + dir[d][0]; + x = mcol + dir[d][1]; + if (y < high && x < wide && map[y*wide+x] > 0) { + sum += (1 + (d & 1)) * map[y*wide+x]; + count += 1 + (d & 1); + } + } + if (count > 3) + map[mrow*wide+mcol] = - (sum+grow) / (count+grow); + } + for (change=i=0; i < high*wide; i++) + if (map[i] < 0) { + map[i] = -map[i]; + change = 1; + } + if (!change) break; + } + for (i=0; i < high*wide; i++) + if (map[i] == 0) map[i] = 1; + for (mrow=0; mrow < high; mrow++) + for (mcol=0; mcol < wide; mcol++) { + for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++) + for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) { + pixel = image[row*width+col]; + if (pixel[c] / hsat[c] > 1) { + val = pixel[kc] * map[mrow*wide+mcol]; + if (pixel[c] < val) pixel[c] = CLIP(val); + } + } + } + } + free (map); +} +#undef SCALE + +void CLASS tiff_get (unsigned base, + unsigned *tag, unsigned *type, unsigned *len, unsigned *save) +{ + *tag = get2(); + *type = get2(); + *len = get4(); + *save = ftell(ifp) + 4; + if (*len * ("11124811248488"[*type < 14 ? *type:0]-'0') > 4) + fseek (ifp, get4()+base, SEEK_SET); +} + +void CLASS parse_thumb_note (int base, unsigned toff, unsigned tlen) +{ + unsigned entries, tag, type, len, save; + + entries = get2(); + while (entries--) { + tiff_get (base, &tag, &type, &len, &save); + if (tag == toff) thumb_offset = get4()+base; + if (tag == tlen) thumb_length = get4(); + fseek (ifp, save, SEEK_SET); + } +} + +#line 5393 "dcraw/dcraw.c" +void CLASS parse_makernote (int base, int uptag) +{ + static const uchar xlat[2][256] = { + { 0xc1,0xbf,0x6d,0x0d,0x59,0xc5,0x13,0x9d,0x83,0x61,0x6b,0x4f,0xc7,0x7f,0x3d,0x3d, + 0x53,0x59,0xe3,0xc7,0xe9,0x2f,0x95,0xa7,0x95,0x1f,0xdf,0x7f,0x2b,0x29,0xc7,0x0d, + 0xdf,0x07,0xef,0x71,0x89,0x3d,0x13,0x3d,0x3b,0x13,0xfb,0x0d,0x89,0xc1,0x65,0x1f, + 0xb3,0x0d,0x6b,0x29,0xe3,0xfb,0xef,0xa3,0x6b,0x47,0x7f,0x95,0x35,0xa7,0x47,0x4f, + 0xc7,0xf1,0x59,0x95,0x35,0x11,0x29,0x61,0xf1,0x3d,0xb3,0x2b,0x0d,0x43,0x89,0xc1, + 0x9d,0x9d,0x89,0x65,0xf1,0xe9,0xdf,0xbf,0x3d,0x7f,0x53,0x97,0xe5,0xe9,0x95,0x17, + 0x1d,0x3d,0x8b,0xfb,0xc7,0xe3,0x67,0xa7,0x07,0xf1,0x71,0xa7,0x53,0xb5,0x29,0x89, + 0xe5,0x2b,0xa7,0x17,0x29,0xe9,0x4f,0xc5,0x65,0x6d,0x6b,0xef,0x0d,0x89,0x49,0x2f, + 0xb3,0x43,0x53,0x65,0x1d,0x49,0xa3,0x13,0x89,0x59,0xef,0x6b,0xef,0x65,0x1d,0x0b, + 0x59,0x13,0xe3,0x4f,0x9d,0xb3,0x29,0x43,0x2b,0x07,0x1d,0x95,0x59,0x59,0x47,0xfb, + 0xe5,0xe9,0x61,0x47,0x2f,0x35,0x7f,0x17,0x7f,0xef,0x7f,0x95,0x95,0x71,0xd3,0xa3, + 0x0b,0x71,0xa3,0xad,0x0b,0x3b,0xb5,0xfb,0xa3,0xbf,0x4f,0x83,0x1d,0xad,0xe9,0x2f, + 0x71,0x65,0xa3,0xe5,0x07,0x35,0x3d,0x0d,0xb5,0xe9,0xe5,0x47,0x3b,0x9d,0xef,0x35, + 0xa3,0xbf,0xb3,0xdf,0x53,0xd3,0x97,0x53,0x49,0x71,0x07,0x35,0x61,0x71,0x2f,0x43, + 0x2f,0x11,0xdf,0x17,0x97,0xfb,0x95,0x3b,0x7f,0x6b,0xd3,0x25,0xbf,0xad,0xc7,0xc5, + 0xc5,0xb5,0x8b,0xef,0x2f,0xd3,0x07,0x6b,0x25,0x49,0x95,0x25,0x49,0x6d,0x71,0xc7 }, + { 0xa7,0xbc,0xc9,0xad,0x91,0xdf,0x85,0xe5,0xd4,0x78,0xd5,0x17,0x46,0x7c,0x29,0x4c, + 0x4d,0x03,0xe9,0x25,0x68,0x11,0x86,0xb3,0xbd,0xf7,0x6f,0x61,0x22,0xa2,0x26,0x34, + 0x2a,0xbe,0x1e,0x46,0x14,0x68,0x9d,0x44,0x18,0xc2,0x40,0xf4,0x7e,0x5f,0x1b,0xad, + 0x0b,0x94,0xb6,0x67,0xb4,0x0b,0xe1,0xea,0x95,0x9c,0x66,0xdc,0xe7,0x5d,0x6c,0x05, + 0xda,0xd5,0xdf,0x7a,0xef,0xf6,0xdb,0x1f,0x82,0x4c,0xc0,0x68,0x47,0xa1,0xbd,0xee, + 0x39,0x50,0x56,0x4a,0xdd,0xdf,0xa5,0xf8,0xc6,0xda,0xca,0x90,0xca,0x01,0x42,0x9d, + 0x8b,0x0c,0x73,0x43,0x75,0x05,0x94,0xde,0x24,0xb3,0x80,0x34,0xe5,0x2c,0xdc,0x9b, + 0x3f,0xca,0x33,0x45,0xd0,0xdb,0x5f,0xf5,0x52,0xc3,0x21,0xda,0xe2,0x22,0x72,0x6b, + 0x3e,0xd0,0x5b,0xa8,0x87,0x8c,0x06,0x5d,0x0f,0xdd,0x09,0x19,0x93,0xd0,0xb9,0xfc, + 0x8b,0x0f,0x84,0x60,0x33,0x1c,0x9b,0x45,0xf1,0xf0,0xa3,0x94,0x3a,0x12,0x77,0x33, + 0x4d,0x44,0x78,0x28,0x3c,0x9e,0xfd,0x65,0x57,0x16,0x94,0x6b,0xfb,0x59,0xd0,0xc8, + 0x22,0x36,0xdb,0xd2,0x63,0x98,0x43,0xa1,0x04,0x87,0x86,0xf7,0xa6,0x26,0xbb,0xd6, + 0x59,0x4d,0xbf,0x6a,0x2e,0xaa,0x2b,0xef,0xe6,0x78,0xb6,0x4e,0xe0,0x2f,0xdc,0x7c, + 0xbe,0x57,0x19,0x32,0x7e,0x2a,0xd0,0xb8,0xba,0x29,0x00,0x3c,0x52,0x7d,0xa8,0x49, + 0x3b,0x2d,0xeb,0x25,0x49,0xfa,0xa3,0xaa,0x39,0xa7,0xc5,0xa7,0x50,0x11,0x36,0xfb, + 0xc6,0x67,0x4a,0xf5,0xa5,0x12,0x65,0x7e,0xb0,0xdf,0xaf,0x4e,0xb3,0x61,0x7f,0x2f } }; + unsigned offset=0, entries, tag, type, len, save, c; + unsigned ver97=0, serial=0, i, wbi=0, wb[4]={0,0,0,0}; + uchar buf97[324], ci, cj, ck; + short sorder=order; + char buf[10]; +/* + The MakerNote might have its own TIFF header (possibly with + its own byte-order!), or it might just be a table. + */ + fread (buf, 1, 10, ifp); + if (!strncmp (buf,"KDK" ,3) || /* these aren't TIFF tables */ + !strncmp (buf,"VER" ,3) || + !strncmp (buf,"IIII",4) || + !strncmp (buf,"MMMM",4)) return; + if (!strncmp (buf,"KC" ,2) || /* Konica KD-400Z, KD-510Z */ + !strncmp (buf,"MLY" ,3)) { /* Minolta DiMAGE G series */ + order = 0x4d4d; + while ((i=ftell(ifp)) < data_offset && i < 16384) { + wb[0] = wb[2]; wb[2] = wb[1]; wb[1] = wb[3]; + wb[3] = get2(); + if (wb[1] == 256 && wb[3] == 256 && + wb[0] > 256 && wb[0] < 640 && wb[2] > 256 && wb[2] < 640) + FORC4 cam_mul[c] = wb[c]; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + goto quit; + } + if (!strcmp (buf,"Nikon")) { + base = ftell(ifp); + order = get2(); + if (get2() != 42) goto quit; + offset = get4(); + fseek (ifp, offset-8, SEEK_CUR); + } else if (!strcmp (buf,"OLYMPUS")) { + base = ftell(ifp)-10; + fseek (ifp, -2, SEEK_CUR); + order = get2(); get2(); + } else if (!strncmp (buf,"FUJIFILM",8) || + !strncmp (buf,"SONY",4) || + !strcmp (buf,"Panasonic")) { + order = 0x4949; + fseek (ifp, 2, SEEK_CUR); + } else if (!strcmp (buf,"OLYMP") || + !strcmp (buf,"LEICA") || + !strcmp (buf,"Ricoh") || + !strcmp (buf,"EPSON")) + fseek (ifp, -2, SEEK_CUR); + else if (!strcmp (buf,"AOC") || + !strcmp (buf,"QVC")) + fseek (ifp, -4, SEEK_CUR); + else fseek (ifp, -10, SEEK_CUR); + + entries = get2(); + if (entries > 1000) return; + while (entries--) { + tiff_get (base, &tag, &type, &len, &save); + tag |= uptag << 16; + if (tag == 2 && strstr(make,"NIKON")) + iso_speed = (get2(),get2()); + if (tag == 4 && len > 26 && len < 35) { + if ((i=(get4(),get2())) != 0x7fff && !iso_speed) + iso_speed = 50 * pow (2, i/32.0 - 4); + if ((i=(get2(),get2())) != 0x7fff && !aperture) + aperture = pow (2, i/64.0); + if ((i=get2()) != 0xffff && !shutter) + shutter = pow (2, (short) i/-32.0); + wbi = (get2(),get2()); + shot_order = (get2(),get2()); + } + if (tag == 8 && type == 4) + shot_order = get4(); + if (tag == 9 && !strcmp(make,"Canon")) + fread (artist, 64, 1, ifp); + if (tag == 0xc && len == 4) { + cam_mul[0] = getreal(type); + cam_mul[2] = getreal(type); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + if (tag == 0x10 && type == 4) + unique_id = get4(); + if (tag == 0x11 && is_raw && !strncmp(make,"NIKON",5)) { + fseek (ifp, get4()+base, SEEK_SET); + parse_tiff_ifd (base); + } + if (tag == 0x14 && len == 2560 && type == 7) { + fseek (ifp, 1248, SEEK_CUR); + goto get2_256; + } + if (tag == 0x15 && type == 2 && is_raw) + fread (model, 64, 1, ifp); + if (strstr(make,"PENTAX")) { + if (tag == 0x1b) tag = 0x1018; + if (tag == 0x1c) tag = 0x1017; + } + if (tag == 0x1d) + while ((c = fgetc(ifp)) && c != EOF) + serial = serial*10 + (isdigit(c) ? c - '0' : c % 10); + if (tag == 0x81 && type == 4) { + data_offset = get4(); + fseek (ifp, data_offset + 41, SEEK_SET); + raw_height = get2() * 2; + raw_width = get2(); + filters = 0x61616161; + } + if (tag == 0x29 && type == 1) { + c = wbi < 18 ? "012347800000005896"[wbi]-'0' : 0; + fseek (ifp, 8 + c*32, SEEK_CUR); + FORC4 cam_mul[c ^ (c >> 1) ^ 1] = get4(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + if ((tag == 0x81 && type == 7) || + (tag == 0x100 && type == 7) || + (tag == 0x280 && type == 1)) { + thumb_offset = ftell(ifp); + thumb_length = len; + } + if (tag == 0x88 && type == 4 && (thumb_offset = get4())) + thumb_offset += base; + if (tag == 0x89 && type == 4) + thumb_length = get4(); + if (tag == 0x8c || tag == 0x96) + meta_offset = ftell(ifp); + if (tag == 0x97) { + for (i=0; i < 4; i++) + ver97 = ver97 * 10 + fgetc(ifp)-'0'; + switch (ver97) { + case 100: + fseek (ifp, 68, SEEK_CUR); + FORC4 cam_mul[(c >> 1) | ((c & 1) << 1)] = get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + break; + case 102: + fseek (ifp, 6, SEEK_CUR); + goto get2_rggb; + case 103: + fseek (ifp, 16, SEEK_CUR); + FORC4 cam_mul[c] = get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + if (ver97 >= 200) { + if (ver97 != 205) fseek (ifp, 280, SEEK_CUR); + fread (buf97, 324, 1, ifp); + } + } + if (tag == 0xa4 && type == 3) { + fseek (ifp, wbi*48, SEEK_CUR); + FORC3 cam_mul[c] = get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + if (tag == 0xa7 && (unsigned) (ver97-200) < 12 && !cam_mul[0]) { + ci = xlat[0][serial & 0xff]; + cj = xlat[1][fgetc(ifp)^fgetc(ifp)^fgetc(ifp)^fgetc(ifp)]; + ck = 0x60; + for (i=0; i < 324; i++) + buf97[i] ^= (cj += ci * ck++); + i = "66666>666;6A"[ver97-200] - '0'; + FORC4 cam_mul[c ^ (c >> 1) ^ (i & 1)] = + sget2 (buf97 + (i & -2) + c*2); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + if (tag == 0x200 && len == 3) + shot_order = (get4(),get4()); + if (tag == 0x200 && len == 4) + black = (get2()+get2()+get2()+get2())/4; + if (tag == 0x201 && len == 4) + goto get2_rggb; + if (tag == 0x401 && len == 4) { + black = (get4()+get4()+get4()+get4())/4; + } + if (tag == 0xe01) { /* Nikon Capture Note */ + type = order; + order = 0x4949; + fseek (ifp, 22, SEEK_CUR); + for (offset=22; offset+22 < len; offset += 22+i) { + tag = get4(); + fseek (ifp, 14, SEEK_CUR); + i = get4()-4; + if (tag == 0x76a43207) flip = get2(); + else fseek (ifp, i, SEEK_CUR); + } + order = type; + } + if (tag == 0xe80 && len == 256 && type == 7) { + fseek (ifp, 48, SEEK_CUR); + cam_mul[0] = get2() * 508 * 1.078 / 0x10000; + cam_mul[2] = get2() * 382 * 1.173 / 0x10000; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + if (tag == 0xf00 && type == 7) { + if (len == 614) + fseek (ifp, 176, SEEK_CUR); + else if (len == 734 || len == 1502) + fseek (ifp, 148, SEEK_CUR); + else goto next; + goto get2_256; + } + if ((tag == 0x1011 && len == 9) || tag == 0x20400200) + { + for (i=0; i < 3; i++) + FORC3 cmatrix[i][c] = ((short) get2()) / 256.0; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cmatrix_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + if ((tag == 0x1012 || tag == 0x20400600) && len == 4) + for (black = i=0; i < 4; i++) + black += get2() << 2; + if (tag == 0x1017 || tag == 0x20400100) + { + cam_mul[0] = get2() / 256.0; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + if (tag == 0x1018 || tag == 0x20400100) + { + cam_mul[2] = get2() / 256.0; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + if (tag == 0x2011 && len == 2) { +get2_256: + order = 0x4d4d; + cam_mul[0] = get2() / 256.0; + cam_mul[2] = get2() / 256.0; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + if ((tag | 0x70) == 0x2070 && type == 4) + fseek (ifp, get4()+base, SEEK_SET); + if (tag == 0x2010 && type != 7) + load_raw = &CLASS olympus_e410_load_raw; + if (tag == 0x2020) + parse_thumb_note (base, 257, 258); + if (tag == 0x2040) + parse_makernote (base, 0x2040); + if (tag == 0xb028) { + fseek (ifp, get4(), SEEK_SET); + parse_thumb_note (base, 136, 137); + } + if (tag == 0x4001 && len > 500) { + i = len == 582 ? 50 : len == 653 ? 68 : len == 5120 ? 142 : 126; + fseek (ifp, i, SEEK_CUR); +get2_rggb: + FORC4 cam_mul[c ^ (c >> 1)] = get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + fseek (ifp, 22, SEEK_CUR); + FORC4 sraw_mul[c ^ (c >> 1)] = get2(); + } +next: + fseek (ifp, save, SEEK_SET); + } +quit: + order = sorder; +} + +/* + Since the TIFF DateTime string has no timezone information, + assume that the camera's clock was set to Universal Time. + */ +void CLASS get_timestamp (int reversed) +{ + struct tm t; + char str[20]; + int i; + + str[19] = 0; + if (reversed) + for (i=19; i--; ) str[i] = fgetc(ifp); + else + fread (str, 19, 1, ifp); + memset (&t, 0, sizeof t); + if (sscanf (str, "%d:%d:%d %d:%d:%d", &t.tm_year, &t.tm_mon, + &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec) != 6) + return; + t.tm_year -= 1900; + t.tm_mon -= 1; + if (mktime(&t) > 0) + timestamp = mktime(&t); +} + +void CLASS parse_exif (int base) +{ + unsigned kodak, entries, tag, type, len, save, c; + double expo; + + kodak = !strncmp(make,"EASTMAN",7); + entries = get2(); + while (entries--) { + tiff_get (base, &tag, &type, &len, &save); + switch (tag) { + case 33434: shutter = getreal(type); break; + case 33437: aperture = getreal(type); break; + case 34855: iso_speed = get2(); break; + case 36867: + case 36868: get_timestamp(0); break; + case 37377: if ((expo = -getreal(type)) < 128) + shutter = pow (2, expo); break; + case 37378: aperture = pow (2, getreal(type)/2); break; + case 37386: focal_len = getreal(type); break; + case 37500: parse_makernote (base, 0); break; + case 40962: if (kodak) raw_width = get4(); break; + case 40963: if (kodak) raw_height = get4(); break; + case 41730: + if (get4() == 0x20002) + for (exif_cfa=c=0; c < 8; c+=2) + exif_cfa |= fgetc(ifp) * 0x01010101 << c; + } + fseek (ifp, save, SEEK_SET); + } +} + +void CLASS parse_gps (int base) +{ + unsigned entries, tag, type, len, save, c; + + entries = get2(); + while (entries--) { + tiff_get (base, &tag, &type, &len, &save); + switch (tag) { + case 1: case 3: case 5: + gpsdata[29+tag/2] = getc(ifp); break; + case 2: case 4: case 7: + FORC(6) gpsdata[tag/3*6+c] = get4(); break; + case 6: + FORC(2) gpsdata[18+c] = get4(); break; + case 18: case 29: + fgets ((char *) (gpsdata+14+tag/3), MIN(len,12), ifp); + } + fseek (ifp, save, SEEK_SET); + } +} + +void CLASS romm_coeff (float romm_cam[3][3]) +{ + static const float rgb_romm[3][3] = /* ROMM == Kodak ProPhoto */ + { { 2.034193, -0.727420, -0.306766 }, + { -0.228811, 1.231729, -0.002922 }, + { -0.008565, -0.153273, 1.161839 } }; + int i, j, k; + + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + for (cmatrix[i][j] = k=0; k < 3; k++) + cmatrix[i][j] += rgb_romm[i][k] * romm_cam[k][j]; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cmatrix_state = LIBRAW_COLORSTATE_CALCULATED; +#endif +} + +void CLASS parse_mos (int offset) +{ + char data[40]; + int skip, from, i, c, neut[4], planes=0, frot=0; + static const char *mod[] = + { "","DCB2","Volare","Cantare","CMost","Valeo 6","Valeo 11","Valeo 22", + "Valeo 11p","Valeo 17","","Aptus 17","Aptus 22","Aptus 75","Aptus 65", + "Aptus 54S","Aptus 65S","Aptus 75S","AFi 5","AFi 6","AFi 7" }; + float romm_cam[3][3]; + + fseek (ifp, offset, SEEK_SET); + while (1) { + if (get4() != 0x504b5453) break; + get4(); + fread (data, 1, 40, ifp); + skip = get4(); + from = ftell(ifp); + if (!strcmp(data,"JPEG_preview_data")) { + thumb_offset = from; + thumb_length = skip; + } + if (!strcmp(data,"icc_camera_profile")) { + profile_offset = from; + profile_length = skip; + } + if (!strcmp(data,"ShootObj_back_type")) { + fscanf (ifp, "%d", &i); + if ((unsigned) i < sizeof mod / sizeof (*mod)) + strcpy (model, mod[i]); + } + if (!strcmp(data,"icc_camera_to_tone_matrix")) { + for (i=0; i < 9; i++) + romm_cam[0][i] = int_to_float(get4()); + romm_coeff (romm_cam); + } + if (!strcmp(data,"CaptProf_color_matrix")) { + for (i=0; i < 9; i++) + fscanf (ifp, "%f", &romm_cam[0][i]); + romm_coeff (romm_cam); + } + if (!strcmp(data,"CaptProf_number_of_planes")) + fscanf (ifp, "%d", &planes); + if (!strcmp(data,"CaptProf_raw_data_rotation")) + fscanf (ifp, "%d", &flip); + if (!strcmp(data,"CaptProf_mosaic_pattern")) + FORC4 { + fscanf (ifp, "%d", &i); + if (i == 1) frot = c ^ (c >> 1); + } + if (!strcmp(data,"ImgProf_rotation_angle")) { + fscanf (ifp, "%d", &i); + flip = i - flip; + } + if (!strcmp(data,"NeutObj_neutrals") && !cam_mul[0]) { + FORC4 fscanf (ifp, "%d", neut+c); + FORC3 cam_mul[c] = (float) neut[0] / neut[c+1]; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + parse_mos (from); + fseek (ifp, skip+from, SEEK_SET); + } + if (planes) + filters = (planes == 1) * 0x01010101 * + (uchar) "\x94\x61\x16\x49"[(flip/90 + frot) & 3]; +} + +void CLASS linear_table (unsigned len) +{ + int i; + if (len > 0x1000) len = 0x1000; + read_shorts (curve, len); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.curve_state = LIBRAW_COLORSTATE_LOADED; +#endif + for (i=len; i < 0x1000; i++) + curve[i] = curve[i-1]; + maximum = curve[0xfff]; +} + +void CLASS parse_kodak_ifd (int base) +{ + unsigned entries, tag, type, len, save; + int i, c, wbi=-2, wbtemp=6500; + float mul[3], num; + + entries = get2(); + if (entries > 1024) return; + while (entries--) { + tiff_get (base, &tag, &type, &len, &save); + if (tag == 1020) wbi = getint(type); + if (tag == 1021 && len == 72) { /* WB set in software */ + fseek (ifp, 40, SEEK_CUR); + FORC3 cam_mul[c] = 2048.0 / get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + wbi = -2; + } + if (tag == 2118) wbtemp = getint(type); + if (tag == 2130 + wbi) + FORC3 mul[c] = getreal(type); + if (tag == 2140 + wbi && wbi >= 0) + { + FORC3 { + for (num=i=0; i < 4; i++) + num += getreal(type) * pow (wbtemp/100.0, i); + cam_mul[c] = 2048 / (num * mul[c]); + } +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + if (tag == 2317) linear_table (len); + if (tag == 6020) iso_speed = getint(type); + fseek (ifp, save, SEEK_SET); + } +} + +#line 5922 "dcraw/dcraw.c" +int CLASS parse_tiff_ifd (int base) +{ + unsigned entries, tag, type, len, plen=16, save; + int ifd, use_cm=0, cfa, i, j, c, ima_len=0; + char software[64], *cbuf, *cp; + uchar cfa_pat[16], cfa_pc[] = { 0,1,2,3 }, tab[256]; + double dblack, cc[4][4], cm[4][3], cam_xyz[4][3], num; + double ab[]={ 1,1,1,1 }, asn[] = { 0,0,0,0 }, xyz[] = { 1,1,1 }; + unsigned sony_curve[] = { 0,0,0,0,0,4095 }; + unsigned *buf, sony_offset=0, sony_length=0, sony_key=0; + struct jhead jh; + FILE *sfp; + + if (tiff_nifds >= sizeof tiff_ifd / sizeof tiff_ifd[0]) + return 1; + ifd = tiff_nifds++; + for (j=0; j < 4; j++) + for (i=0; i < 4; i++) + cc[j][i] = i == j; + entries = get2(); + if (entries > 512) return 1; + while (entries--) { + tiff_get (base, &tag, &type, &len, &save); + switch (tag) { + case 17: case 18: + if (type == 3 && len == 1) + { + cam_mul[(tag-17)*2] = get2() / 256.0; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + break; + case 23: + if (type == 3) iso_speed = get2(); + break; + case 36: case 37: case 38: + cam_mul[tag-0x24] = get2(); + break; + case 39: + if (len < 50 || cam_mul[0]) break; + fseek (ifp, 12, SEEK_CUR); + FORC3 cam_mul[c] = get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + break; + case 46: + if (type != 7 || fgetc(ifp) != 0xff || fgetc(ifp) != 0xd8) break; + thumb_offset = ftell(ifp) - 2; + thumb_length = len; + break; + case 2: case 256: /* ImageWidth */ + tiff_ifd[ifd].t_width = getint(type); + break; + case 3: case 257: /* ImageHeight */ + tiff_ifd[ifd].t_height = getint(type); + break; + case 258: /* BitsPerSample */ + tiff_ifd[ifd].samples = len & 7; + tiff_ifd[ifd].bps = get2(); + break; + case 259: /* Compression */ + tiff_ifd[ifd].comp = get2(); + break; + case 262: /* PhotometricInterpretation */ + tiff_ifd[ifd].phint = get2(); + break; + case 270: /* ImageDescription */ + fread (desc, 512, 1, ifp); + break; + case 271: /* Make */ + fgets (make, 64, ifp); + break; + case 272: /* Model */ + fgets (model, 64, ifp); + break; + case 280: /* Panasonic RW2 offset */ + if (type != 4) break; + load_raw = &CLASS panasonic_load_raw; + load_flags = 0x2008; + case 273: /* StripOffset */ + case 513: + tiff_ifd[ifd].offset = get4()+base; + if (!tiff_ifd[ifd].bps) { + fseek (ifp, tiff_ifd[ifd].offset, SEEK_SET); + if (ljpeg_start (&jh, 1)) { + tiff_ifd[ifd].comp = 6; + tiff_ifd[ifd].t_width = jh.wide << (jh.clrs == 2); + tiff_ifd[ifd].t_height = jh.high; + tiff_ifd[ifd].bps = jh.bits; + tiff_ifd[ifd].samples = jh.clrs; + } + } + break; + case 274: /* Orientation */ + tiff_ifd[ifd].t_flip = "50132467"[get2() & 7]-'0'; + break; + case 277: /* SamplesPerPixel */ + tiff_ifd[ifd].samples = getint(type) & 7; + break; + case 279: /* StripByteCounts */ + case 514: + tiff_ifd[ifd].bytes = get4(); + break; + case 305: case 11: /* Software */ + fgets (software, 64, ifp); + if (!strncmp(software,"Adobe",5) || + !strncmp(software,"dcraw",5) || + !strncmp(software,"UFRaw",5) || + !strncmp(software,"Bibble",6) || + !strncmp(software,"Nikon Scan",10) || + !strcmp (software,"Digital Photo Professional")) + is_raw = 0; + break; + case 306: /* DateTime */ + get_timestamp(0); + break; + case 315: /* Artist */ + fread (artist, 64, 1, ifp); + break; + case 322: /* TileWidth */ + tile_width = getint(type); + break; + case 323: /* TileLength */ + tile_length = getint(type); + break; + case 324: /* TileOffsets */ + tiff_ifd[ifd].offset = len > 1 ? ftell(ifp) : get4(); + if (len == 4) { + load_raw = &CLASS sinar_4shot_load_raw; + is_raw = 5; + } + break; + case 330: /* SubIFDs */ + if (!strcmp(model,"DSLR-A100") && tiff_ifd[ifd].t_width == 3872) { + load_raw = &CLASS sony_arw_load_raw; + data_offset = get4()+base; + ifd++; break; + } + while (len--) { + i = ftell(ifp); + fseek (ifp, get4()+base, SEEK_SET); + if (parse_tiff_ifd (base)) break; + fseek (ifp, i+4, SEEK_SET); + } + break; + case 400: + strcpy (make, "Sarnoff"); + maximum = 0xfff; + break; + case 28688: + FORC4 sony_curve[c+1] = get2() >> 2 & 0xfff; + for (i=0; i < 5; i++) + for (j = sony_curve[i]+1; j <= sony_curve[i+1]; j++) + curve[j] = curve[j-1] + (1 << i); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.curve_state = LIBRAW_COLORSTATE_LOADED; +#endif + break; + case 29184: sony_offset = get4(); break; + case 29185: sony_length = get4(); break; + case 29217: sony_key = get4(); break; + case 29264: + parse_minolta (ftell(ifp)); + raw_width = 0; + break; + case 29443: + FORC4 cam_mul[c ^ (c < 2)] = get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + break; + case 29459: + FORC4 cam_mul[c ^ (c >> 1)] = get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + break; + case 33405: /* Model2 */ + fgets (model2, 64, ifp); + break; + case 33422: /* CFAPattern */ + case 64777: /* Kodak P-series */ + if ((plen=len) > 16) plen = 16; + fread (cfa_pat, 1, plen, ifp); + for (colors=cfa=i=0; i < plen; i++) { + colors += !(cfa & (1 << cfa_pat[i])); + cfa |= 1 << cfa_pat[i]; + } + if (cfa == 070) memcpy (cfa_pc,"\003\004\005",3); /* CMY */ + if (cfa == 072) memcpy (cfa_pc,"\005\003\004\001",4); /* GMCY */ + goto guess_cfa_pc; + case 33424: + fseek (ifp, get4()+base, SEEK_SET); + parse_kodak_ifd (base); + break; + case 33434: /* ExposureTime */ + shutter = getreal(type); + break; + case 33437: /* FNumber */ + aperture = getreal(type); + break; + case 34306: /* Leaf white balance */ + FORC4 cam_mul[c ^ 1] = 4096.0 / get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + break; + case 34307: /* Leaf CatchLight color matrix */ + fread (software, 1, 7, ifp); + if (strncmp(software,"MATRIX",6)) break; + colors = 4; + for (raw_color = i=0; i < 3; i++) { + FORC4 fscanf (ifp, "%f", &rgb_cam[i][c^1]); + if (!use_camera_wb) continue; + num = 0; + FORC4 num += rgb_cam[i][c]; + FORC4 rgb_cam[i][c] /= num; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.rgb_cam_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + break; + case 34310: /* Leaf metadata */ + parse_mos (ftell(ifp)); + case 34303: + strcpy (make, "Leaf"); + break; + case 34665: /* EXIF tag */ + fseek (ifp, get4()+base, SEEK_SET); + parse_exif (base); + break; + case 34853: /* GPSInfo tag */ + fseek (ifp, get4()+base, SEEK_SET); + parse_gps (base); + break; + case 34675: /* InterColorProfile */ + case 50831: /* AsShotICCProfile */ + profile_offset = ftell(ifp); + profile_length = len; + break; + case 37122: /* CompressedBitsPerPixel */ + kodak_cbpp = get4(); + break; + case 37386: /* FocalLength */ + focal_len = getreal(type); + break; + case 37393: /* ImageNumber */ + shot_order = getint(type); + break; + case 37400: /* old Kodak KDC tag */ + for (raw_color = i=0; i < 3; i++) { + getreal(type); + FORC3 rgb_cam[i][c] = getreal(type); + } +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.rgb_cam_state = LIBRAW_COLORSTATE_LOADED; +#endif + break; + case 46275: /* Imacon tags */ + strcpy (make, "Imacon"); + data_offset = ftell(ifp); + ima_len = len; + break; + case 46279: + if (!ima_len) break; + fseek (ifp, 78, SEEK_CUR); + raw_width = get4(); + raw_height = get4(); + left_margin = get4() & 7; + width = raw_width - left_margin - (get4() & 7); + top_margin = get4() & 7; + height = raw_height - top_margin - (get4() & 7); + if (raw_width == 7262) { + height = 5444; + width = 7244; + left_margin = 7; + } + fseek (ifp, 52, SEEK_CUR); + FORC3 cam_mul[c] = getreal(11); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + fseek (ifp, 114, SEEK_CUR); + flip = (get2() >> 7) * 90; + if (width * height * 6 == ima_len) { + if (flip % 180 == 90) SWAP(width,height); + filters = flip = 0; + } + sprintf (model, "Ixpress %d-Mp", height*width/1000000); + load_raw = &CLASS imacon_full_load_raw; + if (filters) { + if (left_margin & 1) filters = 0x61616161; + load_raw = &CLASS unpacked_load_raw; + } + maximum = 0xffff; + break; + case 50454: /* Sinar tag */ + case 50455: + if (!(cbuf = (char *) malloc(len))) break; + fread (cbuf, 1, len, ifp); + for (cp = cbuf-1; cp && cp < cbuf+len; cp = strchr(cp,'\n')) + if (!strncmp (++cp,"Neutral ",8)) + { + sscanf (cp+8, "%f %f %f", cam_mul, cam_mul+1, cam_mul+2); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + free (cbuf); + break; + case 50458: + if (!make[0]) strcpy (make, "Hasselblad"); + break; + case 50459: /* Hasselblad tag */ + i = order; + j = ftell(ifp); + c = tiff_nifds; + order = get2(); + fseek (ifp, j+(get2(),get4()), SEEK_SET); + parse_tiff_ifd (j); + maximum = 0xffff; + tiff_nifds = c; + order = i; + break; + case 50706: /* DNGVersion */ + FORC4 dng_version = (dng_version << 8) + fgetc(ifp); + if (!make[0]) strcpy (make, "DNG"); + is_raw = 1; + break; + case 50710: /* CFAPlaneColor */ + if (len > 4) len = 4; + colors = len; + fread (cfa_pc, 1, colors, ifp); +guess_cfa_pc: + FORCC tab[cfa_pc[c]] = c; + cdesc[c] = 0; + for (i=16; i--; ) + filters = filters << 2 | tab[cfa_pat[i % plen]]; + break; + case 50711: /* CFALayout */ + if (get2() == 2) { + fuji_width = 1; + filters = 0x49494949; + } + break; + case 291: + case 50712: /* LinearizationTable */ + linear_table (len); + break; + case 50714: /* BlackLevel */ + case 50715: /* BlackLevelDeltaH */ + case 50716: /* BlackLevelDeltaV */ + for (dblack=i=0; i < len; i++) + dblack += getreal(type); + black += dblack/len + 0.5; + break; + case 50717: /* WhiteLevel */ + maximum = getint(type); + break; + case 50718: /* DefaultScale */ + pixel_aspect = getreal(type); + pixel_aspect /= getreal(type); + break; + case 50721: /* ColorMatrix1 */ + case 50722: /* ColorMatrix2 */ + FORCC for (j=0; j < 3; j++) + cm[c][j] = getreal(type); + use_cm = 1; + break; + case 50723: /* CameraCalibration1 */ + case 50724: /* CameraCalibration2 */ + for (i=0; i < colors; i++) + FORCC cc[i][c] = getreal(type); + case 50727: /* AnalogBalance */ + FORCC ab[c] = getreal(type); + break; + case 50728: /* AsShotNeutral */ + FORCC asn[c] = getreal(type); + break; + case 50729: /* AsShotWhiteXY */ + xyz[0] = getreal(type); + xyz[1] = getreal(type); + xyz[2] = 1 - xyz[0] - xyz[1]; + FORC3 xyz[c] /= d65_white[c]; + break; + case 50740: /* DNGPrivateData */ + if (dng_version) break; + parse_minolta (j = get4()+base); + fseek (ifp, j, SEEK_SET); + parse_tiff_ifd (base); + break; + case 50752: + read_shorts (cr2_slice, 3); + break; + case 50829: /* ActiveArea */ + top_margin = getint(type); + left_margin = getint(type); + height = getint(type) - top_margin; + width = getint(type) - left_margin; + break; + case 64772: /* Kodak P-series */ + fseek (ifp, 16, SEEK_CUR); + data_offset = get4(); + fseek (ifp, 28, SEEK_CUR); + data_offset += get4(); + load_raw = &CLASS packed_12_load_raw; + } + fseek (ifp, save, SEEK_SET); + } + if (sony_length && (buf = (unsigned *) malloc(sony_length))) { + fseek (ifp, sony_offset, SEEK_SET); + fread (buf, sony_length, 1, ifp); + sony_decrypt (buf, sony_length/4, 1, sony_key); +#ifndef LIBRAW_LIBRARY_BUILD + sfp = ifp; + if ((ifp = tmpfile())) { + fwrite (buf, sony_length, 1, ifp); + fseek (ifp, 0, SEEK_SET); + parse_tiff_ifd (-sony_offset); + fclose (ifp); + } + ifp = sfp; +#else + if( !ifp->tempbuffer_open(buf,sony_length)) + { + parse_tiff_ifd(-sony_offset); + ifp->tempbuffer_close(); + } +#endif + free (buf); + } + for (i=0; i < colors; i++) + FORCC cc[i][c] *= ab[i]; + if (use_cm) { + FORCC for (i=0; i < 3; i++) + for (cam_xyz[c][i]=j=0; j < colors; j++) + cam_xyz[c][i] += cc[c][j] * cm[j][i] * xyz[i]; + cam_xyz_coeff (cam_xyz); + } + if (asn[0]) { + cam_mul[3] = 0; + FORCC cam_mul[c] = 1 / asn[c]; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + if (!use_cm) + { + FORCC pre_mul[c] /= cc[c][c]; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + + return 0; +} + +void CLASS parse_tiff (int base) +{ + int doff, max_samp=0, raw=-1, thm=-1, i; + struct jhead jh; + + fseek (ifp, base, SEEK_SET); + order = get2(); + if (order != 0x4949 && order != 0x4d4d) return; + get2(); + memset (tiff_ifd, 0, sizeof tiff_ifd); + tiff_nifds = 0; + while ((doff = get4())) { + fseek (ifp, doff+base, SEEK_SET); + if (parse_tiff_ifd (base)) break; + } + thumb_misc = 16; + if (thumb_offset) { + fseek (ifp, thumb_offset, SEEK_SET); + if (ljpeg_start (&jh, 1)) { + thumb_misc = jh.bits; + thumb_width = jh.wide; + thumb_height = jh.high; + } + } + for (i=0; i < tiff_nifds; i++) { + if (max_samp < tiff_ifd[i].samples) + max_samp = tiff_ifd[i].samples; + if (max_samp > 3) max_samp = 3; + if ((tiff_ifd[i].comp != 6 || tiff_ifd[i].samples != 3) && + tiff_ifd[i].t_width*tiff_ifd[i].t_height > raw_width*raw_height) { + raw_width = tiff_ifd[i].t_width; + raw_height = tiff_ifd[i].t_height; + tiff_bps = tiff_ifd[i].bps; + tiff_compress = tiff_ifd[i].comp; + data_offset = tiff_ifd[i].offset; + tiff_flip = tiff_ifd[i].t_flip; + tiff_samples = tiff_ifd[i].samples; + raw = i; + } + } + fuji_width *= (raw_width+1)/2; + if (tiff_ifd[0].t_flip) tiff_flip = tiff_ifd[0].t_flip; + if (raw >= 0 && !load_raw) + switch (tiff_compress) { + case 0: case 1: + switch (tiff_bps) { + case 8: load_raw = &CLASS eight_bit_load_raw; break; + case 12: load_raw = &CLASS packed_12_load_raw; + if (tiff_ifd[raw].phint == 2) + load_flags = 6; + if (strncmp(make,"PENTAX",6)) break; + case 14: + case 16: load_raw = &CLASS unpacked_load_raw; break; + } + if (tiff_ifd[raw].bytes*5 == raw_width*raw_height*8) + load_raw = &CLASS olympus_e300_load_raw; + break; + case 6: case 7: case 99: + load_raw = &CLASS lossless_jpeg_load_raw; break; + case 262: + load_raw = &CLASS kodak_262_load_raw; break; + case 32767: + load_raw = &CLASS sony_arw2_load_raw; + if (tiff_ifd[raw].bytes*8 == raw_width*raw_height*tiff_bps) + break; + raw_height += 8; + load_raw = &CLASS sony_arw_load_raw; break; + case 32769: + load_flags = 8; + case 32773: + load_raw = &CLASS packed_12_load_raw; break; + case 34713: + load_raw = &CLASS nikon_compressed_load_raw; break; + case 65535: + load_raw = &CLASS pentax_k10_load_raw; break; + case 65000: + switch (tiff_ifd[raw].phint) { + case 2: load_raw = &CLASS kodak_rgb_load_raw; filters = 0; break; + case 6: load_raw = &CLASS kodak_ycbcr_load_raw; filters = 0; break; + case 32803: load_raw = &CLASS kodak_65000_load_raw; + } + case 32867: break; + default: is_raw = 0; + } + if (!dng_version && tiff_samples == 3) + if (tiff_ifd[raw].bytes && tiff_bps != 14 && tiff_bps != 2048) + is_raw = 0; + if (!dng_version && tiff_bps == 8 && tiff_compress == 1 && + tiff_ifd[raw].phint == 1) is_raw = 0; + if (tiff_bps == 8 && tiff_samples == 4) is_raw = 0; + for (i=0; i < tiff_nifds; i++) + if (i != raw && tiff_ifd[i].samples == max_samp && + tiff_ifd[i].t_width * tiff_ifd[i].t_height / SQR(tiff_ifd[i].bps+1) > + thumb_width * thumb_height / SQR(thumb_misc+1)) { + thumb_width = tiff_ifd[i].t_width; + thumb_height = tiff_ifd[i].t_height; + thumb_offset = tiff_ifd[i].offset; + thumb_length = tiff_ifd[i].bytes; + thumb_misc = tiff_ifd[i].bps; + thm = i; + } + if (thm >= 0) { + thumb_misc |= tiff_ifd[thm].samples << 5; + switch (tiff_ifd[thm].comp) { + case 0: + write_thumb = &CLASS layer_thumb; + break; + case 1: + if (tiff_ifd[thm].bps > 8) + thumb_load_raw = &CLASS kodak_thumb_load_raw; + else + write_thumb = &CLASS ppm_thumb; + break; + case 65000: + thumb_load_raw = tiff_ifd[thm].phint == 6 ? + &CLASS kodak_ycbcr_load_raw : &CLASS kodak_rgb_load_raw; + } + } +} + +void CLASS parse_minolta (int base) +{ + int save, tag, len, offset, high=0, wide=0, i, c; + short sorder=order; + + fseek (ifp, base, SEEK_SET); + if (fgetc(ifp) || fgetc(ifp)-'M' || fgetc(ifp)-'R') return; + order = fgetc(ifp) * 0x101; + offset = base + get4() + 8; + while ((save=ftell(ifp)) < offset) { + for (tag=i=0; i < 4; i++) + tag = tag << 8 | fgetc(ifp); + len = get4(); + switch (tag) { + case 0x505244: /* PRD */ + fseek (ifp, 8, SEEK_CUR); + high = get2(); + wide = get2(); + break; + case 0x574247: /* WBG */ + get4(); + i = strcmp(model,"DiMAGE A200") ? 0:3; + FORC4 cam_mul[c ^ (c >> 1) ^ i] = get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + break; + case 0x545457: /* TTW */ + parse_tiff (ftell(ifp)); + data_offset = offset; + } + fseek (ifp, save+len+8, SEEK_SET); + } + raw_height = high; + raw_width = wide; + order = sorder; +} + +/* + Many cameras have a "debug mode" that writes JPEG and raw + at the same time. The raw file has no header, so try to + to open the matching JPEG file and read its metadata. + */ +void CLASS parse_external_jpeg() +{ + char *file, *ext, *jname, *jfile, *jext; +#ifndef LIBRAW_LIBRARY_BUILD + FILE *save=ifp; +#else + if(!ifp->fname()) + { + imgdata.process_warnings |= LIBRAW_WARN_NO_METADATA ; + return; + } +#endif + + ext = strrchr (ifname, '.'); + file = strrchr (ifname, '/'); + if (!file) file = strrchr (ifname, '\\'); +#ifndef LIBRAW_LIBRARY_BUILD + if (!file) file = ifname-1; +#else + if (!file) file = (char*)ifname-1; +#endif + file++; + if (!ext || strlen(ext) != 4 || ext-file != 8) return; + jname = (char *) malloc (strlen(ifname) + 1); + merror (jname, "parse_external_jpeg()"); + strcpy (jname, ifname); + jfile = file - ifname + jname; + jext = ext - ifname + jname; + if (strcasecmp (ext, ".jpg")) { + strcpy (jext, isupper(ext[1]) ? ".JPG":".jpg"); + if (isdigit(*file)) { + memcpy (jfile, file+4, 4); + memcpy (jfile+4, file, 4); + } + } else + while (isdigit(*--jext)) { + if (*jext != '9') { + (*jext)++; + break; + } + *jext = '0'; + } +#ifndef LIBRAW_LIBRARY_BUILD + if (strcmp (jname, ifname)) { + if ((ifp = fopen (jname, "rb"))) { +#ifdef DCRAW_VERBOSE + if (verbose) + fprintf (stderr,_("Reading metadata from %s ...\n"), jname); +#endif + parse_tiff (12); + thumb_offset = 0; + is_raw = 1; + fclose (ifp); + } + } +#else + if (strcmp (jname, ifname)) + { + if(!ifp->subfile_open(jname)) + { + parse_tiff (12); + thumb_offset = 0; + is_raw = 1; + ifp->subfile_close(); + } + else + imgdata.process_warnings |= LIBRAW_WARN_NO_METADATA ; + } +#endif + if (!timestamp) + { +#ifdef LIBRAW_LIBRARY_BUILD + imgdata.process_warnings |= LIBRAW_WARN_NO_METADATA ; +#endif +#ifdef DCRAW_VERBOSE + fprintf (stderr,_("Failed to read metadata from %s\n"), jname); +#endif + } + free (jname); +#ifndef LIBRAW_LIBRARY_BUILD + ifp = save; +#endif +} + +/* + CIFF block 0x1030 contains an 8x8 white sample. + Load this into white[][] for use in scale_colors(). + */ +void CLASS ciff_block_1030() +{ + static const ushort key[] = { 0x410, 0x45f3 }; + int i, bpp, row, col, vbits=0; + unsigned long bitbuf=0; + + if ((get2(),get4()) != 0x80008 || !get4()) return; + bpp = get2(); + if (bpp != 10 && bpp != 12) return; + for (i=row=0; row < 8; row++) + for (col=0; col < 8; col++) { + if (vbits < bpp) { + bitbuf = bitbuf << 16 | (get2() ^ key[i++ & 1]); + vbits += 16; + } + white[row][col] = + bitbuf << (LONG_BIT - vbits) >> (LONG_BIT - bpp); + vbits -= bpp; + } +} + +/* + Parse a CIFF file, better known as Canon CRW format. + */ +void CLASS parse_ciff (int offset, int length) +{ + int tboff, nrecs, c, type, len, save, wbi=-1; + ushort key[] = { 0x410, 0x45f3 }; + + fseek (ifp, offset+length-4, SEEK_SET); + tboff = get4() + offset; + fseek (ifp, tboff, SEEK_SET); + nrecs = get2(); + if (nrecs > 100) return; + while (nrecs--) { + type = get2(); + len = get4(); + save = ftell(ifp) + 4; + fseek (ifp, offset+get4(), SEEK_SET); + if ((((type >> 8) + 8) | 8) == 0x38) + parse_ciff (ftell(ifp), len); /* Parse a sub-table */ + + if (type == 0x0810) + fread (artist, 64, 1, ifp); + if (type == 0x080a) { + fread (make, 64, 1, ifp); + fseek (ifp, strlen(make) - 63, SEEK_CUR); + fread (model, 64, 1, ifp); + } + if (type == 0x1810) { + fseek (ifp, 12, SEEK_CUR); + flip = get4(); + } + if (type == 0x1835) /* Get the decoder table */ + tiff_compress = get4(); + if (type == 0x2007) { + thumb_offset = ftell(ifp); + thumb_length = len; + } + if (type == 0x1818) { + shutter = pow (2.0f, -int_to_float((get4(),get4()))); + aperture = pow (2.0f, int_to_float(get4())/2); + } + if (type == 0x102a) { + iso_speed = pow (2, (get4(),get2())/32.0 - 4) * 50; + aperture = pow (2, (get2(),(short)get2())/64.0); + shutter = pow (2,-((short)get2())/32.0); + wbi = (get2(),get2()); + if (wbi > 17) wbi = 0; + fseek (ifp, 32, SEEK_CUR); + if (shutter > 1e6) shutter = get2()/10.0; + } + if (type == 0x102c) { + if (get2() > 512) { /* Pro90, G1 */ + fseek (ifp, 118, SEEK_CUR); + FORC4 cam_mul[c ^ 2] = get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } else { /* G2, S30, S40 */ + fseek (ifp, 98, SEEK_CUR); + FORC4 cam_mul[c ^ (c >> 1) ^ 1] = get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + } + if (type == 0x0032) { + if (len == 768) { /* EOS D30 */ + fseek (ifp, 72, SEEK_CUR); + FORC4 cam_mul[c ^ (c >> 1)] = 1024.0 / get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + if (!wbi) cam_mul[0] = -1; /* use my auto white balance */ + } else if (!cam_mul[0]) { + if (get2() == key[0]) /* Pro1, G6, S60, S70 */ + c = (strstr(model,"Pro1") ? + "012346000000000000":"01345:000000006008")[wbi]-'0'+ 2; + else { /* G3, G5, S45, S50 */ + c = "023457000000006000"[wbi]-'0'; + key[0] = key[1] = 0; + } + fseek (ifp, 78 + c*8, SEEK_CUR); + FORC4 cam_mul[c ^ (c >> 1) ^ 1] = get2() ^ key[c & 1]; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + if (!wbi) cam_mul[0] = -1; + } + } + if (type == 0x10a9) { /* D60, 10D, 300D, and clones */ + if (len > 66) wbi = "0134567028"[wbi]-'0'; + fseek (ifp, 2 + wbi*8, SEEK_CUR); + FORC4 cam_mul[c ^ (c >> 1)] = get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + if (type == 0x1030 && (0x18040 >> wbi & 1)) + ciff_block_1030(); /* all that don't have 0x10a9 */ + if (type == 0x1031) { + raw_width = (get2(),get2()); + raw_height = get2(); + } + if (type == 0x5029) { + focal_len = len >> 16; + if ((len & 0xffff) == 2) focal_len /= 32; + } + if (type == 0x5813) flash_used = int_to_float(len); + if (type == 0x5814) canon_ev = int_to_float(len); + if (type == 0x5817) shot_order = len; + if (type == 0x5834) unique_id = len; + if (type == 0x580e) timestamp = len; + if (type == 0x180e) timestamp = get4(); +#ifdef LOCALTIME + if ((type | 0x4000) == 0x580e) + timestamp = mktime (gmtime (×tamp)); +#endif + fseek (ifp, save, SEEK_SET); + } +} + +void CLASS parse_rollei() +{ + char line[128], *val; + struct tm t; + + fseek (ifp, 0, SEEK_SET); + memset (&t, 0, sizeof t); + do { + fgets (line, 128, ifp); + if ((val = strchr(line,'='))) + *val++ = 0; + else + val = line + strlen(line); + if (!strcmp(line,"DAT")) + sscanf (val, "%d.%d.%d", &t.tm_mday, &t.tm_mon, &t.tm_year); + if (!strcmp(line,"TIM")) + sscanf (val, "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec); + if (!strcmp(line,"HDR")) + thumb_offset = atoi(val); + if (!strcmp(line,"X ")) + raw_width = atoi(val); + if (!strcmp(line,"Y ")) + raw_height = atoi(val); + if (!strcmp(line,"TX ")) + thumb_width = atoi(val); + if (!strcmp(line,"TY ")) + thumb_height = atoi(val); + } while (strncmp(line,"EOHD",4)); + data_offset = thumb_offset + thumb_width * thumb_height * 2; + t.tm_year -= 1900; + t.tm_mon -= 1; + if (mktime(&t) > 0) + timestamp = mktime(&t); + strcpy (make, "Rollei"); + strcpy (model,"d530flex"); + write_thumb = &CLASS rollei_thumb; +} + +void CLASS parse_sinar_ia() +{ + int entries, off; + char str[8], *cp; + + order = 0x4949; + fseek (ifp, 4, SEEK_SET); + entries = get4(); + fseek (ifp, get4(), SEEK_SET); + while (entries--) { + off = get4(); get4(); + fread (str, 8, 1, ifp); + if (!strcmp(str,"META")) meta_offset = off; + if (!strcmp(str,"THUMB")) thumb_offset = off; + if (!strcmp(str,"RAW0")) data_offset = off; + } + fseek (ifp, meta_offset+20, SEEK_SET); + fread (make, 64, 1, ifp); + make[63] = 0; + if ((cp = strchr(make,' '))) { + strcpy (model, cp+1); + *cp = 0; + } + raw_width = get2(); + raw_height = get2(); + load_raw = &CLASS unpacked_load_raw; + thumb_width = (get4(),get2()); + thumb_height = get2(); + write_thumb = &CLASS ppm_thumb; + maximum = 0x3fff; +} + +void CLASS parse_phase_one (int base) +{ + unsigned entries, tag, type, len, data, save, i, c; + float romm_cam[3][3]; + char *cp; + + memset (&ph1, 0, sizeof ph1); + fseek (ifp, base, SEEK_SET); + order = get4() & 0xffff; + if (get4() >> 8 != 0x526177) return; /* "Raw" */ + fseek (ifp, get4()+base, SEEK_SET); + entries = get4(); + get4(); + while (entries--) { + tag = get4(); + type = get4(); + len = get4(); + data = get4(); + save = ftell(ifp); + fseek (ifp, base+data, SEEK_SET); + switch (tag) { + case 0x100: flip = "0653"[data & 3]-'0'; break; + case 0x106: + for (i=0; i < 9; i++) + romm_cam[0][i] = getreal(11); + romm_coeff (romm_cam); + break; + case 0x107: + FORC3 cam_mul[c] = getreal(11); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + break; + case 0x108: raw_width = data; break; + case 0x109: raw_height = data; break; + case 0x10a: left_margin = data; break; + case 0x10b: top_margin = data; break; + case 0x10c: width = data; break; + case 0x10d: height = data; break; + case 0x10e: ph1.format = data; break; + case 0x10f: data_offset = data+base; break; + case 0x110: meta_offset = data+base; + meta_length = len; break; + case 0x112: ph1.key_off = save - 4; break; + case 0x210: ph1.tag_210 = int_to_float(data); break; + case 0x21a: ph1.tag_21a = data; break; + case 0x21c: strip_offset = data+base; break; + case 0x21d: ph1.t_black = data; break; + case 0x222: ph1.split_col = data - left_margin; break; + case 0x223: ph1.black_off = data+base; break; + case 0x301: + model[63] = 0; + fread (model, 1, 63, ifp); + if ((cp = strstr(model," camera"))) *cp = 0; + } + fseek (ifp, save, SEEK_SET); + } + load_raw = ph1.format < 3 ? + &CLASS phase_one_load_raw : &CLASS phase_one_load_raw_c; + maximum = 0xffff; + strcpy (make, "Phase One"); + if (model[0]) return; + switch (raw_height) { + case 2060: strcpy (model,"LightPhase"); break; + case 2682: strcpy (model,"H 10"); break; + case 4128: strcpy (model,"H 20"); break; + case 5488: strcpy (model,"H 25"); break; + } +} + +void CLASS parse_fuji (int offset) +{ + unsigned entries, tag, len, save, c; + + fseek (ifp, offset, SEEK_SET); + entries = get4(); + if (entries > 255) return; + while (entries--) { + tag = get2(); + len = get2(); + save = ftell(ifp); + if (tag == 0x100) { + raw_height = get2(); + raw_width = get2(); + } else if (tag == 0x121) { + height = get2(); + if ((width = get2()) == 4284) width += 3; + } else if (tag == 0x130) + fuji_layout = fgetc(ifp) >> 7; + if (tag == 0x2ff0) + { + FORC4 cam_mul[c ^ 1] = get2(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } + fseek (ifp, save+len, SEEK_SET); + } + height <<= fuji_layout; + width >>= fuji_layout; +} + +int CLASS parse_jpeg (int offset) +{ + int len, save, hlen, mark; + + fseek (ifp, offset, SEEK_SET); + if (fgetc(ifp) != 0xff || fgetc(ifp) != 0xd8) return 0; + + while (fgetc(ifp) == 0xff && (mark = fgetc(ifp)) != 0xda) { + order = 0x4d4d; + len = get2() - 2; + save = ftell(ifp); + if (mark == 0xc0 || mark == 0xc3) { + fgetc(ifp); + raw_height = get2(); + raw_width = get2(); + } + order = get2(); + hlen = get4(); + if (get4() == 0x48454150) /* "HEAP" */ + parse_ciff (save+hlen, len-hlen); + parse_tiff (save+6); + fseek (ifp, save+len, SEEK_SET); + } + return 1; +} + +void CLASS parse_riff() +{ + unsigned i, size, end; + char tag[4], date[64], month[64]; + static const char mon[12][4] = + { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" }; + struct tm t; + + order = 0x4949; + fread (tag, 4, 1, ifp); + size = get4(); + end = ftell(ifp) + size; + if (!memcmp(tag,"RIFF",4) || !memcmp(tag,"LIST",4)) { + get4(); + while (ftell(ifp) < end) + parse_riff(); + } else if (!memcmp(tag,"nctg",4)) { + while (ftell(ifp) < end) { + i = get2(); + size = get2(); + if ((i+1) >> 1 == 10 && size == 20) + get_timestamp(0); + else fseek (ifp, size, SEEK_CUR); + } + } else if (!memcmp(tag,"IDIT",4) && size < 64) { + fread (date, 64, 1, ifp); + date[size] = 0; + memset (&t, 0, sizeof t); + if (sscanf (date, "%*s %s %d %d:%d:%d %d", month, &t.tm_mday, + &t.tm_hour, &t.tm_min, &t.tm_sec, &t.tm_year) == 6) { + for (i=0; i < 12 && strcasecmp(mon[i],month); i++); + t.tm_mon = i; + t.tm_year -= 1900; + if (mktime(&t) > 0) + timestamp = mktime(&t); + } + } else + fseek (ifp, size, SEEK_CUR); +} + +void CLASS parse_smal (int offset, int fsize) +{ + int ver; + + fseek (ifp, offset+2, SEEK_SET); + order = 0x4949; + ver = fgetc(ifp); + if (ver == 6) + fseek (ifp, 5, SEEK_CUR); + if (get4() != fsize) return; + if (ver > 6) data_offset = get4(); + raw_height = height = get2(); + raw_width = width = get2(); + strcpy (make, "SMaL"); + sprintf (model, "v%d %dx%d", ver, width, height); + if (ver == 6) load_raw = &CLASS smal_v6_load_raw; + if (ver == 9) load_raw = &CLASS smal_v9_load_raw; +} + +void CLASS parse_cine() +{ + unsigned off_head, off_setup, off_image, i; + + order = 0x4949; + fseek (ifp, 4, SEEK_SET); + is_raw = get2() == 2; + fseek (ifp, 14, SEEK_CUR); + is_raw *= get4(); + off_head = get4(); + off_setup = get4(); + off_image = get4(); + timestamp = get4(); + if ((i = get4())) timestamp = i; + fseek (ifp, off_head+4, SEEK_SET); + raw_width = get4(); + raw_height = get4(); + switch (get2(),get2()) { + case 8: load_raw = &CLASS eight_bit_load_raw; break; + case 16: load_raw = &CLASS unpacked_load_raw; + } + fseek (ifp, off_setup+792, SEEK_SET); + strcpy (make, "CINE"); + sprintf (model, "%d", get4()); + fseek (ifp, 12, SEEK_CUR); + switch ((i=get4()) & 0xffffff) { + case 3: filters = 0x94949494; break; + case 4: filters = 0x49494949; break; + default: is_raw = 0; + } + fseek (ifp, 72, SEEK_CUR); + switch ((get4()+3600) % 360) { + case 270: flip = 4; break; + case 180: flip = 1; break; + case 90: flip = 7; break; + case 0: flip = 2; + } + cam_mul[0] = getreal(11); + cam_mul[2] = getreal(11); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + maximum = ~(-1 << get4()); + fseek (ifp, 668, SEEK_CUR); + shutter = get4()/1000000000.0; + fseek (ifp, off_image, SEEK_SET); + if (shot_select < is_raw) + fseek (ifp, shot_select*8, SEEK_CUR); + data_offset = (INT64) get4() + 8; + data_offset += (INT64) get4() << 32; +} +#line 7187 "dcraw/dcraw.c" +#ifdef LIBRAW_LIBRARY_BUILD +void CLASS adobe_coeff (const char *p_make, const char *p_model) +#else +void CLASS adobe_coeff (char *p_make, char *p_model) +#endif +{ + static const struct { + const char *prefix; + short t_black, t_maximum, trans[12]; + } table[] = { + { "Apple QuickTake", 0, 0, /* DJC */ + { 17576,-3191,-3318,5210,6733,-1942,9031,1280,-124 } }, + { "Canon EOS D2000", 0, 0, + { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } }, + { "Canon EOS D6000", 0, 0, + { 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } }, + { "Canon EOS D30", 0, 0, + { 9805,-2689,-1312,-5803,13064,3068,-2438,3075,8775 } }, + { "Canon EOS D60", 0, 0xfa0, + { 6188,-1341,-890,-7168,14489,2937,-2640,3228,8483 } }, + { "Canon EOS 5D Mark II", 0, 0x3cf0, + { 4716,603,-830,-7798,15474,2480,-1496,1937,6651 } }, + { "Canon EOS 5D", 0, 0xe6c, + { 6347,-479,-972,-8297,15954,2480,-1968,2131,7649 } }, + { "Canon EOS 10D", 0, 0xfa0, + { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } }, + { "Canon EOS 20Da", 0, 0, + { 14155,-5065,-1382,-6550,14633,2039,-1623,1824,6561 } }, + { "Canon EOS 20D", 0, 0xfff, + { 6599,-537,-891,-8071,15783,2424,-1983,2234,7462 } }, + { "Canon EOS 30D", 0, 0, + { 6257,-303,-1000,-7880,15621,2396,-1714,1904,7046 } }, + { "Canon EOS 40D", 0, 0x3f60, + { 6071,-747,-856,-7653,15365,2441,-2025,2553,7315 } }, + { "Canon EOS 50D", 0, 0x3d93, + { 4920,616,-593,-6493,13964,2784,-1774,3178,7005 } }, + { "Canon EOS 300D", 0, 0xfa0, + { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } }, + { "Canon EOS 350D", 0, 0xfff, + { 6018,-617,-965,-8645,15881,2975,-1530,1719,7642 } }, + { "Canon EOS 400D", 0, 0xe8e, + { 7054,-1501,-990,-8156,15544,2812,-1278,1414,7796 } }, + { "Canon EOS 450D", 0, 0x390d, + { 5784,-262,-821,-7539,15064,2672,-1982,2681,7427 } }, + { "Canon EOS 1000D", 0, 0xe43, + { 6771,-1139,-977,-7818,15123,2928,-1244,1437,7533 } }, + { "Canon EOS-1Ds Mark III", 0, 0x3bb0, + { 5859,-211,-930,-8255,16017,2353,-1732,1887,7448 } }, + { "Canon EOS-1Ds Mark II", 0, 0xe80, + { 6517,-602,-867,-8180,15926,2378,-1618,1771,7633 } }, + { "Canon EOS-1D Mark II N", 0, 0xe80, + { 6240,-466,-822,-8180,15825,2500,-1801,1938,8042 } }, + { "Canon EOS-1D Mark III", 0, 0x3bb0, + { 6291,-540,-976,-8350,16145,2311,-1714,1858,7326 } }, + { "Canon EOS-1D Mark II", 0, 0xe80, + { 6264,-582,-724,-8312,15948,2504,-1744,1919,8664 } }, + { "Canon EOS-1DS", 0, 0xe20, + { 4374,3631,-1743,-7520,15212,2472,-2892,3632,8161 } }, + { "Canon EOS-1D", 0, 0xe20, + { 6806,-179,-1020,-8097,16415,1687,-3267,4236,7690 } }, + { "Canon EOS", 0, 0, + { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } }, + { "Canon PowerShot A50", 0, 0, + { -5300,9846,1776,3436,684,3939,-5540,9879,6200,-1404,11175,217 } }, + { "Canon PowerShot A5", 0, 0, + { -4801,9475,1952,2926,1611,4094,-5259,10164,5947,-1554,10883,547 } }, + { "Canon PowerShot G10", 0, 0, + { 11093,-3906,-1028,-5047,12492,2879,-1003,1750,5561 } }, + { "Canon PowerShot G1", 0, 0, + { -4778,9467,2172,4743,-1141,4344,-5146,9908,6077,-1566,11051,557 } }, + { "Canon PowerShot G2", 0, 0, + { 9087,-2693,-1049,-6715,14382,2537,-2291,2819,7790 } }, + { "Canon PowerShot G3", 0, 0, + { 9212,-2781,-1073,-6573,14189,2605,-2300,2844,7664 } }, + { "Canon PowerShot G5", 0, 0, + { 9757,-2872,-933,-5972,13861,2301,-1622,2328,7212 } }, + { "Canon PowerShot G6", 0, 0, + { 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } }, + { "Canon PowerShot G9", 0, 0, + { 7368,-2141,-598,-5621,13254,2625,-1418,1696,5743 } }, + { "Canon PowerShot Pro1", 0, 0, + { 10062,-3522,-999,-7643,15117,2730,-765,817,7323 } }, + { "Canon PowerShot Pro70", 34, 0, + { -4155,9818,1529,3939,-25,4522,-5521,9870,6610,-2238,10873,1342 } }, + { "Canon PowerShot Pro90", 0, 0, + { -4963,9896,2235,4642,-987,4294,-5162,10011,5859,-1770,11230,577 } }, + { "Canon PowerShot S30", 0, 0, + { 10566,-3652,-1129,-6552,14662,2006,-2197,2581,7670 } }, + { "Canon PowerShot S40", 0, 0, + { 8510,-2487,-940,-6869,14231,2900,-2318,2829,9013 } }, + { "Canon PowerShot S45", 0, 0, + { 8163,-2333,-955,-6682,14174,2751,-2077,2597,8041 } }, + { "Canon PowerShot S50", 0, 0, + { 8882,-2571,-863,-6348,14234,2288,-1516,2172,6569 } }, + { "Canon PowerShot S60", 0, 0, + { 8795,-2482,-797,-7804,15403,2573,-1422,1996,7082 } }, + { "Canon PowerShot S70", 0, 0, + { 9976,-3810,-832,-7115,14463,2906,-901,989,7889 } }, + { "Canon PowerShot A610", 0, 0, /* DJC */ + { 15591,-6402,-1592,-5365,13198,2168,-1300,1824,5075 } }, + { "Canon PowerShot A620", 0, 0, /* DJC */ + { 15265,-6193,-1558,-4125,12116,2010,-888,1639,5220 } }, + { "Canon PowerShot A630", 0, 0, /* DJC */ + { 14201,-5308,-1757,-6087,14472,1617,-2191,3105,5348 } }, + { "Canon PowerShot A640", 0, 0, /* DJC */ + { 13124,-5329,-1390,-3602,11658,1944,-1612,2863,4885 } }, + { "Canon PowerShot A650", 0, 0, /* DJC */ + { 9427,-3036,-959,-2581,10671,1911,-1039,1982,4430 } }, + { "Canon PowerShot A720", 0, 0, /* DJC */ + { 14573,-5482,-1546,-1266,9799,1468,-1040,1912,3810 } }, + { "Canon PowerShot S3 IS", 0, 0, /* DJC */ + { 14062,-5199,-1446,-4712,12470,2243,-1286,2028,4836 } }, + { "CINE 650", 0, 0, + { 3390,480,-500,-800,3610,340,-550,2336,1192 } }, + { "CINE 660", 0, 0, + { 3390,480,-500,-800,3610,340,-550,2336,1192 } }, + { "CINE", 0, 0, + { 20183,-4295,-423,-3940,15330,3985,-280,4870,9800 } }, + { "Contax N Digital", 0, 0xf1e, + { 7777,1285,-1053,-9280,16543,2916,-3677,5679,7060 } }, + { "EPSON R-D1", 0, 0, + { 6827,-1878,-732,-8429,16012,2564,-704,592,7145 } }, + { "FUJIFILM FinePix E550", 0, 0, + { 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } }, + { "FUJIFILM FinePix E900", 0, 0, + { 9183,-2526,-1078,-7461,15071,2574,-2022,2440,8639 } }, + { "FUJIFILM FinePix F8", 0, 0, + { 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } }, + { "FUJIFILM FinePix F7", 0, 0, + { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } }, + { "FUJIFILM FinePix S100FS", 514, 0, + { 11521,-4355,-1065,-6524,13767,3058,-1466,1984,6045 } }, + { "FUJIFILM FinePix S20Pro", 0, 0, + { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } }, + { "FUJIFILM FinePix S2Pro", 128, 0, + { 12492,-4690,-1402,-7033,15423,1647,-1507,2111,7697 } }, + { "FUJIFILM FinePix S3Pro", 0, 0, + { 11807,-4612,-1294,-8927,16968,1988,-2120,2741,8006 } }, + { "FUJIFILM FinePix S5Pro", 0, 0, + { 12300,-5110,-1304,-9117,17143,1998,-1947,2448,8100 } }, + { "FUJIFILM FinePix S5000", 0, 0, + { 8754,-2732,-1019,-7204,15069,2276,-1702,2334,6982 } }, + { "FUJIFILM FinePix S5100", 0, 0x3e00, + { 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } }, + { "FUJIFILM FinePix S5500", 0, 0x3e00, + { 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } }, + { "FUJIFILM FinePix S5200", 0, 0, + { 9636,-2804,-988,-7442,15040,2589,-1803,2311,8621 } }, + { "FUJIFILM FinePix S5600", 0, 0, + { 9636,-2804,-988,-7442,15040,2589,-1803,2311,8621 } }, + { "FUJIFILM FinePix S6", 0, 0, + { 12628,-4887,-1401,-6861,14996,1962,-2198,2782,7091 } }, + { "FUJIFILM FinePix S7000", 0, 0, + { 10190,-3506,-1312,-7153,15051,2238,-2003,2399,7505 } }, + { "FUJIFILM FinePix S9000", 0, 0, + { 10491,-3423,-1145,-7385,15027,2538,-1809,2275,8692 } }, + { "FUJIFILM FinePix S9500", 0, 0, + { 10491,-3423,-1145,-7385,15027,2538,-1809,2275,8692 } }, + { "FUJIFILM FinePix S9100", 0, 0, + { 12343,-4515,-1285,-7165,14899,2435,-1895,2496,8800 } }, + { "FUJIFILM FinePix S9600", 0, 0, + { 12343,-4515,-1285,-7165,14899,2435,-1895,2496,8800 } }, + { "FUJIFILM IS-1", 0, 0, + { 21461,-10807,-1441,-2332,10599,1999,289,875,7703 } }, + { "FUJIFILM IS Pro", 0, 0, + { 12300,-5110,-1304,-9117,17143,1998,-1947,2448,8100 } }, + { "Imacon Ixpress", 0, 0, /* DJC */ + { 7025,-1415,-704,-5188,13765,1424,-1248,2742,6038 } }, + { "KODAK NC2000", 0, 0, + { 13891,-6055,-803,-465,9919,642,2121,82,1291 } }, + { "Kodak DCS315C", 8, 0, + { 17523,-4827,-2510,756,8546,-137,6113,1649,2250 } }, + { "Kodak DCS330C", 8, 0, + { 20620,-7572,-2801,-103,10073,-396,3551,-233,2220 } }, + { "KODAK DCS420", 0, 0, + { 10868,-1852,-644,-1537,11083,484,2343,628,2216 } }, + { "KODAK DCS460", 0, 0, + { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } }, + { "KODAK EOSDCS1", 0, 0, + { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } }, + { "KODAK EOSDCS3B", 0, 0, + { 9898,-2700,-940,-2478,12219,206,1985,634,1031 } }, + { "Kodak DCS520C", 180, 0, + { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } }, + { "Kodak DCS560C", 188, 0, + { 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } }, + { "Kodak DCS620C", 180, 0, + { 23617,-10175,-3149,-2054,11749,-272,2586,-489,3453 } }, + { "Kodak DCS620X", 185, 0, + { 13095,-6231,154,12221,-21,-2137,895,4602,2258 } }, + { "Kodak DCS660C", 214, 0, + { 18244,-6351,-2739,-791,11193,-521,3711,-129,2802 } }, + { "Kodak DCS720X", 0, 0, + { 11775,-5884,950,9556,1846,-1286,-1019,6221,2728 } }, + { "Kodak DCS760C", 0, 0, + { 16623,-6309,-1411,-4344,13923,323,2285,274,2926 } }, + { "Kodak DCS Pro SLR", 0, 0, + { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } }, + { "Kodak DCS Pro 14nx", 0, 0, + { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } }, + { "Kodak DCS Pro 14", 0, 0, + { 7791,3128,-776,-8588,16458,2039,-2455,4006,6198 } }, + { "Kodak ProBack645", 0, 0, + { 16414,-6060,-1470,-3555,13037,473,2545,122,4948 } }, + { "Kodak ProBack", 0, 0, + { 21179,-8316,-2918,-915,11019,-165,3477,-180,4210 } }, + { "KODAK P712", 0, 0, + { 9658,-3314,-823,-5163,12695,2768,-1342,1843,6044 } }, + { "KODAK P850", 0, 0xf7c, + { 10511,-3836,-1102,-6946,14587,2558,-1481,1792,6246 } }, + { "KODAK P880", 0, 0xfff, + { 12805,-4662,-1376,-7480,15267,2360,-1626,2194,7904 } }, + { "Leaf CMost", 0, 0, + { 3952,2189,449,-6701,14585,2275,-4536,7349,6536 } }, + { "Leaf Valeo 6", 0, 0, + { 3952,2189,449,-6701,14585,2275,-4536,7349,6536 } }, + { "Leaf Aptus 54S", 0, 0, + { 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 } }, + { "Leaf Aptus 65", 0, 0, + { 7914,1414,-1190,-8777,16582,2280,-2811,4605,5562 } }, + { "Leaf Aptus 75", 0, 0, + { 7914,1414,-1190,-8777,16582,2280,-2811,4605,5562 } }, + { "Leaf", 0, 0, + { 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 } }, + { "Mamiya ZD", 0, 0, + { 7645,2579,-1363,-8689,16717,2015,-3712,5941,5961 } }, + { "Micron 2010", 110, 0, /* DJC */ + { 16695,-3761,-2151,155,9682,163,3433,951,4904 } }, + { "Minolta DiMAGE 5", 0, 0xf7d, + { 8983,-2942,-963,-6556,14476,2237,-2426,2887,8014 } }, + { "Minolta DiMAGE 7Hi", 0, 0xf7d, + { 11368,-3894,-1242,-6521,14358,2339,-2475,3056,7285 } }, + { "Minolta DiMAGE 7", 0, 0xf7d, + { 9144,-2777,-998,-6676,14556,2281,-2470,3019,7744 } }, + { "Minolta DiMAGE A1", 0, 0xf8b, + { 9274,-2547,-1167,-8220,16323,1943,-2273,2720,8340 } }, + { "MINOLTA DiMAGE A200", 0, 0, + { 8560,-2487,-986,-8112,15535,2771,-1209,1324,7743 } }, + { "Minolta DiMAGE A2", 0, 0xf8f, + { 9097,-2726,-1053,-8073,15506,2762,-966,981,7763 } }, + { "Minolta DiMAGE Z2", 0, 0, /* DJC */ + { 11280,-3564,-1370,-4655,12374,2282,-1423,2168,5396 } }, + { "MINOLTA DYNAX 5", 0, 0xffb, + { 10284,-3283,-1086,-7957,15762,2316,-829,882,6644 } }, + { "MINOLTA DYNAX 7", 0, 0xffb, + { 10239,-3104,-1099,-8037,15727,2451,-927,925,6871 } }, + { "NIKON D100", 0, 0, + { 5902,-933,-782,-8983,16719,2354,-1402,1455,6464 } }, + { "NIKON D1H", 0, 0, + { 7577,-2166,-926,-7454,15592,1934,-2377,2808,8606 } }, + { "NIKON D1X", 0, 0, + { 7702,-2245,-975,-9114,17242,1875,-2679,3055,8521 } }, + { "NIKON D1", 0, 0, /* multiplied by 2.218750, 1.0, 1.148438 */ + { 16772,-4726,-2141,-7611,15713,1972,-2846,3494,9521 } }, + { "NIKON D2H", 0, 0, + { 5710,-901,-615,-8594,16617,2024,-2975,4120,6830 } }, + { "NIKON D2X", 0, 0, + { 10231,-2769,-1255,-8301,15900,2552,-797,680,7148 } }, + { "NIKON D40X", 0, 0, + { 8819,-2543,-911,-9025,16928,2151,-1329,1213,8449 } }, + { "NIKON D40", 0, 0, + { 6992,-1668,-806,-8138,15748,2543,-874,850,7897 } }, + { "NIKON D50", 0, 0, + { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } }, + { "NIKON D60", 0, 0, + { 8736,-2458,-935,-9075,16894,2251,-1354,1242,8263 } }, + { "NIKON D700", 0, 0, + { 8139,-2171,-663,-8747,16541,2295,-1925,2008,8093 } }, + { "NIKON D70", 0, 0, + { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } }, + { "NIKON D80", 0, 0, + { 8629,-2410,-883,-9055,16940,2171,-1490,1363,8520 } }, + { "NIKON D90", 0, 0xf00, + { 7309,-1403,-519,-8474,16008,2622,-2434,2826,8064 } }, + { "NIKON D200", 0, 0xfbc, + { 8367,-2248,-763,-8758,16447,2422,-1527,1550,8053 } }, + { "NIKON D300", 0, 0, + { 9030,-1992,-715,-8465,16302,2255,-2689,3217,8069 } }, + { "NIKON D3", 0, 0, + { 8139,-2171,-663,-8747,16541,2295,-1925,2008,8093 } }, + { "NIKON E950", 0, 0x3dd, /* DJC */ + { -3746,10611,1665,9621,-1734,2114,-2389,7082,3064,3406,6116,-244 } }, + { "NIKON E995", 0, 0, /* copied from E5000 */ + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E2100", 0, 0, /* copied from Z2, new white balance */ + { 13142,-4152,-1596,-4655,12374,2282,-1769,2696,6711} }, + { "NIKON E2500", 0, 0, + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E4300", 0, 0, /* copied from Minolta DiMAGE Z2 */ + { 11280,-3564,-1370,-4655,12374,2282,-1423,2168,5396 } }, + { "NIKON E4500", 0, 0, + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E5000", 0, 0, + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E5400", 0, 0, + { 9349,-2987,-1001,-7919,15766,2266,-2098,2680,6839 } }, + { "NIKON E5700", 0, 0, + { -5368,11478,2368,5537,-113,3148,-4969,10021,5782,778,9028,211 } }, + { "NIKON E8400", 0, 0, + { 7842,-2320,-992,-8154,15718,2599,-1098,1342,7560 } }, + { "NIKON E8700", 0, 0, + { 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } }, + { "NIKON E8800", 0, 0, + { 7971,-2314,-913,-8451,15762,2894,-1442,1520,7610 } }, + { "NIKON COOLPIX P6000", 0, 0, + { 9698,-3367,-914,-4706,12584,2368,-837,968,5801 } }, + { "OLYMPUS C5050", 0, 0, + { 10508,-3124,-1273,-6079,14294,1901,-1653,2306,6237 } }, + { "OLYMPUS C5060", 0, 0, + { 10445,-3362,-1307,-7662,15690,2058,-1135,1176,7602 } }, + { "OLYMPUS C7070", 0, 0, + { 10252,-3531,-1095,-7114,14850,2436,-1451,1723,6365 } }, + { "OLYMPUS C70", 0, 0, + { 10793,-3791,-1146,-7498,15177,2488,-1390,1577,7321 } }, + { "OLYMPUS C80", 0, 0, + { 8606,-2509,-1014,-8238,15714,2703,-942,979,7760 } }, + { "OLYMPUS E-10", 0, 0xffc0, + { 12745,-4500,-1416,-6062,14542,1580,-1934,2256,6603 } }, + { "OLYMPUS E-1", 0, 0xfff0, + { 11846,-4767,-945,-7027,15878,1089,-2699,4122,8311 } }, + { "OLYMPUS E-20", 0, 0xffc0, + { 13173,-4732,-1499,-5807,14036,1895,-2045,2452,7142 } }, + { "OLYMPUS E-300", 0, 0, + { 7828,-1761,-348,-5788,14071,1830,-2853,4518,6557 } }, + { "OLYMPUS E-330", 0, 0, + { 8961,-2473,-1084,-7979,15990,2067,-2319,3035,8249 } }, + { "OLYMPUS E-3", 0, 0xf99, + { 9487,-2875,-1115,-7533,15606,2010,-1618,2100,7389 } }, + { "OLYMPUS E-400", 0, 0xfff0, + { 6169,-1483,-21,-7107,14761,2536,-2904,3580,8568 } }, + { "OLYMPUS E-410", 0, 0xf6a, + { 8856,-2582,-1026,-7761,15766,2082,-2009,2575,7469 } }, + { "OLYMPUS E-420", 0, 0xfd7, + { 8746,-2425,-1095,-7594,15612,2073,-1780,2309,7416 } }, + { "OLYMPUS E-500", 0, 0, + { 8136,-1968,-299,-5481,13742,1871,-2556,4205,6630 } }, + { "OLYMPUS E-510", 0, 0xf6a, + { 8785,-2529,-1033,-7639,15624,2112,-1783,2300,7817 } }, + { "OLYMPUS E-520", 0, 0xfd2, + { 8344,-2322,-1020,-7596,15635,2048,-1748,2269,7287 } }, + { "OLYMPUS SP350", 0, 0, + { 12078,-4836,-1069,-6671,14306,2578,-786,939,7418 } }, + { "OLYMPUS SP3", 0, 0, + { 11766,-4445,-1067,-6901,14421,2707,-1029,1217,7572 } }, + { "OLYMPUS SP500UZ", 0, 0xfff, + { 9493,-3415,-666,-5211,12334,3260,-1548,2262,6482 } }, + { "OLYMPUS SP510UZ", 0, 0xffe, + { 10593,-3607,-1010,-5881,13127,3084,-1200,1805,6721 } }, + { "OLYMPUS SP550UZ", 0, 0xffe, + { 11597,-4006,-1049,-5432,12799,2957,-1029,1750,6516 } }, + { "OLYMPUS SP560UZ", 0, 0xff9, + { 10915,-3677,-982,-5587,12986,2911,-1168,1968,6223 } }, + { "OLYMPUS SP570UZ", 0, 0, + { 11522,-4044,-1146,-4736,12172,2904,-988,1829,6039 } }, + { "PENTAX *ist DL2", 0, 0, + { 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } }, + { "PENTAX *ist DL", 0, 0, + { 10829,-2838,-1115,-8339,15817,2696,-837,680,11939 } }, + { "PENTAX *ist DS2", 0, 0, + { 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } }, + { "PENTAX *ist DS", 0, 0, + { 10371,-2333,-1206,-8688,16231,2602,-1230,1116,11282 } }, + { "PENTAX *ist D", 0, 0, + { 9651,-2059,-1189,-8881,16512,2487,-1460,1345,10687 } }, + { "PENTAX K10D", 0, 0, + { 9566,-2863,-803,-7170,15172,2112,-818,803,9705 } }, + { "PENTAX K1", 0, 0, + { 11095,-3157,-1324,-8377,15834,2720,-1108,947,11688 } }, + { "PENTAX K20D", 0, 0, + { 9427,-2714,-868,-7493,16092,1373,-2199,3264,7180 } }, + { "PENTAX K200D", 0, 0, + { 9186,-2678,-907,-8693,16517,2260,-1129,1094,8524 } }, + { "PENTAX K2000", 0, 0, + { 11057,-3604,-1155,-5152,13046,2329,-282,375,8104 } }, + { "Panasonic DMC-FZ8", 0, 0xf7f0, + { 8986,-2755,-802,-6341,13575,3077,-1476,2144,6379 } }, + { "Panasonic DMC-FZ18", 0, 0, + { 9932,-3060,-935,-5809,13331,2753,-1267,2155,5575 } }, + { "Panasonic DMC-FZ28", 15, 0xfff, + { 10109,-3488,-993,-5412,12812,2916,-1305,2140,5543 } }, + { "Panasonic DMC-FZ30", 0, 0xf94c, + { 10976,-4029,-1141,-7918,15491,2600,-1670,2071,8246 } }, + { "Panasonic DMC-FZ50", 0, 0xfff0, /* aka "LEICA V-LUX1" */ + { 7906,-2709,-594,-6231,13351,3220,-1922,2631,6537 } }, + { "Panasonic DMC-L10", 15, 0xf96, + { 8025,-1942,-1050,-7920,15904,2100,-2456,3005,7039 } }, + { "Panasonic DMC-L1", 0, 0xf7fc, /* aka "LEICA DIGILUX 3" */ + { 8054,-1885,-1025,-8349,16367,2040,-2805,3542,7629 } }, + { "Panasonic DMC-LC1", 0, 0, /* aka "LEICA DIGILUX 2" */ + { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } }, + { "Panasonic DMC-LX1", 0, 0xf7f0, /* aka "LEICA D-LUX2" */ + { 10704,-4187,-1230,-8314,15952,2501,-920,945,8927 } }, + { "Panasonic DMC-LX2", 0, 0, /* aka "LEICA D-LUX3" */ + { 8048,-2810,-623,-6450,13519,3272,-1700,2146,7049 } }, + { "Panasonic DMC-LX3", 15, 0xfff, /* aka "LEICA D-LUX4" */ + { 8128,-2668,-655,-6134,13307,3161,-1782,2568,6083 } }, + { "Panasonic DMC-FX150", 15, 0xfff, + { 9082,-2907,-925,-6119,13377,3058,-1797,2641,5609 } }, + { "Panasonic DMC-G1", 15, 0xfff, + { 8199,-2065,-1056,-8124,16156,2033,-2458,3022,7220 } }, + { "Phase One H 20", 0, 0, /* DJC */ + { 1313,1855,-109,-6715,15908,808,-327,1840,6020 } }, + { "Phase One P 2", 0, 0, + { 2905,732,-237,-8134,16626,1476,-3038,4253,7517 } }, + { "Phase One P 30", 0, 0, + { 4516,-245,-37,-7020,14976,2173,-3206,4671,7087 } }, + { "Phase One P 45", 0, 0, + { 5053,-24,-117,-5684,14076,1702,-2619,4492,5849 } }, + { "SAMSUNG GX-1", 0, 0, + { 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } }, + { "Sinar", 0, 0, /* DJC */ + { 16442,-2956,-2422,-2877,12128,750,-1136,6066,4559 } }, + { "SONY DSC-F828", 491, 0, + { 7924,-1910,-777,-8226,15459,2998,-1517,2199,6818,-7242,11401,3481 } }, + { "SONY DSC-R1", 512, 0, + { 8512,-2641,-694,-8042,15670,2526,-1821,2117,7414 } }, + { "SONY DSC-V3", 0, 0, + { 7511,-2571,-692,-7894,15088,3060,-948,1111,8128 } }, + { "SONY DSLR-A100", 0, 0xfeb, + { 9437,-2811,-774,-8405,16215,2290,-710,596,7181 } }, + { "SONY DSLR-A200", 0, 0, + { 9847,-3091,-928,-8485,16345,2225,-715,595,7103 } }, + { "SONY DSLR-A300", 0, 0, + { 9847,-3091,-928,-8485,16345,2225,-715,595,7103 } }, + { "SONY DSLR-A350", 0, 0xffc, + { 6038,-1484,-578,-9146,16746,2513,-875,746,7217 } }, + { "SONY DSLR-A700", 254, 0x1ffe, + { 5775,-805,-359,-8574,16295,2391,-1943,2341,7249 } }, + { "SONY DSLR-A900", 254, 0x1ffe, + { 5209,-1072,-397,-8845,16120,2919,-1618,1803,8654 } } + }; + double cam_xyz[4][3]; + char name[130]; + int i, j; + + sprintf (name, "%s %s", p_make, p_model); + for (i=0; i < sizeof table / sizeof *table; i++) + if (!strncmp (name, table[i].prefix, strlen(table[i].prefix))) { + if (table[i].t_black) black = (ushort) table[i].t_black; + if (table[i].t_maximum) maximum = (ushort) table[i].t_maximum; + for (j=0; j < 12; j++) +#ifdef LIBRAW_LIBRARY_BUILD + imgdata.color.cam_xyz[0][j] = +#endif + cam_xyz[0][j] = table[i].trans[j] / 10000.0; + cam_xyz_coeff (cam_xyz); + break; + } +} + +void CLASS simple_coeff (int index) +{ + static const float table[][12] = { + /* index 0 -- all Foveon cameras */ + { 1.4032,-0.2231,-0.1016,-0.5263,1.4816,0.017,-0.0112,0.0183,0.9113 }, + /* index 1 -- Kodak DC20 and DC25 */ + { 2.25,0.75,-1.75,-0.25,-0.25,0.75,0.75,-0.25,-0.25,-1.75,0.75,2.25 }, + /* index 2 -- Logitech Fotoman Pixtura */ + { 1.893,-0.418,-0.476,-0.495,1.773,-0.278,-1.017,-0.655,2.672 }, + /* index 3 -- Nikon E880, E900, and E990 */ + { -1.936280, 1.800443, -1.448486, 2.584324, + 1.405365, -0.524955, -0.289090, 0.408680, + -1.204965, 1.082304, 2.941367, -1.818705 } + }; + int i, c; + + for (raw_color = i=0; i < 3; i++) + FORCC rgb_cam[i][c] = table[index][i*colors+c]; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.rgb_cam_state = LIBRAW_COLORSTATE_CALCULATED; +#endif +} + +short CLASS guess_byte_order (int words) +{ + uchar test[4][2]; + int t=2, msb; + double diff, sum[2] = {0,0}; + + fread (test[0], 2, 2, ifp); + for (words-=2; words--; ) { + fread (test[t], 2, 1, ifp); + for (msb=0; msb < 2; msb++) { + diff = (test[t^2][msb] << 8 | test[t^2][!msb]) + - (test[t ][msb] << 8 | test[t ][!msb]); + sum[msb] += diff*diff; + } + t = (t+1) & 3; + } + return sum[0] < sum[1] ? 0x4d4d : 0x4949; +} + +/* + Identify which camera created this file, and set global variables + accordingly. + */ +void CLASS identify() +{ + char head[32], *cp; + unsigned hlen, fsize, i, c, is_canon; + struct jhead jh; + static const struct { + int fsize; + char t_make[12], t_model[19], withjpeg; + } table[] = { + { 62464, "Kodak", "DC20" ,0 }, + { 124928, "Kodak", "DC20" ,0 }, + { 1652736, "Kodak", "DCS200" ,0 }, + { 4159302, "Kodak", "C330" ,0 }, + { 4162462, "Kodak", "C330" ,0 }, + { 460800, "Kodak", "C603v" ,0 }, + { 614400, "Kodak", "C603v" ,0 }, + { 6163328, "Kodak", "C603" ,0 }, + { 6166488, "Kodak", "C603" ,0 }, + { 9116448, "Kodak", "C603y" ,0 }, + { 311696, "ST Micro", "STV680 VGA" ,0 }, /* SPYz */ + { 614400, "Kodak", "KAI-0340" ,0 }, + { 787456, "Creative", "PC-CAM 600" ,0 }, + { 1138688, "Minolta", "RD175" ,0 }, + { 3840000, "Foculus", "531C" ,0 }, + { 786432, "AVT", "F-080C" ,0 }, + { 1447680, "AVT", "F-145C" ,0 }, + { 1920000, "AVT", "F-201C" ,0 }, + { 5067304, "AVT", "F-510C" ,0 }, + { 10134608, "AVT", "F-510C" ,0 }, + { 16157136, "AVT", "F-810C" ,0 }, + { 1409024, "Sony", "XCD-SX910CR" ,0 }, + { 2818048, "Sony", "XCD-SX910CR" ,0 }, + { 3884928, "Micron", "2010" ,0 }, + { 6624000, "Pixelink", "A782" ,0 }, + { 13248000, "Pixelink", "A782" ,0 }, + { 6291456, "RoverShot","3320AF" ,0 }, + { 6553440, "Canon", "PowerShot A460" ,0 }, + { 6653280, "Canon", "PowerShot A530" ,0 }, + { 6573120, "Canon", "PowerShot A610" ,0 }, + { 9219600, "Canon", "PowerShot A620" ,0 }, + { 10341600, "Canon", "PowerShot A720" ,0 }, + { 10383120, "Canon", "PowerShot A630" ,0 }, + { 12945240, "Canon", "PowerShot A640" ,0 }, + { 15636240, "Canon", "PowerShot A650" ,0 }, + { 5298000, "Canon", "PowerShot SD300" ,0 }, + { 7710960, "Canon", "PowerShot S3 IS" ,0 }, + { 5939200, "OLYMPUS", "C770UZ" ,0 }, + { 1581060, "NIKON", "E900" ,1 }, /* or E900s,E910 */ + { 2465792, "NIKON", "E950" ,1 }, /* or E800,E700 */ + { 2940928, "NIKON", "E2100" ,1 }, /* or E2500 */ + { 4771840, "NIKON", "E990" ,1 }, /* or E995, Oly C3030Z */ + { 4775936, "NIKON", "E3700" ,1 }, /* or Optio 33WR */ + { 5869568, "NIKON", "E4300" ,1 }, /* or DiMAGE Z2 */ + { 5865472, "NIKON", "E4500" ,1 }, + { 7438336, "NIKON", "E5000" ,1 }, /* or E5700 */ + { 8998912, "NIKON", "COOLPIX S6" ,1 }, + { 1976352, "CASIO", "QV-2000UX" ,1 }, + { 3217760, "CASIO", "QV-3*00EX" ,1 }, + { 6218368, "CASIO", "QV-5700" ,1 }, + { 6054400, "CASIO", "QV-R41" ,1 }, + { 7530816, "CASIO", "QV-R51" ,1 }, + { 7684000, "CASIO", "QV-4000" ,1 }, + { 4948608, "CASIO", "EX-S100" ,1 }, + { 7542528, "CASIO", "EX-Z50" ,1 }, + { 7753344, "CASIO", "EX-Z55" ,1 }, + { 7426656, "CASIO", "EX-P505" ,1 }, + { 9313536, "CASIO", "EX-P600" ,1 }, + { 10979200, "CASIO", "EX-P700" ,1 }, + { 3178560, "PENTAX", "Optio S" ,1 }, + { 4841984, "PENTAX", "Optio S" ,1 }, + { 6114240, "PENTAX", "Optio S4" ,1 }, /* or S4i, CASIO EX-Z4 */ + { 10702848, "PENTAX", "Optio 750Z" ,1 }, + { 16098048, "SAMSUNG", "S85" ,1 }, + { 16215552, "SAMSUNG", "S85" ,1 }, + { 12582980, "Sinar", "" ,0 }, + { 33292868, "Sinar", "" ,0 }, + { 44390468, "Sinar", "" ,0 } }; + static const char *corp[] = + { "Canon", "NIKON", "EPSON", "KODAK", "Kodak", "OLYMPUS", "PENTAX", + "MINOLTA", "Minolta", "Konica", "CASIO", "Sinar", "Phase One", + "SAMSUNG", "Mamiya" }; + +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_IDENTIFY,0,2); +#endif + + tiff_flip = flip = filters = -1; /* 0 is valid, so -1 is unknown */ + raw_height = raw_width = fuji_width = fuji_layout = cr2_slice[0] = 0; + maximum = height = width = top_margin = left_margin = 0; + cdesc[0] = desc[0] = artist[0] = make[0] = model[0] = model2[0] = 0; + iso_speed = shutter = aperture = focal_len = unique_id = 0; + memset (gpsdata, 0, sizeof gpsdata); + memset (white, 0, sizeof white); + thumb_offset = thumb_length = thumb_width = thumb_height = 0; + load_raw = thumb_load_raw = 0; + write_thumb = &CLASS jpeg_thumb; + data_offset = meta_length = tiff_bps = tiff_compress = 0; + kodak_cbpp = zero_after_ff = dng_version = load_flags = 0; + timestamp = shot_order = tiff_samples = black = is_foveon = 0; + mix_green = profile_length = data_error = zero_is_bad = 0; + pixel_aspect = is_raw = raw_color = use_gamma = 1; + tile_width = tile_length = INT_MAX; + for (i=0; i < 4; i++) { + cam_mul[i] = i == 1; + pre_mul[i] = i < 3; + FORC3 cmatrix[c][i] = 0; + FORC3 rgb_cam[c][i] = c == i; + } +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cmatrix_state = LIBRAW_COLORSTATE_INIT; + color_flags.rgb_cam_state = LIBRAW_COLORSTATE_INIT; + color_flags.pre_mul_state = LIBRAW_COLORSTATE_INIT; + color_flags.cam_mul_state = LIBRAW_COLORSTATE_INIT; +#endif + colors = 3; + tiff_bps = 12; + for (i=0; i < 0x4000; i++) curve[i] = i; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.curve_state = LIBRAW_COLORSTATE_INIT; +#endif + + order = get2(); + hlen = get4(); + fseek (ifp, 0, SEEK_SET); + fread (head, 1, 32, ifp); + fseek (ifp, 0, SEEK_END); + fsize = ftell(ifp); + if ((cp = (char *) memmem (head, 32, "MMMM", 4)) || + (cp = (char *) memmem (head, 32, "IIII", 4))) { + parse_phase_one (cp-head); + if (cp-head) parse_tiff(0); + } else if (order == 0x4949 || order == 0x4d4d) { + if (!memcmp (head+6,"HEAPCCDR",8)) { + data_offset = hlen; + parse_ciff (hlen, fsize - hlen); + } else { + parse_tiff(0); + } + } else if (!memcmp (head,"\xff\xd8\xff\xe1",4) && + !memcmp (head+6,"Exif",4)) { + fseek (ifp, 4, SEEK_SET); + data_offset = 4 + get2(); + fseek (ifp, data_offset, SEEK_SET); + if (fgetc(ifp) != 0xff) + parse_tiff(12); + thumb_offset = 0; + } else if (!memcmp (head+25,"ARECOYK",7)) { + strcpy (make, "Contax"); + strcpy (model,"N Digital"); + fseek (ifp, 33, SEEK_SET); + get_timestamp(1); + fseek (ifp, 60, SEEK_SET); + FORC4 cam_mul[c ^ (c >> 1)] = get4(); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED; +#endif + } else if (!strcmp (head, "PXN")) { + strcpy (make, "Logitech"); + strcpy (model,"Fotoman Pixtura"); + } else if (!strcmp (head, "qktk")) { + strcpy (make, "Apple"); + strcpy (model,"QuickTake 100"); + } else if (!strcmp (head, "qktn")) { + strcpy (make, "Apple"); + strcpy (model,"QuickTake 150"); + } else if (!memcmp (head,"FUJIFILM",8)) { + fseek (ifp, 84, SEEK_SET); + thumb_offset = get4(); + thumb_length = get4(); + fseek (ifp, 92, SEEK_SET); + parse_fuji (get4()); + if (thumb_offset > 120) { + fseek (ifp, 120, SEEK_SET); + is_raw += (i = get4()) && 1; + if (is_raw == 2 && shot_select) + parse_fuji (i); + } + fseek (ifp, 100, SEEK_SET); + data_offset = get4(); + parse_tiff (thumb_offset+12); + } else if (!memcmp (head,"RIFF",4)) { + fseek (ifp, 0, SEEK_SET); + parse_riff(); + } else if (!memcmp (head,"\0\001\0\001\0@",6)) { + fseek (ifp, 6, SEEK_SET); + fread (make, 1, 8, ifp); + fread (model, 1, 8, ifp); + fread (model2, 1, 16, ifp); + data_offset = get2(); + get2(); + raw_width = get2(); + raw_height = get2(); + load_raw = &CLASS nokia_load_raw; + filters = 0x61616161; + } else if (!memcmp (head,"DSC-Image",9)) + parse_rollei(); + else if (!memcmp (head,"PWAD",4)) + parse_sinar_ia(); + else if (!memcmp (head,"\0MRM",4)) + parse_minolta(0); + else if (!memcmp (head,"FOVb",4)) + parse_foveon(); + else if (!memcmp (head,"CI",2)) + parse_cine(); + else + for (i=0; i < sizeof table / sizeof *table; i++) + if (fsize == table[i].fsize) { + strcpy (make, table[i].t_make ); + strcpy (model, table[i].t_model); + if (table[i].withjpeg) + parse_external_jpeg(); + } + if (make[0] == 0) parse_smal (0, fsize); + if (make[0] == 0) parse_jpeg (is_raw = 0); + + for (i=0; i < sizeof corp / sizeof *corp; i++) + if (strstr (make, corp[i])) /* Simplify company names */ + strcpy (make, corp[i]); + if (!strncmp (make,"KODAK",5)) + make[16] = model[16] = 0; + cp = make + strlen(make); /* Remove trailing spaces */ + while (*--cp == ' ') *cp = 0; + cp = model + strlen(model); + while (*--cp == ' ') *cp = 0; + i = strlen(make); /* Remove make from model */ + if (!strncasecmp (model, make, i) && model[i++] == ' ') + memmove (model, model+i, 64-i); + if (!strncmp (model,"Digital Camera ",15)) + strcpy (model, model+15); + desc[511] = artist[63] = make[63] = model[63] = model2[63] = 0; + if (!is_raw) goto notraw; + + if (!maximum) maximum = (1 << tiff_bps) - 1; + if (!height) height = raw_height; + if (!width) width = raw_width; + if (fuji_width) { + width = height + fuji_width; + height = width - 1; + pixel_aspect = 1; + } + if (height == 2624 && width == 3936) /* Pentax K10D and Samsung GX10 */ + { height = 2616; width = 3896; } + if (height == 3136 && width == 4864) /* Pentax K20D */ + { height = 3124; width = 4688; } + if (height == 3014 && width == 4096) /* Ricoh GX200 */ + width = 4014; + if (dng_version) { + if (filters == UINT_MAX) filters = 0; + if (filters) is_raw = tiff_samples; + else colors = tiff_samples; + if (tiff_compress == 1) + load_raw = &CLASS adobe_dng_load_raw_nc; + if (tiff_compress == 7) + load_raw = &CLASS adobe_dng_load_raw_lj; + goto dng_skip; + } + if ((is_canon = !strcmp(make,"Canon"))) + load_raw = memcmp (head+6,"HEAPCCDR",8) ? + &CLASS lossless_jpeg_load_raw : &CLASS canon_compressed_load_raw; + if (!strcmp(make,"NIKON")) { + if (!load_raw) + load_raw = &CLASS packed_12_load_raw; + if (model[0] == 'E') + load_flags |= !data_offset << 2 | 2; + } + if (!strcmp(make,"CASIO")) { + load_raw = &CLASS packed_12_load_raw; + maximum = 0xf7f; + } + +/* Set parameters based on camera name (for non-DNG files). */ + + if (is_foveon) { + if (height*2 < width) pixel_aspect = 0.5; + if (height > width) pixel_aspect = 2; + filters = 0; + load_raw = &CLASS foveon_load_raw; + simple_coeff(0); + } else if (is_canon && tiff_bps == 15) { + switch (width) { + case 3344: width -= 66; + case 3872: width -= 6; + } + filters = 0; + load_raw = &CLASS canon_sraw_load_raw; + } else if (!strcmp(model,"PowerShot 600")) { + height = 613; + width = 854; + raw_width = 896; + pixel_aspect = 607/628.0; + colors = 4; + filters = 0xe1e4e1e4; + load_raw = &CLASS canon_600_load_raw; + } else if (!strcmp(model,"PowerShot A5") || + !strcmp(model,"PowerShot A5 Zoom")) { + height = 773; + width = 960; + raw_width = 992; + pixel_aspect = 256/235.0; + colors = 4; + filters = 0x1e4e1e4e; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot A50")) { + height = 968; + width = 1290; + raw_width = 1320; + colors = 4; + filters = 0x1b4e4b1e; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot Pro70")) { + height = 1024; + width = 1552; + colors = 4; + filters = 0x1e4b4e1b; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot SD300")) { + height = 1752; + width = 2344; + raw_height = 1766; + raw_width = 2400; + top_margin = 12; + left_margin = 12; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot A460")) { + height = 1960; + width = 2616; + raw_height = 1968; + raw_width = 2664; + top_margin = 4; + left_margin = 4; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot A530")) { + height = 1984; + width = 2620; + raw_height = 1992; + raw_width = 2672; + top_margin = 6; + left_margin = 10; + load_raw = &CLASS canon_a5_load_raw; + raw_color = 0; + } else if (!strcmp(model,"PowerShot A610")) { + if (canon_s2is()) strcpy (model+10, "S2 IS"); + height = 1960; + width = 2616; + raw_height = 1968; + raw_width = 2672; + top_margin = 8; + left_margin = 12; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot A620")) { + height = 2328; + width = 3112; + raw_height = 2340; + raw_width = 3152; + top_margin = 12; + left_margin = 36; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot A720")) { + height = 2472; + width = 3298; + raw_height = 2480; + raw_width = 3336; + top_margin = 5; + left_margin = 6; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot A630")) { + height = 2472; + width = 3288; + raw_height = 2484; + raw_width = 3344; + top_margin = 6; + left_margin = 12; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot A640")) { + height = 2760; + width = 3672; + raw_height = 2772; + raw_width = 3736; + top_margin = 6; + left_margin = 12; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot A650")) { + height = 3024; + width = 4032; + raw_height = 3048; + raw_width = 4104; + top_margin = 12; + left_margin = 48; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot S3 IS")) { + height = 2128; + width = 2840; + raw_height = 2136; + raw_width = 2888; + top_margin = 8; + left_margin = 44; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot Pro90 IS")) { + width = 1896; + colors = 4; + filters = 0xb4b4b4b4; + } else if (is_canon && raw_width == 2144) { + height = 1550; + width = 2088; + top_margin = 8; + left_margin = 4; + if (!strcmp(model,"PowerShot G1")) { + colors = 4; + filters = 0xb4b4b4b4; + } + } else if (is_canon && raw_width == 2224) { + height = 1448; + width = 2176; + top_margin = 6; + left_margin = 48; + } else if (is_canon && raw_width == 2376) { + height = 1720; + width = 2312; + top_margin = 6; + left_margin = 12; + } else if (is_canon && raw_width == 2672) { + height = 1960; + width = 2616; + top_margin = 6; + left_margin = 12; + } else if (is_canon && raw_width == 3152) { + height = 2056; + width = 3088; + top_margin = 12; + left_margin = 64; + if (unique_id == 0x80000170) + adobe_coeff ("Canon","EOS 300D"); + } else if (is_canon && raw_width == 3160) { + height = 2328; + width = 3112; + top_margin = 12; + left_margin = 44; + } else if (is_canon && raw_width == 3344) { + height = 2472; + width = 3288; + top_margin = 6; + left_margin = 4; + } else if (!strcmp(model,"EOS D2000C")) { + filters = 0x61616161; + black = curve[200]; + } else if (is_canon && raw_width == 3516) { + top_margin = 14; + left_margin = 42; + if (unique_id == 0x80000189) + adobe_coeff ("Canon","EOS 350D"); + goto canon_cr2; + } else if (is_canon && raw_width == 3596) { + top_margin = 12; + left_margin = 74; + goto canon_cr2; + } else if (is_canon && raw_width == 3944) { + height = 2602; + width = 3908; + top_margin = 18; + left_margin = 30; + } else if (is_canon && raw_width == 3948) { + top_margin = 18; + left_margin = 42; + height -= 2; + if (unique_id == 0x80000236) + adobe_coeff ("Canon","EOS 400D"); + if (unique_id == 0x80000254) + adobe_coeff ("Canon","EOS 1000D"); + goto canon_cr2; + } else if (is_canon && raw_width == 3984) { + top_margin = 20; + left_margin = 76; + height -= 2; + goto canon_cr2; + } else if (is_canon && raw_width == 4104) { + height = 3024; + width = 4032; + top_margin = 12; + left_margin = 48; + } else if (is_canon && raw_width == 4312) { + top_margin = 18; + left_margin = 22; + height -= 2; + if (unique_id == 0x80000176) + adobe_coeff ("Canon","EOS 450D"); + goto canon_cr2; + } else if (is_canon && raw_width == 4476) { + top_margin = 34; + left_margin = 90; + goto canon_cr2; + } else if (is_canon && raw_width == 4480) { + height = 3326; + width = 4432; + top_margin = 10; + left_margin = 12; + filters = 0x49494949; + } else if (is_canon && raw_width == 1208) { + top_margin = 51; + left_margin = 62; + raw_width = width *= 4; + goto canon_cr2; + } else if (is_canon && raw_width == 1448) { + top_margin = 51; + left_margin = 158; + raw_width = width *= 4; + goto canon_cr2; + } else if (is_canon && raw_width == 5108) { + top_margin = 13; + left_margin = 98; +canon_cr2: + height -= top_margin; + width -= left_margin; + } else if (is_canon && raw_width == 5712) { + height = 3752; + width = 5640; + top_margin = 20; + left_margin = 62; + } else if (!strcmp(model,"D1")) { + cam_mul[0] *= 256/527.0; + cam_mul[2] *= 256/317.0; + } else if (!strcmp(model,"D1X")) { + width -= 4; + pixel_aspect = 0.5; + } else if (!strcmp(model,"D40X") || + !strcmp(model,"D60") || + !strcmp(model,"D80")) { + height -= 3; + width -= 4; + } else if (!strcmp(model,"D3") || + !strcmp(model,"D700")) { + width -= 4; + left_margin = 2; + } else if (!strncmp(model,"D40",3) || + !strncmp(model,"D50",3) || + !strncmp(model,"D70",3)) { + width--; + } else if (!strcmp(model,"D90")) { + width -= 42; + } else if (!strcmp(model,"D100")) { + if (tiff_compress == 34713 && !nikon_is_compressed()) { + load_raw = &CLASS packed_12_load_raw; + load_flags |= 8; + raw_width = (width += 3) + 3; + } + } else if (!strcmp(model,"D200")) { + left_margin = 1; + width -= 4; + filters = 0x94949494; + } else if (!strncmp(model,"D2H",3)) { + left_margin = 6; + width -= 14; + } else if (!strncmp(model,"D2X",3)) { + if (width == 3264) width -= 32; + else width -= 8; + } else if (!strcmp(model,"D300")) { + width -= 32; + } else if (!strcmp(model,"COOLPIX P6000")) { + load_flags = 1; + filters = 0x94949494; + } else if (fsize == 1581060) { + height = 963; + width = 1287; + raw_width = 1632; + load_raw = &CLASS nikon_e900_load_raw; + maximum = 0x3f4; + colors = 4; + filters = 0x1e1e1e1e; + simple_coeff(3); + pre_mul[0] = 1.2085; + pre_mul[1] = 1.0943; + pre_mul[3] = 1.1103; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else if (fsize == 2465792) { + height = 1203; + width = 1616; + raw_width = 2048; + load_raw = &CLASS nikon_e900_load_raw; + colors = 4; + filters = 0x4b4b4b4b; + adobe_coeff ("NIKON","E950"); + } else if (fsize == 4771840) { + height = 1540; + width = 2064; + colors = 4; + filters = 0xe1e1e1e1; + load_raw = &CLASS packed_12_load_raw; + load_flags = 6; + if (!timestamp && nikon_e995()) + strcpy (model, "E995"); + if (strcmp(model,"E995")) { + filters = 0xb4b4b4b4; + simple_coeff(3); + pre_mul[0] = 1.196; + pre_mul[1] = 1.246; + pre_mul[2] = 1.018; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } + } else if (!strcmp(model,"E2100")) { + if (!timestamp && !nikon_e2100()) goto cp_e2500; + height = 1206; + width = 1616; + load_flags = 7; + } else if (!strcmp(model,"E2500")) { +cp_e2500: + strcpy (model, "E2500"); + height = 1204; + width = 1616; + colors = 4; + filters = 0x4b4b4b4b; + } else if (fsize == 4775936) { + height = 1542; + width = 2064; + load_raw = &CLASS packed_12_load_raw; + load_flags = 7; + pre_mul[0] = 1.818; + pre_mul[2] = 1.618; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + if (!timestamp) nikon_3700(); + if (model[0] == 'E' && atoi(model+1) < 3700) + filters = 0x49494949; + if (!strcmp(model,"Optio 33WR")) { + flip = 1; + filters = 0x16161616; + pre_mul[0] = 1.331; + pre_mul[2] = 1.820; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } + } else if (fsize == 5869568) { + height = 1710; + width = 2288; + filters = 0x16161616; + if (!timestamp && minolta_z2()) { + strcpy (make, "Minolta"); + strcpy (model,"DiMAGE Z2"); + } + load_raw = &CLASS packed_12_load_raw; + load_flags = 6 + (make[0] == 'M'); + } else if (!strcmp(model,"E4500")) { + height = 1708; + width = 2288; + colors = 4; + filters = 0xb4b4b4b4; + } else if (fsize == 7438336) { + height = 1924; + width = 2576; + colors = 4; + filters = 0xb4b4b4b4; + } else if (fsize == 8998912) { + height = 2118; + width = 2832; + maximum = 0xf83; + load_raw = &CLASS packed_12_load_raw; + load_flags = 7; + } else if (!strcmp(model,"FinePix S5100") || + !strcmp(model,"FinePix S5500")) { + load_raw = &CLASS unpacked_load_raw; + } else if (!strcmp(make,"FUJIFILM")) { + if (!strcmp(model+7,"S2Pro")) { + strcpy (model+7," S2Pro"); + height = 2144; + width = 2880; + flip = 6; + } else + maximum = 0x3e00; + if (is_raw == 2 && shot_select) + maximum = 0x2f00; + top_margin = (raw_height - height)/2; + left_margin = (raw_width - width )/2; + if (is_raw == 2) + data_offset += (shot_select > 0) * ( fuji_layout ? + (raw_width *= 2) : raw_height*raw_width*2 ); + fuji_width = width >> !fuji_layout; + width = (height >> fuji_layout) + fuji_width; + raw_height = height; + height = width - 1; + load_raw = &CLASS fuji_load_raw; + if (!(fuji_width & 1)) filters = 0x49494949; + } else if (!strcmp(model,"RD175")) { + height = 986; + width = 1534; + data_offset = 513; + filters = 0x61616161; + load_raw = &CLASS minolta_rd175_load_raw; + } else if (!strcmp(model,"KD-400Z")) { + height = 1712; + width = 2312; + raw_width = 2336; + goto konica_400z; + } else if (!strcmp(model,"KD-510Z")) { + goto konica_510z; + } else if (!strcasecmp(make,"MINOLTA")) { + load_raw = &CLASS unpacked_load_raw; + maximum = 0xfff; + if (!strncmp(model,"DiMAGE A",8)) { + if (!strcmp(model,"DiMAGE A200")) + filters = 0x49494949; + load_raw = &CLASS packed_12_load_raw; + } else if (!strncmp(model,"ALPHA",5) || + !strncmp(model,"DYNAX",5) || + !strncmp(model,"MAXXUM",6)) { + sprintf (model+20, "DYNAX %-10s", model+6+(model[0]=='M')); + adobe_coeff (make, model+20); + load_raw = &CLASS packed_12_load_raw; + } else if (!strncmp(model,"DiMAGE G",8)) { + if (model[8] == '4') { + height = 1716; + width = 2304; + } else if (model[8] == '5') { +konica_510z: + height = 1956; + width = 2607; + raw_width = 2624; + } else if (model[8] == '6') { + height = 2136; + width = 2848; + } + data_offset += 14; + filters = 0x61616161; +konica_400z: + load_raw = &CLASS unpacked_load_raw; + maximum = 0x3df; + order = 0x4d4d; + } + } else if (!strcmp(model,"*ist DS")) { + height -= 2; + } else if (!strcmp(model,"K20D")) { + filters = 0x16161616; + } else if (!strcmp(model,"Optio S")) { + if (fsize == 3178560) { + height = 1540; + width = 2064; + load_raw = &CLASS eight_bit_load_raw; + cam_mul[0] *= 4; + cam_mul[2] *= 4; + pre_mul[0] = 1.391; + pre_mul[2] = 1.188; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else { + height = 1544; + width = 2068; + raw_width = 3136; + load_raw = &CLASS packed_12_load_raw; + maximum = 0xf7c; + pre_mul[0] = 1.137; + pre_mul[2] = 1.453; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } + } else if (fsize == 6114240) { + height = 1737; + width = 2324; + raw_width = 3520; + load_raw = &CLASS packed_12_load_raw; + maximum = 0xf7a; + pre_mul[0] = 1.980; + pre_mul[2] = 1.570; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else if (!strcmp(model,"Optio 750Z")) { + height = 2302; + width = 3072; + load_raw = &CLASS packed_12_load_raw; + load_flags = 7; + } else if (!strcmp(model,"S85")) { + height = 2448; + width = 3264; + raw_width = fsize/height/2; + order = 0x4d4d; + load_raw = &CLASS unpacked_load_raw; + maximum = 0xffff; + } else if (!strcmp(model,"STV680 VGA")) { + height = 484; + width = 644; + load_raw = &CLASS eight_bit_load_raw; + flip = 2; + filters = 0x16161616; + black = 16; + pre_mul[0] = 1.097; + pre_mul[2] = 1.128; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else if (!strcmp(model,"KAI-0340")) { + height = 477; + width = 640; + order = 0x4949; + data_offset = 3840; + load_raw = &CLASS unpacked_load_raw; + pre_mul[0] = 1.561; + pre_mul[2] = 2.454; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else if (!strcmp(model,"N95")) { + height = raw_height - (top_margin = 2); + } else if (!strcmp(model,"531C")) { + height = 1200; + width = 1600; + load_raw = &CLASS unpacked_load_raw; + filters = 0x49494949; + pre_mul[1] = 1.218; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else if (!strcmp(model,"F-080C")) { + height = 768; + width = 1024; + load_raw = &CLASS eight_bit_load_raw; + } else if (!strcmp(model,"F-145C")) { + height = 1040; + width = 1392; + load_raw = &CLASS eight_bit_load_raw; + } else if (!strcmp(model,"F-201C")) { + height = 1200; + width = 1600; + load_raw = &CLASS eight_bit_load_raw; + } else if (!strcmp(model,"F-510C")) { + height = 1958; + width = 2588; + load_raw = fsize < 7500000 ? + &CLASS eight_bit_load_raw : &CLASS unpacked_load_raw; + maximum = 0xfff0; + } else if (!strcmp(model,"F-810C")) { + height = 2469; + width = 3272; + load_raw = &CLASS unpacked_load_raw; + maximum = 0xfff0; + } else if (!strcmp(model,"XCD-SX910CR")) { + height = 1024; + width = 1375; + raw_width = 1376; + filters = 0x49494949; + maximum = 0x3ff; + load_raw = fsize < 2000000 ? + &CLASS eight_bit_load_raw : &CLASS unpacked_load_raw; + } else if (!strcmp(model,"2010")) { + height = 1207; + width = 1608; + order = 0x4949; + filters = 0x16161616; + data_offset = 3212; + maximum = 0x3ff; + load_raw = &CLASS unpacked_load_raw; + } else if (!strcmp(model,"A782")) { + height = 3000; + width = 2208; + filters = 0x61616161; + load_raw = fsize < 10000000 ? + &CLASS eight_bit_load_raw : &CLASS unpacked_load_raw; + maximum = 0xffc0; + } else if (!strcmp(model,"3320AF")) { + height = 1536; + raw_width = width = 2048; + filters = 0x61616161; + load_raw = &CLASS unpacked_load_raw; + maximum = 0x3ff; + pre_mul[0] = 1.717; + pre_mul[2] = 1.138; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + fseek (ifp, 0x300000, SEEK_SET); + if ((order = guess_byte_order(0x10000)) == 0x4d4d) { + height -= (top_margin = 16); + width -= (left_margin = 28); + maximum = 0xf5c0; + strcpy (make, "ISG"); + model[0] = 0; + } + } else if (!strcmp(make,"Hasselblad")) { + if (load_raw == &CLASS lossless_jpeg_load_raw) + load_raw = &CLASS hasselblad_load_raw; + if (raw_width == 7262) { + height = 5444; + width = 7248; + top_margin = 4; + left_margin = 7; + filters = 0x61616161; + } else if (raw_width == 4090) { + strcpy (model, "V96C"); + height -= (top_margin = 6); + width -= (left_margin = 3) + 7; + filters = 0x61616161; + } + } else if (!strcmp(make,"Sinar")) { + if (!memcmp(head,"8BPS",4)) { + fseek (ifp, 14, SEEK_SET); + height = get4(); + width = get4(); + filters = 0x61616161; + data_offset = 68; + } + if (!load_raw) load_raw = &CLASS unpacked_load_raw; + maximum = 0x3fff; + } else if (!strcmp(make,"Leaf")) { + maximum = 0x3fff; + fseek (ifp, data_offset, SEEK_SET); + if (ljpeg_start (&jh, 1) && jh.bits == 15) + maximum = 0x1fff; + if (tiff_samples > 1) filters = 0; + if (tiff_samples > 1 || tile_length < raw_height) + load_raw = &CLASS leaf_hdr_load_raw; + if ((width | height) == 2048) { + if (tiff_samples == 1) { + filters = 1; + strcpy (cdesc, "RBTG"); + strcpy (model, "CatchLight"); + top_margin = 8; left_margin = 18; height = 2032; width = 2016; + } else { + strcpy (model, "DCB2"); + top_margin = 10; left_margin = 16; height = 2028; width = 2022; + } + } else if (width+height == 3144+2060) { + if (!model[0]) strcpy (model, "Cantare"); + if (width > height) { + top_margin = 6; left_margin = 32; height = 2048; width = 3072; + filters = 0x61616161; + } else { + left_margin = 6; top_margin = 32; width = 2048; height = 3072; + filters = 0x16161616; + } + if (!cam_mul[0] || model[0] == 'V') filters = 0; + else is_raw = tiff_samples; + } else if (width == 2116) { + strcpy (model, "Valeo 6"); + height -= 2 * (top_margin = 30); + width -= 2 * (left_margin = 55); + filters = 0x49494949; + } else if (width == 3171) { + strcpy (model, "Valeo 6"); + height -= 2 * (top_margin = 24); + width -= 2 * (left_margin = 24); + filters = 0x16161616; + } + } else if (!strcmp(make,"LEICA") || !strcmp(make,"Panasonic")) { + maximum = 0xfff0; + if ((fsize-data_offset) / (width*8/7) == height) + load_raw = &CLASS panasonic_load_raw; + if (!load_raw) load_raw = &CLASS unpacked_load_raw; + switch (width) { + case 2568: + adobe_coeff ("Panasonic","DMC-LC1"); break; + case 3130: + left_margin = -14; + case 3170: + left_margin += 18; + width = 3096; + if (height > 2326) { + height = 2326; + top_margin = 13; + filters = 0x49494949; + } + zero_is_bad = 1; + adobe_coeff ("Panasonic","DMC-FZ8"); break; + case 3213: + width -= 27; + case 3177: + width -= 10; + filters = 0x49494949; + zero_is_bad = 1; + adobe_coeff ("Panasonic","DMC-L1"); break; + case 3304: + width -= 17; + zero_is_bad = 1; + adobe_coeff ("Panasonic","DMC-FZ30"); break; + case 3330: + width += 43; + left_margin = -6; + maximum = 0xf7f0; + case 3370: + width -= 82; + left_margin += 15; + if (height > 2480) + height = 2480 - (top_margin = 10); + filters = 0x49494949; + zero_is_bad = 1; + adobe_coeff ("Panasonic","DMC-FZ18"); break; + case 3690: + height -= 2; + left_margin = -14; + maximum = 0xf7f0; + case 3770: + width = 3672; + if (--height == 2798 && (height = 2760)) + top_margin = 15; + else filters = 0x49494949; + left_margin += 17; + zero_is_bad = 1; + adobe_coeff ("Panasonic","DMC-FZ50"); break; + case 3710: + width = 3682; + filters = 0x49494949; + adobe_coeff ("Panasonic","DMC-L10"); break; + case 3724: + width -= 14; + case 3836: + width -= 42; +lx3: filters = 0x16161616; + if (make[0] != 'P') + adobe_coeff ("Panasonic","DMC-LX3"); + break; + case 3880: + width -= 22; + left_margin = 6; + zero_is_bad = 1; + adobe_coeff ("Panasonic","DMC-LX1"); break; + case 4060: + width = 3982; + if (height == 2250) goto lx3; + width = 4018; + filters = 0x49494949; + zero_is_bad = 1; + adobe_coeff ("Panasonic","DMC-G1"); break; + case 4290: + height += 38; + left_margin = -14; + filters = 0x49494949; + case 4330: + width = 4248; + if ((height -= 39) == 2400) + top_margin = 15; + left_margin += 17; + adobe_coeff ("Panasonic","DMC-LX2"); break; + case 4508: + height -= 6; + width = 4429; + filters = 0x16161616; + adobe_coeff ("Panasonic","DMC-FX150"); break; + } + } else if (!strcmp(model,"C770UZ")) { + height = 1718; + width = 2304; + filters = 0x16161616; + load_raw = &CLASS packed_12_load_raw; + load_flags = 7; + } else if (!strcmp(make,"OLYMPUS")) { + height += height & 1; + filters = exif_cfa; + if (load_raw == &CLASS olympus_e410_load_raw) { + black >>= 4; + } else if (!strcmp(model,"E-10") || + !strncmp(model,"E-20",4)) { + black <<= 2; + } else if (!strcmp(model,"E-300") || + !strcmp(model,"E-500")) { + width -= 20; + if (load_raw == &CLASS unpacked_load_raw) { + maximum = 0xfc30; + black = 0; + } + } else if (!strcmp(model,"E-330")) { + width -= 30; + if (load_raw == &CLASS unpacked_load_raw) + maximum = 0xf790; + } else if (!strcmp(model,"SP550UZ")) { + thumb_length = fsize - (thumb_offset = 0xa39800); + thumb_height = 480; + thumb_width = 640; + } + } else if (!strcmp(model,"N Digital")) { + height = 2047; + width = 3072; + filters = 0x61616161; + data_offset = 0x1a00; + load_raw = &CLASS packed_12_load_raw; + } else if (!strcmp(model,"DSC-F828")) { + width = 3288; + left_margin = 5; + data_offset = 862144; + load_raw = &CLASS sony_load_raw; + filters = 0x9c9c9c9c; + colors = 4; + strcpy (cdesc, "RGBE"); + } else if (!strcmp(model,"DSC-V3")) { + width = 3109; + left_margin = 59; + data_offset = 787392; + load_raw = &CLASS sony_load_raw; + } else if (!strcmp(make,"SONY") && raw_width == 3984) { + adobe_coeff ("SONY","DSC-R1"); + width = 3925; + order = 0x4d4d; + } else if (!strcmp(model,"DSLR-A100")) { + height--; + } else if (!strcmp(model,"DSLR-A350")) { + height -= 4; + } else if (!strcmp(model,"C603v")) { + height = 480; + width = 640; + goto c603v; + } else if (!strcmp(model,"C603y")) { + height = 2134; + width = 2848; +c603v: + filters = 0; + load_raw = &CLASS kodak_yrgb_load_raw; + } else if (!strcmp(model,"C603")) { + raw_height = height = 2152; + raw_width = width = 2864; + goto c603; + } else if (!strcmp(model,"C330")) { + height = 1744; + width = 2336; + raw_height = 1779; + raw_width = 2338; + top_margin = 33; + left_margin = 1; +c603: + order = 0x4949; + if ((data_offset = fsize - raw_height*raw_width)) { + fseek (ifp, 168, SEEK_SET); + read_shorts (curve, 256); +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.curve_state = LIBRAW_COLORSTATE_LOADED; +#endif + } else use_gamma = 0; + load_raw = &CLASS eight_bit_load_raw; + } else if (!strcasecmp(make,"KODAK")) { + if (filters == UINT_MAX) filters = 0x61616161; + if (!strncmp(model,"NC2000",6)) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"EOSDCS3B")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"EOSDCS1")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"DCS420")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"DCS460")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"DCS460A")) { + width -= 4; + left_margin = 2; + colors = 1; + filters = 0; + } else if (!strcmp(model,"DCS660M")) { + black = 214; + colors = 1; + filters = 0; + } else if (!strcmp(model,"DCS760M")) { + colors = 1; + filters = 0; + } + if (strstr(model,"DC25")) { + strcpy (model, "DC25"); + data_offset = 15424; + } + if (!strncmp(model,"DC2",3)) { + height = 242; + if (fsize < 100000) { + raw_width = 256; width = 249; + pixel_aspect = (4.0*height) / (3.0*width); + } else { + raw_width = 512; width = 501; + pixel_aspect = (493.0*height) / (373.0*width); + } + data_offset += raw_width + 1; + colors = 4; + filters = 0x8d8d8d8d; + simple_coeff(1); + pre_mul[1] = 1.179; + pre_mul[2] = 1.209; + pre_mul[3] = 1.036; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + load_raw = &CLASS eight_bit_load_raw; + } else if (!strcmp(model,"40")) { + strcpy (model, "DC40"); + height = 512; + width = 768; + data_offset = 1152; + load_raw = &CLASS kodak_radc_load_raw; + } else if (strstr(model,"DC50")) { + strcpy (model, "DC50"); + height = 512; + width = 768; + data_offset = 19712; + load_raw = &CLASS kodak_radc_load_raw; + } else if (strstr(model,"DC120")) { + strcpy (model, "DC120"); + height = 976; + width = 848; + pixel_aspect = height/0.75/width; + load_raw = tiff_compress == 7 ? + &CLASS kodak_jpeg_load_raw : &CLASS kodak_dc120_load_raw; + } else if (!strcmp(model,"DCS200")) { + thumb_height = 128; + thumb_width = 192; + thumb_offset = 6144; + thumb_misc = 360; + write_thumb = &CLASS layer_thumb; + height = 1024; + width = 1536; + data_offset = 79872; + load_raw = &CLASS eight_bit_load_raw; + black = 17; + } + } else if (!strcmp(model,"Fotoman Pixtura")) { + height = 512; + width = 768; + data_offset = 3632; + load_raw = &CLASS kodak_radc_load_raw; + filters = 0x61616161; + simple_coeff(2); + } else if (!strcmp(model,"QuickTake 100")) { + fseek (ifp, 544, SEEK_SET); + height = get2(); + width = get2(); + data_offset = (get4(),get2()) == 30 ? 738:736; + if (height > width) { + SWAP(height,width); + fseek (ifp, data_offset-6, SEEK_SET); + flip = ~get2() & 3 ? 5:6; + } + load_raw = &CLASS quicktake_100_load_raw; + filters = 0x61616161; + } else if (!strcmp(model,"QuickTake 150")) { + data_offset = 738 - head[5]; + if (head[5]) strcpy (model+10, "200"); + load_raw = &CLASS kodak_radc_load_raw; + height = 480; + width = 640; + filters = 0x61616161; + } else if (!strcmp(make,"Rollei") && !load_raw) { + switch (raw_width) { + case 1316: + height = 1030; + width = 1300; + top_margin = 1; + left_margin = 6; + break; + case 2568: + height = 1960; + width = 2560; + top_margin = 2; + left_margin = 8; + } + filters = 0x16161616; + load_raw = &CLASS rollei_load_raw; + pre_mul[0] = 1.8; + pre_mul[2] = 1.3; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else if (!strcmp(model,"PC-CAM 600")) { + height = 768; + data_offset = width = 1024; + filters = 0x49494949; + load_raw = &CLASS eight_bit_load_raw; + pre_mul[0] = 1.14; + pre_mul[2] = 2.73; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else if (!strcmp(model,"QV-2000UX")) { + height = 1208; + width = 1632; + data_offset = width * 2; + load_raw = &CLASS eight_bit_load_raw; + } else if (fsize == 3217760) { + height = 1546; + width = 2070; + raw_width = 2080; + load_raw = &CLASS eight_bit_load_raw; + } else if (!strcmp(model,"QV-4000")) { + height = 1700; + width = 2260; + load_raw = &CLASS unpacked_load_raw; + maximum = 0xffff; + } else if (!strcmp(model,"QV-5700")) { + height = 1924; + width = 2576; + load_raw = &CLASS casio_qv5700_load_raw; + } else if (!strcmp(model,"QV-R41")) { + height = 1720; + width = 2312; + raw_width = 3520; + left_margin = 2; + } else if (!strcmp(model,"QV-R51")) { + height = 1926; + width = 2580; + raw_width = 3904; + pre_mul[0] = 1.340; + pre_mul[2] = 1.672; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else if (!strcmp(model,"EX-S100")) { + height = 1544; + width = 2058; + raw_width = 3136; + pre_mul[0] = 1.631; + pre_mul[2] = 1.106; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else if (!strcmp(model,"EX-Z50")) { + height = 1931; + width = 2570; + raw_width = 3904; + pre_mul[0] = 2.529; + pre_mul[2] = 1.185; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else if (!strcmp(model,"EX-Z55")) { + height = 1960; + width = 2570; + raw_width = 3904; + pre_mul[0] = 1.520; + pre_mul[2] = 1.316; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else if (!strcmp(model,"EX-P505")) { + height = 1928; + width = 2568; + raw_width = 3852; + maximum = 0xfff; + pre_mul[0] = 2.07; + pre_mul[2] = 1.88; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else if (fsize == 9313536) { /* EX-P600 or QV-R61 */ + height = 2142; + width = 2844; + raw_width = 4288; + pre_mul[0] = 1.797; + pre_mul[2] = 1.219; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } else if (!strcmp(model,"EX-P700")) { + height = 2318; + width = 3082; + raw_width = 4672; + pre_mul[0] = 1.758; + pre_mul[2] = 1.504; +#ifdef LIBRAW_LIBRARY_BUILD + color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST; +#endif + } + if (!model[0]) + sprintf (model, "%dx%d", width, height); + if (filters == UINT_MAX) filters = 0x94949494; + if (raw_color) adobe_coeff (make, model); + if (thumb_offset && !thumb_height) { + fseek (ifp, thumb_offset, SEEK_SET); + if (ljpeg_start (&jh, 1)) { + thumb_width = jh.wide; + thumb_height = jh.high; + } + } +dng_skip: + if (!load_raw || height < 22) is_raw = 0; +#ifdef NO_JPEG + if (load_raw == &CLASS kodak_jpeg_load_raw) { +#ifdef DCRAW_VERBOSE + fprintf (stderr,_("%s: You must link dcraw with libjpeg!!\n"), ifname); +#endif + is_raw = 0; +#ifdef LIBRAW_LIBRARY_BUILD + imgdata.process_warnings |= LIBRAW_WARN_NO_JPEGLIB; +#endif + } +#endif + if (!cdesc[0]) + strcpy (cdesc, colors == 3 ? "RGB":"GMCY"); + if (!raw_height) raw_height = height; + if (!raw_width ) raw_width = width; + if (filters && colors == 3) + for (i=0; i < 32; i+=4) { + if ((filters >> i & 15) == 9) + filters |= 2 << i; + if ((filters >> i & 15) == 6) + filters |= 8 << i; + } +notraw: + if (flip == -1) flip = tiff_flip; + if (flip == -1) flip = 0; +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_IDENTIFY,1,2); +#endif +} +#line 9177 "dcraw/dcraw.c" +void CLASS convert_to_rgb() +{ + int row, col, c, i, j, k; + ushort *img; + float out[3], out_cam[3][4]; + double num, inverse[3][3]; + static const double xyzd50_srgb[3][3] = + { { 0.436083, 0.385083, 0.143055 }, + { 0.222507, 0.716888, 0.060608 }, + { 0.013930, 0.097097, 0.714022 } }; + static const double rgb_rgb[3][3] = + { { 1,0,0 }, { 0,1,0 }, { 0,0,1 } }; + static const double adobe_rgb[3][3] = + { { 0.715146, 0.284856, 0.000000 }, + { 0.000000, 1.000000, 0.000000 }, + { 0.000000, 0.041166, 0.958839 } }; + static const double wide_rgb[3][3] = + { { 0.593087, 0.404710, 0.002206 }, + { 0.095413, 0.843149, 0.061439 }, + { 0.011621, 0.069091, 0.919288 } }; + static const double prophoto_rgb[3][3] = + { { 0.529317, 0.330092, 0.140588 }, + { 0.098368, 0.873465, 0.028169 }, + { 0.016879, 0.117663, 0.865457 } }; + static const double (*out_rgb[])[3] = + { rgb_rgb, adobe_rgb, wide_rgb, prophoto_rgb, xyz_rgb }; + static const char *name[] = + { "sRGB", "Adobe RGB (1998)", "WideGamut D65", "ProPhoto D65", "XYZ" }; + static const unsigned phead[] = + { 1024, 0, 0x2100000, 0x6d6e7472, 0x52474220, 0x58595a20, 0, 0, 0, + 0x61637370, 0, 0, 0x6e6f6e65, 0, 0, 0, 0, 0xf6d6, 0x10000, 0xd32d }; + unsigned pbody[] = + { 10, 0x63707274, 0, 36, /* cprt */ + 0x64657363, 0, 40, /* desc */ + 0x77747074, 0, 20, /* wtpt */ + 0x626b7074, 0, 20, /* bkpt */ + 0x72545243, 0, 14, /* rTRC */ + 0x67545243, 0, 14, /* gTRC */ + 0x62545243, 0, 14, /* bTRC */ + 0x7258595a, 0, 20, /* rXYZ */ + 0x6758595a, 0, 20, /* gXYZ */ + 0x6258595a, 0, 20 }; /* bXYZ */ + static const unsigned pwhite[] = { 0xf351, 0x10000, 0x116cc }; + unsigned pcurve[] = { 0x63757276, 0, 1, 0x1000000 }; + +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_CONVERT_RGB,0,2); +#endif + + memcpy (out_cam, rgb_cam, sizeof out_cam); + raw_color |= colors == 1 || document_mode || + output_color < 1 || output_color > 5; + if (!raw_color) { + oprof = (unsigned *) calloc (phead[0], 1); + merror (oprof, "convert_to_rgb()"); + memcpy (oprof, phead, sizeof phead); + if (output_color == 5) oprof[4] = oprof[5]; + oprof[0] = 132 + 12*pbody[0]; + for (i=0; i < pbody[0]; i++) { + oprof[oprof[0]/4] = i ? (i > 1 ? 0x58595a20 : 0x64657363) : 0x74657874; + pbody[i*3+2] = oprof[0]; + oprof[0] += (pbody[i*3+3] + 3) & -4; + } + memcpy (oprof+32, pbody, sizeof pbody); + oprof[pbody[5]/4+2] = strlen(name[output_color-1]) + 1; + memcpy ((char *)oprof+pbody[8]+8, pwhite, sizeof pwhite); + if (output_bps == 8 || gamma_16bit) +#ifdef SRGB_GAMMA + pcurve[3] = 0x2330000; +#else + pcurve[3] = 0x1f00000; +#endif + for (i=4; i < 7; i++) + memcpy ((char *)oprof+pbody[i*3+2], pcurve, sizeof pcurve); + pseudoinverse ((double (*)[3]) out_rgb[output_color-1], inverse, 3); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) { + for (num = k=0; k < 3; k++) + num += xyzd50_srgb[i][k] * inverse[j][k]; + oprof[pbody[j*3+23]/4+i+2] = num * 0x10000 + 0.5; + } + for (i=0; i < phead[0]/4; i++) + oprof[i] = htonl(oprof[i]); + strcpy ((char *)oprof+pbody[2]+8, "auto-generated by dcraw"); + strcpy ((char *)oprof+pbody[5]+12, name[output_color-1]); + for (i=0; i < 3; i++) + for (j=0; j < colors; j++) + for (out_cam[i][j] = k=0; k < 3; k++) + out_cam[i][j] += out_rgb[output_color-1][i][k] * rgb_cam[k][j]; + } +#ifdef DCRAW_VERBOSE + if (verbose) + fprintf (stderr, raw_color ? _("Building histograms...\n") : + _("Converting to %s colorspace...\n"), name[output_color-1]); + +#endif +#ifdef LIBRAW_LIBRARY_BUILD + memset(histogram,0,sizeof(int)*LIBRAW_HISTOGRAM_SIZE*4); +#else + memset (histogram, 0, sizeof histogram); +#endif + for (img=image[0], row=0; row < height; row++) + for (col=0; col < width; col++, img+=4) { + if (!raw_color) { + out[0] = out[1] = out[2] = 0; + FORCC { + out[0] += out_cam[0][c] * img[c]; + out[1] += out_cam[1][c] * img[c]; + out[2] += out_cam[2][c] * img[c]; + } + FORC3 img[c] = CLIP((int) out[c]); + } + else if (document_mode) + img[0] = img[FC(row,col)]; + FORCC histogram[c][img[c] >> 3]++; + } + if (colors == 4 && output_color) colors = 3; + if (document_mode && filters) colors = 1; +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_CONVERT_RGB,1,2); +#endif +} + +void CLASS fuji_rotate() +{ + int i, row, col; + double step; + float r, c, fr, fc; + unsigned ur, uc; + ushort wide, high, (*img)[4], (*pix)[4]; + + if (!fuji_width) return; +#ifdef DCRAW_VERBOSE + if (verbose) + fprintf (stderr,_("Rotating image 45 degrees...\n")); +#endif + fuji_width = (fuji_width - 1 + shrink) >> shrink; + step = sqrt(0.5); + wide = fuji_width / step; + high = (height - fuji_width) / step; + img = (ushort (*)[4]) calloc (wide*high, sizeof *img); + merror (img, "fuji_rotate()"); + +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_FUJI_ROTATE,0,2); +#endif + + for (row=0; row < high; row++) + for (col=0; col < wide; col++) { + ur = r = fuji_width + (row-col)*step; + uc = c = (row+col)*step; + if (ur > height-2 || uc > width-2) continue; + fr = r - ur; + fc = c - uc; + pix = image + ur*width + uc; + for (i=0; i < colors; i++) + img[row*wide+col][i] = + (pix[ 0][i]*(1-fc) + pix[ 1][i]*fc) * (1-fr) + + (pix[width][i]*(1-fc) + pix[width+1][i]*fc) * fr; + } + free (image); + width = wide; + height = high; + image = img; + fuji_width = 0; +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_FUJI_ROTATE,1,2); +#endif +} + +void CLASS stretch() +{ + ushort newdim, (*img)[4], *pix0, *pix1; + int row, col, c; + double rc, frac; + + if (pixel_aspect == 1) return; +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_STRETCH,0,2); +#endif +#ifdef DCRAW_VERBOSE + if (verbose) fprintf (stderr,_("Stretching the image...\n")); +#endif + if (pixel_aspect < 1) { + newdim = height / pixel_aspect + 0.5; + img = (ushort (*)[4]) calloc (width*newdim, sizeof *img); + merror (img, "stretch()"); + for (rc=row=0; row < newdim; row++, rc+=pixel_aspect) { + frac = rc - (c = rc); + pix0 = pix1 = image[c*width]; + if (c+1 < height) pix1 += width*4; + for (col=0; col < width; col++, pix0+=4, pix1+=4) + FORCC img[row*width+col][c] = pix0[c]*(1-frac) + pix1[c]*frac + 0.5; + } + height = newdim; + } else { + newdim = width * pixel_aspect + 0.5; + img = (ushort (*)[4]) calloc (height*newdim, sizeof *img); + merror (img, "stretch()"); + for (rc=col=0; col < newdim; col++, rc+=1/pixel_aspect) { + frac = rc - (c = rc); + pix0 = pix1 = image[c]; + if (c+1 < width) pix1 += 4; + for (row=0; row < height; row++, pix0+=width*4, pix1+=width*4) + FORCC img[row*newdim+col][c] = pix0[c]*(1-frac) + pix1[c]*frac + 0.5; + } + width = newdim; + } + free (image); + image = img; +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_STRETCH,1,2); +#endif +} + +int CLASS flip_index (int row, int col) +{ + if (flip & 4) SWAP(row,col); + if (flip & 2) row = iheight - 1 - row; + if (flip & 1) col = iwidth - 1 - col; + return row * iwidth + col; +} + +void CLASS gamma_lut (ushort lut[0x10000]) +{ + int perc, c, val, total, i; + float t_white=0, r; + + perc = width * height * 0.01; /* 99th percentile white level */ + if (fuji_width) perc /= 2; + if ((highlight & ~2) || no_auto_bright) perc = -1; + FORCC { + for (val=0x2000, total=0; --val > 32; ) + if ((total += histogram[c][val]) > perc) break; + if (t_white < val) t_white = val; + } + t_white *= 8 / bright; + for (i=0; i < 0x10000; i++) { + r = i / t_white; + val = 65536 * ( !use_gamma ? r : +#ifdef SRGB_GAMMA + r <= 0.00304 ? r*12.92 : pow((double)r,2.5/6)*1.055-0.055 ); +#else + r <= 0.018 ? r*4.5 : pow((double)r,0.45)*1.099-0.099 ); +#endif + if (val > 65535) val = 65535; + lut[i] = val; + } +} + + +#line 9453 "dcraw/dcraw.c" +void CLASS tiff_set (ushort *ntag, + ushort tag, ushort type, int count, int val) +{ + struct tiff_tag *tt; + int c; + + tt = (struct tiff_tag *)(ntag+1) + (*ntag)++; + tt->tag = tag; + tt->type = type; + tt->count = count; + if (type < 3 && count <= 4) + FORC(4) tt->val.c[c] = val >> (c << 3); + else if (type == 3 && count <= 2) + FORC(2) tt->val.s[c] = val >> (c << 4); + else tt->val.i = val; +} + +#define TOFF(ptr) ((char *)(&(ptr)) - (char *)th) + +void CLASS tiff_head (struct tiff_hdr *th, int full) +{ + int c, psize=0; + struct tm *t; + + memset (th, 0, sizeof *th); + th->t_order = htonl(0x4d4d4949) >> 16; + th->magic = 42; + th->ifd = 10; + if (full) { + tiff_set (&th->ntag, 254, 4, 1, 0); + tiff_set (&th->ntag, 256, 4, 1, width); + tiff_set (&th->ntag, 257, 4, 1, height); + tiff_set (&th->ntag, 258, 3, colors, output_bps); + if (colors > 2) + th->tag[th->ntag-1].val.i = TOFF(th->bps); + FORC4 th->bps[c] = output_bps; + tiff_set (&th->ntag, 259, 3, 1, 1); + tiff_set (&th->ntag, 262, 3, 1, 1 + (colors > 1)); + } + tiff_set (&th->ntag, 270, 2, 512, TOFF(th->t_desc)); + tiff_set (&th->ntag, 271, 2, 64, TOFF(th->t_make)); + tiff_set (&th->ntag, 272, 2, 64, TOFF(th->t_model)); + if (full) { + if (oprof) psize = ntohl(oprof[0]); + tiff_set (&th->ntag, 273, 4, 1, sizeof *th + psize); + tiff_set (&th->ntag, 277, 3, 1, colors); + tiff_set (&th->ntag, 278, 4, 1, height); + tiff_set (&th->ntag, 279, 4, 1, height*width*colors*output_bps/8); + } else + tiff_set (&th->ntag, 274, 3, 1, "12435867"[flip]-'0'); + tiff_set (&th->ntag, 282, 5, 1, TOFF(th->rat[0])); + tiff_set (&th->ntag, 283, 5, 1, TOFF(th->rat[2])); + tiff_set (&th->ntag, 284, 3, 1, 1); + tiff_set (&th->ntag, 296, 3, 1, 2); + tiff_set (&th->ntag, 305, 2, 32, TOFF(th->soft)); + tiff_set (&th->ntag, 306, 2, 20, TOFF(th->date)); + tiff_set (&th->ntag, 315, 2, 64, TOFF(th->t_artist)); + tiff_set (&th->ntag, 34665, 4, 1, TOFF(th->nexif)); + if (psize) tiff_set (&th->ntag, 34675, 7, psize, sizeof *th); + tiff_set (&th->nexif, 33434, 5, 1, TOFF(th->rat[4])); + tiff_set (&th->nexif, 33437, 5, 1, TOFF(th->rat[6])); + tiff_set (&th->nexif, 34855, 3, 1, iso_speed); + tiff_set (&th->nexif, 37386, 5, 1, TOFF(th->rat[8])); + if (gpsdata[1]) { + tiff_set (&th->ntag, 34853, 4, 1, TOFF(th->ngps)); + tiff_set (&th->ngps, 0, 1, 4, 0x202); + tiff_set (&th->ngps, 1, 2, 2, gpsdata[29]); + tiff_set (&th->ngps, 2, 5, 3, TOFF(th->gps[0])); + tiff_set (&th->ngps, 3, 2, 2, gpsdata[30]); + tiff_set (&th->ngps, 4, 5, 3, TOFF(th->gps[6])); + tiff_set (&th->ngps, 5, 1, 1, gpsdata[31]); + tiff_set (&th->ngps, 6, 5, 1, TOFF(th->gps[18])); + tiff_set (&th->ngps, 7, 5, 3, TOFF(th->gps[12])); + tiff_set (&th->ngps, 18, 2, 12, TOFF(th->gps[20])); + tiff_set (&th->ngps, 29, 2, 12, TOFF(th->gps[23])); + memcpy (th->gps, gpsdata, sizeof th->gps); + } + th->rat[0] = th->rat[2] = 300; + th->rat[1] = th->rat[3] = 1; + FORC(6) th->rat[4+c] = 1000000; + th->rat[4] *= shutter; + th->rat[6] *= aperture; + th->rat[8] *= focal_len; + strncpy (th->t_desc, desc, 512); + strncpy (th->t_make, make, 64); + strncpy (th->t_model, model, 64); + strcpy (th->soft, "dcraw v"VERSION); + t = gmtime (×tamp); + sprintf (th->date, "%04d:%02d:%02d %02d:%02d:%02d", + t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); + strncpy (th->t_artist, artist, 64); +} + +void CLASS jpeg_thumb_writer (FILE *tfp,char *t_humb,int t_humb_length) +{ + ushort exif[5]; + struct tiff_hdr th; + fputc (0xff, tfp); + fputc (0xd8, tfp); + if (strcmp (t_humb+6, "Exif")) { + memcpy (exif, "\xff\xe1 Exif\0\0", 10); + exif[1] = htons (8 + sizeof th); + fwrite (exif, 1, sizeof exif, tfp); + tiff_head (&th, 0); + fwrite (&th, 1, sizeof th, tfp); + } + fwrite (t_humb+2, 1, t_humb_length-2, tfp); +} + + +void CLASS jpeg_thumb (FILE *tfp) +{ + char *thumb; + ushort exif[5]; + struct tiff_hdr th; + + thumb = (char *) malloc (thumb_length); + merror (thumb, "jpeg_thumb()"); + fread (thumb, 1, thumb_length, ifp); +#if 0 + fputc (0xff, tfp); + fputc (0xd8, tfp); + if (strcmp (thumb+6, "Exif")) { + memcpy (exif, "\xff\xe1 Exif\0\0", 10); + exif[1] = htons (8 + sizeof th); + fwrite (exif, 1, sizeof exif, tfp); + tiff_head (&th, 0); + fwrite (&th, 1, sizeof th, tfp); + } + fwrite (thumb+2, 1, thumb_length-2, tfp); +#else + jpeg_thumb_writer(tfp,thumb,thumb_length); +#endif + free (thumb); +} + +void CLASS write_ppm_tiff (FILE *ofp) +{ + struct tiff_hdr th; + uchar *ppm; + ushort *ppm2,lut16[0x10000]; + int c, row, col, soff, rstep, cstep; + + iheight = height; + iwidth = width; + if (flip & 4) SWAP(height,width); + ppm = (uchar *) calloc (width, colors*output_bps/8); + ppm2 = (ushort *) ppm; + merror (ppm, "write_ppm_tiff()"); + if (output_tiff) { + tiff_head (&th, 1); + fwrite (&th, sizeof th, 1, ofp); + if (oprof) + fwrite (oprof, ntohl(oprof[0]), 1, ofp); + } else if (colors > 3) + fprintf (ofp, + "P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\nTUPLTYPE %s\nENDHDR\n", + width, height, colors, (1 << output_bps)-1, cdesc); + else + fprintf (ofp, "P%d\n%d %d\n%d\n", + colors/2+5, width, height, (1 << output_bps)-1); + + if (output_bps == 8 || gamma_16bit ) gamma_lut (lut16); + + soff = flip_index (0, 0); + cstep = flip_index (0, 1) - soff; + rstep = flip_index (1, 0) - flip_index (0, width); + for (row=0; row < height; row++, soff += rstep) { + for (col=0; col < width; col++, soff += cstep) + if (output_bps == 8) + FORCC ppm [col*colors+c] = lut16[image[soff][c]]/256; + else if(gamma_16bit) FORCC ppm2[col*colors+c] = lut16[image[soff][c]]; + else FORCC ppm2[col*colors+c] = image[soff][c]; + if (output_bps == 16 && !output_tiff && htons(0x55aa) != 0x55aa) + swab ((char*)ppm2, (char*)ppm2, width*colors*2); + fwrite (ppm, colors*output_bps/8, width, ofp); + } + free (ppm); +} diff --git a/src/LibRaw/internal/dcraw_fileio.cpp b/src/LibRaw/internal/dcraw_fileio.cpp new file mode 100644 index 000000000000..284ac31a77af --- /dev/null +++ b/src/LibRaw/internal/dcraw_fileio.cpp @@ -0,0 +1,215 @@ +/* + GENERATED FILE, DO NOT EDIT + Generated from dcraw/dcraw.c at Sat Feb 7 20:23:45 2009 + Look into original file (probably http://cybercom.net/~dcoffin/dcraw/dcraw.c) + for copyright information. +*/ + +#line 3971 "dcraw/dcraw.c" +#define CLASS LibRaw:: +#include "libraw/libraw_types.h" +#define LIBRAW_LIBRARY_BUILD +#include "libraw/libraw.h" +#include "internal/defines.h" +#include "internal/var_defines.h" +#line 3981 "dcraw/dcraw.c" + +/* + Seach from the current directory up to the root looking for + a ".badpixels" file, and fix those pixels now. + */ +void CLASS bad_pixels (char *fname) +{ + FILE *fp=0; + char *cp, line[128]; + int len, time, row, col, r, c, rad, tot, n, fixed=0; + + if (!filters) return; +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_BAD_PIXELS,0,2); +#endif + if (fname) + fp = fopen (fname, "r"); +#line 4024 "dcraw/dcraw.c" + if (!fp) + { +#ifdef LIBRAW_LIBRARY_BUILD + imgdata.process_warnings |= LIBRAW_WARN_NO_BADPIXELMAP; +#endif + return; + } + while (fgets (line, 128, fp)) { + cp = strchr (line, '#'); + if (cp) *cp = 0; + if (sscanf (line, "%d %d %d", &col, &row, &time) != 3) continue; + if ((unsigned) col >= width || (unsigned) row >= height) continue; + if (time > timestamp) continue; + for (tot=n=0, rad=1; rad < 3 && n==0; rad++) + for (r = row-rad; r <= row+rad; r++) + for (c = col-rad; c <= col+rad; c++) + if ((unsigned) r < height && (unsigned) c < width && + (r != row || c != col) && fc(r,c) == fc(row,col)) { + tot += BAYER2(r,c); + n++; + } + BAYER2(row,col) = tot/n; +#ifdef DCRAW_VERBOSE + if (verbose) { + if (!fixed++) + fprintf (stderr,_("Fixed dead pixels at:")); + fprintf (stderr, " %d,%d", col, row); + } +#endif + } +#ifdef DCRAW_VERBOSE + if (fixed) fputc ('\n', stderr); +#endif + fclose (fp); +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_BAD_PIXELS,1,2); +#endif +} + +void CLASS subtract (char *fname) +{ + FILE *fp; + int dim[3]={0,0,0}, comment=0, number=0, error=0, nd=0, c, row, col; + ushort *pixel; +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_DARK_FRAME,0,2); +#endif + + if (!(fp = fopen (fname, "rb"))) { +#ifdef DCRAW_VERBOSE + perror (fname); +#endif +#ifdef LIBRAW_LIBRARY_BUILD + imgdata.process_warnings |= LIBRAW_WARN_BAD_DARKFRAME_FILE; +#endif + return; + } + if (fgetc(fp) != 'P' || fgetc(fp) != '5') error = 1; + while (!error && nd < 3 && (c = fgetc(fp)) != EOF) { + if (c == '#') comment = 1; + if (c == '\n') comment = 0; + if (comment) continue; + if (isdigit(c)) number = 1; + if (number) { + if (isdigit(c)) dim[nd] = dim[nd]*10 + c -'0'; + else if (isspace(c)) { + number = 0; nd++; + } else error = 1; + } + } + if (error || nd < 3) { + fprintf (stderr,_("%s is not a valid PGM file!\n"), fname); + fclose (fp); return; + } else if (dim[0] != width || dim[1] != height || dim[2] != 65535) { +#ifdef DCRAW_VERBOSE + fprintf (stderr,_("%s has the wrong dimensions!\n"), fname); +#endif +#ifdef LIBRAW_LIBRARY_BUILD + imgdata.process_warnings |= LIBRAW_WARN_BAD_DARKFRAME_DIM; +#endif + fclose (fp); return; + } + pixel = (ushort *) calloc (width, sizeof *pixel); + merror (pixel, "subtract()"); + for (row=0; row < height; row++) { + fread (pixel, 2, width, fp); + for (col=0; col < width; col++) + BAYER(row,col) = MAX (BAYER(row,col) - ntohs(pixel[col]), 0); + } + free (pixel); + black = 0; +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_DARK_FRAME,1,2); +#endif +} +#line 9088 "dcraw/dcraw.c" + +#ifndef NO_LCMS +void CLASS apply_profile (char *input, char *output) +{ + char *prof; + cmsHPROFILE hInProfile=0, hOutProfile=0; + cmsHTRANSFORM hTransform; + FILE *fp; + unsigned size; + + cmsErrorAction (LCMS_ERROR_SHOW); + if (strcmp (input, "embed")) + hInProfile = cmsOpenProfileFromFile (input, "r"); + else if (profile_length) { +#ifndef LIBRAW_LIBRARY_BUILD + prof = (char *) malloc (profile_length); + merror (prof, "apply_profile()"); + fseek (ifp, profile_offset, SEEK_SET); + fread (prof, 1, profile_length, ifp); + hInProfile = cmsOpenProfileFromMem (prof, profile_length); + free (prof); +#else + hInProfile = cmsOpenProfileFromMem (imgdata.color.profile, profile_length); +#endif + } else + { +#ifdef LIBRAW_LIBRARY_BUILD + imgdata.process_warnings |= LIBRAW_WARN_NO_EMBEDDED_PROFILE; +#endif +#ifdef DCRAW_VERBOSE + fprintf (stderr,_("%s has no embedded profile.\n"), ifname); +#endif + } + if (!hInProfile) + { +#ifdef LIBRAW_LIBRARY_BUILD + imgdata.process_warnings |= LIBRAW_WARN_NO_INPUT_PROFILE; +#endif + return; + } + if (!output) + hOutProfile = cmsCreate_sRGBProfile(); + else if ((fp = fopen (output, "rb"))) { + fread (&size, 4, 1, fp); + fseek (fp, 0, SEEK_SET); + oprof = (unsigned *) malloc (size = ntohl(size)); + merror (oprof, "apply_profile()"); + fread (oprof, 1, size, fp); + fclose (fp); + if (!(hOutProfile = cmsOpenProfileFromMem (oprof, size))) { + free (oprof); + oprof = 0; + } +#ifdef DCRAW_VERBOSE + } else + fprintf (stderr,_("Cannot open file %s!\n"), output); +#else +} +#endif + if (!hOutProfile) + { +#ifdef LIBRAW_LIBRARY_BUILD + imgdata.process_warnings |= LIBRAW_WARN_BAD_OUTPUT_PROFILE; +#endif + goto quit; + } +#ifdef DCRAW_VERBOSE + if (verbose) + fprintf (stderr,_("Applying color profile...\n")); +#endif +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_APPLY_PROFILE,0,2); +#endif + hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16, + hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0); + cmsDoTransform (hTransform, image, image, width*height); + raw_color = 1; /* Don't use rgb_cam with a profile */ + cmsDeleteTransform (hTransform); + cmsCloseProfile (hOutProfile); +quit: + cmsCloseProfile (hInProfile); +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_APPLY_PROFILE,1,2); +#endif +} +#endif diff --git a/src/LibRaw/internal/defines.h b/src/LibRaw/internal/defines.h new file mode 100644 index 000000000000..ff5def5edd44 --- /dev/null +++ b/src/LibRaw/internal/defines.h @@ -0,0 +1,134 @@ +/* + GENERATED FILE, DO NOT EDIT + Generated from dcraw/dcraw.c at Sat Feb 7 20:23:43 2009 + Look into original file (probably http://cybercom.net/~dcoffin/dcraw/dcraw.c) + for copyright information. +*/ + +#line 27 "dcraw/dcraw.c" +#define NO_JPEG +#line 32 "dcraw/dcraw.c" +#define VERSION "8.91" + +#define _GNU_SOURCE +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _OPENMP +#include +#endif +/* + NO_JPEG disables decoding of compressed Kodak DC120 files. + NO_LCMS disables the "-p" option. + */ +#ifndef NO_JPEG +#include +#endif +#ifndef NO_LCMS +#include +#endif +#ifdef LOCALEDIR +#include +#define _(String) gettext(String) +#else +#define _(String) (String) +#endif +#line 75 "dcraw/dcraw.c" +#ifdef __CYGWIN__ +#include +#endif +#ifdef WIN32 +#include +#include +#pragma comment(lib, "ws2_32.lib") +#define snprintf _snprintf +#define strcasecmp _stricmp +#define strncasecmp strnicmp +typedef __int64 INT64; +typedef unsigned __int64 UINT64; +#else +#include +#include +#include +typedef long long INT64; +typedef unsigned long long UINT64; +#endif + +#ifdef LJPEG_DECODE +#error Please compile dcraw.c by itself. +#error Do not link it with ljpeg_decode. +#endif + +#ifndef LONG_BIT +#define LONG_BIT (8 * sizeof (long)) +#endif +#line 169 "dcraw/dcraw.c" +#define FORC(cnt) for (c=0; c < cnt; c++) +#define FORC3 FORC(3) +#define FORC4 FORC(4) +#define FORCC FORC(colors) + +#define SQR(x) ((x)*(x)) +#define ABS(x) (((int)(x) ^ ((int)(x) >> 31)) - ((int)(x) >> 31)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define LIM(x,min,max) MAX(min,MIN(x,max)) +#define ULIM(x,y,z) ((y) < (z) ? LIM(x,y,z) : LIM(x,z,y)) +#define CLIP(x) LIM(x,0,65535) +#define SWAP(a,b) { a ^= b; a ^= (b ^= a); } + +/* + In order to inline this calculation, I make the risky + assumption that all filter patterns can be described + by a repeating pattern of eight rows and two columns + + Do not use the FC or BAYER macros with the Leaf CatchLight, + because its pattern is 16x16, not 2x8. + + Return values are either 0/1/2/3 = G/M/C/Y or 0/1/2/3 = R/G1/B/G2 + + PowerShot 600 PowerShot A50 PowerShot Pro70 Pro90 & G1 + 0xe1e4e1e4: 0x1b4e4b1e: 0x1e4b4e1b: 0xb4b4b4b4: + + 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 + 0 G M G M G M 0 C Y C Y C Y 0 Y C Y C Y C 0 G M G M G M + 1 C Y C Y C Y 1 M G M G M G 1 M G M G M G 1 Y C Y C Y C + 2 M G M G M G 2 Y C Y C Y C 2 C Y C Y C Y + 3 C Y C Y C Y 3 G M G M G M 3 G M G M G M + 4 C Y C Y C Y 4 Y C Y C Y C + PowerShot A5 5 G M G M G M 5 G M G M G M + 0x1e4e1e4e: 6 Y C Y C Y C 6 C Y C Y C Y + 7 M G M G M G 7 M G M G M G + 0 1 2 3 4 5 + 0 C Y C Y C Y + 1 G M G M G M + 2 C Y C Y C Y + 3 M G M G M G + + All RGB cameras use one of these Bayer grids: + + 0x16161616: 0x61616161: 0x49494949: 0x94949494: + + 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 + 0 B G B G B G 0 G R G R G R 0 G B G B G B 0 R G R G R G + 1 G R G R G R 1 B G B G B G 1 R G R G R G 1 G B G B G B + 2 B G B G B G 2 G R G R G R 2 G B G B G B 2 R G R G R G + 3 G R G R G R 3 B G B G B G 3 R G R G R G 3 G B G B G B + */ + +#line 227 "dcraw/dcraw.c" +#define BAYER(row,col) \ + image[((row) >> shrink)*iwidth + ((col) >> shrink)][FC(row,col)] + +#define BAYER2(row,col) \ + image[((row) >> shrink)*iwidth + ((col) >> shrink)][fc(row,col)] diff --git a/src/LibRaw/internal/foveon.cpp b/src/LibRaw/internal/foveon.cpp new file mode 100644 index 000000000000..521d28a00b9e --- /dev/null +++ b/src/LibRaw/internal/foveon.cpp @@ -0,0 +1,817 @@ +/* + GENERATED FILE, DO NOT EDIT + Generated from dcraw/dcraw.c at Sat Feb 7 20:23:43 2009 + Look into original file (probably http://cybercom.net/~dcoffin/dcraw/dcraw.c) + for copyright information. +*/ + +#line 3250 "dcraw/dcraw.c" +#define CLASS LibRaw:: +#include "libraw/libraw_types.h" +#define LIBRAW_IO_REDEFINED +#define LIBRAW_LIBRARY_BUILD +#include "libraw/libraw.h" +#include "internal/defines.h" +#define SRC_USES_SHRINK +#define SRC_USES_BLACK +#define SRC_USES_CURVE +#include "internal/var_defines.h" +#define sget4(s) sget4((uchar *)s) +#line 3263 "dcraw/dcraw.c" + +/* RESTRICTED code starts here */ + +void CLASS foveon_decoder (unsigned size, unsigned code) +{ +#ifndef LIBRAW_NOTHREADS +#define huff tls->foveon_decoder_huff +#else + static unsigned huff[1024]; +#endif + struct decode *cur; + int i, len; + + if (!code) { + for (i=0; i < size; i++) + huff[i] = get4(); + init_decoder(); + } + cur = free_decode++; + if (free_decode > first_decode+2048) { +#ifdef LIBRAW_LIBRARY_BUILD + throw LIBRAW_EXCEPTION_DECODE_RAW; +#else + fprintf (stderr,_("%s: decoder table overflow\n"), ifname); + longjmp (failure, 2); +#endif + } + if (code) + for (i=0; i < size; i++) + if (huff[i] == code) { + cur->leaf = i; + return; + } + if ((len = code >> 27) > 26) return; + code = (len+1) << 27 | (code & 0x3ffffff) << 1; + + cur->branch[0] = free_decode; + foveon_decoder (size, code); + cur->branch[1] = free_decode; + foveon_decoder (size, code+1); +#ifndef LIBRAW_NOTHREADS +#undef huff +#endif +} + +void CLASS foveon_thumb (FILE *tfp) +{ + unsigned bwide, row, col, bitbuf=0, bit=1, c, i; + char *buf; + struct decode *dindex; + short pred[3]; + + bwide = get4(); + fprintf (tfp, "P6\n%d %d\n255\n", thumb_width, thumb_height); + if (bwide > 0) { + if (bwide < thumb_width*3) return; + buf = (char *) malloc (bwide); + merror (buf, "foveon_thumb()"); + for (row=0; row < thumb_height; row++) { + fread (buf, 1, bwide, ifp); + fwrite (buf, 3, thumb_width, tfp); + } + free (buf); + return; + } + foveon_decoder (256, 0); + + for (row=0; row < thumb_height; row++) { + memset (pred, 0, sizeof pred); + if (!bit) get4(); + for (bit=col=0; col < thumb_width; col++) + FORC3 { + for (dindex=first_decode; dindex->branch[0]; ) { + if ((bit = (bit-1) & 31) == 31) + for (i=0; i < 4; i++) + bitbuf = (bitbuf << 8) + fgetc(ifp); + dindex = dindex->branch[bitbuf >> bit & 1]; + } + pred[c] += dindex->leaf; + fputc (pred[c], tfp); + } + } +} + +void CLASS foveon_load_camf() +{ + unsigned key, i, val; + + fseek (ifp, meta_offset, SEEK_SET); + key = get4(); + fread (meta_data, 1, meta_length, ifp); + for (i=0; i < meta_length; i++) { + key = (key * 1597 + 51749) % 244944; + val = key * (INT64) 301593171 >> 24; + meta_data[i] ^= ((((key << 8) - val) >> 1) + val) >> 17; + } +} + +void CLASS foveon_load_raw() +{ + struct decode *dindex; + short diff[1024]; + unsigned bitbuf=0; + int pred[3], fixed, row, col, bit=-1, c, i; + + fixed = get4(); + read_shorts ((ushort *) diff, 1024); + if (!fixed) foveon_decoder (1024, 0); + + for (row=0; row < height; row++) { + memset (pred, 0, sizeof pred); + if (!bit && !fixed && atoi(model+2) < 14) get4(); + for (col=bit=0; col < width; col++) { + if (fixed) { + bitbuf = get4(); + FORC3 pred[2-c] += diff[bitbuf >> c*10 & 0x3ff]; + } + else FORC3 { + for (dindex=first_decode; dindex->branch[0]; ) { + if ((bit = (bit-1) & 31) == 31) + for (i=0; i < 4; i++) + bitbuf = (bitbuf << 8) + fgetc(ifp); + dindex = dindex->branch[bitbuf >> bit & 1]; + } + pred[c] += diff[dindex->leaf]; + if (pred[c] >> 16 && ~pred[c] >> 16) derror(); + } + FORC3 image[row*width+col][c] = pred[c]; + } + } + if (document_mode) + for (i=0; i < height*width*4; i++) + if ((short) image[0][i] < 0) image[0][i] = 0; + foveon_load_camf(); +} + +const char * CLASS foveon_camf_param (const char *block, const char *param) +{ + unsigned idx, num; + char *pos, *cp, *dp; + + for (idx=0; idx < meta_length; idx += sget4(pos+8)) { + pos = meta_data + idx; + if (strncmp (pos, "CMb", 3)) break; + if (pos[3] != 'P') continue; + if (strcmp (block, pos+sget4(pos+12))) continue; + cp = pos + sget4(pos+16); + num = sget4(cp); + dp = pos + sget4(cp+4); + while (num--) { + cp += 8; + if (!strcmp (param, dp+sget4(cp))) + return dp+sget4(cp+4); + } + } + return 0; +} + +void * CLASS foveon_camf_matrix (unsigned dim[3], const char *name) +{ + unsigned i, idx, type, ndim, size, *mat; + char *pos, *cp, *dp; + double dsize; + + for (idx=0; idx < meta_length; idx += sget4(pos+8)) { + pos = meta_data + idx; + if (strncmp (pos, "CMb", 3)) break; + if (pos[3] != 'M') continue; + if (strcmp (name, pos+sget4(pos+12))) continue; + dim[0] = dim[1] = dim[2] = 1; + cp = pos + sget4(pos+16); + type = sget4(cp); + if ((ndim = sget4(cp+4)) > 3) break; + dp = pos + sget4(cp+8); + for (i=ndim; i--; ) { + cp += 12; + dim[i] = sget4(cp); + } + if ((dsize = (double) dim[0]*dim[1]*dim[2]) > meta_length/4) break; + mat = (unsigned *) malloc ((size = dsize) * 4); + merror (mat, "foveon_camf_matrix()"); + for (i=0; i < size; i++) + if (type && type != 6) + mat[i] = sget4(dp + i*4); + else + mat[i] = sget4(dp + i*2) & 0xffff; + return mat; + } +#ifdef LIBRAW_LIBRARY_BUILD + imgdata.process_warnings |= LIBRAW_WARN_FOVEON_NOMATRIX; +#endif +#ifdef DCRAW_VERBOSE + fprintf (stderr,_("%s: \"%s\" matrix not found!\n"), ifname, name); +#endif + return 0; +} + +int CLASS foveon_fixed (void *ptr, int size, const char *name) +{ + void *dp; + unsigned dim[3]; + + dp = foveon_camf_matrix (dim, name); + if (!dp) return 0; + memcpy (ptr, dp, size*4); + free (dp); + return 1; +} + +float CLASS foveon_avg (short *pix, int range[2], float cfilt) +{ + int i; + float val, min=FLT_MAX, max=-FLT_MAX, sum=0; + + for (i=range[0]; i <= range[1]; i++) { + sum += val = pix[i*4] + (pix[i*4]-pix[(i-1)*4]) * cfilt; + if (min > val) min = val; + if (max < val) max = val; + } + if (range[1] - range[0] == 1) return sum/2; + return (sum - min - max) / (range[1] - range[0] - 1); +} + +short * CLASS foveon_make_curve (double max, double mul, double filt) +{ + short *curve; + unsigned i, size; + double x; + + if (!filt) filt = 0.8; + size = 4*M_PI*max / filt; + if (size == UINT_MAX) size--; + curve = (short *) calloc (size+1, sizeof *curve); + merror (curve, "foveon_make_curve()"); + curve[0] = size; + for (i=0; i < size; i++) { + x = i*filt/max/4; + curve[i+1] = (cos(x)+1)/2 * tanh(i*filt/mul) * mul + 0.5; + } + return curve; +} + +void CLASS foveon_make_curves + (short **curvep, float dq[3], float div[3], float filt) +{ + double mul[3], max=0; + int c; + + FORC3 mul[c] = dq[c]/div[c]; + FORC3 if (max < mul[c]) max = mul[c]; + FORC3 curvep[c] = foveon_make_curve (max, mul[c], filt); +} + +int CLASS foveon_apply_curve (short *curve, int i) +{ + if (abs(i) >= curve[0]) return 0; + return i < 0 ? -curve[1-i] : curve[1+i]; +} + +#line 3525 "dcraw/dcraw.c" +#ifdef image +#undef image +#endif +#define image ((short(*)[4]) imgdata.image) +#line 3532 "dcraw/dcraw.c" + +void CLASS foveon_interpolate() +{ + static const short hood[] = { -1,-1, -1,0, -1,1, 0,-1, 0,1, 1,-1, 1,0, 1,1 }; + short *pix, prev[3], *curve[8], (*shrink)[3]; + float cfilt=0, ddft[3][3][2], ppm[3][3][3]; + float cam_xyz[3][3], correct[3][3], last[3][3], trans[3][3]; + float chroma_dq[3], color_dq[3], diag[3][3], div[3]; + float (*black)[3], (*sgain)[3], (*sgrow)[3]; + float fsum[3], val, frow, num; + int row, col, c, i, j, diff, sgx, irow, sum, min, max, limit; + int dscr[2][2], dstb[4], (*smrow[7])[3], total[4], ipix[3]; + int work[3][3], smlast, smred, smred_p=0, dev[3]; + int satlev[3], keep[4], active[4]; + unsigned dim[3], *badpix; + double dsum=0, trsum[3]; + char str[128]; + const char* cp; + + +#ifdef DCRAW_VERBOSE + if (verbose) + fprintf(stderr,_("Foveon interpolation...\n")); +#endif +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,0,9); +#endif + + foveon_fixed (dscr, 4, "DarkShieldColRange"); + foveon_fixed (ppm[0][0], 27, "PostPolyMatrix"); + foveon_fixed (satlev, 3, "SaturationLevel"); + foveon_fixed (keep, 4, "KeepImageArea"); + foveon_fixed (active, 4, "ActiveImageArea"); + foveon_fixed (chroma_dq, 3, "ChromaDQ"); + foveon_fixed (color_dq, 3, + foveon_camf_param ("IncludeBlocks", "ColorDQ") ? + "ColorDQ" : "ColorDQCamRGB"); + if (foveon_camf_param ("IncludeBlocks", "ColumnFilter")) + foveon_fixed (&cfilt, 1, "ColumnFilter"); + + memset (ddft, 0, sizeof ddft); + if (!foveon_camf_param ("IncludeBlocks", "DarkDrift") + || !foveon_fixed (ddft[1][0], 12, "DarkDrift")) + for (i=0; i < 2; i++) { + foveon_fixed (dstb, 4, i ? "DarkShieldBottom":"DarkShieldTop"); + for (row = dstb[1]; row <= dstb[3]; row++) + for (col = dstb[0]; col <= dstb[2]; col++) + FORC3 ddft[i+1][c][1] += (short) image[row*width+col][c]; + FORC3 ddft[i+1][c][1] /= (dstb[3]-dstb[1]+1) * (dstb[2]-dstb[0]+1); + } + + if (!(cp = foveon_camf_param ("WhiteBalanceIlluminants", model2))) + { +#ifdef DCRAW_VERBOSE + fprintf (stderr,_("%s: Invalid white balance \"%s\"\n"), ifname, model2); +#endif +#ifdef LIBRAW_LIBRARY_BUILD + imgdata.process_warnings |= LIBRAW_WARN_FOVEON_INVALIDWB; +#endif + return; + } + foveon_fixed (cam_xyz, 9, cp); + foveon_fixed (correct, 9, + foveon_camf_param ("WhiteBalanceCorrections", model2)); + memset (last, 0, sizeof last); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + FORC3 last[i][j] += correct[i][c] * cam_xyz[c][j]; + + #define LAST(x,y) last[(i+x)%3][(c+y)%3] + for (i=0; i < 3; i++) + FORC3 diag[c][i] = LAST(1,1)*LAST(2,2) - LAST(1,2)*LAST(2,1); + #undef LAST + FORC3 div[c] = diag[c][0]*0.3127 + diag[c][1]*0.329 + diag[c][2]*0.3583; + sprintf (str, "%sRGBNeutral", model2); + if (foveon_camf_param ("IncludeBlocks", str)) + foveon_fixed (div, 3, str); + num = 0; + FORC3 if (num < div[c]) num = div[c]; + FORC3 div[c] /= num; + + memset (trans, 0, sizeof trans); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + FORC3 trans[i][j] += rgb_cam[i][c] * last[c][j] * div[j]; + FORC3 trsum[c] = trans[c][0] + trans[c][1] + trans[c][2]; + dsum = (6*trsum[0] + 11*trsum[1] + 3*trsum[2]) / 20; + for (i=0; i < 3; i++) + FORC3 last[i][c] = trans[i][c] * dsum / trsum[i]; + memset (trans, 0, sizeof trans); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + FORC3 trans[i][j] += (i==c ? 32 : -1) * last[c][j] / 30; + + foveon_make_curves (curve, color_dq, div, cfilt); + FORC3 chroma_dq[c] /= 3; + foveon_make_curves (curve+3, chroma_dq, div, cfilt); + FORC3 dsum += chroma_dq[c] / div[c]; + curve[6] = foveon_make_curve (dsum, dsum, cfilt); + curve[7] = foveon_make_curve (dsum*2, dsum*2, cfilt); + + sgain = (float (*)[3]) foveon_camf_matrix (dim, "SpatialGain"); + if (!sgain) return; + sgrow = (float (*)[3]) calloc (dim[1], sizeof *sgrow); + sgx = (width + dim[1]-2) / (dim[1]-1); + + black = (float (*)[3]) calloc (height, sizeof *black); +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,1,9); +#endif + for (row=0; row < height; row++) { + for (i=0; i < 6; i++) + ddft[0][0][i] = ddft[1][0][i] + + row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]); + FORC3 black[row][c] = + ( foveon_avg (image[row*width]+c, dscr[0], cfilt) + + foveon_avg (image[row*width]+c, dscr[1], cfilt) * 3 + - ddft[0][c][0] ) / 4 - ddft[0][c][1]; + } + memcpy (black, black+8, sizeof *black*8); + memcpy (black+height-11, black+height-22, 11*sizeof *black); + memcpy (last, black, sizeof last); + + for (row=1; row < height-1; row++) { + FORC3 if (last[1][c] > last[0][c]) { + if (last[1][c] > last[2][c]) + black[row][c] = (last[0][c] > last[2][c]) ? last[0][c]:last[2][c]; + } else + if (last[1][c] < last[2][c]) + black[row][c] = (last[0][c] < last[2][c]) ? last[0][c]:last[2][c]; + memmove (last, last+1, 2*sizeof last[0]); + memcpy (last[2], black[row+1], sizeof last[2]); + } + FORC3 black[row][c] = (last[0][c] + last[1][c])/2; + FORC3 black[0][c] = (black[1][c] + black[3][c])/2; + + val = 1 - exp(-1/24.0); + memcpy (fsum, black, sizeof fsum); + for (row=1; row < height; row++) + FORC3 fsum[c] += black[row][c] = + (black[row][c] - black[row-1][c])*val + black[row-1][c]; + memcpy (last[0], black[height-1], sizeof last[0]); + FORC3 fsum[c] /= height; + for (row = height; row--; ) + FORC3 last[0][c] = black[row][c] = + (black[row][c] - fsum[c] - last[0][c])*val + last[0][c]; + + memset (total, 0, sizeof total); + for (row=2; row < height; row+=4) + for (col=2; col < width; col+=4) { + FORC3 total[c] += (short) image[row*width+col][c]; + total[3]++; + } + for (row=0; row < height; row++) + FORC3 black[row][c] += fsum[c]/2 + total[c]/(total[3]*100.0); + +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,2,9); +#endif + for (row=0; row < height; row++) { + for (i=0; i < 6; i++) + ddft[0][0][i] = ddft[1][0][i] + + row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]); + pix = image[row*width]; + memcpy (prev, pix, sizeof prev); + frow = row / (height-1.0) * (dim[2]-1); + if ((irow = frow) == dim[2]-1) irow--; + frow -= irow; + for (i=0; i < dim[1]; i++) + FORC3 sgrow[i][c] = sgain[ irow *dim[1]+i][c] * (1-frow) + + sgain[(irow+1)*dim[1]+i][c] * frow; + for (col=0; col < width; col++) { + FORC3 { + diff = pix[c] - prev[c]; + prev[c] = pix[c]; + ipix[c] = pix[c] + floor ((diff + (diff*diff >> 14)) * cfilt + - ddft[0][c][1] - ddft[0][c][0] * ((float) col/width - 0.5) + - black[row][c] ); + } + FORC3 { + work[0][c] = ipix[c] * ipix[c] >> 14; + work[2][c] = ipix[c] * work[0][c] >> 14; + work[1][2-c] = ipix[(c+1) % 3] * ipix[(c+2) % 3] >> 14; + } + FORC3 { + for (val=i=0; i < 3; i++) + for ( j=0; j < 3; j++) + val += ppm[c][i][j] * work[i][j]; + ipix[c] = floor ((ipix[c] + floor(val)) * + ( sgrow[col/sgx ][c] * (sgx - col%sgx) + + sgrow[col/sgx+1][c] * (col%sgx) ) / sgx / div[c]); + if (ipix[c] > 32000) ipix[c] = 32000; + pix[c] = ipix[c]; + } + pix += 4; + } + } + free (black); + free (sgrow); + free (sgain); + +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,3,9); +#endif + if ((badpix = (unsigned int *) foveon_camf_matrix (dim, "BadPixels"))) { + for (i=0; i < dim[0]; i++) { + col = (badpix[i] >> 8 & 0xfff) - keep[0]; + row = (badpix[i] >> 20 ) - keep[1]; + if ((unsigned)(row-1) > height-3 || (unsigned)(col-1) > width-3) + continue; + memset (fsum, 0, sizeof fsum); + for (sum=j=0; j < 8; j++) + if (badpix[i] & (1 << j)) { + FORC3 fsum[c] += (short) + image[(row+hood[j*2])*width+col+hood[j*2+1]][c]; + sum++; + } + if (sum) FORC3 image[row*width+col][c] = fsum[c]/sum; + } + free (badpix); + } + + /* Array for 5x5 Gaussian averaging of red values */ + smrow[6] = (int (*)[3]) calloc (width*5, sizeof **smrow); + merror (smrow[6], "foveon_interpolate()"); + for (i=0; i < 5; i++) + smrow[i] = smrow[6] + i*width; + + /* Sharpen the reds against these Gaussian averages */ + for (smlast=-1, row=2; row < height-2; row++) { + while (smlast < row+2) { + for (i=0; i < 6; i++) + smrow[(i+5) % 6] = smrow[i]; + pix = image[++smlast*width+2]; + for (col=2; col < width-2; col++) { + smrow[4][col][0] = + (pix[0]*6 + (pix[-4]+pix[4])*4 + pix[-8]+pix[8] + 8) >> 4; + pix += 4; + } + } + pix = image[row*width+2]; + for (col=2; col < width-2; col++) { + smred = ( 6 * smrow[2][col][0] + + 4 * (smrow[1][col][0] + smrow[3][col][0]) + + smrow[0][col][0] + smrow[4][col][0] + 8 ) >> 4; + if (col == 2) + smred_p = smred; + i = pix[0] + ((pix[0] - ((smred*7 + smred_p) >> 3)) >> 3); + if (i > 32000) i = 32000; + pix[0] = i; + smred_p = smred; + pix += 4; + } + } + +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,4,9); +#endif + /* Adjust the brighter pixels for better linearity */ + min = 0xffff; + FORC3 { + i = satlev[c] / div[c]; + if (min > i) min = i; + } + limit = min * 9 >> 4; + for (pix=image[0]; pix < image[height*width]; pix+=4) { + if (pix[0] <= limit || pix[1] <= limit || pix[2] <= limit) + continue; + min = max = pix[0]; + for (c=1; c < 3; c++) { + if (min > pix[c]) min = pix[c]; + if (max < pix[c]) max = pix[c]; + } + if (min >= limit*2) { + pix[0] = pix[1] = pix[2] = max; + } else { + i = 0x4000 - ((min - limit) << 14) / limit; + i = 0x4000 - (i*i >> 14); + i = i*i >> 14; + FORC3 pix[c] += (max - pix[c]) * i >> 14; + } + } +/* + Because photons that miss one detector often hit another, + the sum R+G+B is much less noisy than the individual colors. + So smooth the hues without smoothing the total. + */ +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,5,9); +#endif + for (smlast=-1, row=2; row < height-2; row++) { + while (smlast < row+2) { + for (i=0; i < 6; i++) + smrow[(i+5) % 6] = smrow[i]; + pix = image[++smlast*width+2]; + for (col=2; col < width-2; col++) { + FORC3 smrow[4][col][c] = (pix[c-4]+2*pix[c]+pix[c+4]+2) >> 2; + pix += 4; + } + } + pix = image[row*width+2]; + for (col=2; col < width-2; col++) { + FORC3 dev[c] = -foveon_apply_curve (curve[7], pix[c] - + ((smrow[1][col][c] + 2*smrow[2][col][c] + smrow[3][col][c]) >> 2)); + sum = (dev[0] + dev[1] + dev[2]) >> 3; + FORC3 pix[c] += dev[c] - sum; + pix += 4; + } + } + for (smlast=-1, row=2; row < height-2; row++) { + while (smlast < row+2) { + for (i=0; i < 6; i++) + smrow[(i+5) % 6] = smrow[i]; + pix = image[++smlast*width+2]; + for (col=2; col < width-2; col++) { + FORC3 smrow[4][col][c] = + (pix[c-8]+pix[c-4]+pix[c]+pix[c+4]+pix[c+8]+2) >> 2; + pix += 4; + } + } + pix = image[row*width+2]; + for (col=2; col < width-2; col++) { + for (total[3]=375, sum=60, c=0; c < 3; c++) { + for (total[c]=i=0; i < 5; i++) + total[c] += smrow[i][col][c]; + total[3] += total[c]; + sum += pix[c]; + } + if (sum < 0) sum = 0; + j = total[3] > 375 ? (sum << 16) / total[3] : sum * 174; + FORC3 pix[c] += foveon_apply_curve (curve[6], + ((j*total[c] + 0x8000) >> 16) - pix[c]); + pix += 4; + } + } + +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,6,9); +#endif + /* Transform the image to a different colorspace */ + for (pix=image[0]; pix < image[height*width]; pix+=4) { + FORC3 pix[c] -= foveon_apply_curve (curve[c], pix[c]); + sum = (pix[0]+pix[1]+pix[1]+pix[2]) >> 2; + FORC3 pix[c] -= foveon_apply_curve (curve[c], pix[c]-sum); + FORC3 { + for (dsum=i=0; i < 3; i++) + dsum += trans[c][i] * pix[i]; + if (dsum < 0) dsum = 0; + if (dsum > 24000) dsum = 24000; + ipix[c] = dsum + 0.5; + } + FORC3 pix[c] = ipix[c]; + } + + /* Smooth the image bottom-to-top and save at 1/4 scale */ + shrink = (short (*)[3]) calloc ((width/4) * (height/4), sizeof *shrink); + merror (shrink, "foveon_interpolate()"); + for (row = height/4; row--; ) + for (col=0; col < width/4; col++) { + ipix[0] = ipix[1] = ipix[2] = 0; + for (i=0; i < 4; i++) + for (j=0; j < 4; j++) + FORC3 ipix[c] += image[(row*4+i)*width+col*4+j][c]; + FORC3 + if (row+2 > height/4) + shrink[row*(width/4)+col][c] = ipix[c] >> 4; + else + shrink[row*(width/4)+col][c] = + (shrink[(row+1)*(width/4)+col][c]*1840 + ipix[c]*141 + 2048) >> 12; + } + /* From the 1/4-scale image, smooth right-to-left */ +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,7,9); +#endif + for (row=0; row < (height & ~3); row++) { + ipix[0] = ipix[1] = ipix[2] = 0; + if ((row & 3) == 0) + for (col = width & ~3 ; col--; ) + FORC3 smrow[0][col][c] = ipix[c] = + (shrink[(row/4)*(width/4)+col/4][c]*1485 + ipix[c]*6707 + 4096) >> 13; + + /* Then smooth left-to-right */ + ipix[0] = ipix[1] = ipix[2] = 0; + for (col=0; col < (width & ~3); col++) + FORC3 smrow[1][col][c] = ipix[c] = + (smrow[0][col][c]*1485 + ipix[c]*6707 + 4096) >> 13; + + /* Smooth top-to-bottom */ + if (row == 0) + memcpy (smrow[2], smrow[1], sizeof **smrow * width); + else + for (col=0; col < (width & ~3); col++) + FORC3 smrow[2][col][c] = + (smrow[2][col][c]*6707 + smrow[1][col][c]*1485 + 4096) >> 13; + + /* Adjust the chroma toward the smooth values */ + for (col=0; col < (width & ~3); col++) { + for (i=j=30, c=0; c < 3; c++) { + i += smrow[2][col][c]; + j += image[row*width+col][c]; + } + j = (j << 16) / i; + for (sum=c=0; c < 3; c++) { + ipix[c] = foveon_apply_curve (curve[c+3], + ((smrow[2][col][c] * j + 0x8000) >> 16) - image[row*width+col][c]); + sum += ipix[c]; + } + sum >>= 3; + FORC3 { + i = image[row*width+col][c] + ipix[c] - sum; + if (i < 0) i = 0; + image[row*width+col][c] = i; + } + } + } + free (shrink); + free (smrow[6]); + for (i=0; i < 8; i++) + free (curve[i]); +#ifdef LIBRAW_LIBRARY_BUILD + RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,8,9); +#endif + + /* Trim off the black border */ + active[1] -= keep[1]; + active[3] -= 2; + i = active[2] - active[0]; + for (row=0; row < active[3]-active[1]; row++) + memcpy (image[row*i], image[(row+active[1])*width+active[0]], + i * sizeof *image); + width = i; + height = row; +} +#undef image + +/* RESTRICTED code ends here */ +#line 7086 "dcraw/dcraw.c" +char * CLASS foveon_gets (int offset, char *str, int len) +{ + int i; + fseek (ifp, offset, SEEK_SET); + for (i=0; i < len-1; i++) + if ((str[i] = get2()) == 0) break; + str[i] = 0; + return str; +} + +void CLASS parse_foveon() +{ + int entries, img=0, off, len, tag, save, i, wide, high, pent, poff[256][2]; + char name[64], value[64]; + + order = 0x4949; /* Little-endian */ + fseek (ifp, 36, SEEK_SET); + flip = get4(); + fseek (ifp, -4, SEEK_END); + fseek (ifp, get4(), SEEK_SET); + if (get4() != 0x64434553) return; /* SECd */ + entries = (get4(),get4()); + while (entries--) { + off = get4(); + len = get4(); + tag = get4(); + save = ftell(ifp); + fseek (ifp, off, SEEK_SET); + if (get4() != (0x20434553 | (tag << 24))) return; + switch (tag) { + case 0x47414d49: /* IMAG */ + case 0x32414d49: /* IMA2 */ + fseek (ifp, 12, SEEK_CUR); + wide = get4(); + high = get4(); + if (wide > raw_width && high > raw_height) { + raw_width = wide; + raw_height = high; + data_offset = off+24; + } + fseek (ifp, off+28, SEEK_SET); + if (fgetc(ifp) == 0xff && fgetc(ifp) == 0xd8 + && thumb_length < len-28) { + thumb_offset = off+28; + thumb_length = len-28; + write_thumb = &CLASS jpeg_thumb; + } + if (++img == 2 && !thumb_length) { + thumb_offset = off+24; + thumb_width = wide; + thumb_height = high; + write_thumb = &CLASS foveon_thumb; + } + break; + case 0x464d4143: /* CAMF */ + meta_offset = off+24; + meta_length = len-28; + if (meta_length > 0x20000) + meta_length = 0x20000; + break; + case 0x504f5250: /* PROP */ + pent = (get4(),get4()); + fseek (ifp, 12, SEEK_CUR); + off += pent*8 + 24; + if ((unsigned) pent > 256) pent=256; + for (i=0; i < pent*2; i++) + poff[0][i] = off + get4()*2; + for (i=0; i < pent; i++) { + foveon_gets (poff[i][0], name, 64); + foveon_gets (poff[i][1], value, 64); + if (!strcmp (name, "ISO")) + iso_speed = atoi(value); + if (!strcmp (name, "CAMMANUF")) + strcpy (make, value); + if (!strcmp (name, "CAMMODEL")) + strcpy (model, value); + if (!strcmp (name, "WB_DESC")) + strcpy (model2, value); + if (!strcmp (name, "TIME")) + timestamp = atoi(value); + if (!strcmp (name, "EXPTIME")) + shutter = atoi(value) / 1000000.0; + if (!strcmp (name, "APERTURE")) + aperture = atof(value); + if (!strcmp (name, "FLENGTH")) + focal_len = atof(value); + } +#ifdef LOCALTIME + timestamp = mktime (gmtime (×tamp)); +#endif + } + fseek (ifp, save, SEEK_SET); + } + is_foveon = 1; +} diff --git a/src/LibRaw/internal/libraw_internal_funcs.h b/src/LibRaw/internal/libraw_internal_funcs.h new file mode 100644 index 000000000000..33d11ffbfc10 --- /dev/null +++ b/src/LibRaw/internal/libraw_internal_funcs.h @@ -0,0 +1,200 @@ +/* -*- C++ -*- + * File: libraw_internal_funcs.h + * Copyright 2008 Alex Tutubalin + * Created: Sat Mar 14, 2008 + * + * LibRaw internal data structures (not visible outside) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _LIBRAW_INTERNAL_FUNCS_H +#define _LIBRAW_INTERNAL_FUNCS_H + +#ifndef LIBRAW_LIBRARY_BUILD +#error "This file should be used only for libraw library build" +#else +// inline functions + ushort sget2 (uchar *s); + ushort get2(); + unsigned sget4 (uchar *s); + unsigned getint (int type); + float int_to_float (int i); + double getreal (int type); + void read_shorts (ushort *pixel, int count); + +// Canon P&S cameras + void canon_600_fixed_wb (int temp); + int canon_600_color (int ratio[2], int mar); + void canon_600_auto_wb(); + void canon_600_coeff(); + void canon_600_load_raw(); + int canon_s2is(); + void canon_a5_load_raw(); + void parse_ciff (int offset, int length); + void ciff_block_1030(); + +// LJPEG decoder + unsigned getbits (int nbits); + void init_decoder(); + uchar * make_decoder (const uchar *source, int level); + int ljpeg_start (struct jhead *jh, int info_only); + int ljpeg_diff (struct decode *dindex); + ushort * ljpeg_row (int jrow, struct jhead *jh); + +// Canon DSLRs + void crw_init_tables (unsigned table); + int canon_has_lowbits(); + void canon_compressed_load_raw(); + void lossless_jpeg_load_raw(); + void canon_sraw_load_raw(); + void canon_black(double *); +// Adobe DNG + void adobe_copy_pixel (int row, int col, ushort **rp); + void adobe_dng_load_raw_lj(); + void adobe_dng_load_raw_nc(); + +// Pentax + void pentax_k10_load_raw(); + +// Nikon (and Minolta Z2) + void nikon_compressed_load_raw(); + void nikon_load_raw(); + int nikon_is_compressed(); + int nikon_e995(); + int nikon_e2100(); + void nikon_3700(); + int minolta_z2(); + void nikon_e900_load_raw(); + void nikon_e2100_load_raw(); + +// Fuji + void fuji_load_raw(); + void parse_fuji (int offset); + + + + +// Rollei + void rollei_load_raw(); + void parse_rollei(); + +// MF backs + int bayer (unsigned row, unsigned col); + void phase_one_flat_field (int is_float, int nc); + void phase_one_correct(); + void phase_one_load_raw(); + unsigned ph1_bits (int nbits); + void phase_one_load_raw_c(); + void hasselblad_load_raw(); + void leaf_hdr_load_raw(); + void sinar_4shot_load_raw(); + void imacon_full_load_raw(); + void packed_12_load_raw(); + void unpacked_load_raw(); + void parse_sinar_ia(); + void parse_phase_one (int base); + +// Misc P&S cameras + void nokia_load_raw(); + unsigned pana_bits (int nbits); + void panasonic_load_raw(); + void olympus_e300_load_raw(); + void olympus_e410_load_raw(); + void olympus_cseries_load_raw(); + void minolta_rd175_load_raw(); + void casio_qv5700_load_raw(); + void quicktake_100_load_raw(); + const int* make_decoder_int (const int *source, int level); + int radc_token (int tree); + void kodak_radc_load_raw(); + void kodak_jpeg_load_raw(); + void kodak_dc120_load_raw(); + void eight_bit_load_raw(); + void smal_decode_segment (unsigned seg[2][2], int holes); + void smal_v6_load_raw(); + int median4 (int *p); + void fill_holes (int holes); + void smal_v9_load_raw(); + void parse_riff(); + void parse_cine(); + void parse_smal (int offset, int fsize); + int parse_jpeg (int offset); + +// Kodak + void kodak_262_load_raw(); + int kodak_65000_decode (short *out, int bsize); + void kodak_65000_load_raw(); + void kodak_rgb_load_raw(); + void kodak_yrgb_load_raw(); + +// It's a Sony (and K&M) + void sony_decrypt (unsigned *data, int len, int start, int key); + void sony_load_raw(); + void sony_arw_load_raw(); + void sony_arw2_load_raw(); + void parse_minolta (int base); + +#ifndef NO_FOVEON +// Foveon/Sigma + void foveon_load_camf(); + void foveon_load_raw(); + const char* foveon_camf_param (const char *block, const char *param); + void * foveon_camf_matrix (unsigned dim[3], const char *name); + int foveon_fixed (void *ptr, int size, const char *name); + float foveon_avg (short *pix, int range[2], float cfilt); + short * foveon_make_curve (double max, double mul, double filt); + void foveon_make_curves(short **curvep, float dq[3], float div[3], float filt); + int foveon_apply_curve (short *curve, int i); + void foveon_interpolate(); + char * foveon_gets (int offset, char *str, int len); + void parse_foveon(); +#endif + + +// CAM/RGB + void pseudoinverse (double (*in)[3], double (*out)[3], int size); + void cam_xyz_coeff (double cam_xyz[4][3]); + void adobe_coeff (const char *, const char *); + void simple_coeff (int index); + + +// Tiff/Exif parsers + void tiff_get (unsigned base,unsigned *tag, unsigned *type, unsigned *len, unsigned *save); + void parse_thumb_note (int base, unsigned toff, unsigned tlen); + void parse_makernote (int base, int uptag); + void parse_exif (int base); + void linear_table (unsigned len); + void parse_kodak_ifd (int base); + int parse_tiff_ifd (int base); + void parse_tiff (int base); + void parse_gps (int base); + void romm_coeff (float romm_cam[3][3]); + void parse_mos (int offset); + void get_timestamp (int reversed); + +// External JPEGs, what cameras uses it ? + void parse_external_jpeg(); + +// The identify + short guess_byte_order (int words); + +// Tiff writer + void tiff_set (ushort *ntag, ushort tag, ushort type, int count, int val); + void tiff_head (struct tiff_hdr *th, int full); +#endif + +#endif diff --git a/src/LibRaw/internal/var_defines.h b/src/LibRaw/internal/var_defines.h new file mode 100644 index 000000000000..8adc954956d7 --- /dev/null +++ b/src/LibRaw/internal/var_defines.h @@ -0,0 +1,180 @@ +/* -*- C++ -*- + * File: var_defines.h + * Copyright 2008-2009 Alex Tutubalin + * Created: Sat Mar 8, 2008 + * + * LibRaw redefinitions of dcraw internal variables + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef VAR_DEFINES_H +#define VAR_DEFINES_H + +// imgdata.idata +#define make (imgdata.idata.make) +#define model (imgdata.idata.model) +#define is_raw (imgdata.idata.raw_count) +#define dng_version (imgdata.idata.dng_version) +#define is_foveon (imgdata.idata.is_foveon) +#define colors (imgdata.idata.colors) +#define cdesc (imgdata.idata.cdesc) +#define filters (imgdata.idata.filters) + +//imgdata image +#define image (imgdata.image) + +// imgdata.sizes +#define raw_height (imgdata.sizes.raw_height) +#define raw_width (imgdata.sizes.raw_width) +#define height (imgdata.sizes.height) +#define width (imgdata.sizes.width) +#define top_margin (imgdata.sizes.top_margin) +#define left_margin (imgdata.sizes.left_margin) +#define bottom_margin (imgdata.sizes.bottom_margin) +#define right_margin (imgdata.sizes.right_margin) +#define iheight (imgdata.sizes.iheight) +#define iwidth (imgdata.sizes.iwidth) +#define pixel_aspect (imgdata.sizes.pixel_aspect) +#define flip (imgdata.sizes.flip) + +//imgdata.color +#define white (imgdata.color.white) +#define cam_mul (imgdata.color.cam_mul) +#define pre_mul (imgdata.color.pre_mul) +#define cmatrix (imgdata.color.cmatrix) +#define rgb_cam (imgdata.color.rgb_cam) +#ifndef SRC_USES_CURVE +#define curve (imgdata.color.curve) +#endif +#ifndef SRC_USES_BLACK +#define black (imgdata.color.black) +#endif +#define maximum (imgdata.color.maximum) +#define profile_length (imgdata.color.profile_length) +#define color_flags (imgdata.color.color_flags) +#define ph1 (imgdata.color.phase_one_data) +#define flash_used (imgdata.color.flash_used) +#define canon_ev (imgdata.color.canon_ev) +#define model2 (imgdata.color.model2) + +//imgdata.thumbnail + +#define thumb_width (imgdata.thumbnail.twidth) +#define thumb_height (imgdata.thumbnail.theight) +#define thumb_length (imgdata.thumbnail.tlength) + + +//imgdata.others +#define iso_speed (imgdata.other.iso_speed) +#define shutter (imgdata.other.shutter) +#define aperture (imgdata.other.aperture) +#define focal_len (imgdata.other.focal_len) +#define timestamp (imgdata.other.timestamp) +#define shot_order (imgdata.other.shot_order) +#define gpsdata (imgdata.other.gpsdata) +#define desc (imgdata.other.desc) +#define artist (imgdata.other.artist) + +//imgdata.output +#define greybox (imgdata.params.greybox) +#define aber (imgdata.params.aber) +#define user_mul (imgdata.params.user_mul) +#define shot_select (imgdata.params.shot_select) +#define bright (imgdata.params.bright) +#define threshold (imgdata.params.threshold) +#define half_size (imgdata.params.half_size) +#define four_color_rgb (imgdata.params.four_color_rgb) +#define document_mode (imgdata.params.document_mode) +#define highlight (imgdata.params.highlight) +//#define verbose (imgdata.params.verbose) +#define use_auto_wb (imgdata.params.use_auto_wb) +#define use_camera_wb (imgdata.params.use_camera_wb) +#define use_camera_matrix (imgdata.params.use_camera_matrix) +#define output_color (imgdata.params.output_color) +#define output_bps (imgdata.params.output_bps) +#define gamma_16bit (imgdata.params.gamma_16bit) +#define output_tiff (imgdata.params.output_tiff) +#define med_passes (imgdata.params.med_passes) +#define no_auto_bright (imgdata.params.no_auto_bright) +#define use_fuji_rotate (imgdata.params.use_fuji_rotate) +#define filtering_mode (imgdata.params.filtering_mode) + +//rgb_constants +#define xyz_rgb (rgb_constants.xyz_rgb) +#define d65_white (rgb_constants.d65_white) + +//libraw_internal_data.internal_data +#define meta_data (libraw_internal_data.internal_data.meta_data) +#define ifp libraw_internal_data.internal_data.input +#define ifname ((char*)libraw_internal_data.internal_data.input->fname()) +#define profile_offset (libraw_internal_data.internal_data.profile_offset) +#define thumb_offset (libraw_internal_data.internal_data.toffset) + +//libraw_internal_data.internal_output_params +#define mix_green (libraw_internal_data.internal_output_params.mix_green) +#define raw_color (libraw_internal_data.internal_output_params.raw_color) +#define use_gamma (libraw_internal_data.internal_output_params.use_gamma) +#define zero_is_bad (libraw_internal_data.internal_output_params.zero_is_bad) +#ifndef SRC_USES_SHRINK +#define shrink (libraw_internal_data.internal_output_params.shrink) +#endif +#define fuji_width (libraw_internal_data.internal_output_params.fuji_width) + + +//libraw_internal_data.output_data +#define histogram (libraw_internal_data.output_data.histogram) +#define oprof (libraw_internal_data.output_data.oprof) + +//libraw_internal_data.identify_data +#define exif_cfa (libraw_internal_data.identify_data.olympus_exif_cfa) +#define unique_id (libraw_internal_data.identify_data.unique_id) +#define tiff_nifds (libraw_internal_data.identify_data.tiff_nifds) +#define tiff_flip (libraw_internal_data.identify_data.tiff_flip) + +//libraw_internal_data.unpacker_data +#define order (libraw_internal_data.unpacker_data.order) +#define data_error (libraw_internal_data.unpacker_data.data_error) +#define cr2_slice (libraw_internal_data.unpacker_data.cr2_slice) +#define sraw_mul (libraw_internal_data.unpacker_data.sraw_mul) +#define kodak_cbpp (libraw_internal_data.unpacker_data.kodak_cbpp) +#define strip_offset (libraw_internal_data.unpacker_data.strip_offset) +#define data_offset (libraw_internal_data.unpacker_data.data_offset) +#define meta_offset (libraw_internal_data.unpacker_data.meta_offset) +#define meta_length (libraw_internal_data.unpacker_data.meta_length) +#define thumb_misc (libraw_internal_data.unpacker_data.thumb_misc) +#define fuji_layout (libraw_internal_data.unpacker_data.fuji_layout) +#define tiff_samples (libraw_internal_data.unpacker_data.tiff_samples) +#define tiff_bps (libraw_internal_data.unpacker_data.tiff_bps) +#define tiff_compress (libraw_internal_data.unpacker_data.tiff_compress) +#define zero_after_ff (libraw_internal_data.unpacker_data.zero_after_ff) +#define tile_width (libraw_internal_data.unpacker_data.tile_width) +#define tile_length (libraw_internal_data.unpacker_data.tile_length) +#define load_flags (libraw_internal_data.unpacker_data.load_flags) + +#ifdef LIBRAW_IO_REDEFINED +#define fread(ptr,size,n,stream) stream->read(ptr,size,n) +#define fseek(stream,o,w) stream->seek(o,w) +#define fseeko(stream,o,w) stream->seek(o,w) +#define ftell(stream) stream->tell() +#define ftello(stream) stream->tell() +#define getc(stream) stream->get_char() +#define fgetc(stream) stream->get_char() +#define fgets(str,n,stream) stream->gets(str,n) +#define fscanf(stream,fmt,ptr) stream->scanf_one(fmt,ptr) +#endif + +#endif diff --git a/src/LibRaw/libraw/libraw.h b/src/LibRaw/libraw/libraw.h new file mode 100644 index 000000000000..6bd12d6a27b0 --- /dev/null +++ b/src/LibRaw/libraw/libraw.h @@ -0,0 +1,229 @@ +/* -*- C++ -*- + * File: libraw.h + * Copyright 2008-2009 Alex Tutubalin + * Created: Sat Mar 8, 2008 + * + * LibRaw C++ interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _LIBRAW_CLASS_H +#define _LIBRAW_CLASS_H + +#include +#include +#include +#include + + +#include "libraw_datastream.h" +#include "libraw_types.h" +#include "libraw_const.h" +#include "libraw_internal.h" +#include "libraw_alloc.h" + +//#define DCRAW_VERBOSE + +#ifdef __cplusplus +extern "C" +{ +#endif +DllDef const char *libraw_strerror(int errorcode); +DllDef const char *libraw_strprogress(enum LibRaw_progress); + // LibRaw C API +DllDef libraw_data_t *libraw_init(unsigned int flags); +DllDef int libraw_open_file(libraw_data_t*, const char *); +DllDef int libraw_open_buffer(libraw_data_t*, void * buffer, size_t size); +DllDef int libraw_unpack(libraw_data_t*); +DllDef int libraw_unpack_thumb(libraw_data_t*); +DllDef void libraw_recycle(libraw_data_t*); +DllDef void libraw_close(libraw_data_t*); + // version helpers +DllDef const char* libraw_version(); +DllDef int libraw_versionNumber(); + // Camera list +DllDef const char** libraw_cameraList(); +DllDef int libraw_cameraCount(); + +DllDef void libraw_set_memerror_handler(libraw_data_t*, memory_callback cb, void *datap); +DllDef void libraw_set_dataerror_handler(libraw_data_t*,data_callback func,void *datap); +DllDef void libraw_set_progress_handler(libraw_data_t*,progress_callback cb,void *datap); +DllDef int libraw_add_masked_borders_to_bitmap(libraw_data_t* lr); +DllDef const char * libraw_unpack_function_name(libraw_data_t* lr); +DllDef int libraw_rotate_fuji_raw(libraw_data_t* lr); + + // DCRAW compatibility +DllDef int libraw_adjust_sizes_info_only(libraw_data_t*); +DllDef int libraw_dcraw_document_mode_processing(libraw_data_t*); +DllDef int libraw_dcraw_ppm_tiff_writer(libraw_data_t* lr,const char *filename); +DllDef int libraw_dcraw_thumb_writer(libraw_data_t* lr,const char *fname); +DllDef int libraw_dcraw_process(libraw_data_t* lr); +DllDef libraw_processed_image_t* dcraw_make_mem_image(libraw_data_t* lr, int *errc); +DllDef libraw_processed_image_t* dcraw_make_mem_thumb(libraw_data_t* lr, int *errc); + +#ifdef __cplusplus +} +#endif + + +#ifdef __cplusplus + +class DllDef LibRaw +{ + public: + libraw_data_t imgdata; + int verbose; + + LibRaw(unsigned int flags = LIBRAW_OPTIONS_NONE); + + libraw_output_params_t* output_params_ptr() { return &imgdata.params;} + int open_file(const char *fname); + int open_buffer(void *buffer, size_t size); + int open_datastream(LibRaw_abstract_datastream *); + int unpack(void); + int unpack_thumb(void); + + int adjust_sizes_info_only(void); + void set_memerror_handler( memory_callback cb,void *data) {callbacks.memcb_data = data; callbacks.mem_cb = cb; } + void set_dataerror_handler(data_callback func, void *data) { callbacks.datacb_data = data; callbacks.data_cb = func;} + void set_progress_handler(progress_callback pcb, void *data) { callbacks.progresscb_data = data; callbacks.progress_cb = pcb;} + + // helpers + static const char* version() { return LIBRAW_VERSION_STR;} + static int versionNumber() { return LIBRAW_VERSION; } + static const char** cameraList(); + static int cameraCount(); + static const char* strprogress(enum LibRaw_progress); + static const char* strerror(int p) { return libraw_strerror(p);} + // dcraw emulation + int dcraw_document_mode_processing(); + int dcraw_ppm_tiff_writer(const char *filename); + int dcraw_thumb_writer(const char *fname); + int dcraw_process(void); + // memory writers + libraw_processed_image_t* dcraw_make_mem_image(int *errcode=NULL); + libraw_processed_image_t* dcraw_make_mem_thumb(int *errcode=NULL); + + // free all internal data structures + void recycle(); + ~LibRaw(void) { recycle(); delete tls; } + + int FC(int row,int col) { return (imgdata.idata.filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3);} + int fc (int row, int col); + int add_masked_borders_to_bitmap(); + + const char *unpack_function_name(); + int rotate_fuji_raw(); + + private: + void* malloc(size_t t); + void* calloc(size_t n,size_t t); + void free(void *p); + void merror (void *ptr, const char *where); + void derror(); + +// data + + LibRaw_TLS *tls; + libraw_internal_data_t libraw_internal_data; + decode first_decode[2048], *second_decode, *free_decode; + tiff_ifd_t tiff_ifd[10]; + libraw_memmgr memmgr; + libraw_callbacks_t callbacks; + + LibRaw_constants rgb_constants; + void (LibRaw:: *write_thumb)(FILE *), + (LibRaw:: *write_fun)(FILE *); + void (LibRaw:: *load_raw)(), + (LibRaw:: *thumb_load_raw)(); + + void kodak_thumb_loader(); + void write_thumb_ppm_tiff(FILE *); // kodak + void foveon_thumb_loader (void); //Sigma + + + // moved from implementation level to private: visibility + void init_masked_ptrs(); + ushort *get_masked_pointer(int row, int col); + + int own_filtering_supported(){ return 0;} + void identify(); + void write_ppm_tiff (FILE *ofp); + void convert_to_rgb(); + void kodak_ycbcr_load_raw(); + void remove_zeroes(); +#ifndef NO_LCMS + void apply_profile(char*,char*); +#endif +// Iterpolators + void pre_interpolate(); + void border_interpolate (int border); + void lin_interpolate(); + void vng_interpolate(); + void ppg_interpolate(); + void ahd_interpolate(); + +// Image filters + void bad_pixels(char*); + void subtract(char*); + void hat_transform (float *temp, float *base, int st, int size, int sc); + void wavelet_denoise(); + void scale_colors(); + void median_filter (); + void blend_highlights(); + void recover_highlights(); + + void fuji_rotate(); + void stretch(); + +// Thmbnail functions + void foveon_thumb (FILE *tfp); + void jpeg_thumb_writer (FILE *tfp,char *thumb,int thumb_length); + void jpeg_thumb (FILE *tfp); + void ppm_thumb (FILE *tfp); + void layer_thumb (FILE *tfp); + void rollei_thumb (FILE *tfp); + void kodak_thumb_load_raw(); + + // utility for cut'n'pasted code + void foveon_decoder (unsigned size, unsigned code); + unsigned get4(); + + int flip_index (int row, int col); + void gamma_lut(ushort lut[0x10000]); + + +// == internal functions + +#ifdef LIBRAW_LIBRARY_BUILD +#include "internal/libraw_internal_funcs.h" +#endif + +}; + +#ifdef LIBRAW_LIBRARY_BUILD +#define RUN_CALLBACK(stage,iter,expect) if(callbacks.progress_cb) { \ + int rr = (*callbacks.progress_cb)(callbacks.progresscb_data,stage,iter,expect); \ + if(rr!=0) throw LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK; \ + } +#endif + + +#endif // __cplusplus + + +#endif // _LIBRAW_CLASS_H diff --git a/src/LibRaw/libraw/libraw_alloc.h b/src/LibRaw/libraw/libraw_alloc.h new file mode 100644 index 000000000000..84970d2f601a --- /dev/null +++ b/src/LibRaw/libraw/libraw_alloc.h @@ -0,0 +1,98 @@ +/* -*- C++ -*- + * File: libraw_alloc.h + * Copyright 2008-2009 Alex Tutubalin + * Created: Sat Mar 22, 2008 + * + * LibRaw C++ interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __LIBRAW_ALLOC_H +#define __LIBRAW_ALLOC_H + +#include +#include +#ifdef WIN32 +#define bzero(p,sz) memset(p,0,sz) +#endif + +#ifdef __cplusplus + +#define MSIZE 32 + +class libraw_memmgr +{ + public: + libraw_memmgr() + { + bzero(mems,sizeof(mems)); + calloc_cnt=0; + } + void *malloc(size_t sz) + { + void *ptr = ::malloc(sz); + mem_ptr(ptr); + return ptr; + } + void *calloc(size_t n, size_t sz) + { + void *ptr = ::calloc(n,sz); + mem_ptr(ptr); + return ptr; + } + void free(void *ptr) + { + ::free(ptr); + forget_ptr(ptr); + } + void cleanup(void) + { + for(int i = 0; i< MSIZE; i++) + if(mems[i]) + { +// fprintf(stderr,"Found lost fragment at 0x%x\n",mems[i]); + free(mems[i]); + mems[i] = NULL; + } + } + + private: + void *mems[MSIZE]; + int calloc_cnt; + void mem_ptr(void *ptr) + { + if(ptr) + for(int i=0;i < MSIZE; i++) + if(!mems[i]) + { + mems[i] = ptr; + break; + } + } + void forget_ptr(void *ptr) + { + if(ptr) + for(int i=0;i < MSIZE; i++) + if(mems[i] == ptr) + mems[i] = NULL; + } + +}; + +#endif //C++ + +#endif diff --git a/src/LibRaw/libraw/libraw_const.h b/src/LibRaw/libraw/libraw_const.h new file mode 100644 index 000000000000..1c4b32c72311 --- /dev/null +++ b/src/LibRaw/libraw/libraw_const.h @@ -0,0 +1,160 @@ +/* -*- C++ -*- + * File: libraw_const.h + * Copyright 2008-2009 Alex Tutubalin + * Created: Sat Mar 8 , 2008 + * + * LibRaw error codes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _LIBRAW_ERRORS_H +#define _LIBRAW_ERRORS_H + +enum LibRaw_constructor_flags +{ + LIBRAW_OPTIONS_NONE =0, + LIBRAW_OPIONS_NO_MEMERR_CALLBACK=1, + LIBRAW_OPIONS_NO_DATAERR_CALLBACK=1<<1 +}; + +enum LibRaw_warnings +{ + LIBRAW_WARN_NONE =0, + LIBRAW_WARN_FOVEON_NOMATRIX =1, + LIBRAW_WARN_FOVEON_INVALIDWB =1<<1, + LIBRAW_WARN_BAD_CAMERA_WB =1<<2, + LIBRAW_WARN_NO_METADATA =1<<3, + LIBRAW_WARN_NO_JPEGLIB = 1<<4, + LIBRAW_WARN_NO_EMBEDDED_PROFILE = 1<<5, + LIBRAW_WARN_NO_INPUT_PROFILE = 1<<6, + LIBRAW_WARN_BAD_OUTPUT_PROFILE= 1<<7, + LIBRAW_WARN_NO_BADPIXELMAP=1<<8, + LIBRAW_WARN_BAD_DARKFRAME_FILE=1<<9, + LIBRAW_WARN_BAD_DARKFRAME_DIM=1<<10 +}; + +enum LibRaw_exceptions +{ + LIBRAW_EXCEPTION_NONE =0, + LIBRAW_EXCEPTION_ALLOC =1, + LIBRAW_EXCEPTION_DECODE_RAW =2, + LIBRAW_EXCEPTION_DECODE_JPEG=3, + LIBRAW_EXCEPTION_IO_EOF =4, + LIBRAW_EXCEPTION_IO_CORRUPT =5, + LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK=6 +}; + + +enum LibRaw_colorstate +{ + LIBRAW_COLORSTATE_UNKNOWN =0, + LIBRAW_COLORSTATE_INIT =1, + LIBRAW_COLORSTATE_CONST =2, + LIBRAW_COLORSTATE_LOADED =3, + LIBRAW_COLORSTATE_CALCULATED=4, + LIBRAW_COLORSTATE_RESERVED1 =5, + LIBRAW_COLORSTATE_RESERVED2 =6, + LIBRAW_COLORSTATE_RESERVED3 =7 +}; + +enum LibRaw_filtering +{ + LIBRAW_FILTERING_DEFAULT =0, + LIBRAW_FILTERING_NOZEROES =1, // no remove zeroes + LIBRAW_FILTERING_NOBLACKS =2, // no black subtraction + LIBRAW_FILTERING_NORAWCURVE =4, // no raw data postprocessing (e.g. PhaseOne corrections etc) + LIBRAW_FILTERING_NONE =7, // (_NOZEROES | _NOBLACKS | _NORAWCURVE) + LIBRAW_FILTERING_LIBRAWOWN =(8 | LIBRAW_FILTERING_NONE), // NONE + 8 + LIBRAW_FILTERING_AUTOMATIC_BIT =16, // - restore automatic mode after processing + LIBRAW_FILTERING_AUTOMATIC = (LIBRAW_FILTERING_LIBRAWOWN | LIBRAW_FILTERING_AUTOMATIC_BIT) +}; + + +enum LibRaw_progress +{ + LIBRAW_PROGRESS_START = 0, + LIBRAW_PROGRESS_OPEN = 1, + LIBRAW_PROGRESS_IDENTIFY = 1<<1, + LIBRAW_PROGRESS_SIZE_ADJUST = 1<<2, + LIBRAW_PROGRESS_LOAD_RAW = 1<<3, + LIBRAW_PROGRESS_REMOVE_ZEROES = 1<<4, + LIBRAW_PROGRESS_BAD_PIXELS = 1<<5, + LIBRAW_PROGRESS_DARK_FRAME = 1<<6, + LIBRAW_PROGRESS_FOVEON_INTERPOLATE = 1<<7, + LIBRAW_PROGRESS_SCALE_COLORS = 1<<8, + LIBRAW_PROGRESS_PRE_INTERPOLATE = 1<<9, + LIBRAW_PROGRESS_INTERPOLATE = 1<<10, + LIBRAW_PROGRESS_MIX_GREEN = 1<<11, + LIBRAW_PROGRESS_MEDIAN_FILTER = 1<<12, + LIBRAW_PROGRESS_HIGHLIGHTS = 1<<13, + LIBRAW_PROGRESS_FUJI_ROTATE = 1<<14, + LIBRAW_PROGRESS_FLIP = 1<<15, + LIBRAW_PROGRESS_APPLY_PROFILE = 1<<16, + LIBRAW_PROGRESS_CONVERT_RGB = 1<<17, + LIBRAW_PROGRESS_STRETCH = 1<<18, +// reserved + LIBRAW_PROGRESS_STAGE19 = 1<<19, + LIBRAW_PROGRESS_STAGE20 = 1<<20, + LIBRAW_PROGRESS_STAGE21 = 1<<21, + LIBRAW_PROGRESS_STAGE22 = 1<<22, + LIBRAW_PROGRESS_STAGE23 = 1<<23, + LIBRAW_PROGRESS_STAGE24 = 1<<24, + LIBRAW_PROGRESS_STAGE25 = 1<<25, + LIBRAW_PROGRESS_STAGE26 = 1<<26, + LIBRAW_PROGRESS_STAGE27 = 1<<27, + + LIBRAW_PROGRESS_THUMB_LOAD = 1<<28, + LIBRAW_PROGRESS_TRESERVED1 = 1<<29, + LIBRAW_PROGRESS_TRESERVED2 = 1<<30, + LIBRAW_PROGRESS_TRESERVED3 = 1<<31 +}; +#define LIBRAW_PROGRESS_THUMB_MASK 0x0fffffff + +enum LibRaw_errors +{ + LIBRAW_SUCCESS = 0, + LIBRAW_UNSPECIFIED_ERROR=-1, + LIBRAW_FILE_UNSUPPORTED = -2, + LIBRAW_REQUEST_FOR_NONEXISTENT_IMAGE=-3, + LIBRAW_OUT_OF_ORDER_CALL=-4, + LIBRAW_NO_THUMBNAIL=-5, + LIBRAW_UNSUPPORTED_THUMBNAIL=-6, + LIBRAW_CANNOT_ADDMASK=-7, + LIBRAW_UNSUFFICIENT_MEMORY=-100007, + LIBRAW_DATA_ERROR=-100008, + LIBRAW_IO_ERROR=-100009, + LIBRAW_CANCELLED_BY_CALLBACK=-100010 +}; + +#define LIBRAW_FATAL_ERROR(ec) ((ec)<-100000) + +enum LibRaw_thumbnail_formats +{ + LIBRAW_THUMBNAIL_UNKNOWN=0, + LIBRAW_THUMBNAIL_JPEG=1, + LIBRAW_THUMBNAIL_BITMAP=2, + LIBRAW_THUMBNAIL_LAYER=4, + LIBRAW_THUMBNAIL_ROLLEI=5 +}; + +enum LibRaw_image_formats +{ + LIBRAW_IMAGE_BITMAP=1, + LIBRAW_IMAGE_JPEG=2 +}; + +#endif diff --git a/src/LibRaw/libraw/libraw_datastream.h b/src/LibRaw/libraw/libraw_datastream.h new file mode 100644 index 000000000000..2f3989409aa4 --- /dev/null +++ b/src/LibRaw/libraw/libraw_datastream.h @@ -0,0 +1,296 @@ +/* -*- C -*- + * File: libraw_datastream.h + * Copyright 2008-2009 Alex Tutubalin + * Created: Sun Jan 18 13:07:35 2009 + * + * LibRaw Data stream interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __LIBRAW_DATASTREAM_H +#define __LIBRAW_DATASTREAM_H + +#include +#include +#include +#include + +#ifndef __cplusplus + +struct LibRaw_abstract_datastream; + +#else // __cplusplus + +#include "libraw_const.h" + +class LibRaw_buffer_datastream; + +class LibRaw_abstract_datastream +{ + public: + LibRaw_abstract_datastream(){substream=0;}; + virtual ~LibRaw_abstract_datastream(void){if(substream) delete substream;} + virtual int valid(){return 0;} + // file input emulation + virtual int read(void *,size_t, size_t ){ return -1;} + virtual int seek(off_t o, int whence){return -1;} + virtual int tell(){return -1;} + virtual int get_char(){return -1;} + virtual char* gets(char *, int){ return NULL;} + virtual int scanf_one(const char *, void *){return -1;} + virtual int eof(){return -1;} + + virtual const char* fname(){ return NULL;}; + virtual int subfile_open(const char*){ return EINVAL;} + virtual void subfile_close(){} + virtual int tempbuffer_open(void*, size_t); + virtual void tempbuffer_close() + { + if(substream) delete substream; + substream = NULL; + } + + protected: + LibRaw_abstract_datastream *substream; +}; + + +class LibRaw_file_datastream : public LibRaw_abstract_datastream +{ + public: + LibRaw_file_datastream(const char *fname) + { + if(fname) + {filename = fname; f = fopen(fname,"rb");} + else + {filename=0;f=0;} + sav=0; + } + + virtual ~LibRaw_file_datastream() {if(f)fclose(f); if(sav)fclose(sav);} + + virtual int valid() { return f?1:0;} + +#define CHK() do {if(!f) throw LIBRAW_EXCEPTION_IO_EOF;}while(0) + virtual int read(void * ptr,size_t size, size_t nmemb) + { + CHK(); + return substream?substream->read(ptr,size,nmemb):fread(ptr,size,nmemb,f); + } + virtual int eof() + { + CHK(); + return substream?substream->eof():feof(f); + } + virtual int seek(off_t o, int whence) + { + CHK(); + return substream?substream->seek(o,whence):fseek(f,o,whence); + } + virtual int tell() + { + CHK(); + return substream?substream->tell():ftell(f); + } + virtual int get_char() + { + CHK(); + return substream?substream->get_char():fgetc(f); + } + virtual char* gets(char *str, int sz) + { + CHK(); + return substream?substream->gets(str,sz):fgets(str,sz,f); + } + virtual int scanf_one(const char *fmt, void*val) + { + CHK(); + return substream?substream->scanf_one(fmt,val):fscanf(f,fmt,val); + } + + virtual const char *fname() { return filename; } + + // secondary + virtual int subfile_open(const char *fn) + { + if(sav) return EBUSY; + sav = f; + f = fopen(fn,"rb"); + if(!f) + { + f = sav; + sav = NULL; + return ENOENT; + } + else + return 0; + } + virtual void subfile_close() + { + if(!sav) return; + fclose(f); + f = sav; + sav = 0; + } + + private: + FILE *f,*sav; + const char *filename; +}; +#undef CHK + +class LibRaw_buffer_datastream : public LibRaw_abstract_datastream +{ + public: + LibRaw_buffer_datastream(void *buffer, size_t bsize) + { + buf = (unsigned char*)buffer; streampos = 0; streamsize = bsize; + } + virtual ~LibRaw_buffer_datastream(){} + virtual int valid() { return buf?1:0;} + virtual int read(void * ptr,size_t sz, size_t nmemb) + { + if(substream) return substream->read(ptr,sz,nmemb); + size_t to_read = sz*nmemb; + if(to_read > streamsize - streampos) + to_read = streamsize-streampos; + if(to_read<1) + return 0; + memmove(ptr,buf+streampos,to_read); + streampos+=to_read; + return (to_read+sz-1)/sz; + } + + virtual int eof() + { + if(substream) return substream->eof(); + return streampos >= streamsize; + } + + virtual int seek(off_t o, int whence) + { + if(substream) return substream->seek(o,whence); + switch(whence) + { + case SEEK_SET: + streampos = o > streamsize ? streamsize: o; + return 0; + case SEEK_CUR: + if(o<0) + { + if (o < - streampos) + o = - streampos; + streampos += o; + } + else if (o>0) + { + if(o> streamsize-streampos) + o = streamsize - streampos; + streampos += o; + } + return 0; + case SEEK_END: + if(o>0) + streampos = streamsize; + else if ( -o > streamsize) + streampos = 0; + else + streampos = streamsize+o; + return 0; + default: + return 0; + } + } + + virtual int tell() + { + if(substream) return substream->tell(); + return streampos; + } + + virtual int get_char() + { + if(substream) return substream->get_char(); + if(streampos>=streamsize) + return -1; + return buf[streampos++]; + } + virtual char* gets(char *s, int sz) + { + if (substream) return substream->gets(s,sz); + unsigned char *psrc,*pdest,*str; + str = (unsigned char *)s; + psrc = buf+streampos; + pdest = str; + while ( ((psrc - buf) < streamsize) + && + ((pdest-str)scanf_one(fmt,val); + int scanf_res; + if(streampos>streamsize) return 0; + scanf_res = sscanf((char*)(buf+streampos),fmt,val); + if(scanf_res>0) + { + int xcnt=0; + while(streampos24) + break; + } + } + return scanf_res; + } + private: + unsigned char *buf; + size_t streampos,streamsize; +}; + +inline int LibRaw_abstract_datastream::tempbuffer_open(void *buf, size_t size) +{ + if(substream) return EBUSY; + substream = new LibRaw_buffer_datastream(buf,size); + return substream?0:EINVAL; +} + + +#endif + +#endif + diff --git a/src/LibRaw/libraw/libraw_internal.h b/src/LibRaw/libraw/libraw_internal.h new file mode 100644 index 000000000000..109b3a83ea76 --- /dev/null +++ b/src/LibRaw/libraw/libraw_internal.h @@ -0,0 +1,241 @@ +/* -*- C++ -*- + * File: libraw_internal.h + * Copyright 2008-2009 Alex Tutubalin + * Created: Sat Mar 8 , 2008 + * + * LibRaw internal data structures (not visible outside) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _LIBRAW_INTERNAL_TYPES_H +#define _LIBRAW_INTERNAL_TYPES_H + +#include +#ifdef __cplusplus + + +#ifndef CLASS +#define CLASS LibRaw:: +#endif + +#else +// C build +#ifndef CLASS +#define CLASS +#endif +#endif + + +#ifdef __cplusplus + +#include "libraw_datastream.h" + +class LibRaw_TLS +{ +public: + struct + { + unsigned bitbuf; + int vbits, reset; + }getbits; + struct + { + UINT64 bitbuf; + int vbits; + + }ph1_bits; + int make_decoder_leaf; + struct + { + struct decode *dstart[18], *dindex; + const int *s; + }radc_token; + struct + { + unsigned pad[128], p; + }sony_decrypt; + unsigned foveon_decoder_huff[1024]; + uchar jpeg_buffer[4096]; + struct + { + uchar buf[0x4000]; + int vbits, padding; + }pana_bits; + + // init - should use in constructor/recycle + void init() + { + getbits.bitbuf = 0; getbits.vbits = getbits.reset = 0; + ph1_bits.bitbuf = 0; ph1_bits.vbits = 0; + pana_bits.vbits = 0; + } +}; + + +class LibRaw_constants +{ + public: + static const float d65_white[3]; + static const double xyz_rgb[3][3]; +}; +#endif // __cplusplus + +#ifdef WIN32 +typedef long off_t; +#endif + +typedef struct +{ +#ifndef __cplusplus + struct +#endif + LibRaw_abstract_datastream *input; + int input_internal; +// char *ifname; + char *meta_data; + off_t profile_offset; + off_t toffset; + +} internal_data_t; + +typedef struct +{ + unsigned mix_green; + unsigned raw_color; + unsigned use_gamma; + unsigned zero_is_bad; + ushort shrink; + ushort fuji_width; + ushort fwidth,fheight; +} internal_output_params_t; + +#define LIBRAW_HISTOGRAM_SIZE 0x2000 +typedef struct +{ + int (*histogram)[LIBRAW_HISTOGRAM_SIZE]; + unsigned *oprof; +} output_data_t; + +typedef struct +{ + unsigned olympus_exif_cfa; + unsigned unique_id; + unsigned tiff_nifds; + int tiff_flip; +}identify_data_t; + +typedef struct +{ + short order; // II* / MM* - file word byte order + ushort sraw_mul[4],cr2_slice[3]; + unsigned kodak_cbpp; + off_t strip_offset, data_offset; + off_t meta_offset; + unsigned meta_length; + unsigned thumb_misc; + unsigned fuji_layout; + unsigned tiff_samples; + unsigned tiff_bps; + unsigned tiff_compress; + unsigned zero_after_ff; + unsigned tile_width, tile_length,load_flags; + unsigned data_error; +}unpacker_data_t; + + + +typedef struct +{ + internal_data_t internal_data; + internal_output_params_t internal_output_params; + output_data_t output_data; + identify_data_t identify_data; + unpacker_data_t unpacker_data; +// callbacks_t callbacks; +} libraw_internal_data_t; + + +struct decode +{ + struct decode *branch[2]; + int leaf; +}; + +struct tiff_ifd_t +{ + int t_width, t_height, bps, comp, phint, offset, t_flip, samples, bytes; +}; + + +struct jhead { + int bits, high, wide, clrs, sraw, psv, restart, vpred[6]; + struct decode *huff[6]; + ushort *row; +}; +struct tiff_tag { + ushort tag, type; + int count; + union { char c[4]; short s[2]; int i; } val; +}; + +struct tiff_hdr { + ushort t_order, magic; + int ifd; + ushort pad, ntag; + struct tiff_tag tag[23]; + int nextifd; + ushort pad2, nexif; + struct tiff_tag exif[4]; + ushort pad3, ngps; + struct tiff_tag gpst[10]; + short bps[4]; + int rat[10]; + unsigned gps[26]; + char t_desc[512], t_make[64], t_model[64], soft[32], date[20], t_artist[64]; +}; + + + +#ifdef DEBUG_STAGE_CHECKS +#define CHECK_ORDER_HIGH(expected_stage) \ + do { if((imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK) >= expected_stage) {fprintf(stderr,"CHECK_HIGH: check %d >= %d\n",imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK,expected_stage);return LIBRAW_OUT_OF_ORDER_CALL;} } while(0) + +#define CHECK_ORDER_LOW(expected_stage) \ + do { printf("Checking LOW %d/%d : %d\n",imgdata.progress_flags,expected_stage,imgdata.progress_flags= expected_stage) \ + {return LIBRAW_OUT_OF_ORDER_CALL;} } while(0) + +#define CHECK_ORDER_LOW(expected_stage) \ + do { if((imgdata.progress_flags&LIBRAW_PROGRESS_THUMB_MASK) < expected_stage) \ + return LIBRAW_OUT_OF_ORDER_CALL; } while(0) + +#define CHECK_ORDER_BIT(expected_stage) \ + do { if(imgdata.progress_flags & expected_stage) return LIBRAW_OUT_OF_ORDER_CALL; } while(0) + +#define SET_PROC_FLAG(stage) do {imgdata.progress_flags |= stage;} while (0) + +#endif + +#endif diff --git a/src/LibRaw/libraw/libraw_types.h b/src/LibRaw/libraw/libraw_types.h new file mode 100644 index 000000000000..0520cab55688 --- /dev/null +++ b/src/LibRaw/libraw/libraw_types.h @@ -0,0 +1,291 @@ +/* -*- C++ -*- + * File: libraw_types.h + * Copyright 2008-2009 Alex Tutubalin + * Created: Sat Mar 8 , 2008 + * + * LibRaw C data structures + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _LIBRAW_TYPES_H +#define _LIBRAW_TYPES_H + +#ifndef WIN32 +#include +#endif +#include +#ifdef _OPENMP +#include +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef USE_LCMS +#define NO_LCMS +#endif + +#include "libraw_const.h" +#include "libraw_version.h" + +typedef long long INT64; +typedef unsigned long long UINT64; +//#define ushort UshORt +typedef unsigned char uchar; +typedef unsigned short ushort; + +#ifdef WIN32 +#ifdef LIBRAW_NODLL +# define DllDef +#else +# ifdef LIBRAW_BUILDLIB +# define DllDef __declspec( dllexport ) +# else +# define DllDef __declspec( dllimport ) +# endif +#endif +// NO Win32 +#else +# define DllDef +#endif + + +//class LibRaw; + +typedef void (* memory_callback)(void * data, const char *file, const char *where); + +DllDef void default_memory_callback(void *data,const char *file, const char *where); + +typedef void (*data_callback)(void *data,const char *file, const int offset); + +DllDef void default_data_callback(void *data,const char *file, const int offset); + +typedef int (* progress_callback) (void *data,enum LibRaw_progress stage, int iteration,int expected); + +typedef struct +{ + memory_callback mem_cb; + void* memcb_data; + + data_callback data_cb; + void* datacb_data; + + progress_callback progress_cb; + void *progresscb_data; +} libraw_callbacks_t; + +// Output bitmap type + +typedef struct +{ + enum LibRaw_image_formats type; + ushort height, + width, + colors, + bits, + gamma_corrected; +#if 0//def _OPENMP +#pragma omp firstprivate(colors,height,width) +#endif + unsigned int data_size; // ðàçìåð ïîëÿ äàííûõ â áàéòàõ + unsigned char data[1]; // we'll allocate more! +}libraw_processed_image_t; + + +//Decoded from exif and used in calculations +typedef struct +{ + char make[64]; + char model[64]; + + unsigned raw_count; + unsigned dng_version; + unsigned is_foveon; + int colors; + + unsigned filters; // camera CFA pattern mask + char cdesc[5]; + +}libraw_iparams_t; + +typedef struct +{ + ushort raw_height, + raw_width, + height, + width, + top_margin, + left_margin; + ushort iheight, + iwidth; +#if 0//def _OPENMP +#pragma omp firstprivate(iheight,iwidth) +#endif + double pixel_aspect; + int flip; + + // masked border sizes + ushort right_margin,bottom_margin; // right masked width and bottom height, inited after idendify() + +} libraw_image_sizes_t; + +//Phase One data +struct ph1_t +{ + int format, key_off, t_black, black_off, split_col, tag_21a; + float tag_210; +}; + + +typedef struct +{ + // 32 bits total + unsigned curve_state : 3; + unsigned rgb_cam_state : 3; + unsigned cmatrix_state : 3; + unsigned pre_mul_state : 3; + unsigned cam_mul_state : 3; + unsigned filler : 17; +} color_data_state_t; + +typedef struct +{ + color_data_state_t color_flags; + ushort white[8][8]; // white block extracted from ciff/CRW + float cam_mul[4]; // camera white balance (from RAW) + float pre_mul[4]; // either set in identify() or calculated. Used on output + float cmatrix[3][4]; // camera color matrix + float rgb_cam[3][4]; // another way to set color matrix + float cam_xyz[4][3]; // Camera to XYZ matrix (DNG coeffs) + ushort curve[0x4001]; // camera tone curve/ljpeg curve + unsigned black; + unsigned maximum; + struct ph1_t phase_one_data; + float flash_used; // canon/CRW only + float canon_ev; // canon/CRW only + char model2[64]; + // profile + void *profile; + unsigned profile_length; +}libraw_colordata_t; + +typedef struct +{ + enum LibRaw_thumbnail_formats tformat; + ushort twidth, + theight; + unsigned tlength; + int tcolors; + + // thumbnail buffer + char *thumb; +}libraw_thumbnail_t; + +// Decoded from exif/raw, but not used in real calculations +typedef struct +{ + float iso_speed; + float shutter; + float aperture; + float focal_len; + time_t timestamp; + unsigned shot_order; + unsigned gpsdata[32]; + // string variables + char desc[512], + artist[64]; +} libraw_imgother_t; + +typedef struct +{ + unsigned greybox[4]; /* -A x1 y1 x2 y2 */ + double aber[4]; /* -C */ + float user_mul[4]; /* -r mul0 mul1 mul2 mul3 */ + unsigned shot_select; /* -s */ + float bright; /* -b */ + float threshold; /* -n */ +#if 0//def _OPENMP +#pragma omp firstprivate(threshold) +#endif + int half_size; /* -h */ + int four_color_rgb; /* -f */ + int document_mode; /* -d/-D */ + int highlight; /* -H */ +// int verbose; /* -v */ + int use_auto_wb; /* -a */ + int use_camera_wb; /* -w */ + int use_camera_matrix; /* +M/-M */ + int output_color; /* -o */ + char *output_profile; /* -o */ + char *camera_profile; /* -p */ + char *bad_pixels; /* -P */ + char *dark_frame; /* -K */ + int output_bps; /* -4 */ + int gamma_16bit; /* -1 */ + int output_tiff; /* -T */ + int user_flip; /* -t */ + int user_qual; /* -q */ + int user_black; /* -k */ + int user_sat; /* -S */ + + int med_passes; /* -m */ + int no_auto_bright; /* -W */ + int use_fuji_rotate;/* -j */ + enum LibRaw_filtering filtering_mode; +}libraw_output_params_t; + +typedef struct +{ + ushort *buffer; // actual pixel buffer size=(raw_width*raw_height - width*height) + ushort *tl; // top left size=(top_margin*left_margin) + ushort *top; // top size=(top_margin*width) + ushort *tr; // top right size=((raw_width-width-left_margin)*top_margin) + ushort *left; // left size=(left_margin*height) + ushort *right; // right size=(raw_width-width-left_margin)*height; + ushort *bl; // bottom left size=(raw_height-height-top_margin)*left_margin + ushort *bottom; // bottom size=(raw_height-height-top_margin)*width + ushort *br; // bottom right size=(raw_height-height-top_margin)* + ushort (*ph1_black)[2]; // Phase One black +}libraw_masked_t; + +typedef struct +{ + unsigned int progress_flags; + unsigned int process_warnings; + libraw_iparams_t idata; + libraw_image_sizes_t sizes; + libraw_colordata_t color; + libraw_imgother_t other; + libraw_thumbnail_t thumbnail; + libraw_masked_t masked_pixels; + ushort (*image)[4] ; +#if 0//def _OPENMP +#pragma omp shared(image) +#endif + libraw_output_params_t params; + // pointer to LibRaw class for use in C calls + void *parent_class; +} libraw_data_t; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/LibRaw/libraw/libraw_version.h b/src/LibRaw/libraw/libraw_version.h new file mode 100644 index 000000000000..7a4844cf3a90 --- /dev/null +++ b/src/LibRaw/libraw/libraw_version.h @@ -0,0 +1,47 @@ +/* -*- C++ -*- + * File: version.h + * Copyright 2008-2009 Alex Tutubalin + * Created: Mon Sept 8, 2008 + * + * LibRaw C++ interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __VERSION_H +#define __VERSION_H + +#define LIBRAW_MAJOR_VERSION 0 +#define LIBRAW_MINOR_VERSION 7 +#define LIBRAW_PATCH_VERSION 0 +#define LIBRAW_VERSION_TAIL Beta1 + +#define _LIBRAW_VERSION_MAKE(a,b,c,d) #a"."#b"."#c"-"#d +#define LIBRAW_VERSION_MAKE(a,b,c,d) _LIBRAW_VERSION_MAKE(a,b,c,d) + +#define LIBRAW_VERSION_STR LIBRAW_VERSION_MAKE(LIBRAW_MAJOR_VERSION,LIBRAW_MINOR_VERSION,LIBRAW_PATCH_VERSION,LIBRAW_VERSION_TAIL) + +#define LIBRAW_MAKE_VERSION(major,minor,patch) \ + (((major) << 16) | ((minor) << 8) | (patch)) + +#define LIBRAW_VERSION \ + LIBRAW_MAKE_VERSION(LIBRAW_MAJOR_VERSION,LIBRAW_MINOR_VERSION,LIBRAW_PATCH_VERSION) + +#define LIBRAW_CHECK_VERSION(major,minor,patch) \ + ( LibRaw::versionNumber() >= LIBRAW_MAKE_VERSION(major,minor,patch) ) + + +#endif diff --git a/src/LibRaw/src/libraw_c_api.cpp b/src/LibRaw/src/libraw_c_api.cpp new file mode 100644 index 000000000000..3770ac31745b --- /dev/null +++ b/src/LibRaw/src/libraw_c_api.cpp @@ -0,0 +1,149 @@ +/* -*- C++ -*- + * File: libraw_c_api.cpp + * Copyright 2008-2009 Alex Tutubalin + * Created: Sat Mar 8 , 2008 + * + * LibRaw C++ interface (implementation) + */ +#include +#include "libraw/libraw.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + libraw_data_t *libraw_init(unsigned int flags) + { + LibRaw *ret = new LibRaw(flags); + return &(ret->imgdata); + } + + const char* libraw_version() { return LibRaw::version();} + const char* libraw_strprogress(enum LibRaw_progress p) { return LibRaw::strprogress(p);} + int libraw_versionNumber() { return LibRaw::versionNumber();} + const char** libraw_cameraList() { return LibRaw::cameraList();} + int libraw_cameraCount() { return LibRaw::cameraCount(); } + const char* libraw_unpack_function_name(libraw_data_t* lr) + { + if(!lr) return "NULL parameter passed"; + LibRaw *ip = (LibRaw*) lr->parent_class; + return ip->unpack_function_name(); + } + int libraw_rotate_fuji_raw(libraw_data_t* lr) + { + if(!lr) return EINVAL; + LibRaw *ip = (LibRaw*) lr->parent_class; + return ip->rotate_fuji_raw(); + } + + int libraw_add_masked_borders_to_bitmap(libraw_data_t* lr) + { + if(!lr) return EINVAL; + LibRaw *ip = (LibRaw*) lr->parent_class; + return ip->add_masked_borders_to_bitmap(); + } + + int libraw_open_file(libraw_data_t* lr, const char *file) + { + if(!lr) return EINVAL; + LibRaw *ip = (LibRaw*) lr->parent_class; + return ip->open_file(file); + } + int libraw_open_buffer(libraw_data_t* lr, void *buffer, size_t size) + { + if(!lr) return EINVAL; + LibRaw *ip = (LibRaw*) lr->parent_class; + return ip->open_buffer(buffer,size); + } + int libraw_unpack(libraw_data_t* lr) + { + if(!lr) return EINVAL; + LibRaw *ip = (LibRaw*) lr->parent_class; + return ip->unpack(); + } + int libraw_unpack_thumb(libraw_data_t* lr) + { + if(!lr) return EINVAL; + LibRaw *ip = (LibRaw*) lr->parent_class; + return ip->unpack_thumb(); + } + void libraw_recycle(libraw_data_t* lr) + { + if(!lr) return; + LibRaw *ip = (LibRaw*) lr->parent_class; + ip->recycle(); + } + void libraw_close(libraw_data_t* lr) + { + if(!lr) return; + LibRaw *ip = (LibRaw*) lr->parent_class; + delete ip; + } + + void libraw_set_memerror_handler(libraw_data_t* lr, memory_callback cb,void *data) + { + if(!lr) return; + LibRaw *ip = (LibRaw*) lr->parent_class; + ip->set_memerror_handler(cb,data); + + } + void libraw_set_dataerror_handler(libraw_data_t* lr,data_callback func,void *data) + { + if(!lr) return; + LibRaw *ip = (LibRaw*) lr->parent_class; + ip->set_dataerror_handler(func,data); + + } + void libraw_set_progress_handler(libraw_data_t* lr, progress_callback cb,void *data) + { + if(!lr) return; + LibRaw *ip = (LibRaw*) lr->parent_class; + ip->set_progress_handler(cb,data); + + } + + // DCRAW + int libraw_adjust_sizes_info_only(libraw_data_t* lr) + { + if(!lr) return EINVAL; + LibRaw *ip = (LibRaw*) lr->parent_class; + return ip->adjust_sizes_info_only(); + } + + int libraw_dcraw_document_mode_processing(libraw_data_t* lr) + { + if(!lr) return EINVAL; + LibRaw *ip = (LibRaw*) lr->parent_class; + return ip->dcraw_document_mode_processing(); + + } + int libraw_dcraw_ppm_tiff_writer(libraw_data_t* lr,const char *filename) + { + if(!lr) return EINVAL; + LibRaw *ip = (LibRaw*) lr->parent_class; + return ip->dcraw_ppm_tiff_writer(filename); + } + int libraw_dcraw_thumb_writer(libraw_data_t* lr,const char *fname) + { + if(!lr) return EINVAL; + LibRaw *ip = (LibRaw*) lr->parent_class; + return ip->dcraw_thumb_writer(fname); + + } + int libraw_dcraw_process(libraw_data_t* lr) + { + if(!lr) return EINVAL; + LibRaw *ip = (LibRaw*) lr->parent_class; + return ip->dcraw_process(); + } + libraw_processed_image_t *dcraw_make_mem_image(libraw_data_t* lr, int *errorcode) + { + if(!lr) return NULL; + LibRaw *ip = (LibRaw*) lr->parent_class; + return ip->dcraw_make_mem_image(errorcode); + } + +#ifdef __cplusplus +} +#endif diff --git a/src/LibRaw/src/libraw_cxx.cpp b/src/LibRaw/src/libraw_cxx.cpp new file mode 100644 index 000000000000..75053b549fe4 --- /dev/null +++ b/src/LibRaw/src/libraw_cxx.cpp @@ -0,0 +1,1935 @@ +/* -*- C++ -*- + * File: libraw_cxx.cpp + * Copyright 2008-2009 Alex Tutubalin + * Created: Sat Mar 8 , 2008 + * + * LibRaw C++ interface (implementation) + */ + +#include +#include +#include +#ifndef WIN32 +#include +#else +#include +#endif +#define LIBRAW_LIBRARY_BUILD +#include "libraw/libraw.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + void default_memory_callback(void *,const char *file,const char *where) + { + fprintf (stderr,"%s: Out of memory in %s\n", file?file:"unknown file", where); + } + + void default_data_callback(void*,const char *file, const int offset) + { + if(offset < 0) + fprintf (stderr,"%s: Unexpected end of file\n", file?file:"unknown file"); + else + fprintf (stderr,"%s: data corrupted at %d\n",file?file:"unknown file",offset); + } + const char *libraw_strerror(int e) + { + enum LibRaw_errors errorcode = (LibRaw_errors)e; + switch(errorcode) + { + case LIBRAW_SUCCESS: + return "No error"; + case LIBRAW_UNSPECIFIED_ERROR: + return "Unspecified error"; + case LIBRAW_FILE_UNSUPPORTED: + return "Unsupported file format or not RAW file"; + case LIBRAW_REQUEST_FOR_NONEXISTENT_IMAGE: + return "Request for nonexisting image number"; + case LIBRAW_OUT_OF_ORDER_CALL: + return "Out of order call of libraw function"; + case LIBRAW_NO_THUMBNAIL: + return "No thumbnail in file"; + case LIBRAW_UNSUPPORTED_THUMBNAIL: + return "Unsupported thumbnail format"; + case LIBRAW_CANNOT_ADDMASK: + return "Cannot add masked pixels to resized image"; + case LIBRAW_UNSUFFICIENT_MEMORY: + return "Unsufficient memory"; + case LIBRAW_DATA_ERROR: + return "Corrupted data or unexpected EOF"; + case LIBRAW_IO_ERROR: + return "Input/output error"; + case LIBRAW_CANCELLED_BY_CALLBACK: + return "Cancelled by user callback"; + default: + return "Unknown error code"; + } + } + +#ifdef __cplusplus +} +#endif + + +const double LibRaw_constants::xyz_rgb[3][3] = +{ + { 0.412453, 0.357580, 0.180423 }, + { 0.212671, 0.715160, 0.072169 }, + { 0.019334, 0.119193, 0.950227 } +}; + +const float LibRaw_constants::d65_white[3] = { 0.950456, 1, 1.088754 }; + +#define P1 imgdata.idata +#define S imgdata.sizes +#define O imgdata.params +#define C imgdata.color +#define M imgdata.masked_pixels +#define T imgdata.thumbnail +#define IO libraw_internal_data.internal_output_params +#define ID libraw_internal_data.internal_data + +#define EXCEPTION_HANDLER(e) do{ \ + fprintf(stderr,"Exception %d caught\n",e); \ + switch(e) \ + { \ + case LIBRAW_EXCEPTION_ALLOC: \ + recycle(); \ + return LIBRAW_UNSUFFICIENT_MEMORY; \ + case LIBRAW_EXCEPTION_DECODE_RAW: \ + case LIBRAW_EXCEPTION_DECODE_JPEG: \ + recycle(); \ + return LIBRAW_DATA_ERROR; \ + case LIBRAW_EXCEPTION_IO_EOF: \ + case LIBRAW_EXCEPTION_IO_CORRUPT: \ + recycle(); \ + return LIBRAW_IO_ERROR; \ + case LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK:\ + recycle(); \ + return LIBRAW_CANCELLED_BY_CALLBACK; \ + default: \ + return LIBRAW_UNSPECIFIED_ERROR; \ + } \ + }while(0) + +void LibRaw::derror() +{ + if (!libraw_internal_data.unpacker_data.data_error && libraw_internal_data.internal_data.input) + { + if (libraw_internal_data.internal_data.input->eof()) + { + if(callbacks.data_cb)(*callbacks.data_cb)(callbacks.datacb_data, + libraw_internal_data.internal_data.input->fname(),-1); + throw LIBRAW_EXCEPTION_IO_EOF; + } + else + { + if(callbacks.data_cb)(*callbacks.data_cb)(callbacks.datacb_data, + libraw_internal_data.internal_data.input->fname(), + libraw_internal_data.internal_data.input->tell()); + throw LIBRAW_EXCEPTION_IO_CORRUPT; + } + } + libraw_internal_data.unpacker_data.data_error = 1; +} +LibRaw:: LibRaw(unsigned int flags) +{ + double aber[4] = {1,1,1,1}; + unsigned greybox[4] = { 0, 0, UINT_MAX, UINT_MAX }; +#ifdef DCRAW_VERBOSE + verbose = 1; +#else + verbose = 0; +#endif + bzero(&imgdata,sizeof(imgdata)); + bzero(&libraw_internal_data,sizeof(libraw_internal_data)); + bzero(&callbacks,sizeof(callbacks)); + callbacks.mem_cb = (flags & LIBRAW_OPIONS_NO_MEMERR_CALLBACK) ? NULL: &default_memory_callback; + callbacks.data_cb = (flags & LIBRAW_OPIONS_NO_DATAERR_CALLBACK)? NULL : &default_data_callback; + memmove(&imgdata.params.aber,&aber,sizeof(aber)); + memmove(&imgdata.params.greybox,&greybox,sizeof(greybox)); + + imgdata.params.bright=1; + imgdata.params.use_camera_matrix=-1; + imgdata.params.user_flip=-1; + imgdata.params.user_black=-1; + imgdata.params.user_sat=-1; + imgdata.params.user_qual=-1; + imgdata.params.output_color=1; + imgdata.params.output_bps=8; + imgdata.params.use_fuji_rotate=1; + imgdata.parent_class = this; + imgdata.progress_flags = 0; + tls = new LibRaw_TLS; + tls->init(); +} + + +void* LibRaw:: malloc(size_t t) +{ + void *p = memmgr.malloc(t); + return p; +} +void* LibRaw:: calloc(size_t n,size_t t) +{ + void *p = memmgr.calloc(n,t); + return p; +} +void LibRaw:: free(void *p) +{ + memmgr.free(p); +} + + +int LibRaw:: fc (int row, int col) +{ + static const char filter[16][16] = + { { 2,1,1,3,2,3,2,0,3,2,3,0,1,2,1,0 }, + { 0,3,0,2,0,1,3,1,0,1,1,2,0,3,3,2 }, + { 2,3,3,2,3,1,1,3,3,1,2,1,2,0,0,3 }, + { 0,1,0,1,0,2,0,2,2,0,3,0,1,3,2,1 }, + { 3,1,1,2,0,1,0,2,1,3,1,3,0,1,3,0 }, + { 2,0,0,3,3,2,3,1,2,0,2,0,3,2,2,1 }, + { 2,3,3,1,2,1,2,1,2,1,1,2,3,0,0,1 }, + { 1,0,0,2,3,0,0,3,0,3,0,3,2,1,2,3 }, + { 2,3,3,1,1,2,1,0,3,2,3,0,2,3,1,3 }, + { 1,0,2,0,3,0,3,2,0,1,1,2,0,1,0,2 }, + { 0,1,1,3,3,2,2,1,1,3,3,0,2,1,3,2 }, + { 2,3,2,0,0,1,3,0,2,0,1,2,3,0,1,0 }, + { 1,3,1,2,3,2,3,2,0,2,0,1,1,0,3,0 }, + { 0,2,0,3,1,0,0,1,1,3,3,2,3,2,2,1 }, + { 2,1,3,2,3,1,2,1,0,3,0,2,0,2,0,2 }, + { 0,3,1,0,0,2,0,3,2,1,3,1,1,3,1,3 } }; + + if (imgdata.idata.filters != 1) return FC(row,col); + return filter[(row+imgdata.sizes.top_margin) & 15][(col+imgdata.sizes.left_margin) & 15]; +} + +void LibRaw:: recycle() +{ + if(libraw_internal_data.internal_data.input && libraw_internal_data.internal_data.input_internal) + { + delete libraw_internal_data.internal_data.input; + libraw_internal_data.internal_data.input = NULL; + } + libraw_internal_data.internal_data.input_internal = 0; +#define FREE(a) do { if(a) { free(a); a = NULL;} }while(0) + + FREE(imgdata.image); + FREE(imgdata.thumbnail.thumb); + FREE(libraw_internal_data.internal_data.meta_data); + FREE(libraw_internal_data.output_data.histogram); + FREE(libraw_internal_data.output_data.oprof); + FREE(imgdata.color.profile); + FREE(imgdata.masked_pixels.buffer); + FREE(imgdata.masked_pixels.ph1_black); +#undef FREE +#define ZERO(a) bzero(&a,sizeof(a)) + ZERO(imgdata.masked_pixels); + ZERO(imgdata.sizes); + ZERO(libraw_internal_data.internal_output_params); +#undef ZERO + memmgr.cleanup(); + imgdata.thumbnail.tformat = LIBRAW_THUMBNAIL_UNKNOWN; + imgdata.progress_flags = 0; + + tls->init(); +} + +const char * LibRaw::unpack_function_name() +{ + if(!load_raw) return "Function not set"; + + // sorted names order + if (load_raw == &LibRaw::adobe_dng_load_raw_lj) return "adobe_dng_load_raw_lj()"; + if (load_raw == &LibRaw::adobe_dng_load_raw_nc) return "adobe_dng_load_raw_nc()"; + if (load_raw == &LibRaw::canon_600_load_raw) return "canon_600_load_raw()"; + + if (load_raw == &LibRaw::canon_a5_load_raw) return "canon_a5_load_raw()"; + if (load_raw == &LibRaw::canon_compressed_load_raw) return "canon_compressed_load_raw()"; + if (load_raw == &LibRaw::canon_sraw_load_raw) return "canon_sraw_load_raw()"; + + if (load_raw == &LibRaw::casio_qv5700_load_raw ) return "casio_qv5700_load_raw()"; + if (load_raw == &LibRaw::eight_bit_load_raw ) return "eight_bit_load_raw()"; + if (load_raw == &LibRaw::foveon_load_raw ) return "foveon_load_raw()"; + if (load_raw == &LibRaw::fuji_load_raw ) return "fuji_load_raw()"; + // 10 + if (load_raw == &LibRaw::hasselblad_load_raw ) return "hasselblad_load_raw()"; + if (load_raw == &LibRaw::imacon_full_load_raw ) return "imacon_full_load_raw()"; + if (load_raw == &LibRaw::kodak_262_load_raw ) return "kodak_262_load_raw()"; + + if (load_raw == &LibRaw::kodak_65000_load_raw ) return "kodak_65000_load_raw()"; + if (load_raw == &LibRaw::kodak_dc120_load_raw ) return "kodak_dc120_load_raw()"; + if (load_raw == &LibRaw::kodak_jpeg_load_raw ) return "kodak_jpeg_load_raw()"; + + if (load_raw == &LibRaw::kodak_radc_load_raw ) return "kodak_radc_load_raw()"; + if (load_raw == &LibRaw::kodak_rgb_load_raw ) return "kodak_rgb_load_raw()"; + if (load_raw == &LibRaw::kodak_yrgb_load_raw ) return "kodak_yrgb_load_raw()"; + if (load_raw == &LibRaw::kodak_ycbcr_load_raw ) return "kodak_ycbcr_load_raw()"; + // 20 + if (load_raw == &LibRaw::leaf_hdr_load_raw ) return "leaf_hdr_load_raw()"; + if (load_raw == &LibRaw::lossless_jpeg_load_raw) return "lossless_jpeg_load_raw()"; + if (load_raw == &LibRaw::minolta_rd175_load_raw ) return "minolta_rd175_load_raw()"; + + if (load_raw == &LibRaw::nikon_compressed_load_raw) return "nikon_compressed_load_raw()"; + if (load_raw == &LibRaw::nikon_e900_load_raw ) return "nikon_e900_load_raw()"; + if (load_raw == &LibRaw::nokia_load_raw ) return "nokia_load_raw()"; + + if (load_raw == &LibRaw::olympus_e300_load_raw ) return "olympus_e300_load_raw()"; + if (load_raw == &LibRaw::olympus_e410_load_raw ) return "olympus_e410_load_raw()"; + if (load_raw == &LibRaw::packed_12_load_raw ) return "packed_12_load_raw()"; + if (load_raw == &LibRaw::panasonic_load_raw ) return "panasonic_load_raw()"; + // 30 + if (load_raw == &LibRaw::pentax_k10_load_raw ) return "pentax_k10_load_raw()"; + if (load_raw == &LibRaw::phase_one_load_raw ) return "phase_one_load_raw()"; + if (load_raw == &LibRaw::phase_one_load_raw_c ) return "phase_one_load_raw_c()"; + + if (load_raw == &LibRaw::quicktake_100_load_raw ) return "quicktake_100_load_raw()"; + if (load_raw == &LibRaw::rollei_load_raw ) return "rollei_load_raw()"; + if (load_raw == &LibRaw::sinar_4shot_load_raw ) return "sinar_4shot_load_raw()"; + + if (load_raw == &LibRaw::smal_v6_load_raw ) return "smal_v6_load_raw()"; + if (load_raw == &LibRaw::smal_v9_load_raw ) return "smal_v9_load_raw()"; + if (load_raw == &LibRaw::sony_load_raw ) return "sony_load_raw()"; + if (load_raw == &LibRaw::sony_arw_load_raw ) return "sony_arw_load_raw()"; + // 40 + if (load_raw == &LibRaw::sony_arw2_load_raw ) return "sony_arw2_load_raw()"; + if (load_raw == &LibRaw::unpacked_load_raw ) return "unpacked_load_raw()"; + // 42 total + + return "Unknown unpack function"; +} + + +void LibRaw:: merror (void *ptr, const char *where) +{ + if (ptr) return; + if(callbacks.mem_cb)(*callbacks.mem_cb)(callbacks.memcb_data, + libraw_internal_data.internal_data.input + ?libraw_internal_data.internal_data.input->fname() + :NULL, + where); + throw LIBRAW_EXCEPTION_ALLOC; +} + +ushort * LibRaw::get_masked_pointer(int row, int col) +{ + if(row<0 || col < 0) return NULL; + if(!M.buffer) return NULL; + if(row < S.top_margin) + { + // top band + if(col < S.left_margin) + { + return &(M.tl[row*S.left_margin+col]); + } + else if (col < S.left_margin + S.width) + { + int icol = col - S.left_margin; + return &(M.top[row*S.width+icol]); + } + else if (col < S.raw_width) + { + int icol = col - S.left_margin - S.width; + return &(M.tr[row*S.right_margin+icol]); + } + else + return NULL; // out of bounds + } + else if (row < S.top_margin + S.height) + { + //normal image height + int irow = row - S.top_margin; + if(col < S.left_margin) + { + return &M.left[irow*S.left_margin + col]; + } + else if (col < S.left_margin + S.width) + { + // central image + return NULL; + } + else if (col < S.raw_width) + { + int icol = col - S.left_margin - S.width; + return &M.right[irow*S.right_margin+icol]; + } + else + return NULL; // out of bounds + } + else if (row < S.raw_height) + { + int irow = row - S.top_margin - S.height; + // bottom band + if(col < S.left_margin) + { + return &M.bl[irow*S.left_margin+col]; + } + else if (col < S.left_margin + S.width) + { + int icol = col - S.left_margin; + return &M.bottom[irow*S.width + icol]; + } + else if (col < S.raw_width) + { + int icol = col - S.left_margin - S.width; + return &M.br[irow*S.right_margin + icol]; + } + else + return NULL; // out of bounds + } + else + { + // out of bounds + return NULL; + } + // fallback + return NULL; +} + +void LibRaw:: init_masked_ptrs() +{ + if(!M.buffer) return; + + // top band + M.tl = M.buffer; + M.top = M.tl +(S.top_margin*S.left_margin); + M.tr = M.top + (S.top_margin*S.width); + + // left-right + M.left = M.tr + (S.top_margin * S.right_margin); + M.right = M.left + (S.left_margin * S.height); + + // bottom band + M.bl = M.right + (S.right_margin * S.height); + M.bottom = M.bl + (S.left_margin * S.bottom_margin); + M.br = M.bottom + (S.width * S.bottom_margin); + +} + +int LibRaw::add_masked_borders_to_bitmap() +{ + CHECK_ORDER_HIGH(LIBRAW_PROGRESS_PRE_INTERPOLATE); + CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW); + + if(S.width != S.iwidth || S.height!=S.iheight) + return LIBRAW_CANNOT_ADDMASK; + + if(P1.is_foveon || !P1.filters) + return LIBRAW_CANNOT_ADDMASK; + + if(!imgdata.image) + return LIBRAW_OUT_OF_ORDER_CALL; + + if(S.raw_width < S.width || S.raw_height < S.height) + return LIBRAW_SUCCESS; // nothing to do or already called + + if(S.width == S.raw_width && S.height == S.raw_height) + return LIBRAW_SUCCESS; // nothing to do or already called + + ushort (*newimage)[4]; + + newimage = (ushort (*)[4]) calloc (S.raw_height*S.raw_width, sizeof (*newimage)); + merror (newimage, "add_masked_borders_to_bitmap()"); + + int r,c; + // top rows + for (r=0; rvalid()) + { + delete stream; + return LIBRAW_IO_ERROR; + } + ID.input_internal = 0; // preserve from deletion on error + int ret = open_datastream(stream); + if (ret == LIBRAW_SUCCESS) + { + ID.input_internal =1 ; // flag to delete datastream on recycle + } + else + { + delete stream; + ID.input_internal = 0; + } + return ret; +} + +int LibRaw::open_buffer(void *buffer, size_t size) +{ + // this stream will close on recycle() + if(!buffer || buffer==(void*)-1) + return LIBRAW_IO_ERROR; + + LibRaw_buffer_datastream *stream = new LibRaw_buffer_datastream(buffer,size); + if(!stream->valid()) + { + delete stream; + return LIBRAW_IO_ERROR; + } + ID.input_internal = 0; // preserve from deletion on error + int ret = open_datastream(stream); + if (ret == LIBRAW_SUCCESS) + { + ID.input_internal =1 ; // flag to delete datastream on recycle + } + else + { + delete stream; + ID.input_internal = 0; + } + return ret; +} + + +int LibRaw::open_datastream(LibRaw_abstract_datastream *stream) +{ + + if(!stream) + return ENOENT; + if(!stream->valid()) + return LIBRAW_IO_ERROR; + recycle(); + + try { + ID.input = stream; + SET_PROC_FLAG(LIBRAW_PROGRESS_OPEN); + + if (O.use_camera_matrix < 0) + O.use_camera_matrix = O.use_camera_wb; + + identify(); + + if(IO.fuji_width) + { + IO.fwidth = S.width; + IO.fheight = S.height; + S.iwidth = S.width = IO.fuji_width << !libraw_internal_data.unpacker_data.fuji_layout; + S.iheight = S.height = S.raw_height; + S.raw_height += 2*S.top_margin; + } + + int saved_raw_width = S.raw_width; + int saved_width = S.width; + // from packed_12_load_raw + if ((load_raw == &LibRaw:: packed_12_load_raw) && (S.raw_width * 2 >= S.width * 3)) + { + // raw_width is in bytes! + S.raw_width = S.raw_width * 2 / 3; + } + else if (S.pixel_aspect < 0.95 || S.pixel_aspect > 1.05) + { + S.width*=S.pixel_aspect; + } + + if(S.raw_width>S.width + S.left_margin) + S.right_margin = S.raw_width - S.width - S.left_margin; + + if(S.raw_height > S.height + S.top_margin) + S.bottom_margin = S.raw_height - S.height - S.top_margin; + + S.raw_width = saved_raw_width; + S.width = saved_width; + + if(C.profile_length) + { + if(C.profile) free(C.profile); + C.profile = malloc(C.profile_length); + merror(C.profile,"LibRaw::open_file()"); + ID.input->seek(ID.profile_offset,SEEK_SET); + ID.input->read(C.profile,C.profile_length,1); + } + + SET_PROC_FLAG(LIBRAW_PROGRESS_IDENTIFY); + } + catch ( LibRaw_exceptions err) { + EXCEPTION_HANDLER(err); + } + + if(P1.raw_count < 1) + return LIBRAW_FILE_UNSUPPORTED; + + if (O.user_flip >= 0) + S.flip = O.user_flip; + + switch ((S.flip+3600) % 360) + { + case 270: S.flip = 5; break; + case 180: S.flip = 3; break; + case 90: S.flip = 6; break; + } + + write_fun = &LibRaw::write_ppm_tiff; + + if (load_raw == &LibRaw::kodak_ycbcr_load_raw) + { + S.height += S.height & 1; + S.width += S.width & 1; + } + + IO.shrink = P1.filters && (O.half_size || O.threshold || O.aber[0] != 1 || O.aber[2] != 1); + S.iheight = (S.height + IO.shrink) >> IO.shrink; + S.iwidth = (S.width + IO.shrink) >> IO.shrink; + + SET_PROC_FLAG(LIBRAW_PROGRESS_SIZE_ADJUST); + + + return LIBRAW_SUCCESS; +} + +int LibRaw::unpack(void) +{ + CHECK_ORDER_HIGH(LIBRAW_PROGRESS_LOAD_RAW); + CHECK_ORDER_LOW(LIBRAW_PROGRESS_IDENTIFY); + try { + + RUN_CALLBACK(LIBRAW_PROGRESS_LOAD_RAW,0,2); + if (O.shot_select >= P1.raw_count) + return LIBRAW_REQUEST_FOR_NONEXISTENT_IMAGE; + + if(!load_raw) + return LIBRAW_UNSPECIFIED_ERROR; + + if (O.use_camera_matrix && C.cmatrix[0][0] > 0.25) + { + memcpy (C.rgb_cam, C.cmatrix, sizeof (C.cmatrix)); + IO.raw_color = 0; + } + // already allocated ? + if(imgdata.image) free(imgdata.image); + + imgdata.image = (ushort (*)[4]) calloc (S.iheight*S.iwidth, sizeof (*imgdata.image)); + merror (imgdata.image, "unpack()"); + + + if(S.top_margin || S.left_margin || S.right_margin || S.bottom_margin) + { + unsigned sz = S.raw_height*(S.left_margin+S.right_margin) + + S.width*(S.top_margin+S.bottom_margin); + imgdata.masked_pixels.buffer = (ushort*) calloc(sz, sizeof(ushort)); + merror (imgdata.masked_pixels.buffer, "unpack()"); + init_masked_ptrs(); + } + if (libraw_internal_data.unpacker_data.meta_length) + { + libraw_internal_data.internal_data.meta_data = + (char *) malloc (libraw_internal_data.unpacker_data.meta_length); + merror (libraw_internal_data.internal_data.meta_data, "LibRaw::unpack()"); + } + ID.input->seek(libraw_internal_data.unpacker_data.data_offset, SEEK_SET); + // foveon_load_raw produces different data for document_mode, we'll + // deal with it in dcraw_document_mode_processing + int save_document_mode = O.document_mode; + O.document_mode = 0; + + if(!own_filtering_supported() && (O.filtering_mode & LIBRAW_FILTERING_AUTOMATIC_BIT)) + O.filtering_mode = LIBRAW_FILTERING_AUTOMATIC_BIT; // turn on black and zeroes filtering + + (this->*load_raw)(); + + O.document_mode = save_document_mode; + + if (O.filtering_mode & LIBRAW_FILTERING_AUTOMATIC_BIT) + O.filtering_mode = LIBRAW_FILTERING_AUTOMATIC; // restore automated mode + + SET_PROC_FLAG(LIBRAW_PROGRESS_LOAD_RAW); + RUN_CALLBACK(LIBRAW_PROGRESS_LOAD_RAW,1,2); + + return 0; + } + catch ( LibRaw_exceptions err) { + EXCEPTION_HANDLER(err); + } +} + +int LibRaw::dcraw_document_mode_processing(void) +{ + CHECK_ORDER_HIGH(LIBRAW_PROGRESS_PRE_INTERPOLATE); + CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW); + + try { + + if(IO.fwidth) + rotate_fuji_raw(); + + if(!own_filtering_supported() && (O.filtering_mode & LIBRAW_FILTERING_AUTOMATIC_BIT)) + O.filtering_mode = LIBRAW_FILTERING_AUTOMATIC_BIT; // turn on black and zeroes filtering + + O.document_mode = 2; + if(P1.is_foveon) + { + // filter image data for foveon document mode + short *iptr = (short *)imgdata.image; + for (int i=0; i < S.height*S.width*4; i++) + { + if ((short) iptr[i] < 0) + iptr[i] = 0; + } + SET_PROC_FLAG(LIBRAW_PROGRESS_FOVEON_INTERPOLATE); + } + + O.use_fuji_rotate = 0; + if (!(O.filtering_mode & LIBRAW_FILTERING_NOZEROES) && IO.zero_is_bad) + { + remove_zeroes(); + SET_PROC_FLAG(LIBRAW_PROGRESS_REMOVE_ZEROES); + } + if(O.bad_pixels) + { + bad_pixels(O.bad_pixels); + SET_PROC_FLAG(LIBRAW_PROGRESS_BAD_PIXELS); + } + if (O.dark_frame) + { + subtract (O.dark_frame); + SET_PROC_FLAG(LIBRAW_PROGRESS_DARK_FRAME); + } + if(O.filtering_mode & LIBRAW_FILTERING_NOBLACKS) + C.black=0; + + if (O.user_black >= 0) + C.black = O.user_black; + + if (O.user_sat > 0) + C.maximum = O.user_sat; + + pre_interpolate(); + SET_PROC_FLAG(LIBRAW_PROGRESS_PRE_INTERPOLATE); + + if (libraw_internal_data.internal_output_params.mix_green) + { + int i; + for (P1.colors=3, i=0; i < S.height*S.width; i++) + imgdata.image[i][1] = (imgdata.image[i][1] + imgdata.image[i][3]) >> 1; + } + SET_PROC_FLAG(LIBRAW_PROGRESS_MIX_GREEN); + + if (!P1.is_foveon && P1.colors == 3) + median_filter(); + SET_PROC_FLAG(LIBRAW_PROGRESS_MEDIAN_FILTER); + + if (!P1.is_foveon && O.highlight == 2) + blend_highlights(); + + if (!P1.is_foveon && O.highlight > 2) + recover_highlights(); + SET_PROC_FLAG(LIBRAW_PROGRESS_HIGHLIGHTS); + + if (O.use_fuji_rotate) + fuji_rotate(); + SET_PROC_FLAG(LIBRAW_PROGRESS_FUJI_ROTATE); +#ifndef NO_LCMS + if(O.camera_profile) + { + apply_profile(O.camera_profile,O.output_profile); + SET_PROC_FLAG(LIBRAW_PROGRESS_APPLY_PROFILE); + } +#endif + if(!libraw_internal_data.output_data.histogram) + { + libraw_internal_data.output_data.histogram = (int (*)[LIBRAW_HISTOGRAM_SIZE]) malloc(sizeof(*libraw_internal_data.output_data.histogram)*4); + merror(libraw_internal_data.output_data.histogram,"LibRaw::dcraw_document_mode_processing()"); + } + convert_to_rgb(); + SET_PROC_FLAG(LIBRAW_PROGRESS_CONVERT_RGB); + + if (O.use_fuji_rotate) + stretch(); + SET_PROC_FLAG(LIBRAW_PROGRESS_STRETCH); + + if (O.filtering_mode & LIBRAW_FILTERING_AUTOMATIC_BIT) + O.filtering_mode = LIBRAW_FILTERING_AUTOMATIC; // restore automated mode + + return 0; + } + catch ( LibRaw_exceptions err) { + EXCEPTION_HANDLER(err); + } + +} + +#if 1 +#define FORC(cnt) for (c=0; c < cnt; c++) +#define FORCC FORC(ret->colors) +#define SWAP(a,b) { a ^= b; a ^= (b ^= a); } + +libraw_processed_image_t * LibRaw::dcraw_make_mem_thumb(int *errcode) +{ + if(!T.thumb) + { + if ( !ID.toffset) + { + if(errcode) *errcode= LIBRAW_NO_THUMBNAIL; + } + else + { + if(errcode) *errcode= LIBRAW_OUT_OF_ORDER_CALL; + } + return NULL; + } + + if (T.tformat == LIBRAW_THUMBNAIL_BITMAP) + { + libraw_processed_image_t * ret = + (libraw_processed_image_t *)::malloc(sizeof(libraw_processed_image_t)+T.tlength); + + if(!ret) + { + if(errcode) *errcode= ENOMEM; + return NULL; + } + + bzero(ret,sizeof(libraw_processed_image_t)); + ret->type = LIBRAW_IMAGE_BITMAP; + ret->height = T.theight; + ret->width = T.twidth; + ret->colors = 3; + ret->bits = 8; + ret->gamma_corrected = 1; + ret->data_size = T.tlength; + memmove(ret->data,T.thumb,T.tlength); + if(errcode) *errcode= 0; + return ret; + } + else if (T.tformat == LIBRAW_THUMBNAIL_JPEG) + { + ushort exif[5]; + int mk_exif = 0; + if(strcmp(T.thumb+6,"Exif")) mk_exif = 1; + + int dsize = T.tlength + mk_exif * (sizeof(exif)+sizeof(tiff_hdr)); + + libraw_processed_image_t * ret = + (libraw_processed_image_t *)::malloc(sizeof(libraw_processed_image_t)+dsize); + + if(!ret) + { + if(errcode) *errcode= ENOMEM; + return NULL; + } + + bzero(ret,sizeof(libraw_processed_image_t)); + + ret->type = LIBRAW_IMAGE_JPEG; + ret->data_size = dsize; + + ret->data[0] = 0xff; + ret->data[1] = 0xd8; + if(mk_exif) + { + struct tiff_hdr th; + memcpy (exif, "\xff\xe1 Exif\0\0", 10); + exif[1] = htons (8 + sizeof th); + memmove(ret->data+2,exif,sizeof(exif)); + tiff_head (&th, 0); + memmove(ret->data+(2+sizeof(exif)),&th,sizeof(th)); + memmove(ret->data+(2+sizeof(exif)+sizeof(th)),T.thumb+2,T.tlength-2); + } + else + { + memmove(ret->data+2,T.thumb+2,T.tlength-2); + } + if(errcode) *errcode= 0; + return ret; + + } + else + { + if(errcode) *errcode= LIBRAW_UNSUPPORTED_THUMBNAIL; + return NULL; + + } +} + + + +libraw_processed_image_t *LibRaw::dcraw_make_mem_image(int *errcode) +{ + if((imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK) < LIBRAW_PROGRESS_PRE_INTERPOLATE) + { + if(errcode) *errcode= LIBRAW_OUT_OF_ORDER_CALL; + return NULL; + } + + if(!libraw_internal_data.output_data.histogram) + { + libraw_internal_data.output_data.histogram = + (int (*)[LIBRAW_HISTOGRAM_SIZE]) malloc(sizeof(*libraw_internal_data.output_data.histogram)*4); + merror(libraw_internal_data.output_data.histogram,"LibRaw::dcraw_make_mem_image()"); + } + + unsigned ds = S.height * S.width * (O.output_bps/8) * P1.colors; + libraw_processed_image_t *ret = (libraw_processed_image_t*)::malloc(sizeof(libraw_processed_image_t)+ds); + if(!ret) + { + if(errcode) *errcode= ENOMEM; + return NULL; + } + bzero(ret,sizeof(libraw_processed_image_t)); + // metadata init + + int s_iheight = S.iheight; + int s_iwidth = S.iwidth; + int s_width = S.width; + int s_hwight = S.height; + + S.iheight = S.height; + S.iwidth = S.width; + + + if (S.flip & 4) SWAP(S.height,S.width); + + + ret->type = LIBRAW_IMAGE_BITMAP; + ret->height = S.height; + ret->width = S.width; + ret->colors = P1.colors; + ret->bits = O.output_bps; + ret->gamma_corrected = (O.output_bps == 8)?1:O.gamma_16bit; + + ret->data_size = ds; + + // Cut'n'paste from write_tiff_ppm, should be generalized later + uchar *bufp = ret->data; + uchar *ppm; + ushort *ppm2,lut16[0x10000]; + int c, row, col, soff, rstep, cstep; + + + if (ret->bits == 8 || ret->gamma_corrected ) gamma_lut (lut16); + soff = flip_index (0, 0); + cstep = flip_index (0, 1) - soff; + rstep = flip_index (1, 0) - flip_index (0, S.width); + + + for (row=0; row < ret->height; row++, soff += rstep) + { + ppm2 = (ushort*) (ppm = bufp); + for (col=0; col < ret->width; col++, soff += cstep) + if (ret->bits == 8) + FORCC ppm [col*ret->colors+c] = lut16[imgdata.image[soff][c]]/256; + else if(ret->gamma_corrected) + FORCC ppm2[col*ret->colors+c] = lut16[imgdata.image[soff][c]]; + else + FORCC ppm2[col*ret->colors+c] = imgdata.image[soff][c]; + bufp+=ret->colors*(ret->bits/8)*ret->width; + } + if(errcode) *errcode= 0; + + S.iheight = s_iheight; + S.iwidth = s_iwidth; + S.width = s_width; + S.height = s_hwight; + + return ret; +} + +#undef FORC +#undef FORCC +#undef SWAP +#endif + + +int LibRaw::dcraw_ppm_tiff_writer(const char *filename) +{ + CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW); + + if(!imgdata.image) + return LIBRAW_OUT_OF_ORDER_CALL; + + if(!filename) + return ENOENT; + FILE *f = fopen(filename,"wb"); + + if(!f) + return errno; + + try { + if(!libraw_internal_data.output_data.histogram) + { + libraw_internal_data.output_data.histogram = + (int (*)[LIBRAW_HISTOGRAM_SIZE]) malloc(sizeof(*libraw_internal_data.output_data.histogram)*4); + merror(libraw_internal_data.output_data.histogram,"LibRaw::dcraw_ppm_tiff_writer()"); + } + write_ppm_tiff(f); + SET_PROC_FLAG(LIBRAW_PROGRESS_FLIP); + fclose(f); + return 0; + } + catch ( LibRaw_exceptions err) { + fclose(f); + EXCEPTION_HANDLER(err); + } +} + +void LibRaw::kodak_thumb_loader() +{ + // some kodak cameras + ushort s_height = S.height, s_width = S.width,s_iwidth = S.iwidth,s_iheight=S.iheight; + int s_colors = P1.colors; + unsigned s_filters = P1.filters; + ushort (*s_image)[4] = imgdata.image; + + + S.height = T.theight; + S.width = T.twidth; + P1.filters = 0; + + if (thumb_load_raw == &CLASS kodak_ycbcr_load_raw) + { + S.height += S.height & 1; + S.width += S.width & 1; + } + + imgdata.image = (ushort (*)[4]) calloc (S.iheight*S.iwidth, sizeof (*imgdata.image)); + merror (imgdata.image, "LibRaw::kodak_thumb_loader()"); + + ID.input->seek(ID.toffset, SEEK_SET); + // read kodak thumbnail into T.image[] + (this->*thumb_load_raw)(); + + // copy-n-paste from image pipe +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define LIM(x,min,max) MAX(min,MIN(x,max)) +#define CLIP(x) LIM(x,0,65535) +#define SWAP(a,b) { a ^= b; a ^= (b ^= a); } + + // from scale_colors + { + double dmax; + float scale_mul[4]; + int c,val; + for (dmax=DBL_MAX, c=0; c < 3; c++) + if (dmax > C.pre_mul[c]) + dmax = C.pre_mul[c]; + + for( c=0; c< 3; c++) + scale_mul[c] = (C.pre_mul[c] / dmax) * 65535.0 / C.maximum; + scale_mul[3] = scale_mul[1]; + + size_t size = S.height * S.width; + for (int i=0; i < size*4 ; i++) + { + val = imgdata.image[0][i]; + if(!val) continue; + val *= scale_mul[i & 3]; + imgdata.image[0][i] = CLIP(val); + } + } + + // from convert_to_rgb + ushort *img; + int row,col; + + int (*t_hist)[LIBRAW_HISTOGRAM_SIZE] = (int (*)[LIBRAW_HISTOGRAM_SIZE]) calloc(sizeof(*t_hist),4); + merror (t_hist, "LibRaw::kodak_thumb_loader()"); + + float out[3], + out_cam[3][4] = + { + {2.81761312, -1.98369181, 0.166078627, 0}, + {-0.111855984, 1.73688626, -0.625030339, 0}, + {-0.0379119813, -0.891268849, 1.92918086, 0} + }; + + for (img=imgdata.image[0], row=0; row < S.height; row++) + for (col=0; col < S.width; col++, img+=4) + { + out[0] = out[1] = out[2] = 0; + for(int c=0;c<3;c++) + { + out[0] += out_cam[0][c] * img[c]; + out[1] += out_cam[1][c] * img[c]; + out[2] += out_cam[2][c] * img[c]; + } + for(int c=0; c<3; c++) + img[c] = CLIP((int) out[c]); + for(int c=0; c> 3]++; + + } + + // from gamma_lut + int (*save_hist)[LIBRAW_HISTOGRAM_SIZE] = libraw_internal_data.output_data.histogram; + libraw_internal_data.output_data.histogram = t_hist; + + ushort *lut16 = (ushort*)calloc(0x10000,sizeof(ushort)); + merror(lut16,"LibRaw::kodak_thumb_loader()"); + gamma_lut(lut16); + + libraw_internal_data.output_data.histogram = save_hist; + + free(t_hist); + + // from write_ppm_tiff - copy pixels into bitmap + + S.iheight = S.height; + S.iwidth = S.width; + if (S.flip & 4) SWAP(S.height,S.width); + + if(T.thumb) free(T.thumb); + T.thumb = (char*) calloc (S.width * S.height, P1.colors); + merror (T.thumb, "LibRaw::kodak_thumb_loader()"); + T.tlength = S.width * S.height * P1.colors; + + // from write_tiff_ppm + { + int soff = flip_index (0, 0); + int cstep = flip_index (0, 1) - soff; + int rstep = flip_index (1, 0) - flip_index (0, S.width); + + for (int row=0; row < S.height; row++, soff += rstep) + { + char *ppm = T.thumb + row*S.width*P1.colors; + for (int col=0; col < S.width; col++, soff += cstep) + for(int c = 0; c < P1.colors; c++) + ppm [col*P1.colors+c] = lut16[imgdata.image[soff][c]]/256; + } + } + free(lut16); + // restore variables + free(imgdata.image); + imgdata.image = s_image; + + T.twidth = S.width; + S.width = s_width; + + S.iwidth = s_iwidth; + S.iheight = s_iheight; + + T.theight = S.height; + S.height = s_height; + + T.tcolors = P1.colors; + P1.colors = s_colors; + + P1.filters = s_filters; +} +#undef MIN +#undef MAX +#undef LIM +#undef CLIP +#undef SWAP + + +void LibRaw::foveon_thumb_loader (void) +{ + unsigned bwide, row, col, bitbuf=0, bit=1, c, i; + struct decode *dindex; + short pred[3]; + + if(T.thumb) free(T.thumb); + T.thumb = NULL; + + bwide = get4(); + if (bwide > 0) + { + if (bwide < T.twidth*3) return; + T.thumb = (char*)malloc(3*T.twidth * T.theight); + merror (T.thumb, "foveon_thumb()"); + char *buf = (char*)malloc(bwide); + merror (buf, "foveon_thumb()"); + for (row=0; row < T.theight; row++) + { + ID.input->read(buf, 1, bwide); + memmove(T.thumb+(row*T.twidth*3),buf,T.twidth*3); + } + free(buf); + T.tlength = 3*T.twidth * T.theight; + T.tformat = LIBRAW_THUMBNAIL_BITMAP; + return; + } + else + { + foveon_decoder (256, 0); + T.thumb = (char*)malloc(3*T.twidth * T.theight); + char *bufp = T.thumb; + merror (T.thumb, "foveon_thumb()"); + for (row=0; row < T.theight; row++) + { + memset (pred, 0, sizeof pred); + if (!bit) get4(); + for (bit=col=0; col < T.twidth; col++) + for(c=0;c<3;c++) + { + for (dindex=first_decode; dindex->branch[0]; ) + { + if ((bit = (bit-1) & 31) == 31) + for (i=0; i < 4; i++) + bitbuf = (bitbuf << 8) + ID.input->get_char(); + dindex = dindex->branch[bitbuf >> bit & 1]; + } + pred[c] += dindex->leaf; + (*bufp++)=pred[c]; + } + } + T.tformat = LIBRAW_THUMBNAIL_BITMAP; + T.tlength = 3*T.twidth * T.theight; + } + return; +} + + +// Äîñòàåò thumbnail èç ôàéëà, ñòàâèò thumb_format â ñîîòâåòñòâèè ñ ôîðìàòîì +int LibRaw::unpack_thumb(void) +{ + CHECK_ORDER_LOW(LIBRAW_PROGRESS_IDENTIFY); + CHECK_ORDER_BIT(LIBRAW_PROGRESS_THUMB_LOAD); + + try { + if ( !ID.toffset) + { + return LIBRAW_NO_THUMBNAIL; + } + else if (thumb_load_raw) + { + kodak_thumb_loader(); + T.tformat = LIBRAW_THUMBNAIL_BITMAP; + SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD); + return 0; + } + else + { + ID.input->seek(ID.toffset, SEEK_SET); + if ( write_thumb == &LibRaw::jpeg_thumb) + { + if(T.thumb) free(T.thumb); + T.thumb = (char *) malloc (T.tlength); + merror (T.thumb, "jpeg_thumb()"); + ID.input->read (T.thumb, 1, T.tlength); + T.tcolors = 3; + T.tformat = LIBRAW_THUMBNAIL_JPEG; + SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD); + return 0; + } + else if (write_thumb == &LibRaw::ppm_thumb) + { + T.tlength = T.twidth * T.theight*3; + if(T.thumb) free(T.thumb); + + T.thumb = (char *) malloc (T.tlength); + merror (T.thumb, "ppm_thumb()"); + + ID.input->read(T.thumb, 1, T.tlength); + + T.tformat = LIBRAW_THUMBNAIL_BITMAP; + SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD); + return 0; + + } + else if (write_thumb == &LibRaw::foveon_thumb) + { + foveon_thumb_loader(); + // may return with error, so format is set in + // foveon thumb loader itself + SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD); + return 0; + } + // else if -- all other write_thumb cases! + else + { + return LIBRAW_UNSUPPORTED_THUMBNAIL; + } + } + // last resort + return LIBRAW_UNSUPPORTED_THUMBNAIL; + } + catch ( LibRaw_exceptions err) { + EXCEPTION_HANDLER(err); + } + +} + +int LibRaw::dcraw_thumb_writer(const char *fname) +{ +// CHECK_ORDER_LOW(LIBRAW_PROGRESS_THUMB_LOAD); + + if(!fname) + return ENOENT; + + FILE *tfp = fopen(fname,"wb"); + + if(!tfp) + return errno; + + if(!T.thumb) + { + fclose(tfp); + return LIBRAW_OUT_OF_ORDER_CALL; + } + + try { + switch (T.tformat) + { + case LIBRAW_THUMBNAIL_JPEG: + jpeg_thumb_writer (tfp,T.thumb,T.tlength); + break; + case LIBRAW_THUMBNAIL_BITMAP: + fprintf (tfp, "P6\n%d %d\n255\n", T.twidth, T.theight); + fwrite (T.thumb, 1, T.tlength, tfp); + break; + default: + fclose(tfp); + return LIBRAW_UNSUPPORTED_THUMBNAIL; + } + fclose(tfp); + return 0; + } + catch ( LibRaw_exceptions err) { + fclose(tfp); + EXCEPTION_HANDLER(err); + } +} + +int LibRaw::adjust_sizes_info_only(void) +{ + CHECK_ORDER_LOW(LIBRAW_PROGRESS_IDENTIFY); + CHECK_ORDER_HIGH(LIBRAW_PROGRESS_FUJI_ROTATE); + if (O.use_fuji_rotate) + { + if (IO.fuji_width) + { + // restore saved values + if(IO.fheight) + { + S.height = IO.fheight; + S.width = IO.fwidth; + S.iheight = (S.height + IO.shrink) >> IO.shrink; + S.iwidth = (S.width + IO.shrink) >> IO.shrink; + S.raw_height -= 2*S.top_margin; + IO.fheight = IO.fwidth = 0; // prevent repeated calls + } + // dcraw code + IO.fuji_width = (IO.fuji_width - 1 + IO.shrink) >> IO.shrink; + S.iwidth = (ushort)(IO.fuji_width / sqrt(0.5)); + S.iheight = (ushort)( (S.iheight - IO.fuji_width) / sqrt(0.5)); + } + else + { + if (S.pixel_aspect < 1) S.iheight = (ushort)( S.iheight / S.pixel_aspect + 0.5); + if (S.pixel_aspect > 1) S.iwidth = (ushort) (S.iwidth * S.pixel_aspect + 0.5); + } + } + SET_PROC_FLAG(LIBRAW_PROGRESS_FUJI_ROTATE); + if (S.flip & 4) + { + unsigned short t = S.iheight; + S.iheight=S.iwidth; + S.iwidth = t; + SET_PROC_FLAG(LIBRAW_PROGRESS_FLIP); + } + return 0; +} + +int LibRaw::rotate_fuji_raw(void) +{ + CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW); + CHECK_ORDER_HIGH(LIBRAW_PROGRESS_PRE_INTERPOLATE); + + + if(!IO.fwidth) return LIBRAW_SUCCESS; + int row,col,r,c; + ushort (*newimage)[4]; + ushort fiwidth,fiheight; + + fiheight = (IO.fheight + IO.shrink) >> IO.shrink; + fiwidth = (IO.fwidth + IO.shrink) >> IO.shrink; + + newimage = (ushort (*)[4]) calloc (fiheight*fiwidth, sizeof (*newimage)); + merror(newimage,"rotate_fuji_raw()"); + for(row=0;row> 1); + c = col + ((row+1) >> 1); + } else { + r = IO.fuji_width - 1 + row - (col >> 1); + c = row + ((col+1) >> 1); + } + newimage[((r) >> IO.shrink)*fiwidth + ((c) >> IO.shrink)][FC(r,c)] = + imgdata.image[((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)][FC(row,col)]; + } + } + // restore fuji sizes! + S.height = IO.fheight; + S.width = IO.fwidth; + S.iheight = (S.height + IO.shrink) >> IO.shrink; + S.iwidth = (S.width + IO.shrink) >> IO.shrink; + S.raw_height -= 2*S.top_margin; + IO.fheight = IO.fwidth = 0; // prevent repeated calls + + free(imgdata.image); + imgdata.image = newimage; + return LIBRAW_SUCCESS; + +} + + +int LibRaw::dcraw_process(void) +{ + int quality,i; + + + CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW); + CHECK_ORDER_HIGH(LIBRAW_PROGRESS_PRE_INTERPOLATE); + + try { + + if(IO.fwidth) + rotate_fuji_raw(); + + + if(!own_filtering_supported() && (O.filtering_mode & LIBRAW_FILTERING_AUTOMATIC_BIT)) + O.filtering_mode = LIBRAW_FILTERING_AUTOMATIC_BIT; // turn on black and zeroes filtering + + if(O.half_size) + O.four_color_rgb = 1; + + if (!(O.filtering_mode & LIBRAW_FILTERING_NOZEROES) && IO.zero_is_bad) + { + remove_zeroes(); + SET_PROC_FLAG(LIBRAW_PROGRESS_REMOVE_ZEROES); + } + if(O.bad_pixels) + { + bad_pixels(O.bad_pixels); + SET_PROC_FLAG(LIBRAW_PROGRESS_BAD_PIXELS); + } + if (O.dark_frame) + { + subtract (O.dark_frame); + SET_PROC_FLAG(LIBRAW_PROGRESS_DARK_FRAME); + } + + quality = 2 + !IO.fuji_width; + + if(O.filtering_mode & LIBRAW_FILTERING_NOBLACKS) + C.black=0; + + if (O.user_qual >= 0) quality = O.user_qual; + if (O.user_black >= 0) C.black = O.user_black; + if (O.user_sat > 0) C.maximum = O.user_sat; + + if (P1.is_foveon && !O.document_mode) + { + foveon_interpolate(); + SET_PROC_FLAG(LIBRAW_PROGRESS_FOVEON_INTERPOLATE); + } + + if (!P1.is_foveon && O.document_mode < 2) + { + scale_colors(); + SET_PROC_FLAG(LIBRAW_PROGRESS_SCALE_COLORS); + } + + pre_interpolate(); + SET_PROC_FLAG(LIBRAW_PROGRESS_PRE_INTERPOLATE); + + if (P1.filters && !O.document_mode) + { + if (quality == 0) + lin_interpolate(); + else if (quality == 1 || P1.colors > 3) + vng_interpolate(); + else if (quality == 2) + ppg_interpolate(); + else + ahd_interpolate(); + SET_PROC_FLAG(LIBRAW_PROGRESS_INTERPOLATE); + } + if (IO.mix_green) + { + for (P1.colors=3, i=0; i < S.height * S.width; i++) + imgdata.image[i][1] = (imgdata.image[i][1] + imgdata.image[i][3]) >> 1; + SET_PROC_FLAG(LIBRAW_PROGRESS_MIX_GREEN); + } + + if(!P1.is_foveon) + { + if (P1.colors == 3) + { + median_filter(); + SET_PROC_FLAG(LIBRAW_PROGRESS_MEDIAN_FILTER); + } + + if (O.highlight == 2) + { + blend_highlights(); + SET_PROC_FLAG(LIBRAW_PROGRESS_HIGHLIGHTS); + } + + if (O.highlight > 2) + { + recover_highlights(); + SET_PROC_FLAG(LIBRAW_PROGRESS_HIGHLIGHTS); + } + } + if (O.use_fuji_rotate) + { + fuji_rotate(); + SET_PROC_FLAG(LIBRAW_PROGRESS_FUJI_ROTATE); + } + + if(!libraw_internal_data.output_data.histogram) + { + libraw_internal_data.output_data.histogram = (int (*)[LIBRAW_HISTOGRAM_SIZE]) malloc(sizeof(*libraw_internal_data.output_data.histogram)*4); + merror(libraw_internal_data.output_data.histogram,"LibRaw::dcraw_process()"); + } +#ifndef NO_LCMS + if(O.camera_profile) + { + apply_profile(O.camera_profile,O.output_profile); + SET_PROC_FLAG(LIBRAW_PROGRESS_APPLY_PROFILE); + } +#endif + + convert_to_rgb(); + SET_PROC_FLAG(LIBRAW_PROGRESS_CONVERT_RGB); + + if (O.use_fuji_rotate) + { + stretch(); + SET_PROC_FLAG(LIBRAW_PROGRESS_STRETCH); + } + if (O.filtering_mode & LIBRAW_FILTERING_AUTOMATIC_BIT) + O.filtering_mode = LIBRAW_FILTERING_AUTOMATIC; // restore automated mode + return 0; + } + catch ( LibRaw_exceptions err) { + EXCEPTION_HANDLER(err); + } +} + +// Supported cameras: +static const char *static_camera_list[] = +{ +"Adobe Digital Negative (DNG)", +"Apple QuickTake 100", +"Apple QuickTake 150", +"Apple QuickTake 200", +"AVT F-080C", +"AVT F-145C", +"AVT F-201C", +"AVT F-510C", +"AVT F-810C", +"Canon PowerShot 600", +"Canon PowerShot A5", +"Canon PowerShot A5 Zoom", +"Canon PowerShot A50", +"Canon PowerShot A460 (CHDK hack)", +"Canon PowerShot A530 (CHDK hack)", +"Canon PowerShot A610 (CHDK hack)", +"Canon PowerShot A620 (CHDK hack)", +"Canon PowerShot A630 (CHDK hack)", +"Canon PowerShot A640 (CHDK hack)", +"Canon PowerShot A650 (CHDK hack)", +"Canon PowerShot A710 IS (CHDK hack)", +"Canon PowerShot A720 IS (CHDK hack)", +"Canon PowerShot Pro70", +"Canon PowerShot Pro90 IS", +"Canon PowerShot G1", +"Canon PowerShot G2", +"Canon PowerShot G3", +"Canon PowerShot G5", +"Canon PowerShot G6", +"Canon PowerShot G7 (CHDK hack)", +"Canon PowerShot G9", +"Canon PowerShot G10", +"Canon PowerShot S2 IS (CHDK hack)", +"Canon PowerShot S3 IS (CHDK hack)", +"Canon PowerShot S5 IS (CHDK hack)", +"Canon PowerShot SD300 (CHDK hack)", +"Canon PowerShot S30", +"Canon PowerShot S40", +"Canon PowerShot S45", +"Canon PowerShot S50", +"Canon PowerShot S60", +"Canon PowerShot S70", +"Canon PowerShot Pro1", +"Canon EOS D30", +"Canon EOS D60", +"Canon EOS 5D", +"Canon EOS 5D Mark II", +"Canon EOS 10D", +"Canon EOS 20D", +"Canon EOS 30D", +"Canon EOS 40D", +"Canon EOS 50D", +"Canon EOS 300D / Digital Rebel / Kiss Digital", +"Canon EOS 350D / Digital Rebel XT / Kiss Digital N", +"Canon EOS 400D / Digital Rebel XTi / Kiss Digital X", +"Canon EOS 450D / Digital Rebel XSi / Kiss Digital X2", +"Canon EOS 1000D / Digital Rebel XS / Kiss Digital F", +"Canon EOS D2000C", +"Canon EOS-1D", +"Canon EOS-1DS", +"Canon EOS-1D Mark II", +"Canon EOS-1D Mark III", +"Canon EOS-1D Mark II N", +"Canon EOS-1Ds Mark II", +"Canon EOS-1Ds Mark III", +"Casio QV-2000UX", +"Casio QV-3000EX", +"Casio QV-3500EX", +"Casio QV-4000", +"Casio QV-5700", +"Casio QV-R41", +"Casio QV-R51", +"Casio QV-R61", +"Casio EX-S100", +"Casio EX-Z4", +"Casio EX-Z50", +"Casio EX-Z55", +"Casio Exlim Pro 505", +"Casio Exlim Pro 600", +"Casio Exlim Pro 700", +"Contax N Digital", +"Creative PC-CAM 600", +"Epson R-D1", +"Foculus 531C", +"Fuji FinePix E550", +"Fuji FinePix E900", +"Fuji FinePix F700", +"Fuji FinePix F710", +"Fuji FinePix F800", +"Fuji FinePix F810", +"Fuji FinePix S2Pro", +"Fuji FinePix S3Pro", +"Fuji FinePix S5Pro", +"Fuji FinePix S20Pro", +"Fuji FinePix S100FS", +"Fuji FinePix S5000", +"Fuji FinePix S5100/S5500", +"Fuji FinePix S5200/S5600", +"Fuji FinePix S6000fd", +"Fuji FinePix S7000", +"Fuji FinePix S9000/S9500", +"Fuji FinePix S9100/S9600", +"Fuji IS-1", +"Hasselblad CFV", +"Hasselblad H3D", +"Hasselblad V96C", +"Imacon Ixpress 16-megapixel", +"Imacon Ixpress 22-megapixel", +"Imacon Ixpress 39-megapixel", +"ISG 2020x1520", +"Kodak DC20 (see Oliver Hartman's page)", +"Kodak DC25 (see Jun-ichiro Itoh's page)", +"Kodak DC40", +"Kodak DC50", +"Kodak DC120 (also try kdc2tiff)", +"Kodak DCS200", +"Kodak DCS315C", +"Kodak DCS330C", +"Kodak DCS420", +"Kodak DCS460", +"Kodak DCS460A", +"Kodak DCS520C", +"Kodak DCS560C", +"Kodak DCS620C", +"Kodak DCS620X", +"Kodak DCS660C", +"Kodak DCS660M", +"Kodak DCS720X", +"Kodak DCS760C", +"Kodak DCS760M", +"Kodak EOSDCS1", +"Kodak EOSDCS3B", +"Kodak NC2000F", +"Kodak ProBack", +"Kodak PB645C", +"Kodak PB645H", +"Kodak PB645M", +"Kodak DCS Pro 14n", +"Kodak DCS Pro 14nx", +"Kodak DCS Pro SLR/c", +"Kodak DCS Pro SLR/n", +"Kodak C330", +"Kodak C603", +"Kodak P850", +"Kodak P880", +"Kodak KAI-0340", +"Konica KD-400Z", +"Konica KD-510Z", +"Leaf AFi 7", +"Leaf Aptus 17", +"Leaf Aptus 22", +"Leaf Aptus 54S", +"Leaf Aptus 65", +"Leaf Aptus 75", +"Leaf Aptus 75S", +"Leaf Cantare", +"Leaf CatchLight", +"Leaf CMost", +"Leaf DCB2", +"Leaf Valeo 6", +"Leaf Valeo 11", +"Leaf Valeo 17", +"Leaf Valeo 22", +"Leaf Volare", +"Leica Digilux 2", +"Leica Digilux 3", +"Leica D-LUX2", +"Leica D-LUX3", +"Leica D-LUX4", +"Leica V-LUX1", +"Logitech Fotoman Pixtura", +"Mamiya ZD", +"Micron 2010", +"Minolta RD175", +"Minolta DiMAGE 5", +"Minolta DiMAGE 7", +"Minolta DiMAGE 7i", +"Minolta DiMAGE 7Hi", +"Minolta DiMAGE A1", +"Minolta DiMAGE A2", +"Minolta DiMAGE A200", +"Minolta DiMAGE G400", +"Minolta DiMAGE G500", +"Minolta DiMAGE G530", +"Minolta DiMAGE G600", +"Minolta DiMAGE Z2", +"Minolta Alpha/Dynax/Maxxum 5D", +"Minolta Alpha/Dynax/Maxxum 7D", +"Nikon D1", +"Nikon D1H", +"Nikon D1X", +"Nikon D2H", +"Nikon D2Hs", +"Nikon D2X", +"Nikon D2Xs", +"Nikon D3", +"Nikon D40", +"Nikon D40X", +"Nikon D50", +"Nikon D60", +"Nikon D70", +"Nikon D70s", +"Nikon D80", +"Nikon D90", +"Nikon D100", +"Nikon D200", +"Nikon D300", +"Nikon D700", +"Nikon E700 (\"DIAG RAW\" hack)", +"Nikon E800 (\"DIAG RAW\" hack)", +"Nikon E880 (\"DIAG RAW\" hack)", +"Nikon E900 (\"DIAG RAW\" hack)", +"Nikon E950 (\"DIAG RAW\" hack)", +"Nikon E990 (\"DIAG RAW\" hack)", +"Nikon E995 (\"DIAG RAW\" hack)", +"Nikon E2100 (\"DIAG RAW\" hack)", +"Nikon E2500 (\"DIAG RAW\" hack)", +"Nikon E3200 (\"DIAG RAW\" hack)", +"Nikon E3700 (\"DIAG RAW\" hack)", +"Nikon E4300 (\"DIAG RAW\" hack)", +"Nikon E4500 (\"DIAG RAW\" hack)", +"Nikon E5000", +"Nikon E5400", +"Nikon E5700", +"Nikon E8400", +"Nikon E8700", +"Nikon E8800", +"Nikon Coolpix P6000", +"Nikon Coolpix S6 (\"DIAG RAW\" hack)", +"Nokia N95", +"Olympus C3030Z", +"Olympus C5050Z", +"Olympus C5060WZ", +"Olympus C7070WZ", +"Olympus C70Z,C7000Z", +"Olympus C740UZ", +"Olympus C770UZ", +"Olympus C8080WZ", +"Olympus E-1", +"Olympus E-3", +"Olympus E-10", +"Olympus E-20", +"Olympus E-300", +"Olympus E-330", +"Olympus E-400", +"Olympus E-410", +"Olympus E-420", +"Olympus E-500", +"Olympus E-510", +"Olympus E-520", +"Olympus SP310", +"Olympus SP320", +"Olympus SP350", +"Olympus SP500UZ", +"Olympus SP510UZ", +"Olympus SP550UZ", +"Olympus SP560UZ", +"Olympus SP570UZ", +"Panasonic DMC-FZ8", +"Panasonic DMC-FZ18", +"Panasonic DMC-FZ28", +"Panasonic DMC-FZ30", +"Panasonic DMC-FZ50", +"Panasonic DMC-FX150", +"Panasonic DMC-G1", +"Panasonic DMC-L1", +"Panasonic DMC-L10", +"Panasonic DMC-LC1", +"Panasonic DMC-LX1", +"Panasonic DMC-LX2", +"Panasonic DMC-LX3", +"Pentax *ist D", +"Pentax *ist DL", +"Pentax *ist DL2", +"Pentax *ist DS", +"Pentax *ist DS2", +"Pentax K10D", +"Pentax K20D", +"Pentax K100D", +"Pentax K100D Super", +"Pentax K200D", +"Pentax Optio S", +"Pentax Optio S4", +"Pentax Optio 33WR", +"Pentax Optio 750Z", +"Phase One LightPhase", +"Phase One H 10", +"Phase One H 20", +"Phase One H 25", +"Phase One P 20", +"Phase One P 25", +"Phase One P 30", +"Phase One P 45", +"Pixelink A782", +"Polaroid x530", +"Rollei d530flex", +"RoverShot 3320af", +"Samsung GX-1S", +"Samsung GX-10", +"Samsung S85 (hacked)", +"Sarnoff 4096x5440", +"Sigma SD9", +"Sigma SD10", +"Sigma SD14", +"Sinar 3072x2048", +"Sinar 4080x4080", +"Sinar 4080x5440", +"Sinar STI format", +"SMaL Ultra-Pocket 3", +"SMaL Ultra-Pocket 4", +"SMaL Ultra-Pocket 5", +"Sony DSC-F828", +"Sony DSC-R1", +"Sony DSC-V3", +"Sony DSLR-A100", +"Sony DSLR-A200", +"Sony DSLR-A300", +"Sony DSLR-A350", +"Sony DSLR-A700", +"Sony DSLR-A900", +"Sony XCD-SX910CR", +"STV680 VGA", + NULL +}; + +const char** LibRaw::cameraList() { return static_camera_list;} +int LibRaw::cameraCount() { return (sizeof(static_camera_list)/sizeof(static_camera_list[0]))-1; } + + +const char * LibRaw::strprogress(enum LibRaw_progress p) +{ + switch(p) + { + case LIBRAW_PROGRESS_START: + return "Starting"; + case LIBRAW_PROGRESS_OPEN : + return "Opening file"; + case LIBRAW_PROGRESS_IDENTIFY : + return "Reading metadata"; + case LIBRAW_PROGRESS_SIZE_ADJUST: + return "Adjusting size"; + case LIBRAW_PROGRESS_LOAD_RAW: + return "Reading RAW data"; + case LIBRAW_PROGRESS_REMOVE_ZEROES: + return "Clearing zero values"; + case LIBRAW_PROGRESS_BAD_PIXELS : + return "Removing dead pixels"; + case LIBRAW_PROGRESS_DARK_FRAME: + return "Subtracting dark frame data"; + case LIBRAW_PROGRESS_FOVEON_INTERPOLATE: + return "Interpolating Foveon sensor data"; + case LIBRAW_PROGRESS_SCALE_COLORS: + return "Scaling colors"; + case LIBRAW_PROGRESS_PRE_INTERPOLATE: + return "Pre-interpolating"; + case LIBRAW_PROGRESS_INTERPOLATE: + return "Interpolating"; + case LIBRAW_PROGRESS_MIX_GREEN : + return "Mixing green channels"; + case LIBRAW_PROGRESS_MEDIAN_FILTER : + return "Median filter"; + case LIBRAW_PROGRESS_HIGHLIGHTS: + return "Highlight recovery"; + case LIBRAW_PROGRESS_FUJI_ROTATE : + return "Rotating Fuji diagonal data"; + case LIBRAW_PROGRESS_FLIP : + return "Flipping image"; + case LIBRAW_PROGRESS_APPLY_PROFILE: + return "ICC conversion"; + case LIBRAW_PROGRESS_CONVERT_RGB: + return "Converting to RGB"; + case LIBRAW_PROGRESS_STRETCH: + return "Stretching image"; + case LIBRAW_PROGRESS_THUMB_LOAD: + return "Loading thumbnail"; + default: + return "Some strange things"; + } +} diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 000000000000..e37caff8397f --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,8 @@ +SUBDIRS=LibRaw +include shared.am +noinst_LIBRARIES=libdarktable.a +libdarktable_a_SOURCES=control/jobs.c library/film.c library/library.c gui/gtk.c gui/navigation.c gui/histogram.c gui/curveeditor.c develop/develop.c develop/imageop.c common/imageio.c common/darktable.c common/nikon_curve.c control/control.c develop/iop_hsb.c gui/develop_view.c common/image.c common/image_cache.c common/image_compression.c +bin_PROGRAMS=darktable +darktable_SOURCES=main.c +darktable_LDADD=-lgomp $(MAGICK_LIBS) $(CAIRO_LIBS) $(GLADE_LIBS) $(GTK_LIBS) $(GTHREAD_LIBS) $(SQLITE_LIBS) libdarktable.a LibRaw/libraw_r.a -lstdc++ +noinst_HEADERS=library/library.h gui/curveeditor.h gui/gtk.h gui/histogram.h gui/navigation.h develop/develop.h develop/imageop.h common/nikon_curve.h common/darktable.h common/imageio.h control/settings.h control/control.h control/jobs.h develop/iop_hsb.h common/image.h common/image_cache.h common/image_compression.h diff --git a/src/common/darktable.c b/src/common/darktable.c new file mode 100644 index 000000000000..9e422e428d82 --- /dev/null +++ b/src/common/darktable.c @@ -0,0 +1,131 @@ +#ifdef HAVE_CONFIG_H + #include "../config.h" +#endif +#include "common/darktable.h" +#include "common/image.h" +#include "common/image_cache.h" +#include "library/library.h" +#include "develop/develop.h" +#include "control/control.h" +#include "gui/gtk.h" +#include +#include +#ifdef HAVE_MAGICK + #include +#endif +#include +#ifdef _OPENMP +# include +#endif + +darktable_t darktable; + +int dt_init(int argc, char *argv[]) +{ +#ifdef _OPENMP + omp_set_num_threads(omp_get_num_procs()); +#endif + darktable.unmuted = 0; + for(int k=0;k k+1) + { + if(!strcmp(argv[k+1], "cache")) darktable.unmuted |= DT_DEBUG_CACHE; // enable debugging for lib/film/cache module + if(!strcmp(argv[k+1], "control")) darktable.unmuted |= DT_DEBUG_CONTROL; // enable debugging for scheduler module + if(!strcmp(argv[k+1], "dev")) darktable.unmuted |= DT_DEBUG_DEV; // develop module + } + } + +#ifdef HAVE_MAGICK + MagickCoreGenesis(*argv, MagickTrue); +#endif + char *homedir = getenv("HOME"); + char filename[512], *c = NULL; + snprintf(filename, 512, "%s/.darktablerc", homedir); + FILE *f = fopen(filename, "rb"); + if(f) + { + c = fgets(filename, 512, f); + fclose(f); + } + if(!c) snprintf(filename, 512, "%s/.darktabledb", homedir); + if(sqlite3_open(filename, &(darktable.db))) + { + fprintf(stderr, "[init] could not open database %s!\n", filename); + sqlite3_close(darktable.db); + exit(1); + } + pthread_mutex_init(&(darktable.db_insert), NULL); + + // has to go first for settings needed by all the others. + darktable.control = (dt_control_t *)malloc(sizeof(dt_control_t)); + dt_control_init(darktable.control); + + darktable.mipmap_cache = (dt_mipmap_cache_t *)malloc(sizeof(dt_mipmap_cache_t)); + dt_mipmap_cache_init(darktable.mipmap_cache, 500); + + darktable.image_cache = (dt_image_cache_t *)malloc(sizeof(dt_image_cache_t)); + dt_image_cache_init(darktable.image_cache, 500); + + darktable.library = (dt_library_t *)malloc(sizeof(dt_library_t)); + dt_library_init(darktable.library); + + darktable.develop = (dt_develop_t *)malloc(sizeof(dt_develop_t)); + dt_dev_init(darktable.develop, 1); + + darktable.gui = (dt_gui_gtk_t *)malloc(sizeof(dt_gui_gtk_t)); + dt_gui_gtk_init(darktable.gui, argc, argv); + + dt_control_load_config(darktable.control); + + return 0; +} + +void dt_cleanup() +{ + dt_ctl_gui_mode_t gui; + DT_CTL_GET_GLOBAL(gui, gui); + if(gui == DT_DEVELOP) dt_dev_leave(); + char *homedir = getenv("HOME"); + char filename[512]; + snprintf(filename, 512, "%s/.darktablerc", homedir); + FILE *f = fopen(filename, "wb"); + if(f) + { + if(fputs(darktable.control->global_settings.dbname, f) == EOF) fprintf(stderr, "[cleanup] could not write to %s!\n", filename); + fclose(f); + } + else fprintf(stderr, "[cleanup] could not write to %s!\n", filename); + dt_control_write_config(darktable.control); + + dt_control_cleanup(darktable.control); + free(darktable.control); + dt_dev_cleanup(darktable.develop); + free(darktable.develop); + dt_library_cleanup(darktable.library); + free(darktable.library); + dt_gui_gtk_cleanup(darktable.gui); + free(darktable.gui); + dt_image_cache_cleanup(darktable.image_cache); + free(darktable.image_cache); + dt_mipmap_cache_cleanup(darktable.mipmap_cache); + free(darktable.mipmap_cache); + + sqlite3_close(darktable.db); + pthread_mutex_destroy(&(darktable.db_insert)); + +#ifdef HAVE_MAGICK + MagickCoreTerminus(); +#endif +} + +void dt_print(dt_debug_thread_t thread, const char *msg, ...) +{ + if(darktable.unmuted & thread) + { + va_list ap; + va_start(ap, msg); + vprintf(msg, ap); + va_end(ap); + } +} diff --git a/src/common/darktable.h b/src/common/darktable.h new file mode 100644 index 000000000000..1160e880c167 --- /dev/null +++ b/src/common/darktable.h @@ -0,0 +1,53 @@ +#ifndef DARKROOM_H +#define DARKROOM_H + +#include +#include +#include + +#define DT_VERSION 9 + +#define HANDLE_SQLITE_ERR(rc) \ + if(rc != SQLITE_OK) \ + { \ + fprintf(stderr, "sqlite3 error: %s\n", sqlite3_errmsg(darktable.db)); \ + return 1; \ + } \ + + +struct dt_library_t; +struct dt_gui_gtk_t; +struct dt_control_t; +struct dt_develop_t; +struct dt_mipmap_cache_t; +struct dt_image_cache_t; + +typedef enum dt_debug_thread_t +{ + DT_DEBUG_CACHE = 1, + DT_DEBUG_CONTROL = 2, + DT_DEBUG_DEV = 4 // powers of two, masking +} +dt_debug_thread_t; + +typedef struct darktable_t +{ + int32_t unmuted; + struct dt_library_t *library; + struct dt_develop_t *develop; + struct dt_control_t *control; + struct dt_gui_gtk_t *gui; + struct dt_mipmap_cache_t *mipmap_cache; + struct dt_image_cache_t *image_cache; + sqlite3 *db; + pthread_mutex_t db_insert; +} +darktable_t; + +extern darktable_t darktable; + +int dt_init(int argc, char *argv[]); +void dt_cleanup(); +void dt_print(dt_debug_thread_t thread, const char *msg, ...); + +#endif diff --git a/src/common/image.c b/src/common/image.c new file mode 100644 index 000000000000..f1d11eb3eece --- /dev/null +++ b/src/common/image.c @@ -0,0 +1,517 @@ + +#include "common/darktable.h" +#include "common/image.h" +#include "common/image_cache.h" +#include "common/imageio.h" +#include "control/control.h" +#include "control/jobs.h" +#include +#include +#include +#include +#include +#include + +// how large would the average screen be (largest mip map size) ? +#define DT_IMAGE_WINDOW_SIZE 1200 + +dt_image_buffer_t dt_image_get_matching_mip_size(const dt_image_t *img, const int32_t width, const int32_t height, int32_t *w, int32_t *h) +{ + const float scale = fminf(DT_IMAGE_WINDOW_SIZE/(float)(img->width), DT_IMAGE_WINDOW_SIZE/(float)(img->height)); + int32_t wd = MIN(img->width, (int)(scale*img->width)), ht = MIN(img->height, (int)(scale*img->height)); + if(wd & 0xf) wd = (wd & ~0xf) + 0x10; + if(ht & 0xf) ht = (ht & ~0xf) + 0x10; + dt_image_buffer_t mip = DT_IMAGE_MIP4; + while((int)mip > (int)DT_IMAGE_MIP0 && wd > width && ht > height) + { + mip--; + if(wd > 32 && ht > 32) + { // only if it's not vanishing completely :) + wd >>= 1; + ht >>= 1; + } + } + *w = wd; + *h = ht; + return mip; +} + +void dt_image_get_exact_mip_size(const dt_image_t *img, dt_image_buffer_t mip, float *w, float *h) +{ + float wd = img->width, ht = img->height; + if((int)mip < (int)DT_IMAGE_FULL) + { + const float scale = fminf(1.0, fminf(DT_IMAGE_WINDOW_SIZE/(float)img->width, DT_IMAGE_WINDOW_SIZE/(float)img->height)); + wd *= scale; ht *= scale; + while((int)mip < (int)DT_IMAGE_MIP4) + { + mip++; + if(wd > 32 && ht > 32) + { // only if it's not vanishing completely :) + wd *= .5; + ht *= .5; + } + } + } + *w = wd; + *h = ht; +} + +void dt_image_get_mip_size(const dt_image_t *img, dt_image_buffer_t mip, int32_t *w, int32_t *h) +{ + int32_t wd = img->width, ht = img->height; + if((int)mip < (int)DT_IMAGE_FULL) + { + const float scale = fminf(1.0, fminf(DT_IMAGE_WINDOW_SIZE/(float)img->width, DT_IMAGE_WINDOW_SIZE/(float)img->height)); + wd *= scale; ht *= scale; + // make exact mip possible (almost power of two) + if(wd & 0xf) wd = (wd & ~0xf) + 0x10; + if(ht & 0xf) ht = (ht & ~0xf) + 0x10; + while((int)mip < (int)DT_IMAGE_MIP4) + { + mip++; + if(wd > 32 && ht > 32) + { // only if it's not vanishing completely :) + wd >>= 1; + ht >>= 1; + } + } + } + *w = wd; + *h = ht; +} + +int dt_image_raw_to_preview(dt_image_t *img) +{ + const int raw_wd = img->width >> img->shrink; + const int raw_ht = img->height >> img->shrink; + int p_wd, p_ht; + float f_wd, f_ht; + dt_image_get_mip_size(img, DT_IMAGE_MIPF, &p_wd, &p_ht); + dt_image_get_exact_mip_size(img, DT_IMAGE_MIPF, &f_wd, &f_ht); + + if(dt_image_alloc(img, DT_IMAGE_MIPF)) return 1; + dt_image_check_buffer(img, DT_IMAGE_MIPF, 3*p_wd*p_ht*sizeof(float)); + + if(raw_wd == p_wd && raw_ht == p_ht) + { // use 1:1 + for(int j=0;jpixels + 3*(j*raw_wd + i); + for(int k=0;k<3;k++) img->mipf[3*(j*p_wd + i) + k] = cam[k]; + } + } + else + { // scale to fit + bzero(img->mipf, 3*p_wd*p_ht*sizeof(float)); + const float scale = fmaxf(raw_wd/f_wd, raw_ht/f_ht); + for(int j=0;jpixels + 3*((int)(scale*j)*raw_wd + (int)(scale*i)); + for(int k=0;k<3;k++) img->mipf[3*(j*p_wd + i) + k] = cam[k]; + } + } + // store in db. + dt_imageio_preview_write(img, DT_IMAGE_MIPF); + + if(dt_image_alloc(img, DT_IMAGE_MIP4)) + { + dt_image_release(img, DT_IMAGE_MIPF, 'w'); + dt_image_release(img, DT_IMAGE_MIPF, 'r'); + return 1; + } + dt_image_check_buffer(img, DT_IMAGE_MIP4, 4*p_wd*p_ht*sizeof(uint8_t)); + dt_image_check_buffer(img, DT_IMAGE_MIPF, 3*p_wd*p_ht*sizeof(float)); + int ret = 0; + dt_imageio_preview_f_to_8(p_wd, p_ht, img->mipf, img->mip[DT_IMAGE_MIP4]); + dt_imageio_preview_write(img, DT_IMAGE_MIP4); + if(dt_image_update_mipmaps(img)) ret = 1; + dt_image_release(img, DT_IMAGE_MIPF, 'w'); + dt_image_release(img, DT_IMAGE_MIPF, 'r'); + dt_image_release(img, DT_IMAGE_MIP4, 'w'); + dt_image_release(img, DT_IMAGE_MIP4, 'r'); + return ret; +} + +int dt_image_import(const int32_t film_id, const char *filename) +{ + int rc; + int ret = 0, id = -1; + // select from images; if found => return + gchar *imgfname = g_path_get_basename((const gchar*)filename); + sqlite3_stmt *stmt; + rc = sqlite3_prepare_v2(darktable.db, "select id from images where film_id = ?1 and filename = ?2", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, film_id); + rc = sqlite3_bind_text(stmt, 2, imgfname, strlen(imgfname), SQLITE_STATIC); + if(sqlite3_step(stmt) == SQLITE_ROW) + { + id = sqlite3_column_int(stmt, 0); + g_free(imgfname); + rc = sqlite3_finalize(stmt); + return dt_image_open(id); // image already in db, open this. + } + rc = sqlite3_finalize(stmt); + + // insert dummy image entry in database + rc = sqlite3_prepare_v2(darktable.db, "insert into images (id, film_id, filename) values (null, -1, ?1)", -1, &stmt, NULL); + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_bind_text(stmt, 1, imgfname, strlen(imgfname), SQLITE_STATIC); + pthread_mutex_lock(&(darktable.db_insert)); + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) fprintf(stderr, "sqlite3 error %d\n", rc); + id = sqlite3_last_insert_rowid(darktable.db); + pthread_mutex_unlock(&(darktable.db_insert)); + rc = sqlite3_finalize(stmt); + + // insert dummy (zeroblob) entries in database! + for(dt_image_buffer_t mip=DT_IMAGE_MIP0;(int)mip<=(int)DT_IMAGE_MIPF;mip++) + { + rc = sqlite3_prepare_v2(darktable.db, "insert into mipmaps (imgid, level) values (?1, ?2)", -1, &stmt, NULL); + rc = sqlite3_bind_int(stmt, 1, id); + rc = sqlite3_bind_int(stmt, 2, mip); + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) fprintf(stderr, "[image_import] could not insert mipmap: %s\n", sqlite3_errmsg(darktable.db)); + rc = sqlite3_finalize(stmt); + } + + dt_image_t *img = dt_image_cache_use(id, 'w'); + strncpy(img->filename, imgfname, 256); + g_free(imgfname); + + // load small raw (try libraw then magick) + img->shrink = 1; + if(dt_imageio_open(img, filename)) + { + dt_image_cleanup(img); + fprintf(stderr, "[image_import] could not open %s\n", filename); + dt_image_cache_release(img, 'w'); + rc = sqlite3_prepare_v2(darktable.db, "delete from images where id = ?1", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, id); + rc = sqlite3_step(stmt); + rc = sqlite3_prepare_v2(darktable.db, "delete from mipmaps where imgid = ?1", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, id); + rc = sqlite3_step(stmt); + return 1; + } + + // update image data + rc = sqlite3_prepare_v2(darktable.db, "update images set width = ?1, height = ?2, maker = ?3, model = ?4, exposure = ?5, aperture = ?6, iso = ?7, focal_length = ?8, film_id = ?9, datetime_taken = ?10 where id = ?11", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, img->width); + rc = sqlite3_bind_int (stmt, 2, img->height); + rc = sqlite3_bind_text(stmt, 3, img->exif_maker, strlen(img->exif_maker), SQLITE_STATIC); + rc = sqlite3_bind_text(stmt, 4, img->exif_model, strlen(img->exif_model), SQLITE_STATIC); + rc = sqlite3_bind_double(stmt, 5, img->exif_exposure); + rc = sqlite3_bind_double(stmt, 6, img->exif_aperture); + rc = sqlite3_bind_double(stmt, 7, img->exif_iso); + rc = sqlite3_bind_double(stmt, 8, img->exif_focal_length); + rc = sqlite3_bind_int (stmt, 9, film_id); + rc = sqlite3_bind_text(stmt, 10, img->exif_datetime_taken, strlen(img->exif_datetime_taken), SQLITE_STATIC); + rc = sqlite3_bind_int (stmt, 11, img->id); + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) fprintf(stderr, "sqlite3 error %d\n", rc); + rc = sqlite3_finalize(stmt); + + // create preview images + if(dt_image_raw_to_preview(img)) ret = 2; + dt_image_release(img, DT_IMAGE_FULL, 'r'); + dt_image_cache_release(img, 'w'); + return ret; +} + +int dt_image_update_mipmaps(dt_image_t *img) +{ + int oldwd, oldht; + dt_image_get_mip_size(img, DT_IMAGE_MIP4, &oldwd, &oldht); + // create 8-bit mip maps: + for(dt_image_buffer_t l=DT_IMAGE_MIP3;(int)l>=(int)DT_IMAGE_MIP0;l--) + { + int p_wd, p_ht; + dt_image_get_mip_size(img, l, &p_wd, &p_ht); + if(dt_image_alloc(img, l)) return 1; + + // printf("creating mipmap %d for img %s: %d x %d\n", l, img->filename, p_wd, p_ht); + // downscale 8-bit mip + if(oldwd != p_wd) + for(int j=0;jmip[l][4*(j*p_wd + i) + k] = ((int)img->mip[l+1][8*(2*j)*p_wd + 4*(2*i) + k] + (int)img->mip[l+1][8*(2*j)*p_wd + 4*(2*i+1) + k] + + (int)img->mip[l+1][8*(2*j+1)*p_wd + 4*(2*i+1) + k] + (int)img->mip[l+1][8*(2*j+1)*p_wd + 4*(2*i) + k])/4; + else memcpy(img->mip[l], img->mip[l+1], 4*sizeof(uint8_t)*p_ht*p_wd); + + if(dt_imageio_preview_write(img, l)) + fprintf(stderr, "[update_mipmaps] could write mip level %d of image %s to database!\n", l, img->filename); + dt_image_release(img, l, 'w'); + dt_image_release(img, l+1, 'r'); + } + dt_image_release(img, DT_IMAGE_MIP0, 'r'); + return 0; +} + +void dt_image_init(dt_image_t *img) +{ + for(int k=0;(int)k<(int)DT_IMAGE_MIPF;k++) img->mip[k] = NULL; + bzero(img->lock, sizeof(dt_image_lock_t)*DT_IMAGE_NONE); + img->width = img->height = 0; + img->mipf = NULL; + img->pixels = NULL; + img->orientation = 0; + img->exposure = 0; + img->wb_auto = img->wb_cam = 1; + img->shrink = 0; + img->film_id = -1; + img->flags = 0; + img->id = -1; + img->cacheline = -1; + strncpy(img->exif_model, "unknown", 20); + strncpy(img->exif_maker, "unknown", 20); + strncpy(img->exif_datetime_taken, "0000:00:00 00:00:00", 20); + img->exif_exposure = img->exif_aperture = img->exif_iso = img->exif_focal_length = 0; +#ifdef _DEBUG + for(int k=0;(int)k<(int)DT_IMAGE_NONE;k++) img->mip_buf_size[k] = 0; +#endif +} + +int dt_image_open(const int32_t id) +{ + dt_image_t *img = dt_image_cache_use(id, 'w'); + int rc = dt_image_open2(img, id); + dt_image_cache_release(img, 'w'); + return rc; +} + +int dt_image_open2(dt_image_t *img, const int32_t id) +{ // load stuff from db and store in cache: + int rc, ret = 1; + sqlite3_stmt *stmt; + rc = sqlite3_prepare_v2(darktable.db, "select id, film_id, width, height, filename, maker, model, exposure, aperture, iso, focal_length from images where id = ?1", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, id); + // rc = sqlite3_bind_text(stmt, 2, img->filename, strlen(img->filename), SQLITE_STATIC); + if(sqlite3_step(stmt) == SQLITE_ROW) + { + img->id = sqlite3_column_int(stmt, 0); + img->film_id = sqlite3_column_int(stmt, 1); + img->width = sqlite3_column_int(stmt, 2); + img->height = sqlite3_column_int(stmt, 3); + strncpy(img->filename, (char *)sqlite3_column_text(stmt, 4), 512); + strncpy(img->exif_maker, (char *)sqlite3_column_text(stmt, 5), 20); + strncpy(img->exif_model, (char *)sqlite3_column_text(stmt, 6), 20); + img->exif_exposure = sqlite3_column_double(stmt, 7); + img->exif_aperture = sqlite3_column_double(stmt, 8); + img->exif_iso = sqlite3_column_double(stmt, 9); + img->exif_focal_length = sqlite3_column_double(stmt, 10); + + ret = 0; + } + else fprintf(stderr, "[image_open2] failed to open image from database: %s\n", sqlite3_errmsg(darktable.db)); + rc = sqlite3_finalize(stmt); + if(ret) return ret; + // read mip 0: + rc = dt_imageio_preview_read(img, DT_IMAGE_MIP0); + if(!rc) dt_image_release(img, DT_IMAGE_MIP0, 'r'); + return rc; +} + +void dt_image_cleanup(dt_image_t *img) +{ + pthread_mutex_lock(&(darktable.mipmap_cache->mutex)); + for(int k=0;(int)k<(int)DT_IMAGE_NONE;k++) dt_image_free(img, k); + pthread_mutex_unlock(&(darktable.mipmap_cache->mutex)); +} + +int dt_image_load(dt_image_t *img, dt_image_buffer_t mip) +{ + int ret = 1, rc; + if(mip == DT_IMAGE_FULL) + { + char filename[1024]; + sqlite3_stmt *stmt; + rc = sqlite3_prepare_v2(darktable.db, "select folder from film_rolls where id = ?1", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, img->film_id); + if(sqlite3_step(stmt) == SQLITE_ROW) + snprintf(filename, 1024, "%s/%s", sqlite3_column_text(stmt, 0), img->filename); + rc = sqlite3_finalize(stmt); + ret = dt_imageio_open(img, filename); + if(ret) dt_image_cleanup(img); + } + else ret = dt_imageio_preview_read(img, mip); + // TODO: insert abstract hook here? + dt_control_queue_draw(); + return ret; +} + +void dt_image_prefetch(dt_image_t *img, dt_image_buffer_t mip) +{ + // TODO: alloc buf, dispatch loader job (which will release the rw lock) +} + + +// ============================= +// mipmap cache functions: +// ============================= + +void dt_mipmap_cache_init(dt_mipmap_cache_t *cache, int32_t entries) +{ + pthread_mutex_init(&(cache->mutex), NULL); + for(int k=0;k<(int)DT_IMAGE_NONE;k++) + { + if(k == DT_IMAGE_FULL) entries = 3; + dt_print(DT_DEBUG_CACHE, "mipmap cache has %d entries for mip %d.\n", entries, k); + cache->num_entries[k] = entries; + cache->mip_lru[k] = (dt_image_t **)malloc(sizeof(dt_image_t*)*entries); + bzero(cache->mip_lru[k], sizeof(dt_image_t*)*entries); + if(entries > 4) entries >>= 1; + } +} + +void dt_mipmap_cache_cleanup(dt_mipmap_cache_t *cache) +{ + // TODO: free all img bufs? + for(int k=0;k<(int)DT_IMAGE_NONE;k++) free(cache->mip_lru[k]); + pthread_mutex_destroy(&(cache->mutex)); +} + +void dt_image_check_buffer(dt_image_t *image, dt_image_buffer_t mip, int32_t size) +{ +#ifdef _DEBUG + assert(image->mip_buf_size[mip] >= size); +#endif +} + +int dt_image_alloc(dt_image_t *img, dt_image_buffer_t mip) +{ + int wd, ht; + dt_image_get_mip_size(img, mip, &wd, &ht); + size_t size = wd*ht; + pthread_mutex_lock(&(darktable.mipmap_cache->mutex)); + void *ptr = NULL; + if ((int)mip < (int)DT_IMAGE_MIPF) { size *= 4*sizeof(uint8_t); ptr = (void *)(img->mip[mip]); } + else if(mip == DT_IMAGE_MIPF) { size *= 3*sizeof(float); ptr = (void *)(img->mipf); } + else if(mip == DT_IMAGE_FULL) { size *= 3*sizeof(float); ptr = (void *)(img->pixels); } + else + { + pthread_mutex_unlock(&(darktable.mipmap_cache->mutex)); + return 1; + } + if(ptr) + { + img->lock[mip].write = 1; // write lock + img->lock[mip].users = 1; // read lock + pthread_mutex_unlock(&(darktable.mipmap_cache->mutex)); + return 0; // all good, already alloc'ed. + } + + // printf("allocing %d x %d x %d for %s (%d)\n", wd, ht, size/(wd*ht), img->filename, mip); + if ((int)mip < (int)DT_IMAGE_MIPF) { img->mip[mip] = (uint8_t *)malloc(size); } + else if(mip == DT_IMAGE_MIPF) { img->mipf = (float *)malloc(size); } + else if(mip == DT_IMAGE_FULL) { img->pixels = (float *)malloc(size); } + + if((mip == DT_IMAGE_FULL && img->pixels == NULL) || (mip == DT_IMAGE_MIPF && img->mipf == NULL) || ((int)mip < (int)DT_IMAGE_MIPF && img->mip[mip] == NULL)) + { + fprintf(stderr, "[image_alloc] malloc of %d x %d x %d for image %s mip %d failed!\n", wd, ht, (int)size/(wd*ht), img->filename, mip); + pthread_mutex_unlock(&(darktable.mipmap_cache->mutex)); + return 1; + } + + // insert image in node list at newest time + for(int k=0;knum_entries[mip];k++) + { + if(darktable.mipmap_cache->mip_lru[mip][k] == NULL || darktable.mipmap_cache->mip_lru[mip][k]->lock[mip].users == 0) + { + dt_image_free(darktable.mipmap_cache->mip_lru[mip][k], mip); + memmove(darktable.mipmap_cache->mip_lru[mip] + k, darktable.mipmap_cache->mip_lru[mip] + k + 1, (darktable.mipmap_cache->num_entries[mip] - k - 1)*sizeof(dt_image_t*)); + darktable.mipmap_cache->mip_lru[mip][darktable.mipmap_cache->num_entries[mip]-1] = img; + img->lock[mip].write = 1; // write lock + img->lock[mip].users = 1; // read lock +#ifdef _DEBUG + img->mip_buf_size[mip] = size; +#if 0 + int allsize = 0; + for(int k=0;knum_entries[mip];k++) if(darktable.mipmap_cache->mip_lru[mip][k]) allsize += darktable.mipmap_cache->mip_lru[mip][k]->mip_buf_size[mip]; + printf("[image_alloc] alloc'ed additional %d bytes, now storing %f MB for mip %d\n", size, allsize/(1024.*1024.), mip); +#endif +#endif + pthread_mutex_unlock(&(darktable.mipmap_cache->mutex)); + return 0; + } + } + fprintf(stderr, "[image_alloc] all cache slots seem to be in use! alloc of %d bytes for img id %d mip %d failed!\n", size, img->id, mip); + pthread_mutex_unlock(&(darktable.mipmap_cache->mutex)); + return 1; +} + +void dt_image_free(dt_image_t *img, dt_image_buffer_t mip) +{ + // mutex is already locked, as only alloc is allowed to free. + if(!img) return; + // dt_image_release(img, mip, "w"); + if((int)mip < (int)DT_IMAGE_MIPF) { free(img->mip[mip]); img->mip[mip] = NULL; } + else if(mip == DT_IMAGE_MIPF) { free(img->mipf); img->mipf = NULL; } + else if(mip == DT_IMAGE_FULL) { free(img->pixels); img->pixels = NULL; } + else return; + for(int k=0;knum_entries[mip];k++) + if(darktable.mipmap_cache->mip_lru[mip][k] == img) darktable.mipmap_cache->mip_lru[mip][k] = NULL; +#ifdef _DEBUG + // printf("[image_free] freed %d bytes\n", img->mip_buf_size[mip]); + if(darktable.control->running) + assert(img->lock[mip].users == 0 && img->lock[mip].write == 0); + img->mip_buf_size[mip] = 0; +#endif +} + +dt_image_buffer_t dt_image_get(dt_image_t *img, const dt_image_buffer_t mip_in, const char mode) +{ + dt_image_buffer_t mip = mip_in; + if(mip == DT_IMAGE_NONE) return mip; + pthread_mutex_lock(&(darktable.mipmap_cache->mutex)); + // get image with no write lock set! + if((int)mip < (int)DT_IMAGE_MIPF) + { + while(mip > 0 && (img->mip[mip] == NULL || img->lock[mip].write)) mip--; // level 0 always there. + } + else if(mip == DT_IMAGE_MIPF) + { + if(img->mipf == NULL || img->lock[mip].write) mip = DT_IMAGE_NONE; + } + else if(mip == DT_IMAGE_FULL) + { + if(img->pixels == NULL || img->lock[mip].write) mip = DT_IMAGE_NONE; + } + if(mip != DT_IMAGE_NONE) + { + if(mode == 'w') + { + img->lock[mip].write = 1; + img->lock[mip].users = 1; + } + else img->lock[mip].users++; + } + + if(mip != mip_in && !img->lock[mip_in].write) + { // start job to load this buf in bg. + dt_print(DT_DEBUG_CACHE, "[image_get] reloading mip %d for image %d\n", mip_in, img->id); + img->lock[mip_in].write = 1; + dt_job_t j; + dt_image_load_job_init(&j, img, mip_in); + dt_control_add_job(darktable.control, &j); + if(mip_in == DT_IMAGE_MIP4) + { + if(!img->lock[DT_IMAGE_MIPF].write) + { // start job to load this buf in bg. + img->lock[DT_IMAGE_MIPF].write = 1; + dt_job_t j; + dt_image_load_job_init(&j, img, DT_IMAGE_MIPF); + dt_control_add_job(darktable.control, &j); + } + } + } + pthread_mutex_unlock(&(darktable.mipmap_cache->mutex)); + return mip; +} + +void dt_image_release(dt_image_t *img, dt_image_buffer_t mip, const char mode) +{ + pthread_mutex_lock(&(darktable.mipmap_cache->mutex)); + if (mode == 'r' && img->lock[mip].users > 0) img->lock[mip].users --; + else if (mode == 'w') img->lock[mip].write = 0; // can only be one writing thread at a time. + pthread_mutex_unlock(&(darktable.mipmap_cache->mutex)); +} + diff --git a/src/common/image.h b/src/common/image.h new file mode 100644 index 000000000000..01d629cded92 --- /dev/null +++ b/src/common/image.h @@ -0,0 +1,112 @@ +#ifndef DT_IMAGE_H +#define DT_IMAGE_H + +#include +#include + +typedef enum +{ + DT_IMAGE_PORTRAIT = 1, + DT_IMAGE_GOOD = 2, + DT_IMAGE_BAD = 4, + DT_IMAGE_SELECTED = 8 +} +dt_image_flags_t; + +typedef enum +{ + DT_IMAGE_MIP0 = 0, + DT_IMAGE_MIP1 = 1, + DT_IMAGE_MIP2 = 2, + DT_IMAGE_MIP3 = 3, + DT_IMAGE_MIP4 = 4, + DT_IMAGE_MIPF = 5, + DT_IMAGE_FULL = 6, + DT_IMAGE_NONE = 7 +} +dt_image_buffer_t; + +typedef struct dt_image_lock_t +{ + unsigned write : 1; + unsigned users : 7; +} +dt_image_lock_t; + +typedef struct dt_image_t +{ + // common stuff + int32_t width, height; + char filename[512]; + // used by library + int32_t num, flags, film_id, id; + // cache + int32_t cacheline; // for image_cache +#ifdef _DEBUG + int32_t mip_buf_size[DT_IMAGE_NONE]; +#endif + uint8_t *mip[DT_IMAGE_MIPF]; // for mipmap_cache + float *mipf; + dt_image_lock_t lock[DT_IMAGE_NONE]; + // raw image + int32_t shrink, wb_auto, wb_cam, orientation; + float exposure; + float *pixels; + // libexif data + // ExifContent *exif; + // minimal exif data here: + char exif_maker[20], exif_model[20], exif_datetime_taken[20]; + float exif_exposure; + float exif_aperture; + float exif_iso; + float exif_focal_length; +} +dt_image_t; + +// image buffer operations: +/** inits basic values to sensible defaults. */ +void dt_image_init(dt_image_t *img); +/** opens an image with minimal storage from the data base and stores it in image cache. */ +int dt_image_open(const int32_t id); +int dt_image_open2(dt_image_t *img, const int32_t id); +/** imports a new image from raw/etc file and adds it to the data base and image cache. */ +int dt_image_import(const int32_t film_id, const char *filename); +/** cleanup. */ +void dt_image_cleanup(dt_image_t *img); +/** loads the requested buffer to cache, with read lock set. */ +int dt_image_load(dt_image_t *img, dt_image_buffer_t mip); +/** prefetches given image buffer (mip map level/float preview/full raw), without marking it as used. */ +void dt_image_prefetch(dt_image_t *img, dt_image_buffer_t mip); +/** returns appropriate mip map size for given area to paint on (width, height). */ +dt_image_buffer_t dt_image_get_matching_mip_size(const dt_image_t *img, const int32_t width, const int32_t height, int32_t *w, int32_t *h); +/** returns appropriate mip map size for given mip level. */ +void dt_image_get_mip_size(const dt_image_t *img, dt_image_buffer_t mip, int32_t *w, int32_t *h); +/** returns real image extends within mip map buffer size, in floating point. */ +void dt_image_get_exact_mip_size(const dt_image_t *img, dt_image_buffer_t mip, float *w, float *h); +/** writes mip4 through to all smaller levels. */ +int dt_image_update_mipmaps(dt_image_t *img); + +// memory management interface +typedef struct dt_mipmap_cache_t +{ + pthread_mutex_t mutex; + int32_t num_entries[DT_IMAGE_NONE]; + dt_image_t **mip_lru[DT_IMAGE_NONE]; +} +dt_mipmap_cache_t; + +void dt_mipmap_cache_init(dt_mipmap_cache_t *cache, int32_t entries); +void dt_mipmap_cache_cleanup(dt_mipmap_cache_t *cache); + +/** if in debug mode, asserts image buffer size for mip is alloc'ed this large. */ +void dt_image_check_buffer(dt_image_t *image, dt_image_buffer_t mip, int32_t size); +/** alloc new buffer for this mip map and image. also lock for writing. */ +int dt_image_alloc(dt_image_t *img, dt_image_buffer_t mip); +/** destroy buffer. */ +void dt_image_free(dt_image_t *img, dt_image_buffer_t mip); +/** gets the requested image buffer or a smaller preview if it is not available (w lock || =NULL), marking this with the read lock. returns found mip level. */ +dt_image_buffer_t dt_image_get(dt_image_t *img, const dt_image_buffer_t mip, const char mode); +/** unflags the used flag of given mip map level. these remove r and w locks, respectively. dropping the w lock will leave the r lock in place. */ +void dt_image_release(dt_image_t *img, dt_image_buffer_t mip, const char mode); + +#endif diff --git a/src/common/image_cache.c b/src/common/image_cache.c new file mode 100644 index 000000000000..7d0d716ac409 --- /dev/null +++ b/src/common/image_cache.c @@ -0,0 +1,136 @@ + +#include "common/darktable.h" +#include "common/image_cache.h" + +#include +#include + +void dt_image_cache_init(dt_image_cache_t *cache, int32_t entries) +{ + pthread_mutex_init(&(cache->mutex), NULL); + cache->num_lines = entries; + cache->line = (dt_image_cache_line_t *)malloc(sizeof(dt_image_cache_line_t)*cache->num_lines); + cache->by_id = (int16_t *)malloc(sizeof(int16_t)*cache->num_lines); + for(int k=0;knum_lines;k++) + { + cache->by_id[k] = k; + dt_image_init(&(cache->line[k].image)); + cache->line[k].lock.users = cache->line[k].lock.write = 0; + cache->line[k].image.cacheline = k; + cache->line[k].lru = k-1; + cache->line[k].mru = k+1; + } + cache->lru = 0; + cache->mru = cache->num_lines-1; +} + +void dt_image_cache_cleanup(dt_image_cache_t *cache) +{ + // free mipmap cache lines + for(int k=0;knum_lines;k++) + { + // TODO: update images set !! + dt_image_cleanup(&(cache->line[k].image)); + } + free(cache->line); + free(cache->by_id); + pthread_mutex_destroy(&(cache->mutex)); +} + +int32_t dt_image_cache_bsearch(const int32_t id) +{ + dt_image_cache_t *cache = darktable.image_cache; + unsigned int min = 0, max = cache->num_lines; + unsigned int t = max/2; + while (t != min) + { + if(cache->line[cache->by_id[t-1]].image.id < id) min = t; + else max = t; + t = (min + max)/2; + } + if(cache->line[cache->by_id[t]].image.id != id) return -1; + return cache->by_id[t]; +} + +int dt_image_cache_compare_id(const int16_t *l1, const int16_t *l2) +{ + return darktable.image_cache->line[*l1].image.id - darktable.image_cache->line[*l2].image.id; +} + +dt_image_t *dt_image_cache_get(int32_t id, const char mode) +{ + dt_image_t *img = dt_image_cache_use(id, mode); + if(img == NULL) return NULL; + if(img->film_id == -1) if(dt_image_open2(img, id)) + { + dt_image_cache_release(img, mode); + return NULL; + } + return img; +} + +dt_image_t *dt_image_cache_use(int32_t id, const char mode) +{ + dt_image_cache_t *cache = darktable.image_cache; + pthread_mutex_lock(&(cache->mutex)); + // int16_t *res = bsearch(&id, cache->by_id, cache->num_lines, sizeof(int16_t), (int(*)(const void *, const void *))&dt_image_cache_compare_id); + int32_t res = dt_image_cache_bsearch(id); + dt_image_t *ret = NULL; + int16_t k = cache->lru; + if(res < 0) + { + // get least recently used image without lock and replace it: + for(int i=0;inum_lines;i++) + { + if(cache->line[k].image.id == -1) break; + if(cache->line[k].lock.write == 0 && cache->line[k].lock.users == 0) break; + k = cache->line[k].mru; + } + if(k == cache->num_lines) + { + fprintf(stderr, "[image_cache_use] all slots are in use!\n"); + pthread_mutex_unlock(&(cache->mutex)); + return NULL; + } + // TODO: update images set !! + dt_image_cleanup(&(cache->line[k].image)); + dt_image_init(&(cache->line[k].image)); + cache->line[k].image.id = id; + cache->line[k].image.cacheline = k; + cache->line[k].image.film_id = -1; + // TODO: insertion sort faster here? + qsort(cache->by_id, cache->num_lines, sizeof(int16_t), (int(*)(const void *, const void *))&dt_image_cache_compare_id); + res = k; + } + if(cache->line[res].lock.write) + { + ret = NULL; + } + else + { + // update lock + cache->line[res].lock.users++; + if(mode == 'w') cache->line[res].lock.write = 1; + ret = &(cache->line[res].image); + } + // update least recently used/most recently used linked list: + cache->line[cache->mru].mru = res; + if(cache->line[res].lru >= 0) cache->line[cache->line[res].lru].mru = cache->line[res].mru; + if(cache->line[res].mru < cache->num_lines) cache->line[cache->line[res].mru].lru = cache->line[res].lru; + if(cache->lru == res) cache->lru = cache->line[res].mru; + cache->line[res].mru = cache->num_lines+1; + cache->line[res].lru = cache->mru; + cache->mru = res; + pthread_mutex_unlock(&(cache->mutex)); + return ret; +} + +void dt_image_cache_release(dt_image_t *img, const char mode) +{ + dt_image_cache_t *cache = darktable.image_cache; + pthread_mutex_lock(&(cache->mutex)); + cache->line[img->cacheline].lock.users--; + if(mode == 'w') cache->line[img->cacheline].lock.write = 0; + pthread_mutex_unlock(&(cache->mutex)); +} + diff --git a/src/common/image_cache.h b/src/common/image_cache.h new file mode 100644 index 000000000000..e0bcdf053e82 --- /dev/null +++ b/src/common/image_cache.h @@ -0,0 +1,48 @@ +#ifndef DT_IMAGE_CACHE_H +#define DT_IMAGE_CACHE_H + +#include "common/image.h" + +#include +#include + +/** + * image cache to hold temporary representations + * from sql queries. + * fast access by img->id via sorted index, + * which is updated each time a new image is alloc'ed or + * an old image is kicked. + * lru list maintained via linked list for fast updates. + */ + +typedef struct dt_image_cache_line_t +{ + dt_image_t image; + dt_image_lock_t lock; + int16_t mru, lru; +} +dt_image_cache_line_t; + +typedef struct dt_image_cache_t +{ + pthread_mutex_t mutex; + int32_t num_lines; + dt_image_cache_line_t *line; + int16_t *by_id; + int16_t lru, mru; +} +dt_image_cache_t; + +void dt_image_cache_init(dt_image_cache_t *cache, int32_t entries); +void dt_image_cache_cleanup(dt_image_cache_t *cache); + +/** returns alloc'ed image (newly or from cache) or NULL on failure. + * lru is freed instead. there is no explicit interface for free. + * result will have users lock incremented. */ +dt_image_t *dt_image_cache_use(int32_t id, const char mode); +/** same as use, but init image from db if it was not already loaded. */ +dt_image_t *dt_image_cache_get(int32_t id, const char mode); +/** decrements users lock. */ +void dt_image_cache_release(dt_image_t *img, const char mode); + +#endif diff --git a/src/common/image_compression.c b/src/common/image_compression.c new file mode 100644 index 000000000000..5326f1a8c5b5 --- /dev/null +++ b/src/common/image_compression.c @@ -0,0 +1,130 @@ +#include "common/image_compression.h" + +#include +#include +#include +#include + +void dt_image_uncompress(const uint8_t *in, float *out, const int32_t width, const int32_t height) +{ + float L[16], chrom[4][3]; + const float fac[3] = {4., 2., 4.}; + uint32_t *L32 = (uint32_t*)L; + uint16_t L16[16]; + int32_t n_zeroes, Lbias; + uint8_t r[4], b[4]; + const uint8_t *block = in; + for(int j=0;j> 3) << 10; + n_zeroes = block[0] & 0x7; + const int shift = 14-n_zeroes-4+1; + + for(int k=0;k<8;k++) + { + L16[2*k ] = ((int)(block[1+k]>> 4) << shift) + Lbias; + L16[2*k+1] = ((int)(block[1+k]&0xf) << shift) + Lbias; + } + for(int k=0;k<16;k++) + { + L32[k] = (((int)(L16[k]) >> 10)-(15-127)) << (23); + L32[k] |= (L16[k] & 0x3ff)<<13; + } + // chroma + r[0] = block[ 9] >> 1; + b[0] = ((block[ 9] & 0x01) << 6) | (block[10] >> 2); + r[1] = ((block[10] & 0x03) << 5) | (block[11] >> 3); + b[1] = ((block[11] & 0x07) << 4) | (block[12] >> 4); + r[2] = ((block[12] & 0x0f) << 3) | (block[13] >> 5); + b[2] = ((block[13] & 0x1f) << 2) | (block[14] >> 6); + r[3] = ((block[14] & 0x3f) << 1) | (block[15] >> 7); + b[3] = block[15] & 0x7f; + + for(int q=0;q<4;q++) + { + chrom[q][0] = r[q]*(1./127.); + chrom[q][2] = b[q]*(1./127.); + chrom[q][1] = 1. - chrom[q][0] - chrom[q][2]; + } + for(int k=0;k<16;k++) + for(int c=0;c<3;c++) + out[3*(i + (k & 3) + width*(j + (k>>2))) + c] = L[k]*fac[c]*chrom[((k>>3)<<1)|((k&3)>>1)][c]; + block += 16*sizeof(uint8_t); + } + } +} + +void dt_image_compress(const float *in, uint8_t *out, const int32_t width, const int32_t height) +{ + float L[16]; + int16_t Lmin, Lmax, n_zeroes, L16[16]; + int32_t L32; + uint8_t *block = out, r[4], b[4]; + for(int j=0;j>13)&0x3ff; + int e = ((L32 >> (23))-(127-15)); + e = e > 0 ? e : 0; + e = e > 30 ? 30 : e; + L16[io+4*jo] |= e<<10; + Lmin = Lmin < L16[io+4*jo] ? Lmin : L16[io+4*jo]; + } + } + const float norm = 1./(chrom[0] + 2*chrom[1] + chrom[2]); + r[q] = (int)(127.*(chrom[0]*norm)); + b[q] = (int)(127.*(chrom[2]*norm)); + } + // store luma + Lmin &= ~0x3ff; + block[0] = (Lmin>>10)<<3; // Lbias + Lmax = 0; + for(int k=0;k<16;k++) + { + L16[k] -= Lmin; + Lmax = Lmax > L16[k] ? Lmax : L16[k]; + } + n_zeroes = 0; + for(int k=1<<14;(k&Lmax)==0&&n_zeroes<7;k>>=1) n_zeroes++; + block[0] |= n_zeroes; + const int shift = 14-n_zeroes-4+1; + const int off = (1<>1; + for(int k=0;k<8;k++) + { + L16[2*k] = ((int)L16[2*k] + off)>>shift; + L16[2*k] = L16[2*k] > 0xf ? 0xf : L16[2*k]; + L16[2*k+1] = ((int)L16[2*k+1] + off)>>shift; + L16[2*k+1] = L16[2*k+1] > 0xf ? 0xf : L16[2*k+1]; + block[k+1] = L16[2*k+1] | (L16[2*k]<<4); + } + // store chroma + block[ 9] = (r[0] << 1) | (b[0] >> 6); + block[10] = (b[0] << 2) | (r[1] >> 5); + block[11] = (r[1] << 3) | (b[1] >> 4); + block[12] = (b[1] << 4) | (r[2] >> 3); + block[13] = (r[2] << 5) | (b[2] >> 2); + block[14] = (b[2] << 6) | (r[3] >> 1); + block[15] = (r[3] << 7) | (b[3] >> 0); + block += 16*sizeof(uint8_t); + } + } +} + diff --git a/src/common/image_compression.h b/src/common/image_compression.h new file mode 100644 index 000000000000..e6613a806cfa --- /dev/null +++ b/src/common/image_compression.h @@ -0,0 +1,8 @@ +#ifndef DT_IMAGE_COMPRESSION +#include + +/** K. Roimela, T. Aarnio and J. Itäranta. High Dynamic Range Texture Compression. Proceedings of SIGGRAPH 2006. */ +void dt_image_compress(const float *in, uint8_t *out, const int32_t width, const int32_t height); +void dt_image_uncompress(const uint8_t *in, float *out, const int32_t width, const int32_t height); + +#endif diff --git a/src/common/imageio.c b/src/common/imageio.c new file mode 100644 index 000000000000..a4ae731f2edc --- /dev/null +++ b/src/common/imageio.c @@ -0,0 +1,629 @@ +#ifdef HAVE_CONFIG_H + #include "../config.h" +#endif +#include "common/imageio.h" +#include "common/image_compression.h" +#include "common/darktable.h" +#include "library/library.h" +#include "control/control.h" +#include "develop/develop.h" +#include "develop/imageop.h" +#include "libraw/libraw.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_MAGICK + #include +#endif + +int dt_imageio_preview_write(dt_image_t *img, dt_image_buffer_t mip) +{ + if(mip == DT_IMAGE_NONE || mip == DT_IMAGE_FULL) return 1; + if(mip == DT_IMAGE_MIPF) + { + sqlite3_stmt *stmt; + int rc, wd, ht; + dt_image_get_mip_size(img, DT_IMAGE_MIPF, &wd, &ht); + dt_image_check_buffer(img, mip, 3*wd*ht*sizeof(float)); + dt_image_alloc(img, DT_IMAGE_MIP4); + dt_image_check_buffer(img, DT_IMAGE_MIP4, 4*wd*ht*sizeof(uint8_t)); + dt_image_compress(img->mipf, img->mip[DT_IMAGE_MIP4], wd, ht); + rc = sqlite3_prepare_v2(darktable.db, "update mipmaps set data = ?1 where imgid = ?2 and level = ?3", -1, &stmt, NULL); + rc = sqlite3_bind_blob(stmt, 1, img->mip[DT_IMAGE_MIP4], sizeof(uint8_t)*wd*ht, SQLITE_STATIC); + rc = sqlite3_bind_int (stmt, 2, img->id); + rc = sqlite3_bind_int (stmt, 3, mip); + rc = sqlite3_step(stmt); + rc = sqlite3_finalize(stmt); + dt_image_release(img, DT_IMAGE_MIP4, 'w'); + return 0; + } + + sqlite3_stmt *stmt; + int rc; + int wd, ht; + dt_image_get_mip_size(img, mip, &wd, &ht); + dt_image_check_buffer(img, mip, 4*wd*ht*sizeof(uint8_t)); +#ifdef HAVE_MAGICK + ExceptionInfo *exception = AcquireExceptionInfo(); + ImageInfo *image_info = CloneImageInfo((ImageInfo *) NULL); + Image *image = ConstituteImage(wd, ht, "RGBA", CharPixel, img->mip[mip], exception); + if (image == (Image *) NULL) + { + image_info = DestroyImageInfo(image_info); + exception = DestroyExceptionInfo(exception); + return 1; + } + (void)strncpy(image_info->magick, "jpeg", 4); + image_info->quality = 95; + size_t length; + uint8_t *blob = ImageToBlob(image_info, image, &length, exception); + rc = sqlite3_prepare_v2(darktable.db, "update mipmaps set data = ?1 where imgid = ?2 and level = ?3", -1, &stmt, NULL); + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_bind_blob(stmt, 1, blob, sizeof(uint8_t)*length, (void (*)(void *))RelinquishMagickMemory); +#else + uint8_t *blob = img->mip[mip]; + size_t length = 4*wd*ht*sizeof(uint8_t); + rc = sqlite3_prepare_v2(darktable.db, "update mipmaps set data = ?1 where imgid = ?2 and level = ?3", -1, &stmt, NULL); + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_bind_blob(stmt, 1, blob, sizeof(uint8_t)*length, SQLITE_STATIC); +#endif + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_bind_int (stmt, 2, img->id); + rc = sqlite3_bind_int (stmt, 3, mip); + rc = sqlite3_step(stmt); + if(rc != SQLITE_DONE) fprintf(stderr, "[preview_write] update mipmap failed: %s\n", sqlite3_errmsg(darktable.db)); + rc = sqlite3_finalize(stmt); + +#ifdef HAVE_MAGICK + image = DestroyImage(image); + image_info = DestroyImageInfo(image_info); + exception = DestroyExceptionInfo(exception); +#endif + return 0; +} + +int dt_imageio_preview_read(dt_image_t *img, dt_image_buffer_t mip) +{ + if(mip == DT_IMAGE_NONE || mip == DT_IMAGE_FULL) return 1; + if(img->mip[mip]) + { + dt_image_buffer_t mip2 = dt_image_get(img, mip, 'r'); + if(mip2 != mip) dt_image_release(img, mip2, 'r'); + else return 0; + } + sqlite3_stmt *stmt; + int rc, wd, ht; + size_t length = 0; + const void *blob = NULL; + dt_image_get_mip_size(img, mip, &wd, &ht); + rc = sqlite3_prepare_v2(darktable.db, "select data from mipmaps where imgid = ?1 and level = ?2", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, img->id); + rc = sqlite3_bind_int (stmt, 2, mip); + HANDLE_SQLITE_ERR(rc); + if(sqlite3_step(stmt) == SQLITE_ROW) + { + blob = sqlite3_column_blob(stmt, 0); + length = sqlite3_column_bytes(stmt, 0); + } + if(!blob) + { + fprintf(stderr, "[preview_read] could not get mipmap from database: %s\n", sqlite3_errmsg(darktable.db)); + return 1; + } + if(dt_image_alloc(img, mip)) + { + rc = sqlite3_finalize(stmt); + return 1; + } + + if(mip == DT_IMAGE_MIPF) + { + assert(length==sizeof(uint8_t)*wd*ht); + dt_image_check_buffer(img, mip, 3*wd*ht*sizeof(float)); + dt_image_uncompress((uint8_t *)blob, img->mipf, wd, ht); + } + else + { + dt_image_check_buffer(img, mip, 4*wd*ht*sizeof(uint8_t)); +#ifdef HAVE_MAGICK + ExceptionInfo *exception = AcquireExceptionInfo(); + ImageInfo *image_info = CloneImageInfo((ImageInfo *) NULL); + Image *image = BlobToImage(image_info, blob, length, exception); + if (image == (Image *) NULL) + { + CatchException(exception); + image_info = DestroyImageInfo(image_info); + exception = DestroyExceptionInfo(exception); + rc = sqlite3_finalize(stmt); + dt_image_release(img, mip, 'w'); + dt_image_release(img, mip, 'r'); + fprintf(stderr, "[preview_read] could not get image from blob!\n"); + return 1; + } + const PixelPacket *p; + assert(image->rows == ht && image->columns == wd); + for (int y=0; y < image->rows; y++) + { + p = AcquireImagePixels(image,0,y,image->columns,1,exception); + if (p == (const PixelPacket *) NULL) + { + dt_image_release(img, mip, 'w'); + dt_image_release(img, mip, 'r'); + fprintf(stderr, "[preview_read] pixel read failed!\n"); + return 1; + } + for (int x=0; x < image->columns; x++) + { + if(QuantumDepth == 16) + { + img->mip[mip][4*wd*y + 4*x + 0] = (int)p->red>>8; + img->mip[mip][4*wd*y + 4*x + 1] = (int)p->green>>8; + img->mip[mip][4*wd*y + 4*x + 2] = (int)p->blue>>8; + } + else + { + img->mip[mip][4*wd*y + 4*x + 0] = (int)p->red; + img->mip[mip][4*wd*y + 4*x + 1] = (int)p->green; + img->mip[mip][4*wd*y + 4*x + 2] = (int)p->blue; + } + p++; + } + } + image = DestroyImage(image); + image_info = DestroyImageInfo(image_info); + exception = DestroyExceptionInfo(exception); +#else + assert(length==sizeof(uint8_t)*4*wd*ht); + for(int k=0;kmip[mip][k] = ((uint8_t *)blob)[k]; +#endif + } + rc = sqlite3_finalize(stmt); + dt_image_release(img, mip, 'w'); + return 0; +} + +void dt_imageio_preview_f_to_8(int32_t p_wd, int32_t p_ht, const float *f, uint8_t *p8) +{ + for(int idx=0;idx < p_wd*p_ht; idx++) + for(int k=0;k<3;k++) p8[4*idx+2-k] = dt_dev_default_gamma[(int)CLAMP(0xffff*f[3*idx+k], 0, 0xffff)]; +} + + +// ================================================= +// begin libraw wrapper functions: +// ================================================= + +#define HANDLE_ERRORS(ret, verb) { \ + if(ret) \ + { \ + if(verb) fprintf(stderr,"[imageio] %s: %s\n", filename, libraw_strerror(ret)); \ + libraw_close(raw); \ + raw = NULL; \ + return -1; \ + } \ +} + +int dt_imageio_open_raw(dt_image_t *img, const char *filename) +{ + int ret; + libraw_data_t *raw = libraw_init(0); + libraw_processed_image_t *image; + raw->params.half_size = img->shrink; /* dcraw -h */ + raw->params.use_camera_wb = img->wb_cam; + raw->params.use_auto_wb = img->wb_auto; + raw->params.output_bps = 16; + raw->params.user_qual = 3; + // img->raw->params.output_color = 1; + raw->params.use_camera_matrix = 1; + // TODO: let this unclipped for develop, clip for preview. + raw->params.highlight = 0; //0 clip, 1 unclip, 2 blend, 3+ rebuild + // img->raw->params.user_flip = img->raw->sizes.flip; + ret = libraw_open_file(raw, filename); + HANDLE_ERRORS(ret, 0); + if(raw->idata.dng_version || (raw->sizes.iwidth <= 1200 && raw->sizes.iheight <= 800)) + { // FIXME: this is a temporary bugfix avoiding segfaults for dng images. (and to avoid shrinking on small images). + raw->params.user_qual = 0; + raw->params.half_size = img->shrink = 0; + } + ret = libraw_unpack(raw); + HANDLE_ERRORS(ret, 1); + ret = libraw_dcraw_process(raw); + HANDLE_ERRORS(ret, 1); + image = dcraw_make_mem_image(raw, &ret); + HANDLE_ERRORS(ret, 1); + + img->shrink = raw->params.half_size; + img->orientation = raw->sizes.flip; + img->width = (img->orientation & 4) ? raw->sizes.iheight : raw->sizes.iwidth; + img->height = (img->orientation & 4) ? raw->sizes.iwidth : raw->sizes.iheight; + img->exif_iso = raw->other.iso_speed; + img->exif_exposure = raw->other.shutter; + img->exif_aperture = raw->other.aperture; + img->exif_focal_length = raw->other.focal_len; + strncpy(img->exif_maker, raw->idata.make, 20); + strncpy(img->exif_model, raw->idata.model, 20); + GDate *gd = g_date_new(); + g_date_set_time_t(gd, raw->other.timestamp); + (void) g_date_strftime(img->exif_datetime_taken, 20, "%Y:%m:%d %H:%M:%S", gd); + g_date_free(gd); + + img->width <<= img->shrink; + img->height <<= img->shrink; + if(dt_image_alloc(img, DT_IMAGE_FULL)) + { + libraw_recycle(raw); + libraw_close(raw); + free(image); + return 1; + } + dt_image_check_buffer(img, DT_IMAGE_FULL, 3*(img->width>>img->shrink)*(img->height>>img->shrink)*sizeof(float)); + // img->pixels = (float *)malloc(sizeof(float)*3*img->width*img->height); + const float m = 1./0xffff; + for(int k=0;k<3*(img->width>>img->shrink)*(img->height>>img->shrink);k++) img->pixels[k] = ((uint16_t *)(image->data))[k]*m; + // TODO: wrap all exif data here: + // img->dreggn = + // clean up raw stuff. + libraw_recycle(raw); + libraw_close(raw); + free(image); + raw = NULL; + image = NULL; + dt_image_release(img, DT_IMAGE_FULL, 'w'); + return 0; +} + +#if 0 +static void MaxMidMin(int64_t p[3], int *maxc, int *midc, int *minc) +{ + if (p[0] > p[1] && p[0] > p[2]) { + *maxc = 0; + if (p[1] > p[2]) { *midc = 1; *minc = 2; } + else { *midc = 2; *minc = 1; } + } else if (p[1] > p[2]) { + *maxc = 1; + if (p[0] > p[2]) { *midc = 0; *minc = 2; } + else { *midc = 2; *minc = 0; } + } else { + *maxc = 2; + if (p[0] > p[1]) { *midc = 0; *minc = 1; } + else { *midc = 1; *minc = 0; } + } +} + +// this is stolen from develop_linear (ufraw_developer.c), because it does a great job ;) +void dt_raw_develop(uint16_t *in, uint16_t *out, dt_image_t *img) +{ + int64_t tmppix[4];//, tmp; + int64_t exposure = pow(2, img->exposure) * 0x10000; + int clipped = 0; + for(int c=0;craw->idata.colors;c++) + { + tmppix[c] = (uint64_t)in[c] * img->raw->color.cam_mul[c]/0x10000 - img->raw->color.black; + if(tmppix[c] > img->raw->color.maximum) clipped = 1; + tmppix[c] = tmppix[c] * exposure / img->raw->color.maximum; + // tmppix[c] = (tmppix[c] * 0x10000) / img->raw.color.maximum; + } + if(clipped) + { + int64_t unclipped[3], clipped[3]; + for(int cc=0;cc<3;cc++) + { + // for(int c=0,tmp=0;craw->idata.colors;c++) + // tmp += tmppix[c] * img->raw->color.cmatrix[cc][c]; + // unclipped[cc] = MAX(tmp/0x10000, 0); + unclipped[cc] = tmppix[cc]; + } + for(int c=0; c<3; c++) tmppix[c] = MIN(tmppix[c], 0xFFFF); + // for(int c=0; c<3; c++) tmppix[c] = MIN(tmppix[c], exposure); + for(int cc=0; cc<3; cc++) + { + // for(int c=0, tmp=0; craw->idata.colors; c++) + // tmp += tmppix[c] * img->raw->color.cmatrix[cc][c]; + // clipped[cc] = MAX(tmp/0x10000, 0); + clipped[cc] = tmppix[cc]; + } + int maxc, midc, minc; + MaxMidMin(unclipped, &maxc, &midc, &minc); + int64_t unclippedLum = unclipped[maxc]; + int64_t clippedLum = clipped[maxc]; + int64_t clippedSat; + if(clipped[maxc] < clipped[minc] || clipped[maxc] == 0) + clippedSat = 0; + else + clippedSat = 0x10000 - clipped[minc] * 0x10000 / clipped[maxc]; + int64_t clippedHue; + if(clipped[maxc] == clipped[minc]) clippedHue = 0; + else clippedHue = + (clipped[midc]-clipped[minc])*0x10000 / + (clipped[maxc]-clipped[minc]); + int64_t unclippedHue; + if(unclipped[maxc] == unclipped[minc]) + unclippedHue = clippedHue; + else + unclippedHue = + (unclipped[midc]-unclipped[minc])*0x10000 / + (unclipped[maxc]-unclipped[minc]); + int64_t lum = clippedLum + (unclippedLum - clippedLum) * 1/2; + int64_t sat = clippedSat; + int64_t hue = unclippedHue; + + tmppix[maxc] = lum; + tmppix[minc] = lum * (0x10000-sat) / 0x10000; + tmppix[midc] = lum * (0x10000-sat + sat*hue/0x10000) / 0x10000; + } + for(int c=0; c<3; c++) + out[c] = MIN(MAX(tmppix[c], 0), 0xFFFF); +} +#endif + +// ================================================= +// begin magickcore wrapper functions: +// ================================================= + +// transparent read method to load ldr/raw image to dt_raw_image_t with exif and so on. +int dt_imageio_open_ldr(dt_image_t *img, const char *filename) +{ +#ifdef HAVE_MAGICK + // TODO: shrink!! + // TODO: orientation!! + img->shrink = 0; + img->orientation = 0; + + ImageInfo *image_info; + ExceptionInfo *exception; + Image *image; + exception = AcquireExceptionInfo(); + image_info = CloneImageInfo((ImageInfo *) NULL); + + (void) strcpy(image_info->filename, filename); + image = ReadImage(image_info, exception); + if (image == (Image *) NULL) + { + image_info = DestroyImageInfo(image_info); + exception = DestroyExceptionInfo(exception); + return 1; + } + + img->width = image->columns; + img->height = image->rows; + if(dt_image_alloc(img, DT_IMAGE_FULL)) + { + image = DestroyImage(image); + image_info = DestroyImageInfo(image_info); + exception = DestroyExceptionInfo(exception); + return 2; + } + // img->pixels = (float *)malloc(sizeof(float)*3*img->width*img->height); + + dt_image_check_buffer(img, DT_IMAGE_FULL, 3*img->width*img->height*sizeof(uint8_t)); + const PixelPacket *p; + for (int y=0; y < image->rows; y++) + { + p = AcquireImagePixels(image,0,y,image->columns,1,exception); + if (p == (const PixelPacket *) NULL) + { + image = DestroyImage(image); + image_info = DestroyImageInfo(image_info); + exception = DestroyExceptionInfo(exception); + dt_image_release(img, DT_IMAGE_FULL, 'w'); + dt_image_release(img, DT_IMAGE_FULL, 'r'); + return 1; + } + for (int x=0; x < image->columns; x++) + { + if(QuantumDepth == 16) + { + img->pixels[3*img->width*y + 3*x + 0] = dt_dev_de_gamma[(int)p->red>>8]; + img->pixels[3*img->width*y + 3*x + 1] = dt_dev_de_gamma[(int)p->green>>8]; + img->pixels[3*img->width*y + 3*x + 2] = dt_dev_de_gamma[(int)p->blue>>8]; + } + else + { + img->pixels[3*img->width*y + 3*x + 0] = dt_dev_de_gamma[(int)p->red]; + img->pixels[3*img->width*y + 3*x + 1] = dt_dev_de_gamma[(int)p->green]; + img->pixels[3*img->width*y + 3*x + 2] = dt_dev_de_gamma[(int)p->blue]; + } + // p->opacity + p++; + } + } + + // exif:Flash: 9 + // exif:FlashPixVersion: 0100 + const char *value = NULL; + float num, den; + value = GetImageProperty(image, "exif:DateTimeOriginal"); + if (value != (const char *) NULL) strncpy(img->exif_datetime_taken, value, 20); + value = GetImageProperty(image, "exif:ExposureTime"); + if (value != (const char *) NULL) { num = g_ascii_strtod(value, (gchar**)&value); den = g_ascii_strtod(value+1, NULL); img->exif_exposure = num/den; } + value = GetImageProperty(image, "exif:FNumber"); + if (value != (const char *) NULL) { num = g_ascii_strtod(value, (gchar**)&value); den = g_ascii_strtod(value+1, NULL); img->exif_aperture = num/den; + printf("aperture for image %s is %f/%f = %f\n", img->filename, num, den, num/den);} + value = GetImageProperty(image, "exif:FocalLength"); + if (value != (const char *) NULL) { num = g_ascii_strtod(value, (gchar**)&value); den = g_ascii_strtod(value+1, NULL); img->exif_focal_length = num/den; } + value = GetImageProperty(image, "exif:ISOSpeedRatings"); + if (value != (const char *) NULL) img->exif_iso = g_ascii_strtod(value, NULL); + value = GetImageProperty(image, "exif:Make"); + if (value != (const char *) NULL) strncpy(img->exif_maker, value, 20); + value = GetImageProperty(image, "exif:Model"); + if (value != (const char *) NULL) strncpy(img->exif_model, value, 20); + // TODO: + // exif:Orientation: 1 + // http://sylvana.net/jpegcrop/exif_orientation.html + value = GetImageProperty(image, "exif:Orientation"); + if (value != (const char *) NULL) img->orientation = atol(value); + +#if 0 + (void) GetImageProperty(image,"exif:*"); + ResetImagePropertyIterator(image); + char *property=GetNextImageProperty(image); + if (property != (const char *) NULL) + { + (void) printf(" Properties:\n"); + while (property != (const char *) NULL) + { + (void) printf(" %c",*property); + if (strlen(property) > 1) + (void) printf("%s: ",property+1); + if (strlen(property) > 80) + (void) printf("\n"); + const char *value=GetImageProperty(image,property); + if (value != (const char *) NULL) + (void) printf("%s\n",value); + property=GetNextImageProperty(image); + } + } +#endif + image = DestroyImage(image); + image_info = DestroyImageInfo(image_info); + exception = DestroyExceptionInfo(exception); + dt_image_release(img, DT_IMAGE_FULL, 'w'); + return 0; +#else + fprintf(stderr, "[open_ldr] compiled without Magick support!\n"); + return 1; +#endif +} + +// batch-processing enabled write method: history stack, raw image, custom copy of gamma/tonecurves +int dt_imageio_export_f(dt_image_t *img, const char *filename) +{ + dt_develop_t dev; + dt_dev_init(&dev, 0); + dt_dev_load_image(&dev, img); + // go through stack, exec stuff (like dt_dev_load_small_cache, only operate on full buf this time) + const int wd = dev.image->width; + const int ht = dev.image->height; + for(int k=1;kpixels, dev.image->pixels, wd, ht, wd, ht, hist->operation, &(hist->op_params)); + } + + dt_image_check_buffer(dev.image, DT_IMAGE_FULL, 3*wd*ht*sizeof(float)); + int status = 1; + FILE *f = fopen(filename, "wb"); + if(f) + { + (void)fprintf(f, "PF\n%d %d\n-1.0\n", wd, ht); + float tmp[3]; + for(int i=0;ipixels[3*i+k], 0, 0xffff)]/(float)0x10000; + (void)fwrite(tmp, sizeof(float)*3, 1, f); + } + fclose(f); + status = 0; + } + + dt_dev_cleanup(&dev); + return status; +} + +int dt_imageio_export_16(dt_image_t *img, const char *filename) +{ + dt_develop_t dev; + dt_dev_init(&dev, 0); + dt_dev_load_image(&dev, img); + // go through stack, exec stuff (like dt_dev_load_small_cache, only operate on full buf this time) + const int wd = dev.image->width; + const int ht = dev.image->height; + dt_image_check_buffer(dev.image, DT_IMAGE_FULL, 3*wd*ht*sizeof(float)); + uint16_t *buf16 = (uint16_t *)malloc(sizeof(uint16_t)*3*wd*ht); + for(int k=1;kpixels, dev.image->pixels, wd, ht, wd, ht, hist->operation, &(hist->op_params)); + } + for(int i=0;ipixels[3*i+k], 0, 0xffff)]; + buf16[3*i+k] = (0xff00 & (buf16[3*i+k]<<8))|(buf16[3*i+k]>>8); + } + + int status = 1; + FILE *f = fopen(filename, "wb"); + if(f) + { + (void)fprintf(f, "P6\n%d %d\n65535\n", wd, ht); + (void)fwrite(buf16, sizeof(uint16_t)*3, wd*ht, f); + fclose(f); + status = 0; + } + + dt_dev_cleanup(&dev); + free(buf16); + return status; +} + +int dt_imageio_export_8(dt_image_t *img, const char *filename) +{ +#ifdef HAVE_MAGICK + dt_develop_t dev; + dt_dev_init(&dev, 0); + dt_dev_load_image(&dev, img); + // go through stack, exec stuff (like dt_dev_load_small_cache, only operate on full buf this time) + const int wd = dev.image->width; + const int ht = dev.image->height; + dt_image_check_buffer(dev.image, DT_IMAGE_FULL, 3*wd*ht*sizeof(float)); + uint8_t *buf8 = (uint8_t *)malloc(sizeof(uint8_t)*3*wd*ht); + for(int k=1;kpixels, dev.image->pixels, wd, ht, wd, ht, hist->operation, &(hist->op_params)); + } + for(int i=0;ipixels[3*i+k], 0, 0xffff)]]; + + // MagickCore write + ImageInfo *image_info; + ExceptionInfo *exception; + Image *image; + exception = AcquireExceptionInfo(); + image_info = CloneImageInfo((ImageInfo *) NULL); + image = ConstituteImage(wd, ht, "RGB", CharPixel, buf8, exception); + if (image == (Image *) NULL) + { + image_info = DestroyImageInfo(image_info); + exception = DestroyExceptionInfo(exception); + dt_dev_cleanup(&dev); + free(buf8); + return 1; + } + // TODO: MagickCore set image properties! + // MagickBooleanType DefineImageProperty(Image *image, const char *property) + // MagickBooleanType SetImageProperty(Image *image,const char *property, const char *value) + + image_info->quality = 97; + (void) strcpy(image->filename, filename); + WriteImage(image_info, image); + + image = DestroyImage(image); + image_info = DestroyImageInfo(image_info); + exception = DestroyExceptionInfo(exception); + free(buf8); + dt_dev_cleanup(&dev); + return 0; +#else + fprintf(stderr, "[export_8] compiled without Magick support!\n"); + return 1; +#endif +} + +// ================================================= +// combined reading +// ================================================= + +int dt_imageio_open(dt_image_t *img, const char *filename) +{ // first try raw loading + if(!dt_imageio_open_raw(img, filename)) return 0; + if(!dt_imageio_open_ldr(img, filename)) return 0; + return 1; +} + diff --git a/src/common/imageio.h b/src/common/imageio.h new file mode 100644 index 000000000000..ba7de199acc4 --- /dev/null +++ b/src/common/imageio.h @@ -0,0 +1,28 @@ +#ifndef DT_IMAGE_IO_H +#define DT_IMAGE_IO_H + +#include +#include +#include "common/image.h" + +#include + +// opens the file using libraw, doing interpolation and stuff +int dt_imageio_open_raw(dt_image_t *img, const char *filename); +// opens file using imagemagick +int dt_imageio_open_ldr(dt_image_t *img, const char *filename); +// try both, first libraw. +int dt_imageio_open(dt_image_t *img, const char *filename); + +// write cache to database, returns 0 on success. assumes buf to be locked. +int dt_imageio_preview_write(dt_image_t *img, dt_image_buffer_t mip); +// read database to cache, returns 0 on success. leaves 'w' locked buf. +int dt_imageio_preview_read(dt_image_t *img, dt_image_buffer_t mip); + +int dt_imageio_export_8 (dt_image_t *img, const char *filename); +int dt_imageio_export_16(dt_image_t *img, const char *filename); +int dt_imageio_export_f (dt_image_t *img, const char *filename); + +void dt_imageio_preview_f_to_8(int32_t wd, int32_t ht, const float *f, uint8_t *p8); + +#endif diff --git a/src/common/nikon_curve.c b/src/common/nikon_curve.c new file mode 100644 index 000000000000..127ce67194af --- /dev/null +++ b/src/common/nikon_curve.c @@ -0,0 +1,2183 @@ +/*************************************************** + nikon_curve.c - read Nikon NTC/NCV files + + Copyright 2004-2008 by Shawn Freeman, Udi Fuchs + + This program reads in a Nikon NTC/NCV file, + interperates it's tone curve, and writes out a + simple ASCII file containing a table of interpolation + values. See the header file for more information. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + +****************************************************/ + +#include +#include +#include +#include +#include +#include /* For variable argument lists */ +#include +#include "nikon_curve.h" + +#if 0 +#ifdef __WITH_UFRAW__ + #include "uf_glib.h" + #include "ufraw.h" +#else + #define MAX(a,b) ((a) > (b) ? (a) : (b)) + #define MIN(a,b) ((a) < (b) ? (a) : (b)) + #define g_fopen fopen +#endif +#endif +#define g_fopen fopen + +/************************************************* + * Internal static data + *************************************************/ + +//file offsets for the different data in different file types +static const int FileOffsets[2][4] = { + {NTC_PATCH_OFFSET,NTC_BOX_DATA, NTC_NUM_ANCHOR_POINTS, NTC_ANCHOR_DATA_START}, + {NCV_PATCH_OFFSET,NCV_BOX_DATA, NCV_NUM_ANCHOR_POINTS, NCV_ANCHOR_DATA_START}, +}; + +//file header indicating ntc file +static const unsigned char NTCFileHeader[] = {0x9d,0xdc,0x7d,0x00,0x65,0xd4, + 0x11,0xd1,0x91,0x94,0x44,0x45,0x53,0x54,0x00,0x00}; + +//file header indicating an ncv file +static const unsigned char NCVFileHeader[] = {0x40,0xa9,0x86,0x7a,0x1b,0xe9, + 0xd2,0x11,0xa9,0x0a,0x00,0xaa,0x00,0xb1,0xc1,0xb7}; + +//This is an additional header chunk at the beginning of the file +//There are some similarities between the headers, but not enough to fully crack. +//This does not appear to change. +static const unsigned char NCVSecondFileHeader[] = {0x01,0x32,0xa4,0x76,0xa2, + 0x17,0xd4,0x11,0xa9,0x0a,0x00,0xaa,0x00,0xb1,0xc1, + 0xb7,0x01,0x00,0x05,0x00,0x00,0x00,0x01}; + +//This is the terminator of an NCV file. Again there are some similarites +//to other sections, but not enough for to crack what it means. However, +//it does not appear to change. +static const unsigned char NCVFileTerminator[] = {0x45,0xd3,0x0d,0x77,0xa3,0x6e, + 0x1e,0x4e,0xa4,0xbe,0xcf,0xc1,0x8e,0xb5,0xb7,0x47, + 0x01,0x00,0x05,0x00,0x00,0x00,0x01 }; + +//File section header. Only a one byte difference between this and an NTC file header +static const unsigned char FileSectionHeader[] = {0x9d,0xdc,0x7d,0x03,0x65,0xd4, + 0x11,0xd1,0x91,0x94,0x44,0x45,0x53,0x54,0x00,0x00}; +//file type header array +static const unsigned char *FileTypeHeaders[NUM_FILE_TYPES] = { + NTCFileHeader, + NCVFileHeader, +}; + +/**STANDALONE**/ +#ifdef _STAND_ALONE_ + +//filenames +char exportFilename[1024]; +char nikonFilename[1024]; + +unsigned int standalone_samplingRes = 65536; +unsigned int standalone_outputRes = 256; +unsigned int program_mode = CURVE_MODE; + +/******************************************* +ProcessArgs: + Convenient function for processing the args + for the test runner. +********************************************/ +int ProcessArgs(int num_args, char *args[]) +{ + exportFilename[0] = '\0'; + nikonFilename[0] = '\0'; + + int i; + for(i = 0; i < num_args; i++) + { + if (strcmp(args[i],"-h") == 0 || strcmp(args[i],"-H") == 0 || num_args <= 1) + { + printf("NikonCurveGenerator %s %s\n",NC_VERSION, NC_DATE); + printf("Written by Shawn Freeman\n"); + printf("Thanks go out to Udi Fuchs, UFRaw, and GIMP :)\n\n"); + printf("Usage:\n"); + printf("-o Specify output file.\n"); + printf("-sr Specify sampling resolution. Default is 65536.\n"); + printf("-or Specify output resolution. Default is 256.\n\n"); + printf("-nef Specify an NEF file to get tone curve data from.\n\n"); + printf(" The -or and -sr options are ignored for NEF files\n\n"); + printf("NOTE: If a resolution is not specified, a default one will be used.\n"); + printf(" If the -o option is not specified, default files will be used.\n\n"); + printf("Example:\n"); + printf("NikonCurveGenerator -sr 65536 -or 256 curveFile -o exportFile\n"); + + //signal that processing cannot occur + return NC_ERROR; + } + else if (strcmp(args[i],"-o") == 0 || strcmp(args[i],"-O") == 0) + { + i++; + strncpy(exportFilename, args[i], 1023); + exportFilename[1023] = '\0'; + } + else if (strcmp(args[i],"-sr") == 0) + { + i++; + standalone_samplingRes = atoi(args[i]); + + if (standalone_samplingRes < 1) + { + nc_message(NC_WARNING, "WARNING: Sampling resolution must be" + ">= 1! Using default of 65535.\n"); + } + } + else if (strcmp(args[i],"-or") == 0) + { + i++; + standalone_outputRes = atoi(args[i]); + + if (standalone_outputRes < 1) + { + nc_message(NC_WARNING, "WARNING: Output resolution must be" + ">= 1! Using default of 256.\n"); + } + } + else if (strcmp(args[i],"-nef") == 0) + { + i++; + program_mode = NEF_MODE; + strncpy(nikonFilename, args[i], 1023); + nikonFilename[1023] = '\0'; + } + //don't load argument 0 + else if (i != 0) + { + //consider this the file name to load + strncpy(nikonFilename, args[i], 1023); + nikonFilename[1023] = '\0'; + } + } + + if (strlen(exportFilename) == 0) + { + //set it to have a default output file name + strncpy(exportFilename, nikonFilename, 1023); + strncat(exportFilename, "_CURVE_OUTPUT.txt", 1023); + exportFilename[1023] = '\0'; + } + + return NC_SUCCESS; +} +#endif //End STAND_ALONE + +/************************************************************ +nc_message_handler: + The Nikon Curve message handler. Udi Fuchs created this +to make the error handling consistent acros the code. + + code - Message code + message - The message +**************************************************************/ +void nc_message(int code, char *format, ...) +{ + char message[256]; + va_list ap; + va_start(ap, format); + + vsnprintf(message, 255, format, ap); + message[255] = '\0'; + va_end(ap); + +#ifdef _STAND_ALONE_ //if we're running standalone mode + + code = code; + fprintf(stderr, "%s", message); + fflush(stderr); + +#else + +#ifdef __WITH_UFRAW__ //and if compiling with UFRAW + + if (code==NC_SET_ERROR) + { + ufraw_message(UFRAW_SET_ERROR, message); + } + else + { + ufraw_message(code, message); + } + +#else //else, just print out the errors normally + + code = code; + g_printerr("%s", message); + +#endif //End WITH_UFRAW + +#endif //End STAND_ALONE +} + +void DEBUG_PRINT(char *format, ...) +{ +#if 0//def _DEBUG + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + fflush(stderr); + va_end(ap); +#else + format = format; +#endif +} + +/* nc_merror(): Handle memory allocaltion errors */ +void nc_merror(void *ptr, char *where) +{ + if (ptr) return; +#ifdef __WITH_UFRAW__ + g_error("Out of memory in %s\n", where); +#else + fprintf(stderr, "Out of memory in %s\n", where); + exit(1); +#endif +} + +size_t nc_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + size_t num = fread(ptr, size, nmemb, stream); + if ( num!=nmemb ) + nc_message(NC_WARNING, "WARNING: fread %d != %d\n", num, nmemb); + return num; +} + +size_t nc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + size_t num = nc_fwrite(ptr, size, nmemb, stream); + if ( num!=nmemb ) + nc_message(NC_WARNING, "WARNING: nc_fwrite %d != %d\n", num, nmemb); + return num; +} + +// Assert something at compile time (must use this inside a function); +// works because compilers won't let us declare negative-length arrays. +#define STATIC_ASSERT(cond) \ + { (void)((int (*)(char failed_static_assertion[(cond)?1:-1]))0); } + +/*********************************************************************** +isBigEndian: + Determines if the machine we are running on is big endian or not. +************************************************************************/ +int isBigEndian() +{ + STATIC_ASSERT(sizeof(short)==2); + short x; + unsigned char EndianTest[2] = { 1, 0 }; + + x = *(short *) EndianTest; + + return (x!=1); +} + +/*********************************************************************** +ShortVal: + Convert short int (16 bit) from little endian to machine endianess. +************************************************************************/ +short ShortVal(short s) +{ + STATIC_ASSERT(sizeof(short)==2); + if (isBigEndian()) { + unsigned char b1, b2; + + b1 = s & 255; + b2 = (s >> 8) & 255; + + return (b1 << 8) + b2; + } else + return s; +} + +/*********************************************************************** +LongVal: + Convert long int (32 bit) from little endian to machine endianess. +************************************************************************/ +int LongVal(int i) +{ + STATIC_ASSERT(sizeof(int)==4); + if (isBigEndian()) { + unsigned char b1, b2, b3, b4; + + b1 = i & 255; + b2 = ( i >> 8 ) & 255; + b3 = ( i>>16 ) & 255; + b4 = ( i>>24 ) & 255; + + return ((int)b1 << 24) + ((int)b2 << 16) + ((int)b3 << 8) + b4; + } else + return i; +} + +/*********************************************************************** +FloatVal: + Convert float from little endian to machine endianess. +************************************************************************/ +float FloatVal(float f) +{ + STATIC_ASSERT(sizeof(float)==4); + if (isBigEndian()) { + union { + float f; + unsigned char b[4]; + } dat1, dat2; + + dat1.f = f; + dat2.b[0] = dat1.b[3]; + dat2.b[1] = dat1.b[2]; + dat2.b[2] = dat1.b[1]; + dat2.b[3] = dat1.b[0]; + return dat2.f; + } else + return f; +} + +/*********************************************************************** +DoubleVal: + Convert double from little endian to machine endianess. +************************************************************************/ +double DoubleVal(double d) +{ + STATIC_ASSERT(sizeof(double)==8); + if (isBigEndian()) { + union { + double d; + unsigned char b[8]; + } dat1, dat2; + + dat1.d = d; + dat2.b[0] = dat1.b[7]; + dat2.b[1] = dat1.b[6]; + dat2.b[2] = dat1.b[5]; + dat2.b[3] = dat1.b[4]; + dat2.b[4] = dat1.b[3]; + dat2.b[5] = dat1.b[2]; + dat2.b[6] = dat1.b[1]; + dat2.b[7] = dat1.b[0]; + return dat2.d; + } else + return d; +} + +//********************************************************************** +// +// Purpose: +// +// D3_NP_FS factors and solves a D3 system. +// +// Discussion: +// +// The D3 storage format is used for a tridiagonal matrix. +// The superdiagonal is stored in entries (1,2:N), the diagonal in +// entries (2,1:N), and the subdiagonal in (3,1:N-1). Thus, the +// original matrix is "collapsed" vertically into the array. +// +// This algorithm requires that each diagonal entry be nonzero. +// It does not use pivoting, and so can fail on systems that +// are actually nonsingular. +// +// Example: +// +// Here is how a D3 matrix of order 5 would be stored: +// +// * A12 A23 A34 A45 +// A11 A22 A33 A44 A55 +// A21 A32 A43 A54 * +// +// Modified: +// +// 07 January 2005 Shawn Freeman (pure C modifications) +// 15 November 2003 John Burkardt +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Input, int N, the order of the linear system. +// +// Input/output, double A[3*N]. +// On input, the nonzero diagonals of the linear system. +// On output, the data in these vectors has been overwritten +// by factorization information. +// +// Input, double B[N], the right hand side. +// +// Output, double D3_NP_FS[N], the solution of the linear system. +// This is NULL if there was an error because one of the diagonal +// entries was zero. +// +double *d3_np_fs ( int n, double a[], double b[] ) + +{ + int i; + double *x; + double xmult; +// +// Check. +// + for ( i = 0; i < n; i++ ) + { + if ( a[1+i*3] == 0.0E+00 ) + { + return NULL; + } + } + x = (double *)calloc(n,sizeof(double)); + nc_merror(x, "d3_np_fs"); + + for ( i = 0; i < n; i++ ) + { + x[i] = b[i]; + } + + for ( i = 1; i < n; i++ ) + { + xmult = a[2+(i-1)*3] / a[1+(i-1)*3]; + a[1+i*3] = a[1+i*3] - xmult * a[0+i*3]; + x[i] = x[i] - xmult * x[i-1]; + } + + x[n-1] = x[n-1] / a[1+(n-1)*3]; + for ( i = n-2; 0 <= i; i-- ) + { + x[i] = ( x[i] - a[0+(i+1)*3] * x[i+1] ) / a[1+i*3]; + } + + return x; +} + +//********************************************************************** +// +// Purpose: +// +// SPLINE_CUBIC_SET computes the second derivatives of a piecewise cubic spline. +// +// Discussion: +// +// For data interpolation, the user must call SPLINE_SET to determine +// the second derivative data, passing in the data to be interpolated, +// and the desired boundary conditions. +// +// The data to be interpolated, plus the SPLINE_SET output, defines +// the spline. The user may then call SPLINE_VAL to evaluate the +// spline at any point. +// +// The cubic spline is a piecewise cubic polynomial. The intervals +// are determined by the "knots" or abscissas of the data to be +// interpolated. The cubic spline has continous first and second +// derivatives over the entire interval of interpolation. +// +// For any point T in the interval T(IVAL), T(IVAL+1), the form of +// the spline is +// +// SPL(T) = A(IVAL) +// + B(IVAL) * ( T - T(IVAL) ) +// + C(IVAL) * ( T - T(IVAL) )**2 +// + D(IVAL) * ( T - T(IVAL) )**3 +// +// If we assume that we know the values Y(*) and YPP(*), which represent +// the values and second derivatives of the spline at each knot, then +// the coefficients can be computed as: +// +// A(IVAL) = Y(IVAL) +// B(IVAL) = ( Y(IVAL+1) - Y(IVAL) ) / ( T(IVAL+1) - T(IVAL) ) +// - ( YPP(IVAL+1) + 2 * YPP(IVAL) ) * ( T(IVAL+1) - T(IVAL) ) / 6 +// C(IVAL) = YPP(IVAL) / 2 +// D(IVAL) = ( YPP(IVAL+1) - YPP(IVAL) ) / ( 6 * ( T(IVAL+1) - T(IVAL) ) ) +// +// Since the first derivative of the spline is +// +// SPL'(T) = B(IVAL) +// + 2 * C(IVAL) * ( T - T(IVAL) ) +// + 3 * D(IVAL) * ( T - T(IVAL) )**2, +// +// the requirement that the first derivative be continuous at interior +// knot I results in a total of N-2 equations, of the form: +// +// B(IVAL-1) + 2 C(IVAL-1) * (T(IVAL)-T(IVAL-1)) +// + 3 * D(IVAL-1) * (T(IVAL) - T(IVAL-1))**2 = B(IVAL) +// +// or, setting H(IVAL) = T(IVAL+1) - T(IVAL) +// +// ( Y(IVAL) - Y(IVAL-1) ) / H(IVAL-1) +// - ( YPP(IVAL) + 2 * YPP(IVAL-1) ) * H(IVAL-1) / 6 +// + YPP(IVAL-1) * H(IVAL-1) +// + ( YPP(IVAL) - YPP(IVAL-1) ) * H(IVAL-1) / 2 +// = +// ( Y(IVAL+1) - Y(IVAL) ) / H(IVAL) +// - ( YPP(IVAL+1) + 2 * YPP(IVAL) ) * H(IVAL) / 6 +// +// or +// +// YPP(IVAL-1) * H(IVAL-1) + 2 * YPP(IVAL) * ( H(IVAL-1) + H(IVAL) ) +// + YPP(IVAL) * H(IVAL) +// = +// 6 * ( Y(IVAL+1) - Y(IVAL) ) / H(IVAL) +// - 6 * ( Y(IVAL) - Y(IVAL-1) ) / H(IVAL-1) +// +// Boundary conditions must be applied at the first and last knots. +// The resulting tridiagonal system can be solved for the YPP values. +// +// Modified: +// +// 07 January 2005 Shawn Freeman (pure C modifications) +// 06 February 2004 John Burkardt +// +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Input, int N, the number of data points. N must be at least 2. +// In the special case where N = 2 and IBCBEG = IBCEND = 0, the +// spline will actually be linear. +// +// Input, double T[N], the knot values, that is, the points were data is +// specified. The knot values should be distinct, and increasing. +// +// Input, double Y[N], the data values to be interpolated. +// +// Input, int IBCBEG, left boundary condition flag: +// 0: the cubic spline should be a quadratic over the first interval; +// 1: the first derivative at the left endpoint should be YBCBEG; +// 2: the second derivative at the left endpoint should be YBCBEG. +// +// Input, double YBCBEG, the values to be used in the boundary +// conditions if IBCBEG is equal to 1 or 2. +// +// Input, int IBCEND, right boundary condition flag: +// 0: the cubic spline should be a quadratic over the last interval; +// 1: the first derivative at the right endpoint should be YBCEND; +// 2: the second derivative at the right endpoint should be YBCEND. +// +// Input, double YBCEND, the values to be used in the boundary +// conditions if IBCEND is equal to 1 or 2. +// +// Output, double SPLINE_CUBIC_SET[N], the second derivatives of the cubic spline. +// +double *spline_cubic_set ( int n, double t[], double y[], int ibcbeg, + double ybcbeg, int ibcend, double ybcend ) +{ + double *a; + double *b; + int i; + double *ypp; +// +// Check. +// + if ( n <= 1 ) + { + nc_message(NC_SET_ERROR, "spline_cubic_set() error: " + "The number of data points must be at least 2.\n"); + return NULL; + } + + for ( i = 0; i < n - 1; i++ ) + { + if ( t[i+1] <= t[i] ) + { + nc_message(NC_SET_ERROR, "spline_cubic_set() error: " + "The knots must be strictly increasing, but " + "T(%u) = %e, T(%u) = %e\n",i,t[i],i+1,t[i+1]); + return NULL; + } + } + a = (double *)calloc(3*n,sizeof(double)); + nc_merror(a, "spline_cubic_set"); + b = (double *)calloc(n,sizeof(double)); + nc_merror(b, "spline_cubic_set"); +// +// Set up the first equation. +// + if ( ibcbeg == 0 ) + { + b[0] = 0.0E+00; + a[1+0*3] = 1.0E+00; + a[0+1*3] = -1.0E+00; + } + else if ( ibcbeg == 1 ) + { + b[0] = ( y[1] - y[0] ) / ( t[1] - t[0] ) - ybcbeg; + a[1+0*3] = ( t[1] - t[0] ) / 3.0E+00; + a[0+1*3] = ( t[1] - t[0] ) / 6.0E+00; + } + else if ( ibcbeg == 2 ) + { + b[0] = ybcbeg; + a[1+0*3] = 1.0E+00; + a[0+1*3] = 0.0E+00; + } + else + { + nc_message(NC_SET_ERROR, "spline_cubic_set() error: " + "IBCBEG must be 0, 1 or 2. The input value is %u.\n", ibcbeg); + free(a); + free(b); + return NULL; + } +// +// Set up the intermediate equations. +// + for ( i = 1; i < n-1; i++ ) + { + b[i] = ( y[i+1] - y[i] ) / ( t[i+1] - t[i] ) + - ( y[i] - y[i-1] ) / ( t[i] - t[i-1] ); + a[2+(i-1)*3] = ( t[i] - t[i-1] ) / 6.0E+00; + a[1+ i *3] = ( t[i+1] - t[i-1] ) / 3.0E+00; + a[0+(i+1)*3] = ( t[i+1] - t[i] ) / 6.0E+00; + } +// +// Set up the last equation. +// + if ( ibcend == 0 ) + { + b[n-1] = 0.0E+00; + a[2+(n-2)*3] = -1.0E+00; + a[1+(n-1)*3] = 1.0E+00; + } + else if ( ibcend == 1 ) + { + b[n-1] = ybcend - ( y[n-1] - y[n-2] ) / ( t[n-1] - t[n-2] ); + a[2+(n-2)*3] = ( t[n-1] - t[n-2] ) / 6.0E+00; + a[1+(n-1)*3] = ( t[n-1] - t[n-2] ) / 3.0E+00; + } + else if ( ibcend == 2 ) + { + b[n-1] = ybcend; + a[2+(n-2)*3] = 0.0E+00; + a[1+(n-1)*3] = 1.0E+00; + } + else + { + nc_message(NC_SET_ERROR, "spline_cubic_set() error: " + "IBCEND must be 0, 1 or 2. The input value is %u", ibcend); + free(a); + free(b); + return NULL; + } +// +// Solve the linear system. +// + if ( n == 2 && ibcbeg == 0 && ibcend == 0 ) + { + ypp = (double *)calloc(2,sizeof(double)); + nc_merror(ypp, "spline_cubic_set"); + + ypp[0] = 0.0E+00; + ypp[1] = 0.0E+00; + } + else + { + ypp = d3_np_fs ( n, a, b ); + + if ( !ypp ) + { + nc_message(NC_SET_ERROR, "spline_cubic_set() error: " + "The linear system could not be solved.\n"); + free(a); + free(b); + return NULL; + } + + } + + free(a); + free(b); + return ypp; +} + +//********************************************************************** +// +// Purpose: +// +// SPLINE_CUBIC_VAL evaluates a piecewise cubic spline at a point. +// +// Discussion: +// +// SPLINE_CUBIC_SET must have already been called to define the values of YPP. +// +// For any point T in the interval T(IVAL), T(IVAL+1), the form of +// the spline is +// +// SPL(T) = A +// + B * ( T - T(IVAL) ) +// + C * ( T - T(IVAL) )**2 +// + D * ( T - T(IVAL) )**3 +// +// Here: +// A = Y(IVAL) +// B = ( Y(IVAL+1) - Y(IVAL) ) / ( T(IVAL+1) - T(IVAL) ) +// - ( YPP(IVAL+1) + 2 * YPP(IVAL) ) * ( T(IVAL+1) - T(IVAL) ) / 6 +// C = YPP(IVAL) / 2 +// D = ( YPP(IVAL+1) - YPP(IVAL) ) / ( 6 * ( T(IVAL+1) - T(IVAL) ) ) +// +// Modified: +// +// 07 January 2005 Shawn Freeman (pure C modifications) +// 04 February 1999 John Burkardt +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Input, int n, the number of knots. +// +// Input, double Y[N], the data values at the knots. +// +// Input, double T[N], the knot values. +// +// Input, double TVAL, a point, typically between T[0] and T[N-1], at +// which the spline is to be evalulated. If TVAL lies outside +// this range, extrapolation is used. +// +// Input, double Y[N], the data values at the knots. +// +// Input, double YPP[N], the second derivatives of the spline at +// the knots. +// +// Output, double *YPVAL, the derivative of the spline at TVAL. +// +// Output, double *YPPVAL, the second derivative of the spline at TVAL. +// +// Output, double SPLINE_VAL, the value of the spline at TVAL. +// +double spline_cubic_val ( int n, double t[], double tval, double y[], + double ypp[], double *ypval, double *yppval ) +{ + double dt; + double h; + int i; + int ival; + double yval; +// +// Determine the interval [ T(I), T(I+1) ] that contains TVAL. +// Values below T[0] or above T[N-1] use extrapolation. +// + ival = n - 2; + + for ( i = 0; i < n-1; i++ ) + { + if ( tval < t[i+1] ) + { + ival = i; + break; + } + } +// +// In the interval I, the polynomial is in terms of a normalized +// coordinate between 0 and 1. +// + dt = tval - t[ival]; + h = t[ival+1] - t[ival]; + + yval = y[ival] + + dt * ( ( y[ival+1] - y[ival] ) / h + - ( ypp[ival+1] / 6.0E+00 + ypp[ival] / 3.0E+00 ) * h + + dt * ( 0.5E+00 * ypp[ival] + + dt * ( ( ypp[ival+1] - ypp[ival] ) / ( 6.0E+00 * h ) ) ) ); + + *ypval = ( y[ival+1] - y[ival] ) / h + - ( ypp[ival+1] / 6.0E+00 + ypp[ival] / 3.0E+00 ) * h + + dt * ( ypp[ival] + + dt * ( 0.5E+00 * ( ypp[ival+1] - ypp[ival] ) / h ) ); + + *yppval = ypp[ival] + dt * ( ypp[ival+1] - ypp[ival] ) / h; + + return yval; +} + +/********************************************* +GetNikonFileType: + Gets the nikon file type by comparing file + headers. + + file - The file to check. +**********************************************/ +int GetNikonFileType(FILE *file) +{ + unsigned char buff[HEADER_SIZE]; + int i = 0, j = 0; + int found = 1; + + nc_fread(buff,HEADER_SIZE,1,file); + + for(i = 0; i < NUM_FILE_TYPES; i++) + { + found = 1; + for(j = 0; j < HEADER_SIZE; j++) + { + if (buff[j] != FileTypeHeaders[i][j]) + { + found = 0; + break; + } + } + + if (found) + { + //return the file type + return i; + } + } + nc_message(NC_SET_ERROR, "Error, no compatible file types found!\n"); + return -1; +} + +/********************************************* +LoadNikonCurve: + Loads all curves from a Nikon ntc or ncv file. + + fileName - The filename. + curve - Pointer to curve struct to hold the data. + resolution - How many data points to sample from the curve. +**********************************************/ +int LoadNikonData(char *fileName, NikonData *data) +{ + FILE *input = NULL; + int offset = 0; + CurveData *curve = NULL; + + if (fileName == NULL || strlen(fileName) == 0) + { + nc_message(NC_SET_ERROR, + "Error, input filename cannot be NULL or empty!\n"); + return NC_ERROR; + } + + DEBUG_PRINT("DEBUG: OPENING FILE: %s\n",fileName); + + //open file for reading only! + input = g_fopen(fileName,"rb"); + + //make sure we have a valid file + if (input == NULL) + { + nc_message(NC_SET_ERROR, "Error opening '%s': %s\n", + fileName, strerror(errno)); + return NC_ERROR; + } + + //init the curve; + memset(data,0,sizeof(NikonData)); + + //get the file type + data->m_fileType = GetNikonFileType(input); + // set file seek positions for curve tones depending of file type + // todo: is it possible to find one common rule? + long curveFilePos[4][4] = { + {FileOffsets[data->m_fileType][BOX_DATA], SEEK_SET, FileOffsets[data->m_fileType][ANCHOR_DATA], SEEK_SET}, + {NEXT_SECTION_BOX_DATA_OFFSET, SEEK_CUR, NUM_POINTS_TO_ANCHOR_OFFSET, SEEK_CUR}, + {NEXT_SECTION_BOX_DATA_OFFSET, SEEK_CUR, NUM_POINTS_TO_ANCHOR_OFFSET, SEEK_CUR}, + {NEXT_SECTION_BOX_DATA_OFFSET, SEEK_CUR, NUM_POINTS_TO_ANCHOR_OFFSET, SEEK_CUR} + }; + + //make sure we have a good file type + if (data->m_fileType == -1) + return NC_ERROR; + + //advance file pointer to necessary data section + fseek(input,offset,SEEK_SET); + + //Conevenience and opt if compiler doesn't already do it + curve = &data->curves[0]; + + //set curve type + curve->m_curveType = TONE_CURVE; + + //read patch version + fseek(input,FileOffsets[data->m_fileType][PATCH_DATA],SEEK_SET); + nc_fread(&data->m_patch_version,sizeof(unsigned short),1,input); + data->m_patch_version = ShortVal(data->m_patch_version); + + // read all tone curves data follow from here + int i; + for(i = 0; i < NUM_CURVE_TYPES; i++) + { + curve = &data->curves[i]; + + //set curve type + curve->m_curveType = i; + + //get box data + fseek(input, curveFilePos[i][0], curveFilePos[i][1]); + + nc_fread(&curve->m_min_x,sizeof(double),1,input); + curve->m_min_x = DoubleVal(curve->m_min_x); + + nc_fread(&curve->m_max_x,sizeof(double),1,input); + curve->m_max_x = DoubleVal(curve->m_max_x); + + nc_fread(&curve->m_gamma,sizeof(double),1,input); + curve->m_gamma = DoubleVal(curve->m_gamma); + + nc_fread(&curve->m_min_y,sizeof(double),1,input); + curve->m_min_y = DoubleVal(curve->m_min_y); + + nc_fread(&curve->m_max_y,sizeof(double),1,input); + curve->m_max_y = DoubleVal(curve->m_max_y); + + //get number of anchors (always located after box data) + nc_fread(&curve->m_numAnchors,1,1,input); + + // It seems that if there is no curve then the 62 bytes in the buffer + // are either all 0x00 (D70) or 0xFF (D2H). + // We therefore switch these values with the default values. + if (curve->m_min_x==1.0) { + curve->m_min_x = 0.0; + DEBUG_PRINT("DEBUG: NEF X MIN -> %e (changed)\n",curve->m_min_x); + } + if (curve->m_max_x==0.0) { + curve->m_max_x = 1.0; + DEBUG_PRINT("DEBUG: NEF X MAX -> %e (changed)\n",curve->m_max_x); + } + if (curve->m_min_y==1.0) { + curve->m_min_y = 0.0; + DEBUG_PRINT("DEBUG: NEF Y MIN -> %e (changed)\n",curve->m_min_y); + } + if (curve->m_max_y==0.0) { + curve->m_max_y = 1.0; + DEBUG_PRINT("DEBUG: NEF Y MAX -> %e (changed)\n",curve->m_max_y); + } + if (curve->m_gamma==0.0 || curve->m_gamma==255.0+255.0/256.0) { + curve->m_gamma = 1.0; + DEBUG_PRINT("DEBUG: NEF GAMMA -> %e (changed)\n",curve->m_gamma); + } + if (curve->m_numAnchors==255) { + curve->m_numAnchors = 0; + DEBUG_PRINT("DEBUG: NEF NUMBER OF ANCHORS -> %u (changed)\n", + curve->m_numAnchors); + } + if (curve->m_numAnchors > NIKON_MAX_ANCHORS) { + curve->m_numAnchors = NIKON_MAX_ANCHORS; + DEBUG_PRINT("DEBUG: NEF NUMBER OF ANCHORS -> %u (changed)\n", + curve->m_numAnchors); + } + //Move to start of the anchor data + fseek(input, curveFilePos[i][2], curveFilePos[i][3]); + + //read in the anchor points + int rs = nc_fread(curve->m_anchors, sizeof(CurveAnchorPoint), + curve->m_numAnchors, input); + if (curve->m_numAnchors != rs) { + nc_message(NC_SET_ERROR, "Error reading all anchor points\n"); + return NC_ERROR; + } + + int j; + for (j = 0; j < curve->m_numAnchors; j++) + { + curve->m_anchors[j].x = DoubleVal(curve->m_anchors[j].x); + curve->m_anchors[j].y = DoubleVal(curve->m_anchors[j].y); + } + + DEBUG_PRINT("DEBUG: Loading Data:\n"); + DEBUG_PRINT("DEBUG: CURVE_TYPE: %u \n",curve->m_curveType); + DEBUG_PRINT("DEBUG: BOX->MIN_X: %f\n",curve->m_min_x); + DEBUG_PRINT("DEBUG: BOX->MAX_X: %f\n",curve->m_max_x); + DEBUG_PRINT("DEBUG: BOX->GAMMA: %f\n",curve->m_gamma); + DEBUG_PRINT("DEBUG: BOX->MIN_Y: %f\n",curve->m_min_y); + DEBUG_PRINT("DEBUG: BOX->MAX_Y: %f\n",curve->m_max_x); + +#ifdef _DEBUG + int i_dbg; + for(i_dbg = 0; i_dbg < curve->m_numAnchors; i_dbg++) + { + DEBUG_PRINT("DEBUG: ANCHOR X,Y: %f,%f\n", + curve->m_anchors[i_dbg].x,curve->m_anchors[i_dbg].y); + } + DEBUG_PRINT("\n"); +#endif + } + fclose(input); + return NC_SUCCESS; +} + +/********************************************* +CurveDataSample: + Samples from a spline curve constructed from + the Nikon data. + + curve - Pointer to curve struct to hold the data. + sample - Pointer to sample struct to hold the data. +**********************************************/ +int CurveDataSample(CurveData *curve, CurveSample *sample) +{ + int i = 0, n; + + double x[20]; + double y[20]; + + //The box points (except the gamma) are what the anchor points are relative + //to so... + + double box_width = curve->m_max_x - curve->m_min_x; + double box_height = curve->m_max_y - curve->m_min_y; + double gamma = 1.0/curve->m_gamma; + + //build arrays for processing + if (curve->m_numAnchors == 0) + { + //just a straight line using box coordinates + x[0] = curve->m_min_x; + y[0] = curve->m_min_y; + x[1] = curve->m_max_x; + y[1] = curve->m_max_y; + n = 2; + } + else + { + for(i = 0; i < curve->m_numAnchors; i++) + { + x[i] = curve->m_anchors[i].x*box_width + curve->m_min_x; + y[i] = curve->m_anchors[i].y*box_height + curve->m_min_y; + } + n = curve->m_numAnchors; + } + //returns an array of second derivatives used to calculate the spline curve. + //this is a malloc'd array that needs to be freed when done. + //The setings currently calculate the natural spline, which closely matches + //camera curve output in raw files. + double *ypp = spline_cubic_set(n, x, y, 2, 0.0, 2, 0.0); + if (ypp==NULL) return NC_ERROR; + + //first derivative at a point + double ypval = 0; + + //second derivate at a point + double yppval = 0; + + //Now build a table + int val; + double res = 1.0/(double)(sample->m_samplingRes-1); + + //allocate enough space for the samples + DEBUG_PRINT("DEBUG: SAMPLING ALLOCATION: %u bytes\n", + sample->m_samplingRes*sizeof(int)); + DEBUG_PRINT("DEBUG: SAMPLING OUTPUT RANGE: 0 -> %u\n", sample->m_outputRes); + + // sample->m_Samples = (unsigned short int *)realloc(sample->m_Samples, + // sample->m_samplingRes * sizeof(short int)); + // nc_merror(sample->m_Samples, "CurveDataSample"); + + int firstPointX = x[0] * (sample->m_samplingRes-1); + int firstPointY = pow(y[0], gamma) * (sample->m_outputRes-1); + int lastPointX = x[n-1] * (sample->m_samplingRes-1); + int lastPointY = pow(y[n-1], gamma) * (sample->m_outputRes-1); + int maxY = curve->m_max_y * (sample->m_outputRes-1); + int minY = curve->m_min_y * (sample->m_outputRes-1); + + for(i = 0; i < (int)sample->m_samplingRes; i++) + { + //get the value of the curve at a point + //take into account that curves may not necessarily begin at x = 0.0 + //nor end at x = 1.0 + + //Before the first point and after the last point, take a strait line + if (i < firstPointX) { + sample->m_Samples[i] = firstPointY; + } else if (i > lastPointX) { + sample->m_Samples[i] = lastPointY; + } else { + //within range, we can sample the curve + if (gamma==1.0) + val = spline_cubic_val( n, x, i*res, y, + ypp, &ypval, &yppval ) * (sample->m_outputRes-1) + 0.5; + else + val = pow(spline_cubic_val( n, x, i*res, y, + ypp, &ypval, &yppval ), gamma) * + (sample->m_outputRes-1) + 0.5; + + sample->m_Samples[i] = MIN(MAX(val,minY),maxY); + } + } + + free(ypp); + return NC_SUCCESS; +} + +/********************************************* +CurveDataReset: + Reset curve to straight line but don't touch the curve name. +**********************************************/ +void CurveDataReset(CurveData *curve) +{ + curve->m_min_x = 0; + curve->m_max_x = 1; + curve->m_min_y = 0; + curve->m_max_y = 1; + curve->m_gamma = 1; + curve->m_numAnchors = 2; + curve->m_anchors[0].x = 0; + curve->m_anchors[0].y = 0; + curve->m_anchors[1].x = 1; + curve->m_anchors[1].y = 1; +} + +/********************************************* +CurveDataIsTrivial: + Check if the curve is a trivial linear curve. +**********************************************/ +int CurveDataIsTrivial(CurveData *curve) +{ + if ( curve->m_min_x != 0 ) return FALSE; + if ( curve->m_max_x != 1 ) return FALSE; + if ( curve->m_min_y != 0 ) return FALSE; + if ( curve->m_max_y != 1 ) return FALSE; + if ( curve->m_numAnchors < 2 ) return TRUE; + if ( curve->m_numAnchors != 2 ) return FALSE; + if ( curve->m_anchors[0].x != 0 ) return FALSE; + if ( curve->m_anchors[0].y != 0 ) return FALSE; + if ( curve->m_anchors[1].x != 1 ) return FALSE; + if ( curve->m_anchors[1].y != 1 ) return FALSE; + return TRUE; +} + +/********************************************* +CurveDataSetPoint: + Change the position of point to the new (x,y) coordinate. + The end-points get a special treatment. When these are moved all the + other points are moved together, keeping their relative position constant. +**********************************************/ +void CurveDataSetPoint(CurveData *curve, int point, double x, double y) +{ + int i; + double left = curve->m_anchors[0].x; + double right = curve->m_anchors[curve->m_numAnchors-1].x; + if (point==0) { + for (i=0; im_numAnchors; i++) + curve->m_anchors[i].x = x + (curve->m_anchors[i].x-left) * + (right-x) / (right-left); + } else if (point==curve->m_numAnchors-1) { + for (i=0; im_numAnchors; i++) + curve->m_anchors[i].x = left + (curve->m_anchors[i].x-left) * + (x-left) / (right-left); + } else { + curve->m_anchors[point].x = x; + } + curve->m_anchors[point].y = y; +} + +/**************************************************** +SampleToCameraCurve: + + EXPERIMENTAL!!!!! + + Transforms the curve generated by sampling the + spline interpolator into the curve that is used by + the camera. + + This is a special function. While the function places + no special restrictions on sampling resolution or + output resolution, it should be noted that Nikon D70 + camera curve is 4096 entries of 0-255. + + If you intend on using this function as such, you should + set the sampling resolution and output resolution + accordingly. + + curve - The Nikon curve to sample and transform. +*****************************************************/ +#define CAMERA_LINEAR_CURVE_SLOPE 0.26086956521739130434782608695652 +#define CAMERA_LINEAR_LIMIT ((276.0/4096.0)*65536.0) + +int SampleToCameraCurve(CurveData *curve, CurveSample *sample) +{ + unsigned int i = 0; + + if (curve->m_numAnchors < 2) + { + nc_message(NC_SET_ERROR, "Not enough anchor points(need at least two)!\n"); + return NC_ERROR; + } + + double x[20]; + double y[20]; + + //The box points (except the gamma) are what the anchor points are relative + //to so... + + double box_width = curve->m_max_x - curve->m_min_x; + double box_height = curve->m_max_y - curve->m_min_y; + double gamma = 1.0/curve->m_gamma; + + //build arrays for processing + if (curve->m_numAnchors == 0) + { + //just a straight line using box coordinates + x[0] = curve->m_min_x; + y[0] = curve->m_min_y; + x[1] = curve->m_max_x; + y[1] = curve->m_max_y; + } + else + { + for(i = 0; i < (unsigned int)curve->m_numAnchors; i++) + { + x[i] = curve->m_anchors[i].x*box_width + curve->m_min_x; + y[i] = curve->m_anchors[i].y*box_height + curve->m_min_y; + } + } + + //returns an array of second derivatives used to calculate the spline curve. + //this is a malloc'd array that needs to be freed when done. + //The setings currently calculate the natural spline, which closely matches + //camera curve output in raw files. + double *ypp = spline_cubic_set(curve->m_numAnchors,x,y,2, 0.0, 2, 0.0); + if (ypp==NULL) return NC_ERROR; + + //first derivative at a point + double ypval = 0; + + //second derivate at a point + double yppval = 0; + + //Now build a table + double val = 0; + double res = 1.0/(double)sample->m_samplingRes; + + DEBUG_PRINT("DEBUG: SAMPLING RESOLUTION: %u bytes\n", + sample->m_samplingRes*sizeof(int)); + DEBUG_PRINT("DEBUG: SAMPLING OUTPUT RANGE: 0 -> %u\n", sample->m_outputRes); + + double outres = sample->m_outputRes; + + for(i = 0; i < sample->m_samplingRes; i++) + { + //get the value of the curve at a point + //take into account that curves may not necessarily begin at x = 0.0 + //nor end at x = 1.0 + if (i*res < curve->m_min_x || i*res > curve->m_max_x) + { + val = 0.0; + } + else + { + //within range, okay to sample the curve + val = spline_cubic_val ( curve->m_numAnchors, x, i*res, + y, ypp, &ypval, &yppval ); + + //Compensate for gamma. + val = pow(val,gamma); + + //cap at the high end of the range + if (val > curve->m_max_y) + { + val = curve->m_max_y; + } + //cap at the low end of the range + else if (val < curve->m_min_y) + { + val = curve->m_min_y; + } + + //transform "linear curve" to the camera curve + //outres = 4096; + //val *= outres; + + //this equation is used inside Nikon's program to transform + //the curves into the camera curves. + //FIX LINEAR SECTION + /*if (val < CAMERA_LINEAR_CURVE_SLOPE) + { + //do linear + val = val*4096*CAMERA_LINEAR_CURVE_SLOPE; + + } + else*/ + { + //do real curve?? + val = ( log(7*val+1.0) / log(4*val+2.0) )*142.0+104.0*(val); + } + + //cap at the high end of the range + if (val > outres*curve->m_max_y) + { + val = outres; + } + //cap at the low end of the range + else if (val < curve->m_min_y*outres) + { + val = curve->m_min_y*outres; + } + + } + + //save the sample + sample->m_Samples[i] = (unsigned int)floor(val); + } + + free(ypp); + return NC_SUCCESS; +} + +/************************************************************ +SaveNikonDataFile: + Savess a curve to a Nikon ntc or ncv file. + + data - A NikonData structure containing info of all the curves. + fileName - The filename. + filetype - Indicator for an NCV or NTC file. + version - The version of the Nikon file to write +**************************************************************/ +int SaveNikonDataFile(NikonData *data, char *outfile, int filetype, int version) +{ + FILE *output = NULL; + int i = 0,r = 0,g = 0,b = 0; + unsigned short_tmp = 0; + unsigned int long_tmp = 0; + double double_tmp = 0; + CurveData *curve = NULL; + version = version; + + //used for file padding + unsigned char pad[32]; + memset(pad,0,32); + + output = g_fopen(outfile,"wb+"); + if (!output) + { + nc_message(NC_SET_ERROR, "Error creating curve file '%s': %s\n", + outfile, strerror(errno)); + return NC_ERROR; + } + + //write out file header + nc_fwrite(FileTypeHeaders[filetype],HEADER_SIZE,1,output); + + if (filetype == NCV_FILE) + { + //write out unknown header bytes + short_tmp = ShortVal(NCV_UNKNOWN_HEADER_DATA); + nc_fwrite(&short_tmp, 2, 1, output); + + //write out file size - header + //Placeholder.The real filesize is written at the end. + //NCV files have two size location, one here and one in the + //NTC section of the file + long_tmp = 0; + nc_fwrite(&long_tmp, 4, 1, output); + + //write second header chunk + nc_fwrite(NCVSecondFileHeader,1,NCV_SECOND_HEADER_LENGTH,output); + + //From here until almost the end, the file is an NTC file + nc_fwrite(NTCFileHeader,NTC_FILE_HEADER_LENGTH,1,output); + } + + //patch version? (still unsure about this one) + if (data->m_patch_version < NIKON_PATCH_4) + { + data->m_patch_version = NIKON_PATCH_5; + } + short_tmp = ShortVal(data->m_patch_version); + nc_fwrite(&short_tmp, 2, 1, output); + + //write out file size - header + //Placeholder.The real filesize is written at the end. + long_tmp = 0; + nc_fwrite(&long_tmp, 4, 1, output); + + //write out version + unsigned int forced_ver = ShortVal(NIKON_VERSION_4_1); + nc_fwrite(&forced_ver, 4, 1, output); + + //write out pad (this is a 7 byte pad) + nc_fwrite(&pad,1,7,output); + + //now wash and repeat for the four sections of data + for(i = 0; i < 4; i++) + { + //write out section header (same as NTC file header) + nc_fwrite(FileSectionHeader,1,NTC_FILE_HEADER_LENGTH,output); + + //write out section type + long_tmp = LongVal(i); + nc_fwrite(&long_tmp,4,1,output); + + //write out unknown data + short_tmp = ShortVal(NTC_UNKNOWN_DATA); + nc_fwrite(&short_tmp,2,1,output); + + //write out pad byte + nc_fwrite(pad,1,1,output); + + //write out components + switch (i) + { + case 0: + r = g = b = 0; + break; + + case 1: + r = 255; + g = b = 0; + break; + + case 2: + g = 255; + r = b = 0; + break; + + case 3: + b = 255; + g = r = 0; + break; + } + + long_tmp = LongVal(r); + nc_fwrite(&long_tmp,4,1,output); + + long_tmp = LongVal(g); + nc_fwrite(&long_tmp,4,1,output); + + long_tmp = LongVal(b); + nc_fwrite(&long_tmp,4,1,output); + + //write out pad (12 byte pad) + nc_fwrite(pad,12,1,output); + + //write out rgb weights + switch (i) + { + case 0: + r = g = b = 255; + break; + + case 1: + r = 255; + g = b = 0; + break; + + case 2: + g = 255; + r = b = 0; + break; + + case 3: + b = 255; + g = r = 0; + break; + } + + long_tmp = LongVal(r); + nc_fwrite(&long_tmp,4,1,output); + + long_tmp = LongVal(g); + nc_fwrite(&long_tmp,4,1,output); + + long_tmp = LongVal(b); + nc_fwrite(&long_tmp,4,1,output); + + curve = &data->curves[i]; + //write out curve data + if (curve->m_numAnchors >= 2) + { + //we have a legit curve, use the data as is + double_tmp = DoubleVal(curve->m_min_x); + nc_fwrite(&double_tmp,sizeof(double),1,output); + + double_tmp = DoubleVal(curve->m_max_x); + nc_fwrite(&double_tmp,sizeof(double),1,output); + + double_tmp = DoubleVal(curve->m_gamma); + nc_fwrite(&double_tmp,sizeof(double),1,output); + + double_tmp = DoubleVal(curve->m_min_y); + nc_fwrite(&double_tmp,sizeof(double),1,output); + + double_tmp = DoubleVal(curve->m_max_y); + nc_fwrite(&double_tmp,sizeof(double),1,output); + + //write out number of anchor points (minimum is two) + nc_fwrite(&curve->m_numAnchors,1,1,output); + + //write out pad + nc_fwrite(pad,NUM_POINTS_TO_ANCHOR_OFFSET,1,output); + + //write out anchor point data + if (curve->m_anchors) + { + int i; + for (i = 0; i < curve->m_numAnchors; i++) + { + double_tmp = DoubleVal(curve->m_anchors[i].x); + nc_fwrite(&double_tmp,sizeof(double),1,output); + double_tmp = DoubleVal(curve->m_anchors[i].y); + nc_fwrite(&double_tmp,sizeof(double),1,output); + } + } + else + { + nc_message(NC_SET_ERROR,"Curve anchor data is NULL! Aborting file write!\n"); + return NC_ERROR; + } + } + else + { + DEBUG_PRINT("NOTE: There are < 2 anchor points for curve %u! Forcing curve defaults.\n",i); + DEBUG_PRINT("This should not be a concern unless it is happening for curve 0\n"); + //This curve either has not been correctly initialized or is empty. + //Force defaults. + double default_val = 0; + nc_fwrite(&default_val,sizeof(double),1,output); //min x + default_val = DoubleVal(1.0); + nc_fwrite(&default_val,sizeof(double),1,output); //max_x + //gamma has a default of 1 + default_val = DoubleVal(1.0); + nc_fwrite(&default_val,sizeof(double),1,output); //gamma + default_val = 0; + nc_fwrite(&default_val,sizeof(double),1,output); //min y + default_val = DoubleVal(1.0); + nc_fwrite(&default_val,sizeof(double),1,output); //max y + + //force the number of anchors to be 2 + unsigned char num = 2; + nc_fwrite(&num,1,1,output); + + //write out pad + nc_fwrite(pad,NUM_POINTS_TO_ANCHOR_OFFSET,1,output); + + //if the number of anchors was < 2, force default values. + default_val = 0; + nc_fwrite(&default_val,sizeof(double),1,output); //min x + nc_fwrite(&default_val,sizeof(double),1,output); //min y + default_val = DoubleVal(1.0); + nc_fwrite(&default_val,sizeof(double),1,output); //max x + nc_fwrite(&default_val,sizeof(double),1,output); //max y + + } + + //write out pad + nc_fwrite(pad,END_ANCHOR_DATA_PAD_LENGTH,1,output); + } + + if (filetype == NCV_FILE) + { + //write out the file terminator if this is an NCV file + nc_fwrite(NCVFileTerminator,NCV_FILE_TERMINATOR_LENGTH,1,output); + } + + //calculate the file size + //size = filesize - size of header - 2 bytes (unknown data after the end of the header) + long size = ftell(output)-HEADER_SIZE-2; + + //set the file write position to the size location + fseek(output, FILE_SIZE_OFFSET, SEEK_SET); + + //write out the file size + size = LongVal(size); + nc_fwrite(&size,4,1,output); + + if (filetype == NCV_FILE) + { + //another size needs to placed in the NTC header inside the file + fseek(output, NCV_SECOND_FILE_SIZE_OFFSET, SEEK_SET); + + //The - 6 is interesting. The last 6 bytes of the terminator must have some special meaning because + //the calculated size in files from the Nikon progs always calculate size bytes short. + //I'm assuming it is more than coincedence that those bytes match the last 6 bytes + //of the NCV second file header. I've yet to determine their significance. + size = LongVal(size - NCV_HEADER_SIZE - 6); + nc_fwrite(&size,4,1,output); + + } + fclose(output); + + return NC_SUCCESS; +} + +/************************************************************ +SaveNikonCurveFile: + Saves out curves to a Nikon ntc or ncv file. This function + takes a single curve and uses defaults for the other curves. + Typically, the curve used is the tone curve. + + curve - A CurveData structure. This is usually the tone curve + curve_type - The curve type (TONE_CURVE, RED_CURVE, etc.) + fileName - The filename. + filetype - Indicator for an NCV or NTC file. + version - The version of the Nikon file to write + +NOTE: The only version tested is Nikon 4.1 anything + other than this may result in unpredictable behavior. + For now, the version passed in is ignored and is forced + to 4.1. + + This function is just a helper function that allows the user + to just carry around a single curve. +**************************************************************/ +int SaveNikonCurveFile(CurveData *curve, int curve_type, char *outfile, + int filetype, int version) +{ + NikonData data; + //clear the structure + memset(&data,0,sizeof(data)); + //assume that it's the tone curve + data.curves[curve_type] = *curve; + //call the work horse + return SaveNikonDataFile(&data, outfile, filetype, version); +} + +/********************************************* +SaveSampledNikonCurve: + Saves a sampling from a curve to text file to + be processed by UFRaw. + + sample - Pointer to sampled curve struct to hold the data. + fileName - The filename. +**********************************************/ +int SaveSampledNikonCurve(CurveSample *sample, char *outfile) +{ + unsigned int i = 0; + FILE *output = NULL; + + if (outfile == NULL || strlen(outfile) == 0) + { + nc_message(NC_SET_ERROR, + "Output filename cannot be null or empty!\n"); + } + + output = g_fopen(outfile,"wb+"); + + if (!output) + { + nc_message(NC_SET_ERROR, "Error creating curve file '%s': %s\n", + outfile, strerror(errno)); + return NC_ERROR; + } + + if (!sample->m_Samples) + { + nc_message(NC_SET_ERROR, + "Sample array has not been allocated or is corrupt!\n"); + return NC_ERROR; + } + + DEBUG_PRINT("DEBUG: OUTPUT FILENAME: %s\n",outfile); + fprintf(output,"%u %u\n",0,sample->m_Samples[0]); + for(i = 1; i < sample->m_samplingRes; i++) + { + // Print sample point only if different than previous one + if (sample->m_Samples[i]!=sample->m_Samples[i-1]) + { + fprintf(output,"%u %u\n",i,sample->m_Samples[i]); + } + } + // Make sure the last point is also printed + if (sample->m_Samples[i-1]==sample->m_Samples[i-2]) + { + fprintf(output,"%u %u\n",i-1,sample->m_Samples[i-1]); + } + + fclose(output); + return NC_SUCCESS; +} + +/******************************************************* +CurveSampleInit: + Init and allocate curve sample. +********************************************************/ +CurveSample *CurveSampleInit(unsigned int samplingRes, unsigned int outputRes) +{ + CurveSample *sample = (CurveSample*)calloc(1, sizeof(CurveSample)); + nc_merror(sample, "CurveSampleInit"); + sample->m_samplingRes = samplingRes; + sample->m_outputRes = outputRes; + if (samplingRes>0) { + sample->m_Samples = (unsigned short int*)calloc(samplingRes, sizeof(short int)); + nc_merror(sample->m_Samples, "CurveSampleInit"); + } else { + sample->m_Samples = NULL; + } + return sample; +} + +/******************************************************* +CurveSampleFree: + Frees memory allocated for this curve sample. +********************************************************/ +int CurveSampleFree(CurveSample *sample) +{ + //if these are null, they've already been deallocated + if (sample==NULL) return NC_SUCCESS; + + if (sample->m_Samples!=NULL) + { + free(sample->m_Samples); + sample->m_Samples = NULL; + } + + free(sample); + + return NC_SUCCESS; +} + +/**************************************** +ConvertNikonCurveData: + The main driver. Takes a filename and + processes the curve, if possible. + + fileName - The file to process. +*****************************************/ +int ConvertNikonCurveData(char *inFileName, char *outFileName, + unsigned int samplingRes, unsigned int outputRes) +{ + //Load the curve data from the ncv/ntc file + NikonData data; + char tmpstr[1024]; + + if ( samplingRes <= 1 || outputRes <= 1 || samplingRes > MAX_RESOLUTION + || outputRes > MAX_RESOLUTION ) + { + nc_message(NC_SET_ERROR, "Error, sampling and output resolution" + "must be 1 <= res <= %u\n", MAX_RESOLUTION); + return NC_ERROR; + } + + //loads all the curve data. Does not allocate sample arrays. + if (LoadNikonData(inFileName, &data) != NC_SUCCESS) + { + return NC_ERROR; + } + + CurveSample *sample = CurveSampleInit(samplingRes, outputRes); + + //Cycle through all curves + int i; + for (i = 0; i < NUM_CURVE_TYPES; i++) + { + //Populates the samples array for the given curve + if (SampleToCameraCurve( &data.curves[i], sample) != NC_SUCCESS) + { + CurveSampleFree(sample); + return NC_ERROR; + } + + //rename output files + strncpy(tmpstr, outFileName, 1023); + tmpstr[1023] = '\0'; + //if the name has an extension, attempt to remove it + if (tmpstr[strlen(tmpstr)-4] == '.') + { + tmpstr[strlen(tmpstr)-4] = '\0'; + } + + switch(i) + { + case TONE_CURVE: + strncat(tmpstr, "_TONE.txt", 1023); + break; + + case RED_CURVE: + strncat(tmpstr, "_RED.txt", 1023); + break; + + case GREEN_CURVE: + strncat(tmpstr, "_GREEN.txt", 1023); + break; + + case BLUE_CURVE: + strncat(tmpstr, "_BLUE.txt", 1023); + break; + + default: + //should never get here + break; + } + + //print out curve data + if (SaveSampledNikonCurve(sample, tmpstr) != NC_SUCCESS) + { + CurveSampleFree(sample); + return NC_ERROR; + } + } + + //must be called when finished with a CurveSample structure + CurveSampleFree(sample); + + return NC_SUCCESS; +} + +/***************************************************** +FindTIFFOffset: + Moves the file pointer to the location + indicated by the TAG-TYPE pairing. This is meant just + as a helper function for this code. Uses elsewhere + may be limited. + + file - Nikon File ptr + num_entries - Number of entries to search through + tiff_tag - The tiff tag to match. + tiff_type - The tiff type to match. +*******************************************************/ +int FindTIFFOffset(FILE *file, unsigned short num_entries, unsigned short tiff_tag, unsigned short tiff_type) +{ + unsigned short tag = 0; + unsigned short type = 0; + unsigned int offset = 0; + + int i; + for(i = 0; i < num_entries; i++) + { + //get tag 2 bytes + tag = (fgetc(file)<<8)|fgetc(file); + if (tag == tiff_tag) + { + //get type 2 bytes + type = (fgetc(file)<<8)|fgetc(file); + if (type == tiff_type) //Type for length of field + { + //get length (4 bytes) + offset = (fgetc(file)<<24)|(fgetc(file)<<16)|(fgetc(file)<<8)|fgetc(file); + //get value\offset 4 bytes + offset = (fgetc(file)<<24)|(fgetc(file)<<16)|(fgetc(file)<<8)|fgetc(file); + fseek(file,offset,SEEK_SET); + return 1; //true; + } + } + else + { + //advance to next entry + fseek(file,10,SEEK_CUR); + } + } + return 0; //false; +} + +/******************************************************* +RipNikonNEFData: + Gets Nikon NEF data. For now, this is just the tone + curve data. + + infile - The input file + curve - data structure to hold data in. + sample_p - pointer to the curve sample reference. + can be NULL if curve sample is not needed. +********************************************************/ +int RipNikonNEFData(char *infile, CurveData *data, CurveSample **sample_p) +{ + unsigned short byte_order = 0; + unsigned short num_entries = 0; + unsigned short version = 0; + unsigned int offset = 0; + + //open the file + FILE *file = g_fopen(infile,"rb"); + + //make sure we have a valid file + if (file == NULL) + { + nc_message(NC_SET_ERROR, "Error opening '%s': %s\n", + infile, strerror(errno)); + return NC_ERROR; + } + + //gets the byte order + nc_fread(&byte_order,2,1,file); + byte_order = ShortVal(byte_order); + if (byte_order != 0x4d4d) + { + //Must be in motorola format if it came from a NIKON + nc_message(NC_SET_ERROR, + "NEF file data format is Intel. Data format should be Motorola.\n"); + return NC_ERROR; + } + + //get the version + //nc_fread(&version,2,1,file); + version = (fgetc(file)<<8)|fgetc(file); + if (version != 0x002a) + { + //must be 42 or not a valid TIFF + nc_message(NC_SET_ERROR, + "NEF file version is %u. Version should be 42.\n",version); + return NC_ERROR; + } + + //get offset to first IFD + offset = (fgetc(file)<<24)|(fgetc(file)<<16)|(fgetc(file)<<8)|fgetc(file); + //go to the IFD + fseek(file,offset,SEEK_SET); + //get number of entries + num_entries = (fgetc(file)<<8)|fgetc(file); + + //move file pointer to exif offset + if (!FindTIFFOffset(file,num_entries,TIFF_TAG_EXIF_OFFSET,TIFF_TYPE_LONG)) + { + nc_message(NC_SET_ERROR, + "NEF data entry could not be found with tag %u and type %u.\n", + TIFF_TAG_EXIF_OFFSET, TIFF_TYPE_LONG); + return NC_ERROR; + } + + //get number of entries + num_entries = (fgetc(file)<<8)|fgetc(file); + + //move file pointer to maker note offset + if (!FindTIFFOffset(file,num_entries,TIFF_TAG_MAKER_NOTE_OFFSET,TIFF_TYPE_UNDEFINED)) + { + nc_message(NC_SET_ERROR, + "NEF data entry could not be found with tag %u and type %u.\n", + TIFF_TAG_MAKER_NOTE_OFFSET, TIFF_TYPE_UNDEFINED); + return NC_ERROR; + } + + ////////////////////////////////////////////////////////////////////////// + //NOTE: At this point, this section of the file acts almost like another + // file header. Skip the first bytes, (which just say nikon with a + // few bytes at the end. Offsets from here on in are from the start + // of this section, not the start of the file. + ////////////////////////////////////////////////////////////////////////// + + //Check the name. If it isn't Nikon then we can't do anything with this file. + char name[6]; + nc_fread(name,6,1,file); + if (strcmp(name,"Nikon") != 0) + { + nc_message(NC_SET_ERROR, + "NEF string identifier is %s. Should be: Nikon.\n",name); + return NC_ERROR; + } + fseek(file,4,SEEK_CUR); + + //save the current file location, as all other offsets for this section run off this. + unsigned long pos = ftell(file); + + //get byte order (use a regular fread) + nc_fread(&byte_order,2,1,file); + byte_order = ShortVal(byte_order); + if (byte_order != 0x4d4d) + { + //Must be in motorola format or not from a Nikon + nc_message(NC_SET_ERROR, + "NEF secondary file data format is Intel. " + "Data format should be Motorola.\n"); + return NC_ERROR; + } + + //get version + version = (fgetc(file)<<8)|fgetc(file); + if (version != 0x002a) + { + nc_message(NC_SET_ERROR, + "NEF secondary file version is %u. Version should be 42.\n", + version); + return NC_ERROR; + } + + //get offset to first IFD + offset = (fgetc(file)<<24)| (fgetc(file)<<16)|(fgetc(file)<<8)|fgetc(file); + //go to the IFD (these offsets are NOT from the start of the file, + //just the start of the section). + fseek(file,pos+offset,SEEK_SET); + //get number of entries + num_entries = (fgetc(file)<<8)|fgetc(file); + + //move file position to tone curve data + if (!FindTIFFOffset(file,num_entries,TIFF_TAG_CURVE_OFFSET,TIFF_TYPE_UNDEFINED)) + { + nc_message(NC_SET_ERROR, + "NEF data entry could not be found with tag %u and type %u.\n", + TIFF_TAG_CURVE_OFFSET, TIFF_TYPE_UNDEFINED); + return NC_ERROR; + } + + offset = ftell(file); + return RipNikonNEFCurve(file, offset+pos, data, sample_p); +} + + +/******************************************************* +RipNikonNEFCurve: + The actual retriever for the curve data from the NEF + file. + + file - The input file. + infile - Offset to retrieve the data + curve - data structure to hold curve in. + sample_p - pointer to the curve sample reference. + can be NULL if curve sample is not needed. +********************************************************/ +int RipNikonNEFCurve(FILE *file, int offset, CurveData *data, + CurveSample **sample_p) +{ + int i; + + //seek to the offset of the data. Skip first two bytes (section isn't needed). + fseek(file, offset+2, SEEK_SET); + + memset(data,0,sizeof(CurveData)); + ///////////////////////////////////////////////// + //GET CURVE DATA + ///////////////////////////////////////////////// + //get box data and gamma + data->m_min_x = (double)fgetc(file)/255.0; + data->m_max_x = (double)fgetc(file)/255.0; + data->m_min_y = (double)fgetc(file)/255.0; + data->m_max_y = (double)fgetc(file)/255.0; + //16-bit fixed point. + data->m_gamma = (double)fgetc(file) + ((double)fgetc(file)/256.0); + + //DEBUG_PRINT("DEBUG: NEF SECTION SIZE -> %u\n",data->section_size); + DEBUG_PRINT("DEBUG: NEF X MIN -> %e\n",data->m_min_x); + DEBUG_PRINT("DEBUG: NEF X MAX -> %e\n",data->m_max_x); + DEBUG_PRINT("DEBUG: NEF Y MIN -> %e\n",data->m_min_y); + DEBUG_PRINT("DEBUG: NEF Y MAX -> %e\n",data->m_max_y); + //DEBUG_PRINT("DEBUG: NEF GAMMA (16-bit fixed point) -> %e\n",(data->m_gamma>>8)+(data->m_gamma&0x00ff)/256.0); + DEBUG_PRINT("DEBUG: NEF GAMMA -> %e\n",data->m_gamma); + // It seems that if there is no curve then the 62 bytes in the buffer + // are either all 0x00 (D70) or 0xFF (D2H). + // We therefore switch these values with the default values. + if (data->m_min_x==1.0) { + data->m_min_x = 0.0; + DEBUG_PRINT("DEBUG: NEF X MIN -> %e (changed)\n",data->m_min_x); + } + if (data->m_max_x==0.0) { + data->m_max_x = 1.0; + DEBUG_PRINT("DEBUG: NEF X MAX -> %e (changed)\n",data->m_max_x); + } + if (data->m_min_y==1.0) { + data->m_min_y = 0.0; + DEBUG_PRINT("DEBUG: NEF Y MIN -> %e (changed)\n",data->m_min_y); + } + if (data->m_max_y==0.0) { + data->m_max_y = 1.0; + DEBUG_PRINT("DEBUG: NEF Y MAX -> %e (changed)\n",data->m_max_y); + } + if (data->m_gamma==0.0 || data->m_gamma==255.0+255.0/256.0) { + data->m_gamma = 1.0; + DEBUG_PRINT("DEBUG: NEF GAMMA -> %e (changed)\n",data->m_gamma); + } + + //get number of anchor points (there should be at least 2 + nc_fread(&data->m_numAnchors,1,1,file); + DEBUG_PRINT("DEBUG: NEF NUMBER OF ANCHORS -> %u\n",data->m_numAnchors); + if (data->m_numAnchors==255) { + data->m_numAnchors = 0; + DEBUG_PRINT("DEBUG: NEF NUMBER OF ANCHORS -> %u (changed)\n", + data->m_numAnchors); + } + if (data->m_numAnchors > NIKON_MAX_ANCHORS) { + data->m_numAnchors = NIKON_MAX_ANCHORS; + DEBUG_PRINT("DEBUG: NEF NUMBER OF ANCHORS -> %u (changed)\n", + data->m_numAnchors); + } + + //convert data to doubles + for(i = 0; i < data->m_numAnchors; i++) + { + //get anchor points + data->m_anchors[i].x = (double)fgetc(file)/255.0; + data->m_anchors[i].y = (double)fgetc(file)/255.0; + } + + //The total number of points possible is 25 (50 bytes). + //At this point we subtract the number of bytes read for points from the max (50+1) + fseek(file,(51 - data->m_numAnchors*2),SEEK_CUR); + + //get data (always 4096 entries, 1 byte apiece) + DEBUG_PRINT("DEBUG: NEF data OFFSET -> %ld\n",ftell(file)); + + if (sample_p!=NULL) + { + // Sampling res is always 4096, and output res is alway 256 + *sample_p = CurveSampleInit(4096, 256); + + //get the samples + for(i = 0; i < 4096; i++) + { + (*sample_p)->m_Samples[i] = (unsigned int)fgetc(file); + } + } + + return NC_SUCCESS; +} + +/******************************* +main: + Uh....no comment. :) +********************************/ +#ifdef _STAND_ALONE_ + +int main(int argc, char* argv[]) +{ + //make sure we can continue processing + if (ProcessArgs(argc,argv) == NC_SUCCESS) + { + + //if we are in NEF mode, rip the curve out of the RAW file + if (program_mode == NEF_MODE) + { + NikonData data; + + //intiialze the structure to zero + memset(&data,0,sizeof(NikonData)); + + if (RipNikonNEFData(nikonFilename, &data.curves[TONE_CURVE], NULL) + != NC_SUCCESS) + { + return NC_ERROR; + } + + CurveSample *sample = CurveSampleInit(65536, 256); + + if (CurveDataSample(&data.curves[TONE_CURVE], sample) + != NC_SUCCESS) + { + CurveSampleFree(sample); + return NC_ERROR; + } + + if (SaveSampledNikonCurve(sample, exportFilename) != NC_SUCCESS) + { + CurveSampleFree(sample); + return NC_ERROR; + } + + if (SaveNikonCurveFile(&data.curves[TONE_CURVE], TONE_CURVE, + "outcurve.ncv",NCV_FILE, NIKON_VERSION_4_1)) + { + CurveSampleFree(sample); + return NC_ERROR; + } + + //This can also be used + if (SaveNikonDataFile(&data,"outcurve2.ncv",NCV_FILE, NIKON_VERSION_4_1)) + { + CurveSampleFree(sample); + return NC_ERROR; + } + + CurveSampleFree(sample); + } + //else, process a nikon curve file + else + { + //do the deed + ConvertNikonCurveData(nikonFilename,exportFilename, + standalone_samplingRes, standalone_outputRes); + } + } + return NC_SUCCESS; +} +#endif diff --git a/src/common/nikon_curve.h b/src/common/nikon_curve.h new file mode 100644 index 000000000000..99f27949c9d1 --- /dev/null +++ b/src/common/nikon_curve.h @@ -0,0 +1,717 @@ +/*************************************************** + nikon_curve.h - read Nikon NTC/NCV files + + Copyright 2004-2008 by Shawn Freeman, Udi Fuchs + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + +****************************************************/ + +/*************************************************** + + This program reads in a Nikon NTC/NCV file, + interperates it's tone curve, and writes out a + simple ascii file containing a table of interpolation + values. + + You'll note that this has been written in way that can be used in a standalone program or + incorporated into another program. You can use the functions seperately or + you can just call ConvertNikonCurveData with an input and output file. + + I've tried to document the code as clearly as possible. Let me know if you + have any problems! + + Thanks goes out to Udi Fuchs for wanting to incorporate nikon curve loading into his program. + This will make GIMP just that much better. :) + + NOTES: + 08/06/2005 (Shawn Freeman) + The last endian fix would not have worked correctly. The new fix should correct the + endianess problem. + + The fix uses a new function DetermineEndianess to assign function pointers the correct + swapping function based on the endianess of the machine. + + For programs that use this code on big endian machines, it is important that this function + be called. So for now, each function that reads or writes data to a file will call this + function. + + + 07/27/2005 (Shawn Freeman) + Someone discovered the dreaded endianess bug. NTC and NCV files are written in little-endian + format, so when a Mac (or other big endian machine) tries to read in the file it blows up. + + There doesn't appear to be a way to determine the endianess of a machine a runtime (at + least not in an ANSI compliant way). The solution? Custom read and write functions that + can handle the endianess. + + The ENDIANESS define can be set to either LITTLE_ENDIAN (compiles code for little endian + machines), BIG_ENDIAN (compiles code for big endian machines), or UNKNOWN_ENDIAN (compiles + code that dynamically determines endianess and runs appropriate code). + + Performance isn't much of an issues since files are small, but it is more efficient + to compile with the endianess set than to leave it at unknown. + + Unfortunately, I don't have a way to test this code. But it should do the trick. + + 06/20/2005 (Udi Fuchs) + Added the function CurveDataReset() + + 06/14/2005 (Udi Fuchs) + Added the function CurveDataSetPoint() + + 06/13/2005 (Shawn Freeman) + Added a couple of explicit casts to keep some compilers from complaining. + At this point, I'd say this code is decent enough to classified as 1.0. Right now + there are no outstanding bugs left that I am aware of. The code has been fairly well + documented and has a significant amount of error checking built in. + + There are still some experimental code in here that could be removed, along with options + for compiling on differnt compilers. + + I don't have as much time as I used to have, due to a new baby and everything.But + I should still have time to do some tweaks, features, and fixes if necessary. + + Enjoy! + + 06/10/2005 (Udi Fuchs) + Removed the name "Nikon" from general structures/functions + NikonAnchorPoint -> CurveAnchorPoint + NikonCurve -> CurveData + SampleNikonCurve() -> CurveDataSample() + FreeNikonCurve() -> CurveSampleFree() + Moved the sampling data to the CurveSample structure, now nikon_curve + and UFRaw can use the sampel CurveData structure. + Removed unused functions RemoveRedundantData(), ResampleNEFCurve() + Curve sampling changed - the curve before first point and after + the last point is flattened. + + 04/16/2005 Removed malloc calls and replaced them with fixed arrays. The Nikon camera + does not support any more than 20 anchors for their curve. This speeds up the + code a little bit. + + This version also supports the latest version of the NTC/NCV file format. + + 03/18/2005 The last remaining incompatibility between MSVC and gcc: + + #define vsnprintf _vsnprintf + + Other Fixes: + 1. Fixed a bug that was incorrectly writing out minimum y-axis values for color curves. + + 03/12/2005 I found some additional bugs when writing out ntc/ncv files that would cause the Nikon + software to crash. The problem was two-fold. + + First, all four curves in the file must contain some information. The SaveNikonCurveFile now + checks to make sure that a curve has good data. If it doesn't, then defaults are written in their + place. + + Why would a curve contain incorrect information? If you're only working with a tone curve in a NikonData + structure, the color curves would not contain any information. The save function automatically corrects + this. + + Second, the version number. Right now, the only version this code supports is Nikon 4.1. So while the + save function takes the version as a paramter, internally it is ignored and forced to 4.1. + + A second version of the SaveNikonCurveFile function now takes a single curve as an argument and uses + defaults for the rest of the curves in the file. The function allows you to specify which curve your + saving (TONE_CURVE, RED_CURVE, GREEN_CURVE, or BLUE_CURVE). Typically, the curve would be tone. + + + 03/11/2005 Udi added some debugging code for use with UFRaw. Unfortunately, the debugging code uses + macros with variable arguments, which are not supported by all compilers (namely MSVC). + I added the following: + + __MSVC__ : Define this if you are compiling with MSVC. This enables code that allows + the program to still print out debug information, though not quite as robust + as with using gcc. + + __WITH_UFRAW__ : Define this if this code is compiling with UFRaw, This allows for connecting + out to the UFRaw error handler. + + In regards to these flags, the code has been changed around a little bit. I also moved some code + to more "appropriate" locations, in keeping with the layout of these files. + + I also renamed the SaveNikonCurve function to SaveNikonCurveFile for clarity. I also fixed a bug + in the function to handle NikonData structures with only one used curve. This allows extracting + the curve from an NEF file and saving it to an NTC/NCV file. This would also allow a curve editor + to write out files successfully, even if they only use tone curves. + + 03/05/2005 Added a fix to allow SampleNikonCurve to be called more than once so that a programmer + can resample the curve. This would be used when extracting a curve from an NEF file + and rebuilding the NTC/NCV file curve from the data. + + Also added a fix that allows for curves with no anchors, just box coordinates. + + The ResampleCurve function has been renamed to ResampleNEFCurve. This is to avoid confusion. + THIS FUNCTION SHOULD NOT BE USED FOR STANDARD NTC/NCV/NEF CURVE GENERATION. This function is + only useful for building a smooth curve from the 4096 byte tone curve table in an NEF file. + This function may end up going away if it's not found useful. + + 03/04/2005 Added a new function called ResampleCurve that allows the programmer to resample curves + that are contained in a NikonCurve structure. A cubic spline interpolator is used in the + resampling for the smoothest possible curve. This functions was added at the request of + Udi Fuchs to allow UFRaw to read in and resample curves stored in NEF files. + + A word of caution when using this function. While the function stores all of it's results + in the passed in curve structure, those results may not be compatible with the camera and/or + the NTC/NCV files. Specifically, this function will most likely contain anchor counts much + greater than 20, which is the maximum. It is NOT RECOMMENDED that you try and save out to + NTC/NCV format after running this function unless you're absolutely sure you know what you + are doing. + + With that out of the way, this function is most useful to create a smoother curve than what + comes from the camera and the NTC/NCV curve files. + + 02/09/2005 Found a fixed a subtle, yet glaring bug in the curve sampling functions. The sampling + functions were not obeying the limits set in the ncv/ntc files. Loading one of these + files that contained any start and end points other than 0 and 1 would result in + the code sampling from the extrapolated parts of the spline curve instead of the + interpolated curve. This actually resulted in a crash in UFRaw, since it was returning + huge numbers as a result of signed to unsigned conversion. + + 02/06/2005 Finally figured out the mathematical relationship (or a close approximation) between + the Nikon curves and curves imbedded in the NEF files. General form is: + + x = a*x + b*ln(cx+1)/ln(dx+2) + + I've found this gives a very good match for the curves. The new function to use to get + this capability is SampleToCameraCurve. This does a standard curve sample then runs the + value through the above transform. Quite useful if you want to force-embed a particular + curve into a batch of NEF files. + + + 02/01/2005 Fixed some issues to better operate with UFRaw. Removed macros and replaced with + with byte reading and shifting (to avoid including windows crud). + + The function RipNEFCurve now uses the standard Nikon Curve structure. The NEFData + structure is now obsolete, along with the save and free functions (Removed). + + 01/29/2005 Added support for getting the tone curve data out of an NEF file. This will generate + a file similar to the other curve data files generated by this progrom in standalone + mode. + + A new command line option has been added to specify NEF processing (-nef filename). The + NEF capabilities have only been tested with Nikon D70 NEF's. + + 01/14/2005 Fixed some bugs. The static data declarations in the header were not declared + static, resulting in errors when trying to compile into another program. + + Added a function to remove redundant calculated curve data and placing it in a + an allocated array. This should help facilitate a no diska access interface to UFRaw. + + Tweaked a few lines that were either not compiling or had warnings when compiled with + gcc. + + Changed the unsigned shorts to unsigned ints just for the sake of it. May be useful if + anyone wants to do bor than 16 bits of sampling. Also means that the program uses a + bit more memory when processing. + + 01/11/2005 Merged changes from Udi. Added the ability to set both sampling resolution and + output resolution in LoadNikonData. For example, you can set the sampling resolution + to 65536 and output resolution to 256. + + I also added a #define (_STAND_ALONE_) for compiling as a standalone program.Undef + if you just want to use the functions. + + Additional command line options are avalable: + -sr Specifies sampling resolution + -or Specifies output resolution + + Some "how to use" info comes up if there are no arguments on the command line. + If these aren't specified, defaults are used. This only applies to standalone. + + 01/08/2005 Removed all code relating to based on Numerical Recipes since it requires + a soul-sucking liscense. The spline calculation code has been replaced by + the freely available code from John Bukardt.I only included the functions from + the library that were necessary to avoid bloat. The code has also been converted + to using standard C. The original code is available from: + + http://www.csit.fsu.edu/~burkardt/cpp_src/spline/spline.html + + New Features: + 1. Heavy error checking, with verbose output. + 2. Supports writing out to NTC and NCV files. + 2. In debug mode, lots of information is displayed to the console. + 3. Supports exporting Tone and RGB curves from NCV and NTC files. + 4. Removed non-ANSI functions, data declarations etc. + 4. Alot of tweaks. + 5. Alot more testing ;) + + The standalone prorgam outputs 4 files, each ending in the name of the curve data + it contains. If you use this like a library, you can make it do whatever you want. + + 01/06/2005 This is version .01. The gamma calculation might not match + what Nikon's Capture does. It only supports NTC files right now. + NCV format will be supported shortly, but will not impact + the structure of this "library". Other additional functionality + will be to support the color curve sections of NTC/NCV files. + + @author: Shawn Freeman 1/06/2005 + @liscense: GNU GPL +****************************************************/ +#ifndef _NIKON_CURVE_H +#define _NIKON_CURVE_H + +#include + +#define NC_VERSION "1.2" +#define NC_DATE "2005-08-06" + +////////////////////////////////////////// +//COMPILER CONTROLS +////////////////////////////////////////// +//Undef this if you don't want to use the stand-alone program +//This is mainly for debugging purposes +//#define _STAND_ALONE_ + +//Define this if you are using Microsoft Visual C++. This enables code to +//deal with the fact the MSVC does not support variable argument macros. +//#define __MSVC__ + +//the only remaining incompatibility between MSVC and gcc +#ifdef __MSVC__ + #define vsnprintf _vsnprintf +#endif + +//Define this if using with UFRaw +//#define __WITH_UFRAW__ + +//Flags used to determine what file we're trying to process. +//Should only be used in standalone mode. +#ifdef _STAND_ALONE_ + +#define CURVE_MODE 0 +#define NEF_MODE 1 + +#endif + + +/******************************************************************************* +Information regarding the file format. + +Section Headers: + +Order of Box Data: Left x, Right x, Midpoint x (gamma), Bottom y, Top y + +Order of Anchor Data: Start x, Start y, Anchor 1 x, Anchor 1 y, ... , End x, End y + +Anchor Point Data: This is aligned on 8 byte boundries. However, the section must + end on a 16 byte boundary, which means an 8 byte pad is added. +********************************************************************************/ + +//DEFINES FOR WRITING OUT DATA (for ntc/ncv files) +#define NCV_HEADER_SIZE 0x3E //Combined header length for an NCV file +#define NCV_SECOND_FILE_SIZE_OFFSET 0x3F //4 bytes (int). File size - NCV_header +#define NCV_UNKNOWN_HEADER_DATA 0x002 //2 bytes. (?) +#define NCV_SECOND_HEADER_LENGTH 23 +#define NCV_FILE_TERMINATOR_LENGTH 23 + +#define NTC_FILE_HEADER_LENGTH 0x10 //16 bytes. Doesn't change +//This seemed to change when Nikon released an updated capture program +//from 4.1 to 4.2. This may be an int but not sure. +#define NCV_PATCH_OFFSET 0x3D //2 bytes(?) +#define NTC_PATCH_OFFSET 0x10 //2 bytes(?) +#define FILE_SIZE_OFFSET 0x12 //4 bytes (int). File size - header. +#define NTC_VERSION_OFFSET 0x16 //4 bytes (int).(?) + //9 byte pad(?) + //16 bytes. Another section header goes here. + +//From here down repeats for every section +#define NTC_SECTION_TYPE_OFFSET 0x00 //4 bytes (int) (0,1,2,3) + +#define NTC_UNKNOWN 0x05 //2 bytes. Doesn't change but not sure what they do (0x03ff) +#define NTC_UNKNOWN_DATA 0x3FF // + +#define NTC_RED_COMPONENT_OFFSET 0x08 //4 bytes (int) (0-255) +#define NTC_GREEN_COMPONENT_OFFSET 0x0C //4 bytes (int) (0-255) +#define NTC_BLUE_COMPONENT_OFFSET 0x0F //4 bytes (int) (0-255) + //12 byte pad all zeros(align to 16?) + +#define NTC_RED_WEIGHT_OFFSET 0x1F //4 bytes (int) (0-255) +#define NTC_GREEN_WEIGHT_OFFSET 0x23 //4 bytes (int) (0-255) +#define NTC_BLUE_WEIGHT_OFFSET 0x27 //4 bytes (int) (0-255) + +#define END_ANCHOR_DATA_PAD_LENGTH 0x08 //Always all zeros +#define NTC_SECTION_HEADER_LENGTH 0x10 //Doesn't change + + +//DEFINES FOR READING IN DATA +#define HEADER_SIZE 0x10 //First characters may be unicode Japanese? + +#define NTC_BOX_DATA 0x5C //Start of box data +#define NTC_NUM_ANCHOR_POINTS 0x84 //Number of anchor points plus 2 for start and end points +#define NTC_ANCHOR_DATA_START 0x88 //Beginning of anchor point data + +#define NCV_BOX_DATA 0x89 //Start of box data +#define NCV_NUM_ANCHOR_POINTS 0xB2 //Number of anchor points plus 2 for start and end points +#define NCV_ANCHOR_DATA_START 0xB5 //Beginning of anchor point data + +//array indices to retrive data +#define PATCH_DATA 0 +#define BOX_DATA 1 +#define NUM_ANCHOR_POINTS 2 +#define ANCHOR_DATA 3 + +//Some data sections sizes for calculating offsets +#define NEXT_SECTION_BOX_DATA_OFFSET 0x43 //after the anchor data, this is the offset to + //the beginning of the next section's box data + +#define NUM_POINTS_TO_ANCHOR_OFFSET 0x03 //number of bytes from the number of anchor points + //byte to the start of anchor data. +//Nikon version defines +#define NIKON_VERSION_4_1 0x00000401 +#define NIKON_PATCH_4 0x04ff +#define NIKON_PATCH_5 0x05ff +#define NIKON_MAX_ANCHORS 20 + +//file types +#define NTC_FILE 0 +#define NCV_FILE 1 +#define NUM_FILE_TYPES 2 + +//Curve Types +#define TONE_CURVE 0 +#define RED_CURVE 1 +#define GREEN_CURVE 2 +#define BLUE_CURVE 3 +#define NUM_CURVE_TYPES 4 + +//Maximum resoltuion allowed due to space considerations. +#define MAX_RESOLUTION 65536 + +////////////////////////////// +//NEF/TIFF MACROS AND DEFINES +////////////////////////////// +#define TIFF_TAG_EXIF_OFFSET 34665 +#define TIFF_TAG_MAKER_NOTE_OFFSET 37500 +#define TIFF_TAG_CURVE_OFFSET 140 + +#define TIFF_TYPE_UNDEFINED 7 +#define TIFF_TYPE_LONG 4 + + +//////////////////////// +//////////////////////// +//ERROR HANDLING +//////////////////////// +//////////////////////// +#define NC_SUCCESS 0 +#define NC_ERROR 100 +#define NC_WARNING 104 +#define NC_SET_ERROR 200 + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//DATA STRUCTURES +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/********************************************************** +CurveData: + Structure for the curve data inside a NTC/NCV file. +***********************************************************/ +typedef struct +{ + double x; + double y; +} CurveAnchorPoint; + +typedef struct +{ + char name[80]; + + //Type for this curve + unsigned int m_curveType; + + //Box data + double m_min_x; + double m_max_x; + double m_min_y; + double m_max_y; + double m_gamma; + + //Number of anchor points + unsigned char m_numAnchors; + + //contains a list of anchors, 2 doubles per each point, x-y format + //max is 20 points + CurveAnchorPoint m_anchors[NIKON_MAX_ANCHORS]; + +} CurveData; + +typedef struct +{ + //Number of samples to use for the curve. + unsigned int m_samplingRes; + unsigned int m_outputRes; + + //Sampling array + unsigned short int *m_Samples; // jo: changed to short int to save memory + +} CurveSample; + + +/******************************************** +NikonPoint: + Simple point structure. Used for storing +reduced data from a sampled curve. +*********************************************/ +typedef struct +{ + unsigned int x; + unsigned int y; +} NikonPoint; + +/********************************************* +NikonData: + Overall data structure for Nikon file data +**********************************************/ +typedef struct +{ + //Number of output points + int m_fileType; + unsigned short m_patch_version; + CurveData curves[4]; +} NikonData; + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//FUNCTIONS +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/************************************************************ +nc_message: + The Nikon Curve message handler. Udi Fuchs created this +to make the error handling consistent across the code. + + code - Message code + format - The message to format + ... - variable arguments +**************************************************************/ + +void nc_message(int code, char *format, ...); +void DEBUG_PRINT(char *format, ...); + +/******************************************************************* + d3_np_fs: + Helper function for calculating and storing tridiagnol matrices. + Cubic spline calculations involve these types of matrices. +*********************************************************************/ +double *d3_np_fs ( int n, double a[], double b[] ); + +/******************************************************************* + spline_cubic_set: + spline_cubic_set gets the second derivatives for the curve to be used in + spline construction + + n = number of control points + t[] = x array + y[] = y array + ibcbeg = initial point condition (see function notes). + ybcbeg = beginning value depending on above flag + ibcend = end point condition (see function notes). + ybcend = end value depending on above flag + + returns the y value at the given tval point +*********************************************************************/ +double *spline_cubic_set ( int n, double t[], double y[], int ibcbeg, + double ybcbeg, int ibcend, double ybcend ); +/******************************************************************* + spline_cubic_val: + spline_cubic_val gets a value from spline curve. + + n = number of control points + t[] = x array + tval = x value you're requesting the data for, can be anywhere on the interval. + y[] = y array + ypp[] = second derivative array calculated by spline_cubic_set + ypval = first derivative value of requested point + yppval = second derivative value of requested point + + returns the y value at the given tval point +*********************************************************************/ +double spline_cubic_val ( int n, double t[], double tval, double y[], + double ypp[], double *ypval, double *yppval ); + + +/********************************************* +LoadNikonData: + Loads a curve from a Nikon ntc or ncv file. + + fileName - The filename. + curve - Pointer to curve struct to hold the data. + resolution - How many data points to sample from the curve +**********************************************/ +int LoadNikonData(char *fileName, NikonData *data); + +/********************************************* +CurveDataSample: + Samples from a spline curve constructed from + the curve data. + + curve - Pointer to curve struct to hold the data. + sample - Pointer to sample struct to hold the data. +**********************************************/ +int CurveDataSample(CurveData *curve, CurveSample *sample); + +/********************************************* + * CurveDataReset: + * Reset curve to straight line but don't touch the curve name. + **********************************************/ +void CurveDataReset(CurveData *curve); + +/********************************************* + * CurveDataIsTrivial: + * Check if the curve is a trivial linear curve. + ***********************************************/ +int CurveDataIsTrivial(CurveData *curve); + +/********************************************* + CurveDataSetPoint: + Change the position of point to the new (x,y) coordinate. + The end-points get a special treatment. When these are moved all the + other points are moved together, keeping their relative position constant. +**********************************************/ +void CurveDataSetPoint(CurveData *curve, int point, double x, double y); + +/**************************************************** +SampleToCameraCurve: + Transforms the curve generated by sampling the + spline interpolator into the curve that is used by + the camera. + + This is a special function. While the function places + no special restrictions on sampling resolution or + output resolution, it should be noted that Nikon D70 + camera curve is 4096 entries of 0-255. + + If you intend on using this function as such, you should + set the sampling resolution and output resolution + accordingly. + + curve - The Nikon curve to sample and transform. +*****************************************************/ +int SampleToCameraCurve(CurveData *curve, CurveSample *sample); + +/************************************************************ +SaveNikonDataFile: + Savess a curve to a Nikon ntc or ncv file. + + data - A NikonData structure containing info of all the curves. + fileName - The filename. + filetype - Indicator for an NCV or NTC file. + version - The version of the Nikon file to write + +NOTE: The only version tested is Nikon 4.1 anything + other than this may result in unpredictable behavior. + For now, the version passed in is ignored and is forced + to 4.1. +**************************************************************/ +int SaveNikonDataFile(NikonData *data, char *outfile, int filetype, int version); + +/************************************************************ +SaveNikonCurveFile: + Saves out curves to a Nikon ntc or ncv file. This function + takes a single curve and uses defaults for the other curves. + Typically, the curve used is the tone curve. + + curve - A NikonCurve structure. This is usually the tone curve + curve_type - The curve type (TONE_CURVE, RED_CURVE, etc.) + fileName - The filename. + filetype - Indicator for an NCV or NTC file. + version - The version of the Nikon file to write + +NOTE: The only version tested is Nikon 4.1 anything + other than this may result in unpredictable behavior. + For now, the version passed in is ignored and is forced + to 4.1. + + This function is just a helper function that allows the user + to just carry around a single curve. +**************************************************************/ +int SaveNikonCurveFile(CurveData *curve, int curve_type, char *outfile, + int filetype, int version); + +/********************************************* +SaveSampledNikonCurve: + Saves a sampling from a curve to text file to + be processed by UFRaw. + + sample - Pointer to the sampled curve. + fileName - The filename. +**********************************************/ +int SaveSampledNikonCurve(CurveSample *sample, char *outfile); + +/***************************************************** +FindTIFFOffset: + Moves the file pointer to the location + indicated by the TAG-TYPE pairing. This is meant just + as a helper function for this code. Uses elsewhere + may be limited. + + file - Nikon File ptr + num_entries - Number of entries to search through + tiff_tag - The tiff tag to match. + tiff_type - The tiff type to match. +*******************************************************/ +int FindTIFFOffset(FILE *file, unsigned short num_entries, unsigned short tiff_tag, + unsigned short tiff_type); + +/******************************************************* +RipNikonNEFData: + Gets Nikon NEF data. For now, this is just the tone + curve data. This is more of a helper function for running + in stand-alone. This function basically finds the correct + file offset, and then calls RipNikonNEFCurve. + + infile - The input file + curve - data structure to hold data in. + sample_p - pointer to the curve sample reference. + can be NULL if curve sample is not needed. +********************************************************/ +int RipNikonNEFData(char *infile, CurveData *data, CurveSample **sample_p); + +/******************************************************* +RipNikonNEFCurve: + The actual retriever for the curve data from the NEF + file. + + file - The input file. + infile - Offset to retrieve the data + curve - data structure to hold curve in. + sample_p - pointer to the curve sample reference. + can be NULL if curve sample is not needed. +********************************************************/ +int RipNikonNEFCurve(FILE *file, int offset, CurveData *data, + CurveSample **sample_p); + +/******************************************************* +CurveSampleInit: + Init and allocate curve sample. +********************************************************/ +CurveSample *CurveSampleInit(unsigned int samplingRes, unsigned int outputRes); + +/******************************************************* +CurveSampleFree: + Frees memory allocated for this curve sample. +********************************************************/ +int CurveSampleFree(CurveSample *sample); + +/**************************************** +ConvertNikonCurveData: + The main driver. Takes a filename and + processes the curve, if possible. + + fileName - The file to process. +*****************************************/ +int ConvertNikonCurveData(char *inFileName, char *outFileName, unsigned int samplingRes, unsigned int outputRes); + +#endif + + diff --git a/src/control/control.c b/src/control/control.c new file mode 100644 index 000000000000..4535a1b375c7 --- /dev/null +++ b/src/control/control.c @@ -0,0 +1,954 @@ +#include "control/control.h" +#include "library/library.h" +#include "develop/develop.h" +#include "common/darktable.h" +#include "gui/gtk.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef HAVE_QWERTY +#define KEY_LEFT GDK_a +#define KEY_RIGHT GDK_e +#define KEY_UP GDK_comma +#define KEY_DOWN GDK_o +#define KEY_FIT GDK_apostrophe +#define KEY_SWITCH GDK_period +#define KEY_TOGGLE_FULLSCREEN GDK_f +#define KEY_LEAVE_FULLSCREEN GDK_Escape +#else +#define KEY_LEFT GDK_a +#define KEY_RIGHT GDK_d +#define KEY_UP GDK_w +#define KEY_DOWN GDK_s +#define KEY_FIT GDK_q +#define KEY_SWITCH GDK_e +#define KEY_TOGGLE_FULLSCREEN GDK_f +#define KEY_LEAVE_FULLSCREEN GDK_Escape +#endif + +void dt_ctl_settings_init(dt_control_t *s) +{ + // init global defaults. + pthread_mutex_init(&(s->global_mutex), NULL); + pthread_mutex_init(&(s->image_mutex), NULL); + + char *homedir = getenv("HOME"); + snprintf(s->global_settings.dbname, 512, "%s/.darktabledb", homedir); + + s->global_settings.version = DT_VERSION; + + s->global_settings.gui = DT_LIBRARY; + s->global_settings.gui_fullscreen = 0; + // expand everything + // except top/bottm, retract everything but the lib button in lib mode + // retract lib button in dev mode + s->global_settings.gui_top = s->global_settings.gui_bottom = 0; + s->global_settings.gui_left = s->global_settings.gui_right = -1; + s->global_settings.gui_export = + s->global_settings.gui_library = 1<global_settings.gui_navigation = + s->global_settings.gui_history = s->global_settings.gui_histogram = + s->global_settings.gui_tonecurve = s->global_settings.gui_gamma = + s->global_settings.gui_hsb = 1<global_settings.lib_zoom = DT_LIBRARY_MAX_ZOOM; + s->global_settings.lib_zoom_x = 0.0f; + s->global_settings.lib_zoom_y = 0.0f; + s->global_settings.lib_center = 0; + s->global_settings.lib_pan = 0; + s->global_settings.lib_track = 0; + s->global_settings.lib_image_mouse_over_id = -1; + + s->global_settings.dev_closeup = 0; + s->global_settings.dev_zoom_x = 0; + s->global_settings.dev_zoom_y = 0; + s->global_settings.dev_zoom = DT_ZOOM_FIT; + + s->global_settings.dev_export_format = DT_DEV_EXPORT_JPG; + + strncpy(s->global_settings.dev_op, "original", 20); + + s->global_settings.dev_gamma_linear = 0.1; + s->global_settings.dev_gamma_gamma = 0.45; + + s->image_settings.dev_gamma_linear = 0.1; + s->image_settings.dev_gamma_gamma = 0.45; + + s->image_settings.tonecurve_preset = 0; + s->image_settings.tonecurve_x[0] = s->image_settings.tonecurve_y[0] = 0.0; + s->image_settings.tonecurve_x[1] = s->image_settings.tonecurve_y[1] = 0.08; + s->image_settings.tonecurve_x[2] = s->image_settings.tonecurve_y[2] = 0.4;//0.3; + s->image_settings.tonecurve_x[3] = s->image_settings.tonecurve_y[3] = 0.6;//0.7; + s->image_settings.tonecurve_x[4] = s->image_settings.tonecurve_y[4] = 0.92; + s->image_settings.tonecurve_x[5] = s->image_settings.tonecurve_y[5] = 1.0; + + memcpy(&(s->global_defaults), &(s->global_settings), sizeof(dt_ctl_settings_t)); + memcpy(&(s->image_defaults), &(s->image_settings), sizeof(dt_ctl_image_settings_t)); +} + +int dt_control_load_config(dt_control_t *c) +{ + int rc; + sqlite3_stmt *stmt; + rc = sqlite3_prepare_v2(darktable.db, "select settings from settings", -1, &stmt, NULL); + if(rc == SQLITE_OK && sqlite3_step(stmt) == SQLITE_ROW) + { + pthread_mutex_lock(&(darktable.control->global_mutex)); + darktable.control->global_settings.version = -1; + const void *set = sqlite3_column_blob(stmt, 0); + int len = sqlite3_column_bytes(stmt, 0); + if(len == sizeof(dt_ctl_settings_t)) memcpy(&(darktable.control->global_settings), set, len); + rc = sqlite3_finalize(stmt); + + if(darktable.control->global_settings.version != DT_VERSION) + { + fprintf(stderr, "[load_config] wrong version %d (should be %d), substituting defaults.\n", darktable.control->global_settings.version, DT_VERSION); + memcpy(&(darktable.control->global_settings), &(darktable.control->global_defaults), sizeof(dt_ctl_settings_t)); + pthread_mutex_unlock(&(darktable.control->global_mutex)); + // revert to matching version: + rc = sqlite3_prepare_v2(darktable.db, "update settings set settings = ?1 where rowid = 1", -1, &stmt, NULL); + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_bind_blob(stmt, 1, &(darktable.control->global_defaults), sizeof(dt_ctl_settings_t), SQLITE_STATIC); + rc = sqlite3_step(stmt); + rc = sqlite3_finalize(stmt); + DT_CTL_SET_GLOBAL(gui, DT_LIBRARY); + dt_control_restore_gui_settings(DT_LIBRARY); + } + else + { + pthread_mutex_unlock(&(darktable.control->global_mutex)); + DT_CTL_SET_GLOBAL(gui, DT_LIBRARY); + dt_control_restore_gui_settings(DT_LIBRARY); + // TODO: get last from sql query! + // dt_film_roll_open(darktable.library->film, film_id); + // weg: dt_film_roll_import(darktable.library->film, darktable.control->global_settings.lib_last_film); + int width, height; + DT_CTL_GET_GLOBAL(width, gui_w); + DT_CTL_GET_GLOBAL(height, gui_h); + GtkWidget *widget = glade_xml_get_widget (darktable.gui->main_window, "main_window"); + gtk_window_resize(GTK_WINDOW(widget), width, height); + } + } + else + { // db not yet there, create it + rc = sqlite3_finalize(stmt); + rc = sqlite3_exec(darktable.db, "create table settings (settings blob)", NULL, NULL, NULL); + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_exec(darktable.db, "create table film_rolls (id integer primary key, datetime_accessed char(20), folder varchar(1024))", NULL, NULL, NULL); + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_exec(darktable.db, "create table images (id integer primary key, film_id integer, width int, height int, filename varchar(256), maker varchar(30), model varchar(30), exposure real, aperture real, iso real, focal_length real, datetime_taken char(20), integer flags, foreign key(film_id) references film_rolls(id))", NULL, NULL, NULL); + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_exec(darktable.db, "create table mipmaps (imgid int, level int, data blob, primary key(imgid, level), foreign key(imgid) references images(id))", NULL, NULL, NULL); + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_exec(darktable.db, "create table selected_images (imgid integer, foreign key(imgid) references images(id))", NULL, NULL, NULL); + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_exec(darktable.db, "create table history (imgid integer, num integer, hash integer, operation char(20), op_params blob, settings blob, foreign key(imgid) references images(id))", NULL, NULL, NULL); + HANDLE_SQLITE_ERR(rc); + + // TODO: - table tags "tag str" "key#" + // TODO: - table frequency tagXtag? + // TODO: - table tag X film_roll + // TODO: - table tag X image + rc = sqlite3_prepare_v2(darktable.db, "insert into settings (settings) values (?1)", -1, &stmt, NULL); + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_bind_blob(stmt, 1, &(darktable.control->global_defaults), sizeof(dt_ctl_settings_t), SQLITE_STATIC); + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_step(stmt); + rc = sqlite3_finalize(stmt); + DT_CTL_SET_GLOBAL(gui, DT_LIBRARY); + dt_control_restore_gui_settings(DT_LIBRARY); + } + return 0; +} + +int dt_control_write_config(dt_control_t *c) +{ + dt_ctl_gui_mode_t gui; + DT_CTL_GET_GLOBAL(gui, gui); + dt_control_save_gui_settings(gui); + + GtkWidget *widget = glade_xml_get_widget (darktable.gui->main_window, "main_window"); + DT_CTL_SET_GLOBAL(gui_x, widget->allocation.x); + DT_CTL_SET_GLOBAL(gui_y, widget->allocation.y); + DT_CTL_SET_GLOBAL(gui_w, widget->allocation.width); + DT_CTL_SET_GLOBAL(gui_h, widget->allocation.height); + + int rc; + sqlite3_stmt *stmt; + pthread_mutex_lock(&(darktable.control->global_mutex)); + rc = sqlite3_prepare_v2(darktable.db, "update settings set settings = ?1 where rowid = 1", -1, &stmt, NULL); + rc = sqlite3_bind_blob(stmt, 1, &(darktable.control->global_settings), sizeof(dt_ctl_settings_t), SQLITE_STATIC); + rc = sqlite3_step(stmt); + rc = sqlite3_finalize(stmt); + pthread_mutex_unlock(&(darktable.control->global_mutex)); + return 0; +} + +void dt_control_init(dt_control_t *s) +{ + dt_ctl_settings_init(s); + s->progress = 200.0f; + + pthread_cond_init(&s->cond, NULL); + pthread_mutex_init(&s->cond_mutex, NULL); + pthread_mutex_init(&s->queue_mutex, NULL); + + int k; for(k=0;kidle[k] = k; + s->idle_top = DT_CONTROL_MAX_JOBS; + s->queued_top = 0; + // start threads + s->num_threads = DT_CTL_WORKER_RESERVED + 6;//4; // TODO: omp_get procs equiv.! + s->thread = (pthread_t *)malloc(sizeof(pthread_t)*s->num_threads); + s->running = 1; + for(k=0;knum_threads;k++) + pthread_create(s->thread + k, NULL, dt_control_work, s); + for(k=0;kthread_res + k, NULL, dt_control_work_res, s); + s->new_res[k] = 0; + } + s->button_down = 0; +} + +void dt_control_cleanup(dt_control_t *s) +{ + pthread_mutex_lock(&s->cond_mutex); + s->running = 0; + pthread_mutex_unlock(&s->cond_mutex); + pthread_cond_broadcast(&s->cond); + gdk_threads_leave(); + int k; for(k=0;knum_threads;k++) + pthread_join(s->thread[k], NULL); + for(k=0;kthread_res[k], NULL); + gdk_threads_enter(); + pthread_mutex_destroy(&s->queue_mutex); + pthread_mutex_destroy(&s->cond_mutex); +} + +void dt_control_job_init(dt_job_t *j, const char *msg, ...) +{ +#ifdef DT_CONTROL_JOB_DEBUG + va_list ap; + va_start(ap, msg); + vsnprintf(j->description, DT_CONTROL_DESCRIPTION_LEN, msg, ap); + va_end(ap); +#endif +} + +void dt_control_job_print(dt_job_t *j) +{ +#ifdef DT_CONTROL_JOB_DEBUG + dt_print(DT_DEBUG_CONTROL, "%s", j->description); +#endif +} + +int32_t dt_control_run_job_res(dt_control_t *s, int32_t res) +{ + assert(res < DT_CTL_WORKER_RESERVED && res >= 0); + dt_job_t *j = NULL; + pthread_mutex_lock(&s->queue_mutex); + if(s->new_res[res]) j = s->job_res + res; + s->new_res[res] = 0; + pthread_mutex_unlock(&s->queue_mutex); + if(!j) return -1; + + dt_print(DT_DEBUG_CONTROL, "[run_job_res %d] ", (int)pthread_self()); + dt_control_job_print(j); + dt_print(DT_DEBUG_CONTROL, "\n"); + + j->execute(j); + return 0; +} + +int32_t dt_control_run_job(dt_control_t *s) +{ + dt_job_t *j; + int32_t i; + pthread_mutex_lock(&s->queue_mutex); + // dt_print(DT_DEBUG_CONTROL, "[run_job] %d\n", s->queued_top); + if(s->queued_top == 0) + { + pthread_mutex_unlock(&s->queue_mutex); + return -1; + } + i = s->queued[--s->queued_top]; + j = s->job + i; + pthread_mutex_unlock(&s->queue_mutex); + + dt_print(DT_DEBUG_CONTROL, "[run_job %d] ", dt_control_get_threadid()); + dt_control_job_print(j); + dt_print(DT_DEBUG_CONTROL, "\n"); + j->execute(j); + + pthread_mutex_lock(&s->queue_mutex); + assert(s->idle_top < DT_CONTROL_MAX_JOBS); + s->idle[s->idle_top++] = i; + pthread_mutex_unlock(&s->queue_mutex); + return 0; +} + +int32_t dt_control_add_job_res(dt_control_t *s, dt_job_t *job, int32_t res) +{ + // TODO: pthread cancel and restart in tough cases? + pthread_mutex_lock(&s->queue_mutex); + dt_print(DT_DEBUG_CONTROL, "[add_job_res] %d ", res); + dt_control_job_print(job); + dt_print(DT_DEBUG_CONTROL, "\n"); + s->job_res[res] = *job; + s->new_res[res] = 1; + pthread_mutex_unlock(&s->queue_mutex); + pthread_mutex_lock(&s->cond_mutex); + pthread_cond_broadcast(&s->cond); + pthread_mutex_unlock(&s->cond_mutex); + return 0; +} + +int32_t dt_control_add_job(dt_control_t *s, dt_job_t *job) +{ + int32_t i; + pthread_mutex_lock(&s->queue_mutex); + dt_print(DT_DEBUG_CONTROL, "[add_job] %d ", s->idle_top); + dt_control_job_print(job); + dt_print(DT_DEBUG_CONTROL, "\n"); + if(s->idle_top != 0) + { + i = --s->idle_top; + s->job[s->idle[i]] = *job; + s->queued[s->queued_top++] = s->idle[i]; + pthread_mutex_unlock(&s->queue_mutex); + } + else + { + pthread_mutex_unlock(&s->queue_mutex); + return -1; + } + + // notify workers + pthread_mutex_lock(&s->cond_mutex); + pthread_cond_broadcast(&s->cond); + pthread_mutex_unlock(&s->cond_mutex); + return 0; +} + +int32_t dt_control_get_threadid() +{ + int32_t threadid = 0; + while(darktable.control->thread[threadid] != pthread_self()) threadid++; + assert(threadid < darktable.control->num_threads); + return threadid; +} + +int32_t dt_control_get_threadid_res() +{ + int32_t threadid = 0; + while(darktable.control->thread_res[threadid] != pthread_self()) threadid++; + assert(threadid < DT_CTL_WORKER_RESERVED); + return threadid; +} + +void *dt_control_work_res(void *ptr) +{ + dt_control_t *s = (dt_control_t *)ptr; + int32_t threadid = dt_control_get_threadid_res(); + while(s->running) + { + // dt_print(DT_DEBUG_CONTROL, "[control_work] %d\n", threadid); + if(dt_control_run_job_res(s, threadid) < 0) + { + // wait for a new job. + int old; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old); + pthread_mutex_lock(&s->cond_mutex); + pthread_cond_wait(&s->cond, &s->cond_mutex); + pthread_mutex_unlock(&s->cond_mutex); + pthread_setcancelstate(old, NULL); + } + } + return NULL; +} + +void *dt_control_work(void *ptr) +{ + dt_control_t *s = (dt_control_t *)ptr; + // int32_t threadid = dt_control_get_threadid(); + while(s->running) + { + // dt_print(DT_DEBUG_CONTROL, "[control_work] %d\n", threadid); + if(dt_control_run_job(s) < 0) + { + // wait for a new job. + pthread_mutex_lock(&s->cond_mutex); + pthread_cond_wait(&s->cond, &s->cond_mutex); + pthread_mutex_unlock(&s->cond_mutex); + } + } + return NULL; +} + + +// ================================================================================ +// gui functions: +// ================================================================================ + +void dt_control_configure(int32_t width, int32_t height) +{ + darktable.control->tabborder = fmaxf(10, width/100.0); + // float tb = darktable.control->tabborder; + // re-configure all components: + // dt_dev_configure(darktable.develop, width - 2*tb, height - 2*tb); +} + +void *dt_control_expose(void *voidptr) +{ + darktable.control->gui_thread = pthread_self(); + while(1) + { + int width, height, pointerx, pointery; + // gdk_threads_enter(); + gdk_drawable_get_size(darktable.gui->pixmap, &width, &height); + GtkWidget *widget = glade_xml_get_widget (darktable.gui->main_window, "center"); + gtk_widget_get_pointer(widget, &pointerx, &pointery); + // gdk_threads_leave(); + + //create a gtk-independant surface to draw on + cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo_t *cr = cairo_create(cst); + + // TODO: control_expose: only redraw the part not overlapped by temporary control panel show! + // + float tb = fmaxf(10, width/100.0); + darktable.control->tabborder = tb; + darktable.control->width = width; + darktable.control->height = height; + + cairo_set_source_rgb (cr, .2, .2, .2); + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + cairo_rectangle(cr, 0, 0, width, height); + cairo_rectangle(cr, tb, tb, width-2*tb, height-2*tb); + cairo_fill(cr); + + cairo_save(cr); + cairo_translate(cr, tb, tb); + cairo_rectangle(cr, 0, 0, width - 2*tb, height - 2*tb); + cairo_clip(cr); + cairo_new_path(cr); + switch(darktable.control->global_settings.gui) + { + case DT_LIBRARY: + dt_library_expose(darktable.library, cr, width - 2*tb, height - 2*tb, pointerx-tb, pointery-tb); + break; + case DT_DEVELOP: + dt_dev_expose(darktable.develop, cr, width - 2*tb, height - 2*tb); + break; + default: + break; + } + cairo_restore(cr); + + // draw gui arrows. + cairo_set_source_rgb (cr, .6, .6, .6); + + cairo_move_to (cr, 0.0, height/2-tb); + cairo_rel_line_to (cr, 0.0, 2*tb); + cairo_rel_line_to (cr, tb, -tb); + cairo_close_path (cr); + cairo_fill(cr); + + cairo_move_to (cr, width, height/2-tb); + cairo_rel_line_to (cr, 0.0, 2*tb); + cairo_rel_line_to (cr, -tb, -tb); + cairo_close_path (cr); + cairo_fill(cr); + + cairo_move_to (cr, width/2-tb, height); + cairo_rel_line_to (cr, 2*tb, 0.0); + cairo_rel_line_to (cr, -tb, -tb); + cairo_close_path (cr); + cairo_fill(cr); + + cairo_move_to (cr, width/2-tb, 0); + cairo_rel_line_to (cr, 2*tb, 0.0); + cairo_rel_line_to (cr, -tb, tb); + cairo_close_path (cr); + cairo_fill(cr); + + // draw status bar, if any + if(darktable.control->progress < 100.0) + { + tb = fmaxf(20, width/40.0); + char num[10]; + cairo_rectangle(cr, width*0.4, height*0.85, width*0.2*darktable.control->progress/100.0f, tb); + cairo_fill(cr); + cairo_set_source_rgb(cr, 0., 0., 0.); + cairo_rectangle(cr, width*0.4, height*0.85, width*0.2, tb); + cairo_stroke(cr); + cairo_set_source_rgb(cr, 0.9, 0.9, 0.9); + cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, tb/3); + cairo_move_to (cr, width/2.0-10, height*0.85+2.*tb/3.); + snprintf(num, 10, "%d%%", (int)darktable.control->progress); + cairo_show_text (cr, num); + } + + cairo_destroy(cr); + + // gdk_threads_enter(); + cairo_t *cr_pixmap = gdk_cairo_create(darktable.gui->pixmap); + cairo_set_source_surface (cr_pixmap, cst, 0, 0); + cairo_paint(cr_pixmap); + cairo_destroy(cr_pixmap); + + // GtkWidget *widget = glade_xml_get_widget (darktable.gui->main_window, "center"); + // gtk_widget_queue_draw(widget); + // gdk_threads_leave(); + + cairo_surface_destroy(cst); + return NULL; + } + return NULL; +} + +void dt_control_mouse_leave() +{ + dt_ctl_gui_mode_t gui; + DT_CTL_GET_GLOBAL(gui, gui); + if(gui == DT_LIBRARY) + { + dt_library_mouse_leave(darktable.library); + } +} + +void dt_control_mouse_moved(double x, double y, int which) +{ + float tb = darktable.control->tabborder; + float wd = darktable.control->width; + float ht = darktable.control->height; + + dt_ctl_gui_mode_t gui; + DT_CTL_GET_GLOBAL(gui, gui); + if(x > tb && x < wd-tb && y > tb && y < ht-tb) + { + if(gui == DT_LIBRARY) + { + // fwd to lib or dev + dt_library_mouse_moved(darktable.library, x-tb, y-tb, which); + } + else // DT_DEVELOP + { + if(darktable.control->button_down) + { // depending on dev_zoom, adjust dev_zoom_x/y. + dt_develop_t *dev = darktable.develop; + const int cwd = dev->cache_width, cht = dev->cache_height; + const int iwd = dev->image->width, iht = dev->image->height; + float scale = 1.0f; + dt_dev_zoom_t zoom; + int closeup; + DT_CTL_GET_GLOBAL(zoom, dev_zoom); + DT_CTL_GET_GLOBAL(closeup, dev_closeup); + if(zoom == DT_ZOOM_FIT) return; //scale = fminf(iwd/(float)cwd, iht/(float)cht); + if(closeup) scale = .5f; + if(zoom == DT_ZOOM_FILL) scale = fmaxf(iwd/(float)cwd, iht/(float)cht); + float old_zoom_x, old_zoom_y; + DT_CTL_GET_GLOBAL(old_zoom_x, dev_zoom_x); + DT_CTL_GET_GLOBAL(old_zoom_y, dev_zoom_y); + float zx = old_zoom_x - scale*(x - darktable.control->button_x)/iwd; + float zy = old_zoom_y - scale*(y - darktable.control->button_y)/iht; + dt_dev_check_zoom_bounds(darktable.develop, &zx, &zy, zoom, closeup, NULL, NULL); + DT_CTL_SET_GLOBAL(dev_zoom_x, zx); + DT_CTL_SET_GLOBAL(dev_zoom_y, zy); + darktable.control->button_x = x; + darktable.control->button_y = y; + dt_control_queue_draw(); + } + } + } +} + +void dt_control_button_released(double x, double y, int which, uint32_t state) +{ + darktable.control->button_down = 0; + float tb = darktable.control->tabborder; + // float wd = darktable.control->width; + // float ht = darktable.control->height; + + // always do this, to avoid missing some events. + // if(x > tb && x < wd-tb && y > tb && y < ht-tb) + { + // fwd to lib or dev + dt_library_button_released(darktable.library, x-tb, y-tb, which, state); + } +} + +void dt_ctl_switch_mode() +{ + dt_ctl_gui_mode_t gui; + DT_CTL_GET_GLOBAL(gui, gui); + int32_t id; + DT_CTL_GET_GLOBAL(id, lib_image_mouse_over_id); + dt_control_save_gui_settings(gui); + if(gui == DT_DEVELOP) + { + gui = DT_LIBRARY; + dt_control_restore_gui_settings(gui); + dt_dev_leave(); + } + else if(id >= 0) + { + gui = DT_DEVELOP; + dt_control_restore_gui_settings(gui); + DT_CTL_SET_GLOBAL(dev_zoom, DT_ZOOM_FIT); + DT_CTL_SET_GLOBAL(dev_closeup, 0); + dt_dev_enter(); + } + DT_CTL_SET_GLOBAL(gui, gui); +} + +void dt_control_button_pressed(double x, double y, int which, int type, uint32_t state) +{ + darktable.control->button_down = 1; + darktable.control->button_x = x; + darktable.control->button_y = y; + float tb = darktable.control->tabborder; + float wd = darktable.control->width; + float ht = darktable.control->height; + GtkWidget *widget; + + if(x > tb && x < wd-tb && y > tb && y < ht-tb) + { + if(type == GDK_2BUTTON_PRESS) dt_ctl_switch_mode(); + // fwd to lib or dev + // dt_library_button_pressed(darktable.library, (x-tb)/(wd-2.0*tb), (y-tb)/(wd-2.0*tb), which); + else dt_library_button_pressed(darktable.library, x-tb, y-tb, which, state); + } + else if(x < tb) + { + widget = glade_xml_get_widget (darktable.gui->main_window, "left"); + if(GTK_WIDGET_VISIBLE(widget)) gtk_widget_hide(widget); + else gtk_widget_show(widget); + } + else if(x > wd-tb) + { + widget = glade_xml_get_widget (darktable.gui->main_window, "right"); + if(GTK_WIDGET_VISIBLE(widget)) gtk_widget_hide(widget); + else gtk_widget_show(widget); + } + else if(y < tb) + { + widget = glade_xml_get_widget (darktable.gui->main_window, "top"); + if(GTK_WIDGET_VISIBLE(widget)) gtk_widget_hide(widget); + else gtk_widget_show(widget); + } + else if(y > ht-tb) + { + widget = glade_xml_get_widget (darktable.gui->main_window, "bottom"); + if(GTK_WIDGET_VISIBLE(widget)) gtk_widget_hide(widget); + else gtk_widget_show(widget); + } +} + +void dt_control_gui_queue_draw() +{ + if(darktable.control->running) + { + GtkWidget *widget = glade_xml_get_widget (darktable.gui->main_window, "center"); + gtk_widget_queue_draw(widget); + } +} + +void dt_control_queue_draw() +{ + if(darktable.control->running) + { + if(pthread_self() != darktable.control->gui_thread) gdk_threads_enter(); + GtkWidget *widget = glade_xml_get_widget (darktable.gui->main_window, "center"); + gtk_widget_queue_draw(widget); + if(pthread_self() != darktable.control->gui_thread) gdk_threads_leave(); + } +} + +void dt_control_restore_gui_settings(dt_ctl_gui_mode_t mode) +{ + int8_t bit; + GtkWidget *widget; + + DT_CTL_GET_GLOBAL(bit, gui_left); + widget = glade_xml_get_widget (darktable.gui->main_window, "left"); + if(bit & (1<main_window, "right"); + if(bit & (1<main_window, "top"); + if(bit & (1<main_window, "bottom"); + if(bit & (1<main_window, "navigation_expander"); + gtk_expander_set_expanded(GTK_EXPANDER(widget), (bit & (1<main_window, "library_expander"); + gtk_expander_set_expanded(GTK_EXPANDER(widget), (bit & (1<main_window, "history_expander"); + gtk_expander_set_expanded(GTK_EXPANDER(widget), (bit & (1<main_window, "histogram_expander"); + gtk_expander_set_expanded(GTK_EXPANDER(widget), (bit & (1<main_window, "tonecurve_expander"); + gtk_expander_set_expanded(GTK_EXPANDER(widget), (bit & (1<main_window, "gamma_expander"); + gtk_expander_set_expanded(GTK_EXPANDER(widget), (bit & (1<main_window, "hsb_expander"); + gtk_expander_set_expanded(GTK_EXPANDER(widget), (bit & (1<main_window, "export_expander"); + gtk_expander_set_expanded(GTK_EXPANDER(widget), (bit & (1<main_window, "left"); + if(GTK_WIDGET_VISIBLE(widget)) bit |= 1<main_window, "right"); + if(GTK_WIDGET_VISIBLE(widget)) bit |= 1<main_window, "bottom"); + if(GTK_WIDGET_VISIBLE(widget)) bit |= 1<main_window, "top"); + if(GTK_WIDGET_VISIBLE(widget)) bit |= 1<main_window, "navigation_expander"); + if(gtk_expander_get_expanded(GTK_EXPANDER(widget))) bit |= 1<main_window, "library_expander"); + if(gtk_expander_get_expanded(GTK_EXPANDER(widget))) bit |= 1<main_window, "history_expander"); + if(gtk_expander_get_expanded(GTK_EXPANDER(widget))) bit |= 1<main_window, "histogram_expander"); + if(gtk_expander_get_expanded(GTK_EXPANDER(widget))) bit |= 1<main_window, "tonecurve_expander"); + if(gtk_expander_get_expanded(GTK_EXPANDER(widget))) bit |= 1<main_window, "gamma_expander"); + if(gtk_expander_get_expanded(GTK_EXPANDER(widget))) bit |= 1<main_window, "hsb_expander"); + if(gtk_expander_get_expanded(GTK_EXPANDER(widget))) bit |= 1<main_window, "export_expander"); + if(gtk_expander_get_expanded(GTK_EXPANDER(widget))) bit |= 1<main_window, "main_window"); + DT_CTL_GET_GLOBAL(fullscreen, gui_fullscreen); + if(fullscreen) gtk_window_unfullscreen(GTK_WINDOW(widget)); + else gtk_window_fullscreen (GTK_WINDOW(widget)); + fullscreen ^= 1; + DT_CTL_SET_GLOBAL(gui_fullscreen, fullscreen); + break; + case KEY_LEAVE_FULLSCREEN: + widget = glade_xml_get_widget (darktable.gui->main_window, "main_window"); + gtk_window_unfullscreen(GTK_WINDOW(widget)); + fullscreen = 0; + DT_CTL_SET_GLOBAL(gui_fullscreen, fullscreen); + break; + case GDK_Tab: + widget = glade_xml_get_widget (darktable.gui->main_window, "left"); + visible = GTK_WIDGET_VISIBLE(widget); + if(visible) gtk_widget_hide(widget); + else gtk_widget_show(widget); + + widget = glade_xml_get_widget (darktable.gui->main_window, "right"); + if(visible) gtk_widget_hide(widget); + else gtk_widget_show(widget); + + widget = glade_xml_get_widget (darktable.gui->main_window, "bottom"); + if(visible) gtk_widget_hide(widget); + else gtk_widget_show(widget); + + widget = glade_xml_get_widget (darktable.gui->main_window, "top"); + if(visible) gtk_widget_hide(widget); + else gtk_widget_show(widget); + break; + default: + break; + } + if(gui == DT_LIBRARY) switch (which) + { +#if 0 + case GDK_Left: case KEY_LEFT: + DT_CTL_GET_GLOBAL(selected, lib_image_mouse_over_i); + if(selected > 0) selected --; + DT_CTL_SET_GLOBAL(lib_image_mouse_over_i, selected); + DT_CTL_SET_GLOBAL(lib_track, 1); + break; + case GDK_Right: case KEY_RIGHT: + DT_CTL_GET_GLOBAL(selected, lib_image_mouse_over_i); + if(selected < DT_LIBRARY_MAX_ZOOM-1) selected ++; + DT_CTL_SET_GLOBAL(lib_image_mouse_over_i, selected); + DT_CTL_SET_GLOBAL(lib_track, 1); + break; + case GDK_Up: case KEY_UP: + DT_CTL_GET_GLOBAL(selected, lib_image_mouse_over_j); + if(selected > 0) selected --; + DT_CTL_SET_GLOBAL(lib_image_mouse_over_j, selected); + DT_CTL_SET_GLOBAL(lib_track, 1); + break; + case GDK_Down: case KEY_DOWN: + DT_CTL_GET_GLOBAL(selected, lib_image_mouse_over_j); + selected ++; + DT_CTL_SET_GLOBAL(lib_image_mouse_over_j, selected); + DT_CTL_SET_GLOBAL(lib_track, 1); + break; +#endif + case GDK_1: + DT_CTL_SET_GLOBAL(lib_zoom, 1); + break; + case KEY_FIT: + DT_CTL_SET_GLOBAL(lib_zoom, DT_LIBRARY_MAX_ZOOM); + DT_CTL_SET_GLOBAL(lib_center, 1); + break; + default: + break; + } + else if(gui == DT_DEVELOP) switch (which) + { + case GDK_1: + DT_CTL_GET_GLOBAL(zoom, dev_zoom); + DT_CTL_GET_GLOBAL(closeup, dev_closeup); + if(zoom == DT_ZOOM_1) closeup ^= 1; + DT_CTL_SET_GLOBAL(dev_closeup, closeup); + DT_CTL_SET_GLOBAL(dev_zoom, DT_ZOOM_1); + break; + case GDK_2: + DT_CTL_SET_GLOBAL(dev_zoom, DT_ZOOM_FILL); + DT_CTL_SET_GLOBAL(dev_zoom_x, 0.0); + DT_CTL_SET_GLOBAL(dev_zoom_y, 0.0); + DT_CTL_SET_GLOBAL(dev_closeup, 0); + break; + case GDK_3: + DT_CTL_SET_GLOBAL(dev_zoom, DT_ZOOM_FIT); + DT_CTL_SET_GLOBAL(dev_closeup, 0); + break; + default: + break; + } + widget = glade_xml_get_widget (darktable.gui->main_window, "center"); + gtk_widget_queue_draw(widget); + widget = glade_xml_get_widget (darktable.gui->main_window, "navigation"); + gtk_widget_queue_draw(widget); + return 1; +} + +void dt_control_get_tonecurve(uint16_t *tonecurve, dt_ctl_image_settings_t *settings) +{ + dt_gui_curve_editor_get_curve(&(darktable.gui->tonecurve), tonecurve, settings); +} + +void dt_control_add_history_item(int32_t num, const char *label) +{ + // TODO: add new widget if this happens: + assert(num < 10); + char wdname[20]; + snprintf(wdname, 20, "history_%02d", num); + GtkWidget *widget = glade_xml_get_widget (darktable.gui->main_window, wdname); + gtk_widget_show(widget); + gtk_button_set_label(GTK_BUTTON(widget), label); +} + +void dt_control_clear_history_items(int32_t num) +{ + assert(num >= 0 && num <= 10); + char wdname[20]; + for(int k=num+1;k<10;k++) + { + snprintf(wdname, 20, "history_%02d", k); + GtkWidget *widget = glade_xml_get_widget (darktable.gui->main_window, wdname); + gtk_widget_hide(widget); + } +} + diff --git a/src/control/control.h b/src/control/control.h new file mode 100644 index 000000000000..4fafb06287e0 --- /dev/null +++ b/src/control/control.h @@ -0,0 +1,124 @@ +#ifndef DT_CONTROL_H +#define DT_CONTROL_H + +#include +#include +#ifdef _OPENMP +# include +#endif + +#include "control/settings.h" +// #include "control/job.def" + +#define DT_CONTROL_MAX_JOBS 100 +#define DT_CONTROL_JOB_DEBUG +#define DT_CONTROL_DESCRIPTION_LEN 256 +// reserved workers +#define DT_CTL_WORKER_RESERVED 6 +#define DT_CTL_WORKER_1 0 // dev load raw +#define DT_CTL_WORKER_2 1 // dev zoom 1 +#define DT_CTL_WORKER_3 2 // dev zoom fill +#define DT_CTL_WORKER_4 3 // dev zoom fit +#define DT_CTL_WORKER_5 4 // dev small prev +#define DT_CTL_WORKER_6 5 // dev prefetch + + +// called from gui +void *dt_control_expose(void *voidptr); +void dt_control_button_pressed(double x, double y, int which, int type, uint32_t state); +void dt_control_button_released(double x, double y, int which, uint32_t state); +void dt_control_mouse_moved(double x, double y, int which); +void dt_control_mouse_leave(); +int dt_control_key_pressed(uint32_t which); +void dt_control_configure(int32_t width, int32_t height); +void dt_control_gui_queue_draw(); + +// called from core +void dt_control_queue_draw(); +void dt_control_get_tonecurve(uint16_t *tonecurve, dt_ctl_image_settings_t *settings); +void dt_control_add_history_item(int32_t num, const char *label); +void dt_control_clear_history_items(int32_t num); + +void dt_control_save_gui_settings(dt_ctl_gui_mode_t mode); +void dt_control_restore_gui_settings(dt_ctl_gui_mode_t mode); + +/** + * smalles unit of work. + */ +struct dt_job_t; +typedef struct dt_job_t +{ + void (*execute)(struct dt_job_t *job); + int32_t param[32]; +#ifdef DT_CONTROL_JOB_DEBUG + char description[DT_CONTROL_DESCRIPTION_LEN]; +#endif +} +dt_job_t; + +void dt_control_job_init(dt_job_t *j, const char *msg, ...); +void dt_control_job_print(dt_job_t *j); + + +/** + * this manages everything time-consuming. + * distributes the jobs on all processors, + * performs scheduling. + */ +typedef struct dt_control_t +{ + // gui related stuff + double tabborder; + int32_t width, height; + float progress; + pthread_t gui_thread; + int button_down; + double button_x, button_y; + + // gui settings + dt_ctl_settings_t global_settings, global_defaults; + dt_ctl_image_settings_t image_settings, image_defaults; + pthread_mutex_t global_mutex, image_mutex; + + // job management + int32_t running; + pthread_mutex_t queue_mutex, cond_mutex; + pthread_cond_t cond; + int32_t num_threads; + pthread_t *thread; + dt_job_t job[DT_CONTROL_MAX_JOBS]; + int32_t idle[DT_CONTROL_MAX_JOBS]; + int32_t queued[DT_CONTROL_MAX_JOBS]; + int32_t idle_top, queued_top; + dt_job_t job_res[DT_CTL_WORKER_RESERVED]; + uint8_t new_res[DT_CTL_WORKER_RESERVED]; + pthread_t thread_res[DT_CTL_WORKER_RESERVED]; +} +dt_control_t; + +void dt_control_init(dt_control_t *s); +void dt_control_cleanup(dt_control_t *s); + +int dt_control_load_config(dt_control_t *c); +int dt_control_write_config(dt_control_t *c); + +int32_t dt_control_run_job(dt_control_t *s); +int32_t dt_control_add_job(dt_control_t *s, dt_job_t *job); +int32_t dt_control_run_job_res(dt_control_t *s, int32_t res); +int32_t dt_control_add_job_res(dt_control_t *s, dt_job_t *job, int32_t res); + +void *dt_control_work(void *ptr); +void *dt_control_work_res(void *ptr); +int32_t dt_control_get_threadid(); +int32_t dt_control_get_threadid_res(); + +static inline int32_t dt_ctl_get_num_procs() +{ +#ifdef _OPENMP + return omp_get_num_procs(); +#else + return 1; +#endif +} + +#endif diff --git a/src/control/jobs.c b/src/control/jobs.c new file mode 100644 index 000000000000..ec0d95897d9d --- /dev/null +++ b/src/control/jobs.c @@ -0,0 +1,87 @@ +#include "control/jobs.h" + +void dt_image_load_job_init(dt_job_t *job, dt_image_t *image, dt_image_buffer_t mip) +{ + job->execute = &dt_image_load_job_run; + dt_image_load_t *t = (dt_image_load_t *)job->param; + t->image = image; + t->mip = mip; + dt_control_job_init(job, "load image %d mip %d", image->id, mip); +} + +void dt_image_load_job_run(dt_job_t *job) +{ + dt_image_load_t *t = (dt_image_load_t *)job->param; + int ret = dt_image_load(t->image, t->mip); + // drop read lock, as this is only speculative async loading. + if(!ret) dt_image_release(t->image, t->mip, 'r'); +} + +void dt_film_import1_init(dt_job_t *job, dt_film_roll_t *film) +{ + job->execute = &dt_film_import1_run; + dt_film_import1_t *t = (dt_film_import1_t *)job->param; + t->film = film; + dt_control_job_init(job, "cache load raw images for preview"); +} + +void dt_film_import1_run(dt_job_t *job) +{ + dt_film_import1_t *t = (dt_film_import1_t *)job->param; + dt_film_import1(t->film); +} + +// ==================== +// develop: +// ==================== + +void dt_dev_raw_load_job_run(dt_job_t *job) +{ + dt_dev_raw_load_t *t = (dt_dev_raw_load_t *)job->param; + dt_dev_raw_load(t->dev, t->image); +} + +void dt_dev_raw_load_job_init(dt_job_t *job, dt_develop_t *dev, dt_image_t *image) +{ + job->execute =&dt_dev_raw_load_job_run; + dt_dev_raw_load_t *t = (dt_dev_raw_load_t *)job->param; + t->dev = dev; + t->image = image; + dt_control_job_init(job, "develop load raw image %s", image->filename); +} + +void dt_dev_cache_load_job_run(dt_job_t *job) +{ + dt_dev_cache_load_t *t = (dt_dev_cache_load_t *)job->param; + dt_dev_cache_load(t->dev, t->stackpos, t->zoom); +} + +void dt_dev_cache_load_job_init(dt_job_t *job, dt_develop_t *dev, int32_t stackpos, dt_dev_zoom_t zoom) +{ + job->execute = &dt_dev_cache_load_job_run; + dt_dev_cache_load_t *t = (dt_dev_cache_load_t *)job->param; + t->dev = dev; + t->stackpos = stackpos; + t->zoom = zoom; + dt_control_job_init(job, "develop load cache history %d for zoom level %d", stackpos, zoom); +} + +void dt_dev_small_cache_load_run(dt_job_t *job) +{ + dt_dev_small_cache_load_t *t = (dt_dev_small_cache_load_t *)job->param; + (void)dt_dev_small_cache_load(t->dev); +} + +void dt_dev_small_cache_load_init(dt_job_t *job, dt_develop_t *dev) +{ + job->execute = &dt_dev_small_cache_load_run; + dt_dev_small_cache_load_t *t = (dt_dev_small_cache_load_t *)job->param; + t->dev = dev; + dt_control_job_init(job, "develop load small cache"); +} + +void dt_dev_export_init(dt_job_t *job) +{ + job->execute = &dt_dev_export; + dt_control_job_init(job, "develop export selected"); +} diff --git a/src/control/jobs.h b/src/control/jobs.h new file mode 100644 index 000000000000..1eefc50e3f5b --- /dev/null +++ b/src/control/jobs.h @@ -0,0 +1,62 @@ +#ifndef DT_CONTROL_JOBS_H +#define DT_CONTROL_JOBS_H + +#include "common/image.h" +#include "control/control.h" +#include "library/library.h" +#include "develop/develop.h" + +typedef struct dt_image_load_t +{ + dt_image_t *image; + dt_image_buffer_t mip; +} +dt_image_load_t; + +void dt_image_load_job_run(dt_job_t *job); +void dt_image_load_job_init(dt_job_t *job, dt_image_t *image, dt_image_buffer_t mip); + +typedef struct dt_film_import1_t +{ + dt_film_roll_t *film; +} +dt_film_import1_t; + +void dt_film_import1_run(dt_job_t *job); +void dt_film_import1_init(dt_job_t *job, dt_film_roll_t *film); + + +typedef struct dt_dev_raw_load_t +{ + dt_develop_t *dev; + dt_image_t *image; +} +dt_dev_raw_load_t; + +void dt_dev_raw_load_job_run(dt_job_t *job); +void dt_dev_raw_load_job_init(dt_job_t *job, dt_develop_t *dev, dt_image_t *image); + +enum dt_dev_zoom_t; +typedef struct dt_dev_cache_load_t +{ + dt_develop_t *dev; + int32_t stackpos; + enum dt_dev_zoom_t zoom; +} +dt_dev_cache_load_t; + +void dt_dev_cache_load_job_run(dt_job_t *job); +void dt_dev_cache_load_job_init(dt_job_t *job, dt_develop_t *dev, int32_t stackpos, enum dt_dev_zoom_t zoom); + +typedef struct dt_dev_small_cache_load_t +{ + dt_develop_t *dev; +} +dt_dev_small_cache_load_t; + +void dt_dev_small_cache_load_run(dt_job_t *job); +void dt_dev_small_cache_load_init(dt_job_t *job, dt_develop_t *dev); + +void dt_dev_export_init(dt_job_t *job); + +#endif diff --git a/src/control/settings.h b/src/control/settings.h new file mode 100644 index 000000000000..12ee24717ba4 --- /dev/null +++ b/src/control/settings.h @@ -0,0 +1,120 @@ +#ifndef DT_CTL_SETTINGS_H +#define DT_CTL_SETTINGS_H + +#include + +// thread-safe interface between core and gui. +// also serves to store user settings. + +#define DT_CTL_GET_GLOBAL(x, attrib) \ + pthread_mutex_lock(&(darktable.control->global_mutex)); \ + x = darktable.control->global_settings.attrib; \ + pthread_mutex_unlock(&(darktable.control->global_mutex)) + +#define DT_CTL_SET_GLOBAL(attrib, x) \ + pthread_mutex_lock(&(darktable.control->global_mutex)); \ + darktable.control->global_settings.attrib = x; \ + pthread_mutex_unlock(&(darktable.control->global_mutex)) + +#define DT_CTL_GET_GLOBAL_STR(x, attrib, n) \ + pthread_mutex_lock(&(darktable.control->global_mutex)); \ + strncpy(x, darktable.control->global_settings.attrib, n); \ + pthread_mutex_unlock(&(darktable.control->global_mutex)) + +#define DT_CTL_SET_GLOBAL_STR(attrib, x, n) \ + pthread_mutex_lock(&(darktable.control->global_mutex)); \ + strncpy(darktable.control->global_settings.attrib, x, n); \ + pthread_mutex_unlock(&(darktable.control->global_mutex)) + +#define DT_CTL_GET_IMAGE(x, attrib) \ + pthread_mutex_lock(&(darktable.control->image_mutex)); \ + x = darktable.control->image_settings.attrib; \ + pthread_mutex_unlock(&(darktable.control->image_mutex)) + +#define DT_CTL_SET_IMAGE(attrib, x) \ + pthread_mutex_lock(&(darktable.control->image_mutex)); \ + darktable.control->image_settings.attrib = x; \ + pthread_mutex_unlock(&(darktable.control->image_mutex)) + +typedef enum dt_ctl_gui_mode_t +{ + DT_LIBRARY = 1, + DT_DEVELOP = 2 +} +dt_ctl_gui_mode_t; + +typedef enum dt_dev_zoom_t +{ + DT_ZOOM_FIT = 0, + DT_ZOOM_FILL = 1, + DT_ZOOM_1 = 2 +} +dt_dev_zoom_t; + +typedef char dt_dev_operation_t[20]; + +#define DEV_NUM_OP_PARAMS 10 + +typedef union dt_dev_operation_params_t +{ + int32_t i[DEV_NUM_OP_PARAMS]; + float f[DEV_NUM_OP_PARAMS]; +} +dt_dev_operation_params_t; + +typedef enum dt_dev_export_format_t +{ + DT_DEV_EXPORT_JPG, + DT_DEV_EXPORT_PNG, + DT_DEV_EXPORT_PPM16, + DT_DEV_EXPORT_PFM +} +dt_dev_export_format_t; + +typedef struct dt_ctl_settings_t +{ + // global + int32_t version; + char dbname[512]; + + // develop + float dev_gamma_linear, dev_gamma_gamma; + dt_dev_operation_t dev_op; // currently active op + dt_dev_operation_params_t dev_op_params; // params for currently active op + dt_dev_export_format_t dev_export_format; + + // library + float lib_zoom, lib_zoom_x, lib_zoom_y; + int32_t lib_center, lib_pan, lib_track; + int32_t lib_image_mouse_over_id; + + // navigation + float dev_zoom_x, dev_zoom_y; + dt_dev_zoom_t dev_zoom; + int dev_closeup; + + // gui + // widget settings for lib/dev view, fullscreen, window geometry + dt_ctl_gui_mode_t gui; + int32_t gui_x, gui_y, gui_w, gui_h; + uint8_t gui_fullscreen; + uint8_t gui_left, gui_right, gui_bottom, gui_top; + uint8_t gui_navigation, gui_library, gui_history; + uint8_t gui_histogram, gui_tonecurve, gui_gamma, gui_hsb, gui_export; +} +dt_ctl_settings_t; + +enum dt_dev_zoom_t; +// gui controlled per-image parameters +typedef struct dt_ctl_image_settings_t +{ + // gamma correction + float dev_gamma_linear, dev_gamma_gamma; + + // tone curve + float tonecurve_x[6], tonecurve_y[6]; + int tonecurve_preset; +} +dt_ctl_image_settings_t; + +#endif diff --git a/src/develop/develop.c b/src/develop/develop.c new file mode 100644 index 000000000000..b018e07e9942 --- /dev/null +++ b/src/develop/develop.c @@ -0,0 +1,881 @@ +#include "develop/develop.h" +#include "develop/imageop.h" +#include "control/jobs.h" +#include "control/control.h" +#include "common/image_cache.h" + +#include +#include +#include +#include + +uint8_t dt_dev_default_gamma[0x10000]; +float dt_dev_de_gamma[0x100]; + +int32_t dt_dev_update_fixed_pipeline(dt_develop_t *dev, dt_dev_operation_t op, int32_t hash) +{ + if(strncmp(op, "original", 20) == 0) + { + dt_control_get_tonecurve(dev->tonecurve, &(dev->history[dev->history_top-1].settings)); + dt_dev_set_gamma(dev); + dt_dev_set_histogram_pre(dev); + } + else if(strncmp(op, "tonecurve", 20) == 0) + { + dt_control_get_tonecurve(dev->tonecurve, &(dev->history[dev->history_top-1].settings)); + } + else if(strncmp(op, "gamma", 20) == 0) + { + dt_dev_set_gamma(dev); + } + else + { // plugin op on buffer + dt_dev_set_histogram_pre(dev); + hash += (dev->last_hash++) * (1<<11); + } + dt_dev_set_histogram(dev); + return hash; +} + +void dt_dev_update_cache(dt_develop_t *dev, dt_dev_image_t *img, dt_dev_zoom_t zoom) +{ + float zoom_x, zoom_y; + DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); + DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); + int test = dt_dev_test_cached_buf(dev, img, zoom) && + dev->cache_zoom_x[img->cacheline[zoom]] == zoom_x && dev->cache_zoom_y[img->cacheline[zoom]] == zoom_y; + pthread_mutex_lock(&dev->cache_mutex); + if(!dev->image || !dev->gui_attached || dev->image_loading || test) + { + pthread_mutex_unlock(&dev->cache_mutex); + return; // refuse silly cache op. + } + pthread_mutex_unlock(&dev->cache_mutex); + dt_job_t job; + dt_dev_cache_load_job_init(&job, dev, dev->history_top - 1, zoom); + int err = dt_control_add_job_res(darktable.control, &job, DT_CTL_WORKER_2 + zoom); + if(err) fprintf(stderr, "[dev_update_cache] job queue exceeded!\n"); +} + +void dt_dev_update_small_cache(dt_develop_t *dev) +{ + if(!dev->gui_attached) return; + // only one process running anyways, jobs will not pile up + // pthread_mutex_lock(&dev->cache_mutex); + // if(dev->small_raw_loading || dev->small_raw_hash == dev->history[dev->history_top-1].num) + // { + // pthread_mutex_unlock(&dev->cache_mutex); + // return; + // } + // dev->small_raw_loading = 2; + // pthread_mutex_unlock(&dev->cache_mutex); + dt_job_t job; + dt_dev_small_cache_load_init(&job, dev); + int err = dt_control_add_job_res(darktable.control, &job, DT_CTL_WORKER_5); + if(err) fprintf(stderr, "[dev_update_small_cache] job queue exceeded!\n"); +} + +void dt_dev_set_gamma_array(dt_develop_t *dev, const float linear, const float gamma, uint8_t *arr) +{ + double a, b, c, g; + if(linear<1.0) + { + g = gamma*(1.0-linear)/(1.0-gamma*linear); + a = 1.0/(1.0+linear*(g-1)); + b = linear*(g-1)*a; + c = pow(a*linear+b, g)/linear; + } + else + { + a = b = g = 0.0; + c = 1.0; + } + + for(int k=0;k<0x10000;k++) + { + int32_t tmp = 0x10000 * powf(k/(float)0x10000, 1./2.2); + if (k<0x10000*linear) tmp = MIN(c*k, 0xFFFF); + else tmp = MIN(pow(a*k/0x10000+b, g)*0x10000, 0xFFFF); + arr[k] = tmp>>8; + } +} + +void dt_dev_set_gamma(dt_develop_t *dev) +{ + dt_dev_image_t *img = dev->history + dev->history_top - 1; + dt_dev_set_gamma_array(dev, img->settings.dev_gamma_linear, img->settings.dev_gamma_gamma, dev->gamma); +} + +void dt_dev_init(dt_develop_t *dev, int32_t gui_attached) +{ + dev->gui_attached = gui_attached; + dev->small_backbuf = dev->backbuf = NULL; + dev->backbuf_hash = dev->small_backbuf_hash = -1; + dev->image = NULL; + dev->image_loading = 0; + dev->small_raw_cached = NULL; + dev->small_raw_loading = 0; + dev->small_raw_hash = -1; + dev->small_raw_height = dev->small_raw_width = -1; + pthread_mutex_init(&dev->cache_mutex, NULL); + dev->num_cachelines = DT_DEV_CACHELINES; + dev->cache = (float **)malloc(dev->num_cachelines*sizeof(float*)); + dev->cache_zoom_x = (float *)malloc(dev->num_cachelines*sizeof(float)); + dev->cache_zoom_y = (float *)malloc(dev->num_cachelines*sizeof(float)); + dev->cache_sorted = (int32_t *)malloc(dev->num_cachelines*sizeof(int32_t)); + dev->cache_hash = (int32_t *)malloc(dev->num_cachelines*sizeof(uint32_t)); + dev->cache_used = (int32_t *)malloc(dev->num_cachelines*sizeof(uint32_t)); + dev->small_raw_buf = NULL; + for(int k=0;knum_cachelines;k++) + { + dev->cache_hash[k] = -1; + dev->cache[k] = NULL; + dev->cache_used[k] = 0; + dev->cache_sorted[k] = k; + } + dev->cache_width = dev->cache_height = -1; + + dev->history_top = 0; + dev->history_max = 10; + dev->history = (dt_dev_image_t *)malloc(sizeof(dt_dev_image_t)*dev->history_max); + for(int k=0;k<3;k++) dev->history[0].cacheline[k] = 0; + + float lin, gam; + DT_CTL_GET_GLOBAL(lin, dev_gamma_linear); + DT_CTL_GET_GLOBAL(gam, dev_gamma_gamma); + dt_dev_set_gamma_array(dev, lin, gam, dt_dev_default_gamma); + dt_dev_set_gamma_array(dev, lin, gam, dev->gamma); + int last = 0; // invert 0.1 0.45 fn + for(int i=0;i<0x100;i++) for(int k=last;k<0x10000;k++) + if(dt_dev_default_gamma[k] >= i) { last = k; dt_dev_de_gamma[i] = k/(float)0x10000; break; } + // for(int k=0;k<0x10000;k++) dt_dev_de_gamma[k] = CLAMP(0xffff*powf(k/(double)0xffff, 2.2), 0, 0xffff); + + dev->histogram = (uint32_t *)malloc(sizeof(int32_t)*256*4); + bzero(dev->histogram, sizeof(int32_t)*256*4); + dev->histogram_max = -1; + dev->histogram_pre = (uint32_t *)malloc(sizeof(int32_t)*256*4); + bzero(dev->histogram_pre, sizeof(int32_t)*256*4); + dev->histogram_pre_max = -1; +} + +void dt_dev_cleanup(dt_develop_t *dev) +{ + if(dev->image) + { + dt_image_release(dev->image, DT_IMAGE_FULL, 'w'); + dt_image_release(dev->image, DT_IMAGE_FULL, 'r'); + dt_image_release(dev->image, DT_IMAGE_MIPF, 'r'); + } + + pthread_mutex_destroy(&dev->cache_mutex); + for(int k=0;knum_cachelines;k++) free(dev->cache[k]); + free(dev->cache); + free(dev->cache_sorted); + free(dev->cache_used); + free(dev->cache_hash); + free(dev->cache_zoom_x); + free(dev->cache_zoom_y); + free(dev->history); + free(dev->histogram); + free(dev->histogram_pre); + free(dev->backbuf); + free(dev->small_backbuf); + free(dev->small_raw_buf); +} + +int dt_dev_small_cache_load(dt_develop_t *dev) +{ + dt_print(DT_DEBUG_DEV, "[small_cache_load] hash %d -> %d\n", dev->small_raw_hash, dev->history[dev->history_top-1].num); + if(!dev->image) return 1; + // pthread_mutex_lock(&dev->cache_mutex); + // if(dev->small_raw_loading == 1) + // { + // pthread_mutex_unlock(&dev->cache_mutex); + // return; + // } + // dev->small_raw_loading = 1; + // pthread_mutex_unlock(&dev->cache_mutex); + dt_image_buffer_t mip = dt_image_get(dev->image, DT_IMAGE_MIPF, 'r'); + if(mip != DT_IMAGE_MIPF) + { // fail. :( + pthread_mutex_lock(&dev->cache_mutex); + dev->small_raw_loading = 0; + pthread_mutex_unlock(&dev->cache_mutex); + return 1; + } + int wd, ht; + dt_image_get_mip_size(dev->image, DT_IMAGE_MIPF, &wd, &ht); + dev->small_raw_width = wd; + dev->small_raw_height = ht; +restart_small_cache: + dt_image_check_buffer(dev->image, DT_IMAGE_MIPF, 3*wd*ht*sizeof(float)); + // need 16-bit buf update? + pthread_mutex_lock(&dev->cache_mutex); + int hash_in = dev->history[dev->history_top-1].num; + int test = dev->small_raw_hash != dev->history[dev->history_top-1].num; + pthread_mutex_unlock(&dev->cache_mutex); + if(test) + { + float *tmp = dev->image->mipf; + // memcpy(dev->small_raw_cached, dev->small_raw, wd*ht*3*sizeof(uint16_t)); + // perform all image operations on stack + const int num_in = dev->history[0].num; + for(int k=1;khistory_top;k++) + { + dt_dev_image_t *img = dev->history + k; + dt_iop_execute(dev->small_raw_buf, tmp, wd, ht, wd, ht, img->operation, &(img->op_params)); + if(img->num != num_in) tmp = dev->small_raw_buf; + } + dev->small_raw_cached = tmp; // set to orig or buf. + if(dev->history[dev->history_top-1].num != hash_in) goto restart_small_cache; // obsoleted + } + if(dev->small_backbuf_hash != dev->history[dev->history_top-1].num) + { +// #pragma omp parallel for schedule(static) shared(dev) + for(int i=0;ismall_backbuf[4*i+2-k] = dev->gamma[dev->tonecurve[(int)CLAMP(0xffff*dev->small_raw_cached[3*i+k], 0, 0xffff)]]; + } + + dt_image_release(dev->image, mip, 'r'); + pthread_mutex_lock(&dev->cache_mutex); + dev->small_backbuf_hash = + dev->small_raw_hash = dev->history[dev->history_top-1].num; + dev->small_raw_loading = 0; + pthread_mutex_unlock(&dev->cache_mutex); + (void)dt_dev_update_fixed_pipeline(dev, dev->history[dev->history_top-1].operation, 0); + dt_control_queue_draw(); + return 0; +} + +void dt_dev_cache_load(dt_develop_t *dev, int32_t stackpos, dt_dev_zoom_t zoom) +{ + pthread_mutex_lock(&dev->cache_mutex); + if(!dev->image || dev->image_loading || !dev->image->pixels || dev->cache_width < 0 || dev->image->shrink) + { + pthread_mutex_unlock(&dev->cache_mutex); + return; // refuse silly cache op. + } + pthread_mutex_unlock(&dev->cache_mutex); + + dt_dev_image_t *img = dev->history + stackpos; + float zoom_x, zoom_y; + DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); + DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); + const int cwd = dev->cache_width, cht = dev->cache_height; + const int iwd = dev->image->width, iht = dev->image->height; + // already there, just recently written by this worker thread? + if(dt_dev_test_cached_buf(dev, img, zoom) && dev->cache_zoom_x[img->cacheline[zoom]] == zoom_x && dev->cache_zoom_y[img->cacheline[zoom]] == zoom_y) + return; + // goto finalize; + // printf("[dev_cache_load] img %d zoom %d to cacheline %d for stackpos %d\n", img->num, zoom, img->cacheline[zoom], stackpos); + + if(stackpos == 0) + { // clip and zoom from original raw image + dt_dev_reserve_new_cached_buf(dev, img, zoom); + dev->cache_zoom_x[img->cacheline[zoom]] = zoom_x; + dev->cache_zoom_y[img->cacheline[zoom]] = zoom_y; + dt_image_check_buffer(dev->image, DT_IMAGE_FULL, 3*iwd*iht*sizeof(float)); + float *pixels = dev->image->pixels; + // resample original image + if(zoom == DT_ZOOM_1) + { + int x = MAX(0, (int)((zoom_x + .5f)*iwd - cwd/2)), y = MAX(0, (int)((zoom_y + .5f)*iht - cht/2)); + const int cht2 = MIN(cht, iht - y); + const int cwd2 = MIN(cwd, iwd - x); + int idx = 0; + for(int j=0;jcache[img->cacheline[zoom]][3*idx + k] = pixels[3*(iwd*y + x) + k]; + x++; idx++; + } + y++; x = MAX(0, (int)((zoom_x + .5f)*iwd - cwd/2)); + idx = cwd*j; + } + } + else if(zoom == DT_ZOOM_FILL) + { + float boxw = cwd/(iwd*fmaxf(cwd/(float)iwd, cht/(float)iht)), boxh = cht/(iht*fmaxf(cwd/(float)iwd, cht/(float)iht)); + dt_iop_clip_and_zoom(pixels, (zoom_x + .5f)*iwd - boxw*iwd/2, (zoom_y + .5f)*iht - boxh*iht/2, boxw*iwd, boxh*iht, iwd, iht, + dev->cache[img->cacheline[zoom]], 0, 0, cwd, cht, cwd, cht); + } + else + { + float scale = fminf(cwd/(float)iwd, cht/(float)iht); + dt_iop_clip_and_zoom(pixels, 0, 0, iwd, iht, iwd, iht, + dev->cache[img->cacheline[zoom]], 0, 0, scale*iwd, scale*iht, cwd, cht); + } + dt_dev_release_cached_buf(dev, img, zoom); + } + else + { + // get image from last operation (stack - 1): + float *buf = NULL, *dst = NULL; + // recursively acquire buffers of previous operations, let cache_load decide whether loading is necessary. + dt_dev_cache_load(dev, stackpos-1, zoom); + buf = dt_dev_get_cached_buf(dev, img-1, zoom, 'r'); + if(buf) + { + dst = dt_dev_get_cached_buf(dev, img, zoom, 'w'); // maybe parent buffer is ours? + if(!dst) dt_dev_reserve_new_cached_buf(dev, img, zoom); + dev->cache_zoom_x[img->cacheline[zoom]] = zoom_x; + dev->cache_zoom_y[img->cacheline[zoom]] = zoom_y; + if(buf != dev->cache[img->cacheline[zoom]]) + { // only perform op if buffer is there and op affected it + // get operation and params, apply to buffer. + const float sx = iwd*fminf(cwd/(float)iwd, cht/(float)iht); + const float sy = iht*fminf(cwd/(float)iwd, cht/(float)iht); + const int cwd2 = zoom == DT_ZOOM_FIT ? sx : cwd; + const int cht2 = zoom == DT_ZOOM_FIT ? sy : cht; + dt_iop_execute(dev->cache[img->cacheline[zoom]], buf, cwd2, cht2, cwd, cht, img->operation, &(img->op_params)); + dt_dev_release_cached_buf(dev, img-1, zoom); + } + dt_dev_release_cached_buf(dev, img, zoom); + } + } + (void)dt_dev_update_fixed_pipeline(dev, img->operation, 0); +// finalize: + if(stackpos == dev->history_top - 1) + { + dt_control_queue_draw(); +// uint16_t *buf = dt_dev_get_cached_buf(dev, img, zoom, 'w'); +// #pragma omp parallel for schedule(static) shared(dev,buf) +// for(int i=0;ibackbuf[4*i+2-k] = dev->gamma[dev->tonecurve[buf[3*i+k]]]>>8; +// dt_dev_release_cached_buf(dev, img, zoom); + } +} + +void dt_dev_raw_load(dt_develop_t *dev, dt_image_t *img) +{ + int err; + pthread_mutex_lock(&dev->cache_mutex); + // only load if not already there. + if(!dev->image->pixels || dev->image->shrink) + { + // TODO: + if(dev->image_loading) + { + // kill still running job + fprintf(stderr, "[dev_raw_load] still old image loading! TODO: kill job!\n"); + pthread_mutex_unlock(&dev->cache_mutex); + return; + } + +restart: + dev->image_loading = 1; + dev->image->shrink = 0; + pthread_mutex_unlock(&dev->cache_mutex); + err = dt_image_load(img, DT_IMAGE_FULL); + if(err) + { + fprintf(stderr, "[dev_raw_load] failed to load image %s!\n", img->filename); + } + + // obsoleted by another job? + if(dev->image != img) + { + printf("[dev_raw_load] recovering from obsoleted read!\n"); + img = dev->image; + pthread_mutex_lock(&dev->cache_mutex); + goto restart; + } + + pthread_mutex_lock(&dev->cache_mutex); + dev->image_loading = 0; + pthread_mutex_unlock(&dev->cache_mutex); + + // flush cashe + dev->backbuf_hash = -1; + dev->histogram_max = -1; + dev->histogram_pre_max = -1; + for(int k=0;knum_cachelines;k++) dev->cache_hash[k] = -1; + + // printf("[dev_raw_load] starting cache jobs %s\n", img->filename); + // create cached arrays in threads. + for(int k=0;k<3;k++) + dt_dev_update_cache(dev, dev->history + dev->history_top - 1, k); + } + else pthread_mutex_unlock(&dev->cache_mutex); +} + +void dt_dev_load_image(dt_develop_t *dev, dt_image_t *image) +{ + // printf("[dt_dev_load_image]\n"); + // int selected; + dev->last_hash = 1; + // DT_CTL_GET_GLOBAL(selected, lib_image_mouse_over); + if(dev->image != image) + { + dev->image = image; + // invalidate all caches. + dev->small_backbuf_hash = -1; + dev->backbuf_hash = -1; + dev->small_raw_hash = -1; + for(int k=0;knum_cachelines;k++) dev->cache_hash[k] = -1; + } + dt_dev_read_history(dev); + + // DT_CTL_SET_GLOBAL(dev_zoom_x, 0); + // DT_CTL_SET_GLOBAL(dev_zoom_y, 0); + pthread_mutex_lock(&dev->cache_mutex); + dev->small_raw_loading = 0; + pthread_mutex_unlock(&dev->cache_mutex); + + dt_control_get_tonecurve(dev->tonecurve, &(dev->history[dev->history_top-1].settings)); + dt_dev_set_gamma(dev); + + if(dev->gui_attached) + { + if(!dev->image->pixels || dev->image->shrink) + { + dt_job_t job; + dt_dev_raw_load_job_init(&job, dev, dev->image); + int32_t err = dt_control_add_job_res(darktable.control, &job, DT_CTL_WORKER_1); + if(err) fprintf(stderr, "[dev_load_image] job queue exceeded!\n"); + } + } + else dt_dev_raw_load(dev, dev->image); +} + +void dt_dev_configure(dt_develop_t *dev, int32_t width, int32_t height) +{ + // re-alloc cache on resize, flushes all entries :( + if(dev->cache_width != width || dev->cache_height != height) + { + pthread_mutex_lock(&dev->cache_mutex); + // jobs still working will schedule a redraw when finished. + for(int k=0;knum_cachelines;k++) + { + if(dev->cache_used[k]) + { + pthread_mutex_unlock(&dev->cache_mutex); + return; + } + } + dev->cache_width = width; + dev->cache_height = height; + for(int k=0;knum_cachelines;k++) + { + dev->cache_hash[k] = -1; + dev->cache_used[k] = 0; + free(dev->cache[k]); + dev->cache[k] = (float *)malloc(3*sizeof(float)*dev->cache_width*dev->cache_height); + bzero(dev->cache[k], 3*sizeof(float)*dev->cache_width*dev->cache_height); + dev->cache_sorted[k] = k; + } + free(dev->backbuf); + free(dev->small_backbuf); + free(dev->small_raw_buf); + int wd, ht; + dt_image_get_mip_size(dev->image, DT_IMAGE_MIPF, &wd, &ht); + // printf("cache configure! size %d\n", size); + dev->backbuf = (uint8_t *)malloc(4*sizeof(uint8_t)*dev->cache_width*dev->cache_height); + dev->small_raw_buf = (float *)malloc(3*sizeof(float)*wd*ht); + bzero(dev->small_raw_buf, 3*sizeof(float)*wd*ht); + dev->small_backbuf = (uint8_t *)malloc(4*sizeof(uint8_t)*wd*ht); + pthread_mutex_unlock(&dev->cache_mutex); + // update caches + dt_job_t job; + dt_dev_raw_load_job_init(&job, dev, dev->image); + int32_t err = dt_control_add_job_res(darktable.control, &job, DT_CTL_WORKER_1); + if(err) fprintf(stderr, "[dev_expose] job queue exceeded!\n"); + } +} + +void dt_dev_set_histogram_pre(dt_develop_t *dev) +{ + if(!dev->image) return; + dt_dev_image_t *img = dev->history + dev->history_top - 1; + float *buf = dt_dev_get_cached_buf(dev, img, DT_ZOOM_FIT, 'r'); + if(buf) + { + const float sx = dev->image->width *fminf(dev->cache_width/(float)dev->image->width, dev->cache_height/(float)dev->image->height); + const float sy = dev->image->height*fminf(dev->cache_width/(float)dev->image->width, dev->cache_height/(float)dev->image->height); + dev->histogram_pre_max = dt_iop_create_histogram_f(buf, sx, sy, dev->cache_width, dev->histogram_pre); + dt_dev_release_cached_buf(dev, img, DT_ZOOM_FIT); + } + else if(dev->small_raw_hash == img->num) + { + dev->histogram_pre_max = dt_iop_create_histogram_f(dev->small_raw_cached, dev->small_raw_width, dev->small_raw_height, dev->small_raw_width, dev->histogram_pre); + dt_dev_update_cache(dev, img, DT_ZOOM_FIT); + } +} + +void dt_dev_set_histogram(dt_develop_t *dev) +{ + if(!dev->image) return; + dt_dev_image_t *img = dev->history + dev->history_top - 1; + float *buf = dt_dev_get_cached_buf(dev, img, DT_ZOOM_FIT, 'r'); + if(buf) + { + const float sx = dev->image->width *fminf(dev->cache_width/(float)dev->image->width, dev->cache_height/(float)dev->image->height); + const float sy = dev->image->height*fminf(dev->cache_width/(float)dev->image->width, dev->cache_height/(float)dev->image->height); + dev->histogram_max = dt_iop_create_histogram_final_f(buf, sx, sy, dev->cache_width, dev->histogram, dev->gamma, dev->tonecurve); + dt_dev_release_cached_buf(dev, img, DT_ZOOM_FIT); + } + else if(dev->small_raw_hash == img->num) + { + dev->histogram_max = dt_iop_create_histogram_final_f(dev->small_raw_cached, dev->small_raw_width, dev->small_raw_height, dev->small_raw_width, dev->histogram, dev->gamma, dev->tonecurve); + dt_dev_update_cache(dev, img, DT_ZOOM_FIT); + } +} + +int32_t dt_dev_test_cached_buf(dt_develop_t *dev, dt_dev_image_t *img, dt_dev_zoom_t zoom) +{ + pthread_mutex_lock(&dev->cache_mutex); + int32_t cacheline = img->cacheline[zoom]; + int32_t hash = (img->num << 4) | zoom; + int32_t res = 0; + if(dev->cache_hash[cacheline] == hash) res = 1; // found it! + pthread_mutex_unlock(&dev->cache_mutex); + return res; +} + +float *dt_dev_get_cached_buf(dt_develop_t *dev, dt_dev_image_t *img, dt_dev_zoom_t zoom, char mode) +{ + // dt_print(DT_DEBUG_DEV, "[dev_get_cached_buf] img %d %d %d\n", img->cacheline[0], img->cacheline[1], img->cacheline[2]); + pthread_mutex_lock(&dev->cache_mutex); + int32_t cacheline = img->cacheline[zoom]; + int32_t hash = (img->num << 4) | zoom; + if(dev->cache_hash[cacheline] != hash || (dev->cache_used[cacheline] & 2)) + { + dt_print(DT_DEBUG_DEV, "[dev_get_cached_buf] miss line %d for img %d|%d, write lock: %d\n", cacheline, img->num, zoom, dev->cache_used[cacheline] & 2); + pthread_mutex_unlock(&dev->cache_mutex); + return NULL; // cache miss: not there or currently writing. + } + // sort lru list + for(int k=0;knum_cachelines;k++) + { + if(dev->cache_sorted[k] == cacheline) + { + for(int i=k;inum_cachelines-1;i++) dev->cache_sorted[i] = dev->cache_sorted[i+1]; + dev->cache_sorted[dev->num_cachelines-1] = cacheline; + break; + } + } + dev->cache_used[cacheline] = 1; + if(mode == 'w') dev->cache_used[cacheline] |= 2; + dt_print(DT_DEBUG_DEV, "[dev_get_cached_buf] img %d zoom %d in cacheline %d mode '%c'\n", img->num, zoom, cacheline, mode); + pthread_mutex_unlock(&dev->cache_mutex); + return dev->cache[cacheline]; +} + +int32_t dt_dev_reserve_new_cached_buf(dt_develop_t *dev, dt_dev_image_t *img, dt_dev_zoom_t zoom) +{ + pthread_mutex_lock(&dev->cache_mutex); + int32_t hash = (img->num << 4) | zoom; + // kick lru object and reserve it as mru for this hash. + int32_t line = img->cacheline[zoom]; + if(dev->cache_hash[line] != hash) line = dev->cache_sorted[0]; + int32_t kicked = dev->cache_hash[line]; + dev->cache_hash[line] = hash; + img->cacheline[zoom] = line; + int found = 0; + for(int i=0;inum_cachelines-1;i++) + { + if(dev->cache_sorted[i] == line) found = 1; + if(found) dev->cache_sorted[i] = dev->cache_sorted[i+1]; + } + dt_print(DT_DEBUG_DEV, "[dev_reserve_new_cached_buf] line %d for img %d|%d (was hash %d), write lock: %d\n", line, img->num, zoom, kicked, dev->cache_used[line] == 2); + + dev->cache_sorted[dev->num_cachelines-1] = line; + dev->cache_used[line] = 2; + // propagate up the stack + for(dt_dev_image_t *i2 = img+1;i2 < dev->history+dev->history_top && i2->num == img->num;i2++) for(int k=0;k<3;k++) i2->cacheline[k] = img->cacheline[k]; + pthread_mutex_unlock(&dev->cache_mutex); + return line; +} + +void dt_dev_release_cached_buf(dt_develop_t *dev, dt_dev_image_t *img, dt_dev_zoom_t zoom) +{ + pthread_mutex_lock(&dev->cache_mutex); + int32_t line = img->cacheline[zoom]; + dev->cache_used[line] = 0; + pthread_mutex_unlock(&dev->cache_mutex); + dt_print(DT_DEBUG_DEV, "[dev_release_buf] line %d for img %d|%d\n", line, img->num, zoom); +} + +// helper used to synch a single history item with db +int dt_dev_write_history_item(dt_develop_t *dev, dt_dev_image_t *h, int32_t num) +{ + if(!dev->image) return 1; + sqlite3_stmt *stmt; + int rc; + rc = sqlite3_prepare_v2(darktable.db, "select num from history where imgid = ?1 and num = ?2", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, dev->image->id); + rc = sqlite3_bind_int (stmt, 2, num); + if(sqlite3_step(stmt) != SQLITE_ROW) + { + rc = sqlite3_finalize(stmt); + rc = sqlite3_prepare_v2(darktable.db, "insert into history (imgid, num) values (?1, ?2)", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, dev->image->id); + rc = sqlite3_bind_int (stmt, 2, num); + rc = sqlite3_step (stmt); + } + rc = sqlite3_finalize (stmt); + rc = sqlite3_prepare_v2(darktable.db, "update history set operation = ?1, op_params = ?2, settings = ?3, hash = ?4 where imgid = ?5 and num = ?6", -1, &stmt, NULL); + rc = sqlite3_bind_text(stmt, 1, h->operation, strlen(h->operation), SQLITE_STATIC); + rc = sqlite3_bind_blob(stmt, 2, &(h->op_params), sizeof(dt_dev_operation_params_t), SQLITE_STATIC); + rc = sqlite3_bind_blob(stmt, 3, &(h->settings), sizeof(dt_ctl_image_settings_t), SQLITE_STATIC); + rc = sqlite3_bind_int (stmt, 4, h->num); + rc = sqlite3_bind_int (stmt, 5, dev->image->id); + rc = sqlite3_bind_int (stmt, 6, num); + rc = sqlite3_step (stmt); + rc = sqlite3_finalize (stmt); + return 0; +} + +void dt_dev_add_history_item(dt_develop_t *dev, dt_dev_operation_t op) +{ + dt_dev_image_t *img = dev->history + dev->history_top - 1; + if(dev->gui_attached) + { + dt_ctl_gui_mode_t gui; + DT_CTL_GET_GLOBAL(gui, gui); + if(gui != DT_DEVELOP) return; + dt_control_clear_history_items(dev->history_top-1); + } + // this is called exclusively from the gtk thread, so no lock is necessary. + int32_t num = dt_dev_update_fixed_pipeline(dev, op, img->num); + + if(strncmp(img->operation, op, 20) != 0) + { + if(dev->gui_attached) dt_control_add_history_item(dev->history_top, op); + for(int k=0;k<3;k++) img[1].cacheline[k] = img[0].cacheline[k]; + img ++; + dev->history_top++; + if(dev->history_top >= dev->history_max) + { + dev->history_max *= 2; + dt_dev_image_t *tmp = (dt_dev_image_t *)malloc(sizeof(dt_dev_image_t)*dev->history_max); + memcpy(tmp, dev->history, dev->history_max/2 * sizeof(dt_dev_image_t)); + free(dev->history); + dev->history = tmp; + } + } + if(dev->gui_attached) + { + pthread_mutex_lock(&(darktable.control->image_mutex)); + img->settings = darktable.control->image_settings; + pthread_mutex_unlock(&(darktable.control->image_mutex)); + } + DT_CTL_SET_GLOBAL_STR(dev_op, img->operation, 20); + DT_CTL_GET_GLOBAL(img->op_params, dev_op_params); + img->num = num; + strncpy(img->operation, op, 20); + dt_print(DT_DEBUG_DEV, "pushing history %d item with hash %d, operation %d and cachelines %d %d %d\n", dev->history_top-1, num, op, img->cacheline[0], img->cacheline[1], img->cacheline[2]); + + dev->small_backbuf_hash = -1; + dt_dev_update_small_cache(dev); +} + +void dt_dev_pop_history_items(dt_develop_t *dev, int32_t cnt) +{ + // this is called exclusively from the gtk thread, so no lock is necessary. + if(cnt == dev->history_top-1) return; + dev->history_top = cnt + 1; + if(dev->gui_attached) + { + pthread_mutex_lock(&(darktable.control->image_mutex)); + darktable.control->image_settings = dev->history[dev->history_top-1].settings; + pthread_mutex_unlock(&(darktable.control->image_mutex)); + DT_CTL_SET_GLOBAL_STR(dev_op, dev->history[dev->history_top-1].operation, 20); + DT_CTL_SET_GLOBAL(dev_op_params, dev->history[dev->history_top-1].op_params); + } + (void)dt_dev_update_fixed_pipeline(dev, "original", 0); + + dev->small_backbuf_hash = -1; + dt_dev_update_small_cache(dev); +} + +void dt_dev_write_history(dt_develop_t *dev) +{ + for(int k=0;khistory_top;k++) + (void)dt_dev_write_history_item(dev, dev->history+k, k); +#if 0 + char filename[512]; + snprintf(filename, 512, "%s/.darktable_cache/%s.hist", darktable.library->film->dirname, dev->image->filename); + FILE *f = fopen(filename, "wb"); + if(!f) return; // TODO: write to common stream, error handling + // write stack + (void)fwrite(&(dev->history_top), 1, sizeof(int32_t), f); + for(int k=0;khistory_top;k++) + (void)fwrite(dev->history + k, 1, sizeof(dt_dev_image_t), f); + fclose(f); +#endif +} + +void dt_dev_read_history(dt_develop_t *dev) +{ + if(dev->gui_attached) dt_control_clear_history_items(0); + if(!dev->image) return; + sqlite3_stmt *stmt; + int rc; + rc = sqlite3_prepare_v2(darktable.db, "select * from history where imgid = ?1 order by num", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, dev->image->id); + dev->history_top = 0; + if(sqlite3_step(stmt) == SQLITE_ROW) + { + do + { + dt_dev_image_t *h = dev->history + dev->history_top; + h->num = sqlite3_column_int(stmt, 2); + strncpy(h->operation, (char *)sqlite3_column_text(stmt, 3), 20); + memcpy(&(h->op_params), sqlite3_column_blob(stmt, 4), sizeof(dt_dev_operation_params_t)); + memcpy(&(h->settings), sqlite3_column_blob(stmt, 5), sizeof(dt_ctl_image_settings_t)); + dev->history_top++; + if(dev->history_top >= dev->history_max) + { + dev->history_max *= 2; + dt_dev_image_t *tmp = (dt_dev_image_t *)malloc(sizeof(dt_dev_image_t)*dev->history_max); + memcpy(tmp, dev->history, dev->history_max/2 * sizeof(dt_dev_image_t)); + free(dev->history); + dev->history = tmp; + } + if(dev->gui_attached) dt_control_add_history_item(dev->history_top-1, dev->history[dev->history_top-1].operation); + } + while(sqlite3_step(stmt) == SQLITE_ROW); + rc = sqlite3_finalize (stmt); + } + else + { + rc = sqlite3_finalize (stmt); + dev->history_top = 1; + dev->history[0].num = dev->image->id << 6; + for(int k=0;k<3;k++) dev->history[0].cacheline[k] = 0; + dev->history[0].settings = darktable.control->image_defaults; + strncpy(dev->history[0].operation, "original", 20); + if(dev->gui_attached) dt_control_add_history_item(0, "original"); + dt_dev_write_history_item(dev, dev->history, 0); + } + +#if 0 + char filename[512]; + snprintf(filename, 512, "%s/.darktable_cache/%s.hist", darktable.library->film->dirname, dev->image->filename); + FILE *f = fopen(filename, "rb"); + if(!f) + { + dev->history_top = 1; + dev->history[0].num = dev->image->id << 6; + for(int k=0;k<3;k++) dev->history[0].cacheline[k] = 0; + dev->history[0].settings = darktable.control->image_defaults; + strncpy(dev->history[0].operation, "original", 20); + if(dev->gui_attached) dt_control_add_history_item(0, "original"); + } + else + { + (void)fread(&(dev->history_top), 1, sizeof(int32_t), f); + for(int k=0;khistory_top;k++) + { + (void)fread(dev->history + k, 1, sizeof(dt_dev_image_t), f); + if(dev->gui_attached) dt_control_add_history_item(k, dev->history[k].operation); + } + fclose(f); + } +#endif + if(dev->gui_attached) + { + DT_CTL_SET_GLOBAL_STR(dev_op, dev->history[dev->history_top-1].operation, 20); + DT_CTL_SET_GLOBAL(dev_op_params, dev->history[dev->history_top-1].op_params); + pthread_mutex_lock(&(darktable.control->image_mutex)); + darktable.control->image_settings = dev->history[dev->history_top - 1].settings; + pthread_mutex_unlock(&(darktable.control->image_mutex)); + } + (void)dt_dev_update_fixed_pipeline(dev, "original", 0); +} + +void dt_dev_check_zoom_bounds(dt_develop_t *dev, float *zoom_x, float *zoom_y, dt_dev_zoom_t zoom, int closeup, float *boxww, float *boxhh) +{ + float boxw = 1, boxh = 1; + if(zoom == DT_ZOOM_1) + { + const float imgw = (closeup ? 2 : 1)*dev->image->width; + const float imgh = (closeup ? 2 : 1)*dev->image->height; + const float devw = MIN(imgw, dev->cache_width); + const float devh = MIN(imgh, dev->cache_height); + boxw = fminf(1.0, devw/imgw); boxh = fminf(1.0, devh/imgh); + } + else if(zoom == DT_ZOOM_FILL) + { + const float imgw = dev->image->width; + const float imgh = dev->image->height; + const float devw = dev->cache_width; + const float devh = dev->cache_height; + boxw = devw/(imgw*fmaxf(devw/imgw, devh/imgh)); boxh = devh/(imgh*fmaxf(devw/imgw, devh/imgh)); + } + + if(*zoom_x < boxw/2 - .5f) *zoom_x = boxw/2 - .5f; + if(*zoom_x > .5f - boxw/2) *zoom_x = .5f - boxw/2; + if(*zoom_y < boxh/2 - .5f) *zoom_y = boxh/2 - .5f; + if(*zoom_y > .5f - boxh/2) *zoom_y = .5f - boxh/2; + + if(boxww) *boxww = boxw; + if(boxhh) *boxhh = boxh; +} + +void dt_dev_export(dt_job_t *job) +{ + while(1) + { + // TODO: progress bar in ctl? + char filename[512]; + dt_image_t *img = NULL; + pthread_mutex_lock(&(darktable.library->film->images_mutex)); + int32_t rc, start = darktable.library->film->last_exported++; + pthread_mutex_unlock(&(darktable.library->film->images_mutex)); + sqlite3_stmt *stmt; + rc = sqlite3_prepare_v2(darktable.db, "select * from (select * from selected_images) as dreggn limit ?1, 1", -1, &stmt, NULL); + rc = sqlite3_bind_int(stmt, 1, start); + if(sqlite3_step(stmt) == SQLITE_ROW) + { + int imgid = sqlite3_column_int(stmt, 0); + img = dt_image_cache_get(imgid, 'r'); + } + sqlite3_finalize(stmt); + if(!img) + { + // TODO: reset progress bar + return; + } + + snprintf(filename, 512, "%s/darktable_exported", darktable.library->film->dirname); + if(g_mkdir_with_parents(filename, 0755)) + { + dt_image_cache_release(img, 'r'); + fprintf(stderr, "[dev_export] could not create directory %s!\n", filename); + return; + } + snprintf(filename, 512-4, "%s/darktable_exported/%s", darktable.library->film->dirname, img->filename); + char *c = filename + strlen(filename); + for(;c>filename && *c != '.';c--); + if(c <= filename) c = filename + strlen(filename); + + // read type from global config. + dt_dev_export_format_t fmt; + DT_CTL_GET_GLOBAL(fmt, dev_export_format); + switch(fmt) + { + case DT_DEV_EXPORT_JPG: + strncpy(c, ".jpg", 4); + dt_imageio_export_8(img, filename); + break; + case DT_DEV_EXPORT_PNG: + strncpy(c, ".png", 4); + dt_imageio_export_8(img, filename); + break; + case DT_DEV_EXPORT_PPM16: + strncpy(c, ".ppm", 4); + dt_imageio_export_16(img, filename); + break; + case DT_DEV_EXPORT_PFM: + strncpy(c, ".pfm", 4); + dt_imageio_export_f(img, filename); + break; + default: + break; + } + dt_image_cache_release(img, 'r'); + } +} diff --git a/src/develop/develop.h b/src/develop/develop.h new file mode 100644 index 000000000000..1a3c2ab790fc --- /dev/null +++ b/src/develop/develop.h @@ -0,0 +1,123 @@ +#ifndef DARKTABLE_DEVELOP_H +#define DARKTABLE_DEVELOP_H + +#include "common/imageio.h" +#include "library/library.h" +#include "control/settings.h" + +#include +#include + +#define DT_DEV_CACHELINES 5 + +extern uint8_t dt_dev_default_gamma[0x10000]; +extern float dt_dev_de_gamma[0x100]; + +/** + * image for devel module. + * stores cache line pointers, + * along with an img-op last performed on this img. + * can be stacked for history. + */ +typedef struct dt_dev_image_t +{ + dt_dev_operation_t operation; + dt_dev_operation_params_t op_params; + int32_t cacheline[3]; + int32_t num; + dt_ctl_image_settings_t settings; +} +dt_dev_image_t; + + +typedef struct dt_develop_t +{ + int32_t gui_attached; // != 0 if the gui should be notified of changes in hist stack etc. + int32_t image_loading; + + // small cached version while loading + float *small_raw_buf; + float *small_raw_cached; + int32_t small_raw_hash; + int32_t small_raw_loading; + int32_t small_raw_width, small_raw_height; + + // converted img last viewed + uint8_t *backbuf; + uint8_t *small_backbuf; + int32_t backbuf_hash, small_backbuf_hash; + + // image under consideration. + dt_image_t *image; + + // cached versions of raw images, various zoom rates + pthread_mutex_t cache_mutex; + int32_t num_cachelines; + int32_t cache_width, cache_height; + float *cache_zoom_x, *cache_zoom_y; + float **cache; + int32_t *cache_sorted; + int32_t *cache_used; + int32_t *cache_hash; + + // history stack + uint32_t last_hash; + int32_t history_top, history_max; + dt_dev_image_t *history; + + // image specific stuff + uint32_t *histogram, *histogram_pre; + uint32_t histogram_max, histogram_pre_max; + + // gamma table + uint8_t gamma[0x10000]; + uint16_t tonecurve[0x10000]; +} +dt_develop_t; + +void dt_dev_init(dt_develop_t *dev, int32_t gui_attached); +void dt_dev_cleanup(dt_develop_t *dev); + +void dt_dev_cache_load(dt_develop_t *dev, int32_t stackpos, dt_dev_zoom_t zoom); +void dt_dev_raw_load(dt_develop_t *dev, dt_image_t *img); +int dt_dev_small_cache_load(dt_develop_t *dev); + +void dt_dev_load_image(dt_develop_t *dev, struct dt_image_t *img); + +void dt_dev_add_history_item(dt_develop_t *dev, dt_dev_operation_t op); +void dt_dev_pop_history_items(dt_develop_t *dev, int32_t cnt); +void dt_dev_write_history(dt_develop_t *dev); +void dt_dev_read_history(dt_develop_t *dev); + +void dt_dev_set_gamma(dt_develop_t *dev); +void dt_dev_set_histogram(dt_develop_t *dev); +void dt_dev_set_histogram_pre(dt_develop_t *dev); + +void dt_dev_configure(dt_develop_t *dev, int32_t width, int32_t height); +void dt_dev_check_zoom_bounds(dt_develop_t *dev, float *zoom_x, float *zoom_y, dt_dev_zoom_t zoom, int closeup, float *boxw, float *boxh); + +float *dt_dev_get_cached_buf(dt_develop_t *dev, dt_dev_image_t *img, dt_dev_zoom_t zoom, char mode); +int32_t dt_dev_reserve_new_cached_buf(dt_develop_t *dev, dt_dev_image_t *img, dt_dev_zoom_t zoom); +int32_t dt_dev_test_cached_buf(dt_develop_t *dev, dt_dev_image_t *img, dt_dev_zoom_t zoom); +void dt_dev_release_cached_buf(dt_develop_t *dev, dt_dev_image_t *img, dt_dev_zoom_t zoom); + +void dt_dev_update_cache(dt_develop_t *dev, dt_dev_image_t *img, dt_dev_zoom_t zoom); +void dt_dev_update_small_cache(dt_develop_t *dev); + +struct dt_job_t; +void dt_dev_export(struct dt_job_t *job); + + +// ==================== +// gui methods + +// void dt_dev_init(dt_develop_t *dev); +// void dt_dev_cleanup(dt_develop_t *dev); + +void dt_dev_leave(); +void dt_dev_enter(); + +void dt_dev_image_expose(struct dt_develop_t *dev, dt_dev_image_t *image, cairo_t *cr, int32_t width, int32_t height); +void dt_dev_expose(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height); + +#endif diff --git a/src/develop/imageop.c b/src/develop/imageop.c new file mode 100644 index 000000000000..eb94cd729d4a --- /dev/null +++ b/src/develop/imageop.c @@ -0,0 +1,143 @@ +#include "control/control.h" +#include "develop/imageop.h" +#include +#include +#include +#include +#include + +void dt_iop_init(dt_iop_t *iop) +{ + iop->module = NULL; + // TODO: add dummy module entries for fixed function pipeline with empty execute etc. + // TODO: query modules directory (-ies) + // TODO: read libmodule.so + // TODO: init dt_iop_module_t: + // name, functions, execute gui_init method: + // TODO: gui system: add expander to right panel, gui_init return: a container + // TODO: develop read history: check if operation is available in this distro! + // TODO: qsort module by name! +} + +void dt_iop_cleanup(dt_iop_t *iop) +{ + // TODO: + // for(int k=0;knum_modules) iop->module[k].gui_cleanup(); + free(iop->module); +} + +void dt_iop_gui_reset() +{ + // TODO: reset all modules + dt_iop_gui_reset_hsb(); +} + +void dt_iop_gui_init() +{ + // TODO: init all modules' guis? + dt_iop_gui_init_hsb(); +} + +void dt_iop_execute(float *dst, const float *src, const int32_t wd, const int32_t ht, const int32_t bufwd, const int32_t bufht, + dt_dev_operation_t operation, dt_dev_operation_params_t *params) +{ + // TODO: bsearch module op => execute (else print some error) + if(strncmp(operation, "hsb", 20) == 0) + { + dt_iop_execute_hsb(dst, src, wd, ht, bufwd, bufht, operation, params); + } + // fprintf(stderr, "[dt_iop_execute] unknown operation %d\n", operation); +} + +void dt_iop_clip_and_zoom(const float *i, int32_t ix, int32_t iy, int32_t iw, int32_t ih, int32_t ibw, int32_t ibh, + float *o, int32_t ox, int32_t oy, int32_t ow, int32_t oh, int32_t obw, int32_t obh) +{ + const float scalex = iw/(float)ow; + const float scaley = ih/(float)oh; + int32_t ix2 = MAX(ix, 0); + int32_t iy2 = MAX(iy, 0); + int32_t ox2 = MAX(ox, 0); + int32_t oy2 = MAX(oy, 0); + int32_t oh2 = MIN(MIN(oh, (ibh - iy2)/scaley), obh - oy2); + int32_t ow2 = MIN(MIN(ow, (ibw - ix2)/scalex), obw - ox2); + assert((int)(ix2 + ow2*scalex) <= ibw); + assert((int)(iy2 + oh2*scaley) <= ibh); + assert(ox2 + ow2 <= obw); + assert(oy2 + oh2 <= obh); + assert(ix2 >= 0 && iy2 >= 0 && ox2 >= 0 && oy2 >= 0); + float x = ix2, y = iy2; + for(int s=0;s rgb[1] ? (rgb[0] > rgb[2] ? rgb[0] : rgb[2]) : (rgb[1] > rgb[2] ? rgb[1] : rgb[2]); + hist[4*lum+3] ++; + } + // don't count <= 0 pixels + for(int k=19;k<4*256;k+=4) max = max > hist[k] ? max : hist[k]; + return max; +} + +uint32_t dt_iop_create_histogram_f(const float *pixels, int32_t width, int32_t height, int32_t stride, uint32_t *hist) +{ + uint32_t max = 0; + bzero(hist, sizeof(uint32_t)*4*0x100); + for(int j=0;j rgb[1] ? (rgb[0] > rgb[2] ? rgb[0] : rgb[2]) : (rgb[1] > rgb[2] ? rgb[1] : rgb[2]); + hist[4*lum+3] ++; + } + // don't count <= 0 pixels + for(int k=19;k<4*256;k+=4) max = max > hist[k] ? max : hist[k]; + return max; +} + +uint32_t dt_iop_create_histogram_8(const uint8_t *pixels, int32_t width, int32_t height, int32_t stride, uint32_t *hist) +{ + uint32_t max = 0; + bzero(hist, sizeof(uint32_t)*4*0x100); + for(int j=0;j rgb[1] ? (rgb[0] > rgb[2] ? rgb[0] : rgb[2]) : (rgb[1] > rgb[2] ? rgb[1] : rgb[2]); + hist[4*lum+3] ++; + } + for(int k=19;k<4*256;k+=4) max = max > hist[k] ? max : hist[k]; + return max; +} + diff --git a/src/develop/imageop.h b/src/develop/imageop.h new file mode 100644 index 000000000000..a2d5c992fee8 --- /dev/null +++ b/src/develop/imageop.h @@ -0,0 +1,44 @@ +#ifndef DT_DEVELOP_IMAGEOP_H +#define DT_DEVELOP_IMAGEOP_H + +#include "develop/develop.h" +#include "develop/iop_hsb.h" + +#define dt_red 2 +#define dt_green 1 +#define dt_blue 0 + +typedef struct dt_iop_module_t +{ + dt_dev_operation_t op; // TODO: typedef char[20] + void (*gui_reset) (); // TODO: darktable as param + void (*gui_init) (); // TODO: return container + void (*gui_cleanup) (); // TODO: pass this container + void (*execute) (float *dst, const float *src, const int32_t wd, const int32_t ht, const int32_t bufwd, const int32_t bufht, + dt_dev_operation_t operation, dt_dev_operation_params_t *params); +} +dt_iop_module_t; + +typedef struct dt_iop_t +{ + int32_t num_modules; + dt_iop_module_t *module; +} +dt_iop_t; + + +// job that transforms in pixel block to out pixel block, 4x supersampling +void dt_iop_clip_and_zoom(const float *i, int32_t ix, int32_t iy, int32_t iw, int32_t ih, int32_t ibw, int32_t ibh, + float *o, int32_t ox, int32_t oy, int32_t ow, int32_t oh, int32_t obw, int32_t obh); + +uint32_t dt_iop_create_histogram_final_f(const float *pixels, int32_t width, int32_t height, int32_t stride, uint32_t *hist, uint8_t *gamma, uint16_t *tonecurve); +uint32_t dt_iop_create_histogram_f(const float *pixels, int32_t width, int32_t height, int32_t stride, uint32_t *hist); +uint32_t dt_iop_create_histogram_8 (const uint8_t *pixels, int32_t width, int32_t height, int32_t stride, uint32_t *hist); + +void dt_iop_execute(float *dst, const float *src, const int32_t wd, const int32_t ht, const int32_t bufwd, const int32_t bufht, + dt_dev_operation_t operation, dt_dev_operation_params_t *params); + +void dt_iop_gui_reset(); +void dt_iop_gui_init(); + +#endif diff --git a/src/develop/iop_hsb.c b/src/develop/iop_hsb.c new file mode 100644 index 000000000000..e29f62cc4d84 --- /dev/null +++ b/src/develop/iop_hsb.c @@ -0,0 +1,193 @@ +#include "develop/iop_hsb.h" +#include "gui/gtk.h" +// #include "control/settings.h" +#include "control/control.h" +#include "develop/imageop.h" + +#include +#include + +void +dt_iop_gui_reset_hsb () +{ + GtkWidget *widget; + float hue, sat, bri; + dt_dev_operation_t op; + DT_CTL_GET_GLOBAL_STR(op, dev_op, 20); + DT_CTL_GET_GLOBAL(hue, dev_op_params.f[0]); + DT_CTL_GET_GLOBAL(sat, dev_op_params.f[1]); + DT_CTL_GET_GLOBAL(bri, dev_op_params.f[2]); + if(strncmp(op, "hsb", 20)) + { + hue = sat = bri = 1.0; + widget = glade_xml_get_widget (darktable.gui->main_window, "hsb_hue"); + gtk_range_set_value(GTK_RANGE(widget), hue); + widget = glade_xml_get_widget (darktable.gui->main_window, "hsb_saturation"); + gtk_range_set_value(GTK_RANGE(widget), sat); + widget = glade_xml_get_widget (darktable.gui->main_window, "hsb_brightness"); + gtk_range_set_value(GTK_RANGE(widget), bri); + } +} + +void +dt_iop_gui_init_hsb () +{ + // hsb expander + GtkWidget *widget; + widget = glade_xml_get_widget (darktable.gui->main_window, "hsb_hue"); + g_signal_connect (G_OBJECT (widget), "value-changed", + G_CALLBACK (dt_iop_gui_callback_hsb), (gpointer)0); + + widget = glade_xml_get_widget (darktable.gui->main_window, "hsb_saturation"); + g_signal_connect (G_OBJECT (widget), "value-changed", + G_CALLBACK (dt_iop_gui_callback_hsb), (gpointer)1); + + widget = glade_xml_get_widget (darktable.gui->main_window, "hsb_brightness"); + g_signal_connect (G_OBJECT (widget), "value-changed", + G_CALLBACK (dt_iop_gui_callback_hsb), (gpointer)2); +} + +void +dt_iop_gui_callback_hsb (GtkRange *range, gpointer user_data) +{ + if(darktable.gui->reset) return; + // FIXME: is this block needed?? + dt_dev_operation_t op; + DT_CTL_GET_GLOBAL_STR(op, dev_op, 20); + if(strncmp(op, "hsb", 20)) + { + DT_CTL_SET_GLOBAL(dev_op_params.f[0], 1.0); + DT_CTL_SET_GLOBAL(dev_op_params.f[1], 1.0); + DT_CTL_SET_GLOBAL(dev_op_params.f[2], 1.0); + DT_CTL_SET_GLOBAL_STR(dev_op, "hsb", 20); + } + // FIXME: end. + if(user_data == (gpointer)0) + { + float tmp = gtk_range_get_value(range); + DT_CTL_SET_GLOBAL(dev_op_params.f[0], tmp); + } + else if(user_data == (gpointer)1) + { + float tmp = gtk_range_get_value(range); + DT_CTL_SET_GLOBAL(dev_op_params.f[1], tmp); + } + else + { + float tmp = gtk_range_get_value(range); + DT_CTL_SET_GLOBAL(dev_op_params.f[2], tmp); + } + dt_dev_add_history_item(darktable.develop, "hsb"); +} + +// helper functions: +void +dt_rgb_to_hsv_f(const float *rgb, float *hsv) +{ + float min, max, delta; + min = fminf(rgb[dt_red], fminf(rgb[dt_green], rgb[dt_blue])); + max = fmaxf(rgb[dt_red], fmaxf(rgb[dt_green], rgb[dt_blue])); + hsv[2] = max; + delta = max - min; + + if(max == 0) hsv[1] = hsv[0] = 0; + else + { + hsv[1] = delta / max; + float tmph; + if (delta == 0) tmph = 0; + else if(rgb[dt_red] == max) tmph = (rgb[dt_green] - rgb[dt_blue]) / delta; // between yellow & magenta + else if(rgb[dt_green] == max) tmph = 2 + (rgb[dt_blue] - rgb[dt_red]) / delta; // between cyan & yellow + else tmph = 4 + (rgb[dt_red] - rgb[dt_green]) / delta; // between magenta & cyan + hsv[0] = tmph*60./360.; + } +} + +void +dt_hsv_to_rgb_f(const float *hsv, float *rgb) +{ + const float h = hsv[0]*6.0; + const int i = (int)h; + const float f = (h - i), s = hsv[1], v = hsv[2]; + const float p = v * (1. - s); + const float q = v * (1. - (s * f)); + const float t = v * (1. - (s * (1. - f ))); + switch(i) + { + case 0: rgb[dt_red] = v; rgb[dt_green] = t; rgb[dt_blue] = p; break; + case 1: rgb[dt_red] = q; rgb[dt_green] = v; rgb[dt_blue] = p; break; + case 2: rgb[dt_red] = p; rgb[dt_green] = v; rgb[dt_blue] = t; break; + case 3: rgb[dt_red] = p; rgb[dt_green] = q; rgb[dt_blue] = v; break; + case 4: rgb[dt_red] = t; rgb[dt_green] = p; rgb[dt_blue] = v; break; + default: rgb[dt_red] = v; rgb[dt_green] = p; rgb[dt_blue] = q; break; + } +} + +#if 0 +void dt_rgb_to_hsv_16(const uint16_t *rgb, uint16_t *hsv) +{ + uint64_t min, max, delta; + min = MIN(rgb[red], MIN(rgb[green], rgb[blue])); + max = MAX(rgb[red], MAX(rgb[green], rgb[blue])); + hsv[2] = max; + delta = max - min; + + if(max == 0) hsv[1] = hsv[0] = 0; + else + { + hsv[1] = (0xffff * delta) / max;//I16CLAMP(delta / (float)max); + double tmph; + if (delta == 0) tmph = 0; + else if(rgb[red] == max) tmph = (rgb[green] - rgb[blue]) / (double)delta; // between yellow & magenta + else if(rgb[green] == max) tmph = 2 + (rgb[blue] - rgb[red]) / (double)delta; // between cyan & yellow + else tmph = 4 + (rgb[red] - rgb[green]) / (double)delta; // between magenta & cyan + hsv[0] = (uint16_t)(0x10000*tmph*60./360.); // rotation allowed + } +} + +void dt_hsv_to_rgb_16(const uint16_t *hsv, uint16_t *rgb) +{ + const double h = hsv[0]*6.0/0x10000; + const int i = (int)h; + const int64_t f = 0x10000*(h - i), s = hsv[1], v = hsv[2]; + const uint16_t p = v * (0x10000 - s)/0x10000; + const uint16_t q = v * (0x10000 - (s * f)/0x10000)/0x10000; + const uint16_t t = v * (0x10000 - (s * ( 0x10000 - f ))/0x10000)/0x10000; + switch(i) + { + case 0: rgb[red] = v; rgb[green] = t; rgb[blue] = p; break; + case 1: rgb[red] = q; rgb[green] = v; rgb[blue] = p; break; + case 2: rgb[red] = p; rgb[green] = v; rgb[blue] = t; break; + case 3: rgb[red] = p; rgb[green] = q; rgb[blue] = v; break; + case 4: rgb[red] = t; rgb[green] = p; rgb[blue] = v; break; + default: rgb[red] = v; rgb[green] = p; rgb[blue] = q; break; + } +} +#endif + +// main op function: convert buffer applying hue saturation brightness changes. +void +dt_iop_execute_hsb(float *dst, const float *src, const int32_t wd, const int32_t ht, const int32_t bufwd, const int32_t bufht, + dt_dev_operation_t operation, dt_dev_operation_params_t *params) +{ + float hsbmul[3]; + hsbmul[0] = params->f[0]; + hsbmul[1] = params->f[1]; + hsbmul[2] = params->f[2]; + float hsv[3]; + const int ht2 = MIN(ht, bufht); + const int wd2 = MIN(wd, bufwd); + int idx = 0; + for(int j=0;j +#include +#include +#include "gui/curveeditor.h" +#include "gui/histogram.h" +#include "develop/develop.h" +#include "control/control.h" +#include "gui/gtk.h" + +#define DT_GUI_CURVE_EDITOR_DRAW_RES 64 +#define DT_GUI_CURVE_EDITOR_INSET 5 +#define DT_GUI_CURVE_INFL .3f + +#ifndef M_PI +# define M_PI 3.14159265358979323846 /* pi */ +#endif + +void dt_gui_curve_editor_init(dt_gui_curve_editor_t *c, GtkWidget *widget) +{ + c->mouse_x = c->mouse_y = -1.0; + c->selected = -1; c->selected_offset = 0.0; + c->dragging = 0; + + c->draw.m_samplingRes = DT_GUI_CURVE_EDITOR_DRAW_RES; + c->draw.m_outputRes = 0x10000; + c->draw.m_Samples = (uint16_t *)malloc(c->draw.m_samplingRes*sizeof(uint16_t)); + c->draw_min.m_samplingRes = DT_GUI_CURVE_EDITOR_DRAW_RES; + c->draw_min.m_outputRes = 0x10000; + c->draw_min.m_Samples = (uint16_t *)malloc(c->draw.m_samplingRes*sizeof(uint16_t)); + c->draw_max.m_samplingRes = DT_GUI_CURVE_EDITOR_DRAW_RES; + c->draw_max.m_outputRes = 0x10000; + c->draw_max.m_Samples = (uint16_t *)malloc(c->draw.m_samplingRes*sizeof(uint16_t)); + + c->convert.m_samplingRes = 0x10000; + c->convert.m_outputRes = 0x10000; + c->convert.m_Samples = NULL; + + c->curve.m_curveType = TONE_CURVE; + c->curve.m_numAnchors = 6; + c->curve.m_gamma = 1.0; + c->curve.m_min_x = 0.0; + c->curve.m_max_x = 1.0; + c->curve.m_min_y = 0.0; + c->curve.m_max_y = 1.0; + + // CurveDataSample(&c->curve, &c->draw); + + g_signal_connect (G_OBJECT (widget), "expose-event", + G_CALLBACK (dt_gui_curve_editor_expose), c); + g_signal_connect (G_OBJECT (widget), "button-press-event", + G_CALLBACK (dt_gui_curve_editor_button_press), c); + g_signal_connect (G_OBJECT (widget), "button-release-event", + G_CALLBACK (dt_gui_curve_editor_button_release), c); + g_signal_connect (G_OBJECT (widget), "motion-notify-event", + G_CALLBACK (dt_gui_curve_editor_motion_notify), c); + g_signal_connect (G_OBJECT (widget), "leave-notify-event", + G_CALLBACK (dt_gui_curve_editor_leave_notify), c); +} + +void dt_gui_curve_editor_cleanup(dt_gui_curve_editor_t *c) +{ + free(c->draw.m_Samples); +} + +gboolean dt_gui_curve_editor_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data) +{ + dt_gui_curve_editor_t *c = (dt_gui_curve_editor_t *)user_data; + c->mouse_x = c->mouse_y = -1.0; + gtk_widget_queue_draw(widget); + return TRUE; +} + +gboolean dt_gui_curve_editor_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) +{ + dt_gui_curve_editor_t *c = (dt_gui_curve_editor_t *)user_data; + const int inset = DT_GUI_CURVE_EDITOR_INSET; + int width = widget->allocation.width, height = widget->allocation.height; + cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo_t *cr = cairo_create(cst); + // clear bg + cairo_set_source_rgb (cr, .2, .2, .2); + cairo_paint(cr); + + cairo_translate(cr, inset, inset); + width -= 2*inset; height -= 2*inset; + + for(int k=0;k<6;k++) + { + DT_CTL_GET_IMAGE(c->curve.m_anchors[k].x, tonecurve_x[k]); + DT_CTL_GET_IMAGE(c->curve.m_anchors[k].y, tonecurve_y[k]); + } +#if 0 + // draw shadow around + float alpha = 1.0f; + for(int k=0;kmouse_y > 0 || c->dragging) + { + const float tmp = c->curve.m_anchors[c->selected].y; + float tmp2 = 0.0f; + if(c->selected == 2) tmp2 = c->curve.m_anchors[1].y; + if(c->selected == 3) tmp2 = c->curve.m_anchors[4].y; + if(c->selected == 2) c->curve.m_anchors[1].y = fminf(c->selected_min, fmaxf(0.0, c->curve.m_anchors[1].y + DT_GUI_CURVE_INFL*(c->selected_min - c->curve.m_anchors[2].y))); + if(c->selected == 3) c->curve.m_anchors[4].y = fmaxf(c->selected_min, fminf(1.0, c->curve.m_anchors[4].y + DT_GUI_CURVE_INFL*(c->selected_min - c->curve.m_anchors[3].y))); + c->curve.m_anchors[c->selected].y = c->selected_min; + CurveDataSample(&c->curve, &c->draw_min); + if(c->selected == 2) c->curve.m_anchors[1].y = fminf(c->selected_max, fmaxf(0.0, c->curve.m_anchors[1].y + DT_GUI_CURVE_INFL*(c->selected_max - c->curve.m_anchors[2].y))); + if(c->selected == 3) c->curve.m_anchors[4].y = fmaxf(c->selected_max, fminf(1.0, c->curve.m_anchors[4].y + DT_GUI_CURVE_INFL*(c->selected_max - c->curve.m_anchors[3].y))); + c->curve.m_anchors[c->selected].y = c->selected_max; + CurveDataSample(&c->curve, &c->draw_max); + c->curve.m_anchors[c->selected].y = tmp; + if(c->selected == 2) c->curve.m_anchors[1].y = tmp2; + if(c->selected == 3) c->curve.m_anchors[4].y = tmp2; + } + CurveDataSample(&c->curve, &c->draw); + + // draw grid + cairo_set_line_width(cr, .4); + cairo_set_source_rgb (cr, .1, .1, .1); + for(int k=1;k<4;k++) + { + cairo_move_to(cr, k/4.0*width, 0); cairo_line_to(cr, k/4.0*width, height); + // cairo_move_to(cr, c->curve.m_anchors[k].x*width, 0); cairo_line_to(cr, c->curve.m_anchors[k].x*width, height); + cairo_stroke(cr); + cairo_move_to(cr, 0, k/4.0*height); cairo_line_to(cr, width, k/4.0*height); + cairo_stroke(cr); + } + + // draw selected cursor + cairo_set_line_width(cr, 1.); + cairo_translate(cr, 0, height); + + // draw lum h istogram in background + dt_develop_t *dev = darktable.develop; + uint32_t *hist, hist_max; + hist = dev->histogram_pre; + hist_max = dev->histogram_pre_max; + if(hist_max > 0) + { + cairo_save(cr); + cairo_scale(cr, 256.0/width, -height/(float)hist_max); + cairo_set_source_rgba(cr, .2, .2, .2, 0.5); + dt_gui_histogram_draw_8(cr, hist, 3); + cairo_restore(cr); + } + + if(c->mouse_y > 0 || c->dragging) + { + // draw min/max, if selected + cairo_set_source_rgba(cr, .6, .6, .6, .5); + cairo_move_to(cr, 0, 0); + for(int k=0;kdraw.m_samplingRes;k++) cairo_line_to(cr, k*width/(float)c->draw_min.m_samplingRes, - height/(float)c->draw_min.m_outputRes*c->draw_min.m_Samples[k]); + for(int k=c->draw.m_samplingRes-2;k>0;k--) cairo_line_to(cr, k*width/(float)c->draw_max.m_samplingRes, - height/(float)c->draw_max.m_outputRes*c->draw_max.m_Samples[k]); + cairo_close_path(cr); + cairo_fill(cr); + // draw mouse focus circle + cairo_set_source_rgb(cr, .9, .9, .9); + const float pos = c->draw.m_samplingRes * c->mouse_x/(float)width; + int k = (int)pos; const float f = k - pos; + if(k >= c->draw.m_samplingRes-1) k = c->draw.m_samplingRes - 2; + float ht = -height/(float)c->draw.m_outputRes*(f*c->draw.m_Samples[k] + (1-f)*c->draw.m_Samples[k+1]); + cairo_arc(cr, c->mouse_x, ht+2.5, 4, 0, 2.*M_PI); + cairo_stroke(cr); + } + + // draw curve + cairo_set_line_width(cr, 2.); + cairo_set_source_rgb(cr, .9, .9, .9); + // cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); + cairo_move_to(cr, 0, 0); + for(int k=0;kdraw.m_samplingRes;k++) cairo_line_to(cr, k*width/(float)c->draw.m_samplingRes, - height/(float)c->draw.m_outputRes*c->draw.m_Samples[k]); + cairo_stroke(cr); + + cairo_destroy(cr); + cairo_t *cr_pixmap = gdk_cairo_create(gtk_widget_get_window(widget)); + cairo_set_source_surface (cr_pixmap, cst, 0, 0); + cairo_paint(cr_pixmap); + cairo_destroy(cr_pixmap); + cairo_surface_destroy(cst); + return TRUE; +} + +gboolean dt_gui_curve_editor_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +{ + const int inset = DT_GUI_CURVE_EDITOR_INSET; + dt_gui_curve_editor_t *c = (dt_gui_curve_editor_t *)user_data; + int height = widget->allocation.height - 2*inset, width = widget->allocation.width - 2*inset; + if(!c->dragging) c->mouse_x = CLAMP(event->x - inset, 0, width); + c->mouse_y = CLAMP(event->y - inset, 0, height); + + if(c->dragging) + { + float f = c->selected_y - (c->mouse_y-c->selected_offset)/height; + f = fmaxf(c->selected_min, fminf(c->selected_max, f)); + if(c->selected == 2) c->curve.m_anchors[1].y = fminf(f, fmaxf(0.0, c->curve.m_anchors[1].y + DT_GUI_CURVE_INFL*(f - c->curve.m_anchors[2].y))); + if(c->selected == 3) c->curve.m_anchors[4].y = fmaxf(f, fminf(1.0, c->curve.m_anchors[4].y + DT_GUI_CURVE_INFL*(f - c->curve.m_anchors[3].y))); + c->curve.m_anchors[c->selected].y = f; + DT_CTL_SET_IMAGE(tonecurve_y[c->selected], f); + if(c->selected == 2) DT_CTL_SET_IMAGE(tonecurve_y[1], c->curve.m_anchors[1].y); + if(c->selected == 3) DT_CTL_SET_IMAGE(tonecurve_y[4], c->curve.m_anchors[4].y); + dt_dev_add_history_item(darktable.develop, "tonecurve"); + } + else + { + float pos = (event->x - inset)/width; + float min = 100.0; + int nearest = 0; + for(int k=1;kcurve.m_numAnchors-1;k++) + { + float dist = (pos - c->curve.m_anchors[k].x); dist *= dist; + if(dist < min) { min = dist; nearest = k; } + } + c->selected = nearest; + c->selected_y = c->curve.m_anchors[c->selected].y; + c->selected_offset = c->mouse_y; + // c->selected_min = .5f*(c->selected_y + c->curve.m_anchors[c->selected-1].y); + // c->selected_max = .5f*(c->selected_y + c->curve.m_anchors[c->selected+1].y); + const float f = 0.8f; + c->selected_min = fmaxf(c->selected_y - 0.2f, (1.-f)*c->selected_y + f*c->curve.m_anchors[c->selected-1].y); + c->selected_max = fminf(c->selected_y + 0.2f, (1.-f)*c->selected_y + f*c->curve.m_anchors[c->selected+1].y); + if(c->selected == 1) c->selected_max *= 0.7; + if(c->selected == 4) c->selected_min = 1.0 - 0.7*(1.0 - c->selected_min); + } + gtk_widget_queue_draw(widget); + gint x, y; + gdk_window_get_pointer(event->window, &x, &y, NULL); + return TRUE; +} + +gboolean dt_gui_curve_editor_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ // set active point + dt_gui_curve_editor_t *c = (dt_gui_curve_editor_t *)user_data; + c->dragging = 1; + return TRUE; +} + +gboolean dt_gui_curve_editor_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + dt_gui_curve_editor_t *c = (dt_gui_curve_editor_t *)user_data; + c->dragging = 0; + return TRUE; +} + +void dt_gui_curve_editor_get_curve(dt_gui_curve_editor_t *c, uint16_t *curve_data, dt_ctl_image_settings_t *settings) +{ + CurveData curve; + curve.m_curveType = TONE_CURVE; + curve.m_numAnchors = 6; + curve.m_gamma = 1.0; + curve.m_min_x = 0.0; + curve.m_max_x = 1.0; + curve.m_min_y = 0.0; + curve.m_max_y = 1.0; + + for(int k=0;k<6;k++) + { + curve.m_anchors[k].x = settings->tonecurve_x[k]; + curve.m_anchors[k].y = settings->tonecurve_y[k]; + } + assert(curve.m_anchors[0].x != curve.m_anchors[1].x); + c->convert.m_Samples = curve_data; + CurveDataSample(&curve, &c->convert); + + /*for(int k=0;k<6;k++) + { + DT_CTL_GET_IMAGE(c->curve.m_anchors[k].x, tonecurve_x[k]); + DT_CTL_GET_IMAGE(c->curve.m_anchors[k].y, tonecurve_y[k]); + } + c->convert.m_Samples = curve; + CurveDataSample(&c->curve, &c->convert);*/ +} + + diff --git a/src/gui/curveeditor.h b/src/gui/curveeditor.h new file mode 100644 index 000000000000..349a8bfa748d --- /dev/null +++ b/src/gui/curveeditor.h @@ -0,0 +1,30 @@ +#ifndef DARKTABLE_CURVE_EDITOR_H +#define DARKTABLE_CURVE_EDITOR_H + +#include +#include + +#include "common/nikon_curve.h" +#include "control/settings.h" + +typedef struct dt_gui_curve_editor_t +{ + double mouse_x, mouse_y; + int selected, dragging; + double selected_offset, selected_y, selected_min, selected_max; + CurveData curve; + CurveSample draw, draw_max, draw_min; + CurveSample convert; +} +dt_gui_curve_editor_t; + +void dt_gui_curve_editor_init(dt_gui_curve_editor_t *c, GtkWidget *widget); +void dt_gui_curve_editor_cleanup(dt_gui_curve_editor_t *c); +gboolean dt_gui_curve_editor_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data); +gboolean dt_gui_curve_editor_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data); +gboolean dt_gui_curve_editor_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data); +gboolean dt_gui_curve_editor_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data); +gboolean dt_gui_curve_editor_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data); +void dt_gui_curve_editor_get_curve(dt_gui_curve_editor_t *c, uint16_t *curve, dt_ctl_image_settings_t *settings); + +#endif diff --git a/src/gui/develop_view.c b/src/gui/develop_view.c new file mode 100644 index 000000000000..bb942baa11b7 --- /dev/null +++ b/src/gui/develop_view.c @@ -0,0 +1,168 @@ +#include "develop/develop.h" +#include "control/jobs.h" +#include "develop/imageop.h" +#include "common/image_cache.h" +#include "control/control.h" + +#include +#include +#include + +void dt_dev_image_expose(dt_develop_t *dev, dt_dev_image_t *image, cairo_t *cr, int32_t width, int32_t height) +{ + // float bordercol = 1.0f; + float *buf = NULL; + // get cached image for this zoom rate (or 1:1 for closeup view) + + int32_t zoom, closeup; + float zoom_x, zoom_y; + DT_CTL_GET_GLOBAL(zoom, dev_zoom); + DT_CTL_GET_GLOBAL(closeup, dev_closeup); + DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); + DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); + buf = dt_dev_get_cached_buf(dev, image, zoom, 'r'); + + dt_print(DT_DEBUG_DEV, "[dev_expose] hashes: %d %d\n", dev->small_raw_hash, dev->history[dev->history_top-1].num); + if(buf && zoom_x == dev->cache_zoom_x[image->cacheline[zoom]] && zoom_y == dev->cache_zoom_y[image->cacheline[zoom]]) + { + int32_t wd = dev->cache_width, ht = dev->cache_height; + // TODO: if(dev->backbuf_hash != dev->history[dev->history_top-1].num) +// #pragma omp parallel for schedule(static) shared(dev,buf) + for(int i=0;ibackbuf[4*i+2-k] = dev->gamma[dev->tonecurve[(int)CLAMP(buf[3*i+k]*0xffff, 0, 0xffff)]]; + int32_t stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, wd); + cairo_surface_t *surface = cairo_image_surface_create_for_data (dev->backbuf, CAIRO_FORMAT_RGB24, wd, ht, stride); + + if(zoom == DT_ZOOM_FIT) + { + cairo_set_source_rgb (cr, .2, .2, .2); + cairo_paint(cr); + const float scale = fminf(wd/(float)dev->image->width, ht/(float)dev->image->height); + cairo_translate(cr, wd/2, ht/2); + cairo_translate(cr, -scale*dev->image->width/2, -scale*dev->image->height/2); + cairo_rectangle(cr, 0, 0, scale*dev->image->width, scale*dev->image->height); + } + else if(zoom == DT_ZOOM_1) + { + // center lo-res images + const int iwd = (closeup ? 2 : 1)*dev->image->width; + const int iht = (closeup ? 2 : 1)*dev->image->height; + if(iwd < wd) cairo_translate(cr, (wd-iwd)/2, 0); + if(iht < ht) cairo_translate(cr, 0, (ht-iht)/2); + if(iwd < wd || iht < ht) + { + cairo_set_source_rgb (cr, .2, .2, .2); + cairo_paint(cr); + } + if(closeup) + { + cairo_scale(cr, 2., 2.); + cairo_translate(cr, -wd/4, -ht/4); + // fix for border near zero, to avoid buffer segfault in load_cache + if((zoom_x+.5f)*dev->image->width <= wd/2) cairo_translate(cr, wd/2 - (zoom_x+.5f)*dev->image->width, 0); + if((zoom_y+.5f)*dev->image->height <= ht/2) cairo_translate(cr, 0, ht/2 - (zoom_y+.5f)*dev->image->height); + } + cairo_rectangle(cr, 0, 0, MIN(iwd,wd), MIN(iht,ht)); + } + else cairo_rectangle(cr, 0, 0, wd, ht); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_FAST); + cairo_fill(cr); + cairo_surface_destroy (surface); + dt_dev_release_cached_buf(dev, image, zoom); + } + else if(dev->small_backbuf_hash == dev->history[dev->history_top-1].num) + { + dt_dev_update_cache(dev, image, zoom); + int32_t wd = dev->small_raw_width, ht = dev->small_raw_height; + float fwd, fht; + dt_image_get_exact_mip_size(dev->image, DT_IMAGE_MIPF, &fwd, &fht); + float zoom_scale; + uint8_t *buf8 = dev->small_backbuf; + switch(zoom) + { + case DT_ZOOM_FIT: + cairo_set_source_rgb (cr, .2, .2, .2); + cairo_paint(cr); + zoom_x = zoom_y = 0.0f; + zoom_scale = fminf(width/fwd, height/fht); + break; + case DT_ZOOM_FILL: + zoom_scale = fmaxf(width/fwd, height/fht); + break; + default: // 1:1 or higher + cairo_set_source_rgb (cr, .2, .2, .2); + cairo_paint(cr); + zoom_scale = dev->image->width/fwd; + break; + } + int32_t stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, dev->small_raw_width); + cairo_surface_t *surface = cairo_image_surface_create_for_data (buf8, CAIRO_FORMAT_RGB24, dev->small_raw_width, dev->small_raw_height, stride); + cairo_translate(cr, width/2.0, height/2.0f); + cairo_translate(cr, (dev->small_raw_width-fwd), (dev->small_raw_height-fht)); + // cairo_scale(cr, zoom_scale*(dev->small_raw_width/fwd), zoom_scale*(dev->small_raw_height/fht)); + cairo_scale(cr, zoom_scale, zoom_scale); + if(zoom == DT_ZOOM_1 && closeup) cairo_scale(cr, 2., 2.); + cairo_translate(cr, -.5f*wd-zoom_x*fwd, -.5f*ht-zoom_y*fht); + // cairo_translate(cr, -1, -1); // compensate jumping draw below pointer + cairo_rectangle(cr, 0, 0, fwd, fht); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_FAST); + cairo_fill(cr); + cairo_surface_destroy (surface); + } + else + { + dt_dev_update_cache(dev, image, zoom); + dt_dev_update_small_cache(dev); + } + DT_CTL_SET_GLOBAL(dev_zoom_y, zoom_y); + DT_CTL_SET_GLOBAL(dev_zoom_x, zoom_x); +} + +void dt_dev_enter() +{ + int selected; + DT_CTL_GET_GLOBAL(selected, lib_image_mouse_over_id); + + DT_CTL_SET_GLOBAL(dev_zoom_x, 0); + DT_CTL_SET_GLOBAL(dev_zoom_y, 0); + + dt_dev_load_image(darktable.develop, dt_image_cache_use(selected, 'r')); + dt_dev_configure(darktable.develop, darktable.control->width - 2*darktable.control->tabborder, darktable.control->height - 2*darktable.control->tabborder); +} + +void dt_dev_leave() +{ + if(darktable.develop->image->pixels) + dt_image_release(darktable.develop->image, DT_IMAGE_FULL, 'r'); + + dt_dev_write_history(darktable.develop); + DT_CTL_SET_GLOBAL_STR(dev_op, "original", 20); + + int wd = darktable.develop->small_raw_width, ht = darktable.develop->small_raw_height; + if(!dt_dev_small_cache_load(darktable.develop)) + { + if(dt_image_alloc(darktable.develop->image, DT_IMAGE_MIP4)) return; + dt_image_check_buffer(darktable.develop->image, DT_IMAGE_MIP4, sizeof(uint8_t)*4*wd*ht); + memcpy(darktable.develop->image->mip[DT_IMAGE_MIP4], darktable.develop->small_backbuf, sizeof(uint8_t)*4*wd*ht); + if(dt_imageio_preview_write(darktable.develop->image, DT_IMAGE_MIP4)) + fprintf(stderr, "[dev_leave] could write mip level %d of image %s to database!\n", DT_IMAGE_MIP4, darktable.develop->image->filename); + dt_image_update_mipmaps(darktable.develop->image); + dt_image_release(darktable.develop->image, DT_IMAGE_MIP4, 'w'); + dt_image_release(darktable.develop->image, DT_IMAGE_MIP4, 'r'); + dt_image_release(darktable.develop->image, DT_IMAGE_MIPF, 'r'); + } + dt_image_cache_release(darktable.develop->image, 'r'); +} + +void dt_dev_expose(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height) +{ + // if(dev->cache_width != width || dev->cache_height != height) return; + + // dt_dev_configure(dev, width, height); + // draw top history item. + if(dev->history_top > 0) + dt_dev_image_expose(dev, dev->history + dev->history_top - 1, cr, width, height); +} + diff --git a/src/gui/gtk.c b/src/gui/gtk.c new file mode 100644 index 000000000000..de4c8037dbf8 --- /dev/null +++ b/src/gui/gtk.c @@ -0,0 +1,375 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/darktable.h" +#include "library/library.h" +#include "develop/develop.h" +#include "develop/imageop.h" +#include "gui/gtk.h" +#include "control/control.h" +#include "control/jobs.h" + +static gboolean +expose (GtkWidget *da, GdkEventExpose *event, gpointer user_data) +{ + dt_control_expose(NULL); + gdk_draw_drawable(da->window, + da->style->fg_gc[GTK_WIDGET_STATE(da)], darktable.gui->pixmap, + // Only copy the area that was exposed. + event->area.x, event->area.y, + event->area.x, event->area.y, + event->area.width, event->area.height); + + // update other widgets + GtkWidget *widget = glade_xml_get_widget (darktable.gui->main_window, "navigation"); + gtk_widget_queue_draw(widget); + + widget = glade_xml_get_widget (darktable.gui->main_window, "histogram"); + gtk_widget_queue_draw(widget); + + widget = glade_xml_get_widget (darktable.gui->main_window, "tonecurve"); + gtk_widget_queue_draw(widget); + + // test quit cond (thread safe, 2nd pass) + if(!darktable.control->running) + { + dt_cleanup(); + gtk_main_quit(); + } + + // reset operations, update expanders + dt_dev_operation_t op; + DT_CTL_GET_GLOBAL_STR(op, dev_op, 20); + darktable.gui->reset = 1; + float gamma, linear, zoom; + DT_CTL_GET_IMAGE(gamma, dev_gamma_gamma); + DT_CTL_GET_IMAGE(linear, dev_gamma_linear); + widget = glade_xml_get_widget (darktable.gui->main_window, "gamma_linear"); + gtk_range_set_value(GTK_RANGE(widget), linear); + widget = glade_xml_get_widget (darktable.gui->main_window, "gamma_gamma"); + gtk_range_set_value(GTK_RANGE(widget), gamma); + + // reset non-fixed pipeline: + dt_iop_gui_reset(); + + DT_CTL_GET_GLOBAL(zoom, lib_zoom); + widget = glade_xml_get_widget (darktable.gui->main_window, "library_zoom"); + gtk_range_set_value(GTK_RANGE(widget), zoom); + darktable.gui->reset = 0; + + return TRUE; +} + +void +export_button_clicked (GtkWidget *widget, gpointer user_data) +{ + // read "export_format" to global settings + GtkWidget *wid = glade_xml_get_widget (darktable.gui->main_window, "export_format"); + gchar *text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(wid)); + if(text != NULL) + { + if (!strcmp(text, "8-bit jpg")) { DT_CTL_SET_GLOBAL(dev_export_format, DT_DEV_EXPORT_JPG); } + else if(!strcmp(text, "8-bit png")) { DT_CTL_SET_GLOBAL(dev_export_format, DT_DEV_EXPORT_PNG); } + else if(!strcmp(text, "16-bit ppm")) { DT_CTL_SET_GLOBAL(dev_export_format, DT_DEV_EXPORT_PPM16); } + else if(!strcmp(text, "float pfm")) { DT_CTL_SET_GLOBAL(dev_export_format, DT_DEV_EXPORT_PFM); } + g_free(text); + } + pthread_mutex_lock(&(darktable.library->film->images_mutex)); + darktable.library->film->last_exported = 0; + pthread_mutex_unlock(&(darktable.library->film->images_mutex)); + for(int k=0;kmain_window, "main_window"); + GtkWidget *filechooser = gtk_file_chooser_dialog_new ("Import file or directory", + GTK_WINDOW (win), + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, //GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + + if (gtk_dialog_run (GTK_DIALOG (filechooser)) == GTK_RESPONSE_ACCEPT) + { + char *filename; + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filechooser)); + dt_film_roll_import(darktable.library->film, filename); + g_free (filename); + } + gtk_widget_destroy (filechooser); + win = glade_xml_get_widget (darktable.gui->main_window, "center"); + gtk_widget_queue_draw(win); +} + +static gboolean +scrolled (GtkWidget *widget, GdkEventScroll *event, gpointer user_data) +{ + int zoom; + DT_CTL_GET_GLOBAL(zoom, lib_zoom); + if(event->direction == GDK_SCROLL_UP) + { + zoom-=2; + if(zoom < 1) zoom = 1; + DT_CTL_SET_GLOBAL(lib_zoom, zoom); + } + else if(event->direction == GDK_SCROLL_DOWN) + { + zoom+=2; + if(zoom > 2*DT_LIBRARY_MAX_ZOOM) zoom = 2*DT_LIBRARY_MAX_ZOOM; + DT_CTL_SET_GLOBAL(lib_zoom, zoom); + } + GtkWidget *range = glade_xml_get_widget (darktable.gui->main_window, "library_zoom"); + gtk_range_set_value(GTK_RANGE(range), zoom); + gtk_widget_queue_draw(widget); + return TRUE; +} + +void quit() +{ + // thread safe quit, 1st pass: + pthread_mutex_lock(&darktable.control->cond_mutex); + darktable.control->running = 0; + pthread_mutex_unlock(&darktable.control->cond_mutex); + GtkWidget *widget = glade_xml_get_widget (darktable.gui->main_window, "center"); + gtk_widget_queue_draw(widget); +} + +static gboolean +configure (GtkWidget *da, GdkEventConfigure *event, gpointer user_data) +{ + static int oldw = 0; + static int oldh = 0; + //make our selves a properly sized pixmap if our window has been resized + if (oldw != event->width || oldh != event->height) + { + //create our new pixmap with the correct size. + GdkPixmap *tmppixmap = gdk_pixmap_new(da->window, event->width, event->height, -1); + //copy the contents of the old pixmap to the new pixmap. This keeps ugly uninitialized + //pixmaps from being painted upon resize + int minw = oldw, minh = oldh; + if(event->width < minw) minw = event->width; + if(event->height < minh) minh = event->height; + gdk_draw_drawable(tmppixmap, da->style->fg_gc[GTK_WIDGET_STATE(da)], darktable.gui->pixmap, 0, 0, 0, 0, minw, minh); + //we're done with our old pixmap, so we can get rid of it and replace it with our properly-sized one. + g_object_unref(darktable.gui->pixmap); + darktable.gui->pixmap = tmppixmap; + } + oldw = event->width; + oldh = event->height; + + dt_control_configure(event->width, event->height); + + return TRUE; +} + +static void +zoom (GtkRange *range, gpointer user_data) +{ + GtkWidget *widget; + int zoom; + zoom = gtk_range_get_value(range); + DT_CTL_SET_GLOBAL(lib_zoom, zoom); + widget = glade_xml_get_widget (darktable.gui->main_window, "center"); + gtk_widget_queue_draw(widget); +} + +static void +gamma (GtkRange *range, gpointer user_data) +{ + if(darktable.gui->reset) return; + if(user_data == (gpointer)0) + { + float linear = gtk_range_get_value(range); + DT_CTL_SET_IMAGE(dev_gamma_linear, linear); + } + else + { + float gamma = gtk_range_get_value(range); + DT_CTL_SET_IMAGE(dev_gamma_gamma, gamma); + } + dt_dev_add_history_item(darktable.develop, "gamma"); +} + + +static void +realize (GtkWidget *widget, + gpointer data) +{ +} + +static gboolean +key_pressed (GtkWidget *w, GdkEventKey *event, gpointer user_data) +{ + return dt_control_key_pressed(event->keyval); +} + +static gboolean +button_pressed (GtkWidget *w, GdkEventButton *event, gpointer user_data) +{ + dt_control_button_pressed(event->x, event->y, event->button, event->type, event->state); + gtk_widget_queue_draw(w); + return TRUE; +} + +static gboolean +button_released (GtkWidget *w, GdkEventButton *event, gpointer user_data) +{ + dt_control_button_released(event->x, event->y, event->button, event->state); + gtk_widget_queue_draw(w); + return TRUE; +} + +static gboolean +mouse_moved (GtkWidget *w, GdkEventMotion *event, gpointer user_data) +{ + dt_control_mouse_moved(event->x, event->y, event->state); + gint x, y; + gdk_window_get_pointer(event->window, &x, &y, NULL); + return TRUE; +} + +gboolean center_leave(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data) +{ + dt_control_mouse_leave(); + return TRUE; +} + + +int +dt_gui_gtk_init(dt_gui_gtk_t *gui, int argc, char *argv[]) +{ + GtkWidget *widget; + + if (!g_thread_supported ()) g_thread_init(NULL); + gdk_threads_init(); + gdk_threads_enter(); + gtk_init (&argc, &argv); + + gtk_rc_parse ("darktable.gtkrc"); + + /* load the interface */ + darktable.gui->main_window = glade_xml_new ("darktable.glade", NULL, NULL); + + /* connect the signals in the interface */ + + widget = glade_xml_get_widget (darktable.gui->main_window, "button_import"); + g_signal_connect (G_OBJECT (widget), "clicked", + G_CALLBACK (import_button_clicked), + NULL); + + /* Have the delete event (window close) end the program */ + widget = glade_xml_get_widget (darktable.gui->main_window, "main_window"); + g_signal_connect (G_OBJECT (widget), "delete_event", + G_CALLBACK (quit), NULL); + g_signal_connect (G_OBJECT (widget), "key-press-event", + G_CALLBACK (key_pressed), NULL); + + gtk_widget_show_all(widget); + widget = glade_xml_get_widget (darktable.gui->main_window, "center"); + + g_signal_connect_after (G_OBJECT (widget), "realize", + G_CALLBACK (realize), NULL); + g_signal_connect (G_OBJECT (widget), "configure-event", + G_CALLBACK (configure), NULL); + g_signal_connect (G_OBJECT (widget), "expose-event", + G_CALLBACK (expose), NULL); + g_signal_connect (G_OBJECT (widget), "motion-notify-event", + G_CALLBACK (mouse_moved), NULL); + g_signal_connect (G_OBJECT (widget), "leave-notify-event", + G_CALLBACK (center_leave), NULL); + g_signal_connect (G_OBJECT (widget), "button-press-event", + G_CALLBACK (button_pressed), NULL); + g_signal_connect (G_OBJECT (widget), "button-release-event", + G_CALLBACK (button_released), NULL); + g_signal_connect (G_OBJECT (widget), "scroll-event", + G_CALLBACK (scrolled), NULL); + // TODO: left, right, top, bottom: + //leave-notify-event + + // lib zoom + widget = glade_xml_get_widget (darktable.gui->main_window, "library_zoom"); + g_signal_connect (G_OBJECT (widget), "value-changed", + G_CALLBACK (zoom), NULL); + + // gamma correction expander + widget = glade_xml_get_widget (darktable.gui->main_window, "gamma_linear"); + g_signal_connect (G_OBJECT (widget), "value-changed", + G_CALLBACK (gamma), (gpointer)0); + + widget = glade_xml_get_widget (darktable.gui->main_window, "gamma_gamma"); + g_signal_connect (G_OBJECT (widget), "value-changed", + G_CALLBACK (gamma), (gpointer)1); + + // tone curve + widget = glade_xml_get_widget (darktable.gui->main_window, "tonecurve"); + dt_gui_curve_editor_init(&gui->tonecurve, widget); + + widget = glade_xml_get_widget (darktable.gui->main_window, "navigation"); + dt_gui_navigation_init(&gui->navigation, widget); + + widget = glade_xml_get_widget (darktable.gui->main_window, "histogram"); + dt_gui_histogram_init(&gui->histogram, widget); + + for(long int k=0;k<10;k++) + { + char wdname[20]; + snprintf(wdname, 20, "history_%02ld", k); + widget = glade_xml_get_widget (darktable.gui->main_window, wdname); + g_signal_connect (G_OBJECT (widget), "clicked", + G_CALLBACK (history_button_clicked), + (gpointer)k); + } + + widget = glade_xml_get_widget (darktable.gui->main_window, "export_button"); + g_signal_connect (G_OBJECT (widget), "clicked", + G_CALLBACK (export_button_clicked), + (gpointer)0); + + widget = glade_xml_get_widget (darktable.gui->main_window, "center"); + GTK_WIDGET_UNSET_FLAGS (widget, GTK_DOUBLE_BUFFERED); + // GTK_WIDGET_SET_FLAGS (widget, GTK_DOUBLE_BUFFERED); + GTK_WIDGET_SET_FLAGS (widget, GTK_APP_PAINTABLE); + + darktable.gui->reset = 0; + dt_iop_gui_init(); + return 0; +} + +void dt_gui_gtk_cleanup(dt_gui_gtk_t *gui) +{ + dt_gui_curve_editor_cleanup(&gui->tonecurve); + dt_gui_navigation_cleanup(&gui->navigation); + dt_gui_histogram_cleanup(&gui->histogram); +} + +void dt_gui_gtk_run(dt_gui_gtk_t *gui) +{ + GtkWidget *widget = glade_xml_get_widget (darktable.gui->main_window, "center"); + darktable.gui->pixmap = gdk_pixmap_new(widget->window, widget->allocation.width, widget->allocation.height, -1); + /* start the event loop */ + gtk_main (); + gdk_threads_leave(); +} + diff --git a/src/gui/gtk.h b/src/gui/gtk.h new file mode 100644 index 000000000000..e6213963e050 --- /dev/null +++ b/src/gui/gtk.h @@ -0,0 +1,26 @@ +#ifndef DT_GUI_GTK_H +#define DT_GUI_GTK_H + +#include +#include +#include "gui/curveeditor.h" +#include "gui/navigation.h" +#include "gui/histogram.h" + + +typedef struct dt_gui_gtk_t +{ + GladeXML *main_window; + GdkPixmap *pixmap; + dt_gui_curve_editor_t tonecurve; + dt_gui_navigation_t navigation; + dt_gui_histogram_t histogram; + int32_t reset; +} +dt_gui_gtk_t; + +int dt_gui_gtk_init(dt_gui_gtk_t *gui, int argc, char *argv[]); +void dt_gui_gtk_run(dt_gui_gtk_t *gui); +void dt_gui_gtk_cleanup(dt_gui_gtk_t *gui); + +#endif diff --git a/src/gui/histogram.c b/src/gui/histogram.c new file mode 100644 index 000000000000..8a7edb42389e --- /dev/null +++ b/src/gui/histogram.c @@ -0,0 +1,125 @@ +#include "gui/histogram.h" +#include "develop/develop.h" +#include "develop/imageop.h" + +#define DT_HIST_INSET 5 + +void dt_gui_histogram_init(dt_gui_histogram_t *n, GtkWidget *widget) +{ + n->dragging = 0; + g_signal_connect (G_OBJECT (widget), "expose-event", + G_CALLBACK (dt_gui_histogram_expose), n); + g_signal_connect (G_OBJECT (widget), "button-press-event", + G_CALLBACK (dt_gui_histogram_button_press), n); + g_signal_connect (G_OBJECT (widget), "button-release-event", + G_CALLBACK (dt_gui_histogram_button_release), n); + g_signal_connect (G_OBJECT (widget), "motion-notify-event", + G_CALLBACK (dt_gui_histogram_motion_notify), n); + g_signal_connect (G_OBJECT (widget), "leave-notify-event", + G_CALLBACK (dt_gui_histogram_leave_notify), n); +} + +void dt_gui_histogram_cleanup(dt_gui_histogram_t *n) {} + +gboolean dt_gui_histogram_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) +{ + const int inset = DT_HIST_INSET; + int width = widget->allocation.width, height = widget->allocation.height; + cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo_t *cr = cairo_create(cst); + cairo_set_source_rgb(cr, 0.2, 0.2, 0.2); + cairo_paint(cr); + + cairo_translate(cr, inset, inset); + width -= 2*inset; height -= 2*inset; + +#if 0 + // draw shadow around + float alpha = 1.0f; + for(int k=0;khistogram; + hist_max = dev->histogram_max; + if(hist_max > 0) + { + // cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); + cairo_translate(cr, 0, height); + cairo_scale(cr, 256.0/width, -height/(float)hist_max); + cairo_set_line_width(cr, 1.); + cairo_set_source_rgba(cr, 1., 0., 0., 0.5); + dt_gui_histogram_draw_8(cr, hist, 0); + cairo_set_source_rgba(cr, 0., 1., 0., 0.5); + dt_gui_histogram_draw_8(cr, hist, 1); + cairo_set_source_rgba(cr, 0., 0., 1., 0.5); + dt_gui_histogram_draw_8(cr, hist, 2); + // cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT); + } + + cairo_destroy(cr); + cairo_t *cr_pixmap = gdk_cairo_create(gtk_widget_get_window(widget)); + cairo_set_source_surface (cr_pixmap, cst, 0, 0); + cairo_paint(cr_pixmap); + cairo_destroy(cr_pixmap); + cairo_surface_destroy(cst); + return TRUE; +} + +gboolean dt_gui_histogram_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +{ + return TRUE; +} +gboolean dt_gui_histogram_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + return TRUE; +} +gboolean dt_gui_histogram_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + return TRUE; +} +gboolean dt_gui_histogram_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data) +{ + return TRUE; +} + +void dt_gui_histogram_draw_8(cairo_t *cr, uint32_t *hist, int32_t channel) +{ + cairo_move_to(cr, 0, 0); + for(int k=0;k<256;k++) + { + // cairo_move_to(cr, k, 0); + cairo_line_to(cr, k, hist[4*k+channel]); + //cairo_stroke(cr); + } + cairo_line_to(cr, 255, 0); + cairo_close_path(cr); + cairo_fill(cr); +} diff --git a/src/gui/histogram.h b/src/gui/histogram.h new file mode 100644 index 000000000000..06b6cd1c8259 --- /dev/null +++ b/src/gui/histogram.h @@ -0,0 +1,22 @@ +#ifndef DARKTABLE_GUI_HISTOGRAM_H +#define DARKTABLE_GUI_HISTOGRAM_H + +#include +#include + +typedef struct dt_gui_histogram_t +{ + int32_t dragging; +} +dt_gui_histogram_t; + +void dt_gui_histogram_init(dt_gui_histogram_t *n, GtkWidget *widget); +void dt_gui_histogram_cleanup(dt_gui_histogram_t *n); +gboolean dt_gui_histogram_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data); +gboolean dt_gui_histogram_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data); +gboolean dt_gui_histogram_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data); +gboolean dt_gui_histogram_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data); +gboolean dt_gui_histogram_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data); +void dt_gui_histogram_draw_8(cairo_t *cr, uint32_t *hist, int32_t channel); + +#endif diff --git a/src/gui/navigation.c b/src/gui/navigation.c new file mode 100644 index 000000000000..fd4bf186bdaa --- /dev/null +++ b/src/gui/navigation.c @@ -0,0 +1,170 @@ + +#include "gui/gtk.h" +#include "gui/navigation.h" +#include "library/library.h" +#include "develop/develop.h" +#include "control/control.h" + +#include +#define DT_NAVIGATION_INSET 5 + + +void dt_gui_navigation_init(dt_gui_navigation_t *n, GtkWidget *widget) +{ + n->dragging = 0; + g_signal_connect (G_OBJECT (widget), "expose-event", + G_CALLBACK (dt_gui_navigation_expose), n); + g_signal_connect (G_OBJECT (widget), "button-press-event", + G_CALLBACK (dt_gui_navigation_button_press), n); + g_signal_connect (G_OBJECT (widget), "button-release-event", + G_CALLBACK (dt_gui_navigation_button_release), n); + g_signal_connect (G_OBJECT (widget), "motion-notify-event", + G_CALLBACK (dt_gui_navigation_motion_notify), n); + g_signal_connect (G_OBJECT (widget), "leave-notify-event", + G_CALLBACK (dt_gui_navigation_leave_notify), n); +} + +void dt_gui_navigation_cleanup(dt_gui_navigation_t *n) {} + +gboolean dt_gui_navigation_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) +{ + const int inset = DT_NAVIGATION_INSET; + int width = widget->allocation.width, height = widget->allocation.height; + cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo_t *cr = cairo_create(cst); + + cairo_set_source_rgb(cr, 0.2, 0.2, 0.2); + cairo_paint(cr); + + width -= 2*inset; height -= 2*inset; + cairo_translate(cr, inset, inset); + + dt_image_t *image = darktable.develop->image; + dt_image_buffer_t mip = DT_IMAGE_NONE; + int32_t iiwd, iiht; + float iwd, iht; + if(image) + { + mip = dt_image_get_matching_mip_size(image, width, height, &iiwd, &iiht); + mip = dt_image_get(image, mip, 'r'); + dt_image_get_exact_mip_size(image, mip, &iwd, &iht); + dt_image_get_mip_size(image, mip, &iiwd, &iiht); + } + if(mip != DT_IMAGE_NONE) + { + const float scale = fminf(width/iwd, height/iht); + int32_t stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, iiwd); + // cairo_save(cr); + cairo_surface_t *surface = cairo_image_surface_create_for_data (image->mip[mip], CAIRO_FORMAT_RGB24, iiwd, iiht, stride); + cairo_translate(cr, width/2.0, height/2.0f); + cairo_scale(cr, scale, scale); + cairo_translate(cr, -.5f*iwd, -.5f*iht); + + // draw shadow around + float alpha = 1.0f; + for(int k=0;k<4;k++) + { + cairo_rectangle(cr, -k/scale, -k/scale, iwd + 2*k/scale, iht + 2*k/scale); + cairo_set_source_rgba(cr, 0, 0, 0, alpha); + alpha *= 0.6f; + cairo_fill(cr); + } + + cairo_rectangle(cr, 0, 0, iwd, iht); + // cairo_stroke_preserve(cr); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_fill(cr); + cairo_surface_destroy (surface); + // cairo_restore(cr); + // draw box where we are + dt_dev_zoom_t zoom; + int closeup; + float zoom_x, zoom_y; + DT_CTL_GET_GLOBAL(zoom, dev_zoom); + DT_CTL_GET_GLOBAL(closeup, dev_closeup); + DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); + DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); + if(zoom != DT_ZOOM_FIT) + { + float boxw = 1, boxh = 1; + dt_dev_check_zoom_bounds(darktable.develop, &zoom_x, &zoom_y, zoom, closeup, &boxw, &boxh); + + DT_CTL_SET_GLOBAL(dev_zoom_x, zoom_x); + DT_CTL_SET_GLOBAL(dev_zoom_y, zoom_y); + + cairo_translate(cr, iwd*(.5f+zoom_x), iht*(.5f+zoom_y)); + cairo_set_source_rgb(cr, 0., 0., 0.); + cairo_set_line_width(cr, 1.f); + boxw *= iwd; + boxh *= iht; + cairo_rectangle(cr, -boxw/2-1, -boxh/2-1, boxw+2, boxh+2); + cairo_stroke(cr); + cairo_set_source_rgb(cr, 1., 1., 1.); + cairo_rectangle(cr, -boxw/2, -boxh/2, boxw, boxh); + cairo_stroke(cr); + } + dt_image_release(image, mip, 'r'); + } + + cairo_destroy(cr); + cairo_t *cr_pixmap = gdk_cairo_create(gtk_widget_get_window(widget)); + cairo_set_source_surface (cr_pixmap, cst, 0, 0); + cairo_paint(cr_pixmap); + cairo_destroy(cr_pixmap); + cairo_surface_destroy(cst); + return TRUE; +} + +void dt_gui_navigation_set_position(dt_gui_navigation_t *n, double x, double y, int wd, int ht) +{ + dt_dev_zoom_t zoom; + int closeup; + float zoom_x, zoom_y; + DT_CTL_GET_GLOBAL(zoom, dev_zoom); + DT_CTL_GET_GLOBAL(closeup, dev_closeup); + DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); + DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); + + if(n->dragging && zoom != DT_ZOOM_FIT) + { + const int inset = DT_NAVIGATION_INSET; + const float width = wd - 2*inset, height = ht - 2*inset; + const float iwd = darktable.develop->image->width, iht = darktable.develop->image->height; + zoom_x = fmaxf(-.5, fminf(((x-inset)/width - .5f), .5))/(iwd*fminf(wd/iwd, ht/iht)/wd); + zoom_y = fmaxf(-.5, fminf(((y-inset)/height - .5f), .5))/(iht*fminf(wd/iwd, ht/iht)/ht); + dt_dev_check_zoom_bounds(darktable.develop, &zoom_x, &zoom_y, zoom, closeup, NULL, NULL); + DT_CTL_SET_GLOBAL(dev_zoom_x, zoom_x); + DT_CTL_SET_GLOBAL(dev_zoom_y, zoom_y); + + dt_control_gui_queue_draw(); + } +} + +gboolean dt_gui_navigation_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +{ + dt_gui_navigation_t *n = (dt_gui_navigation_t *)user_data; + dt_gui_navigation_set_position(n, event->x, event->y, widget->allocation.width, widget->allocation.height); + gint x, y; // notify gtk for motion_hint. + gdk_window_get_pointer(event->window, &x, &y, NULL); + return TRUE; +} +gboolean dt_gui_navigation_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + dt_gui_navigation_t *n = (dt_gui_navigation_t *)user_data; + n->dragging = 1; + dt_gui_navigation_set_position(n, event->x, event->y, widget->allocation.width, widget->allocation.height); + return TRUE; +} +gboolean dt_gui_navigation_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + dt_gui_navigation_t *n = (dt_gui_navigation_t *)user_data; + n->dragging = 0; + return TRUE; +} +gboolean dt_gui_navigation_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data) +{ + return TRUE; +} +void dt_gui_navigation_get_pos(dt_gui_navigation_t *n, float *x, float *y) +{ +} diff --git a/src/gui/navigation.h b/src/gui/navigation.h new file mode 100644 index 000000000000..0f136b47a8d3 --- /dev/null +++ b/src/gui/navigation.h @@ -0,0 +1,22 @@ +#ifndef DARKTABLE_NAVIGATION_H +#define DARKTABLE_NAVIGATION_H + +#include +#include + +typedef struct dt_gui_navigation_t +{ + int32_t dragging; +} +dt_gui_navigation_t; + +void dt_gui_navigation_init(dt_gui_navigation_t *n, GtkWidget *widget); +void dt_gui_navigation_cleanup(dt_gui_navigation_t *n); +gboolean dt_gui_navigation_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data); +gboolean dt_gui_navigation_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data); +gboolean dt_gui_navigation_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data); +gboolean dt_gui_navigation_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data); +gboolean dt_gui_navigation_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data); +void dt_gui_navigation_get_pos(dt_gui_navigation_t *n, float *x, float *y); + +#endif diff --git a/src/library/film.c b/src/library/film.c new file mode 100644 index 000000000000..86eea6a01bf1 --- /dev/null +++ b/src/library/film.c @@ -0,0 +1,122 @@ +#include "library/library.h" +#include "common/imageio.h" +#include "control/control.h" +#include "control/jobs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void dt_film_roll_init(dt_film_roll_t *film) +{ + pthread_mutex_init(&film->images_mutex, NULL); + film->last_loaded = film->num_images = film->last_exported = 0; + film->dirname[0] = '\0'; + film->dir = NULL; +} + +void dt_film_import1(dt_film_roll_t *film) +{ + const gchar *d_name; + char filename[1024]; + dt_image_t image; + + while(1) + { + pthread_mutex_lock(&film->images_mutex); + if (film->dir && (d_name = g_dir_read_name(film->dir)) && darktable.control->running) + { + snprintf(filename, 1024, "%s/%s", film->dirname, d_name); + image.film_id = film->id; + film->last_loaded++; + } + else + { + if(film->dir) + { + g_dir_close(film->dir); + film->dir = NULL; + } + darktable.control->progress = 200.0f; + pthread_mutex_unlock(&film->images_mutex); + return; + } + pthread_mutex_unlock(&film->images_mutex); + + if(!dt_image_import(film->id, filename)) + { + pthread_mutex_lock(&film->images_mutex); + darktable.control->progress = 100.0f*film->last_loaded/(float)film->num_images; + pthread_mutex_unlock(&film->images_mutex); + dt_control_queue_draw(); + } // else not an image. + } +} + +void dt_film_roll_cleanup(dt_film_roll_t *film) +{ + pthread_mutex_destroy(&film->images_mutex); +} + +int dt_film_roll_open(dt_film_roll_t *film, const int32_t id) +{ + // TODO: + // select id from images where film_id = id + // and prefetch to cache using image_open + // TODO: update last used film date stamp + DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, -1); + return 1; +} +int dt_film_roll_import(dt_film_roll_t *film, const char *dirname) +{ + film->id = -1; + int rc; + sqlite3_stmt *stmt; + rc = sqlite3_prepare_v2(darktable.db, "select id from film_rolls where folder = ?1", -1, &stmt, NULL); + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_bind_text(stmt, 1, dirname, strlen(dirname), SQLITE_STATIC); + HANDLE_SQLITE_ERR(rc); + if(sqlite3_step(stmt) == SQLITE_ROW) film->id = sqlite3_column_int(stmt, 0); + rc = sqlite3_finalize(stmt); + if(film->id <= 0) + { + // TODO: insert timestamp + // gsize gret = g_date_strftime(gchar *s, gsize slen, "%Y:%m:%d %H:%M:%S", const GDate *date); + + rc = sqlite3_prepare_v2(darktable.db, "insert into film_rolls (id, folder) values (null, ?1)", -1, &stmt, NULL); + HANDLE_SQLITE_ERR(rc); + rc = sqlite3_bind_text(stmt, 1, dirname, strlen(dirname), SQLITE_STATIC); + HANDLE_SQLITE_ERR(rc); + pthread_mutex_lock(&(darktable.db_insert)); + rc = sqlite3_step(stmt); + if(rc != SQLITE_DONE) fprintf(stderr, "failed to insert film roll! %s\n", sqlite3_errmsg(darktable.db)); + rc = sqlite3_finalize(stmt); + film->id = sqlite3_last_insert_rowid(darktable.db); + pthread_mutex_unlock(&(darktable.db_insert)); + } + if(film->id <= 0) return 1; + + DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, -1); + + film->last_loaded = 0; + strncpy(film->dirname, dirname, 512); + film->dir = g_dir_open(film->dirname, 0, NULL); + darktable.control->progress = .001f; + for(int k=0;k +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +void dt_library_init(dt_library_t *lib) +{ + lib->film = (dt_film_roll_t *)malloc(sizeof(dt_film_roll_t)); + lib->select_offset_x = lib->select_offset_y = 0.5f; + lib->last_selected_id = -1; + lib->button = 0; + lib->modifiers = 0; + dt_film_roll_init(lib->film); +} + +void dt_library_cleanup(dt_library_t *lib) +{ + dt_film_roll_cleanup(lib->film); + free(lib->film); +} + +void dt_library_toggle_selection(int iid) +{ + int rc; + sqlite3_stmt *stmt; + rc = sqlite3_prepare_v2(darktable.db, "select * from selected_images where imgid = ?1", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, iid); + if(sqlite3_step(stmt) == SQLITE_ROW) + { + sqlite3_finalize(stmt); + rc = sqlite3_prepare_v2(darktable.db, "delete from selected_images where imgid = ?1", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, iid); + sqlite3_step(stmt); + sqlite3_finalize(stmt); + } + else + { + sqlite3_finalize(stmt); + rc = sqlite3_prepare_v2(darktable.db, "insert into selected_images values (?1)", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, iid); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + } +} + +void dt_library_expose(dt_library_t *lib, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery) +{ + float zoom, zoom_x, zoom_y; + int32_t mouse_over_id, pan, track, center; + DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); + DT_CTL_GET_GLOBAL(zoom, lib_zoom); + DT_CTL_GET_GLOBAL(zoom_x, lib_zoom_x); + DT_CTL_GET_GLOBAL(zoom_y, lib_zoom_y); + DT_CTL_GET_GLOBAL(pan, lib_pan); + DT_CTL_GET_GLOBAL(track, lib_track); + DT_CTL_GET_GLOBAL(center, lib_center); + + cairo_set_source_rgb (cr, .2, .2, .2); + cairo_paint(cr); + + const float wd = width/zoom; + const float ht = width/zoom; + + static int oldpan = 0; + static float oldzoom = -1; + if(oldzoom < 0) oldzoom = zoom; + + // TODO: exaggerate mouse gestures to pan when zoom == 1 + if(pan)// && mouse_over_id >= 0) + { + zoom_x = lib->select_offset_x - /* (zoom == 1 ? 2. : 1.)*/pointerx; + zoom_y = lib->select_offset_y - /* (zoom == 1 ? 2. : 1.)*/pointery; + } + + if(oldzoom != zoom) + { + float oldx = (pointerx + zoom_x)*oldzoom/width; + float oldy = (pointery + zoom_y)*oldzoom/width; + if(zoom == 1) + { + zoom_x = (int)oldx*wd; + zoom_y = (int)oldy*ht; + } + else + { + zoom_x = oldx*wd - pointerx; + zoom_y = oldy*ht - pointery; + } + } + oldzoom = zoom; + + // TODO: track on aoe, keys! + if(center) + { + if(mouse_over_id >= 0) + { + zoom_x -= width*.5f - pointerx; + zoom_y -= height*.5f - pointery; + } + else zoom_x = zoom_y = 0.0; + center = 0; + } + + // mouse left the area + if(!pan) DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, -1); + + int offset_i = (int)(zoom_x/wd); + int offset_j = (int)(zoom_y/ht); + float offset_x = zoom == 1 ? 0.0 : zoom_x/wd - (int)(zoom_x/wd); + float offset_y = zoom == 1 ? 0.0 : zoom_y/ht - (int)(zoom_y/ht); + const int max_rows = zoom == 1 ? 1 : 2 + (int)((height)/ht + .5); + const int max_cols = zoom == 1 ? 1 : MIN(DT_LIBRARY_MAX_ZOOM - MAX(0, offset_i), 1 + (int)(zoom+.5)); + + int offset = MAX(0, offset_i) + DT_LIBRARY_MAX_ZOOM*offset_j; + int seli = (int)((pointerx + zoom_x)/wd) - MAX(offset_i, 0); + int selj = (int)((pointery + zoom_y)/ht) - offset_j; + + sqlite3_stmt *stmt, *stmt2; + int rc, rc2, id, clicked1, last_seli = 1<<30, last_selj = 1<<30; + clicked1 = (oldpan == 0 && pan == 1 && lib->button == 1); + if(clicked1 && + (lib->modifiers & GDK_SHIFT_MASK) == 0 && (lib->modifiers & GDK_CONTROL_MASK) == 0) + { // clear selected if no modifier + rc2 = sqlite3_prepare_v2(darktable.db, "delete from selected_images", -1, &stmt2, NULL); + rc2 = sqlite3_step(stmt2); + sqlite3_finalize(stmt2); + } + // TODO: order by and where clauses from sort widget! + rc = sqlite3_prepare_v2(darktable.db, "select * from (select id from images where film_id = ?1 order by filename) as dreggn limit ?2, ?3", -1, &stmt, NULL); + cairo_translate(cr, -offset_x*wd, -offset_y*ht); + cairo_translate(cr, -MIN(offset_i*wd, 0.0), 0.0); + for(int row = 0; row < max_rows; row++) + { + if(offset < 0) + { + cairo_translate(cr, 0, ht); + offset += DT_LIBRARY_MAX_ZOOM; + continue; + } + rc = sqlite3_bind_int (stmt, 1, lib->film->id); + rc = sqlite3_bind_int (stmt, 2, offset); + rc = sqlite3_bind_int (stmt, 3, max_cols); + for(int col = 0; col < max_cols; col++) + { + if(sqlite3_step(stmt) == SQLITE_ROW) + { + id = sqlite3_column_int(stmt, 0); + dt_image_t *image = dt_image_cache_get(id, 'r'); + if(image) + { + // set mouse over id + if((zoom == 1 && mouse_over_id < 0) || (!pan && seli == col && selj == row)) + { + mouse_over_id = image->id; + DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, mouse_over_id); + } + // add clicked image to selected table + if(clicked1) + { + // FIXME: whatever comes first assumtion is broken! + // if((lib->modifiers & GDK_SHIFT_MASK) && (last_seli == (1<<30)) && + // (image->id == lib->last_selected_id || image->id == mouse_over_id)) { last_seli = col; last_selj = row; } + // if(last_seli < (1<<30) && ((lib->modifiers & GDK_SHIFT_MASK) && (col >= MIN(last_seli,seli) && row >= MIN(last_selj,selj) && + // col <= MAX(last_seli,seli) && row <= MAX(last_selj,selj)) && (col != last_seli || row != last_selj)) || + if((lib->modifiers & GDK_SHIFT_MASK) && image->id == lib->last_selected_id) { last_seli = col; last_selj = row; } + if(last_seli < (1<<30) && ((lib->modifiers & GDK_SHIFT_MASK) && (col >= last_seli && row >= last_selj && + col <= seli && row <= selj) && (col != last_seli || row != last_selj)) || + (seli == col && selj == row)) + { // insert all in range if shift, or only the one the mouse is over for ctrl or plain click. + dt_library_toggle_selection(image->id); + lib->last_selected_id = image->id; + } + } + cairo_save(cr); + dt_image_expose(image, image->id, cr, wd, zoom == 1 ? height : ht, zoom); + cairo_restore(cr); + dt_image_cache_release(image, 'r'); + } + } + cairo_translate(cr, wd, 0.0f); + } + cairo_translate(cr, -max_cols*wd, ht); + offset += DT_LIBRARY_MAX_ZOOM; + rc = sqlite3_reset(stmt); + rc = sqlite3_clear_bindings(stmt); + } + sqlite3_finalize(stmt); + + oldpan = pan; + DT_CTL_SET_GLOBAL(lib_zoom_x, zoom_x); + DT_CTL_SET_GLOBAL(lib_zoom_y, zoom_y); + DT_CTL_SET_GLOBAL(lib_track, track); + DT_CTL_SET_GLOBAL(lib_center, center); +} + +void dt_library_button_released(dt_library_t *lib, double x, double y, int which, uint32_t state) +{ + DT_CTL_SET_GLOBAL(lib_pan, 0); +} + +void dt_library_button_pressed(dt_library_t *lib, double xx, double yy, int which, uint32_t state) +{ + lib->modifiers = state; + lib->button = which; + DT_CTL_GET_GLOBAL(lib->select_offset_x, lib_zoom_x); + DT_CTL_GET_GLOBAL(lib->select_offset_y, lib_zoom_y); + lib->select_offset_x += xx; + lib->select_offset_y += yy; + DT_CTL_SET_GLOBAL(lib_pan, 1); +} + +void dt_library_mouse_leave(dt_library_t *lib) +{ + if(!darktable.control->global_settings.lib_pan && darktable.control->global_settings.lib_zoom != 1) + { + DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, -1); + dt_control_queue_draw(); // remove focus + } +} + +void dt_library_mouse_moved(dt_library_t *lib, double xx, double yy, int which) +{ + dt_control_queue_draw(); +} + +void dt_image_expose(dt_image_t *img, int32_t index, cairo_t *cr, int32_t width, int32_t height, int32_t zoom) +{ + float bgcol = 0.4, fontcol = 0.5, bordercol = 0.1; + int selected = 0, imgsel; + DT_CTL_GET_GLOBAL(imgsel, lib_image_mouse_over_id); + // if(img->flags & DT_IMAGE_SELECTED) selected = 1; + sqlite3_stmt *stmt; + int rc; + rc = sqlite3_prepare_v2(darktable.db, "select * from selected_images where imgid = ?1", -1, &stmt, NULL); + rc = sqlite3_bind_int (stmt, 1, img->id); + if(sqlite3_step(stmt) == SQLITE_ROW) selected = 1; + sqlite3_finalize(stmt); + if(selected == 1) + { + bgcol = 0.6; fontcol = 0.5; + } + if(imgsel == img->id) { bgcol = 0.8; fontcol = 0.7; } // selected = 1; + float imgwd = 0.8f; + if(zoom == 1) + { + // TODO: draw exif info etc + imgwd = .97f; + // cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); + } + else + { + char num[10]; + cairo_rectangle(cr, 0.01f*width, 0.01f*height, 0.99f*width, 0.99f*height); + cairo_set_source_rgb(cr, bgcol, bgcol, bgcol); + cairo_fill(cr); + // cairo_rectangle(cr, 0, 0, width, height); + // cairo_set_source_rgb(cr, bgcol, bgcol, bgcol); + // cairo_fill_preserve(cr); + // cairo_set_source_rgb(cr, fontcol, fontcol, fontcol); + // cairo_set_line_width(cr, 1); + // cairo_stroke(cr); + + cairo_set_source_rgb(cr, fontcol, fontcol, fontcol); + cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, .3*width); + + cairo_move_to (cr, .0*width, .24*height); + snprintf(num, 10, "%d", index); + cairo_show_text (cr, num); + } + +#if 1 + int32_t iwd = width*imgwd, iht = height*imgwd, stride; + float scale = 1.0; + dt_image_buffer_t mip; + mip = dt_image_get_matching_mip_size(img, imgwd*width, imgwd*height, &iwd, &iht); + mip = dt_image_get(img, mip, 'r'); + dt_image_get_mip_size(img, mip, &iwd, &iht); + float fwd, fht; + dt_image_get_exact_mip_size(img, mip, &fwd, &fht); + cairo_surface_t *surface = NULL; + if(mip != DT_IMAGE_NONE) + { + stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, iwd); + surface = cairo_image_surface_create_for_data (img->mip[mip], CAIRO_FORMAT_RGB24, iwd, iht, stride); + scale = fminf(width*imgwd/fwd, height*imgwd/fht); + } + + // draw centered and fitted: + cairo_save(cr); + cairo_translate(cr, width/2.0, height/2.0f); + cairo_scale(cr, scale, scale); + cairo_translate(cr, -.5f*fwd, -.5f*fht); + + const float border = zoom == 1 ? 16/scale : 2/scale; + + if(mip != DT_IMAGE_NONE) + { + cairo_set_source_surface (cr, surface, 0, 0); + cairo_rectangle(cr, 0, 0, fwd, fht); + cairo_fill(cr); + cairo_surface_destroy (surface); + dt_image_release(img, mip, 'r'); + } + + if(zoom == 1) cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BEST); + cairo_rectangle(cr, 0, 0, fwd, fht); + cairo_set_source_rgb(cr, bordercol, bordercol, bordercol); + if(selected) + { + cairo_set_line_width(cr, 1./scale); + if(zoom == 1) + { // draw shadow around border + cairo_set_source_rgb(cr, 0.2, 0.2, 0.2); + cairo_stroke(cr); + // cairo_new_path(cr); + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + float alpha = 1.0f; + for(int k=0;k<16;k++) + { + cairo_rectangle(cr, 0, 0, fwd, fht); + cairo_new_sub_path(cr); + cairo_rectangle(cr, -k/scale, -k/scale, fwd+2.*k/scale, fht+2.*k/scale); + cairo_set_source_rgba(cr, 0, 0, 0, alpha); + alpha *= 0.6f; + cairo_fill(cr); + } + } + else + { + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + cairo_new_sub_path(cr); + cairo_rectangle(cr, -border, -border, fwd+2.*border, fht+2.*border); + cairo_stroke_preserve(cr); + cairo_set_source_rgb(cr, 1.0-bordercol, 1.0-bordercol, 1.0-bordercol); + cairo_fill(cr); + } + } + else + { + cairo_set_line_width(cr, 1); + cairo_stroke(cr); + } + cairo_restore(cr); + + if(selected && (zoom == 1)) + { + cairo_set_source_rgb(cr, fontcol, fontcol, fontcol); + cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, .02*width); + + cairo_move_to (cr, .0*width, .024*height); + cairo_show_text(cr, img->filename); + // cairo_text_path(cr, image->filename); + // cairo_fill_preserve(cr); + // cairo_set_source_rgb(cr, 0, 0, 0); + // cairo_stroke(cr); + } + + // if(zoom == 1) cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT); +#endif +} + diff --git a/src/library/library.h b/src/library/library.h new file mode 100644 index 000000000000..b8155e5a9daa --- /dev/null +++ b/src/library/library.h @@ -0,0 +1,63 @@ + +#ifndef DARKTABLE_LIBRARY_H +#define DARKTABLE_LIBRARY_H + +#include "common/darktable.h" +#include "common/image.h" +#include +#include +#include + +#define DT_LIBRARY_MAX_ZOOM 13 + +void dt_image_expose(dt_image_t *image, int32_t i, cairo_t *cr, int32_t width, int32_t height, int32_t zoom); + +/** + * film roll. + * this is one directory of images on disk. + * also manages the preview image cache. + */ +typedef struct dt_film_roll_t +{ + int32_t id; + char dirname[512]; + pthread_mutex_t images_mutex; + GDir *dir; + int32_t num_images, last_loaded, last_exported; +} +dt_film_roll_t; + +struct dt_library_t; +void dt_film_roll_init(dt_film_roll_t *film); +void dt_film_roll_cleanup(dt_film_roll_t *film); +int dt_film_roll_open(dt_film_roll_t *film, const int32_t id); +int dt_film_roll_import(dt_film_roll_t *film, const char *dirname); + +void dt_film_import1(dt_film_roll_t *film); + +/** + * this organises the whole library: + * previously imported film rolls.. + */ +typedef struct dt_library_t +{ + dt_film_roll_t *film; + // tmp mouse vars: + float select_offset_x, select_offset_y; + int32_t last_selected_id; + int button; + uint32_t modifiers; +} +dt_library_t; + +void dt_library_init(dt_library_t *lib); +void dt_library_cleanup(dt_library_t *lib); +// event handling: +void dt_library_expose(dt_library_t *lib, cairo_t *cr, int width, int height, int32_t pointerx, int32_t pointery); +void dt_library_button_pressed(dt_library_t *lib, double x, double y, int which, uint32_t state); +void dt_library_button_released(dt_library_t *lib, double x, double y, int which, uint32_t state); +void dt_library_mouse_moved(dt_library_t *lib, double x, double y, int which); +void dt_library_mouse_leave(dt_library_t *lib); + + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 000000000000..2801583b5183 --- /dev/null +++ b/src/main.c @@ -0,0 +1,11 @@ +#include "common/darktable.h" +#include "gui/gtk.h" +#include + +int main (int argc, char *argv[]) +{ + if(dt_init(argc, argv)) exit(1); + dt_gui_gtk_run(darktable.gui); + exit(0); +} + diff --git a/src/shared.am b/src/shared.am new file mode 100644 index 000000000000..b3cee9c5cfdf --- /dev/null +++ b/src/shared.am @@ -0,0 +1 @@ +AM_CFLAGS=$(MAGICK_CPPFLAGS) $(CAIRO_CFLAGS) $(GLADE_CFLAGS) $(GTK_CFLAGS) $(GTHREAD_CFLAGS) $(SQLITE_CFLAGS) -Wall -std=c99 -g -march=native -mfpmath=sse -ffast-math -msse2 -I$(srcdir)/LibRaw -fopenmp -fPIC -fno-strict-aliasing