commit 5494f610e2055314475af3f3014ea0d1ab71be03 Author: John Cupitt Date: Wed Aug 29 16:23:50 2007 +0000 split to trunk/branches diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..7b9b342f --- /dev/null +++ b/AUTHORS @@ -0,0 +1,19 @@ +Authors of VIPS + +See also the files THANKS and ChangeLog + +Nicos Dessipris and Kirk Martinez started VIPS in 1990. + +John Cupitt started ip in late 1990, and took over maintenance of the VIPS +library in 1995. + +Ruven Pillay, Steve Perry, Lars Raffelt, David Saunders, Jean-Philippe +Laurant, Ahmed Abood, Helene Chahine, Joe Padfield, Andrey Kiselev, Lev +Serebryakov, Simon Goodall, Konrad Lang, Markus Wollgarten, Jesper Friis +and others contributed patches for the library and ip. + +Hans Breuer contributed many win32 compatibility fixes and a win32 build +system. Dennis Lubert cleaned up the C++ API. + +Jose Manuel Menendez Garcia, Javier Alejandre Arenas, and Juan Torres Arjona +contributed the tmake VIPS.DLL build system and the MSVC project files. diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..b1e3f5a2 --- /dev/null +++ b/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey 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 library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..e382f49e --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1511 @@ +1/8/07 started 7.12.5 +- im_embed() is more general ... x and y can be negative +- predicate.c is smaller and cleaner +- libsrcCC link improvement from Pablo +- support 32/64-bit ImageMagick as well (thanks Marcel) +- better im_magick2vips() for Q8 ImageMagick + +27/7/06 started 7.12.4 +- proto.h had vars called small, breaking on win32 +- more python fixing, we now have working matricies too + +17/7/06 started 7.12.3 +- fix to VImage.i for gcc 4.2 (thanks Damir) +- eek, off by 1 for more than 1 band hists +- needed a slightly larger worst-case buffer in im__b64_encode() +- tiny cleanup for make_hI() prevents cond jump on ui in valgrind +- --disable-threads was broken again +- remove .svn dirs from dist +- now passes distcheck again + +17/7/06 started 7.12.2 +- added im_bandmean() +- added support for TIFFTAG_PREDICTOR (Andrey Kiselev) +- fix TIFFOpen() mode snafu (Andrey Kiselev) + +11/5/06 started 7.12.1 +- memory.c abort()s with DEBUG +- oops, im_bits_of_fmt() manpage was not being installed +- im_histcum() can do all image types +- updated NEWS +- added im_csv2vips_header() +- command-line csv read was broken (thanks Tom) +- removed length limit on argument vectors (Tom) +- added IM_PREFIX, configure-time install prefix +- oop, turned off memory.c DEBUG +- fix some bogus gcc 4.1 warnings with im_open_local_array() +- better vips usage message +- oops, IM_ANY missing from im_demand_hint() manpage (thanks Shahid) +- just warn if plugins fail to load in im_init_world() +- expose Vargv and make refblock public rather than private so that + subclasses of VImage can add vips-style member operations (thanks Pablo) +- oops, im_initdesc() needed to have bbits set correctly (thanks Shahid) +- make VError derive from std::exception more officially +- woo, got exceptions working in SWIG +- soname version bumped to 12.x.x +- oops, added vector ops to Python + +26/3/07 started 7.11.21 +- ooo, added %include "std_except.i" & friends to VError.i, VImage.i +- im_init_world() is more lenient about recursive invocation +- im_gbandjoin() falls back to im_copy() for 1 input image +- race condition fixed in im_render.c (thanks Simon) +- bump for 7.12!!! + +26/1/07 started 7.11.20 +- another fix to im_region_image() (thanks Mikkel) +- tiny speed up to im_rect_includesrect() +- avoid recursive invocation in im_init_world() (thanks Christian) +- fix to extract_prefix (thanks Christian) +- buffer cache is now per thread +- combine buffer unref and ref in a single operation to reduce malloc/free + cycles +- new internal API for passing regions between threads means we can remove + buffer locks +- more buffer/region sanity checks, plus a memory barrier +- lock around error buffer changes +- im_vips2mask() was wrong for nx1 m-band images +- liboil back to "test" +- add buffer_cache_list to avoid GHashTable _insert()s +- oop, --vips-concurrency was broken +- renamed (in dispatch layer) im_and/or/eor_const -> im_and/or/eorimageconst + for consistency +- C++ API wraps IMAGEVEC, DOUBLEVEC, INTVEC arguments +- oop, IMAGE % vector was broken + +21/12/06 started 7.11.19 +- added im_isscalar() (Tom) +- added IM_REGION_ADDR_TOPLEFT() (Tom) +- reduce size of im_rightshift_size.c to help compile (Tom) +- added im_linreg() (Tom) +- various C++ API polishes, plus a bugfix (Dennis Lubert) +- vips.spec split to devel and python too (Dennis Lubert) +- be more explicit about sizeof(magic) +- init magic to native order by default (thanks Dennis) +- Hist becomes im_history_get() +- new history mechanism is faster, uses much less memory, and removes + duplicate lines +- added im_get_option_group() +- added official im_concurrency_set()/_get()() +- don't read bbits from vips files ... set ourselves from bandfmt +- oops add RGB16 and GREY16 to C++ header +- --list packages option to vips.c +- updated docs to 7.12 +- oops, im_region_image() snafu was causing a lot of recomputation +- make im_mpercent() suck a little less +- EXIF save was a bit bOrked ... cause of mac crashes? +- im_histgr(), im_heq(), im_hist() all number bands from zero now +- fix stride in liboil calls +- set RGB16 on 16-bit RGB ICC export + +29/11/06 started 7.11.18 +- added im_buffer_t so regions can share calculated pixels: 2-3x speedup on + the benchmark +- im_region_local() -> im_region_buffer() +- im_sharpen() order change to help sharing +- im_invalidate() clears buffer caches +- add sentinel attributes +- add some missing im_demand_hint()s +- paint ops invalidate the output image +- fix nothread eval +- raise threads limit to 1024 (thanks Christian) +- manual redone +- vipsCC python init() hooks +- add liboil dependency +- use liboil for im_abs(), im_add(), im_divide(), im_floor(), im_multiply(), + im_subtract(), im_lintra(), im_avg(),im_deviate() +- quiet libtoolize test (thanks Tom) +- im_benchmarkn now regrows image each time +- strip meta from sample2.v ... saves a lot of mem (esp. Hist) + +24/11/06 started 7.11.17 +- better benchmark script makes graphing easier +- double-buffer image file writes +- reuse write threads +- clean up threadgroup / iterate / generate +- added im_benchmarkn to make it easier to make a CPU-bound op on large + machines +- im_cache() failed for cpus > 1 + +1/11/06 started 7.11.16 +- moved im__convert_saveable() into im_copy() (thanks Christian) +- missing gobject dependency (thanks Christian) +- --enable-threads was broken (thanks Christian) +- eval without theads was broken (thanks Christian) +- LIBADD libvips.la to libvipsCC.la (thanks Simon) +- benchmark.sh is now plain sh, not bash +- set ORIENTATION_TOPLEFT in im_vips2tiff (thanks Josef) +- oops, im_vips2csv() output separator was broken +- added im_benchmark2 +- move XYZ2Lab LUT build outside the eval thread + +30/10/06 started 7.11.15 +- print leaked windows +- oops, race condition in im_window_unref() +- integrated im_region_window() into im_region_image(), tiny speed up + +6/10/06 started 7.11.14 +- ifthenelse and affine dhints revised +- buildlut no longer outputs x cods +- configure asks for glib >= 2.6 +- configure uses AC_TOOL_CHECK to find tool names to help cross-compiling. +- better configure test for libexif +- add C++ include ... include in a namespace +- added im_benchmark / SMP benchmark script + +8/9/06 started 7.11.11 +- add im_norm_dmask() +- removed old code for gradient and lindetect +- internal decls split from proto.h to help SWIG +- test for python and SWIG during configure +- added python dir for the binding +- python binding done! +- oops, --without-python was broken (thanks Tom) +- added python/test + +23/6/06 started 7.11.10 +- still more im_affine() rounding/clipping tweaks +- ignore "--" arguments to vips.c +- im_init_world() also sets g_*_prgname() and loads plugins +- add manpage for im_init_world() (oops) +- error_exit() prints prgname +- various cygwin fixes +- fix cache thread assert failure (thanks Joe) +- "header" now uses GOption, slightly different args, will loop over args +- fixed assert() overenthusiasm in im_prepare() +- im_csv2vips() now has separate whitespace / separator tables + +23/6/06 started 7.11.9 +- back on sourceforge CVS again +- require openexr 1.2.2 or greater +- range check xy on im_insert*() for sanity +- VMask::invertlut decl removed (thanks Jean) +- added \"all\" option to vips.c + +17/5/06 started 7.11.8 +- debrokened openexr read +- added im_tile_cache() +- added tiled read to im_exr2vips() +- im_tiff2vips() now uses im_tile_cache() rather than its own cache ... + faster in some cases, less RAM use in some cases, saves 200+ lines +- removed 'broken' read option from im_tiff2vips() +- read/write doubles with g_ascii_strtod() and friends where appropriate +- add a "thread" member to region to help sanity check region ownership +- saner threadgroup fixes a race problem on gcc 4.0.3 / amd64 +- added im_vips2csv() +- im_open() now does CSV read/write too +- oops, broke vips main prog for function name in argv1 case + +22/4/06 started 7.11.7 +- split vips_png.c to im_vips2png.c and im_png2vips.c +- added OpenEXR dependency +- added im_exr2vips(), im_exr2vips_header() +- added im_isexr(), im_open() knows about OpenEXR +- added im_contrast_surface(), im_contrast_surface_raw() (Tom) +- added im_msb(), im_msb_band() (Tom) +- im_scale() sets Type on output +- added RGB16, GREY16 types +- im_*2vips() set these types if appropriate +- configure fixes for mac +- vips main prog uses GOption +- im_icc_* locks around calls to cmsDoTransform() to avoid corruption on SMP + machines + +10/3/06 started 7.11.6 +- typo in manpage for im_rect_dup() (thanks Tom) +- don't abort image load if XML read fails +- added im_video_test() ... test video source +- oops, lcms .pc finder was not working +- clipping problem in im_affine() fixed (thanks Clare) +- test for attr support in libmagick +- im_text() returns an error for empty string +- im_falsecolour() scale reversed +- im_remosaic() could crash on bad mosaics +- configure changes to fix --without-magick, lcms and fftw (but sadly we now + require .pc files for these libs) +- im_vips2jpeg() automatically converts to 1 or 3 band sRGB uchar for write +- also im_vips2png() +- added im_project() + +20/2/06 started 7.11.5 +- added im_csv2vips() +- commas in filename options can be escaped with a '\' +- raise tile buffer limit (thanks Ruven) +- im_spcor() and im_fastcor() have prettier borders +- im_fastcor() returns sum of squares of differences, not sum of abs of + differences + +18/11/05 started 7.11.4 +- small win32 fixes, thanks Juan +- added im_flood_blob_copy() ... a temporary hack +- much faster im_histplot() +- read RGBA palette-ized PNG images more robustly (thanks Tom) +- turn on -ms-bitfields automatically for mingw + +26/9/05 started 7.11.3 +- better error recovery for im_binfile() file too large +- all raw files now use mmap windows, so (eg.) ppm and analyze reads can go + >2GB +- remove DISABLE_COMPAT ... you now have to define IM_ENABLE_DEPRECATED to get + broken #defines +- fix to build without exif, thanks Chas +- use native win32 API for seek()/truncate() to work with large files +- use attribute to check printf-style args with gcc +- fix gcc4 warnings +- removed ebuild, since it's in gentoo now +- im_magick2vips() sets meta from attributes (good for dicom) +- im_magick2vips() writes many-frame images as tall thin VIPS images +- im_histcum() was broken for vertical histograms +- im_histnorm() is neater +- simpler and faster inner loop for im_conv() and im_convf() avoids gcc4 bug +- appendc() was reading past the end of the buffer on MSB machines + +13/6/05 started 7.11.2 +- im_copy_set() was messed up in 7.11.1 +- put into CVS, phew +- fixed a rounding bug in im_affine() ... should no longer get black edges on + image resize +- if TIFF open fails in im_open(), try going through libmagick +- merge requires all bands == 0 for transparency (used to just check 1st + band) +- 16 bit LAB TIFF read/write was wrong +- new GType for refstring makes it visible from im_header_map() +- jpeg loader attaches exif data (and human-readable meta fields) +- jpeg saver writes any exif data +- meta not wiped by im_*incheck() in a "w" image +- meta keeps traverse order +- now require glib >= 2.4 +- require libxml-2.0 for meta save and new history mechanism +- no more .desc files, history saved in XML after pixel data +- i/s/d meta fields saved there too +- added base64 encode/decode +- added blob header write +- added a save string type: types which define transforms to and from the + save format get serialized +- GValue meta API now exposed, since we can serialise anything +- jpeg loader loads ICC profiles +- jpeg saver saves ICC profiles from the VIPS header +- src/header.c knows about meta system +- added im_analyze2vips(), im_grid(), im_raw2vips() +- extract/grid/replicate/zoom were not setting TRANSFORM flag +- better falsecolour LUT +- less stupid + more portable read/write of VIPS header +- better im_updatehist() +- jpeg load sets vips xres/yres from exif, if possible +- jpeg save sets exif xres/yres from vips, if possible +- icc_export and icc_transform attach profiles and intents to output images +- added im_icc_import_embedded() to import with an embedded profile +- split vips_jpeg.c into two, it was getting too big +- added im_cp_descv(), im_cp_desc_array(), funcs use them +- removed im_append_Hist() from API +- fixed meta copy bug +- better history copy, removed nonsense about 1st line of Hist being special +- tiff read/write now reads/writes ICC profile from meta +- edvips rewritten to remove stupidness, and can now set xml +- header can now print xml extension block +- IM_ prefix for colour temp names + +1/6/05 started 7.11 +- added im_copy_morph() +- im_region_region() allows Bands and BandFmt to differ, provided + sizeof( pel ) is the same ... makes im_copy_morph() work +- added im_meta*() functions (MW) +- im_header_*() rewritten for meta +- added im_header_exists(), im_header_map() +- use pkg-config to find libpng and ImageMagick +- added im_lineset() +- added im_extract_areabands() (JF) +- added im_copy_from() (JF) + +15/4/05 started 7.10.12 +- im_ifthenelse just evals left/right for region all zero/all one +- also im_blend +- swap g_setenv() back to plain setenv() so we work with glib 2.2 + +9/4/05 JC started 7.10.11 +- docs no longer have broken links +- fixed memleak in im_text() + +8/4/05 +- one bit tiff read was sometimes reading a byte too far in each scanline + +14/1/05 started 7.10.9 +- im_filename_split() will now usually work for filenames containing ':' + characters +- added im_render_fade() for fancier nip2 image repaint +- added "ccittfax4" as a TIFF compression option +- fix all 64-bit compiler warnings +- "," allowed as column separator in mask read +- better at spotting singular matricies +- small im_render() tidies +- glib dependancy reduced to just 2.0, but untested ... helps people building + on older systems who aren't interested in nip2 +- removing leading spaces from IMAGEVEC arguments +- load non-interlaced PNGs more efficiently +- 1 point mosaic functions work on more image types +- better memory allocation debugging info +- local memory on regions can shrink as well as grow +- shut down threadgroups on render if no dirty tiles +- limit number of simultaneous renders +- higher mmap window threshold +- allow max == -1 for unlimited render cache +- 'priority' marks non-suspendable renders +- im_embed() mode == 4 paints white pels +- im_tiff2vips() was broken with --disable-threads +- oops, im_errormsg() compat macros were GCC only +- larger default tile size and strip height +- tiff write sets PHOTOMETRIC_CIELAB for vips TYPE_LAB images ... so we can + write float LAB as well as float RGB (thanks Ruven) +- also 16 bit LAB TIFF write +- im_render() rewritten + +20/11/04 started 7.10.8 +- im_sharpen() is ~15% faster +- more quoting for MAGICK finder +- im_XYZ2Lab() uses a LUT rather than cbrt(), 5x faster +- --disable-threads removes gthread dependency completely (thanks Simon) +- intercept TIFF warnings as well as errors ... stops occasional libMagick + exceptions +- add im_init_world() to im_init() as well to help backwards compat (thanks + Simon) +- im_icc_present() function description was broken, thanks Jay +- oops, libtool library versioning was wrong, thanks Jay +- can now make TIFF pyramids of any non-complex image type (was uchar and LAB + only), thanks Ruven +- 1st order mosaic code now works for LABQ too +- build system changes to make "make distcheck" work +- RPM .spec files fixed up and updated by configure (thanks Simon) +- tiny cleanups for vdump +- use g_setenv()/g_getenv() +- tiny improvements to IM_FREE*() +- tiny VImage debug print fixes (thanks Jay) +- swap off_t for gint64 to fix LARGEFILE support on win32 +- computation feedback now uses gint64 for number of pels, so we give feedback + correctly on images with >2**31 pels +- other small fixes for >2**31 pels in an image + +10/11/04 started 7.10.7 +- im_histnD() was not checking BandFmt (thanks Kirk) +- improvements to threading system speed up non-vips output in some cases +- use cbrt(x) where we can ... 10x faster than pow(x,1.0/3) on win32 +- typeo in im_text() when built without PANGOFT2 (thanks Stefan) + +1/11/04 styarted 7.10.6 +- tiny doc fixes +- scripts now only depend on 'vips' program +- im_open( "r" ) is now lazier ... for non-VIPS formats, we delay read until + the first ->generate() +- so im_open_header() now deprecated since im_open("r") is identical +- now looks for fftw3 as well as fftw2 ... slightly faster ffts + +19/10/04 started 7.10.5 +- fix to light_correct (thanks Jay) +- edvips knows about xoffset/yoffset +- better vips enum<->char conversions + +4/10/04 started 7.10.4 +- man page fixes (thanks Jay) +- removed last csh scripts (thanks Jay) +- scripts default VIPSHOME to $prefix (thanks Jay) +- doc build system tidies +- im_rank() edge padding was wrong +- im_vips2tiff() can now embed ICC profiles + +22/9/04 started 7.10.3 +- mildly better im_vips2tiff() +- *, -, +, /, % between two images now work for mixed number of bands +- im_free() was missing a man page +- revised documentation + +1/9/04 started 7.10.2 +- C++ .pc files were still set for 7.9, grr +- im_insertplace() didn't check compatibility of images (thanks Matt) + +27/7/04 started 7.10.1 +- set default stack size explicitly to help platforms with a very low default +- 16 bit RGB tiff read was broken (bug introduced in 7.9.5, thanks Haida) +- !pangoft2 was broken, thanks Kenneth +- win32 build fixes + +12/7/04 renamed as 7.10.0 +- added NOCACHE function flag ... stops nip memoising video & paint ops +- added im_extract_bands() ... takes out many bands from an image +- im_vips2tiff() scanline write speed up for area pipelines + +10/6/04 started 7.9.6 +- tiny polishing of im_ppm2vips() +- im_blend() can now work on labq +- boolean ops all work on float/complex images (by casting to int) +- im_maplut() was broken for 1 band image + many band lut + >8 bit output +- im_lintra_vec() now handles 1 band image and many band vector to make many + band image +- oops, im_lintra_vec() was missing a man page +- im_measure() can work on labq +- im_lhisteq() uses new embed mode, _raw() version is one pixel smaller, sets + Xoffset/Yoffset for new origin scheme +- generalised im_tone_build() to any image type to make im_tone_build_range() + +20/5/04 started 7.9.5 +- tiff output res can be a single number too +- added im_text() to make a bitmap from a string with pango +- im_tiff2vips() does 16 bit RGBA +- im_binfile() was broken since 7.9.2 due to im_mapfile() change +- im_ppm2vips() now works for 16 bit images +- added im_copy_swap() ... copies, reversing byte order +- im_resize_linear() was broken for some images, thanks Javi + +8/3/04 started 7.9.4 +- oops, config.h include missing in a few places +- im_vips2tiff() can now write 1 bit images +- im__find_lr/tboverlap() now exported to nip +- better edge tile handling for tiff read/write (thanks Ruven) +- added extend-pixels-from-edge mode to im_embed() +- im_conv*(), im_rank(), im_stdif(), im_dilate(), im_erode() all use it to + expand their input, so their output now has guess borders, not black borders +- im_fastcor() now does an im_embed( 1 ) on the output ... the zero borders + were very annoying before, since you would usually be searching for the + minimum point +- no change to im_spcor(), since you will usually be searching for the maximum +- better im_render() cache behaviour under heavy loads +- im_affine() revised + * clip, resample and transform is now pixel-perfect for all + inputs (I hope) + * uses the new embed to make sure there are no black borders + from edge interpolation + * about 20 - 30% faster +- policy change: Xoffset and Yoffset are now set by all operations to record + the position of the input origin in the output +- im_replicate() is much faster for some cases +- added tile and mirror flags to im_embed() +- added im_cache() convenience function +- better ETA for image calculation +- im_tiff2vips() now has a "broken" option so it can read tiled tiffs made + with earlier versions of vips +- on convert float to int format, now does floor() not rint() ... more + 'mathematical' +- added im_rint() +- im_sharpen() now uses a gaussian mask +- im_convsep() more resistant to int overflow problems +- added im_make_xy(), avoids rounding problems with the old float-based thing +- im_profile() now makes vertical images for a vertical profile +- added im_vips2tiff() option to set the resolution in inches not cm (thanks + Andrey) +- im_binfile() is now exported + +6/2/04 started 7.9.3 +- added an im_init_world() to im_open(), to help old progs +- renamed VSemaphore as im_semaphore_t +- started using libtool library versioning +- now uses g_module_*() in place of dlopen() +- now uses pkg-config instead of vips-config (thanks Simon) +- fixes to vips.h for _ADDR() with DEBUG on (thanks Konrad) + +10/12/03 started 7.9.2 +- patches for freebsd, thanks Lev Serebryakov +- vips2dj knows about my colour laser printer +- added i18n support, glib/gmodule/gthread dependency +- im_error*() API revised to be more i18n friendly +- List type removed, now uses g_slist +- VBuf added, some more utility funcs pushed down from nip +- im_thread stuff removed, now uses g_thread +- im_lock stuff removed, now uses g_mutex +- im_semaphore_t renamed to VSemaphore, not sure this is a good idea +- build with --disable-threads to turn off threaded render +- #include now pulls in most of the public API, you shouldn't + need other vips includes very often +- im_close() is better at cleaning up if there's an error +- inverse FFTs could fail for wider-than-high images with fftw +- better im_icc_transform error messages +- bug fix in im_render with large caches +- im_binfile() now has an offset parameter +- im_mapfile()/im_unmapfile() now work on IMAGE and record the length of the + file they mapped ... this lets VIPS successfully unmap a file if it changes + size while it's open + +20/10/03 started 7.9.1 +- threadgroups now have their own kill flag +- im_plotmask() now does anti-aliasing +- im_iterate() fix for operations on mmap window images (thanks Clare) +- im_writeline() stops on kill +- fix for im_fwfft() segv for wider-than-high real images (thanks Andrey) +- fix for im_fwfft() to work for non-square real images (thanks Andrey) +- can now read and write 32-bit IEEE float TIFF (Andrey Kiselev) +- clean-ups for colour.c (Andrey Kiselev) +- no longer lets you make an image with width|height|bands == 0 (thanks Joe) +- im_vips2tiff(), im_vips2*jpeg*(), im_vips2png(), im_vips2ppm() could + sometimes fail for mmap window input images (thanks David) +- added IM_RECT_HCENTRE(), IM_RECT_VCENTRE() macros + +20/8/03 JC +- started 7.9.0 +- added im_rank_image() ... im_maxvalue() a special case of this +- im_subtract() goes up to int earlier for better value preserving, thanks + Haida +- im_rank() much faster for large windows, correct result on all platforms + (dratted memcpy() was causing problems before) +- fixed problem with libMagick config if installed somewhere strange +- fixed problem with include order in library compile +- added --without-magick configure option +- added im_render(), threaded background image paint +- added im_replicate(), replicate an image horizontally and vertically + +31/5/03 JC +- started 7.8.11 +- fixed a problem with relational operators and some combinations of input + types (bug introduced in 7.8.9), thanks Haida +- vips-7.8 script overrides VIPSHOME environment variable +- better im_guess_prefix +- stupid light_correct script no longer uses /pics/tmp +- added batch_crop script + +22/5/03 +- started 7.8.10 +- the JPEG writer can embed ICC profiles in output images ... although I've + yet to see it make any difference :-( test this carefully at some point +- fixed a possible coredumper in jpeg write +- jpeg read now spots truncated files +- im_invertlut() now makes an image, not a mask ... sorry :-( +- im_histnD() makes an n-dimensional histogram from an n-band image +- im_col_pythagoras() patch +- IM_NUMBER() now returns int not size_t +- new win32 build system from Juan and friends, based on tmake +- sample project files for MSVC added, thanks Juan +- win32/ subdir now has the win32 build systems +- spec/ subdir now has the spec files for building RPMs +- dist now includes formatted documentation +- license change: VIPS is now LGPL, nip stays GPL ... this means proprietary + programs can link against the vips library +- had a report of a working VIPS build on a 64 bit system (!) +- im_log_dmask() now includes all of the negative lobe, thanks matt +- vips-7.8 start script now auto-relocates +- im_spcor_raw(), im_fastcor_raw() now exported + +29/4/03 +- started 7.8.9 +- changes to build to help MSVC +- oops, makedef.pl missed out function names with an initial cap, and + error_exit() +- im_min() and im_max() gave random wrong results for >1 thread on >1 CPU + machines (bug introduced in 7.7.20), thanks Joe +- vips.c no longer generates C++ wrappers for functions with no image argument + (thanks Haida) +- im_invertlut() now wrapped by hand in VMask.cc +- C++ docs updated +- added im_open_header(), returns an IMAGE with just width/height/etc and no + data +- ... so now "header" will print useful stuff even on truncated files +- tiff writer knows about alpha (thanks Jenny) + +7/2/03 +- started 7.8.8 +- build failed with lcms turned off +- im_spcor() could segv for 16bit images (thanks Joe) +- im_tiff2vips() read resolution expressed as pixels/cm incorrectly +- im_vips2tiff() tries not to write mad resolutions +- header and im_open file type tests reordered for slight speedup +- im_copy_set() had a broken dispatch function for xres/yres +- im_fwfft() exploits libfftw real -> complex transform if possible for a 2x + speed-up (thanks Matt) +- im_invfftr() added for complex -> real inverse transform for 2x speed-up + (thanks Matt) +- im_freqflt() now uses im_invfftr() for real result and speedup +- im_flipver() could segv on some inputs, thanks Clare +- relational operators now work on complex +- relational rewritten ... now fractionally slower, but 1/3 the size +- vips2dj -1:1 produced incorrect height +- better overlap-too-small detection in mosaicing code +- im_system() can have NULL output +- global balance ignores overlaps with only transparent pixels + +3/1/03 +- started 7.8.7 +- worked in patch from Hans Breuer (thanks!) + - png read/write with im_png2vips(), im_png2vips_header(), + im_vips2png(), im_ispng() + - im_errorstring() and im_col_displays() are now functions not externs + (helps DLLs) + - many include fixes to help native win32 build +- added libMagick support, 78 file formats now loadable with + im_magick2vips(), im_magick2vips_header() and im_ismagick(), w00t +- now installs vips.m4 to $prefix/share/aclocal +- added im_icc_export_depth() ... export to device space with a specified bit + depth (8 or 16) +- vips.def now rebuilt with custom rule in libsrc/Makefile.am +- removed externs im_Type, im_BandFmt, im_Coding, im_Compression to simplify + DLL build +- im_mmap() -> im__mmap(), since it's supposed to be an internal function +- new vips-7.8.x/proj directory holds unsupported sample makefiles and + config.h for building with the MSC toolchain +- new scripts batch_image_convert and batch_rubber_sheet (thanks Joe) +- added the RPM .spec files to the main distribution +- InitializeMagic() now passed "" rather than NULL to avoid assert() problems + on some libMagic versions + +2/12/02 +- started 7.8.6 +- now reads 8-bit RGBA tiff +- C++ build guide fixes (thanks fsicre) +- im_Type2char array text slightly messed up +- global_balance is safer for complex mixed mosaics +- removed im_lintra() fallback to im_copy() for scale == 1, offset == 0 ... + too confusing +- im_tiff2vips() now reads 16-bit LAB +- added im_Lab2LabS() and im_LabS2Lab() + +5/11/02 +- started 7.8.5 +- fix for mmap window of local region ... caused im_iterate() to break + sometimes for large images, in turn occasionally breaking + im_max()/im_min()/etc. (thanks Joe) +- tiny speed up for im_rot90()/270() +- on install on win32, add .exe suffix for links +- vips.c knows to remove .exe suffix for linked commands +- added im_errormsg_system() ... decode win32 error codes too +- pagesize calcs for roving mmap windows were messed up on win32 (thanks Kirk) +- some TODO cleanups +- global balance broke horribly if you had filenames with spaces in (thanks + Clare) + +31/10/02 +- started 7.8.4 +- im_unmapfile() includes mixed up on mac os x +- libtool patched for mac os x +- vips.c sets numeric locale to "C" + +27/10/02 +- started 7.8.3 +- configure fixes help mac os x +- im_guess_prefix() adds ".exe" suffix on w32 if not there +- changed im_measure() error messages to number bands from 1 +- added func descriptor for im_read_dmask() to help nip, updated C++ API, docs + +21/10/02 JC +- started 7.8.2 +- tries rand() if random() is not available +- tries mktemp() if mkstemp() is not available +- turns off realpath() if not available +- added IM_DIR_SEP/IM_DIR_SEP_STR directory separator character/string +- added IM_PATH_SEP/IM_PATH_SEP_STR path separator character/string +- added im_path_is_absolute() +- vips.c knows to link to vips.exe on win32 +- spot mingw* and set BINARY_OPEN +- open images in binary too (since we now read() the header) + +10/10/02 JC +- im_lintra() and im_lintra_vec() were broken for complex images :-( thanks + matt +- renamed im_and() as im_andimage(), im_eor() as im_eorimage() and im_or() as + im_orimage() ... avoids breakage in the C++ layer +- added im_dE00_fromLab() +- limited release as vips-7.8.0 + +2/10/02 +- renamed as vips-7.8, woohoo +- revised documentation + +19/9/02 JC +- started sorting out VIPS #defines ... there are now a sensible set of new + names (eg. NOCODING becomes IM_CODING_NONE, LAB becomes IM_TYPE_LAB) +- define IM_NO_VIPS7_COMPAT to turn off the old names +- added im_mmap()/im_munmap() layer for windows portability +- removed the contents of history.h .. obsolete +- added IM_IMAGE_ADDR() macro + +10/9/02 JC +- handle errors from TIFF lib correctly +- configure fixes for cygwin +- CMYK TIFF write fixed +- configure fixes for mingw + +5/9/02 JC +- im_cp_desc() now copies Xoffset/Yoffset + +21/8/02 JC +- started 7.7.24 +- reads CMYK TIFF +- reads dpi from TIFFs +- better float Xres/Yres + +14/8/02 JC +- new header fields Xoffset and Yoffset ... used by functions to hint + the position of the origin in output images +- support added to c++ api and to header +- im__lrmerge(), im__tbmerge(), im__affine(), im_insert(), + set Xoffset/Yoffset +- now uses , not for better suse w0rkage +- better configure for fftw (uses libdfftw name if libfftw not found) + +8/8/02 JC +- large file support with mmap() windows ... had to change + im_prepare_inplace() to im_prepare_to() + + benchmark: + + - system + + hardware: 2 x 2.5GHz P4, 1GB RAM, 15k SCSI, ReiserFS + os: suse 8 (kernel 2.4.18) + compiler: gcc 2.95.3, -O2, threads turned on + images: fred.v, fred2.v; both 4k by 4k LABPACK (64MB) + images: jim.v, jim2.v; both 15k by 15k LABPACK (900MB) + time: smallest real of 5 runs, system idle + vips: 7.7.23, debug on in im_openin.c, window limit set with an + environment variable + + - benchmarks + + cpu-bound: im_sharpen fred.v fred3.v 11 1.5 20 50 1 2 + io-bound: im_insert fred.v fred2.v fred3.v 4000 0 + worst-case: im_rot90 fred.v fred3.v + + - results + + desktop: + + no mmap windows mmap windows + + cpu-bound real 0m3.712s real 0m3.970s + user 0m6.010s user 0m6.390s + sys 0m0.900s sys 0m1.110s + + io-bound real 0m1.813s real 0m1.865s + user 0m0.900s user 0m0.990s + sys 0m1.720s sys 0m1.520s + + worst-case real 0m1.344s real 0m3.039s + user 0m1.270s user 0m2.230s + sys 0m0.850s sys 0m3.050s + + not quite sure why sharpen is a little slower (4%?) ... IO speed is about + the same though ... worst-case is having to constantly move windows about + (500,000 page faults, vs 10,000 for no windows) + + again, with an image larger than RAM + + no mmap windows mmap windows + + io-bound real 2m52.759s real 2m11.172s + user 0m14.940s user 0m14.890s + sys 0m29.940s sys 0m26.560s + + worst-case real 3m35.391s real 3m50.760s + user 0m19.850s user 0m26.600s + sys 0m12.650s sys 0m43.130s + + mmap windows actually slightly faster in this case ... plus they stress the + OS less + +31/7/02 JC +- added -lm for better lcms detect +- README notes for fftw on suse8 +- im_profile() sets HISTOGRAM for output image +- im_copy()/im_copy_set() function descriptor no longer sets PTOP ... helps + avoid LUT problems +- im_subsample()/im_zoom() fall back to im_copy() for shrink/grow == 1 +- im_lintra() falls back to im_copy() for scale == 1, offset == 0 +- no longer use Type == LUT ... all just Type == HISTOGRAM now +- im_blend() was messed up for > 1 band images :( + +16/7/02 JC +- started 7.7.23 +- im_XYZ2sRGB() wasn't setting Type = sRGB +- im_icc_import() was broken for rgb +- im_header_string() had wrong return type in function database + +13/7/02 JC +- added im_flood_blob() +- added im_open_local_array() ... C API convenience function +- oop, im_flood() was missing a man page +- Type == FOURIER added to help visualisation +- released as 7.7.22 + +30/6/02 JC +- JPEG, TIFF and PPM import all now set sRGB Type for RGB import +- im_header_int(), im_header_double() and im_header_string() added to aid + UIs +- now uses gettimeofday(), not time() +- for consistency with other trig functions, im_c2amph() now returns degrees + not radians (ouch) +- added im_c2rect() ... turn (amp, phase) to rectangular +- added im_sign() ... unit vector in direction of value +- better im_scaleps() ... old code was terrible +- rewritten im_rotquad() ... now partial +- im_icc_export()/_import() now do ABSOLUTE correctly +- added im_icc_ac2rc() ... converts absolute to relative colorimetry + +25/6/02 JC +- added im_copy_set(3) ... like im_copy(), but set informational header fields + +20/6/02 JC +- added im_ceil(), im_floor() +- im_Lab2LabQ was not clipping a/b range correctly +- im_icc_export(), own ABSOLUTE mode +- released as 7.7.21 + +28/5/02 JC +- im_remainderconst_vec broken for float/double +- added Yxy colourspace + +16/05/02 JC +- auug, libtool was all messed up ... redone all the autotools stuff +- uses libtool convenience libraries to build vips in sections +- uses config subdir for temp files and .m4 things +- patched stupid suse config.guess +- vips2dj patched for better raw cmyk +- released as 7.7.20 + +12/5/02 JC +- im_vips2jpeg*() and im_vips2ppm() now both partial +- started updating the C++ guide +- had to change the location of the C++ headers :-( all C++ progs should now + have: + + #include + + this is so things can work on systems which do not have case sensitive + file systems + +- changes for Mac OS X + * im_system() TRUE/FALSE removed + * searches /*/[lib|include] to get fink libs for tiff and jpeg + +30/4/02 JC +- several functions were missing IM_FN_PIO in their descriptor ... this was + harmless for nip/ip/C, but broke the ref counting in the C++ layer +- im_system() now defaults "/tmp" for temp files +- STRING input and output args were broken for C++ :-( +- threads exit more quickly on error +- im_min()/im_max() now partial (at last) +- im_remainderconst()/im_remainderconst_vec() added +- --with-dmalloc configure switch +- vips2dj does CMYK and mono too +- im_vips2tiff() allows any number of bands (but not the right way to + write CMYK, see TODO) + +26/4/02 JC +- old ICC profile reader removed +- little cms wrapped ... configure spots it, im_icc_transform() uses it to map + between two images +- also im_icc_import() and im_icc_export() so you can see PCS images +- im_icc_present() to test for existence of lib +- README fixes + +4/4/02 JC +- TODO changes +- oops, DEBUG left on in im_invertlut() + +2/4/02 JC +- im_fwfft.c/im_invfft.c now use libfftw if available ... about 5x speed up + and double precision +- added FIND_FFTW autoconf macro +- include/vips/proto.h changes + +26/3/02 JC +- started 7.7.19 + +25/3/02 JC +- im_log_dmask() was broken (thanks matt) +- casts between VDMask and VIMask were broken (thanks matt) +- various error msgs improvements and tiny man page fixes + +13/3/02 JC +- tb/lr merge first/last cache moved to per-call state for better sharing +- im_remosaic() bails out faster on error and makes better error messages + +13/3/02 ruven +- im_vips2tiff() pyramids stop at tilesize, not 64x64 + +25/02/02 JC +- im_remosaic() is smarter, and works better with im_global_balance() +- im_affine() 2x faster + +14/2/02 JC +- started 7.7.18 +- vips.m4 and libsrc/Makefile.am fixes for IRIX + +11/02/02 JC +- vips/thread.h and vips/threadgroup.h were missing extern "C" for C++ +- VImage::write() now tracks dependencies, so you can write() to a partial + safely ... although it's not a very useful thing to do (thanks Mike) +- new VImage::print() function for debugging +- added im_print() + +22/01/02 JC +- started 7.7.17 + +15/01/02 JC +- im_rect_unionrect() and im_rect_intersectrect() safer for repeated args +- im_video_v4l() no longer perrror()s on ioctl fail for less spam + +03/01/02 JC +- started 7.7.16 +- im_version_string() really does return the date as well now + +12/12/01 JC +- im_guess_prefix() extra smartness for relative path names +- VImage() no longer uses tmpnam() (thanks Paul) + +11/12/01 JC +- renamed im_fexists() as im_existsf() + +7/12/01 JC +- ppm man pages added (doh) + +28/11/01 JC +- warnings on g++ 2.96 fixed + +22/11/01 JC +- started 7.7.15 +- im_video_v4l() failed to compile on non-linux platforms + +7/11/01 JC +- im_remosaic() added +- im_*merge() are more intelligent about transparency in bizarre overlaps +- grr! putenv() semantics change on more recent clibs ... should be safer + now + +19/10/01 JC +- VDisplay( "display name" ) segved on unknown display :-( thanks mike + +26/9/01 JC +- contrib tools get data files from share/vips/xxx area now +- im_vipshome() renamed as im_guess_prefix(), reworked for new package layout +- doc/ build sorted out +- ... but of course, docs still need updating for 7.8 + +20/9/01 JC +- fix to im_vipshome() +- ip2 renamed as nip +- split to library only ... separate ip and nip packages +- new VIPS_VERSION_* macros set from configure.in in vips/version.h.in +- vips.m4 VIPS finder +- reworked README, doc/README and TODO +- now installs to /usr/local/ by default +- fmask4th.c was including varargs.h ... d'oh +- include area reorganised: everything inside now ... hopefully the + only user-visible change is that all plain C progs need to change: + + #include + + to: + + #include + + the C++ API should be unaltered + +21/8/01 ruven +- im_setupout() was missing some #includes + +20/8/01 JC +- started 7.7.14 + +15/8/01 JC +- added libxml dependency for ip2 + +27/7/01 JC +- im_conv(), im_convf(), im_convsep(), im_convsepf() now reject masks with + scale == 0 + +26/7/01 JC +- started 7.7.12 + +25/7/01 JC +- started 7.7.11 +- oop, im_histeq() and im_tonemap() also missed +- better error messages from im_run_command() + +23/7/01 JC +- started 7.7.10 +- im_sharpen() failed due to change in im_band_extract() offset + +20/7/01 JC +- started 7.7.9 + +4/7/01 JC +- im_open(,"w") open() delayed until im_setupout(), very slightly safer +- updated im_open() man page +- im_tiff2vips() now embeds index in filename ... and it's page number (from + 0), not subsample factor +- finally bit the bullet ... im_extract()/im_extract_band() now number from + zero (sorry!) +- and im_lrmosaic()/im_tbmosaic() bandno param too + +29/6/01 JC +- im_region_free() now frees immediately + +27/6/01 JC +- im_vips2tiff() man page updated for deflate, 2 years late + +22/6/01 JC +- oops, limit wrong on im_rank() + +21/6/01 JC +- better post_install for --prefix outside VIPS's tree +- -ltiff needs -lm in acinclude.m4 ... fixes configure on redhat 7.x + +13/6/01 JC +- started 7.7.8 + +6/6/01 JC +- im_invertlut() added + +31/5/01 JC +- im_colour_temperature, im_XYZ2Lab_temp, im_Lab2XYZ_temp added +- ... colour temp stuff needs sorting out properly + +25/5/01 JC +- added vips-config script, cf. gtk-config +- --without-threads option added +- did a bit of work on the C++ API docs + +24/5/01 JC +- added im_tiff2vips_header(), im_jpeg2vips_header() and im_ppm2vips_header() +- header uses these to print fields quickly +- switched to config.h +- configure.in rewritten ... much nicer, fewer options, more automatic + +17/5/01 JC +- im_matinv() didn't free stuff correctly on singular matrix + +16/5/01 JC +- vips2dj now knows about 5000ps printers +- allow RW mode for non-native VIPS image files, for 8 bit images + +2/5/01 JC +- started 7.7.7 + +1/5/01 JC +- im_addgnoise() did not work for >1 band images + +23/4/01 JC +- configure options to remove support for JPEG and TIFF ... helpful for a no- + dependencies build + +20/4/01 JC +- im_(v)snprintf() added +- all sprintf()s removed + +15/4/01 JC +- im_affine() had a rounding problem + +11/4/01 JC +- tiny mosaicing bug fixed in im__lrcalcon +- started 7.7.6 + +21/3/01 JC +- new iblend code in im_tbmerge() was typo-d +- mosaic1 was broken by affine too + +20/3/01 JC +- im_image() failed for FMTUCHAR + +12/3/01 JC +- started 7.7.5 +- im_sharpen() uses seperable convolution for big speed up +- new "Print" menu + +11/3/01 JC +- REALVEC renamed as DOUBLEVEC +- added IMAGEVEC +- added IM_INPUT_IMAGEVEC +- gbandjoin now has function description +- new function im_maxvalue() +- im_compass()/im_lindetect() reimplemented with im_conv()/im_maxvalue(), + about 15% faster, works for any type, partial +- im_gradient() reimplemented with im_conv()/im_abs()/im_add(), about 30% + slower, works for any type, partial + +10/3/01 JC +- new function, im_clip2fmt() converts between any image formats ... + slightly faster than the old im_clip() +- legacy im_clip2us() etc. functions now just call this + +9/3/01 JC +- im_conv() rewritten, simpler, about 10% faster +- im_convsep() rewritten, now does any non-complex type, partial, 20% faster +- new functions: im_convf(), im_convsepf() for DOUBLEMASK +- raw versions of each +- legacy convolvers (eg. im_convbi()) removed + +8/3/01 JC +- new function im_blend() +- new function im_lab_morph() +- speed up to im_ifthenelse() +- speed up to im_*merge() (uses integer arithmetic for integer blends) + +4/3/01 JC +- tiny speed ups to im_histgr() +- speed ups to im_maplut() + +3/3/01 JC +- new functions: im_histnorm(), im_histcum() +- im_histeq() more general +- im_vipshome() --- better behaviour for relative paths + +2/3/01 JC +- new video package +- im_video_v4l1() (video for linux) added +- configure.in switches to turn v4l1 on and off + +1/3/01 JC +- new im_histspec() implementation ... more general, bugs removed + +14/2/01 JC +- better vips2dj usage message + +13/2/01 JC +- im_image_sanity() added, called in various places in iofuncs + +9/2/01 JC +- added 'check' and 'name' class member stuff to ip from ip_gtk2 +- new "Plot" menu +- new "Overlay" menu +- more stuff in _stdenv/_list ... curried forms of head/tail etc. + +7/2/01 JC +- started 7.7.4 +- vips2dj and vdump now use im_vipshome() + +5/2/01 JC +- new im_vipshome() function +- min()/max() macros renamed as MIN()/MAX() +- new im_load_plugins() function +- vips.exe and ip now load $VIPSHOME/lib plugins at startup + +2/2/01 JC +- mosaicing functions now have an extra max blend width parameter + +30/1/01 JC +- fixed tbmerge no overlap detect + +13/12/00 JC +- started 7.7.3 + +30/12/00 JC +- vips.h fixes for cygwin/wingdi conflict + +27/11/00 JC +- added im_vips2ppm(), im_open() imports and exports it +- fixed nasty implicit output conversion problem for PIO dispatch() calls + +21/11/00 JC +- added im_ppm2vips() + +16/11/00 JC +- configure.in fixes ... jpeg found correctly now +- searches for libz as well + +16/11/00 JC +- started 7.7.2 + +5/11/00 JC +- speed up to lab2labq + +19/10/00 JC +- started 7.7.1 + +13/1/00 JC +- oops, im_open() was missing an 'else' in jpeg/tiff load + +5/8/00 JC +- im_vips2tiff() now has mode string embedded in output filename +- im_vips2jpeg() now has qfac in output filename +- im_open() understands this + +11/7/00 JC +- new im_image() function ... wraps a VIPS image around a memory buffer +- C++ layer changes: + * now use #include + * error renamed as VError + * new VMask() constructors + * new VImage() constructor + * new VImage::data() access member + * more operator equivalences: <, >, <=, >=, ==, !=, &, |, ^, % + +17/6/00 JC +- more consts added to vips protos + +10/5/00 JC +- minor configure.in changes to help solaris +- removed _TIFFmalloc() and _TIFFfree() calls + +9/3/00 JC +- fixed rounding problem in generate grid + +8/3/00 JC +- fixup to im_system() temp dir + +7/3/00 JC +- added im_system() +- small tidies + +1/3/00 JC +- better plugin test in configure.in + +26/2/00 JC +- fixes to Makefile.am in ip/src* and configure.in, to help ip find the right + gtk includes on systems with more than one gtk-xxx installed + +21/2/00 JC +- now builds ip and ip_gtk2 + +16/2/00 JC +- configure.in fixes for xil and some TIFF/JPEG strangeness + +15/2/00 JC +- im_global_balancef() was broken! d'oh +- note in README about enabling video cards + +10/2/00 JC +- configure now searches for xil ... FIND_XIL macro +- im_zoom() spots integer overflow +- better shape set on region drag end in ip +- better zoom-too-far handling in ip + +13/1/00 JC +- fixes to configure etc. to help cygwin +- jpeg is now searched for too ... FIND_JPEG macro + +10/1/00 JC +- global_balance() now uses new affine() atuff + +27/12/99 JC +- mosaic1 stuff now uses new affine() funcs + +21/12/99 JC +- added Joe's docs +- im_LabS2LabQ() rounding on a/b slightly broken for a/b == 0 + +20/12/99 JC +- new function: im_affine() +- similarity*() now in terms of affine() +- tests for error return in reduce.c from maplut +- small clean-ups + +15/12/99 +- im_version() added, new iofuncs package for it +- ip did not call zero-input-arg vips functions +- vips.c did not like zero-input-arg functions + +6/12/99 JC +- Sobel filter was a bit broken + +3/12/99 JC +- menu reorganisation +- generate gauss mask dialog + +2/12/99 JC +- reworked text file IO, better error messages + +1/12/99 JC +- reworked .iprc filename stuff, cleaner + +30/11/99 JC +- better menu set switcher in calc preferences +- ip now thinks it's 7.7 +- did a spellcheck on the ip guide + +29/11/99 JC +- 7.7 started! +- srgb D65->D50 converter +- Negate added to arith +- find similar pixel value dialog +- find similar colour dialog +- paste into background dialog + +19/11/99 JC +- adjust labq was mising +- shrink image defaulted to /2 not /1 +- sharpdropshadow broken +- Rotate.* broken +- vips-7.6.3 release + +18/11/99 JC +- ip didn't report space free on >2GB filesystems correctly +- ip did not link statically against libXpm on solaris7 + +17/11/99 JC +- im_histplot() failed for all 0's histogram +- new profile_image dialog +- man pages for im_sRGB2XYZ/im_XYZ2sRGB were broken +- new colourize image dialog +- new shrink image image dialog +- new expand image image dialog +- better doc Makefiles + +16/11/99 JC +- more menu reorganising +- new image_to_mask/mask_to_image buttons +- new match_two_images dialog +- new measure_colour_chart dialog + +15/11/99 JC +- new custom LABQ sharpen in Image menu +- configure support for SGI video (thanks Ruven) +- menu fiddling in ip +- new "resize canvas" menu item +- im_insert_noexpand() added, no docs tho' + +11/11/99 JC +- new ip menu item: Image=>Adjust white/black/saturation of LabQ +- fixed bug in decompose complex number +- browse-icons now ignores errors +- better error msg for "12 12" etc cases + +10/11/99 JC +- open hi-res gives proper error msg if no file found +- made file-select boxes a bit more compact and clearer +- updated configure.in for vips-examples-7.6 + +9/11/99 JC +- new column start point moved +- vdump now defaults to subsample 1, portrait +- navigation boxes were broken +- no longer shrink-to-fit if loading as high res +- better positioning of zoom windows +- removed some old cruft + +8/11/99 JC +- ooops, im_remainder() got lost somehow +- refguide converted to latex and updated +- cppguide updated + +5/11/99 JC +- libguide converted to latex and updated + +4/11/99 JC +- ip guide now latex2htmls cleanly ... still needs updating tho' +- cpp/app guide latex2html redone + +2/11/99 JC +- configure no longer adds -32 to IRIX builds for you +- some more stuff in README +- simple DEBUGM malloc tracking, made libsrc/iofuncs/memory.c +- changed im_malloc() to return void * +- removed duplicate im_malloc() proto from util.h +- changed all malloc/free to go through im_malloc()/im_free() + +23/10/99 JC +- double-click on error image now pops a load browser and an error dialog +- add-new-column no longer scrolls to right edge of workspace +- better scroll-to-bottom on item add +- load ws twice does not cause 'already open' errors +- memorise directory button in fsb + +18/10/99 JC +- new FIND_TIFF/FIND_MOTIF macros for acinclude.m4 +- new ./configure switches, see ./configure --help +- enabled static libs + +8/10/99 +- broke action_proc_bop() into smaller functions, stops bad code gen on + gcc2.95.1 (and others, prolly) +- restored old Makefiles in doc/src/ipguide +- vips7.6 script renamed as vips-7.6 + +5/10/99 +- replace image was broken +- some menu reorganisation +- 'reload all menus' button + +4/10/99 +- computed regions in ip were broken +- updated system.iprc defaults +- new menu item: adjust white/black points + +2/10/99 +- browse icons was broken +- greyscale 16-bit tiled tiff was broken +- extra mutex locks for TIFF*() in im_tiff2vips() + +1/10/99 +- some automake probs fixed +- squished two ip bugs + +30/9/99 +- fixed problem with pthreads, now works on suse6.2 as well +- new API stuff for threaded evaluation with im_threadgroup_t +- new public interface provide platform independent threads/locks/semaphores +- no error box if you zoom out too far now + +26/9/99 +- lr/tb merge blend was not quite right ... should be smoother now +- histplot broken for float images + +24/9/99 +- better mono->labq converter +- more portable ispoweroftwo detect for freq filter stuff + +23/9/99 +- better graphics expose handling + +17/9/99 +- >/< stuff in ip was a bit mixed up +- ink preview fixed for mono images +- help popup fixed + +15/9/99 +- linedetect and sobel filters for ip, thnx Kirk + +14/9/99 +- Find_histogram was broken +- im_profile() man pages was broken +- ooops, ip had old set of macros + +10/9/99 +- im_and/im_or/im_eor now work for any integer type + +Summer hols: (2nd half August '99) +- initial heap block larger to avoid start gcs +- def slicer fixed +- larger max heap +- toolkits with initial '_' hidden by default +- custom recomb +- dialog.def removed +- ... other menu fixes +- generate grid menu +- im_remainder() added +- new cursor change code, hglass rotates during comp! +- dialog.c handles cursor changes better +- rubber band in paintbox displays +- mag widget stays on right! +- undo/redo single pixel paint ops +- heap size control from prefs +- cancel for reductions as well as for image calc +- browse stuff reworked, no more .icon.v files +- save stops you overwriting open .v files +- better animate_countdown() handling +- better resize behaviour for bars added to images +- better code generation, bug fixed in state tracking +- auto recover from crash diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..b42a17ac --- /dev/null +++ b/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..26c657e7 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,56 @@ +# only build in the python dir if we can +if HAVE_PYTHON +P_COMPILE_DIR = python +P_DIST_DIR = +else +P_COMPILE_DIR = +P_DIST_DIR = python +endif + +SUBDIRS = \ + libsrc \ + src \ + include \ + libsrcCC \ + contrib \ + po \ + $(P_COMPILE_DIR) + +EXTRA_DIST = \ + doc \ + benchmark \ + bootstrap.sh \ + win32 \ + vipsCC-7.12.pc.in \ + vips-7.12.pc.in \ + vips-7.12.spec.in \ + acinclude.m4 \ + depcomp \ + intltool-extract.in \ + intltool-merge.in \ + intltool-update.in \ + $(P_DIST_DIR) + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = vips-7.12.pc vipsCC-7.12.pc + +install-exec-hook: + -rm -rf ${DESTDIR}$(datadir)/doc/vips + $(mkinstalldirs) ${DESTDIR}$(datadir)/doc/vips + -cp -r ${top_srcdir}/doc/html ${top_srcdir}/doc/pdf ${DESTDIR}$(datadir)/doc/vips + +dist-hook: +# make sure we don't get any .svn dirs from EXTRA_DIST + -find $(distdir) -name .svn -exec rm -rf {} \; + +uninstall-hook: +# make sure we have write permission for 'rm' + -chmod -R u+w ${DESTDIR}$(datadir)/doc/vips + -rm -rf ${DESTDIR}$(datadir)/doc/vips + +distclean-local: +# need to remove expanded intltool files in build area to pass distcheck + -rm ${top_builddir}/intltool-extract \ + ${top_builddir}/intltool-merge \ + ${top_builddir}/intltool-update + diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..ac7ec9f3 --- /dev/null +++ b/NEWS @@ -0,0 +1,232 @@ +VIPS changed from 7.10 to 7.12 (not exhaustive, see ChangeLog for details) + +Non-backwards compatible changes +================================ + +- IMAGE->Hist is deprecated, use im_history_get() instead +- im_append_Hist() removed +- IMAGE->Bbits is deprecated (now ignored) +- im_region_local() replaced by im_region_buffer() + +VIPS enhancements +================= + +- new backwards and forwards compatible VIPS file format: it has a new + metadata system that efficiently shares and propogates ICC profiles, + EXIF data, etc. & whatever +- speed ups: new pixel buffer cache saves and reuses computations, uses liboil + where possible for a small speedup +- SMP scaling: simplified lock system improves SMP scaling, double-buffered + image writes overlap computation +- lower mem use: new mmap window cache shares file mappings, history buffers + share and reuse image history metadata +- built-in support for new image formats: OpenEXR, Analyze7, CSV +- RGB16 and GREY16 image hints +- uses GOption for much better command-line argument handling +- better C++ binding: namespaces, VError derives from std::exception, image + and number vector constants +- python binding +- gcc sentinel attributes added +- added GValue as an arg type +- added im_benchmark() and friends for testing +- new functions: + im_analyze2vips(), im_benchmark*(), im_buildlut(), + im_concurrency_get/set(), im_contrast_surface(), + im_contrast_surface_raw(), im_copy_from(), im_copy_morph(), + im_cp_desc_array(), im_cp_descv(), im_extract_areabands(), + im_flood_blob_copy(), im_get_option_group(), im_grid(), + im_header_exists(), im_header_map(), im_history_get(), + im_invalidate(), im_isscalar(), im_lineset(), im_linreg(), + im_meta*(), im_msb(), im_norm_dmask(), im_project(), + im_raw2vips(), IM_REGION_ADDR_TOPLEFT(), im_tile_cache(), + im_video_test() + + +VIPS changed from 7.8 to 7.10 (not exhaustive, see ChangeLog for details) + +Non-backwards compatible changes +================================ + +- no longer generates vips-config script, instead it makes .pc files for + pkg-config ... replace any "vips-config" with "pkg-config vips-7.10" +- origin hint semantics changed ... it now records the position in the output + image of the origin of the input image +- all float to int casts on pixel values are now floor() rather than rint(), + for consistency with C casting semantics + +VIPS enhancements +================= + +- nip reworked for gtk+-2.4, now called nip2 +- now based on glib, so VIPS no longer has it's own thread, plugin and data + structure libraries +- i18n support, although not quite complete yet +- new error message API to make i18n possible (compatibility macros mean the + old API still works) +- new 'start up VIPS' call, im_init_world(), sets up i18n and threads + (optional to call it, but i18n won't fully work unless you do) +- C programs now just need "#include ", no other includes + required +- wraps the Pango font rendering library, see im_text() +- new function flag: NOCACHE ... means the result of the call should not be + cached, useful for video grab functions and paintbox functions +- im_embed() now supports a wider range of embedding styles, including a fast + edge stretch +- all area operations use the new embed style to expand their input rather + than their output, so there are no more black borders on filtered images +- other new functions: im_render(), im_cache(), im_extract_bands(), + im_copy_swap(), im_rint(), im_make_xy(), im_init_world(), im_error(), + im_warn(), im_diag(), im_rank_image(), im_replicate() +- many fixes and improvements to old funcs +- configure and portability improvements + +Bug fixes +========= + +- all serious bug fixes got back-ported to 7.8, I think + + +VIPS changes from 7.6 to 7.8 (again, not exhaustive, see ChangeLog for details) + +Non-backwards compatible changes +================================ + +- output format options specified in filenames: new prototypes for load and + save tiff and jpeg +- C++ API name changes ... error => VError +- include path change + - C includes + - C++ includes +- im_extract*() numbers bands from zero, not from 1 ... also other funcs which + refer to bands (eg. im_lrmosaic() etc.) +- mosaic funcs have extra max blend width parameter + +VIPS enhancements +================= + +- rewritten and much fancier ip (see separate docs), now called nip ... old ip + (slightly fixed) still there +- mmap windows remove 2GB image size limit +- images have an origin hint field +- icc transforms supported via wrapping of Little CMS +- much, much faster Fourier transforms via wrapping of libfwfft +- ppm/pgm/pbm read/write +- C++ API enhancements: new constructors, more operator equivalences +- new colourspace: Yxy +- support for D50 colour temperature images +- new functions: im_image(), im_system(), im_version*(), im_blend(), + im_copy_set(), im_flood_blob(), im_icc_*(), im_open_local_array(), + im_header_*(), im_sign(), im_ceil(), im_floor(), im_remainderconst*(), + im_print(), im_guess_prefix(), im_remosaic(), im_invertlut(), Yxy funcs, + color temperature funcs, im_clip2fmt(), im_blend(), im_lab_morph(), + im_histnorm(), im_histcum(), im_video_v4l(), im_LabS2Lab(), im_Lab2LabS() +- new type: IMAGEVEC +- header is much faster +- ip/nip split to separate packages +- better vips2dj +- better cygwin support, mingw support too +- Mac OS X and Windows build support +- new set of sensible #define names, old names can be turned off with + -DIM_NO_VIPS7_COMPAT +- many configure improvements + +Bug fixes +========= + +- speed ups +- fixes to implicit image format conversions +- im_zoom() spots integer overflow +- im_LabS2LabQ() rounding on a/b slightly broken for a/b == 0 +- fixes to refcounting in C++ API +- mask casts in C++ were broken + + + + +VIPS Changes for 7.4 to 7.6 + +Non-backwards compatible changes +================================ + +- im_histplot() has new (and better) rules +- im_andconst(), im_orconst() and im_eorconst() now take a + double, not an unsigned char, as their constant argument type +- im_global_balance_float() renamed as im_global_balancef() to be + more consistent with other functions +- im_global_balance_search() removed ... you can do this efficiently + with an ip macro now +- new parameter "gamma" to im_global_balance() and + im_global_balancef() ... sets the gamma for the input device with + which the images were captured, set to 1.0 for old behaviour +- im_malloc() now returns void *, not char * + +Bug fixes +========= + +- tiny memory leak in im_list_remove() fixed +- oops, the value for sRGB in the header was wrong, now 22 +- missing function descriptor for im_rank_raw() +- im_dECMC_fromLab() was broken +- im_erode() and im_dilate() output incorrect error messages + if mask elements were not one of 0, 128, 255 +- im_rotate_*mask*() functions were not preserving scale and offset + values + +Package enhancements +==================== + +The whole thing is now GPL, with GNU configure + +ip changes +========== + +- better display control bar +- matrix operations now appear in the workspace +- new UI elements: input options and toggles +- better LUT optimisation --- arithmetic operations on UCHAR images should be + much faster +- new macro language --- same as old one, but faster and much more powerful +- all standard menus rewritten in new macro language, much nicer! +- batch operations with Group/Ungroup +- now uses GDK for drawing in image windows, much better colour handling on + 8/16-bit graphics cards +- image repaint is now threaded for an about-linear speedup as you add + more CPUs +- many interface improvements and polishes + +VIPS enhancements +================= + +- VIPS now uses POSIX threads (7.4 used Solaris threads) and has been rejigged + to provide a nice threading API to functions which call the library +- im_open() now knows about LSB- and MSB- first VIPS images, and + silently converts them to native order ... it also now ignores case when + deciding what format to write +- new parameter type IM_INPUT_REALVEC, a vector of doubles +- new set of functions with vector constants: im_lintra_vec(), + im_powtra_vec(), im_expntra_vec(), all relational + (im_equal_vec(), im_notequal_vec(), etc.), all boolean + (im_and_vec() etc.) +- new flag for function descriptors: "IM_FN_PTOP" set for point-to-point + functions ... makes ip use LUTs for operation +- im_tiff2vips() now reads and writes 16-bit images, and knows about zip + (deflate) compression +- convenience functions im_extract_band() extracts a band from an image; + im_extract_area() extracts an area +- im_list_member() tests for list contains object +- new functions im_write_*mask_name(), im_create_*maskv() +_ new functions im_remainder() and im_profile() +- fourier filtering, forward transform, reverse transform, make power spectrum + all free memory more quickly, making them suitable for very large images +- new functions im_isMSBfirst() and im_amiMSBfirst() test images + and this processor for MSB first byte order +- im_malloc() now prints low-on-memory warnings to stderr with + im_warning() for easier bug catching +- D65_X0 updated for latest recommedations, D60, D93, + D55, D50, A, B, C added +- minor fixes to the C++ interface to bring it in line with the newer ANSI + standards +- more and more comprehensive prototypes in "proto.h" to aid C++ (use of + "const" etc.) +- im_and*(), im_or*() and im_eor*() can now work on any + integer image diff --git a/README b/README new file mode 100644 index 00000000..e2573272 --- /dev/null +++ b/README @@ -0,0 +1,143 @@ +VIPS 7.12 +========= + +VIPS is an image processing library. It's good for large images and for +colour. There's a GUI as well, see the VIPS website: + + http://www.vips.ecs.soton.ac.uk + +Getting VIPS from SVN +===================== + +Enter: + + svn co https://vips.svn.sourceforge.net/svnroot/vips/vips7 + +Building VIPS from source +========================= + +In the VIPS directory, you should just be able to do: + + user% ./configure + user% make + +then as root: + + root% make install + +By default this will install files to /usr/local/bin, /usr/local/share/vips, +/usr/local/include, /usr/local/lib and /usr/local/man. + +If you have problems, read on. + +Building VIPS on win32 +---------------------- + +Probably the easiest route is to use mingw/msys. This provides a GNU-style +build environment for win32. + + http://www.vips.ecs.soton.ac.uk/index.php?title=Build_on_windows + +Alternatively, vips-7.x/win32 contains sample build systems using the +Microsoft toolchain. See the README in there for details. + +Building VIPS on OS X +--------------------- + +I used macports to install all the dependencies. Fink also works. + + http://www.macports.org + +You need to tell configure to use the msgfmt from macports. Something like: + + GMSGFMT="/opt/local/bin/msgfmt" ./configure --prefix=/Users/john/vips + +Dependencies +============ + +VIPS has to have glib-2.x and libxml-2.0. The build system needs perl, +pkg-config and gnu make. + +Optional dependencies +--------------------- + +Optional support libraries ... also try ./configure --help to see flags for +controlling these libs. By default, if suitable versions are found, VIPS will +build support for them automatically. + +VIPS looks for stuff in /usr. If you have installed you own versions of these +libraries to /usr/local, vips will not see them. Use switches to VIPS +configure like: + + ./configure --prefix=/home/john/vips \ + --with-tiff-includes=/home/john/vips/include \ + --with-tiff-libraries=/home/john/vips/lib + +to get VIPS to see your builds. + +libjpeg + The IJG JPEG library. We use 6b, but 6a works too. + +libexif + if available, VIPS adds support for EXIF metadata in JPEG files + +libtiff + The TIFF library. It needs to be built with support for JPEG and + ZIP compression. 3.4b037 and later are known to be OK. + You'll need libz for this too. We use 1.1.3, others should work. + +libz + If your TIFF library includes ZIP compression, you'll need this too. + +videodev.h + If VIPS finds linux/videodev.h, you get support for Linux video + grabbing. + +fftw3 + If VIPS finds this library, it uses it for fourier transforms. It can + also use fftw2, but 3 is faster and more accurate. + + If the library is not found, VIPS falls back to it's own internal FFT + routines which are slower and less accurate. + +lcms + If present, im_icc_import(), _export() and _transform() are available + for transforming images with ICC profiles. + +large files + VIPS uses the standard autoconf tests to work out how to support large + files (>2GB) on your system. Any reasonably recent *nix should be OK. + +libpng + if present, VIPS can load and save png files. Version 1.2+ preferred. + +libMagick + if available, VIPS adds support for loading all libMagick supported + image file types (about 80 different formats). No saving though. + +pango +freetype2 +fontconfig + if available, VIPS adds support for text rendering. You need the + package pangoft2 in "pkg-config --list-all" + +liboil + if available, you get some inner loops replcaed by calls to liboil's + library of optimised inner loops + +OpenEXR + if available, VIPS will directly read (but not write, sadly) OpenEXR + images + +swig +python +python-dev + if available, we build the python binding too + +Disclaimer +---------- + +Disclaimer: No guarantees of performance accompany this software, nor is any +responsibility assumed on the part of the authors. Please read the licence +agreement. + diff --git a/THANKS b/THANKS new file mode 100644 index 00000000..9d6e6396 --- /dev/null +++ b/THANKS @@ -0,0 +1,25 @@ +VIPS THANKS file + +VIPS was originally written by Nicos Dessipris, Kirk Martinez and John Cupitt. + +Many people have contributed to VIPS by reporting problems, suggesting +improvements, or offering code. + + Matthew Hanson + Joe Padfield + Haida Liang + Ian Clarke + Steve Perry + Stephen Chang + David Saunders + Mike Westmacott + Chris Hurst + Jim Coddington + Lou + Rachel Billinge + Colin White + ENST + Thomson-CSF + +We've also had very helpful funding from the European Commission and +Hewlett-Packard. diff --git a/TODO b/TODO new file mode 100644 index 00000000..ea99d302 --- /dev/null +++ b/TODO @@ -0,0 +1,147 @@ +- when we fork for 7.13 do this stuff ... don't do now, we'll break the other + packages + + http://www.freshports.org/graphics/vips + + freebsd packaging now does: + + In both: + - use explict --mandir=${PREFIX}/man to avoid man-pages getting + into ${PREFIX}/shared/man incorrectly + - deal with the NOPORTDOCS situation by simply not-extracting + the extra documentation from the distribution tarball + - parallelize the build to scale with the number of CPUs + + In vips: + - move the (giant) list of man-pages into a separate Makefile.man + - turn the pages, which contain only `.so other-page', into + MANLINKS (specified in Makefile.man) + - provide a "maintainance target" to regenerate the Makefile.man + during the next upgrade + - do not install the HTML-ized versions of man-pages + - create OPTIONS for use of devel/liboil and graphics/ImageMagick + (OPTION to use PYTHON awaits portmgr's decision/action) + + In nip2: + - do install the HTML pages regardless of NOPORTDOCS -- these + are accessible to the user through the application GUI + - arrange for update-mime-database and update-desktop-database + to be run upon install (@exec) and uninstall (@unexec) + - LIB_DEPEND on math/gsl, which nip2 can use for extra functionality + +Python binding +============== + +- python startup fails with plugins in vipslib: + + Fatal Python error: can't initialise module vips + plugin: unable to open plugin "/home/john/vips/lib/resample.plg" + plugin: /home/john/vips/lib/resample.plg: undefined symbol: im_start_one + + do we need to build plugins with -rpath etc. and more libs? + + or do we need to make sure our python modules export all their im_ symbols? + +WONTFIX +======= + +- TIFF load/save should use meta system for unknown tags + +- balance should use new meta stuff + +- magick2vips should spot ICC profiles and attach them as meta + +- magick should set some header field for n_frames and frame_height? see also + analyze + +- see TODO notes in openexr read (though they all need more openexr C API) + + consider openexr write + +- matrix invert is a copypaste of NR :-( fix this + +- add GREYCstoration filter + + http://www.haypocalc.com/wiki/Gimp_Plugin_GREYCstoration + + actually, it has to be a plugin, since their code is GPL + + and very memory-hungry for large images :-( needs 20x size of image to be + processed + + could we rewrite with VIPS? how much more stuff would we need to add? + + try again using the current version of the filter from the suthors rather + than the gimp plugin + +- im_csv2vips() could use "-" for filename to mean stdin + + but then we'd have to read to a malloced buffer of some sort rather than an + image, since we might need to grow it during the read, since we couldn't + then seek + +- add erode/dilate 3x3 special case using a 512-bit LUT + + ... nope, actually slower :-( we end up doing an inner loop like + + for( i = 0; i < 9; i++ ) + bits |= (p[o[i]] != 0) << i; + + which is horrible. Maybe if we had a one band true 1 bit image it'd be + quicker since we could get bits out in parallel and wouldn't have to worry + about converting non-zero to 1 + + could have a Coding type for bitpack? eg. relational produces a bitpack + image by default, boolean & morph can work on bitpack etc + + maybe something for vips8 ... we could have a flag on operations for the + coding types they can accept, and if they are passed other type, they are + automatically converted + +- non-linear sharpen: replace each pixel by the lightest or darkest neighbour + depending on which is closer in value + +- can wrap other inplace funcs which use ink now we have vector_to_ink() in + inplace_dispatch.c + + see also comments in nip2 TODO ... we could auto-wrap in vips_call.c + + cleaner! + +- on win32, should not write matrix files in binary mode, we want CR/LF chars + so we can load into excel etc easily + + how odd, we're doing + + if( !(fp = fopen( name, "w" )) ) { + + shouldn't be binary ... hmm + +Build +===== + +- xmlFree() is still broken :-( + + maybe we are not importing it correctly? im_readhist.c references + + _imp__xmlFree + + how is this made? look at gcc -E output ... maybe there's an extra define we + need to make it generate the right link code? + + see what libxml2.dll.a is exporting that looks anything like xmlFree + +- can we make a fftw3.dll? also, magick.dll? + + maybe just build with no-undefined? can we then link the DLL against the + static lib? + +- update gtk/glib/etc. on the PC to the latest versions, apparently much + quicker (esp. pango) + + + + +This TODO list is now held on the VIPS Wiki + + http://wiki.vips.ecs.soton.ac.uk/bin/view/Vips/TodoVips7 diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 00000000..5a2fd214 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,584 @@ +dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding +dnl +dnl FIND_ZIP[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]] +dnl ------------------------------------------------ +dnl +dnl Find ZIP libraries and headers +dnl +dnl Put includes stuff in ZIP_INCLUDES +dnl Put link stuff in ZIP_LIBS +dnl +dnl Default ACTION-IF-FOUND defines HAVE_ZIP +dnl +AC_DEFUN([FIND_ZIP], [ +AC_REQUIRE([AC_PATH_XTRA]) + +ZIP_INCLUDES="" +ZIP_LIBS="" + +AC_ARG_WITH(zip, +[ --without-zip do not use libz]) +# Treat --without-zip like --without-zip-includes --without-zip-libraries. +if test "$with_zip" = "no"; then + ZIP_INCLUDES=no + ZIP_LIBS=no +fi + +AC_ARG_WITH(zip-includes, +[ --with-zip-includes=DIR ZIP include files are in DIR], +ZIP_INCLUDES="-I$withval") +AC_ARG_WITH(zip-libraries, +[ --with-zip-libraries=DIR ZIP libraries are in DIR], +ZIP_LIBS="-L$withval -lz") + +AC_MSG_CHECKING(for ZIP) + +# Look for zlib.h +if test "$ZIP_INCLUDES" = ""; then + zip_save_LIBS="$LIBS" + + LIBS="-lz $LIBS" + + # Check the standard search path + AC_TRY_COMPILE([#include ],[int a;],[ + ZIP_INCLUDES="" + ], [ + # zlib.h is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/include /usr/*/include \ + /usr/local/*/include \ + "${prefix}"/include/* \ + /usr/include/* /usr/local/include/* /*/include; do + if test -f "$dir/zlib.h"; then + ZIP_INCLUDES="-I$dir" + break + fi + done + + if test "$ZIP_INCLUDES" = ""; then + ZIP_INCLUDES=no + fi + ]) + + LIBS="$zip_save_LIBS" +fi + +# Now for the libraries +if test "$ZIP_LIBS" = ""; then + zip_save_LIBS="$LIBS" + zip_save_INCLUDES="$INCLUDES" + + LIBS="-lz $LIBS" + INCLUDES="$ZIP_INCLUDES $INCLUDES" + + # Try the standard search path first + AC_TRY_LINK([#include ],[zlibVersion()], [ + ZIP_LIBS="-lz" + ], [ + # libz is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/lib /usr/*/lib /usr/local/*/lib \ + "${prefix}"/lib/* /usr/lib/* \ + /usr/local/lib/* /*/lib; do + if test -d "$dir" && test "`ls $dir/libz.* 2> /dev/null`" != ""; then + ZIP_LIBS="-L$dir -lz" + break + fi + done + + if test "$ZIP_LIBS" = ""; then + ZIP_LIBS=no + fi + ]) + + LIBS="$zip_save_LIBS" + INCLUDES="$zip_save_INCLUDES" +fi + +AC_SUBST(ZIP_LIBS) +AC_SUBST(ZIP_INCLUDES) + +# Print a helpful message +zip_libraries_result="$ZIP_LIBS" +zip_includes_result="$ZIP_INCLUDES" + +if test x"$zip_libraries_result" = x""; then + zip_libraries_result="in default path" +fi +if test x"$zip_includes_result" = x""; then + zip_includes_result="in default path" +fi + +if test "$zip_libraries_result" = "no"; then + zip_libraries_result="(none)" +fi +if test "$zip_includes_result" = "no"; then + zip_includes_result="(none)" +fi + +AC_MSG_RESULT( + [libraries $zip_libraries_result, headers $zip_includes_result]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "$ZIP_INCLUDES" != "no" && test "$ZIP_LIBS" != "no"; then + ifelse([$1],,AC_DEFINE(HAVE_ZIP,1,[Define if you have libz libraries and header files.]),[$1]) + : +else + ZIP_LIBS="" + ZIP_INCLUDES="" + $2 +fi + +])dnl + +dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding +dnl +dnl FIND_TIFF[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]] +dnl ------------------------------------------------ +dnl +dnl Find TIFF libraries and headers +dnl +dnl Put compile stuff in TIFF_INCLUDES +dnl Put link stuff in TIFF_LIBS +dnl +dnl Default ACTION-IF-FOUND defines HAVE_TIFF +dnl +AC_DEFUN([FIND_TIFF], [ +AC_REQUIRE([AC_PATH_XTRA]) + +TIFF_INCLUDES="" +TIFF_LIBS="" + +AC_ARG_WITH(tiff, +[ --without-tiff do not use libtiff]) +# Treat --without-tiff like --without-tiff-includes --without-tiff-libraries. +if test "$with_tiff" = "no"; then + TIFF_INCLUDES=no + TIFF_LIBS=no +fi + +AC_ARG_WITH(tiff-includes, +[ --with-tiff-includes=DIR TIFF include files are in DIR], +TIFF_INCLUDES="-I$withval") +AC_ARG_WITH(tiff-libraries, +[ --with-tiff-libraries=DIR TIFF libraries are in DIR], +TIFF_LIBS="-L$withval -ltiff") + +AC_MSG_CHECKING(for TIFF) + +# Look for tiff.h +if test "$TIFF_INCLUDES" = ""; then + # Check the standard search path + AC_TRY_COMPILE([#include ],[int a;],[ + TIFF_INCLUDES="" + ], [ + # tiff.h is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/include /usr/*/include \ + /usr/local/*/include "${prefix}"/include/* \ + /usr/include/* /usr/local/include/* \ + /opt/include /opt/*/include /*/include; do + if test -f "$dir/tiff.h"; then + TIFF_INCLUDES="-I$dir" + break + fi + done + + if test "$TIFF_INCLUDES" = ""; then + TIFF_INCLUDES=no + fi + ]) +fi + +# Now for the libraries +if test "$TIFF_LIBS" = ""; then + tiff_save_LIBS="$LIBS" + tiff_save_INCLUDES="$INCLUDES" + + LIBS="-ltiff -lm $LIBS" + INCLUDES="$TIFF_INCLUDES $INCLUDES" + + # Try the standard search path first + AC_TRY_LINK([#include ],[TIFFGetVersion();], [ + TIFF_LIBS="-ltiff" + ], [ + # libtiff is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/lib /usr/*/lib /usr/local/*/lib \ + "${prefix}"/lib/* /usr/lib/* \ + /usr/local/lib/* \ + /opt/lib /opt/*/lib /*/lib; do + if test -d "$dir" && test "`ls $dir/libtiff.* 2> /dev/null`" != ""; then + TIFF_LIBS="-L$dir -ltiff" + break + fi + done + + if test "$TIFF_LIBS" = ""; then + TIFF_LIBS=no + fi + ]) + + LIBS="$tiff_save_LIBS" + INCLUDES="$tiff_save_INCLUDES" +fi + +AC_SUBST(TIFF_LIBS) +AC_SUBST(TIFF_INCLUDES) + +# Print a helpful message +tiff_libraries_result="$TIFF_LIBS" +tiff_includes_result="$TIFF_INCLUDES" + +if test x"$tiff_libraries_result" = x""; then + tiff_libraries_result="in default path" +fi +if test x"$tiff_includes_result" = x""; then + tiff_includes_result="in default path" +fi + +if test "$tiff_libraries_result" = "no"; then + tiff_libraries_result="(none)" +fi +if test "$tiff_includes_result" = "no"; then + tiff_includes_result="(none)" +fi + +AC_MSG_RESULT( + [libraries $tiff_libraries_result, headers $tiff_includes_result]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "$TIFF_INCLUDES" != "no" && test "$TIFF_LIBS" != "no"; then + ifelse([$1],,AC_DEFINE(HAVE_TIFF,1,[Define if you have tiff libraries and header files.]),[$1]) + : +else + TIFF_INCLUDES="" + TIFF_LIBS="" + $2 +fi + +])dnl + +dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding +dnl +dnl FIND_JPEG[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]] +dnl ------------------------------------------------ +dnl +dnl Find JPEG libraries and headers +dnl +dnl Put compile stuff in JPEG_INCLUDES +dnl Put link stuff in JPEG_LIBS +dnl +dnl Default ACTION-IF-FOUND defines HAVE_JPEG +dnl +AC_DEFUN([FIND_JPEG], [ +AC_REQUIRE([AC_PATH_XTRA]) + +JPEG_INCLUDES="" +JPEG_LIBS="" + +AC_ARG_WITH(jpeg, +[ --without-jpeg do not use libjpeg]) +# Treat --without-jpeg like --without-jpeg-includes --without-jpeg-libraries. +if test "$with_jpeg" = "no"; then + JPEG_INCLUDES=no + JPEG_LIBS=no +fi + +AC_ARG_WITH(jpeg-includes, +[ --with-jpeg-includes=DIR JPEG include files are in DIR], +JPEG_INCLUDES="-I$withval") +AC_ARG_WITH(jpeg-libraries, +[ --with-jpeg-libraries=DIR JPEG libraries are in DIR], +JPEG_LIBS="-L$withval -ljpeg") + +AC_MSG_CHECKING(for JPEG) + +# Look for jpeg.h +if test "$JPEG_INCLUDES" = ""; then + jpeg_save_LIBS="$LIBS" + + LIBS="-ljpeg $LIBS" + + # Check the standard search path + AC_TRY_COMPILE([ + #include + #include ],[int a],[ + JPEG_INCLUDES="" + ], [ + # jpeg.h is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/include \ + /usr/local/include \ + /usr/*/include \ + /usr/local/*/include /usr/*/include \ + "${prefix}"/include/* \ + /usr/include/* /usr/local/include/* \ + /opt/include /opt/*/include /*/include; do + if test -f "$dir/jpeglib.h"; then + JPEG_INCLUDES="-I$dir" + break + fi + done + + if test "$JPEG_INCLUDES" = ""; then + JPEG_INCLUDES=no + fi + ]) + + LIBS="$jpeg_save_LIBS" +fi + +# Now for the libraries +if test "$JPEG_LIBS" = ""; then + jpeg_save_LIBS="$LIBS" + jpeg_save_INCLUDES="$INCLUDES" + + LIBS="-ljpeg $LIBS" + INCLUDES="$JPEG_INCLUDES $INCLUDES" + + # Try the standard search path first + AC_TRY_LINK([ + #include + #include ],[jpeg_abort((void*)0)], [ + JPEG_LIBS="-ljpeg" + ], [ + # libjpeg is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/lib \ + /usr/local/lib \ + /usr/*/lib \ + "${prefix}"/lib/* /usr/lib/* \ + /usr/local/lib/* \ + /opt/lib /opt/*/lib /*/lib; do + if test -d "$dir" && test "`ls $dir/libjpeg.* 2> /dev/null`" != ""; then + JPEG_LIBS="-L$dir -ljpeg" + break + fi + done + + if test "$JPEG_LIBS" = ""; then + JPEG_LIBS=no + fi + ]) + + LIBS="$jpeg_save_LIBS" + INCLUDES="$jpeg_save_INCLUDES" +fi + +AC_SUBST(JPEG_LIBS) +AC_SUBST(JPEG_INCLUDES) + +# Print a helpful message +jpeg_libraries_result="$JPEG_LIBS" +jpeg_includes_result="$JPEG_INCLUDES" + +if test x"$jpeg_libraries_result" = x""; then + jpeg_libraries_result="in default path" +fi +if test x"$jpeg_includes_result" = x""; then + jpeg_includes_result="in default path" +fi + +if test "$jpeg_libraries_result" = "no"; then + jpeg_libraries_result="(none)" +fi +if test "$jpeg_includes_result" = "no"; then + jpeg_includes_result="(none)" +fi + +AC_MSG_RESULT( + [libraries $jpeg_libraries_result, headers $jpeg_includes_result]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "$JPEG_INCLUDES" != "no" && test "$JPEG_LIBS" != "no"; then + ifelse([$1],,AC_DEFINE(HAVE_JPEG,1,[Define if you have jpeg libraries and header files.]),[$1]) + : +else + JPEG_INCLUDES="" + JPEG_LIBS="" + $2 +fi + +])dnl + +dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding +dnl +dnl FIND_PNG[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]] +dnl ------------------------------------------------ +dnl +dnl Find PNG libraries and headers +dnl +dnl Put compile stuff in PNG_INCLUDES +dnl Put link stuff in PNG_LIBS +dnl +dnl Default ACTION-IF-FOUND defines HAVE_PNG +dnl +AC_DEFUN([FIND_PNG], [ +AC_REQUIRE([AC_PATH_XTRA]) + +PNG_INCLUDES="" +PNG_LIBS="" + +AC_ARG_WITH(png, +[ --without-png do not use libpng]) +# Treat --without-png like --without-png-includes --without-png-libraries. +if test "$with_png" = "no"; then + PNG_INCLUDES=no + PNG_LIBS=no +fi + +AC_ARG_WITH(png-includes, +[ --with-png-includes=DIR PNG include files are in DIR], +PNG_INCLUDES="-I$withval") +AC_ARG_WITH(png-libraries, +[ --with-png-libraries=DIR PNG libraries are in DIR], +PNG_LIBS="-L$withval -lpng") + +AC_MSG_CHECKING(for PNG) + +# Look for png.h +if test "$PNG_INCLUDES" = ""; then + png_save_LIBS="$LIBS" + + LIBS="-lpng $LIBS" + + # Check the standard search path + AC_TRY_COMPILE([ + #include ],[int a],[ + PNG_INCLUDES="" + ], [ + # png.h is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/include \ + /usr/local/include \ + /usr/*/include \ + /usr/local/*/include /usr/*/include \ + "${prefix}"/include/* \ + /usr/include/* /usr/local/include/* /*/include; do + if test -f "$dir/png.h"; then + PNG_INCLUDES="-I$dir" + break + fi + done + + if test "$PNG_INCLUDES" = ""; then + PNG_INCLUDES=no + fi + ]) + + LIBS="$png_save_LIBS" +fi + +# Now for the libraries +if test "$PNG_LIBS" = ""; then + png_save_LIBS="$LIBS" + png_save_INCLUDES="$INCLUDES" + + LIBS="-lpng $LIBS" + INCLUDES="$PNG_INCLUDES $INCLUDES" + + # Try the standard search path first + AC_TRY_LINK([ + #include ],[png_access_version_number()], [ + PNG_LIBS="-lpng" + ], [ + # libpng is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/lib \ + /usr/local/lib \ + /usr/*/lib \ + "${prefix}"/lib/* /usr/lib/* \ + /usr/local/lib/* /*/lib; do + if test -d "$dir" && test "`ls $dir/libpng.* 2> /dev/null`" != ""; then + PNG_LIBS="-L$dir -lpng" + break + fi + done + + if test "$PNG_LIBS" = ""; then + PNG_LIBS=no + fi + ]) + + LIBS="$png_save_LIBS" + INCLUDES="$png_save_INCLUDES" +fi + +AC_SUBST(PNG_LIBS) +AC_SUBST(PNG_INCLUDES) + +# Print a helpful message +png_libraries_result="$PNG_LIBS" +png_includes_result="$PNG_INCLUDES" + +if test x"$png_libraries_result" = x""; then + png_libraries_result="in default path" +fi +if test x"$png_includes_result" = x""; then + png_includes_result="in default path" +fi + +if test "$png_libraries_result" = "no"; then + png_libraries_result="(none)" +fi +if test "$png_includes_result" = "no"; then + png_includes_result="(none)" +fi + +AC_MSG_RESULT( + [libraries $png_libraries_result, headers $png_includes_result]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "$PNG_INCLUDES" != "no" && test "$PNG_LIBS" != "no"; then + ifelse([$1],,AC_DEFINE(HAVE_PNG,1,[Define if you have png libraries and header files.]),[$1]) + : +else + PNG_INCLUDES="" + PNG_LIBS="" + $2 +fi + +])dnl + +dnl a macro to check for ability to create python extensions +dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE]) +dnl function also defines PYTHON_INCLUDES +AC_DEFUN([AM_CHECK_PYTHON_HEADERS], +[AC_REQUIRE([AM_PATH_PYTHON]) +AC_MSG_CHECKING(for headers required to compile python extensions) +dnl deduce PYTHON_INCLUDES +py_prefix=`$PYTHON -c "import sys; print sys.prefix"` +py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"` +PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}" +if test "$py_prefix" != "$py_exec_prefix"; then + PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}" +fi +AC_SUBST(PYTHON_INCLUDES) +dnl check if the headers exist: +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES" +AC_TRY_CPP([#include ],dnl +[AC_MSG_RESULT(found) +$1],dnl +[AC_MSG_RESULT(not found) +$2]) +CPPFLAGS="$save_CPPFLAGS" +]) + diff --git a/benchmark/README b/benchmark/README new file mode 100644 index 00000000..788edeb1 --- /dev/null +++ b/benchmark/README @@ -0,0 +1,19 @@ +VIPS SMP benchmark +------------------ + +This is adapted from the system used to generate images for POD: + + http://cima.ng-london.org.uk/~john/POD + +Images from a 10k by 10k studio digital camera are colour processed, resized, +cropped and sharpened. + +This thing was originally processing images off a remote server over a 100mbit +network. No attempt was made to make it quick (there was no point): you +could make it a lot faster very easily. + +See + + http://www.vips.ecs.soton.ac.uk/index.php?title=Benchmarks + +for results. diff --git a/benchmark/benchmarkn.sh b/benchmark/benchmarkn.sh new file mode 100755 index 00000000..cb7aaf31 --- /dev/null +++ b/benchmark/benchmarkn.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +uname -a +gcc --version +vips --version + +# how large an image do you want to process? +# sample2.v is 290x442 pixels ... replicate this many times horizontally and +# vertically to get a highres image for the benchmark +tile=13 + +# how complex an operation do you want to run? +# this sets the number of copies of the benchmark we chain together: +# higher values run more slowly and are more likely to be CPU-bound +chain=1 + +echo building test image ... +echo "tile=$tile" +vips im_replicate sample2.v temp.v $tile $tile +if [ $? != 0 ]; then + echo "build of test image failed -- out of disc space?" + exit 1 +fi +echo -n "test image is" `header -f Xsize temp.v` +echo " by" `header -f Ysize temp.v` "pixels" + +echo "starting benchmark ..." +echo "chain=$chain" + +for cpus in 1 2 3 ; do + export IM_CONCURRENCY=$cpus + + echo IM_CONCURRENCY=$IM_CONCURRENCY + echo time -p vips im_benchmarkn temp.v temp2.v $chain + time -p vips im_benchmarkn temp.v temp2.v $chain + time -p vips im_benchmarkn temp.v temp2.v $chain + + if [ $? != 0 ]; then + echo "benchmark failed -- install problem?" + exit 1 + fi + + # find pixel average ... should be the same for all IM_CONCURRENCY settings + # or we have some kind of terrible bug + echo vips im_avg temp2.v + vips im_avg temp2.v +done diff --git a/benchmark/sample2.v b/benchmark/sample2.v new file mode 100644 index 00000000..2f0749d0 Binary files /dev/null and b/benchmark/sample2.v differ diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 00000000..97af3c92 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# set -x + +# a bunch of cleaning up ... make certain everything will be regenerated +rm -f Makefile Makefile.in aclocal.m4 +rm -rf autom4te.cache +rm -f config.* configure depcomp +rm -f install-sh intltool-* libtool ltmain.sh missing mkinstalldirs +rm -f stamp-* vipsCC-7.12.pc vips-7.12.spec vips-7.12.pc +rm -f python/vipsCC/*.cxx +rm -f python/vipsCC/VImage.h +rm -f python/vipsCC/VImage.py python/vipsCC/VError.py python/vipsCC/VMask.py python/vipsCC/Display.py +rm -f benchmark/temp* + +# some systems need libtoolize, some glibtoolize ... how annoying +echo testing for glibtoolize ... +if glibtoolize --version >/dev/null 2>&1; then + LIBTOOLIZE=glibtoolize + echo using glibtoolize +else + LIBTOOLIZE=libtoolize + echo using libtoolize +fi + +aclocal +glib-gettextize --force --copy +test -r aclocal.m4 && chmod u+w aclocal.m4 +intltoolize --copy --force --automake +autoconf +autoheader +$LIBTOOLIZE --copy --force --automake +automake --add-missing --copy + diff --git a/configure.in b/configure.in new file mode 100644 index 00000000..149ae783 --- /dev/null +++ b/configure.in @@ -0,0 +1,382 @@ +# Process this file with autoconf to produce a configure script. +AC_INIT(include/vips/colour.h) +AM_CONFIG_HEADER(config.h) + +# user-visible library versioning +IM_MAJOR_VERSION=7 +IM_MINOR_VERSION=12 +IM_MICRO_VERSION=5 +IM_VERSION=$IM_MAJOR_VERSION.$IM_MINOR_VERSION.$IM_MICRO_VERSION +IM_VERSION_STRING=$IM_VERSION-`date` + +VERSION=$IM_VERSION +PACKAGE=vips + +# libtool library versioning ... not user-visible (except as part of the +# library file name) and does not correspond to major/minor/micro above + +# rules: +# sources changed: increment revision +# interface changed: increment current, reset revision to 0 +# interface changes backwards compatible?: increment age +# interface changes not backwards compatible?: reset age to 0 + +LIBRARY_CURRENT=14 +LIBRARY_REVISION=2 +LIBRARY_AGE=2 + +AM_INIT_AUTOMAKE($PACKAGE,$VERSION) + +# patched into include/vips/version.h +AC_SUBST(IM_VERSION) +AC_SUBST(IM_VERSION_STRING) +AC_SUBST(IM_MAJOR_VERSION) +AC_SUBST(IM_MINOR_VERSION) +AC_SUBST(IM_MICRO_VERSION) + +# put into library name by libsrc/Makefile.am and libsrcCC/Makefile.am +AC_SUBST(LIBRARY_CURRENT) +AC_SUBST(LIBRARY_REVISION) +AC_SUBST(LIBRARY_AGE) + +AC_CANONICAL_HOST + +AC_MSG_CHECKING([for native Win32]) +case "$host" in + *-*-mingw*) + vips_os_win32=yes + ;; + *) + vips_os_win32=no + ;; +esac +AC_MSG_RESULT([$vips_os_win32]) + +if test x"$vips_os_win32" = "xyes"; then + AC_DEFINE(OS_WIN32,1,[native win32]) + + # makes gcc use win native alignment + VIPS_CFLAGS="-mms-bitfields $VIPS_CFLAGS" +fi + +# Cygwin/mingw need binary open to avoid CR/LF madness +# ... should be a better way to test for this +AC_MSG_CHECKING([for binary open needed]) +case "$host_os" in + cygwin* | mingw*) + vips_binary_open=yes + ;; + *) + vips_binary_open=no + ;; +esac +AC_MSG_RESULT([$vips_binary_open]) +if test x"$vips_binary_open" = "xyes"; then + AC_DEFINE(BINARY_OPEN,1,[define to open non-text files in binary mode]) +fi + +# we want largefile support, if possible +AC_SYS_LARGEFILE + +# Checks for programs. +AC_PROG_AWK +AC_PROG_CC +AC_PROG_CC_STDC +AC_C_CONST +AC_PROG_RANLIB +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_CXX +AM_WITH_DMALLOC + +# vips.c/im_guess_prefix.c need to know the exe suffix and (as a fallback) +# the install prefix +AC_DEFINE_UNQUOTED(IM_EXEEXT,"$EXEEXT",[extension for executable files]) +AC_DEFINE_UNQUOTED(IM_PREFIX,"$prefix",[configure-time install prefix]) + +# i18n +GETTEXT_PACKAGE=vips7 +AC_SUBST(GETTEXT_PACKAGE) +AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", + [The prefix for our gettext translation domains.]) +ALL_LINGUAS="en_GB malkovich" +AC_PROG_INTLTOOL +AM_GLIB_GNU_GETTEXT + +# Checks for libraries. + +# build list of pkg-config packages we used here +PACKAGES_USED="" + +# Checks for header files. +AC_HEADER_DIRENT +AC_HEADER_STDC +AC_CHECK_HEADERS([errno.h math.h fcntl.h limits.h stdlib.h string.h sys/file.h sys/ioctl.h sys/param.h sys/time.h sys/mman.h sys/types.h sys/stat.h unistd.h io.h direct.h windows.h]) + +# uncomment to change which libs we build +# AC_DISABLE_SHARED +# AC_DISABLE_STATIC +# couldn't get this working :-( maybe try again with the next libtool +AC_LIBTOOL_WIN32_DLL +AC_CHECK_TOOL(DLLWRAP, dllwrap) +AC_CHECK_TOOL(DLLTOOL, dlltool) +AC_CHECK_TOOL(OBJDUMP, objdump) +AC_CHECK_TOOL(RANLIB, ranlib) +AC_CHECK_TOOL(STRIP, strip) +AC_CHECK_TOOL(AR, ar) +AC_CHECK_TOOL(AS, as) +AC_CHECK_TOOL(LD, ld) +AC_PROVIDE(AC_LIBTOOL_WIN32_DLL) +AC_PROG_LIBTOOL + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_MODE_T +AC_TYPE_OFF_T +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_MEMCMP +AC_FUNC_MMAP +AC_FUNC_VPRINTF +AC_CHECK_FUNCS([getcwd gettimeofday getwd memset munmap putenv realpath strcasecmp strchr strcspn strdup strerror strrchr strspn vsnprintf realpath mkstemp mktemp random rand]) +AC_CHECK_LIB(m,cbrt,[AC_DEFINE(HAVE_CBRT,1,[have cbrt() in libm.])]) + +# have to have these +PKG_CHECK_MODULES(REQUIRED, glib-2.0 >= 2.6 gmodule-2.0 >= 2.4 libxml-2.0 gobject-2.0) +PACKAGES_USED="$PACKAGES_USED glib-2.0 gmodule-2.0 libxml-2.0 gobject-2.0" + +# option to eval without threads +AC_ARG_ENABLE(threads, AS_HELP_STRING([--enable-threads], [evaluate with threads (default: yes)])) + +if test "x$enable_threads" != "xno"; then + AC_DEFINE(HAVE_THREADS,1,[threaded evaluation]) + PKG_CHECK_MODULES(GTHREAD, gthread-2.0) + PACKAGES_USED="$PACKAGES_USED gthread-2.0" +fi + +# optional supporting libraries + +# we can wrap fftw3 and fftw2 ... but just look for fftw3, since we can do +# that with pkg-config +AC_ARG_WITH([fftw3], AS_HELP_STRING([--without-fftw3], [build without fftw3 (default: test)])) + +if test "x$with_fftw3" != "xno"; then + PKG_CHECK_MODULES(FFTW3, fftw3, + [AC_DEFINE(HAVE_FFTW3,1,[define if you have fftw3 installed.]) + PACKAGES_USED="$PACKAGES_USED fftw3"], + [AC_MSG_WARN([fftw3 not found; disabling fftw support])]) +fi + +# ImageMagic ... detect attribute iteration too +AC_ARG_WITH([magick], AS_HELP_STRING([--without-magick], [build without libMagic (default: test)])) + +if test "x$with_magick" != "xno"; then + PKG_CHECK_MODULES(MAGICK, ImageMagick, + [AC_DEFINE(HAVE_MAGICK,1,[define if you have libMagick installed.]) + PACKAGES_USED="$PACKAGES_USED ImageMagick"], + [AC_MSG_WARN([libMagick not found; disabling Magick support])]) +fi + +if test "x$with_magick" != "xno"; then + # we need ResetImageAttributeIterator() / GetNextImageAttribute() to get + # attrs, but that's 6.2+ I think ... test for them + save_LIBS=$LIBS + LIBS="$LIBS $MAGICK_LIBS" + AC_CHECK_FUNCS(GetNextImageAttribute, + AC_DEFINE(HAVE_MAGICK_ATTR,1,[define if your imagemagick has attribute support.])) + LIBS=$save_LIBS +fi + +# liboil +AC_ARG_WITH([liboil], AS_HELP_STRING([--without-liboil], [build without liboil +(default: test)])) + +if test "x$with_liboil" != "xno"; then + PKG_CHECK_MODULES(LIBOIL, liboil-0.3, + [AC_DEFINE(HAVE_LIBOIL,1,[define if you have liboil-0.3 installed.]) + PACKAGES_USED="$PACKAGES_USED liboil-0.3"], + [AC_MSG_WARN([liboil not found; disabling liboil support])]) +fi + +# lcms +AC_ARG_WITH([lcms], AS_HELP_STRING([--without-lcms], [build without lcms (default: test)])) + +if test "x$with_lcms" != "xno"; then + PKG_CHECK_MODULES(LCMS, lcms, + [AC_DEFINE(HAVE_LCMS,1,[define if you have lcms installed.]) + PACKAGES_USED="$PACKAGES_USED lcms"], + [AC_MSG_WARN([lcms not found; disabling lcms support])]) +fi + +# OpenEXR +AC_ARG_WITH([OpenEXR], AS_HELP_STRING([--without-OpenEXR], [build without OpenEXR (default: test)])) + +# require 1.2.2 since 1.2.1 has a broken ImfCloseTiledInputFile() +if test "x$with_OpenEXR" != "xno"; then + PKG_CHECK_MODULES(OPENEXR, OpenEXR >= 1.2.2, + [AC_DEFINE(HAVE_OPENEXR,1,[define if you have OpenEXR >=1.2.2 installed.]) + PACKAGES_USED="$PACKAGES_USED OpenEXR"], + [AC_MSG_WARN([OpenEXR not found; disabling OpenEXR support])]) +fi + +# pangoft2 +AC_ARG_WITH([pangoft2], AS_HELP_STRING([--without-pangoft2], [build without pangoft2 (default: test)])) + +if test "x$with_pangoft2" != "xno"; then + PKG_CHECK_MODULES(PANGOFT2, pangoft2, + [AC_DEFINE(HAVE_PANGOFT2,1,[define if you have pangoft2 installed.]) + PACKAGES_USED="$PACKAGES_USED pangoft2"], + [AC_MSG_WARN([pangoft2 not found; disabling pangoft2 support])]) +fi + +# hmm, these don't have .pc files on ubuntu 5.10, how odd +FIND_TIFF(,[AC_MSG_WARN([libtiff not found; disabling TIFF support])]) +FIND_ZIP(,[AC_MSG_WARN([libz not found; disabling ZIP support])]) +FIND_JPEG(,[AC_MSG_WARN([libjpeg not found; disabling JPEG support])]) + +# look for PNG with pkg-config ... fall back to our tester +PKG_CHECK_MODULES(PNG, libpng, + [AC_DEFINE(HAVE_PNG,1,[define if you have libpng installed.]) + PACKAGES_USED="$PACKAGES_USED libpng"], + [FIND_PNG(,[AC_MSG_WARN([libpng not found; disabling PNG support])])]) + +# libexif +AC_ARG_WITH([libexif], AS_HELP_STRING([--without-libexif], [build without libexif (default: test)])) + +if test "x$with_libexif" != "xno"; then + PKG_CHECK_MODULES(EXIF, libexif >= 0.6, + [AC_DEFINE(HAVE_EXIF,1,[define if you have libexif >= 0.6 installed.]) + PACKAGES_USED="$PACKAGES_USED libexif"], + [AC_MSG_WARN([libexif >= 0.6 not found; disabling exif support]) + with_libexif=no]) +fi + +# some libexif packages need include , some just +# how annoying +if test "x$with_libexif" != "xno"; then + # cppflags not cflags because we want the preproc to see the -I as well + save_CPPFLAGS=$CPPFLAGS + CPPFLAGS="$EXIF_CFLAGS $CPPFLAGS" + AC_CHECK_HEADER(exif-data.h, + AC_DEFINE(UNTAGGED_EXIF,1,[libexif includes don't need libexif prefix])) + CPPFLAGS=$save_CPPFLAGS +fi + +# Look for linux video +AC_CHECK_HEADER(linux/videodev.h, + AC_DEFINE(HAVE_VIDEODEV,1,[have video4linux 1]),[ + AC_MSG_WARN([linux/videodev.h not found; disabling Linux video support]) +]) + +# make python binding? +AC_ARG_WITH([python], AS_HELP_STRING([--without-python], [build without Python bindings (default: test)])) + +if test "x$with_python" != "xno"; then + AM_PATH_PYTHON(2.2,, + [with_python=no + AC_MSG_WARN([Python not found; disabling Python binding])]) +fi + +if test "x$with_python" != "xno"; then + AM_CHECK_PYTHON_HEADERS(, + [with_python=no + AC_MSG_WARN([Python headers not found])]) +fi + +# need SWIG too +if test "x$with_python" != "xno"; then + AC_CHECK_PROG(HAVE_SWIG, swig, [yes]) + + if test "x$HAVE_SWIG" != "xyes"; then + with_python=no + AC_MSG_WARN([SWIG not found; disabling Python binding]) + else + with_python=yes + fi +fi + +if test "x$with_python" = "xyes"; then + AM_CONDITIONAL(HAVE_PYTHON, true) +else + AM_CONDITIONAL(HAVE_PYTHON, false) +fi + +# Gather all up for VIPS_CFLAGS, VIPS_INCLUDES and VIPS_LIBS +VIPS_CFLAGS="$VIPS_CFLAGS $GTHREAD_CFLAGS $REQUIRED_CFLAGS $PANGOFT2_CFLAGS $FFTW3_CFLAGS $MAGICK_CFLAGS $PNG_CFLAGS $EXIF_CFLAGS $OPENEXR_CFLAGS $LIBOIL_CFLAGS" +VIPS_INCLUDES="$PNG_INCLUDES $TIFF_INCLUDES $ZIP_INCLUDES $JPEG_INCLUDES $FFTW_INCLUDES $LCMS_INCLUDES" +VIPS_LIBS="$MAGICK_LIBS $PNG_LIBS $TIFF_LIBS $ZIP_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $PANGOFT2_LIBS $FFTW3_LIBS $FFTW_LIBS $LCMS_LIBS $LIBOIL_LIBS $OPENEXR_LIBS $EXIF_LIBS -lm" + +AC_SUBST(VIPS_CFLAGS) +AC_SUBST(VIPS_INCLUDES) +AC_SUBST(VIPS_LIBS) +AC_SUBST(PACKAGES_USED) + +AC_OUTPUT([ + vips-7.12.pc + vipsCC-7.12.pc + vips-7.12.spec + Makefile + include/vips/version.h + include/Makefile + include/vips/Makefile + libsrc/Makefile + libsrc/acquire/Makefile + libsrc/arithmetic/Makefile + libsrc/boolean/Makefile + libsrc/colour/Makefile + libsrc/conversion/Makefile + libsrc/convolution/Makefile + libsrc/freq_filt/Makefile + libsrc/histograms_lut/Makefile + libsrc/inplace/Makefile + libsrc/iofuncs/Makefile + libsrc/matrix/Makefile + libsrc/morphology/Makefile + libsrc/mosaicing/Makefile + libsrc/other/Makefile + libsrc/relational/Makefile + libsrc/video/Makefile + libsrc/acquire/man3/Makefile + libsrc/arithmetic/man3/Makefile + libsrc/boolean/man3/Makefile + libsrc/colour/man3/Makefile + libsrc/conversion/man3/Makefile + libsrc/convolution/man3/Makefile + libsrc/freq_filt/man3/Makefile + libsrc/histograms_lut/man3/Makefile + libsrc/inplace/man3/Makefile + libsrc/iofuncs/man3/Makefile + libsrc/matrix/man3/Makefile + libsrc/morphology/man3/Makefile + libsrc/mosaicing/man3/Makefile + libsrc/other/man3/Makefile + libsrc/relational/man3/Makefile + libsrc/video/man3/Makefile + libsrcCC/Makefile + src/Makefile + src/iofuncs/Makefile + src/mosaicing/Makefile + src/other/Makefile + src/scripts/Makefile + src/scripts/batch_crop + src/scripts/batch_image_convert + src/scripts/batch_rubber_sheet + src/scripts/light_correct + src/scripts/shrink_width + src/iofuncs/man1/Makefile + src/other/man1/Makefile + src/scripts/man1/Makefile + contrib/Makefile + contrib/vips2dj/Makefile + contrib/vips2dj/share/Makefile + contrib/vips2dj/share/vips2dj/Makefile + contrib/vips2dj/share/vips2dj/lab/Makefile + contrib/vips2dj/share/vips2dj/cmyk/Makefile + contrib/vips2dj/share/vips2dj/mono/Makefile + contrib/vdump/Makefile + contrib/mitsub/Makefile + python/Makefile + python/vipsCC/Makefile + po/Makefile.in +]) diff --git a/contrib/Makefile.am b/contrib/Makefile.am new file mode 100644 index 00000000..288e4702 --- /dev/null +++ b/contrib/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = \ + vips2dj \ + mitsub \ + vdump diff --git a/contrib/mitsub/Makefile.am b/contrib/mitsub/Makefile.am new file mode 100644 index 00000000..79ccae56 --- /dev/null +++ b/contrib/mitsub/Makefile.am @@ -0,0 +1,7 @@ +bin_PROGRAMS = mitsub + +mitsub_SOURCES = mitsub.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +AM_LDFLAGS = @LDFLAGS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libsrc/libvips.la @VIPS_LIBS@ diff --git a/contrib/mitsub/mitsub.c b/contrib/mitsub/mitsub.c new file mode 100644 index 00000000..8e3cb2ed --- /dev/null +++ b/contrib/mitsub/mitsub.c @@ -0,0 +1,491 @@ +/* + * Version which sents raw data to the printer, which enlarges and centers. + * Works for monochrome and four band IM_TYPE_CMYK images. Uses putc instead of + * fprintf. Sents data straight to the printer. If enlargement, it is full and + * x and y ratios are the same (aspect ratio is not changed) + * + * Helene Chahine, July 95 + * + * JC 4/8/95 + * - small tidies and bug fixes + * - now does 1, 3 and 4 band + * - resets printer after use + * JC 1/9/95 + * - colour reverse mode added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#define NBPRINT 1 /* Maximun 15 copies */ +#define VMAX 2904 /* Number of horizontal pixels */ +#define HMAX 2368 /* Number of vertical pixels */ + +int +main( int argc, char *argv[] ) +{ + int enlar = 0; + int center = 0; + int rev = 0; + + IMAGE *vips; + FILE *out; + int n1, n2; + int x, y; + int xsize, ysize; + int c; + PEL *p; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + while( --argc > 0 && (*++argv)[0] == '-' ) + while( (c = *++argv[0]) ) + switch( c ) { + case 'e': + enlar = 1; + break; + + case 'c': + center = 1; + break; + + case 'r': + rev = 1; + break; + + default: + error_exit( "mitsub: illegal option %c", c ); + } + + if( argc != 2 ) + error_exit( "usage: mitsub [-ecr] vipsfile mitfile\n" + "where:\n" + "\tvipsfile may be 1, 3 or 4 bands for mono, IM_TYPE_RGB or " + "IM_TYPE_CMYK printing\n" + "\tmitfile may be '-', meaning send to stdout\n" + "\t-e means enlarge to fill page\n" + "\t-c means centre within page\n" + "\t-r means reverse black/white\n" + "\tNOTE: data is sent raw, with 0 == no ink - all correction is up to " + "you\n" + "example:\n" + "\t%% mitsub -ec fred.v - > /dev/bpp0" ); + + if( !(vips = im_open( argv[0], "r" )) ) + error_exit( "mitsub: unable to open \"%s\" for input", + argv[0] ); + + if( strcmp( argv[1], "-" ) == 0 ) + out = stdout; + else if( !(out = fopen( argv[1], "w" )) ) + error_exit( "mitsub: unable to open \"%s\" for output", + argv[1] ); + + if( vips->Coding != IM_CODING_NONE || vips->BandFmt != IM_BANDFMT_UCHAR ) + error_exit( "mitsub: uncoded uchar only" ); + if( vips->Bands != 1 && vips->Bands != 3 && vips->Bands != 4 ) + error_exit( "mitsub: 1,3 and 4 band images only" ); + + /* Set xsize and ysize. + */ + if( vips->Xsize <= vips->Ysize ) { + xsize = vips->Xsize; + ysize = vips->Ysize; + } + else { + im_diagnostics( "mitsub: rotating ..." ); + xsize = vips->Ysize; + ysize = vips->Xsize; + } + + /* Shrink if image is too big. + */ + if( xsize > HMAX || ysize > VMAX ) { + double x_factor = HMAX/xsize; + double y_factor = VMAX/ysize; + double factor = IM_MAX( x_factor, y_factor ); + IMAGE *sh = im_open( "shrink", "t" ); + + im_diagnostics( "mitsub: shrinking by %g ...", factor ); + if( !sh || im_shrink( vips, sh, factor, factor ) ) + error_exit( "mitsub: shrink failed" ); + + vips = sh; + enlar = 0; + } + + /* On line command and buffer clear. + */ + putc( 0x11, out ); + putc( 0x1b, out ); + putc( 'Z', out ); + + /* Memory clear. + */ + putc( 0x1b, out ); + putc( 'Z', out ); + + /* Media size. (Size A4) + */ + putc( 0x1b, out ); + putc( '#', out ); + putc( 'P', out ); + putc( '0', out ); + + /* Enlargement. + */ + if( enlar ) { + double rh, rv; + int n, m; + + /* Enlarge method: ('0'=simple enlargement, + * '1'=linear enlargement) + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'O', out ); + putc( '1', out ); + + rh = HMAX/(double) xsize; + rv = VMAX/(double) ysize; + if( rh > 8 || rv > 8 ) { + n = 8; + m = 1; + } + else if( rh > rv ) { + double fact = VMAX/255; + + n = 255; + m = (int) ysize/fact + 1; + } + else { + double fact = HMAX/255; + + n = 255; + m = (int) xsize/fact + 1; + } + im_diagnostics( "mitsub: enlarging by %g ...", (double) n/m ); + + /* Horizontal enlarge. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'P', out ); + putc( n, out ); + putc( m, out ); + + /* Vertical enlarge. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'Q', out ); + putc( n, out ); + putc( m, out ); + + } + else { + /* No enlargement. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'O', out ); + putc( '1', out ); + putc( 0x1b, out ); + putc( '&', out ); + putc( 'P', out ); + putc( 1, out ); + putc( 1, out ); + putc( 0x1b, out ); + putc( '&', out ); + putc( 'Q', out ); + putc( 1, out ); + putc( 1, out ); + } + + if( rev ) { + /* Colour reversing. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'W', out ); + putc( '2', out ); + } + else { + /* No reverse. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'W', out ); + putc( '0', out ); + } + + /* Number of copies. + */ + putc( 0x1b, out ); + putc( '#', out ); + putc( 'C', out ); + putc( NBPRINT, out ); + + /* Left margin. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'S', out ); + putc( 0, out ); + + /* Top margin. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'T', out ); + putc( 0, out ); + + /* Centering. ('1' = centering available, '0'= no centering). + */ + if( center ) { + im_diagnostics( "mitsub: centering ..." ); + putc( 0x1b, out ); + putc( '&', out ); + putc( 'C', out ); + putc( '1', out ); + } + else { + /* No centering. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'C', out ); + putc( '0', out ); + } + + /* Transfer format = pixel order method for colour, = frame order + * method for monochrome. + */ + switch( vips->Bands ) { + case 3: + case 4: + putc( 0x1b, out ); + putc( '&', out ); + putc( 'A', out ); + putc( '2', out ); + break; + + case 1: + putc( 0x1b, out ); + putc( '&', out ); + putc( 'A', out ); + putc( '0', out ); + break; + + default: + error_exit( "internal error" ); + /*NOTREACHED*/ + } + + /* Colour specification. + */ + switch( vips->Bands ) { + case 4: + case 1: + /* IM_TYPE_CMYK. For mono, send just K. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'I', out ); + putc( '2', out ); + break; + + case 3: + /* IM_TYPE_RGB. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'I', out ); + putc( '0', out ); + break; + + default: + error_exit( "internal error" ); + /*NOTREACHED*/ + } + + /* Gray scale level. + */ + putc( 0x1b, out ); + putc( '#', out ); + putc( 'L', out ); + putc( 8, out ); + + /* Rotation. + */ + if( vips->Xsize <= vips->Ysize ) { + putc( 0x1b, out ); + putc( '#', out ); + putc( 'R', out ); + putc( '0', out ); + } + else { + putc( 0x1b, out ); + putc( '#', out ); + putc( 'R', out ); + putc( '1', out ); + } + + /* Horizontal shift. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'J', out ); + putc( 0, out ); + putc( 0, out ); + + /* Vertical shift. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'K', out ); + putc( 0, out ); + putc( 0, out ); + + /* Number of horizontal pixels. + */ + n1 = vips->Xsize >> 8; + n2 = vips->Xsize & 0xff; + putc( 0x1b, out ); + putc( '&', out ); + putc( 'H', out ); + putc( n1, out ); + putc( n2, out ); + + /* Number of vertical pixels. + */ + n1 = vips->Ysize >> 8; + n2 = vips->Ysize & 0xff; + putc( 0x1b, out ); + putc( '&', out ); + putc( 'V', out ); + putc( n1, out ); + putc( n2, out ); + + /* Transfer colour (for monochrome image only). + */ + if( vips->Bands == 1 ) { + putc( 0x1b, out ); + putc( 'C', out ); + putc( '4', out ); + } + + /* Image data transfer. Image must be sent as YMCK. + */ + putc( 0x1b, out ); + putc( 'O', out ); + if( im_incheck( vips ) ) + error_exit( "mitsub: unable to read image data" ); + p = (PEL *) vips->data; + switch( vips->Bands ) { + case 4: + im_diagnostics( "mitsub: sending IM_TYPE_CMYK ..." ); + for( y = 0; y < vips->Ysize; y++ ) + for( x = 0; x < vips->Xsize; x++ ) { + putc( p[2], out ); + putc( p[1], out ); + putc( p[0], out ); + putc( p[3], out ); + p += 4; + } + break; + + case 3: + im_diagnostics( "mitsub: sending IM_TYPE_RGB ..." ); + for( y = 0; y < vips->Ysize; y++ ) + for( x = 0; x < vips->Xsize; x++ ) { + putc( p[0], out ); + putc( p[1], out ); + putc( p[2], out ); + p += 3; + } + break; + + case 1: + im_diagnostics( "mitsub: sending K ..." ); + for( y = 0; y < vips->Ysize; y++ ) + for( x = 0; x < vips->Xsize; x++ ) + putc( *p++, out ); + break; + } + + /* Form feed. Page end. + */ + putc( 0x0c, out ); + + /* Now try to reset printer to default settings. + * + * No enlargement. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'O', out ); + putc( '1', out ); + putc( 0x1b, out ); + putc( '&', out ); + putc( 'P', out ); + putc( 1, out ); + putc( 1, out ); + putc( 0x1b, out ); + putc( '&', out ); + putc( 'Q', out ); + putc( 1, out ); + putc( 1, out ); + + /* No centering. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'C', out ); + putc( '0', out ); + + /* No colour reverse. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'W', out ); + putc( '0', out ); + + return( 0 ); +} + diff --git a/contrib/vdump/Makefile.am b/contrib/vdump/Makefile.am new file mode 100644 index 00000000..49113dd0 --- /dev/null +++ b/contrib/vdump/Makefile.am @@ -0,0 +1,15 @@ +bin_PROGRAMS = vdump + +vdump_SOURCES = vdump.c + +man_MANS = \ + vdump.1 + +pkgdata_DATA = \ + vdump.pro + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +AM_LDFLAGS = @LDFLAGS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libsrc/libvips.la @VIPS_LIBS@ + +EXTRA_DIST = $(pkgdata_DATA) $(man_MANS) diff --git a/contrib/vdump/vdump.1 b/contrib/vdump/vdump.1 new file mode 100644 index 00000000..2c84cd82 --- /dev/null +++ b/contrib/vdump/vdump.1 @@ -0,0 +1,69 @@ +.TH VDUMP 1 "July 1990" +.SH NAME +vdump \- convert VIPS image files to Postscript +.SH SYNOPSIS +.B vdump +[ +.B \-slpDa +] +.IR filename +.SH DESCRIPTION +.B vdump +turns the vasari format file in its argument to encapsulated PostScript on its +stadard output. The result can be +either sent directly to a printer or included in any document processor which +supports encapsulated PostScript diagrams - eg. WordPerfect, Tex etc. For +example: +.IP +.B +example% vdump campin.v | lpr -Plaser +.LP +or +.IP +.B +example% vdump -l -s4 -D campin.v > campin.PS +.LP +.br +.B vdump +normally outputs portrait, you can force output to portrait or landscape with the +.BR \-p +or +.BR \-l +flags. + +.br +The +.BR \-a +flag makes vdump select +either portrait or landscape orientation so as to get +the largest possible image onto an A4 sheet. + +.br +.B vdump +will dump one or three band unsigned char images only. +.SH OPTIONS +.TP +.B \-p +Force portrait output. +.TP +.B \-l +Force landscape output. +.TP +.B \-a +Automatically select portrait/landscape. +.TP +.B \-s +Set a sub-sampling factor (default 1). +.BR \-s1 +will not sub-sample at all, +.BR \-s4 +will reduce by a factor of 4. +.TP +.B \-D +Produce output suitable for including in documents. This option +simply supresses the generation of a showpage command. +.SH SEE\ ALSO +ip(1), vips2dj(1) +.SH COPYRIGHT +.br +1990: J. Cupitt, National Gallery diff --git a/contrib/vdump/vdump.c b/contrib/vdump/vdump.c new file mode 100644 index 00000000..e79ea385 --- /dev/null +++ b/contrib/vdump/vdump.c @@ -0,0 +1,399 @@ +/* This is incredibly primitive and annoying. + * + * Turn a VASARI format file into PostScript. Do simple subsampling of + * images to get the size down .. no point in sending anything much larger + * than 100x100 to the laserwriter if it's going in a document. The output + * conforms to PS-Adobe-2.0 EPSF-2.0, I think. + * + * Options: + * -s Average an nxn area in the image for each pixel in the output. + * This reduces the size of the files significantly (obviously). + * Default 1. + * -l Force landscape output + * -p Force portrait output (default) + * -a Automatic choice of portrait/landscape + * Nasty: as we have to include a %%BoundingBox: line, we can't + * size the image to fit comfortably in whatever size paper this + * PostScript printer takes. + * -D Supress generation of showpage. Sometimes necessary if you + * want to include the PS file in a document. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#define USAGE "usage: [-s -alpD] vasari_format_file" +#define PROLOGUE "vdump.pro" +#define PAPER_WIDTH (8.25*72.0) /* Paper size .. A4 */ +#define PAPER_HEIGHT (11.75*72.0) +#define PAPER_MARGIN (1.0*72.0) /* Margin we leave around the edge */ +#define PRINT_WIDTH (PAPER_WIDTH - 2.0*PAPER_MARGIN) +#define PRINT_HEIGHT (PAPER_HEIGHT - 2.0*PAPER_MARGIN) +#define PRINT_RATIO (PRINT_WIDTH / PRINT_HEIGHT) + +/* Useful: a pixel. We mmap the file, then cast the pointer to the image to + * a pointer to one of these things. + */ +struct pixel { + unsigned char p_red; + unsigned char p_green; + unsigned char p_blue; +}; + +/* A monochrome pixel. + */ +struct mpixel { + unsigned char p_val; +}; + +enum output_format { + LANDSCAPE, /* Rotated by 90 degrees */ + PORTRAIT, /* Vertical */ + AUTOMATIC /* Whichever fits best */ +}; + +static const char *our_name; /* Name of this prog */ +static char *file_name; /* Name of file we dump */ +static int print_on = 1; /* Generate showpage */ + +/* Copy between two fds + */ +static void +copy_file( from, to ) +FILE *from, *to; +{ int ch; + + while( (ch = getc( from )) != EOF ) + putc( ch, to ); +} + +/* Send a file to stdout. Used to transmit the prelude. + */ +static int +transmit_file( name ) +char *name; +{ const char *prefix; + char buf[PATH_MAX]; + FILE *fp; + + if( !(prefix = im_guess_prefix( our_name, "VIPSHOME" )) ) + error_exit( "VIPSHOME not defined" ); + im_snprintf( buf, PATH_MAX, "%s/share/%s/%s", prefix, PACKAGE, name ); + + /* Send it! + */ + if( !(fp = fopen( buf, "r" )) ) + error_exit( "can't find %s", name ); + copy_file( fp, stdout ); + fclose( fp ); + + return( -1 ); +} + +/* Encode a colour VASARI file as mono hex bytes. Scale down by a factor of + * s. We scale by averaging regions of sxs pixels .. is this the best way? + * works ok for our laserwriter anyway. We lose incomplete regions down the RH + * side and across the bottom. + */ +static void +encode_colour( im, s, data ) +IMAGE *im; +int s; +struct pixel *data; +{ int p = 35; + int x, y; + int i, j; + + /* Scan across and down. Make sure we chop off those incomplete + * regions on the RH side and across the bottom. + */ + for( y = 0; y <= im->Ysize - s; y += s ) + for( x = 0; x <= im->Xsize - s; x += s ) { + int col = 0; + struct pixel *rs = &data[y * im->Xsize + x]; + + /* Now average the region. We monochromise each pixel + * and add it to the running total. + */ + for( i = 0; i < s; i++ ) { + struct pixel *d = &rs[i * im->Xsize]; + + for( j = 0; j < s; j++ ) { + col += (int) (d->p_red + d->p_green + + d->p_blue) / 3; + d++; + } + } + col /= s*s; + + /* Output the averaged pixel. + */ + printf( "%02x", col ); + if( !p-- ) { + printf( "\n" ); + p = 35; + } + } + printf( "\n" ); +} + +/* Encode a mono VASARI file as hex bytes. Scale down by a factor of + * s. We scale by averaging regions of sxs pixels .. is this the best way? + * works ok for our laserwriter anyway. We lose incomplete regions down the RH + * side and across the bottom. + */ +static void +encode_mono( im, s, data ) +IMAGE *im; +int s; +struct mpixel *data; +{ int p = 35; + int x, y; + int i, j; + + /* Scan across and down. Make sure we chop off those incomplete + * regions on the RH side and across the bottom. + */ + for( y = 0; y <= im->Ysize - s; y += s ) + for( x = 0; x <= im->Xsize - s; x += s ) { + int col = 0; + struct mpixel *rs = &data[y * im->Xsize + x]; + + /* Now average the region. + */ + for( i = 0; i < s; i++ ) { + struct mpixel *d = &rs[i * im->Xsize]; + + for( j = 0; j < s; j++ ) + col += d++->p_val; + } + col /= s*s; + + /* Output the averaged pixel. + */ + printf( "%02x", col ); + if( !p-- ) { printf( "\n" ); p = 35; } + } + printf( "\n" ); +} + +/* Print the image. Work out the orientation, print the prologue, then call + * one of the dumps above to do the image. + */ +static void +dump( im, format, scale ) +IMAGE *im; +enum output_format format; +int scale; +{ float r, width, height, xstart, ystart; + + /* Fix orientation, then set our origin and output size. Four cases .. + * can any of these be combined? Perhaps not. + */ + r = (float) im->Xsize / im->Ysize; + if( format == AUTOMATIC ) { + if( im->Xsize > im->Ysize ) + format = LANDSCAPE; + else + format = PORTRAIT; + } + + if( format == PORTRAIT ) { + /* Is it going to be smaller than the paper vertically or + * horizontally? + */ + if( r > PRINT_RATIO ) { + /* It's too wide. We make it as large as possible + * horizontally, then center it vertically. + */ + width = PRINT_WIDTH; + height = PRINT_WIDTH / r; + xstart = PAPER_MARGIN; + ystart = (PRINT_HEIGHT - height) / 2.0 + PAPER_MARGIN; + } + else { + /* Too high. Make as large as possible vertically, + * then center it horizontally. + */ + height = PRINT_HEIGHT; + width = PRINT_HEIGHT * r; + ystart = PAPER_MARGIN; + xstart = (PRINT_WIDTH - width) / 2.0 + PAPER_MARGIN; + } + } + else { + /* Do a landscape picture. Will we run out of space + * horizontally or vertically? + */ + if( 1.0 / r < PRINT_RATIO ) { + /* Very wide indeed! Fit it horizontally, then center + * it vertically. + */ + height = PRINT_HEIGHT; + width = PRINT_HEIGHT / r; + ystart = PAPER_MARGIN; + xstart = (PRINT_WIDTH - width) / 2.0 + PAPER_MARGIN; + } + else { + /* Too tall. Make as large as possible vertically, + * then center it horizontally. + */ + width = PRINT_WIDTH; + height = PRINT_WIDTH * r; + xstart = PAPER_MARGIN; + ystart = (PRINT_HEIGHT - height) / 2.0 + PAPER_MARGIN; + } + } + + /* Print header. + */ + printf( "%%!PS-Adobe-2.0 EPSF-2.0\n" ); + printf( "%%%%BoundingBox: %d %d %d %d\n", (int) xstart, (int) ystart, + (int) (width + xstart), (int) (height + ystart) ); + printf( "%%%%Title: %s\n", file_name ); + printf( "%%%%Creator: %s\n", our_name ); + + /* Print prologue. + */ + transmit_file( PROLOGUE ); + + /* Print position, scale and rotation. Print size in pixels and call + * doimage. + */ + if( format == LANDSCAPE ) + printf( "%d %d translate\n", + (int) (xstart + width), (int) ystart ); + else + printf( "%d %d translate\n", (int) xstart, (int) ystart ); + printf( "%d %d scale\n", (int) width, (int) height ); + if( format == LANDSCAPE ) + printf( "90 rotate\n" ); + printf( "%d %d 8 doimage\n", + (int) (im->Xsize / scale), (int) (im->Ysize / scale) ); + + /* Print body of file. + */ + if( im->Bands == 3 ) + encode_colour( im, scale, (struct pixel *) im->data ); + else + encode_mono( im, scale, (struct mpixel *) im->data ); + + /* Print trailer. + */ + if( print_on ) + printf( "showpage\n" ); + printf( "%%%%EndDocument\n" ); +} + +/* Start here! + */ +int +main( argc, argv ) +int argc; +char **argv; +{ int scale = 1; + enum output_format format = PORTRAIT; + IMAGE *im = NULL; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + our_name = *argv; + + /* Decode args .. just look for file names and our three options. + */ + while( --argc ) + if( *argv[argc] == '-' ) + switch( argv[argc][1] ) { + case 's': + if( sscanf( argv[argc] + 2, + "%d", &scale ) != 1 ) + error_exit( USAGE ); + break; + + case 'l': + format = LANDSCAPE; + break; + + case 'p': + format = PORTRAIT; + break; + + case 'a': + format = AUTOMATIC; + break; + + case 'D': + print_on = 0; + break; + + default: + error_exit( USAGE ); + break; + } + else { + /* Try to open the file. If we have previously opened, + * then flag an error. + */ + if( im != NULL ) + error_exit( USAGE ); + file_name = argv[argc]; + if( !(im = im_open( file_name, "r" )) ) + error_exit( "unable to open %s", file_name ); + } + if( im == NULL ) error_exit( USAGE ); + + /* Check it for suitability. We can print colour + * or monochrome pictures. + */ + if( im->Coding != IM_CODING_NONE ) + error_exit( "cannot print compressed pictures" ); + if( !( + (im->Bands == 3 && im->Bbits == 8 && + im->BandFmt == IM_BANDFMT_UCHAR) || + (im->Bands == 1 && im->Bbits == 8 && + im->BandFmt == IM_BANDFMT_UCHAR) + ) ) + error_exit( "can only print mono or colour images" ); + if( im_incheck( im ) ) + error_exit( "unable to get pixels" ); + + dump( im, format, scale); + + return( 0 ); +} diff --git a/contrib/vdump/vdump.pro b/contrib/vdump/vdump.pro new file mode 100644 index 00000000..16e0a771 --- /dev/null +++ b/contrib/vdump/vdump.pro @@ -0,0 +1,25 @@ +%%Pages: 1 +%%Creator: vdump +%%EndComments +%%BeginDocument: vdump +/doimage { + /b exch def /m exch def /n exch def + /pix n string def + n m b [n 0 0 m neg 0 m] + { currentfile pix readhexstring pop } + image +} def +/spotsize { + /perinch exch def + currentscreen 3 -1 roll + pop perinch + 3 1 roll setscreen +} def +/invert { + /curtran currenttransfer cvlit def + /newtran curtran length 3 add array def + newtran 0 {1 exch sub} putinterval + newtran 3 curtran putinterval + newtran cvx settransfer +} def +80 spotsize diff --git a/contrib/vips2dj/Makefile.am b/contrib/vips2dj/Makefile.am new file mode 100644 index 00000000..7c982e3c --- /dev/null +++ b/contrib/vips2dj/Makefile.am @@ -0,0 +1,17 @@ +SUBDIRS = share + +bin_PROGRAMS = \ + vips2dj + +vips2dj_DEPENDENCIES = vips2dj.h + +vips2dj_SOURCES = \ + vips2ah.c \ + vips2dj.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +AM_LDFLAGS = @LDFLAGS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libsrc/libvips.la @VIPS_LIBS@ + +EXTRA_DIST = ${vips2dj_DEPENDENCIES} + diff --git a/contrib/vips2dj/share/Makefile.am b/contrib/vips2dj/share/Makefile.am new file mode 100644 index 00000000..23636af7 --- /dev/null +++ b/contrib/vips2dj/share/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = vips2dj + diff --git a/contrib/vips2dj/share/vips2dj/Makefile.am b/contrib/vips2dj/share/vips2dj/Makefile.am new file mode 100644 index 00000000..e492c393 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = lab cmyk mono + diff --git a/contrib/vips2dj/share/vips2dj/cmyk/Makefile.am b/contrib/vips2dj/share/vips2dj/cmyk/Makefile.am new file mode 100644 index 00000000..22acd499 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/Makefile.am @@ -0,0 +1,14 @@ +vips2djcmykpsbitsdir = $(datadir)/vips/vips2dj/cmyk + +vips2djcmykpsbits_DATA = \ + head1 \ + head2 \ + head3 \ + head4 \ + head5 \ + head6 + +install-exec-hook: + $(mkinstalldirs) $(DESTDIR)$(vips2djcmykpsbitsdir) + +EXTRA_DIST = $(vips2djcmykpsbits_DATA) diff --git a/contrib/vips2dj/share/vips2dj/cmyk/head1 b/contrib/vips2dj/share/vips2dj/cmyk/head1 new file mode 100644 index 00000000..f77a2336 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/head1 @@ -0,0 +1,686 @@ +%!PS-Adobe-3.0 +%%Title: (micro_65_macbeth.tif) +%%Creator: (Adobe\250 Photoshop\250 6.0: AdobePS 8.7.0) +%%CreationDate: (2:17 pm Monday, April 29, 2002) +%%For: (FOTOG4) +%%Routing: (mailto:Colin.White@ng-london.org.uk) +%%Pages: 1 +%%DocumentFonts: +%%DocumentNeededResources: +%%DocumentSuppliedResources: +%%DocumentData: Clean7Bit +%%PageOrder: Ascend +%%Orientation: Portrait +%%DocumentMedia: (Default) 612 792 0 () () +%RBINumCopies: 1 +%RBINupNess: 1 1 +%ADO_ImageableArea: 30 33 582 761 +%RBIDocumentSuppliedFonts: +[{ +%%BeginPluginPS: SetTime +/HPDict /ProcSet findresource /SetTime get (20020429140835) exch exec +%%EndPluginPS +}stopped cleartomark +[{ +%%BeginPluginPS: HPJobname +/HPDict /ProcSet findresource /SetJobName get (micro_65_macbeth_tif) exch exec +%%EndPluginPS +}stopped cleartomark +%%EndComments +%%BeginDefaults +%%ViewingOrientation: 1 0 0 1 +%%EndDefaults +userdict/dscInfo 5 dict dup begin +/Title(micro_65_macbeth.tif)def +/Creator(Adobe\250 Photoshop\250 6.0: AdobePS 8.7.0)def +/CreationDate(2:17 pm Monday, April 29, 2002)def +/For(FOTOG4)def +/Pages 1 def +end put +%%BeginProlog +/md 178 dict def md begin/currentpacking where {pop /sc_oldpacking currentpacking def true setpacking}if +%%BeginFile: lw8_feature-1.01 +%%Copyright: Copyright 1990-1999 Adobe Systems Incorporated and Apple Computer Incorporated. All Rights Reserved. +/bd{bind def}bind def +/ld{load def}bd +/xs{exch store}bd +/Z{0 def}bd +/T true def +/F false def +/level2 +/languagelevel where +{ +pop languagelevel 2 ge +}{ +F +}ifelse +def +/odictstk Z +/oopstk Z +/fcl +{ +count oopstk sub dup 0 gt +{ +{pop}repeat +}{ +pop +}ifelse +countdictstack odictstk sub dup 0 gt +{ +{end}repeat +}{ +pop +}ifelse +}bd +/sfcl2 +{ +/odictstk countdictstack store +count/oopstk xs +}bd +/efcl2 +{ +stopped{$error/newerror F put}if +fcl +}bd +/noload Z +/startnoload +{ +{/noload save store}if +}bd +/endnoload +{ +{noload restore}if +}bd +/setcopies{ +level2 +{ +1 dict begin/NumCopies exch def currentdict end setpagedevice +}{ +userdict/#copies 3 -1 roll put +}ifelse +}def +level2 startnoload +/ststpgdev{}def +/dopgdev{}def +/stpgdev{}def +/buf Z +/didstop T def +/sfcl +{ +/didstop T store +/odictstk countdictstack store +count/oopstk xs +currentfile cvx stopped +{ +$error/newerror F put +didstop +{ +save/didstop xs +/buf vmstatus exch sub exch pop dup 0 lt{pop 0}if +dup 64000 gt{pop 64000}if string store +{ +currentfile buf readline +{ +(}efcl)eq{exit}if +}{ +/UnexpectedEOF errordict/rangecheck get exec +}ifelse +}loop +didstop restore +}if +}if +fcl +}bd +/efcl +{ +/didstop F store +exec +stop +}bd +level2 endnoload level2 not startnoload +/setpagedevice where{pop/realstpgdev/setpagedevice ld}if +/SC_topddict Z +/SC_spdict Z +/$spusrdict F def +/dopgdev +{ +userdict/setpagedevice undef +$spusrdict +{ +userdict/setpagedevice/realstpgdev load put +/$spusrdict F store +}if +SC_topddict realstpgdev +}bd +/stpgdev +{ +SC_topddict dup 3 -1 roll +{ +SC_spdict 2 index known +{ +SC_spdict 2 index get +dup 3 -1 roll +{ +put dup +}forall +pop put dup +}{ +put dup +}ifelse +}forall +pop pop +}bd +/ststpgdev +{ +/setpagedevice where +{ +userdict eq +{ +/$spusrdict T store +}if +}if +userdict/setpagedevice/stpgdev load put +/SC_topddict 0 dict store +/SC_spdict 3 dict begin +/InputAttributes 0 dict def +/Policies 0 dict def +/OutputAttributes 0 dict def +currentdict +end +store +}def +/sfcl/sfcl2 ld +/efcl/efcl2 ld +level2 not endnoload +%%EndFile +%%BeginFile: lw8_basic-4.0 +/xdf{exch def}bd +/:L/lineto +/lw/setlinewidth +/:M/moveto +/rl/rlineto +/rm/rmoveto +/:C/curveto +/:T/translate +/:K/closepath +/:mf/makefont +/gS/gsave +/gR/grestore +/np/newpath +12{ld}repeat +/framewidth -1 def +/QDframwid -1 def +/numframes Z +/mTS matrix def +/$m matrix def +/av 87 def +/por T def +/normland F def +/psb-nosave{}def +/pse-nosave{}def +/us Z +/psb{/us save store}bd +/pse{us restore}bd +/level3 +/languagelevel where +{ +pop languagelevel 3 ge +}{ +F +}ifelse +def +level2 startnoload +/setjob +{ +statusdict/jobname 3 -1 roll put +}bd +/devg/DeviceGray def +/devr/DeviceRGB def +/devc/DeviceCMYK def +level2 endnoload level2 not startnoload +/setjob +{ +1 dict begin/JobName xdf currentdict end setuserparams +}bd +/devg[/DeviceGray]def +/devr[/DeviceRGB]def +/devc[/DeviceCMYK]def +level2 not endnoload +/pm Z +/mT Z +/sD Z +/mTSsetup{ +mT $m currentmatrix mTS concatmatrix pop +}bd +/pmSVsetup{ +/pm save store +}bd +/initializepage +{ +mT concat +}bd +/endp +{ +pm restore +}bd +/adjRect +{ +dup 2 mul 6 2 roll +4 index sub exch 5 -1 roll sub exch +4 2 roll +4 index add exch 5 -1 roll add exch +4 2 roll +}bd +/frame1up +{ +gS +mTS setmatrix +QDframwid lw +/setstrokeadjust where{pop T setstrokeadjust}if +clippath pathbbox +2 index sub exch +3 index sub exch +currentlinewidth framewidth mul +adjRect +numframes dup 0 lt{pop 0}if +{ +4 copy +rS +currentlinewidth framewidth +mul 4 mul +adjRect +}repeat +pop pop pop pop +gR +}bd +/$c devr def +/rectclip where +{ +pop/rC/rectclip ld +}{ +/rC +{ +np 4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +:K +clip np +}bd +}ifelse +/rectfill where +{ +pop/rF/rectfill ld +}{ +/rF +{ +gS +np +4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +fill +gR +}bd +}ifelse +/rectstroke where +{ +pop/rS/rectstroke ld +}{ +/rS +{ +gS +np +4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +:K +stroke +gR +}bd +}ifelse +%%EndFile +%%BeginFile: lw8_level1_colorspace-2.0 +/G/setgray ld +/:F1/setgray ld +/:F/setrgbcolor ld +/:F4/setcmykcolor where +{ +pop +/setcmykcolor ld +}{ +{ +3 +{ +dup +3 -1 roll add +dup 1 gt{pop 1}if +1 exch sub +4 1 roll +}repeat +pop +setrgbcolor +}bd +}ifelse +/:Fx +{ +counttomark +{0{G}0{:F}{:F4}} +exch get +exec +pop +}bd +/$cs Z +/:rg{devr :ss}bd +/:sc{$cs :ss}bd +/:dc +{ +dup type/arraytype eq{0 get}if +dup/DeviceCMYK eq +{ +pop devc +}{ +/DeviceGray eq +{ +devg +}{ +devr +}ifelse +}ifelse +/$cs xdf +}bd +/:sgl{}def +/:dr{}bd +/:fCRD{pop}bd +/:ckcs{}bd +/:ss{/$c xdf}bd +%%EndFile +%%BeginFile: lw8_uniform_graphics-2.0 +/@a +{ +np :M 0 rl :L 0 exch rl 0 rl :L fill +}bd +/@b +{ +np :M 0 rl 0 exch rl :L 0 rl 0 exch rl fill +}bd +/@c +{ +moveto 0 rlineto stroke +}bd +/@w +{ +moveto 0 exch rlineto stroke +}bd +/arct where +{ +pop +}{ +/arct +{ +arcto pop pop pop pop +}bd +}ifelse +/x1 Z +/x2 Z +/y1 Z +/y2 Z +/rad Z +/@q +{ +/rad xs +/y2 xs +/x2 xs +/y1 xs +/x1 xs +np +x2 x1 add 2 div y1 :M +x2 y1 x2 y2 rad arct +x2 y2 x1 y2 rad arct +x1 y2 x1 y1 rad arct +x1 y1 x2 y1 rad arct +fill +}bd +/@s +{ +/rad xs +/y2 xs +/x2 xs +/y1 xs +/x1 xs +np +x2 x1 add 2 div y1 :M +x2 y1 x2 y2 rad arct +x2 y2 x1 y2 rad arct +x1 y2 x1 y1 rad arct +x1 y1 x2 y1 rad arct +:K +stroke +}bd +/@i +{ +np 0 360 arc fill +}bd +/@j +{ +gS +np +:T +scale +0 0 .5 0 360 arc +fill +gR +}bd +/@e +{ +np +0 360 arc +:K +stroke +}bd +/@f +{ +np +$m currentmatrix +pop +:T +scale +0 0 .5 0 360 arc +:K +$m setmatrix +stroke +}bd +/@k +{ +gS +np +:T +0 0 :M +0 0 5 2 roll +arc fill +gR +}bd +/@l +{ +gS +np +:T +0 0 :M +scale +0 0 .5 5 -2 roll arc +fill +gR +}bd +/@m +{ +np +arc +stroke +}bd +/@n +{ +np +$m currentmatrix +pop +:T +scale +0 0 .5 5 -2 roll arc +$m setmatrix +stroke +}bd +%%EndFile +%%BeginFile: lw8_bubn-2.1 +/$t Z +/$p Z +/$s Z +/$o 1. def +/2state? F def +/ps Z +level2 startnoload +/pushcolor/currentrgbcolor ld +/popcolor/setrgbcolor ld +/setcmykcolor where +{ +pop/currentcmykcolor where +{ +pop/pushcolor/currentcmykcolor ld +/popcolor/setcmykcolor ld +}if +}if +level2 endnoload level2 not startnoload +/pushcolor +{ +currentcolorspace $c eq +{ +currentcolor currentcolorspace T +}{ +currentcmykcolor F +}ifelse +}bd +/popcolor +{ +{ +setcolorspace setcolor +}{ +setcmykcolor +}ifelse +}bd +level2 not endnoload +/pushstatic +{ +2state? +$o +$t +$p +$s +$cs +ps +}bd +/popstatic +{ +/ps xs +/$cs xs +/$s xs +/$p xs +/$t xs +/$o xs +/2state? xs +}bd +/pushgstate +{ +currentpoint +pushcolor +currentlinewidth +currentlinecap +currentlinejoin +currentdash exch aload length +np clippath pathbbox +$m currentmatrix aload pop +}bd +/popgstate +{ +$m astore setmatrix +2 index sub exch +3 index sub exch +rC +array astore exch setdash +setlinejoin +setlinecap +lw +popcolor +np :M +}bd +/bu +{ +errordict/nocurrentpoint{pop 0 0}put +2state? +{ +pushgstate +gR +}if +pushgstate +gR +pushgstate +pushstatic +pm restore +mTS setmatrix +}bd +/bn +{ +/pm save store +popstatic +popgstate +gS +popgstate +2state? +{ +gS +popgstate +}if +}bd +/cpat{pop 64 div setgray 8{pop}repeat}bd +%%EndFile +/currentpacking where {pop sc_oldpacking setpacking}if end +%%EndProlog +%%BeginSetup +md begin +%RBIIncludeNonPPDFeature: NumCopies 1 +%RBIBeginNonPPDFeature: WaitTimeout 600 + 600/languagelevel where{pop languagelevel 2 ge}{false}ifelse{1 dict dup/WaitTimeout 4 -1 roll put setuserparams}{statusdict/waittimeout 3 -1 roll put}ifelse +%RBIEndNonPPDFeature +sfcl{ +%%BeginFeature: *HPColorAsGray No +<< /ProcessColorModel /DeviceCMYK >> setpagedevice +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPIntent Colorimetric + + + userdict /UserRenderIntent (RelativeColorimetric) put + + <<>> setpagedevice +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPAutoScaling Off + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *PageSize Letter +/HPDict /ProcSet findresource /SetMargins get [ 0 0 0 0 ] exch exec diff --git a/contrib/vips2dj/share/vips2dj/cmyk/head2 b/contrib/vips2dj/share/vips2dj/cmyk/head2 new file mode 100644 index 00000000..49615f82 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/head2 @@ -0,0 +1,160 @@ +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *OutputMode Best + + + << /PostRenderingEnhance true + + /PostRenderingEnhanceDetails + + << /PrintQuality 3 + + /Type 36 >> + + >> systemdict /setpagedevice get exec +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPMaxDetail False + + + << /PostRenderingEnhance true + + /PostRenderingEnhanceDetails + + << /MaxQualityResolution false + + /Type 36 >> + + >> systemdict /setpagedevice get exec +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *MirrorPrint False +<>setpagedevice +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPTransverse False + + +userdict /HPCustTrans known + + { + + (<<) cvx exec + + /Orientation + + userdict /HPCustTrans get + + (>>) cvx exec setpagedevice + + } + + { + + <> setpagedevice + + } ifelse +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPPantone False + + +/SpotColorMatching where { + +pop + +false SpotColorMatching + +} if +%%EndFeature + + +}efcl + +sfcl{ + +%%BeginFeature: *HPColorMan NoEmulation + + + /CMYKColorManagement where { + + pop + + /None CMYKColorManagement + + /None RGBColorManagement + + } if +%%EndFeature + +}efcl + +sfcl{ +%%BeginFeature: *HPCMYKEmulation None + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPRGBEmulation None + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPCyanBrightness leveleven + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPMagentaBrightness leveleven + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPYellowBrightness leveleven + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPBlackBrightness leveleven + +%%EndFeature + + +}efcl + +(FOTOG4)setjob diff --git a/contrib/vips2dj/share/vips2dj/cmyk/head3 b/contrib/vips2dj/share/vips2dj/cmyk/head3 new file mode 100644 index 00000000..685562e1 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/head3 @@ -0,0 +1,12 @@ +%RBIIncludeStartNup +/sD 16 dict def +{/Courier findfont[10 0 0 -10 0 0]:mf setfont}stopped{$error/newerror F put}if +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%RBIIncludePageSlotInvocation +mTSsetup +pmSVsetup +initializepage +(FOTOG4; page: 1 of 1)setjob +%%EndPageSetup diff --git a/contrib/vips2dj/share/vips2dj/cmyk/head4 b/contrib/vips2dj/share/vips2dj/cmyk/head4 new file mode 100644 index 00000000..68581c6c --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/head4 @@ -0,0 +1,76 @@ +11 -30000 -29999.5 @c +-29990 -30000 :M +psb +gsave %% Print PostScript gsave +/hascolor +/deviceinfo where +{pop deviceinfo /Colors known +{deviceinfo /Colors get exec 1 gt} +{false} ifelse} +{/statusdict where +{pop statusdict /processcolors known +{statusdict /processcolors get exec 1 gt} +{false} ifelse} +{false} ifelse} +ifelse +def +40 dict begin +/_image systemdict /image get def +/_setgray systemdict /setgray get def +/_currentgray systemdict /currentgray get def +/_settransfer systemdict /settransfer get def +/_currenttransfer systemdict /currenttransfer get def +/blank 0 _currenttransfer exec +1 _currenttransfer exec eq def +/negative blank +{0 _currenttransfer exec 0.5 lt} +{0 _currenttransfer exec 1 _currenttransfer exec gt} +ifelse def +/inverted? negative def +/level2 systemdict /languagelevel known +{languagelevel 2 ge} {false} ifelse def +/level3 systemdict /languagelevel known +{languagelevel 3 ge} {false} ifelse def +/foureq {4 index eq 8 1 roll +4 index eq 8 1 roll +4 index eq 8 1 roll +4 index eq 8 1 roll +pop pop pop pop and and and} def +hascolor {/band 0 def} {/band 5 def} ifelse +/setcmykcolor where {pop +1 0 0 0 setcmykcolor _currentgray 1 exch sub +0 1 0 0 setcmykcolor _currentgray 1 exch sub +0 0 1 0 setcmykcolor _currentgray 1 exch sub +0 0 0 1 setcmykcolor _currentgray 1 exch sub +4 {4 copy} repeat +1 0 0 0 foureq {/band 1 store} if +0 1 0 0 foureq {/band 2 store} if +0 0 1 0 foureq {/band 3 store} if +0 0 0 1 foureq {/band 4 store} if +0 0 0 0 foureq {/band 6 store} if} if +blank {/band 6 store} if +blank not { +{} bind +{} bind +{} bind +{} bind +/__settransfer {{dummy1 exec dummy2 exec} +dup 0 4 -1 roll put dup 2 _currenttransfer put +_settransfer} def +band 0 eq { +systemdict /currentcolortransfer get exec +{dummy1 exec dummy2 exec} +dup 0 11 -1 roll put dup 2 7 -1 roll put +{dummy1 exec dummy2 exec} +dup 0 10 -1 roll put dup 2 7 -1 roll put +{dummy1 exec dummy2 exec} +dup 0 9 -1 roll put dup 2 7 -1 roll put +{dummy1 exec dummy2 exec} +dup 0 8 -1 roll put dup 2 7 -1 roll put +systemdict /setcolortransfer get exec} if +band 1 eq {pop pop pop __settransfer} if +band 2 eq {pop pop __settransfer pop} if +band 3 eq {pop __settransfer pop pop} if +band 4 ge {__settransfer pop pop pop} if +} if +gsave % Image Header gsave diff --git a/contrib/vips2dj/share/vips2dj/cmyk/head5 b/contrib/vips2dj/share/vips2dj/cmyk/head5 new file mode 100644 index 00000000..cbade898 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/head5 @@ -0,0 +1,101 @@ +level2 { +band 0 eq { +/DeviceCMYK +} {/DeviceGray} ifelse +setcolorspace currentdict /PhotoshopDuotoneColorSpace undef currentdict /PhotoshopDuotoneAltColorSpace undef } if +/picstr1 cols string def +/picstr2 cols string def +/picstr3 cols string def +/picstr4 cols string def +/picstr5 cols string def +/_rowpadstr cols string def +/rawreaddata {currentfile exch readhexstring pop} def +/padreaddata { _topPad 0 gt { /_topPad _topPad 1 sub def pop _rowpadstr } + { _subImageRows 0 gt { /_subImageRows _subImageRows 1 sub def + dup _leftPad _picsubstr rawreaddata putinterval } + { pop _rowpadstr } ifelse } ifelse } def +/image2 level2 {/image load def} {{begin +Width Height BitsPerComponent ImageMatrix +Decode length 2 eq +{/DataSource load image} if +Decode length 6 eq +{DataSource 0 get DataSource 1 get DataSource 2 get +true 3 colorimage} if +Decode length 8 eq +{DataSource 0 get DataSource 1 get +DataSource 2 get DataSource 3 get +true 4 colorimage} if +end} def} ifelse +/_image2 level2 {/_image load def} {{begin +Width Height BitsPerComponent ImageMatrix +/DataSource load _image end} def} ifelse +/beginimage { +band 0 eq band 5 eq or +{image2} if +band 1 ge band 4 le and +{{1 exch sub dummy exec} dup 3 _currenttransfer put +_settransfer _image2} if +band 6 eq +{negative {{pop 0}} {{pop 1}} ifelse +_settransfer _image2} if +} def +/readdata /rawreaddata load bind def +12 dict begin +/ImageType 1 def +/Width cols def +/Height rows def +/ImageMatrix [cols 0 0 rows 0 0] def +/BitsPerComponent 8 def +band 0 eq +{/Decode [0 1 0 1 0 1 0 1] def} +{/Decode [0 1] def} ifelse +band 0 eq { +/MultipleDataSources true def +/DataSource [ +{picstr1 readdata} +{picstr2 readdata} +{picstr3 readdata} +{picstr4 readdata picstr5 readdata pop} +] def} if +band 1 eq { +/DataSource { +picstr1 readdata +picstr2 readdata pop +picstr3 readdata pop +picstr4 readdata pop +picstr5 readdata pop +} def} if +band 2 eq { +/DataSource { +picstr1 readdata pop +picstr2 readdata +picstr3 readdata pop +picstr4 readdata pop +picstr5 readdata pop +} def} if +band 3 eq { +/DataSource { +picstr1 readdata pop +picstr2 readdata pop +picstr3 readdata +picstr4 readdata pop +picstr5 readdata pop +} def} if +band 4 eq { +/DataSource { +picstr1 readdata pop +picstr2 readdata pop +picstr3 readdata pop +picstr4 readdata +picstr5 readdata pop +} def} if +band 5 ge { +/DataSource { +picstr1 readdata pop +picstr2 readdata pop +picstr3 readdata pop +picstr4 readdata pop +picstr5 readdata +} def} if +currentdict end +beginimage diff --git a/contrib/vips2dj/share/vips2dj/cmyk/head6 b/contrib/vips2dj/share/vips2dj/cmyk/head6 new file mode 100644 index 00000000..7bea01fb --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/head6 @@ -0,0 +1,10 @@ +grestore end % Image Trailer grestore +grestore % Print PostScript grestore +pse +endp +showpage +%%PageTrailer +%%Trailer +end +%%EOF + diff --git a/contrib/vips2dj/share/vips2dj/lab/Makefile.am b/contrib/vips2dj/share/vips2dj/lab/Makefile.am new file mode 100644 index 00000000..a8aaa0b8 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/Makefile.am @@ -0,0 +1,14 @@ +vips2djlabpsbitsdir = $(datadir)/vips/vips2dj/lab + +vips2djlabpsbits_DATA = \ + head1 \ + head2 \ + head3 \ + head4 \ + head5 \ + head6 + +install-exec-hook: + $(mkinstalldirs) $(DESTDIR)$(vips2djlabpsbitsdir) + +EXTRA_DIST = $(vips2djlabpsbits_DATA) diff --git a/contrib/vips2dj/share/vips2dj/lab/head1 b/contrib/vips2dj/share/vips2dj/lab/head1 new file mode 100644 index 00000000..78210203 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/head1 @@ -0,0 +1,600 @@ +%!PS-Adobe-3.0 +%%Title: (Lab_example) +%%Creator: (Adobe Photoshop\250 4.0: PSPrinter 8.3.1) +%%CreationDate: (12:54 martes, 5 agosto 1997) +%%For: (Johan Lammens) +%%Pages: 1 +%%DocumentFonts: +%%DocumentNeededFonts: +%%DocumentSuppliedFonts: +%%DocumentData: Clean7Bit +%%PageOrder: Ascend +%%Orientation: Portrait +%%DocumentMedia: Default 612 792 0 () () +%ADO_ImageableArea: 51 77 561 715 +%%EndComments +%%BeginDefaults +%%ViewingOrientation: 1 0 0 1 +%%EndDefaults +userdict begin/dscInfo 5 dict dup begin +/Title(Lab_example)def +/Creator(Adobe Photoshop\250 4.0: PSPrinter 8.3.1)def +/CreationDate(12:54 martes, 5 agosto 1997)def +/For(Johan Lammens)def +/Pages 1 def +end def end +/md 163 dict def md begin/currentpacking where {pop /sc_oldpacking currentpacking def true setpacking}if +%%BeginFile: adobe_psp_basic +%%Copyright: Copyright 1990-1996 Adobe Systems Incorporated. All Rights Reserved. +/bd{bind def}bind def +/xdf{exch def}bd +/xs{exch store}bd +/ld{load def}bd +/Z{0 def}bd +/T/true +/F/false +/:L/lineto +/lw/setlinewidth +/:M/moveto +/rl/rlineto +/rm/rmoveto +/:C/curveto +/:T/translate +/:K/closepath +/:mf/makefont +/gS/gsave +/gR/grestore +/np/newpath +14{ld}repeat +/$m matrix def +/av 83 def +/por true def +/normland false def +/psb-nosave{}bd +/pse-nosave{}bd +/us Z +/psb{/us save store}bd +/pse{us restore}bd +/level2 +/languagelevel where +{ +pop languagelevel 2 ge +}{ +false +}ifelse +def +/featurecleanup +{ +stopped +cleartomark +countdictstack exch sub dup 0 gt +{ +{end}repeat +}{ +pop +}ifelse +}bd +/noload Z +/startnoload +{ +{/noload save store}if +}bd +/endnoload +{ +{noload restore}if +}bd +level2 startnoload +/setjob +{ +statusdict/jobname 3 -1 roll put +}bd +/setcopies +{ +userdict/#copies 3 -1 roll put +}bd +level2 endnoload level2 not startnoload +/setjob +{ +1 dict begin/JobName xdf currentdict end setuserparams +}bd +/setcopies +{ +1 dict begin/NumCopies xdf currentdict end setpagedevice +}bd +level2 not endnoload +/pm Z +/mT Z +/sD Z +/realshowpage Z +/initializepage +{ +/pm save store mT concat +}bd +/endp +{ +pm restore showpage +}def +/endp1 +{ +pm restore +}def +/endp2 +{ +showpage +}def +/$c/DeviceRGB def +/rectclip where +{ +pop/rC/rectclip ld +}{ +/rC +{ +np 4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +:K +clip np +}bd +}ifelse +/rectfill where +{ +pop/rF/rectfill ld +}{ +/rF +{ +gS +np +4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +fill +gR +}bd +}ifelse +/rectstroke where +{ +pop/rS/rectstroke ld +}{ +/rS +{ +gS +np +4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +:K +stroke +gR +}bd +}ifelse +%%EndFile +%%BeginFile: adobe_psp_colorspace_level1 +%%Copyright: Copyright 1991-1996 Adobe Systems Incorporated. All Rights Reserved. +/G/setgray ld +/:F1/setgray ld +/:F/setrgbcolor ld +/:F4/setcmykcolor where +{ +pop +/setcmykcolor ld +}{ +{ +3 +{ +dup +3 -1 roll add +dup 1 gt{pop 1}if +1 exch sub +4 1 roll +}repeat +pop +setrgbcolor +}bd +}ifelse +/:Fx +{ +counttomark +{0{G}0{:F}{:F4}} +exch get +exec +pop +}bd +/:rg{/DeviceRGB :ss}bd +/:sc{$cs :ss}bd +/:dc{/$cs xdf}bd +/:sgl{}def +/:dr{}bd +/:nmCRD{pop}bd +/:fCRD{pop}bd +/:ckcs{}bd +/:ss{/$c xdf}bd +/$cs Z +%%EndFile +%%BeginFile: adobe_psp_uniform_graphics +%%Copyright: Copyright 1990-1996 Adobe Systems Incorporated. All Rights Reserved. +/@a +{ +np :M 0 rl :L 0 exch rl 0 rl :L fill +}bd +/@b +{ +np :M 0 rl 0 exch rl :L 0 rl 0 exch rl fill +}bd +/@c +{ +moveto lineto stroke +}bd +/arct where +{ +pop +}{ +/arct +{ +arcto pop pop pop pop +}bd +}ifelse +/x1 Z +/x2 Z +/y1 Z +/y2 Z +/rad Z +/@q +{ +/rad xs +/y2 xs +/x2 xs +/y1 xs +/x1 xs +np +x2 x1 add 2 div y1 :M +x2 y1 x2 y2 rad arct +x2 y2 x1 y2 rad arct +x1 y2 x1 y1 rad arct +x1 y1 x2 y1 rad arct +fill +}bd +/@s +{ +/rad xs +/y2 xs +/x2 xs +/y1 xs +/x1 xs +np +x2 x1 add 2 div y1 :M +x2 y1 x2 y2 rad arct +x2 y2 x1 y2 rad arct +x1 y2 x1 y1 rad arct +x1 y1 x2 y1 rad arct +:K +stroke +}bd +/@i +{ +np 0 360 arc fill +}bd +/@j +{ +gS +np +:T +scale +0 0 .5 0 360 arc +fill +gR +}bd +/@e +{ +np +0 360 arc +:K +stroke +}bd +/@f +{ +np +$m currentmatrix +pop +:T +scale +0 0 .5 0 360 arc +:K +$m setmatrix +stroke +}bd +/@k +{ +gS +np +:T +0 0 :M +0 0 5 2 roll +arc fill +gR +}bd +/@l +{ +gS +np +:T +0 0 :M +scale +0 0 .5 5 -2 roll arc +fill +gR +}bd +/@m +{ +np +arc +stroke +}bd +/@n +{ +np +$m currentmatrix +pop +:T +scale +0 0 .5 5 -2 roll arc +$m setmatrix +stroke +}bd +%%EndFile +%%BeginFile: adobe_psp_customps +%%Copyright: Copyright 1990-1996 Adobe Systems Incorporated. All Rights Reserved. +/$t Z +/$p Z +/$s Z +/$o 1. def +/2state? false def +/ps Z +level2 startnoload +/pushcolor/currentrgbcolor ld +/popcolor/setrgbcolor ld +/setcmykcolor where +{ +pop/currentcmykcolor where +{ +pop/pushcolor/currentcmykcolor ld +/popcolor/setcmykcolor ld +}if +}if +level2 endnoload level2 not startnoload +/pushcolor +{ +currentcolorspace $c eq +{ +currentcolor currentcolorspace true +}{ +currentcmykcolor false +}ifelse +}bd +/popcolor +{ +{ +setcolorspace setcolor +}{ +setcmykcolor +}ifelse +}bd +level2 not endnoload +/pushstatic +{ +ps +2state? +$o +$t +$p +$s +$cs +}bd +/popstatic +{ +/$cs xs +/$s xs +/$p xs +/$t xs +/$o xs +/2state? xs +/ps xs +}bd +/pushgstate +{ +save errordict/nocurrentpoint{pop 0 0}put +currentpoint +3 -1 roll restore +pushcolor +currentlinewidth +currentlinecap +currentlinejoin +currentdash exch aload length +np clippath pathbbox +$m currentmatrix aload pop +}bd +/popgstate +{ +$m astore setmatrix +2 index sub exch +3 index sub exch +rC +array astore exch setdash +setlinejoin +setlinecap +lw +popcolor +np :M +}bd +/bu +{ +pushgstate +gR +pushgstate +2state? +{ +gR +pushgstate +}if +pushstatic +pm restore +mT concat +}bd +/bn +{ +/pm save store +popstatic +popgstate +gS +popgstate +2state? +{ +gS +popgstate +}if +}bd +/cpat{pop 64 div setgray 8{pop}repeat}bd +%%EndFile +%%BeginFile: adobe_psp_basic_text +%%Copyright: Copyright 1990-1996 Adobe Systems Incorporated. All Rights Reserved. +/S/show ld +/A{ +0.0 exch ashow +}bd +/R{ +0.0 exch 32 exch widthshow +}bd +/W{ +0.0 3 1 roll widthshow +}bd +/J{ +0.0 32 4 2 roll 0.0 exch awidthshow +}bd +/V{ +0.0 4 1 roll 0.0 exch awidthshow +}bd +/fcflg true def +/fc{ +fcflg{ +vmstatus exch sub 50000 lt{ +(%%[ Warning: Running out of memory ]%%\r)print flush/fcflg false store +}if pop +}if +}bd +/$f[1 0 0 -1 0 0]def +/:ff{$f :mf}bd +/MacEncoding StandardEncoding 256 array copy def +MacEncoding 39/quotesingle put +MacEncoding 96/grave put +/Adieresis/Aring/Ccedilla/Eacute/Ntilde/Odieresis/Udieresis/aacute +/agrave/acircumflex/adieresis/atilde/aring/ccedilla/eacute/egrave +/ecircumflex/edieresis/iacute/igrave/icircumflex/idieresis/ntilde/oacute +/ograve/ocircumflex/odieresis/otilde/uacute/ugrave/ucircumflex/udieresis +/dagger/degree/cent/sterling/section/bullet/paragraph/germandbls +/registered/copyright/trademark/acute/dieresis/notequal/AE/Oslash +/infinity/plusminus/lessequal/greaterequal/yen/mu/partialdiff/summation +/product/pi/integral/ordfeminine/ordmasculine/Omega/ae/oslash +/questiondown/exclamdown/logicalnot/radical/florin/approxequal/Delta/guillemotleft +/guillemotright/ellipsis/space/Agrave/Atilde/Otilde/OE/oe +/endash/emdash/quotedblleft/quotedblright/quoteleft/quoteright/divide/lozenge +/ydieresis/Ydieresis/fraction/currency/guilsinglleft/guilsinglright/fi/fl +/daggerdbl/periodcentered/quotesinglbase/quotedblbase/perthousand +/Acircumflex/Ecircumflex/Aacute/Edieresis/Egrave/Iacute/Icircumflex/Idieresis/Igrave +/Oacute/Ocircumflex/apple/Ograve/Uacute/Ucircumflex/Ugrave/dotlessi/circumflex/tilde +/macron/breve/dotaccent/ring/cedilla/hungarumlaut/ogonek/caron +MacEncoding 128 128 getinterval astore pop +level2 startnoload +/copyfontdict +{ +findfont dup length dict +begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +}bd +level2 endnoload level2 not startnoload +/copyfontdict +{ +findfont dup length dict +copy +begin +}bd +level2 not endnoload +md/fontname known not{ +/fontname/customfont def +}if +/Encoding Z +/:mre +{ +copyfontdict +/Encoding MacEncoding def +fontname currentdict +end +definefont :ff def +}bd +/:bsr +{ +copyfontdict +/Encoding Encoding 256 array copy def +Encoding dup +}bd +/pd{put dup}bd +/:esr +{ +pop pop +fontname currentdict +end +definefont :ff def +}bd +/scf +{ +scalefont def +}bd +/scf-non +{ +$m scale :mf setfont +}bd +/ps Z +/fz{/ps xs}bd +/sf/setfont ld +/cF/currentfont ld +/mbf +{ +/makeblendedfont where +{ +pop +makeblendedfont +/ABlend exch definefont +}{ +pop +}ifelse +def +}def +%%EndFile +/currentpacking where {pop sc_oldpacking setpacking}if end +%%EndProlog +%%BeginSetup +md begin +countdictstack[{ +%%BeginFeature: *InputSlot OnlyOne + +%%EndFeature +}featurecleanup +countdictstack[{ +%%BeginFeature: *InstalledMemory standard + +%%EndFeature +}featurecleanup +countdictstack[{ +%%BeginFeature: *HPIntent Colorimetric + userdict /UserRenderIntent (Colorimetric) put + <<>> setpagedevice +%%EndFeature +}featurecleanup +countdictstack[{ +%%BeginFeature: *PageRegion Letter diff --git a/contrib/vips2dj/share/vips2dj/lab/head2 b/contrib/vips2dj/share/vips2dj/lab/head2 new file mode 100644 index 00000000..f972de6c --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/head2 @@ -0,0 +1,3 @@ +%%EndFeature +}featurecleanup +(Johan Lammens; document: Lab_example)setjob diff --git a/contrib/vips2dj/share/vips2dj/lab/head3 b/contrib/vips2dj/share/vips2dj/lab/head3 new file mode 100644 index 00000000..d7a83ad4 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/head3 @@ -0,0 +1,9 @@ +/sD 16 dict def +300 level2{1 dict dup/WaitTimeout 4 -1 roll put setuserparams}{statusdict/waittimeout 3 -1 roll put}ifelse +/Courier findfont[10 0 0 -10 0 0]:mf setfont +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +initializepage +(Johan Lammens; document: Lab_example; page: 1 of 1)setjob +%%EndPageSetup diff --git a/contrib/vips2dj/share/vips2dj/lab/head4 b/contrib/vips2dj/share/vips2dj/lab/head4 new file mode 100644 index 00000000..e64faa14 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/head4 @@ -0,0 +1,71 @@ +-30000 -29999 -1 1 -29989 -30000 1 -30000 -30000 @a +-30000 -30000 :M +psb +gsave +/hascolor +/deviceinfo where +{pop deviceinfo /Colors known +{deviceinfo /Colors get exec 1 gt} +{false} ifelse} +{/statusdict where +{pop statusdict /processcolors known +{statusdict /processcolors get exec 1 gt} +{false} ifelse} +{false} ifelse} +ifelse +def +40 dict begin +/_image systemdict /image get def +/_setgray systemdict /setgray get def +/_currentgray systemdict /currentgray get def +/_settransfer systemdict /settransfer get def +/_currenttransfer systemdict /currenttransfer get def +/blank 0 _currenttransfer exec +1 _currenttransfer exec eq def +/negative blank +{0 _currenttransfer exec 0.5 lt} +{0 _currenttransfer exec 1 _currenttransfer exec gt} +ifelse def +/inverted? negative def +/level2 systemdict /languagelevel known +{languagelevel 2 ge} {false} ifelse def +/foureq {4 index eq 8 1 roll +4 index eq 8 1 roll +4 index eq 8 1 roll +4 index eq 8 1 roll +pop pop pop pop and and and} def +hascolor {/band 0 def} {/band 5 def} ifelse +/setcmykcolor where {pop +1 0 0 0 setcmykcolor _currentgray 1 exch sub +0 1 0 0 setcmykcolor _currentgray 1 exch sub +0 0 1 0 setcmykcolor _currentgray 1 exch sub +0 0 0 1 setcmykcolor _currentgray 1 exch sub +4 {4 copy} repeat +1 0 0 0 foureq {/band 1 store} if +0 1 0 0 foureq {/band 2 store} if +0 0 1 0 foureq {/band 3 store} if +0 0 0 1 foureq {/band 4 store} if +0 0 0 0 foureq {/band 6 store} if} if +blank {/band 6 store} if +blank not { +{} bind +{} bind +{} bind +{} bind +/__settransfer {{dummy1 exec dummy2 exec} +dup 0 4 -1 roll put dup 2 _currenttransfer put +_settransfer} def +band 0 eq { +systemdict /currentcolortransfer get exec +{dummy1 exec dummy2 exec} +dup 0 11 -1 roll put dup 2 7 -1 roll put +{dummy1 exec dummy2 exec} +dup 0 10 -1 roll put dup 2 7 -1 roll put +{dummy1 exec dummy2 exec} +dup 0 9 -1 roll put dup 2 7 -1 roll put +{dummy1 exec dummy2 exec} +dup 0 8 -1 roll put dup 2 7 -1 roll put +systemdict /setcolortransfer get exec} if +band 0 ne {__settransfer pop pop pop} if +} if +gsave diff --git a/contrib/vips2dj/share/vips2dj/lab/head5 b/contrib/vips2dj/share/vips2dj/lab/head5 new file mode 100644 index 00000000..fc3b5619 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/head5 @@ -0,0 +1,70 @@ +level2 { +band 0 eq { +[/CIEBasedABC 5 dict begin +/RangeABC [0 100 -128 127 -128 127] def +/DecodeABC [{16 add 116 div} bind +{500 div} bind {200 div} bind] def +/MatrixABC [1 1 1 1 0 0 0 0 -1] def +/DecodeLMN [ +{dup 6 29 div ge {dup dup mul mul} +{4 29 div sub 108 841 div mul} +ifelse 0.9505 mul} bind +{dup 6 29 div ge {dup dup mul mul} +{4 29 div sub 108 841 div mul} +ifelse} bind +{dup 6 29 div ge {dup dup mul mul} +{4 29 div sub 108 841 div mul} +ifelse 1.0890 mul} bind +] def +/WhitePoint [0.9505 1 1.0890] def +currentdict end] +} {/DeviceGray} ifelse +setcolorspace} if +/picstr1 cols string def +/picstr2 cols string def +/picstr3 cols string def +/readdata {currentfile exch readhexstring pop} def +/image2 level2 {/image load def} {{begin +Width Height BitsPerComponent ImageMatrix +Decode length 2 eq +{/DataSource load image} if +Decode length 6 eq +{DataSource 0 get DataSource 1 get DataSource 2 get +true 3 colorimage} if +Decode length 8 eq +{DataSource 0 get DataSource 1 get +DataSource 2 get DataSource 3 get +true 4 colorimage} if +end} def} ifelse +/_image2 level2 {/_image load def} {{begin +Width Height BitsPerComponent ImageMatrix +/DataSource load _image end} def} ifelse +/beginimage { +band 0 eq band 4 eq or band 5 eq or +{image2} +{negative {{pop 0}} {{pop 1}} ifelse +_settransfer _image2} ifelse +} def +12 dict begin +/ImageType 1 def +/Width cols def +/Height rows def +/ImageMatrix [cols 0 0 rows 0 0] def +/BitsPerComponent 8 def +band 0 eq level2 and +{/Decode [0 100 -128 127 -128 127] def +/MultipleDataSources true def +/DataSource [ +{picstr1 readdata} +{picstr2 readdata} +{picstr3 readdata} +] def} +{/Decode [0 1] def +/DataSource { +picstr1 readdata +picstr2 readdata pop +picstr3 readdata pop +} def} +ifelse +currentdict end +beginimage diff --git a/contrib/vips2dj/share/vips2dj/lab/head6 b/contrib/vips2dj/share/vips2dj/lab/head6 new file mode 100644 index 00000000..31f60335 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/head6 @@ -0,0 +1,9 @@ + +grestore end +grestore +pse +endp +%%PageTrailer +%%Trailer +end +%%EOF diff --git a/contrib/vips2dj/share/vips2dj/mono/Makefile.am b/contrib/vips2dj/share/vips2dj/mono/Makefile.am new file mode 100644 index 00000000..6fa3d0df --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/Makefile.am @@ -0,0 +1,14 @@ +vips2djmonopsbitsdir = $(datadir)/vips/vips2dj/mono + +vips2djmonopsbits_DATA = \ + head1 \ + head2 \ + head3 \ + head4 \ + head5 \ + head6 + +install-exec-hook: + $(mkinstalldirs) $(DESTDIR)$(vips2djmonopsbitsdir) + +EXTRA_DIST = $(vips2djmonopsbits_DATA) diff --git a/contrib/vips2dj/share/vips2dj/mono/head1 b/contrib/vips2dj/share/vips2dj/mono/head1 new file mode 100644 index 00000000..6e23bccd --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/head1 @@ -0,0 +1,686 @@ +%!PS-Adobe-3.0 +%%Title: (Untitled-1) +%%Creator: (Adobe\250 Photoshop\250 6.0: AdobePS 8.7.0) +%%CreationDate: (3:00 pm Tuesday, April 30, 2002) +%%For: (FOTOG4) +%%Routing: (mailto:Colin.White@ng-london.org.uk) +%%Pages: 1 +%%DocumentFonts: +%%DocumentNeededResources: +%%DocumentSuppliedResources: +%%DocumentData: Clean7Bit +%%PageOrder: Ascend +%%Orientation: Portrait +%%DocumentMedia: (Default) 612 792 0 () () +%RBINumCopies: 1 +%RBINupNess: 1 1 +%ADO_ImageableArea: 30 33 582 761 +%RBIDocumentSuppliedFonts: +[{ +%%BeginPluginPS: SetTime +/HPDict /ProcSet findresource /SetTime get (20020430145733) exch exec +%%EndPluginPS +}stopped cleartomark +[{ +%%BeginPluginPS: HPJobname +/HPDict /ProcSet findresource /SetJobName get (Untitled_1) exch exec +%%EndPluginPS +}stopped cleartomark +%%EndComments +%%BeginDefaults +%%ViewingOrientation: 1 0 0 1 +%%EndDefaults +userdict/dscInfo 5 dict dup begin +/Title(Untitled-1)def +/Creator(Adobe\250 Photoshop\250 6.0: AdobePS 8.7.0)def +/CreationDate(3:00 pm Tuesday, April 30, 2002)def +/For(FOTOG4)def +/Pages 1 def +end put +%%BeginProlog +/md 178 dict def md begin/currentpacking where {pop /sc_oldpacking currentpacking def true setpacking}if +%%BeginFile: lw8_feature-1.01 +%%Copyright: Copyright 1990-1999 Adobe Systems Incorporated and Apple Computer Incorporated. All Rights Reserved. +/bd{bind def}bind def +/ld{load def}bd +/xs{exch store}bd +/Z{0 def}bd +/T true def +/F false def +/level2 +/languagelevel where +{ +pop languagelevel 2 ge +}{ +F +}ifelse +def +/odictstk Z +/oopstk Z +/fcl +{ +count oopstk sub dup 0 gt +{ +{pop}repeat +}{ +pop +}ifelse +countdictstack odictstk sub dup 0 gt +{ +{end}repeat +}{ +pop +}ifelse +}bd +/sfcl2 +{ +/odictstk countdictstack store +count/oopstk xs +}bd +/efcl2 +{ +stopped{$error/newerror F put}if +fcl +}bd +/noload Z +/startnoload +{ +{/noload save store}if +}bd +/endnoload +{ +{noload restore}if +}bd +/setcopies{ +level2 +{ +1 dict begin/NumCopies exch def currentdict end setpagedevice +}{ +userdict/#copies 3 -1 roll put +}ifelse +}def +level2 startnoload +/ststpgdev{}def +/dopgdev{}def +/stpgdev{}def +/buf Z +/didstop T def +/sfcl +{ +/didstop T store +/odictstk countdictstack store +count/oopstk xs +currentfile cvx stopped +{ +$error/newerror F put +didstop +{ +save/didstop xs +/buf vmstatus exch sub exch pop dup 0 lt{pop 0}if +dup 64000 gt{pop 64000}if string store +{ +currentfile buf readline +{ +(}efcl)eq{exit}if +}{ +/UnexpectedEOF errordict/rangecheck get exec +}ifelse +}loop +didstop restore +}if +}if +fcl +}bd +/efcl +{ +/didstop F store +exec +stop +}bd +level2 endnoload level2 not startnoload +/setpagedevice where{pop/realstpgdev/setpagedevice ld}if +/SC_topddict Z +/SC_spdict Z +/$spusrdict F def +/dopgdev +{ +userdict/setpagedevice undef +$spusrdict +{ +userdict/setpagedevice/realstpgdev load put +/$spusrdict F store +}if +SC_topddict realstpgdev +}bd +/stpgdev +{ +SC_topddict dup 3 -1 roll +{ +SC_spdict 2 index known +{ +SC_spdict 2 index get +dup 3 -1 roll +{ +put dup +}forall +pop put dup +}{ +put dup +}ifelse +}forall +pop pop +}bd +/ststpgdev +{ +/setpagedevice where +{ +userdict eq +{ +/$spusrdict T store +}if +}if +userdict/setpagedevice/stpgdev load put +/SC_topddict 0 dict store +/SC_spdict 3 dict begin +/InputAttributes 0 dict def +/Policies 0 dict def +/OutputAttributes 0 dict def +currentdict +end +store +}def +/sfcl/sfcl2 ld +/efcl/efcl2 ld +level2 not endnoload +%%EndFile +%%BeginFile: lw8_basic-4.0 +/xdf{exch def}bd +/:L/lineto +/lw/setlinewidth +/:M/moveto +/rl/rlineto +/rm/rmoveto +/:C/curveto +/:T/translate +/:K/closepath +/:mf/makefont +/gS/gsave +/gR/grestore +/np/newpath +12{ld}repeat +/framewidth -1 def +/QDframwid -1 def +/numframes Z +/mTS matrix def +/$m matrix def +/av 87 def +/por T def +/normland F def +/psb-nosave{}def +/pse-nosave{}def +/us Z +/psb{/us save store}bd +/pse{us restore}bd +/level3 +/languagelevel where +{ +pop languagelevel 3 ge +}{ +F +}ifelse +def +level2 startnoload +/setjob +{ +statusdict/jobname 3 -1 roll put +}bd +/devg/DeviceGray def +/devr/DeviceRGB def +/devc/DeviceCMYK def +level2 endnoload level2 not startnoload +/setjob +{ +1 dict begin/JobName xdf currentdict end setuserparams +}bd +/devg[/DeviceGray]def +/devr[/DeviceRGB]def +/devc[/DeviceCMYK]def +level2 not endnoload +/pm Z +/mT Z +/sD Z +/mTSsetup{ +mT $m currentmatrix mTS concatmatrix pop +}bd +/pmSVsetup{ +/pm save store +}bd +/initializepage +{ +mT concat +}bd +/endp +{ +pm restore +}bd +/adjRect +{ +dup 2 mul 6 2 roll +4 index sub exch 5 -1 roll sub exch +4 2 roll +4 index add exch 5 -1 roll add exch +4 2 roll +}bd +/frame1up +{ +gS +mTS setmatrix +QDframwid lw +/setstrokeadjust where{pop T setstrokeadjust}if +clippath pathbbox +2 index sub exch +3 index sub exch +currentlinewidth framewidth mul +adjRect +numframes dup 0 lt{pop 0}if +{ +4 copy +rS +currentlinewidth framewidth +mul 4 mul +adjRect +}repeat +pop pop pop pop +gR +}bd +/$c devr def +/rectclip where +{ +pop/rC/rectclip ld +}{ +/rC +{ +np 4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +:K +clip np +}bd +}ifelse +/rectfill where +{ +pop/rF/rectfill ld +}{ +/rF +{ +gS +np +4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +fill +gR +}bd +}ifelse +/rectstroke where +{ +pop/rS/rectstroke ld +}{ +/rS +{ +gS +np +4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +:K +stroke +gR +}bd +}ifelse +%%EndFile +%%BeginFile: lw8_level1_colorspace-2.0 +/G/setgray ld +/:F1/setgray ld +/:F/setrgbcolor ld +/:F4/setcmykcolor where +{ +pop +/setcmykcolor ld +}{ +{ +3 +{ +dup +3 -1 roll add +dup 1 gt{pop 1}if +1 exch sub +4 1 roll +}repeat +pop +setrgbcolor +}bd +}ifelse +/:Fx +{ +counttomark +{0{G}0{:F}{:F4}} +exch get +exec +pop +}bd +/$cs Z +/:rg{devr :ss}bd +/:sc{$cs :ss}bd +/:dc +{ +dup type/arraytype eq{0 get}if +dup/DeviceCMYK eq +{ +pop devc +}{ +/DeviceGray eq +{ +devg +}{ +devr +}ifelse +}ifelse +/$cs xdf +}bd +/:sgl{}def +/:dr{}bd +/:fCRD{pop}bd +/:ckcs{}bd +/:ss{/$c xdf}bd +%%EndFile +%%BeginFile: lw8_uniform_graphics-2.0 +/@a +{ +np :M 0 rl :L 0 exch rl 0 rl :L fill +}bd +/@b +{ +np :M 0 rl 0 exch rl :L 0 rl 0 exch rl fill +}bd +/@c +{ +moveto 0 rlineto stroke +}bd +/@w +{ +moveto 0 exch rlineto stroke +}bd +/arct where +{ +pop +}{ +/arct +{ +arcto pop pop pop pop +}bd +}ifelse +/x1 Z +/x2 Z +/y1 Z +/y2 Z +/rad Z +/@q +{ +/rad xs +/y2 xs +/x2 xs +/y1 xs +/x1 xs +np +x2 x1 add 2 div y1 :M +x2 y1 x2 y2 rad arct +x2 y2 x1 y2 rad arct +x1 y2 x1 y1 rad arct +x1 y1 x2 y1 rad arct +fill +}bd +/@s +{ +/rad xs +/y2 xs +/x2 xs +/y1 xs +/x1 xs +np +x2 x1 add 2 div y1 :M +x2 y1 x2 y2 rad arct +x2 y2 x1 y2 rad arct +x1 y2 x1 y1 rad arct +x1 y1 x2 y1 rad arct +:K +stroke +}bd +/@i +{ +np 0 360 arc fill +}bd +/@j +{ +gS +np +:T +scale +0 0 .5 0 360 arc +fill +gR +}bd +/@e +{ +np +0 360 arc +:K +stroke +}bd +/@f +{ +np +$m currentmatrix +pop +:T +scale +0 0 .5 0 360 arc +:K +$m setmatrix +stroke +}bd +/@k +{ +gS +np +:T +0 0 :M +0 0 5 2 roll +arc fill +gR +}bd +/@l +{ +gS +np +:T +0 0 :M +scale +0 0 .5 5 -2 roll arc +fill +gR +}bd +/@m +{ +np +arc +stroke +}bd +/@n +{ +np +$m currentmatrix +pop +:T +scale +0 0 .5 5 -2 roll arc +$m setmatrix +stroke +}bd +%%EndFile +%%BeginFile: lw8_bubn-2.1 +/$t Z +/$p Z +/$s Z +/$o 1. def +/2state? F def +/ps Z +level2 startnoload +/pushcolor/currentrgbcolor ld +/popcolor/setrgbcolor ld +/setcmykcolor where +{ +pop/currentcmykcolor where +{ +pop/pushcolor/currentcmykcolor ld +/popcolor/setcmykcolor ld +}if +}if +level2 endnoload level2 not startnoload +/pushcolor +{ +currentcolorspace $c eq +{ +currentcolor currentcolorspace T +}{ +currentcmykcolor F +}ifelse +}bd +/popcolor +{ +{ +setcolorspace setcolor +}{ +setcmykcolor +}ifelse +}bd +level2 not endnoload +/pushstatic +{ +2state? +$o +$t +$p +$s +$cs +ps +}bd +/popstatic +{ +/ps xs +/$cs xs +/$s xs +/$p xs +/$t xs +/$o xs +/2state? xs +}bd +/pushgstate +{ +currentpoint +pushcolor +currentlinewidth +currentlinecap +currentlinejoin +currentdash exch aload length +np clippath pathbbox +$m currentmatrix aload pop +}bd +/popgstate +{ +$m astore setmatrix +2 index sub exch +3 index sub exch +rC +array astore exch setdash +setlinejoin +setlinecap +lw +popcolor +np :M +}bd +/bu +{ +errordict/nocurrentpoint{pop 0 0}put +2state? +{ +pushgstate +gR +}if +pushgstate +gR +pushgstate +pushstatic +pm restore +mTS setmatrix +}bd +/bn +{ +/pm save store +popstatic +popgstate +gS +popgstate +2state? +{ +gS +popgstate +}if +}bd +/cpat{pop 64 div setgray 8{pop}repeat}bd +%%EndFile +/currentpacking where {pop sc_oldpacking setpacking}if end +%%EndProlog +%%BeginSetup +md begin +%RBIIncludeNonPPDFeature: NumCopies 1 +%RBIBeginNonPPDFeature: WaitTimeout 600 + 600/languagelevel where{pop languagelevel 2 ge}{false}ifelse{1 dict dup/WaitTimeout 4 -1 roll put setuserparams}{statusdict/waittimeout 3 -1 roll put}ifelse +%RBIEndNonPPDFeature +sfcl{ +%%BeginFeature: *HPColorAsGray No +<< /ProcessColorModel /DeviceCMYK >> setpagedevice +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPIntent Colorimetric + + + userdict /UserRenderIntent (RelativeColorimetric) put + + <<>> setpagedevice +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPAutoScaling Off + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *PageSize Letter +/HPDict /ProcSet findresource /SetMargins get [ 0 0 0 0 ] exch exec diff --git a/contrib/vips2dj/share/vips2dj/mono/head2 b/contrib/vips2dj/share/vips2dj/mono/head2 new file mode 100644 index 00000000..3cbee582 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/head2 @@ -0,0 +1,160 @@ +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *OutputMode Best + + + << /PostRenderingEnhance true + + /PostRenderingEnhanceDetails + + << /PrintQuality 3 + + /Type 36 >> + + >> systemdict /setpagedevice get exec +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPMaxDetail False + + + << /PostRenderingEnhance true + + /PostRenderingEnhanceDetails + + << /MaxQualityResolution false + + /Type 36 >> + + >> systemdict /setpagedevice get exec +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *MirrorPrint False +<>setpagedevice +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPTransverse False + + +userdict /HPCustTrans known + + { + + (<<) cvx exec + + /Orientation + + userdict /HPCustTrans get + + (>>) cvx exec setpagedevice + + } + + { + + <> setpagedevice + + } ifelse +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPPantone False + + +/SpotColorMatching where { + +pop + +false SpotColorMatching + +} if +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPColorMan Native + + + /CMYKColorManagement where { + + pop + + /Native CMYKColorManagement + + /sRGB RGBColorManagement + + } if +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPCMYKEmulation None + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPRGBEmulation None + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPCyanBrightness leveleven + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPMagentaBrightness leveleven + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPYellowBrightness leveleven + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPBlackBrightness leveleven + +%%EndFeature + + +}efcl + +(FOTOG4)setjob diff --git a/contrib/vips2dj/share/vips2dj/mono/head3 b/contrib/vips2dj/share/vips2dj/mono/head3 new file mode 100644 index 00000000..685562e1 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/head3 @@ -0,0 +1,12 @@ +%RBIIncludeStartNup +/sD 16 dict def +{/Courier findfont[10 0 0 -10 0 0]:mf setfont}stopped{$error/newerror F put}if +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%RBIIncludePageSlotInvocation +mTSsetup +pmSVsetup +initializepage +(FOTOG4; page: 1 of 1)setjob +%%EndPageSetup diff --git a/contrib/vips2dj/share/vips2dj/mono/head4 b/contrib/vips2dj/share/vips2dj/mono/head4 new file mode 100644 index 00000000..6a625041 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/head4 @@ -0,0 +1,29 @@ +11 -30000 -29999.5 @c +-29990 -30000 :M +psb +gsave %% Print PostScript gsave +40 dict begin +/_image systemdict /image get def +/_setgray systemdict /setgray get def +/_currentgray systemdict /currentgray get def +/_settransfer systemdict /settransfer get def +/_currenttransfer systemdict /currenttransfer get def +/blank 0 _currenttransfer exec +1 _currenttransfer exec eq def +/negative blank +{0 _currenttransfer exec 0.5 lt} +{0 _currenttransfer exec 1 _currenttransfer exec gt} +ifelse def +/inverted? negative def +/level2 systemdict /languagelevel known +{languagelevel 2 ge} {false} ifelse def +/level3 systemdict /languagelevel known +{languagelevel 3 ge} {false} ifelse def +blank not { +{} bind +/__settransfer {{dummy1 exec dummy2 exec} +dup 0 4 -1 roll put dup 2 _currenttransfer put +_settransfer} def +__settransfer +} if +gsave % Image Header gsave diff --git a/contrib/vips2dj/share/vips2dj/mono/head5 b/contrib/vips2dj/share/vips2dj/mono/head5 new file mode 100644 index 00000000..04bf69aa --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/head5 @@ -0,0 +1,27 @@ +level2 { +/DeviceGray +setcolorspace currentdict /PhotoshopDuotoneColorSpace undef currentdict /PhotoshopDuotoneAltColorSpace undef } if +/picstr1 cols string def +/_rowpadstr cols string def +/rawreaddata {currentfile exch readhexstring pop} def +/padreaddata { _topPad 0 gt { /_topPad _topPad 1 sub def pop _rowpadstr } + { _subImageRows 0 gt { /_subImageRows _subImageRows 1 sub def + dup _leftPad _picsubstr rawreaddata putinterval } + { pop _rowpadstr } ifelse } ifelse } def +/image2 level2 {/image load def} {{begin +Width Height BitsPerComponent ImageMatrix +/DataSource load image end} def} ifelse +/beginimage { +image2 +} def +/readdata /rawreaddata load bind def +12 dict begin +/ImageType 1 def +/Width cols def +/Height rows def +/ImageMatrix [cols 0 0 rows 0 0] def +/BitsPerComponent 8 def +/Decode [0 1] def +/DataSource {picstr1 readdata} def +currentdict end +beginimage diff --git a/contrib/vips2dj/share/vips2dj/mono/head6 b/contrib/vips2dj/share/vips2dj/mono/head6 new file mode 100644 index 00000000..81dc27d0 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/head6 @@ -0,0 +1,11 @@ + +grestore end % Image Trailer grestore +grestore % Print PostScript grestore +pse +endp +showpage +%%PageTrailer +%%Trailer +end +%%EOF + diff --git a/contrib/vips2dj/vips2ah.c b/contrib/vips2dj/vips2ah.c new file mode 100644 index 00000000..782f7f4d --- /dev/null +++ b/contrib/vips2dj/vips2ah.c @@ -0,0 +1,170 @@ +/* Output IM_CODING_LABQ as band-separated ASCIIHEX for PostScript + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include +#include + +static int +writeimage( REGION *ir, FILE *out ) +{ + IMAGE *im = ir->im; + int x, y, z; + int l = 0; + PEL *p; + Rect area; + + /* Set up input area. + */ + area.left = 0; + area.top = 0; + area.width = im->Xsize; + area.height = 1; + +/* Write a byte. + */ +#define PUT( C ) {\ + int c1 = (C);\ + \ + if( putc( c1, out ) == EOF ) {\ + im_errormsg( "vips2hp2500cp: write error - disc full?" );\ + return( -1 );\ + }\ +} + +/* Write a hex character. + */ +#define writehexc( C ) {\ + int c = (C);\ + \ + if( c < 10 ) {\ + PUT( c + '0' );\ + }\ + else\ + PUT( (c - 10) + 'A' );\ +} + +/* Write a hex byte. + */ +#define writehexb( B ) { \ + int b = (B);\ + \ + writehexc( (b >> 4 ) & 0xf );\ + writehexc( b & 0xf );\ +} + +/* Output a hex byte, linefeed on eol. + */ +#define writewrap( B ) { \ + writehexb( B ); \ + if( l++ > 30 ) { \ + PUT( '\n' ); \ + l = 0; \ + } \ +} + + /* Loop for each scan-line. + */ + for( y = 0; y < im->Ysize; y++ ) { + /* Ask for this scan-line. + */ + area.top = y; + if( im_prepare( ir, &area ) ) + return( -1 ); + p = (PEL *) IM_REGION_ADDR( ir, 0, y ); + + if( im->Coding == IM_CODING_LABQ ) { + /* Do L* ... easy. + */ + for( x = 0; x < im->Xsize; x++ ) + writewrap( p[x*4] ); + + /* a* and b* ... more difficult. Photoshop uses + * bizzare coding for a/b. + */ + for( z = 1; z < 3; z++ ) { + for( x = 0; x < im->Xsize; x++ ) { + int i = (signed char) p[x*4 + z]; + + writewrap( i + 128 ); + } + } + } + else if( im->Bands == 4 ) { + for( z = 0; z < 4; z++ ) + for( x = 0; x < im->Xsize; x++ ) { + int v = p[x*4 + z]; + + writewrap( v ); + } + + /* Extra channel?? Just send zeros. + */ + for( x = 0; x < im->Xsize; x++ ) + writewrap( 0xff ); + } + else if( im->Bands == 1 ) { + for( x = 0; x < im->Xsize; x++ ) { + int v = p[x]; + + writewrap( v ); + } + } + } + PUT( '\n' ); + + return( 0 ); +} + +/* Start here! + */ +int +vips2asciihex( IMAGE *in, FILE *out ) +{ + REGION *ir; + + if( im_pincheck( in ) ) + return( -1 ); + if( !(ir = im_region_create( in )) ) + return( -1 ); + + if( writeimage( ir, out ) ) { + im_region_free( ir ); + return( -1 ); + } + im_region_free( ir ); + + return( 0 ); +} diff --git a/contrib/vips2dj/vips2dj.c b/contrib/vips2dj/vips2dj.c new file mode 100644 index 00000000..f75d13ef --- /dev/null +++ b/contrib/vips2dj/vips2dj.c @@ -0,0 +1,377 @@ +/* Convert lab, cmyk and mono images to postscript. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#include "vips2dj.h" + +static const char *argv0 = NULL; + +/* Geometries for the printers we know about. + */ +PrinterGeometry printer_data[] = { + /* name, paper width, print width, print length, left, top */ + { "2500cp", 2592, 2502, 3728, 51, 82 }, + { "3500cp", 3888, 3786, 5212, 51, 82 }, + { "5000ps", 4320, 4280, 5212, 20, 99 }, + { "4600dn", 594, 570, 817, 11, 15 } +}; + +/* Print a geo entry. + */ +static void +print_printers( void ) +{ + int i; + + printf( "%12s %12s %12s %12s %12s %12s\n", "printer name", + "paper width", "print width", "print length", + "left margin", "top margin" ); + for( i = 0; i < IM_NUMBER( printer_data ); i++ ) + printf( "%12s %12d %12d %12d %12d %12d\n", + printer_data[i].name, + printer_data[i].pwidth, + printer_data[i].width, + printer_data[i].length, + printer_data[i].left, + printer_data[i].top ); +} + +/* Turn a name to a printer geometry. + */ +static PrinterGeometry * +find_printer( char *name ) +{ + int i; + + for( i = 0; i < IM_NUMBER( printer_data ); i++ ) + if( strcmp( name, printer_data[i].name ) == 0 ) + return( &printer_data[i] ); + + im_errormsg( "vips2dj: unknown printer \"%s\"", name ); + return( NULL ); +} + +/* Copy between two fds + */ +static int +copy_bytes( FILE *in, FILE *out ) +{ + int ch; + + while( (ch = getc( in )) != EOF ) + if( putc( ch, out ) == EOF ) { + im_errormsg( "vips2dj: write error -- disc full?" ); + return( -1 ); + } + + return( 0 ); +} + +/* Send a file to out. Used to transmit the preludes. + */ +static int +transmit_file( char *mode, char *name, FILE *out ) +{ + const char *prefix; + char buf[PATH_MAX]; + FILE *in; + + if( !(prefix = im_guess_prefix( argv0, "VIPSHOME" )) ) + return( -1 ); + + /* Send it! + */ + im_snprintf( buf, PATH_MAX, "%s/share/vips/vips2dj/%s/%s", + prefix, mode, name ); + if( !(in = fopen( buf, "r" )) ) { + im_errormsg( "vips2dj: can't find \"%s\"", name ); + return( -1 ); + } + if( copy_bytes( in, out ) ) { + fclose( in ); + return( -1 ); + } + fclose( in ); + + return( 0 ); +} + +/* Send the file to fp. width and height are the size to print at in points. + */ +static int +send_file( PrinterGeometry *geo, IMAGE *im, char *mode, + FILE *out, int width, int height ) +{ + /* Send all the start stuff. + */ + if( transmit_file( mode, "head1", out ) ) + return( -1 ); + + /* Set page size. + */ + fprintf( out, "<>setpagedevice\n", + geo->pwidth, height + 2*geo->top ); + + if( transmit_file( mode, "head2", out ) ) + return( -1 ); + + /* Set mT (margin transform? don't know) + */ + fprintf( out, "/mT[1 0 0 -1 %d %d]def\n", + geo->left, height + geo->top ); + + if( transmit_file( mode, "head3", out ) ) + return( -1 ); + + /* Set rC ... printable area. + */ + fprintf( out, "gS 0 0 %d %d rC\n", width, height ); + + if( transmit_file( mode, "head4", out ) ) + return( -1 ); + + /* Set image params. + */ + fprintf( out, "/rows %d def\n", im->Ysize ); + fprintf( out, "/cols %d def\n", im->Xsize ); + fprintf( out, "%d %d scale\n", width, height ); + + if( transmit_file( mode, "head5", out ) ) + return( -1 ); + + /* Send the body of the image. + */ + if( vips2asciihex( im, out ) ) + return( -1 ); + + if( transmit_file( mode, "head6", out ) ) + return( -1 ); + + return( 0 ); +} + +/* Start here! + */ +int +main( int argc, char **argv ) +{ + IMAGE *im = NULL; + FILE *out = stdout; + int width = -1; + int height = -1; + int dpi = -1; + int max = 0; + int one2one = 0; + PrinterGeometry *geo = find_printer( "2500cp" ); + char *mode; + int i; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + argv0 = argv[0]; + + if( argc <= 1 ) { + printf( +"usage:\n" +"\t%s [options] \n" +"convert LAB, CMYK and mono image files to postscript\n" +"\tLAB printed with printer colour management\n" +"\tCMYK sent directly as dot percent\n" +"\tmono prints as K only\n" +"options include:\n" +"\t-printer \tformat for printer \n" +"\t-3500cp\t\tfor HP 3500CP printer (default 2500cp)\n" +"\t-max\t\tprint as large as possible\n" +"\t-1:1\t\tsize the image to print at 1:1 ... resolution in\n" +"\t\t\timage header must be set for this\n" +"\t-width \tforce specified width, in points\n" +"\t-height \tforce specified height, in points\n" +"\t-dpi \tforce specified resolution (default 150dpi)\n" +"\t-a5, -a4, -a3, -a2, -a1, -a0\n" +"\t\t\tforce specified height (width ignored)\n" +"\t-o \toutput to file (default stdout)\n", + argv0 ); + printf( "supported printers:\n" ); + print_printers(); + return( 1 ); + } + + /* Decode args .. just look for file names and our three options. + */ + for( i = 1; i < argc; i++ ) + if( *argv[i] == '-' ) { + if( strcmp( argv[i]+1, "width" ) == 0 ) { + if( !argv[i+1] || sscanf( argv[i+1], + "%d", &width ) != 1 || width <= 10 ) + error_exit( "bad width" ); + i++; + } + else if( strcmp( argv[i]+1, "height" ) == 0 ) { + if( !argv[i+1] || sscanf( argv[i+1], + "%d", &height ) != 1 || height <= 10 ) + error_exit( "bad height" ); + i++; + } + else if( strcmp( argv[i]+1, "3500cp" ) == 0 ) { + geo = find_printer( "3500cp" ); + } + else if( strcmp( argv[i]+1, "printer" ) == 0 ) { + if( !argv[i+1] || + !(geo = find_printer( argv[i+1] )) ) + error_exit( "bad printer model" ); + i++; + } + else if( strcmp( argv[i]+1, "dpi" ) == 0 ) { + if( !argv[i+1] || sscanf( argv[i+1], + "%d", &dpi ) != 1 || dpi <= 1 || + dpi >= 600 ) + error_exit( "bad dpi" ); + i++; + } + else if( strcmp( argv[i]+1, "o" ) == 0 ) { + if( !argv[i+1] || !(out = fopen( + argv[i+1], "w" )) ) + error_exit( "bad output name" ); + i++; + } + else if( strcmp( argv[i]+1, "1:1" ) == 0 ) + one2one = 1; + else if( strcmp( argv[i]+1, "a5" ) == 0 ) + height = 595; + else if( strcmp( argv[i]+1, "a4" ) == 0 ) + height = 839; + else if( strcmp( argv[i]+1, "a3" ) == 0 ) + height = 1187; + else if( strcmp( argv[i]+1, "a2" ) == 0 ) + height = 1678; + else if( strcmp( argv[i]+1, "a1" ) == 0 ) + height = 2373; + else if( strcmp( argv[i]+1, "a0" ) == 0 ) + height = 3356; + else if( strcmp( argv[i]+1, "max" ) == 0 ) + max = 1; + else + error_exit( "bad flag" ); + } + else { + /* Try to open the file. + */ + if( im != NULL || !(im = im_open( argv[i], "r" )) ) + error_exit( "bad input image" ); + } + + if( im == NULL ) + error_exit( "no input image" ); + + /* Stop used-before-set complaints on mode. + */ + mode = "lab"; + + /* Pick a PS mode. + */ + if( im->Coding == IM_CODING_LABQ ) + mode = "lab"; + else if( im->Coding == IM_CODING_NONE && + im->Bands == 4 && im->BandFmt == IM_BANDFMT_UCHAR ) + mode = "cmyk"; + else if( im->Coding == IM_CODING_NONE && + im->Bands == 1 && im->BandFmt == IM_BANDFMT_UCHAR ) + mode = "mono"; + else + error_exit( "unsupported image type " + "(IM_CODING_LABQ, mono, IM_TYPE_CMYK only)" ); + + /* Make sure width and height are both set. + */ + if( one2one ) { + /* Set width/height from res. + */ + if( im->Xres <= 0 || im->Xres >= 100 || + im->Yres <= 0 || im->Yres >= 100 ) + error_exit( "uanble to print 1:1 - resolution not " + "set in image" ); + + height = (((im->Ysize / im->Yres) / 10.0) / 2.54) * 72.0; + width = (((im->Xsize / im->Xres) / 10.0) / 2.54) * 72.0; + } + else if( max ) { + float iaspect = (float) im->Xsize / im->Ysize; + float paspect = (float) geo->width / geo->length; + + if( iaspect > paspect ) + /* Image aspect ratio > paper ... fit width. + */ + width = geo->width; + else + height = geo->length; + } + else if( dpi > 0 ) { + /* Given res ... set width/height. + */ + height = (im->Ysize / (float) dpi) * 72.0; + width = (im->Xsize / (float) dpi) * 72.0; + } + + if( width >= 0 || height >= 0 ) { + /* Given width or height or both --- set other one. + */ + if( height < 0 ) { + float fdpi = im->Xsize / (width / 72.0); + height = (im->Ysize / fdpi) * 72.0; + } + else { + float fdpi = im->Ysize / (height / 72.0); + width = (im->Xsize / fdpi) * 72.0; + } + } + else { + /* Nothing set ... default to 150 dpi. + */ + height = (im->Ysize / 150.0) * 72.0; + width = (im->Xsize / 150.0) * 72.0; + } + + if( send_file( geo, im, mode, out, width, height ) ) + error_exit( "error sending file" ); + + return( 0 ); +} diff --git a/contrib/vips2dj/vips2dj.h b/contrib/vips2dj/vips2dj.h new file mode 100644 index 00000000..bbbd51c7 --- /dev/null +++ b/contrib/vips2dj/vips2dj.h @@ -0,0 +1,45 @@ +/* Header for vips2dj. + */ + +/* + + Copyright (C) 1991-2003 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Geometry for a printer model. + */ +typedef struct { + char *name; /* Printer name (eg. "2500cp") */ + int pwidth; /* Paper width (36/54 inches) */ + int width; /* Printable width, points */ + int length; /* Printable length, points */ + int left; /* Left margin, points */ + int top; /* Top margin, points */ +} PrinterGeometry; + +/* All the models. + */ +extern PrinterGeometry printer_data[]; + +extern int vips2asciihex( IMAGE *in, FILE *out ); diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 00000000..c2f953ee --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,13 @@ +# + +prefix = /home/john/vips + +install all: + cd src; $(MAKE) -f Makefile install + echo "Making html man pages" + ./Makehtmlman $(prefix)/share/man/man1 + ./Makehtmlman $(prefix)/share/man/man3 + +.PHONEY: clean +clean: + cd src; $(MAKE) -f Makefile clean diff --git a/doc/Makehtmlman b/doc/Makehtmlman new file mode 100755 index 00000000..806bea96 --- /dev/null +++ b/doc/Makehtmlman @@ -0,0 +1,93 @@ +#!/bin/bash + +# Make html in html/man from the man pages in the named +# directories + +# Config stuff +out_dir=`pwd`/html/man + +# print usage and exit +usage() { + echo usage: $0 dir1 dir2 ... + echo make html in $out_dir from the man pages in the + echo named directories + + exit 1 +} + +# If a dir does not exist, make it +makedir() { + if test ! -d "$1"; then + mkdir "$1" + status=$? + if test $status -ne 0 && test ! -d "$1"; then + exit $status + fi + fi +} + +makedir `pwd`/html +makedir `pwd`/html/man +makedir $out_dir + +# echo the filename given a path +filename() { + echo ${1/#*\//} +} + +# echo the dirname given a path... make sure there's a trailing slash +dirname() { + # break off the filename, then junk that many chars off the end of + # $1 to make the dirname + local file=`filename $1` + local dir=${1:0:$((${#1}-${#file}))} + + # is dir "/"? return immediately + if test "$dir" == "/"; then + echo "/" + return + fi + + # remove trailing slash, provided we're not removing everything + dir=${dir/%\//} + + # if there's no dir, we must be in the current dir + if test "$dir" == ""; then + dir="." + fi + + # finally add a trailing "/" back again + echo $dir/ +} + +# Need VIPSHOME +export VIPSHOME=`vips im_guess_prefix im_version VIPSHOME` + +: ${VIPSHOME:?} + +if test $# -le 0; then + usage +fi + +# Loop over args, adding source man pages +for dir in $*; do + if test -d "$dir"; then + files="$files $dir/*.[0-9]" + else + echo "$0: directory $dir does not exist" + exit 1 + fi +done + +# Make output! +let j=0 +for i in $files; do + file=`filename $i` + dir=`dirname $i` + + ( cd $dir/.. ; + rman -f html -r '%s.%s.html' $dir$file > $out_dir/$file.html ) + let j+=1 +done + +echo "$0: made $j pages of html" diff --git a/doc/README b/doc/README new file mode 100644 index 00000000..9054eb38 --- /dev/null +++ b/doc/README @@ -0,0 +1,15 @@ +VIPS documentation + +edit Makefile and change "prefix" to be your install prefix (eg. +"/usr/local/vips-7.8") ... the HTML man page maker will pull manual pages from +this area + +type 'make' to rebuild postscript and html. You will need gnu make, latex +and latex2html. It will also scan the VIPS man pages and build a set of +HTML versions using the Makehtmlman script. + +src/intro/ has some MS word documents written by Joe Padfield while at the +Hamilton-Karr. + +once everything has built, copy the html and ps directories to +$prefix/share/doc/vips diff --git a/doc/src/Makefile b/doc/src/Makefile new file mode 100644 index 00000000..07ca891f --- /dev/null +++ b/doc/src/Makefile @@ -0,0 +1,65 @@ +# + +PDF = vipsmanual.pdf +SRC = \ + applintro.tex \ + cppintro.tex \ + fileformat.tex \ + func.tex \ + iosys.tex \ + ipio.tex \ + mydefs.tex \ + operintro.tex \ + packages.tex \ + pio.tex \ + refintro.tex \ + vdisplay.tex \ + verror.tex \ + vimage.tex \ + vipsmanual.tex \ + vmask.tex \ + wio.tex + +mkinstalldirs = $(SHELL) ../../mkinstalldirs +destdir = ../ + +all: $(PDF) html + +install: all $(PDF) html + -rm -rf ${destdir}/pdf/*.pdf + -rm -rf ${destdir}/html/vips* + -rm -rf ${destdir}/html/figs + $(mkinstalldirs) ${destdir}/pdf + $(mkinstalldirs) ${destdir}/html + -cp $(PDF) ${destdir}/pdf + -cp -r vipsmanual/* ${destdir}/html + +$(PDF): $(SRC) + pdflatex vipsmanual.tex + pdflatex vipsmanual.tex + +.PHONEY: html +html: + -rm -rf vipsmanual + mkdir vipsmanual + htlatex vipsmanual.tex html.cfg,3 "" -dvipsmanual/ + cp -r figs vipsmanual + +.PHONEY: clean +clean: + -rm -f *.4ct + -rm -f *.4tc + -rm -f *.log + -rm -f *.xref + -rm -f *.tmp + -rm -f *.html + -rm -f *.css + -rm -f *.lg + -rm -f *.idv + -rm -f *.aux + -rm -f *.dvi + -rm -f *.lof + -rm -f *.lot + -rm -f *.toc + -rm -f *.pdf + -rm -rf vipsmanual diff --git a/doc/src/applintro.tex b/doc/src/applintro.tex new file mode 100644 index 00000000..690300fe --- /dev/null +++ b/doc/src/applintro.tex @@ -0,0 +1,51 @@ +\section{Introduction} +\mylabel{sec:appl} + +This chapter explains how to call VIPS functions from C programs. It does not +explain how to write new image processing operations (See \pref{sec:oper}), +only how to call the ones that VIPS provides. If you want to call VIPS +functions from C++ programs, you can either use the interface described here +or you can try out the much nicer C++ interface described in \pref{sec:cpp}. + +See \pref{sec:ref} for an introduction to the image processing operations +available in the library. \fref{fg:architecture} tries to show +an overview of this structure. + +\begin{fig2} +\figw{5in}{arch.png} +\caption{VIPS software architecture} +\label{fg:architecture} +\end{fig2} + +VIPS includes a set of UNIX manual pages. Enter (for example): + +\begin{verbatim} +example% man im_extract +\end{verbatim} + +\noindent +to get an explanation of the \verb+im_extract()+ function. + +All the comand-line vips operations will print help text too. For example: + +\begin{verbatim} +example% vips im_extract +usage: vips im_extract input output + left top width height band +where: + input is of type "image" + output is of type "image" + left is of type "integer" + top is of type "integer" + width is of type "integer" + height is of type "integer" + band is of type "integer" +extract area/band, from package + "conversion" +flags: (PIO function) + (coordinate transformer) + (area operation) + (result can be cached) +vips: error calling function +im_run_command: too few arguments +\end{verbatim} diff --git a/doc/src/cppintro.tex b/doc/src/cppintro.tex new file mode 100644 index 00000000..99e7ba43 --- /dev/null +++ b/doc/src/cppintro.tex @@ -0,0 +1,105 @@ +\section{Introduction} +\mylabel{sec:cpp} + +This chapter describes the C++ API for the VIPS image processing library, +version 7.12. The C++ API is as efficient as the C interface to VIPS, but is +far easier to use: almost all creation, destruction and error handling issues +are handled for you automatically. + +The Python interface is a very simple wrapping of this C++ API generated +automatically with SWIG. The two interfaces are identical, except for language +syntax. + +\subsection{If you've used the C API} + +To show how much easier the VIPS C++ API is to use, compare \fref{fg:negative} +to \fref{fg:invert-c++}. \fref{fg:invert-py} is the same thing in Python. + +A typical build line for the C++ program might be: + +\begin{verbatim} +g++ invert.cc \ + `pkg-config vipsCC-7.12 \ + --cflags --libs` +\end{verbatim} + +The key points are: + +\begin{itemize} + +\item +You just include \verb++ --- this then gets all of the +other includes you need. Everything is in the \verb+vips+ namespace. + +\item +The C++ API replaces all of the VIPS C types --- \verb+IMAGE+ becomes +\verb+VImage+ and so on. The C++ API also includes \verb+VDisplay+, +\verb+VMask+ and \verb+VError+. + +\item +Image processing operations are member functions of the \verb+VImage+ class +--- here, \verb+VImage( argv[1] )+ creates a new \verb+VImage+ object using +the first argument to initialise it (the input filename). It then calls the +member function \verb+invert()+, which inverts the \verb+VImage+ and returns a +new \verb+VImage+. Finally it calls the member function \verb+write()+, which +writes the result image to the named file. + +\item +The VIPS C++ API uses exceptions --- the \verb+VError+ class is covered +later. If you run this program with a bad input file, for example, you get the +following output: + +\begin{verbatim} +example% invert jim fred +invert: VIPS error: im_open: + "jim" is not a supported + format +\end{verbatim} + +\end{itemize} + +\begin{fig2} +\begin{verbatim} +#include +#include + +int +main( int argc, char **argv ) +{ + if( argc != 3 ) { + std::cerr << "usage: " << argv[0] << " infile outfile\n"; + exit( 1 ); + } + + try { + vips::VImage fred( argv[1] ); + + fred.invert().write( argv[2] ); + } + catch( vips::VError e ) { + e.perror( argv[0] ); + } + + return( 0 ); +} +\end{verbatim} +\caption{\texttt{invert} program in C++} +\label{fg:invert-c++} +\end{fig2} + +\begin{fig2} +\begin{verbatim} +#!/usr/bin/python + +import sys +from vipsCC import * + +try: + a = VImage.VImage (sys.argv[1]) + a.invert ().write (sys.argv[2]) +except VError.VError, e: + e.perror (sys.argv[0]) +\end{verbatim} +\caption{\texttt{invert} program in Python} +\label{fg:invert-py} +\end{fig2} diff --git a/doc/src/figs/arch.png b/doc/src/figs/arch.png new file mode 100644 index 00000000..2719aea8 Binary files /dev/null and b/doc/src/figs/arch.png differ diff --git a/doc/src/figs/arch.svg b/doc/src/figs/arch.svg new file mode 100644 index 00000000..6e44ee30 --- /dev/null +++ b/doc/src/figs/arch.svg @@ -0,0 +1,385 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + Python binding + + + + C++ binding + + + VIPS imageprocessingoperations + User imageprocessingoperations + + VIPS IO system + Function dispatch + + + + Command-lineinterface + + + + diff --git a/doc/src/figs/interconvert.png b/doc/src/figs/interconvert.png new file mode 100644 index 00000000..bc98d461 Binary files /dev/null and b/doc/src/figs/interconvert.png differ diff --git a/doc/src/figs/interconvert.svg b/doc/src/figs/interconvert.svg new file mode 100644 index 00000000..f505bd1d --- /dev/null +++ b/doc/src/figs/interconvert.svg @@ -0,0 +1,505 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + Lab + + + + XYZ + + + + LCh + + + + disp + + + + Yxy + + + + UCS + + + + LabQ + + + + LabS + + + + ICC + + + + + + + + + + Any VIPS RGB space + Any device withan ICC profile + + diff --git a/doc/src/fileformat.tex b/doc/src/fileformat.tex new file mode 100644 index 00000000..f80ae96b --- /dev/null +++ b/doc/src/fileformat.tex @@ -0,0 +1,160 @@ +\section{The VIPS file format} + +VIPS has its own very simple file format. It is used inside VIPS to hold +images during computation. You can save images in VIPS format if you want, +but the VIPS format is not widely used and you may have problems reading +your images into other packages. + +If you intend to keep an image, it's much better to save it as TIFF, +JPEG, PNG or PBM/PGM/PPM. VIPS can transparently read and write all these +formats. + +\subsection{VIPS file header} +\label{sec:header} + +All VIPS image files start with a 64-byte header giving basic information +about the image dimensions, see \tref{fg:header}. This is followed by the +image data. This is usually just the pixel values in native format (ie. the +byte order used by the machine that wrote the file) laid out left-to-right and +top-to-bottom. After the image data comes a block of optional XML which holds +extra image metadata, such as ICC profiles and image history. +You can use the command-line program \verb+header+ to extract the XML from an +image and \verb+edvips+ to replace it, see the man pages. + +The \ct{Type} field, the \ct{Xres}/\ct{Yres} fields, and the +\ct{Xoffset}/\ct{Yoffset} fields are advisory. VIPS maintains their value +(if you convert an image to \cielab{} colour space with \ct{im\_XYZ2Lab()}, +for example, VIPS will set \ct{Type} to be \ct{IM\_TYPE\_LAB}), but never +uses these values itself in determining the action of an image processing +function. These fields are to help the user, and to help application +programs built on VIPS which are trying to present image data to the user +in a meaningful way. + +The \ct{BandFmt}, \ct{Coding} and \ct{Type} fields can take the values +shown in tables~\ref{fg:bandfmt}, \ref{fg:coding} and \ref{fg:type}. The C++ +and Python names for these values are slightly different, for historical +reasons. + +\begin{tab2} +\begin{center} +\begin{tabular}{|l|l|l|} +\hline +Bytes & Represent & VIPS name \\ +\hline +0--3 & VIPS magic number (in hex, 08 f2 f6 b6) & \\ +4--7 & Number of pels per horizontal line (integer) & \ct{Xsize} \\ +8--11 & Number of horizontal lines (integer) & \ct{Ysize} \\ +12--15 & Number of bands (integer) & \ct{Bands} \\ +16--19 & Unused (legacy) & \ct{Bbits} \\ +20--23 & Band format (eg. \ct{IM\_BANDFMT\_USHORT}) & \ct{BandFmt} \\ +24--27 & Coding type (eg. \ct{IM\_CODING\_NONE}) & \ct{Coding} \\ +28--31 & Type (eg. \ct{IM\_TYPE\_LAB}) & \ct{Type} \\ +32--35 & Horizontal resolution (float, pixels mm$^{-1}$) & \ct{Xres} \\ +36--39 & Vertical resolution (float, pixels mm$^{-1}$) & \ct{Yres} \\ +40--43 & Unused (legacy) & \ct{Length} \\ +44--45 & Unused (legacy) & \ct{Compression} \\ +46--47 & Unused (legacy) & \ct{Level} \\ +48--51 & Horizontal offset of origin & \ct{Xoffset} \\ +52--55 & Vertical offset of origin & \ct{Yoffset} \\ +56--63 & For future expansion (all zeros for now) & \\ +\hline +\end{tabular} +\end{center} +\caption{VIPS header\label{fg:header}} +\end{tab2} + +\begin{tab2} +\begin{center} +\begin{tabular}{|l|l|l|l|} +\hline +\ct{BandFmt} & C++ and Python name & Value & Meaning \\ +\hline +\ct{IM\_BANDFMT\_NOTSET} & \ct{FMTNOTSET} & -1 & \\ +\ct{IM\_BANDFMT\_UCHAR} & \ct{FMTUCHAR} & 0 & Unsigned 8-bit int \\ +\ct{IM\_BANDFMT\_CHAR} & \ct{FMTCHAR} & 1 & Signed 8-bit int \\ +\ct{IM\_BANDFMT\_USHORT} & \ct{FMTUSHORT} & 2 & Unsigned 16-bit int \\ +\ct{IM\_BANDFMT\_SHORT} & \ct{FMTSHORT} & 3 & Signed 16-bit int \\ +\ct{IM\_BANDFMT\_UINT} & \ct{FMTUINT} & 4 & Unsigned 32-bit int \\ +\ct{IM\_BANDFMT\_INT} & \ct{FMTINT} & 5 & Signed 32-bit int \\ +\ct{IM\_BANDFMT\_FLOAT} & \ct{FMTFLOAT} & 6 & 32-bit IEEE float \\ +\ct{IM\_BANDFMT\_COMPLEX} & \ct{FMTCOMPLEX} & 7 & Complex (2 floats) \\ +\ct{IM\_BANDFMT\_DOUBLE} & \ct{FMTDOUBLE} & 8 & 64-bit IEEE double \\ +\ct{IM\_BANDFMT\_DPCOMPLEX} & \ct{FMTDPCOMPLEX} & 9 & Complex (2 doubles) \\ +\hline +\end{tabular} +\end{center} +\caption{Possible values for \ct{BandFmt}\label{fg:bandfmt}} +\end{tab2} + +\begin{tab2} +\begin{center} +\begin{tabular}{|l|l|l|l|} +\hline +\ct{Coding} & C++ and Python name & Value & Meaning \\ +\hline +\ct{IM\_CODING\_NONE} & \ct{NOCODING} & 0 & VIPS computation format \\ +\ct{IM\_CODING\_LABQ} & \ct{LABQ} & 2 & LABQ storage format \\ +\hline +\end{tabular} +\end{center} +\caption{Possible values for \texttt{Coding}\label{fg:coding}} +\end{tab2} + +\begin{tab2} +\begin{center} +\begin{tabular}{|l|l|l|l|} +\hline +\ct{Type} & C++ and Python name & Value & Meaning \\ +\hline +\ct{IM\_TYPE\_MULTIBAND} & \ct{MULTIBAND} & 0 & Some multiband image \\ +\ct{IM\_TYPE\_B\_W} & \ct{B\_W} & 1 & Some single band image \\ +\ct{IM\_TYPE\_HISTOGRAM} & \ct{HISTOGRAM} & 10 & Histogram or LUT \\ +\ct{IM\_TYPE\_FOURIER} & \ct{FOURIER} & 24 & Image in Fourier space \\ +\ct{IM\_TYPE\_XYZ} & \ct{XYZ} & 12 & \ciexyz{} colour space \\ +\ct{IM\_TYPE\_LAB} & \ct{LAB} & 13 & \cielab{} colour space \\ +\ct{IM\_TYPE\_CMYK} & \ct{CMYK} & 15 & \ct{im\_icc\_export()} \\ +\ct{IM\_TYPE\_LABQ} & \ct{LABQ} & 16 & 32-bit \cielab{} \\ +\ct{IM\_TYPE\_RGB} & \ct{RGB} & 17 & Some RGB \\ +\ct{IM\_TYPE\_UCS} & \ct{UCS} & 18 & \cieucs{} colour space \\ +\ct{IM\_TYPE\_LCH} & \ct{LCH} & 19 & \cielch{} colour space \\ +\ct{IM\_TYPE\_LABS} & \ct{LABS} & 21 & 48-bit \cielab{} \\ +\ct{IM\_TYPE\_sRGB} & \ct{sRGB} & 22 & sRGB colour space \\ +\ct{IM\_TYPE\_YXY} & \ct{YXY} & 23 & \cieyxy{} colour space \\ +\ct{IM\_TYPE\_RGB16} & \ct{RGB16} & 25 & 16-bit RGB \\ +\ct{IM\_TYPE\_GREY16} & \ct{GREY16} & 26 & 16-bit monochrome \\ +\hline +\end{tabular} +\end{center} +\caption{Possible values for \texttt{Type}\label{fg:type}} +\end{tab2} + +\subsection{Computation formats} + +This type of image has \ct{Coding} set to \ct{IM\_CODING\_NONE}. The +header is then followed by a large array of pixels, laid out left-to-right, +top-to-bottom. Each pixel contains the specified number of bands. Each band +has the specified band format, which may be an 8-, 16- or 32-bit integer +(either signed or unsigned), a single or double precision IEEE floating +point number, or a pair of single or double precision floats forming a +complex number. + +All values are stored in the host-machine's native number representation (that +is, either most-significant first, as in SPARC and 680x0 machines, or +least-significant first, for Intel and DEC machines). The VIPS library will +automatically byte-swap for you if necessary, but this can be slow. + +\subsection{Storage formats} + +All storage formats have other values for the \ct{Coding} field. This +release supports only \ct{IM\_CODING\_LABQ} format. + +\ct{IM\_CODING\_LABQ} stores $L^{*}$, $a^{*}$ and $b^{*}$ for each pixel, +with 10 bits for $L^{*}$ and 11 bits for each of $a^{*}$ and $b^{*}$. These +32 bits are packed into 4 bytes, with the most significant 8 bits of each +value in the first 3 bytes, and the left-over bits packed into the final +byte as 2:3:3. + +This format is a little awkward to process. Some VIPS functions can work +directly on \ct{IM\_CODING\_LABQ} images (\ct{im\_extract()}, for example), +but most will require you to unpack the image to one of the computation +formats (for example with \ct{im\_LabQ2Lab()}) first. diff --git a/doc/src/func.tex b/doc/src/func.tex new file mode 100644 index 00000000..e6cb9fff --- /dev/null +++ b/doc/src/func.tex @@ -0,0 +1,676 @@ +\section{Function dispatch and plug-ins} + +As image processing libraries increase in size it becomes progressively more +difficult to build applications which present the operations the libbrary +offers to the user. Every time a new operation is added, every user interface +needs to be adapted --- a job which can rapidly become unmanageable. + +To address this problem VIPS includes a simple database which stores an +abstract description of every image processing operation. User interfaces, +rather than having special code wired into them for each operation, can +simply interrogate the database and present what they find to the user. + +The operation database is extensible. You can define new operations, and even +new types, and add them to VIPS. These new operations will then automatically +appear in all VIPS user interfaces with no extra programming effort. Plugins +can extend the database at runtime: when VIPS starts, it loads all the plugin +in the VIPS library area. + +\subsection{Simple plugin example} + +As an example, consider this function: + +\begin{verbatim} +#include + +#include + +/* The function we define. Call this + * from other parts of your C + * application. + */ +int +double_integer( int in ) +{ + return( in * 2 ); +} +\end{verbatim} + +\noindent +The source for all the example code in this section is in the vips-examples +package. + +The first step is to make a layer over this function which will make it +look like a standard VIPS function. VIPS insists on the following pattern: + +\begin{itemize} + +\item +The function should be int-valued, and return 0 for success and non-zero +for error. It should set \verb+im_error()+. + +\item +The function should take a single argument: a pointer to a +\verb+NULL+-terminated array of \verb+im_objects+. + +\item +Each \verb+im_object+ represents one argument to the function (either output +or input) in the form specified by the corresponding entry in the function's +argument descriptor. + +\end{itemize} + +The argument descriptor is an array of structures, each describing one +argument. For this example, it is: + +\begin{verbatim} +/* Describe the type of our function. + * One input int, and one output int. + */ +static im_arg_desc arg_types[] = { + IM_INPUT_INT( "in" ), + IM_OUTPUT_INT( "out" ) +}; +\end{verbatim} + +\verb+IM_INPUT_INT()+ and \verb+IM_OUTPUT_INT()+ are macros defined in +\verb++ which make argument types easy to define. Other +macros available are listed in table~\ref{tab:type}. + +\begin{tab2} +\begin{center} +\begin{tabular}{|l|l|l|} +\hline +Macro & Meaning & + \texttt{im\_object} has type \\ +\hline +\texttt{IM\_INPUT\_IMAGEVEC} & Vector of input images & + \texttt{IMAGE **} \\ +\texttt{IM\_INPUT\_IMAGE} & Input image & + \texttt{IMAGE *} \\ +\texttt{IM\_OUTPUT\_IMAGE} & Output image & + \texttt{IMAGE *} \\ +\texttt{IM\_RW\_IMAGE} & Read-write image & + \texttt{IMAGE *} \\ +\texttt{IM\_INPUT\_DOUBLE} & Input double & + \texttt{double *} \\ +\texttt{IM\_INPUT\_DOUBLEVEC} & Input vector of double & + \texttt{im\_realvec\_object *} \\ +\texttt{IM\_INPUT\_INTVEC} & Input vector of int & + \texttt{im\_intvec\_object *} \\ +\texttt{IM\_OUTPUT\_DOUBLE} & Output double & + \texttt{double *} \\ +\texttt{IM\_INPUT\_INT} & Input int & + \texttt{int *} \\ +\texttt{IM\_OUTPUT\_INT} & Output int & + \texttt{int *} \\ +\texttt{IM\_INPUT\_STRING} & Input string & + \texttt{char *} \\ +\texttt{IM\_OUTPUT\_STRING} & Output string & + \texttt{char *} \\ +\texttt{IM\_INPUT\_DISPLAY} & Input display & + \texttt{im\_col\_display *} \\ +\texttt{IM\_OUTPUT\_DISPLAY} & Output display & + \texttt{im\_col\_display *} \\ +\texttt{IM\_OUTPUT\_COMPLEX} & Output complex & + \texttt{double *} \\ +\texttt{IM\_INPUT\_DMASK} & Input double array & + \texttt{im\_mask\_object *} \\ +\texttt{IM\_OUTPUT\_DMASK} & Output double array to file & + \texttt{im\_mask\_object *} \\ +\texttt{IM\_OUTPUT\_DMASK\_STATS}& Output double array to screen & + \\ +\texttt{IM\_INPUT\_IMASK} & Input int array & + \texttt{im\_mask\_object *} \\ +\texttt{IM\_OUTPUT\_IMASK} & Output int array to file & + \texttt{im\_mask\_object *} \\ +\texttt{IM\_INPUT\_GVALUE} & Input GValue & + \texttt{GValue *} \\ +\texttt{IM\_OUTPUT\_GVALUE} & Output GValue & + \texttt{GValue *} \\ +\hline +\end{tabular} +\end{center} +\caption{Argument type macros\label{tab:type}} +\end{tab2} + +The argument to the type macro is the name of the argument. These names +are used by user-interface programs to provide feedback, and sometimes as +variable names. The order in which you list the arguments is the order in +which user-interfaces will present them to the user. You should use the +following conventions when selecting names and an order for your arguments: + +\begin{itemize} + +\item +Names should be entirely in lower-case and contain no special characters, +apart from the digits 0-9 and the underscore character `\_'. + +\item +Names should indicate the function of the argument. For example, +\verb+im_add()+ has the following argument names: + +\begin{verbatim} +example% vips -help im_add +vips: args: in1 in2 out +where: + in1 is of type "image" + in2 is of type "image" + out is of type "image" +add two images, from package + "arithmetic" +flags: + (PIO function) + (no coordinate transformation) + (point-to-point operation) +\end{verbatim} + +\item +You should order arguments with large input objects first, then output +objects, then any extra arguments or options. For example, \verb+im_extract()+ +has the following sequence of arguments: + +\begin{verbatim} +example% vips -help im_extract +vips: args: input output left top + width height channel +where: + input is of type "image" + output is of type "image" + left is of type "integer" + top is of type "integer" + width is of type "integer" + height is of type "integer" + channel is of type "integer" +extract area/band, from package + "conversion" +flags: + (PIO function) + (no coordinate transformation) + (point-to-point operation) +\end{verbatim} + +\end{itemize} + +This function sits over \verb+double_integer()+, providing VIPS with an +interface which it can call: + +\begin{verbatim} +/* Call our function via a VIPS + * im_object vector. + */ +static int +double_vec( im_object *argv ) +{ + int *in = (int *) argv[0]; + int *out = (int *) argv[1]; + + *out = double_integer( *in ); + + /* Always succeed. + */ + return( 0 ); +} +\end{verbatim} + +Finally, these two pieces of information (the argument description and +the VIPS-style function wrapper) can be gathered together into a function +description. + +\begin{verbatim} +/* Description of double_integer. + */ +static im_function double_desc = { + "double_integer", + "double an integer", + 0, + double_vec, + IM_NUMBER( arg_types ), + arg_types +}; +\end{verbatim} +\label{sec:number} + +\verb+IM_NUMBER()+ is a macro which returns the number of elements in a +static array. The \verb+flags+ field contains hints which user-interfaces +can use for various optimisations. At present, the possible values are: + +\begin{description} + +\item[\texttt{IM\_FN\_PIO}] +This function uses the VIPS PIO system (see \pref{sec:pio}). + +\item[\texttt{IM\_FN\_TRANSFORM}] +This the function transforms coordinates. + +\item[\texttt{IM\_FN\_PTOP}] +This is a point-to-point operation, that is, it can be replaced with a +look-up table. + +\item[\texttt{IM\_FN\_NOCACHE}] +This operation has side effects and should not be cached. Useful for video +grabbers, for example. + +\end{description} + +This function description now needs to be added to the VIPS function database. +VIPS groups sets of related functions together in packages. There is only +a single function in this example, so we can just write: + +\begin{verbatim} +/* Group up all the functions in this + * file. + */ +static im_function + *function_list[] = { + &double_desc +}; + +/* Define the package_table symbol. + * This is what VIPS looks for when + * loading the plugin. + */ +im_package package_table = { + "example", + IM_NUMBER( function_list ), + function_list +}; +\end{verbatim} + +The package has to be named \verb+package_table+, and has to be exported +from the file (that is, not a static). VIPS looks for a symbol of this name +when it opens your object file. + +This file needs to be made into a dynamically loadable object. On my machine, +I can do this with: + +\begin{verbatim} +example% gcc -fPIC -DPIC -c + `pkg-config vips-7.12 --cflags` + plug.c -o plug.o +example% gcc -shared plug.o + -o double.plg +\end{verbatim} + +You can now use \verb+double.plg+ with any of the VIPS applications which +support function dispatch. For example: + +\begin{verbatim} +example% vips -plugin double.plg \ + double_integer 12 +24 +example% +\end{verbatim} + +If you copy \verb+double.plg+ into your VIPS library area (the directory where +libvips is installed, or \verb+$VIPSHOME/lib+) it will be automatically +loaded by all VIPS programs as they start up. + +\subsection{A more complicated example} + +This section lists the source for \verb+im_extract()+'s function +description. Almost all functions in the VIPS library have descriptors --- +if you are not sure how to write a description, it's usually easiest to +copy one from a similar function in the library. + +\begin{verbatim} +/* Args to im_extract. + */ +static im_arg_desc + extract_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "left" ), + IM_INPUT_INT( "top" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ), + IM_INPUT_INT( "channel" ) +}; + +/* Call im_extract via arg vector. + */ +static int +extract_vec( im_object *argv ) +{ + IMAGE_BOX box; + + box.xstart = *((int *) argv[2]); + box.ystart = *((int *) argv[3]); + box.xsize = *((int *) argv[4]); + box.ysize = *((int *) argv[5]); + box.chsel = *((int *) argv[6]); + + return( im_extract( + argv[0], argv[1], &box ) ); +} + +/* Description of im_extract. + */ +static im_function + extract_desc = { + "im_extract", + "extract area/band", + IM_FN_PIO | IM_FN_TRANSFORM, + extract_vec, + NUMBER( extract_args ), + extract_args +}; +\end{verbatim} + +\subsection{Adding new types} + +The VIPS type mechanism is extensible. User plug-ins can add new types +and user-interfaces can (to a certain extent) provide interfaces to these +user-defined types. + +Here is the definition of \verb+im_arg_desc+: + +\begin{verbatim} +/* Describe a VIPS command argument. + */ +typedef struct { + char *name; + im_type_desc *desc; + im_print_obj_fn print; +} im_arg_desc; +\end{verbatim} + +The \verb+name+ field is the argument name above. The \verb+desc+ field +points to a structure defining the argument type, and the \verb+print+ field +is an (optionally \verb+NULL+) pointer to a function which VIPS will call +for output arguments after your function successfully completes and before +the object is destroyed. It can be used to print results to the terminal, +or to copy results into a user-interface layer. + +\begin{verbatim} +/* Success on an argument. This is + * called if the image processing + * function succeeds and should be + * used to (for example) print + * output. + */ +typedef int (*im_print_obj_fn) + ( im_object obj ); +\end{verbatim} + +\verb+im_type_desc+ is defined as: + +\begin{verbatim} +/* Describe a VIPS type. + */ +typedef struct { + im_arg_type type; + int size; + im_type_flags flags; + im_init_obj_fn init; + im_dest_obj_fn dest; +} im_type_desc; +\end{verbatim} + +Where \verb+im_arg_type+ is defined as + +\begin{verbatim} +/* Type names. You may define your + * own, but if you use one of these, + * then you should use the built-in + * VIPS type converters. + */ +#define IM_TYPE_IMAGEVEC "imagevec" +#define IM_TYPE_DOUBLEVEC "doublevec" +#define IM_TYPE_INTVEC "intvec" +#define IM_TYPE_DOUBLE "double" +#define IM_TYPE_INT "integer" +#define IM_TYPE_COMPLEX "complex" +#define IM_TYPE_STRING "string" +#define IM_TYPE_IMASK "intmask" +#define IM_TYPE_DMASK "doublemask" +#define IM_TYPE_IMAGE "image" +#define IM_TYPE_DISPLAY "display" +#define IM_TYPE_GVALUE "gvalue" +typedef char *im_arg_type; +\end{verbatim} + +In other words, it's just a string. When you add a new type, you just need +to choose a new unique string to name it. Be aware that the string is printed +to the user by various parts of VIPS, and so needs to be ``human-readable''. +The flags are: + +\begin{verbatim} +/* These bits are ored together to + * make the flags in a type + * descriptor. + * + * IM_TYPE_OUTPUT: set to indicate + * output, otherwise input. + * + * IM_TYPE_ARG: Two ways of making + * an im_object --- with and without + * a command-line string to help you + * along. Arguments with a string + * are thing like IMAGE descriptors, + * which require a filename to + * initialise. Arguments without are + * things like output numbers, where + * making the object simply involves + * allocating storage. + */ + +typedef enum { + IM_TYPE_OUTPUT = 0x1, + IM_TYPE_ARG = 0x2 +} im_type_flags; +\end{verbatim} + +And the \verb+init+ and \verb+destroy+ functions are: + +\begin{verbatim} +/* Initialise and destroy objects. + * The "str" argument to the init + * function will not be supplied + * if this is not an ARG type. + */ +typedef int (*im_init_obj_fn) + ( im_object *obj, char *str ); +typedef int (*im_dest_obj_fn) + ( im_object obj ); +\end{verbatim} + +As an example, here is the definition for a new type of unsigned +integers. First, we need to define the \verb+init+ and \verb+print+ +functions. These transform objects of the type to and from string +representation. + +\begin{verbatim} +/* Init function for unsigned int + * input. + */ +static int +uint_init( im_object *obj, char *str ) +{ + unsigned int *i = (int *) *obj; + + if( sscanf( str, "%d", i ) != 1 || + *i < 0 ) { + im_error( "uint_init", + "bad format" ); + return( -1 ); + } + + return( 0 ); +} + +/* Print function for unsigned int + * output. + */ +static int +uint_print( im_object obj ) +{ + unsigned int *i = + (unsigned int *) obj; + + printf( "%d\n", (int) *i ); + + return( 0 ); +} +\end{verbatim} + +Now we can define the type itself. We make two of these --- one for unsigned +int used as input, and one for output. + +\begin{verbatim} +/* Name our type. + */ +#define TYPE_UINT "uint" + +/* Input unsigned int type. + */ +static im_type_desc input_uint = { + TYPE_UINT, /* Its an int */ + sizeof( unsigned int ),/* Memory */ + IM_TYPE_ARG, /* Needs arg */ + uint_init, /* Init */ + NULL /* Destroy */ +}; + +/* Output unsigned int type. + */ +static im_type_desc output_uint = { + TYPE_UINT, /* It's an int */ + sizeof( unsigned int ),/* Memory */ + IM_TYPE_OUTPUT, /* It's output */ + NULL, /* Init */ + NULL /* Destroy */ +}; +\end{verbatim} + +Finally, we can define two macros to make structures of type +\verb+im_arg_desc+ for us. + +\begin{verbatim} +#define INPUT_UINT( S ) \ + { S, &input_uint, NULL } +#define OUTPUT_UINT( S ) \ + { S, &output_uint, uint_print } +\end{verbatim} + +For more examples, see the definitions for the built-in VIPS types. + +\subsection{Using function dispatch in your application} + +VIPS provides a set of functions for adding new image processing functions +to the VIPS function database, finding functions by name, and calling +functions. See the manual pages for full details. + +\subsubsection{Adding and removing functions} + +\begin{verbatim} +im_package *im_load_plugin( + const char *name ); +\end{verbatim} + +This function opens the named file, searches it for a symbol named +\verb+package_table+, and adds any functions it finds to the VIPS function +database. When you search for a function, any plug-ins are searched first, +so you can override standard VIPS function with your own code. + +The function returns a pointer to the package it added, or \verb+NULL+ +on error. + +\begin{verbatim} +int im_close_plugins( void ) +\end{verbatim} + +This function closes all plug-ins, removing then from the VIPS function +database. It returns non-zero on error. + +\subsubsection{Searching the function database} + +\begin{verbatim} +void *im_map_packages( + im_list_map_fn fn, void *a ) +\end{verbatim} + +This function applies the argument function \verb+fn+ to every package +in the database, starting with the most recently added package. As with +\verb+im_list_map()+, the argument function should return \verb+NULL+ +to continue searching, or non-\verb+NULL+ to terminate the search +early. \verb+im_map_packages()+ returns \verb+NULL+ if \verb+fn+ returned +\verb+NULL+ for all arguments. The extra argument \verb+a+ is carried around +by VIPS for your use. + +For example, this fragment of code prints the names of all loaded packages +to \verb+fd+: + +\begin{verbatim} +static void * +print_package_name( im_package *pack, + FILE *fp ) +{ + (void) fprintf( fp, + "package: \"%s\"\n", + pack->name ); + + /* Continue search. + */ + return( NULL ); +} + +static void +print_packages( FILE *fp ) +{ + (void) im_map_packages( + (im_list_map_fn) + print_package_name, fp ); +} +\end{verbatim} + +VIPS defines three convenience functions based on \verb+im_map_packages()+ +which simplify searching for specific functions: + +\begin{verbatim} +im_function * + im_find_function( char *name ) +im_package * + im_find_package( char *name ) +im_package * + im_package_of_function( char *name ) +\end{verbatim} + +\subsubsection{Building argument structures and running commands} + +\begin{verbatim} +int im_free_vargv( im_function *fn, + im_object *vargv ) +int im_allocate_vargv( + im_function *fn, + im_object *vargv ) +\end{verbatim} + +These two functions allocate space for and free VIPS argument lists. The +allocate function simply calls \verb+im_malloc()+ to allocate any store +that the types require (and also initializes it to zero). The free function +just calls \verb+im_free()+ for any storage that was allocated. + +Note that neither of these functions calls the \verb+init+, \verb+dest+ +or \verb+print+ functions for the types --- that's up to you. + +\begin{verbatim} +int im_run_command( char *name, + int argc, char **argv ) +\end{verbatim} + +This function does everything. In effect, + +\begin{verbatim} +im_run_command( "im_invert", 2, + { "fred.v", "fred2.v", NULL } ) +\end{verbatim} + +is exactly equivalent to + +\begin{verbatim} +system( "vips im_invert fred.v " + "fred2.v" ) +\end{verbatim} + +but no process is forked. diff --git a/doc/src/html.cfg b/doc/src/html.cfg new file mode 100644 index 00000000..c8033ad9 --- /dev/null +++ b/doc/src/html.cfg @@ -0,0 +1,17 @@ +% configuration file for output of nipguide as html +\Preamble{html} +\begin{document} + +% stop the mono font shrinkage we do for paper output +\renewenvironment{ctd}{\begin{quote}\tt}{\end{quote}} + +% make a label +% in html, write an extra label which we can link to nip's help system +\renewcommand{\mylabel}[1]{ + \label{#1} + \HCode{} +} + +% supress " on page xx" if we're making HTML +\renewcommand{\onpage}[1]{} +\EndPreamble diff --git a/doc/src/iosys.tex b/doc/src/iosys.tex new file mode 100644 index 00000000..40b88e37 --- /dev/null +++ b/doc/src/iosys.tex @@ -0,0 +1,845 @@ +\section{Core C API} + +VIPS is +built on top of several other libraries, two of which, glib and gobject, are +exposed at various points in the C API. + +You can read up on glib at the GTK+ website: + +\begin{verbatim} +http://www.gtk.org/api +\end{verbatim} + +There's also an excellent book by Matthias Warkus, \emph{The Official +GNOME 2 Developer's Guide}, which covers the same material in a tutorial +manner. + +\subsection{Startup} + +Before calling any VIPS function, you need to start VIPS up: + +\begin{verbatim} +int im_init_world( const char *argv0 ); +\end{verbatim} + +The \verb+argv0+ argument is the value of \verb+argv[0]+ your program was passed +by the host operating system. VIPS uses this with \verb+im_guess_prefix()+ +to try to find various VIPS data files. + +If you don't call this function, VIPS will call it for you the first time you +use a VIPS function. But it won't be able to get the \verb+argv0+ value for +you, so it may not be able to find it's data files. + +VIPS also offers the optional: + +\begin{verbatim} +GOptionGroup *im_get_option_group( void ); +\end{verbatim} + +You can use this with GOption to parse your program's command-line arguments. +It adds several useful VIPS flags, including \verb+--vips-concurrency+. + +\fref{fg:hello} shows both these functions in use. Again, the GOption stuff is +optional and just lets VIPS add some flags to your program. You do need the +\verb+im_init_world()+ though. + +\begin{fig2} +\begin{verbatim} +#include +#include + +static gboolean print_stuff; + +static GOptionEntry options[] = { + { "print", 'p', 0, G_OPTION_ARG_NONE, &print_stuff, + "print \"hello world!\"", NULL }, + { NULL } +}; + +int +main( int argc, char **argv ) +{ + GOptionContext *context; + GError *error = NULL; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + context = g_option_context_new( "- my program" ); + g_option_context_add_main_entries( context, + options, "main" ); + g_option_context_add_group( context, im_get_option_group() ); + if( !g_option_context_parse( context, &argc, &argv, &error ) ) { + if( error ) { + fprintf( stderr, "%s\n", error->message ); + g_error_free( error ); + } + error_exit( "try \"%s --help\"", g_get_prgname() ); + } + g_option_context_free( context ); + + if( print_stuff ) + printf( "hello, world!\n" ); + + return( 0 ); +} +\end{verbatim} +\caption{Hello World for VIPS} +\label{fg:hello} +\end{fig2} + +\subsection{Image descriptors} + +The base level of the VIPS I/O system provides \verb+IMAGE+ descriptors. +An image represented by a descriptor may be an image file on disc, an area +of memory that has been allocated for the image, an output file, a delayed +computation, and so on. Programs need (usually) only know that they have +a descriptor, they do not see many of the details. \fref{fg:image} +shows the definition of the \verb+IMAGE+ descriptor. + +\begin{fig2} +\begin{verbatim} +typedef struct { + /* Fields from image header. + */ + int Xsize; /* Pels per line */ + int Ysize; /* Lines */ + int Bands; /* Number of bands */ + int Bbits; /* Bits per band */ + int BandFmt; /* Band format */ + int Coding; /* Coding type */ + int Type; /* Type of file */ + float XRes; /* Horizontal res in pels/mm */ + float YRes; /* Vertical res in pels/mm */ + int Length; /* Obsolete (unused) */ + short Compression; /* Obsolete (unused) */ + short Level; /* Obsolete (unused) */ + int Xoffset; /* Position of origin */ + int Yoffset; + + /* Derived fields that may be read by the user. + */ + char *filename; /* File name */ + struct time_info *time;/* Timing for eval callback */ + int kill; /* Set to non-zero to block eval */ + + ... and lots of other private fields used by VIPS for + ... housekeeping. +} IMAGE; +\end{verbatim} +\caption{The \texttt{IMAGE} descriptor} +\label{fg:image} +\end{fig2} + +The first set of fields simply come from the image file header: +see \pref{sec:header} for a full description of all the fields. The next +set are maintained for you by the VIPS I/O system. \verb+filename+ is the +name of the file that this image came from. If you have attached an eval +callback to this image, \verb+time+ points to a set of timing statistics +which can be used by user-interfaces built on VIPS to provide feedback +about the progress of evaluation --- see \pref{sec:eval}. Finally, if you +set \verb+kill+ to non-zero, VIPS will block any pipelines which use this +descriptor as an intermediate. See \pref{sec:block}. + +The remaining fields are private and are used by VIPS for housekeeping. + +\subsection{Header fields} +\label{sec:fields} + +You can access header fields either directly (as \verb+im->Xsize+, for +example) or programmatically with \verb+im_header_int()+ and friends. For +example: + +\begin{verbatim} +int i; + +im_header_int( im, "Xsize", &i ); +\end{verbatim} + +There's also \verb+im_header_map()+ to loop over header fields, and +\verb+im_header_get_type+ to test the type of fields. These functions work for +image meta fields as well, see \pref{sec:meta}. + +\subsection{Opening and closing} + +Descriptors are created with \verb+im_open()+. This takes a file name +and a string representing the mode with which the descriptor is to be opened: + +\begin{verbatim} +IMAGE *im_open( const char *filename, + const char *mode ) +\end{verbatim} + +The possible values for mode are: + +\begin{description} + +\item[\texttt{"r"}] +The file is opened read-only. +If you open a non-VIPS image, or a VIPS image written on a machine with a +different byte ordering, \verb+im_open()+ will automatically convert it to +native VIPS format. If the underlying file does not support random access +(JPEG, for example), the entire file will be converted in memory. + +VIPS can read images in TIFF, JPEG, PPM/\-PGM/\-PBM, PNG and VIPS +format, all in both big- and little-endian varieties. You can control the +details of the conversion with extra characters embedded in the filename. For +example: + +\begin{verbatim} +fred = im_open( "fred.tif:2", + "r" ); +\end{verbatim} + +\noindent +will read page 2 of a multi-page TIFF. See the man pages for details. + +If VIPS has been built with libMagick, it can also read any of the 80 or so +libMagick-supported image file formats. + +\item[\texttt{"w"}] +An \verb+IMAGE+ descriptor is created which, when written to, will write +pixels to disc in the specified file. + +VIPS looks at the filename suffix to determine the save format. If there +is no suffix, or the filename ends in \verb+".v"+, the image is written +in VIPS native format. + +If the filename ends in \verb+".tif"+ or \verb+".tiff"+, the image +is written with \verb+im_vips2tiff()+. If the filename ends in +\verb+".jpg"+, \verb+".jpeg"+ or \verb+".jpe"+, the image is written with +\verb+im_vips2jpg()+. If the filename ends with \verb+".pbm"+, \verb+".pgm"+ +or \verb+".ppm"+, the image is written using \verb+im_vips2ppm()+. +If the filename ends with \verb+".png"+, the image is written using +\verb+im_vips2png()+. Case is not considered when testing the suffix. + +If you want to control the details of the conversion to the disc format (such +as setting the Q factor for a JPEG, for example), you embed extra control +characters in the filename. For example: + +\begin{verbatim} +fred = im_open( "fred.jpg:95", + "w" ); +\end{verbatim} + +\noindent +writes to \verb+fred+ will write a JPEG with Q 95. Again, see the man pages +for the conversion functions for details. + +\item[\texttt{"t"}] +As the \verb+"w"+ mode, but pels written to the descriptor will be saved +in a temporary memory buffer. + +\item[\texttt{"p"}] +This creates a special partial image. Partial images are used to join VIPS +operations together, see \pref{sec:joinup}. + +\item[\texttt{"rw"}] +As the \verb+"r"+ mode, but the image is mapped into the caller's address +space read-write. This mode is only provided for the use of paintbox-style +applications which need to directly modify an image. Most programs should +use the \verb+"w"+ mode for image output. + +\end{description} + +If an error occurs opening the image, \verb+im_open()+ calls +\verb+im_error()+ with a string describing the cause of the error and +returns \verb+NULL+. \verb+im_error()+ has type + +\begin{verbatim} +void im_error( const char *domain, + const char *format, ... ) +\end{verbatim} + +\noindent +The first argument is a string giving the name of the thing that raised +the error (just \verb+"im_open"+, for example). The format and subsequent +arguments work exactly as \verb+printf()+. It formats the message and +appends the string formed to the error log. You can get a pointer to the +error text with \verb+im_error_buffer()+. + +\begin{verbatim} +const char *im_error_buffer() +\end{verbatim} + +\noindent +Applications may display this string to give users feedback about the +cause of the error. The VIPS exit function, \verb+error_exit()+, prints +\verb+im_error_buffer()+ to \verb+stderr+ and terminates the program with an +error code of 1. + +\begin{verbatim} +void error_exit( const char *format, + ... ) +\end{verbatim} + +\noindent +There are other functions for handling errors: see the man page for +\verb+im_error()+. + +If the file name given to \verb+im_open()+ ends with \verb+".v"+, VIPS +looks in the same directory as the image for a file with the same name +but with the extension \verb+".desc"+. If present, this file is read in +and a pointer to the data put in the \verb+Hist+ field of the descriptor. See +the notes on \verb+im_updatehist()+ in \pref{sec:examples}. + +Descriptors are closed with \verb+im_close()+. It has type: + +\begin{verbatim} +int im_close( IMAGE *im ) +\end{verbatim} + +\verb+im_close()+ returns 0 on success and non-zero on error. +If the descriptor represents a disc file which +has been written to and whose name ends in \verb+".v"+, VIPS writes the +\verb+Hist+ field of the image descriptor to a file in the same directory +whose name ends in \verb+".desc"+. + +\subsection{Examples} +\label{sec:examples} + +As an example, \fref{fg:widthheight} will print the width and height +of an image stored on disc. + +\begin{fig2} +\begin{verbatim} +#include +#include + +int +main( int argc, char **argv ) +{ + IMAGE *im; + + /* Check arguments. + */ + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + if( argc != 2 ) + error_exit( "usage: %s filename", argv[0] ); + + /* Open file. + */ + if( !(im = im_open( argv[1], "r" )) ) + error_exit( "unable to open %s for input", argv[1] ); + + /* Process. + */ + printf( "width = %d, height = %d\n", im->Xsize, im->Ysize ); + + /* Close. + */ + if( im_close( im ) ) + error_exit( "unable to close %s", argv[1] ); + + return( 0 ); +} +\end{verbatim} +\label{fg:widthheight} +\caption{Print width and height of an image} +\end{fig2} + +To compile this example, use: + +\begin{verbatim} +cc `pkg-config vips-7.12 \ + --cflags --libs` myfunc.c +\end{verbatim} + +As a slightly more complicated example, \fref{fg:negative} +will calculate the photographic negative of an image. + +\begin{fig2} +\begin{verbatim} +#include +#include + +int +main( int argc, char **argv ) +{ + IMAGE *in, *out; + + /* Check arguments. + */ + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + if( argc != 3 ) + error_exit( "usage: %s infile outfile", argv[0] ); + + /* Open images for read and write, invert, update the history with our + * args, and close. + */ + if( !(in = im_open( argv[1], "r" )) || + !(out = im_open( argv[2], "w" )) || + im_invert( in, out ) || + im_updatehist( out, argc, argv ) || + im_close( in ) || + im_close( out ) ) + error_exit( argv[0] ); + + return( 0 ); +} +\end{verbatim} +\label{fg:negative} +\caption{Find photographic negative} +\end{fig2} + +\subsection{Metadata} +\label{sec:meta} + +VIPS lets you attach arbitrary metadata to an IMAGE. For example, ICC +profiles, EXIF tags, whatever you like. VIPS will efficiently propogate +metadata as images are processed (usually just by copying pointers) and will +automatically save and load metadata from VIPS files (see +\pref{sec:header}). + +A piece of metadata is a value and an identifying name. A set of +convenience functions let you set and get int, double, string and blob. For +example: + +\begin{verbatim} +int im_meta_set_int( IMAGE *, + const char *field, int ); +int im_meta_get_int( IMAGE *, + const char *field, int * ); +\end{verbatim} + +So you can do: + +\begin{verbatim} +if( im_meta_set_int( im, "poop", 42 ) ) + return( -1 ); +\end{verbatim} + +\noindent +to create an int called \verb+"poop"+, then at some later point (possibly much, +much later), in an image distantly derived from \verb+im+, you can use: + +\begin{verbatim} +int i; + +if( im_meta_get_int( im, "poop", &i ) ) + return( -1 ); +\end{verbatim} + +\noindent +And get the value 42 back. + +You can use \verb+im_meta_set()+ and \verb+im_meta_get()+ to attach arbitrary +\verb+GValue+ to images. +See the man page for \verb+im_meta_set()+ for full +details. + +You can test for a field being present with \verb+im_meta_get_type()+ (you'll +get \verb+G_TYPE_INT+ back for \verb+"poop"+, for example, or 0 if it is not +defined for this image). + +\subsection{History} +\label{sec:history} + +VIPS tracks the history of an image, that is, the sequence of operations which +have led to the creation of an image. You can view a VIPS image's history with +the \verb+header+ command, or with \nip{}'s \ct{View Header} menu. Whenever an +application performs an action, it should append a line of shell script to the +history which would perform the same action. + +The call to \verb+im_updatehist()+ in \fref{fg:negative} adds a line to the +image history noting the invocation of this program, its arguments, and the +time and date at which it was run. You may also find \verb+im_histlin()+ +helpful. It has type: + +\begin{verbatim} +void im_histlin( IMAGE *im, + const char *fmt, ... ) +\end{verbatim} + +\noindent +It formats its arguments as \verb+printf()+ and appends the string formed +to the image history. + +You read an image's history with \verb+im_history_get()+. It returns the +entire history of an image, one action per line. No need to free the result. + +\begin{verbatim} +const char * + im_history_get( IMAGE *im ); +\end{verbatim} + +\subsection{Eval callbacks} +\label{sec:eval} + +VIPS lets you attach callbacks to image descriptors. These are functions +you provide which VIPS will call when certain events occur. See +\pref{sec:callback} for a full list. + +Eval callbacks are called repeatedly during evaluation and can be used by +user-interface programs to give feedback about the progress of evaluation. + +\subsection{Detailed rules for descriptors} + +These rules are intended to answer awkward questions. + +\begin{enumerate} + +\item +You can output to a descriptor only once. + +\item +You can use a descriptor as an input many times. + +\item +You can only output to a descriptor that was opened with modes \verb+"w"+, +\verb+"t"+ and \verb+"p"+. + +\item +You can only use a descriptor as input if it was opened with modes \verb+"r"+ +or \verb+"rw"+. + +\item +If you have output to a descriptor, you may subsequently use it as an +input. \verb+"w"+ descriptors are automatically changed to \verb+"r"+ +descriptors. + +If the function you are passing the descriptor to uses WIO (see +\pref{sec:limit}), then \verb+"p"+ descriptors become \verb+"t"+. +If the function you are passing the descriptor to uses PIO, then \verb+"p"+ +descriptors are unchanged. + +\end{enumerate} + +\subsection{Automatic resource deallocation} + +VIPS lets you allocate resources local to an image descriptor, that is, +when the descriptor is closed, all resources which were allocated local to +that descriptor are automatically released for you. + +\subsubsection{Local image descriptors} + +VIPS provides a function which will open a new image local to +an existing image. \verb+im_open_local()+ has type: + +\begin{verbatim} +IMAGE *im_open_local( IMAGE *im, + const char *filename, + const char *mode ) +\end{verbatim} + +It behaves exactly as \verb+im_open()+, except that you do not need to close +the descriptor it returns. It will be closed automatically when its parent +descriptor \verb+im+ is closed. + +\begin{fig2} +\begin{verbatim} +/* Add another image to the accumulated total. + */ +static int +sum1( IMAGE *acc, IMAGE **in, int nin, IMAGE *out ) +{ + IMAGE *t; + + if( nin == 0 ) + /* All done ... copy to out. + */ + return( im_copy( acc, out ) ); + + /* Make a new intermediate, and add to it.. + */ + return( !(t = im_open_local( out, "sum1:1", "p" )) || + im_add( acc, in[0], t ) || + sum1( t, in + 1, nin - 1, out ) ); +} + +/* Sum the array of images in[]. nin is the number of images in + * in[], out is the descriptor we write the final image to. + */ +int +total( IMAGE **in, int nin, IMAGE *out ) +{ + /* Check that we have at least one image. + */ + if( nin <= 0 ) { + im_error( "total", "nin should be > 0" ); + return( -1 ); + } + + /* More than 1, sum recursively. + */ + return( sum1( in[0], in + 1, nin - 1, out ) ); +} +\end{verbatim} +\caption{Sum an array of images} +\label{fg:addemup} +\end{fig2} + +\fref{fg:addemup} is a function which will sum an array of images. +We need never close any of the (unknown) number of intermediate images which +we open. They will all be closed for us by our caller, when our caller +finally closes \verb+out+. VIPS lets local images themselves have local +images and automatically makes sure that all are closed in the correct order. + +It is very important that these intermediate images are made local to +\verb+out+ rather than \verb+in+, for reasons which should become apparent +in the section on combining operations below. + +There's also \verb+im_open_local_array()+ for when you need a lot of local +descriptors, see the man page. + +\subsubsection{Local memory allocation} +\label{sec:malloc} + +VIPS includes a set of functions for memory allocation local to an image +descriptor. The base memory allocation function is \verb+im_malloc()+. It +has type: + +\begin{verbatim} +void *im_malloc( IMAGE *im, + size_t size ) +\end{verbatim} + +It operates exactly as the standard \verb+malloc()+ C library function, +except that the area of memory it allocates is local to image descriptor +\verb+im+. If \verb+im_malloc()+ is unable to allocate memory, it returns +\verb+NULL+. If you pass \verb+NULL+ instead of a valid image descriptor, +then \verb+im_malloc()+ allocates memory globally and you must free it +yourself at some stage. + +To free memory explicitly, use \verb+im_free()+: + +\begin{verbatim} +int im_free( void *mem ) +\end{verbatim} + +\noindent +\verb+im_free()+ always returns 0, so you can use it as an argument to a +callback. + +Three macros make memory allocation even easier. \verb+IM_NEW()+ allocates +a new object. You give it a descriptor and a type, and it returns a pointer +to enough space to hold an object of that type. It has type: + +\begin{verbatim} +type-name *IM_NEW( IMAGE *im, type-name ) +\end{verbatim} + +The second macro, \verb+IM_ARRAY()+, is very similar, but allocates +space for an array of objects. Note that, unlike the usual \verb+calloc()+ +C library function, it does not initialise the array to zero. It has type: + +\begin{verbatim} +type-name *IM_ARRAY( IMAGE *im, + int n, type-name ) +\end{verbatim} + +Finally, \verb+IM_NUMBER()+ returns the number of elements in an array of +defined size. See the man pages for a series of examples, or +see \pref{sec:number}. + +\subsubsection{Other local operations} + +The above facilities are implemented with the VIPS core function +\verb+im_add_close_callback()+. You can use this facility to make your own +local resource allocators for other types of object --- see the manual page +for more help. + +\subsection{Error handling} + +All VIPS operations return 0 on success and non-zero on error, setting +\verb+im_error()+. As a consequence, when a VIPS function fails, you do not +need to generate an error message --- you can simply propogate the error back +up to your caller. If however you detect some error yourself (for example, +the bad parameter in the example above), you must call \verb+im_error()+ +to let your caller know what the problem was. + +VIPS provides two more functions for error message handling: \verb+im_warn()+ +and \verb+im_diag()+. These are intended to be used for less serious +messages, as their names suggest. Currently, they simply format and print +their arguments to \verb+stderr+, optionally supressed by the setting of an +environment variable. Future releases of VIPS may allow more sophisticated +trapping of these functions to allow their text to be easily presented to +the user by VIPS applications. See the manual pages. + +\subsection{Joining operations together} +\label{sec:joinup} + +VIPS lets you join image processing operations together so that they +behave as a single unit. \fref{fg:join} shows the definition of the +function \verb+im_Lab2disp()+ from the VIPS library. This function converts +an image in \cielab{} colour space to an RGB image for a monitor. The +monitor characteristics (gamma, phosphor type, etc.) are described by the +\verb+im_col_display+ structure, see the man page for \verb+im_col_XYZ2rgb()+. + +\begin{fig2} +\begin{verbatim} +int +im_Lab2disp( IMAGE *in, IMAGE *out, struct im_col_display *disp ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_Lab2disp:1", "p" )) || + im_Lab2XYZ( in, t1 ) || + im_XYZ2disp( t1, out, disp ) ) + return( -1 ); + + return( 0 ); +} +\end{verbatim} +\caption{Two image-processing operations joined together} +\label{fg:join} +\end{fig2} + +The special \verb+"p"+ mode (for partial) used to open the image descriptor +used as the intermediate image in this function `glues' the two operations +together. When you use \verb+im_Lab2disp()+, the two operations inside it +will execute together and no extra storage is necessary for the intermediate +image (\verb+t1+ in this example). This is important if you want to process +images larger than the amount of RAM you have on your machine. + +As an added bonus, if you have more than one CPU in your computer, the work +will be automatically spread across the processors for you. You can control +this parallelisation with the \verb+IM_CONCURRENCY+ environment variable and +with \verb+im_concurrency_set()+. See the man page for \verb+im_generate()+. + +\subsubsection{How it works} + +When a VIPS function is asked to output to a \verb+"p"+ image descriptor, +all the fields in the descriptor are set (the output image size and type +are set, for example), but no image data is actually generated. Instead, +the function attaches callbacks to the image descriptor which VIPS can use +later to generate any piece of the output image that might be needed. + +When a VIPS function is asked to output to a \verb+"w"+ or a \verb+"t"+ +descriptor (a real disc file or a real memory buffer), it evaluates +immediately and its evaluation in turn forces the evaluation of any earlier +\verb+"p"+ images. + +In the example in \fref{fg:join}, whether or not any pixels are really +processed when \verb+im_Lab2disp()+ is called depends upon the mode in +which \verb+out+ was opened. If \verb+out+ is also a partial image, then +no pixels will be calculated --- instead, a pipeline of VIPS operations +will be constructed behind the scenes and attached to \verb+out+. + +Conversely, if \verb+out+ is a real image (that is, either \verb+"w"+ +or \verb+"t"+), then the final VIPS operation in the function +(\verb+im_XYZ2disp()+) will output the entire image to \verb+out+, causing +the earlier parts of \verb+im_Lab2disp()+ (and indeed possibly some earlier +pieces of program, if \verb+in+ was also a \verb+"p"+ image) to run. + +When a VIPS pipeline does finally evaluate, all of the functions in the +pipeline execute together, sucking image data through the system in small +pieces. As a consequence, no intermediate images are generated, large +amounts of RAM are not needed, and no slow disc I/O needs to be performed. + +Since VIPS partial I/O is demand-driven rather than data-driven this works +even if some of the operations perform coordinate transformations. We could, +for example, include a call to \verb+im_affine()+, which performs +arbitrary rotation and scaling, and everything would still work correctly. + +\subsubsection{Pitfalls with partials} + +To go with all of the benefits that partial image I/O brings, there are +also some problems. The most serious is that you are often not quite certain +when computation will happen. This can cause problems if you close an input +file, thinking that it is finished with, when in fact that file has not +been processed yet. Doing this results in dangling pointers and an almost +certain core-dump. + +You can prevent this from happening with careful use of +\verb+im_open_local()+. If you always open local to your output image, +you can be sure that the input will not be closed before the output has been +generated to a file or memory buffer. You do not need to be so careful with +non-image arguments. VIPS functions which take extra non-image arguments +(a matrix, perhaps) are careful to make their own copy of the object before +returning. + +\subsubsection{Non-image output} + +Some VIPS functions consume images, but make no image +output. \verb+im_stats()+ for example, scans an image calculating various +statistical values. When you use \verb+im_stats()+, it behaves as a data +sink, sucking image data through any earlier pipeline stages. + +\subsubsection{Calculating twice} + +In some circumstances, the same image data can be generated twice. +\fref{fg:thrmean} is a function which finds the mean value of an +image, and writes a new image in which pixels less than the mean are set to +0 and images greater than the mean are set to 255. + +\begin{fig2} +\begin{verbatim} +int +threshold_at_mean( IMAGE *in, IMAGE *out ) +{ + double mean; + + if( im_avg( in, &mean ) || + im_moreconst( in, out, mean ) ) + return( -1 ); + + return( 0 ); +} +\end{verbatim} +\caption{Threshold an image at the mean value} +\label{fg:thrmean} +\end{fig2} + +This seems straightforward --- but consider if image \verb+in+ were a +\verb+"p"+, and represented the output of a large pipeline of operations. The +call to \verb+im_avg()+ would force the evaluation of the entire pipeline, +and throw it all away, keeping only the average value. The subsequent call to +\verb+im_moreconst()+ will cause the pipeline to be evaluated a second time. + +When designing a program, it is sensible to pay attention to these +issues. It might be faster, in some cases, to output to a file before +calling \verb+im_avg()+, find the average of the disc file, and then run +\verb+im_moreconst()+ from that. + +\subsubsection{Blocking computation} +\label{sec:block} + +\verb+IMAGE+ descriptors have a flag called \verb+kill+ which can be used +to block computation. If \verb+im->kill+ is set to a non-zero value, +then any VIPS pipelines which use \verb+im+ as an intermediate will fail +with an error message. This is useful for user-interface writers --- +suppose your interface is forced to close an image which many other images +are using as a source of data. You can just set the \verb+kill+ flag in all +of the deleted image's immediate children and prevent any dangling pointers +from being followed. + +\subsubsection{Limitations} +\label{sec:limit} + +Not all VIPS operations are partial-aware. These non-partial operations +use a pre-VIPS7.0 I/O scheme in which the whole of the input image has to +be present at the same time. In some cases, this is because partial I/O +simply makes no sense --- for example, a Fourier Transform can produce no +output until it has seen all of the input. \verb+im_fwfft()+ is therefore +not a partial operation. In other cases, we have simply not got around to +rewriting the old non-partial operation in the newer partial style. + +You can mix partial and non-partial VIPS operations freely, without worrying +about which type they are. The only effect will be on the time your pipeline +takes to execute, and the memory requirements of the intermediate images. VIPS +uses the following rules when you mix the two styles of operation: + +\begin{enumerate} + +\item +When a non-partial operation is asked to output to a partial image descriptor, +the \verb+"p"+ descriptor is magically transformed into a \verb+"t"+ +descriptor. + +\item +When a non-partial operation is asked to read from a \verb+"p"+ descriptor, +the \verb+"p"+ descriptor is turned into a \verb+"t"+ type, and any earlier +stages in the pipeline forced to evaluate into that memory buffer. + +The non-partial operation then processes from the memory buffer. + +\end{enumerate} + +These rules have the consequence that you may only process very large images +if you only use partial operations. If you use any non-partial operations, +then parts of your pipelines will fall back to old whole-image I/O and you +will need to think carefully about where your intermediates should be stored. + diff --git a/doc/src/ipio.tex b/doc/src/ipio.tex new file mode 100644 index 00000000..c0f39806 --- /dev/null +++ b/doc/src/ipio.tex @@ -0,0 +1,56 @@ +\section{Programming in-place functions} + +VIPS includes a little support for in-place functions --- functions +which operate directly on an image, both reading and writing from the same +descriptor via the data pointer. This is an extremely dangerous way to +handle IO, since any bugs in your program will trash your input image. + +Operations of this type should call \verb+im_rwcheck()+ instead of +\verb+im_incheck()+. \verb+im_rwcheck()+ tries to get a descriptor ready +for in-place writing. For example, a function which cleared an image to +black might be written as: + +\begin{verbatim} +#include +#include + +#include + +int +black_inplace( IMAGE *im ) +{ + /* Check that we can RW to im. + */ + if( im_rwcheck( im ) ) + return( -1 ); + + /* Zap the image! + */ + memset( im->data, 0, + IM_IMAGE_SIZEOF_LINE( im ) * + im->Ysize ); + + return( 0 ); +} +\end{verbatim} + +This function might be called from an application as: + +\begin{verbatim} +#include +#include + +#include + +void +zap( char *name ) +{ + IMAGE *im; + + if( !(im = im_open( name, "rw" )) || + black_inplace( im ) || + im_updatehist( im, "zap image" ) || + im_close( im ) ) + error_exit( "failure!" ); +} +\end{verbatim} diff --git a/doc/src/mydefs.tex b/doc/src/mydefs.tex new file mode 100644 index 00000000..5310b8ce --- /dev/null +++ b/doc/src/mydefs.tex @@ -0,0 +1,113 @@ +% My defs + +% Computer Text, Computer text=>, Computer Text Display +\newcommand{\ct}[1]{\texttt{#1}} +\newcommand{\ctr}[1]{\ct{#1} / } + +\newenvironment{ctd}{\begin{quote}\footnotesize\tt}{\end{quote}} +\pagecolor{white} + +% abbreviations +\newcommand{\vips}{\ct{vips}} +\newcommand{\nip}{\ct{nip2}} +\newcommand{\bs}{$\backslash$} +\newcommand{\rtp}{\^{ }} +\newcommand{\cielab}{\emph{CIE~}$L^{*}a^{*}b^{*}$} +\newcommand{\ciexyz}{\emph{CIE XYZ}} +\newcommand{\cieyxy}{\emph{CIE Yxy}} +\newcommand{\cielch}{\emph{CIE LCh}} +\newcommand{\cieucs}{\emph{UCS(1:1)}} +\newcommand{\cross}{$\times{}$} + +% make a label ... override this for HTML output +\newcommand{\mylabel}[1]{\label{#1}} + +% generate " on page xx" if a label is referring to something on another page +% override this for HTML output +\newcounter{boink} +\newcommand{\onpage}[1]{% +\addtocounter{boink}{1}% +\label{atref\theboink{}}% +\ifthenelse{\pageref{atref\theboink{}}=\pageref{#1}}% +{}% +{ on page~\pageref{#1}}} + +% format a reference to a section .. "$3.11 on page 37" +\newcommand{\pref}[1]{\S\ref{#1}\onpage{#1}} +\newcommand{\tref}[1]{Table~\ref{#1}\onpage{#1}} +\newcommand{\fref}[1]{Figure~\ref{#1}\onpage{#1}} +\newcommand{\cref}[1]{Chapter~\ref{#1}\onpage{#1}} +\newcommand{\aref}[1]{Appendix~\ref{#1}\onpage{#1}} + +% Insert a file ... height and name. +\newcommand{\fig}[2]{ + \begin{center} + \includegraphics[height=#1]{figs/#2} + \end{center} +} + +% Insert a file ... width and name. +\newcommand{\figw}[2]{ + \begin{center} + \includegraphics[width=#1]{figs/#2} + \end{center} +} + +% make a 2-column figure ... define our own so we can easily override in html +% output +\newenvironment{fig2}{\begin{figure*}}{\end{figure*}} + +% same for 2-col tables +\newenvironment{tab2}{\begin{table*}}{\end{table*}} + +% environment for setting ip defs +\newenvironment{ipdef}{ +\par +\samepage +\begin{ctd} +\begin{tabular}{@{\hspace{0.2em}}ll@{\hspace{0.2em}}ll@{\hspace{0.2em}}ll@{\hspace{0.2em}}ll@{\hspace{0.2em}}ll@{\hspace{0.2em}}ll@{\hspace{0.2em}}ll@{\hspace{0.2em}}lllllllllllllllllllllllllllll} +~~~ & ~ & ~~~ & ~ & ~~~ & ~ & ~~~ & ~ & ~~~ & ~ & ~~~ & ~ & ~~~ & ~ & ~~~ & \\[-1.3em] +}{ +\end{tabular} +\end{ctd} +\par +} + +% causes problems for htlatex :-( +% make this a noop for now +% \newcommand{\dtxt}[1]{\multicolumn{25}{@{\hspace{0.2em}}l}{#1}} +\newcommand{\dtxt}[1]{#1} + +% Insert a blank page +\newcommand{\blankpage}{% +\newpage +~~~~ +\pagestyle{plain} +\newpage +% Another one necessary in twocolumn mode +~~~~ +\newpage +\pagestyle{fancy} +} + +%\addtolength{\headheight}{3pt} + +% Make text a bit wider, since we are two column. +\addtolength{\textwidth}{0.5in} +\addtolength{\oddsidemargin}{-0.25in} +\addtolength{\evensidemargin}{-0.25in} + +% twocolumn seems to remove the binding offset ... add it back +%\addtolength{\oddsidemargin}{-0.2in} +%\addtolength{\evensidemargin}{0.2in} + +% More space between headers and footers and the body +\addtolength{\topmargin}{-0.5em} +\addtolength{\headsep}{0.5em} +\addtolength{\footskip}{0.5em} + +% Swap left and right binding offsets +\newlength{\fred} +\setlength{\fred}{\oddsidemargin} +\setlength{\oddsidemargin}{\evensidemargin} +\setlength{\evensidemargin}{\fred} diff --git a/doc/src/operintro.tex b/doc/src/operintro.tex new file mode 100644 index 00000000..20ae3db3 --- /dev/null +++ b/doc/src/operintro.tex @@ -0,0 +1,109 @@ +\section{Introduction} +\mylabel{sec:oper} + +This chapter explains how to write image processing operations using the +VIPS image I/O (input-output) system. For background, you should probably +take a look at \pref{sec:appl}. This is supposed to be a tutorial, if you +need detailed information on any particular function, use the on-line UNIX +manual pages. + +\subsection{Why use VIPS?} + +If you use the VIPS image I/O system, you get a number of benefits: + +\begin{description} + +\item[Threading] +If your computer has more than one CPU, the VIPS I/O system will automatically +split your image processing operation into separate threads (provided you +use PIO, see below). You should get an approximately linear speed-up as +you add more CPUs. + +\item[Pipelining] +Provided you use PIO (again, see below), VIPS can automatically join +operations together. A sequence of image processing operations will all +execute together, with image data flowing through the processing pipeline +in small pieces. This makes it possible to perform complex processing on +very large images with no need to worry about storage management. + +\item[Composition] +Because VIPS can efficiently compose image processing operations, you can +implement your new operation in small, reusable, easy-to-understand +pieces. VIPS already has a lot of these: many new operations can be +implemented by simply composing existing operations. + +\item[Large files] +Provided you use PIO and as long as the underlying OS supports large files +(that is, files larger than 2GB), VIPS operations can work on files larger +than can be addressed with 32 bits on a plain 32-bit machine. VIPS operations +only see 32 bit addresses; the VIPS I/O system transparently maps these to +64 bit operations for I/O. Large file support is included on most unixes after +about 1998. + +\item[Abstraction] +VIPS operations see only arrays of numbers in native format. Details of +representation (big/little endian, VIPS/TIFF/JPEG file format, etc.) are +hidden from you. + +\item[Interfaces] +Once you have your image processing operation implemented, it automatically +appears in all of the VIPS interfaces. VIPS comes with a GUI (\nip{}), a +UNIX command-line interface (\vips{}) and a C++ and Python API. + +\item[Portability] +VIPS operations can be compiled on most unixes, Mac OS X and Windows NT, 2000 +and XP without modification. Mostly. + +\end{description} + +\subsection{I/O styles} + +The I/O system supports three styles of input-output. + +\begin{description} + +\item[Whole-image I/O (WIO)] +This style is a largely a left-over from VIPS 6.x. WIO image-processing +operations have all of the input image given to them in a large memory +array. They can read any of the input pels at will with simple pointer +arithmetic. + +\item[Partial-image I/O (PIO)] +In this style operations only have a small part of the input image available +to them at any time. When PIO operations are joined together into a pipeline, +images flow through them in small pieces, with all the operations in a +pipeline executing at the same time. + +\item[In-place] +The third style allows pels to be read and written anywhere in +the image at any time, and is used by the VIPS in-place operations, such +as \verb+im_fastline()+. You should only use it for operations which would +just be impossibly inefficient to write with either of the other two styles. + +\end{description} + +WIO operations are easy to program, but slow and inflexible when images +become large. PIO operations are harder to program, but scale well as images +become larger, and are automatically parallelized by the VIPS I/O system. + +If you can face it, and if your algorithm can be expressed in this way, you +should write your operations using PIO. Whichever you choose, applications +which call your operation will see no difference, except in execution speed. + +If your image processing operation performs no coordinate transformations, +that is, if your output image is the same size as your input image or images, +and if each output pixel depends only upon the pixel at the corresponding +position in the input images, then you can use the \verb+im_wrapone()+ +and \verb+im_wrapmany()+ operations. These take a simple buffer-processing +operation supplied by you and wrap it up as a full-blown PIO operation. +See~\pref{sec:wrapone}. + +\subsection{What's new in this version} + +The VIPS API is mostly unaltered since 7.3, so there are not many major +changes. I've just reworked the text, reformatted, fixed a few typos, +and changed the dates. + +VIPS has acquired some crud over the years. We are planning to clean all +this stuff up at some stage (and break backwards-compatibility). Maybe for +VIPS 8 :-( diff --git a/doc/src/packages.tex b/doc/src/packages.tex new file mode 100644 index 00000000..b72c2635 --- /dev/null +++ b/doc/src/packages.tex @@ -0,0 +1,822 @@ +\section{VIPS packages} +\mylabel{sec:packages} + +\subsection{Arithmetic} + +See \fref{fg:arithmetic}. + +Arithmetic functions work on images as if each band element were a separate +number. All operations are point-to-point --- each output element depends +exactly upon the corresponding input element. All (except in a few cases +noted in the manual pages) will work with images of any type (or any mixture +of types), of any size and of any number of bands. + +Arithmetic operations try to preserve precision by increasing the number of +bits in the output image when necessary. Generally, this follows the ANSI C +conventions for type promotion --- so multiplying two \verb+IM_BANDFMT_UCHAR+ +images together, for example, produces a \verb+IM_BANDFMT_USHORT+ image, and +taking the \verb+im_costra()+ of a \verb+IM_BANDFMT_USHORT+ image produces +a \verb+IM_BANDFMT_FLOAT+ image. The details of the type conversions are +in the manual pages. + +\begin{fig2} +\begin{verbatim} +john% vips --list arithmetic +im_abs - absolute value +im_acostra - acos of image (result in degrees) +im_add - add two images +im_asintra - asin of image (result in degrees) +im_atantra - atan of image (result in degrees) +im_avg - average value of image +im_point_bilinear - interpolate value at single point, linearly +im_ceil - round to smallest integal value not less than +im_cmulnorm - multiply two complex images, normalising output +im_costra - cos of image (angles in degrees) +im_deviate - standard deviation of image +im_divide - divide two images +im_exp10tra - 10^pel of image +im_expntra - x^pel of image +im_expntra_vec - [x,y,z]^pel of image +im_exptra - e^pel of image +im_fav4 - average of 4 images +im_floor - round to largest integal value not greater than +im_gadd - calculate a*in1 + b*in2 + c = outfile +im_invert - photographic negative +im_lintra - calculate a*in + b = outfile +im_linreg - pixelwise linear regression +im_lintra_vec - calculate a*in + b -> out, a and b vectors +im_litecor - calculate max(white)*factor*(in/white), if clip == 1 +im_log10tra - log10 of image +im_logtra - ln of image +im_max - maximum value of image +im_maxpos - position of maximum in image +im_maxpos_avg - position of maximum, averaging in case of draw +im_measure - measure averages of a grid of patches +im_min - minimum value of image +im_minpos - position of minimum value of image +im_multiply - multiply two images +im_powtra - pel^x ofbuildimage +im_powtra_vec - pel^[x,y,z] of image +im_remainder - remainder after integer division +im_remainderconst - remainder after integer division by a constant +im_remainderconst_vec - remainder after integer division by a vector +im_rint - round to nearest integal value +im_sign - unit vector in direction of value +im_sintra - sin of image (angles in degrees) +im_stats - many image statistics in one pass +im_subtract - subtract two images +im_tantra - tan of image (angles in degrees) +\end{verbatim} +\caption{Arithmetic functions} +\label{fg:arithmetic} +\end{fig2} + +\subsection{Relational} + +See \fref{fg:relational}. + +Relational functions compare images to other images or to constants. They +accept any image or pair of images (provided they are the same size and +have the same number of bands --- their types may differ) and produce a +\verb+IM_BANDFMT_UCHAR+ image with the same number of bands as the input +image, with 255 in every band element for which the condition is true and +0 elsewhere. + +They may be combined with the boolean functions to form complex relational +conditions. Use \verb+im_max()+ (or \verb+im_min()+) to find out if a +condition is true (or false) for a whole image. + +\begin{fig2} +\begin{verbatim} +john% vips --list relational +im_blend - use cond image to blend between images in1 and in2 +im_equal - two images equal in value +im_equal_vec - image equals doublevec +im_equalconst - image equals const +im_ifthenelse - use cond image to choose pels from image in1 or in2 +im_less - in1 less than in2 in value +im_less_vec - in less than doublevec +im_lessconst - in less than const +im_lesseq - in1 less than or equal to in2 in value +im_lesseq_vec - in less than or equal to doublevec +im_lesseqconst - in less than or equal to const +im_more - in1 more than in2 in value +im_more_vec - in more than doublevec +im_moreconst - in more than const +im_moreeq - in1 more than or equal to in2 in value +im_moreeq_vec - in more than or equal to doublevec +im_moreeqconst - in more than or equal to const +im_notequal - two images not equal in value +im_notequal_vec - image does not equal doublevec +im_notequalconst - image does not equal const +\end{verbatim} +\caption{Relational functions} +\label{fg:relational} +\end{fig2} + +\subsection{Boolean} + +See \fref{fg:boolean}. + +The boolean functions perform boolean arithmetic on pairs of +\verb+IM_BANDFMT_UCHAR+ images. They are useful for combining the results of +the relational and morphological functions. You can use +\verb+im_eorconst()+ with 255 as \verb+im_not()+. + +\begin{fig2} +\begin{verbatim} +john% vips --list boolean +im_andimage - bitwise and of two images +im_andimageconst - bitwise and of an image with a constant +im_andimage_vec - bitwise and of an image with a vector constant +im_orimage - bitwise or of two images +im_orimageconst - bitwise or of an image with a constant +im_orimage_vec - bitwise or of an image with a vector constant +im_eorimage - bitwise eor of two images +im_eorimageconst - bitwise eor of an image with a constant +im_eorimage_vec - bitwise eor of an image with a vector constant +im_shiftleft - shift integer image n bits to left +im_shiftright - shift integer image n bits to right +\end{verbatim} +\caption{Boolean functions} +\label{fg:boolean} +\end{fig2} + +\subsection{Colour} +\label{sec:colour} + +See \fref{fg:colour}. + +The colour functions can be divided into two main types. First, functions to +transform images between the different colour spaces supported by VIPS: +\verb+RGB+ (also referred to as \verb+disp+), \verb+sRGB+, \verb+XYZ+, +\verb+Yxy+, \verb+Lab+, \verb+LabQ+, \verb+LabS+, \verb+LCh+ and +\verb+UCS+), and second, functions for calculating colour difference +metrics. Figure~\ref{fg:convert} shows how the VIPS colour spaces +interconvert. + +\begin{fig2} +\figw{5in}{interconvert.png} +\caption{VIPS colour space conversion} +\label{fg:convert} +\end{fig2} + +The colour spaces supported by VIPS are: + +\begin{description} + +\item[\texttt{LabQ}] +This is the principal VIPS colorimetric storage format. See the +man page for \verb+im_LabQ2Lab()+ for an explanation. You cannot perform +calculations on \verb+LabQ+ images. They are for storage only. Also refered +to as \verb+LABPACK+. + +\item[\texttt{LabS}] +This format represents coordinates in \cielab{} space as a three- +band \verb+IM_BANDFMT_SHORT+ image, scaled to fit the full range of bits. It is +the best format for computation, being relatively compact, quick, and +accurate. Colour values expressed in this way are hard to visualise. + +\item[\texttt{Lab}] +\verb+Lab+ colourspace represents \cielab{} colour values with a three-band +\verb+IM_BANDFMT_FLOAT+ image. This is the simplest format for general work: adding the +constant 50 to the L channel, for example, has the expected result. + +\item[\texttt{XYZ}] +\ciexyz{} colour space represented as a three-band \verb+IM_BANDFMT_FLOAT+ +image. + +\item[\texttt{XYZ}] +\cieyxy{} colour space represented as a three-band \verb+IM_BANDFMT_FLOAT+ +image. + +\item[\texttt{RGB}] +(also refered to as \verb+disp+) This format is similar to the RGB colour +systems used in other packages. If you want to export your image to a PC, +for example, convert your colorimetric image to \verb+RGB+, then turn it +to TIFF with \verb+im_vips2tiff()+. You need to supply a structure which +characterises your display. See the manual page for \verb+im_col_XYZ2rgb()+ +for hints on these guys. + +VIPS also supports \verb+sRGB+. This is a version of RGB with a carefully +defined and standard conversion from XYZ. See: + +\begin{verbatim} +http://www.color.org/ +\end{verbatim} + +\item[\texttt{LCh}] +Like \verb+Lab+, but rectangular $ab$ coordinates are replaced with polar $Ch$ +(Chroma and hue) coordinates. Hue angles are expressed in degrees. + +\item[\texttt{UCS}] +A colour space based on the CMC(1:1) colour difference measurement. This +is a highly uniform colour space, much better than \cielab{} for expressing +small differences. Conversions to and from \verb+UCS+ are extremely slow. + +\end{description} + +All VIPS colourspaces assume a D65 illuminant. + +The colour-difference functions calculate either $\Delta{}E$ \cielab{} (1976 +or 2000) or $\Delta{}E$ CMC(1:1) on two images in \verb+Lab+, \verb+XYZ+ +or \verb+disp+ colour space. + +\begin{fig2} +\begin{verbatim} +example% vips --list colour +im_LCh2Lab - convert LCh to Lab +im_LCh2UCS - convert LCh to UCS +im_Lab2LCh - convert Lab to LCh +im_Lab2LabQ - convert Lab to LabQ +im_Lab2LabS - convert Lab to LabS +im_Lab2UCS - convert Lab to UCS +im_Lab2XYZ - convert D65 Lab to XYZ +im_Lab2XYZ_temp - convert Lab to XYZ, with a specified colour temperature +im_Lab2disp - convert Lab to displayable +im_LabQ2LabS - convert LabQ to LabS +im_LabQ2Lab - convert LabQ to Lab +im_LabQ2XYZ - convert LabQ to XYZ +im_LabQ2disp - convert LabQ to displayable +im_LabS2LabQ - convert LabS to LabQ +im_LabS2Lab - convert LabS to Lab +im_UCS2LCh - convert UCS to LCh +im_UCS2Lab - convert UCS to Lab +im_UCS2XYZ - convert UCS to XYZ +im_XYZ2Lab - convert D65 XYZ to Lab +im_XYZ2Lab_temp - convert XYZ to Lab, with a specified colour temperature +im_XYZ2UCS - convert XYZ to UCS +im_XYZ2Yxy - convert XYZ to Yxy +im_XYZ2disp - convert XYZ to displayble +im_XYZ2sRGB - convert XYZ to sRGB +im_Yxy2XYZ - convert Yxy to XYZ +im_dE00_fromLab - calculate delta-E CIE2000 for two Lab images +im_dECMC_fromLab - calculate delta-E CMC(1:1) for two Lab images +im_dECMC_fromdisp - calculate delta-E CMC(1:1) for two displayable images +im_dE_fromLab - calculate delta-E for two Lab images +im_dE_fromXYZ - calculate delta-E for two XYZ images +im_dE_fromdisp - calculate delta-E for two displayable images +im_disp2Lab - convert displayable to Lab +im_disp2XYZ - convert displayable to XYZ +im_icc_ac2rc - convert LAB from AC to RC using a profile +im_icc_export - convert LAB to 8-bit device with a profile +im_icc_export_depth - convert LAB to device with a profile +im_icc_import - convert device to LAB with a profile +im_icc_import_embedded - convert device to LAB using the embedded profile +im_icc_present - test for presence of ICC library +im_icc_transform - convert between two device images with a pair of profiles +im_lab_morph - morph colourspace of a LAB image +im_sRGB2XYZ - convert sRGB to XYZ +\end{verbatim} +\caption{Colour functions} +\label{fg:colour} +\end{fig2} + +\subsection{Conversion} + +See \fref{fg:conversion}. + +These functions may be split into three broad groups: functions which convert +between the VIPS numeric formats (\verb+im_clip2fmt()+, for example, converts +an image of any type to the specified \verb+IM_BANDFMT+), functions +supporting complex arithmetic (\verb+im_c2amph()+, for example, converts +a complex image from rectangular to polar co ordinates) and functions +which perform some simple geometric conversion (\verb+im_extract()+ forms +a sub-image). + +\verb+gbandjoin+ and the C function \verb+im_gbandjoin()+ will do a bandwise +join of many images at the same time. See the manual pages. + +\begin{fig2} +\begin{verbatim} +example% vips --list conversion +im_bandjoin - bandwise join of two images +im_bernd - extract from pyramid as jpeg +im_black - generate black image +im_c2amph - convert real and imaginary to phase and amplitude +im_c2imag - extract imaginary part of complex image +im_c2ps - find power spectrum of complex image +im_c2real - extract real part of complex image +im_c2rect - convert phase and amplitude to real and imaginary +im_clip2c - convert to signed 8-bit integer +im_clip2cm - convert to complex +im_clip2d - convert to double-precision float +im_clip2dcm - convert to double complex +im_clip2f - convert to single-precision float +im_clip2fmt - convert image format to ofmt +im_clip2i - convert to signed 32-bit integer +im_clip2s - convert to signed 16-bit integer +im_clip2ui - convert to unsigned 32-bit integer +im_clip2us - convert to unsigned 16-bit integer +im_clip - convert to unsigned 8-bit integer +im_copy - copy image +im_copy_morph - copy image, setting pixel layout +im_copy_swap - copy image, swapping byte order +im_copy_set - copy image, setting informational fields +im_copy_set_meta - copy image, setting a meta field +im_csv2vips - read a file in csv format +im_extract_area - extract area +im_extract_areabands - extract area and bands +im_extract_band - extract band +im_extract_bands - extract several bands +im_extract - extract area/band +im_falsecolour - turn luminance changes into chrominance changes +im_fliphor - flip image left-right +im_flipver - flip image top-bottom +im_gbandjoin - bandwise join of many images +im_grid - chop a tall thin image into a grid of images +im_insert - insert sub-image into main image at position +im_insert_noexpand - insert sub-image into main image at position, no expansion +im_jpeg2vips - convert from jpeg +im_lrjoin - join two images left-right +im_magick2vips - load file with libMagick +im_mask2vips - convert DOUBLEMASK to VIPS image +im_msb - convert to uchar by discarding bits +im_msb_band - convert to single band uchar by discarding bits +im_png2vips - convert PNG file to VIPS image +im_exr2vips - convert an OpenEXR file to VIPS +im_ppm2vips - read a file in pbm/pgm/ppm format +im_analyze2vips - read a file in analyze format +im_print - print string to stdout +im_recomb - linear recombination with mask +im_replicate - replicate an image horizontally and vertically +im_ri2c - join two non-complex images to form complex +im_rot180 - rotate image 180 degrees +im_rot270 - rotate image 270 degrees clockwise +im_rot90 - rotate image 90 degrees clockwise +im_scale - scale image linearly to fit range 0-255 +im_scaleps - logarithmic scale of image to fit range 0-255 +im_rightshift_size - decrease size by a power-of-two factor +im_slice - slice an image using two thresholds +\end{verbatim} +\caption{Conversion functions} +\label{fg:conversion} +\end{fig2} + +\begin{fig2} +\begin{verbatim} +im_subsample - subsample image by integer factors +im_system - run command on image +im_tbjoin - join two images top-bottom +im_text - generate text image +im_thresh - slice an image at a threshold +im_tiff2vips - convert TIFF file to VIPS image +im_vips2csv - write an image in csv format +im_vips2jpeg - convert to jpeg +im_vips2mask - convert VIPS image to DOUBLEMASK +im_vips2mimejpeg - convert to jpeg as mime type on stdout +im_vips2png - convert VIPS image to PNG file +im_vips2ppm - write a file in pbm/pgm/ppm format +im_vips2tiff - convert VIPS image to TIFF file +im_zoom - simple zoom of an image by integer factors +\end{verbatim} +\caption{Conversion functions (cont.)} +\end{fig2} + +\subsection{Matricies} + +See \fref{fg:matricies}. + +VIPS uses matricies for morphological operations, for convolutions, and +for some colour-space conversions. There are two types of matrix: integer +(\verb+INTMASK+) and double precision floating point (\verb+DOUBLEMASK+). + +For convenience, both types are stored in files as ASCII. The first +line of the file should start with the matrix dimensions, width first, +then on the same line an optional scale and offset. The two size fields +should be integers; the scale and offset may be floats. Subsequent lines +should contain the matrix elements, one row per line. The scale and +offset are the conventional ones used to represent non-integer values in +convolution masks --- in other words: + +\[ +result = {value \over scale} + offset +\] + +If the scale and offset are missing, they default to 1.0 and 0.0. See the +sections on convolution for more on the use of these fields. So as an example, +a 4 by 4 identity matrix would be stored as: + +\begin{verbatim} +4 4 +1 0 0 0 +0 1 0 0 +0 0 1 0 +0 0 0 1 +\end{verbatim} + +And a 3 by 3 mask for block averaging with convolution might be stored as: + +\begin{verbatim} +3 3 9 0 +1 1 1 +1 1 1 +1 1 1 +\end{verbatim} + +\noindent +(in other words, sum all the pels in every 3 by 3 area, and divide by 9). + +This matrix contains only integer elements and so could be used as an +argument to functions expecting both \verb+INTMASK+ and \verb+DOUBLEMASK+ +matricies. However, masks containing floating-point values (such as the +output of \verb+im_matinv()+) can only be used as arguments to functions +expecting \verb+DOUBLEMASK+s. + +A set of functions for mask input and output are also available for +C-programmers --- see the manual pages for \verb+im_read_dmask()+. For +other matrix functions, see also the convolution sections and the arithmetic +sections. + +\begin{fig2} +\begin{verbatim} +example% vips --list matrix +im_matcat - append matrix in2 to the end of matrix in1 +im_matinv - invert matrix +im_matmul - multiply matrix in1 by matrix in2 +im_mattrn - transpose matrix +\end{verbatim} +\caption{Matrix functions} +\label{fg:matricies} +\end{fig2} + +\subsection{Convolution} + +See \fref{fg:convolution}. + +The functions available in the convolution package can be split into five +main groups. + +First, are the convolution functions. The most useful function is +\verb+im_conv()+ which will convolve any non-complex type with an +\verb+INTMASK+ matrix. The output image will have the same size, type, and +number of bands as the input image. Of the other \verb+im_conv()+ functions, +functions whose name ends in \verb+_raw+ do not add a black border around the +output image, functions ending in \verb+f+ use a \verb+DOUBLEMASK+ matrix +and write float (or double) output, and functions containing \verb+sep+ +are for seperable convolutions. \verb+im_compass()+, \verb+im_lindetect()+ +and \verb+im_gradient()+ convolve with rotating masks. \verb+im_embed()+ +is used by the convolution functions to add the border to the output. + +Next, are the build functions. \verb+im_gauss_*mask()+ and its ilk +generate gaussian masks, \verb+im_log_*mask()+ generate logs of Laplacians. +\verb+im_addgnoise()+ and \verb+im_gaussnoise()+ create or add gaussian +noise to an image. + +Two functions do correlation: \verb+im_fastcor()+ does a quick and dirty +correlation, \verb+im_spcor()+ calculates true spatial correlation, and is +rather slow. + +Some functions are provided for analysing images: \verb+im_zerox()+ counts +zero-crossing points in an image, \verb+im_mpercent()+ finds a threshold +that will isolate a percentage of points in an image. + +Finally, \verb+im_resize_linear()+ and \verb+im_shrink()+ do as you would +expect. + +\begin{fig2} +\begin{verbatim} +example% vips --list convolution +im_addgnoise - add gaussian noise with mean 0 and std. dev. sigma +im_compass - convolve with 8-way rotating integer mask +im_contrast_surface - find high-contrast points in an image +im_contrast_surface_raw - find high-contrast points in an image +im_conv - convolve +im_conv_raw - convolve, no border +im_convf - convolve, with DOUBLEMASK +im_convf_raw - convolve, with DOUBLEMASK, no border +im_convsep - seperable convolution +im_convsep_raw - seperable convolution, no border +im_convsepf - seperable convolution, with DOUBLEMASK +im_convsepf_raw - seperable convolution, with DOUBLEMASK, no border +im_convsub - convolve uchar to uchar, sub-sampling by xskip, yskip +im_embed - embed in within a set of borders +im_fastcor - fast correlate in2 within in1 +im_fastcor_raw - fast correlate in2 within in1, no border +im_gauss_dmask - generate gaussian DOUBLEMASK +im_gauss_imask - generate gaussian INTMASK +im_gaussnoise - generate image of gaussian noise with specified statistics +im_gradient - convolve with 2-way rotating mask +im_rank_image - point-wise pixel rank +im_lindetect - convolve with 4-way rotating mask +im_log_dmask - generate laplacian of gaussian DOUBLEMASK +im_log_imask - generate laplacian of gaussian INTMASK +im_maxvalue - point-wise maximum value +im_mpercent - find threshold above which there are percent values +im_rank - rank filter nth element of xsize/ysize window +im_rank_raw - rank filter nth element of xsize/ysize window, no border +im_read_dmask - read matrix of double from file +im_resize_linear - resize to X by Y pixels with linear interpolation +im_rotate_dmask45 - rotate DOUBLEMASK clockwise by 45 degrees +im_rotate_dmask90 - rotate DOUBLEMASK clockwise by 90 degrees +im_rotate_imask45 - rotate INTMASK clockwise by 45 degrees +im_rotate_imask90 - rotate INTMASK clockwise by 90 degrees +im_sharpen - sharpen high frequencies of L channel of LabQ +im_shrink - shrink image by xfac, yfac times +im_spcor - normalised correlation of in2 within in1 +im_spcor_raw - normalised correlation of in2 within in1, no black padding +im_spcor2 - normalised correlation of in2 within in1 +im_spcor2_raw - normalised correlation of in2 within in1, no black padding +im_stretch3 - stretch 3%, sub-pixel displace by xdisp/ydisp +im_zerox - find +ve or -ve zero crossings in IM_BANDFMT_INT image +\end{verbatim} +\caption{Convolution functions} +\label{fg:convolution} +\end{fig2} + +\subsection{In-place operations} +\label{sec:inplace} + +See \fref{fg:inplace}. + +A few of the in-place operations are available from the command-line. Most are +not. More will be added. + +\begin{fig2} +\begin{verbatim} +example% vips --list inplace +im_circle - plot circle on image +im_flood_blob_copy - flood while pixel == start pixel +im_insertplace - draw image sub inside image main at position (x,y) +im_line - draw line between points (x1,y1) and (x2,y2) +im_lineset - draw line between points (x1,y1) and (x2,y2) +\end{verbatim} +\caption{In-place operations} +\label{fg:inplace} +\end{fig2} + +\subsection{Frequency filtering} + +See \fref{fg:freq}. + +The basic Fourier functions are \verb+im_fwfft()+ and +\verb+im_invfft()+, which calculate the fast-fourier transform and inverse +transform of an image. Also \verb+im_invfftr()+, which just returns the real +part of the inverse transform. +The Fourier image has its origin at pel (0,0) --- +for viewing, use \verb+im_rotquad()+ to move the origin to the centre of +the image. + +Once an image is in the frequency domain, it can be filtered by multiplying +it with a mask image. The VIPS mask generator is \verb+im_create_fmask()+ +see the manual page for details of the arguments, but it will create low +pass, high pass, ring pass and band pass filters, which may each be ideal, +Gaussian or Butterworth. There is also a fractal mask option. + +The other functions in the package build on these base +facilities. \verb+im_freqflt()+ transforms an input image to +Fourier space, multiplies it by a mask image, and transforms it back +again. \verb+im_flt_image_freq()+ will create a mask image of the correct +size for you, and call \verb+im_freqflt()+. \verb+im_disp_ps()+ will call +the right combinations of functions to make a displayable power spectrum +for an image. + +\begin{fig2} +\begin{verbatim} +example% vips --list freq_filt +im_create_fmask - create frequency domain filter mask +im_disp_ps - make displayable power spectrum +im_flt_image_freq - frequency domain filter image +im_fractsurf - generate a fractal surface of given dimension +im_freqflt - frequency-domain filter of in with mask +im_fwfft - forward fast-fourier transform +im_rotquad - rotate image quadrants to move origin to centre +im_invfft - inverse fast-fourier transform +im_invfftr - real part of inverse fast-fourier transform +\end{verbatim} +\caption{Fourier functions} +\label{fg:freq} +\end{fig2} + +\subsection{Histograms and LUTs} + +See \fref{fg:hist}. + +VIPS represents histograms and look-up tables in the same way --- as images. + +They should have either \verb+Xsize+ or \verb+Ysize+ set to 1, and the +other dimension set to the number of elements in the table. The table can be +of any size, have any band format, and have any number of bands. + +Use \verb+im_histgr()+ to find the histogram of an image. Use +\verb+im_histnD()+ to find the n-dimensional histogram of an n-band +image. Perform operations on histograms with \verb+im_histcum()+, +\verb+im_histnorm()+, \verb+im_histspec()+, \verb+im_invertlut()+. Visualise +histograms with \verb+im_histplot()+. Use a histogram (or LUT) to transform +an image with \verb+im_maplut()+. Build a histogram from scratch with +\verb+im_identity()+ or \verb+im_identity_ushort()+. + +Use \verb+im_lhist*()+ for local histogram equalisation, and +\verb+im_stdif*()+ for statisticaol differencing. The \verb+im_tone_*()+ +functions are for operations on the L channel of a LAB image. Other +functions are useful combinations of these basic operations. + +\begin{fig2} +\begin{verbatim} +example% vips --list histograms_lut +im_gammacorrect - gamma-correct image +im_heq - histogram-equalise image +im_hist - find and graph histogram of image +im_histcum - turn histogram to cumulative histogram +im_histeq - form histogram equalistion LUT +im_histgr - find histogram of image +im_histnD - find 1D, 2D or 3D histogram of image +im_histnorm - form normalised histogram +im_histplot - plot graph of histogram +im_histspec - find histogram which will make pdf of in match ref +im_hsp - match stats of in to stats of ref +im_identity - generate identity histogram +im_identity_ushort - generate ushort identity histogram +im_ismonotonic - test LUT for monotonicity +im_lhisteq - local histogram equalisation +im_lhisteq_raw - local histogram equalisation, no border +im_invertlut - generate correction table from set of measures +im_buildlut - generate LUT table from set of x/y positions +im_maplut - map image through LUT +im_project - find horizontal and vertical projections of an image +im_stdif - statistical differencing +im_stdif_raw - statistical differencing, no border +im_tone_analyse - analyse in and create LUT for tone adjustment +im_tone_build - create LUT for tone adjustment of LabS images +im_tone_build_range - create LUT for tone adjustment +im_tone_map - map L channel of LabS or LabQ image through LUT +\end{verbatim} +\caption{Histogram/LUT functions} +\label{fg:hist} +\end{fig2} + +\subsection{Morphology} + +See \fref{fg:morph}. + +The morphological functions are used on one-band \verb+IM_BANDFMT_UCHAR+ binary +images (images containing only zero and not-zero). They search images +for particular patterns of pixels (specified with the mask argument), +either adding or removing pixels when they find a match. They are useful +for cleaning up images --- for example, you might threshold an image, and +then use one of the morphological functions to remove all single isolated +pixels from the result. + +If you combine the morphological operators with the mask rotators +(\verb+im_rotate_imask45()+, for example) and apply them repeatedly, you +can achieve very complicated effects: you can thin, prune, fill, open edges, +close gaps, and many others. For example, see `Fundamentals of Digital +Image Processing' by A. Jain, pp 384-388, Prentice-Hall, 1989 for more ideas. + +Beware that VIPS reverses the usual image processing convention, by assuming +white objects on a black background. + +The mask you give to the morphological functions should contain only the +values 0 (for background), 128 (for don't care) and 255 (for object). The +mask must have odd length sides --- the origin of the mask is taken to be +the centre value. For example, the mask: + +\begin{verbatim} +3 3 +128 255 128 +255 0 255 +128 255 128 +\end{verbatim} + +\noindent +applied to an image with \verb+im_erode()+, will find all black pixels +4-way connected with white pixels. Essentially, \verb+im_dilate()+ +sets pixels in the output if any part of the mask matches, whereas +\verb+im_erode()+ sets pixels only if all of the mask matches. + +The \verb+_raw()+ version of the functions do not add a black border to the +output. \verb+im_cntlines()+ and \verb+im_profile+ are occasionally useful for +analysing results. + +See the boolean operations \verb+im_and()+, \verb+im_or()+ and +\verb+im_eor()+ for analogues of the usual set difference and set +union operations. + +\begin{fig2} +\begin{verbatim} +example% vips --list morphology +im_cntlines - count horizontal or vertical lines +im_dilate - dilate image with mask, adding a black border +im_dilate_raw - dilate image with mask +im_erode - erode image with mask, adding a black border +im_erode_raw - erode image with mask +im_profile - find first horizontal/vertical edge +\end{verbatim} +\caption{Morphological functions} +\label{fg:morph} +\end{fig2} + +\subsection{Mosaicing} + +See \fref{fg:mosaicing}. + +These functions are useful for joining many small images together to make one +large image. They can cope with unstable contrast, and arbitary sub-image +layout, but will not do any geometric correction. The mosaicing functions +can be grouped into layers: + +The lowest level functions are \verb+im_correl()+. and \verb+im_affine()+. +\verb+im_correl()+ searches a large image for a small sub-image, returning +the position of the best sub-image match. \verb+im_affine()+ performs +a general affine transform on an image: that is, any transform in which +parallel lines remain parallel. + +Next, \verb+im_lrmerge()+ and \verb+im_tbmerge()+ blend two images together +left-right or up-down. + +Next up are \verb+im_lrmosaic()+ and \verb+im_tbmosaic()+. These use the +two low-level merge operations to join two images given just an approximate +overlap as a start point. Optional extra parameters let you do 'balancing' +too: if your images have come from a source where there is no precise +control over the exposure (for example, images from a tube camera, or a +set of images scanned from photographic sources), \verb+im_lrmosaic()+ +and \verb+im_tbmosaic()+ will adjust the contrast of the left image to +match the right, the right to the left, or both to some middle value. + +The functions \verb+im_lrmosaic1()+ and \verb+im_tbmosaic1()+ are first-order +analogues of the basic mosaic functions: they take two tie-points and use +them to rotate and scale the right-hand or bottom image before starting to join. + +Finally, \verb+im_global_balance()+ can be used to re-balance a mosaic +which has been assembled with these functions. It will generally do a +better job than the low-level balancer built into \verb+im_lrmosaic()+ +and \verb+im_tbmosaic()+. See the man page. \verb+im_remosaic()+ uses the same +techniques, but will reassemble the image from a different set of source +images. + +\begin{fig2} +\begin{verbatim} +example% vips --list mosaicing +im_affine - affine transform +im_correl - search area around sec for match for area around ref +im__find_lroverlap - search for left-right overlap of ref and sec +im__find_tboverlap - search for top-bottom overlap of ref and sec +im_global_balance - automatically rebuild mosaic with balancing +im_global_balancef - automatically rebuild mosaic with balancing, float output +im_lrmerge - left-right merge of in1 and in2 +im_lrmerge1 - first-order left-right merge of ref and sec +im_lrmosaic - left-right mosaic of ref and sec +im_lrmosaic1 - first-order left-right mosaic of ref and sec +im_match_linear - resample ref so that tie-points match +im_match_linear_search - search sec, then resample so that tie-points match +im_remosaic - automatically rebuild mosaic with new files +im_similarity_area - output area xywh of similarity transformation +im_similarity - similarity transformation +im_tbmerge - top-bottom merge of in1 and in2 +im_tbmerge1 - first-order top-bottom merge of in1 and in2 +im_tbmosaic - top-bottom mosaic of in1 and in2 +im_tbmosaic1 - first-order top-bottom mosaic of ref and sec +\end{verbatim} +\caption{Mosaic functions} +\label{fg:mosaicing} +\end{fig2} + +\subsection{Other} + +See \fref{fg:other}. + +These functions generate various test images. You can combine them with +the arithmetic and rotate functions to build more complicated images. + +The \verb+im_benchmark*()+ operations are for testing the VIPS SMP system. + +\begin{fig2} +\begin{verbatim} +example% vips --list other +im_benchmark - do something complicated for testing +im_benchmark2 - do something complicated for testing +im_benchmarkn - do something complicated for testing +im_eye - generate IM_BANDFMT_UCHAR [0,255] frequency/amplitude image +im_grey - generate IM_BANDFMT_UCHAR [0,255] grey scale image +im_feye - generate IM_BANDFMT_FLOAT [-1,1] frequency/amplitude image +im_fgrey - generate IM_BANDFMT_FLOAT [0,1] grey scale image +im_fzone - generate IM_BANDFMT_FLOAT [-1,1] zone plate image +im_make_xy - generate image with pixel value equal to coordinate +im_zone - generate IM_BANDFMT_UCHAR [0,255] zone plate image +\end{verbatim} +\caption{Other functions} +\label{fg:other} +\end{fig2} + +\subsection{IO functions} + +See \fref{fg:other}. + +These functions are related to the image IO system. + +\begin{fig2} +\begin{verbatim} +example% vips --list iofuncs +im_binfile - open a headerless binary file +im_cache - cache results of an operation +im_guess_prefix - guess install area +im_header_get_type - return field type +im_header_int - extract int fields from header +im_header_double - extract double fields from header +im_header_string - extract string fields from header +im_version - VIPS version number +im_version_string - VIPS version string +\end{verbatim} +\caption{IO functions} +\label{fg:io} +\end{fig2} diff --git a/doc/src/pio.tex b/doc/src/pio.tex new file mode 100644 index 00000000..c951c50d --- /dev/null +++ b/doc/src/pio.tex @@ -0,0 +1,868 @@ +\section{Programming PIO functions} +\label{sec:pio} + +The VIPS PIO system has a number of advantages over WIO, as summarised in +the introduction. On the other hand, they are a bit more complicated. + +\subsection{Easy PIO with \texttt{im\_wrapone()} and \texttt{im\_wrapmany()}} +\label{sec:wrapone} + +PIO is a very general image IO system, and because of this flexibility, +can be complicated to program. As a convenience, VIPS offers an easy-to-use +layer over PIO with the funtions \verb+im_wrapone()+ and \verb+im_wrapmany()+. + +If your image processing function is uninterested in coordinates, that is, +if your input and output images are the same size, and each output pixel +depends only upon the value of the corresponding pixel in the input image +or images, then these functions are for you. + +Consider the \verb+invert()+ function of figure~\ref{fg:invert}. First, +we have to write the core of this as a buffer-processing function: + +\begin{verbatim} +#include +#include + +#include + +/* p points to a buffer of pixels which + * need inverting, q points to the buffer + * we should write the result to, and n + * is the number of pels present. + */ +static void +invert_buffer( unsigned char *p, + unsigned char *q, int n ) +{ + int i; + + for( i = 0; i < n; i++ ) + q[i] = 255 - p[i]; +} +\end{verbatim} + +Now we have to wrap up this very primitive expression of the invert operation +as a PIO function. We use \verb+im_wrapone()+ to do this. It has type: + +\begin{verbatim} +int +im_wrapone( IMAGE *in, IMAGE *out, + im_wrapone_fn fn, void *a, void *b ) +\end{verbatim} + +\noindent +where: + +\begin{verbatim} +void +(*im_wrapone_fn)(void *in, void *out, + int n, void *a, void *b ) +\end{verbatim} + +\noindent +almost the same type as our buffer-processing function above. The values +\verb+a+ and \verb+b+ are carried around by VIPS for whatever use you +fancy. \verb+invert()+ can now be written as: + +\begin{verbatim} +int +invert( IMAGE *in, IMAGE *out ) +{ + /* Check parameters. + */ + if( in->BandFmt != IM_BANDFMT_UCHAR || + in->Bands != 1 || + in->Coding != IM_CODING_NONE ) { + im_error( "invert", "bad image" ); + return( -1 ); + } + + /* Set fields in output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Process! We don't use either of the + * user parameters in this function, + * so leave them as NULL. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) invert_buffer, + NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} +\end{verbatim} + +And that's all there is to it. This function will have all of the desirable +properties of PIO functions, while being as easy to program as the WIO +\verb+invert()+ earlier in this chapter. + +This version of \verb+invert()+ is not very general: it will only accept +one-band unsigned char images. It is easy to modify for n-band images: + +\begin{verbatim} +/* As before, but use one of the user + * parameters to pass in the number of + * bands in the image. + */ +static void +invert_buffer( unsigned char *p, + unsigned char *q, int n, + IMAGE *in ) +{ + int i; + int sz = n * in->Bands; + + for( i = 0; i < sz; i++ ) + q[i] = 255 - p[i]; +} +\end{verbatim} + +We must also modify \verb+invert()+: + +\begin{verbatim} +int +invert( IMAGE *in, IMAGE *out ) +{ + /* Check parameters. + */ + if( in->BandFmt != IM_BANDFMT_UCHAR || + in->Coding != IM_CODING_NONE ) { + im_error( "invert", "bad image" ); + return( -1 ); + } + + /* Set fields in output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Process! The first user-parameter + * is the number of bands involved. + */ + if( im_wrapone( in, out, + (im_wrapone_fn)invert_buffer, + in, NULL ) ) + return( -1 ); + + return( 0 ); +} +\end{verbatim} + +There are two significant hidden traps here. First, inside the buffer +processing functions, you may only read the contents of the user parameters +\verb+a+ and \verb+b+, you may not write to them. This is because on a +multi-CPU machine, several copies of your buffer-processing functions will +be run in parallel --- if they all write to the same place, there will be +complete confusion. If you need writeable parameters (for example, to count +and report overflows), you can't use \verb+im_wrapone()+, you'll have to +use the PIO system in all its gory detail, see below. + +Secondly, your buffer processing function may not be called immediately. VIPS +may decide to delay evaluation of your operation until long after the call +to \verb+invert()+ has returned. As a result, care is needed to ensure +that you never read anything in your buffer-processing function that may +have been freed. The best way to ensure this is to use the local resource +allocators, such as \verb+im_open_local()+ and \verb+im_malloc()+. This issue +is discussed at length in the sections below, and in \pref{sec:appl}. + +\verb+im_wrapone()+ is for operations which take exactly one input image. VIPS +provides a second function, \verb+im_wrapmany()+, which works for any number +of input images. The type of \verb+im_wrapmany()+ is slightly different: + +\begin{verbatim} +int +im_wrapmany( IMAGE **in, IMAGE *out, + im_wrapmany_fn fn, void *a, void *b ) +\end{verbatim} + +\noindent + +\begin{verbatim} +void +(*im_wrapmany_fn)( void **in, void *out, + int n, void *a, void *b ) +\end{verbatim} + +\noindent +\verb+im_wrapmany()+ takes a \verb+NULL+-terminated array of input images, +and creates a \verb+NULL+-terminated array of buffers for the use of your +buffer processing function. A function to add two \verb+IM_BANDFMT_UCHAR+ +images to make a \verb+IM_BANDFMT_UCHAR+ image might be written as: + +\begin{verbatim} +static void +add_buffer( unsigned char **in, + unsigned short *out, int n, + IMAGE *in ) +{ + int i; + int sz = n * in->Bands; + unsigned char *p1 = in[0]; + unsigned char *p2 = in[1]; + + for( i = 0; i < sz; i++ ) + out[i] = p1[i] + p2[i]; +} +\end{verbatim} + +This can be made into a PIO function with: + +\begin{verbatim} +int +add_uchar( IMAGE *i1, IMAGE *i2, + IMAGE *out ) +{ + IMAGE *invec[3]; + + /* Check parameters. We don't need to + * check that i1 and i2 are the same + * size, im_wrapmany() does that for + * us. + */ + if( i1->BandFmt != IM_BANDFMT_UCHAR || + i1->Coding != IM_CODING_NONE || + i2->BandFmt != IM_BANDFMT_UCHAR || + i2->Coding != IM_CODING_NONE || + i1->Bands != i2->Bands ) { + im_error( "add_uchar", "bad in" ); + return( -1 ); + } + + /* Set fields in output image. As + * input image, but we want a USHORT. + */ + if( im_cp_desc( out, i1 ) ) + return( -1 ); + out->BandFmt = IM_BANDFMT_USHORT; + out->Bbits = IM_BBITS_SHORT; + + /* Process! The first user-parameter + * is the number of bands involved. + * invec is a NULL-terminated array of + * input images. + */ + invec[0] = i1; invec[1] = i2; + invec[2] = NULL; + if( im_wrapmany( invec, out, + (im_wrapone_fn)add_buffer, + i1, NULL ) ) + return( -1 ); + + return( 0 ); +} +\end{verbatim} + +\subsection{Region descriptors} + +Regions are the next layer of abstraction above image descriptors. A region +is a small part of an image, held in memory ready for processing. A region +is defined as: + +\begin{verbatim} +typedef struct { + Rect valid; + IMAGE *im; + + ... and some other private fields, + ... used by VIPS for housekeeping +} REGION; +\end{verbatim} + +\noindent +where \verb+valid+ holds the sub-area of image \verb+im+ that this region +represents, and \verb+Rect+ is defined as: + +\begin{verbatim} +typedef struct { + int left, top; + int width, height; +} Rect; +\end{verbatim} + +\noindent +two macros are available for \verb+Rect+ calculations: + +\begin{verbatim} +int IM_RECT_RIGHT( Rect *r ) +int IM_RECT_BOTTOM( Rect *r ) +\end{verbatim} + +\noindent +where \verb+IM_RECT_RIGHT()+ returns \verb+left+ + \verb+width+, and +\verb+IM_RECT_BOTTOM()+ returns \verb+top+ + \verb+height+. A small library +of C functions are also available for \verb+Rect+ algebra, see the manual +pages for \verb+im_rect_intersectrect()+. + +Regions are created with \verb+im_region_create()+. This has type: + +\begin{verbatim} +REGION *im_region_create( IMAGE *im ) +\end{verbatim} + +\noindent +\verb+im_region_create()+ returns a pointer to a new region structure, +or \verb+NULL+ on error. Regions returned by \verb+im_region_create()+ +are blank --- they contain no image data and cannot be read from or written +to. See the next couple of sections for calls to fill regions with data. + +Regions are destroyed with \verb+im_region_free()+. It has type: + +\begin{verbatim} +int im_region_free( REGION *reg ) +\end{verbatim} + +\noindent +And, as usual, returns 0 on success and non-zero on error, setting +\verb+im_error()+. You must free all regions you create. If you close +an image without freeing all the regions defined on that image, the image is +just marked for future closure --- it is not actually closed until the final +region is freed. This behaviour helps to prevent dangling pointers, and it +is not difficult to make sure you free all regions --- see the examples below. + +\subsection{Image input with regions} + +Before you can read from a region, you need to call \verb+im_prepare()+ +to fill the region with image data. It has type: + +\begin{verbatim} +int im_prepare( REGION *reg, Rect *r ) +\end{verbatim} + +Area \verb+r+ of the image on which \verb+reg+ has been created is prepared +and attached to the region. + +Exactly what this preparation involves depends upon the image --- it can +vary from simply adjusting some pointers, to triggering the evaluation of a +series of other functions. If it returns successfully, \verb+im_prepare()+ +guarantees that all pixels within \verb+reg->valid+ may be accessed. Note +that this may be smaller or larger than \verb+r+, since \verb+im_prepare()+ +clips \verb+r+ against the size of the image. + +Programs can access image data in the region by calling the macro +\verb+IM_REGION_ADDR()+. It has type + +\begin{verbatim} +char *IM_REGION_ADDR( REGION *reg, + int x, int y ) +\end{verbatim} + +Provided that point (x,y) lies inside \verb+reg->valid+, +\verb+IM_REGION_ADDR()+ returns a pointer to pel $(x,y)$. Adding to the result +of \verb+IM_REGION_ADDR()+ moves to the right along the line of pels, provided +you stay strictly within \verb+reg->valid+. Add \verb+IM_REGION_LSKIP()+ +to move down a line, see below. \verb+IM_REGION_ADDR()+ has some other +useful features --- see the manual page. + +Other macros are available to ease address calculation: + +\begin{verbatim} +int IM_REGION_LSKIP( REGION *reg ) +int IM_REGION_N_ELEMENTS( REGION *reg ) +int IM_REGION_SIZEOF_LINE( REGION *reg ) +\end{verbatim} + +\noindent +These find the number of bytes to add to the result of \verb+IM_REGION_ADDR()+ +to move down a line, the number of band elements across the region and the +number of bytes across the region. + +\fref{fg:paverage} is a version of \verb+average()+ which uses +regions rather than WIO input. Two things: first, we should really be +using \verb+im_iterate()+, see \pref{sec:sequence}, to do the rectangle +algebra for us. Secondly, note that we call \verb+im_pincheck()+ rather +than \verb+im_incheck()+. \verb+im_pincheck()+ signals to the IO system +that you are a PIO-aware function, giving \verb+im_prepare()+ much more +flexibility in the sorts of preparation it can do. Also see the manual +pages for \verb+im_poutcheck()+ and \verb+im_piocheck()+. + +\begin{fig2} +\begin{verbatim} +#include +#include +#include +#include + +int +average( IMAGE *im, double *out ) +{ + int total, i, y; + REGION *reg; + Rect area, *r; + + /* Check im. + */ + if( im_pincheck( im ) ) + return( -1 ); + if( im->BandFmt != IM_BANDFMT_UCHAR || im->Coding != IM_CODING_NONE ) { + im_error( "average", "uncoded uchar images only" ); + return( -1 ); + } + + /* Make a region on im which we can use for reading. + */ + if( !(reg = im_region_create( im )) ) + return( -1 ); +\end{verbatim} +\caption{First PIO average of image} +\label{fg:paverage} +\end{fig2} + +\begin{fig2} +\begin{verbatim} + /* Move area over the image in 100x100 pel chunks. + * im_prepare() will clip against the edges of the image + * for us. + */ + total = 0; + r = ®->valid; + area.width = 100; area.height = 100; + for( area.top = 0; area.top < im->Ysize; area.top += 100 ) + for( area.left = 0; area.left < im->Xsize; + area.left += 100 ) { + /* Fill reg with pels. + */ + if( im_prepare( reg, &area ) ) { + /* We must free the region! + */ + im_region_free( reg ); + return( -1 ); + } + + /* Loop over reg, adding to our total. + */ + for( y = r->top; y < IM_RECT_BOTTOM( r ); y++ ) { + unsigned char *p = IM_REGION_ADDR( reg, r->left, y ); + + for( i = 0; i < IM_REGION_N_ELEMENTS( reg ); i++ ) + total += p[i]; + } + } + + /* Make sure we free the region. + */ + im_region_free( reg ); + + /* Find average. + */ + *out = (double) total / (IM_IMAGE_N_ELEMENTS( im ) * im->Ysize); + + return( 0 ); +} +\end{verbatim} +\caption{First PIO average of image (cont.)} +\end{fig2} + +This version of \verb+average()+ can be called in exactly the same way as +the previous one, but this version has the great advantage of not needing +to have the whole of the input image available at once. + +We can do one better than this --- if the image is being split into small +pieces, we can assign each piece to a separate thread of execution and get +parallelism. To support this splitting of tasks, VIPS has the notion of +a sequence. + +\subsection{Splitting into sequences} +\label{sec:sequence} + +A sequence comes in three parts: a start function, a processing function, +and a stop function. When VIPS starts up a new sequence, it runs the +start function. Start functions return sequence values: a void pointer +representing data local to this sequence. VIPS then repeatedly calls the +processing function, passing in the sequence value and a new piece of image +data for processing. Finally, when processing is complete, VIPS cleans up by +calling the stop function, passing in the sequence value as an argument. The +types look like this: + +\begin{verbatim} +void * +(*start_fn)( IMAGE *out, + void *a, void *b ) +int +(*process_fn)( REGION *reg, + void *seq, void *a, void *b ) +int +(*stop_fn)( void *seq, void *a, void *b ) +\end{verbatim} + +\noindent +The values \verb+a+ and \verb+b+ are carried around by VIPS for your use. + +For functions like \verb+average()+ which consume images but produce no image +output, VIPS provides \verb+im_iterate()+. This has type: + +\begin{verbatim} +int im_iterate( IMAGE *in, + void *(*start_fn)(), + int (*process_fn)(), + int (*stop_fn)(), + void *a, void *b ) +\end{verbatim} + +VIPS starts one or more sequences, runs one or more processing functions +over image \verb+in+ until all of \verb+in+ has been consumed, and then closes +all of the sequences down and returns. VIPS guarantees that the regions +the \verb+process_fn()+ is given will be complete and disjoint, that is, +every pixel in the image will be passed through exactly one sequence. To +make it possible for the sequences to each contribute to the result of the +function in an orderly manner, VIPS also guarantees that all start and stop +functions are mutually exclusive. + +A note on types: \verb++ declares prototypes for +\verb+im_iterate()+ and \verb+im_generate()+ (see \pref{sec:generate}), +but does not give prototypes for the function arguments. This loses a +little type-safety, but gains some convenience. + +An example should make this clearer. This version of \verb+average()+ +is very similar to the average function in the VIPS library --- it is only +missing polymorphism. + +\begin{fig2} +\begin{verbatim} +#include +#include +#include +#include + +/* Start function for average(). We allocate a small piece of + * storage which this sequence will accumulate its total in. Our + * sequence value is just a pointer to this storage area. + * + * The first of the two pointers VIPS carries around for us is a + * pointer to the space where we store the grand total. + */ +static int * +average_start( IMAGE *out ) +{ + int *seq = IM_NEW( out, int ); + + if( !seq ) + return( NULL ); + *seq = 0; + + return( seq ); +} + +/* Stop function for average(). Add the total which has + * accumulated in our sequence value to the grand total for + * the program. + */ +static int +average_stop( int *seq, int *gtotal ) +{ + /* Stop functions are mutually exclusive, so we can write + * to gtotal without clashing with any other stop functions. + */ + *gtotal += *seq; + + return( 0 ); +} +\end{verbatim} +\caption{Final PIO average of image} +\label{fg:p2average} +\end{fig2} + +\begin{fig2} +\begin{verbatim} +/* Process function for average(). Total this region, and + * add that total to the sequence value. + */ +static int +average_process( REGION *reg, int *seq ) +{ + int total, i, y; + Rect *r = ®->valid; + + /* Get the appropriate part of the input image ready. + */ + if( im_prepare( reg, r ) ) + return( -1 ); + + /* Loop over the region. + */ + total = 0; + for( y = r->top; y < IM_RECT_BOTTOM( r ); y++ ) { + unsigned char *p = IM_REGION_ADDR( reg, r->left, y ); + + for( i = 0; i < IM_REGION_N_ELEMENTS( reg ); i++ ) + total += p[i]; + } + + /* Add to the total for this sequence. + */ + *seq += total; + + return( 0 ); +} +\end{verbatim} +\caption{Final PIO average of image (cont.)} +\end{fig2} + +\begin{fig2} +\begin{verbatim} +/* Find average of image. + */ +int +average( IMAGE *im, double *out ) +{ + /* Accumulate grand total here. + */ + int gtotal = 0; + + /* Prepare im for PIO reading. + */ + if( im_pincheck( im ) ) + return( -1 ); + + /* Check it is the sort of thing we can process. + */ + if( im->BandFmt != IM_BANDFMT_UCHAR || + im->Coding != IM_CODING_NONE ) { + im_error( "average", "uncoded uchar images only" ); + return( -1 ); + } + + /* Loop over the image in pieces, and possibly in parallel. + */ + if( im_iterate( im, + average_start, average_process, average_stop, + >otal, NULL ) ) + return( -1 ); + + /* Calculate average. + */ + *out = (double) gtotal / (IM_IMAGE_N_ELEMENTS( im ) * im->Ysize); + + return( 0 ); +} +\end{verbatim} +\caption{Final PIO average of image (cont.)} +\end{fig2} + +There are a couple of variations on \verb+im_prepare()+: you can use +\verb+im_prepare_to()+ to force writing to a particular place, and +\verb+im_prepare_thread()+ to use threaded evaluation. See the man pages. + +\subsection{Output to regions} +\label{sec:generate} + +Regions are written to in just the same way they are read from --- by +writing to a pointer found with the \verb+IM_REGION_ADDR()+ macro. + +\verb+im_iterate()+ does input --- \verb+im_generate()+ does output. It +has the same type as \verb+im_iterate()+: + +\begin{verbatim} +int +im_generate( IMAGE *out, + void *(*start_fn)(), + int (*process_fn)(), + int (*stop_fn)(), + void *a, void *b ) +\end{verbatim} + +The region given to the process function is ready for output. Each time +the process function is called, it should fill in the pels in the region +it was given. Note that, unlike \verb+im_iterate()+, the areas the process +function is asked to produce are not guaranteed to be either disjoint or +complete. Again, VIPS may start up many process functions if it sees fit. + +Here is \verb+invert()+, rewritten to use PIO. This piece of code makes use +of a pair of standard start and stop functions provided by the VIPS library: +\verb+im_start_one()+ and \verb+im_stop_one()+. They assume that the first +of the two user arguments to \verb+im_generate()+ is the input image. They are +defined as: + +\begin{verbatim} +REGION * +im_start_one( IMAGE *out, IMAGE *in ) +{ + return( im_region_create( in ) ); +} +\end{verbatim} + +\noindent +and: + +\begin{verbatim} +int +im_stop_one( REGION *seq ) +{ + return( im_region_free( seq ) ); +} +\end{verbatim} + +They are useful for simple functions which expect only one input +image. See the manual page for \verb+im_start_many()+ for many-input +functions. + +\begin{fig2} +\begin{verbatim} +#include +#include +#include +#include + +/* Process function for invert(). Build the pixels in or + * from the appropriate pixels in ir. + */ +static int +invert_process( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + int i, y; + + /* Ask for the part of ir we need to make or. In this + * case, the two areas will be the same. + */ + if( im_prepare( ir, r ) ) + return( -1 ); + + /* Loop over or writing pels calculated from ir. + */ + for( y = r->top; y < IM_RECT_BOTTOM( r ); y++ ) { + unsigned char *p = IM_REGION_ADDR( ir, r->left, y ); + unsigned char *q = IM_REGION_ADDR( or, r->left, y ); + + for( i = 0; i < IM_REGION_N_ELEMENTS( or ); i++ ) + q[i] = 255 - p[i]; + } + + /* Success! + */ + return( 0 ); +} +\end{verbatim} +\caption{PIO invert} +\label{fg:p2invert} +\end{fig2} + +\begin{fig2} +\begin{verbatim} +/* Invert an image. + */ +int +invert( IMAGE *in, IMAGE *out ) +{ + /* Check descriptors for PIO compatibility. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Check input image for compatibility with us. + */ + if( in->BandFmt != IM_BANDFMT_UCHAR || in->Coding != IM_CODING_NONE ) { + im_error( "invert", "uncoded uchar images only" ); + return( -1 ); + } + + /* out inherits from in, as before. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Set demand hints for out. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Build out in pieces, and possibly in parallel! + */ + if( im_generate( out, + im_start_one, invert_process, im_stop_one, + in, NULL ) ) + return( -1 ); + + return( 0 ); +} +\end{verbatim} +\caption{PIO invert (cont.)} +\end{fig2} + +Functions have some choice about the way they write their output. Usually, they +should just write to the region they were given by \verb+im_generate()+. They +can, if they wish, set up the region for output to some other place. See +the manual page for \verb+im_region_region()+. See also the source for +\verb+im_copy()+ and \verb+im_extract()+ for examples of these tricks. + +Note also the call to \verb+im_demand_hint()+. This function hints to the IO +system, suggesting the sorts of shapes of region this function is happiest +with. VIPS supports four basic shapes --- choosing the correct shape can +have a dramatic effect on the speed of your function. See the man page for +full details. + +\subsection{Callbacks} +\label{sec:callback} + +VIPS lets you attach callbacks to image descriptors. These are functions +you provide that VIPS will call when certain events occur. This release +of VIPS supports three callbacks: + +\subsubsection{Close callbacks} + +These callbacks are invoked just before an image is closed. They are useful +for freeing objects which are associated with the image. All callbacks are +triggered in the reverse order to the order in which they were attached. This +is sometimes important when freeing objects which contain pointers to +other objects. + +Use \verb+im_add_close_callback()+ to add a close callback: + +\begin{verbatim} +int +im_add_close_callback( IMAGE *im, + int (*callback)(), + void *a, void *b ) +\end{verbatim} + +\noindent +where \verb+callback()+ is a function supplied by you which has type: + +\begin{verbatim} +int callback( void *a, void *b ) +\end{verbatim} + +\noindent +and which returns 0 on success, and -1 on error, setting +\verb+im_error()+. As with \verb+im_generate()+, the values \verb+a+ +and \verb+b+ are carried around for you by VIPS, and may be used as your +function sees fit. + +\subsubsection{Eval callbacks} + +These are callbacks which are invoked by VIPS during evaluation. They +are called each time a region is filled with data (for PIO functions), or +each time \verb+im_writeline()+ is called (for WIO functions). The callback +has access to a large struct containing information about the progress of +evaluation, useful for user-interfaces built on top of VIPS. See the manual +page for \verb+im_add_eval_callback()+ for full details. + +\subsubsection{Evalend callbacks} + +These are triggered after completion of evaluation of an image, just after all +stop functions have been called. Evalend callbacks are useful for summarising +the results of a computation, see the example in the section below. + +\subsection{Memory allocation revisited} + +When you are using PIO, memory allocation becomes rather more complicated than +it was before. There are essentially two types of memory which your function +might want to use for working space: memory which is associated with each +instance of your function (remember that two copies of you function may be +joined together in a pipeline and be running at the same time --- you can't +just use global variables), and memory which is local to each sequence +which VIPS starts on your argument image. + +The first type, memory local to this function instance, typically holds +copies of any parameters passed to your image processing function, and links +to any read-only tables used by sequences which you run over the image. This +should be allocated in your main function. + +The second type of memory, memory local to a sequence, should be allocated +in a start function. Because this space is private to a sequence, it may be +written to. Start and stop functions are guaranteed +to be single-threaded, so you may write to the function-local memory within +them. + diff --git a/doc/src/refintro.tex b/doc/src/refintro.tex new file mode 100644 index 00000000..30290c05 --- /dev/null +++ b/doc/src/refintro.tex @@ -0,0 +1,105 @@ +\section{Introduction} +\mylabel{sec:ref} + +This document introduces the functions available in the VIPS image +processing library. For detailed information on particular functions, +refer to the UNIX on-line manual pages. Enter (for example): + +\begin{verbatim} +example% man im_abs +\end{verbatim} + +for information on the function \verb+im_abs()+. + +All the comand-line vips operations will print help text too. For example: + +\begin{verbatim} +example% vips im_extract +usage: vips im_extract input output + left top width height band +where: + input is of type "image" + output is of type "image" + left is of type "integer" + top is of type "integer" + width is of type "integer" + height is of type "integer" + band is of type "integer" +extract area/band, from package + "conversion" +flags: (PIO function) + (coordinate transformer) + (area operation) + (result can be cached) +vips: error calling function +im_run_command: too few arguments +\end{verbatim} + +Once you have found a function you need to use, you can call it from a C +program (see \pref{sec:appl}), you can call +it from C++ or Python (see \pref{sec:cpp}), you can call +it from the \nip{} ((see the \emph{nip Manual}), or SIAM graphical +user-interfaces, or you can run it from the UNIX command line with the +\vips{} program. For example: + +\begin{verbatim} +john% vips im_vips2tiff cam.v t1.tif none +john% vips im_tiff2vips t1.tif t2.v.v 0 +john% vips im_equal cam.v t2.v t3.v +john% vips im_min t3.v +255 +\end{verbatim} + +VIPS may have been set up at your site with a set of links which call the +vips program for you. You may also be able to type: + +\begin{verbatim} +john% im_vips2tiff cam.v t1.tif none +john% im_tiff2vips t1.tif t2.v.v 0 +john% im_equal cam.v t2.v t3.v +john% im_min t3.v +\end{verbatim} + +There are a few VIPS programs which you cannot run with \vips{}, either +because their arguments are a very strange, or because they are complete +mini-applications (like \verb+vips2dj+). These programs are listed in +table~\ref{tb:nondb}, see the man pages for full details. + +\begin{tab2} +\centerline{ +\begin{tabular}{|l|l|} +\hline +Name & Description \\ +\hline +\texttt{binfile} & Read RAW image \\ +\texttt{debugim} & Print an image pixel by pixel \\ +\texttt{edvips} & Change fields in a VIPS header \\ +\texttt{header} & Print fields from a VIPS header \\ +\texttt{printlines} & Print an image a line at a time \\ +\texttt{vips} & VIPS universal main program \\ +\texttt{vips-7.12} & VIPS wrapper script \\ +\texttt{find\_mosaic} & Analyse a set of images for overlaps \\ +\texttt{mergeup} & Join a set of images together \\ +\texttt{cooc\_features} & Calculate features of a co-occurence matrix \\ +\texttt{cooc} & Calculate a co-occurence matrix \\ +\texttt{glds\_features} & Calculate features of a grey-level + distribution matrix \\ +\texttt{glds} & Calculate a grey-level distribution matrix \\ +\texttt{simcontr} & Demonstrate simultaneous contrast \\ +\texttt{sines} & Generate a sinusoidal test pattern \\ +\texttt{spatres} & Generate a spatial resolution test pattern \\ +\texttt{squares} & Generate some squares \\ +\texttt{batch\_crop} & Crop a lot of images \\ +\texttt{batch\_image\_convert} & File format convert a lot of images \\ +\texttt{batch\_rubber\_sheet} & Warp a lot of images \\ +\texttt{light\_correct} & Correct a set of images for shading errors \\ +\texttt{mitsub} & Format a VIPS image for output to a Mitsubishi 3600 \\ +\texttt{shrink\_width} & Shrink to a specific width \\ +\texttt{vdump} & VIPS to mono Postscript \\ +\texttt{vips2dj} & VIPS to high-quality colour Postscript \\ +\hline +\end{tabular} +} +\caption{Miscellaneous programs} +\label{tb:nondb} +\end{tab2} diff --git a/doc/src/vdisplay.tex b/doc/src/vdisplay.tex new file mode 100644 index 00000000..7c5080dc --- /dev/null +++ b/doc/src/vdisplay.tex @@ -0,0 +1,75 @@ +\section{The \texttt{VDisplay} class} + +The \verb+VDisplay+ class is an abstraction over the VIPS \verb+im_col_display+ +type which gives convenient and safe representation of VIPS display profiles. + +VIPS display profiles are now obsolete. You're better off using the +ICC colour management \verb+VImage+ member functions \verb+ICC_export()+ and +\verb+ICC_import()+. + +\subsection{Constructors} + +There are two constructors for \verb+VDisplay+: + +\begin{verbatim} +VDisplay( const char *name ); +VDisplay(); +\end{verbatim} + +The first form initialises the display from one of the standard VIPS display +types. For example: + +\begin{verbatim} +VDisplay fred( "sRGB" ); +VDisplay jim( "ultra2-20/2/98" ); +\end{verbatim} + +Makes \verb+fred+ a profile for making images in sRGB format, and \verb+jim+ a +profile representing my workstation display, as of 20/2/98. The second form +of constructor makes an uninitialised display. + +\subsection{Projection functions} + +A set of member functions of \verb+VDisplay+ provide read and write access to +the fields in the display. + +\begin{verbatim} +char *name(); +VDisplayType &type(); +matrix &mat(); +float &YCW(); +float &xCW(); +float &yCW(); +float &YCR(); +float &YCG(); +float &YCB(); +int &Vrwr(); +int &Vrwg(); +int &Vrwb(); +float &Y0R(); +float &Y0G(); +float &Y0B(); +float &gammaR(); +float &gammaG(); +float &gammaB(); +float &B(); +float &P(); +\end{verbatim} + +Where \verb+VDisplayType+ is defined as: + +\begin{verbatim} +enum VDisplayType { + BARCO, + DUMB +}; +\end{verbatim} + +And \verb+matrix+ is defined as: + +\begin{verbatim} +typedef float matrix[3][3]; +\end{verbatim} + +For a description of all the fields in a VIPS display profile, see the manual +page for \verb+im_XYZ2RGB()+. diff --git a/doc/src/verror.tex b/doc/src/verror.tex new file mode 100644 index 00000000..5680e43e --- /dev/null +++ b/doc/src/verror.tex @@ -0,0 +1,76 @@ +\section{The \texttt{VError} class} + +The \verb+VError+ class is the class thrown by the VIPS C++ API when an +error is detected. It is derived from \verb+std::exception+ in the usual way. + +\subsection{Constructors} + +There are two constructors for \verb+VError+: + +\begin{verbatim} +VError( std::string str ); +VError(); +\end{verbatim} + +The first form creates an error object initialised with the specified +string, the last form creates an empty error object. + +\subsection{Projection functions} + +A function gives access to the string held by \verb+VError+: + +\begin{verbatim} +const char *what(); +\end{verbatim} + +You can also send to an \verb+ostream+. + +\begin{verbatim} +std::ostream& operator<<( + std::ostream&, const error& ); +\end{verbatim} + +\subsection{Computing with \texttt{VError}} + +Two member functions let you append elements to an error: + +\begin{verbatim} +VError &app( std::string txt ); +VError &app( const int i ); +\end{verbatim} + +For example: + +\begin{verbatim} +VError wombat; +int n = 12; + +wombat.app( "possum: no more than " ). + app( n ).app( " elements\n" ); +throw( wombat ); +\end{verbatim} + +\noindent +will throw a \verb+VError+ with a diagnostic of: + +\begin{verbatim} +possum: no more than 12 elements +\end{verbatim} + +The member function \verb+perror()+ prints the error message to \verb+stdout+ +and exits with a code of 1. + +\begin{verbatim} +void perror( const char * ); +void perror(); +\end{verbatim} + +\subsection{Convenience function} + +The convenience function \verb+verror+ creates an \verb+VError+ with the +specified error string, and throws it. If you pass \verb+""+ for the string, +verror uses the contents of the VIPS error buffer instead. + +\begin{verbatim} +extern void verror( std::string str = "" ); +\end{verbatim} diff --git a/doc/src/vimage.tex b/doc/src/vimage.tex new file mode 100644 index 00000000..c897725f --- /dev/null +++ b/doc/src/vimage.tex @@ -0,0 +1,279 @@ +\section{The \texttt{VImage} class} + +The \verb+VImage+ class is a layer over the VIPS \verb+IMAGE+ type. It +automates almost all of the image creation and destruction issues that +complicate the C API, it automates error handling, and it provides a +convenient system for composing operations. + +\subsection{Constructors} + +There are two principal constructors for \verb+VImage+: + +\begin{verbatim} +VImage::VImage( const char *name, + const char *mode = "r" ); +VImage::VImage(); +\end{verbatim} + +The first form creates a new \verb+VImage+, linking it to the named file. +\verb+mode+ sets the mode for the file: it can take the following values: + +\begin{description} + +\item[\texttt{"r"}] +The named image file is opened read-only. This is the default mode. + +\item[\texttt{"w"}] +A \verb+VImage+ is created which, when written to, will write pixels to disc +in the specified file. + +\item[\texttt{"t"}] +As the \verb'"w"' mode, but pixels written to the \verb+VImage+ will be saved +in a temporary memory buffer. + +\item[\texttt{"p"}] +This creates a special `partial' image. Partial images represent +intermediate results, and are used to join VIPS operations together, +see~\pref{sec:compute}. + +\item[\texttt{"rw"}] +As the \verb'"r"' mode, but the image is mapped into your address space +read-write. This mode is only provided for the use of paintbox-style +applications, which need to directly modify an image. See \pref{sec:inplace}. + +\end{description} + +The second form of constructor is shorthand for: + +\begin{verbatim} +VImage( "VImage:1", "p" ) +\end{verbatim} + +\noindent +It is used for representing intermediate results of computations. + +Two further constructors are handy for wrapping \verb+VImage+ around existing +images. + +\begin{verbatim} +VImage( void *buffer, + int width, int height, int bands, + TBandFmt format ); +VImage( void *image ); +\end{verbatim} + +\noindent +The first constructor makes a \verb+VImage+ from an area of memory (perhaps +from another image processing system), and the second makes a \verb+VImage+ +from an \verb+IMAGE+. + +In both these two cases, the VIPS C++ API does not assume responsibility +for the resouces: it's up to you to make sure the buffer is freed. + +\subsection{File conversion} + +VIPS can read and write a number of different file formats. Information about +file format conversion is taken from the filename. For example: + +\begin{verbatim} +VImage jim( "fred.jpg" ); +\end{verbatim} + +\noindent +This will decompress the file \verb+fred.jpg+ to a memory buffer, wrap a VIPS +image around the buffer and build a reference to it called \verb+jim+. + +Options are passed to the file format converters embedded in the filename. For +example: + +\begin{verbatim} +VImage out( "jen.tif:deflate", "w" ); +\end{verbatim} + +\noindent + +\noindent +Writing to the descriptor \verb+out+ will cause a TIFF image to be written to +disc with deflate compression. + +See the manual page for \verb+im_open(3)+ for details of all the file formats +and conversions available. + +\subsection{Projection functions} + +A set of member functions of \verb+VImage+ provide access to the fields in +the header: + +\begin{verbatim} +int Xsize(); +int Ysize(); +int Bands(); +TBandFmt BandFmt(); +TCoding Coding(); +TType Type(); +float Xres(); +float Yres(); +int Length(); +TCompression Compression(); +short Level(); +int Xoffset(); +int Yoffset(); +\end{verbatim} + +\noindent +Where \verb+TBandFmt+, \verb+TCoding+, \verb+TType+ and \verb+TCompression+ +are \verb+enum+s for the types in the VIPS file header. See section~\pref{sec:header} for an explanation of all of these fields. + +The \verb+image()+ member function provides access to the \verb+IMAGE+ +descriptor underlying the C++ API. See the \pref{sec:appl} for details. + +\begin{verbatim} +void *image(); +\end{verbatim} + +The \verb+data()+ member function returns a pointer to an array of pixel data +for the image. + +\begin{verbatim} +void *data() const; +\end{verbatim} + +\noindent +This can be very slow and use huge amounts of RAM. + +Finally, two projection functions give access to the filename and history +fields maintained by the VIPS IO system. + +\begin{verbatim} +char *filename(); +char *Hist(); +\end{verbatim} + +\subsection{Assignment} + +\verb+VImage+ defines copy and assignment, with reference-counted +pointer-style semantics. For example, if you write: + +\begin{verbatim} +VImage fred( "fred.v" ); +VImage jim( "jim.v" ); + +fred = jim; +\end{verbatim} + +This will automatically close the file \verb+fred.v+, and make the variable +\verb+fred+ point to the image \verb+jim.v+ instead. Both \verb+jim+ and +\verb+fred+ now point to the same underlying image object. + +Internally, a \verb+VImage+ object is just a pointer to a reference-counting +block, which in turn holds a pointer to the underlying VIPS \verb+IMAGE+ type. +You can therefore efficiently pass \verb+VImage+ objects to functions by +value, and return \verb+VImage+ objects as function results. + +\subsection{Computing with \texttt{VImage}s} +\label{sec:compute} + +All VIPS image processing operations are member functions of the \verb+VImage+ +class. For example: + +\begin{verbatim} +VImage fred( "fred.v" ); +VImage jim( "jim.v" ); + +VImage result = fred.cos() + jim; +\end{verbatim} + +Will apply \verb+im_costra()+ to \verb+fred.v+, making an image where each +pixel is the cosine of the corresponding pixel in \verb+fred.v+; then add that +image to \verb+jim.v+. Finally, the result will be held in \verb+result+. + +VIPS is a demand-driven image processing system: when it computes expressions +like this, no actual pixels are calculated (although you can use the +projection functions on images --- \verb+result.BandFmt()+ for example). When +you finally write the result to a file (or use some operation that needs pixel +values, such as \verb+min()+, find minimum value), VIPS evaluates all of the +operations you have called to that point in parallel. If you have more than one +CPU in your machine, the load is spread over the available processors. This +means that there is no limit to the size of the images you can process. + +\pref{sec:packages} lists all of the VIPS packages. These general +rules apply: + +\begin{itemize} + +\item +VIPS operation names become C++ member function names by dropping the +\verb+im_+ prefix, and if present, the \verb+tra+ postfix, the \verb+const+ +postfix and the \verb+_vec+ postfix. For example, the +VIPS operation \verb+im_extract()+ becomes \verb+extract()+, and +\verb+im_costra()+ becomes \verb+cos()+. + +\item +The \verb+VImage+ object to which you apply the member function is the first +input image, the member function returns the first output. If there is no +image input, the member is declared \verb+static+. + +\item +\verb+INTMASK+ and \verb+DOUBLEMASK+ types become \verb+VMask+ objects, +\verb+im_col_display+ types become \verb+VDisplay+ objects. + +\item +Several C API functions can map to the same C++ API member. For example, +\verb+im_andimage+, \verb+im_andimage_vec+ and \verb+im_andimageconst+ all map +to the member \verb+andimage+. The API relies on overloading to +discriminate between these functions. + +\end{itemize} + +This part of the C++ API is generated automatically from the VIPS function +database, so it should all be up-to-date. + +There are a set of arithmetic operators defined for your convenience. You can +generally write any arithmetic expression and include \verb+VImage+ in there. + +\begin{verbatim} +VImage fred( "fred.v" ); +VImage jim( "jim.v" ); + +Vimage v = int((fred + jim) / 2); +\end{verbatim} + +\subsection{Writing results} + +Once you have computed some result, you can write it to a file with the member +\verb+write()+. It takes the following forms: + +\begin{verbatim} +VImage write( const char *name ); +VImage write( VImage out ); +VImage write(); +\end{verbatim} + +The first form simply writes the image to the named file. The second form +writes the image to the specified \verb+VImage+ object, for example: + +\begin{verbatim} +VImage fred( "fred.v" ); +VImage jim( "jim buffer", "t" ); + +Vimage v = (fred + 42).write( jim ); +\end{verbatim} + +\noindent +This creates a temporary memory buffer called \verb+jim+, and fills it with +the result of adding 42 to every pixel in \verb+fred.v+. + +The final form of \verb+write()+ writes the image to a memory buffer, and +returns that. + +\subsection{Type conversions} + +Two type conversions are defined: you can cast \verb+VImage+ to a +\verb+VDMask+ and to a \verb+VIMask+. + +\begin{verbatim} +operator VDMask(); +operator VIMask(); +\end{verbatim} + +These operations are slow and need a lot of memory! Emergencies only. diff --git a/doc/src/vipsmanual.tex b/doc/src/vipsmanual.tex new file mode 100644 index 00000000..e1f5f78f --- /dev/null +++ b/doc/src/vipsmanual.tex @@ -0,0 +1,88 @@ +\documentclass[a4paper,twocolumn,dvips]{book} +\usepackage[dvips=false,pdftex=false,vtex=false]{geometry} +\usepackage{ifpdf} +\ifpdf + \usepackage[pdftex]{graphicx,color} +\else + \usepackage{graphicx,color} +\fi +\usepackage{times} +\usepackage{fancyhdr} +\usepackage{ifthen} + +\input{mydefs} + +\fancyhead{} % clear all fields +\fancyhead[LE,RO]{\leftmark} % left-even, right-odd +\fancyhead[RE,LO]{VIPS Manual} % right-even, left-odd +\fancyfoot[LE,RO]{\thepage} % left-even, right-odd +\fancyfoot[RE,LO]{January 2007} + +\begin{document} + +\pagenumbering{roman} + +\begin{titlepage} +\thispagestyle{empty} +\begin{center} +\huge +VIPS Manual\\ +\large Version 7.12\\ +\vspace{0.5in} +\large +John Cupitt, +Kirk Martinez\\ +\end{center} + +% hmm ... must be a better way to get the quote at the bottom of the page +\vspace{6in} + +This manual formatted \today +\setcounter{page}{1} +\end{titlepage} + +% \blankpage +\tableofcontents +\thispagestyle{plain} + +% \blankpage +\listoffigures +\thispagestyle{plain} + +% \blankpage +\listoftables +\thispagestyle{plain} + +% \blankpage +\pagenumbering{arabic} +\thispagestyle{plain} +\cfoot{} + +\chapter{VIPS from C++ and Python} + +\input{cppintro} +\input{fileformat} +\input{vimage} +\input{vmask} +\input{vdisplay} +\input{verror} + +\chapter{VIPS for C programmers} + +\input{applintro} +\input{iosys} +\input{func} + +\chapter{Writing VIPS operations} + +\input{operintro} +\input{wio} +\input{pio} +\input{ipio} + +\chapter{VIPS reference} + +\input{refintro} +\input{packages} + +\end{document} diff --git a/doc/src/vmask.tex b/doc/src/vmask.tex new file mode 100644 index 00000000..45817e7f --- /dev/null +++ b/doc/src/vmask.tex @@ -0,0 +1,151 @@ +\section{The \texttt{VMask} class} + +The \verb+VMask+ class is an abstraction over the VIPS \verb+DOUBLEMASK+ and +\verb+INTMASK+ types which gives convenient and safe representation of +matricies. + +\verb+VMask+ has two sub-classes, \verb+VIMask+ and \verb+VDMask+. These +represent matricies of integers and doubles respectively. + +\subsection{Constructors} + +There are three constructors for \verb+VIMask+ and \verb+VDMask+: + +\begin{verbatim} +VIMask( int xsize, int ysize ); +VIMask( int xsize, int ysize, + int scale, int offset, ... ); +VIMask( const char *name ); +VIMask(); +VDMask( int xsize, int ysize ); +VDMask( int xsize, int ysize, + double scale, double offset, ... ); +VDMask( const char *name ); +VDMask(); +\end{verbatim} + +The first form creates an empty matrix, with the specified dimensions; +the second form initialises a matrix from a varargs list; the third form +reads the matrix from the named file. The final form makes a mask object +with no contents yet. + +\subsection{Projection functions} + +A set of member functions of \verb+VIMask+ provide access to the fields in +the matrix: + +\begin{verbatim} +int xsize() const; +int ysize() const; +int scale() const; +int offset() const; +const char *filename() const; +\end{verbatim} + +\verb+VDMask+ is the same, except that the \verb+scale()+ and \verb+offset()+ +members return \verb+double+. \verb+VMask+ allows all operations that are +common to \verb+VIMask+ and \verb+VDMask+. + +\subsection{Assignment} + +\verb+VMask+ defines copy and assignment with pointer-style +semantics. You can write stuff like: + +\begin{verbatim} +VIMask fred( "mask" ); +VMask jim; + +jim = fred; +\end{verbatim} + +This reads the file \verb+mask+, noting a pointer to the mask in \verb+fred+. +It then makes \verb+jim+ also point to it, so \verb+jim+ and \verb+fred+ are +sharing the same underlying matrix values. + +Internally, a \verb+VMask+ object is just a pointer to a reference-counting +block, which in turn holds a pointer to the underlying VIPS \verb+MASK+ type. +You can therefore efficiently pass \verb+VMask+ objects to functions by +value, and return \verb+VMask+ objects as function results. + +\subsection{Computing with \texttt{VMask}} + +You can use \verb+[]+ to get at matrix elements, numbered left-to-right, +top-to-bottom. Alternatively, use \verb+()+ to address elements by $x,y$ +position. For example: + +\begin{verbatim} +VIMask fred( "mask" ); + +for( int i = 0; i < fred.xsize(); i++ ) + fred[i] = 12; +\end{verbatim} + +\noindent +will set the first line of the matrix to 12, and: + +\begin{verbatim} +VDMask fred( "mask" ); + +for( int x = 0; x < fred.xsize(); x++ ) + fred(x, x) = 12.0; +\end{verbatim} + +\noindent +will set the leading diagonal to 12. + +See the member functions below for other operations on \verb+VMask+. + +\subsection{\texttt{VIMask} operations} + +The following operations are defined for \verb+VIMask+: + +\begin{verbatim} +// Cast to VDMask and VImage +operator VDMask(); +operator VImage(); + +// Build gaussian and log masks +static VIMask gauss( double, double ); +static VIMask log( double, double ); + +// Rotate +VIMask rotate45(); +VIMask rotate90(); + +// Transpose, invert, join and multiply +VDMask trn() ; +VDMask inv(); +VDMask cat( VDMask ); +VDMask mul( VDMask ); +\end{verbatim} + +\subsection{\texttt{VDMask} operations} + +The following operations are defined for \verb+VDMask+: + +\begin{verbatim} +// Cast to VIMask and VImage +operator VIMask(); +operator VImage(); + +// Build gauss and log masks +static VDMask gauss( double, double ); +static VDMask log( double, double ); + +// Rotate +VDMask rotate45(); +VDMask rotate90(); + +// Scale to intmask +VIMask scalei(); + +// Transpose, invert, join and multiply +VDMask trn(); +VDMask inv(); +VDMask cat( VDMask ); +VDMask mul( VDMask ); +\end{verbatim} + +\subsection{Output of masks} + +You can output masks with the usual \verb+<<+ operator. diff --git a/doc/src/wio.tex b/doc/src/wio.tex new file mode 100644 index 00000000..6f8eb278 --- /dev/null +++ b/doc/src/wio.tex @@ -0,0 +1,363 @@ +\section{Programming WIO operations} + +WIO is the style for you if you want ease of programming, or if your +algorithm must have the whole of the input image available at the same +time. For example, a Fourier transform operation is unable to produce any +output until it has seen the whole of the input image. + +\subsection{Input from an image} + +In WIO input, the whole of the image data is made available to the program +via the \verb+data+ field of the descriptor. To make an image ready for reading +in this style, programs should call \verb+im_incheck()+: + +\begin{verbatim} +int im_incheck( IMAGE *im ) +\end{verbatim} + +\noindent +If it succeeds, it returns 0, if it fails, it returns non-zero and +sets \verb+im_error()+. On success, VIPS guarantees that all of the +user-accessible fields in the descriptor contain valid data, and that all +of the image data may be read by simply reading from the \verb+data+ field +(see below for an example). This will only work for images less than about +2GB in size. + +VIPS has some simple macros to help address calculations on images: + +\begin{verbatim} +int IM_IMAGE_SIZEOF_ELEMENT( IMAGE * ) +int IM_IMAGE_SIZEOF_PEL( IMAGE * ) +int IM_IMAGE_SIZEOF_LINE( IMAGE * ) +int IM_IMAGE_N_ELEMENTS( IMAGE * ) +char *IM_IMAGE_ADDR( IMAGE *, + int x, int y ) +\end{verbatim} + +\noindent +These macros calculate \verb+sizeof()+ a band element, a pel and a horizontal +line of pels. \verb+IM_IMAGE_N_ELEMENTS+ returns the number of band elements +across an image. \verb+IM_IMAGE_ADDR+ calculates the address of a pixel in an +image. If \verb+DEBUG+ is defined, it does bounds checking too. + +\begin{fig2} +\begin{verbatim} +#include +#include + +#include + +int +average( IMAGE *im, double *out ) +{ + int x, y; + long total; + + /* Prepare for reading. + */ + if( im_incheck( im ) ) + return( -1 ); + + /* Check that this is the kind of image we can process. + */ + if( im->BandFmt != IM_BANDFMT_UCHAR || + im->Coding != IM_CODING_NONE ) { + im_error( "average", "uncoded uchar images only" ); + return( -1 ); + } + + /* Loop over the image, summing pixels. + */ + total = 0; + for( y = 0; y < im->Ysize; y++ ) { + unsigned char *p = (unsigned char *) IM_IMAGE_ADDR( im, 0, y ); + + for( x = 0; x < IM_IMAGE_N_ELEMENTS( im ); x++ ) + total += p[x]; + } + + /* Calculate average. + */ + *out = (double) total / + (IM_IMAGE_N_ELEMENTS( im ) * im->Ysize)); + + /* Success! + */ + return( 0 ); +} +\end{verbatim} +\caption{Find average of image} +\label{fg:average} +\end{fig2} + +\fref{fg:average} is a simple WIO operation which calculates the +average of an unsigned char image. It will work for any size image, with any +number of bands. See~\pref{sec:poly} for techniques for making operations +which will work for any image type. This operation might be called from an +application with: + +\begin{verbatim} +#include +#include + +#include + +void +find_average( char *name ) +{ + IMAGE *im; + double avg; + + if( !(im = im_open( name, "r" )) || + average( im, &avg ) || + im_close( im ) ) + error_exit( "failure!" ); + + printf( "Average of \"%s\" is %G\n", + name, avg ); +} +\end{verbatim} + +\noindent +When you write an image processing operation, you can test it by writing +a VIPS function descriptor and calling it from the \vips{} universal +main program, or from the \nip{} interface. See \pref{sec:appl}. + +\subsection{Output to an image} + +Before attempting WIO output, programs should call \verb+im_outcheck()+. It +has type: + +\begin{verbatim} +int im_outcheck( IMAGE *im ) +\end{verbatim} + +\noindent +If \verb+im_outcheck()+ succeeds, VIPS guarantees that WIO output is sensible. + +Programs should then set fields in the output descriptor to describe +the sort of image they wish to write (size, type, and so on) and call +\verb+im_setupout()+. It has type: + +\begin{verbatim} +int im_setupout( IMAGE *im ) +\end{verbatim} + +\noindent +\verb+im_setupout()+ creates the output file or memory buffer, using the +size and type fields that were filled in by the program between the calls to +\verb+im_outcheck()+ and \verb+im_setupout()+, and gets it ready for writing. + +Pels are written with \verb+im_writeline()+. This takes a y position (pel +(0,0) is in the top-left-hand corner of the image), a descriptor and a +pointer to a line of pels. It has type: + +\begin{verbatim} +int im_writeline( int y, + IMAGE *im, unsigned char *pels ) +\end{verbatim} + +Two convenience functions are available to make this process slightly +easier. \verb+im_iocheck()+ is useful for programs which take one input +image and produce one image output. It simply calls \verb+im_incheck()+ +and \verb+im_outcheck()+. It has type: + +\begin{verbatim} +int im_iocheck( IMAGE *in, IMAGE *out ) +\end{verbatim} + +The second convenience function copies the fields describing size, type, +metadata and history from one image descriptor to another. It is useful when +the output +image will be similar in size and type to the input image. It has type: + +\begin{verbatim} +int im_cp_desc( IMAGE *out, IMAGE *in ) +\end{verbatim} + +\noindent +There's also \verb+im_cp_descv()+, see the man page. + +\begin{fig2} +\begin{verbatim} +#include +#include + +#include +#include + +int +invert( IMAGE *in, IMAGE *out ) +{ + int x, y; + unsigned char *buffer; + + /* Check images. + */ + if( im_iocheck( in, out ) ) + return( -1 ); + if( in->BandFmt != IM_BANDFMT_UCHAR || in->Coding != IM_CODING_NONE ) { + im_error( "invert", "uncoded uchar images only" ); + return( -1 ); + } + + /* Make output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_setupout( out ) ) + return( -1 ); + + /* Allocate a line buffer and make sure it will be freed correctly. + */ + if( !(buffer = IM_ARRAY( out, + IM_IMAGE_SIZEOF_LINE( in ), unsigned char )) ) + return( -1 ); + + /* Loop over the image! + */ + for( y = 0; y < in->Ysize; y++ ) { + unsigned char *p = (unsigned char *) IM_IMAGE_ADDR( in, 0, y ); + + for( x = 0; x < IM_IMAGE_N_ELEMENTS( in ); x++ ) + buffer[x] = 255 - p[x]; + if( im_writeline( y, out, buffer ) ) + return( -1 ); + } + + return( 0 ); +} +\end{verbatim} +\caption{Invert an image} +\label{fg:invert} +\end{fig2} + +\fref{fg:invert} is a WIO VIPS operation which finds the photographic +negative of an unsigned char image. See \pref{sec:malloc} for an explanation +of \verb+IM_ARRAY+. This operation might be called from an +application with: + +\begin{verbatim} +#include +#include + +#include + +void +find_negative( char *inn, char *outn ) +{ + IMAGE *in, *out; + + if( !(in = im_open( inn, "r" )) || + !(out = im_open( outn, "w" )) || + invert( in, out ) || + im_updatehist( out, "invert" ) || + im_close( in ) || + im_close( out ) ) + error_exit( "failure!" ); +} +\end{verbatim} + +See \pref{sec:history} for an explanation of the call to \verb+im_updatehist()+. + +\subsection{Polymorphism} +\label{sec:poly} + +Most image processing operations in the VIPS library can operate on +images of any type (\verb+IM_BANDFMT_UCHAR+, as in our examples above, +also \verb+IM_BANDFMT_UINT+ etc.). This is usually implemented with code +replication: the operation contains loops for processing every kind of image, +and when called, invokes the appropriate loop for the image it is given. + +As an example, figure~\ref{fg:exp} calculates \verb+exp()+ for every pel +in an image. If the input image is \verb+double+, we write \verb+double+ +output. If it is any other non-complex type, we write \verb+float+. If it +is complex, we flag an error (\verb+exp()+ of a complex number is fiddly). +The example uses an image type predicate, \verb+im_iscomplex()+. There are +a number of these predicate functions, see the manual page. + +\begin{fig2} +\begin{verbatim} +#include +#include +#include + +#include +#include + +/* Exponential transform. + */ +int +exptra( IMAGE *in, IMAGE *out ) +{ + int x, y; + unsigned char *buffer; + + /* Check descriptors. + */ + if( im_iocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_error( "exptra", "uncoded non-complex only" ); + return( -1 ); + } + + /* Make output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( in->BandFmt != IM_BANDFMT_DOUBLE ) + out->BandFmt = IM_BANDFMT_FLOAT; + if( im_setupout( out ) ) + return( -1 ); +\end{verbatim} +\caption{Calculate \texttt{exp()} for an image} +\label{fg:exp} +\end{fig2} + +\begin{fig2} +\begin{verbatim} + /* Allocate a line buffer. + */ + if( !(buffer = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( in ), unsigned char )) ) + return( -1 ); + +/* Our inner loop, parameterised for both the input and output + * types. Note the use of `\', since macros have to be all on + * one line. + */ +#define loop(IN, OUT) { \ + for( y = 0; y < in->Ysize; y++ ) { \ + IN *p = (IN *) IM_IMAGE_ADDR( in, 0, y ); \ + OUT *q = (OUT *) buffer; \ + \ + for( x = 0; x < IM_IMAGE_N_ELEMENTS( in ); x++ ) \ + q[x] = exp( p[x] ); \ + if( im_writeline( y, out, buffer ) ) \ + return( -1 ); \ + } \ +} + + /* Switch for all the types we can handle. + */ + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: loop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: loop( char, float ); break; + case IM_BANDFMT_USHORT:loop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: loop( short, float ); break; + case IM_BANDFMT_UINT: loop( unsigned int, float ); break; + case IM_BANDFMT_INT: loop( int, float ); break; + case IM_BANDFMT_FLOAT: loop( float, float ); break; + case IM_BANDFMT_DOUBLE:loop( double, double ); break; + default: + im_error( "exptra", "internal error" ); + return( -1 ); + } + + /* Success. + */ + return( 0 ); +} +\end{verbatim} +\caption{Calculate \texttt{exp()} for an image (cont)} +\end{fig2} diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 00000000..d3c74249 --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = vips diff --git a/include/vips/Makefile.am b/include/vips/Makefile.am new file mode 100644 index 00000000..42edea2e --- /dev/null +++ b/include/vips/Makefile.am @@ -0,0 +1,34 @@ +pkginclude_HEADERS = \ + VDisplay.h \ + VError.h \ + VImage.h \ + VMask.h \ + vipscpp.h \ + colour.h \ + debug.h \ + dispatch.h \ + fmask.h \ + history.h \ + mosaic.h \ + proto.h \ + rect.h \ + region.h \ + r_access.h \ + struct.h \ + semaphore.h \ + threadgroup.h \ + thread.h \ + time.h \ + util.h \ + meta.h \ + version.h \ + vips.h \ + vips \ + intl.h \ + vbuf.h \ + vipsc++.h + +vipsc++.h: + vips --cpph all > vipsc++.h + +EXTRA_DIST = version.h.in internal.h diff --git a/include/vips/VDisplay.h b/include/vips/VDisplay.h new file mode 100644 index 00000000..5e027a65 --- /dev/null +++ b/include/vips/VDisplay.h @@ -0,0 +1,113 @@ +/* VIPS display class. + * + * Hide details of im_col_display API. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VDISPLAY_H +#define IM_VDISPLAY_H + +/* SWIG includes this file directly rather than going through vipscpp.h ... so + * we have to define these macros here as well. + */ +#ifdef SWIG +#define VIPS_NAMESPACE_START namespace vips { +#define VIPS_NAMESPACE_END } +#endif /*SWIG*/ + +/* Wrap pointers to these, but we don't want to import all the old C API. Just + * declare them. + */ +extern "C" { + struct im_col_display; + struct im_col_tab_disp; +} + +VIPS_NAMESPACE_START + +// Wrapper over im_col_display with ref counting +class VDisplay { + struct refblock { + im_col_display *disp; // im_col_display struct + im_col_tab_disp *luts; // luts built from this display + int priv; // disp is ours, or system + int nrefs; // Refs to us + + // Invalidate lut + void cleanlut(); + + // Break attached stuff + void cleanref(); + + // Get ready to write + void wready() throw( VError ); + + // Check that luts are up-to-date + void cluts() throw( VError ); + + refblock() : disp(0), luts(0), priv(0), nrefs(1) {} + ~refblock() { cleanref(); } + }; + + refblock *ref; + +public: + enum VDisplayType { + BARCO, // Does many corrections for us + DUMB // Needs many corrections + }; + + // Get named display + VDisplay( const char *name ) throw( VError ); + + // Get default display + VDisplay(); + + // Copy constructor + VDisplay( const VDisplay &a ) { ref = a.ref; ref->nrefs++; } + + // Assignment + VDisplay &operator=( const VDisplay &a ); + + // Destructor + virtual ~VDisplay(); + + // The matrix type we use + typedef float matrix[3][3]; + + // Extract display pointer + void *disp() const { return( ref->disp ); } + + // Extract luts pointer, rebuilding luts if necessary + im_col_tab_disp *luts() const throw( VError ) + { ref->cluts(); return( ref->luts ); } +}; + +VIPS_NAMESPACE_END + +#endif /*IM_VDISPLAY_H*/ diff --git a/include/vips/VError.h b/include/vips/VError.h new file mode 100644 index 00000000..69aa4b83 --- /dev/null +++ b/include/vips/VError.h @@ -0,0 +1,82 @@ +// Header for error type + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VERROR_H +#define IM_VERROR_H + +/* SWIG includes this file directly rather than going through vipscpp.h ... so + * we have to define these macros here as well. + */ +#ifdef SWIG +#define VIPS_NAMESPACE_START namespace vips { +#define VIPS_NAMESPACE_END } +#endif /*SWIG*/ + +/* Don't include these when parsing for SWIG. + */ +#ifndef SWIG +# include +# include +# include +#endif /*!SWIG*/ + +VIPS_NAMESPACE_START + +// Error type +class VError : public std::exception { + std::string _what; + +public: + VError( std::string what ) : _what( what ) {} + VError() {} + virtual ~VError() throw() {} + + // Print message and exit + void perror( const char * ); + void perror(); + + // Append some more text to the message + VError &app( std::string txt ); + VError &app( const int i ); + + // Extract string + virtual const char *what() const throw() { return _what.c_str(); } + void ostream_print( std::ostream & ) const; +}; + +inline std::ostream &operator<<( std::ostream &file, const VError &err ) +{ + err.ostream_print( file ); + return( file ); +} + +void verror( std::string str = "" ) throw( VError ); + +VIPS_NAMESPACE_END + +#endif /*IM_VERROR_H*/ diff --git a/include/vips/VImage.h b/include/vips/VImage.h new file mode 100644 index 00000000..3444f2fa --- /dev/null +++ b/include/vips/VImage.h @@ -0,0 +1,401 @@ +// VIPS image wrapper + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VIMAGE_H +#define IM_VIMAGE_H + +/* SWIG includes this file directly rather than going through vipscpp.h ... so + * we have to define these macros here as well. + */ +#ifdef SWIG +# define VIPS_NAMESPACE_START namespace vips { +# define VIPS_NAMESPACE_END } +#endif /*SWIG*/ + +/* Don't include these when parsing for SWIG. + */ +#ifndef SWIG +# include +# include +# include +#endif /*!SWIG*/ + +/* Wrap pointers to these, but we don't want to import all the old C API. Just + * declare them. + */ +extern "C" { + struct im__IMAGE; + + /* Needed by Vargv, see below. + */ + struct im__function; + typedef void *im__object; +} + +VIPS_NAMESPACE_START + +/* VIPS image class. + * + * Slightly tricky: we have two sorts of sharing. Several VImage can share one + * refblock (while results are being returned from functions, for example), + * and several other refblocks can have IMAGEs which depend upon this IMAGE + * for their result. + */ +class VImage { + /* We'd like this to be protected so that user subclasses can define + * their own member wrappers. But sadly C++ doesn't work like that: + * subclasses of VImage can only refer to protected members via + * this->, which isn't what we need. Just make it public and hope no + * one touches it. + */ +public: +/* Doesn't need to be wrapped. + */ +#ifndef SWIG + // Count ref etc. in one of these. One for each open VIPS image. + struct refblock { + im__IMAGE *im; // IMAGE pointer + int close_on_delete; // Set if we must im_close() + int nrefs; // Number of refs to us + std::list orefs; // Refs im makes + + // Construct/destruct + refblock(); + virtual ~refblock() throw( VError ); + + // Add a ref - this (output image) depends upon IMAGE in + void addref( refblock *in ) throw( VError ); + + // Remove a ref + void removeref() throw( VError ); + + // Debugging + void debug_print(); + + // Linked list needs "==" -- use address equivalence + friend int operator==( const refblock &left, + const refblock &right ) { return( &left == &right ); } + }; + + refblock *_ref; +#endif /*!SWIG*/ + +public: +#ifdef DEBUG + /* All the refblocks in the world. + */ + static std::list all_refblock; +#endif /*DEBUG*/ + + /* Print all refblocks ... debugging. Compile with DEBUG to enable + * this. + */ + static void print_all(); + + /* Typedefs and enums we need. + */ + + // Type type + enum TType { + MULTIBAND = 0, + B_W = 1, + LUMINACE = 2, + XRAY = 3, + IR = 4, + YUV = 5, + RED_ONLY = 6, + GREEN_ONLY = 7, + BLUE_ONLY = 8, + POWER_SPECTRUM = 9, + HISTOGRAM = 10, + LUT = 11, + XYZ = 12, + LAB = 13, + CMC = 14, + CMYK = 15, + LABQ = 16, + RGB = 17, + UCS = 18, + LCH = 19, + LABS = 21, + sRGB = 22, + YXY = 23, + FOURIER = 24, + RGB16 = 25, + GREY16 = 26 + }; + + // Format type + enum TBandFmt { + FMTNOTSET = -1, + FMTUCHAR = 0, + FMTCHAR = 1, + FMTUSHORT = 2, + FMTSHORT = 3, + FMTUINT = 4, + FMTINT = 5, + FMTFLOAT = 6, + FMTCOMPLEX = 7, + FMTDOUBLE = 8, + FMTDPCOMPLEX = 9 + }; + + // Coding type + enum TCoding { + NOCODING = 0, + COLQUANT = 1, + LABPACK = 2, + LABPACK_COMPRESSED = 3, + RGB_COMPRESSED = 4, + LUM_COMPRESSED = 5 + }; + + // Compression type + enum TCompression { + NO_COMPRESSION = 0, + TCSF_COMPRESSION = 1, + JPEG_COMPRESSION = 2 + }; + + /* Start of wrappers for iofuncs. + */ + + // Plain constructors + VImage( const char *name, const char *mode = "r" ) throw( VError ); + VImage( void *data, int width, int height, + int bands, TBandFmt format ) throw( VError ); + VImage( im__IMAGE *image ); + VImage() throw( VError ); + + // Copy constructor + VImage( const VImage &a ); + + // Assignment - delete old ref + VImage &operator=( const VImage &a ) throw( VError ); + + // Destructor + virtual ~VImage() throw( VError ) { _ref->removeref(); } + + // Extract underlying IMAGE* pointer + im__IMAGE *image() const { return( _ref->im ); } + + // Extract underlying data pointer + void *data() const throw( VError ); + + // Write this to another VImage, to a file, or to a mem buffer + VImage write( VImage out ) throw( VError ); + VImage write( const char *name ) throw( VError ); + VImage write() throw( VError ); + + // Debugging ... print header fields + void debug_print(); + + // Projection functions to get header fields + int Xsize(); + int Ysize(); + int Bands(); + TBandFmt BandFmt(); + TCoding Coding(); + TType Type(); + float Xres(); + float Yres(); + int Length(); + TCompression Compression(); + short Level(); + int Xoffset(); + int Yoffset(); + + // Derived fields + const char *filename(); + const char *Hist(); + + // Set header fields + void initdesc( int, int, int, TBandFmt, TCoding, TType, + float = 1.0, float = 1.0, int = 0, int = 0 ) throw( VError ); + + /* Insert automatically generated headers. + */ +#include "vipsc++.h" + +/* No point getting SWIG to wrap these ... we do this by hand later so we can + * handle things like "a + 12" correctly. + */ +#ifndef SWIG + // And some in-line operator equivalences done by hand + friend VImage operator+( VImage a, VImage b ) throw( VError ) + { return( a.add( b ) ); } + friend VImage operator+( double a, VImage b ) throw( VError ) + { return( b.lin( 1.0, a ) ); } + friend VImage operator+( VImage a, double b ) throw( VError ) + { return( a.lin( 1.0, b ) ); } + + friend VImage operator-( VImage a, VImage b ) throw( VError ) + { return( a.subtract( b ) ); } + friend VImage operator-( double a, VImage b ) throw( VError ) + { return( b.lin( -1.0, a ) ); } + friend VImage operator-( VImage a, double b ) throw( VError ) + { return( a.lin( 1.0, -b ) ); } + + friend VImage operator*( VImage a, VImage b ) throw( VError ) + { return( a.multiply( b ) ); } + friend VImage operator*( double a, VImage b ) throw( VError ) + { return( b.lin( a, 0.0 ) ); } + friend VImage operator*( VImage a, double b ) throw( VError ) + { return( a.lin( b, 0.0 ) ); } + + friend VImage operator/( VImage a, VImage b ) throw( VError ) + { return( a.divide( b ) ); } + friend VImage operator/( double a, VImage b ) throw( VError ) + { return( b.pow( -1.0 ).lin( a, 0.0 ) ); } + friend VImage operator/( VImage a, double b ) throw( VError ) + { return( a.lin( 1.0/b, 0.0 ) ); } + + friend VImage operator%( VImage a, VImage b ) throw( VError ) + { return( a.remainder( b ) ); } + friend VImage operator%( VImage a, double b ) throw( VError ) + { return( a.remainder( b ) ); } + + friend VImage operator<( VImage a, VImage b ) throw( VError ) + { return( a.less( b ) ); } + friend VImage operator<( double a, VImage b ) throw( VError ) + { return( b.more( a ) ); } + friend VImage operator<( VImage a, double b ) throw( VError ) + { return( a.less( b ) ); } + + friend VImage operator<=( VImage a, VImage b ) throw( VError ) + { return( a.lesseq( b ) ); } + friend VImage operator<=( double a, VImage b ) throw( VError ) + { return( b.moreeq( a ) ); } + friend VImage operator<=( VImage a, double b ) throw( VError ) + { return( a.lesseq( b ) ); } + + friend VImage operator>( VImage a, VImage b ) throw( VError ) + { return( a.more( b ) ); } + friend VImage operator>( double a, VImage b ) throw( VError ) + { return( b.less( a ) ); } + friend VImage operator>( VImage a, double b ) throw( VError ) + { return( a.more( b ) ); } + + friend VImage operator>=( VImage a, VImage b ) throw( VError ) + { return( a.moreeq( b ) ); } + friend VImage operator>=( double a, VImage b ) throw( VError ) + { return( b.lesseq( a ) ); } + friend VImage operator>=( VImage a, double b ) throw( VError ) + { return( a.moreeq( b ) ); } + + friend VImage operator==( VImage a, VImage b ) throw( VError ) + { return( a.equal( b ) ); } + friend VImage operator==( double a, VImage b ) throw( VError ) + { return( b.equal( a ) ); } + friend VImage operator==( VImage a, double b ) throw( VError ) + { return( a.equal( b ) ); } + + friend VImage operator!=( VImage a, VImage b ) throw( VError ) + { return( a.notequal( b ) ); } + friend VImage operator!=( double a, VImage b ) throw( VError ) + { return( b.notequal( a ) ); } + friend VImage operator!=( VImage a, double b ) throw( VError ) + { return( a.notequal( b ) ); } + + friend VImage operator&( VImage a, VImage b ) throw( VError ) + { return( a.andimage( b ) ); } + friend VImage operator&( int a, VImage b ) throw( VError ) + { return( b.andimage( a ) ); } + friend VImage operator&( VImage a, int b ) throw( VError ) + { return( a.andimage( b ) ); } + + friend VImage operator|( VImage a, VImage b ) throw( VError ) + { return( a.orimage( b ) ); } + friend VImage operator|( int a, VImage b ) throw( VError ) + { return( b.orimage( a ) ); } + friend VImage operator|( VImage a, int b ) throw( VError ) + { return( a.orimage( b ) ); } + + friend VImage operator^( VImage a, VImage b ) throw( VError ) + { return( a.eorimage( b ) ); } + friend VImage operator^( int a, VImage b ) throw( VError ) + { return( b.eorimage( a ) ); } + friend VImage operator^( VImage a, int b ) throw( VError ) + { return( a.eorimage( b ) ); } + + friend VImage operator<<( VImage a, int b ) throw( VError ) + { return( a.shiftleft( b ) ); } + friend VImage operator>>( VImage a, int b ) throw( VError ) + { return( a.shiftright( b ) ); } + + friend VImage operator-( VImage a ) throw( VError ) + { return( a * -1 ); } + + // Type conversion: VImage to VDMask and VIMask + operator VDMask() throw( VError ) + { return( this->vips2mask() ); } + operator VIMask() throw( VError ) + { return( VIMask( VDMask( *this ) ) ); } +#endif /*!SWIG*/ +}; + +/* Don't include these when parsing for SWIG. + */ +#ifndef SWIG + +/* Class wrapping up a vargv. Member function wrappers need this. It needs to + * be part of the public API in case people subclass VImage and add their own + * members. + */ +class Vargv { + // Function we are args to + im__function *fn; + + // Base of object vector + im__object *base; + +public: + Vargv( const char *name ); + ~Vargv(); + + // Reference to element of base + im__object &data( int i = 0 ) { return( base[i] ); }; + + // Invoke function + void call(); +}; + +#endif /*!SWIG*/ + +VIPS_NAMESPACE_END + +// Other VIPS protos +extern "C" { +extern int im_init_world( const char *argv0 ); +extern void im__print_all(); +extern void im_col_Lab2XYZ( + float, float, float, + float *, float *, float * ); +} + +#endif /*IM_VIMAGE_H*/ diff --git a/include/vips/VMask.h b/include/vips/VMask.h new file mode 100644 index 00000000..76a7e4ae --- /dev/null +++ b/include/vips/VMask.h @@ -0,0 +1,375 @@ +/* VIPS mask class. + * + * Just like VImage, but we don't need dependency stuff. Instead, have a base + * wrapper over *MASK, derive VMaskD and VMaskI from that, and then put + * refcounting over all of them. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VMASK_H +#define IM_VMASK_H + +/* SWIG includes this file directly rather than going through vipscpp.h ... so + * we have to define these macros here as well. + */ +#ifdef SWIG +# define VIPS_NAMESPACE_START namespace vips { +# define VIPS_NAMESPACE_END } +#endif /*SWIG*/ + +/* Don't include these when parsing for SWIG. + */ +#ifndef SWIG +# include +# include +#endif /*!SWIG*/ + +/* Wrap pointers to these, but we don't want to import all the old C API. Just + * declare them. + */ +extern "C" { + struct im__INTMASK; + struct im__DOUBLEMASK; +} + +VIPS_NAMESPACE_START + +/* This first section is private. Only expose the non-P versions of these + * classes later on. Don't need to wrap then in SWIG either. + */ +#ifndef SWIG +namespace _private_detail { + +union MASKUNION { + im__INTMASK *iptr; + im__DOUBLEMASK *dptr; +}; + +// Private wrapper over *MASK - user does not see this +class VPMask { + friend class VMask; + +public: + // Track type of mask with this + enum VMaskType { + UNASSIGNED, // Not yet set + INT, // mask points to INTMASK + DOUBLE // mask points to DOUBLEMASK + }; + + MASKUNION data; // Mask pointer - INT or DOUBLE + VMaskType type; // Track type too, for safety + + virtual ~VPMask() {}; + + // Duplicate + virtual VPMask *dup() const = 0; + + // Projection functions to get MASK fields + virtual int xsize() const = 0; + virtual int ysize() const = 0; + virtual const char *filename() const = 0; + + // Output + virtual void ostream_print( std::ostream & ) const = 0; +}; + +// Specialise for INTMASK +class VPIMask : public VPMask { +public: + VPIMask( int xsize, int ysize ) throw( VError ); + VPIMask( int xsize, int ysize, int scale, int offset, va_list ap ) + throw( VError ); + VPIMask( const char * ) + throw( VError ); + VPIMask( im__INTMASK * ); + VPIMask(); + virtual ~VPIMask(); + + VPMask *dup() const throw( VError ); + void embed( im__INTMASK * ) throw( VError ); + + int xsize() const throw( VError ); + int ysize() const throw( VError ); + int scale() const throw( VError ); + int offset() const throw( VError ); + const char *filename() const throw( VError ); + + // Output + virtual void ostream_print( std::ostream & ) const throw( VError ); + + // Extract start of array of ints + int *array() const; +}; + +// Specialise for DOUBLEMASK +class VPDMask : public VPMask { +public: + VPDMask( int xsize, int ysize ) throw( VError ); + VPDMask( int xsize, int ysize, + double scale, double offset, va_list ap ) throw( VError ); + VPDMask( const char * ) throw( VError ); + VPDMask( im__DOUBLEMASK * ); + VPDMask(); + virtual ~VPDMask(); + + VPMask *dup() const throw( VError ); + void embed( im__DOUBLEMASK * ) throw( VError ); + + int xsize() const throw( VError ); + int ysize() const throw( VError ); + double scale() const throw( VError ); + double offset() const throw( VError ); + const char *filename() const throw( VError ); + + // Output + virtual void ostream_print( std::ostream & ) const throw( VError ); + + // Extract start of array of doubles + double *array() const; +}; + +} // end of namespace _private_detail + +inline std::ostream &operator<<( std::ostream &file, + const _private_detail::VPMask &msk ) +{ + msk.ostream_print( file ); + return( file ); +} + +#endif /*!SWIG*/ + +// Wrapper over VP?Mask with ref counting +class VMask { +protected: + struct refblock { + _private_detail::VPMask *pmask; // Mask: double or int + int nrefs; // Refs to us + + refblock() : pmask(0), nrefs(1) {} + virtual ~refblock() { delete pmask; } + }; + + refblock *ref; + + // Make sure this is a private copy of pmask --- dup if nrefs != 1 + void make_private(); + +public: + // Constructor leaves msk uninitialised + VMask() { ref = new refblock; } + + // Copy constructor + VMask( const VMask &a ) { ref = a.ref; ref->nrefs++; } + + // Assignment + VMask &operator=( const VMask &a ); + + // Destructor + virtual ~VMask(); + + int xsize() const throw( VError ) + { return( ref->pmask->xsize() ); } + int ysize() const throw( VError ) + { return( ref->pmask->ysize() ); } + int size() const throw( VError ) + { return( xsize() * ysize() ); } + const char *filename() const throw( VError ) + { return( ref->pmask->filename() ); } + + // Extract underlying type + _private_detail::VPMask::VMaskType type() const + { return( ref->pmask->type ); } + + // Extract underlying VIPS pointer + _private_detail::MASKUNION mask() const { return( ref->pmask->data ); } + + void ostream_print( std::ostream & ) const; +}; + +inline std::ostream &operator<<( std::ostream &file, const VMask &msk ) +{ + msk.ostream_print( file ); + return( file ); +} + +// Need to forward ref these +class VDMask; +class VImage; + +// Wrapper over _private_detail::VPIMask with ref counting +class VIMask : public VMask { +public: + VIMask( int xsize, int ysize ) + { + ref->pmask = new _private_detail::VPIMask( xsize, ysize ); + } + + VIMask( int xsize, int ysize, int scale, int offset, ... ) + { + va_list ap; + + va_start( ap, offset ); + ref->pmask = new _private_detail::VPIMask( xsize, ysize, + scale, offset, ap ); + va_end( ap ); + } + + VIMask( const char *name ) + { + ref->pmask = new _private_detail::VPIMask( name ); + } + + // No mask there yet + VIMask() {} + + int scale() + { + return( ((_private_detail::VPIMask *)ref->pmask)->scale() ); + } + + int offset() + { + return( ((_private_detail::VPIMask *)ref->pmask)->offset() ); + } + + // Embed INTMASK in VIMask + void embed( im__INTMASK * ) throw( VError ); + + // Overload [] to get linear array subscript. + int &operator[]( int ) throw( VError ); + + // Overload () to get matrix subscript. + int &operator()( int x, int y ) throw( VError ) + { return( (*this)[x + y*xsize()] ); } + + // and as a function call that SWIG can wrap + int get( int i ) throw( VError ) + { return( (*this)[i] ); } + + // Type conversion: INTMASK->DOUBLEMASK + operator VDMask(); + + // Type conversion: INTMASK->image + operator VImage(); + + // VIMask build functions + static VIMask gauss( double, double ) throw( VError ); + static VIMask log( double, double ) throw( VError ); + + // VIMask manipulation + VIMask rotate45() throw( VError ); + VIMask rotate90() throw( VError ); + + // Arithmetic ... cast to double, and use VDMask funcs. For some + // reason, the compiler won't let us do casts to VDImage yet, so no + // inlines. + VDMask trn() throw( VError ); + VDMask inv() throw( VError ); + VDMask cat( VDMask ) throw( VError ); + VDMask mul( VDMask ) throw( VError ); +}; + +// Wrapper over _private_detail::VPDMask with ref counting +class VDMask : public VMask { +public: + VDMask( int xsize, int ysize ) + { + ref->pmask = new _private_detail::VPDMask( xsize, ysize ); + } + + VDMask( int xsize, int ysize, double scale, double offset, ... ) + { + va_list ap; + + va_start( ap, offset ); + ref->pmask = new _private_detail::VPDMask( xsize, ysize, + scale, offset, ap ); + va_end( ap ); + } + + VDMask( const char *name ) + { + ref->pmask = new _private_detail::VPDMask( name ); + } + + // No mask yet + VDMask() { } + + // Embed DOUBLEMASK in VDMask + void embed( im__DOUBLEMASK * ) throw( VError ); + + double scale() throw( VError ) + { + return( ((_private_detail::VPDMask *)ref->pmask)->scale() ); + } + + double offset() throw( VError ) + { + return( ((_private_detail::VPDMask *)ref->pmask)->offset() ); + } + + // Overload [] to get linear array subscript. + double &operator[]( int ) throw( VError ); + + // Overload () to get matrix subscript. + double &operator()( int x, int y ) throw( VError ) + { return( (*this)[x + y*xsize()] ); } + + // and as a function call that SWIG can wrap + double get( int i ) throw( VError ) + { return( (*this)[i] ); } + + // Type conversion: double->int + operator VIMask(); + + // Type conversion: DOUBLEMASK->image + operator VImage() throw( VError ); + + // VDMask build functions + static VDMask gauss( double, double ) throw( VError ); + static VDMask log( double, double ) throw( VError ); + + // VDMask manipulation + VDMask rotate45() throw( VError ); + VDMask rotate90() throw( VError ); + + // Scale to intmask + VIMask scalei() throw( VError ); + + // Simple arithmetic + VDMask trn() throw( VError ); + VDMask inv() throw( VError ); + VDMask cat( VDMask ) throw( VError ); + VDMask mul( VDMask ) throw( VError ); +}; + +VIPS_NAMESPACE_END + +#endif /*IM_VMASK_H*/ diff --git a/include/vips/colour.h b/include/vips/colour.h new file mode 100644 index 00000000..088fdebf --- /dev/null +++ b/include/vips/colour.h @@ -0,0 +1,234 @@ +/* Definitions for VIPS colour package. + * + * J.Cupitt, 8/4/93 + * 15/7/96 JC + * - C++ stuff added + * 20/2/98 JC + * - new display calibration added + * 26/9/05 + * - added IM_ prefix to colour temps + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_COLOUR_H +#define IM_COLOUR_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#include + +/* Convert degrees->rads and vice-versa. + */ +#define IM_RAD( r ) (((r) / 360.0) * 2.0 * IM_PI) +#define IM_DEG( a ) (((a) / (2.0 * IM_PI)) * 360.0) + +/* Areas under curves for Dxx. 2 degree observer. + */ +#define IM_D93_X0 (89.7400) +#define IM_D93_Y0 (100.0) +#define IM_D93_Z0 (130.7700) + +#define IM_D75_X0 (94.9682) +#define IM_D75_Y0 (100.0) +#define IM_D75_Z0 (122.5710) + +/* D65 temp 6504. + */ +#define IM_D65_X0 (95.0470) +#define IM_D65_Y0 (100.0) +#define IM_D65_Z0 (108.8827) + +#define IM_D55_X0 (95.6831) +#define IM_D55_Y0 (100.0) +#define IM_D55_Z0 (92.0871) + +#define IM_D50_X0 (96.4250) +#define IM_D50_Y0 (100.0) +#define IM_D50_Z0 (82.4680) + +/* A temp 2856k. + */ +#define IM_A_X0 (109.8503) +#define IM_A_Y0 (100.0) +#define IM_A_Z0 (35.5849) + +/* B temp 4874k. + */ +#define IM_B_X0 (99.0720) +#define IM_B_Y0 (100.0) +#define IM_B_Z0 (85.2230) + +/* C temp 6774k. + */ +#define IM_C_X0 (98.0700) +#define IM_C_Y0 (100.0) +#define IM_C_Z0 (118.2300) + +#define IM_E_X0 (100.0) +#define IM_E_Y0 (100.0) +#define IM_E_Z0 (100.0) + +#define IM_D3250_X0 (105.6590) +#define IM_D3250_Y0 (100.0) +#define IM_D3250_Z0 (45.8501) + +/* Two kinds of display. A DISP_BARCO does gamma correction etc etc for us and + * needs only a colour space transform, a DISP_DUMB is an ordinary display and + * needs a full range of corrections. + */ +enum im_col_disp_type { + DISP_BARCO = 0, + DISP_DUMB +}; + +/* Structure for holding information about a display device. See the BARCO + * papers for details on the fields. + */ +struct im_col_display { + char *d_name; /* Display name */ + enum im_col_disp_type d_type; /* Display type */ + float d_mat[3][3]; /* XYZ -> luminance matrix */ + float d_YCW; /* Luminosity of reference white */ + float d_xCW; /* x, y for reference white */ + float d_yCW; + float d_YCR; /* Light o/p for reference white */ + float d_YCG; + float d_YCB; + int d_Vrwr; /* Pixel values for ref. white */ + int d_Vrwg; + int d_Vrwb; + float d_Y0R; /* Residual light for black pixel */ + float d_Y0G; + float d_Y0B; + float d_gammaR; /* Gamma values for the three guns */ + float d_gammaG; + float d_gammaB; + float d_B; /* 'Background' (like brightness) */ + float d_P; /* 'Picture' (like contrast) */ +}; + +/* Structure for holding the lookup tables for XYZ<=>rgb conversion. + * Also holds the luminance to XYZ matrix and the inverse one. + */ +struct im_col_tab_disp { + float t_Yr2r[1501]; /* Conversion of Yr to r */ + float t_Yg2g[1501]; /* Conversion of Yg to g */ + float t_Yb2b[1501]; /* Conversion of Yb to b */ + float t_r2Yr[1501]; /* Conversion of r to Yr */ + float t_g2Yg[1501]; /* Conversion of g to Yg */ + float t_b2Yb[1501]; /* Conversion of b to Yb */ + float mat_XYZ2lum[3][3]; /* XYZ to Yr, Yg, Yb matrix */ + float mat_lum2XYZ[3][3]; /* Yr, Yg, Yb to XYZ matrix */ + float rstep, gstep, bstep; + float ristep, gistep, bistep; +}; + +/* Colour loading and conversion functions. + */ +void im_col_ab2Ch( float a, float b, float *C, float *h ); +void im_col_LCh2ab( float L, float C, float h, float *a, float *b ); +void im_col_XYZ2Lab( float X, float Y, float Z, float *L, float *a, float *b ); +void im_col_Lab2XYZ( float L, float a, float b, float *X, float *Y, float *Z ); +float im_col_pythagoras( float L1, float a1, float b1, + float L2, float a2, float b2 ); +struct im_col_tab_disp *im_col_make_tables_RGB( + IMAGE *im, + struct im_col_display *d ); +int im_col_rgb2XYZ( struct im_col_display *d, + struct im_col_tab_disp *table, + int r, int g, int b, + float *X, float *Y, float *Z ); +int im_col_XYZ2rgb( + struct im_col_display *d, struct im_col_tab_disp *table, + float X, float Y, float Z, + int *r_ret, int *g_ret, int *b_ret, + int *or_ret ); + +float im_col_L2Lucs( float L ); +float im_col_Lucs2L( float Lucs ); +float im_col_C2Cucs( float C ); +float im_col_Cucs2C( float Cucs ); +float im_col_Ch2hucs( float C, float h ); +float im_col_Chucs2h( float C, float hucs ); +double im_col_ab2h( double a, double b ); + +int im_ICC2display( char *filename, struct im_col_display *dpy ); +int im_XYZ2disp( IMAGE *, IMAGE *, struct im_col_display * ); +int im_Lab2disp( IMAGE *, IMAGE *, struct im_col_display * ); +int im_LabQ2disp( IMAGE *, IMAGE *, struct im_col_display * ); +int im_disp2XYZ( IMAGE *, IMAGE *, struct im_col_display * ); +int im_disp2Lab( IMAGE *, IMAGE *, struct im_col_display * ); + +void *im_LabQ2disp_build_table( IMAGE *out, struct im_col_display *d ); +int im_LabQ2disp_table( IMAGE *in, IMAGE *out, void *table ); + +int im_dE_fromdisp( IMAGE *, IMAGE *, IMAGE *, struct im_col_display * ); +int im_dECMC_fromdisp( IMAGE *, IMAGE *, IMAGE *, struct im_col_display * ); +int im_dE00_fromLab( IMAGE *, IMAGE *, IMAGE * ); + +/* Colour display values and arrays + &im_col_screen_white, index 0 + &im_col_SPARC_white, index 1 + &im_col_D65_white, index 2 + &im_col_barco_white, index 3 + &im_col_mitsubishi, index 4 + &im_col_relative, index 5 + &ultra2, index 6 + &srgb_profile, index 7 + */ +struct im_col_display *im_col_displays( int ); +struct im_col_display *im_col_display_name( const char * ); + +/* Render intents for icc wrappers. + */ +#define IM_INTENT_PERCEPTUAL (0) +#define IM_INTENT_RELATIVE_COLORIMETRIC (1) +#define IM_INTENT_SATURATION (2) +#define IM_INTENT_ABSOLUTE_COLORIMETRIC (3) + +int im_icc_present( void ); +int im_icc_transform( IMAGE *in, IMAGE *out, + const char *input_profile_filename, + const char *output_profile_filename, + int intent ); +int im_icc_import( IMAGE *in, IMAGE *out, + const char *input_profile_filename, int intent ); +int im_icc_import_embedded( IMAGE *in, IMAGE *out, int intent ); +int im_icc_export( IMAGE *in, IMAGE *out, + const char *output_profile_filename, int intent ); +int im_icc_export_depth( IMAGE *in, IMAGE *out, int depth, + const char *output_profile_filename, int intent ); +int im_icc_ac2rc( IMAGE *in, IMAGE *out, const char *profile_filename ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_COLOUR_H*/ diff --git a/include/vips/debug.h b/include/vips/debug.h new file mode 100644 index 00000000..71a9732a --- /dev/null +++ b/include/vips/debug.h @@ -0,0 +1,50 @@ +/* Support for debug.c in iofuncs. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_DEBUG_H +#define IM_DEBUG_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* All open image descriptors ... see im_init() and im_close(). + */ +extern GSList *im__open_images; + +/* Print one line for each descriptor, complete dump for one descriptor. + */ +void im__print_one( int n ); +void im__print_all( void ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /* IM_DEBUG_H */ diff --git a/include/vips/dispatch.h b/include/vips/dispatch.h new file mode 100644 index 00000000..e9fb4359 --- /dev/null +++ b/include/vips/dispatch.h @@ -0,0 +1,269 @@ +/* VIPS function dispatch. + * + * J. Cupitt, 8/4/93. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_DISPATCH_H +#define IM_DISPATCH_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#include +#include + +/* Type names. You may define your own, but if you use one of these, then + * you should use the built-in VIPS type converters. + */ +#define IM_TYPE_IMAGEVEC "imagevec" /* im_object is ptr to IMAGE[] */ +#define IM_TYPE_DOUBLEVEC "doublevec" /* im_object is ptr to double[] */ +#define IM_TYPE_INTVEC "intvec" /* im_object is ptr to int[] */ +#define IM_TYPE_DOUBLE "double" /* im_object is ptr to double */ +#define IM_TYPE_INT "integer" /* 32-bit integer */ +#define IM_TYPE_COMPLEX "complex" /* Pair of doubles */ +#define IM_TYPE_STRING "string" /* Zero-terminated char array */ +#define IM_TYPE_IMASK "intmask" /* Integer mask type */ +#define IM_TYPE_DMASK "doublemask" /* Double mask type */ +#define IM_TYPE_IMAGE "image" /* IMAGE descriptor */ +#define IM_TYPE_DISPLAY "display" /* Display descriptor */ +#define IM_TYPE_GVALUE "gvalue" /* GValue wrapper */ +typedef char *im_arg_type; /* Type of argument id */ + +/* Internal representation of an argument to an image processing function. + */ +typedef void *im_object; + +/* These bits are ored together to make the flags in a type descriptor. + * + * IM_TYPE_OUTPUT: set to indicate output, otherwise input. + * + * IM_TYPE_ARG: Two ways of making an im_object --- with and without a + * command-line string to help you along. Arguments with a string are thing + * like IMAGE descriptors, which require a filename to initialise. + * Arguments without are things like output numbers, where making the object + * simply involves allocating storage. + */ +typedef enum { + IM_TYPE_NONE = 0, /* No flags */ + IM_TYPE_OUTPUT = 0x1, /* Output/input object */ + IM_TYPE_ARG = 0x2 /* Uses a str arg in construction */ +} im_type_flags; + +/* Initialise and destroy objects. The "str" argument to the init function + * will not be supplied if this is not an ARG type. + */ +typedef int (*im_init_obj_fn)( im_object *obj, char *str ); +typedef int (*im_dest_obj_fn)( im_object obj ); + +/* Describe a VIPS type. + */ +typedef struct { + im_arg_type type; /* Type of argument */ + int size; /* sizeof( im_object repres. ) */ + im_type_flags flags; /* Flags */ + im_init_obj_fn init; /* Operation functions */ + im_dest_obj_fn dest; +} im_type_desc; + +/* Success on an argument. This is called if the image processing function + * succeeds and should be used to (for example) print output. + */ +typedef int (*im_print_obj_fn)( im_object obj ); + +/* Describe a VIPS command argument. + */ +typedef struct { + char *name; /* eg. "width" */ + im_type_desc *desc; /* Type description */ + im_print_obj_fn print; /* Print some output objects */ +} im_arg_desc; + +/* Type of VIPS dispatch funtion. + */ +typedef int (*im_dispatch_fn)( im_object *argv ); + +/* Maximum size of arg table. + */ +#define IM_MAX_ARGS (1000) + +/* Flags for functions. These are for information only, and more may be + * added. + */ +typedef enum { + IM_FN_NONE = 0, /* No flags set */ + IM_FN_PIO = 0x1, /* Is a partial function */ + IM_FN_TRANSFORM = 0x2, /* Performs coordinate transformations */ + IM_FN_PTOP = 0x4, /* Point-to-point ... can be done with a LUT */ + IM_FN_NOCACHE = 0x8 /* Result should not be cached */ +} im_fn_flags; + +/* Describe a VIPS function. + */ +typedef struct { + char *name; /* eg "im_invert" */ + char *desc; /* Description - eg "photographic negative" */ + im_fn_flags flags; /* Flags for this function */ + im_dispatch_fn disp; /* Dispatch */ + int argc; /* Number of args */ + im_arg_desc *argv; /* Arg table */ +} im_function; + +/* A set of VIPS functions forming a package. + */ +typedef struct { + char *name; /* Package name (eg "arithmetic") */ + int nfuncs; /* Number of functions in package */ + im_function **table; /* Array of function descriptors */ +} im_package; + +/* Externs for dispatch. + */ + +/* Struct for mask IO to a file. + */ +typedef struct { + char *name; /* Command-line name in */ + void *mask; /* Mask --- DOUBLE or INT */ +} im_mask_object; + +/* Struct for doublevec IO + */ +typedef struct { + int n; /* Vector length */ + double *vec; /* Vector */ +} im_doublevec_object; + +/* Struct for intvec IO + */ +typedef struct { + int n; /* Vector length */ + int *vec; /* Vector */ +} im_intvec_object; + +/* Struct for imagevec IO + */ +typedef struct { + int n; /* Vector length */ + IMAGE **vec; /* Vector */ +} im_imagevec_object; + +/* Built-in VIPS types. + */ +extern im_type_desc im__input_imagevec; +extern im_type_desc im__input_image; +extern im_type_desc im__output_image; +extern im_type_desc im__rw_image; +extern im_type_desc im__input_doublevec; +extern im_type_desc im__input_intvec; +extern im_type_desc im__input_double; +extern im_type_desc im__output_double; +extern im_type_desc im__input_int; +extern im_type_desc im__output_int; +extern im_type_desc im__input_string; +extern im_type_desc im__output_string; +extern im_type_desc im__output_complex; +extern im_type_desc im__input_dmask; +extern im_type_desc im__output_dmask; +extern im_type_desc im__output_dmask_screen; +extern im_type_desc im__input_display; +extern im_type_desc im__output_display; +extern im_type_desc im__input_imask; +extern im_type_desc im__output_imask; +extern im_type_desc im__input_gvalue; + +/* VIPS print functions. + */ +int im__iprint( im_object obj ); /* int */ +int im__dprint( im_object obj ); /* double */ +int im__cprint( im_object obj ); /* complex */ +int im__sprint( im_object obj ); /* string */ +int im__displayprint( im_object obj ); /* im_col_display */ +int im__dmsprint( im_object obj ); /* DOUBLEMASK as stats */ +int im__gprint( im_object obj ); /* GValue */ + +/* Macros for convenient creation. + */ +#define IM_INPUT_IMAGEVEC( S ) { S, &im__input_imagevec, NULL } +#define IM_INPUT_IMAGE( S ) { S, &im__input_image, NULL } +#define IM_OUTPUT_IMAGE( S ) { S, &im__output_image, NULL } +#define IM_RW_IMAGE( S ) { S, &im__rw_image, NULL } +#define IM_INPUT_DOUBLE( S ) { S, &im__input_double, NULL } +#define IM_INPUT_DOUBLEVEC( S ) { S, &im__input_doublevec, NULL } +#define IM_INPUT_INTVEC( S ) { S, &im__input_intvec, NULL } +#define IM_OUTPUT_DOUBLE( S ) { S, &im__output_double, im__dprint } +#define IM_INPUT_INT( S ) { S, &im__input_int, NULL } +#define IM_OUTPUT_INT( S ) { S, &im__output_int, im__iprint } +#define IM_INPUT_STRING( S ) { S, &im__input_string, NULL } +#define IM_OUTPUT_STRING( S ) { S, &im__output_string, im__sprint } +#define IM_INPUT_DISPLAY( S ) { S, &im__input_display, NULL } +#define IM_OUTPUT_DISPLAY( S ) { S, &im__output_display, im__displayprint } +#define IM_OUTPUT_COMPLEX( S ) { S, &im__output_complex, im__cprint } +#define IM_INPUT_DMASK( S ) { S, &im__input_dmask, NULL } +#define IM_OUTPUT_DMASK( S ) { S, &im__output_dmask, NULL } +#define IM_OUTPUT_DMASK_STATS( S ) { S, &im__output_dmask_screen, im__dmsprint } +#define IM_INPUT_IMASK( S ) { S, &im__input_imask, NULL } +#define IM_OUTPUT_IMASK( S ) { S, &im__output_imask, NULL } +#define IM_INPUT_GVALUE( S ) { S, &im__input_gvalue, NULL } +#define IM_OUTPUT_GVALUE( S ) { S, &im__output_gvalue, im__gprint } + +/* Add a plug-in package. + */ +im_package *im_load_plugin( const char *name ); +int im_load_plugins( const char *fmt, ... ) + __attribute__((format(printf, 1, 2))); + +/* Close all plug-ins. + */ +int im_close_plugins( void ); + +/* Loop over all loaded packages. + */ +void *im_map_packages( VSListMap2Fn fn, void *a ); + +/* Convenience functions for finding packages, functions, etc. + */ +im_function *im_find_function( const char *name ); +im_package *im_find_package( const char *name ); +im_package *im_package_of_function( const char *name ); + +/* Allocate space for, and free im_object argument lists. + */ +int im_free_vargv( im_function *fn, im_object *vargv ); +int im_allocate_vargv( im_function *fn, im_object *vargv ); + +/* Run a VIPS command by name. + */ +int im_run_command( char *name, int argc, char **argv ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_DISPATCH_H*/ diff --git a/include/vips/fmask.h b/include/vips/fmask.h new file mode 100644 index 00000000..49e1ebd5 --- /dev/null +++ b/include/vips/fmask.h @@ -0,0 +1,86 @@ +/* @(#) Typical filter function + * va_list is filter parameters + * lowpass highpass filters + * flag = 0 -> idealhpf, parameters: frequency cutoff + * flag = 1 -> ideallpf, parameters: frequency cutoff + * flag = 2 -> buthpf, parameters: order, frequency cutoff, amplitude cutoff + * flag = 3 -> butlpf, parameters: order, frequency cutoff, amplitude cutoff + * flag = 4 -> gaussianlpf, parameters: frequency cutoff, amplitude cutoff + * flag = 5 -> gaussianhpf, parameters: frequency cutoff, amplitude cutoff + * ring pass ring reject filters + * flag = 6 -> idealrpf, parameters: frequency cutoff, width + * flag = 7 -> idealrrf, parameters: frequency cutoff, width + * flag = 8 -> butrpf, parameters: order, freq cutoff, width, ampl cutoff + * flag = 9 -> butrrf, parameters: order, freq cutoff, width, ampl cutoff + * flag = 10 -> gaussianrpf, parameters: frequency cutoff, width, ampl cutoff + * flag = 11 -> gaussianrrf, parameters: frequency cutoff, width, ampl cutoff + * bandpass bandreject filters + * flag = 12 -> idealbpf, parameters: center frequency, 2*radius + * flag = 13 -> idealbrf, parameters: centre frequency, 2*radius + * flag = 14 -> butbpf, parameters: order, frequency, 2*radius, ampl cutoff + * flag = 15 -> butbrf, parameters: order, frequency, 2*radius, ampl cutoff + * flag = 16 -> gaussianbpf, parameters: frequency cutoff, width, ampl cutoff + * flag = 17 -> gaussianbrf, parameters: frequency cutoff, width, ampl cutoff + * fractal filters (for filtering gaussian noises only) + * flag = 18 -> fractal, parameters: fractal dimension + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_FMASK_H +#define IM_FMASK_H + +typedef enum mask_type { + MASK_IDEAL_HIGHPASS = 0, + MASK_IDEAL_LOWPASS = 1, + MASK_BUTTERWORTH_HIGHPASS = 2, + MASK_BUTTERWORTH_LOWPASS = 3, + MASK_GAUSS_HIGHPASS = 4, + MASK_GAUSS_LOWPASS = 5, + + MASK_IDEAL_RINGPASS = 6, + MASK_IDEAL_RINGREJECT = 7, + MASK_BUTTERWORTH_RINGPASS = 8, + MASK_BUTTERWORTH_RINGREJECT = 9, + MASK_GAUSS_RINGPASS = 10, + MASK_GAUSS_RINGREJECT = 11, + + MASK_IDEAL_BANDPASS = 12, + MASK_IDEAL_BANDREJECT = 13, + MASK_BUTTERWORTH_BANDPASS = 14, + MASK_BUTTERWORTH_BANDREJECT = 15, + MASK_GAUSS_BANDPASS = 16, + MASK_GAUSS_BANDREJECT = 17, + + MASK_FRACTAL_FLT = 18 +} MaskType; + +int im_flt_image_freq( IMAGE *in, IMAGE *out, MaskType flag, ... ); +int im_create_fmask( IMAGE *out, int xsize, int ysize, MaskType flag, ... ); +int im__fmaskcir( IMAGE *out, MaskType flag, va_list ap ); + +#endif /*IM_FMASK_H*/ diff --git a/include/vips/history.h b/include/vips/history.h new file mode 100644 index 00000000..5af9d69b --- /dev/null +++ b/include/vips/history.h @@ -0,0 +1,59 @@ +/* @(#) Useful macros for appending one line in the History field of the + * @(#) output image descriptor when a function is called + * @(#) The main program should use im_updatehist() + * @(#) The added line corresponds to the command relevant to the function + * @(#) for instance + * @(#) for the function: im_add(in1, in2, out) the following lines of code can + * @(#) be used to add a line of history in the Hist member + * @(#) of the out image descriptor + * @(#) .... + * @(#) IMAGE *in1, *in2, *out; + * @(#) .... + * @(#) if ( im_add(in1, in2, out) == -1 ) return(-1); + * @(#) if ( IM_ADD(in1, in2, out) == -1 ) return(-1); + * @(#) .... + * @(#) + * @(#) The first function will add the two images in1 and in2, + * @(#) whereas the second call will append + * @(#) at the history descriptor of out the line: + * @(#) add infile outfile # date + * @(#) where infile is in.filename and outfile is out.filename + * @(#) The history line has been prepared in such a way that the first + * @(#) argument is the UNIX command which corresponds to the function + * @(#) As a general rule, all functions in im_funcs directory which + * @(#) have a correponding command in src directory are listed here + * @(#) + * @(#) Since the macros presented in this file correspond to the function + * @(#) im_histlin() the returned value is 0 on success and -1 on error. + * @(#) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Made obsolete by the function database stuff ... just here in case anyone + * still includes it. + */ diff --git a/include/vips/internal.h b/include/vips/internal.h new file mode 100644 index 00000000..185a6686 --- /dev/null +++ b/include/vips/internal.h @@ -0,0 +1,121 @@ +/* Prototypes for internal VIPS functions. + * + * 11/9/06 + * - cut from proto.h + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_INTERNAL_H +#define IM_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +typedef int (*im__fftproc_fn)( IMAGE *, IMAGE *, IMAGE * ); + +/* iofuncs + */ +void im__read_4byte( int msb_first, unsigned char *to, unsigned char **from ); +void im__read_2byte( int msb_first, unsigned char *to, unsigned char **from ); +void im__write_4byte( unsigned char **to, unsigned char *from ); +void im__write_2byte( unsigned char **to, unsigned char *from ); + +int im__read_header_bytes( IMAGE *im, unsigned char *from ); +int im__write_header_bytes( IMAGE *im, unsigned char *to ); +int im__has_extension_block( IMAGE *im ); +void *im__read_extension_block( IMAGE *im, int *size ); +int im__readhist( IMAGE *image ); +int im__write_extension_block( IMAGE *im, void *buf, int size ); +int im__writehist( IMAGE *image ); + +extern int im__read_test; +extern int im__mmap_limit; +extern GMutex *im__global_lock; + +IMAGE *im__convert_saveable( IMAGE *in, gboolean allow_alpha ); + +void im__link_make( IMAGE *parent, IMAGE *child ); +void im__link_break_all( IMAGE *im ); +void *im__link_map( IMAGE *im, VSListMap2Fn fn, void *a, void *b ); + +GValue *im__gvalue_ref_string_new( const char *text ); +void im__gslist_gvalue_free( GSList *list ); +GSList *im__gslist_gvalue_copy( const GSList *list ); +GSList *im__gslist_gvalue_merge( GSList *a, const GSList *b ); +char *im__gslist_gvalue_get( const GSList *list ); + +void im__buffer_init( void ); + +int im__cast_and_call(); +int im__read_header( IMAGE *image ); +int im__test_kill( IMAGE *im ); +void *im__mmap( int fd, int writeable, size_t length, gint64 offset ); +int im__munmap( void *start, size_t length ); +int im__write( int, const void *, size_t ); +int im__open_image_file( const char *filename ); +gint64 im__image_pixel_length( IMAGE *im ); +void im__change_suffix( const char *name, char *out, int mx, + const char *new_suff, const char **olds, int nolds ); +void im__print_all( void ); +void im__print_one( int ); +int im__trigger_callbacks( GSList *cblist ); +int im__close( IMAGE * ); +int im__handle_eval( IMAGE *im, int w, int h ); +int im__create_int_luts( int *, int, int **, int **, int * ); +int im__create_double_luts( double *, int, double **, double **, int * ); +int im__fft_sp( float *rvec, float *ivec, int logrows, int logcols ); +int im__fftproc( IMAGE *dummy, IMAGE *in, IMAGE *out, im__fftproc_fn fn ); +int im__mean_std_double_buffer( double *buffer, int size, + double *pmean, double *pstd ); +int im__mean_std_int_buffer( int *buffer, int size, + double *pmean, double *pstd ); +int im__find_lroverlap( IMAGE *ref_in, IMAGE *sec_in, IMAGE *out, + int bandno_in, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int *dx0, int *dy0, + double *scale1, double *angle1, double *dx1, double *dy1 ); +int im__find_tboverlap( IMAGE *ref_in, IMAGE *sec_in, IMAGE *out, + int bandno_in, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int *dx0, int *dy0, + double *scale1, double *angle1, double *dx1, double *dy1 ); +int im__find_best_contrast( IMAGE *image, + int xpos, int ypos, int xsize, int ysize, + int xarray[], int yarray[], int cont[], + int nbest, int hcorsize ); +int im__balance( IMAGE *ref, IMAGE *sec, IMAGE *out, + IMAGE **ref_out, IMAGE **sec_out, int dx, int dy, int balancetype ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_INTERNAL_H*/ diff --git a/include/vips/intl.h b/include/vips/intl.h new file mode 100644 index 00000000..3974b9dc --- /dev/null +++ b/include/vips/intl.h @@ -0,0 +1,44 @@ +/* i18n stuff for vips. + */ + +#ifndef IM_VIPS_INTL_H +#define IM_VIPS_INTL_H + +const char *im__gettext( const char *msgid ); +const char *im__ngettext( const char *msgid, + const char *plural, unsigned long int n ); + +#ifdef ENABLE_NLS + +#include +#define _(String) im__gettext(String) +/* ngettext may be defined as a macro if we're optimised. + */ +#ifdef ngettext +#undef ngettext +#endif /*ngettext*/ +#define ngettext(String,Plural,number) im__ngettext(String,Plural,number) +#ifdef gettext_noop +#define N_(String) gettext_noop(String) +#else +#define N_(String) (String) +#endif + +#else /*!ENABLE_NLS*/ + +#define _(String) (String) +#define N_(String) (String) +#define textdomain(String) (String) +#define gettext(String) (String) +#define dgettext(Domain,String) (String) +#define dcgettext(Domain,String,Type) (String) +#define bindtextdomain(Domain,Directory) (Domain) +#define bind_textdomain_codeset(Domain,Codeset) (Codeset) +#define ngettext(S, P, N) ((N) == 1 ? (S) : (P)) +#define dngettext(D, S, P, N) ngettext(S, P, N) + +#endif /* ENABLE_NLS */ + +#endif /* IM_VIPS_INTL_H */ + + diff --git a/include/vips/meta.h b/include/vips/meta.h new file mode 100644 index 00000000..a300609a --- /dev/null +++ b/include/vips/meta.h @@ -0,0 +1,107 @@ +/* Metadata API. + */ + +/* + + Copyright (C) 1991-2005 The National Gallery + + 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 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_META_H +#define IM_META_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* Reserved header names. + */ +#define IM_META_EXIF_NAME "exif-data" +#define IM_META_ICC_NAME "icc-profile-data" +#define IM_META_XML "xml-header" + +/* Types we add for meta fields. + */ +#define IM_TYPE_SAVE_STRING (im_save_string_get_type()) +GType im_save_string_get_type( void ); +const char *im_save_string_get( const GValue *value ); +void im_save_string_set( GValue *value, const char *str ); +void im_save_string_setf( GValue *value, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); + +#define IM_TYPE_AREA (im_area_get_type()) +GType im_area_get_type( void ); + +#define IM_TYPE_REF_STRING (im_ref_string_get_type()) +GType im_ref_string_get_type( void ); +int im_ref_string_set( GValue *value, const char *str ); +const char *im_ref_string_get( const GValue *value ); +size_t im_ref_string_get_length( const GValue *value ); + +#define IM_TYPE_BLOB (im_blob_get_type()) +GType im_blob_get_type( void ); +void *im_blob_get( const GValue *value, size_t *data_length ); +int im_blob_set( GValue *value, im_callback_fn free_fn, + void *data, size_t length ); + +/* What we store in the Meta hash table. We can't just use GHashTable's + * key/value pairs, since we need to iterate over meta in Meta_traverse order. + * + * We don't refcount at this level ... large meta values are refcounted by + * their GValue implementation, see eg. MetaArea below. + */ +typedef struct _Meta { + IMAGE *im; + + char *field; /* strdup() of field name */ + GValue value; /* copy of value */ +} Meta; + +int im_meta_set( IMAGE *, const char *field, GValue * ); +int im_meta_get( IMAGE *, const char *field, GValue * ); +GType im_meta_get_type( IMAGE *im, const char *field ); + +int im_meta_set_int( IMAGE *, const char *field, int i ); +int im_meta_get_int( IMAGE *, const char *field, int *i ); +int im_meta_set_double( IMAGE *, const char *field, double d ); +int im_meta_get_double( IMAGE *, const char *field, double *d ); +int im_meta_set_area( IMAGE *, const char *field, im_callback_fn, void * ); +int im_meta_get_area( IMAGE *, const char *field, void **data ); +int im_meta_set_string( IMAGE *, const char *field, const char *str ); +int im_meta_get_string( IMAGE *, const char *field, char **str ); +int im_meta_set_blob( IMAGE *im, const char *field, + im_callback_fn free_fn, void *blob, size_t blob_length ); +int im_meta_get_blob( IMAGE *im, const char *field, + void **blob, size_t *blob_length ); + +/* Internal. + */ +void im__meta_init_types( void ); +void im__meta_destroy( IMAGE *im ); +int im__meta_cp( IMAGE *, const IMAGE * ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*!IM_META_H*/ diff --git a/include/vips/mosaic.h b/include/vips/mosaic.h new file mode 100644 index 00000000..dc2d26a8 --- /dev/null +++ b/include/vips/mosaic.h @@ -0,0 +1,86 @@ +/* @(#) Local definitions used by the mosaicing program + * @(#) If MAXPOINTS change please ensure that it is still a multiple of + * @(#) AREAS or else AREAS must change as well. Initial setup is for + * @(#) MAXPOINTS = 60, AREAS = 3. + * @(#) + * Copyright: 1990, 1991 N. Dessipris + * Author: Nicos Dessipris + * Written on: 07/11/1989 + * Modified on : 29/11/1989 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_MOSAIC_H +#define IM_MOSAIC_H + +#define MAXPOINTS 60 /* MAXPOINTS % AREAS (in im_calcon) must be zero */ + +typedef struct { + char *reference; /* filename of reference */ + char *secondary; /* filename of secondary */ + int deltax; /* initial estimate of displacement */ + int deltay; /* initial estimate of displacement */ + int nopoints; /* must be multiple of AREAS and <= MAXPOINTS */ + int halfcorsize; /* recommended 5 */ + int halfareasize; /* recommended 8 */ + + /* x, y_reference and contrast found by im_calcon() + */ + int x_reference[MAXPOINTS], y_reference[MAXPOINTS]; + int contrast[MAXPOINTS]; + + /* x, y_secondary and correlation set by im_chkpair() + */ + int x_secondary[MAXPOINTS], y_secondary[MAXPOINTS]; + + /* returns the corrected best correlation + * as detected in 2*halfareasize+1 + * centered at point (x2, y2) and using + * correlation area 2*halfareasize+1 + */ + double correlation[MAXPOINTS]; + + /* Coefficients calculated by im_clinear() + */ + double l_scale, l_angle, l_deltax, l_deltay; + + /* used by im_clinear() + */ + double dx[MAXPOINTS], dy[MAXPOINTS]; + double deviation[MAXPOINTS]; +} TIE_POINTS; + +int im_clinear( TIE_POINTS *points ); +int im__chkpair( IMAGE *, IMAGE *, TIE_POINTS *point ); +int im__initialize( TIE_POINTS *points ); +int im__improve( TIE_POINTS *inpoints, TIE_POINTS *outpoints ); +int im__avgdxdy( TIE_POINTS *points, int *dx, int *dy ); +int im__lrcalcon( IMAGE *ref, TIE_POINTS *points ); +int im__tbcalcon( IMAGE *ref, TIE_POINTS *points ); + +#endif /*IM_MOSAIC_H*/ diff --git a/include/vips/proto.h b/include/vips/proto.h new file mode 100644 index 00000000..2f1060b0 --- /dev/null +++ b/include/vips/proto.h @@ -0,0 +1,749 @@ +/* @(#) Header file for Birkbeck/VIPS Image Processing Library + * Authors: N. Dessipris, K. Martinez, Birkbeck College, London. + * and J. Cupitt The National Gallery, London. + * + * Sept 94 + * + * 15/7/96 JC + * - now does C++ extern stuff + * - many more protos + * 15/4/97 JC + * - protos split out here, more of them + * - still not complete tho' ... + * 8/4/99 JC + * - lots of consts added to please C++ + * - and more protos added + * 11/9/06 + * - internal protos cut out to help SWIG + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_PROTO_H +#define IM_PROTO_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* Need these for some protos. + */ +#include +#include +#include +#include + +/* If we're being parsed by SWIG, remove gcc attributes. + */ +#ifdef SWIG +# ifndef __attribute__ +# define __attribute__(x) /*NOTHING*/ +# endif +#endif /*SWIG*/ + +typedef int (*im_callback_fn)( void *, void * ); +typedef void *(*im_construct_fn)( void *, void *, void * ); +typedef void *(*im_header_map_fn)( IMAGE *, const char *, GValue *, void * ); + +/* iofuncs + */ +int im_init_world( const char *argv0 ); +GOptionGroup *im_get_option_group( void ); + +const char *im_error_buffer( void ); +int im_debugim( IMAGE * ); +int im_printlines( IMAGE * ); + +int im_header_int( IMAGE *im, const char *field, int *out ); +int im_header_double( IMAGE *im, const char *field, double *out ); +int im_header_string( IMAGE *im, const char *field, char **out ); +GType im_header_get_type( IMAGE *im, const char *field ); +int im_header_get( IMAGE *im, const char *field, GValue *value_copy ); +void *im_header_map( IMAGE *im, im_header_map_fn fn, void *a ); + +const char *im_version_string( void ); +int im_version( int flag ); +const char *im_guess_prefix( const char *, const char * ); +IMAGE *im_init( const char * ); +IMAGE *im_openout( const char * ); +int im_openin( IMAGE *image ); +int im_openinrw( IMAGE *image ); +IMAGE *im_setbuf( const char * ); +IMAGE *im_partial( const char * ); +IMAGE *im_binfile( const char *, int, int, int, int ); +IMAGE *im_image( void *, int, int, int, int ); + +int im_mapfile( IMAGE * ); +int im_mapfilerw( IMAGE * ); +int im_remapfilerw( IMAGE *image ); + +IMAGE *im_open( const char *, const char * ); +IMAGE *im_open_header( const char * ); +int im_image_sanity( IMAGE * ); + +void *im_malloc( IMAGE *im, size_t sz ); +int im_free( void * ); + +int im_close( IMAGE * ); +int im_rwcheck( IMAGE * ); +int im_iocheck( IMAGE *, IMAGE * ); +int im_incheck( IMAGE * ); +int im_outcheck( IMAGE * ); +int im_piocheck( IMAGE *, IMAGE * ); +int im_pincheck( IMAGE * ); +int im_poutcheck( IMAGE * ); +int im_cp_desc( IMAGE *, IMAGE * ); +int im_cp_descv( IMAGE *out, IMAGE *in1, ... ) + __attribute__((sentinel)); +int im_cp_desc_array( IMAGE *out, IMAGE *in[] ); +int im_setupout( IMAGE * ); +int im_writeline( int, IMAGE *, PEL * ); + +int im_isuint( IMAGE * ); +int im_isint( IMAGE * ); +int im_isfloat( IMAGE * ); +int im_isscalar( IMAGE * ); +int im_iscomplex( IMAGE * ); +int im_isfile( IMAGE * ); +int im_ispartial( IMAGE * ); +int im_isMSBfirst( IMAGE * ); +int im_amiMSBfirst( void ); + +int im_ispoweroftwo( int ); + +int im_existsf( const char *name, ... ) + __attribute__((format(printf, 1, 2))); +int im_istiff( const char * ); +int im_istifftiled( const char * ); +int im_istiffpyramid( const char * ); +int im_isjpeg( const char * ); +int im_isvips( const char * ); +int im_isexr( const char * ); +int im_isppm( const char * ); +int im_ispng( const char * ); +int im_ismagick( const char * ); +int im_isanalyze( const char *filename ); + +int im_add_close_callback( IMAGE *, im_callback_fn, void *, void * ); +int im_add_eval_callback( IMAGE *, im_callback_fn, void *, void * ); +int im_add_evalend_callback( IMAGE *, im_callback_fn, void *, void * ); + +void error_exit( const char *, ... ) + __attribute__((noreturn, format(printf, 1, 2))); +void im_error_clear( void ); +void im_verror( const char *domain, const char *fmt, va_list ap ); +void im_error( const char *domain, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); +void im_error_system( int err, const char *domain, const char *fmt, ... ) + __attribute__((format(printf, 3, 4))); +void im_warn( const char *domain, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); +void im_diag( const char *domain, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); + +int im_bits_of_fmt( int ); +const char *im_Type2char( int ); +const char *im_BandFmt2char( int ); +const char *im_Coding2char( int ); +const char *im_Compression2char( int ); +const char *im_dhint2char( im_demand_type ); +const char *im_dtype2char( im_desc_type ); +int im_char2Type( const char * ); +int im_char2BandFmt( const char * ); +int im_char2Coding( const char * ); +int im_char2Compression( const char * ); + +int im_unmapfile( IMAGE * ); +void im_printdesc( IMAGE * ); +void im_initdesc( IMAGE *, + int, int, int, int, int, int, int, float, float, + int, int ); +int im_histlin( IMAGE *image, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); +int im_updatehist( IMAGE *out, const char *name, int argc, char *argv[] ); +const char *im_history_get( IMAGE *im ); + +int im_render( IMAGE *in, IMAGE *out, IMAGE *mask, + int width, int height, int max, + void (*notify)( IMAGE *, Rect *, void * ), void *client ); +int im_render_fade( IMAGE *in, IMAGE *out, IMAGE *mask, + int width, int height, int max, + int fps, int steps, + int priority, + void (*notify)( IMAGE *, Rect *, void * ), void *client ); +int im_cache( IMAGE *in, IMAGE *out, int width, int height, int max ); + +/* morphology + */ +int im_dilate( IMAGE *in, IMAGE *out, INTMASK *m ); +int im_dilate_raw( IMAGE *in, IMAGE *out, INTMASK *m ); +int im_erode( IMAGE *in, IMAGE *out, INTMASK *m ); +int im_erode_raw( IMAGE *in, IMAGE *out, INTMASK *m ); +int im_cntlines( IMAGE *im, double *nolines, int flag ); +int im_profile( IMAGE *in, IMAGE *out, int dir ); + +/* convolution + */ +void im_copy_dmask_matrix( DOUBLEMASK *mask, double **matrix ); +void im_copy_matrix_dmask( double **matrix, DOUBLEMASK *mask ); +INTMASK *im_create_imask( const char *, int, int ); +INTMASK *im_create_imaskv( const char *, int, int, ... ); +DOUBLEMASK *im_create_dmask( const char *, int, int ); +DOUBLEMASK *im_create_dmaskv( const char *, int, int, ... ); +INTMASK *im_dup_imask( INTMASK *, const char * ); +DOUBLEMASK *im_dup_dmask( DOUBLEMASK *, const char * ); +int im_free_imask( INTMASK * ); +int im_free_dmask( DOUBLEMASK * ); +INTMASK *im_read_imask( const char * ); +DOUBLEMASK *im_read_dmask( const char * ); +void im_print_imask( INTMASK * ); +void im_print_dmask( DOUBLEMASK * ); +int im_write_imask( INTMASK * ); +int im_write_dmask( DOUBLEMASK * ); +int im_write_imask_name( INTMASK *, const char * ); +int im_write_dmask_name( DOUBLEMASK *, const char * ); +INTMASK *im_scale_dmask( DOUBLEMASK *, const char * ); +void im_norm_dmask( DOUBLEMASK *mask ); +int *im_offsets45( int ); +int *im_offsets90( int ); +INTMASK *im_rotate_imask90( INTMASK *, const char * ); +INTMASK *im_rotate_imask45( INTMASK *, const char * ); +DOUBLEMASK *im_rotate_dmask90( DOUBLEMASK *, const char * ); +DOUBLEMASK *im_rotate_dmask45( DOUBLEMASK *, const char * ); +INTMASK *im_log_imask( const char *, double, double ); +DOUBLEMASK *im_log_dmask( const char *, double, double ); +INTMASK *im_gauss_imask( const char *, double, double ); +DOUBLEMASK *im_gauss_dmask( const char *, double, double ); + +int im_rank( IMAGE *, IMAGE *, int, int, int ); +int im_sharpen( IMAGE *, IMAGE *, int, double, double, double, double, double ); +int im_addgnoise( IMAGE *, IMAGE *, double ); +int im_gaussnoise( IMAGE *, int, int, double, double ); + +int im_zerox( IMAGE *, IMAGE *, int ); + +int im_maxvalue( IMAGE **in, IMAGE *out, int n ); +int im_rank_image( IMAGE **in, IMAGE *out, int n, int index ); +int im_compass( IMAGE *, IMAGE *, INTMASK * ); +int im_gradient( IMAGE *, IMAGE *, INTMASK * ); +int im_lindetect( IMAGE *, IMAGE *, INTMASK * ); +int im_conv( IMAGE *, IMAGE *, INTMASK * ); +int im_conv_raw( IMAGE *, IMAGE *, INTMASK * ); +int im_convf( IMAGE *, IMAGE *, DOUBLEMASK * ); +int im_convf_raw( IMAGE *, IMAGE *, DOUBLEMASK * ); +int im_convsep( IMAGE *, IMAGE *, INTMASK * ); +int im_convsep_raw( IMAGE *, IMAGE *, INTMASK * ); +int im_convsepf( IMAGE *, IMAGE *, DOUBLEMASK * ); +int im_convsepf_raw( IMAGE *, IMAGE *, DOUBLEMASK * ); +int im_convsub( IMAGE *, IMAGE *, INTMASK *, int, int ); + +int im_grad_x( IMAGE *in, IMAGE *out ); +int im_grad_y( IMAGE *in, IMAGE *out ); + +int im_fastcor( IMAGE *, IMAGE *, IMAGE * ); +int im_fastcor_raw( IMAGE *, IMAGE *, IMAGE * ); +int im_spcor( IMAGE *, IMAGE *, IMAGE * ); +int im_spcor_raw( IMAGE *, IMAGE *, IMAGE * ); +int im_spcor2( IMAGE *, IMAGE *, IMAGE * ); +int im_spcor2_raw( IMAGE *, IMAGE *, IMAGE * ); +int im_gradcor( IMAGE *, IMAGE *, IMAGE * ); +int im_gradcor_raw( IMAGE *, IMAGE *, IMAGE * ); +int im_contrast_surface( IMAGE *, IMAGE *, int, int ); +int im_contrast_surface_raw( IMAGE *, IMAGE *, int, int ); + +int im_resize_linear( IMAGE *, IMAGE *, int, int ); +int im_mpercent( IMAGE *, double, int * ); +int im_shrink( IMAGE *, IMAGE *, double, double ); +int im_embed( IMAGE *, IMAGE *, int, int, int, int, int ); + +int im_stretch3( IMAGE *in, IMAGE *out, double dx, double dy ); +int im_rank_raw( IMAGE *in, IMAGE *out, int xsize, int ysize, int n ); + +/* freq_filt + */ +int im_fractsurf( IMAGE *out, int size, double frd ); +int im_freqflt( IMAGE *, IMAGE *, IMAGE * ); +int im_disp_ps( IMAGE *, IMAGE * ); +int im_rotquad( IMAGE *, IMAGE * ); +int im_fwfft( IMAGE *, IMAGE * ); +int im_invfft( IMAGE *, IMAGE * ); +int im_invfftr( IMAGE *, IMAGE * ); + +/* arithmetic + */ +DOUBLEMASK *im_measure( IMAGE *, IMAGE_BOX *, + int, int, int *, int, const char * ); +DOUBLEMASK *im_stats( IMAGE * ); +int im_abs( IMAGE *in, IMAGE *out ); +int im_max( IMAGE *in, double *out ); +int im_min( IMAGE *in, double *out ); +int im_avg( IMAGE *in, double *out ); +int im_deviate( IMAGE *in, double *out ); +int im_maxpos( IMAGE *in, int *xpos, int *ypos, double *out ); +int im_minpos( IMAGE *in, int *xpos, int *ypos, double *out ); +int im_maxpos_avg( IMAGE *im, double *xpos, double *ypos, double *out ); +int im_maxpos_vec( IMAGE *im, int *xpos, int *ypos, double *maxima, int n ); +int im_minpos_vec( IMAGE *im, int *xpos, int *ypos, double *minima, int n ); +int im_add( IMAGE *, IMAGE *, IMAGE * ); +int im_subtract( IMAGE *, IMAGE *, IMAGE * ); +int im_invert( IMAGE *, IMAGE * ); +int im_linreg( IMAGE **ins, IMAGE *out, double *xs ); +int im_lintra( double, IMAGE *, double, IMAGE * ); +int im_lintra_vec( int n, double *a, IMAGE *in, double *b, IMAGE *out ); +int im_multiply( IMAGE *, IMAGE *, IMAGE * ); +int im_divide( IMAGE *, IMAGE *, IMAGE * ); +int im_point_bilinear( IMAGE *im, double x, double y, int band, double *val ); +int im_powtra( IMAGE *, IMAGE *, double ); +int im_powtra_vec( IMAGE *in, IMAGE *out, int n, double *e ); +int im_exptra( IMAGE *, IMAGE * ); +int im_exp10tra( IMAGE *, IMAGE * ); +int im_expntra( IMAGE *, IMAGE *, double ); +int im_expntra_vec( IMAGE *in, IMAGE *out, int n, double *e ); +int im_logtra( IMAGE *, IMAGE * ); +int im_log10tra( IMAGE *, IMAGE * ); +int im_remainder( IMAGE *, IMAGE *, IMAGE * ); +int im_remainderconst( IMAGE *, IMAGE *, double ); +int im_remainderconst_vec( IMAGE *, IMAGE *, int, double * ); +int im_floor( IMAGE *, IMAGE * ); +int im_rint( IMAGE *, IMAGE * ); +int im_ceil( IMAGE *, IMAGE * ); +int im_sintra( IMAGE *, IMAGE * ); +int im_sign( IMAGE *in, IMAGE *out ); +int im_costra( IMAGE *, IMAGE * ); +int im_tantra( IMAGE *, IMAGE * ); +int im_asintra( IMAGE *, IMAGE * ); +int im_acostra( IMAGE *, IMAGE * ); +int im_atantra( IMAGE *, IMAGE * ); +int im_cmulnorm( IMAGE *, IMAGE *, IMAGE * ); +int im_fav4( IMAGE **, IMAGE * ); +int im_gadd( double, IMAGE *, double, IMAGE *, double, IMAGE *); +int im_litecor( IMAGE *, IMAGE *, IMAGE *, int, double ); +int im_bandmean( IMAGE *in, IMAGE *out ); + +/* boolean + */ +int im_andimage( IMAGE *, IMAGE *, IMAGE * ); +int im_andconst( IMAGE *, IMAGE *, double ); +int im_and_vec( IMAGE *, IMAGE *, int, double * ); +int im_orimage( IMAGE *, IMAGE *, IMAGE * ); +int im_orconst( IMAGE *, IMAGE *, double ); +int im_or_vec( IMAGE *, IMAGE *, int, double * ); +int im_eorimage( IMAGE *, IMAGE *, IMAGE * ); +int im_eorconst( IMAGE *, IMAGE *, double ); +int im_eor_vec( IMAGE *, IMAGE *, int, double * ); +int im_shiftleft( IMAGE *, IMAGE *, int ); +int im_shiftright( IMAGE *, IMAGE *, int ); + +/* histogram + */ +int im_maplut( IMAGE *, IMAGE *, IMAGE * ); +int im_gammacorrect( IMAGE *, IMAGE *, double ); +int im_heq( IMAGE *in, IMAGE *out, int bandno ); +int im_hist( IMAGE *in, IMAGE *out, int bandno ); +int im_histeq( IMAGE *in, IMAGE *out ); +int im_histnorm( IMAGE *in, IMAGE *out ); +int im_histcum( IMAGE *in, IMAGE *out ); +int im_histgr( IMAGE *in, IMAGE *out, int bandno ); +int im_histnD( IMAGE *in, IMAGE *out, int bins ); +int im_histplot( IMAGE *hist, IMAGE *histplot ); +int im_histspec( IMAGE *hin, IMAGE *href, IMAGE *lut ); +int im_hsp( IMAGE *in, IMAGE *ref, IMAGE *out ); +int im_identity( IMAGE *lut, int bands ); +int im_identity_ushort( IMAGE *lut, int bands, int sz ); +int im_lhisteq( IMAGE *in, IMAGE *out, int xwin, int ywin ); +int im_lhisteq_raw( IMAGE *in, IMAGE *out, int xwin, int ywin ); +int im_invertlut( DOUBLEMASK *input, IMAGE *output, int lut_size ); +int im_buildlut( DOUBLEMASK *input, IMAGE *output ); +int im_stdif( IMAGE *in, IMAGE *out, + double a, double m0, double b, double s0, int xwin, int ywin ); +int im_stdif_raw( IMAGE *in, IMAGE *out, + double a, double m0, double b, double s0, int xwin, int ywin ); +int im_tone_build_range( IMAGE *out, + int in_max, int out_max, + double Lb, double Lw, double Ps, double Pm, double Ph, + double S, double M, double H ); +int im_tone_build( IMAGE *out, + double Lb, double Lw, double Ps, double Pm, double Ph, + double S, double M, double H ); +int im_tone_analyse( IMAGE *in, IMAGE *lut, + double Ps, double Pm, double Ph, double S, double M, double H ); +int im_ismonotonic( IMAGE *lut, int *out ); +int im_tone_map( IMAGE *in, IMAGE *out, IMAGE *lut ); +int im_project( IMAGE *in, IMAGE *hout, IMAGE *vout ); + +/* conversion + */ + +/* Copy and swap types. + */ +typedef enum { + IM_ARCH_NATIVE, + IM_ARCH_BYTE_SWAPPED, + IM_ARCH_LSB_FIRST, + IM_ARCH_MSB_FIRST +} im_arch_type; + +DOUBLEMASK *im_vips2mask( IMAGE *, const char * ); +int im_mask2vips( DOUBLEMASK *, IMAGE * ); +int im_copy_set( IMAGE *, IMAGE *, int, float, float, int, int ); +int im_copy_set_meta( IMAGE *in, IMAGE *out, const char *field, GValue *meta ); +int im_copy_morph( IMAGE *, IMAGE *, int, int, int ); +int im_copy( IMAGE *, IMAGE * ); +int im_copy_swap( IMAGE *in, IMAGE *out ); +int im_copy_from( IMAGE *in, IMAGE *out, im_arch_type architecture ); +int im_extract( IMAGE *, IMAGE *, IMAGE_BOX * ); +int im_extract_band( IMAGE *in, IMAGE *out, int band ); +int im_extract_bands( IMAGE *in, IMAGE *out, int band, int nbands ); +int im_extract_area( IMAGE *in, IMAGE *out, int x, int y, int w, int h ); +int im_extract_areabands( IMAGE *in, IMAGE *out, + int left, int top, int width, int height, int band, int nbands ); +int im_subsample( IMAGE *, IMAGE *, int, int ); +int im_zoom( IMAGE *, IMAGE *, int, int ); +int im_bandjoin( IMAGE *, IMAGE *, IMAGE * ); +int im_gbandjoin( IMAGE **, IMAGE *, int ); +int im_black( IMAGE *, int, int, int ); +int im_text( IMAGE *out, const char *text, const char *font, + int width, int alignment, int dpi ); +int im_c2amph( IMAGE *, IMAGE * ); +int im_c2rect( IMAGE *, IMAGE * ); +int im_clip2fmt( IMAGE *in, IMAGE *out, int ofmt ); +int im_clip2dcm( IMAGE *, IMAGE * ); +int im_clip2cm( IMAGE *, IMAGE * ); +int im_clip2us( IMAGE *, IMAGE * ); +int im_clip2ui( IMAGE *, IMAGE * ); +int im_clip2s( IMAGE *, IMAGE * ); +int im_clip2i( IMAGE *, IMAGE * ); +int im_clip2d( IMAGE *, IMAGE * ); +int im_clip2f( IMAGE *, IMAGE * ); +int im_clip2c( IMAGE *, IMAGE * ); +int im_clip( IMAGE *, IMAGE * ); +int im_ri2c( IMAGE *, IMAGE *, IMAGE * ); +int im_c2imag( IMAGE *, IMAGE * ); +int im_c2real( IMAGE *, IMAGE * ); +int im_c2ps( IMAGE *, IMAGE * ); +int im_fliphor( IMAGE *, IMAGE * ); +int im_flipver( IMAGE *, IMAGE * ); +int im_falsecolour( IMAGE *, IMAGE * ); +int im_recomb( IMAGE *, IMAGE *, DOUBLEMASK * ); +int im_insert( IMAGE *, IMAGE *, IMAGE *, int, int ); +int im_insert_noexpand( IMAGE *, IMAGE *, IMAGE *, int, int ); +int im_rot90( IMAGE *, IMAGE * ); +int im_rot180( IMAGE *, IMAGE * ); +int im_rot270( IMAGE *, IMAGE * ); +int im_lrjoin( IMAGE *, IMAGE *, IMAGE * ); +int im_tbjoin( IMAGE *, IMAGE *, IMAGE * ); +int im_scale( IMAGE *, IMAGE * ); +int im_scaleps( IMAGE *, IMAGE * ); +int im_slice( IMAGE *, IMAGE *, double, double ); +int im_system( IMAGE *im, const char *cmd, char **out ); +int im_print( const char *message ); +int im_thresh( IMAGE *, IMAGE *, double ); +int im_jpeg2vips( const char *, IMAGE * ); +int im_jpeg2vips_header( const char *, IMAGE * ); +int im_vips2jpeg( IMAGE *, const char * ); +int im_vips2mimejpeg( IMAGE *, int ); +int im_vips2bufjpeg( IMAGE *, IMAGE *, int, char **, int * ); +int im_vips2tiff( IMAGE *, const char * ); +int im_bernd( const char *, int, int, int, int ); +int im_tiff2vips( const char *, IMAGE * ); +int im_tiff2vips_header( const char *, IMAGE * ); +int im_tile_cache( IMAGE *, IMAGE *, int, int, int ); +int im_magick2vips( const char *, IMAGE * ); +int im_magick2vips_header( const char *, IMAGE * ); +int im_png2vips( const char *, IMAGE * ); +int im_png2vips_header( const char *, IMAGE * ); +int im_exr2vips( const char *, IMAGE * ); +int im_exr2vips_header( const char *, IMAGE * ); +int im_ppm2vips( const char *, IMAGE * ); +int im_ppm2vips_header( const char *, IMAGE * ); +int im_vips2ppm( IMAGE *, const char * ); +int im_analyze2vips( const char *filename, IMAGE *out ); +int im_analyze2vips_header( const char *filename, IMAGE *out ); +int im_vips2csv( IMAGE *in, const char *filename ); +int im_csv2vips( const char *filename, IMAGE *out ); +int im_csv2vips_header( const char *filename, IMAGE *out ); +int im_vips2png( IMAGE *, const char * ); +int im_raw2vips( const char *filename, IMAGE *out, + int width, int height, int bpp, int offset ); +int im_replicate( IMAGE *in, IMAGE *out, int across, int down ); +int im_grid( IMAGE *in, IMAGE *out, int tile_height, int across, int down ); +int im_msb ( IMAGE * in, IMAGE * out ); +int im_msb_band ( IMAGE * in, IMAGE * out, int band ); + +/* colour + */ +int im_Lab2LCh( IMAGE *, IMAGE * ); +int im_LCh2Lab( IMAGE *, IMAGE * ); +int im_LabQ2XYZ( IMAGE *, IMAGE * ); +int im_LCh2UCS( IMAGE *, IMAGE * ); +int im_Lab2LCh( IMAGE *, IMAGE * ); +int im_Lab2LabQ( IMAGE *, IMAGE * ); +int im_Lab2LabS( IMAGE *, IMAGE * ); +int im_Lab2XYZ( IMAGE *, IMAGE * ); +int im_Lab2XYZ_temp( IMAGE *, IMAGE *, double X0, double Y0, double Z0 ); +int im_Lab2UCS( IMAGE *, IMAGE * ); +int im_LabQ2Lab( IMAGE *, IMAGE * ); +int im_LabQ2LabS( IMAGE *, IMAGE * ); +int im_LabS2LabQ( IMAGE *, IMAGE * ); +int im_LabS2Lab( IMAGE *, IMAGE * ); +int im_UCS2XYZ( IMAGE *, IMAGE * ); +int im_UCS2LCh( IMAGE *, IMAGE * ); +int im_UCS2Lab( IMAGE *, IMAGE * ); +int im_XYZ2Lab( IMAGE *, IMAGE * ); +int im_XYZ2Lab_temp( IMAGE *, IMAGE *, double X0, double Y0, double Z0 ); +int im_XYZ2UCS( IMAGE *, IMAGE * ); +int im_sRGB2XYZ( IMAGE *, IMAGE * ); +int im_XYZ2sRGB( IMAGE *, IMAGE * ); +int im_Yxy2XYZ( IMAGE *, IMAGE * ); +int im_XYZ2Yxy( IMAGE *, IMAGE * ); + +int im_dECMC_fromLab( IMAGE *, IMAGE *, IMAGE * ); +int im_dE_fromXYZ( IMAGE *, IMAGE *, IMAGE * ); +int im_dE_fromLab( IMAGE *, IMAGE *, IMAGE * ); + +void imb_Lab2LCh( float *, float *, int ); +void imb_LCh2Lab( float *, float *, int ); +void imb_XYZ2Lab_tables( void ); +void imb_XYZ2Lab( float *, float *, int, im_colour_temperature * ); +void imb_Lab2XYZ( float *, float *, int, im_colour_temperature * ); +void imb_LabQ2Lab( PEL *, float *, int ); +void imb_Lab2LabQ( float *, PEL *, int ); +void imb_LabS2Lab( signed short *, float *, int ); +void imb_Lab2LabS( float *, signed short *, int n ); + +void im_col_make_tables_UCS( void ); + +float im_col_dECMC( float, float, float, float, float, float ); +float im_col_dE00( float, float, float, float, float, float ); + +int im_lab_morph( IMAGE *in, IMAGE *out, + DOUBLEMASK *mask, + double L_offset, double L_scale, + double a_scale, double b_scale ); + +/* other + */ +int im_feye( IMAGE *image, + const int xsize, const int ysize, const double factor ); +int im_eye( IMAGE *image, + const int xsize, const int ysize, const double factor ); +int im_zone( IMAGE *im, int size ); +int im_fzone( IMAGE *im, int size ); +int im_grey( IMAGE *im, const int xsize, const int ysize ); +int im_fgrey( IMAGE *im, const int xsize, const int ysize ); +int im_make_xy( IMAGE *out, const int xsize, const int ysize ); +int im_benchmarkn( IMAGE *in, IMAGE *out, int n ); +int im_benchmark2( IMAGE *in, double *out ); + +int im_cooc_matrix( IMAGE *im, IMAGE *m, + int xp, int yp, int xs, int ys, int dx, int dy, int flag ); +int im_cooc_asm( IMAGE *m, double *asmoment ); +int im_cooc_contrast( IMAGE *m, double *contrast ); +int im_cooc_correlation( IMAGE *m, double *correlation ); +int im_cooc_entropy( IMAGE *m, double *entropy ); + +int im_glds_matrix( IMAGE *im, IMAGE *m, + int xpos, int ypos, int xsize, int ysize, int dx, int dy ); +int im_glds_asm( IMAGE *m, double *asmoment ); +int im_glds_contrast( IMAGE *m, double *contrast ); +int im_glds_entropy( IMAGE *m, double *entropy ); +int im_glds_mean( IMAGE *m, double *mean ); + +int im_simcontr( IMAGE *image, int xs, int ys ); +int im_sines( IMAGE *image, + int xsize, int ysize, double horfreq, double verfreq ); +int im_spatres( IMAGE *in, IMAGE *out, int step ); + +int im_rightshift_size( IMAGE *in, IMAGE *out, int xshift, int yshift, int band_fmt ); + +/* mosaicing + */ +int im_lrmerge( IMAGE *ref, IMAGE *sec, IMAGE *out, + int dx, int dy, int mwidth ); +int im_tbmerge( IMAGE *ref, IMAGE *sec, IMAGE *out, + int dx, int dy, int mwidth ); + +int im_lrmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int mwidth ); +int im_tbmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int mwidth ); + +int im_lrmosaic( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ); +int im_tbmosaic( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ); + +int im_lrmosaic1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ); +int im_tbmosaic1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ); + +int im_global_balance( IMAGE *in, IMAGE *out, double gamma ); +int im_global_balancef( IMAGE *in, IMAGE *out, double gamma ); + +int im_match_linear( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2 ); +int im_match_linear_search( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int hwindowsize, int hsearchsize ); + +int im_affine( IMAGE *in, IMAGE *out, + double a, double b, double c, double d, double dx, double dy, + int ox, int oy, int ow, int oh ); +int im_similarity( IMAGE *in, IMAGE *out, + double a, double b, double dx, double dy ); +int im_similarity_area( IMAGE *in, IMAGE *out, + double a, double b, double dx, double dy, + int ox, int oy, int ow, int oh ); +int im_correl( IMAGE *ref, IMAGE *sec, + int xref, int yref, int xsec, int ysec, + int hwindowsize, int hsearchsize, + double *correlation, int *x, int *y ); +int im_remosaic( IMAGE *in, IMAGE *out, + const char *old_str, const char *new_str ); + +/* inplace + */ +int im_plotmask( IMAGE *, int, int, PEL *, PEL *, Rect * ); +int im_smear( IMAGE *, int, int, Rect * ); +int im_smudge( IMAGE *, int, int, Rect * ); +int im_paintrect( IMAGE *, Rect *, PEL * ); +int im_circle( IMAGE *, int, int, int, int ); +int im_insertplace( IMAGE *, IMAGE *, int, int ); +int im_line( IMAGE *, int, int, int, int, int ); +int im_fastlineuser(); +int im_readpoint( IMAGE *, int, int, PEL * ); +int im_flood( IMAGE *, int, int, PEL *, Rect * ); +int im_flood_blob( IMAGE *, int, int, PEL *, Rect * ); +int im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink ); +int im_lineset( IMAGE *in, IMAGE *out, IMAGE *mask, IMAGE *ink, + int n, int *x1v, int *y1v, int *x2v, int *y2v ); + +/* relational + */ +int im_equal( IMAGE *, IMAGE *, IMAGE * ); +int im_equalconst( IMAGE *, IMAGE *, double ); +int im_equal_vec( IMAGE *, IMAGE *, int, double * ); +int im_notequal( IMAGE *, IMAGE *, IMAGE * ); +int im_notequalconst( IMAGE *, IMAGE *, double ); +int im_notequal_vec( IMAGE *, IMAGE *, int, double * ); +int im_more( IMAGE *, IMAGE *, IMAGE * ); +int im_moreconst( IMAGE *, IMAGE *, double ); +int im_more_vec( IMAGE *, IMAGE *, int, double * ); +int im_less( IMAGE *, IMAGE *, IMAGE * ); +int im_lessconst( IMAGE *, IMAGE *, double ); +int im_less_vec( IMAGE *, IMAGE *, int, double * ); +int im_moreeq( IMAGE *, IMAGE *, IMAGE * ); +int im_moreeqconst( IMAGE *, IMAGE *, double ); +int im_moreeq_vec( IMAGE *, IMAGE *, int, double * ); +int im_lesseq( IMAGE *, IMAGE *, IMAGE * ); +int im_lesseqconst( IMAGE *, IMAGE *, double ); +int im_lesseq_vec( IMAGE *, IMAGE *, int, double * ); +int im_ifthenelse( IMAGE *, IMAGE *, IMAGE *, IMAGE * ); +int im_blend( IMAGE *, IMAGE *, IMAGE *, IMAGE * ); + +/* matrix + */ +DOUBLEMASK *im_mattrn( DOUBLEMASK *, const char * ); +DOUBLEMASK *im_matcat( DOUBLEMASK *, DOUBLEMASK *, const char * ); +DOUBLEMASK *im_matmul( DOUBLEMASK *, DOUBLEMASK *, const char * ); + +DOUBLEMASK *im_lu_decomp( const DOUBLEMASK *mat, const char *name ); +int im_lu_solve( const DOUBLEMASK *lu, double *vec ); +DOUBLEMASK *im_matinv( const DOUBLEMASK *mat, const char *name ); +int im_matinv_inplace( DOUBLEMASK *mat ); + + +int *im_ivector(); +float *im_fvector(); +double *im_dvector(); +void im_free_ivector(); +void im_free_fvector(); +void im_free_dvector(); + +int **im_imat_alloc(); +float **im_fmat_alloc(); +double **im_dmat_alloc(); +void im_free_imat(); +void im_free_fmat(); +void im_free_dmat(); + +int im_invmat( double **, int ); + +/* video + */ +int im_video_v4l1( IMAGE *im, const char *device, + int channel, int brightness, int colour, int contrast, int hue, + int ngrabs ); +int im_video_test( IMAGE *im, int brightness, int error ); + +/* Backwards compatibility macros. + */ +#define im_clear_error_string() im_error_clear() +#define im_errorstring() im_error_buffer() + +/* Deprecated API. + */ +void im_errormsg( const char *fmt, ... ) + __attribute__((format(printf, 1, 2))); +void im_verrormsg( const char *fmt, va_list ap ); +void im_errormsg_system( int err, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); +void im_diagnostics( const char *fmt, ... ) + __attribute__((format(printf, 1, 2))); +void im_warning( const char *fmt, ... ) + __attribute__((format(printf, 1, 2))); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_PROTO_H*/ diff --git a/include/vips/r_access.h b/include/vips/r_access.h new file mode 100644 index 00000000..1a1cebc7 --- /dev/null +++ b/include/vips/r_access.h @@ -0,0 +1,85 @@ +/* r_access.h + * + * 2006-09-21 tcv + * random access to images and regions + */ + +#ifndef IM_R_ACCESS_H +#define IM_R_ACCESS_H + +#include + + +/** ARRAY MEMBER MACROS **/ +/* these are local */ + +#define IM__TYPE_FROM_ARRAY(type,vptr,i) ( ((type*) (vptr))[i] ) + +#define IM__CHAR_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( gint8, (vptr), (i) ) +#define IM__UCHAR_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( guint8, (vptr), (i) ) +#define IM__SHORT_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( gint16, (vptr), (i) ) +#define IM__USHORT_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( guint16, (vptr), (i) ) +#define IM__INT_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( gint32, (vptr), (i) ) +#define IM__UINT_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( guint32, (vptr), (i) ) +#define IM__FLOAT_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( float, (vptr), (i) ) +#define IM__DOUBLE_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( double, (vptr), (i) ) + +#define IM__VALUE_FROM_ARRAY(band_fmt,vptr,i) ( \ + ( IM_BANDFMT_DOUBLE == (band_fmt) ) ? IM__DOUBLE_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_FLOAT == (band_fmt) ) ? IM__FLOAT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_INT == (band_fmt) ) ? IM__INT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_UINT == (band_fmt) ) ? IM__UINT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_SHORT == (band_fmt) ) ? IM__SHORT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_USHORT == (band_fmt) ) ? IM__USHORT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_CHAR == (band_fmt) ) ? IM__CHAR_FROM_ARRAY( (vptr), (i) ) \ + : IM__UCHAR_FROM_ARRAY( (vptr), (i) ) ) + +#define IM__ARRAY_ASSIGNMENT(band_fmt,vptr,i,val) ( \ + ( IM_BANDFMT_DOUBLE == (band_fmt) ) ? ( IM__DOUBLE_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM_BANDFMT_FLOAT == (band_fmt) ) ? ( IM__FLOAT_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM_BANDFMT_INT == (band_fmt) ) ? ( IM__INT_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM_BANDFMT_UINT == (band_fmt) ) ? ( IM__UINT_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM_BANDFMT_SHORT == (band_fmt) ) ? ( IM__SHORT_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM_BANDFMT_USHORT == (band_fmt) ) ? ( IM__USHORT_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM_BANDFMT_CHAR == (band_fmt) ) ? ( IM__CHAR_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM__UCHAR_FROM_ARRAY( (vptr), (i) )= (val) ) ) + +#define IM__ARRAY_INCREMENT(band_fmt,vptr,i,val) ( \ + ( IM_BANDFMT_DOUBLE == (band_fmt) ) ? ( IM__DOUBLE_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM_BANDFMT_FLOAT == (band_fmt) ) ? ( IM__FLOAT_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM_BANDFMT_INT == (band_fmt) ) ? ( IM__INT_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM_BANDFMT_UINT == (band_fmt) ) ? ( IM__UINT_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM_BANDFMT_SHORT == (band_fmt) ) ? ( IM__SHORT_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM_BANDFMT_USHORT == (band_fmt) ) ? ( IM__USHORT_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM_BANDFMT_CHAR == (band_fmt) ) ? ( IM__CHAR_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM__UCHAR_FROM_ARRAY( (vptr), (i) )+= (val) ) ) + + +/** IMAGE MEMBER MACROS **/ +/* export these */ + +#define IM_IMAGE_VALUE(im,x,y,band) IM__VALUE_FROM_ARRAY( (im)-> BandFmt, \ + IM_IMAGE_ADDR( (im), (x), (y) ), (band) ) + +#define IM_IMAGE_ASSIGNMENT(im,x,y,band,val) IM__ARRAY_ASSIGNMENT( (im)-> BandFmt, \ + IM_IMAGE_ADDR( (im), (x), (y) ), (band), (val) ) + +#define IM_IMAGE_INCREMENT(im,x,y,band,val) IM__ARRAY_INCREMENT( (im)-> BandFmt, \ + IM_IMAGE_ADDR( (im), (x), (y) ), (band), (val) ) + + +/** REGION MEMBER MACROS **/ +/* export these */ + +#define IM_REGION_VALUE(reg,x,y,band) IM__VALUE_FROM_ARRAY( (reg)-> im-> BandFmt, \ + IM_REGION_ADDR( (reg), (x), (y) ), (band) ) + +#define IM_REGION_ASSIGNMENT(reg,x,y,band,val) IM__ARRAY_ASSIGNMENT( (reg)-> im-> BandFmt, \ + IM_REGION_ADDR( (reg), (x), (y) ), (band), (val) ) + +#define IM_REGION_INCREMENT(reg,x,y,band,val) IM__ARRAY_INCREMENT( (reg)-> im-> BandFmt, \ + IM_REGION_ADDR( (reg), (x), (y) ), (band), (val) ) + + +#endif /* IM_R_ACCESS_H */ + diff --git a/include/vips/rect.h b/include/vips/rect.h new file mode 100644 index 00000000..8baebbb7 --- /dev/null +++ b/include/vips/rect.h @@ -0,0 +1,75 @@ +/* Simple rectangle algebra. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_RECT_H +#define IM_RECT_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* A rectangle. + */ +typedef struct im_rect_struct { + int left, top, width, height; +} Rect; + +/* Only define old, broken names if asked. + */ +#ifdef IM_ENABLE_DEPRECATED + +/* Useful macros. Compatibility ... see below for new names. + */ +#define right(R) ((R)->left + (R)->width) +#define bottom(R) ((R)->top + (R)->height) + +#endif /*IM_ENABLE_DEPRECATED*/ + +#define IM_RECT_RIGHT(R) ((R)->left + (R)->width) +#define IM_RECT_BOTTOM(R) ((R)->top + (R)->height) +#define IM_RECT_HCENTRE(R) ((R)->left + (R)->width / 2) +#define IM_RECT_VCENTRE(R) ((R)->top + (R)->height / 2) + +/* Rectangle algebra functions. + */ +void im_rect_marginadjust( Rect *r, int n ); +int im_rect_includespoint( Rect *r, int x, int y ); +int im_rect_includesrect( Rect *r1, Rect *r2 ); +void im_rect_intersectrect( Rect *r1, Rect *r2, Rect *r3 ); +int im_rect_isempty( Rect *r ); +void im_rect_unionrect( Rect *r1, Rect *r2, Rect *r3 ); +int im_rect_equalsrect( Rect *r1, Rect *r2 ); +Rect *im_rect_dup( Rect *r ); +void im_rect_normalise( Rect *r ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_RECT_H*/ diff --git a/include/vips/region.h b/include/vips/region.h new file mode 100644 index 00000000..a9897e40 --- /dev/null +++ b/include/vips/region.h @@ -0,0 +1,293 @@ +/* Definitions for partial image regions. + * + * J.Cupitt, 8/4/93 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_REGION_H +#define IM_REGION_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#ifdef TIME_THREAD +#include +#endif /*TIME_THREAD*/ + +#include "rect.h" + +/* Per-thread buffer cache. Held in a GPrivate. + */ +typedef struct im__buffer_cache_t { + GHashTable *hash; /* Hash to im_buffer_cache_list_t* */ + GThread *thread; /* Just for sanity checking */ +} im_buffer_cache_t; + +/* Per-image buffer cache. Hash to this from im_buffer_cache_t. + * We can't store the GSList directly in the hash table, as GHashTable lacks an + * update operation and we'd need to _remove() and _insert() on every list + * operation. + */ +typedef struct im__buffer_cache_list_t { + GSList *buffers; /* GSList of im_buffer_t* */ + GThread *thread; /* Just for sanity checking */ + IMAGE *im; + im_buffer_cache_t *cache; +} im_buffer_cache_list_t; + +/* What we track for each pixel buffer. + */ +typedef struct { + int ref_count; /* # of regions referencing us */ + IMAGE *im; /* IMAGE we are attached to */ + + Rect area; /* Area this pixel buffer covers */ + gboolean done; /* Calculated and in cache */ + im_buffer_cache_t *cache; + gboolean invalid; /* Needs to be recalculated */ + char *buf; /* Private malloc() area */ + size_t bsize; /* Size of private malloc() */ +} im_buffer_t; + +/* Region types. + */ +typedef enum region_type { + IM_REGION_NONE, + IM_REGION_BUFFER, /* a pixel buffer */ + IM_REGION_OTHER_REGION, /* memory on another region */ + IM_REGION_OTHER_IMAGE, /* memory on another image */ + IM_REGION_WINDOW /* mmap() buffer on fd on another image */ +} RegionType; + +/* Sub-area of image. + */ +typedef struct region_struct { + /* Users may read these two fields. + */ + IMAGE *im; /* Link back to parent image */ + Rect valid; /* Area of parent we can see */ + + /* The rest of REGION is private. + */ + RegionType type; /* What kind of attachment */ + char *data; /* Off here to get data */ + int bpl; /* Bytes-per-line for data */ + void *seq; /* Sequence we are using to fill region */ + + /* The thread that made this region. Used to assert() test that + * regions are not being shared between threads. + */ + GThread *thread; + + /* Ref to the window we use for this region, if any. + */ + im_window_t *window; + + /* Ref to the buffer we use for this region, if any. + */ + im_buffer_t *buffer; +} REGION; + +/* Private to iofuncs: the size of the `tiles' requested by im_generate() + * when acting as a data sink. + */ +#define IM__TILE_WIDTH (64) +#define IM__TILE_HEIGHT (64) + +/* The height of the strips for the other two request styles. + */ +#define IM__THINSTRIP_HEIGHT (1) +#define IM__FATSTRIP_HEIGHT (16) + +/* Functions on regions. + */ +void im__region_take_ownership( REGION *reg ); +void im__region_check_ownership( REGION *reg ); +void im__region_no_ownership( REGION *reg ); + +REGION *im_region_create( IMAGE *im ); +void im_region_free( REGION *reg ); +int im_region_buffer( REGION *reg, Rect *r ); +int im_region_image( REGION *reg, Rect *r ); +int im_region_region( REGION *reg, REGION *to, Rect *r, int x, int y ); +int im_region_equalsregion( REGION *reg1, REGION *reg2 ); +int im_region_position( REGION *reg1, int x, int y ); +typedef int (*im_region_fill_fn)( REGION *, void * ); +int im_region_fill( REGION *reg, Rect *r, im_region_fill_fn fn, void *a ); + +/* IMAGE functions which use regions. We do not strictly type the function + * arguments to avoid hassle. + */ +int im_prepare( REGION *reg, Rect *r ); +int im_prepare_many( REGION **reg, Rect *r ); +int im_prepare_to( REGION *reg, REGION *dest, Rect *r, int x, int y ); +int im_generate( IMAGE *im, + void *(*start_fn)(), int (*gen_fn)(), int (*stop_fn)(), + void *a, void *b +); +int im_iterate( IMAGE *im, + void *(*start_fn)(), int (*scan_fn)(), int (*stop_fn)(), + void *a, void *b +); +void im__copy_region( REGION *reg, REGION *dest, Rect *r, int x, int y ); + +/* Convenience functions for im_generate()/im_iterate(). + */ +void *im_start_one( IMAGE *out, IMAGE *in, void *dummy ); +int im_stop_one( REGION *reg, void *dummy1, void *dummy2 ); +void *im_start_many( IMAGE *out, IMAGE **in, void *dummy ); +int im_stop_many( REGION **out, void *dummy1, void *dummy2 ); +IMAGE **im_allocate_input_array( IMAGE *out, ... ); +int im_demand_hint( IMAGE *im, im_demand_type hint, ... ) + __attribute__((sentinel)); +int im_demand_hint_array( IMAGE *im, im_demand_type hint, IMAGE **in ); +void im_free_region_array( REGION **regs ); +REGION **im_allocate_region_array( IMAGE *im, int count ); +void im__find_demand_size( IMAGE *im, int *pw, int *ph ); + +/* Buffer processing. + */ +typedef void (*im_wrapmany_fn)( void **in, void *out, int width, + void *a, void *b ); +int im_wrapmany( IMAGE **in, IMAGE *out, + im_wrapmany_fn fn, void *a, void *b ); +typedef void (*im_wrapone_fn)( void *in, void *out, int width, + void *a, void *b ); +int im_wrapone( IMAGE *in, IMAGE *out, + im_wrapone_fn fn, void *a, void *b ); + +/* Internal VIPS functions shared by partials. + */ +int im__call_start( REGION *reg ); +void im__call_stop( REGION *reg ); + +/* window manager. + */ +im_window_t *im_window_ref( IMAGE *im, int top, int height ); +int im_window_unref( im_window_t *window ); +void im_window_print( im_window_t *window ); + +/* buffer manager. + */ +void im_buffer_done( im_buffer_t *buffer ); +void im_buffer_undone( im_buffer_t *buffer ); +void im_buffer_unref( im_buffer_t *buffer ); +im_buffer_t *im_buffer_ref( IMAGE *im, Rect *area ); +im_buffer_t *im_buffer_unref_ref( im_buffer_t *buffer, IMAGE *im, Rect *area ); +void im_buffer_print( im_buffer_t *buffer ); +void im_invalidate( IMAGE *im ); + +/* Only define if IM_ENABLE_DEPRECATED is set. + */ +#ifdef IM_ENABLE_DEPRECATED + +/* Compatibilty macros ... delete soon. See below for the new names. + */ + +/* Macros on REGIONs. + * lskip() add to move down line + * nele() number of elements across region + * rsize() sizeof width of region + * addr() address of pixel in region + */ +#define lskip(B) ((B)->bpl) +#define nele(B) ((B)->valid.width*(B)->im->Bands) +#define rsize(B) ((B)->valid.width*psize((B)->im)) + +/* addr() is special: if DEBUG is defined, make an addr() with bounds checking. + */ +#ifdef DEBUG +#define addr(B,X,Y) \ + ( (im_rect_includespoint( &(B)->valid, (X), (Y) ))? \ + ((B)->data + ((Y) - (B)->valid.top)*lskip(B) + \ + ((X) - (B)->valid.left)*psize((B)->im)): \ + (fprintf( stderr, \ + "addr: point out of bounds, file \"%s\", line %d\n" \ + "(point x=%d, y=%d\n" \ + " should have been within Rect left=%d, top=%d, " \ + "width=%d, height=%d)\n", \ + __FILE__, __LINE__, \ + (X), (Y), \ + (B)->valid.left, \ + (B)->valid.top, \ + (B)->valid.width, \ + (B)->valid.height ), abort(), (char *) NULL) \ + ) +#else /*DEBUG*/ +#define addr(B,X,Y) ((B)->data + ((Y)-(B)->valid.top)*lskip(B) + \ + ((X)-(B)->valid.left)*psize((B)->im)) +#endif /*DEBUG*/ + +#endif /*IM_ENABLE_DEPRECATED*/ + +/* Macros on REGIONs. + * IM_REGION_LSKIP() add to move down line + * IM_REGION_N_ELEMENTS() number of elements across region + * IM_REGION_SIZEOF_LINE() sizeof width of region + * IM_REGION_ADDR() address of pixel in region + */ +#define IM_REGION_LSKIP(R) ((R)->bpl) +#define IM_REGION_N_ELEMENTS(R) ((R)->valid.width*(R)->im->Bands) +#define IM_REGION_SIZEOF_LINE(R) \ + ((R)->valid.width * IM_IMAGE_SIZEOF_PEL((R)->im)) + +/* If DEBUG is defined, add bounds checking. + */ +#ifdef DEBUG +#define IM_REGION_ADDR(B,X,Y) \ + ( (im_rect_includespoint( &(B)->valid, (X), (Y) ))? \ + ((B)->data + ((Y) - (B)->valid.top)*IM_REGION_LSKIP(B) + \ + ((X) - (B)->valid.left)*IM_IMAGE_SIZEOF_PEL((B)->im)): \ + (fprintf( stderr, \ + "IM_REGION_ADDR: point out of bounds, " \ + "file \"%s\", line %d\n" \ + "(point x=%d, y=%d\n" \ + " should have been within Rect left=%d, top=%d, " \ + "width=%d, height=%d)\n", \ + __FILE__, __LINE__, \ + (X), (Y), \ + (B)->valid.left, \ + (B)->valid.top, \ + (B)->valid.width, \ + (B)->valid.height ), abort(), (char *) NULL) \ + ) +#else /*DEBUG*/ +#define IM_REGION_ADDR(B,X,Y) \ + ((B)->data + \ + ((Y)-(B)->valid.top) * IM_REGION_LSKIP(B) + \ + ((X)-(B)->valid.left) * IM_IMAGE_SIZEOF_PEL((B)->im)) +#endif /*DEBUG*/ + +#define IM_REGION_ADDR_TOPLEFT(B) ( (B)->data ) + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_REGION_H*/ diff --git a/include/vips/semaphore.h b/include/vips/semaphore.h new file mode 100644 index 00000000..112cd8dd --- /dev/null +++ b/include/vips/semaphore.h @@ -0,0 +1,64 @@ +/* Definitions for thread support. + * + * JC, 9/5/94 + * 30/7/99 RP, JC + * - reworked for posix/solaris threads + * 28/9/99 JC + * - restructured, made part of public API + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_SEMAPHORE_H +#define IM_SEMAPHORE_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* Implement our own semaphores. + */ +typedef struct { + char *name; + int v; + + GMutex *mutex; + GCond *cond; +} im_semaphore_t; + +int im_semaphore_up( im_semaphore_t *s ); +int im_semaphore_down( im_semaphore_t *s ); +int im_semaphore_upn( im_semaphore_t *s, int n ); +int im_semaphore_downn( im_semaphore_t *s, int n ); +void im_semaphore_destroy( im_semaphore_t *s ); +void im_semaphore_init( im_semaphore_t *s, int v, char *name ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_SEMAPHORE_H*/ diff --git a/include/vips/struct.h b/include/vips/struct.h new file mode 100644 index 00000000..99a8bdf9 --- /dev/null +++ b/include/vips/struct.h @@ -0,0 +1,44 @@ +/* Header file for structures using the mosaicing programs */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_STRUCT_H +#define IM_STRUCT_H + +typedef struct { + char *reference, *secondary; + int nopoints; + float *xref, *yref, *xsec, *ysec; +} CNTRL_POINTS; + +typedef struct { + char *reference, *secondary; + float scale, angle, deltax, deltay; + float Xcoef[6], Ycoef[6]; +} MERGE_PARAM; + +#endif /*IM_STRUCT_H*/ diff --git a/include/vips/thread.h b/include/vips/thread.h new file mode 100644 index 00000000..4d1b78f7 --- /dev/null +++ b/include/vips/thread.h @@ -0,0 +1,77 @@ +/* Private include file ... if we've been configured without gthread, we need + * to point the g_thread_*() and g_mutex_*() functions at our own stubs. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_THREAD_H +#define IM_THREAD_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#ifndef HAVE_THREADS +#undef g_thread_supported +#define g_thread_supported() (0) + +#define g_thread_init im__g_thread_init +#define g_thread_join im__g_thread_join +#define g_thread_self im__g_thread_self +#define g_thread_create_full im__g_thread_create_full + +/* We don't need a shadow imlementation of g_thread_create(), even though we + * use it, because it's just a macro over g_thread_create_full(). + */ + +void im__g_thread_init( GThreadFunctions *vtable ); +gpointer im__g_thread_join( GThread * ); +gpointer im__g_thread_self( void ); +GThread *im__g_thread_create_full( GThreadFunc, + gpointer, gulong, gboolean, gboolean, GThreadPriority, GError ** ); + +#undef g_mutex_new +#undef g_mutex_free +#undef g_mutex_lock +#undef g_mutex_unlock + +#define g_mutex_new im__g_mutex_new +#define g_mutex_free im__g_mutex_free +#define g_mutex_lock im__g_mutex_lock +#define g_mutex_unlock im__g_mutex_unlock + +GMutex *im__g_mutex_new( void ); +void im__g_mutex_free( GMutex * ); +void im__g_mutex_lock( GMutex * ); +void im__g_mutex_unlock( GMutex * ); +#endif /*!HAVE_THREADS*/ + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_THREAD_H*/ diff --git a/include/vips/threadgroup.h b/include/vips/threadgroup.h new file mode 100644 index 00000000..f55022fc --- /dev/null +++ b/include/vips/threadgroup.h @@ -0,0 +1,134 @@ +/* Thread eval for VIPS. + * + * 29/9/99 JC + * - from thread.h + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_THREADGROUP_H +#define IM_THREADGROUP_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#include + +/* Stack size for each thread. Need to set this explicitly because some + * systems have a very low default. + + FIXME ... should have an environment variable for this? + + */ +#define IM__DEFAULT_STACK_SIZE (2 * 1024 * 1024) + +/* Default tile geometry. + */ +extern int im__tile_width; +extern int im__tile_height; +extern int im__fatstrip_height; +extern int im__thinstrip_height; + +/* Default n threads. + */ +extern int im__concurrency; + +/* A work function. + */ +typedef int (*im__work_fn)( REGION *, void *, void *, void * ); + +/* What we track for each thread. + */ +typedef struct { + REGION *reg; /* Region this thread operates on */ + struct im__threadgroup_t *tg; /* Thread group we are part of */ + + GThread *thread; /* Thread for this region */ + im_semaphore_t go; /* Thread waits here to start work */ + int kill; /* Set this to make thread exit */ + int error; /* Set by thread if work fn fails */ + + REGION *oreg; /* If part of an inplace threadgroup, */ + Rect pos; /* where this thread should write */ + int x, y; /* it's result */ + + void *a, *b, *c; /* User arguments to work fns */ + +#ifdef TIME_THREAD + hrtime_t *btime, *etime; + int tpos; +#endif /*TIME_THREAD*/ +} im_thread_t; + +/* What we track for a group of threads working together. + */ +typedef struct im__threadgroup_t { + int zombie; /* Set if has been freed */ + + IMAGE *im; /* Image we are calculating */ + int pw, ph; /* Tile size */ + int nlines; /* Scanlines-at-once we prefer for iteration */ + + im__work_fn work; /* Work fn for this threadgroup */ + int inplace; /* Regions should be contiguous */ + + int nthr; /* Number of threads in group */ + im_thread_t **thr; /* Threads */ + + im_semaphore_t idle_sem;/* The number of idle threads */ + GSList *idle; /* All the idle threads */ + GMutex *idle_lock; +#ifdef DEBUG_HIGHWATER + int nidle; /* Number of idles */ + int min_idle; /* How short idle got */ +#endif /*DEBUG_HIGHWATER*/ + + int kill; /* Set this to stop threadgroup early */ +} im_threadgroup_t; + +void im_concurrency_set( int concurrency ); +int im_concurrency_get( void ); + +/* Thread group functions. + */ +im_threadgroup_t *im_threadgroup_create( IMAGE *im ); +int im_threadgroup_free( im_threadgroup_t *tg ); +im_thread_t *im_threadgroup_get( im_threadgroup_t *tg ); +void im_threadgroup_wait( im_threadgroup_t *tg ); +int im_threadgroup_iserror( im_threadgroup_t *tg ); +void im_threadgroup_trigger( im_thread_t *thr ); + +/* Threaded im_prepare() + */ +int im_prepare_thread( im_threadgroup_t *tg, REGION *oreg, Rect *r ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_THREADGROUP_H*/ diff --git a/include/vips/time.h b/include/vips/time.h new file mode 100644 index 00000000..5d073323 --- /dev/null +++ b/include/vips/time.h @@ -0,0 +1,63 @@ +/* Definitions for time struct. + * + * J.Cupitt, 8/4/93 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_TIME_H +#define IM_TIME_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif /*HAVE_SYS_TIME_H*/ + +/* Struct we keep a record of execution time in. Passed to eval callback, so + * it can assess progress. + */ +struct time_info { + IMAGE *im; /* Image we are part of */ + time_t start; /* Start time, in seconds */ + int run; /* Time we have been running */ + int eta; /* Estimated seconds of computation left */ + gint64 tpels; /* Number of pels we expect to calculate */ + gint64 npels; /* Number of pels calculated so far */ + int percent; /* Percent complete */ +}; + +extern int im__handle_eval( IMAGE *im, int w, int h ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_TIME_H*/ diff --git a/include/vips/util.h b/include/vips/util.h new file mode 100644 index 00000000..2cd58a2a --- /dev/null +++ b/include/vips/util.h @@ -0,0 +1,324 @@ +/* Various useful definitions. + * + * J.Cupitt, 8/4/93 + * 15/7/96 JC + * - C++ stuff added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_UTIL_H +#define IM_UTIL_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* Some platforms don't have M_PI in math.h :-( + */ +#define IM_PI (3.14159265358979323846) + +/* Only define if IM_ENABLE_DEPRECATED is set. + */ +#ifdef IM_ENABLE_DEPRECATED + +#ifndef MAX +#define MAX(A,B) ((A)>(B)?(A):(B)) +#define MIN(A,B) ((A)<(B)?(A):(B)) +#endif /*MAX*/ + +#define CLIP(A,V,B) MAX( (A), MIN( (B), (V) ) ) +#define NEW(IM,A) ((A *)im_malloc((IM),sizeof(A))) +#define NUMBER(R) (sizeof(R)/sizeof(R[0])) +#define ARRAY(IM,N,T) ((T *)im_malloc((IM),(N) * sizeof(T))) + +/* Duff's device. Do OPERation N times in a 16-way unrolled loop. + */ +#define UNROLL( N, OPER ) { \ + if( (N) ) { \ + int duff_count = ((N) + 15) / 16; \ + \ + switch( (N) % 16 ) { \ + case 0: do { OPER; \ + case 15: OPER; \ + case 14: OPER; \ + case 13: OPER; \ + case 12: OPER; \ + case 11: OPER; \ + case 10: OPER; \ + case 9: OPER; \ + case 8: OPER; \ + case 7: OPER; \ + case 6: OPER; \ + case 5: OPER; \ + case 4: OPER; \ + case 3: OPER; \ + case 2: OPER; \ + case 1: OPER; \ + } while( --duff_count > 0 ); \ + } \ + } \ +} + +/* Round a float to the nearest integer. This should give an identical result + * to the math.h rint() function (and the old SunOS nint() function), but be + * much faster. Beware: it evaluates its argument more than once, so don't use + * ++! + */ +#define RINT( R ) ((int)((R)>0?((R)+0.5):((R)-0.5))) + +/* Various integer range clips. Record over/under flows. + */ +#define CLIP_UCHAR( V, SEQ ) { \ + if( (V) & (UCHAR_MAX ^ -1) ) { \ + if( (V) < 0 ) { \ + (SEQ)->underflow++; \ + (V) = 0; \ + } \ + if( (V) > UCHAR_MAX ) { \ + (SEQ)->overflow++; \ + (V) = UCHAR_MAX; \ + } \ + } \ +} + +#define CLIP_USHORT( V, SEQ ) { \ + if( (V) & (USHRT_MAX ^ -1) ) { \ + if( (V) < 0 ) { \ + (SEQ)->underflow++; \ + (V) = 0; \ + } \ + if( (V) > USHRT_MAX ) { \ + (SEQ)->overflow++; \ + (V) = USHRT_MAX; \ + } \ + } \ +} + +#define CLIP_CHAR( V, SEQ ) { \ + if( (V) < SCHAR_MIN ) { \ + (SEQ)->underflow++; \ + (V) = SCHAR_MIN; \ + } \ + if( (V) > SCHAR_MAX ) { \ + (SEQ)->overflow++; \ + (V) = SCHAR_MAX; \ + } \ +} + +#define CLIP_SHORT( V, SEQ ) { \ + if( (V) < SHRT_MIN ) { \ + (SEQ)->underflow++; \ + (V) = SHRT_MIN; \ + } \ + if( (V) > SHRT_MAX ) { \ + (SEQ)->overflow++; \ + (V) = SHRT_MAX; \ + } \ +} + +#define CLIP_NONE( V, SEQ ) {} + +#endif /*IM_ENABLE_DEPRECATED*/ + +#define IM_MAX(A,B) ((A)>(B)?(A):(B)) +#define IM_MIN(A,B) ((A)<(B)?(A):(B)) +#define IM_ABS(x) (((x) >= 0) ? (x) : -(x)) + +#define IM_CLIP(A,V,B) IM_MAX( (A), IM_MIN( (B), (V) ) ) +#define IM_NEW(IM,A) ((A *)im_malloc((IM),sizeof(A))) +#define IM_NUMBER(R) ((int)(sizeof(R)/sizeof(R[0]))) +#define IM_ARRAY(IM,N,T) ((T *)im_malloc((IM),(N) * sizeof(T))) +#define IM_FREEF( F, S ) do { \ + if( S ) { \ + (void) F( (S) ); \ + (S) = 0; \ + } \ +} while( 0 ) +#define IM_FREE( A ) IM_FREEF( im_free, A ) +#define IM_SETSTR( S, V ) do { \ + if( (S) != (V) ) { \ + if( !(S) || !(V) || strcmp( (S), (V) ) != 0 ) { \ + const char *sst = (V); \ + \ + IM_FREE( S ); \ + if( sst ) \ + (S) = im_strdup( NULL, sst ); \ + } \ + } \ +} while( 0 ) + +/* Duff's device. Do OPERation N times in a 16-way unrolled loop. + */ +#define IM_UNROLL( N, OPER ) { \ + if( (N) ) { \ + int duff_count = ((N) + 15) / 16; \ + \ + switch( (N) % 16 ) { \ + case 0: do { OPER; \ + case 15: OPER; \ + case 14: OPER; \ + case 13: OPER; \ + case 12: OPER; \ + case 11: OPER; \ + case 10: OPER; \ + case 9: OPER; \ + case 8: OPER; \ + case 7: OPER; \ + case 6: OPER; \ + case 5: OPER; \ + case 4: OPER; \ + case 3: OPER; \ + case 2: OPER; \ + case 1: OPER; \ + } while( --duff_count > 0 ); \ + } \ + } \ +} + +/* Round a float to the nearest integer. This should give an identical result + * to the math.h rint() function (and the old SunOS nint() function), but be + * much faster. Beware: it evaluates its argument more than once, so don't use + * ++! + */ +#define IM_RINT( R ) ((int)((R)>0?((R)+0.5):((R)-0.5))) + +/* Various integer range clips. Record over/under flows. + */ +#define IM_CLIP_UCHAR( V, SEQ ) { \ + if( (V) < 0 ) { \ + (SEQ)->underflow++; \ + (V) = 0; \ + } \ + else if( (V) > UCHAR_MAX ) { \ + (SEQ)->overflow++; \ + (V) = UCHAR_MAX; \ + } \ +} + +#define IM_CLIP_USHORT( V, SEQ ) { \ + if( (V) < 0 ) { \ + (SEQ)->underflow++; \ + (V) = 0; \ + } \ + else if( (V) > USHRT_MAX ) { \ + (SEQ)->overflow++; \ + (V) = USHRT_MAX; \ + } \ +} + +#define IM_CLIP_CHAR( V, SEQ ) { \ + if( (V) < SCHAR_MIN ) { \ + (SEQ)->underflow++; \ + (V) = SCHAR_MIN; \ + } \ + else if( (V) > SCHAR_MAX ) { \ + (SEQ)->overflow++; \ + (V) = SCHAR_MAX; \ + } \ +} + +#define IM_CLIP_SHORT( V, SEQ ) { \ + if( (V) < SHRT_MIN ) { \ + (SEQ)->underflow++; \ + (V) = SHRT_MIN; \ + } \ + else if( (V) > SHRT_MAX ) { \ + (SEQ)->overflow++; \ + (V) = SHRT_MAX; \ + } \ +} + +#define IM_CLIP_NONE( V, SEQ ) {} + +/* Now implemented as macros. + */ +#define im_open_local( IM, NAME, MODE ) \ + ((IMAGE *) im_local( (IM), \ + (im_construct_fn) im_open, (im_callback_fn) im_close, \ + (void *) (NAME), (void *) (MODE), NULL )) + +/* Strange double cast stops bogus warnings from gcc 4.1 + */ +#define im_open_local_array( IM, OUT, N, NAME, MODE ) \ + (im_local_array( (IM), (void **)((void*)(OUT)), (N),\ + (im_construct_fn) im_open, (im_callback_fn) im_close, \ + (void *) (NAME), (void *) (MODE), NULL )) + +/* strtok replacement. + */ +char *im__break_token( char *str, char *brk ); + +/* Like GFunc, but return a value. + */ +typedef void *(*VSListMap2Fn)( void *, void *, void * ); +typedef void *(*VSListMap4Fn)( void *, void *, void *, void *, void * ); +typedef void *(*VSListFold2Fn)( void *, void *, void *, void * ); + +gboolean im_slist_equal( GSList *l1, GSList *l2 ); +void *im_slist_map2( GSList *list, VSListMap2Fn fn, void *a, void *b ); +void *im_slist_map2_rev( GSList *list, VSListMap2Fn fn, void *a, void *b ); +void *im_slist_map4( GSList *list, + VSListMap4Fn fn, void *a, void *b, void *c, void *d ); +void *im_slist_fold2( GSList *list, void *start, + VSListFold2Fn fn, void *a, void *b ); +GSList *im_slist_filter( GSList *list, VSListMap2Fn fn, void *a, void *b ); +void im_slist_free_all( GSList *list ); + +void *im_map_equal( void *a, void *b ); + +char *im_strncpy( char *dest, const char *src, int n ); +char *im_strrstr( const char *haystack, const char *needle ); +char *im_strdup( IMAGE *im, const char *str ); +gboolean im_ispostfix( const char *a, const char *b ); +gboolean im_isprefix( const char *a, const char *b ); +int im_vsnprintf( char *str, size_t size, const char *format, va_list ap ); +int im_snprintf( char *str, size_t size, const char *format, ... ) + __attribute__((format(printf, 3, 4))); +char *im_break_token( char *str, const char *brk ); + +const char *im_skip_dir( const char *filename ); +void im_filename_split( const char *path, char *name, char *mode ); +void im_filename_suffix( const char *path, char *suffix ); +int im_filename_suffix_match( const char *path, const char *suffixes[] ); +char *im_getnextoption( char **in ); +char *im_getsuboption( const char *buf ); + +void *im_local( IMAGE *im, + im_construct_fn cons, im_callback_fn dest, void *a, void *b, void *c ); +int im_local_array( IMAGE *im, void **out, int n, + im_construct_fn cons, im_callback_fn dest, void *a, void *b, void *c ); + +gint64 im_file_length( int fd ); + +char *im__file_read( FILE *fp, const char *name, unsigned int *length_out ); +char *im__file_read_name( const char *name, unsigned int *length_out ); +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_UTIL_H*/ diff --git a/include/vips/vbuf.h b/include/vips/vbuf.h new file mode 100644 index 00000000..45180fe2 --- /dev/null +++ b/include/vips/vbuf.h @@ -0,0 +1,84 @@ +/* A static string buffer, with overflow protection. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VBUF_H +#define IM_VBUF_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* A string in the process of being written to ... multiple calls to + * buf_append add to it, on overflow append "..." and block further writes. + */ +typedef struct { + char *base; /* String base */ + int mx; /* Maximum length */ + int i; /* Current write point */ + gboolean full; /* String has filled, block writes */ + int lasti; /* For read-recent */ + gboolean dynamic; /* We own the string with malloc() */ +} VBuf; + +/* Static init of one of these. + */ +#define IM_BUF_STATIC( TEXT, MAX ) \ + { &TEXT[0], MAX, 0, FALSE, 0, FALSE } + +void im_buf_rewind( VBuf *buf ); +void im_buf_destroy( VBuf *buf ); +void im_buf_init( VBuf *buf ); +void im_buf_set_static( VBuf *buf, char *base, int mx ); +void im_buf_set_dynamic( VBuf *buf, int mx ); +void im_buf_init_static( VBuf *buf, char *base, int mx ); +void im_buf_init_dynamic( VBuf *buf, int mx ); +gboolean im_buf_appendns( VBuf *buf, const char *str, int sz ); +gboolean im_buf_appends( VBuf *buf, const char *str ); +gboolean im_buf_appendline( VBuf *buf, const char *str ); +gboolean im_buf_appendf( VBuf *buf, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); +gboolean im_buf_vappendf( VBuf *buf, const char *fmt, va_list ap ); +gboolean im_buf_appendc( VBuf *buf, char ch ); +gboolean im_buf_appendg( VBuf *buf, double g ); +gboolean im_buf_appendsc( VBuf *buf, const char *str ); +gboolean im_buf_removec( VBuf *buf, char ch ); +gboolean im_buf_change( VBuf *buf, const char *old, const char * ); +gboolean im_buf_isempty( VBuf *buf ); +gboolean im_buf_isfull( VBuf *buf ); +const char *im_buf_all( VBuf *buf ); +gboolean im_buf_appendd( VBuf *buf, int d ); +int im_buf_len( VBuf *buf ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_VBUF_H*/ + + diff --git a/include/vips/version.h.in b/include/vips/version.h.in new file mode 100644 index 00000000..2529a6da --- /dev/null +++ b/include/vips/version.h.in @@ -0,0 +1,15 @@ +/* Macros for the header version. + */ + +#ifndef IM_VERSION_H +#define IM_VERSION_H + +#define IM_VERSION "@IM_VERSION@" +#define IM_VERSION_STRING "@IM_VERSION_STRING@" +#define IM_MAJOR_VERSION (@IM_MAJOR_VERSION@) +#define IM_MINOR_VERSION (@IM_MINOR_VERSION@) +#define IM_MICRO_VERSION (@IM_MICRO_VERSION@) +#define IM_INTERFACE_AGE (@IM_INTERFACE_AGE@) +#define IM_BINARY_AGE (@IM_BINARY_AGE@) + +#endif /*IM_VERSION_H*/ diff --git a/include/vips/vips b/include/vips/vips new file mode 100644 index 00000000..06490d14 --- /dev/null +++ b/include/vips/vips @@ -0,0 +1,106 @@ +// Include file to get all VIPS C++ bindings + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VIPS +#define IM_VIPS + +#include + +// If we have already #included the C vips headers, we have to undef a load of +// stuff to stop vips's stupid macros messing up our enums +#ifdef IM_VIPS_H +#ifdef IM_ENABLE_DEPRECATED + +#undef MULTIBAND +#undef B_W +#undef LUMINACE +#undef XRAY +#undef IR +#undef YUV +#undef RED_ONLY +#undef GREEN_ONLY +#undef BLUE_ONLY +#undef POWER_SPECTRUM +#undef HISTOGRAM + +#undef LUT +#undef XYZ +#undef LAB +#undef CMC +#undef CMYK +#undef LABQ +#undef RGB +#undef UCS +#undef LCH +#undef LABS +#undef sRGB + +#undef FMTNOTSET +#undef FMTUCHAR +#undef FMTCHAR +#undef FMTUSHORT +#undef FMTSHORT +#undef FMTUINT +#undef FMTINT +#undef FMTFLOAT +#undef FMTCOMPLEX +#undef FMTDOUBLE +#undef FMTDPCOMPLEX + +#undef NOCODING +#undef COLQUANT +#undef LABPACK +#undef LABPACK_COMPRESSED +#undef RGB_COMPRESSED +#undef LUM_COMPRESSED + +#undef NO_COMPRESSION +#undef TCSF_COMPRESSION +#undef JPEG_COMPRESSION + +#endif /*IM_ENABLE_DEPRECATED*/ +#endif /*IM_VIPS_H*/ + +#ifdef IM_RECT_H +#ifdef IM_ENABLE_DEPRECATED + +#undef right +#undef bottom + +#endif /*IM_ENABLE_DEPRECATED*/ +#endif /*IM_RECT_H*/ + +#define VIPS_NAMESPACE_START namespace vips { +#define VIPS_NAMESPACE_END } + +#include +#include +#include +#include + +#endif /*IM_VIPS*/ diff --git a/include/vips/vips.h b/include/vips/vips.h new file mode 100644 index 00000000..9305d55e --- /dev/null +++ b/include/vips/vips.h @@ -0,0 +1,463 @@ +/* @(#) Header file for Birkbeck/VIPS Image Processing Library + * Authors: N. Dessipris, K. Martinez, Birkbeck College, London. + * Sept 94 + * + * 15/7/96 JC + * - now does C++ extern stuff + * - many more protos + * 15/4/97 JC + * - protos split out + * 4/3/98 JC + * - IM_ANY added + * - sRGB colourspace added + * 28/10/98 JC + * - VASARI_MAGIC_INTEL and VASARI_MAGIC_SPARC added + * 29/9/99 JC + * - new locks for threading, no more threadgroup stuff in IMAGE + * 30/11/00 JC + * - override RGB/CMYK macros on cygwin + * 21/9/02 JC + * - new Xoffset/Yoffset fields + * - rationalized macro names + * 6/6/05 Markus Wollgarten + * - added Meta header field + * 31/7/05 + * - added meta.h for new metadata API + * 22/8/05 + * - scrapped stupid VAS_HD + * 30/9/05 + * - added sizeof_header field for mmap window read of RAW files + * 4/10/05 + * - now you have to define IM_ENABLE_DEPRECATED to get broken #defined + * 5/10/05 + * - added GNUC attributes + * 8/5/06 + * - added RGB16, GREY16 + * 30/10/06 + * - added im_window_t + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VIPS_H +#define IM_VIPS_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* If we're not using GNU C, elide __attribute__ + */ +#ifndef __GNUC__ +# ifndef __attribute__ +# define __attribute__(x) /*NOTHING*/ +# endif +#endif + +#include +#include + +#include +#include + +#define IM_SPARE (8) + +/* If you read MSB first, you get these two values. + * intel order: byte 0 = b6 + * SPARC order: byte 0 = 08 + */ +#define IM_MAGIC_INTEL (0xb6a6f208) +#define IM_MAGIC_SPARC (0x08f2a6b6) + +/* Private to iofuncs: the image size above which we switch from + * mmap()-whole-image behaviour to mmap()-window, plus window margins. + */ +#define IM__MMAP_LIMIT (1024*1024*30) +#define IM__WINDOW_MARGIN (128) + +/* sizeof() a VIPS header on disc. + */ +#define IM_SIZEOF_HEADER (64) + +typedef unsigned char PEL; /* useful datum */ + +/* All these #defines are here for backwards compatibility ... delete them + * soon. See the bottom of this file for the new names. + */ + +/* Only define old, broken names if asked. + */ +#ifdef IM_ENABLE_DEPRECATED + +/* On win32, need to override the wingdi defs for these. Yuk! + */ +#ifdef HAVE_WINDOWS_H +#ifdef RGB +#undef RGB +#endif +#ifdef CMYK +#undef CMYK +#endif +#endif /*HAVE_WINDOWS_H*/ + +/* Bits per Band */ +#define BBBYTE 8 +#define BBSHORT 16 +#define BBINT 32 +#define BBFLOAT 32 +#define BBCOMPLEX 64 /* complex consisting of two floats */ +#define BBDOUBLE 64 +#define BBDPCOMPLEX 128 /* complex consisting of two doubles */ + +/* picture Type */ +#define MULTIBAND 0 +#define B_W 1 +#define LUMINACE 2 +#define XRAY 3 +#define IR 4 +#define YUV 5 +#define RED_ONLY 6 /* red channel only */ +#define GREEN_ONLY 7 /* green channel only */ +#define BLUE_ONLY 8 /* blue channel only */ +#define POWER_SPECTRUM 9 +#define HISTOGRAM 10 +#define FOURIER 24 + +/* Colour spaces. + */ +#define LUT 11 +#define XYZ 12 +#define LAB 13 +#define CMC 14 +#define CMYK 15 +#define LABQ 16 +#define RGB 17 +#define UCS 18 +#define LCH 19 +#define LABS 21 +#define sRGB 22 +#define YXY 23 + +/* BandFmt + */ +#define FMTNOTSET -1 +#define FMTUCHAR 0 /* pels interpreted as unsigned chars */ +#define FMTCHAR 1 /* pels interpreted as signed chars */ +#define FMTUSHORT 2 /* pels interpreted as unsigned shorts */ +#define FMTSHORT 3 /* pels interpreted as signed shorts */ +#define FMTUINT 4 /* pels interpreted as unsigned ints */ +#define FMTINT 5 /* pels interpreted as signed ints */ +#define FMTFLOAT 6 /* pels interpreted as floats */ +#define FMTCOMPLEX 7 /* pels interpreted as complex (2 float each) */ +#define FMTDOUBLE 8 /* pels interpreted as unsigned double */ +#define FMTDPCOMPLEX 9 /* pels interpreted as complex (2 double each)*/ + +/* Coding type + */ +#define NOCODING 0 +#define COLQUANT 1 +#define LABPACK 2 +#define LABPACK_COMPRESSED 3 +#define RGB_COMPRESSED 4 +#define LUM_COMPRESSED 5 + +/* Compression type + */ +#define NO_COMPRESSION 0 +#define TCSF_COMPRESSION 1 +#define JPEG_COMPRESSION 2 + +#endif /*IM_ENABLE_DEPRECATED*/ + +/* Types of image descriptor we may have. The type field is advisory only: it + * does not imply that any fields in IMAGE have valid data. + */ +typedef enum { + IM_NONE, /* no type set */ + IM_SETBUF, /* malloced memory array */ + IM_SETBUF_FOREIGN, /* memory array, don't free on close */ + IM_OPENIN, /* input from fd */ + IM_MMAPIN, /* memory mapped input file */ + IM_MMAPINRW, /* memory mapped read/write file */ + IM_OPENOUT, /* output to fd */ + IM_PARTIAL /* partial image */ +} im_desc_type; + +/* Demand style from im_generate(). See im_demand_hint(). + */ +typedef enum { + IM_SMALLTILE, + IM_FATSTRIP, + IM_THINSTRIP, + IM_ANY /* Not from a disc file, any geometry */ +} im_demand_type; + +/* What we track for each mmap window. Have a list of these on an openin + * IMAGE. + */ +typedef struct { + int ref_count; /* # of regions referencing us */ + struct im__IMAGE *im; /* IMAGE we are attached to */ + + int top; /* Area of image we have mapped, in pixels */ + int height; + char *data; /* First pixel of line 'top' */ + + PEL *baseaddr; /* Base of window */ + size_t length; /* Size of window */ +} im_window_t; + +/* Image descriptor for subroutine i/o args + */ +typedef struct im__IMAGE { + /* Fields from file header. + */ + int Xsize; + int Ysize; + int Bands; + int Bbits; + int BandFmt; + int Coding; + int Type; + float Xres; + float Yres; + int Length; + short Compression; + short Level; + int Xoffset; + int Yoffset; + + /* Derived fields that user can fiddle with. + */ + char *Hist; /* don't use ... call im_history_get() */ + char *filename; /* pointer to copy of filename */ + char *data; /* start of image data for WIO */ + struct time_info *time; /* time struct for eval callback */ + int kill; /* set to non-zero to block partial eval */ + + /* Private fields. + */ + im_desc_type dtype; /* descriptor type */ + int fd; /* file descriptor */ + char *baseaddr; /* pointer to the start of an mmap file */ + size_t length; /* size of mmap area */ + GSList *closefns; /* list of close callbacks */ + GSList *evalfns; /* list of eval callbacks */ + GSList *evalendfns; /* list of eval end callbacks */ + int closing; /* true for this descriptor is closing */ + int close_pending; /* true for this descriptor is a zombie */ + guint32 magic; /* magic from header, endian-ness of image */ + + /* Partial image stuff. All private! All these fields are initialised + * to NULL and ignored unless set by im_generate() or im_partial(). + */ + void *(*start)(); /* user-supplied start function */ + int (*generate)(); /* user-supplied generate function */ + int (*stop)(); /* user-supplied stop function */ + void *client1; /* user arguments */ + void *client2; + GMutex *sslock; /* start-stop lock */ + GSList *regions; /* list of regions current for this image */ + im_demand_type dhint; /* demand style hint */ + + /* Extra user-defined fields ... see im_meta_get_int() etc. + */ + GHashTable *Meta; /* GhashTable of GValue */ + GSList *Meta_traverse; /* Traverse order for Meta */ + + /* Part of mmap() read ... the sizeof() the header we skip from the + * file start. Usually IM_SIZEOF_HEADER, but can be soomething else + * for binary file read. + */ + int sizeof_header; + + /* If this is a large disc image, don't map the whole thing, instead + * have a set of windows shared between the regions active on the + * image. List of im_window_t. + */ + GSList *windows; + + /* Parent/child relationships, built from args to im_demand_hint(). + * We use these to invalidate pixel buffers on im_invalidate(). Use + * 'serial' to spot circular dependencies. + */ + GSList *parents; + GSList *children; + int serial; + + /* Keep a list of recounted GValue strings so we can share hist + * efficiently. + */ + GSList *history_list; +} IMAGE; + +/* Only define if IM_ENABLE_DEPRECATED is set. + */ +#ifdef IM_ENABLE_DEPRECATED + +/* Macros on IMAGEs. + * esize() sizeof band element + * psize() sizeof pel + * lsize() sizeof scan line + * niele() number of elements in scan line + */ +#define esize(I) ((I)->Bbits >> 3) +#define psize(I) (esize(I)*(I)->Bands) +#define lsize(I) (psize(I)*(I)->Xsize) +#define niele(I) ((I)->Bands*(I)->Xsize) + +#endif /*IM_ENABLE_DEPRECATED*/ + +/* Used to define a region of interest for im_extract() etc. + */ +typedef struct { + int xstart; + int ystart; + int xsize; + int ysize; + int chsel; /* 1 2 3 or 0, for r g b or all respectively + *(channel select) */ +} IMAGE_BOX; + +/* @(#) Definition for structure to hold integer or double masks + */ + +typedef struct im__INTMASK { + int xsize; + int ysize; + int scale; + int offset; + int *coeff; + char *filename; +} INTMASK ; + +typedef struct im__DOUBLEMASK { + int xsize; + int ysize; + double scale; + double offset; + double *coeff; + char *filename; +} DOUBLEMASK ; + +/* A colour temperature. + */ +typedef struct { + double X0, Y0, Z0; +} im_colour_temperature; + +/* Sensible names for our #defines. Only bother with + * the ones we actually use. Switch over to defining only these ones at some + * point (vips8?). + */ + +#define IM_BBITS_BYTE (8) +#define IM_BBITS_SHORT (16) +#define IM_BBITS_INT (32) +#define IM_BBITS_FLOAT (32) +#define IM_BBITS_COMPLEX (64) +#define IM_BBITS_DOUBLE (64) +#define IM_BBITS_DPCOMPLEX (128) + +#define IM_TYPE_MULTIBAND (0) +#define IM_TYPE_B_W (1) +#define IM_TYPE_HISTOGRAM (10) +#define IM_TYPE_FOURIER (24) +#define IM_TYPE_XYZ (12) +#define IM_TYPE_LAB (13) +#define IM_TYPE_CMYK (15) +#define IM_TYPE_LABQ (16) +#define IM_TYPE_RGB (17) +#define IM_TYPE_UCS (18) +#define IM_TYPE_LCH (19) +#define IM_TYPE_LABS (21) +#define IM_TYPE_sRGB (22) +#define IM_TYPE_YXY (23) +#define IM_TYPE_RGB16 (25) +#define IM_TYPE_GREY16 (26) + +#define IM_BANDFMT_NOTSET (-1) +#define IM_BANDFMT_UCHAR (0) +#define IM_BANDFMT_CHAR (1) +#define IM_BANDFMT_USHORT (2) +#define IM_BANDFMT_SHORT (3) +#define IM_BANDFMT_UINT (4) +#define IM_BANDFMT_INT (5) +#define IM_BANDFMT_FLOAT (6) +#define IM_BANDFMT_COMPLEX (7) +#define IM_BANDFMT_DOUBLE (8) +#define IM_BANDFMT_DPCOMPLEX (9) + +#define IM_CODING_NONE (0) +#define IM_CODING_LABQ (2) + +#define IM_IMAGE_SIZEOF_ELEMENT(I) ((I)->Bbits >> 3) +#define IM_IMAGE_SIZEOF_PEL(I) \ + (IM_IMAGE_SIZEOF_ELEMENT(I) * (I)->Bands) +#define IM_IMAGE_SIZEOF_LINE(I) (IM_IMAGE_SIZEOF_PEL(I) * (I)->Xsize) +#define IM_IMAGE_N_ELEMENTS(I) ((I)->Bands * (I)->Xsize) + +/* If DEBUG is defined, add bounds checking. + */ +#ifdef DEBUG +#define IM_IMAGE_ADDR(I,X,Y) \ + ( ((X) >= 0 && (X) < (I)->Xsize && \ + (Y) >= 0 && (Y) < (I)->Ysize) ? \ + ((I)->data + (Y) * IM_IMAGE_SIZEOF_LINE(I) + \ + (X) * IM_IMAGE_SIZEOF_PEL(I)) : \ + (fprintf( stderr, \ + "IM_IMAGE_ADDR: point out of bounds, " \ + "file \"%s\", line %d\n" \ + "(point x=%d, y=%d\n" \ + " should have been within Rect left=%d, top=%d, " \ + "width=%d, height=%d)\n", \ + __FILE__, __LINE__, \ + (X), (Y), \ + 0, 0, \ + (I)->Xsize, \ + (I)->Ysize ), abort(), (char *) NULL) \ + ) +#else /*DEBUG*/ +#define IM_IMAGE_ADDR(I,X,Y) \ + ((I)->data + \ + (Y) * IM_IMAGE_SIZEOF_LINE(I) + \ + (X) * IM_IMAGE_SIZEOF_PEL(I)) +#endif /*DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_VIPS_H*/ diff --git a/include/vips/vipsc++.h b/include/vips/vipsc++.h new file mode 100644 index 00000000..3541bff3 --- /dev/null +++ b/include/vips/vipsc++.h @@ -0,0 +1,308 @@ +// this file automatically generated from +// VIPS library 7.12.2-Tue Jul 17 23:36:09 BST 2007 +VImage abs() throw( VError ); +VImage acos() throw( VError ); +VImage add( VImage ) throw( VError ); +VImage asin() throw( VError ); +VImage atan() throw( VError ); +double avg() throw( VError ); +double point_bilinear( double, double, int ) throw( VError ); +VImage bandmean() throw( VError ); +VImage ceil() throw( VError ); +VImage cmulnorm( VImage ) throw( VError ); +VImage cos() throw( VError ); +double deviate() throw( VError ); +VImage divide( VImage ) throw( VError ); +VImage exp10() throw( VError ); +VImage expn( double ) throw( VError ); +VImage expn( std::vector ) throw( VError ); +VImage exp() throw( VError ); +VImage fav4( VImage, VImage, VImage ) throw( VError ); +VImage floor() throw( VError ); +VImage gadd( double, double, VImage, double ) throw( VError ); +VImage invert() throw( VError ); +VImage lin( double, double ) throw( VError ); +static VImage linreg( std::vector, std::vector ) throw( VError ); +VImage lin( std::vector, std::vector ) throw( VError ); +VImage litecor( VImage, int, double ) throw( VError ); +VImage log10() throw( VError ); +VImage log() throw( VError ); +double max() throw( VError ); +std::complex maxpos() throw( VError ); +double maxpos_avg( double&, double& ) throw( VError ); +VDMask measure( int, int, int, int, int, int ) throw( VError ); +double min() throw( VError ); +std::complex minpos() throw( VError ); +VImage multiply( VImage ) throw( VError ); +VImage pow( double ) throw( VError ); +VImage pow( std::vector ) throw( VError ); +VImage remainder( VImage ) throw( VError ); +VImage remainder( double ) throw( VError ); +VImage remainder( std::vector ) throw( VError ); +VImage rint() throw( VError ); +VImage sign() throw( VError ); +VImage sin() throw( VError ); +VDMask stats() throw( VError ); +VImage subtract( VImage ) throw( VError ); +VImage tan() throw( VError ); +VImage andimage( VImage ) throw( VError ); +VImage andimage( int ) throw( VError ); +VImage andimage( std::vector ) throw( VError ); +VImage orimage( VImage ) throw( VError ); +VImage orimage( int ) throw( VError ); +VImage orimage( std::vector ) throw( VError ); +VImage eorimage( VImage ) throw( VError ); +VImage eorimage( int ) throw( VError ); +VImage eorimage( std::vector ) throw( VError ); +VImage shiftleft( int ) throw( VError ); +VImage shiftright( int ) throw( VError ); +VImage LCh2Lab() throw( VError ); +VImage LCh2UCS() throw( VError ); +VImage Lab2LCh() throw( VError ); +VImage Lab2LabQ() throw( VError ); +VImage Lab2LabS() throw( VError ); +VImage Lab2UCS() throw( VError ); +VImage Lab2XYZ() throw( VError ); +VImage Lab2XYZ_temp( double, double, double ) throw( VError ); +VImage Lab2disp( VDisplay ) throw( VError ); +VImage LabQ2LabS() throw( VError ); +VImage LabQ2Lab() throw( VError ); +VImage LabQ2XYZ() throw( VError ); +VImage LabQ2disp( VDisplay ) throw( VError ); +VImage LabS2LabQ() throw( VError ); +VImage LabS2Lab() throw( VError ); +VImage UCS2LCh() throw( VError ); +VImage UCS2Lab() throw( VError ); +VImage UCS2XYZ() throw( VError ); +VImage XYZ2Lab() throw( VError ); +VImage XYZ2Lab_temp( double, double, double ) throw( VError ); +VImage XYZ2UCS() throw( VError ); +VImage XYZ2Yxy() throw( VError ); +VImage XYZ2disp( VDisplay ) throw( VError ); +VImage XYZ2sRGB() throw( VError ); +VImage Yxy2XYZ() throw( VError ); +VImage dE00_fromLab( VImage ) throw( VError ); +VImage dECMC_fromLab( VImage ) throw( VError ); +VImage dECMC_fromdisp( VImage, VDisplay ) throw( VError ); +VImage dE_fromLab( VImage ) throw( VError ); +VImage dE_fromXYZ( VImage ) throw( VError ); +VImage dE_fromdisp( VImage, VDisplay ) throw( VError ); +VImage disp2Lab( VDisplay ) throw( VError ); +VImage disp2XYZ( VDisplay ) throw( VError ); +VImage icc_ac2rc( char* ) throw( VError ); +VImage icc_export( char*, int ) throw( VError ); +VImage icc_export_depth( int, char*, int ) throw( VError ); +VImage icc_import( char*, int ) throw( VError ); +VImage icc_import_embedded( int ) throw( VError ); +VImage icc_transform( char*, char*, int ) throw( VError ); +VImage lab_morph( VDMask, double, double, double, double ) throw( VError ); +VImage sRGB2XYZ() throw( VError ); +VImage bandjoin( VImage ) throw( VError ); +static VImage black( int, int, int ) throw( VError ); +VImage c2amph() throw( VError ); +VImage c2imag() throw( VError ); +VImage c2ps() throw( VError ); +VImage c2real() throw( VError ); +VImage c2rect() throw( VError ); +VImage clip2c() throw( VError ); +VImage clip2cm() throw( VError ); +VImage clip2d() throw( VError ); +VImage clip2dcm() throw( VError ); +VImage clip2f() throw( VError ); +VImage clip2fmt( int ) throw( VError ); +VImage clip2i() throw( VError ); +VImage clip2s() throw( VError ); +VImage clip2ui() throw( VError ); +VImage clip2us() throw( VError ); +VImage clip() throw( VError ); +VImage copy() throw( VError ); +VImage copy_morph( int, int, int ) throw( VError ); +VImage copy_swap() throw( VError ); +VImage copy_set( int, double, double, int, int ) throw( VError ); +static VImage csv2vips( char* ) throw( VError ); +VImage extract_area( int, int, int, int ) throw( VError ); +VImage extract_areabands( int, int, int, int, int, int ) throw( VError ); +VImage extract_band( int ) throw( VError ); +VImage extract_bands( int, int ) throw( VError ); +VImage extract( int, int, int, int, int ) throw( VError ); +VImage falsecolour() throw( VError ); +VImage fliphor() throw( VError ); +VImage flipver() throw( VError ); +static VImage gbandjoin( std::vector ) throw( VError ); +VImage grid( int, int, int ) throw( VError ); +VImage insert( VImage, int, int ) throw( VError ); +VImage insert_noexpand( VImage, int, int ) throw( VError ); +static VImage jpeg2vips( char* ) throw( VError ); +VImage lrjoin( VImage ) throw( VError ); +static VImage magick2vips( char* ) throw( VError ); +static VImage mask2vips( VDMask ) throw( VError ); +VImage msb() throw( VError ); +VImage msb_band( int ) throw( VError ); +static VImage png2vips( char* ) throw( VError ); +static VImage exr2vips( char* ) throw( VError ); +static VImage ppm2vips( char* ) throw( VError ); +static VImage analyze2vips( char* ) throw( VError ); +VImage recomb( VDMask ) throw( VError ); +VImage replicate( int, int ) throw( VError ); +VImage ri2c( VImage ) throw( VError ); +VImage rot180() throw( VError ); +VImage rot270() throw( VError ); +VImage rot90() throw( VError ); +VImage scale() throw( VError ); +VImage scaleps() throw( VError ); +VImage rightshift_size( int, int, int ) throw( VError ); +VImage slice( double, double ) throw( VError ); +VImage subsample( int, int ) throw( VError ); +char* system( char* ) throw( VError ); +VImage tbjoin( VImage ) throw( VError ); +static VImage text( char*, char*, int, int, int ) throw( VError ); +VImage thresh( double ) throw( VError ); +static VImage tiff2vips( char* ) throw( VError ); +void vips2csv( char* ) throw( VError ); +void vips2jpeg( char* ) throw( VError ); +VDMask vips2mask() throw( VError ); +void vips2mimejpeg( int ) throw( VError ); +void vips2png( char* ) throw( VError ); +void vips2ppm( char* ) throw( VError ); +void vips2tiff( char* ) throw( VError ); +VImage zoom( int, int ) throw( VError ); +VImage addgnoise( double ) throw( VError ); +VImage compass( VIMask ) throw( VError ); +VImage contrast_surface( int, int ) throw( VError ); +VImage contrast_surface_raw( int, int ) throw( VError ); +VImage conv( VIMask ) throw( VError ); +VImage conv_raw( VIMask ) throw( VError ); +VImage convf( VDMask ) throw( VError ); +VImage convf_raw( VDMask ) throw( VError ); +VImage convsep( VIMask ) throw( VError ); +VImage convsep_raw( VIMask ) throw( VError ); +VImage convsepf( VDMask ) throw( VError ); +VImage convsepf_raw( VDMask ) throw( VError ); +VImage convsub( VIMask, int, int ) throw( VError ); +VImage embed( int, int, int, int, int ) throw( VError ); +VImage fastcor( VImage ) throw( VError ); +VImage fastcor_raw( VImage ) throw( VError ); +static VImage gaussnoise( int, int, double, double ) throw( VError ); +VImage grad_x() throw( VError ); +VImage grad_y() throw( VError ); +VImage gradcor( VImage ) throw( VError ); +VImage gradcor_raw( VImage ) throw( VError ); +VImage gradient( VIMask ) throw( VError ); +static VImage rank_image( std::vector, int ) throw( VError ); +VImage lindetect( VIMask ) throw( VError ); +static VImage maxvalue( std::vector ) throw( VError ); +int mpercent( double ) throw( VError ); +VImage rank( int, int, int ) throw( VError ); +VImage rank_raw( int, int, int ) throw( VError ); +VImage resize_linear( int, int ) throw( VError ); +VImage sharpen( int, double, double, double, double, double ) throw( VError ); +VImage shrink( double, double ) throw( VError ); +VImage spcor( VImage ) throw( VError ); +VImage spcor_raw( VImage ) throw( VError ); +VImage spcor2( VImage ) throw( VError ); +VImage spcor2_raw( VImage ) throw( VError ); +VImage stretch3( double, double ) throw( VError ); +VImage zerox( int ) throw( VError ); +static VImage create_fmask( int, int, int, double, double, double, double, double ) throw( VError ); +VImage disp_ps() throw( VError ); +VImage flt_image_freq( int, double, double, double, double, double ) throw( VError ); +static VImage fractsurf( int, double ) throw( VError ); +VImage freqflt( VImage ) throw( VError ); +VImage fwfft() throw( VError ); +VImage rotquad() throw( VError ); +VImage invfft() throw( VError ); +VImage invfftr() throw( VError ); +VImage gammacorrect( double ) throw( VError ); +VImage heq( int ) throw( VError ); +VImage hist( int ) throw( VError ); +VImage histcum() throw( VError ); +VImage histeq() throw( VError ); +VImage histgr( int ) throw( VError ); +VImage histnD( int ) throw( VError ); +VImage histnorm() throw( VError ); +VImage histplot() throw( VError ); +VImage histspec( VImage ) throw( VError ); +VImage hsp( VImage ) throw( VError ); +static VImage identity( int ) throw( VError ); +static VImage identity_ushort( int, int ) throw( VError ); +int ismonotonic() throw( VError ); +VImage lhisteq( int, int ) throw( VError ); +VImage lhisteq_raw( int, int ) throw( VError ); +static VImage invertlut( VDMask, int ) throw( VError ); +static VImage buildlut( VDMask ) throw( VError ); +VImage maplut( VImage ) throw( VError ); +VImage project( VImage& ) throw( VError ); +VImage stdif( double, double, double, double, int, int ) throw( VError ); +VImage stdif_raw( double, double, double, double, int, int ) throw( VError ); +VImage tone_analyse( double, double, double, double, double, double ) throw( VError ); +static VImage tone_build( double, double, double, double, double, double, double, double ) throw( VError ); +static VImage tone_build_range( int, int, double, double, double, double, double, double, double, double ) throw( VError ); +VImage tone_map( VImage ) throw( VError ); +void circle( int, int, int, int ) throw( VError ); +VImage flood_blob_copy( int, int, std::vector ) throw( VError ); +void insertplace( VImage, int, int ) throw( VError ); +void line( int, int, int, int, int ) throw( VError ); +VImage lineset( VImage, VImage, std::vector, std::vector, std::vector, std::vector ) throw( VError ); +static VImage binfile( char*, int, int, int, int ) throw( VError ); +VImage cache( int, int, int ) throw( VError ); +int header_get_type( char* ) throw( VError ); +int header_int( char* ) throw( VError ); +double header_double( char* ) throw( VError ); +char* header_string( char* ) throw( VError ); +double cntlines( int ) throw( VError ); +VImage dilate( VIMask ) throw( VError ); +VImage dilate_raw( VIMask ) throw( VError ); +VImage erode( VIMask ) throw( VError ); +VImage erode_raw( VIMask ) throw( VError ); +VImage profile( int ) throw( VError ); +VImage affine( double, double, double, double, double, double, int, int, int, int ) throw( VError ); +double correl( VImage, int, int, int, int, int, int, int&, int& ) throw( VError ); +int _find_lroverlap( VImage, int, int, int, int, int, int, int, int&, double&, double&, double&, double& ) throw( VError ); +int _find_tboverlap( VImage, int, int, int, int, int, int, int, int&, double&, double&, double&, double& ) throw( VError ); +VImage global_balance( double ) throw( VError ); +VImage global_balancef( double ) throw( VError ); +VImage lrmerge( VImage, int, int, int ) throw( VError ); +VImage lrmerge1( VImage, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage lrmosaic( VImage, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage lrmosaic1( VImage, int, int, int, int, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage match_linear( VImage, int, int, int, int, int, int, int, int ) throw( VError ); +VImage match_linear_search( VImage, int, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage remosaic( char*, char* ) throw( VError ); +VImage similarity_area( double, double, double, double, int, int, int, int ) throw( VError ); +VImage similarity( double, double, double, double ) throw( VError ); +VImage tbmerge( VImage, int, int, int ) throw( VError ); +VImage tbmerge1( VImage, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage tbmosaic( VImage, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage tbmosaic1( VImage, int, int, int, int, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage benchmark() throw( VError ); +double benchmark2() throw( VError ); +VImage benchmarkn( int ) throw( VError ); +static VImage eye( int, int, double ) throw( VError ); +static VImage grey( int, int ) throw( VError ); +static VImage feye( int, int, double ) throw( VError ); +static VImage fgrey( int, int ) throw( VError ); +static VImage fzone( int ) throw( VError ); +static VImage make_xy( int, int ) throw( VError ); +static VImage zone( int ) throw( VError ); +VImage blend( VImage, VImage ) throw( VError ); +VImage equal( VImage ) throw( VError ); +VImage equal( std::vector ) throw( VError ); +VImage equal( double ) throw( VError ); +VImage ifthenelse( VImage, VImage ) throw( VError ); +VImage less( VImage ) throw( VError ); +VImage less( std::vector ) throw( VError ); +VImage less( double ) throw( VError ); +VImage lesseq( VImage ) throw( VError ); +VImage lesseq( std::vector ) throw( VError ); +VImage lesseq( double ) throw( VError ); +VImage more( VImage ) throw( VError ); +VImage more( std::vector ) throw( VError ); +VImage more( double ) throw( VError ); +VImage moreeq( VImage ) throw( VError ); +VImage moreeq( std::vector ) throw( VError ); +VImage moreeq( double ) throw( VError ); +VImage notequal( VImage ) throw( VError ); +VImage notequal( std::vector ) throw( VError ); +VImage notequal( double ) throw( VError ); +static VImage video_test( int, int ) throw( VError ); +static VImage video_v4l1( char*, int, int, int, int, int, int ) throw( VError ); diff --git a/include/vips/vipscpp.h b/include/vips/vipscpp.h new file mode 100644 index 00000000..ec263c70 --- /dev/null +++ b/include/vips/vipscpp.h @@ -0,0 +1,39 @@ +// Include file to get all VIPS C++ bindings + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* This header is just for compatibility with the pre-namespace C++ bindings. + */ + +#ifndef IM_VIPSCPP_H +#define IM_VIPSCPP_H + +#include + +using namespace vips; + +#endif /*IM_VIPSCPP_H*/ diff --git a/libsrc/Makefile.am b/libsrc/Makefile.am new file mode 100644 index 00000000..6d956bbd --- /dev/null +++ b/libsrc/Makefile.am @@ -0,0 +1,58 @@ + +SUBDIRS = \ + acquire \ + arithmetic \ + boolean \ + colour \ + conversion \ + convolution \ + freq_filt \ + histograms_lut \ + inplace \ + iofuncs \ + matrix \ + morphology \ + mosaicing \ + other \ + relational \ + video \ + . + +lib_LTLIBRARIES = libvips.la + +libvips_la_SOURCES = dummy.c + +# DLLs need dependant libs there too ... put @VIPS_LIBS@ at the end +libvips_la_LIBADD = \ + acquire/libacquire.la \ + arithmetic/libarithmetic.la \ + boolean/libboolean.la \ + colour/libcolour.la \ + conversion/libconversion.la \ + convolution/libconvolution.la \ + freq_filt/libfreq_filt.la \ + histograms_lut/libhistograms_lut.la \ + inplace/libinplace.la \ + iofuncs/libiofuncs.la \ + matrix/libmatrix.la \ + morphology/libmorphology.la \ + mosaicing/libmosaicing.la \ + other/libother.la \ + relational/librelational.la \ + video/libvideo.la \ + @VIPS_LIBS@ + +# use "-no-undefined" because we need to be able to generate dlls as well + +# commented out +# -no-undefined +# because I can't get libtool DLL builds working :-( + +libvips_la_LDFLAGS = \ + -no-undefined \ + -version-info @LIBRARY_CURRENT@:@LIBRARY_REVISION@:@LIBRARY_AGE@ + +vips.def: + ./makedef.pl $(prefix) > vips.def + +EXTRA_DIST = vips.def makedef.pl diff --git a/libsrc/acquire/Makefile.am b/libsrc/acquire/Makefile.am new file mode 100644 index 00000000..733c4645 --- /dev/null +++ b/libsrc/acquire/Makefile.am @@ -0,0 +1,9 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libacquire.la + +libacquire_la_SOURCES = \ + im_clamp.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ + diff --git a/libsrc/acquire/im_clamp.c b/libsrc/acquire/im_clamp.c new file mode 100644 index 00000000..df072687 --- /dev/null +++ b/libsrc/acquire/im_clamp.c @@ -0,0 +1,127 @@ +/* @(#) Function to perform black level correction given black image + * @(#) designed for PAD camera single field black to apply in blocks + * @(#) as each is reused for higher resolution pels (eg: 6 8 for Progres) + * @(#) IM_BANDFMT_UCHAR images only. Always writes UCHAR. + * @(#) int im_clamp(in, w, out, hstep, vstep) + * @(#) IMAGE *in, *w, *out; int hstep, vstep; + * @(#) - Compute clip(image - (black)) ie subtract black no negatives + * @(#) scales for low res Progres images to replicate black value + * @(#) Returns 0 on success and -1 on error + * fiddle at your peril - nasty code + * Copyright: 1993 KM + * 20/8/93 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_clamp( in, out,black, hstep, vstep ) +IMAGE *in, *black, *out; +int hstep, vstep; +{ PEL *p, *blk, *bline, *bexp; +PEL *q, *outbuf; +int rep; +int x, y, bnd; +int temp, blacky, newblacky; + +if( im_iocheck( in, out ) ) + return( -1 ); +if( in->Bbits != 8 || + in->Coding != IM_CODING_NONE || in->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_clamp", _( "bad input format" ) ); + return( -1 ); +} +if( black->Bbits != 8 || + black->Coding != IM_CODING_NONE || black->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_clamp", _( "bad black format" ) ); + return( -1 ); +} + +/* Set up the output header. + */ +if( im_cp_desc( out, in ) ) + return( -1 ); +if( im_setupout( out ) ) + return( -1 ); + +/* Make buffer for expanded black line + */ +if( !(bline = (PEL *) im_malloc( out, black->Bands * hstep * in->Xsize )) ) + return( -1 ); +/* Make buffer we write to. + */ +if( !(outbuf = (PEL *) im_malloc( out, out->Bands * out->Xsize )) ) + return( -1 ); +blacky = -1; +p = (PEL *) in->data; + +for( y = 0; y < in->Ysize; y++ ) { + /* calc corresponding black line - get new one if different */ + newblacky = (vstep * black->Ysize - in->Ysize + y)/vstep; + if( newblacky != blacky){ + blacky = newblacky; + /* time to expand a new black line */ + blk = (PEL *) (black->data + + black->Xsize * black->Bands * blacky); + for(bexp = bline, x = 0; x < black->Xsize; x++){ + for(rep = 0; rep < hstep; rep++) + for(q=blk, bnd = 0; bnd < in->Bands; bnd++) + *bexp++ = *q++; + blk += black->Bands; + } + } + + /* correct a line of image */ + bexp = bline; + q = outbuf; + for( x = 0; x < (out->Bands * out->Xsize); x++ ) { + temp = ((int) *p++ - *bexp++); + if( temp < 0 ) temp = 0; + *q++ = (PEL)temp; + } + + if( im_writeline( y, out, outbuf ) ) + return( -1 ); +} /* end of a line */ + +return( 0 ); +} diff --git a/libsrc/acquire/man3/Makefile.am b/libsrc/acquire/man3/Makefile.am new file mode 100644 index 00000000..27aac5d4 --- /dev/null +++ b/libsrc/acquire/man3/Makefile.am @@ -0,0 +1 @@ +man_MANS = diff --git a/libsrc/arithmetic/Makefile.am b/libsrc/arithmetic/Makefile.am new file mode 100644 index 00000000..e1273aa3 --- /dev/null +++ b/libsrc/arithmetic/Makefile.am @@ -0,0 +1,46 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libarithmetic.la + +libarithmetic_la_SOURCES = \ + arith_dispatch.c \ + im_abs.c \ + im_add.c \ + im_avg.c \ + im_point_bilinear.c \ + im_bandmean.c \ + im_cmulnorm.c \ + im_costra.c \ + im_deviate.c \ + im_divide.c \ + im_ceil.c \ + im_floor.c \ + im_expntra.c \ + im_fav4.c \ + im_gadd.c \ + im_gaddim.c \ + im_gfadd.c \ + im_invert.c \ + im_linreg.c \ + im_lintra.c \ + im_litecor.c \ + im_log10tra.c \ + im_logtra.c \ + im_max.c \ + im_maxpos.c \ + im_maxpos_avg.c \ + im_maxpos_vec.c \ + im_measure.c \ + im_min.c \ + im_minpos.c \ + im_multiply.c \ + im_powtra.c \ + im_remainder.c \ + im_rint.c \ + im_sign.c \ + im_sintra.c \ + im_stats.c \ + im_subtract.c \ + im_tantra.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/arithmetic/arith_dispatch.c b/libsrc/arithmetic/arith_dispatch.c new file mode 100644 index 00000000..579c7deb --- /dev/null +++ b/libsrc/arithmetic/arith_dispatch.c @@ -0,0 +1,1247 @@ +/* Function dispatch tables for arithmetic. + * + * J. Cupitt, 8/4/93. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* One image in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Two images in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Image in, number out. + */ +static im_arg_desc image_in_num_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_DOUBLE( "value" ) +}; + +/* Call im_abs via arg vector. + */ +static int +abs_vec( im_object *argv ) +{ + return( im_abs( argv[0], argv[1] ) ); +} + +/* Description of im_abs. + */ +static im_function abs_desc = { + "im_abs", /* Name */ + N_( "absolute value" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + abs_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_add via arg vector. + */ +static int +add_vec( im_object *argv ) +{ + return( im_add( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_add. + */ +static im_function add_desc = { + "im_add", /* Name */ + N_( "add two images" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + add_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_avg via arg vector. + */ +static int +avg_vec( im_object *argv ) +{ + double f; + + if( im_avg( argv[0], &f ) ) + return( -1 ); + + *((double *) argv[1]) = f; + return( 0 ); +} + +/* Description of im_avg. + */ +static im_function avg_desc = { + "im_avg", /* Name */ + N_( "average value of image" ), /* Description */ + IM_FN_PIO, /* Flags */ + avg_vec, /* Dispatch function */ + IM_NUMBER( image_in_num_out ), /* Size of arg list */ + image_in_num_out /* Arg list */ +}; + +/* Args to im_point_bilinear. + */ +static im_arg_desc point_bilinear_args[] = { + IM_INPUT_IMAGE ("in"), + IM_INPUT_DOUBLE("x"), + IM_INPUT_DOUBLE("y"), + IM_INPUT_INT("band"), + IM_OUTPUT_DOUBLE("val") +}; + +/* Call im_point_bilinear via arg vector. + */ +static int +point_bilinear_vec( im_object *argv ) +{ + return im_point_bilinear( argv[0], *(double*)argv[1], *(double*)argv[2], *(int*)argv[3], argv[4] ); +} + +/* Description of im_point_bilinear. + */ +static im_function point_bilinear_desc = { + "im_point_bilinear", + "interpolate value at single point, linearly", + IM_FN_PIO, + point_bilinear_vec, + IM_NUMBER( point_bilinear_args ), + point_bilinear_args +}; + +/* Call im_cmulnorm via arg vector. + */ +static int +cmulnorm_vec( im_object *argv ) +{ + return( im_cmulnorm( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_cmulnorm. + */ +static im_function cmulnorm_desc = { + "im_cmulnorm", /* Name */ + N_( "multiply two complex images, normalising output" ), + IM_FN_PIO, /* Flags */ + cmulnorm_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_deviate via arg vector. + */ +static int +deviate_vec( im_object *argv ) +{ + double f; + + if( im_deviate( argv[0], &f ) ) + return( -1 ); + + *((double *) argv[1]) = f; + return( 0 ); +} + +/* Description of im_deviate. + */ +static im_function deviate_desc = { + "im_deviate", /* Name */ + N_( "standard deviation of image" ), /* Description */ + IM_FN_PIO, /* Flags */ + deviate_vec, /* Dispatch function */ + IM_NUMBER( image_in_num_out ), /* Size of arg list */ + image_in_num_out /* Arg list */ +}; + +/* Call im_exp10tra via arg vector. + */ +static int +exp10tra_vec( im_object *argv ) +{ + return( im_exp10tra( argv[0], argv[1] ) ); +} + +/* Description of im_exp10tra. + */ +static im_function exp10tra_desc = { + "im_exp10tra", /* Name */ + N_( "10^pel of image" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + exp10tra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_exptra via arg vector. + */ +static int +exptra_vec( im_object *argv ) +{ + return( im_exptra( argv[0], argv[1] ) ); +} + +/* Description of im_exptra. + */ +static im_function exptra_desc = { + "im_exptra", /* Name */ + N_( "e^pel of image" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + exptra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args for im_powtra(). + */ +static im_arg_desc powtra_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "x" ) +}; + +/* Call im_expntra via arg vector. + */ +static int +expntra_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + + return( im_expntra( argv[0], argv[1], a ) ); +} + +/* Description of im_expntra. + */ +static im_function expntra_desc = { + "im_expntra", /* Name */ + N_( "x^pel of image" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + expntra_vec, /* Dispatch function */ + IM_NUMBER( powtra_args ), /* Size of arg list */ + powtra_args /* Arg list */ +}; + +/* Args for im_expntra_vec(). + */ +static im_arg_desc expntra_vec_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLEVEC( "v" ) +}; + +/* Call im_expntra_vec() via arg vector. + */ +static int +expntra_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_expntra_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_expntra_vec. + */ +static im_function expntra_vec_desc = { + "im_expntra_vec", /* Name */ + N_( "[x,y,z]^pel of image" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + expntra_vec_vec, /* Dispatch function */ + IM_NUMBER( expntra_vec_args ), /* Size of arg list */ + expntra_vec_args /* Arg list */ +}; + +/* Four images in, one out. + */ +static im_arg_desc fav4_args[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_INPUT_IMAGE( "in3" ), + IM_INPUT_IMAGE( "in4" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_fav4 via arg vector. + */ +static int +fav4_vec( im_object *argv ) +{ + IMAGE *buf[4]; + + buf[0] = argv[0]; + buf[1] = argv[1]; + buf[2] = argv[2]; + buf[3] = argv[3]; + + return( im_fav4( &buf[0], argv[4] ) ); +} + +/* Description of im_fav4. + */ +static im_function fav4_desc = { + "im_fav4", /* Name */ + N_( "average of 4 images" ), + 0, /* Flags */ + fav4_vec, /* Dispatch function */ + IM_NUMBER( fav4_args ), /* Size of arg list */ + fav4_args /* Arg list */ +}; + +/* Call im_divide via arg vector. + */ +static int +divide_vec( im_object *argv ) +{ + return( im_divide( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_divide. + */ +static im_function divide_desc = { + "im_divide", /* Name */ + N_( "divide two images" ), + IM_FN_PIO, /* Flags */ + divide_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Args for im_gadd(). + */ +static im_arg_desc gadd_args[] = { + IM_INPUT_DOUBLE( "a" ), + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_DOUBLE( "b" ), + IM_INPUT_IMAGE( "in2" ), + IM_INPUT_DOUBLE( "c" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_gadd() via arg vector. + */ +static int +gadd_vec( im_object *argv ) +{ + double a = *((double *) argv[0]); + double b = *((double *) argv[2]); + double c = *((double *) argv[4]); + + return( im_gadd( a, argv[1], b, argv[3], c, argv[5] ) ); +} + +/* Description of im_gadd(). + */ +static im_function gadd_desc = { + "im_gadd", /* Name */ + N_( "calculate a*in1 + b*in2 + c = outfile" ), + 0, /* Flags */ + gadd_vec, /* Dispatch function */ + IM_NUMBER( gadd_args ), /* Size of arg list */ + gadd_args /* Arg list */ +}; + +/* Call im_invert via arg vector. + */ +static int +invert_vec( im_object *argv ) +{ + return( im_invert( argv[0], argv[1] ) ); +} + +/* Description of im_invert. + */ +static im_function invert_desc = { + "im_invert", /* Name */ + N_( "photographic negative" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + invert_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args for im_lintra(). + */ +static im_arg_desc lintra_args[] = { + IM_INPUT_DOUBLE( "a" ), + IM_INPUT_IMAGE( "in" ), + IM_INPUT_DOUBLE( "b" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_lintra() via arg vector. + */ +static int +lintra_vec( im_object *argv ) +{ + double a = *((double *) argv[0]); + double b = *((double *) argv[2]); + + return( im_lintra( a, argv[1], b, argv[3] ) ); +} + +/* Description of im_lintra(). + */ +static im_function lintra_desc = { + "im_lintra", /* Name */ + N_( "calculate a*in + b = outfile" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + lintra_vec, /* Dispatch function */ + IM_NUMBER( lintra_args ), /* Size of arg list */ + lintra_args /* Arg list */ +}; + +/* Args for im_lintra_vec(). + */ +static im_arg_desc lintra_vec_args[] = { + IM_INPUT_DOUBLEVEC( "a" ), + IM_INPUT_IMAGE( "in" ), + IM_INPUT_DOUBLEVEC( "b" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_lintra_vec() via arg vector. + */ +static int +lintra_vec_vec( im_object *argv ) +{ + im_doublevec_object *dva = (im_doublevec_object *) argv[0]; + im_doublevec_object *dvb = (im_doublevec_object *) argv[2]; + + if( dva->n != dvb->n ) { + im_error( "im_lintra_vec", _( "vectors not same length" ) ); + return( -1 ); + } + + return( im_lintra_vec( dva->n, dva->vec, argv[1], dvb->vec, argv[3] ) ); +} + +/* Description of im_lintra_vec(). + */ +static im_function lintra_vec_desc = { + "im_lintra_vec", /* Name */ + N_( "calculate a*in + b -> out, a and b vectors" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + lintra_vec_vec, /* Dispatch function */ + IM_NUMBER( lintra_vec_args ), /* Size of arg list */ + lintra_vec_args /* Arg list */ +}; + +/* Args for im_litecor(). + */ +static im_arg_desc litecor_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_IMAGE( "white" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "clip" ), + IM_INPUT_DOUBLE( "factor" ) +}; + +/* Call im_litecor() via arg vector. + */ +static int +litecor_vec( im_object *argv ) +{ + int clip = *((int *) argv[3]); + double factor = *((double *) argv[4]); + + return( im_litecor( argv[0], argv[1], argv[2], clip, factor ) ); +} + +/* Description of im_litecor(). + */ +static im_function litecor_desc = { + "im_litecor", /* Name */ + N_( "calculate max(white)*factor*(in/white), if clip == 1" ), + 0, /* Flags */ + litecor_vec, /* Dispatch function */ + IM_NUMBER( litecor_args ), /* Size of arg list */ + litecor_args /* Arg list */ +}; + +/* Call im_log10tra via arg vector. + */ +static int +log10tra_vec( im_object *argv ) +{ + return( im_log10tra( argv[0], argv[1] ) ); +} + +/* Description of im_log10tra. + */ +static im_function log10tra_desc = { + "im_log10tra", /* Name */ + N_( "log10 of image" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + log10tra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_logtra via arg vector. + */ +static int +logtra_vec( im_object *argv ) +{ + return( im_logtra( argv[0], argv[1] ) ); +} + +/* Description of im_logtra. + */ +static im_function logtra_desc = { + "im_logtra", /* Name */ + N_( "ln of image" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + logtra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_tantra via arg vector. + */ +static int +tantra_vec( im_object *argv ) +{ + return( im_tantra( argv[0], argv[1] ) ); +} + +/* Description of im_tantra. + */ +static im_function tantra_desc = { + "im_tantra", /* Name */ + N_( "tan of image (angles in degrees)" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + tantra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_atantra via arg vector. + */ +static int +atantra_vec( im_object *argv ) +{ + return( im_atantra( argv[0], argv[1] ) ); +} + +/* Description of im_atantra. + */ +static im_function atantra_desc = { + "im_atantra", /* Name */ + N_( "atan of image (result in degrees)" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + atantra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_costra via arg vector. + */ +static int +costra_vec( im_object *argv ) +{ + return( im_costra( argv[0], argv[1] ) ); +} + +/* Description of im_costra. + */ +static im_function costra_desc = { + "im_costra", /* Name */ + N_( "cos of image (angles in degrees)" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + costra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_acostra via arg vector. + */ +static int +acostra_vec( im_object *argv ) +{ + return( im_acostra( argv[0], argv[1] ) ); +} + +/* Description of im_acostra. + */ +static im_function acostra_desc = { + "im_acostra", /* Name */ + N_( "acos of image (result in degrees)" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + acostra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_ceil via arg vector. + */ +static int +ceil_vec( im_object *argv ) +{ + return( im_ceil( argv[0], argv[1] ) ); +} + +/* Description of im_ceil. + */ +static im_function ceil_desc = { + "im_ceil", /* Name */ + N_( "round to smallest integal value not less than" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + ceil_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_floor via arg vector. + */ +static int +floor_vec( im_object *argv ) +{ + return( im_floor( argv[0], argv[1] ) ); +} + +/* Description of im_floor. + */ +static im_function floor_desc = { + "im_floor", /* Name */ + N_( "round to largest integal value not greater than" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + floor_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_rint via arg vector. + */ +static int +rint_vec( im_object *argv ) +{ + return( im_rint( argv[0], argv[1] ) ); +} + +/* Description of im_rint. + */ +static im_function rint_desc = { + "im_rint", /* Name */ + N_( "round to nearest integal value" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + rint_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_sintra via arg vector. + */ +static int +sintra_vec( im_object *argv ) +{ + return( im_sintra( argv[0], argv[1] ) ); +} + +/* Description of im_sintra. + */ +static im_function sintra_desc = { + "im_sintra", /* Name */ + N_( "sin of image (angles in degrees)" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + sintra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_bandmean via arg vector. + */ +static int +bandmean_vec( im_object *argv ) +{ + return( im_bandmean( argv[0], argv[1] ) ); +} + +/* Description of im_bandmean. + */ +static im_function bandmean_desc = { + "im_bandmean", /* Name */ + N_( "average image bands" ), + IM_FN_PIO, /* Flags */ + bandmean_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_sign via arg vector. + */ +static int +sign_vec( im_object *argv ) +{ + return( im_sign( argv[0], argv[1] ) ); +} + +/* Description of im_sign. + */ +static im_function sign_desc = { + "im_sign", /* Name */ + N_( "unit vector in direction of value" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + sign_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_asintra via arg vector. + */ +static int +asintra_vec( im_object *argv ) +{ + return( im_asintra( argv[0], argv[1] ) ); +} + +/* Description of im_asintra. + */ +static im_function asintra_desc = { + "im_asintra", /* Name */ + N_( "asin of image (result in degrees)" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + asintra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_max via arg vector. + */ +static int +max_vec( im_object *argv ) +{ + double f; + + if( im_max( argv[0], &f ) ) + return( -1 ); + *((double *) argv[1]) = f; + + return( 0 ); +} + +/* Description of im_max. + */ +static im_function max_desc = { + "im_max", /* Name */ + N_( "maximum value of image" ), /* Description */ + IM_FN_PIO, /* Flags */ + max_vec, /* Dispatch function */ + IM_NUMBER( image_in_num_out ), /* Size of arg list */ + image_in_num_out /* Arg list */ +}; + +/* Args for maxpos (and minpos). + */ +static im_arg_desc maxpos_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_COMPLEX( "position" ) +}; + +/* Call im_maxpos via arg vector. + */ +static int +maxpos_vec( im_object *argv ) +{ + double f; + int x, y; + + if( im_maxpos( argv[0], &x, &y, &f ) ) + return( -1 ); + + ((double *) argv[1])[0] = x; + ((double *) argv[1])[1] = y; + + return( 0 ); +} + +/* Description of im_maxpos. + */ +static im_function maxpos_desc = { + "im_maxpos", /* Name */ + N_( "position of maximum value of image" ), + 0, /* Flags */ + maxpos_vec, /* Dispatch function */ + IM_NUMBER( maxpos_args ), /* Size of arg list */ + maxpos_args /* Arg list */ +}; + +/* Args to im_maxpos_avg. + */ +static im_arg_desc maxpos_avg_args[] = { + IM_INPUT_IMAGE ("in"), + IM_OUTPUT_DOUBLE("x"), + IM_OUTPUT_DOUBLE("y"), + IM_OUTPUT_DOUBLE("out") +}; + +/* Call im_maxpos_avg via arg vector. + */ +static int +maxpos_avg_vec( im_object *argv ) +{ + return im_maxpos_avg( argv[0], argv[1], argv[2], argv[3] ); +} + +/* Description of im_maxpos_avg. + */ +static im_function maxpos_avg_desc = { + "im_maxpos_avg", + "position of maximum value of image, averaging in case of draw", + IM_FN_PIO, + maxpos_avg_vec, + IM_NUMBER( maxpos_avg_args ), + maxpos_avg_args +}; + +/* Args for measure. + */ +static im_arg_desc measure_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_DMASK( "mask" ), + IM_INPUT_INT( "x" ), + IM_INPUT_INT( "y" ), + IM_INPUT_INT( "w" ), + IM_INPUT_INT( "h" ), + IM_INPUT_INT( "h_patches" ), + IM_INPUT_INT( "v_patches" ) +}; + +/* Call im_measure via arg vector. + */ +static int +measure_vec( im_object *argv ) +{ + IMAGE_BOX box; + int h, v, i; + int *sel; + int nsel; + im_mask_object *mo = argv[1]; + + box.xstart = *((int *) argv[2]); + box.ystart = *((int *) argv[3]); + box.xsize = *((int *) argv[4]); + box.ysize = *((int *) argv[5]); + box.chsel = 0; + + h = *((int *) argv[6]); + v = *((int *) argv[7]); + + nsel = h * v; + if( !(sel = IM_ARRAY( NULL, nsel, int )) ) + return( -1 ); + for( i = 0; i < nsel; i++ ) + sel[i] = i + 1; + + if( !(mo->mask = + im_measure( argv[0], &box, h, v, sel, nsel, mo->name )) ) { + im_free( sel ); + return( -1 ); + } + im_free( sel ); + + return( 0 ); +} + +/* Description of im_measure. + */ +static im_function measure_desc = { + "im_measure", /* Name */ + N_( "measure averages of a grid of patches" ), + IM_FN_PIO, /* Flags */ + measure_vec, /* Dispatch function */ + IM_NUMBER( measure_args ), /* Size of arg list */ + measure_args /* Arg list */ +}; + +/* Call im_min via arg vector. + */ +static int +min_vec( im_object *argv ) +{ + double f; + + if( im_min( argv[0], &f ) ) + return( -1 ); + *((double *) argv[1]) = f; + + return( 0 ); +} + +/* Description of im_min. + */ +static im_function min_desc = { + "im_min", /* Name */ + N_( "minimum value of image" ), /* Description */ + IM_FN_PIO, /* Flags */ + min_vec, /* Dispatch function */ + IM_NUMBER( image_in_num_out ), /* Size of arg list */ + image_in_num_out /* Arg list */ +}; + +/* Call im_minpos via arg vector. + */ +static int +minpos_vec( im_object *argv ) +{ + double f; + int x, y; + + if( im_minpos( argv[0], &x, &y, &f ) ) + return( -1 ); + + ((double *) argv[1])[0] = x; + ((double *) argv[1])[1] = y; + + return( 0 ); +} + +/* Description of im_minpos. + */ +static im_function minpos_desc = { + "im_minpos", /* Name */ + N_( "position of minimum value of image" ), + 0, /* Flags */ + minpos_vec, /* Dispatch function */ + IM_NUMBER( maxpos_args ), /* Size of arg list */ + maxpos_args /* Arg list */ +}; + +/* Call im_remainder via arg vector. + */ +static int +remainder_vec( im_object *argv ) +{ + return( im_remainder( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_remainder. + */ +static im_function remainder_desc = { + "im_remainder", /* Name */ + N_( "remainder after integer division" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + remainder_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_remainderconst via arg vector. + */ +static int +remainderconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_remainderconst( argv[0], argv[1], c ) ); +} + +/* Args for im_remainderconst(). + */ +static im_arg_desc remainderconst_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "x" ) +}; + +/* Description of im_remainderconst. + */ +static im_function remainderconst_desc = { + "im_remainderconst", /* Name */ + N_( "remainder after integer division by a constant" ),/* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + remainderconst_vec, /* Dispatch function */ + IM_NUMBER( remainderconst_args ), /* Size of arg list */ + remainderconst_args /* Arg list */ +}; + +/* Call im_remainderconst_vec via arg vector. + */ +static int +remainderconst_vec_vec( im_object *argv ) +{ + im_doublevec_object *dv = (im_doublevec_object *) argv[2]; + + return( im_remainderconst_vec( argv[0], argv[1], dv->n, dv->vec ) ); +} + +/* Args for im_remainderconst_vec(). + */ +static im_arg_desc remainderconst_vec_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLEVEC( "x" ) +}; + +/* Description of im_remainderconst_vec. + */ +static im_function remainderconst_vec_desc = { + "im_remainderconst_vec", /* Name */ + N_( "remainder after integer division by a vector of constants" ), + /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + remainderconst_vec_vec, /* Dispatch function */ + IM_NUMBER( remainderconst_vec_args ), /* Size of arg list */ + remainderconst_vec_args /* Arg list */ +}; + +/* Call im_multiply via arg vector. + */ +static int +multiply_vec( im_object *argv ) +{ + return( im_multiply( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_multiply. + */ +static im_function multiply_desc = { + "im_multiply", /* Name */ + N_( "multiply two images" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + multiply_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_powtra() via arg vector. + */ +static int +powtra_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + + return( im_powtra( argv[0], argv[1], a ) ); +} + +/* Description of im_powtra(). + */ +static im_function powtra_desc = { + "im_powtra", /* Name */ + N_( "pel^x ofbuildimage" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + powtra_vec, /* Dispatch function */ + IM_NUMBER( powtra_args ), /* Size of arg list */ + powtra_args /* Arg list */ +}; + +/* Call im_powtra_vec() via arg vector. + */ +static int +powtra_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_powtra_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_powtra_vec(). + */ +static im_function powtra_vec_desc = { + "im_powtra_vec", /* Name */ + N_( "pel^[x,y,z] of image" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + powtra_vec_vec, /* Dispatch function */ + IM_NUMBER( expntra_vec_args ), /* Size of arg list */ + expntra_vec_args /* Arg list */ +}; + +/* Args for im_stats. + */ +static im_arg_desc stats_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_DMASK_STATS( "statistics" ) +}; + +/* Call im_stats() via arg vector. + */ +static int +stats_vec( im_object *argv ) +{ + im_mask_object *mo = argv[1]; + + if( !(mo->mask = im_stats( argv[0] )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_stats(). + */ +static im_function stats_desc = { + "im_stats", /* Name */ + N_( "many image statistics in one pass" ), + IM_FN_PIO, /* Flags */ + stats_vec, /* Dispatch function */ + IM_NUMBER( stats_args ), /* Size of arg list */ + stats_args /* Arg list */ +}; + +/* Call im_subtract via arg vector. + */ +static int +subtract_vec( im_object *argv ) +{ + return( im_subtract( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_subtract. + */ +static im_function subtract_desc = { + "im_subtract", /* Name */ + N_( "subtract two images" ), /* Description */ + IM_FN_PIO, /* Flags */ + subtract_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Args for im_linreg. + */ +static im_arg_desc linreg_args[] = { + IM_INPUT_IMAGEVEC( "ins" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLEVEC( "xs" ) +}; + +/* Call im_linreg() via arg vector. + */ +static int +linreg_vec( im_object *argv ) +{ +#define FUNCTION_NAME "im_linreg_vec" + im_imagevec_object *ins_vec= (im_imagevec_object*) argv[0]; + im_doublevec_object *xs_vec= (im_doublevec_object*) argv[2]; + IMAGE *out= (IMAGE*) argv[1]; + IMAGE **ins= IM_ARRAY( out, ins_vec-> n + 1, IMAGE* ); + int i; + + if( ! ins ) + return -1; + + for( i= 0; i < ins_vec-> n; ++i ) + ins[ i ]= ins_vec-> vec[ i ]; + + ins[ ins_vec-> n ]= NULL; + + if( xs_vec-> n != ins_vec-> n ){ + im_error( FUNCTION_NAME, "image vector and x vector differ in length" ); + return -1; + } + return im_linreg( ins, out, xs_vec-> vec ); + +#undef FUNCTION_NAME +} + +/* Description of im_linreg(). + */ +static im_function linreg_desc = { + "im_linreg", /* Name */ + N_( "pixelwise linear regression" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + linreg_vec, /* Dispatch function */ + IM_NUMBER( linreg_args ), /* Size of arg list */ + linreg_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *arith_list[] = { + &abs_desc, + &acostra_desc, + &add_desc, + &asintra_desc, + &atantra_desc, + &avg_desc, + &point_bilinear_desc, + &bandmean_desc, + &ceil_desc, + &cmulnorm_desc, + &costra_desc, + &deviate_desc, + ÷_desc, + &exp10tra_desc, + &expntra_desc, + &expntra_vec_desc, + &exptra_desc, + &fav4_desc, + &floor_desc, + &gadd_desc, + &invert_desc, + &lintra_desc, + &linreg_desc, + &lintra_vec_desc, + &litecor_desc, + &log10tra_desc, + &logtra_desc, + &max_desc, + &maxpos_desc, + &maxpos_avg_desc, + &measure_desc, + &min_desc, + &minpos_desc, + &multiply_desc, + &powtra_desc, + &powtra_vec_desc, + &remainder_desc, + &remainderconst_desc, + &remainderconst_vec_desc, + &rint_desc, + &sign_desc, + &sintra_desc, + &stats_desc, + &subtract_desc, + &tantra_desc +}; + +/* Package of functions. + */ +im_package im__arithmetic = { + "arithmetic", + IM_NUMBER( arith_list ), + arith_list +}; diff --git a/libsrc/arithmetic/im_abs.c b/libsrc/arithmetic/im_abs.c new file mode 100644 index 00000000..e9dabcdc --- /dev/null +++ b/libsrc/arithmetic/im_abs.c @@ -0,0 +1,241 @@ +/* @(#) Find the absolute value of an image. Copy for UNSIGNED types, negate + * @(#) for int types, fabs() for float types, and calculate modulus for + * @(#) complex types. + * @(#) + * @(#) int + * @(#) im_abs( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 5/5/93 J.Cupitt + * - adapted from im_lintra to work with partial images + * - complex and signed support added + * 30/6/93 JC + * - adapted for partial v2 + * - ANSI conversion + * - spe29873r6k3h()**!@lling errors removed + * 9/2/95 JC + * - adapted for im_wrap... + * 20/6/02 JC + * - tiny speed up + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Integer abs operation: just test and negate. + */ +#define intabs(TYPE) \ + { \ + TYPE *p = (TYPE *) in; \ + TYPE *q = (TYPE *) out; \ + \ + for( x = 0; x < sz; x++ ) { \ + TYPE v = p[x]; \ + \ + if( v < 0 ) \ + q[x] = 0 - v; \ + else \ + q[x] = v; \ + } \ + } + +/* Float abs operation: call fabs(). + */ +#define floatabs(TYPE)\ + {\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = fabs( p[x] );\ + } + +/* Complex abs operation: calculate modulus. + */ +#define complexabs(TYPE)\ + {\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double rp = p[0];\ + double ip = p[1];\ + \ + p += 2;\ + q[x] = sqrt( rp * rp + ip * ip );\ + }\ + } + +/* Abs a buffer of PELs. + */ +static void +abs_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Abs all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: +#ifdef HAVE_LIBOIL + oil_abs_u8_s8( (uint8_t *) out, sizeof( uint8_t ), + (int8_t *) in, sizeof( int8_t ), sz ); +#else /*!HAVE_LIBOIL*/ + intabs( signed char ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_SHORT: +#ifdef HAVE_LIBOIL + oil_abs_u16_s16( (uint16_t *) out, sizeof( uint16_t ), + (int16_t *) in, sizeof( int16_t ), sz ); +#else /*!HAVE_LIBOIL*/ + intabs( signed short ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_INT: +#ifdef HAVE_LIBOIL + oil_abs_u32_s32( (uint32_t *) out, sizeof( uint32_t ), + (int32_t *) in, sizeof( int32_t ), sz ); +#else /*!HAVE_LIBOIL*/ + intabs( signed int ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + oil_abs_f32_f32( (float *) out, sizeof( float ), + (float *) in, sizeof( float ), sz ); +#else /*!HAVE_LIBOIL*/ + floatabs( float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: +#ifdef HAVE_LIBOIL + oil_abs_f64_f64( (double *) out, sizeof( double ), + (double *) in, sizeof( double ), sz ); +#else /*!HAVE_LIBOIL*/ + floatabs( float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_COMPLEX: complexabs( float ); break; + case IM_BANDFMT_DPCOMPLEX: complexabs( double ); break; + + default: + assert( 0 ); + } +} + +/* Abs of image. + */ +int +im_abs( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_abs", _( "not uncoded" ) ); + return( -1 ); + } + + /* Is this one of the unsigned types? Degenerate to im_copy() if it + * is. + */ + if( im_isuint( in ) ) + return( im_copy( in, out ) ); + + /* Prepare output header. Output type == input type, except for + * complex. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_INT: + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + /* No action. + */ + break; + + case IM_BANDFMT_COMPLEX: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_DPCOMPLEX: + out->Bbits = IM_BBITS_DOUBLE; + out->BandFmt = IM_BANDFMT_DOUBLE; + break; + + default: + im_error( "im_abs", _( "unknown input type" ) ); + return( -1 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) abs_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_add.c b/libsrc/arithmetic/im_add.c new file mode 100644 index 00000000..df9c31f8 --- /dev/null +++ b/libsrc/arithmetic/im_add.c @@ -0,0 +1,302 @@ +/* @(#) Add two vasari images + * @(#) Function im_add() assumes that the both input files + * @(#) are either memory mapped or in a buffer. + * @(#) Images must have the same no of bands and can be of any type + * @(#) No check for overflow is carried out. + * @(#) + * @(#) int + * @(#) im_add(in1, in2, out) + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 29/4/93 J.Cupitt + * - now works for partial images + * 1/7/93 JC + * - adapted for partial v2 + * 9/5/95 JC + * - simplified: now just handles 10 cases (instead of 50), using + * im_clip2*() to help + * - now uses im_wrapmany() rather than im_generate() + * 31/5/96 JC + * - SWAP() removed, *p++ removed + * 27/9/04 + * - im__cast_and_call() now matches bands as well + * - ... so 1 band + 4 band image -> 4 band image + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Complex add. + */ +#define cloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double rp1 = p1[0];\ + double ip1 = p1[1];\ + \ + double rp2 = p2[0];\ + double ip2 = p2[1];\ + \ + p1 += 2;\ + p2 += 2;\ + \ + q[0] = rp1 + rp2;\ + q[1] = ip1 + ip2;\ + \ + q += 2;\ + }\ +} + +/* Real add. + */ +#define rloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = p1[x] + p2[x];\ +} + +static void +add_buffer( PEL **in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Add all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: rloop( signed char ); break; + case IM_BANDFMT_UCHAR: rloop( unsigned char ); break; + case IM_BANDFMT_SHORT: rloop( signed short ); break; + case IM_BANDFMT_USHORT: rloop( unsigned short ); break; + case IM_BANDFMT_INT: rloop( signed int ); break; + case IM_BANDFMT_UINT: rloop( unsigned int ); break; + + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + oil_add_f32( (float *) out, + (float *) in[0], (float *) in[1], sz ); +#else /*!HAVE_LIBOIL*/ + rloop( float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: rloop( double ); break; + case IM_BANDFMT_COMPLEX: cloop( float ); break; + case IM_BANDFMT_DPCOMPLEX: cloop( double ); break; + + default: + assert( 0 ); + } +} + +/* Save a bit of typing. + */ +#define UC IM_BANDFMT_UCHAR +#define C IM_BANDFMT_CHAR +#define US IM_BANDFMT_USHORT +#define S IM_BANDFMT_SHORT +#define UI IM_BANDFMT_UINT +#define I IM_BANDFMT_INT +#define F IM_BANDFMT_FLOAT +#define M IM_BANDFMT_COMPLEX +#define D IM_BANDFMT_DOUBLE +#define DM IM_BANDFMT_DPCOMPLEX + +/* Type conversions for two integer inputs. Rules for float and complex + * encoded with ifs. We are sign and value preserving. + */ +static int iformat[6][6] = { + /* UC C US S UI I */ +/* UC */ { US, S, UI, I, UI, I }, +/* C */ { S, S, I, I, I, I }, +/* US */ { UI, I, UI, I, UI, I }, +/* S */ { I, I, I, I, I, I }, +/* UI */ { UI, I, UI, I, UI, I }, +/* I */ { I, I, I, I, I, I } +}; + +/* Make an n-band image. Input 1 or n bands. + */ +static int +im__bandup( IMAGE *in, IMAGE *out, int n ) +{ + IMAGE *bands[256]; + int i; + + if( in->Bands == n ) + return( im_copy( in, out ) ); + if( in->Bands != 1 ) { + im_error( "im__bandup", _( "not one band or %d bands" ), n ); + return( -1 ); + } + if( n > 256 || n < 1 ) { + im_error( "im__bandup", _( "bad bands" ) ); + return( -1 ); + } + + for( i = 0; i < n; i++ ) + bands[i] = in; + + return( im_gbandjoin( bands, out, n ) ); +} + +/* Convert in1 and in2 to the bands and type of out, and call the function. + * Also used by subtract, multiply, divide, etc. + */ +int +im__cast_and_call( IMAGE *in1, IMAGE *in2, IMAGE *out, + im_wrapmany_fn fn, void *a ) +{ + IMAGE *t[5]; + + if( im_open_local_array( out, t, 4, "type cast:1", "p" ) ) + return( -1 ); + + /* Make sure bbits is set correctly. + */ + out->Bbits = im_bits_of_fmt( out->BandFmt ); + + /* Cast our input images up to the same type as the output. + */ + if( im_clip2fmt( in1, t[0], out->BandFmt ) || + im_clip2fmt( in2, t[1], out->BandFmt ) ) + return( -1 ); + + /* Force bands up to the same as out. + */ + if( im__bandup( t[0], t[2], out->Bands ) || + im__bandup( t[1], t[3], out->Bands ) ) + return( -1 ); + + /* And add! + */ + t[4] = NULL; + if( im_wrapmany( t + 2, out, fn, out, a ) ) + return( -1 ); + + return( 0 ); +} + +int +im_add( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + /* Basic checks. + */ + if( im_piocheck( in1, out ) || im_pincheck( in2 ) ) + return( -1 ); + if( in1->Bands != in2->Bands && + (in1->Bands != 1 && in2->Bands != 1) ) { + im_error( "im_add", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_add", _( "not uncoded" ) ); + return( -1 ); + } + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + + /* What number of bands will we write? + */ + out->Bands = IM_MAX( in1->Bands, in2->Bands ); + + /* What output type will we write? int, float or complex. + */ + if( im_iscomplex( in1 ) || im_iscomplex( in2 ) ) { + /* What kind of complex? + */ + if( in1->BandFmt == IM_BANDFMT_DPCOMPLEX || + in2->BandFmt == IM_BANDFMT_DPCOMPLEX ) + /* Output will be DPCOMPLEX. + */ + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + else + out->BandFmt = IM_BANDFMT_COMPLEX; + + } + else if( im_isfloat( in1 ) || im_isfloat( in2 ) ) { + /* What kind of float? + */ + if( in1->BandFmt == IM_BANDFMT_DOUBLE || + in2->BandFmt == IM_BANDFMT_DOUBLE ) + out->BandFmt = IM_BANDFMT_DOUBLE; + else + out->BandFmt = IM_BANDFMT_FLOAT; + } + else + /* Must be int+int -> int. + */ + out->BandFmt = iformat[in1->BandFmt][in2->BandFmt]; + + /* And process! + */ + if( im__cast_and_call( in1, in2, out, + (im_wrapmany_fn) add_buffer, NULL ) ) + return( -1 ); + + /* Success! + */ + return( 0 ); +} diff --git a/libsrc/arithmetic/im_avg.c b/libsrc/arithmetic/im_avg.c new file mode 100644 index 00000000..960fd9c7 --- /dev/null +++ b/libsrc/arithmetic/im_avg.c @@ -0,0 +1,203 @@ +/* @(#) Find the average of an image. Takes any non-complex image format, + * @(#) returns a double. Finds the average of all bands. + * @(#) + * @(#) int + * @(#) im_avg( im, out ) + * @(#) IMAGE *im; + * @(#) double *out; + * @(#) + * @(#) Returns 0 on success and -1 on error. + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/08/1990 + * Modified on: + * 5/5/93 JC + * - now does partial images + * - less likely to overflow + * 1/7/93 JC + * - adapted for partial v2 + * - ANSI C + * 21/2/95 JC + * - modernised again + * 11/5/95 JC + * - oops! return( NULL ) in im_avg(), instead of return( -1 ) + * 20/6/95 JC + * - now returns double + * 13/1/05 + * - use 64 bit arithmetic + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Start function: allocate space for a double in which we can accululate the + * sum. + */ +static void * +start_fn( IMAGE *out ) +{ + double *tmp; + + if( !(tmp = IM_ARRAY( out, 1, double )) ) + return( NULL ); + *tmp = 0.0; + + return( (void *) tmp ); +} + +/* Stop function. Add this little sum to the main sum. + */ +static int +stop_fn( double *tmp, double *sum ) +{ + *sum += *tmp; + + return( 0 ); +} + +/* Loop over region, accumulating a sum in *tmp. + */ +static int +scan_fn( REGION *reg, double *tmp ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( reg ); + double sum = 0.0; + int x, y; + +/* Sum pels in this section. + */ +#define loop(TYPE) \ + { TYPE *p;\ + \ + for( y = to; y < bo; y++ ) { \ + p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( x = 0; x < sz; x++ ) \ + sum += *p++;\ + }\ + } + + /* Now generate code for all types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop(unsigned char); break; + case IM_BANDFMT_CHAR: loop(signed char); break; + case IM_BANDFMT_USHORT: loop(unsigned short); break; + case IM_BANDFMT_SHORT: loop(signed short); break; + case IM_BANDFMT_UINT: loop(unsigned int); break; + case IM_BANDFMT_INT: loop(signed int); break; + case IM_BANDFMT_FLOAT: loop(float); break; + + case IM_BANDFMT_DOUBLE: +#ifdef HAVE_LIBOIL + for( y = to; y < bo; y++ ) { + double *p = (double *) IM_REGION_ADDR( reg, le, y ); + double t; + + oil_sum_f64( &t, p, sizeof( double ), sz ); + + sum += t; + } +#else /*!HAVE_LIBOIL*/ + loop(double); +#endif /*HAVE_LIBOIL*/ + break; + + default: + assert( 0 ); + } + + /* Add to sum for this sequence. + */ + *tmp += sum; + + return( 0 ); +} + +/* Find the average of an image. + */ +int +im_avg( IMAGE *in, double *out ) +{ + double sum = 0.0; + gint64 vals, pels; + + /* Check our args. + */ + if( im_pincheck( in ) ) + return( -1 ); + if( im_iscomplex( in ) ) { + im_error( "im_avg", _( "bad input type" ) ); + return( -1 ); + } + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_avg", _( "not uncoded" ) ); + return( -1 ); + } + + /* Loop over input, summing pixels. + */ + if( im_iterate( in, start_fn, scan_fn, stop_fn, &sum, NULL ) ) + return( -1 ); + + /* Calculate and return average. + */ + pels = (gint64) in->Xsize * in->Ysize; + vals = pels * in->Bands; + *out = sum / vals; + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_bandmean.c b/libsrc/arithmetic/im_bandmean.c new file mode 100644 index 00000000..721eb6eb --- /dev/null +++ b/libsrc/arithmetic/im_bandmean.c @@ -0,0 +1,161 @@ +/* @(#) Average the bands in an image. + * @(#) + * @(#) int + * @(#) im_bandmean(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Author: Simon Goodall + * Written on: 17/7/07 + * 17/7/07 JC + * - hacked about a bit + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Int types. Round, keep sum in a larger variable. + */ +#define ILOOP( TYPE, STYPE ) { \ + TYPE *p1 = (TYPE *) p; \ + TYPE *q1 = (TYPE *) q; \ + \ + for( i = 0; i < n; i++ ) { \ + STYPE sum; \ + \ + sum = 0; \ + for( j = 0; j < b; j++ ) \ + sum += p1[j]; \ + *q1++ = sum > 0 ? (sum + b / 2) / b : (sum - b / 2) / b; \ + p1 += b; \ + } \ +} + +/* Float loop. No rounding, sum in same container. + */ +#define FLOOP( TYPE ) { \ + TYPE *p1 = (TYPE *) p; \ + TYPE *q1 = (TYPE *) q; \ + \ + for( i = 0; i < n; i++ ) { \ + TYPE sum; \ + \ + sum = 0; \ + for( j = 0; j < b; j++ ) \ + sum += p1[j]; \ + *q1++ = sum / b; \ + p1 += b; \ + } \ +} + +/* Complex loop. Mean reals and imaginaries separately. + */ +#define CLOOP( TYPE ) { \ + TYPE *p1 = (TYPE *) p; \ + TYPE *q1 = (TYPE *) q; \ + \ + for( i = 0; i < n * 2; i += 2 ) { \ + TYPE sum; \ + \ + sum = 0; \ + for( j = 0; j < b; j++ ) \ + sum += p1[j * 2]; \ + q1[0] = sum / b; \ + sum = 0; \ + for( j = 0; j < b; j++ ) \ + sum += p1[j * 2 + 1]; \ + q1[1] = sum / b; \ + p1 += b; \ + q1 += 2; \ + } \ +} + +static void +bandmean_buffer( PEL *p, PEL *q, int n, IMAGE *in ) +{ + int i, j; + const int b = in->Bands; + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: ILOOP( signed char, int ); break; + case IM_BANDFMT_UCHAR: ILOOP( unsigned char, unsigned int ); break; + case IM_BANDFMT_SHORT: ILOOP( signed short, int ); break; + case IM_BANDFMT_USHORT: ILOOP( unsigned short, unsigned int ); break; + case IM_BANDFMT_INT: ILOOP( signed int, int ); break; + case IM_BANDFMT_UINT: ILOOP( unsigned int, unsigned int ); break; + case IM_BANDFMT_FLOAT: FLOOP( float ); break; + case IM_BANDFMT_DOUBLE: FLOOP( double ); break; + case IM_BANDFMT_COMPLEX: CLOOP( float ); break; + case IM_BANDFMT_DPCOMPLEX: CLOOP( double ); break; + + default: + assert( 0 ); + } +} + +int +im_bandmean( IMAGE *in, IMAGE *out ) +{ + /* Check input params + */ + if( in->Bands == 1 ) + return( im_copy( in, out ) ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_bandmean", _( "uncoded multiband only" ) ); + return( -1 ); + } + + /* Prepare output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bands = 1; + out->Type = IM_TYPE_B_W; + + /* And process! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) bandmean_buffer, in, NULL ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/arithmetic/im_ceil.c b/libsrc/arithmetic/im_ceil.c new file mode 100644 index 00000000..9d85a6e9 --- /dev/null +++ b/libsrc/arithmetic/im_ceil.c @@ -0,0 +1,114 @@ +/* @(#) ceil() an image ... no promotion, so output type == input type + * @(#) + * @(#) int + * @(#) im_ceil( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 20/6/02 JC + * - adapted from im_abs() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define ceil_loop(TYPE)\ + {\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = ceil( p[x] );\ + } + +/* Ceil a buffer of PELs. + */ +static void +ceil_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_FLOAT: ceil_loop(float); break; + case IM_BANDFMT_DOUBLE: ceil_loop(double); break; + case IM_BANDFMT_COMPLEX: sz *= 2; ceil_loop(float); break; + case IM_BANDFMT_DPCOMPLEX: sz *= 2; ceil_loop(double); break; + + default: + assert( 0 ); + } +} + +/* Ceil of image. + */ +int +im_ceil( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_ceil", _( "not uncoded" ) ); + return( -1 ); + } + + /* Is this one of the int types? Degenerate to im_copy() if it + * is. + */ + if( im_isint( in ) ) + return( im_copy( in, out ) ); + + /* Output type == input type. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Generate! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) ceil_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_cmulnorm.c b/libsrc/arithmetic/im_cmulnorm.c new file mode 100644 index 00000000..22eefbb6 --- /dev/null +++ b/libsrc/arithmetic/im_cmulnorm.c @@ -0,0 +1,75 @@ +/* @(#) Multiplies two complex images. complex output is normalised to 1 + * @(#) Inputs can be complex double or complex float + * @(#) Result (double complex or float complex) depends on inputs + * @(#) Function im_cmulnorm() assumes that the both input files + * @(#) are either memory mapped or in a buffer. + * @(#) Images must have the same no of bands and must be complex + * @(#) No check for overflow is carried out. + * @(#) + * @(#) int im_cmulnorm(in1, in2, out) + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 15/4/97 JC + * - thrown away and redone in terms of im_multiply() + * 9/7/02 JC + * - im_sign() broken out, done in terms of that + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_cmulnorm( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_cmulnorm:1", "p" ); + + if( !t1 || im_multiply( in1, in2, t1 ) || im_sign( t1, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_costra.c b/libsrc/arithmetic/im_costra.c new file mode 100644 index 00000000..9330ab5b --- /dev/null +++ b/libsrc/arithmetic/im_costra.c @@ -0,0 +1,239 @@ +/* @(#) Find cos of any non-complex image. Output is always float for integer + * @(#) input and double for double input. All angles in degrees. + * @(#) + * @(#) int + * @(#) im_costra( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 5/5/93 JC + * - adapted from im_lintra to work with partial images + * - incorrect implementation of complex logs removed + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 24/2/95 JC + * - im_logtra() adapted to make im_costra() + * - adapted for im_wrapone() + * 26/1/96 JC + * - im_acostra() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define loop( IN, OUT )\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = cos( IM_RAD( (double) p[x] ) );\ + } + +/* cos a buffer of PELs. + */ +static void +costra_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Switch for all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: loop( signed char, float ); break; + case IM_BANDFMT_USHORT: loop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: loop( signed short, float ); break; + case IM_BANDFMT_UINT: loop( unsigned int, float ); break; + case IM_BANDFMT_INT: loop( signed int, float ); break; + case IM_BANDFMT_FLOAT: loop( float, float ); break; + case IM_BANDFMT_DOUBLE: loop( double, double ); break; + + default: + assert( 0 ); + } +} + +/* Cos transform. + */ +int +im_costra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_costra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_costra", _( "bad input type" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, (im_wrapone_fn) costra_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* And acos(). + */ +#define aloop( IN, OUT )\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = IM_DEG( acos( (double) p[x] ) );\ + } + +/* acos a buffer of PELs. + */ +static void +acostra_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Switch for all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: aloop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: aloop( signed char, float ); break; + case IM_BANDFMT_USHORT: aloop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: aloop( signed short, float ); break; + case IM_BANDFMT_UINT: aloop( unsigned int, float ); break; + case IM_BANDFMT_INT: aloop( signed int, float ); break; + case IM_BANDFMT_FLOAT: aloop( float, float ); break; + case IM_BANDFMT_DOUBLE: aloop( double, double ); break; + + default: + assert( 0 ); + } +} + +/* Acos transform. + */ +int +im_acostra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_acostra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_acostra", _( "bad input type" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, (im_wrapone_fn) acostra_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_deviate.c b/libsrc/arithmetic/im_deviate.c new file mode 100644 index 00000000..93cf0cd0 --- /dev/null +++ b/libsrc/arithmetic/im_deviate.c @@ -0,0 +1,222 @@ +/* @(#) Find the standard deviation of an image. Takes any non-complex image + * @(#) format, returns a double. Finds the deviation of all bands. + * @(#) + * @(#) int + * @(#) im_deviate( im, out ) + * @(#) IMAGE *im; + * @(#) double *out; + * @(#) + * @(#) Returns 0 on success and -1 on error. + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/08/1990 + * Modified on: + * 5/5/93 JC + * - now does partial images + * - less likely to overflow + * - adapted from im_avg + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 21/2/95 JC + * - modernised again + * 11/5/95 JC + * - oops! return( NULL ) in im_avg(), instead of return( -1 ) + * 20/6/95 JC + * - now returns double, not float + * 13/1/05 + * - use 64 bit arithmetic + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Start function: allocate space for a pair of doubles in which we can + * accumulate the sum and the sum of squares. + */ +static void * +start_fn( IMAGE *out ) +{ + double *tmp; + + if( !(tmp = IM_ARRAY( out, 2, double )) ) + return( NULL ); + tmp[0] = 0.0; + tmp[1] = 0.0; + + return( (void *) tmp ); +} + +/* Stop function. Add this little sum to the main sum. + */ +static int +stop_fn( double *tmp, double *sum ) +{ + sum[0] += tmp[0]; + sum[1] += tmp[1]; + + return( 0 ); +} + +/* Loop over region, adding information to the appropriate fields of tmp. + */ +static int +scan_fn( REGION *reg, double *tmp ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( reg ); + double s = 0.0; + double s2 = 0.0; + int x, y; + +/* Sum pels in this section. + */ +#define loop(TYPE) \ + { TYPE *p; \ + \ + for( y = to; y < bo; y++ ) { \ + p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + TYPE v = p[x]; \ + \ + s += v; \ + s2 += (double) v * (double) v; \ + }\ + }\ + } + + /* Now generate code for all types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop(unsigned char); break; + case IM_BANDFMT_CHAR: loop(signed char); break; + case IM_BANDFMT_USHORT: loop(unsigned short); break; + case IM_BANDFMT_SHORT: loop(signed short); break; + case IM_BANDFMT_UINT: loop(unsigned int); break; + case IM_BANDFMT_INT: loop(signed int); break; + case IM_BANDFMT_FLOAT: loop(float); break; + + case IM_BANDFMT_DOUBLE: +#ifdef HAVE_LIBOIL + for( y = to; y < bo; y++ ) { + double *p = (double *) IM_REGION_ADDR( reg, le, y ); + double t; + double t2; + + oil_sum_f64( &t, p, sizeof( double ), sz ); + oil_squaresum_f64( &t2, p, sz ); + + s += t; + s2 += t2; + } +#else /*!HAVE_LIBOIL*/ + loop(double); +#endif /*HAVE_LIBOIL*/ + break; + + default: + assert( 0 ); + } + + /* Add to sum for this sequence. + */ + tmp[0] += s; + tmp[1] += s2; + + return( 0 ); +} + +/* Find the average of an image. + */ +int +im_deviate( IMAGE *in, double *out ) +{ + double sum[2] = { 0.0, 0.0 }; + gint64 N; + + /* Check our args. + */ + if( im_pincheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_deviate", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_deviate", _( "bad input type" ) ); + return( -1 ); + } + + /* Loop over input, summing pixels. + */ + if( im_iterate( in, start_fn, scan_fn, stop_fn, &sum, NULL ) ) + return( -1 ); + + /* + + NOTE: NR suggests a two-pass algorithm to minimise roundoff. + But that's too expensive for us :-( so do it the old one-pass + way. + + */ + + /* Calculate and return deviation. Add a fabs to stop sqrt(<=0). + */ + N = (gint64) in->Xsize * in->Ysize * in->Bands; + *out = sqrt( fabs( sum[1] - (sum[0] * sum[0] / N) ) / (N - 1) ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_divide.c b/libsrc/arithmetic/im_divide.c new file mode 100644 index 00000000..dd8c47b6 --- /dev/null +++ b/libsrc/arithmetic/im_divide.c @@ -0,0 +1,214 @@ +/* @(#) Divide two images + * @(#) Images must have the same no of bands and can be of any type + * @(#) No check for overflow is carried out. + * @(#) + * @(#) int + * @(#) im_divide(in1, in2, out) + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 29/4/93 JC + * - now works for partial images + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 19/10/93 JC + * - coredump-inducing bug in complex*complex fixed + * 13/12/93 + * - char*short bug fixed + * 12/6/95 JC + * - new im_multiply adapted to make new im_divide + * 27/9/04 + * - updated for 1 band $op n band image -> n band image case + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Complex divide. + */ +#define cloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double x1 = p1[0];\ + double y1 = p1[1];\ + double x2 = p2[0];\ + double y2 = p2[1];\ + \ + double quot = x2 * x2 + y2 * y2;\ + \ + p1 += 2;\ + p2 += 2;\ + \ + q[0] = (x1 * x2 + y1 * y2) / quot;\ + q[1] = (y1 * x2 - x1 * y2) / quot;\ + \ + q += 2;\ + }\ +} + +/* Real divide. + */ +#define rloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = p1[x] / p2[x];\ +} + +static void +divide_buffer( PEL **in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Divide all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: rloop( signed char ); break; + case IM_BANDFMT_UCHAR: rloop( unsigned char ); break; + case IM_BANDFMT_SHORT: rloop( signed short ); break; + case IM_BANDFMT_USHORT: rloop( unsigned short ); break; + case IM_BANDFMT_INT: rloop( signed int ); break; + case IM_BANDFMT_UINT: rloop( unsigned int ); break; + + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + oil_divide_f32( (float *) out, + (float *) in[0], (float *) in[1], sz ); +#else /*!HAVE_LIBOIL*/ + rloop( float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: rloop( double ); break; + case IM_BANDFMT_COMPLEX: cloop( float ); break; + case IM_BANDFMT_DPCOMPLEX: cloop( double ); break; + + default: + assert( 0 ); + } +} + +int +im_divide( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + /* Basic checks. + */ + if( im_piocheck( in1, out ) || im_pincheck( in2 ) ) + return( -1 ); + if( in1->Bands != in2->Bands && + (in1->Bands != 1 && in2->Bands != 1) ) { + im_error( "im_divide", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_divide", _( "not uncoded" ) ); + return( -1 ); + } + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + + /* What number of bands will we write? + */ + out->Bands = IM_MAX( in1->Bands, in2->Bands ); + + /* What output type will we write? float, double or complex. + */ + if( im_iscomplex( in1 ) || im_iscomplex( in2 ) ) { + /* What kind of complex? + */ + if( in1->BandFmt == IM_BANDFMT_DPCOMPLEX || + in2->BandFmt == IM_BANDFMT_DPCOMPLEX ) + /* Output will be DPCOMPLEX. + */ + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + else + out->BandFmt = IM_BANDFMT_COMPLEX; + + } + else if( im_isfloat( in1 ) || im_isfloat( in2 ) ) { + /* What kind of float? + */ + if( in1->BandFmt == IM_BANDFMT_DOUBLE || + in2->BandFmt == IM_BANDFMT_DOUBLE ) + out->BandFmt = IM_BANDFMT_DOUBLE; + else + out->BandFmt = IM_BANDFMT_FLOAT; + } + else { + /* An int type -- output must be just float. + */ + out->BandFmt = IM_BANDFMT_FLOAT; + } + + /* And process! + */ + if( im__cast_and_call( in1, in2, out, + (im_wrapmany_fn) divide_buffer, NULL ) ) + return( -1 ); + + /* Success! + */ + return( 0 ); +} diff --git a/libsrc/arithmetic/im_expntra.c b/libsrc/arithmetic/im_expntra.c new file mode 100644 index 00000000..debe8d45 --- /dev/null +++ b/libsrc/arithmetic/im_expntra.c @@ -0,0 +1,249 @@ +/* @(#) Calculates n^pel, with n as a parameter. + * @(#) If input is up to float, output is float, else input is the same as + * @(#) output. Does not work for complex input. + * @(#) + * @(#) int + * @(#) im_expntra( in, out, e ) + * @(#) IMAGE *in, *out; + * @(#) double e; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 10/12/93 JC + * - now reports total number of x/0, rather than each one. + * 1/2/95 JC + * - rewritten for PIO with im_wrapone() + * - incorrect complex code removed + * - /0 reporting removed for ease of programming + * 8/5/95 JC + * - im_expntra() adapted to make this + * 15/4/97 JC + * - oops, return(0) missing + * - M_E removed, as not everywhere + * 6/7/98 JC + * - _vec version added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Parameters saved here. + */ +typedef struct { + int n; /* Number of bands of constants */ + double *e; /* Exponent values, one per band */ +} ExpntraInfo; + +/* Define what we do for each band element type. Single constant. + */ +#define loop1(IN, OUT)\ +{\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double f = (double) p[x];\ + \ + if( e == 0.0 && f < 0.0 ) {\ + /* Division by zero! Difficult to report tho'\ + */\ + q[x] = 0.0;\ + }\ + else\ + q[x] = pow( e, f );\ + }\ +} + +/* Expntra a buffer. + */ +static int +expntra1_gen( PEL *in, PEL *out, int width, IMAGE *im, ExpntraInfo *inf ) +{ + int sz = width * im->Bands; + double e = inf->e[0]; + int x; + + /* Expntra all non-complex input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop1(unsigned char, float); break; + case IM_BANDFMT_CHAR: loop1(signed char, float); break; + case IM_BANDFMT_USHORT: loop1(unsigned short, float); break; + case IM_BANDFMT_SHORT: loop1(signed short, float); break; + case IM_BANDFMT_UINT: loop1(unsigned int, float); break; + case IM_BANDFMT_INT: loop1(signed int, float); break; + case IM_BANDFMT_FLOAT: loop1(float, float); break; + case IM_BANDFMT_DOUBLE: loop1(double, double); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Define what we do for each band element type. One constant per band. + */ +#define loopn(IN, OUT)\ +{\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( i = 0, x = 0; x < width; x++ )\ + for( k = 0; k < im->Bands; k++, i++ ) {\ + double e = inf->e[k];\ + double f = (double) p[i];\ + \ + if( e == 0.0 && f < 0.0 ) {\ + q[i] = 0.0;\ + }\ + else\ + q[i] = pow( e, f );\ + }\ +} + +/* Expntra a buffer. + */ +static int +expntran_gen( PEL *in, PEL *out, int width, IMAGE *im, ExpntraInfo *inf ) +{ + int x, k, i; + + /* Expntra all non-complex input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loopn(unsigned char, float); break; + case IM_BANDFMT_CHAR: loopn(signed char, float); break; + case IM_BANDFMT_USHORT: loopn(unsigned short, float); break; + case IM_BANDFMT_SHORT: loopn(signed short, float); break; + case IM_BANDFMT_UINT: loopn(unsigned int, float); break; + case IM_BANDFMT_INT: loopn(signed int, float); break; + case IM_BANDFMT_FLOAT: loopn(float, float); break; + case IM_BANDFMT_DOUBLE: loopn(double, double); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +int +im_expntra_vec( IMAGE *in, IMAGE *out, int n, double *e ) +{ + ExpntraInfo *inf; + int i; + + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_expntra_vec", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_expntra_vec", _( "not non-complex" ) ); + return( -1 ); + } + if( n != 1 && n != in->Bands ) { + im_error( "im_expntra_vec", + _( "not 1 or %d elements in vector" ), in->Bands ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_isint( in ) ) { + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + } + + /* Make space for a little buffer. + */ + if( !(inf = IM_NEW( out, ExpntraInfo )) || + !(inf->e = IM_ARRAY( out, n, double )) ) + return( -1 ); + for( i = 0; i < n; i++ ) + inf->e[i] = e[i]; + inf->n = n; + + /* Generate! + */ + if( n == 1 ) { + if( im_wrapone( in, out, + (im_wrapone_fn) expntra1_gen, in, inf ) ) + return( -1 ); + } + else { + if( im_wrapone( in, out, + (im_wrapone_fn) expntran_gen, in, inf ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_expntra( IMAGE *in, IMAGE *out, double e ) +{ + return( im_expntra_vec( in, out, 1, &e ) ); +} + +/* Define im_exptra() and im_exp10tra() in terms of im_expntra(). + */ +int +im_exptra( IMAGE *in, IMAGE *out ) +{ + return( im_expntra( in, out, 2.7182818284590452354 ) ); +} + +int +im_exp10tra( IMAGE *in, IMAGE *out ) +{ + return( im_expntra( in, out, 10.0 ) ); +} diff --git a/libsrc/arithmetic/im_fav4.c b/libsrc/arithmetic/im_fav4.c new file mode 100644 index 00000000..c3aa7246 --- /dev/null +++ b/libsrc/arithmetic/im_fav4.c @@ -0,0 +1,95 @@ +/* @(#) Optimised 4 frame average +Copyright (C) 1992, Kirk Martinez, History of Art Dept, Birkbeck College +*/ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define ARGS "fav4: frame average 4 frames\nARGS: im1 im2 im3 im4 outfile" +#define NFRAMES 4 + +/* @(#) Optimised 4 frame average +Copyright (C) 1992, Kirk Martinez, History of Art Dept, Birkbeck College +CHAR images only! +ARGS: array of 4 source images and output image +*/ +int +im_fav4( IMAGE **in, IMAGE *out) +{ + PEL *result, *buffer, *p1, *p2, *p3, *p4; + int x,y; + int linebytes, PICY; + +/* check IMAGEs parameters +*/ +if(im_iocheck(in[1], out)) return(-1); + +/* BYTE images only! +*/ +if( (in[0]->BandFmt != IM_BANDFMT_CHAR) && (in[0]->BandFmt != IM_BANDFMT_UCHAR)) return(-1); + +if ( im_cp_desc(out, in[1]) == -1) /* copy image descriptors */ + return(-1); +if ( im_setupout(out) == -1) + return(-1); + +linebytes = in[0]->Xsize * in[0]->Bands; +PICY = in[0]->Ysize; +buffer = (PEL*)im_malloc(NULL,linebytes); +memset(buffer, 0, linebytes); + + p1 = (PEL*)in[0]->data; + p2 = (PEL*)in[1]->data; + p3 = (PEL*)in[2]->data; + p4 = (PEL*)in[3]->data; + +for (y = 0; y < PICY; y++) + { + result = buffer; + /* average 4 pels with rounding, for whole line*/ + for (x = 0; x < linebytes; x++) { + *result++ = (PEL)((int)((int)*p1++ + (int)*p2++ + (int)*p3++ + (int)*p4++ +2) >> 2); + } + im_writeline(y,out, buffer); + } +im_free(buffer); +return(0); +} + diff --git a/libsrc/arithmetic/im_floor.c b/libsrc/arithmetic/im_floor.c new file mode 100644 index 00000000..2ee3e8b8 --- /dev/null +++ b/libsrc/arithmetic/im_floor.c @@ -0,0 +1,128 @@ +/* @(#) floor() an image ... no promotion, so output type == input type + * @(#) + * @(#) int + * @(#) im_floor( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 20/6/02 JC + * - adapted from im_abs() + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define floor_loop(TYPE)\ + {\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = floor( p[x] );\ + } + +/* Ceil a buffer of PELs. + */ +static void +floor_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + oil_floor_f32( (float *) out, (float *) in, sz ); +#else /*!HAVE_LIBOIL*/ + floor_loop(float); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: floor_loop(double); break; + case IM_BANDFMT_COMPLEX: sz *= 2; floor_loop(float); break; + case IM_BANDFMT_DPCOMPLEX: sz *= 2; floor_loop(double); break; + + default: + assert( 0 ); + } +} + +/* Ceil of image. + */ +int +im_floor( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_floor", _( "not uncoded" ) ); + return( -1 ); + } + + /* Is this one of the int types? Degenerate to im_copy() if it + * is. + */ + if( im_isint( in ) ) + return( im_copy( in, out ) ); + + /* Output type == input type. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Generate! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) floor_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_gadd.c b/libsrc/arithmetic/im_gadd.c new file mode 100644 index 00000000..bd227518 --- /dev/null +++ b/libsrc/arithmetic/im_gadd.c @@ -0,0 +1,127 @@ +/* @(#) Generalised addition of two vasari images using the routines + * @(#) im_gaddim or im_gfadd + * @(#) Convention to ease the complilation time. + * @(#) Function im_gadd() assumes that the both input files + * @(#) are either memory mapped or in a buffer. + * @(#) Images must have the same no of bands and must not be complex + * @(#) No check for overflow is carried out. + * @(#) + * @(#) int im_gadd(a, in1, b, in2, c, out) + * @(#) IMAGE *in1, *in2, *out; + * @(#) double a, b, c; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +extern int im_gfadd(); +extern int im_gaddim(); + +/* This function works on either mmaped files or on images in buffer + */ + +int im_gadd(a, in1, b, in2, c, out) +IMAGE *in1, *in2, *out; +double a, b, c; +{ + int flagint = 0; + int flagfloat = 0; + int value = 0; + + switch(in1->BandFmt) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + flagint = 1; + break; + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + flagfloat = 1; + break; + default: im_error("im_gadd",_("Unable to accept image1")); + return(-1); + } + switch(in2->BandFmt) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + flagint = 1; + break; + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + flagfloat = 1; + break; + default: im_error("im_gadd",_("Unable to accept image1")); + return(-1); + } + /* Select output routines */ + if (flagfloat == 1) + { + value = im_gfadd(a, in1, b, in2, c, out); + if (value == -1) + return(-1); + } + else if (flagint == 1) + { + value = im_gaddim(a, in1, b, in2, c, out); + if (value == -1) + return(-1); + } + else + assert( 0 ); + + return(0); +} diff --git a/libsrc/arithmetic/im_gaddim.c b/libsrc/arithmetic/im_gaddim.c new file mode 100644 index 00000000..a15dd911 --- /dev/null +++ b/libsrc/arithmetic/im_gaddim.c @@ -0,0 +1,241 @@ +/* @(#) Generalised addition of two vasari images. + * @(#)Inputs, outputs are neither float nor double + * @(#) Result at each point is a*in1 + b*in2 + c + * @(#) Result depends on inputs, rounding is carried out; + * @(#) Function im_gaddim() assumes that the both input files + * @(#) are either memory mapped or in a buffer. + * @(#) Images must have the same no of bands and must not be complex + * @(#) No check for overflow is done; + * @(#) + * @(#) int im_gaddim(a, in1, b, in2, c, out) + * @(#) double a, b, c; + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* This function works on either mmaped files or on images in buffer + */ + +/* uchar char ushort short uint int */ +static int array[6][6] = { +/* uchar */ { 2, 3, 2, 3, 4, 5 }, +/* char */ { 3, 3, 3, 3, 5, 5 }, +/* ushort */ { 2, 3, 2, 3, 4, 5 }, +/* short */ { 3, 3, 3, 3, 5, 5 }, +/* uint */ { 4, 5, 4, 5, 4, 5 }, +/* int */ { 5, 5, 5, 5, 5, 5 } + }; + +#define select_tmp2_for_out_int(OUT) \ + case IM_BANDFMT_UCHAR: select_tmp1_for_out_int(unsigned char, OUT); break; \ + case IM_BANDFMT_CHAR: select_tmp1_for_out_int(signed char, OUT); break; \ + case IM_BANDFMT_USHORT: select_tmp1_for_out_int(unsigned short, OUT); break; \ + case IM_BANDFMT_SHORT: select_tmp1_for_out_int(signed short, OUT); break; \ + case IM_BANDFMT_UINT: select_tmp1_for_out_int(unsigned int, OUT); break; \ + case IM_BANDFMT_INT: select_tmp1_for_out_int(signed int, OUT); break; \ +\ + default: \ + im_error("im_gaddim","Wrong tmp2 format(1)"); \ + free( line); \ + return(-1); + +#define select_tmp1_for_out_int(IN2, OUT) \ + switch(tmp1->BandFmt) { \ + case IM_BANDFMT_UINT: loop(unsigned int, IN2, OUT); break; \ + case IM_BANDFMT_INT: loop(int, IN2, OUT); break; \ + default: im_error("im_gaddim","Wrong tmp2 format(2)");\ + free( line);\ + return(-1); \ + } + +#define select_tmp2_for_out_short(OUT) \ + case IM_BANDFMT_UCHAR: select_tmp1_for_out_short(unsigned char, OUT); break; \ + case IM_BANDFMT_CHAR: select_tmp1_for_out_short(signed char, OUT); break; \ + case IM_BANDFMT_USHORT: select_tmp1_for_out_short(unsigned short, OUT); break; \ + case IM_BANDFMT_SHORT: select_tmp1_for_out_short(signed short, OUT); break; +#define select_tmp1_for_out_short(IN2, OUT) \ + switch(tmp1->BandFmt) { \ + case IM_BANDFMT_UCHAR: loop(unsigned char, IN2, OUT); break; \ + case IM_BANDFMT_CHAR: loop(signed char, IN2, OUT); break; \ + case IM_BANDFMT_USHORT: loop(unsigned short, IN2, OUT); break; \ + case IM_BANDFMT_SHORT: loop(signed short, IN2, OUT); break; \ + default: im_error("im_gaddim","Wrong image1 format(4)");\ + free( line);\ + return(-1); \ + } + + + +int im_gaddim(a, in1, b, in2, c, out) +IMAGE *in1, *in2, *out; +double a, b, c; +{ + static int bb[] = { IM_BBITS_BYTE, IM_BBITS_BYTE, IM_BBITS_SHORT, + IM_BBITS_SHORT, IM_BBITS_INT, IM_BBITS_INT }; + static int fmt[] = { IM_BANDFMT_UCHAR, IM_BANDFMT_CHAR, + IM_BANDFMT_USHORT, IM_BANDFMT_SHORT, + IM_BANDFMT_UINT, IM_BANDFMT_INT }; + int y, x; + int first, second, result; + IMAGE *tmp1, *tmp2; + PEL *line; + int os; /* size of a line of output image */ + +/* fd, data filename must have been set before the function is called + * Check whether they are set properly */ + if ((im_iocheck(in1, out) == -1) || (im_iocheck(in2, out) == -1)) + { + return(-1); + } +/* Checks the arguments entered in in and prepares out */ + if ( (in1->Xsize != in2->Xsize) || (in1->Ysize != in2->Ysize) || + (in1->Bands != in2->Bands) || (in1->Coding != in2->Coding) ) + { im_error("im_gaddim"," Input images differ"); return(-1); } + if (in1->Coding != IM_CODING_NONE) + { im_error("im_gaddim"," images must be uncoded"); return(-1);} + + switch(in1->BandFmt) { + case IM_BANDFMT_UCHAR: first = 0; break; + case IM_BANDFMT_CHAR: first = 1; break; + case IM_BANDFMT_USHORT: first = 2; break; + case IM_BANDFMT_SHORT: first = 3; break; + case IM_BANDFMT_UINT: first = 4; break; + case IM_BANDFMT_INT: first = 5; break; + default: im_error("im_gaddim"," Unable to accept image1"); + return(-1); + } + switch(in2->BandFmt) { + case IM_BANDFMT_UCHAR: second = 0; break; + case IM_BANDFMT_CHAR: second = 1; break; + case IM_BANDFMT_USHORT: second = 2; break; + case IM_BANDFMT_SHORT: second = 3; break; + case IM_BANDFMT_UINT: second = 4; break; + case IM_BANDFMT_INT: second = 5; break; + default: im_error("im_gaddim"," Unable to accept image2"); + return(-1); + } +/* Define the output */ + result = array[first][second]; + +/* Prepare the output header */ + if ( im_cp_desc(out, in1) == -1) + { im_error("im_gaddim"," im_cp_desc failed"); return(-1); } + out->Bbits = bb[result]; + out->BandFmt = fmt[result]; + + if( im_setupout(out) == -1) + { im_error("im_gaddim"," im_setupout failed"); return(-1); } + +/* Order in1 and in2 */ + if ( first >= second ) + { tmp1 = in1; tmp2 = in2; } + else + { tmp1 = in2; tmp2 = in1; } + +/* Define what we do for each band element type. */ + +#define loop(IN1, IN2, OUT) \ + { IN1 *input1 = (IN1 *) tmp1->data; \ + IN2 *input2 = (IN2 *) tmp2->data; \ + \ + for (y=0; y Ysize; y++) {\ + OUT *cpline = (OUT*)line; \ + for (x=0; xXsize * out->Bands; + line = (PEL *) calloc ( (unsigned)os, sizeof(double) ); + if (line == NULL) + { + im_error("im_gaddim"," Unable to calloc"); + return(-1); + } + + switch (out->BandFmt) { + case IM_BANDFMT_INT: + switch (tmp2->BandFmt) { + select_tmp2_for_out_int(int); + } + break; + + case IM_BANDFMT_UINT: + switch (tmp2->BandFmt) { + select_tmp2_for_out_int(unsigned int); + } + break; + case IM_BANDFMT_SHORT: + switch (tmp2->BandFmt) { + select_tmp2_for_out_short(short); + } + break; + case IM_BANDFMT_USHORT: + switch (tmp2->BandFmt) { + select_tmp2_for_out_short(unsigned short); + } + break; + default: + im_error("im_gaddim"," Impossible output state"); + free( line); + return(-1); + } + + free( line); + + return(0); +} diff --git a/libsrc/arithmetic/im_gfadd.c b/libsrc/arithmetic/im_gfadd.c new file mode 100644 index 00000000..24fd7bcb --- /dev/null +++ b/libsrc/arithmetic/im_gfadd.c @@ -0,0 +1,351 @@ +/* @(#) Generalised addition of two vasari images. Result (double or float) + * @(#)depends on inputs + * @(#) Function im_gfadd() assumes that the both input files + * @(#) are either memory mapped or in a buffer. + * @(#) Images must have the same no of bands and can be of any type + * @(#) No check for overflow is carried out. If in doubt use im_clip2... + * @(#) Result at eachpoint is a*in1 + b*in2 + c + * @(#) + * @(#) int im_gfadd(a, in1, b, in2, c, out) + * @(#) double a, b, c; + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 15/6/93 J.Cupitt + * - externs removed + * - casts added to please ANSI C + * - includes rationalised + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* uchar char ushort short uint int float double */ +static int array[8][8] = { +/* uchar */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* char */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* ushort */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* short */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* uint */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* int */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* float */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* double */ { 1, 1, 1, 1, 1, 1, 1, 1 } + }; + +#define select_outdouble(IN2, OUT)\ + switch(tmp1->BandFmt) {\ + case IM_BANDFMT_DOUBLE: loop(double, IN2, OUT); break;\ + default: im_error("im_gfadd","Wrong tmp1 format(d)");\ + free( line); return( -1 );\ + } + +#define outfloat_2uchar(IN2, OUT)\ + case IM_BANDFMT_UCHAR: loop(unsigned char, IN2, OUT); break;\ + case IM_BANDFMT_CHAR: loop(signed char, IN2, OUT); break;\ + case IM_BANDFMT_USHORT: loop(unsigned short, IN2, OUT); break;\ + case IM_BANDFMT_SHORT: loop(signed short, IN2, OUT); break;\ + case IM_BANDFMT_UINT: loop(unsigned int, IN2, OUT); break;\ + case IM_BANDFMT_INT: loop(signed int, IN2, OUT); break;\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +#define outfloat_2char(IN2, OUT)\ + case IM_BANDFMT_CHAR: loop(signed char, IN2, OUT); break;\ + case IM_BANDFMT_USHORT: loop(unsigned short, IN2, OUT); break;\ + case IM_BANDFMT_SHORT: loop(signed short, IN2, OUT); break;\ + case IM_BANDFMT_UINT: loop(unsigned int, IN2, OUT); break;\ + case IM_BANDFMT_INT: loop(signed int, IN2, OUT); break;\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +#define outfloat_2ushort(IN2, OUT)\ + case IM_BANDFMT_USHORT: loop(unsigned short, IN2, OUT); break;\ + case IM_BANDFMT_SHORT: loop(signed short, IN2, OUT); break;\ + case IM_BANDFMT_UINT: loop(unsigned int, IN2, OUT); break;\ + case IM_BANDFMT_INT: loop(signed int, IN2, OUT); break;\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +#define outfloat_2short(IN2, OUT)\ + case IM_BANDFMT_SHORT: loop(signed short, IN2, OUT); break;\ + case IM_BANDFMT_UINT: loop(unsigned int, IN2, OUT); break;\ + case IM_BANDFMT_INT: loop(signed int, IN2, OUT); break;\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +#define outfloat_2uint(IN2, OUT)\ + case IM_BANDFMT_UINT: loop(unsigned int, IN2, OUT); break;\ + case IM_BANDFMT_INT: loop(signed int, IN2, OUT); break;\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +#define outfloat_2int(IN2, OUT)\ + case IM_BANDFMT_INT: loop(signed int, IN2, OUT); break;\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +#define outfloat_2float(IN2, OUT)\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +int im_gfadd(a, in1, b, in2, c, out) +double a, b, c; +IMAGE *in1, *in2, *out; +{ + static int bb[] = { IM_BBITS_FLOAT, IM_BBITS_DOUBLE }; + static int fmt[] = { IM_BANDFMT_FLOAT, IM_BANDFMT_DOUBLE }; + int y, x; + int first, second, result; + IMAGE *tmp1, *tmp2; + PEL *line; + int os; /* size of a line of output image */ + +/* fd, data filename must have been set before the function is called + * Check whether they are set properly */ + if ((im_iocheck(in1, out) == -1) || (im_iocheck(in2, out) == -1)) + { im_error("im_gfadd"," im_iocheck failed"); return( -1 ); } +/* Checks the arguments entered in in and prepares out */ + if ( (in1->Xsize != in2->Xsize) || (in1->Ysize != in2->Ysize) || + (in1->Bands != in2->Bands) || (in1->Coding != in2->Coding) ) + { im_error("im_gfadd"," Input images differ"); return( -1 );} + if (in1->Coding != IM_CODING_NONE) + { im_error("im_gfadd"," images are coded"); return( -1 ); } + + switch(in1->BandFmt) { + case IM_BANDFMT_UCHAR: first = 0; break; + case IM_BANDFMT_CHAR: first = 1; break; + case IM_BANDFMT_USHORT: first = 2; break; + case IM_BANDFMT_SHORT: first = 3; break; + case IM_BANDFMT_UINT: first = 4; break; + case IM_BANDFMT_INT: first = 5; break; + case IM_BANDFMT_FLOAT: first = 6; break; + case IM_BANDFMT_DOUBLE: first = 7; break; + default: im_error("im_gfadd"," unable to accept image1"); + return( -1 ); + } + switch(in2->BandFmt) { + case IM_BANDFMT_UCHAR: second = 0; break; + case IM_BANDFMT_CHAR: second = 1; break; + case IM_BANDFMT_USHORT: second = 2; break; + case IM_BANDFMT_SHORT: second = 3; break; + case IM_BANDFMT_UINT: second = 4; break; + case IM_BANDFMT_INT: second = 5; break; + case IM_BANDFMT_FLOAT: second = 6; break; + case IM_BANDFMT_DOUBLE: second = 7; break; + default: im_error("im_gfadd"," unable to accept image2"); + return( -1 ); + } +/* Define the output */ + result = array[first][second]; +/* Prepare output */ + if ( im_cp_desc(out, in1) == -1) + { im_error("im_gfadd"," im_cp_desc failed"); return( -1 ); } + out->Bbits = bb[result]; + out->BandFmt = fmt[result]; + if( im_setupout(out) == -1) + { im_error("im_gfadd"," im_setupout failed"); return( -1 ); } + +/* Order in1 and in2 */ + if ( first >= second ) + { tmp1 = in1; tmp2 = in2; } + else + { tmp1 = in2; tmp2 = in1; } + +/* Define what we do for each band element type. */ + +#define loop(IN1, IN2, OUT)\ + { IN1 *input1 = (IN1 *) tmp1->data;\ + IN2 *input2 = (IN2 *) tmp2->data;\ + \ + for (y=0; y Ysize; y++) {\ + OUT *cpline = (OUT*)line;\ + for (x=0; xXsize * out->Bands; + line = (PEL *) calloc ( (unsigned)os, sizeof(double) ); + if (line == NULL) + { im_error("im_gfadd"," unable to calloc"); return( -1 ); } + + switch (out->BandFmt) { + case IM_BANDFMT_DOUBLE: + switch (tmp2->BandFmt) { + case IM_BANDFMT_UCHAR: + select_outdouble(unsigned char, double); + break; + + case IM_BANDFMT_CHAR: + select_outdouble(signed char, double); + break; + + case IM_BANDFMT_USHORT: + select_outdouble(unsigned short, double); + break; + + case IM_BANDFMT_SHORT: + select_outdouble(signed short, double); + break; + + case IM_BANDFMT_UINT: + select_outdouble(unsigned int, double); + break; + + case IM_BANDFMT_INT: + select_outdouble(signed int, double); + break; + + case IM_BANDFMT_FLOAT: + select_outdouble(float, double); + break; + + case IM_BANDFMT_DOUBLE: + select_outdouble(double, double); + break; + + default: + im_error("im_gfadd","Wrong tmp2 format(d)"); + free( line ); + return( -1 ); + } + + break; + + case IM_BANDFMT_FLOAT : + switch (tmp2->BandFmt) { + case IM_BANDFMT_UCHAR: + switch (tmp1->BandFmt) { + outfloat_2uchar(unsigned char, float); + + default: + im_error("im_gfadd"," Error (a)"); + free( line ); + return( -1 ); + } + break; + + case IM_BANDFMT_CHAR: + switch (tmp1->BandFmt) { + outfloat_2char(signed char, float); + + default: + im_error("im_gfadd"," Error (b)"); + free( line); return( -1 ); + } + break; + + case IM_BANDFMT_USHORT: + switch (tmp1->BandFmt) { + outfloat_2ushort(unsigned short, float); + + default: + im_error("im_gfadd"," Error (c)"); + free( line); return( -1 ); + } + break; + + case IM_BANDFMT_SHORT: + switch (tmp1->BandFmt) { + outfloat_2short(signed short, float); + + default: + im_error("im_gfadd"," Error (d)"); + free( line); return( -1 ); + } + break; + + case IM_BANDFMT_UINT: + switch (tmp1->BandFmt) { + outfloat_2uint(unsigned int, float); + + default: + im_error("im_gfadd"," Error (e)"); + free( line); return( -1 ); + } + break; + + case IM_BANDFMT_INT: + switch (tmp1->BandFmt) { + outfloat_2int(signed int, float); + + default: + im_error("im_gfadd"," Error (f)"); + free( line ); + return( -1 ); + } + break; + + case IM_BANDFMT_FLOAT: + switch (tmp1->BandFmt) { + outfloat_2float(float, float); + + default: + im_error("im_gfadd"," Error (g)"); + free( line ); + return( -1 ); + } + break; + + default: + im_error("im_gfadd"," Wrong tmp2 format(f)"); + free( line ); + return( -1 ); + } + + break; + + default: + im_error("im_gfadd"," Impossible output state"); + free( line ); + return( -1 ); + } + + free( line ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_invert.c b/libsrc/arithmetic/im_invert.c new file mode 100644 index 00000000..a9a474c3 --- /dev/null +++ b/libsrc/arithmetic/im_invert.c @@ -0,0 +1,149 @@ +/* @(#) Invert a UCHAR image. Very simple new-style VIPS routine. See + * @(#) im_exptra() for the next level of complexity. This function is not + * @(#) as quick as it could be - it is intended to be an example rather than + * @(#) to be useful. This should really be written with im_wrapone(). + * @(#) + * @(#) int + * @(#) im_invert( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : + * 7/7/93 JC + * - memory leaks fixed + * - adapted for partial v2 + * - ANSIfied + * 22/2/95 JC + * - tidied up again + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Invert a REGION. We are given the REGION we should write to, the REGION we + * should use for input, and the IMAGE we are processing. On entry to + * invert_gen(), or points to the memory we should write to and ir is blank. + */ +static int +invert_gen( REGION *or, REGION *ir, IMAGE *in ) +{ + /* Left, right, top and bottom for the output region. + */ + int le = or->valid.left; + int to = or->valid.top; + int bo = IM_RECT_BOTTOM( &or->valid ); + + int x, y; + + /* Ask for the section of the input image we need to produce this + * section of the output image. + */ + if( im_prepare( ir, &or->valid ) ) + return( -1 ); + + /* Loop over output, writing input. + */ + for( y = to; y < bo; y++ ) { + /* Point p and q at the start of the line of pels we must + * process this loop. + */ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Loop along the line, processing pels. + * IM_REGION_N_ELEMENTS(region) gives + * the number of band elements across a region. By looping to + * IM_REGION_N_ELEMENTS() rather than ir->valid.width, we work + * for any number of bands. + */ + for( x = 0; x < IM_REGION_N_ELEMENTS( or ); x++ ) + q[x] = 255 - p[x]; + } + + return( 0 ); +} + +/* Invert IMAGE in to IMAGE out. Any number of bands, unsigned char pels + * only. See im_exptra() for an example of a VIPS function which can process + * any input image type. + */ +int +im_invert( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_invert", _( "not uncoded" ) ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_invert", _( "not UCHAR" ) ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Prepare the output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Set demand hints. Like most one-to-one operations, we work best + * with long, thin strips. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate into out. im_start_one() and im_stop_one() are simple + * convenience functions provided by VIPS which do the necessary + * region creation and destruction for one-image-in + * style functions. See im_add(), im_start_many() and im_stop_many() + * for convenience functions for multiple inputs. + */ + if( im_generate( out, + im_start_one, invert_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_linreg.c b/libsrc/arithmetic/im_linreg.c new file mode 100644 index 00000000..94686c14 --- /dev/null +++ b/libsrc/arithmetic/im_linreg.c @@ -0,0 +1,430 @@ +/* @(#) Function to find perform pixelwise linear regression on an array of + * @(#) single band images. + * @(#) + * @(#) int im_linreg( + * @(#) IMAGE **ins, + * @(#) IMAGE *out, + * @(#) double *xs + * @(#) ); + * @(#) + * + * Copyright: 2006, The Nottingham Trent University + * + * Author: Tom Vajzovic + * + * Written on: 2006-12-26 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ +#include + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + + +/** TYPES **/ + +typedef struct { + + unsigned int n; + double *xs; + double *difs; + double mean; + double nsig2; + double err_term; + +} x_set; + +#define LINREG_SEQ( TYPE ) typedef struct { \ + REGION **regs; \ + TYPE **ptrs; \ + size_t *skips; \ +} linreg_seq_ ## TYPE + +LINREG_SEQ( gint8 ); +LINREG_SEQ( guint8 ); +LINREG_SEQ( gint16 ); +LINREG_SEQ( guint16 ); +LINREG_SEQ( gint32 ); +LINREG_SEQ( guint32 ); +LINREG_SEQ( float ); +LINREG_SEQ( double ); + + +/** LOCAL FUNCTION DECLARATIONS **/ + +x_set *x_anal( IMAGE *im, double *xs, unsigned int n ); + +#define LINREG_START_DECL( TYPE ) static linreg_seq_ ## TYPE *linreg_start_ ## TYPE( IMAGE *out, IMAGE **ins, x_set *x_vals ) +#define LINREG_GEN_DECL( TYPE ) static int linreg_gen_ ## TYPE( REGION *to_make, linreg_seq_ ## TYPE *seq, void *unrequired, x_set *x_vals ) +#define LINREG_STOP_DECL( TYPE ) static int linreg_stop_ ## TYPE( linreg_seq_ ## TYPE *seq ) +#define INCR_ALL_DECL( TYPE ) static void incr_all_ ## TYPE( TYPE **ptrs, unsigned int n ) +#define SKIP_ALL_DECL( TYPE ) static void skip_all_ ## TYPE( TYPE **ptrs, size_t *skips, unsigned int n ) + +LINREG_START_DECL( gint8 ); +LINREG_START_DECL( guint8 ); +LINREG_START_DECL( gint16 ); +LINREG_START_DECL( guint16 ); +LINREG_START_DECL( gint32 ); +LINREG_START_DECL( guint32 ); +LINREG_START_DECL( float ); +LINREG_START_DECL( double ); + +LINREG_GEN_DECL( gint8 ); +LINREG_GEN_DECL( guint8 ); +LINREG_GEN_DECL( gint16 ); +LINREG_GEN_DECL( guint16 ); +LINREG_GEN_DECL( gint32 ); +LINREG_GEN_DECL( guint32 ); +LINREG_GEN_DECL( float ); +LINREG_GEN_DECL( double ); + +LINREG_STOP_DECL( gint8 ); +LINREG_STOP_DECL( guint8 ); +LINREG_STOP_DECL( gint16 ); +LINREG_STOP_DECL( guint16 ); +LINREG_STOP_DECL( gint32 ); +LINREG_STOP_DECL( guint32 ); +LINREG_STOP_DECL( float ); +LINREG_STOP_DECL( double ); + +INCR_ALL_DECL( gint8 ); +INCR_ALL_DECL( guint8 ); +INCR_ALL_DECL( gint16 ); +INCR_ALL_DECL( guint16 ); +INCR_ALL_DECL( gint32 ); +INCR_ALL_DECL( guint32 ); +INCR_ALL_DECL( float ); +INCR_ALL_DECL( double ); + +SKIP_ALL_DECL( gint8 ); +SKIP_ALL_DECL( guint8 ); +SKIP_ALL_DECL( gint16 ); +SKIP_ALL_DECL( guint16 ); +SKIP_ALL_DECL( gint32 ); +SKIP_ALL_DECL( guint32 ); +SKIP_ALL_DECL( float ); +SKIP_ALL_DECL( double ); + + +/** EXPORTED FUNCTION DEFINITION **/ + +int im_linreg( IMAGE **ins, IMAGE *out, double *xs ){ +#define FUNCTION_NAME "im_linreg" + int n; + x_set *x_vals; + + if( im_poutcheck( out ) ) + return( -1 ); + + for( n= 0; ins[ n ]; ++n ){ +/* + if( ! isfinite( xs[ n ] ) ){ + im_error( FUNCTION_NAME, "invalid argument" ); + return( -1 ); + } +*/ + if( im_pincheck( ins[ n ] ) ) + return( -1 ); + + if( 1 != ins[ n ]-> Bands ){ + im_error( FUNCTION_NAME, "image is not single band" ); + return( -1 ); + } + if( ins[ n ]-> Coding ){ + im_error( FUNCTION_NAME, "image is not uncoded" ); + return( -1 ); + } + if( n ){ + if( ins[ n ]-> BandFmt != ins[ 0 ]-> BandFmt ){ + im_error( FUNCTION_NAME, "image band formats differ" ); + return( -1 ); + } + } + else { + if( ! im_isscalar( ins[ 0 ] ) ){ + im_error( FUNCTION_NAME, "image has non-scalar band format" ); + return( -1 ); + } + } + if( n && ( ins[ n ]-> Xsize != ins[ 0 ]-> Xsize + || ins[ n ]-> Ysize != ins[ 0 ]-> Ysize ) ){ + + im_error( FUNCTION_NAME, "image sizes differ" ); + return( -1 ); + } + } + if( n < 3 ){ + im_error( FUNCTION_NAME, "not enough input images" ); + return( -1 ); + } + if( im_cp_desc_array( out, ins ) ) + return( -1 ); + + out-> Bands= 7; + out-> BandFmt= IM_BANDFMT_DOUBLE; + out-> Bbits= IM_BBITS_DOUBLE; + out-> Type= 0; + + if( im_demand_hint_array( out, IM_THINSTRIP, ins ) ) + return( -1 ); + + x_vals= x_anal( out, xs, n ); + + if( ! x_vals ) + return( -1 ); + + switch( ins[ 0 ]-> BandFmt ){ +#define LINREG_RET( TYPE ) return im_generate( out, (void*) linreg_start_ ## TYPE, linreg_gen_ ## TYPE, linreg_stop_ ## TYPE, ins, x_vals ) + + case IM_BANDFMT_CHAR: + LINREG_RET( gint8 ); + + case IM_BANDFMT_UCHAR: + LINREG_RET( guint8 ); + + case IM_BANDFMT_SHORT: + LINREG_RET( gint16 ); + + case IM_BANDFMT_USHORT: + LINREG_RET( guint16 ); + + case IM_BANDFMT_INT: + LINREG_RET( gint32 ); + + case IM_BANDFMT_UINT: + LINREG_RET( guint32 ); + + case IM_BANDFMT_FLOAT: + LINREG_RET( float ); + + case IM_BANDFMT_DOUBLE: + LINREG_RET( double ); + + default: /* keep -Wall happy */ + return( -1 ); + } +#undef FUNCTION_NAME +} + + +/** LOCAL FUNCTION DECLARATIONS **/ + +x_set *x_anal( IMAGE *im, double *xs, unsigned int n ){ + int i; + + x_set *x_vals= IM_NEW( im, x_set ); + + if( ! x_vals ) + return( NULL ); + + x_vals-> xs= IM_ARRAY( im, 2 * n, double ); + + if( ! x_vals-> xs ) + return( NULL ); + + x_vals-> difs= x_vals-> xs + n; + x_vals-> n= n; + x_vals-> mean= 0.0; + + for( i= 0; i < n; ++i ){ + x_vals-> xs[ i ]= xs[ i ]; + x_vals-> mean+= xs[ i ]; + } + x_vals-> mean/= n; + x_vals-> nsig2= 0.0; + + for( i= 0; i < n; ++i ){ + x_vals-> difs[ i ]= xs[ i ] - x_vals-> mean; + x_vals-> nsig2+= x_vals-> difs[ i ] * x_vals-> difs[ i ]; + } + x_vals-> err_term= ( 1.0 / (double) n ) + ( ( x_vals-> mean * x_vals-> mean ) / x_vals-> nsig2 ); + + return( x_vals ); +} + +#define LINREG_START_DEFN( TYPE ) static linreg_seq_ ## TYPE *linreg_start_ ## TYPE( IMAGE *out, IMAGE **ins, x_set *x_vals ){ \ + linreg_seq_ ## TYPE *seq= IM_NEW( NULL, linreg_seq_ ## TYPE ); \ + \ + if( ! seq ) \ + return NULL; \ + \ + seq-> regs= im_start_many( NULL, ins, NULL ); \ + seq-> ptrs= IM_ARRAY( NULL, x_vals-> n, TYPE* ); \ + seq-> skips= IM_ARRAY( NULL, x_vals-> n, size_t ); \ + \ + if( ! seq-> ptrs || ! seq-> regs || ! seq-> skips ){ \ + im_stop_many( seq-> regs, NULL, NULL ); \ + im_free( seq-> ptrs ); \ + im_free( seq-> skips ); \ + im_free( seq ); \ + return NULL; \ + } \ + return seq; \ +} + +#define N ( (double) n ) +#define y(a) ( (double) (* seq-> ptrs[(a)] ) ) +#define x(a) ( (double) ( x_vals-> xs[(a)] ) ) +#define xd(a) ( (double) ( x_vals-> difs[(a)] ) ) +#define Sxd2 ( x_vals-> nsig2 ) +#define mean_x ( x_vals-> mean ) +#define mean_y ( out[0] ) +#define dev_y ( out[1] ) +#define y_x0 ( out[2] ) +#define d_y_x0 ( out[3] ) +#define dy_dx ( out[4] ) +#define d_dy_dx ( out[5] ) +#define R ( out[6] ) + +#define LINREG_GEN_DEFN( TYPE ) static int linreg_gen_ ## TYPE( REGION *to_make, linreg_seq_ ## TYPE *seq, void *unrequired, x_set *x_vals ){ \ + unsigned int n= x_vals-> n; \ + double *out= (double*) IM_REGION_ADDR_TOPLEFT( to_make ); \ + size_t out_skip= IM_REGION_LSKIP( to_make ) / sizeof( double ); \ + double *out_end= out + out_skip * to_make-> valid. height; \ + double *out_stop; \ + size_t out_n= IM_REGION_N_ELEMENTS( to_make ); \ + int i; \ + \ + out_skip-= out_n; \ + \ + if( im_prepare_many( seq-> regs, & to_make-> valid ) ) \ + return -1; \ + \ + for( i= 0; i < n; ++i ){ \ + seq-> ptrs[ i ]= (TYPE*) IM_REGION_ADDR( seq-> regs[ i ], to_make-> valid. left, to_make-> valid. top ); \ + seq-> skips[ i ]= ( IM_REGION_LSKIP( seq-> regs[ i ] ) / sizeof( TYPE ) ) - IM_REGION_N_ELEMENTS( seq-> regs[ i ] ); \ + } \ + \ + for( ; out < out_end; out+= out_skip, skip_all_ ## TYPE( seq-> ptrs, seq-> skips, n ) ) \ + for( out_stop= out + out_n; out < out_stop; out+= 7, incr_all_ ## TYPE( seq-> ptrs, n ) ){ \ + double Sy= 0.0; \ + double Sxd_y= 0.0; \ + double Syd2= 0.0; \ + double Sxd_yd= 0.0; \ + double Se2= 0.0; \ + \ + for( i= 0; i < n; ++i ){ \ + Sy+= y(i); \ + Sxd_y+= xd(i) * y(i); \ + } \ + mean_y= Sy / N; \ + dy_dx= Sxd_y / Sxd2; \ + y_x0= mean_y - dy_dx * mean_x; \ + \ + for( i= 0; i < n; ++i ){ \ + double yd= y(i) - mean_y; \ + double e= y(i) - dy_dx * x(i) - y_x0; \ + Syd2+= yd * yd; \ + Sxd_yd+= xd(i) * yd; \ + Se2+= e * e; \ + } \ + dev_y= sqrt( Syd2 / N ); \ + Se2/= ( N - 2.0 ); \ + d_dy_dx= sqrt( Se2 / Sxd2 ); \ + d_y_x0= sqrt( Se2 * x_vals-> err_term ); \ + R= Sxd_yd / sqrt( Sxd2 * Syd2 ); \ + } \ + return 0; \ +} + +#define LINREG_STOP_DEFN( TYPE ) static int linreg_stop_ ## TYPE( linreg_seq_ ## TYPE *seq ){ \ + im_stop_many( seq-> regs, NULL, NULL ); \ + im_free( seq-> ptrs ); \ + im_free( seq-> skips ); \ + im_free( seq ); \ + return 0; \ +} + +#define INCR_ALL_DEFN( TYPE ) static void incr_all_ ## TYPE( TYPE **ptrs, unsigned int n ){ \ + TYPE **stop= ptrs + n; \ + for( ; ptrs < stop; ++ptrs ) \ + ++*ptrs; \ +} + +#define SKIP_ALL_DEFN( TYPE ) static void skip_all_ ## TYPE( TYPE **ptrs, size_t *skips, unsigned int n ){ \ + TYPE **stop= ptrs + n; \ + for( ; ptrs < stop; ++ptrs, ++skips ) \ + *ptrs+= *skips; \ +} + +LINREG_START_DEFN( gint8 ); +LINREG_START_DEFN( guint8 ); +LINREG_START_DEFN( gint16 ); +LINREG_START_DEFN( guint16 ); +LINREG_START_DEFN( gint32 ); +LINREG_START_DEFN( guint32 ); +LINREG_START_DEFN( float ); +LINREG_START_DEFN( double ); + +LINREG_GEN_DEFN( gint8 ); +LINREG_GEN_DEFN( guint8 ); +LINREG_GEN_DEFN( gint16 ); +LINREG_GEN_DEFN( guint16 ); +LINREG_GEN_DEFN( gint32 ); +LINREG_GEN_DEFN( guint32 ); +LINREG_GEN_DEFN( float ); +LINREG_GEN_DEFN( double ); + +LINREG_STOP_DEFN( gint8 ); +LINREG_STOP_DEFN( guint8 ); +LINREG_STOP_DEFN( gint16 ); +LINREG_STOP_DEFN( guint16 ); +LINREG_STOP_DEFN( gint32 ); +LINREG_STOP_DEFN( guint32 ); +LINREG_STOP_DEFN( float ); +LINREG_STOP_DEFN( double ); + +INCR_ALL_DEFN( gint8 ); +INCR_ALL_DEFN( guint8 ); +INCR_ALL_DEFN( gint16 ); +INCR_ALL_DEFN( guint16 ); +INCR_ALL_DEFN( gint32 ); +INCR_ALL_DEFN( guint32 ); +INCR_ALL_DEFN( float ); +INCR_ALL_DEFN( double ); + +SKIP_ALL_DEFN( gint8 ); +SKIP_ALL_DEFN( guint8 ); +SKIP_ALL_DEFN( gint16 ); +SKIP_ALL_DEFN( guint16 ); +SKIP_ALL_DEFN( gint32 ); +SKIP_ALL_DEFN( guint32 ); +SKIP_ALL_DEFN( float ); +SKIP_ALL_DEFN( double ); diff --git a/libsrc/arithmetic/im_lintra.c b/libsrc/arithmetic/im_lintra.c new file mode 100644 index 00000000..7bc89ba3 --- /dev/null +++ b/libsrc/arithmetic/im_lintra.c @@ -0,0 +1,383 @@ +/* @(#) Pass an image through a linear transform - ie. out = in*a + b. Output + * @(#) is always float for integer input, double for double input, complex for + * @(#) complex input and double complex for double complex input. + * @(#) + * @(#) int + * @(#) im_lintra( a, in, b, out ) + * @(#) IMAGE *in, *out; + * @(#) double a, b; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 23/4/93 JC + * - adapted to work with partial images + * 1/7/93 JC + * - adapted for partial v2 + * 7/10/94 JC + * - new IM_NEW() + * - more typedefs + * 9/2/95 JC + * - adapted for im_wrap... + * - operations on complex images now just transform the real channel + * 29/9/95 JC + * - complex was broken + * 15/4/97 JC + * - return(0) missing from generate, arrgh! + * 1/7/98 JC + * - im_lintra_vec added + * 3/8/02 JC + * - fall back to im_copy() for a == 1, b == 0 + * 10/10/02 JC + * - auug, failing to multiply imag for complex! (thanks matt) + * 10/12/02 JC + * - removed im_copy() fallback ... meant that output format could change + * with value :-( very confusing + * 30/6/04 + * - added 1 band image * n band vector case + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Struct we need for im_generate(). + */ +typedef struct { + int n; /* Number of bands of constants */ + double *a, *b; +} LintraInfo; + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define LOOP(IN, OUT) { \ + IN *p = (IN *) in; \ + OUT *q = (OUT *) out; \ + \ + for( x = 0; x < sz; x++ ) \ + q[x] = a * (OUT) p[x] + b; \ +} + +/* Complex input, complex output. + */ +#define LOOPCMPLX(IN, OUT) {\ + IN *p = (IN *) in; \ + OUT *q = (OUT *) out; \ + \ + for( x = 0; x < sz; x++ ) { \ + q[0] = a * p[0] + b; \ + q[1] = a * p[1]; \ + q += 2; \ + p += 2; \ + } \ +} + +#ifdef HAVE_LIBOIL +/* Process granularity. + */ +#define CHUNKS (1000) + +/* d[] = s[] * b + c, with liboil + */ +static void +lintra_f32( float *d, float *s, int n, float b, float c ) +{ + float buf[CHUNKS]; + int i; + + for( i = 0; i < n; i += CHUNKS ) { + oil_scalarmultiply_f32_ns( buf, s, + &b, IM_MIN( CHUNKS, n - i ) ); + oil_scalaradd_f32_ns( d, buf, + &c, IM_MIN( CHUNKS, n - i ) ); + + s += CHUNKS; + d += CHUNKS; + } +} +#endif /*HAVE_LIBOIL*/ + +/* Lintra a buffer, 1 set of scale/offset. + */ +static int +lintra1_gen( PEL *in, PEL *out, int width, IMAGE *im, LintraInfo *inf ) +{ + double a = inf->a[0]; + double b = inf->b[0]; + int sz = width * im->Bands; + int x; + + /* Lintra all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: LOOP( unsigned char, float ); break; + case IM_BANDFMT_CHAR: LOOP( signed char, float ); break; + case IM_BANDFMT_USHORT: LOOP( unsigned short, float ); break; + case IM_BANDFMT_SHORT: LOOP( signed short, float ); break; + case IM_BANDFMT_UINT: LOOP( unsigned int, float ); break; + case IM_BANDFMT_INT: LOOP( signed int, float ); break; + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + lintra_f32( (float *) out, (float *) in, sz, a, b ); +#else /*!HAVE_LIBOIL*/ + LOOP( float, float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: LOOP( double, double ); break; + case IM_BANDFMT_COMPLEX: LOOPCMPLX( float, float ); break; + case IM_BANDFMT_DPCOMPLEX: LOOPCMPLX( double, double ); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define LOOPN(IN, OUT)\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( i = 0, x = 0; x < width; x++ )\ + for( k = 0; k < nb; k++, i++ )\ + q[i] = a[k] * (OUT) p[i] + b[k];\ + } + +/* Complex input, complex output. + */ +#define LOOPCMPLXN(IN, OUT)\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < width; x++ ) \ + for( k = 0; k < nb; k++ ) {\ + q[0] = a[k] * p[0] + b[k];\ + q[1] = a[k] * p[1];\ + q += 2;\ + p += 2;\ + }\ + } + +/* Lintra a buffer, n set of scale/offset. + */ +static int +lintran_gen( PEL *in, PEL *out, int width, IMAGE *im, LintraInfo *inf ) +{ + double *a = inf->a; + double *b = inf->b; + int nb = im->Bands; + int i, x, k; + + /* Lintra all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: LOOPN( unsigned char, float ); break; + case IM_BANDFMT_CHAR: LOOPN( signed char, float ); break; + case IM_BANDFMT_USHORT: LOOPN( unsigned short, float ); break; + case IM_BANDFMT_SHORT: LOOPN( signed short, float ); break; + case IM_BANDFMT_UINT: LOOPN( unsigned int, float ); break; + case IM_BANDFMT_INT: LOOPN( signed int, float ); break; + case IM_BANDFMT_FLOAT: LOOPN( float, float ); break; + case IM_BANDFMT_DOUBLE: LOOPN( double, double ); break; + case IM_BANDFMT_COMPLEX: LOOPCMPLXN( float, float ); break; + case IM_BANDFMT_DPCOMPLEX: LOOPCMPLXN( double, double ); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* 1 band image, n band vector. + */ +#define LOOPNV(IN, OUT) { \ + IN *p = (IN *) in; \ + OUT *q = (OUT *) out; \ + \ + for( i = 0, x = 0; x < width; x++ ) { \ + OUT v = p[x]; \ + \ + for( k = 0; k < nb; k++, i++ ) \ + q[i] = a[k] * v + b[k]; \ + } \ +} + +#define LOOPCMPLXNV(IN, OUT) { \ + IN *p = (IN *) in; \ + OUT *q = (OUT *) out; \ + \ + for( x = 0; x < width; x++ ) { \ + OUT p0 = p[0]; \ + OUT p1 = p[1]; \ + \ + for( k = 0; k < nb; k++ ) { \ + q[0] = a[k] * p0 + b[k]; \ + q[1] = a[k] * p1; \ + q += 2; \ + } \ + \ + p += 2; \ + } \ +} + +static int +lintranv_gen( PEL *in, PEL *out, int width, IMAGE *im, LintraInfo *inf ) +{ + double *a = inf->a; + double *b = inf->b; + int nb = inf->n; + int i, x, k; + + /* Lintra all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: LOOPNV( unsigned char, float ); break; + case IM_BANDFMT_CHAR: LOOPNV( signed char, float ); break; + case IM_BANDFMT_USHORT: LOOPNV( unsigned short, float ); break; + case IM_BANDFMT_SHORT: LOOPNV( signed short, float ); break; + case IM_BANDFMT_UINT: LOOPNV( unsigned int, float ); break; + case IM_BANDFMT_INT: LOOPNV( signed int, float ); break; + case IM_BANDFMT_FLOAT: LOOPNV( float, float ); break; + case IM_BANDFMT_DOUBLE: LOOPNV( double, double ); break; + case IM_BANDFMT_COMPLEX: LOOPCMPLXNV( float, float ); break; + case IM_BANDFMT_DPCOMPLEX: LOOPCMPLXNV( double, double ); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Linear transform n bands. + */ +int +im_lintra_vec( int n, double *a, IMAGE *in, double *b, IMAGE *out ) +{ + LintraInfo *inf; + int i; + + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_lintra_vec", _( "not uncoded" ) ); + return( -1 ); + } + + /* If n and bands differ, one of them must be one (and we multiplex + * the other). + */ + if( n != in->Bands && (n != 1 && in->Bands != 1) ) { + im_error( "im_lintra_vec", + _( "not 1 or %d elements in vector" ), in->Bands ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_isint( in ) ) { + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + } + if( in->Bands == 1 ) + out->Bands = n; + + /* Make space for a little buffer. + */ + if( !(inf = IM_NEW( out, LintraInfo )) || + !(inf->a = IM_ARRAY( out, n, double )) || + !(inf->b = IM_ARRAY( out, n, double )) ) + return( -1 ); + inf->n = n; + for( i = 0; i < n; i++ ) { + inf->a[i] = a[i]; + inf->b[i] = b[i]; + } + + /* Generate! + */ + if( n == 1 ) { + if( im_wrapone( in, out, + (im_wrapone_fn) lintra1_gen, in, inf ) ) + return( -1 ); + } + else if( in->Bands == 1 ) { + if( im_wrapone( in, out, + (im_wrapone_fn) lintranv_gen, in, inf ) ) + return( -1 ); + } + else { + if( im_wrapone( in, out, + (im_wrapone_fn) lintran_gen, in, inf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Linear transform. + */ +int +im_lintra( double a, IMAGE *in, double b, IMAGE *out ) +{ + return( im_lintra_vec( 1, &a, in, &b, out ) ); +} diff --git a/libsrc/arithmetic/im_litecor.c b/libsrc/arithmetic/im_litecor.c new file mode 100644 index 00000000..9b4aa577 --- /dev/null +++ b/libsrc/arithmetic/im_litecor.c @@ -0,0 +1,321 @@ +/* @(#) Function to perform lighting correction. + * @(#) One band IM_BANDFMT_UCHAR images only. Always writes UCHAR. + * @(#) + * @(#) Function im_litecor() assumes that imin + * @(#) is either memory mapped or in a buffer. + * @(#) + * @(#) int im_litecor(in, w, out, clip, factor) + * @(#) IMAGE *in, *w, *out; + * @(#) int clip; + * @(#) double factor; + * @(#) + * @(#) clip==1 + * @(#) - Compute max(white)*factor*(image/white), Clip to 255. + * @(#) clip==0 + * @(#) - Compute factor for you. + * @(#) + * @(#) + * @(#) + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, J. Cupitt, 1991 N. Dessipris + * + * Author: J. Cupitt, N. Dessipris + * Written on: 02/08/1990 + * Modified on : 6/11/1991, by ND to produce a UCHAR output + * 1/4/93 J.Cupitt + * - bugs if white is smaller than image fixed + * - im_warning() now called + * - clip==0 case not tested or changed! do not use! + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* If maximum output is > 255 scale output between minout and maxout, + * by normalising maxout to 255. + * If maximum output is < 255 do the light correction without scaling + */ +static int +im_litecor0( in, white, out ) +IMAGE *in, *white, *out; +{ PEL *p, *w; + PEL *q, *bu; + int c; + int x, y; + float xrat = (float) in->Xsize / white->Xsize; + float yrat = (float) in->Ysize / white->Ysize; + int xstep = (int) xrat; + int ystep = (int) yrat; + double max; + int wtmp, maxw, maxout, temp; + + /* Check white is some simple multiple of image. + */ + if( xrat < 1.0 || xrat != xstep || yrat < 1.0 || yrat != ystep ) { + im_error( "im_litecor", "white not simple scale of image" ); + return( -1 ); + } + + /* Find the maximum of the white. + */ + if( im_max( white, &max ) ) + return( -1 ); + maxw = (int)max; + + /* Set up the output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_setupout( out ) ) + return( -1 ); + + /* Make buffer for outputting to. + */ + if( !(bu = (PEL *) im_malloc( out, out->Xsize )) ) + return( -1 ); + + /* Find largest value we might generate if factor == 1.0 + */ + maxout = -1; + p = (PEL *) in->data; + for( y = 0; y < in->Ysize; y++ ) { + /* Point w to the start of the line in the white + * corresponding to the line we are about to correct. c counts + * up to xstep; each time it wraps, we should move w on one. + */ + w = (PEL *) (white->data + white->Xsize * (int)(y/ystep)); + c = 0; + + /* Scan along line. + */ + for( x = 0; x < out->Xsize; x++ ) { + wtmp = (int)*w; + temp = ( maxw * (int) *p++ + (wtmp>>1) ) / wtmp; + if (temp > maxout ) + maxout = temp; + + /* Move white pointer on if necessary. */ + c++; + if( c == xstep ) { + w++; + c = 0; + } + } + } + + /* Do exactly the same as above by scaling the result with respect to + * maxout + */ + p = (PEL *) in->data; + if (maxout <= 255 ) /* no need for rescaling output */ + { + for( y = 0; y < in->Ysize; y++ ) + { + q = bu; + w = (PEL *) (white->data + + white->Xsize * (int)(y/ystep)); + c = 0; + + /* Scan along line. */ + for( x = 0; x < in->Xsize; x++ ) + { + wtmp = (int)*w; + *q++ = (PEL) + ( ( maxw * (int) *p++ + (wtmp>>1) ) / wtmp ); + /* Move white pointer on if necessary. + */ + c++; + if( c == xstep ) { w++; c = 0; } + } + if( im_writeline( y, out, bu ) ) + { + im_error("im_litecor", "im_writeline failed"); + return( -1 ); + } + } + } + else /* rescale output wrt maxout */ + { + for( y = 0; y < in->Ysize; y++ ) + { + q = bu; + w = (PEL *) (white->data + + white->Xsize * (int)(y/ystep)); + c = 0; + + /* Scan along line. */ + for( x = 0; x < in->Xsize; x++ ) + { + wtmp = maxout * ((int)*w); + *q++ = (PEL) + ( ( maxw * (int) *p++ * 255 + (wtmp>>1)) / wtmp ); + /* Move white pointer on if necessary. + */ + c++; + if( c == xstep ) { w++; c = 0; } + } + if( im_writeline( y, out, bu ) ) + { + im_error("im_litecor", "im_writeline failed"); + return( -1 ); + } + } + } + + return( 0 ); +} + +/* Clip all corrected values above 255, if any. + */ +static int +im_litecor1( in, white, out, factor ) +IMAGE *in, *white, *out; +double factor; +{ PEL *p, *w; + PEL *q, *bu; + int c; + int x, y; + float xrat = (float) in->Xsize / white->Xsize; + float yrat = (float) in->Ysize / white->Ysize; + int xstep = (int) xrat; + int ystep = (int) yrat; + double max; + double maxw, temp; + int nclipped = 0; + + /* Check white is some simple multiple of image. + */ + if( xrat < 1.0 || xrat != xstep || yrat < 1.0 || yrat != ystep ) { + im_error( "im_litecor", "white not simple scale of image" ); + return( -1 ); + } + + /* Find the maximum of the white. + */ + if( im_max( white, &max ) ) + return( -1 ); + maxw = max; + + /* Set up the output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_setupout( out ) ) + return( -1 ); + + /* Make buffer we write to. + */ + if( !(bu = (PEL *) im_malloc( out, out->Xsize )) ) + return( -1 ); + + /* Loop through sorting max output + */ + p = (PEL *) in->data; + for( y = 0; y < in->Ysize; y++ ) { + q = bu; + w = (PEL *) (white->data + white->Xsize * (int)(y / ystep)); + c = 0; + + for( x = 0; x < out->Xsize; x++ ) { + temp = ((factor * maxw * (int) *p++)/((int) *w)) + 0.5; + if( temp > 255.0 ) { + temp = 255; + nclipped++; + } + *q++ = temp; + + /* Move white pointer on if necessary. + */ + c++; + if( c == xstep ) { + w++; + c = 0; + } + } + + if( im_writeline( y, out, bu ) ) + return( -1 ); + } + + if( nclipped ) + im_warn( "im_litecor", "%d pels over 255 clipped", nclipped ); + + return( 0 ); +} + +/* Lighting correction. One band uchar images only. + * Assumes the white is some simple multiple of the image in size; ie. the + * white has been taken with some smaller or equal set of resolution + * parameters. + */ +int +im_litecor( in, white, out, clip, factor ) +IMAGE *in, *white, *out; +int clip; +double factor; +{ /* Check our args. + */ + if( im_iocheck( in, out ) ) + return( -1 ); + if( in->Bands != 1 || in->Bbits != 8 || + in->Coding != IM_CODING_NONE || in->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_litecor", "bad input format" ); + return( -1 ); + } + if( white->Bands != 1 || white->Bbits != 8 || + white->Coding != IM_CODING_NONE || white->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_litecor", "bad white format" ); + return( -1 ); + } + + switch( clip ) { + case 1: + return( im_litecor1( in, white, out, factor ) ); + + case 0: + return( im_litecor0( in, white, out ) ); + + default: + im_error( "im_litecor", "unknown flag %d", clip ); + return( -1 ); + } +} diff --git a/libsrc/arithmetic/im_log10tra.c b/libsrc/arithmetic/im_log10tra.c new file mode 100644 index 00000000..4b450c65 --- /dev/null +++ b/libsrc/arithmetic/im_log10tra.c @@ -0,0 +1,166 @@ +/* @(#) Find base 10 log of any non-complex image. Output + * @(#) is always float for integer input and double for double input. + * @(#) + * @(#) int + * @(#) im_log10tra( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 5/5/93 JC + * - adapted from im_lintra to work with partial images + * - incorrect implementation of complex logs removed + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define loop(IN, OUT)\ + for( y = to; y < bo; y++ ) {\ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y );\ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ )\ + *q++ = log10( *p++ );\ + } + +/* log10tra a small area. + */ +static int +log10tra_gen( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + int x, y; + + /* Ask for input we need. + */ + if( im_prepare( ir, r ) ) + return( -1 ); + + /* log10tra all input types. + */ + switch( ir->im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop(unsigned char, float); break; + case IM_BANDFMT_CHAR: loop(signed char, float); break; + case IM_BANDFMT_USHORT: loop(unsigned short, float); break; + case IM_BANDFMT_SHORT: loop(signed short, float); break; + case IM_BANDFMT_UINT: loop(unsigned int, float); break; + case IM_BANDFMT_INT: loop(signed int, float); break; + case IM_BANDFMT_FLOAT: loop(float, float); break; + case IM_BANDFMT_DOUBLE: loop(double, double); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Log 10 transform. + */ +int +im_log10tra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_log10tra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_log10tra", _( "not non-complex" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, log10tra_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_logtra.c b/libsrc/arithmetic/im_logtra.c new file mode 100644 index 00000000..e56dfa84 --- /dev/null +++ b/libsrc/arithmetic/im_logtra.c @@ -0,0 +1,166 @@ +/* @(#) Find natural log of any non-complex image. Output + * @(#) is always float for integer input and double for double input. + * @(#) + * @(#) int + * @(#) im_logtra( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 5/5/93 JC + * - adapted from im_lintra to work with partial images + * - incorrect implementation of complex logs removed + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define loop(IN, OUT)\ + for( y = to; y < bo; y++ ) {\ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y );\ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ )\ + *q++ = log( *p++ );\ + } + +/* logtra a small area. + */ +static int +logtra_gen( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + int x, y; + + /* Ask for input we need. + */ + if( im_prepare( ir, r ) ) + return( -1 ); + + /* logtra all input types. + */ + switch( ir->im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop(unsigned char, float); break; + case IM_BANDFMT_CHAR: loop(signed char, float); break; + case IM_BANDFMT_USHORT: loop(unsigned short, float); break; + case IM_BANDFMT_SHORT: loop(signed short, float); break; + case IM_BANDFMT_UINT: loop(unsigned int, float); break; + case IM_BANDFMT_INT: loop(signed int, float); break; + case IM_BANDFMT_FLOAT: loop(float, float); break; + case IM_BANDFMT_DOUBLE: loop(double, double); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Log transform. + */ +int +im_logtra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_logtra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_logtra", _( "not non-complex" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, logtra_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_max.c b/libsrc/arithmetic/im_max.c new file mode 100644 index 00000000..d040bf6f --- /dev/null +++ b/libsrc/arithmetic/im_max.c @@ -0,0 +1,242 @@ +/* @(#) Function to find the maximim of an image. Works for any + * @(#) image type. Returns a double. + * @(#) + * @(#) int im_max(in, max) + * @(#) IMAGE *in; + * @(#) double *max; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/05/1990 + * Modified on : 18/03/1991, N. Dessipris + * 7/7/93 JC + * - complex case fixed + * - im_incheck() call added + * 20/6/95 JC + * - now returns double + * - modernised a little + * - now returns max square amplitude rather than amplitude for complex + * 9/5/02 JC + * - partialed + * 3/4/02 JC + * - random wrong result for >1 thread :-( (thanks Joe) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Per-call state. + */ +typedef struct _MaxInfo { + /* Parameters. + */ + IMAGE *in; + double *out; + + /* Global max so far. + */ + double value; + int valid; /* zero means value is unset */ +} MaxInfo; + +/* Per thread state. + */ +typedef struct _Seq { + MaxInfo *inf; + + double value; + int valid; /* zero means value is unset */ +} Seq; + +/* New sequence value. + */ +static void * +start_fn( MaxInfo *inf ) +{ + Seq *seq = IM_NEW( NULL, Seq ); + + seq->inf = inf; + seq->valid = 0; + + return( (void *) seq ); +} + +/* Merge the sequence value back into the per-call state. + */ +static int +stop_fn( Seq *seq, MaxInfo *inf ) +{ + if( seq->valid ) { + if( !inf->valid ) + /* Just copy. + */ + inf->value = seq->value; + else + /* Merge. + */ + inf->value = IM_MAX( inf->value, seq->value ); + + inf->valid = 1; + } + + im_free( seq ); + + return( 0 ); +} + +/* Loop over region, adding to seq. + */ +static int +scan_fn( REGION *reg, Seq *seq ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int nel = IM_REGION_N_ELEMENTS( reg ); + + int x, y; + + double m; + +#define loop(TYPE) { \ + m = *((TYPE *) IM_REGION_ADDR( reg, le, to )); \ + \ + for( y = to; y < bo; y++ ) { \ + TYPE *p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( x = 0; x < nel; x++ ) { \ + double v = p[x]; \ + \ + if( v > m ) \ + m = v; \ + } \ + } \ +} + +#define complex_loop(TYPE) { \ + TYPE *p = (TYPE *) IM_REGION_ADDR( reg, le, to ); \ + double real = p[0]; \ + double imag = p[1]; \ + \ + m = real * real + imag * imag; \ + \ + for( y = to; y < bo; y++ ) { \ + TYPE *p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( x = 0; x < nel * 2; x += 2 ) { \ + double mod; \ + \ + real = p[x]; \ + imag = p[x + 1]; \ + mod = real * real + imag * imag; \ + \ + if( mod > m ) \ + m = mod; \ + } \ + } \ +} + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop( unsigned char ); break; + case IM_BANDFMT_CHAR: loop( signed char ); break; + case IM_BANDFMT_USHORT: loop( unsigned short ); break; + case IM_BANDFMT_SHORT: loop( signed short ); break; + case IM_BANDFMT_UINT: loop( unsigned int ); break; + case IM_BANDFMT_INT: loop( signed int ); break; + case IM_BANDFMT_FLOAT: loop( float ); break; + case IM_BANDFMT_DOUBLE: loop( double ); break; + case IM_BANDFMT_COMPLEX: complex_loop( float ); break; + case IM_BANDFMT_DPCOMPLEX: complex_loop( double ); break; + + default: + assert( 0 ); + } + + if( seq->valid ) { + seq->value = IM_MAX( seq->value, m ); + } + else { + seq->value = m; + seq->valid = 1; + } + +#ifdef DEBUG + printf( "im_max: left = %d, top = %d, width = %d, height = %d\n", + r->left, r->top, r->width, r->height ); + printf( " (max = %g)\n", seq->value ); +#endif /*DEBUG*/ + + return( 0 ); +} + +int +im_max( IMAGE *in, double *out ) +{ + MaxInfo inf; + + inf.in = in; + inf.out = out; + inf.valid = 0; + + if( im_pincheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_max", _( "not uncoded" ) ); + return( -1 ); + } + + if( im_iterate( in, start_fn, scan_fn, stop_fn, &inf, NULL ) ) + return( -1 ); + + *out = inf.value; + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_maxpos.c b/libsrc/arithmetic/im_maxpos.c new file mode 100644 index 00000000..32cd8284 --- /dev/null +++ b/libsrc/arithmetic/im_maxpos.c @@ -0,0 +1,150 @@ +/* @(#) Function to find the maximum of an image. Works for any + * @(#) image type. Returns a double and the location of max. + * @(#) + * @(#) Function im_maxpos() assumes that input + * @(#) is either memory mapped or in a buffer. + * @(#) + * @(#) int im_maxpos(in, xpos, ypos, max) + * @(#) IMAGE *in; + * @(#) int *xpos, *ypos; + * @(#) double *max; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/05/1990 + * Modified on : 18/03/1991, N. Dessipris + * 23/11/92: J.Cupitt - correct result for more than 1 band now. + * 23/7/93 JC + * - im_incheck() call added + * 20/6/95 JC + * - now returns double for value, like im_max() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Useful: Call a macro with the name, type pairs for all VIPS functions. + */ +#define im_for_all_types() \ + case IM_BANDFMT_UCHAR: loop(unsigned char); break; \ + case IM_BANDFMT_CHAR: loop(signed char); break; \ + case IM_BANDFMT_USHORT: loop(unsigned short); break; \ + case IM_BANDFMT_SHORT: loop(signed short); break; \ + case IM_BANDFMT_UINT: loop(unsigned int); break; \ + case IM_BANDFMT_INT: loop(signed int); break; \ + case IM_BANDFMT_FLOAT: loop(float); break; \ + case IM_BANDFMT_DOUBLE: loop(double); break; \ + case IM_BANDFMT_COMPLEX: loopcmplx(float); break; \ + case IM_BANDFMT_DPCOMPLEX: loopcmplx(double); break; + +/* Find the position of the maximum of an image. Take any format, returns a + * float and its position. + */ +int +im_maxpos( IMAGE *in, int *xpos, int *ypos, double *out ) +{ + double m; + int xp=0, yp=0; + int os; + +/* Check our args. */ + if( im_incheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_maxpos", _( "not uncoded" ) ); + return( -1 ); + } + +/* What type? First define the loop we want to perform for all types. */ +#define loop(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y; \ + m = (double) *p; \ + \ + for ( y=0; yYsize; y++ ) \ + for ( x=0; x m ) {\ + m = (double) *p; \ + xp = x; yp = y; \ + }\ + p++ ;\ + }\ + } + +#define loopcmplx(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + double re=(double)*p;\ + double im=(double)*(p+1);\ + double mod = re * re + im * im;\ + int x, y; \ + m = mod; \ + \ + for ( y=0; yYsize; y++ ) \ + for ( x=0; x m ) {\ + m = mod; \ + xp = x; yp = y; \ + }\ + }\ + } + +/* Now generate code for all types. */ + os = in->Xsize * in->Bands; + switch( in->BandFmt ) { + im_for_all_types(); + default: { + assert( 0 ); + return( -1 ); + } + } + + /* Return maxima and position of maxima. Nasty: we divide the xpos by + * the number of bands to get the position in pixels. + */ + *out = m; + *xpos = xp / in->Bands; + *ypos = yp; + return( 0 ); +} diff --git a/libsrc/arithmetic/im_maxpos_avg.c b/libsrc/arithmetic/im_maxpos_avg.c new file mode 100644 index 00000000..4d2bbe11 --- /dev/null +++ b/libsrc/arithmetic/im_maxpos_avg.c @@ -0,0 +1,201 @@ +/* @(#) Function to find the maximum of an image. Returns coords and value at + * @(#) double precision. In the event of a draw, returns average of all + * @(#) drawing coords, and interpolated value at that position. + * @(#) + * @(#) int im_maxpos_avg( + * @(#) IMAGE *im, + * @(#) double *xpos, + * @(#) double *ypos, + * @(#) double *out + * @(#) ); + * @(#) + * + * Copyright: 2006, The Nottingham Trent University + * Copyright: 2006, Tom Vajzovic + * + * Author: Tom Vajzovic + * + * Written on: 2006-09-25 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + + +/** LOCAL TYPES **/ + +typedef struct { + double x_avg; + double y_avg; + double val; + unsigned int occurances; + +} pos_avg_t; + + +/** LOCAL FUNCTIONS DECLARATIONS **/ + +static pos_avg_t * +maxpos_avg_start( + IMAGE *im +); +static int /* should be void (always returns 0) */ +maxpos_avg_scan( + REGION *reg, + pos_avg_t *seq +); +static int +maxpos_avg_stop( + pos_avg_t *seq, + pos_avg_t *master +); + + +/** EXPORTED FUNCTION **/ + +int im_maxpos_avg( IMAGE *im, double *xpos, double *ypos, double *out ){ +#define FUNCTION_NAME "im_maxpos_avg" + + pos_avg_t master= { 0.0, 0.0, 0.0, 0 }; + + if( im_pincheck( im ) ) + return -1; + + if( im-> Coding ){ + im_error( FUNCTION_NAME, _("uncoded images only") ); + return -1; + } + if( !( im_isint( im ) || im_isfloat( im ) ) ){ + im_error( FUNCTION_NAME, _("scalar images only") ); + return -1; + } + if( 1 != im-> Bands ){ + im_error( FUNCTION_NAME, _("single band images only") ); + return -1; + } + if( ! xpos || ! ypos || ! out ){ + im_error( FUNCTION_NAME, _("invalid argument") ); + return -1; + } + if( im_iterate( im, (void*)maxpos_avg_start, maxpos_avg_scan, maxpos_avg_stop, &master, NULL ) ) + return -1; + + *xpos= master. x_avg / master. occurances; + *ypos= master. y_avg / master. occurances; + + return im_point_bilinear( im, *xpos, *ypos, 0, out ); + +#undef FUNCTION_NAME +} + +static pos_avg_t *maxpos_avg_start( IMAGE *im ){ + pos_avg_t *seq; + + seq= im_malloc( NULL, sizeof( pos_avg_t ) ); + if( ! seq ) + return NULL; + + seq-> x_avg= 0.0; + seq-> y_avg= 0.0; + seq-> val= 0.0; + seq-> occurances= 0; + + return seq; +} + +/* should be void (always returns 0) */ +static int maxpos_avg_scan( REGION *reg, pos_avg_t *seq ){ + + const int right= reg-> valid. left + reg-> valid. width; + const int bottom= reg-> valid. top + reg-> valid. height; + int x; + int y; + +#define LOOPS(type){ \ + type *read= (type*) IM_REGION_ADDR( reg, reg-> valid. left, reg-> valid. top ) - reg-> valid. left; \ + size_t skip= IM_REGION_LSKIP( reg ) / sizeof( type ); \ + \ + for( y= reg-> valid. top; y < bottom; ++y, read+= skip ) \ + for( x= reg-> valid. left; x < right; ++x ) \ + if( read[x] > seq-> val ){ \ + seq-> val= read[x]; \ + seq-> x_avg= x; \ + seq-> y_avg= y; \ + seq-> occurances= 1; \ + } \ + else if( read[x] == seq-> val ){ \ + seq-> x_avg+= x; \ + seq-> y_avg+= y; \ + ++ (seq-> occurances); \ + } \ +} + + switch( reg-> im-> BandFmt ){ + case IM_BANDFMT_CHAR: LOOPS( gint8 ) break; + case IM_BANDFMT_UCHAR: LOOPS( guint8 ) break; + case IM_BANDFMT_SHORT: LOOPS( gint16 ) break; + case IM_BANDFMT_USHORT: LOOPS( guint16 ) break; + case IM_BANDFMT_INT: LOOPS( gint32 ) break; + case IM_BANDFMT_UINT: LOOPS( guint32 ) break; + case IM_BANDFMT_FLOAT: LOOPS( float ) break; + case IM_BANDFMT_DOUBLE: LOOPS( double ) break; + } + + return 0; +#undef LOOPS +} + +/* should be void (always returns 0) */ +static int maxpos_avg_stop( pos_avg_t *seq, pos_avg_t *master ){ + + if( seq-> val > master-> val ){ + master-> val= seq-> val; + master-> x_avg= seq-> x_avg; + master-> y_avg= seq-> y_avg; + master-> occurances= seq-> occurances; + } + else if( seq-> val == master-> val ){ + master-> x_avg+= seq-> x_avg; + master-> y_avg+= seq-> y_avg; + master-> occurances+= seq-> occurances; + } + return im_free( seq ); +} + diff --git a/libsrc/arithmetic/im_maxpos_vec.c b/libsrc/arithmetic/im_maxpos_vec.c new file mode 100644 index 00000000..7dcda1e4 --- /dev/null +++ b/libsrc/arithmetic/im_maxpos_vec.c @@ -0,0 +1,459 @@ +/* @(#) Find the coordinates and values of the maxima of an image. + * @(#) + * @(#) int im_maxpos_vec( + * @(#) IMAGE *im, + * @(#) int *xpos, + * @(#) int *ypos, + * @(#) double *maxima, + * @(#) int n + * @(#) ); + * @(#) + * @(#) Find the coordinates and values of the minima of an image. + * @(#) + * @(#) int im_minpos_vec( + * @(#) IMAGE *im, + * @(#) int *xpos, + * @(#) int *ypos, + * @(#) double *minima, + * @(#) int n + * @(#) ); + * @(#) + * + * Copyright: 2006, The Nottingham Trent University + * Copyright: 2006, Tom Vajzovic + * + * Author: Tom Vajzovic + * + * Written on: 2006-09-01 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + + +/** TYPE DEFINITIONS **/ + +typedef struct { + + int *xs; + int *ys; + double *vals; + int *ptrs; + int start; + +} maxpos_list; + + +/** LOCAL FUNCTIONS DECLARATIONS **/ + +static maxpos_list *maxpos_list_alloc( int n ); +static void maxpos_list_free( maxpos_list *list ); + +static void maxpos_list_init( maxpos_list *list, int n ); +static maxpos_list *maxpos_vec_start( void *unrequired, int *n ); +static int maxpos_vec_scan( REGION *reg, maxpos_list *list ); +static void add_to_maxpos_list( maxpos_list *list, int x, int y, double val ); +static int maxpos_vec_stop( maxpos_list *list, int *n, maxpos_list *master_list ); + +static void minpos_list_init( maxpos_list *list, int n ); +static maxpos_list *minpos_vec_start( void *unrequired, int *n ); +static int minpos_vec_scan( REGION *reg, maxpos_list *list ); +static void add_to_minpos_list( maxpos_list *list, int x, int y, double val ); +static int minpos_vec_stop( maxpos_list *list, int *n, maxpos_list *master_list ); + + +/** EXPORTED FUNCTIONS **/ + +int im_maxpos_vec( IMAGE *im, int *xpos, int *ypos, double *maxima, int n ){ +#define FUNCTION_NAME "im_maxpos_vec" + /* number of sequences used is beyond my control at this level, but I note that */ + /* effeciency decreases as more sequences are used - speed may still increase */ + + int result; + int *pointers= im_malloc( NULL, n * sizeof( int* ) ); + maxpos_list master_list= { xpos, ypos, maxima, pointers, 0 }; + + if( im_pincheck( im ) ) + return -1; + + if( !pointers ) + return -1; + + if( ! ( im_isint( im ) || im_isfloat( im ) ) ){ + im_error( FUNCTION_NAME, _( "scalar images only" ) ); + return -1; + } + + if( 1 != im-> Bands ){ + im_error( FUNCTION_NAME, _( "single band images only" ) ); + return -1; + } + + if( IM_CODING_NONE != im-> Coding ){ + im_error( FUNCTION_NAME, _( "uncoded images only" ) ); + return -1; + } + + if( ! xpos || ! ypos || ! maxima || n < 1 ){ + im_error( FUNCTION_NAME, _( "invalid argument" ) ); + return -1; + } + + maxpos_list_init( &master_list, n ); + + result= im_iterate( im, (void*)maxpos_vec_start, maxpos_vec_scan, maxpos_vec_stop, &n, &master_list ); + + im_free( pointers ); + + return result; +#undef FUNCTION_NAME +} + + +int im_minpos_vec( IMAGE *im, int *xpos, int *ypos, double *minima, int n ){ +#define FUNCTION_NAME "im_minpos_vec" + /* number of sequences used is beyond my control at this level, but I note that */ + /* effeciency decreases as more sequences are used - speed may still increase */ + + int result; + int *pointers= im_malloc( NULL, n * sizeof( int* ) ); + maxpos_list master_list= { xpos, ypos, minima, pointers, 0 }; + + if( im_pincheck( im ) ) + return -1; + + if( !pointers ) + return -1; + + if( ! ( im_isint( im ) || im_isfloat( im ) ) ){ + im_error( FUNCTION_NAME, _( "scalar images only" ) ); + return -1; + } + + if( 1 != im-> Bands ){ + im_error( FUNCTION_NAME, _( "single band images only" ) ); + return -1; + } + + if( IM_CODING_NONE != im-> Coding ){ + im_error( FUNCTION_NAME, _( "uncoded images only" ) ); + return -1; + } + + if( ! xpos || ! ypos || ! minima || n < 1 ){ + im_error( FUNCTION_NAME, _( "invalid argument" ) ); + return -1; + } + + minpos_list_init( &master_list, n ); + + result= im_iterate( im, (void*)minpos_vec_start, minpos_vec_scan, minpos_vec_stop, &n, &master_list ); + + im_free( pointers ); + + return result; +#undef FUNCTION_NAME +} + + +/** LOCAL FUNCTION DEFINITIONS **/ + +static maxpos_list *maxpos_list_alloc( int n ){ + + maxpos_list *list= im_malloc( NULL, sizeof( maxpos_list ) ); + + if( ! list ) + return NULL; + + list-> xs= im_malloc( NULL, 3 * n * sizeof( int ) ); + list-> vals= im_malloc( NULL, n * sizeof( double ) ); + + if( ! list-> xs || ! list-> vals ){ + im_free( list-> xs ); + im_free( list-> vals ); + im_free( list ); + return NULL; + } + list-> ys= list-> xs + n; + list-> ptrs= list-> ys + n; + + return list; +} + +static void maxpos_list_free( maxpos_list *list ){ + im_free( list-> xs ); + im_free( list-> vals ); + im_free( list ); +} + + +static void maxpos_list_init( maxpos_list *list, int n ){ + int i; + + for( i= 0; i < n; ++i ){ + list-> xs[ i ]= 0; + list-> ys[ i ]= 0; + list-> vals[ i ]= 0; + list-> ptrs[ i ]= i + 1; + } + + list-> ptrs[ n - 1 ]= -1; + list-> start= 0; +} + +static maxpos_list *maxpos_vec_start( void *unrequired, int *n ){ + + maxpos_list *list= maxpos_list_alloc( *n ); + + if( ! list ) + return NULL; + + maxpos_list_init( list, *n ); + + return list; +} + +static int maxpos_vec_scan( REGION *reg, maxpos_list *list ){ + +#define MAXPOS_VEC_SCAN( type ){ \ + \ + int y= reg-> valid. top; \ + int x; \ + int ymax= y + reg-> valid. height; \ + int xmax= reg-> valid. left + reg-> valid. width; \ + \ + type *row= (type*)IM_REGION_ADDR( reg, reg-> valid. left, y ) - reg-> valid. left; \ + size_t skip= IM_REGION_LSKIP( reg ) / sizeof( type ); \ + \ + for( ; y < ymax; ++y, row+= skip ) \ + for( x= reg-> valid. left; x < xmax; ++x ) \ + if( row[ x ] > list-> vals[ list-> start ] ) \ + add_to_maxpos_list( list, x, y, row[ x ] ); \ +} + + switch( reg-> im-> BandFmt ){ + case IM_BANDFMT_UCHAR: MAXPOS_VEC_SCAN( guint8 ) break; + case IM_BANDFMT_CHAR: MAXPOS_VEC_SCAN( gint8 ) break; + case IM_BANDFMT_USHORT: MAXPOS_VEC_SCAN( guint16 ) break; + case IM_BANDFMT_SHORT: MAXPOS_VEC_SCAN( gint16 ) break; + case IM_BANDFMT_UINT: MAXPOS_VEC_SCAN( guint32 ) break; + case IM_BANDFMT_INT: MAXPOS_VEC_SCAN( gint32 ) break; + case IM_BANDFMT_FLOAT: MAXPOS_VEC_SCAN( float ) break; + case IM_BANDFMT_DOUBLE: MAXPOS_VEC_SCAN( double ) break; + } + +#undef MAXPOS_VEC_SCAN + + return 0; +} + +static void add_to_maxpos_list( maxpos_list *list, int x, int y, double val ){ + + int pointer= list-> start; + + while( -1 != list-> ptrs[ pointer ] && val > list-> vals[ list-> ptrs[ pointer ] ] ) + pointer= list-> ptrs[ pointer ]; + + list-> xs[ list-> start ]= x; + list-> ys[ list-> start ]= y; + list-> vals[ list-> start ]= val; + + if( list-> start != pointer ){ + /* we are adding mid-chain not at the very bottom */ + int second= list-> ptrs[ list-> start ]; + + list-> ptrs[ list-> start ]= list-> ptrs[ pointer ]; + list-> ptrs[ pointer ]= list-> start; + list-> start= second; + } +} + +static int maxpos_vec_stop( maxpos_list *list, int *n, maxpos_list *master_list ){ + + /* reverse list */ + + int prev= -1; + int pointer= list-> start; + + while( -1 != list-> ptrs[ pointer ] ){ + + int next= list-> ptrs[ pointer ]; + + list-> ptrs[ pointer ]= prev; + prev= pointer; + pointer= next; + } + list-> ptrs[ pointer ]= prev; + list-> start= pointer; + + /* add to main list */ + + for( ; -1 != pointer; pointer= list-> ptrs[ pointer ] ) + /* loop over all the ones found in this sequence */ + + if( list-> vals[ pointer ] > master_list-> vals[ master_list-> start ] ) + add_to_maxpos_list( master_list, list-> xs[ pointer ], list-> ys[ pointer ], list-> vals[ pointer ] ); + else + break; + /* since we are now high->low, if this one isn't big enough, none of the rest are */ + + maxpos_list_free( list ); + + return 0; +} + + +static void minpos_list_init( maxpos_list *list, int n ){ + int i; + + for( i= 0; i < n; ++i ){ + list-> xs[ i ]= 0; + list-> ys[ i ]= 0; + list-> vals[ i ]= DBL_MAX; + list-> ptrs[ i ]= i + 1; + } + + list-> ptrs[ n - 1 ]= -1; + list-> start= 0; +} + +static maxpos_list *minpos_vec_start( void *unrequired, int *n ){ + + maxpos_list *list= maxpos_list_alloc( *n ); + + if( ! list ) + return NULL; + + minpos_list_init( list, *n ); + + return list; +} + +static int minpos_vec_scan( REGION *reg, maxpos_list *list ){ + +#define MINPOS_VEC_SCAN( type ){ \ + \ + int y= reg-> valid. top; \ + int x; \ + int ymax= y + reg-> valid. height; \ + int xmax= reg-> valid. left + reg-> valid. width; \ + \ + type *row= (type*)IM_REGION_ADDR( reg, reg-> valid. left, y ) - reg-> valid. left; \ + size_t skip= IM_REGION_LSKIP( reg ) / sizeof( type ); \ + \ + for( ; y < ymax; ++y, row+= skip ) \ + for( x= reg-> valid. left; x < xmax; ++x ) \ + if( row[ x ] < list-> vals[ list-> start ] ) \ + add_to_minpos_list( list, x, y, row[ x ] ); \ +} + + switch( reg-> im-> BandFmt ){ + case IM_BANDFMT_UCHAR: MINPOS_VEC_SCAN( guint8 ) break; + case IM_BANDFMT_CHAR: MINPOS_VEC_SCAN( gint8 ) break; + case IM_BANDFMT_USHORT: MINPOS_VEC_SCAN( guint16 ) break; + case IM_BANDFMT_SHORT: MINPOS_VEC_SCAN( gint16 ) break; + case IM_BANDFMT_UINT: MINPOS_VEC_SCAN( guint32 ) break; + case IM_BANDFMT_INT: MINPOS_VEC_SCAN( gint32 ) break; + case IM_BANDFMT_FLOAT: MINPOS_VEC_SCAN( float ) break; + case IM_BANDFMT_DOUBLE: MINPOS_VEC_SCAN( double ) break; + } + +#undef MINPOS_VEC_SCAN + + return 0; +} + +static void add_to_minpos_list( maxpos_list *list, int x, int y, double val ){ + + int pointer= list-> start; + + while( -1 != list-> ptrs[ pointer ] && val < list-> vals[ list-> ptrs[ pointer ] ] ) + pointer= list-> ptrs[ pointer ]; + + list-> xs[ list-> start ]= x; + list-> ys[ list-> start ]= y; + list-> vals[ list-> start ]= val; + + if( list-> start != pointer ){ + /* we are adding mid-chain not at the very bottom */ + int second= list-> ptrs[ list-> start ]; + + list-> ptrs[ list-> start ]= list-> ptrs[ pointer ]; + list-> ptrs[ pointer ]= list-> start; + list-> start= second; + } +} + +static int minpos_vec_stop( maxpos_list *list, int *n, maxpos_list *master_list ){ + + /* reverse list */ + + int prev= -1; + int pointer= list-> start; + + while( -1 != list-> ptrs[ pointer ] ){ + + int next= list-> ptrs[ pointer ]; + + list-> ptrs[ pointer ]= prev; + prev= pointer; + pointer= next; + } + list-> ptrs[ pointer ]= prev; + list-> start= pointer; + + /* add to main list */ + + for( ; -1 != pointer; pointer= list-> ptrs[ pointer ] ) + /* loop over all the ones found in this sequence */ + + if( list-> vals[ pointer ] < master_list-> vals[ master_list-> start ] ) + add_to_minpos_list( master_list, list-> xs[ pointer ], list-> ys[ pointer ], list-> vals[ pointer ] ); + else + break; + /* since we are now high->low, if this one isn't big enough, none of the rest are */ + + maxpos_list_free( list ); + + return 0; +} + diff --git a/libsrc/arithmetic/im_measure.c b/libsrc/arithmetic/im_measure.c new file mode 100644 index 00000000..31d53fda --- /dev/null +++ b/libsrc/arithmetic/im_measure.c @@ -0,0 +1,210 @@ +/* Analyse a grid of colour patches, producing a DOUBLEMASK of averages. + * Pass an IMAGE, an IMAGE_BOX, the number of horizontal and vertical + * patches, an array giving the numbers of the patches to measure (patches are + * numbered left-to-right, top-to-bottom) and the name we should give the + * output mask. Return a DOUBLEMASK in which rows are patches and columns are + * bands. + * + * Example: 6 band image of 4x2 block of colour patches. + * + * +---+---+---+---+ + * | 1 | 2 | 3 | 4 | + * +---+---+---+---+ + * | 5 | 6 | 7 | 8 | + * +---+---+---+---+ + * + * Then call im_measure( im, box, 4, 2, { 2, 4 }, 2, "fred" ) makes a mask + * "fred" which has 6 columns, two rows. The first row contains the averages + * for patch 2, the second for patch 4. + * + * Modified: + * 19/8/94 JC + * - now uses doubles for addressing + * - could miss by up to h pixels previously! + * - ANSIfied + * - now issues warning if any deviations are greater than 20% of the + * mean + * 31/10/95 JC + * - more careful about warning for averages <0, or averages near zero + * - can get these cases with im_measure() of IM_TYPE_LAB images + * 28/10/02 JC + * - number bands from zero in error messages + * 7/7/04 + * - works on labq + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Measure into array. + */ +static int +measure_patches( IMAGE *im, double *coeff, IMAGE_BOX *box, + int h, int v, int *sel, int nsel ) +{ + IMAGE *tmp; + int patch; + IMAGE_BOX sub; + int i, j; + int m, n; + double avg, dev; + + /* How large are the patches we are to measure? + */ + double pw = (double) box->xsize / (double) h; + double ph = (double) box->ysize / (double) v; + + /* Set up sub to be the size we need for a patch. + */ + sub.xsize = (pw + 1) / 2; + sub.ysize = (ph + 1) / 2; + + /* Loop through sel, picking out areas to measure. + */ + for( j = 0, patch = 0; patch < nsel; patch++ ) { + /* Sanity check. Is the patch number sensible? + */ + if( sel[patch] <= 0 || sel[patch] > h*v ) { + im_error( "im_measure", + _( "patch %d is out of range" ), + sel[patch] ); + return( 1 ); + } + + /* Patch coordinates. + */ + m = (sel[patch] - 1) % h; + n = (sel[patch] - 1) / h; + + /* Move sub to correct position. + */ + sub.xstart = box->xstart + m*pw + (pw + 2)/4; + sub.ystart = box->ystart + n*ph + (ph + 2)/4; + + /* Loop through bands. + */ + for( i = 0; i < im->Bands; i++, j++ ) { + /* Make temp buffer to extract to. + */ + if( !(tmp = im_open( "patch", "t" )) ) + return( -1 ); + + /* Extract and measure. + */ + sub.chsel = i; + if( im_extract( im, tmp, &sub ) || + im_avg( tmp, &avg ) || + im_deviate( tmp, &dev ) ) { + im_close( tmp ); + return( -1 ); + } + im_close( tmp ); + + /* Is the deviation large compared with the average? + * This could be a clue that our parameters have + * caused us to miss the patch. Look out for averages + * <0, or averages near zero (can get these if use + * im_measure() on IM_TYPE_LAB images). + */ + if( dev*5 > fabs( avg ) && fabs( avg ) > 3 ) + im_warn( "im_measure", + _( "patch %d, band %d: " + "avg = %g, sdev = %g" ), + patch, i, avg, dev ); + + /* Save results. + */ + coeff[j] = avg; + } + } + + return( 0 ); +} + +/* Measure up image. + */ +DOUBLEMASK * +im_measure( IMAGE *im, IMAGE_BOX *box, int h, int v, + int *sel, int nsel, const char *name ) +{ + DOUBLEMASK *mask; + + /* Check input image. + */ + if( im->Coding == IM_CODING_LABQ ) { + IMAGE *t1; + + if( !(t1 = im_open( "measure-temp", "p" )) ) + return( NULL ); + if( im_LabQ2Lab( im, t1 ) || + !(mask = im_measure( t1, + box, h, v, sel, nsel, name )) ) { + im_close( t1 ); + return( NULL ); + } + + im_close( t1 ); + + return( mask ); + } + + if( im->Coding != IM_CODING_NONE ) { + im_error( "im_measure", _( "not uncoded" ) ); + return( NULL ); + } + if( im_iscomplex( im ) ) { + im_error( "im_measure", _( "bad input type" ) ); + return( NULL ); + } + + /* What size mask do we need? + */ + if( !(mask = im_create_dmask( name, im->Bands, nsel )) ) + return( NULL ); + + /* Perform measure and return. + */ + if( measure_patches( im, mask->coeff, box, h, v, sel, nsel ) ) { + im_free_dmask( mask ); + return( NULL ); + } + + return( mask ); +} diff --git a/libsrc/arithmetic/im_min.c b/libsrc/arithmetic/im_min.c new file mode 100644 index 00000000..c63cf92e --- /dev/null +++ b/libsrc/arithmetic/im_min.c @@ -0,0 +1,241 @@ +/* @(#) Function to find the minimum of an image. Works for any + * @(#) image type. Returns a double. + * @(#) + * @(#) int im_min(in, min) + * @(#) IMAGE *in; + * @(#) double *min; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/05/1990 + * Modified on : 18/03/1991, N. Dessipris + * 7/7/93 JC + * - complex case fixed + * - im_incheck() call added + * 20/6/95 JC + * - now returns double + * - modernised a little + * - now returns min square amplitude rather than amplitude for complex + * 9/5/02 JC + * - partialed, based in im_max() + * 3/4/02 JC + * - random wrong result for >1 thread :-( (thanks Joe) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Per-call state. + */ +typedef struct _MinInfo { + /* Parameters. + */ + IMAGE *in; + double *out; + + /* Global min so far. + */ + double value; + int valid; /* zero means value is unset */ +} MinInfo; + +/* Per thread state. + */ +typedef struct _Seq { + MinInfo *inf; + + double value; + int valid; /* zero means value is unset */ +} Seq; + +/* New sequence value. + */ +static void * +start_fn( MinInfo *inf ) +{ + Seq *seq = IM_NEW( NULL, Seq ); + + seq->inf = inf; + seq->valid = 0; + + return( (void *) seq ); +} + +/* Merge the sequence value back into the per-call state. + */ +static int +stop_fn( Seq *seq, MinInfo *inf ) +{ + if( seq->valid ) { + if( !inf->valid ) + /* Just copy. + */ + inf->value = seq->value; + else + /* Merge. + */ + inf->value = IM_MIN( inf->value, seq->value ); + + inf->valid = 1; + } + + im_free( seq ); + + return( 0 ); +} + +/* Loop over region, adding to seq. + */ +static int +scan_fn( REGION *reg, Seq *seq ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int nel = IM_REGION_N_ELEMENTS( reg ); + + int x, y; + + double m; + +#ifdef DEBUG + printf( "im_min: left = %d, top = %d, width = %d, height = %d\n", + r->left, r->top, r->width, r->height ); +#endif /*DEBUG*/ + +#define loop(TYPE) { \ + m = *((TYPE *) IM_REGION_ADDR( reg, le, to )); \ + \ + for( y = to; y < bo; y++ ) { \ + TYPE *p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( x = 0; x < nel; x++ ) { \ + double v = p[x]; \ + \ + if( v < m ) \ + m = v; \ + } \ + } \ +} + +#define complex_loop(TYPE) { \ + TYPE *p = (TYPE *) IM_REGION_ADDR( reg, le, to ); \ + double real = p[0]; \ + double imag = p[1]; \ + \ + m = real * real + imag * imag; \ + \ + for( y = to; y < bo; y++ ) { \ + TYPE *p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( x = 0; x < nel * 2; x += 2 ) { \ + double mod; \ + \ + real = p[x]; \ + imag = p[x + 1]; \ + mod = real * real + imag * imag; \ + \ + if( mod < m ) \ + m = mod; \ + } \ + } \ +} + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop( unsigned char ); break; + case IM_BANDFMT_CHAR: loop( signed char ); break; + case IM_BANDFMT_USHORT: loop( unsigned short ); break; + case IM_BANDFMT_SHORT: loop( signed short ); break; + case IM_BANDFMT_UINT: loop( unsigned int ); break; + case IM_BANDFMT_INT: loop( signed int ); break; + case IM_BANDFMT_FLOAT: loop( float ); break; + case IM_BANDFMT_DOUBLE: loop( double ); break; + case IM_BANDFMT_COMPLEX: complex_loop( float ); break; + case IM_BANDFMT_DPCOMPLEX: complex_loop( double ); break; + + default: + assert( 0 ); + } + + if( seq->valid ) { + seq->value = IM_MIN( seq->value, m ); + } + else { + seq->value = m; + seq->valid = 1; + } + + return( 0 ); +} + +int +im_min( IMAGE *in, double *out ) +{ + MinInfo inf; + + inf.in = in; + inf.out = out; + inf.valid = 0; + + if( im_pincheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_min", _( "not uncoded" ) ); + return( -1 ); + } + + if( im_iterate( in, start_fn, scan_fn, stop_fn, &inf, NULL ) ) + return( -1 ); + + *out = inf.value; + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_minpos.c b/libsrc/arithmetic/im_minpos.c new file mode 100644 index 00000000..98a1e966 --- /dev/null +++ b/libsrc/arithmetic/im_minpos.c @@ -0,0 +1,143 @@ +/* @(#) Function to find the minimim of an image. Works for any + * @(#) image type. Returns a double and the location of min + * @(#) + * @(#) Function im_minpos() assumes that input + * @(#) is either memory mapped or in a buffer. + * @(#) + * @(#) int im_minpos(in, xpos, ypos, out) + * @(#) IMAGE *in; + * @(#) int *xpos, *ypos; + * @(#) double *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/05/1990 + * Modified on : 18/03/1991, N. Dessipris + * 23/11/92 JC + * - correct result for more than 1 band now. + * 23/7/93 JC + * - im_incheck() added + * 20/6/95 JC + * - now returns double for value, like im_max() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Find the minimum of an image. Take any format, returns a double. */ +int +im_minpos( IMAGE *in, int *xpos, int *ypos, double *out ) +{ + double m; + int xp=0, yp=0; + int os; + +/* Check our args. */ + if( im_incheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) + { + im_error("im_minpos", _("input must be uncoded")); + return( -1 ); + } + +/* What type? First define the loop we want to perform for all types. */ +#define loop(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y; \ + m = (double) *p; \ + \ + for ( y=0; yYsize; y++ ) \ + for ( x=0; xdata; \ + double re=(double)*p;\ + double im=(double)*(p+1);\ + double mod = re * re + im * im;\ + int x, y; \ + m = mod; \ + \ + for ( y=0; yYsize; y++ ) \ + for ( x=0; xXsize * in->Bands; + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: loop(unsigned char); break; + case IM_BANDFMT_CHAR: loop(signed char); break; + case IM_BANDFMT_USHORT: loop(unsigned short); break; + case IM_BANDFMT_SHORT: loop(signed short); break; + case IM_BANDFMT_UINT: loop(unsigned int); break; + case IM_BANDFMT_INT: loop(signed int); break; + case IM_BANDFMT_FLOAT: loop(float); break; + case IM_BANDFMT_DOUBLE: loop(double); break; + case IM_BANDFMT_COMPLEX: loopcmplx(float); break; + case IM_BANDFMT_DPCOMPLEX: loopcmplx(double); break; + + default: + assert( 0 ); + } + + /* Take out bands on x. + */ + *out = m; + *xpos = xp / in->Bands; + *ypos = yp; + return( 0 ); +} diff --git a/libsrc/arithmetic/im_multiply.c b/libsrc/arithmetic/im_multiply.c new file mode 100644 index 00000000..5747aa18 --- /dev/null +++ b/libsrc/arithmetic/im_multiply.c @@ -0,0 +1,261 @@ +/* @(#) Multiply two images + * @(#) Images must have the same no of bands and can be of any type + * @(#) No check for overflow is carried out. + * @(#) + * @(#) int + * @(#) im_multiply(in1, in2, out) + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 29/4/93 JC + * - now works for partial images + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 19/10/93 JC + * - coredump-inducing bug in complex*complex fixed + * 13/12/93 + * - char*short bug fixed + * 12/6/95 JC + * - new im_add adapted to make new im_multiply + * 27/9/04 + * - updated for 1 band $op n band image -> n band image case + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Swap two IMAGE pointers. + */ +#define SWAP(A,B) { \ + IMAGE *t; \ + t = (A); (A) = (B); (B) = t; \ +} + +/* Complex multiply. + */ +#define cloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double x1 = p1[0];\ + double y1 = p1[1];\ + double x2 = p2[0];\ + double y2 = p2[1];\ + \ + p1 += 2;\ + p2 += 2;\ + \ + q[0] = x1 * x2 - y1 * y2;\ + q[1] = x1 * y2 + x2 * y1;\ + \ + q += 2;\ + }\ +} + +/* Real multiply. + */ +#define rloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = p1[x] * p2[x];\ +} + +static void +multiply_buffer( PEL **in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Multiply all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: rloop( signed char ); break; + case IM_BANDFMT_UCHAR: rloop( unsigned char ); break; + case IM_BANDFMT_SHORT: rloop( signed short ); break; + case IM_BANDFMT_USHORT: rloop( unsigned short ); break; + case IM_BANDFMT_INT: rloop( signed int ); break; + case IM_BANDFMT_UINT: rloop( unsigned int ); break; + + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + oil_multiply_f32( (float *) out, + (float *) in[0], (float *) in[1], sz ); +#else /*!HAVE_LIBOIL*/ + rloop( float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: rloop( double ); break; + case IM_BANDFMT_COMPLEX: cloop( float ); break; + case IM_BANDFMT_DPCOMPLEX: cloop( double ); break; + + default: + assert( 0 ); + } +} + +/* Save a bit of typing. + */ +#define UC IM_BANDFMT_UCHAR +#define C IM_BANDFMT_CHAR +#define US IM_BANDFMT_USHORT +#define S IM_BANDFMT_SHORT +#define UI IM_BANDFMT_UINT +#define I IM_BANDFMT_INT +#define F IM_BANDFMT_FLOAT +#define M IM_BANDFMT_COMPLEX +#define D IM_BANDFMT_DOUBLE +#define DM IM_BANDFMT_DPCOMPLEX + +/* Type conversions for two integer inputs. Rules for float and complex + * encoded with ifs. We are sign and value preserving. + */ +static int iformat[6][6] = { + /* UC C US S UI I */ +/* UC */ { US, S, UI, I, UI, I }, +/* C */ { S, S, I, I, I, I }, +/* US */ { UI, I, UI, I, UI, I }, +/* S */ { I, I, I, I, I, I }, +/* UI */ { UI, I, UI, I, UI, I }, +/* I */ { I, I, I, I, I, I } +}; + +int +im_multiply( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + /* Basic checks. + */ + if( im_piocheck( in1, out ) || im_pincheck( in2 ) ) + return( -1 ); + + if( in1->Xsize != in2->Xsize || in1->Ysize != in2->Ysize ) { + im_error( "im_multiply", _( "not same size" ) ); + return( -1 ); + } + if( in1->Bands != in2->Bands && + (in1->Bands != 1 && in2->Bands != 1) ) { + im_error( "im_multiply", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_multiply", _( "not uncoded" ) ); + return( -1 ); + } + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + + /* What number of bands will we write? + */ + out->Bands = IM_MAX( in1->Bands, in2->Bands ); + + /* Swap arguments to get the largest on the left. + */ + if( in1->Bbits < in2->Bbits ) + SWAP( in1, in2 ); + + /* What output type will we write? int, float or complex. + */ + if( im_iscomplex( in1 ) || im_iscomplex( in2 ) ) { + /* Make sure we have complex on the left. + */ + if( !im_iscomplex( in1 ) ) + SWAP( in1, in2 ); + + /* What kind of complex? + */ + if( in1->BandFmt == IM_BANDFMT_DPCOMPLEX ) + /* Output will be DPCOMPLEX. + */ + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + else + out->BandFmt = IM_BANDFMT_COMPLEX; + } + else if( im_isfloat( in1 ) || im_isfloat( in2 ) ) { + /* Make sure we have float on the left. + */ + if( !im_isfloat( in1 ) ) + SWAP( in1, in2 ); + + /* What kind of float? + */ + if( in1->BandFmt == IM_BANDFMT_DOUBLE ) + out->BandFmt = IM_BANDFMT_DOUBLE; + else + out->BandFmt = IM_BANDFMT_FLOAT; + } + else + /* Must be int+int = int. + */ + out->BandFmt = iformat[in2->BandFmt][in1->BandFmt]; + + /* And process! + */ + if( im__cast_and_call( in1, in2, out, + (im_wrapmany_fn) multiply_buffer, NULL ) ) + return( -1 ); + + /* Success! + */ + return( 0 ); +} diff --git a/libsrc/arithmetic/im_point_bilinear.c b/libsrc/arithmetic/im_point_bilinear.c new file mode 100644 index 00000000..b55d2bad --- /dev/null +++ b/libsrc/arithmetic/im_point_bilinear.c @@ -0,0 +1,127 @@ +/* @(#) Find the value at (x,y) in given band of image. + * @(#) Use bilinear interpolation if x or y are non-integral. + * @(#) + * @(#) int im_maxpos_vec( + * @(#) IMAGE *im, + * @(#) double x, + * @(#) double y, + * @(#) int band, + * @(#) double *val + * @(#) ); + * @(#) + * + * Copyright: 2006, The Nottingham Trent University + * + * Author: Tom Vajzovic + * + * Written on: 2006-09-26 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + + +/** EXPORTED FUNCTION **/ + +int im_point_bilinear( IMAGE *im, double x, double y, int band, double *val ){ +#define FUNCTION_NAME "im_point_bilinear" + + double x_frac= x - (int) x; + double y_frac= y - (int) y; + Rect need= { x, y, ( x_frac ? 2 : 1 ), ( y_frac ? 2 : 1 ) }; + REGION *reg; + + if( im_pincheck( im ) ) + return -1; + + if( im-> Coding ){ + im_error( FUNCTION_NAME, _("uncoded images only") ); + return -1; + } + if( !( im_isint( im ) || im_isfloat( im ) ) ){ + im_error( FUNCTION_NAME, _("scalar images only") ); + return -1; + } + if( band >= im-> Bands || x < 0.0 || y < 0.0 || x > im-> Xsize || y > im-> Ysize ){ + im_error( FUNCTION_NAME, _("coords outside image") ); + return -1; + } + if( ! val ){ + im_error( FUNCTION_NAME, _("invalid arguments") ); + return -1; + } + + reg= im_region_create( im ); + + if( ! reg || im_prepare( reg, &need ) ) + return -1; + + if( ! im_rect_includesrect( ®-> valid, &need ) ){ + im_error( FUNCTION_NAME, _("coords outside image") ); + im_region_free( reg ); + return -1; + } + + if( x_frac ) + if( y_frac ) + *val= x_frac * y_frac * IM_REGION_VALUE( reg, ((int)x + 1), ((int)y + 1), band ) + + x_frac * ( 1.0 - y_frac ) * IM_REGION_VALUE( reg, ((int)x + 1), (int)y , band ) + + ( 1.0 - x_frac ) * y_frac * IM_REGION_VALUE( reg, (int)x, ((int)y + 1), band ) + + ( 1.0 - x_frac ) * ( 1.0 - y_frac ) * IM_REGION_VALUE( reg, (int)x, (int)y , band ); + + else + *val= x_frac * IM_REGION_VALUE( reg, ((int)x + 1), (int)y, band ) + + ( 1.0 - x_frac ) * IM_REGION_VALUE( reg, (int)x, (int)y, band ); + + else + if( y_frac ) + *val= y_frac * IM_REGION_VALUE( reg, (int)x, ((int)y + 1), band ) + + ( 1.0 - y_frac ) * IM_REGION_VALUE( reg, (int)x, (int)y , band ); + + else + *val= IM_REGION_VALUE( reg, (int)x, (int)y, band ); + + im_region_free( reg ); + + return 0; + +#undef FUNCTION_NAME +} + diff --git a/libsrc/arithmetic/im_powtra.c b/libsrc/arithmetic/im_powtra.c new file mode 100644 index 00000000..80e7d368 --- /dev/null +++ b/libsrc/arithmetic/im_powtra.c @@ -0,0 +1,235 @@ +/* @(#) Finds the power of an image + * @(#) Function im_powtra() assumes that the imin file + * @(#) is either memory mapped or in a buffer. + * @(#) Output image pow(imin, exponent) depends on input + * @(#) If input is up to float, output is float + * @(#) else input is the same as output + * @(#) Works on any number of bands. + * @(#) + * @(#) int im_powtra( in, out, e ) + * @(#) IMAGE *in, *out; + * @(#) double e; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 10/12/93 JC + * - now reports total number of x/0, rather than each one. + * 1/2/95 JC + * - rewritten for PIO with im_wrapone() + * - incorrect complex code removed + * - /0 reporting removed for ease of programming + * 15/4/97 JC + * - return( 0 ) missing, oops! + * 6/7/98 JC + * - _vec form added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Parameters saved here. + */ +typedef struct { + int n; + double *e; /* Exponent value */ +} PowtraInfo; + +/* Define what we do for each band element type. Single constant. + */ +#define loop1(IN, OUT)\ +{\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double f = (double) p[x];\ + \ + if( f == 0.0 && e < 0.0 ) {\ + /* Division by zero! Difficult to report tho'\ + */\ + q[x] = 0.0;\ + }\ + else\ + q[x] = pow( f, e );\ + }\ +} + +/* Powtra a buffer. + */ +static int +powtra1_gen( PEL *in, PEL *out, int width, IMAGE *im, PowtraInfo *inf ) +{ + int sz = width * im->Bands; + double e = inf->e[0]; + int x; + + /* Powtra all non-complex input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop1(unsigned char, float); break; + case IM_BANDFMT_CHAR: loop1(signed char, float); break; + case IM_BANDFMT_USHORT: loop1(unsigned short, float); break; + case IM_BANDFMT_SHORT: loop1(signed short, float); break; + case IM_BANDFMT_UINT: loop1(unsigned int, float); break; + case IM_BANDFMT_INT: loop1(signed int, float); break; + case IM_BANDFMT_FLOAT: loop1(float, float); break; + case IM_BANDFMT_DOUBLE: loop1(double, double); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Define what we do for each band element type. One constant per band. + */ +#define loopn(IN, OUT)\ +{\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( i = 0, x = 0; x < width; x++ )\ + for( k = 0; k < im->Bands; k++, i++ ) {\ + double e = inf->e[k];\ + double f = (double) p[i];\ + \ + if( f == 0.0 && e < 0.0 ) {\ + q[i] = 0.0;\ + }\ + else\ + q[i] = pow( f, e );\ + }\ +} + +/* Powtra a buffer. + */ +static int +powtran_gen( PEL *in, PEL *out, int width, IMAGE *im, PowtraInfo *inf ) +{ + int x, k, i; + + /* Powtra all non-complex input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loopn(unsigned char, float); break; + case IM_BANDFMT_CHAR: loopn(signed char, float); break; + case IM_BANDFMT_USHORT: loopn(unsigned short, float); break; + case IM_BANDFMT_SHORT: loopn(signed short, float); break; + case IM_BANDFMT_UINT: loopn(unsigned int, float); break; + case IM_BANDFMT_INT: loopn(signed int, float); break; + case IM_BANDFMT_FLOAT: loopn(float, float); break; + case IM_BANDFMT_DOUBLE: loopn(double, double); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +int +im_powtra_vec( IMAGE *in, IMAGE *out, int n, double *e ) +{ + PowtraInfo *inf; + int i; + + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_powtra_vec", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_powtra_vec", _( "not non-complex" ) ); + return( -1 ); + } + if( n != 1 && n != in->Bands ) { + im_error( "im_powtra_vec", + _( "not 1 or %d elements in vector" ), in->Bands ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_isint( in ) ) { + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + } + + /* Make space for a little buffer. + */ + if( !(inf = IM_NEW( out, PowtraInfo )) || + !(inf->e = IM_ARRAY( out, n, double )) ) + return( -1 ); + for( i = 0; i < n; i++ ) + inf->e[i] = e[i]; + inf->n = n; + + /* Generate! + */ + if( n == 1 ) { + if( im_wrapone( in, out, + (im_wrapone_fn) powtra1_gen, in, inf ) ) + return( -1 ); + } + else { + if( im_wrapone( in, out, + (im_wrapone_fn) powtran_gen, in, inf ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_powtra( IMAGE *in, IMAGE *out, double e ) +{ + return( im_powtra_vec( in, out, 1, &e ) ); +} diff --git a/libsrc/arithmetic/im_remainder.c b/libsrc/arithmetic/im_remainder.c new file mode 100644 index 00000000..3491ec14 --- /dev/null +++ b/libsrc/arithmetic/im_remainder.c @@ -0,0 +1,278 @@ +/* @(#) Remainder after integer division + * + * 2/8/99 JC + * - im_divide adapted to make im_remainder + * 8/5/02 JC + * - im_remainderconst added + * - im_remainderconst_vec added + * 27/9/04 + * - updated for 1 band $op n band image -> n band image case + * 26/2/07 + * - oop, broken for _vec case :-( + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define loop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + if( p2[x] )\ + q[x] = p1[x] % p2[x];\ + else\ + q[x] = -1;\ +} + +static void +remainder_buffer( PEL **in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: loop( signed char ); break; + case IM_BANDFMT_UCHAR: loop( unsigned char ); break; + case IM_BANDFMT_SHORT: loop( signed short ); break; + case IM_BANDFMT_USHORT: loop( unsigned short ); break; + case IM_BANDFMT_INT: loop( signed int ); break; + case IM_BANDFMT_UINT: loop( unsigned int ); break; + + default: + assert( 0 ); + } +} + +int +im_remainder( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + /* Basic checks. + */ + if( im_piocheck( in1, out ) || im_pincheck( in2 ) ) + return( -1 ); + if( in1->Xsize != in2->Xsize || in1->Ysize != in2->Ysize ) { + im_error( "im_remainder", _( "not same size" ) ); + return( -1 ); + } + if( in1->Bands != in2->Bands && + (in1->Bands != 1 && in2->Bands != 1) ) { + im_error( "im_remainder", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_remainder", _( "not uncoded" ) ); + return( -1 ); + } + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + + /* What number of bands will we write? + */ + out->Bands = IM_MAX( in1->Bands, in2->Bands ); + + /* What output type will we write? Same as LHS type, except float + * and double become signed int. + */ + if( im_isfloat( in1 ) || im_iscomplex( in1 ) ) + out->BandFmt = IM_BANDFMT_INT; + + /* And process! + */ + if( im__cast_and_call( in1, in2, out, + (im_wrapmany_fn) remainder_buffer, NULL ) ) + return( -1 ); + + /* Success! + */ + return( 0 ); +} + +/* Parameters saved here. + */ +typedef struct _Remainderconst { + IMAGE *in; + IMAGE *out; + int n; + int *c; +} Remainderconst; + +#define const1_loop(TYPE) \ +{\ + TYPE *p = (TYPE *) in; \ + TYPE *q = (TYPE *) out; \ + \ + for( x = 0; x < sz; x++ ) \ + q[x] = p[x] % c; \ +} + +static void +remainderconst1_buffer( PEL *in, PEL *out, int width, Remainderconst *rc ) +{ + IMAGE *im = rc->in; + int sz = width * im->Bands; + int c = rc->c[0]; + int x; + + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: const1_loop( signed char ); break; + case IM_BANDFMT_UCHAR: const1_loop( unsigned char ); break; + case IM_BANDFMT_SHORT: const1_loop( signed short ); break; + case IM_BANDFMT_USHORT: const1_loop( unsigned short ); break; + case IM_BANDFMT_INT: const1_loop( signed int ); break; + case IM_BANDFMT_UINT: const1_loop( unsigned int ); break; + + default: + assert( 0 ); + } +} + +#define const_loop(TYPE) \ +{\ + TYPE *p = (TYPE *) in; \ + TYPE *q = (TYPE *) out; \ + \ + for( i = 0, x = 0; x < width; x++ ) \ + for( k = 0; k < b; k++, i++ ) \ + q[i] = p[i] % c[k]; \ +} + +static void +remainderconst_buffer( PEL *in, PEL *out, int width, Remainderconst *rc ) +{ + IMAGE *im = rc->in; + int b = im->Bands; + int *c = rc->c; + int i, x, k; + + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: const_loop( signed char ); break; + case IM_BANDFMT_UCHAR: const_loop( unsigned char ); break; + case IM_BANDFMT_SHORT: const_loop( signed short ); break; + case IM_BANDFMT_USHORT: const_loop( unsigned short ); break; + case IM_BANDFMT_INT: const_loop( signed int ); break; + case IM_BANDFMT_UINT: const_loop( unsigned int ); break; + + default: + assert( 0 ); + } +} + +int +im_remainderconst_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + Remainderconst *rc; + int i; + + /* Basic checks. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_remainderconst_vec", _( "not uncoded" ) ); + return( -1 ); + } + if( n != 1 && n != in->Bands ) { + im_error( "im_remainderconst_vec", + _( "not 1 or %d elements in vector" ), in->Bands ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Make space for a little buffer. + */ + if( !(rc = IM_NEW( out, Remainderconst )) || + !(rc->c = IM_ARRAY( out, n, int )) ) + return( -1 ); + rc->in = in; + rc->out = out; + rc->n = n; + for( i = 0; i < n; i++ ) { + /* Cast down to int ... we pass in double for consistency with + * the other _vec functions. + */ + rc->c[i] = c[i]; + + if( c[i] == 0 ) { + im_error( "im_remainderconst_vec", + _( "division by zero" ) ); + return( -1 ); + } + } + + /* What output type will we write? Same as input type, except float + * and double become signed int. + */ + if( im_isfloat( in ) || im_iscomplex( in ) ) { + IMAGE *t = im_open_local( out, "im_remainderconst:1", "p" ); + + out->BandFmt = IM_BANDFMT_INT; + out->Bbits = IM_BBITS_INT; + if( !t || im_clip2fmt( in, t, out->BandFmt ) ) + return( -1 ); + + rc->in = in = t; + } + + if( n == 1 ) { + if( im_wrapone( in, out, + (im_wrapone_fn) remainderconst1_buffer, rc, NULL ) ) + return( -1 ); + } + else { + if( im_wrapone( in, out, + (im_wrapone_fn) remainderconst_buffer, rc, NULL ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_remainderconst( IMAGE *in, IMAGE *out, double c ) +{ + return( im_remainderconst_vec( in, out, 1, &c ) ); +} diff --git a/libsrc/arithmetic/im_rint.c b/libsrc/arithmetic/im_rint.c new file mode 100644 index 00000000..da628948 --- /dev/null +++ b/libsrc/arithmetic/im_rint.c @@ -0,0 +1,114 @@ +/* @(#) rint() an image ... no promotion, so output type == input type + * @(#) + * @(#) int + * @(#) im_rint( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 20/6/02 JC + * - adapted from im_floor() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define rint_loop(TYPE)\ + {\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = IM_RINT( p[x] );\ + } + +/* rint a buffer of PELs. + */ +static void +rint_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_FLOAT: rint_loop(float); break; + case IM_BANDFMT_DOUBLE: rint_loop(double); break; + case IM_BANDFMT_COMPLEX: sz *= 2; rint_loop(float); break; + case IM_BANDFMT_DPCOMPLEX: sz *= 2; rint_loop(double); break; + + default: + assert( 0 ); + } +} + +/* rint of image. + */ +int +im_rint( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_rint", _( "not uncoded" ) ); + return( -1 ); + } + + /* Is this one of the int types? Degenerate to im_copy() if it + * is. + */ + if( im_isint( in ) ) + return( im_copy( in, out ) ); + + /* Output type == input type. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Generate! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) rint_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_sign.c b/libsrc/arithmetic/im_sign.c new file mode 100644 index 00000000..29aa206b --- /dev/null +++ b/libsrc/arithmetic/im_sign.c @@ -0,0 +1,162 @@ +/* @(#) Find the unit vector in the direction of the pixel. + * @(#) + * @(#) int im_sign(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 9/7/02 JC + * - from im_cmulnorm + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define sign_complex( IN, OUT ) \ +{ \ + IN *p = (IN *) in; \ + OUT *q = (OUT *) out; \ + int x; \ + \ + for( x = 0; x < n; x++ ) { \ + IN re = p[0]; \ + IN im = p[1]; \ + double fac = sqrt( re * re + im * im ); \ + \ + p += 2; \ + \ + if( fac == 0.0 ) { \ + q[0] = 0.0; \ + q[1] = 0.0; \ + } \ + else { \ + q[0] = re / fac; \ + q[1] = im / fac; \ + } \ + \ + q += 2; \ + } \ +} + +#define sign( IN, OUT ) \ +{ \ + IN *p = (IN *) in; \ + OUT *q = (OUT *) out; \ + int x; \ + \ + for( x = 0; x < n; x++ ) { \ + IN v = p[x]; \ + \ + if( v > 0 ) \ + q[x] = 1; \ + else if( v == 0 ) \ + q[x] = 0; \ + else \ + q[x] = -1; \ + } \ +} + +/* sign buffer processor. + */ +static void +buffer_sign( void *in, void *out, int w, IMAGE *im ) +{ + int n = w * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + sign( unsigned char, signed char ); + break; + case IM_BANDFMT_CHAR: + sign( signed char, signed char ); + break; + case IM_BANDFMT_USHORT: + sign( unsigned short, signed char ); + break; + case IM_BANDFMT_SHORT: + sign( signed short, signed char ); + break; + case IM_BANDFMT_UINT: + sign( unsigned int, signed char ); + break; + case IM_BANDFMT_INT: + sign( signed int, signed char ); + break; + case IM_BANDFMT_FLOAT: + sign( float, signed char ); + break; + case IM_BANDFMT_DOUBLE: + sign( double, signed char ); + break; + case IM_BANDFMT_COMPLEX: + sign_complex( float, float ); + break; + case IM_BANDFMT_DPCOMPLEX: + sign_complex( double, double ); + break; + default: + assert( 0 ); + } +} + +int +im_sign( IMAGE *in, IMAGE *out ) +{ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_sign", _( "not uncoded" ) ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + if( !im_iscomplex( in ) ) { + out->Bbits = IM_BBITS_BYTE; + out->BandFmt = IM_BANDFMT_CHAR; + } + + /* Do the processing. + */ + if( im_wrapone( in, out, (im_wrapone_fn) buffer_sign, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_sintra.c b/libsrc/arithmetic/im_sintra.c new file mode 100644 index 00000000..2024e5f4 --- /dev/null +++ b/libsrc/arithmetic/im_sintra.c @@ -0,0 +1,239 @@ +/* @(#) Find sin of any non-complex image. Output is always float for integer + * @(#) input and double for double input. All angles in degrees. + * @(#) + * @(#) int + * @(#) im_sintra( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 5/5/93 JC + * - adapted from im_lintra to work with partial images + * - incorrect implementation of complex logs removed + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 24/2/95 JC + * - im_logtra() adapted to make im_sintra() + * - adapted for im_wrapone() + * 26/1/96 JC + * - im_asintra() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define loop( IN, OUT )\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = sin( IM_RAD( (double) p[x] ) );\ + } + +/* sin a buffer of PELs. + */ +static void +sintra_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Switch for all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: loop( signed char, float ); break; + case IM_BANDFMT_USHORT: loop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: loop( signed short, float ); break; + case IM_BANDFMT_UINT: loop( unsigned int, float ); break; + case IM_BANDFMT_INT: loop( signed int, float ); break; + case IM_BANDFMT_FLOAT: loop( float, float ); break; + case IM_BANDFMT_DOUBLE: loop( double, double ); break; + + default: + assert( 0 ); + } +} + +/* Sin transform. + */ +int +im_sintra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_sintra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_sintra", _( "not non-complex" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, (im_wrapone_fn) sintra_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* And asin(). + */ +#define aloop( IN, OUT )\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = IM_DEG( asin( (double) p[x] ) );\ + } + +/* asin a buffer of PELs. + */ +static void +asintra_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Switch for all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: aloop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: aloop( signed char, float ); break; + case IM_BANDFMT_USHORT: aloop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: aloop( signed short, float ); break; + case IM_BANDFMT_UINT: aloop( unsigned int, float ); break; + case IM_BANDFMT_INT: aloop( signed int, float ); break; + case IM_BANDFMT_FLOAT: aloop( float, float ); break; + case IM_BANDFMT_DOUBLE: aloop( double, double ); break; + + default: + assert( 0 ); + } +} + +/* Asin transform. + */ +int +im_asintra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_asintra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_asintra", _( "not non-complex" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, (im_wrapone_fn) asintra_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_stats.c b/libsrc/arithmetic/im_stats.c new file mode 100644 index 00000000..6c2a1266 --- /dev/null +++ b/libsrc/arithmetic/im_stats.c @@ -0,0 +1,277 @@ +/* @(#) im_stats: find general image statistics for all bands separately +(C) Kirk Martinez 1993 +23/4/93 J.Cupitt + - adapted to partial images + - special struct abandoned; now returns DOUBLEMASK. +1/7/93 JC + - adapted for partial v2 + - ANSIfied +27/7/93 JC + - init of mask changed to use actual values, not IM_MAXDOUBLE and + (-IM_MAXDOUBLE). These caused problems when assigned to floats. + funny business with offset==42, yuk! +31/8/93 JC + - forgot to init global max/min properly! sorry. +21/6/95 JC + - still did not init max and min correctly --- now fixed for good + + * 13/1/05 + * - use 64 bit arithmetic + +*/ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Make and initialise a DOUBLEMASK suitable for grabbing statistics. + */ +static void * +make_mask( IMAGE *im ) +{ + DOUBLEMASK *out; + + /* Make temp output. + */ + if( !(out = im_create_dmask( "stats", 6, im->Bands + 1 )) ) + return( NULL ); + + /* Set offset to magic value: 42 indicates we have not yet initialised + * max and min for this mask. + */ + out->offset = 42; + + return( (void *) out ); +} + +/* Merge a temp DOUBLEMASK into the real DOUBLEMASK. Row 0 is unused, row 1 + * has the stats for band 1. These are: (minimum, maximum, sum, sum^2). If the + * offset of out if 42, then it has not been inited yet and we just copy. + */ +static int +merge_mask( DOUBLEMASK *tmp, DOUBLEMASK *out ) +{ + double *rowi, *rowo; + int z; + + /* Merge, or just copy? + */ + if( out->offset != 42 ) + /* Add info from tmp. + */ + for( z = 1; z < tmp->ysize; z++ ) { + rowi = tmp->coeff + z * 6; + rowo = out->coeff + z * 6; + + rowo[0] = IM_MIN( rowi[0], rowo[0] ); + rowo[1] = IM_MAX( rowi[1], rowo[1] ); + rowo[2] += rowi[2]; + rowo[3] += rowi[3]; + } + else { + /* Copy info from tmp. + */ + for( z = 1; z < tmp->ysize; z++ ) { + rowi = tmp->coeff + z * 6; + rowo = out->coeff + z * 6; + + rowo[0] = rowi[0]; + rowo[1] = rowi[1]; + rowo[2] = rowi[2]; + rowo[3] = rowi[3]; + } + + out->offset = 0; + } + + /* Can now free tmp. + */ + im_free_dmask( tmp ); + + return( 0 ); +} + +/* Loop over region, adding information to the appropriate fields of tmp. + * We set max, min, sum, sum of squares. Our caller fills in the rest. + */ +static int +scan_fn( REGION *reg, DOUBLEMASK *tmp ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int bands = im->Bands; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int x, y, z; + +/* What type? First define the loop we want to perform for all types. + * We scan lines bands times to avoid repeating band loops. + * Use temp variables of same type for min/max for faster comparisons. + * Use double to sum bands. + */ +#define non_complex_loop(TYPE) \ + { TYPE *p, *q; \ + TYPE value, small, big; \ + double *row; \ + \ + /* Have min and max been initialised? \ + */ \ + if( tmp->offset == 42 ) { \ + /* Init min and max for each band. \ + */ \ + p = (TYPE *) IM_REGION_ADDR( reg, le, to ); \ + for( z = 1; z < bands + 1; z++ ) { \ + row = tmp->coeff + z * 6; \ + row[0] = p[z - 1]; \ + row[1] = p[z - 1]; \ + } \ + tmp->offset = 0; \ + } \ + \ + for( y = to; y < bo; y++ ) { \ + p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( z = 0; z < bands; z++ ) { \ + q = p + z; \ + row = tmp->coeff + (z + 1)*6; \ + small = row[0]; \ + big = row[1]; \ + \ + for( x = le; x < ri; x++ ) { \ + value = *q; \ + q += bands; \ + row[2] += value;\ + row[3] += (double)value*(double)value;\ + if( value > big ) \ + big = value; \ + else if( value < small ) \ + small = value;\ + }\ + \ + row[0] = small; \ + row[1] = big; \ + }\ + }\ + } + + /* Now generate code for all types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: non_complex_loop(unsigned char); break; + case IM_BANDFMT_CHAR: non_complex_loop(signed char); break; + case IM_BANDFMT_USHORT: non_complex_loop(unsigned short); break; + case IM_BANDFMT_SHORT: non_complex_loop(signed short); break; + case IM_BANDFMT_UINT: non_complex_loop(unsigned int); break; + case IM_BANDFMT_INT: non_complex_loop(signed int); break; + case IM_BANDFMT_DOUBLE: non_complex_loop(double); break; + case IM_BANDFMT_FLOAT: non_complex_loop(float); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Find the statistics of an image. Take any non-complex format. Write the + * stats to a DOUBLEMASK of size 6 by (in->Bands+1). We hold a row for each + * band, plus one row for all bands. Row n has 6 elements, which are, in + * order, (minimum, maximum, sum, sum^2, mean, deviation) for band n. Row 0 has + * the figures for all bands together. + */ +DOUBLEMASK * +im_stats( IMAGE *in ) +{ + DOUBLEMASK *out; + double *row, *base; + gint64 pels, vals, z; + + /* Check our args. + */ + if( im_pincheck( in ) ) + return( NULL ); + if( im_iscomplex( in ) ) { + im_error( "im_stats", _( "bad input type" ) ); + return( NULL ); + } + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_stats", _( "not uncoded" ) ); + return( NULL ); + } + + /* Make output. + */ + pels = (gint64) in->Xsize * in->Ysize; + vals = pels * in->Bands; + if( !(out = make_mask( in )) ) + return( NULL ); + + /* Loop over input, calculating min, max, sum, sum^2 for each band + * separately. + */ + if( im_iterate( in, make_mask, scan_fn, merge_mask, out, NULL ) ) { + im_free_dmask( out ); + return( NULL ); + } + + /* Calculate mean, deviation, plus overall stats. + */ + base = out->coeff; + base[0] = base[6]; /* Init global max/min */ + base[1] = base[7]; + for( z = 0; z < in->Bands; z++ ) { + row = base + (z + 1) * 6; + base[0] = IM_MIN( base[0], row[0] ); + base[1] = IM_MAX( base[1], row[1] ); + base[2] += row[2]; + base[3] += row[3]; + row[4] = row[2] / pels; + row[5] = sqrt( fabs( row[3] - (row[2] * row[2] / pels) ) / + (pels - 1) ); + } + base[4] = base[2] / vals; + base[5] = sqrt( fabs( base[3] - (base[2] * base[2] / vals) ) / + (vals - 1) ); + + return( out ); +} diff --git a/libsrc/arithmetic/im_subtract.c b/libsrc/arithmetic/im_subtract.c new file mode 100644 index 00000000..82e329c7 --- /dev/null +++ b/libsrc/arithmetic/im_subtract.c @@ -0,0 +1,232 @@ +/* @(#) Subtract two images + * @(#) Images must have the same no of bands and can be of any type + * @(#) No check for overflow is carried out. + * @(#) + * @(#) int + * @(#) im_subtract( in1, in2, out) + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 29/4/93 J.Cupitt + * - now works for partial images + * 1/7/93 JC + * - adapted for partial v2 + * 9/5/95 JC + * - simplified: now just handles 10 cases (instead of 50), using + * im_clip2*() to help + * - now uses im_wrapmany() rather than im_generate() + * 12/6/95 JC + * - new im_add() adapted to make this + * 31/5/96 JC + * - what was this SWAP() stuff? failed for small - big! + * 22/8/03 JC + * - cast up more quickly to help accuracy + * 27/9/04 + * - updated for 1 band $op n band image -> n band image case + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Complex subtract. + */ +#define cloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double rp1 = p1[0];\ + double ip1 = p1[1];\ + \ + double rp2 = p2[0];\ + double ip2 = p2[1];\ + \ + p1 += 2;\ + p2 += 2;\ + \ + q[0] = rp1 - rp2;\ + q[1] = ip1 - ip2;\ + \ + q += 2;\ + }\ +} + +/* Real subtract. + */ +#define rloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = p1[x] - p2[x];\ +} + +static void +subtract_buffer( PEL **in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Subtract all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: rloop( signed char ); break; + case IM_BANDFMT_UCHAR: rloop( unsigned char ); break; + case IM_BANDFMT_SHORT: rloop( signed short ); break; + case IM_BANDFMT_USHORT: rloop( unsigned short ); break; + case IM_BANDFMT_INT: rloop( signed int ); break; + case IM_BANDFMT_UINT: rloop( unsigned int ); break; + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + oil_subtract_f32( (float *) out, + (float *) in[0], (float *) in[1], sz ); +#else /*!HAVE_LIBOIL*/ + rloop( float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: rloop( double ); break; + case IM_BANDFMT_COMPLEX: cloop( float ); break; + case IM_BANDFMT_DPCOMPLEX: cloop( double ); break; + + default: + assert( 0 ); + } +} + +/* Save a bit of typing. + */ +#define S IM_BANDFMT_SHORT +#define I IM_BANDFMT_INT +#define D IM_BANDFMT_DOUBLE + +/* Type conversions for two integer inputs. Rules for float and complex + * input encoded with ifs. We are sign and value preserving. + */ +static int iformat[6][6] = { + /* UC C US S UI I */ +/* UC */ { S, S, I, I, I, I }, +/* C */ { S, S, I, I, I, I }, +/* US */ { I, I, I, I, I, I }, +/* S */ { I, I, I, I, I, I }, +/* UI */ { I, I, I, I, I, I }, +/* I */ { I, I, I, I, I, I } +}; + +int +im_subtract( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + /* Basic checks. + */ + if( im_piocheck( in1, out ) || im_pincheck( in2 ) ) + return( -1 ); + if( in1->Bands != in2->Bands && + (in1->Bands != 1 && in2->Bands != 1) ) { + im_error( "im_subtract", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_subtract", _( "not uncoded" ) ); + return( -1 ); + } + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + + /* What number of bands will we write? + */ + out->Bands = IM_MAX( in1->Bands, in2->Bands ); + + /* What output type will we write? int, float or complex. + */ + if( im_iscomplex( in1 ) || im_iscomplex( in2 ) ) { + /* What kind of complex? + */ + if( in1->BandFmt == IM_BANDFMT_DPCOMPLEX || + in2->BandFmt == IM_BANDFMT_DPCOMPLEX ) + /* Output will be DPCOMPLEX. + */ + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + else + out->BandFmt = IM_BANDFMT_COMPLEX; + } + else if( im_isfloat( in1 ) || im_isfloat( in2 ) ) { + /* What kind of float? + */ + if( in1->BandFmt == IM_BANDFMT_DOUBLE || + in2->BandFmt == IM_BANDFMT_DOUBLE ) + out->BandFmt = IM_BANDFMT_DOUBLE; + else + out->BandFmt = IM_BANDFMT_FLOAT; + } + else + /* Must be int+int -> int. + */ + out->BandFmt = iformat[in1->BandFmt][in2->BandFmt]; + + /* And process! + */ + if( im__cast_and_call( in1, in2, out, + (im_wrapmany_fn) subtract_buffer, NULL ) ) + return( -1 ); + + /* Success! + */ + return( 0 ); +} diff --git a/libsrc/arithmetic/im_tantra.c b/libsrc/arithmetic/im_tantra.c new file mode 100644 index 00000000..9cbd0801 --- /dev/null +++ b/libsrc/arithmetic/im_tantra.c @@ -0,0 +1,240 @@ +/* @(#) Find tan of any non-complex image. Output is always float for integer + * @(#) input and double for double input. All angles in degrees. + * @(#) + * @(#) int + * @(#) im_tantra( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 5/5/93 JC + * - adapted from im_lintra to work with partial images + * - incorrect implementation of complex logs removed + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 24/2/95 JC + * - im_logtra() adapted to make im_tantra() + * - adapted for im_wrapone() + * 26/1/96 JC + * - atan() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define loop( IN, OUT )\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = tan( IM_RAD( (double) p[x] ) );\ + } + +/* tan a buffer of PELs. + */ +static void +tantra_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Switch for all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: loop( signed char, float ); break; + case IM_BANDFMT_USHORT: loop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: loop( signed short, float ); break; + case IM_BANDFMT_UINT: loop( unsigned int, float ); break; + case IM_BANDFMT_INT: loop( signed int, float ); break; + case IM_BANDFMT_FLOAT: loop( float, float ); break; + case IM_BANDFMT_DOUBLE: loop( double, double ); break; + + default: + assert( 0 ); + } +} + +/* Tan transform. + */ +int +im_tantra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_tantra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_tantra", _( "not non-complex" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, (im_wrapone_fn) tantra_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define aloop( IN, OUT )\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = IM_DEG( atan( (double) p[x] ) );\ + } + +/* atan a buffer of PELs. + */ +static void +atantra_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Switch for all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: aloop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: aloop( signed char, float ); break; + case IM_BANDFMT_USHORT: aloop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: aloop( signed short, float ); break; + case IM_BANDFMT_UINT: aloop( unsigned int, float ); break; + case IM_BANDFMT_INT: aloop( signed int, float ); break; + case IM_BANDFMT_FLOAT: aloop( float, float ); break; + case IM_BANDFMT_DOUBLE: aloop( double, double ); break; + + default: + assert( 0 ); + } +} + +/* Atan transform. + */ +int +im_atantra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_atantra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_atantra", _( "not non-complex" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, (im_wrapone_fn) atantra_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/man3/Makefile.am b/libsrc/arithmetic/man3/Makefile.am new file mode 100644 index 00000000..fb5bab54 --- /dev/null +++ b/libsrc/arithmetic/man3/Makefile.am @@ -0,0 +1,48 @@ +man_MANS = \ + im_abs.3 \ + im_acostra.3 \ + im_bandmean.3 \ + im_sign.3 \ + im_ceil.3 \ + im_floor.3 \ + im_add.3 \ + im_asintra.3 \ + im_atantra.3 \ + im_avg.3 \ + im_cmulnorm.3 \ + im_costra.3 \ + im_deviate.3 \ + im_divide.3 \ + im_exp10tra.3 \ + im_expntra.3 \ + im_exptra.3 \ + im_fav4.3 \ + im_gadd.3 \ + im_gaddim.3 \ + im_gfadd.3 \ + im_invert.3 \ + im_lintra.3 \ + im_lintra_vec.3 \ + im_litecor.3 \ + im_log10tra.3 \ + im_logtra.3 \ + im_max.3 \ + im_maxpos.3 \ + im_maxpos_vec.3 \ + im_measure.3 \ + im_min.3 \ + im_minpos.3 \ + im_minpos_vec.3 \ + im_multiply.3 \ + im_powtra.3 \ + im_sintra.3 \ + im_rint.3 \ + im_stats.3 \ + im_remainder.3 \ + im_remainderconst.3 \ + im_subtract.3 \ + im_tantra.3 \ + im_expntra_vec.3 \ + im_powtra_vec.3 + +EXTRA_DIST = ${man_MANS} diff --git a/libsrc/arithmetic/man3/im_abs.3 b/libsrc/arithmetic/man3/im_abs.3 new file mode 100644 index 00000000..329746f4 --- /dev/null +++ b/libsrc/arithmetic/man3/im_abs.3 @@ -0,0 +1,18 @@ +.TH IM_ABS 3 "25 April 1991" +.SH NAME +im_abs \- finds the absolute value or the magnitude of an image +.SH SYNOPSIS +#include + +int im_abs( in, out ) +.br +IMAGE *in, *out; +.SH DESCRIPTION +.B im_abs(3) +finds the absolute value of an image. Copy for UNSIGNED types, negate +for int types, fabs(3) for float types, and calculate modulus for +complex types. Any size, any number of bands. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_exp10tra(3), im_sign(3) diff --git a/libsrc/arithmetic/man3/im_acostra.3 b/libsrc/arithmetic/man3/im_acostra.3 new file mode 100644 index 00000000..83ac049e --- /dev/null +++ b/libsrc/arithmetic/man3/im_acostra.3 @@ -0,0 +1 @@ +.so man3/im_costra.3 diff --git a/libsrc/arithmetic/man3/im_add.3 b/libsrc/arithmetic/man3/im_add.3 new file mode 100644 index 00000000..144c589e --- /dev/null +++ b/libsrc/arithmetic/man3/im_add.3 @@ -0,0 +1,102 @@ +.TH ADDITION 3 "24 April 1991" +.SH NAME +im_add, im_gadd, im_gaddim, im_gfadd \- add two images +.SH SYNOPSIS +#include + +int im_add(in1, in2, out) +.br +IMAGE *in1, *in2, *out; + +int im_gadd(a, in1, b, in2, c, out) +.br +double a, b, c; +.br +IMAGE *in1, *in2, *out; + +int im_gaddim(a, in1, b, in2, c, out) +.br +double a, b, c; +.br +IMAGE *in1, *in2, *out; + +int im_gfadd(a, in1, b, in2, c, out) +.br +double a, b, c; +.br +IMAGE *in1, *in2, *out; + +.SH DESCRIPTION +These functions operate on two images held by image descriptors in1 and in2 +and write the result to the image descriptor out. Input images in1 and in2 +should have the same channels and the same size; however they can be of +different types. Only the history of the image descriptor pointed by in1 is +copied to out. + +.B im_add(3) + +For two integer images, add the two images and write the output as + + in1 - uchar char ushort short uint int + -------|----------------------------------------- + in2 | + uchar | ushort short ushort short uint int + char | short short short short int int + ushort | ushort short ushort short uint int + short | short short short short int int + uint | uint int uint int uint int + int | int int int int int int + +If one or more of the images is a floating point type, the output is FMTFLOAT, +unless one or more of the inputs is FMTDOUBLE, in which case the output is +also FMTDOUBLE. + +If one or more of the images is a complex type, the output is FMTCOMPLEX, +unless one or more of the inputs is FMTDPCOMPLEX, in which case the output is +also FMTDPCOMPLEX. + +.B im_gadd(3) +performs generalised addition of two images by calling +.B im_gaddim(3) +and +.B im_gfadd(3). +These are very old and tired things, and should not be used. + +Input should be non complex. Output depends on input according to function +called. The result at each point is: a * pel1 + b * pel2 + c, properly +rounded if necessary. Pel1 and pel2 are the +corresponding pixels from in1 and in2 respectively. + +im_gaddim() performs generalised addition of in1 and in2, on the condition +they are neither float nor double nor complex. The format of the resultant +image is given by the table: + + in1 - uchar char ushort short uint int + -------|----------------------------------------- + in2 | + uchar | ushort short ushort short uint int + char | short short short short int int + ushort | ushort short ushort short uint int + short | short short short short int int + uint | uint int uint int uint int + int | int int int int int int + +The result at each point is: a * pel1 + b * pel2 + c, properly rounded. Pel1 +and pel2 are the corresponding pixels from in1 and in2 respectively. + +.B im_gfadd(3) +adds the non-complex images pointed by in1 and in2. Result is +float except if one (or both) inputs is double. In the latter case the result +is double. The result at each point is: a * pel1 + b * pel2 + c. Pel1 and +pel2 are the corresponding pixels from in1 and in2 respectively. + +.SH BUGS +None of the functions checks the result for over/underflow. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_subtract(3), im_lintra(3), im_multiply(3). +.SH AUTHOR +N. Dessipris \- 22/04/1991 +.br +J. Cupitt, im_add(), \- 21/7/93 diff --git a/libsrc/arithmetic/man3/im_asintra.3 b/libsrc/arithmetic/man3/im_asintra.3 new file mode 100644 index 00000000..83ac049e --- /dev/null +++ b/libsrc/arithmetic/man3/im_asintra.3 @@ -0,0 +1 @@ +.so man3/im_costra.3 diff --git a/libsrc/arithmetic/man3/im_atantra.3 b/libsrc/arithmetic/man3/im_atantra.3 new file mode 100644 index 00000000..83ac049e --- /dev/null +++ b/libsrc/arithmetic/man3/im_atantra.3 @@ -0,0 +1 @@ +.so man3/im_costra.3 diff --git a/libsrc/arithmetic/man3/im_avg.3 b/libsrc/arithmetic/man3/im_avg.3 new file mode 100644 index 00000000..12e9570b --- /dev/null +++ b/libsrc/arithmetic/man3/im_avg.3 @@ -0,0 +1,101 @@ +.TH STATS 3 "24 April 1991" +.SH NAME +im_avg, im_deviate, im_min, im_minpos, im_max, im_maxpos \- find the mean, standard deviation, minimum and maximum of an image +.SH SYNOPSIS +.B #include + +.B int im_avg(im, out) +.br +.B IMAGE *im; +.br +.B double *out; + +.B int im_deviate(im, out) +.br +.B IMAGE *im; +.br +.B double *out; + +.B int im_min(im, out) +.br +.B IMAGE *im; +.br +.B double *out; + +.B int im_minpos(im, xpos, ypos, min) +.br +.B IMAGE *im; +.br +.B int *xpos, *ypos; +.br +.B double *min; + +.B int im_max(im, out) +.br +.B IMAGE *im; +.br +.B double *out; + +.B int im_maxpos(im, xpos, ypos, max) +.br +.B IMAGE *im; +.br +.B int *xpos, *ypos; +.br +.B double *max; + +.SH DESCRIPTION +These functions find the mean, standard deviation, minimum, maximum of an image. +They operate on all bands of the input image. Use +.B im_stats(3) +if you need to calculate on bands separately. +All computations are carried out in +double precision arithmetic. +The standard deviation is calculated using the formula: + + Var{E} = 1 / (N - 1) * (E{X^2} - E{X}^2 / N) + stdev{E} = sqrt(Var{E}). + +.B im_avg(3) +finds the average of an image pointed by im. Takes as input any non-complex +image format and returns a double at the location pointed by out. + +.B im_deviate(3) +finds the standard deviation of an image pointed by im. Takes as +input any non-complex image format and returns +a double at the location pointed by out. + +.B im_min(3) +finds the the minimum value of the image pointed by im and returns it at the +location pointed by out. Takes as +input any image format and returns +a double at the location pointed by out. If input is complex +the min square amplitude (re*re+im*im) is returned. + +.B im_minpos(3) +finds the the minimum value of the image pointed by im and returns it at the +location pointed by out. The coordinates of the last occurrence of +min is returned at locations pointed by xpos, ypos. If input is complex +the min square amplitude (re*re+im*im) is returned. + +.B im_max(3) +finds the the maximum value of the image pointed by im and returns it at the +location pointed by out. If input is complex +the max square amplitude (re*re+im*im) is returned. + +.B im_maxpos(3) +finds the the maximum value of the image pointed by im and returns it at the +location pointed by max. The coordinates of the last occurrence of +max is returned at locations pointed by xpos, ypos. If input is complex +the max square amplitude (re*re+im*im) and its last occurrence is returned. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_exptra(3), im_lintra(3), im_abs(3), im_stats(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 24/04/1991 +.br +J. Cupitt \- 21/7/93 diff --git a/libsrc/arithmetic/man3/im_bandmean.3 b/libsrc/arithmetic/man3/im_bandmean.3 new file mode 100644 index 00000000..69f234ae --- /dev/null +++ b/libsrc/arithmetic/man3/im_bandmean.3 @@ -0,0 +1,20 @@ +.TH IM_BANDMEAN 3 "18 July 2007" +.SH NAME +im_bandmean \- average bands in an image +.SH SYNOPSIS +#include + +int im_bandmean( in, out ) +.br +IMAGE *in, *out; +.SH DESCRIPTION +.B im_bandmean(3) +writes a one-band image where each pixel is the average of the bands for that +pixel in the input image. The output band format is the same as the input +band format. + +Any size, any number of bands, any type. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_add(3), im_mean(3), im_recomb(3) diff --git a/libsrc/arithmetic/man3/im_ceil.3 b/libsrc/arithmetic/man3/im_ceil.3 new file mode 100644 index 00000000..4a52b016 --- /dev/null +++ b/libsrc/arithmetic/man3/im_ceil.3 @@ -0,0 +1,17 @@ +.TH IM_CEIL 3 "20 June 2002" +.SH NAME +im_ceil \- for each pixel, find the smallest integral value not less than +.SH SYNOPSIS +#include + +int im_ceil( in, out ) +.br +IMAGE *in, *out; +.SH DESCRIPTION +.B im_ceil(3) +finds the smallest integral value not less than. Copy for integer types, +call ceil(3) for float and complex types. Output type == input type. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_abs(3), im_clip2fmt(3), im_floor(3) diff --git a/libsrc/arithmetic/man3/im_cmulnorm.3 b/libsrc/arithmetic/man3/im_cmulnorm.3 new file mode 100644 index 00000000..2962752e --- /dev/null +++ b/libsrc/arithmetic/man3/im_cmulnorm.3 @@ -0,0 +1,67 @@ +.TH IM_MULTIPLY 3 "24 April 1991" +.SH NAME +im_cmulnorm, im_multiply \- multiply two images +.SH SYNOPSIS +.B #include + +.B int im_cmulnorm(in1, in2, out) +.br +.B IMAGE *in1, *in2, *out; + +.B int im_multiply(in1, in2, out) +.br +.B IMAGE *in1, *in2, *out; + +.SH DESCRIPTION +These functions operate on two images held by image descriptors in1 and in2 +and write the result to the image descriptor out. Input images in1 and in2 +should have the same channels and the same sizes; however they can be of +different types. Only the history of the image descriptor pointed by in1 is +copied to out. + +.B im_multiply(3) +applied to two integer images multiplies the two images and writes the output +as: + + in1 | uchar char ushort short uint int + -------+----------------------------------------- + in2 | + uchar | ushort short ushort short uint int + char | short short short short int int + ushort | ushort short ushort short uint int + short | short short short short int int + uint | uint int uint int uint int + int | int int int int int int + +If one or more of the images is a floating point type, the output is FMTFLOAT, +unless one or more of the inputs is FMTDOUBLE, in which case the output is +also FMTDOUBLE. + +If one or more of the images is a complex type, the output is FMTCOMPLEX, +unless one or more of the inputs is FMTDPCOMPLEX, in which case the output is +also FMTDPCOMPLEX. + +For complex input pels (x1,y1) and (x2,y2), im_multiply() writes +(x1*x2 - y1*y2, x1*y2 + x2*y1). + +.B im_cmulnorm(3) +multiplies two complex images. The complex output is normalised to 1 by +dividing both the real and the imaginary part of each pel with the norm; for +instance if the complex multiplication produces (a,b) then the output written +by this function is (a/norm, b/norm), where norm=a*a+b*b. Result is checked +for norm=0. The function is useful for phase correlation. Both inputs should +be complex. + +Result is float complex if both inputs are float complex. In any other case +the result is double complex. + +.SH BUGS +None of the functions checks the result for over/underflow. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_subtract(3), im_lintra(3), im_add(3). +.SH AUTHOR +N. Dessipris \- 22/04/1991 +.br +J. Cupitt (im_multiply) \- 22/04/1991 diff --git a/libsrc/arithmetic/man3/im_costra.3 b/libsrc/arithmetic/man3/im_costra.3 new file mode 100644 index 00000000..be8b6393 --- /dev/null +++ b/libsrc/arithmetic/man3/im_costra.3 @@ -0,0 +1,47 @@ +.TH IM_COSTRA 3 "24 April 1991" +.SH NAME +im_costra, im_sintra, im_tantra, +im_acostra, im_asintra, im_atantra \- basic trig functions +.SH SYNOPSIS +#include + +int im_costra(in, out) +.br +IMAGE *in, *out; + +int im_sintra(in, out) +.br +IMAGE *in, *out; + +int im_tantra(in, out) +.br +IMAGE *in, *out; + +int im_acostra(in, out) +.br +IMAGE *in, *out; + +int im_asintra(in, out) +.br +IMAGE *in, *out; + +int im_atantra(in, out) +.br +IMAGE *in, *out; + +.SH DESCRIPTION +Basic trig functions. The input image is mapped through either cos(3), sin(3) or +tan(3) and written to out. All work in degrees. + +The size and number of bands are unchanged, the output type is float, unless +the input is double, in which case the output is double. Non-complex images +only! + +.SH RETURN VALUE +Each function returns 0 on success and -1 on error. +.SH SEE ALSO +im_add(3), im_lintra(3), im_abs(3), im_mean(3), im_logtra(3) +.SH COPYRIGHT +National Gallery, 1995. +.SH AUTHOR +J. Cupitt \- 21/7/93 diff --git a/libsrc/arithmetic/man3/im_deviate.3 b/libsrc/arithmetic/man3/im_deviate.3 new file mode 100644 index 00000000..46443e20 --- /dev/null +++ b/libsrc/arithmetic/man3/im_deviate.3 @@ -0,0 +1 @@ +.so man3/im_avg.3 diff --git a/libsrc/arithmetic/man3/im_divide.3 b/libsrc/arithmetic/man3/im_divide.3 new file mode 100644 index 00000000..49e3b3b0 --- /dev/null +++ b/libsrc/arithmetic/man3/im_divide.3 @@ -0,0 +1,33 @@ +.TH DIVISION 3 "24 April 1991" +.SH NAME +im_divide \- divide two images +.SH SYNOPSIS +.B #include + +.B int im_divide(in1, in2, out) +.br +.B IMAGE *in1, *in2, *out; + +.SH DESCRIPTION +.B im_divide(3) +divides two images. The result is float except if one (or both) input is +double. In the latter case the result is double. If either input is complex, +the result is complex. If either input is double complex, the output is double +complex. + +Input images in1 and in2 should have the same channels and the same sizes, +however they can be of different types. + +For complex input pels (x1,y1) and (x2,y2), +.B im_divide(3) +calculates + ((x1*x2 + y1*y2)/(x2*x2 + y2*y2), (y1*x2 - x1*y2)/(x2*x2 + y2*y2)). + +.SH BUGS +The function does not check the result for over/underflow. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_remainder(3), im_multiply(3), im_subtract(3), im_lintra(3), im_add(3). +.SH COPYRIGHT +National Gallery, 1995 diff --git a/libsrc/arithmetic/man3/im_exp10tra.3 b/libsrc/arithmetic/man3/im_exp10tra.3 new file mode 100644 index 00000000..6538cef4 --- /dev/null +++ b/libsrc/arithmetic/man3/im_exp10tra.3 @@ -0,0 +1,100 @@ +.TH IM_EXPTRA 3 "24 April 1991" +.SH NAME +im_exp10tra, im_exptra, im_expntra, im_expntra_vec, im_log10tra, im_logtra, +im_powtra, im_powtra_vec \- logarithmic, exponential and power transform of an image +.SH SYNOPSIS +.B #include + +.B int im_expntra(in, out, base) +.br +.B IMAGE *in, *out; +.br +.B double base; + +.B int im_expntra_vec(in, out, n, vec) +.br +.B IMAGE *in, *out; +.br +.B int n; +.br +.B double *vec; + +.B int im_exp10tra(in, out) +.br +.B IMAGE *in, *out; + +.B int im_exptra(in, out) +.br +.B IMAGE *in, *out; + +.B int im_log10tra(in, out) +.br +.B IMAGE *in, *out; + +.B int im_logtra(in, out) +.br +.B IMAGE *in, *out; + +.B int im_powtra(in, out, exponent) +.br +.B IMAGE *in, *out; +.br +.B double exponent; + +.B int im_powtra_vec(in, out, n, vec) +.br +.B IMAGE *in, *out; +.br +.B int n; +.br +.B double *vec; + +.SH DESCRIPTION +Each of the above functions maps in through a log or anti-log +function of some sort and writes the result to out. The size and number of +bands are unchanged, the output type is float, unless the input is double, in +which case the output is double. Non-complex images only! + +.B im_expntra(3) +transforms element x of input, to pow(base, x) in output. It detects division +by zero, setting those pixels to zero in the output. Beware: it does this +silently! + +.B im_expntra_vec(3) +works as im_expntra(), but lets you specify a constant per band. + +.B im_exp10tra(3) +transforms element x of input, to pow(10,0, x) in output. Internally, it is +defined in terms of im_expntra(). + +.B im_exptra(3) +transforms element x of input, to pow(e, x) in output, where e is the +mathematical constant. Internally, it is defined in terms of im_expntra(). + +.B im_log10tra(3) +transforms element x of input, to log10tra(x) in output. + +.B im_logtra(3) +transforms element x of input, to logtra(x) in output. + +.B im_powtra(3) +transforms element x of input, to pow(x, exponent) in output. It detects +division by zero, setting those pixels to zero in the output. Beware: it does +this silently! + +.B im_powtra_vec(3) +works as im_powtra(3), but lets you specify a constant per band. + +.SH BUGS +None of the functions checks for under/overflow. Overflow is very common for +many of these functions! + +.SH RETURN VALUE +Each function returns 0 on success and -1 on error. +.SH SEE ALSO +im_add(3), im_multiply(3), im_subtract(3), im_lintra(3), +im_absim(3), im_mean(3), im_max(3). +.SH AUTHOR +N. Dessipris \- 24/04/1991 +.br +J. Cupitt (rewrite) \- 21/7/93 diff --git a/libsrc/arithmetic/man3/im_expntra.3 b/libsrc/arithmetic/man3/im_expntra.3 new file mode 100644 index 00000000..b7685e3d --- /dev/null +++ b/libsrc/arithmetic/man3/im_expntra.3 @@ -0,0 +1 @@ +.so man3/im_exp10tra.3 diff --git a/libsrc/arithmetic/man3/im_expntra_vec.3 b/libsrc/arithmetic/man3/im_expntra_vec.3 new file mode 100644 index 00000000..b7685e3d --- /dev/null +++ b/libsrc/arithmetic/man3/im_expntra_vec.3 @@ -0,0 +1 @@ +.so man3/im_exp10tra.3 diff --git a/libsrc/arithmetic/man3/im_exptra.3 b/libsrc/arithmetic/man3/im_exptra.3 new file mode 100644 index 00000000..b7685e3d --- /dev/null +++ b/libsrc/arithmetic/man3/im_exptra.3 @@ -0,0 +1 @@ +.so man3/im_exp10tra.3 diff --git a/libsrc/arithmetic/man3/im_fav4.3 b/libsrc/arithmetic/man3/im_fav4.3 new file mode 100644 index 00000000..a2af2034 --- /dev/null +++ b/libsrc/arithmetic/man3/im_fav4.3 @@ -0,0 +1,23 @@ +.TH IM_FAV4 3 "13 April 1992" +.SH NAME +im_fav4 \- averages four frames +.SH SYNOPSIS +.B #include + +.B int im_fav4(in, out) +.br +.B IMAGE **in, *out; +.SH DESCRIPTION +.B im_fav4 +averages four input VIPS images, which must be unsigned byte images with any +number of bands. This is useful for noise reduction by grabbing 4 frames +and averaging them. With 7 bit images this will give quite a convincing +eighth bit. +.SH RETURN VALUE +returns 0 on success or -1 on error +.SH SEE\ ALSO +add(1) +.SH COPYRIGHT +.br +K. Martinez 13/4/92 + diff --git a/libsrc/arithmetic/man3/im_floor.3 b/libsrc/arithmetic/man3/im_floor.3 new file mode 100644 index 00000000..79320b9e --- /dev/null +++ b/libsrc/arithmetic/man3/im_floor.3 @@ -0,0 +1,17 @@ +.TH IM_FLOOR 3 "20 June 2002" +.SH NAME +im_floor \- for each pixel, find the largest integral value not greater than +.SH SYNOPSIS +#include + +int im_floor( in, out ) +.br +IMAGE *in, *out; +.SH DESCRIPTION +.B im_floor(3) +finds the largest integral value not greater than. Copy for integer types, +call floor(3) for float and complex types. Output type == input type. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_abs(3), im_clip2fmt(3), im_ceil(3) diff --git a/libsrc/arithmetic/man3/im_gadd.3 b/libsrc/arithmetic/man3/im_gadd.3 new file mode 100644 index 00000000..295df09e --- /dev/null +++ b/libsrc/arithmetic/man3/im_gadd.3 @@ -0,0 +1 @@ +.so man3/im_add.3 diff --git a/libsrc/arithmetic/man3/im_gaddim.3 b/libsrc/arithmetic/man3/im_gaddim.3 new file mode 100644 index 00000000..295df09e --- /dev/null +++ b/libsrc/arithmetic/man3/im_gaddim.3 @@ -0,0 +1 @@ +.so man3/im_add.3 diff --git a/libsrc/arithmetic/man3/im_gfadd.3 b/libsrc/arithmetic/man3/im_gfadd.3 new file mode 100644 index 00000000..295df09e --- /dev/null +++ b/libsrc/arithmetic/man3/im_gfadd.3 @@ -0,0 +1 @@ +.so man3/im_add.3 diff --git a/libsrc/arithmetic/man3/im_invert.3 b/libsrc/arithmetic/man3/im_invert.3 new file mode 100644 index 00000000..dbe092ed --- /dev/null +++ b/libsrc/arithmetic/man3/im_invert.3 @@ -0,0 +1,24 @@ +.TH IM_INVERT 3 "11 April 1990" +.SH NAME +im_invert \- inverts an image pointed by an image descriptor. +.SH SYNOPSIS +.B #include + +.B int im_invert(in, out) +.br +.B IMAGE *in, *out; +.SH DESCRIPTION +.B im_invert +inverts the image held by image descriptor in and writes the result on the +image descriptor out. The function works on FMTUCHAR images only. +The result at point x of the input image is 255-x at the output image. +Input can have any no of channels. +.br + This is not a generally useful program -- it is included as an example of a +very simple new-style IO function. Feel the power of the source, Luke! +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_add(3), im_lintra(3), im_multiply(3). +.SH AUTHOR +J. Cupitt \- 21/7/93 diff --git a/libsrc/arithmetic/man3/im_lintra.3 b/libsrc/arithmetic/man3/im_lintra.3 new file mode 100644 index 00000000..788ba37a --- /dev/null +++ b/libsrc/arithmetic/man3/im_lintra.3 @@ -0,0 +1,58 @@ +.TH IM_LINTRA 3 "24 April 1991" +.SH NAME +im_lintra, im_lintra_vec \- performs a linear transformation on an image +.SH SYNOPSIS +.B #include + +.B int im_lintra_vec(n, a, in, b, out) +.br +.B int n; +.br +.B double *a, *b; +.br +.B IMAGE *in, *out; + +.B int im_lintra(a, in, b, out) +.br +.B double a, b; +.br +.B IMAGE *in, *out; + +.SH DESCRIPTION +.B im_lintra_vec(3) +performs a linear transform on image in, that is, it calculates + + out = a * in + b + +.B a +and +.B b +are vectors, ie. arrays of constants of length +.B n. +If +.B in +has one band, then the vectors may be any length and the output image will +have the same number of bands as the length of the vector. If +.B in +has many bands, then the vector must be length 1, or have the same length as +the number of bands in the image. + +If the input format is one of the integer types +then output is float. In all other cases the +output is the same as the input. + +.B im_lintra(3) +is a convenience function which calls +.B im_lintra_vec(3) +with a vector of length 1. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH BUGS +The function does not check for under/overflow +.SH SEE\ ALSO +im_exptra(3), im_logtra(3) +.SH AUTHOR +N. Dessipris \- 24/04/1991 +.br +J. Cupitt (rewrite) \- 21/7/93 diff --git a/libsrc/arithmetic/man3/im_lintra_vec.3 b/libsrc/arithmetic/man3/im_lintra_vec.3 new file mode 100644 index 00000000..4c6c0ca9 --- /dev/null +++ b/libsrc/arithmetic/man3/im_lintra_vec.3 @@ -0,0 +1 @@ +.so man3/im_lintra.3 diff --git a/libsrc/arithmetic/man3/im_litecor.3 b/libsrc/arithmetic/man3/im_litecor.3 new file mode 100644 index 00000000..46a838b2 --- /dev/null +++ b/libsrc/arithmetic/man3/im_litecor.3 @@ -0,0 +1,52 @@ +.TH IM_LITECOR 3 "5 December 1991" +.SH NAME +im_litecor \- perform light correction +.SH SYNOPSIS +.B #include + +.B int im_litecor(in, white, out, clip, factor) +.br +.B IMAGE *in, *out; +.br +.B int clip; +.br +.B double factor; +.SH DESCRIPTION +.B im_litecor(3) +performs light correction on the image held by the IMAGE descriptor in, +with respect to a reference white image held by the IMAGE descriptor white. +The result is written onto the IMAGE descriptor out. The function works +on byte one channel images only. + +The flag clip can take two values 0 and 1. If clip is 1 then the input is +corrected with reference to the maximum value of white (maxw) as follows. + + pel_out = factor * pel_in * maxw / pel_white. + +If clip is 0 then the output is scaled with the maximum possible output set +to 255. In this case factor is not used but it must be set to a dummy value. + +The basic reason for lighting correction is that the input frame does not +have a uniform distribution of white light due to the optical response of +the lens. The function accepts a white image which is a simple multiple +of the input image in size; for example it is possible that the white +is a subsampled version of in; however the sizes of in must be an exact +multiple of the white. If clip is set to 0, lighting correction is +carried out and the result is scaled between 0 and 255. This can be used +to correct individual frames. + +If multiband images are grabbed, then flag should be set to 1, since +no scaling must be done. In this case the factor can reduce the number of +clipped pels if overshooting occurs in the brightest band. The program +prints the number of clipped pels with im_warning(3). +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH BUGS +clip==0 case not working too well. +.SH SEE\ ALSO +im_add(3), im_lintra(3), im_multiply(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 05/12/1991 diff --git a/libsrc/arithmetic/man3/im_log10tra.3 b/libsrc/arithmetic/man3/im_log10tra.3 new file mode 100644 index 00000000..9ae2c5a1 --- /dev/null +++ b/libsrc/arithmetic/man3/im_log10tra.3 @@ -0,0 +1,2 @@ +.so man3/im_exp10tra.3 + diff --git a/libsrc/arithmetic/man3/im_logtra.3 b/libsrc/arithmetic/man3/im_logtra.3 new file mode 100644 index 00000000..9ae2c5a1 --- /dev/null +++ b/libsrc/arithmetic/man3/im_logtra.3 @@ -0,0 +1,2 @@ +.so man3/im_exp10tra.3 + diff --git a/libsrc/arithmetic/man3/im_max.3 b/libsrc/arithmetic/man3/im_max.3 new file mode 100644 index 00000000..fc6dde89 --- /dev/null +++ b/libsrc/arithmetic/man3/im_max.3 @@ -0,0 +1,2 @@ +.so man3/im_avg.3 + diff --git a/libsrc/arithmetic/man3/im_maxpos.3 b/libsrc/arithmetic/man3/im_maxpos.3 new file mode 100644 index 00000000..fc6dde89 --- /dev/null +++ b/libsrc/arithmetic/man3/im_maxpos.3 @@ -0,0 +1,2 @@ +.so man3/im_avg.3 + diff --git a/libsrc/arithmetic/man3/im_maxpos_vec.3 b/libsrc/arithmetic/man3/im_maxpos_vec.3 new file mode 100644 index 00000000..6a82283c --- /dev/null +++ b/libsrc/arithmetic/man3/im_maxpos_vec.3 @@ -0,0 +1,42 @@ +.TH IM_MAXPOS_VEC 3 "01 September 2006" +.SH NAME + im_maxpos_vec, im_minpos_vec \- Find highest or lowest pixel values +.SH SYNOPSIS +.nf +.B #include +.sp +.BI "int im_maxpos_vec( IMAGE " "*im" ", int " "*xpos" ", int " "*ypos" ", double " "*maxima" ", int " "n" " ); +.br + +.BI "int im_minpos_vec( IMAGE " "*im" ", int " "*xpos" ", int " "*ypos" ", double " "*minima" ", int " "n" " ); +.fi +.SH DESCRIPTION +.B im_maxpos_vec(3) +fills the arrays +.I xpos +and +.I ypos +with the x and y coordinates of the +.I n +pixels in the image +.I im +which have the highest values. It writes those values (at double-precsion) +into the corresponding elements in the array +.IR maxima ". +.PP +If there is a draw for the +.IR n "th +pixel, then which of the drawing pixels is used is not defined. +.PP +.B im_minpos_vec(3) +performs the same operation, looking for the lowest valued pixels. +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_maxpos(3), im_minpos(3) +.SH COPYRIGHT +.br +Copyright 2006, The Nottingham Trent University. +Copyright 2006, Tom Vajzovic. +.SH AUTHOR +Tom Vajzovic diff --git a/libsrc/arithmetic/man3/im_measure.3 b/libsrc/arithmetic/man3/im_measure.3 new file mode 100644 index 00000000..5aa240f1 --- /dev/null +++ b/libsrc/arithmetic/man3/im_measure.3 @@ -0,0 +1,47 @@ +.TH IM_MEASURE 3 "24 October 1992" +.SH NAME +im_measure \- measure colour patches off images +.SH SYNOPSIS +#include + +DOUBLEMASK *im_measure(in, box, h, v, sel, nsel, name) +.br +IMAGE *in; +.br +IMAGE_BOX *box; +.br +int h, v; +.br +int *sel; +.br +int nsel; +.br +char *name; + +.SH DESCRIPTION +Analyse a grid of colour patches, producing a DOUBLEMASK of averages. +Pass an IMAGE, an IMAGE_BOX, the number of horizontal and vertical +patches, an array giving the numbers of the patches to measure (patches +are numbered left-to-right, top-to-bottom, starting with 1) and the name we +should give the output mask. Return a DOUBLEMASK in which rows are patches and +columns are bands. Only the central 50% of each patch is averaged. + +Example: 6 band image of 4x2 block of colour patches. + + +---+---+---+---+ + | 1 | 2 | 3 | 4 | + +---+---+---+---+ + | 5 | 6 | 7 | 8 | + +---+---+---+---+ + +Then call im_measure( im, box, 4, 2, { 2, 4 }, 2, "fred" ) makes a mask +"fred" which has 6 columns, two rows. The first row contains the averages +for patch 2, the second for patch 4. + +Output warnings: a warning is issued if the standard deviation of any patch is +greater than 20% of the mean of that patch. + +.SH RETURN VALUE +NULL on error. +.SH SEE ALSO +im_avg(3), im_deviate(3), im_stats(3). diff --git a/libsrc/arithmetic/man3/im_min.3 b/libsrc/arithmetic/man3/im_min.3 new file mode 100644 index 00000000..46443e20 --- /dev/null +++ b/libsrc/arithmetic/man3/im_min.3 @@ -0,0 +1 @@ +.so man3/im_avg.3 diff --git a/libsrc/arithmetic/man3/im_minpos.3 b/libsrc/arithmetic/man3/im_minpos.3 new file mode 100644 index 00000000..fc6dde89 --- /dev/null +++ b/libsrc/arithmetic/man3/im_minpos.3 @@ -0,0 +1,2 @@ +.so man3/im_avg.3 + diff --git a/libsrc/arithmetic/man3/im_minpos_vec.3 b/libsrc/arithmetic/man3/im_minpos_vec.3 new file mode 100644 index 00000000..b92d839c --- /dev/null +++ b/libsrc/arithmetic/man3/im_minpos_vec.3 @@ -0,0 +1,2 @@ +.so man3/im_maxpos_vec.3 + diff --git a/libsrc/arithmetic/man3/im_multiply.3 b/libsrc/arithmetic/man3/im_multiply.3 new file mode 100644 index 00000000..29c07b75 --- /dev/null +++ b/libsrc/arithmetic/man3/im_multiply.3 @@ -0,0 +1 @@ +.so man3/im_cmulnorm.3 diff --git a/libsrc/arithmetic/man3/im_powtra.3 b/libsrc/arithmetic/man3/im_powtra.3 new file mode 100644 index 00000000..9ae2c5a1 --- /dev/null +++ b/libsrc/arithmetic/man3/im_powtra.3 @@ -0,0 +1,2 @@ +.so man3/im_exp10tra.3 + diff --git a/libsrc/arithmetic/man3/im_powtra_vec.3 b/libsrc/arithmetic/man3/im_powtra_vec.3 new file mode 100644 index 00000000..b7685e3d --- /dev/null +++ b/libsrc/arithmetic/man3/im_powtra_vec.3 @@ -0,0 +1 @@ +.so man3/im_exp10tra.3 diff --git a/libsrc/arithmetic/man3/im_remainder.3 b/libsrc/arithmetic/man3/im_remainder.3 new file mode 100644 index 00000000..3a0a96c6 --- /dev/null +++ b/libsrc/arithmetic/man3/im_remainder.3 @@ -0,0 +1,47 @@ +.TH REMAINDER 3 "May 2002" +.SH NAME +im_remainder, im_remainderconst, im_remainderconst_vec \- remainder after integer division +.SH SYNOPSIS +#include + +int im_remainder( IMAGE *in1, IMAGE *in2, IMAGE *out ) +.br + +int im_remainderconst( IMAGE *in, IMAGE *out, double c ) +.br + +int im_remainderconst_vec( IMAGE *in, IMAGE *out, int n, double *c ) +.SH DESCRIPTION +.B im_remainder(3) +calculates the remainder after integer division of two images. The output +type is the same as the type of +.B in1 +unless +.B in1 +is float or complex, in which +case the output type is signed integer. + +.B im_remainderconst(3) +calculates the remainder after integer division of +.B in +by the constant +.B c. +The output +type is the same as the type of +.B in +unless +.B in +is float or complex, in which +case the output type is signed integer. + +.B im_remainderconst_vec(3) +works as +.B im_remainderconst(3), +but lets you specify a constant per band. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_add(3), im_lintra(3), im_divide(3) +.SH COPYRIGHT +National Gallery, 2002 diff --git a/libsrc/arithmetic/man3/im_remainderconst.3 b/libsrc/arithmetic/man3/im_remainderconst.3 new file mode 100644 index 00000000..bef634ce --- /dev/null +++ b/libsrc/arithmetic/man3/im_remainderconst.3 @@ -0,0 +1 @@ +.so man3/im_remainder.3 diff --git a/libsrc/arithmetic/man3/im_remainderconst_vec.3 b/libsrc/arithmetic/man3/im_remainderconst_vec.3 new file mode 100644 index 00000000..bef634ce --- /dev/null +++ b/libsrc/arithmetic/man3/im_remainderconst_vec.3 @@ -0,0 +1 @@ +.so man3/im_remainder.3 diff --git a/libsrc/arithmetic/man3/im_rint.3 b/libsrc/arithmetic/man3/im_rint.3 new file mode 100644 index 00000000..15f371e1 --- /dev/null +++ b/libsrc/arithmetic/man3/im_rint.3 @@ -0,0 +1,18 @@ +.TH IM_RINT 3 +.SH NAME +im_rint \- for each pixel, find the nearest integral value +.SH SYNOPSIS +#include + +int im_rint( in, out ) +.br +IMAGE *in, *out; +.SH DESCRIPTION +.B im_rint(3) +finds the nearest integral value. Copy for integer types, +call rint(3) for float and complex types. Output type == input type. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_abs(3), im_clip2fmt(3), im_floor(3) + diff --git a/libsrc/arithmetic/man3/im_sign.3 b/libsrc/arithmetic/man3/im_sign.3 new file mode 100644 index 00000000..f6fc7781 --- /dev/null +++ b/libsrc/arithmetic/man3/im_sign.3 @@ -0,0 +1,19 @@ +.TH IM_SIGN 3 "July 2002" +.SH NAME +im_sign \- find the unit vector in the direction of value +.SH SYNOPSIS +#include + +int im_sign( in, out ) +.br +IMAGE *in, *out; +.SH DESCRIPTION +.B im_sign(3) +finds the unit vector in the direction of the pixel value. For non-complex +images, it returns a signed char image with values -1, 0, and 1 for negative, +zero and positive pixels. For complex images it returns a +complex normalised to length 1. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_abs(3), im_cmulnorm(3), im_c2amph(3) diff --git a/libsrc/arithmetic/man3/im_sintra.3 b/libsrc/arithmetic/man3/im_sintra.3 new file mode 100644 index 00000000..83ac049e --- /dev/null +++ b/libsrc/arithmetic/man3/im_sintra.3 @@ -0,0 +1 @@ +.so man3/im_costra.3 diff --git a/libsrc/arithmetic/man3/im_stats.3 b/libsrc/arithmetic/man3/im_stats.3 new file mode 100644 index 00000000..4061098b --- /dev/null +++ b/libsrc/arithmetic/man3/im_stats.3 @@ -0,0 +1,24 @@ +.TH IM_STATS 3 "24 October 1992" +.SH NAME +im_stats \- calculate many image statistics in a single pass +.SH SYNOPSIS +.B #include + +.B DOUBLEMASK *im_stats(in) +.br +.B IMAGE *in; +.SH DESCRIPTION +Find many image statistics in a single pass through the PELs. Returns a +DOUBLEMASK of 6 columns by n+1 (where n is number of bands in image in) rows. +Columns are statistics, and are, in order: minimum, maximum, sum, sum of +squares, mean, standard deviation. Row 0 has statistics for all bands +together, row 1 has stats for band 1, and so on. + +Non-complex images only! +.SH RETURN VALUE +NULL on error. +.SH SEE\ ALSO +im_avg(3), im_deviate(3) +.SH COPYRIGHT +.br +National Gallery, 1992 diff --git a/libsrc/arithmetic/man3/im_subtract.3 b/libsrc/arithmetic/man3/im_subtract.3 new file mode 100644 index 00000000..a20f8b40 --- /dev/null +++ b/libsrc/arithmetic/man3/im_subtract.3 @@ -0,0 +1,41 @@ +.TH SUBTRACTION 3 "24 April 1991" +.SH NAME +im_subtract \- subtracts two images +.SH SYNOPSIS +#include + +int im_subtract(in1, in2, out) +.br +IMAGE *in1, *in2, *out; +.SH DESCRIPTION + +This functions calculates in1 - in2 and writes the result in the image +descriptor out. Input images in1 and in2 should have the same channels and +the same sizes; however they can be of different types. Only the history of +the image descriptor pointed by in1 is copied to out. + +The type of the output is given by the table: + + in1 - uchar char ushort short uint int + -------|----------------------------------------- + in2 | + uchar | short short short short int int + char | short short short short int int + ushort | short short short short int int + short | short short short short int int + uint | int int int int int int + int | int int int int int int + +The result of this operation cannot be unsigned. For float types, the refult +is float unless one of the inputs is double, in which case the result is +double. For complex types the result is FMTCOMPLEX, unless one of the inputs +is FMTDPCOMPLEX, in which case the output is FMTDPCOMPLEX. + +.SH BUGS +None of the functions checks the result for over/underflow. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_add(3), im_lintra(3), im_multiply(3). +.SH COPYRIGHT +National Gallery, 1995 diff --git a/libsrc/arithmetic/man3/im_tantra.3 b/libsrc/arithmetic/man3/im_tantra.3 new file mode 100644 index 00000000..83ac049e --- /dev/null +++ b/libsrc/arithmetic/man3/im_tantra.3 @@ -0,0 +1 @@ +.so man3/im_costra.3 diff --git a/libsrc/boolean/Makefile.am b/libsrc/boolean/Makefile.am new file mode 100644 index 00000000..a56c9b60 --- /dev/null +++ b/libsrc/boolean/Makefile.am @@ -0,0 +1,9 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libboolean.la + +libboolean_la_SOURCES = \ + bool_dispatch.c \ + boolean.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/boolean/bool_dispatch.c b/libsrc/boolean/bool_dispatch.c new file mode 100644 index 00000000..ae2cbb50 --- /dev/null +++ b/libsrc/boolean/bool_dispatch.c @@ -0,0 +1,316 @@ +/* VIPS function dispatch tables for conversion. + * + * J. Cupitt, 8/4/93. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Two images in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* One image plus one constant in, one image out. + */ +static im_arg_desc const_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "c" ) +}; + +/* One image plus doublevec in, one image out. + */ +static im_arg_desc vec_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLEVEC( "vec" ) +}; + +/* Call im_and via arg vector. + */ +static int +and_vec( im_object *argv ) +{ + return( im_andimage( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_and. + */ +static im_function and_desc = { + "im_andimage", /* Name */ + "bitwise and of two images", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + and_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_andconst via arg vector. + */ +static int +andconst_vec( im_object *argv ) +{ + int c = *((int *) argv[2]); + + return( im_andconst( argv[0], argv[1], c ) ); +} + +/* Description of im_andconst. + */ +static im_function andconst_desc = { + "im_andimageconst", /* Name */ + "bitwise and of an image with a constant", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + andconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_and_vec via arg vector. + */ +static int +and_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_and_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_andconst. + */ +static im_function and_vec_desc = { + "im_andimage_vec", /* Name */ + "bitwise and of an image with a vector constant", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + and_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_or via arg vector. + */ +static int +or_vec( im_object *argv ) +{ + return( im_orimage( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_or. + */ +static im_function or_desc = { + "im_orimage", /* Name */ + "bitwise or of two images", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + or_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_orconst via arg vector. + */ +static int +orconst_vec( im_object *argv ) +{ + int c = *((int *) argv[2]); + + return( im_orconst( argv[0], argv[1], c ) ); +} + +/* Description of im_orconst. + */ +static im_function orconst_desc = { + "im_orimageconst", /* Name */ + "bitwise or of an image with a constant", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + orconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_or_vec via arg vector. + */ +static int +or_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_or_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_orconst. + */ +static im_function or_vec_desc = { + "im_orimage_vec", /* Name */ + "bitwise or of an image with a vector constant", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + or_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_eor via arg vector. + */ +static int +eor_vec( im_object *argv ) +{ + return( im_eorimage( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_eor. + */ +static im_function eor_desc = { + "im_eorimage", /* Name */ + "bitwise eor of two images", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + eor_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_eorconst via arg vector. + */ +static int +eorconst_vec( im_object *argv ) +{ + int c = *((int *) argv[2]); + + return( im_eorconst( argv[0], argv[1], c ) ); +} + +/* Description of im_eorconst. + */ +static im_function eorconst_desc = { + "im_eorimageconst", /* Name */ + "bitwise eor of an image with a constant", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + eorconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_eor_vec via arg vector. + */ +static int +eor_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_eor_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_eorconst. + */ +static im_function eor_vec_desc = { + "im_eorimage_vec", /* Name */ + "bitwise eor of an image with a vector constant", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + eor_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_shiftleft via arg vector. + */ +static int +shiftleft_vec( im_object *argv ) +{ + int n = *((int *) argv[2]); + + return( im_shiftleft( argv[0], argv[1], n ) ); +} + +/* Description of im_shiftleft. + */ +static im_function shiftleft_desc = { + "im_shiftleft", /* Name */ + "shift integer image n bits to left", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + shiftleft_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_shiftright via arg vector. + */ +static int +shiftright_vec( im_object *argv ) +{ + int n = *((int *) argv[2]); + + return( im_shiftright( argv[0], argv[1], n ) ); +} + +/* Description of im_shiftright. + */ +static im_function shiftright_desc = { + "im_shiftright", /* Name */ + "shift integer image n bits to right", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + shiftright_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *bool_list[] = { + &and_desc, + &andconst_desc, + &and_vec_desc, + &or_desc, + &orconst_desc, + &or_vec_desc, + &eor_desc, + &eorconst_desc, + &eor_vec_desc, + &shiftleft_desc, + &shiftright_desc +}; + +/* Package of functions. + */ +im_package im__boolean = { + "boolean", + IM_NUMBER( bool_list ), + bool_list +}; diff --git a/libsrc/boolean/boolean.c b/libsrc/boolean/boolean.c new file mode 100644 index 00000000..1653beb6 --- /dev/null +++ b/libsrc/boolean/boolean.c @@ -0,0 +1,668 @@ +/* @(#) Bitwise operations on VASARI images. Inputs must be some + * @(#) integer type and have the same size and number of bands. Use + * @(#) im_eorconst( in, out, -1 ) for im_not. + * @(#) + * @(#) int im_andimage( a, b, out ) int im_andconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) unsigned char c; + * @(#) + * @(#) int im_orimage( a, b, out ) int im_orconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) unsigned char c; + * @(#) + * @(#) int im_eorimage( a, b, out ) int im_eorconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) unsigned char c; + * @(#) + * @(#) int im_shiftleft( in, out, n ) int im_shiftright( in, out, n ) + * @(#) IMAGE *in, *out; IMAGE *in, *out; + * @(#) int n; int n; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail). + * + * Modified: + * 15/12/94 JC + * - ANSIfied + * - adapted to partials with im_wrap... + * 25/1/95 JC + * - added check1ary(), check2ary() + * 8/2/95 JC + * - new im_wrapmany + * 19/7/95 JC + * - added im_shiftleft() and im_shiftright() + * 6/7/98 JC + * - added _vec forms + * - removed *p++ stuff + * 10/9/99 JC + * - and/or/eor now do all int types + * 10/10/02 JC + * - renamed im_and() etc. as im_andimage() to remove breakage in the C++ + * layer if operator names are turned on + * 30/6/04 + * - now cast float/complex args to int + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Save a bit of typing. + */ +#define UC IM_BANDFMT_UCHAR +#define C IM_BANDFMT_CHAR +#define US IM_BANDFMT_USHORT +#define S IM_BANDFMT_SHORT +#define UI IM_BANDFMT_UINT +#define I IM_BANDFMT_INT +#define F IM_BANDFMT_FLOAT +#define M IM_BANDFMT_COMPLEX +#define D IM_BANDFMT_DOUBLE +#define DM IM_BANDFMT_DPCOMPLEX + +/* Type conversions for boolean. + */ +static int iformat[10][10] = { + /* UC C US S UI I F M D DM */ +/* UC */ { UC, C, US, S, UI, I, I, I, I, I }, +/* C */ { C, C, S, S, I, I, I, I, I, I }, +/* US */ { US, S, US, S, UI, I, I, I, I, I }, +/* S */ { S, S, S, S, I, I, I, I, I, I }, +/* UI */ { UI, I, UI, I, UI, I, I, I, I, I }, +/* I */ { I, I, I, I, I, I, I, I, I, I }, +/* F */ { I, I, I, I, I, I, I, I, I, I }, +/* M */ { I, I, I, I, I, I, I, I, I, I }, +/* D */ { I, I, I, I, I, I, I, I, I, I }, +/* DM */ { I, I, I, I, I, I, I, I, I, I } +}; + +/* Check args. Cast inputs to matching integer format. + */ +static int +check( char *name, IMAGE **in, IMAGE *out ) +{ + int i, n; + + /* Count args. + */ + for( n = 0; in[n]; n++ ) { + if( in[n]->Coding != IM_CODING_NONE ) { + im_errormsg( "%s: uncoded images only", name ); + return( -1 ); + } + } + + /* Check sizes match. + */ + for( i = 1; i < n; i++ ) + if( in[0]->Bands != in[i]->Bands || + in[0]->Xsize != in[i]->Xsize || + in[0]->Ysize != in[i]->Ysize ) { + im_errormsg( "%s: images differ in size", name ); + return( -1 ); + } + + /* Prepare the output image. + */ + if( im_cp_desc_array( out, in ) ) + return( -1 ); + + /* Calculate type conversion ... just 1ary and 2ary. + */ + switch( n ) { + case 1: + out->BandFmt = iformat[0][in[0]->BandFmt]; + break; + + case 2: + out->BandFmt = iformat[in[1]->BandFmt][in[0]->BandFmt]; + break; + + default: + assert( FALSE ); + } + + for( i = 0; i < n; i++ ) { + IMAGE *t = im_open_local( out, name, "p" ); + + if( !t || im_clip2fmt( in[i], t, out->BandFmt ) ) + return( -1 ); + in[i] = t; + } + + return( 0 ); +} + +/* A selection of main loops. As with im_add(), only implement monotype + * operations. TYPE is some integer type, signed or unsigned. + */ +#define AND2( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + TYPE *tp1 = (TYPE *) p1; \ + TYPE *tp2 = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + tq[x] = tp1[x] & tp2[x]; \ +} + +#define OR2( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + TYPE *tp1 = (TYPE *) p1; \ + TYPE *tp2 = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + tq[x] = tp1[x] | tp2[x]; \ +} + +#define EOR2( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + TYPE *tp1 = (TYPE *) p1; \ + TYPE *tp2 = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + tq[x] = tp1[x] ^ tp2[x]; \ +} + +#define ANDCONST( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + TYPE *tp = (TYPE *) p; \ + TYPE *tc = (TYPE *) c; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < bands; b++, i++ ) \ + tq[i] = tp[i] & tc[b]; \ +} + +#define ORCONST( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + TYPE *tp = (TYPE *) p; \ + TYPE *tc = (TYPE *) c; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < bands; b++, i++ ) \ + tq[i] = tp[i] | tc[b]; \ +} + +#define EORCONST( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + TYPE *tp = (TYPE *) p; \ + TYPE *tc = (TYPE *) c; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < bands; b++, i++ ) \ + tq[i] = tp[i] ^ tc[b]; \ +} + +/* The above, wrapped up as buffer processing functions. + */ +static void +and_buffer( PEL **p, PEL *q, int n, IMAGE *im ) +{ + int x; + int bands = im->Bands; + int ne = n * bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: AND2( signed char ); break; + case IM_BANDFMT_UCHAR: AND2( unsigned char ); break; + case IM_BANDFMT_SHORT: AND2( signed short ); break; + case IM_BANDFMT_USHORT: AND2( unsigned short ); break; + case IM_BANDFMT_INT: AND2( signed int ); break; + case IM_BANDFMT_UINT: AND2( unsigned int ); break; + + default: + error_exit( "im_and: internal error" ); + } +} + +static void +or_buffer( PEL **p, PEL *q, int n, IMAGE *in1 ) +{ + int x; + int bands = in1->Bands; + int ne = n * bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + + switch( in1->BandFmt ) { + case IM_BANDFMT_CHAR: OR2( signed char ); break; + case IM_BANDFMT_UCHAR: OR2( unsigned char ); break; + case IM_BANDFMT_SHORT: OR2( signed short ); break; + case IM_BANDFMT_USHORT: OR2( unsigned short ); break; + case IM_BANDFMT_INT: OR2( signed int ); break; + case IM_BANDFMT_UINT: OR2( unsigned int ); break; + + default: + error_exit( "im_or: internal error" ); + } +} + +static void +eor_buffer( PEL **p, PEL *q, int n, IMAGE *in1 ) +{ + int x; + int bands = in1->Bands; + int ne = n * bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + + switch( in1->BandFmt ) { + case IM_BANDFMT_CHAR: EOR2( signed char ); break; + case IM_BANDFMT_UCHAR: EOR2( unsigned char ); break; + case IM_BANDFMT_SHORT: EOR2( signed short ); break; + case IM_BANDFMT_USHORT: EOR2( unsigned short ); break; + case IM_BANDFMT_INT: EOR2( signed int ); break; + case IM_BANDFMT_UINT: EOR2( unsigned int ); break; + + default: + error_exit( "im_eor: internal error" ); + } +} + +static void +andconst_buffer( PEL *p, PEL *q, int n, IMAGE *in, PEL *c ) +{ + int x, i, b; + int bands = in->Bands; + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: ANDCONST( signed char ); break; + case IM_BANDFMT_UCHAR: ANDCONST( unsigned char ); break; + case IM_BANDFMT_SHORT: ANDCONST( signed short ); break; + case IM_BANDFMT_USHORT: ANDCONST( unsigned short ); break; + case IM_BANDFMT_INT: ANDCONST( signed int ); break; + case IM_BANDFMT_UINT: ANDCONST( unsigned int ); break; + + default: + error_exit( "im_andconst: internal error" ); + } +} + +static void +orconst_buffer( PEL *p, PEL *q, int n, IMAGE *in, PEL *c ) +{ + int x, i, b; + int bands = in->Bands; + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: ORCONST( signed char ); break; + case IM_BANDFMT_UCHAR: ORCONST( unsigned char ); break; + case IM_BANDFMT_SHORT: ORCONST( signed short ); break; + case IM_BANDFMT_USHORT: ORCONST( unsigned short ); break; + case IM_BANDFMT_INT: ORCONST( signed int ); break; + case IM_BANDFMT_UINT: ORCONST( unsigned int ); break; + + default: + error_exit( "im_orconst: internal error" ); + } +} + +static void +eorconst_buffer( PEL *p, PEL *q, int n, IMAGE *in, PEL *c ) +{ + int x, i, b; + int bands = in->Bands; + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: EORCONST( signed char ); break; + case IM_BANDFMT_UCHAR: EORCONST( unsigned char ); break; + case IM_BANDFMT_SHORT: EORCONST( signed short ); break; + case IM_BANDFMT_USHORT: EORCONST( unsigned short ); break; + case IM_BANDFMT_INT: EORCONST( signed int ); break; + case IM_BANDFMT_UINT: EORCONST( unsigned int ); break; + + default: + error_exit( "im_eorconst: internal error" ); + } +} + +/* The above, wrapped up as im_*() functions. + */ +int +im_andimage( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + /* Check images. + */ + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( check( "im_andimage", invec, out ) ) + return( -1 ); + + /* Process! + */ + if( im_wrapmany( invec, out, (im_wrapmany_fn) and_buffer, out, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_orimage( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( check( "im_orimage", invec, out ) ) + return( -1 ); + + if( im_wrapmany( invec, out, (im_wrapmany_fn) or_buffer, out, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_eorimage( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( check( "im_eorimage", invec, out ) ) + return( -1 ); + + if( im_wrapmany( invec, out, (im_wrapmany_fn) eor_buffer, out, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* Cast a vector of double to a vector of TYPE. + */ +#define CAST( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + \ + for( i = 0; i < out->Bands; i++ ) \ + tq[i] = (TYPE) p[i]; \ +} + +/* Make a pixel of output type from a realvec. + */ +static PEL * +make_pixel( IMAGE *out, double *p ) +{ + PEL *q; + int i; + + if( !(q = IM_ARRAY( out, IM_IMAGE_SIZEOF_PEL( out ), PEL )) ) + return( NULL ); + + switch( out->BandFmt ) { + case IM_BANDFMT_CHAR: CAST( signed char ); break; + case IM_BANDFMT_UCHAR: CAST( unsigned char ); break; + case IM_BANDFMT_SHORT: CAST( signed short ); break; + case IM_BANDFMT_USHORT: CAST( unsigned short ); break; + case IM_BANDFMT_INT: CAST( signed int ); break; + case IM_BANDFMT_UINT: CAST( unsigned int ); break; + + default: + error_exit( "make_pixel: internal error" ); + } + + return( q ); +} + +int +im_and_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + PEL *cb; + + invec[0] = in; invec[1] = NULL; + if( check( "im_andconst", invec, out ) ) + return( -1 ); + in = invec[0]; + if( n != in->Bands ) { + im_errormsg( "im_and_vec: vec size does not match bands" ); + return( -1 ); + } + if( !(cb = make_pixel( out, c )) ) + return( -1 ); + + if( im_wrapone( in, out, + (im_wrapone_fn) andconst_buffer, (void *) in, (void *) cb ) ) + return( -1 ); + + return( 0 ); +} + +int +im_or_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + PEL *cb; + + invec[0] = in; invec[1] = NULL; + if( check( "im_orconst", invec, out ) ) + return( -1 ); + in = invec[0]; + if( n != in->Bands ) { + im_errormsg( "im_or_vec: vec size does not match bands" ); + return( -1 ); + } + if( !(cb = make_pixel( out, c )) ) + return( -1 ); + + if( im_wrapone( in, out, + (im_wrapone_fn) orconst_buffer, (void *) in, (void *) cb ) ) + return( -1 ); + + return( 0 ); +} + +int +im_eor_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + PEL *cb; + + invec[0] = in; invec[1] = NULL; + if( check( "im_eorconst", invec, out ) ) + return( -1 ); + in = invec[0]; + if( n != in->Bands ) { + im_errormsg( "im_eor_vec: vec size does not match bands" ); + return( -1 ); + } + if( !(cb = make_pixel( out, c )) ) + return( -1 ); + + if( im_wrapone( in, out, + (im_wrapone_fn) eorconst_buffer, (void *) in, (void *) cb ) ) + return( -1 ); + + return( 0 ); +} + +/* Cast a double to a vector of TYPE. + */ +#define CCAST( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + \ + for( i = 0; i < in->Bands; i++ ) \ + tq[i] = (TYPE) p; \ +} + +/* Make a pixel of output type from a single double. + */ +static double * +make_pixel_const( IMAGE *in, IMAGE *out, double p ) +{ + double *q; + int i; + + if( !(q = IM_ARRAY( out, in->Bands, double )) ) + return( NULL ); + for( i = 0; i < in->Bands; i++ ) + q[i] = p; + + return( q ); +} + +int +im_andconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v = make_pixel_const( in, out, c ); + + return( !v || im_and_vec( in, out, in->Bands, v ) ); +} + +int +im_orconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v = make_pixel_const( in, out, c ); + + return( !v || im_or_vec( in, out, in->Bands, v ) ); +} + +int +im_eorconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v = make_pixel_const( in, out, c ); + + return( !v || im_eor_vec( in, out, in->Bands, v ) ); +} + +/* Assorted shift operations. + */ +#define SHIFTL( TYPE ) \ +{\ + TYPE *pt = (TYPE *) p;\ + TYPE *qt = (TYPE *) q;\ + \ + for( x = 0; x < ne; x++ )\ + qt[x] = pt[x] << n;\ +} + +#define SHIFTR( TYPE ) \ +{\ + TYPE *pt = (TYPE *) p;\ + TYPE *qt = (TYPE *) q;\ + \ + for( x = 0; x < ne; x++ )\ + qt[x] = pt[x] >> n;\ +} + +/* The above as buffer ops. + */ +static void +shiftleft_buffer( PEL *p, PEL *q, int len, IMAGE *in, int n ) +{ + int x; + int ne = len * in->Bands; + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: SHIFTL(unsigned char); break; + case IM_BANDFMT_CHAR: SHIFTL(signed char); break; + case IM_BANDFMT_USHORT: SHIFTL(unsigned short); break; + case IM_BANDFMT_SHORT: SHIFTL(signed short); break; + case IM_BANDFMT_UINT: SHIFTL(unsigned int); break; + case IM_BANDFMT_INT: SHIFTL(signed int); break; + + default: + error_exit( "im_shiftleft: internal error" ); + /*NOTREACHED*/ + } +} + +static void +shiftright_buffer( PEL *p, PEL *q, int len, IMAGE *in, int n ) +{ + int x; + int ne = len * in->Bands; + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: SHIFTR(unsigned char); break; + case IM_BANDFMT_CHAR: SHIFTR(signed char); break; + case IM_BANDFMT_USHORT: SHIFTR(unsigned short); break; + case IM_BANDFMT_SHORT: SHIFTR(signed short); break; + case IM_BANDFMT_UINT: SHIFTR(unsigned int); break; + case IM_BANDFMT_INT: SHIFTR(signed int); break; + + default: + error_exit( "im_shiftright: internal error" ); + /*NOTREACHED*/ + } +} + +/* The above as im_*() functions. + */ +int +im_shiftleft( IMAGE *in, IMAGE *out, int n ) +{ + IMAGE *invec[2]; + + invec[0] = in; invec[1] = NULL; + if( check( "im_shiftleft", invec, out ) ) + return( -1 ); + in = invec[0]; + + if( im_wrapone( in, out, + (im_wrapone_fn) shiftleft_buffer, in, GINT_TO_POINTER( n ) ) ) + return( -1 ); + + return( 0 ); +} + +int +im_shiftright( IMAGE *in, IMAGE *out, int n ) +{ + IMAGE *invec[2]; + + invec[0] = in; invec[1] = NULL; + if( check( "im_shiftleft", invec, out ) ) + return( -1 ); + in = invec[0]; + + if( im_wrapone( in, out, + (im_wrapone_fn) shiftright_buffer, in, GINT_TO_POINTER( n ) ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/boolean/man3/Makefile.am b/libsrc/boolean/man3/Makefile.am new file mode 100644 index 00000000..9ddfef43 --- /dev/null +++ b/libsrc/boolean/man3/Makefile.am @@ -0,0 +1,16 @@ +man_MANS = \ + im_andimage.3 \ + im_andconst.3 \ + im_eorimage.3 \ + im_eorconst.3 \ + im_orimage.3 \ + im_orconst.3 \ + im_shiftleft.3 \ + im_shiftright.3 \ + im_eor_vec.3 \ + im_or_vec.3 \ + im_and_vec.3 + + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/boolean/man3/im_and_vec.3 b/libsrc/boolean/man3/im_and_vec.3 new file mode 100644 index 00000000..dfcd881e --- /dev/null +++ b/libsrc/boolean/man3/im_and_vec.3 @@ -0,0 +1,2 @@ +.so man3/im_andimage.3 + diff --git a/libsrc/boolean/man3/im_andconst.3 b/libsrc/boolean/man3/im_andconst.3 new file mode 100644 index 00000000..46ff7aff --- /dev/null +++ b/libsrc/boolean/man3/im_andconst.3 @@ -0,0 +1 @@ +.so man3/im_andimage.3 diff --git a/libsrc/boolean/man3/im_andimage.3 b/libsrc/boolean/man3/im_andimage.3 new file mode 100644 index 00000000..63ad74d1 --- /dev/null +++ b/libsrc/boolean/man3/im_andimage.3 @@ -0,0 +1,91 @@ +.TH IM_ANDIMAGE 3 "30 October 1992" +.SH NAME +im_andimage, im_andconst, im_and_vec, im_orimage, im_orconst, im_or_vec, +im_eorimage, im_eorconst, im_eor_vec \- boolean +operations on unsigned char images +.SH SYNOPSIS +.B #include + +.B int im_andimage(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_andconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_and_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_orimage(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_orconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_or_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_eorimage(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_eorconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_eor_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.SH DESCRIPTION +Perform bitwise logical operations on integer images. + +.B im_andimage(3) +performs bitwise and between corresponding pixels in a and b, writing the +result to out. Both images must be the same size and have the same number +of bands. They can have any integer type. + +.B im_andconst(3) +performs bitwise and between pixels in a and a single +constant value. +.B im_and_vec(3) +lets you specify n constants, one per band. + +.B im_orimage(3) +and +.B im_eorimage(3) +behave similarly. Use im_eorconst( in, out, -1 ) or +.B im_invert(3) +as a not operator. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_ifthenelse(3), im_equal(3). +.SH COPYRIGHT +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/boolean/man3/im_eor_vec.3 b/libsrc/boolean/man3/im_eor_vec.3 new file mode 100644 index 00000000..dfcd881e --- /dev/null +++ b/libsrc/boolean/man3/im_eor_vec.3 @@ -0,0 +1,2 @@ +.so man3/im_andimage.3 + diff --git a/libsrc/boolean/man3/im_eorconst.3 b/libsrc/boolean/man3/im_eorconst.3 new file mode 100644 index 00000000..dfcd881e --- /dev/null +++ b/libsrc/boolean/man3/im_eorconst.3 @@ -0,0 +1,2 @@ +.so man3/im_andimage.3 + diff --git a/libsrc/boolean/man3/im_eorimage.3 b/libsrc/boolean/man3/im_eorimage.3 new file mode 100644 index 00000000..dfcd881e --- /dev/null +++ b/libsrc/boolean/man3/im_eorimage.3 @@ -0,0 +1,2 @@ +.so man3/im_andimage.3 + diff --git a/libsrc/boolean/man3/im_or_vec.3 b/libsrc/boolean/man3/im_or_vec.3 new file mode 100644 index 00000000..dfcd881e --- /dev/null +++ b/libsrc/boolean/man3/im_or_vec.3 @@ -0,0 +1,2 @@ +.so man3/im_andimage.3 + diff --git a/libsrc/boolean/man3/im_orconst.3 b/libsrc/boolean/man3/im_orconst.3 new file mode 100644 index 00000000..dfcd881e --- /dev/null +++ b/libsrc/boolean/man3/im_orconst.3 @@ -0,0 +1,2 @@ +.so man3/im_andimage.3 + diff --git a/libsrc/boolean/man3/im_orimage.3 b/libsrc/boolean/man3/im_orimage.3 new file mode 100644 index 00000000..46ff7aff --- /dev/null +++ b/libsrc/boolean/man3/im_orimage.3 @@ -0,0 +1 @@ +.so man3/im_andimage.3 diff --git a/libsrc/boolean/man3/im_shiftleft.3 b/libsrc/boolean/man3/im_shiftleft.3 new file mode 100644 index 00000000..711f6093 --- /dev/null +++ b/libsrc/boolean/man3/im_shiftleft.3 @@ -0,0 +1,30 @@ +.TH SHIFT 3 "30 October 1992" +.SH NAME +im_shiftleft, im_shiftright \- bit shift operations +.SH SYNOPSIS +.B #include + +.B int im_shiftleft( in, out, n ) +.br +.B IMAGE *in, *out; +.br +.B int n; + +.B int im_shiftright( in, out, n ) +.br +.B IMAGE *in, *out; +.br +.B int n; + +.SH DESCRIPTION +Shift integer images n bits left or right. Shifts on signed images are +sign-preserving. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_ifthenelse(3), im_equal(3), im_andimage(3). +.SH COPYRIGHT +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/boolean/man3/im_shiftright.3 b/libsrc/boolean/man3/im_shiftright.3 new file mode 100644 index 00000000..3551b625 --- /dev/null +++ b/libsrc/boolean/man3/im_shiftright.3 @@ -0,0 +1 @@ +.so man3/im_shiftleft.3 diff --git a/libsrc/colour/Makefile.am b/libsrc/colour/Makefile.am new file mode 100644 index 00000000..0d10d472 --- /dev/null +++ b/libsrc/colour/Makefile.am @@ -0,0 +1,32 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libcolour.la + +libcolour_la_SOURCES = \ + colour.c \ + colour_dispatch.c \ + derived.c \ + im_icc_transform.c \ + im_LCh2Lab.c \ + im_LCh2UCS.c \ + im_Lab2LCh.c \ + im_Lab2LabQ.c \ + im_Lab2LabS.c \ + im_Lab2XYZ.c \ + im_LabQ2Lab.c \ + im_LabQ2LabS.c \ + im_LabQ2disp.c \ + im_LabS2LabQ.c \ + im_LabS2Lab.c \ + im_lab_morph.c \ + im_UCS2LCh.c \ + im_XYZ2Lab.c \ + im_XYZ2Yxy.c \ + im_Yxy2XYZ.c \ + im_XYZ2disp.c \ + im_dE00_fromLab.c \ + im_dECMC_fromLab.c \ + im_dE_fromLab.c \ + im_disp2XYZ.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/colour/colour.c b/libsrc/colour/colour.c new file mode 100644 index 00000000..158a2897 --- /dev/null +++ b/libsrc/colour/colour.c @@ -0,0 +1,1118 @@ +/* Convert colours in various ways. + * Written: January 1990 + * Modified .. innumerable times + * Code by: DS, JC, J-Ph.L. + * 18/7/93 JC + * - final tidies before v7 release + * - ANSIfied + * - code for samples removed + * 5/5/94 JC + * - nint() -> rint() to make ANSI easier + * 14/3/96 JC + * - new display characterisation + * - speed-up to im_col_XYZ2rgb() and im_col_rgb2XYZ() + * 4/3/98 JC + * - new display profile for ultra2 + * - new sRGB profile + * 17/8/98 JC + * - error_exit() removed, now clips + * 26/11/03 Andrey Kiselev + * - tiny clean-up for calcul_tables() + * - some reformatting + * 23/7/07 + * - tiny cleanup for make_hI() prevents cond jump on ui in valgrind + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Values for IM_TYPE_sRGB. + */ +static struct im_col_display srgb_profile = { + "sRGB", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { 3.2410, -1.5374, -0.4986 }, + { -0.9692, 1.8760, 0.0416 }, + { 0.0556, -0.2040, 1.0570 } + }, + + 80.0, /* Luminosity of reference white */ + .3127, .3291, /* x, y for reference white */ + 100, 100, 100, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 1, 1, 1, /* Residual light o/p for black pixel */ + 2.4, 2.4, 2.4, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Values for my Ultra2, 20/2/98. Figures from a Minolta CA-100 CRT analyser. + * Contrast at max, brightness at 42, room lights out. + */ +static struct im_col_display ultra2 = { + "ultra2-20/2/98", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { .704, -0.302, -.103 }, + { -.708, 1.317, .032 }, + { .005, -.015, .071 } + }, + + 64.0, /* Luminosity of reference white */ + .2137, .3291, /* x, y for reference white */ + 14.4, 44.0, 5.4, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 0.03, 0.03, 0.03, /* Residual light o/p for black pixel */ + 2.5, 2.5, 2.4, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Values for our display. These were obtained with a TV analyser in late + * Feb. 1990. The reference white is simply r=g=b=255. + */ +static struct im_col_display im_col_screen_white = { + "Screen", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { .660, -0.276, -.10 }, + { -.663, 1.293, .0265 }, + { .003, -.017, .0734 } + }, + + 58.7, /* Luminosity of reference white */ + .284, .273, /* x, y for reference white */ + 14.2, 38.4, 6.1, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 0.0, 0.0, 0.0, /* Residual light o/p for black pixel */ + 2.8, 2.9, 2.9, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Adjusted version of above for SPARCstation2 screens. Turn down the gamma + * to make blacks blacker. + */ +static struct im_col_display im_col_SPARC_white = { + "SPARC", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { .660, -0.276, -.10 }, + { -.663, 1.293, .0265 }, + { .003, -.017, .0734 } + }, + + 58.7, /* Luminosity of reference white */ + .284, .273, /* x, y for reference white */ + 14.2, 38.4, 4, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 0.0, 0.0, 0.0, /* Residual light o/p for black pixel */ + 2.0, 2.0, 2.0, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Values for D65 white. This gives a smaller range of colours than + * screen_white. + */ +static struct im_col_display im_col_D65_white = { + "D65", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { .660, -0.276, -.10 }, + { -.663, 1.293, .0265 }, + { .003, -.017, .0734 } + }, + + 49.9, /* Luminosity of reference white */ + .3127, .3290, /* x, y for reference white */ + 11.6, 35.0, 3.3, /* Light o/p for reference white */ + 241, 255, 177, /* Pixel values for ref. white */ + 0.1, 0.1, 0.1, /* Residual light o/p for black pixel */ + 2.8, 2.9, 2.7, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Values for Barco calibrator monitor + */ +static struct im_col_display im_col_barco_white = { + "Barco", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { .749, -0.322, -.123 }, + { -.755, 1.341, .033 }, + { .007, -.019, .0898 } + }, + + 80.0, /* Luminosity of reference white */ + .3128, .3292, /* x, y for reference white */ + 20.45, 52.73, 6.81, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 0.02, 0.053, 0.007, /* Residual light o/p for black pixel */ + 2.23, 2.13, 2.12, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Values for Mitsubishi dye-sub colour printer. + */ +static struct im_col_display im_col_mitsubishi = { + "Mitsubishi_3_colour", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { 1.1997, -0.6296, -0.2755 }, + { -1.1529, 1.7383, -0.1074 }, + { -0.047, -0.109, 0.3829 } + }, + + 95, /* Luminosity of reference white */ + .3152, .3316, /* x, y for reference white */ + 25.33, 42.57, 15.85, /* Y all red, Y all green, Y all blue */ + 255, 255, 255, /* Pixel values for ref. white */ + 1.0, 1.0, 1.0, /* Residual light o/p for black pixel */ + 1.0, 1.0, 1.0, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Display LAB of 100, 0, 0 as 255, 255, 255. + */ +static struct im_col_display im_col_relative = { + "relative", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { .660, -0.276, -.10 }, + { -.663, 1.293, .0265 }, + { .003, -.017, .0734 } + }, + + 100.0, /* Luminosity of reference white */ + .284, .273, /* x, y for reference white */ + 24.23, 69.20, 6.57, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 0.0, 0.0, 0.0, /* Residual light o/p for black pixel */ + 2.3, 2.3, 2.3, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +struct im_col_display * +im_col_displays( int n ) +{ + static struct im_col_display *displays[] = { + &im_col_screen_white, /* index 0 */ + &im_col_SPARC_white, /* index 1 */ + &im_col_D65_white, /* index 2 */ + &im_col_barco_white, /* index 3 */ + &im_col_mitsubishi, /* index 4 */ + &im_col_relative, /* index 5 */ + &ultra2, /* index 6 */ + &srgb_profile, /* index 7 */ + NULL + }; + + if( n < 0 || n > IM_NUMBER( displays ) ) + return( NULL ); + + return( displays[n] ); +} + +struct im_col_display * +im_col_display_name( const char *name ) +{ + int i; + struct im_col_display *d; + + for( i = 0; (d = im_col_displays( i )); i++ ) + if( g_ascii_strcasecmp( d->d_name, name ) == 0 ) + return( d ); + + return( NULL ); +} + +/* Have the tables been made? + */ +static int made_ucs_tables = 0; + +/* Arrays for lookup tables. + */ +static float LI[ 1001 ]; +static float CI[ 3001 ]; +static float hI[ 101 ][ 361 ]; + +/* Calculate Ch from ab, h in degrees. + */ +void +im_col_ab2Ch( float a, float b, float *C, float *h ) +{ + float in[3], out[3]; + + in[1] = a; + in[2] = b; + imb_Lab2LCh( in, out, 1 ); + *C = out[1]; + *h = out[2]; +} + +/* Calculate ab from Ch, h in degrees. + */ +void +im_col_Ch2ab( float C, float h, float *a, float *b ) +{ + float in[3], out[3]; + + in[1] = C; + in[2] = h; + imb_LCh2Lab( in, out, 1 ); + *a = out[1]; + *b = out[2]; +} + +/* Calculate Lab from XYZ. + */ +void +im_col_XYZ2Lab( float X, float Y, float Z, float *L, float *a, float *b ) +{ + float in[3], out[3]; + im_colour_temperature temp; + + in[0] = X; + in[1] = Y; + in[2] = Z; + temp.X0 = IM_D65_X0; + temp.Y0 = IM_D65_Y0; + temp.Z0 = IM_D65_Z0; + imb_XYZ2Lab( in, out, 1, &temp ); + *L = out[0]; + *a = out[1]; + *b = out[2]; +} + +/* Calculate XYZ from Lab. + */ +void +im_col_Lab2XYZ( float L, float a, float b, float *X, float *Y, float *Z ) +{ + float in[3], out[3]; + im_colour_temperature temp; + + in[0] = L; + in[1] = a; + in[2] = b; + temp.X0 = IM_D65_X0; + temp.Y0 = IM_D65_Y0; + temp.Z0 = IM_D65_Z0; + imb_Lab2XYZ( in, out, 1, &temp ); + *X = out[0]; + *Y = out[1]; + *Z = out[2]; +} + +/* Pythagorean distance between two points in colour space. Lab/XYZ/UCS etc. + */ +float +im_col_pythagoras( float L1, float a1, float b1, float L2, float a2, float b2 ) +{ + float dL = L1 - L2; + float da = a1 - a2; + float db = b1 - b2; + + return( sqrt( dL*dL + da*da + db*db ) ); +} + +/* Make look_up tables for the Yr,Yb,Yg <=> r,g,b conversions. + */ +static void +calcul_tables( struct im_col_display *d, struct im_col_tab_disp *table ) +{ + int i; + float a, ga_i, ga, c, f, yo, p; + float maxr, maxg, maxb; + + c = (d->d_B - 100.0) / 500.0; + + /**** Red ****/ + yo = d->d_Y0R; + a = d->d_YCR - yo; + ga = d->d_gammaR; + ga_i = 1.0 / ga; + p = d->d_P / 100.0; + f = d->d_Vrwr / p; + + maxr = (float) d->d_Vrwr; + table->ristep = maxr / 1500.0; + table->rstep = a / 1500.0; + + for( i = 0; i < 1501; i++ ) + table->t_Yr2r[i] = f * (pow( i * table->rstep / a, ga_i ) - c); + + for( i = 0; i < 1501; i++ ) + table->t_r2Yr[i] = yo + + a * pow( i * table->ristep / f + c, ga ); + + /**** Green ****/ + yo = d->d_Y0G; + a = d->d_YCG - yo; + ga = d->d_gammaG; + ga_i = 1.0 / ga; + p = d->d_P / 100.0; + f = d->d_Vrwg / p; + + maxg = (float)d->d_Vrwg; + table->gistep = maxg / 1500.0; + table->gstep = a / 1500.0; + + for( i = 0; i < 1501; i++ ) + table->t_Yg2g[i] = f * (pow( i * table->gstep / a, ga_i ) - c); + + for( i = 0; i < 1501; i++ ) + table->t_g2Yg[i] = yo + + a * pow( i * table->gistep / f + c, ga ); + + /**** Blue ****/ + yo = d->d_Y0B; + a = d->d_YCB - yo; + ga = d->d_gammaB; + ga_i = 1.0 / ga; + p = d->d_P / 100.0; + f = d->d_Vrwb / p; + + maxb = (float)d->d_Vrwb; + table->bistep = maxb / 1500.0; + table->bstep = a / 1500.0; + + for( i = 0; i < 1501; i++ ) + table->t_Yb2b[i] = f * (pow( i * table->bstep / a, ga_i ) - c); + + for( i = 0; i < 1501; i++ ) + table->t_b2Yb[i] = yo + + a * pow( i * table->bistep / f + c, ga ); +} + +/* Make the lookup tables for rgb. Pass an IMAGE to allocate memory from. + */ +struct im_col_tab_disp * +im_col_make_tables_RGB( IMAGE *im, struct im_col_display *d ) +{ + struct im_col_tab_disp *table; + double **temp; + int i, j; + + if( !(table = IM_NEW( im, struct im_col_tab_disp )) ) + return( NULL ); + + if( d->d_type == DISP_DUMB ) + calcul_tables( d, table ); + + if( !(temp = im_dmat_alloc( 0, 2, 0, 2 )) ) + return( NULL ); + + for( i = 0; i < 3; i++ ) + for( j = 0; j < 3; j++ ) { + table->mat_XYZ2lum[i][j] = d->d_mat[i][j]; + temp[i][j] = d->d_mat[i][j]; + } + + if( im_invmat( temp, 3 ) ) { + im_free_dmat( temp, 0, 2, 0, 2 ); + return( NULL ); + } + + for( i = 0; i < 3; i++ ) + for( j = 0; j < 3; j++ ) + table->mat_lum2XYZ[i][j] = temp[i][j]; + + im_free_dmat( temp, 0, 2, 0, 2 ); + + return( table ); +} + +/* Computes the transform: r,g,b => Yr,Yg,Yb. It finds Y values in + * lookup tables and calculates X, Y, Z. + */ +int +im_col_rgb2XYZ( struct im_col_display *d, struct im_col_tab_disp *table, + int r, int g, int b, float *X, float *Y, float *Z ) +{ + float Yr, Yg, Yb; + float *mat = &table->mat_lum2XYZ[0][0]; + int i; + + if( r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 ) { + im_errormsg( "im_col_rgb2XYZ: out of range [0,255]" ); + return( -1 ); + } + + switch( d->d_type ) { + case DISP_DUMB: + /* Convert rgb to Yr, Yg, Yb. 3 times: r, g, b. + */ + i = r / table->ristep; + Yr = table->t_r2Yr[i]; + + i = g / table->gistep; + Yg = table->t_g2Yg[i]; + + i = b / table->bistep; + Yb = table->t_b2Yb[i]; + + break; + + case DISP_BARCO: + Yr = d->d_Y0R + r*(d->d_YCR-d->d_Y0R)/255.0; + Yg = d->d_Y0G + g*(d->d_YCG-d->d_Y0G)/255.0; + Yb = d->d_Y0B + b*(d->d_YCB-d->d_Y0B)/255.0; + break; + + default: + im_errormsg( "im_col_rgb2XYZ: bad display type" ); + return( -1 ); + } + + /* Multiply through the inverse matrix to get XYZ values. + */ + *X = mat[0] * Yr + mat[1] * Yg + mat[2] * Yb; + *Y = mat[3] * Yr + mat[4] * Yg + mat[5] * Yb; + *Z = mat[6] * Yr + mat[7] * Yg + mat[8] * Yb; + + return( 0 ); +} + +/* Turn XYZ into display colour. Return or=1 for out of gamut - rgb will + * contain an approximation of the right colour. + */ +int +im_col_XYZ2rgb( struct im_col_display *d, struct im_col_tab_disp *table, + float X, float Y, float Z, + int *r_ret, int *g_ret, int *b_ret, + int *or_ret ) +{ + float *mat = &table->mat_XYZ2lum[0][0]; + int or = 0; /* Out of range flag */ + + float Yr, Yg, Yb; + int Yint; + int r, g, b; + + /* Multiply through the matrix to get luminosity values. + */ + Yr = mat[0] * X + mat[1] * Y + mat[2] * Z; + Yg = mat[3] * X + mat[4] * Y + mat[5] * Z; + Yb = mat[6] * X + mat[7] * Y + mat[8] * Z; + + /* Any negatives? If yes, set the out-of-range flag and bump up. + */ + if( Yr < d->d_Y0R ) { + or = 1; + Yr = d->d_Y0R; + } + if( Yg < d->d_Y0G ) { + or = 1; + Yg = d->d_Y0G; + } + if( Yb < d->d_Y0B ) { + or = 1; + Yb = d->d_Y0B; + } + + /* Work out colour value (0-Vrw) to feed the tube to get that + * luminosity. Easy for BARCOs, harder for others. + */ + switch( d->d_type ) { + case DISP_DUMB: + Yint = (Yr - d->d_Y0R) / table->rstep; + if( Yint > 1500 ) { + or = 1; + Yint = 1500; + } + r = IM_RINT( table->t_Yr2r[Yint] ); + + Yint = (Yg - d->d_Y0G) / table->gstep; + if( Yint > 1500 ) { + or = 1; + Yint = 1500; + } + g = IM_RINT( table->t_Yg2g[Yint] ); + + Yint = (Yb - d->d_Y0B) / table->bstep; + if( Yint > 1500 ) { + or = 1; + Yint = 1500; + } + b = IM_RINT( table->t_Yb2b[Yint] ); + + break; + + case DISP_BARCO: + r = IM_RINT( ((Yr - d->d_Y0R) / (d->d_YCR - d->d_Y0R)) * 255 ); + g = IM_RINT( ((Yg - d->d_Y0G) / (d->d_YCG - d->d_Y0G)) * 255 ); + b = IM_RINT( ((Yb - d->d_Y0B) / (d->d_YCB - d->d_Y0B)) * 255 ); + + /* Any silly values? Set out of range and adjust. + */ + if( r > d->d_Vrwr ) { + or = 1; + r = d->d_Vrwr; + } + if( g > d->d_Vrwg ) { + or = 1; + g = d->d_Vrwg; + } + if( b > d->d_Vrwb ) { + or = 1; + b = d->d_Vrwb; + } + if( r < 0 ) { + or = 1; + r = 0; + } + if( g < 0 ) { + or = 1; + g = 0; + } + if( b < 0 ) { + or = 1; + b = 0; + } + + break; + + default: + im_errormsg("XYZ2rgb: display unknown"); + return( -1 ); + /*NOTREACHED*/ + } + + *r_ret = r; + *g_ret = g; + *b_ret = b; + + *or_ret = or; + + return( 0 ); +} + +/* Functions to convert from Lab to uniform colour space and back. + */ + +/* Constants for Lucs. + */ +#define c1 21.75 +#define c2 0.3838 +#define c3 38.54 + +/* Calculate Lucs from L. + */ +float +im_col_L2Lucs( float L ) +{ + float Lucs; + + if( L >= 16.0 ) + Lucs = (c1 * log( L ) + c2 * L - c3); + else + Lucs = 1.744 * L; + + return( Lucs ); +} + +/* Generate Ll and LI (inverse) tables. Don't call the above for speed. + */ +static void +make_LI( void ) +{ + int i, j=0; + float L, Ll[ 1001 ]; + + for( i = 0; i < 1001; i++ ) + { + L = i / 10.0; + if( L >= 16.0 ) + Ll[ i ] = (c1 * log( L ) + c2 * L - c3); + else + Ll[ i ] = 1.744 * L; + } + + for( i = 0; i < 1001; i++ ) + { + while ( (Ll[j]<=i/10.0) && ( j<1001) ) j++; + LI[i] = (j-1)/10.0 + (i/10.0-Ll[j-1]) / ((Ll[j]-Ll[j-1])*10.0); + } +} + + +/* Inverse of above using table. + */ +float +im_col_Lucs2L( float Lucs ) +{ + int known; /* nearest input value in the table, <= Lucs */ + + known = floor(Lucs*10.0); + if( known < 0 ) + known = 0; + if( known > 1000 ) + known = 1000; + + return( LI[known] + (LI[known+1]-LI[known])*(Lucs*10.0-known) ); +} + +/* Constants for Cucs. + */ +#define c4 0.162 +#define c5 10.92 +#define c6 0.638 +#define c7 0.07216 +#define c8 4.907 + +/* Calculate Cucs from C. + */ +float +im_col_C2Cucs( float C ) +{ + float Cucs; + + Cucs = (c4 * C + c5 * (log( c6 + c7 * C )) + c8); + if ( Cucs<0 ) Cucs = 0; + + return( Cucs ); +} + +/* Generate Cucs table. Again, inline the code above. + */ +static void +make_CI( void ) +{ + int i, j=0; + float C, Cl[ 3001]; + + for( i = 0; i < 3001; i++ ) { + C = i / 10.0; + Cl[ i ] = (c4 * C + c5 * (log( c6 + c7 * C )) + c8); + } + + for( i = 0; i < 3001; i++ ) + { + while ( (Cl[j]<=i/10.0) && ( j<3001) ) j++; + CI[i] = (j-1)/10.0 + (i/10.0-Cl[j-1]) / ((Cl[j]-Cl[j-1])*10.0); + } + +} + +/* Inverse of above using table. + */ +float +im_col_Cucs2C( float Cucs ) +{ + int known; /* nearest input value in the table, <= Cucs */ + + known = floor(Cucs*10.0); + if( known < 0 ) + known = 0; + if( known > 3000 ) + known = 3000; + + return( CI[known] + (CI[known+1]-CI[known])*(Cucs*10.0-known) ); +} + +/* Calculate hucs from h and C. + */ +float +im_col_Ch2hucs( float C, float h ) +{ + float P, D, f, g; + float k4, k5, k6, k7, k8; + float hucs; + + if( h < 49.1 ) { + k4 = 133.87; + k5 = -134.5; + k6 = -.924; + k7 = 1.727; + k8 = 340.0; + } + else if( h < 110.1 ) { + k4 = 11.78; + k5 = -12.7; + k6 = -.218; + k7 = 2.12; + k8 = 333.0; + } + else if( h < 269.6 ) { + k4 = 13.87; + k5 = 10.93; + k6 = 0.14; + k7 = 1.0; + k8 = -83.0; + } + else { + k4 = .14; + k5 = 5.23; + k6 = .17; + k7 = 1.61; + k8 = 233.0; + } + + P = cos( IM_RAD( k8 + k7 * h ) ); + D = k4 + k5 * P * pow( fabs( P ), k6 ); + g = C * C * C * C; + f = sqrt( g / (g + 1900.0) ); + hucs = h + D * f; + + return( hucs ); +} + +/* The difficult one: hucs. Again, inline. + */ +static void +make_hI( void ) +{ + int i, j, k; + float P, D, C, f, hl[101][361]; + float k4, k5, k6, k7, k8; + + for( i = 0; i < 361; i++ ) { + if( i < 49.1 ) { + k4 = 133.87; + k5 = -134.5; + k6 = -.924; + k7 = 1.727; + k8 = 340.0; + } + else if( i < 110.1 ) { + k4 = 11.78; + k5 = -12.7; + k6 = -.218; + k7 = 2.12; + k8 = 333.0; + } + else if( i < 269.6 ) { + k4 = 13.87; + k5 = 10.93; + k6 = 0.14; + k7 = 1.0; + k8 = -83.0; + } + else { + k4 = .14; + k5 = 5.23; + k6 = .17; + k7 = 1.61; + k8 = 233.0; + } + + P = cos( IM_RAD( k8 + k7 * i ) ); + D = k4 + k5 * P * pow( fabs( P ), k6 ); + + for( j = 0; j < 101; j++ ) { + float g; + + C = j * 2.0; + g = C * C * C * C; + f = sqrt( g / (g + 1900.0) ); + + hl[j][i] = i + D * f; + } + + } + + for( j = 0; j < 101; j++ ) { + k = 0; + for( i = 0; i < 361; i++ ) { + while( k < 361 && hl[j][k] <= i ) + k++; + hI[j][i] = k - 1 + (i - hl[j][k - 1]) / + (hl[j][k] - hl[j][k - 1]); + } + } +} + +/* Inverse of above using table. + */ +float +im_col_Chucs2h( float C, float hucs ) +{ + int r, known; /* nearest input value in the table, <= hucs */ + + /* Which row of the table? + */ + r = (int) ((C + 1.0) / 2.0); + if( r < 0 ) + r = 0; + if( r > 100 ) + r = 100; + + known = floor(hucs); + if( known < 0 ) + known = 0; + if( known > 360 ) + known = 360; + + return( hI[r][known] + (hI[r][known+1]-hI[r][known])*(hucs-known) ); +} + +/* Make the lookup tables for ucs. + */ +void +im_col_make_tables_UCS( void ) +{ + if( !made_ucs_tables ) { + make_LI(); + make_CI(); + make_hI(); + made_ucs_tables = -1; + } +} + +/* CMC colour difference using the above. + */ +float +im_col_dECMC( float L1, float a1, float b1, + float L2, float a2, float b2 ) +{ + float h1, C1; + float h2, C2; + float Lucs1, Cucs1, hucs1; + float Lucs2, Cucs2, hucs2; + float aucs1, bucs1; + float aucs2, bucs2; + + /* Turn to LCh. + */ + im_col_ab2Ch( a1, b1, &C1, &h1 ); + im_col_ab2Ch( a2, b2, &C2, &h2 ); + + /* Turn to LCh in CMC space. + */ + Lucs1 = im_col_L2Lucs( L1 ); + Cucs1 = im_col_C2Cucs( C1 ); + hucs1 = im_col_Ch2hucs( C1, h1 ); + + Lucs2 = im_col_L2Lucs( L2 ); + Cucs2 = im_col_C2Cucs( C2 ); + hucs2 = im_col_Ch2hucs( C2, h2 ); + + /* Turn to Lab in CMC space. + */ + im_col_Ch2ab( Cucs1, hucs1, &aucs1, &bucs1 ); + im_col_Ch2ab( Cucs2, hucs2, &aucs2, &bucs2 ); + + /* Find difference. + */ + return( im_col_pythagoras( Lucs1, aucs1, bucs1, Lucs2, aucs2, bucs2 ) ); +} + +/* Find h in degrees from a/b. + */ +double +im_col_ab2h( double a, double b ) +{ + double h; + + /* We have to be careful we have the right quadrant! + */ + if( a == 0 ) { + if( b < 0.0 ) + h = 270; + else if( b == 0.0 ) + h = 0; + else + h = 90; + } + else { + double t = atan( b / a ); + + if( a > 0.0 ) + if( b < 0.0 ) + h = IM_DEG( t + IM_PI * 2.0 ); + else + h = IM_DEG( t ); + else + h = IM_DEG( t + IM_PI ); + } + + return( h ); +} + +/* CIEDE2000 ... from + +Luo, Cui, Rigg, "The Development of the CIE 2000 Colour-Difference +Formula: CIEDE2000", COLOR research and application, pp 340 + + */ +float +im_col_dE00( float L1, float a1, float b1, + float L2, float a2, float b2 ) +{ +/* Code if you want XYZ params and the colour temp used in the reference + + float + im_col_dE00( float X1, float Y1, float Z1, + float X2, float Y2, float Z2 ) + { + const double X0 = 94.811; + const double Y0 = 100.0; + const double Z0 = 107.304; + +#define f(I) ((I) > 0.008856 ? \ + cbrt( (I), 1.0 / 3.0 ) : 7.7871 * (I) + (16.0 / 116.0)) + + double nX1 = f( X1 / X0 ); + double nY1 = f( Y1 / Y0 ); + double nZ1 = f( Z1 / Z0 ); + + double L1 = 116 * nY1 - 16; + double a1 = 500 * (nX1 - nY1); + double b1 = 200 * (nY1 - nZ1); + + double nX2 = f( X2 / X0 ); + double nY2 = f( Y2 / Y0 ); + double nZ2 = f( Z2 / Z0 ); + + double L2 = 116 * nY2 - 16; + double a2 = 500 * (nX2 - nY2); + double b2 = 200 * (nY2 - nZ2); + */ + + /* Chroma and mean chroma (C bar) + */ + double C1 = sqrt( a1 * a1 + b1 * b1 ); + double C2 = sqrt( a2 * a2 + b2 * b2 ); + double Cb = (C1 + C2) / 2; + + /* G + */ + double Cb7 = Cb * Cb * Cb * Cb * Cb * Cb * Cb; + double G = 0.5 * (1 - sqrt( Cb7 / (Cb7 + pow( 25, 7 )) )); + + /* L', a', b', C', h' + */ + double L1d = L1; + double a1d = (1 + G) * a1; + double b1d = b1; + double C1d = sqrt( a1d * a1d + b1d * b1d ); + double h1d = im_col_ab2h( a1d, b1d ); + + double L2d = L2; + double a2d = (1 + G) * a2; + double b2d = b2; + double C2d = sqrt( a2d * a2d + b2d * b2d ); + double h2d = im_col_ab2h( a2d, b2d ); + + /* L' bar, C' bar, h' bar + */ + double Ldb = (L1d + L2d) / 2; + double Cdb = (C1d + C2d) / 2; + double hdb = fabs( h1d - h2d ) < 180 ? + (h1d + h2d) / 2 : + fabs( h1d + h2d - 360 ) / 2; + + /* dtheta, RC + */ + double hdbd = (hdb - 275) / 25; + double dtheta = 30 * exp( -(hdbd * hdbd) ); + double Cdb7 = Cdb * Cdb * Cdb * Cdb * Cdb * Cdb * Cdb; + double RC = 2 * sqrt( Cdb7 / (Cdb7 + pow( 25, 7 )) ); + + /* RT, T. + */ + double RT = -sin( IM_RAD( 2 * dtheta ) ) * RC; + double T = 1 - + 0.17 * cos( IM_RAD( hdb - 30 ) ) + + 0.24 * cos( IM_RAD( 2 * hdb ) ) + + 0.32 * cos( IM_RAD( 3 * hdb + 6 ) ) - + 0.20 * cos( IM_RAD( 4 * hdb - 63 ) ); + + /* SL, SC, SH + */ + double Ldb50 = Ldb - 50; + double SL = 1 + (0.015 * Ldb50 * Ldb50) / sqrt( 20 + Ldb50 * Ldb50); + double SC = 1 + 0.045 * Cdb; + double SH = 1 + 0.015 * Cdb * T; + + /* hue difference ... careful! + */ + double dhd = fabs( h1d - h2d ) < 180 ? + h1d - h2d : + 360 - (h1d - h2d); + + /* dLd, dCd dHd + */ + double dLd = L1d - L2d; + double dCd = C1d - C2d; + double dHd = 2 * sqrt( C1d * C2d ) * sin( IM_RAD( dhd / 2 ) ); + + /* Parametric factors for viewing parameters. + */ + const double kL = 1.0; + const double kC = 1.0; + const double kH = 1.0; + + /* Normalised terms. + */ + double nL = dLd / (kL * SL); + double nC = dCd / (kC * SC); + double nH = dHd / (kH * SH); + + /* dE00!! + */ + double dE00 = sqrt( nL * nL + nC * nC + nH * nH + RT * nC * nH ); + + /* + printf( "X1 = %g, Y1 = %g, Z1 = %g\n", X1, Y1, Z1 ); + printf( "X2 = %g, Y2 = %g, Z2 = %g\n", X2, Y2, Z2 ); + printf( "L1 = %g, a1 = %g, b1 = %g\n", L1, a1, b1 ); + printf( "L2 = %g, a2 = %g, b2 = %g\n", L2, a2, b2 ); + printf( "L1d = %g, a1d = %g, b1d = %g, C1d = %g, h1d = %g\n", + L1d, a1d, b1d, C1d, h1d ); + printf( "L2d = %g, a2d = %g, b2d = %g, C2d = %g, h2d = %g\n", + L2d, a2d, b2d, C2d, h2d ); + printf( "G = %g, T = %g, SL = %g, SC = %g, SH = %g, RT = %g\n", + G, T, SL, SC, SH, RT ); + printf( "dE00 = %g\n", dE00 ); + */ + + return( dE00 ); +} + diff --git a/libsrc/colour/colour_dispatch.c b/libsrc/colour/colour_dispatch.c new file mode 100644 index 00000000..032993de --- /dev/null +++ b/libsrc/colour/colour_dispatch.c @@ -0,0 +1,1012 @@ +/* Function dispatch tables for arithmetic. + * + * J. Cupitt, 8/4/93. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* One image in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Two images in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_sRGB2XYZ via arg vector. + */ +static int +sRGB2XYZ_vec( im_object *argv ) +{ + return( im_sRGB2XYZ( argv[0], argv[1] ) ); +} + +/* Description of im_sRGB2XYZ. + */ +static im_function sRGB2XYZ_desc = { + "im_sRGB2XYZ", /* Name */ + "convert sRGB to XYZ", /* Description */ + IM_FN_PIO, /* Flags */ + sRGB2XYZ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_XYZ2sRGB via arg vector. + */ +static int +XYZ2sRGB_vec( im_object *argv ) +{ + return( im_XYZ2sRGB( argv[0], argv[1] ) ); +} + +/* Description of im_XYZ2sRGB. + */ +static im_function XYZ2sRGB_desc = { + "im_XYZ2sRGB", /* Name */ + "convert XYZ to sRGB", /* Description */ + IM_FN_PIO, /* Flags */ + XYZ2sRGB_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LCh2Lab via arg vector. + */ +static int +LCh2Lab_vec( im_object *argv ) +{ + return( im_LCh2Lab( argv[0], argv[1] ) ); +} + +/* Description of im_LCh2Lab. + */ +static im_function LCh2Lab_desc = { + "im_LCh2Lab", /* Name */ + "convert LCh to Lab", /* Description */ + IM_FN_PIO, /* Flags */ + LCh2Lab_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LabQ2XYZ via arg vector. + */ +static int +LabQ2XYZ_vec( im_object *argv ) +{ + return( im_LabQ2XYZ( argv[0], argv[1] ) ); +} + +/* Description of im_LabQ2XYZ. + */ +static im_function LabQ2XYZ_desc = { + "im_LabQ2XYZ", /* Name */ + "convert LabQ to XYZ", /* Description */ + IM_FN_PIO, /* Flags */ + LabQ2XYZ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LCh2UCS via arg vector. + */ +static int +LCh2UCS_vec( im_object *argv ) +{ + return( im_LCh2UCS( argv[0], argv[1] ) ); +} + +/* Description of im_LCh2UCS. + */ +static im_function LCh2UCS_desc = { + "im_LCh2UCS", /* Name */ + "convert LCh to UCS", /* Description */ + IM_FN_PIO, /* Flags */ + LCh2UCS_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_Lab2LCh via arg vector. + */ +static int +Lab2LCh_vec( im_object *argv ) +{ + return( im_Lab2LCh( argv[0], argv[1] ) ); +} + +/* Description of im_Lab2LCh. + */ +static im_function Lab2LCh_desc = { + "im_Lab2LCh", /* Name */ + "convert Lab to LCh", /* Description */ + IM_FN_PIO, /* Flags */ + Lab2LCh_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_Lab2LabQ() via arg vector. + */ +static int +Lab2LabQ_vec( im_object *argv ) +{ + return( im_Lab2LabQ( argv[0], argv[1] ) ); +} + +/* Description of im_Lab2LabQ. + */ +static im_function Lab2LabQ_desc = { + "im_Lab2LabQ", /* Name */ + "convert Lab to LabQ", /* Description */ + IM_FN_PIO, /* Flags */ + Lab2LabQ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_Lab2XYZ() via arg vector. + */ +static int +Lab2XYZ_vec( im_object *argv ) +{ + return( im_Lab2XYZ( argv[0], argv[1] ) ); +} + +/* Description of im_Lab2XYZ. + */ +static im_function Lab2XYZ_desc = { + "im_Lab2XYZ", /* Name */ + "convert D65 Lab to XYZ", /* Description */ + IM_FN_PIO, /* Flags */ + Lab2XYZ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +static int +icc_present_vec( im_object *argv ) +{ + int *present = ((int *) argv[0]); + + *present = im_icc_present(); + + return( 0 ); +} + +static im_arg_desc icc_present_args[] = { + IM_OUTPUT_INT( "present" ) +}; + +/* Description of im_icc_present. + */ +static im_function icc_present_desc = { + "im_icc_present", /* Name */ + "test for presence of ICC library", /* Description */ + 0, /* Flags */ + icc_present_vec, /* Dispatch function */ + IM_NUMBER( icc_present_args ), /* Size of arg list */ + icc_present_args /* Arg list */ +}; + +static int +icc_transform_vec( im_object *argv ) +{ + int intent = *((int *) argv[4]); + + return( im_icc_transform( argv[0], argv[1], + argv[2], argv[3], intent ) ); +} + +static im_arg_desc icc_transform_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "input_profile" ), + IM_INPUT_STRING( "output_profile" ), + IM_INPUT_INT( "intent" ) +}; + +/* Description of im_icc_transform. + */ +static im_function icc_transform_desc = { + "im_icc_transform", /* Name */ + "convert between two device images with a pair of ICC profiles", + /* Description */ + IM_FN_PIO, /* Flags */ + icc_transform_vec, /* Dispatch function */ + IM_NUMBER( icc_transform_args ), /* Size of arg list */ + icc_transform_args /* Arg list */ +}; + +static int +icc_import_embedded_vec( im_object *argv ) +{ + int intent = *((int *) argv[2]); + + return( im_icc_import_embedded( argv[0], argv[1], intent ) ); +} + +static im_arg_desc icc_import_embedded_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "intent" ) +}; + +/* Description of im_icc_import_embedded. + */ +static im_function icc_import_embedded_desc = { + "im_icc_import_embedded", /* Name */ + "convert a device image to float LAB using the embedded profile", + /* Description */ + IM_FN_PIO, /* Flags */ + icc_import_embedded_vec, /* Dispatch function */ + IM_NUMBER( icc_import_embedded_args ), /* Size of arg list */ + icc_import_embedded_args /* Arg list */ +}; + +static int +icc_import_vec( im_object *argv ) +{ + int intent = *((int *) argv[3]); + + return( im_icc_import( argv[0], argv[1], + argv[2], intent ) ); +} + +static im_arg_desc icc_import_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "input_profile" ), + IM_INPUT_INT( "intent" ) +}; + +/* Description of im_icc_import. + */ +static im_function icc_import_desc = { + "im_icc_import", /* Name */ + "convert a device image to float LAB with an ICC profile", + /* Description */ + IM_FN_PIO, /* Flags */ + icc_import_vec, /* Dispatch function */ + IM_NUMBER( icc_import_args ), /* Size of arg list */ + icc_import_args /* Arg list */ +}; + +static int +icc_export_depth_vec( im_object *argv ) +{ + int intent = *((int *) argv[4]); + int depth = *((int *) argv[2]); + + return( im_icc_export_depth( argv[0], argv[1], + depth, argv[3], intent ) ); +} + +static im_arg_desc icc_export_depth_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "depth" ), + IM_INPUT_STRING( "output_profile" ), + IM_INPUT_INT( "intent" ) +}; + +/* Description of im_icc_export_depth. + */ +static im_function icc_export_depth_desc = { + "im_icc_export_depth", /* Name */ + "convert a float LAB to device space with an ICC profile", + /* Description */ + IM_FN_PIO, /* Flags */ + icc_export_depth_vec, /* Dispatch function */ + IM_NUMBER( icc_export_depth_args ), /* Size of arg list */ + icc_export_depth_args /* Arg list */ +}; + +static int +icc_export_vec( im_object *argv ) +{ + int intent = *((int *) argv[3]); + + return( im_icc_export( argv[0], argv[1], + argv[2], intent ) ); +} + +static im_arg_desc icc_export_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "output_profile" ), + IM_INPUT_INT( "intent" ) +}; + +/* Description of im_icc_export. + */ +static im_function icc_export_desc = { + "im_icc_export", /* Name */ + "convert a float LAB to an 8-bit device image with an ICC profile", + /* Description */ + IM_FN_PIO, /* Flags */ + icc_export_vec, /* Dispatch function */ + IM_NUMBER( icc_export_args ), /* Size of arg list */ + icc_export_args /* Arg list */ +}; + +static int +icc_ac2rc_vec( im_object *argv ) +{ + return( im_icc_ac2rc( argv[0], argv[1], argv[2] ) ); +} + +static im_arg_desc icc_ac2rc_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "profile" ) +}; + +/* Description of im_icc_ac2rc. + */ +static im_function icc_ac2rc_desc = { + "im_icc_ac2rc", /* Name */ + "convert LAB from AC to RC using an ICC profile", + /* Description */ + IM_FN_PIO, /* Flags */ + icc_ac2rc_vec, /* Dispatch function */ + IM_NUMBER( icc_ac2rc_args ), /* Size of arg list */ + icc_ac2rc_args /* Arg list */ +}; + +static int +Lab2XYZ_temp_vec( im_object *argv ) +{ + double X0 = *((double *) argv[2]); + double Y0 = *((double *) argv[3]); + double Z0 = *((double *) argv[4]); + + return( im_Lab2XYZ_temp( argv[0], argv[1], X0, Y0, Z0 ) ); +} + +static im_arg_desc temp_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "X0" ), + IM_INPUT_DOUBLE( "Y0" ), + IM_INPUT_DOUBLE( "Z0" ) +}; + +/* Description of im_Lab2XYZ_temp. + */ +static im_function Lab2XYZ_temp_desc = { + "im_Lab2XYZ_temp", /* Name */ + "convert Lab to XYZ, with a specified colour temperature", + /* Description */ + IM_FN_PIO, /* Flags */ + Lab2XYZ_temp_vec, /* Dispatch function */ + IM_NUMBER( temp_args ), /* Size of arg list */ + temp_args /* Arg list */ +}; + +/* Call im_Lab2UCS() via arg vector. + */ +static int +Lab2UCS_vec( im_object *argv ) +{ + return( im_Lab2UCS( argv[0], argv[1] ) ); +} + +/* Description of im_Lab2UCS. + */ +static im_function Lab2UCS_desc = { + "im_Lab2UCS", /* Name */ + "convert Lab to UCS", /* Description */ + IM_FN_PIO, /* Flags */ + Lab2UCS_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LabQ2Lab() via arg vector. + */ +static int +LabQ2Lab_vec( im_object *argv ) +{ + return( im_LabQ2Lab( argv[0], argv[1] ) ); +} + +/* Description of im_LabQ2Lab. + */ +static im_function LabQ2Lab_desc = { + "im_LabQ2Lab", /* Name */ + "convert LabQ to Lab", /* Description */ + IM_FN_PIO, /* Flags */ + LabQ2Lab_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LabQ2LabS() via arg vector. + */ +static int +LabQ2LabS_vec( im_object *argv ) +{ + return( im_LabQ2LabS( argv[0], argv[1] ) ); +} + +/* Description of im_LabQ2LabS. + */ +static im_function LabQ2LabS_desc = { + "im_LabQ2LabS", /* Name */ + "convert LabQ to LabS", /* Description */ + IM_FN_PIO, /* Flags */ + LabQ2LabS_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_Lab2LabS() via arg vector. + */ +static int +Lab2LabS_vec( im_object *argv ) +{ + return( im_Lab2LabS( argv[0], argv[1] ) ); +} + +/* Description of im_Lab2LabS. + */ +static im_function Lab2LabS_desc = { + "im_Lab2LabS", /* Name */ + "convert Lab to LabS", /* Description */ + IM_FN_PIO, /* Flags */ + Lab2LabS_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LabS2Lab() via arg vector. + */ +static int +LabS2Lab_vec( im_object *argv ) +{ + return( im_LabS2Lab( argv[0], argv[1] ) ); +} + +/* Description of im_LabS2Lab. + */ +static im_function LabS2Lab_desc = { + "im_LabS2Lab", /* Name */ + "convert LabS to Lab", /* Description */ + IM_FN_PIO, /* Flags */ + LabS2Lab_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LabS2LabQ() via arg vector. + */ +static int +LabS2LabQ_vec( im_object *argv ) +{ + return( im_LabS2LabQ( argv[0], argv[1] ) ); +} + +/* Description of im_LabS2LabQ. + */ +static im_function LabS2LabQ_desc = { + "im_LabS2LabQ", /* Name */ + "convert LabS to LabQ", /* Description */ + IM_FN_PIO, /* Flags */ + LabS2LabQ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_UCS2XYZ() via arg vector. + */ +static int +UCS2XYZ_vec( im_object *argv ) +{ + return( im_UCS2XYZ( argv[0], argv[1] ) ); +} + +/* Description of im_UCS2XYZ. + */ +static im_function UCS2XYZ_desc = { + "im_UCS2XYZ", /* Name */ + "convert UCS to XYZ", /* Description */ + IM_FN_PIO, /* Flags */ + UCS2XYZ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_UCS2LCh() via arg vector. + */ +static int +UCS2LCh_vec( im_object *argv ) +{ + return( im_UCS2LCh( argv[0], argv[1] ) ); +} + +/* Description of im_UCS2LCh. + */ +static im_function UCS2LCh_desc = { + "im_UCS2LCh", /* Name */ + "convert UCS to LCh", /* Description */ + IM_FN_PIO, /* Flags */ + UCS2LCh_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_UCS2Lab() via arg vector. + */ +static int +UCS2Lab_vec( im_object *argv ) +{ + return( im_UCS2Lab( argv[0], argv[1] ) ); +} + +/* Description of im_UCS2Lab. + */ +static im_function UCS2Lab_desc = { + "im_UCS2Lab", /* Name */ + "convert UCS to Lab", /* Description */ + IM_FN_PIO, /* Flags */ + UCS2Lab_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_Yxy2XYZ via arg vector. + */ +static int +Yxy2XYZ_vec( im_object *argv ) +{ + return( im_Yxy2XYZ( argv[0], argv[1] ) ); +} + +/* Description of im_Yxy2XYZ. + */ +static im_function Yxy2XYZ_desc = { + "im_Yxy2XYZ", /* Name */ + "convert Yxy to XYZ", /* Description */ + IM_FN_PIO, /* Flags */ + Yxy2XYZ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_XYZ2Yxy via arg vector. + */ +static int +XYZ2Yxy_vec( im_object *argv ) +{ + return( im_XYZ2Yxy( argv[0], argv[1] ) ); +} + +/* Description of im_XYZ2Yxy. + */ +static im_function XYZ2Yxy_desc = { + "im_XYZ2Yxy", /* Name */ + "convert XYZ to Yxy", /* Description */ + IM_FN_PIO, /* Flags */ + XYZ2Yxy_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_XYZ2Lab via arg vector. + */ +static int +XYZ2Lab_vec( im_object *argv ) +{ + return( im_XYZ2Lab( argv[0], argv[1] ) ); +} + +/* Description of im_XYZ2Lab. + */ +static im_function XYZ2Lab_desc = { + "im_XYZ2Lab", /* Name */ + "convert D65 XYZ to Lab", /* Description */ + IM_FN_PIO, /* Flags */ + XYZ2Lab_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +static int +XYZ2Lab_temp_vec( im_object *argv ) +{ + double X0 = *((double *) argv[2]); + double Y0 = *((double *) argv[3]); + double Z0 = *((double *) argv[4]); + + return( im_XYZ2Lab_temp( argv[0], argv[1], X0, Y0, Z0 ) ); +} + +/* Description of im_XYZ2Lab_temp. + */ +static im_function XYZ2Lab_temp_desc = { + "im_XYZ2Lab_temp", /* Name */ + "convert XYZ to Lab, with a specified colour temperature", + /* Description */ + IM_FN_PIO, /* Flags */ + XYZ2Lab_temp_vec, /* Dispatch function */ + IM_NUMBER( temp_args ), /* Size of arg list */ + temp_args /* Arg list */ +}; + +/* Call im_XYZ2UCS() via arg vector. + */ +static int +XYZ2UCS_vec( im_object *argv ) +{ + return( im_XYZ2UCS( argv[0], argv[1] ) ); +} + +/* Description of im_XYZ2UCS. + */ +static im_function XYZ2UCS_desc = { + "im_XYZ2UCS", /* Name */ + "convert XYZ to UCS", /* Description */ + IM_FN_PIO, /* Flags */ + XYZ2UCS_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args to XYZ2disp and disp2XYZ. + */ +static im_arg_desc XYZ2disp_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DISPLAY( "disp" ) +}; + +/* Call im_XYZ2disp() via arg vector. + */ +static int +XYZ2disp_vec( im_object *argv ) +{ + return( im_XYZ2disp( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_XYZ2disp. + */ +static im_function XYZ2disp_desc = { + "im_XYZ2disp", /* Name */ + "convert XYZ to displayble", /* Description */ + IM_FN_PIO, /* Flags */ + XYZ2disp_vec, /* Dispatch function */ + IM_NUMBER( XYZ2disp_args ), /* Size of arg list */ + XYZ2disp_args /* Arg list */ +}; + +/* Call im_Lab2disp() via arg vector. + */ +static int +Lab2disp_vec( im_object *argv ) +{ + return( im_Lab2disp( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_Lab2disp. + */ +static im_function Lab2disp_desc = { + "im_Lab2disp", /* Name */ + "convert Lab to displayable", /* Description */ + IM_FN_PIO, /* Flags */ + Lab2disp_vec, /* Dispatch function */ + IM_NUMBER( XYZ2disp_args ), /* Size of arg list */ + XYZ2disp_args /* Arg list */ +}; + +/* Call im_LabQ2disp() via arg vector. + */ +static int +LabQ2disp_vec( im_object *argv ) +{ + return( im_LabQ2disp( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_LabQ2disp. + */ +static im_function LabQ2disp_desc = { + "im_LabQ2disp", /* Name */ + "convert LabQ to displayable", /* Description */ + IM_FN_PIO, /* Flags */ + LabQ2disp_vec, /* Dispatch function */ + IM_NUMBER( XYZ2disp_args ), /* Size of arg list */ + XYZ2disp_args /* Arg list */ +}; + +/* Call im_dE00_fromLab() via arg vector. + */ +static int +dE00_fromLab_vec( im_object *argv ) +{ + return( im_dE00_fromLab( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_dE00_fromLab. + */ +static im_function dE00_fromLab_desc = { + "im_dE00_fromLab", /* Name */ + "calculate delta-E CIE2000 for two Lab images", + IM_FN_PIO, /* Flags */ + dE00_fromLab_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_dECMC_fromLab() via arg vector. + */ +static int +dECMC_fromLab_vec( im_object *argv ) +{ + return( im_dECMC_fromLab( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_dECMC_fromLab. + */ +static im_function dECMC_fromLab_desc = { + "im_dECMC_fromLab", /* Name */ + "calculate delta-E CMC(1:1) for two Lab images", + IM_FN_PIO, /* Flags */ + dECMC_fromLab_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_dE_fromXYZ() via arg vector. + */ +static int +dE_fromXYZ_vec( im_object *argv ) +{ + return( im_dE_fromXYZ( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_dE_fromXYZ. + */ +static im_function dE_fromXYZ_desc = { + "im_dE_fromXYZ", /* Name */ + "calculate delta-E for two XYZ images", + IM_FN_PIO, /* Flags */ + dE_fromXYZ_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_dE_fromLab() via arg vector. + */ +static int +dE_fromLab_vec( im_object *argv ) +{ + return( im_dE_fromLab( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_dE_fromLab. + */ +static im_function dE_fromLab_desc = { + "im_dE_fromLab", /* Name */ + "calculate delta-E for two Lab images", + IM_FN_PIO, /* Flags */ + dE_fromLab_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Two images in, one out. + */ +static im_arg_desc dE_fromdisp_args[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DISPLAY( "disp" ) +}; + +/* Call im_dE_fromdisp() via arg vector. + */ +static int +dE_fromdisp_vec( im_object *argv ) +{ + return( im_dE_fromdisp( argv[0], argv[1], argv[2], argv[3] ) ); +} + +/* Description of im_dE_fromdisp. + */ +static im_function dE_fromdisp_desc = { + "im_dE_fromdisp", /* Name */ + "calculate delta-E for two displayable images", + IM_FN_PIO, /* Flags */ + dE_fromdisp_vec, /* Dispatch function */ + IM_NUMBER( dE_fromdisp_args ), /* Size of arg list */ + dE_fromdisp_args /* Arg list */ +}; + +/* Call im_dECMC_fromdisp() via arg vector. + */ +static int +dECMC_fromdisp_vec( im_object *argv ) +{ + return( im_dECMC_fromdisp( argv[0], argv[1], argv[2], argv[3] ) ); +} + +/* Description of im_dECMC_fromdisp. + */ +static im_function dECMC_fromdisp_desc = { + "im_dECMC_fromdisp", /* Name */ + "calculate delta-E CMC(1:1) for two displayable images", + IM_FN_PIO, /* Flags */ + dECMC_fromdisp_vec, /* Dispatch function */ + IM_NUMBER( dE_fromdisp_args ), /* Size of arg list */ + dE_fromdisp_args /* Arg list */ +}; + +/* Call im_disp2XYZ() via arg vector. + */ +static int +disp2XYZ_vec( im_object *argv ) +{ + return( im_disp2XYZ( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_disp2XYZ. + */ +static im_function disp2XYZ_desc = { + "im_disp2XYZ", /* Name */ + "convert displayable to XYZ", /* Description */ + IM_FN_PIO, /* Flags */ + disp2XYZ_vec, /* Dispatch function */ + IM_NUMBER( XYZ2disp_args ), /* Size of arg list */ + XYZ2disp_args /* Arg list */ +}; + +/* Call im_disp2Lab() via arg vector. + */ +static int +disp2Lab_vec( im_object *argv ) +{ + return( im_disp2Lab( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_disp2Lab. + */ +static im_function disp2Lab_desc = { + "im_disp2Lab", /* Name */ + "convert displayable to Lab", /* Description */ + IM_FN_PIO, /* Flags */ + disp2Lab_vec, /* Dispatch function */ + IM_NUMBER( XYZ2disp_args ), /* Size of arg list */ + XYZ2disp_args /* Arg list */ +}; + +static im_arg_desc morph_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DMASK( "greyscale" ), + IM_INPUT_DOUBLE( "L_offset" ), + IM_INPUT_DOUBLE( "L_scale" ), + IM_INPUT_DOUBLE( "a_scale" ), + IM_INPUT_DOUBLE( "b_scale" ) +}; + +static int +morph_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + double L_offset = *((double *) argv[3]); + double L_scale = *((double *) argv[4]); + double a_scale = *((double *) argv[5]); + double b_scale = *((double *) argv[6]); + + return( im_lab_morph( argv[0], argv[1], + mo->mask, L_offset, L_scale, a_scale, b_scale ) ); +} + +static im_function morph_desc = { + "im_lab_morph", /* Name */ + "morph colourspace of a LAB image", + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + morph_vec, /* Dispatch function */ + IM_NUMBER( morph_args ), /* Size of arg list */ + morph_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *colour_list[] = { + &LCh2Lab_desc, + &LCh2UCS_desc, + &Lab2LCh_desc, + &Lab2LabQ_desc, + &Lab2LabS_desc, + &Lab2UCS_desc, + &Lab2XYZ_desc, + &Lab2XYZ_temp_desc, + &Lab2disp_desc, + &LabQ2LabS_desc, + &LabQ2Lab_desc, + &LabQ2XYZ_desc, + &LabQ2disp_desc, + &LabS2LabQ_desc, + &LabS2Lab_desc, + &UCS2LCh_desc, + &UCS2Lab_desc, + &UCS2XYZ_desc, + &XYZ2Lab_desc, + &XYZ2Lab_temp_desc, + &XYZ2UCS_desc, + &XYZ2Yxy_desc, + &XYZ2disp_desc, + &XYZ2sRGB_desc, + &Yxy2XYZ_desc, + &dE00_fromLab_desc, + &dECMC_fromLab_desc, + &dECMC_fromdisp_desc, + &dE_fromLab_desc, + &dE_fromXYZ_desc, + &dE_fromdisp_desc, + &disp2Lab_desc, + &disp2XYZ_desc, + &icc_ac2rc_desc, + &icc_export_desc, + &icc_export_depth_desc, + &icc_import_desc, + &icc_import_embedded_desc, + &icc_present_desc, + &icc_transform_desc, + &morph_desc, + &sRGB2XYZ_desc +}; + +/* Package of functions. + */ +im_package im__colour = { + "colour", + IM_NUMBER( colour_list ), + colour_list +}; diff --git a/libsrc/colour/derived.c b/libsrc/colour/derived.c new file mode 100644 index 00000000..92109672 --- /dev/null +++ b/libsrc/colour/derived.c @@ -0,0 +1,214 @@ +/* Derived colour space functions. + * + * 14/9/95 JC + * - horrible error killed im_dE_fromXYZ() and im_dE_fromdisp() + * 4/3/98 JC + * - sRGB added + * 17/6/99 JC + * - minor reformatting + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* LabQ to XYZ. + */ +int +im_LabQ2XYZ( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_LabQ2XYZ:1", "p" )) || + im_LabQ2Lab( in, t1 ) || + im_Lab2XYZ( t1, out ) ) + return( -1 ); + + return( 0 ); +} + +/* Lab to UCS. + */ +int +im_Lab2UCS( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_Lab2UCS:1", "p" )) || + im_Lab2LCh( in, t1 ) || + im_LCh2UCS( t1, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_UCS2Lab( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_UCS2Lab intermediate", "p" )) || + im_UCS2LCh( in, t1 ) || + im_LCh2Lab( t1, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_UCS2XYZ( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_UCS2XYZ intermediate", "p" )) || + im_UCS2Lab( in, t1 ) || + im_Lab2XYZ( t1, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_XYZ2UCS( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_XYZ2UCS intermediate", "p" )) || + im_XYZ2Lab( in, t1 ) || + im_Lab2UCS( t1, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_Lab2disp( IMAGE *in, IMAGE *out, struct im_col_display *disp ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_Lab2disp:1", "p" )) || + im_Lab2XYZ( in, t1 ) || + im_XYZ2disp( t1, out, disp ) ) + return( -1 ); + + return( 0 ); +} + +int +im_XYZ2sRGB( IMAGE *in, IMAGE *out ) +{ + if( im_XYZ2disp( in, out, im_col_displays( 7 ) ) ) + return( -1 ); + + out->Type = IM_TYPE_sRGB; + + return( 0 ); +} + +int +im_sRGB2XYZ( IMAGE *in, IMAGE *out ) +{ + if( im_disp2XYZ( in, out, im_col_displays( 7 ) ) ) + return( -1 ); + + return( 0 ); +} + +int +im_dECMC_fromdisp( IMAGE *im1, IMAGE *im2, + IMAGE *out, struct im_col_display *d ) +{ + IMAGE *t1, *t2, *t3, *t4; + + if( !(t1 = im_open_local( out, "im_dECMC_fromdisp:1", "p" )) || + !(t2 = im_open_local( out, "im_dECMC_fromdisp:2", "p" )) || + !(t3 = im_open_local( out, "im_dECMC_fromdisp:3", "p" )) || + !(t4 = im_open_local( out, "im_dECMC_fromdisp:4", "p" )) || + im_disp2XYZ( im1, t1, d ) || + im_XYZ2Lab( t1, t2 ) || + im_disp2XYZ( im2, t3, d ) || + im_XYZ2Lab( t3, t4 ) || + im_dECMC_fromLab( t2, t4, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_dE_fromXYZ( IMAGE *im1, IMAGE *im2, IMAGE *out ) +{ + IMAGE *t1, *t2; + + if( !(t1 = im_open_local( out, "im_dE_fromXYZ:1", "p" )) || + !(t2 = im_open_local( out, "im_dE_fromXYZ:2", "p" )) || + im_XYZ2Lab( im1, t1 ) || + im_XYZ2Lab( im2, t2 ) || + im_dE_fromLab( t1, t2, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_dE_fromdisp( IMAGE *im1, IMAGE *im2, IMAGE *out, struct im_col_display *d ) +{ + IMAGE *t1, *t2; + + if( !(t1 = im_open_local( out, "im_dE_fromdisp:1", "p" )) || + !(t2 = im_open_local( out, "im_dE_fromdisp:2", "p" )) || + im_disp2XYZ( im1, t1, d ) || + im_disp2XYZ( im2, t2, d ) || + im_dE_fromXYZ( t1, t2, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_disp2Lab( IMAGE *in, IMAGE *out, struct im_col_display *d ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_disp2Lab:1", "p" )) || + im_disp2XYZ( in, t1, d ) || + im_XYZ2Lab( t1, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_LCh2Lab.c b/libsrc/colour/im_LCh2Lab.c new file mode 100644 index 00000000..4e7b1c2b --- /dev/null +++ b/libsrc/colour/im_LCh2Lab.c @@ -0,0 +1,108 @@ +/* @(#) Turn LCh to Lab. + * @(#) + * @(#) Usage: + * @(#) im_LCh2Lab( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * 15/11/94 JC + * - error messages added + * - memory leak fixed + * 16/11/94 JC + * - uses im_wrap_oneonebuf() now + * 8/2/95 JC + * - im_wrap v2 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer of data. + */ +void +imb_LCh2Lab( float *p, float *q, int n ) +{ + int x; + + for( x = 0; x < n; x++ ) { + float L = p[0]; + float C = p[1]; + float h = p[2]; + float a, b; + + p += 3; + + a = C * cos( IM_RAD( h ) ); + b = C * sin( IM_RAD( h ) ); + + q[0] = L; + q[1] = a; + q[2] = b; + q += 3; + } +} + +int +im_LCh2Lab( IMAGE *in, IMAGE *out ) +{ + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_LCh2Lab: 3-band float uncoded input only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_LAB; + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) imb_LCh2Lab, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_LCh2UCS.c b/libsrc/colour/im_LCh2UCS.c new file mode 100644 index 00000000..a826ff47 --- /dev/null +++ b/libsrc/colour/im_LCh2UCS.c @@ -0,0 +1,99 @@ +/* @(#) Turn LCh to UCS. + * @(#) + * @(#) Usage: + * @(#) im_LCh2UCS( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modified: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our main loop. + */ +void +imb_LCh2UCS( float *p, float *q, int n ) +{ + int x; + + for( x = 0; x < n; x++ ) { + float L = p[0]; + float C = p[1]; + float h = p[2]; + p += 3; + + /* Turn to UCS. + */ + q[0] = im_col_L2Lucs( L ); + q[1] = im_col_C2Cucs( C ); + q[2] = im_col_Ch2hucs( C, h ); + q += 3; + } +} + +int +im_LCh2UCS( IMAGE *in, IMAGE *out ) +{ + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_LCh2UCS: 3-band uncoded float input only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_UCS; + + /* Process! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) imb_LCh2UCS, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_Lab2LCh.c b/libsrc/colour/im_Lab2LCh.c new file mode 100644 index 00000000..8cbdf471 --- /dev/null +++ b/libsrc/colour/im_Lab2LCh.c @@ -0,0 +1,102 @@ +/* @(#) Turn Lab to LCh + * @(#) + * @(#) Usage: + * @(#) im_Lab2LCh( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our main loop. + */ +void +imb_Lab2LCh( float *p, float *q, int n ) +{ + int x; + + for( x = 0; x < n; x++ ) { + float L = p[0]; + float a = p[1]; + float b = p[2]; + float C, h; + + p += 3; + + C = sqrt( a * a + b * b ); + h = im_col_ab2h( a, b ); + + q[0] = L; + q[1] = C; + q[2] = h; + + q += 3; + } +} + +int +im_Lab2LCh( IMAGE *in, IMAGE *out ) +{ + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_Lab2LCh: 3-band uncoded float input only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_LCH; + + /* Process! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) imb_Lab2LCh, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_Lab2LabQ.c b/libsrc/colour/im_Lab2LabQ.c new file mode 100644 index 00000000..70dc4254 --- /dev/null +++ b/libsrc/colour/im_Lab2LabQ.c @@ -0,0 +1,158 @@ +/* @(#) im_Lab2LabQ: quantise FLOAT Lab image into 10 11 11 format + * 4 bytes per pel: l a b lsbs + * this is an image wrapper which calls line-wise packing + * Copyright K.Martinez 3/5/93 + * Modified: + * 7/6/93 JC + * - adapted for partial v2 + * 5/5/94 JC + * - some nint->+0.5, for speed and to ease portability + * - other nint->rint + * - now inclues ! + * 15/11/94 JC + * - all nint(), rint() removed for speed + * - now -128 rather than -127 for a, b + * - checks input type properly + * 16/11/94 JC + * - uses new im_wrap_oneonebuf() + * 22/5/95 JC + * - changed L to scale by 10.24, not 10.23 + * 11/7/95 JC + * - now uses IM_RINT() for rounding + * 4/9/97 JC + * - L* = 100.0 now allowed + * 5/11/00 JC + * - go int earlier for speed up + * 20/6/02 JC + * - oops, were not clipping a/b range correctly + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* @(#) convert float Lab to packed Lab32 format 10 11 11 bits + * works only on buffers, not IMAGEs + * Copyright 1993 K.Martinez + * Modified: 3/5/93, 16/6/93 + */ +void +imb_Lab2LabQ( float *inp, unsigned char *outbuf, int n ) +{ + float *f, fval; + int lsbs, intv; + int Xc; + unsigned char *out; + + out = outbuf; + f = inp; + for( Xc = 0; Xc < n; Xc++) { + /* Scale L up to 10 bits. Add 0.5 rather than call IM_RINT for + * speed. This will not round negatives correctly! But this + * does not matter, since L is >0. L*=100.0 -> 1023. + */ + intv = 10.23 * f[0] + 0.5; /* scale L up to 10 bits */ + if( intv > 1023 ) + intv = 1023; + if( intv < 0 ) + intv = 0; + lsbs = (intv & 0x3) << 6; /* 00000011 -> 11000000 */ + out[0] = (intv >> 2); /* drop bot 2 bits and store */ + + fval = 8.0 * f[1]; /* do a */ + intv = IM_RINT( fval ); + if( intv > 1023 ) + intv = 1023; + else if( intv < -1024 ) + intv = -1024; + + /* Break into bits. + */ + lsbs |= (intv & 0x7) << 3; /* 00000111 -> 00111000 */ + out[1] = (intv >> 3); /* drop bot 3 bits & store */ + + fval = 8.0 * f[2]; /* do b */ + intv = IM_RINT( fval ); + if( intv > 1023 ) + intv = 1023; + else if( intv < -1024 ) + intv = -1024; + + lsbs |= (intv & 0x7); + out[2] = (intv >> 3); + + out[3] = lsbs; /* store lsb band */ + + f += 3; + out += 4; + } +} + +int +im_Lab2LabQ( IMAGE *labim, IMAGE *outim ) +{ + /* Check for uncoded Lab type + */ + if( labim->Coding != IM_CODING_NONE ) { + im_errormsg( "im_Lab2LabQ: uncoded input only" ); + return( -1 ); + } + if( labim->BandFmt != IM_BANDFMT_FLOAT || labim->Bands != 3 ) { + im_errormsg( "im_Lab2LabQ: three-band float input only" ); + return( -1 ); + } + + /* Set up output image. + */ + if( im_cp_desc( outim, labim ) ) + return( -1 ); + outim->Bands = 4; + outim->Type = IM_TYPE_LAB; + outim->BandFmt = IM_BANDFMT_UCHAR; + outim->Bbits = 8; + outim->Coding = IM_CODING_LABQ; + + /* Process. + */ + if( im_wrapone( labim, outim, + (im_wrapone_fn) imb_Lab2LabQ, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_Lab2LabS.c b/libsrc/colour/im_Lab2LabS.c new file mode 100644 index 00000000..d83ba931 --- /dev/null +++ b/libsrc/colour/im_Lab2LabS.c @@ -0,0 +1,92 @@ +/* @(#) im_Lab2LabS: quantise FLOAT Lab image into signed short format + * 12/12/02 JC + * - from im_Lab2LabQ + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +void +imb_Lab2LabS( float *in, signed short *out, int n ) +{ + float *p = in; + signed short *q = out; + int c; + + for( c = 0; c < n; c++ ) { + q[0] = p[0] * (32767.0 / 100.0); + q[1] = p[1] * (32768.0 / 128.0); + q[2] = p[2] * (32768.0 / 128.0); + + q += 3; + p += 3; + } +} + +int +im_Lab2LabS( IMAGE *labim, IMAGE *outim ) +{ + /* Check for uncoded Lab type + */ + if( labim->Coding != IM_CODING_NONE ) { + im_errormsg( "im_Lab2LabS: uncoded input only" ); + return( -1 ); + } + if( labim->BandFmt != IM_BANDFMT_FLOAT || labim->Bands != 3 ) { + im_errormsg( "im_Lab2LabS: three-band float input only" ); + return( -1 ); + } + + /* Set up output image. + */ + if( im_cp_desc( outim, labim ) ) + return( -1 ); + outim->Type = IM_TYPE_LABS; + outim->BandFmt = IM_BANDFMT_SHORT; + outim->Bbits = IM_BBITS_SHORT; + + /* Process. + */ + if( im_wrapone( labim, outim, + (im_wrapone_fn) imb_Lab2LabS, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_Lab2XYZ.c b/libsrc/colour/im_Lab2XYZ.c new file mode 100644 index 00000000..ce5361fe --- /dev/null +++ b/libsrc/colour/im_Lab2XYZ.c @@ -0,0 +1,144 @@ +/* @(#) Turn Lab to XYZ colourspace. + * @(#) + * @(#) Usage: + * @(#) im_Lab2XYZ( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modified: + * 15/11/94 JC + * - ANSIfied + * - sets Type of output + * - better error messages + * 16/11/94 JC + * - partialed + * - in-line conversion + * 8/2/95 JC + * - new im_wrapone function + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer of data. + */ +void +imb_Lab2XYZ( float *p, float *q, int n, im_colour_temperature *temp ) +{ + int x; + + for( x = 0; x < n; x++ ) { + float L = p[0]; + float a = p[1]; + float b = p[2]; + float X, Y, Z; + double cby, tmp; + p += 3; + + if( L < 8.0 ) { + Y = (L * temp->Y0) / 903.3; + cby = 7.787 * (Y / temp->Y0) + 16.0 / 116.0; + } + else { + cby = (L + 16.0) / 116.0; + Y = temp->Y0 * cby * cby * cby; + } + + tmp = a / 500.0 + cby; + if( tmp < 0.2069 ) + X = temp->X0 * (tmp - 0.13793) / 7.787; + else + X = temp->X0 * tmp * tmp * tmp; + + tmp = cby - b / 200.0; + if( tmp < 0.2069 ) + Z = temp->Z0 * (tmp - 0.13793) / 7.787; + else + Z = temp->Z0 * tmp * tmp * tmp; + + /* Write. + */ + q[0] = X; + q[1] = Y; + q[2] = Z; + q += 3; + } +} + +int +im_Lab2XYZ_temp( IMAGE *in, IMAGE *out, double X0, double Y0, double Z0 ) +{ + im_colour_temperature *temp = IM_NEW( out, im_colour_temperature ); + + /* Check input image. + */ + if( !temp ) + return( -1 ); + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_Lab2XYZ: 3-band uncoded float input only" ); + return( -1 ); + } + + /* Prepare the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_XYZ; + + /* Process! + */ + temp->X0 = X0; + temp->Y0 = Y0; + temp->Z0 = Z0; + if( im_wrapone( in, out, + (im_wrapone_fn) imb_Lab2XYZ, temp, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_Lab2XYZ( IMAGE *in, IMAGE *out ) +{ + return( im_Lab2XYZ_temp( in, out, IM_D65_X0, IM_D65_Y0, IM_D65_Z0 ) ); +} diff --git a/libsrc/colour/im_LabQ2Lab.c b/libsrc/colour/im_LabQ2Lab.c new file mode 100644 index 00000000..327ec18d --- /dev/null +++ b/libsrc/colour/im_LabQ2Lab.c @@ -0,0 +1,133 @@ +/* @(#) LabQ2Lab convert Lab 32bit packed format to float Lab + @(#) uses imb_LabQ2Lab +Copyright Kirk Martinez 2/5/1993 +Modified: 16/6/93 + * 7/6/93 JC + * - adapted for partial v2 + * 16/11/94 JC + * - adapted to new im_wrap_oneonebuf() function. + * 9/2/95 JC + * - new im_wrapone function + * 22/5/95 JC + * - changed char to unsigned char for RS/6000 + * - small tidies and speed-ups + * 4/9/97 JC + * - L* = 100.0 now handled correctly + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* imb_LabQ2Lab: CONVERT n pels from packed 32bit Lab to float values + * in a buffer + * ARGS: PEL *inp pointer to first byte of Lab32 buffer + * float *outbuf destination buffer + * int n number of pels to process + * (C) K.Martinez 2/5/93 + */ +void +imb_LabQ2Lab( PEL *inp, float *outbuf, int n ) +{ + signed char *b; /* to read input bytes */ + int l; + int lsbs; /* for lsbs byte */ + int c; /* counter */ + float *out; + + /* Read input with a signed pointer to get signed ab easily. + */ + b = (signed char *) inp; + out = outbuf; + for( c = 0; c < n; c++ ) { + /* Get extra bits. + */ + lsbs = ((unsigned char *) b)[3]; + + /* Build L. + */ + l = ((unsigned char *)b)[0]; + l = (l << 2) | (lsbs >> 6); + out[0] = (float) l * (100.0 / 1023.0); + + /* Build a. + */ + l = (b[1] << 3) | ((lsbs >> 3) & 0x7); + out[1] = (float) l * 0.125; + + /* And b. + */ + l = (b[2] << 3) | (lsbs & 0x7); + out[2] = (float) l * 0.125; + + b += 4; + out += 3; + } +} + +/* unpack a Lab32 ie 10,11,11 Lab image into float image +this is a wrapper around the buffer processing function +(C) K.Martinez 2/5/93 +*/ + +int +im_LabQ2Lab( IMAGE *labim, IMAGE *outim ) +{ + /* check for coded Lab type + */ + if( labim->Coding != IM_CODING_LABQ ) { + im_errormsg( "im_LabQ2Lab: not a packed Lab image" ); + return( -1 ); + } + + /* set up output image + */ + if( im_cp_desc( outim, labim ) ) + return( -1 ); + outim->Bands = 3; + outim->Type = IM_TYPE_LAB; + outim->BandFmt = IM_BANDFMT_FLOAT; + outim->Bbits = 32; + outim->Coding = IM_CODING_NONE; + + if( im_wrapone( labim, outim, + (im_wrapone_fn) imb_LabQ2Lab, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_LabQ2LabS.c b/libsrc/colour/im_LabQ2LabS.c new file mode 100644 index 00000000..f2fe3a0d --- /dev/null +++ b/libsrc/colour/im_LabQ2LabS.c @@ -0,0 +1,125 @@ +/* @(#) im_LabQ2LabS() - convert IM_CODING_LABQ format to three band signed short. + * @(#) Justify to get msb in bit 15. + * @(#) + * @(#) int im_LabQ2LabS( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) + * + * 17/11/93 JC + * - adapted from im_LabQ2Lab() + * 16/11/94 JC + * - uses new im_wrap_oneonebuf() fn + * 9/2/95 JC + * - new im_wrapone function + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* CONVERT n pels from packed 32bit Lab to signed short. + */ +void +imb_LabQ2LabS( unsigned char *in, signed short *out, int n ) +{ + int c; + unsigned char *p = in; + unsigned char ext; + signed short *q = out; + signed short l, a, b; + + for( c = 0; c < n; c++ ) { + /* Get most significant 8 bits of lab. + */ + l = p[0] << 7; + a = p[1] << 8; + b = p[2] << 8; + + /* Get x-tra bits. + */ + ext = p[3]; + p += 4; + + /* Shift and mask in to lab. + */ + l |= (unsigned char) (ext & 0xc0) >> 1; + a |= (ext & 0x38) << 2; + b |= (ext & 0x7) << 5; + + /* Write! + */ + q[0] = l; + q[1] = a; + q[2] = b; + q += 3; + } +} + +/* unpack a Lab32 ie 10,11,11 Lab image into signed short image +this is a wrapper around the buffer processing function +(C) K.Martinez 2/5/93 +*/ + +int +im_LabQ2LabS( IMAGE *labim, IMAGE *outim ) +{ + /* check for coded Lab type + */ + if( labim->Coding != IM_CODING_LABQ ) { + im_errormsg( "im_LabQ2LabS: not a packed Lab image" ); + return( -1 ); + } + + /* set up output image + */ + if( im_cp_desc( outim, labim ) ) + return( -1 ); + outim->Bands = 3; + outim->Type = IM_TYPE_LABS; + outim->BandFmt = IM_BANDFMT_SHORT; + outim->Bbits = IM_BBITS_SHORT; + outim->Coding = IM_CODING_NONE; + + /* Produce output. + */ + if( im_wrapone( labim, outim, + (im_wrapone_fn) imb_LabQ2LabS, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_LabQ2disp.c b/libsrc/colour/im_LabQ2disp.c new file mode 100644 index 00000000..88fac6ec --- /dev/null +++ b/libsrc/colour/im_LabQ2disp.c @@ -0,0 +1,209 @@ +/* @(#) Turn Lab 32bit packed format into displayable rgb. Fast, but very + * @(#) inaccurate: for display only! + * @(#) + * @(#) Usage: + * @(#) int im_LabQ2disp( IMAGE *in, IMAGE *out, struct im_col_display *d ) + * @(#) + * @(#) Returns: -1 on error, else 0 + * + * 5/11/97 Steve Perry + * - adapted from old ip code + * 7/11/97 JC + * - small tidies, added to VIPS + * - LUT build split into separate function + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Hold a display characterisation, and a set of tables + * computed from that. + */ +typedef struct { + struct im_col_display *disp; + struct im_col_tab_disp *dtab; + PEL red[ 64 * 64 * 64 ]; + PEL green[ 64 * 64 * 64 ]; + PEL blue[ 64 * 64 * 64 ]; +} CalibrateInfo; + +/* Do our own indexing of the arrays, to make sure we get efficient mults. + */ +#define index( L, A, B ) (L + (A << 6) + (B << 12)) + +/* Process a buffer of data. + */ +static void +imb_LabQ2disp( PEL *p, PEL *q, int n, CalibrateInfo *cal ) +{ + int x, t; + + /* Current error. + */ + int le = 0; + int ae = 0; + int be = 0; + + for( x = 0; x < n; x++ ) { + /* Get colour, add in error from previous pixel. + */ + int L = p[0] + le; + int A = (signed char) p[1] + ae; + int B = (signed char) p[2] + be; + p += 4; + + /* Look out for overflow. + */ + L = IM_MIN( 255, L ); + A = IM_MIN( 127, A ); + B = IM_MIN( 127, B ); + + /* Find new quant error. This will always be +ve. + */ + le = L & 3; + ae = A & 3; + be = B & 3; + + /* Scale to 0-63. + */ + L = (L >> 2) & 63; + A = (A >> 2) & 63; + B = (B >> 2) & 63; + + /* Convert to RGB. + */ + t = index( L, A, B ); + q[0] = cal->red[t]; + q[1] = cal->green[t]; + q[2] = cal->blue[t]; + q += 3; + } +} + +/* Build Lab->disp tables. + */ +void * +im_LabQ2disp_build_table( IMAGE *out, struct im_col_display *d ) +{ + CalibrateInfo *cal; + int l, a, b; + int t; + + if( !(cal = IM_NEW( out, CalibrateInfo )) ) + return( NULL ); + cal->disp = d; + if( !(cal->dtab = im_col_make_tables_RGB( out, d ) ) ) { + if( !out ) + im_free( cal ); + return( NULL ); + } + + /* Build our tables. + */ + for( l = 0; l < 64; l++ ) { + for( a = 0; a < 64; a++ ) { + for( b = 0; b < 64; b++ ) { + /* Scale to lab space. + */ + float L = (l << 2) * (100.0/256.0); + float A = (signed char) (a << 2); + float B = (signed char) (b << 2); + float X, Y, Z; + int rb, gb, bb; + int oflow; + + /* Convert to XYZ. + */ + im_col_Lab2XYZ( L, A, B, &X, &Y, &Z ); + + /* Convert to display. + */ + im_col_XYZ2rgb( cal->disp, cal->dtab, + X, Y, Z, &rb, &gb, &bb, &oflow ); + + /* Save RGB. + */ + t = index( l, a, b ); + cal->red[t] = rb; + cal->green[t] = gb; + cal->blue[t] = bb; + } + } + } + + return( (void *) cal ); +} + +int +im_LabQ2disp_table( IMAGE *in, IMAGE *out, void *table ) +{ + CalibrateInfo *cal = (CalibrateInfo *) table; + + if ( in->Coding != IM_CODING_LABQ ) { + im_errormsg( "im_LabQ2Lab: not a packed Lab image" ); + return( -1 ); + } + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bands = 3; + out->Bbits = IM_BBITS_BYTE; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_RGB; + + if( im_wrapone( in, out, (im_wrapone_fn) imb_LabQ2disp, cal, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_LabQ2disp( IMAGE *in, IMAGE *out, struct im_col_display *d ) +{ + void *table; + + if( !(table = im_LabQ2disp_build_table( out, d )) || + im_LabQ2disp_table( in, out, table ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_LabS2Lab.c b/libsrc/colour/im_LabS2Lab.c new file mode 100644 index 00000000..58c8b431 --- /dev/null +++ b/libsrc/colour/im_LabS2Lab.c @@ -0,0 +1,96 @@ +/* @(#) im_LabS2Lab() - convert short LAB format to Lab. + * @(#) + * @(#) int im_LabS2Lab( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) + * + * 12/12/02 JC + * - adapted from im_LabS2LabQ() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Convert n pels from signed short to Lab. + */ +void +imb_LabS2Lab( signed short *in, float *out, int n ) +{ + signed short *p = in; + float *q = out; + int c; + + for( c = 0; c < n; c++ ) { + q[0] = p[0] / (32767.0 / 100.0); + q[1] = p[1] / (32768.0 / 128.0); + q[2] = p[2] / (32768.0 / 128.0); + + p += 3; + q += 3; + } +} + +int +im_LabS2Lab( IMAGE *in, IMAGE *out ) +{ + /* Check type. + */ + if( in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_LabS2Lab: not an uncoded image" ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_SHORT || in->Bands != 3 ) { + im_errormsg( "im_LabS2Lab: not a 3-band signed short image" ); + return( -1 ); + } + + /* Set up output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_LAB; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Bbits = IM_BBITS_FLOAT; + + if( im_wrapone( in, out, + (im_wrapone_fn) imb_LabS2Lab, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_LabS2LabQ.c b/libsrc/colour/im_LabS2LabQ.c new file mode 100644 index 00000000..5e47764a --- /dev/null +++ b/libsrc/colour/im_LabS2LabQ.c @@ -0,0 +1,151 @@ +/* @(#) im_LabS2LabQ() - convert short LAB format to IM_CODING_LABQ. + * @(#) + * @(#) int im_LabS2LabQ( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) + * + * 17/11/93 JC + * - adapted from im_LabQ2LabS() + * 16/11/94 JC + * - adapted to new im_wrap_oneonebuf() function + * 15/6/95 JC + * - oops! rounding was broken + * 6/6/95 JC + * - added round-to-nearest + * - somewhat slower ... + * 21/12/99 JC + * - a/b ==0 rounding was broken + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Convert n pels from signed short to IM_CODING_LABQ. + */ +void +imb_LabS2LabQ( signed short *in, unsigned char *out, int n ) +{ + int c; + signed short *p = in; + int l, a, b; + unsigned char *q = out; + unsigned char ext; + + for( c = 0; c < n; c++ ) { + /* Get LAB, rounding to 10, 11, 11. + */ + l = p[0] + 16; + if( l < 0 ) + l = 0; + else if( l > 32767 ) + l = 32767; + l >>= 5; + + /* Make sure we round -ves in the right direction! + */ + a = p[1]; + if( a >= 0 ) + a += 16; + else + a -= 16; + if( a < -32768 ) + a = -32768; + else if( a > 32767 ) + a = 32767; + a >>= 5; + + b = p[2]; + if( b >= 0 ) + b += 16; + else + b -= 16; + if( b < -32768 ) + b = -32768; + else if( b > 32767 ) + b = 32767; + b >>= 5; + + p += 3; + + /* Extract top 8 bits. + */ + q[0] = l >> 2; + q[1] = a >> 3; + q[2] = b >> 3; + + /* Form extension byte. + */ + ext = (l << 6) & 0xc0; + ext |= (a << 3) & 0x38; + ext |= b & 0x7; + q[3] = ext; + q += 4; + } +} + +int +im_LabS2LabQ( IMAGE *in, IMAGE *out ) +{ + /* Check type. + */ + if( in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_LabS2LabQ: not an uncoded image" ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_SHORT || in->Bands != 3 ) { + im_errormsg( "im_LabS2LabQ: not a 3-band signed short image" ); + return( -1 ); + } + + /* Set up output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bands = 4; + out->Type = IM_TYPE_LAB; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = 8; + out->Coding = IM_CODING_LABQ; + + if( im_wrapone( in, out, + (im_wrapone_fn) imb_LabS2LabQ, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_UCS2LCh.c b/libsrc/colour/im_UCS2LCh.c new file mode 100644 index 00000000..84085e02 --- /dev/null +++ b/libsrc/colour/im_UCS2LCh.c @@ -0,0 +1,109 @@ +/* @(#) Turn UCS to LCh + * @(#) + * @(#) Usage: + * @(#) im_UCS2LCh( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * 15/11/94 JC + * - error messages added + * - memory leak fixed + * 16/11/94 JC + * - uses im_wrap_oneonebuf() now + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer of data. + */ +void +imb_UCS2LCh( float *p, float *q, int n ) +{ + int x; + + for( x = 0; x < n; x++ ) { + float Lucs = p[0]; + float Cucs = p[1]; + float hucs = p[2]; + + /* Turn from UCS. + */ + float C = im_col_Cucs2C( Cucs ); + float h = im_col_Chucs2h( C, hucs ); + float L = im_col_Lucs2L( Lucs ); + + p += 3; + + q[0] = L; + q[1] = C; + q[2] = h; + q += 3; + } +} + +int +im_UCS2LCh( IMAGE *in, IMAGE *out ) +{ + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_UCS2LCh: 3-band float uncoded input only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in) ) + return( -1 ); + out->Type = IM_TYPE_LCH; + + /* Do the processing. + */ + im_col_make_tables_UCS(); + if( im_wrapone( in, out, + (im_wrapone_fn) imb_UCS2LCh, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_XYZ2Lab.c b/libsrc/colour/im_XYZ2Lab.c new file mode 100644 index 00000000..bbd0a038 --- /dev/null +++ b/libsrc/colour/im_XYZ2Lab.c @@ -0,0 +1,185 @@ +/* @(#) Turn XYZ to Lab colourspace. + * @(#) + * @(#) Usage: + * @(#) im_XYZ2Lab( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modifed: + * 16/11/94 JC + * - uses im_wrapone() + * - in-line conversion + * 27/1/03 JC + * - swapped cbrt() for pow(), more portable + * 12/11/04 + * - swapped pow() for cbrt() again, pow() is insanely slow on win32 + * - added a configure test for cbrt(). + * 23/11/04 + * - use a large LUT instead, about 5x faster + * 23/11/06 + * - ahem, build the LUT outside the eval thread + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifndef HAVE_CBRT +#define cbrt( X ) pow( (X), 1.0 / 3.0 ) +#endif /*!HAVE_CBRT*/ + +/* Lookup table size. + */ +#define QUANT_ELEMENTS (100000) + +float cbrt_table[QUANT_ELEMENTS]; + +void +imb_XYZ2Lab_tables( void ) +{ + static int built_tables = 0; + + int i; + + if( built_tables ) + return; + + for( i = 0; i < QUANT_ELEMENTS; i++ ) { + float Y = (double) i / QUANT_ELEMENTS; + + if( Y < 0.008856 ) + cbrt_table[i] = 7.787 * Y + (16.0 / 116.0); + else + cbrt_table[i] = cbrt( Y ); + } + + built_tables = 1; +} + +/* Process a buffer of data. + */ +void +imb_XYZ2Lab( float *p, float *q, int n, im_colour_temperature *temp ) +{ + int x; + + for( x = 0; x < n; x++ ) { + float nX, nY, nZ; + int i; + float f; + float cbx, cby, cbz; + + nX = QUANT_ELEMENTS * p[0] / temp->X0; + nY = QUANT_ELEMENTS * p[1] / temp->Y0; + nZ = QUANT_ELEMENTS * p[2] / temp->Z0; + p += 3; + + i = (int) nX; + if( i < 0 ) + i = 0; + if( i > QUANT_ELEMENTS - 2 ) + i = QUANT_ELEMENTS - 2; + f = nX - i; + cbx = cbrt_table[i] + f * (cbrt_table[i + 1] - cbrt_table[i]); + + i = (int) nY; + if( i < 0 ) + i = 0; + if( i > QUANT_ELEMENTS - 2 ) + i = QUANT_ELEMENTS - 2; + f = nY - i; + cby = cbrt_table[i] + f * (cbrt_table[i + 1] - cbrt_table[i]); + + i = (int) nZ; + if( i < 0 ) + i = 0; + if( i > QUANT_ELEMENTS - 2 ) + i = QUANT_ELEMENTS - 2; + f = nZ - i; + cbz = cbrt_table[i] + f * (cbrt_table[i + 1] - cbrt_table[i]); + + q[0] = 116.0 * cby - 16.0; + q[1] = 500.0 * (cbx - cby); + q[2] = 200.0 * (cby - cbz); + q += 3; + } +} + +int +im_XYZ2Lab_temp( IMAGE *in, IMAGE *out, + double X0, double Y0, double Z0 ) +{ + im_colour_temperature *temp = IM_NEW( out, im_colour_temperature ); + + /* Check input image. + */ + if( !temp ) + return( -1 ); + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_XYZ2Lab: 3-band uncoded float only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in) ) + return( -1 ); + out->Type = IM_TYPE_LAB; + + /* Do the processing. + */ + imb_XYZ2Lab_tables(); + temp->X0 = X0; + temp->Y0 = Y0; + temp->Z0 = Z0; + if( im_wrapone( in, out, + (im_wrapone_fn) imb_XYZ2Lab, temp, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_XYZ2Lab( IMAGE *in, IMAGE *out ) +{ + return( im_XYZ2Lab_temp( in, out, IM_D65_X0, IM_D65_Y0, IM_D65_Z0 ) ); +} diff --git a/libsrc/colour/im_XYZ2Yxy.c b/libsrc/colour/im_XYZ2Yxy.c new file mode 100644 index 00000000..9c8b520d --- /dev/null +++ b/libsrc/colour/im_XYZ2Yxy.c @@ -0,0 +1,101 @@ +/* @(#) Turn XYZ to Yxy colourspace. + * @(#) + * @(#) Usage: + * @(#) im_XYZ2Yxy( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modified: + * 29/5/02 JC + * - from lab2xyz + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer of data. + */ +void +imb_XYZ2Yxy( float *p, float *q, int n ) +{ + int i; + + for( i = 0; i < n; i++ ) { + float X = p[0]; + float Y = p[1]; + float Z = p[2]; + double total = X + Y + Z; + + float x, y; + + p += 3; + + x = X / total; + y = Y / total; + + q[0] = Y; + q[1] = x; + q[2] = y; + q += 3; + } +} + +int +im_XYZ2Yxy( IMAGE *in, IMAGE *out ) +{ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_XYZ2Yxy: 3-band uncoded float input only" ); + return( -1 ); + } + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_YXY; + + if( im_wrapone( in, out, + (im_wrapone_fn) imb_XYZ2Yxy, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/colour/im_XYZ2disp.c b/libsrc/colour/im_XYZ2disp.c new file mode 100644 index 00000000..24477b3d --- /dev/null +++ b/libsrc/colour/im_XYZ2disp.c @@ -0,0 +1,164 @@ +/* @(#) Turn XYZ files into displayable rgb. + * @(#) + * @(#) Usage: + * @(#) int im_XYZ2disp( IMAGE *in, IMAGE *out, struct im_col_display *d ) + * @(#) + * @(#) Returns: -1 on error, else 0 + * + * Author: J-P. Laurent + * Modified: + * 15/11/94 JC + * - error message added + * - out->Type set to IM_TYPE_RGB + * - memory leak fixed + * 16/11/94 JC + * - uses im_wrapone() + * 15/2/95 JC + * - oops! now uses PEL, not float for output pointer + * 2/1/96 JC + * - sometimes produced incorrect result at extrema + * - reformatted + * - now uses IM_RINT() and clip() + * 18/9/96 JC + * - some speed-ups ... 3x faster + * - slightly less accurate, but who cares + * - added out-of-mem check for table build + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer of data. + */ +void +imb_XYZ2disp( float *p, PEL *q, int n, + struct im_col_tab_disp *table, struct im_col_display *d ) +{ + int x; + float rstep = (d->d_YCR - d->d_Y0R) / 1500.0; + float gstep = (d->d_YCG - d->d_Y0G) / 1500.0; + float bstep = (d->d_YCB - d->d_Y0B) / 1500.0; + + for( x = 0; x < n; x++ ) { + float Yr, Yg, Yb; + int i; + int r, g, b; + + float X = p[0]; + float Y = p[1]; + float Z = p[2]; + + p += 3; + + /* Multiply through the matrix to get luminosity values. + */ + Yr = table->mat_XYZ2lum[0][0] * X + + table->mat_XYZ2lum[0][1] * Y + + table->mat_XYZ2lum[0][2] * Z; + Yg = table->mat_XYZ2lum[1][0] * X + + table->mat_XYZ2lum[1][1] * Y + + table->mat_XYZ2lum[1][2] * Z; + Yb = table->mat_XYZ2lum[2][0] * X + + table->mat_XYZ2lum[2][1] * Y + + table->mat_XYZ2lum[2][2] * Z; + + /* Clip -ves. + */ + Yr = IM_MAX( Yr, d->d_Y0R ); + Yg = IM_MAX( Yg, d->d_Y0G ); + Yb = IM_MAX( Yb, d->d_Y0B ); + + /* Turn luminosity to colour value. + */ + i = IM_MIN( 1500, (Yr - d->d_Y0R) / rstep ); + r = table->t_Yr2r[i]; + + i = IM_MIN( 1500, (Yg - d->d_Y0G) / gstep ); + g = table->t_Yg2g[i]; + + i = IM_MIN( 1500, (Yb - d->d_Y0B) / bstep ); + b = table->t_Yb2b[i]; + + /* Clip output. + */ + r = IM_MIN( r, d->d_Vrwr ); + g = IM_MIN( g, d->d_Vrwg ); + b = IM_MIN( b, d->d_Vrwb ); + + q[0] = r; + q[1] = g; + q[2] = b; + + q += 3; + } +} + +int +im_XYZ2disp( IMAGE *in, IMAGE *out, struct im_col_display *d ) +{ + struct im_col_tab_disp *table; + + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_XYZ2disp: 3-band uncoded float only" ); + return( -1 ); + } + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_BYTE; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Type = IM_TYPE_RGB; + + /* Build the lookup tables + */ + if( !(table = im_col_make_tables_RGB( out, d )) ) + return( -1 ); + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) imb_XYZ2disp, table, d ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_Yxy2XYZ.c b/libsrc/colour/im_Yxy2XYZ.c new file mode 100644 index 00000000..6dd1ea09 --- /dev/null +++ b/libsrc/colour/im_Yxy2XYZ.c @@ -0,0 +1,102 @@ +/* @(#) Turn Yxy to XYZ colourspace. + * @(#) + * @(#) Usage: + * @(#) im_Yxy2XYZ( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modified: + * 29/5/02 JC + * - from lab2xyz + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer of data. + */ +void +imb_Yxy2XYZ( float *p, float *q, int n ) +{ + int i; + + for( i = 0; i < n; i++ ) { + float Y = p[0]; + float x = p[1]; + float y = p[2]; + + double total; + float X, Z; + + p += 3; + + total = Y / y; + X = x * total; + Z = (X - x * X - x * Y) / x; + + q[0] = X; + q[1] = Y; + q[2] = Z; + q += 3; + } +} + +int +im_Yxy2XYZ( IMAGE *in, IMAGE *out ) +{ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_Yxy2XYZ: 3-band uncoded float input only" ); + return( -1 ); + } + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_XYZ; + + if( im_wrapone( in, out, + (im_wrapone_fn) imb_Yxy2XYZ, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/colour/im_dE00_fromLab.c b/libsrc/colour/im_dE00_fromLab.c new file mode 100644 index 00000000..a01b3fdb --- /dev/null +++ b/libsrc/colour/im_dE00_fromLab.c @@ -0,0 +1,110 @@ +/* @(#) Calculate dE00 from two Lab images + * @(#) + * @(#) Usage: + * @(#) im_dE00_fromLab( im1, im2, im_out ) + * @(#) IMAGE *im1, *im2, *im_out; + * @(#) + * @(#) float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * 10/10/02 JC + * - from dECMC + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer. + */ +void +imb_dE00_fromLab( float **p, float *q, int n ) +{ + float *p1 = p[0]; + float *p2 = p[1]; + int x; + + for( x = 0; x < n; x++ ) { + float L1 = p1[0]; + float a1 = p1[1]; + float b1 = p1[2]; + float L2 = p2[0]; + float a2 = p2[1]; + float b2 = p2[2]; + + p1 += 3; + p2 += 3; + + q[x] = im_col_dE00( L1, a1, b1, L2, a2, b2 ); + } +} + +int +im_dE00_fromLab( IMAGE *im1, IMAGE *im2, IMAGE *out ) +{ + IMAGE *invec[3]; + + /* Check input types. + */ + if( im1->Bands != 3 || im1->BandFmt != IM_BANDFMT_FLOAT || + im1->Coding != IM_CODING_NONE || + im2->Bands != 3 || im2->BandFmt != IM_BANDFMT_FLOAT || + im2->Coding != IM_CODING_NONE ) { + im_errormsg( "im_dE00_fromLab: 3-band float only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_descv( out, im1, im2, NULL ) ) + return( -1 ); + out->Bbits = IM_BBITS_FLOAT; + out->Bands = 1; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Type = IM_TYPE_B_W; + + /* Do the processing. + */ + invec[0] = im1; invec[1] = im2; invec[2] = NULL; + if( im_wrapmany( invec, out, + (im_wrapmany_fn) imb_dE00_fromLab, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_dECMC_fromLab.c b/libsrc/colour/im_dECMC_fromLab.c new file mode 100644 index 00000000..70a6761b --- /dev/null +++ b/libsrc/colour/im_dECMC_fromLab.c @@ -0,0 +1,110 @@ +/* @(#) Calculate dECMC from two Lab images + * @(#) + * @(#) Usage: + * @(#) im_dECMC_fromLab( im1, im2, im_out ) + * @(#) IMAGE *im1, *im2, *im_out; + * @(#) + * @(#) float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * 5/8/98 JC + * - oops, wasn't testing input args correctly + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer. + */ +void +imb_dECMC_fromLab( float **p, float *q, int n ) +{ + float *p1 = p[0]; + float *p2 = p[1]; + int x; + + for( x = 0; x < n; x++ ) { + float L1 = p1[0]; + float a1 = p1[1]; + float b1 = p1[2]; + float L2 = p2[0]; + float a2 = p2[1]; + float b2 = p2[2]; + + p1 += 3; + p2 += 3; + + q[x] = im_col_dECMC( L1, a1, b1, L2, a2, b2 ); + } +} + +int +im_dECMC_fromLab( IMAGE *im1, IMAGE *im2, IMAGE *out ) +{ + IMAGE *invec[3]; + + /* Check input types. + */ + if( im1->Bands != 3 || im1->BandFmt != IM_BANDFMT_FLOAT || + im1->Coding != IM_CODING_NONE || + im2->Bands != 3 || im2->BandFmt != IM_BANDFMT_FLOAT || + im2->Coding != IM_CODING_NONE ) { + im_errormsg( "im_dECMC_fromLab: 3-band float only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_descv( out, im1, im2, NULL ) ) + return( -1 ); + out->Bbits = IM_BBITS_FLOAT; + out->Bands = 1; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Type = IM_TYPE_B_W; + + /* Do the processing. + */ + invec[0] = im1; invec[1] = im2; invec[2] = NULL; + if( im_wrapmany( invec, out, + (im_wrapmany_fn) imb_dECMC_fromLab, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_dE_fromLab.c b/libsrc/colour/im_dE_fromLab.c new file mode 100644 index 00000000..27e07574 --- /dev/null +++ b/libsrc/colour/im_dE_fromLab.c @@ -0,0 +1,116 @@ +/* @(#) Calculate dE (CIELAB standard) from two LAB images. + * @(#) + * @(#) Usage: + * @(#) im_dE_fromLab( im1, *im2, im_out ) + * @(#) IMAGE *im1, *im2, *im_out; + * @(#) + * @(#) float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modified: + * 16/11/94 JC + * - partialed! + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Find the difference between two buffers of LAB data. + */ +void +imb_dE_fromLab( float **p, float *q, int n ) +{ + float *p1 = p[0]; + float *p2 = p[1]; + int x; + + for( x = 0; x < n; x++ ) { + float L1 = p1[0]; + float a1 = p1[1]; + float b1 = p1[2]; + float L2 = p2[0]; + float a2 = p2[1]; + float b2 = p2[2]; + float dL, da, db; + + p1 += 3; + p2 += 3; + + dL = L1 - L2; + da = a1 - a2; + db = b1 - b2; + + *q++ = sqrt( dL*dL + da*da + db*db ); + } +} + +int +im_dE_fromLab( IMAGE *im1, IMAGE *im2, IMAGE *out ) +{ + IMAGE *invec[3]; + + /* Check input image. + */ + if( im1->Bands != 3 || im1->BandFmt != IM_BANDFMT_FLOAT || + im1->Coding != IM_CODING_NONE || + im2->Bands != 3 || im2->BandFmt != IM_BANDFMT_FLOAT || + im2->Coding != IM_CODING_NONE ) { + im_errormsg( "im_dE_fromLab: inputs should be 3 band float"); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_descv( out, im1, im2, NULL ) ) + return( -1 ); + out->Bbits = IM_BBITS_FLOAT; + out->Bands = 1; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Type = IM_TYPE_B_W; + + /* Do the processing. + */ + invec[0] = im1; invec[1] = im2; invec[2] = NULL; + if( im_wrapmany( invec, out, + (im_wrapmany_fn) imb_dE_fromLab, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_disp2XYZ.c b/libsrc/colour/im_disp2XYZ.c new file mode 100644 index 00000000..233a72f9 --- /dev/null +++ b/libsrc/colour/im_disp2XYZ.c @@ -0,0 +1,115 @@ +/* @(#) Turn displayable rgb files to XYZ. + * @(#) + * @(#) Usage: + * @(#) im_disp2XYZ( imagein, imageout, display ) + * @(#) IMAGE *imagein, *imageout; + * @(#) struct im_col_display *display; + * @(#) + * @(#) uchar in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modified: + * 15/11/94 JC + * - memory leak fixed + * - error message added + * 16/11/94 JC + * - partialed + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Convert a buffer. + */ +void +imb_disp2XYZ( PEL *p, float *q, int n, + struct im_col_display *d, struct im_col_tab_disp *table ) +{ + int x; + + for( x = 0; x < n; x++ ) { + int r = p[0]; + int g = p[1]; + int b = p[2]; + float X, Y, Z; + p += 3; + + im_col_rgb2XYZ(d, table, r, g, b, &X, &Y, &Z); + + q[0] = X; + q[1] = Y; + q[2] = Z; + q += 3; + } +} + +int +im_disp2XYZ( IMAGE *in, IMAGE *out, struct im_col_display *d ) +{ + struct im_col_tab_disp *table; /* pointer to the lookup tables */ + + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_UCHAR || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_disp2XYZ: input not 3-band uncoded char" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Type = IM_TYPE_XYZ; + + /* Prepare the lookup tables + */ + table = im_col_make_tables_RGB( out, d ); + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) imb_disp2XYZ, d, table ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_icc_transform.c b/libsrc/colour/im_icc_transform.c new file mode 100644 index 00000000..609480ec --- /dev/null +++ b/libsrc/colour/im_icc_transform.c @@ -0,0 +1,796 @@ +/* @(#) Transform images with little cms + * @(#) + * + * 26/4/02 JC + * 26/8/05 + * - attach profiles and intents to output images + * - added im_icc_import_embedded() to import with an embedded profile + * 12/5/06 + * - lock around cmsDoTransform + * 23/1/07 + * - set RGB16 on 16-bit RGB export + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_LCMS + +#include +#include + +int +im_icc_present( void ) +{ + return( 0 ); +} + +int +im_icc_transform( IMAGE *in, IMAGE *out, + const char *input_profile_filename, + const char *output_profile_filename, + int intent ) +{ + im_error( "im_icc_transform", + _( "lcms library not linked to this VIPS" ) ); + + return( -1 ); +} + +int +im_icc_import( IMAGE *in, IMAGE *out, + const char *input_profile_filename, int intent ) +{ + im_error( "im_icc_import", + _( "lmcs library not linked to this VIPS" ) ); + + return( -1 ); +} + +int +im_icc_import_embedded( IMAGE *in, IMAGE *out, int intent ) +{ + im_error( "im_icc_import", + _( "lmcs library not linked to this VIPS" ) ); + + return( -1 ); +} + +int +im_icc_export_depth( IMAGE *in, IMAGE *out, int depth, + const char *output_profile_filename, int intent ) +{ + im_error( "im_icc_export_depth", + _( "lmcs library not linked to this VIPS" ) ); + + return( -1 ); +} + +int +im_icc_export( IMAGE *in, IMAGE *out, + const char *output_profile_filename, int intent ) +{ + im_error( "im_icc_export", + _( "lmcs library not linked to this VIPS" ) ); + + return( -1 ); +} + +int +im_icc_ac2rc( IMAGE *in, IMAGE *out, const char *profile_filename ) +{ + im_error( "im_icc_ac2rc", + _( "lmcs library not linked to this VIPS" ) ); + + return( -1 ); +} + +#else + +#include +#include +#include + +/* Has to be before VIPS to avoid nameclashes. + */ +#include +#include + +#include +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Call lcms with up to this many pixels at once. + */ +#define PIXEL_BUFFER_SIZE (10000) + +static const char * +decode_intent( int intent ) +{ + switch( intent ) { + case IM_INTENT_PERCEPTUAL: return( "PERCEPTUAL" ); + case IM_INTENT_RELATIVE_COLORIMETRIC: return( "RELATIVE" ); + case IM_INTENT_SATURATION: return( "SATURATION" ); + case IM_INTENT_ABSOLUTE_COLORIMETRIC: return( "ABSOLUTE" ); + default: return( "" ); + } +} + +int +im_icc_present( void ) +{ + return( 1 ); +} + +/* Global state for a transform. + */ +typedef struct { + IMAGE *in; + IMAGE *out; + const char *input_profile_filename; + const char *output_profile_filename; + int intent; + + cmsHPROFILE in_profile; + cmsHPROFILE out_profile; + cmsHTRANSFORM trans; + + /* We need to single-thread calls to LCMS. + */ + GMutex *lock; +} Icc; + +static int +icc_destroy( Icc *icc ) +{ + IM_FREEF( cmsDeleteTransform, icc->trans ); + IM_FREEF( cmsCloseProfile, icc->in_profile ); + IM_FREEF( cmsCloseProfile, icc->out_profile ); + IM_FREEF( g_mutex_free, icc->lock ); + + return( 0 ); +} + +static Icc * +icc_new( IMAGE *in, IMAGE *out, int intent ) +{ + Icc *icc; + + /* Ask lcms not to abort on error. + */ + cmsErrorAction( LCMS_ERROR_IGNORE ); + + if( !(icc = IM_NEW( out, Icc )) ) + return( NULL ); + + icc->in = in; + icc->out = out; + icc->input_profile_filename = NULL; + icc->output_profile_filename = NULL; + icc->intent = intent; + icc->in_profile = 0; + icc->out_profile = 0; + icc->trans = 0; + icc->lock = g_mutex_new(); + + if( im_add_close_callback( out, + (im_callback_fn) icc_destroy, icc, NULL ) ) + return( NULL ); + + return( icc ); +} + +static Icc * +icc_new_file( IMAGE *in, IMAGE *out, + const char *input_profile_filename, + const char *output_profile_filename, + int intent ) +{ + Icc *icc; + + if( !(icc = icc_new( in, out, intent )) ) + return( NULL ); + + if( input_profile_filename ) { + icc->input_profile_filename = + im_strdup( out, input_profile_filename ); + if( !(icc->in_profile = cmsOpenProfileFromFile( + input_profile_filename, "r" )) ) + im_error( "im_icc_transform", + _( "unable to open profile \"%s\"" ), + input_profile_filename ); + } + + if( output_profile_filename ) { + icc->output_profile_filename = + im_strdup( out, output_profile_filename ); + if( !(icc->out_profile = cmsOpenProfileFromFile( + output_profile_filename, "r" )) ) + im_error( "im_icc_transform", + _( "unable to open profile \"%s\"" ), + output_profile_filename ); + } + + if( !output_profile_filename ) + icc->out_profile = cmsCreateLabProfile( NULL ); + if( !input_profile_filename ) + icc->in_profile = cmsCreateLabProfile( NULL ); + + if( !icc->in_profile || !icc->out_profile ) { + im_error( "im_icc_transform", + _( "unable to create profiles" ) ); + return( NULL ); + } + + return( icc ); +} + +static Icc * +icc_new_mem( IMAGE *in, IMAGE *out, + void *data, int data_length, + int intent ) +{ + Icc *icc; + + if( !(icc = icc_new( in, out, intent )) ) + return( NULL ); + + if( !(icc->in_profile = cmsOpenProfileFromMem( data, data_length )) ) { + im_error( "im_icc_transform", _( "unable to read profile" ) ); + return( NULL ); + } + icc->out_profile = cmsCreateLabProfile( NULL ); + + return( icc ); +} + +/* Pack a buffer of floats into lcms's fixed-point formats. Cut from + * lcms-1.0.8. + */ +static void +encode_lab( float *lab, WORD *fixed, int n ) +{ + int i; + + for( i = 0; i < n; i++ ) { + float L = lab[0]; + float a = lab[1]; + float b = lab[2]; + + if( L < 0 ) + L = 0; + if( L > 100. ) + L = 100.; + + if( a < -128. ) + a = -128; + if( a > 127.9961 ) + a = 127.9961; + if( b < -128. ) + b = -128; + if( b > 127.9961 ) + b = 127.9961; + + fixed[0] = L * 652.800 + 0.5; + fixed[1] = (a + 128.0) * 256.0 + 0.5; + fixed[2] = (b + 128.0) * 256.0 + 0.5; + + lab += 3; + fixed += 3; + } +} + +static void +decode_lab( WORD *fixed, float *lab, int n ) +{ + int i; + + for( i = 0; i < n; i++ ) { + lab[0] = (double) fixed[0] / 652.800; + lab[1] = ((double) fixed[1] / 256.0) - 128.0; + lab[2] = ((double) fixed[2] / 256.0) - 128.0; + + lab += 3; + fixed += 3; + } +} + +/* + + lcms calls this on error ... but only for dynamically linked + programs :-( + +void +cmsSignalError( int code, const char *fmt, ... ) +{ + va_list ap; + + im_error( "im_icc", "error code #%d from little cms: " ); + + va_start( ap, fmt ); + im_verrormsg( fmt, ap ); + va_end( ap ); +} + */ + +static void +transform_buf( PEL *in, PEL *out, int n, Icc *icc ) +{ + g_mutex_lock( icc->lock ); + cmsDoTransform( icc->trans, in, out, n ); + g_mutex_unlock( icc->lock ); +} + +static int +attach_profile( IMAGE *im, const char *filename ) +{ + char *data; + unsigned int data_length; + + if( !(data = im__file_read_name( filename, &data_length )) ) + return( -1 ); + if( im_meta_set_blob( im, IM_META_ICC_NAME, + (im_callback_fn) im_free, data, data_length ) ) { + im_free( data ); + return( -1 ); + } + + return( 0 ); +} + +int +im_icc_transform( IMAGE *in, IMAGE *out, + const char *input_profile_filename, + const char *output_profile_filename, + int intent ) +{ + Icc *icc; + DWORD in_icc_format; + DWORD out_icc_format; + + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_icc_transform", _( "uncoded input only" ) ); + return( -1 ); + } + + if( !(icc = icc_new_file( in, out, + input_profile_filename, output_profile_filename, intent )) ) + return( -1 ); + + if( !cmsIsIntentSupported( icc->in_profile, + intent, LCMS_USED_AS_INPUT ) ) + im_warning( "im_icc_transform: intent %d (%s) not supported by " + "profile \"%s\"; falling back to default intent " + "(usually PERCEPTUAL)", + intent, decode_intent( intent ), + input_profile_filename ); + + if( !cmsIsIntentSupported( icc->out_profile, + intent, LCMS_USED_AS_OUTPUT ) ) + im_warning( "im_icc_transform: intent %d (%s) not supported by " + "profile \"%s\"; falling back to default intent " + "(usually PERCEPTUAL)", + intent, decode_intent( intent ), + output_profile_filename ); + + switch( cmsGetColorSpace( icc->in_profile ) ) { + case icSigCmykData: + if( in->Bands != 4 ) { + im_error( "im_icc_transform", _( "CMYK input profile " + "needs a 4 band input image" ) ); + return( -1 ); + } + in_icc_format = COLORSPACE_SH( PT_CMYK ) | CHANNELS_SH( 4 ); + break; + + case icSigRgbData: + if( in->Bands != 3 ) { + im_error( "im_icc_transform", _( "RGB input profile " + "needs a 3 band input image" ) ); + return( -1 ); + } + in_icc_format = COLORSPACE_SH( PT_RGB ) | CHANNELS_SH( 3 ); + break; + + default: + im_error( "im_icc_transform", _( "unimplemented input colour " + "space 0x%x" ), cmsGetColorSpace( icc->in_profile ) ); + return( -1 ); + } + + /* Prepare the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + switch( cmsGetColorSpace( icc->out_profile ) ) { + case icSigCmykData: + out->Type = IM_TYPE_CMYK; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = IM_BBITS_BYTE; + out->Bands = 4; + out_icc_format = TYPE_CMYK_8; + break; + + case icSigRgbData: + out->Type = IM_TYPE_RGB; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = IM_BBITS_BYTE; + out->Bands = 3; + out_icc_format = TYPE_RGB_8; + break; + + default: + im_error( "im_icc_transform", _( "unimplemented output colour " + "space 0x%x" ), cmsGetColorSpace( icc->out_profile ) ); + return( -1 ); + } + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + in_icc_format |= BYTES_SH( 1 ); + break; + + case IM_BANDFMT_USHORT: + in_icc_format |= BYTES_SH( 2 ); + break; + + default: + im_error( "im_icc_transform", + _( "uchar or ushort input only" ) ); + return( -1 ); + } + + if( !(icc->trans = cmsCreateTransform( icc->in_profile, in_icc_format, + icc->out_profile, out_icc_format, intent, 0 )) ) + return( -1 ); + + /* Process! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) transform_buf, icc, NULL ) ) + return( -1 ); + + if( attach_profile( out, output_profile_filename ) ) + return( -1 ); + + return( 0 ); +} + +static void +import_buf( PEL *in, float *out, int n, Icc *icc ) +{ + /* Buffer of encoded 16-bit pixels we write to. + */ + WORD encoded[3 * PIXEL_BUFFER_SIZE]; + + while( n > 0 ) { + const int chunk = IM_MIN( n, PIXEL_BUFFER_SIZE ); + + g_mutex_lock( icc->lock ); + cmsDoTransform( icc->trans, in, encoded, chunk ); + g_mutex_unlock( icc->lock ); + decode_lab( encoded, out, chunk ); + + in += chunk * IM_IMAGE_SIZEOF_PEL( icc->in ); + out += chunk * 3; + n -= chunk; + } +} + +static int +icc_import( IMAGE *in, IMAGE *out, Icc *icc ) +{ + DWORD icc_format; + + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_icc_import", _( "uncoded input only" ) ); + return( -1 ); + } + + if( !cmsIsIntentSupported( icc->in_profile, + icc->intent, LCMS_USED_AS_INPUT ) ) + im_warn( "im_icc_import", _( "intent %d (%s) not supported by " + "profile; falling back to default intent " + "(usually PERCEPTUAL)" ), + icc->intent, decode_intent( icc->intent ) ); + + /* Prepare the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_LAB; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Bbits = IM_BBITS_FLOAT; + out->Bands = 3; + + switch( cmsGetColorSpace( icc->in_profile ) ) { + case icSigCmykData: + if( in->Bands != 4 ) { + im_error( "im_icc_import", _( "CMYK profile needs a " + "4 band input image" ) ); + return( -1 ); + } + icc_format = COLORSPACE_SH( PT_CMYK ) | CHANNELS_SH( 4 ); + break; + + case icSigRgbData: + if( in->Bands != 3 ) { + im_error( "im_icc_import", _( "RGB profile needs a " + "3 band input image" ) ); + return( -1 ); + } + icc_format = COLORSPACE_SH( PT_RGB ) | CHANNELS_SH( 3 ); + break; + + default: + im_error( "im_icc_import", _( "unimplemented input colour " + "space 0x%x" ), cmsGetColorSpace( icc->in_profile ) ); + return( -1 ); + } + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + icc_format |= BYTES_SH( 1 ); + break; + + case IM_BANDFMT_USHORT: + icc_format |= BYTES_SH( 2 ); + break; + + default: + im_error( "im_icc_transform", + _( "uchar or ushort input only" ) ); + return( -1 ); + } + + if( !(icc->trans = cmsCreateTransform( icc->in_profile, icc_format, + icc->out_profile, TYPE_Lab_16, icc->intent, 0 )) ) + return( -1 ); + + /* Process! + */ + if( im_wrapone( in, out, (im_wrapone_fn) import_buf, icc, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_icc_import( IMAGE *in, IMAGE *out, + const char *input_profile_filename, int intent ) +{ + Icc *icc; + + if( !(icc = icc_new_file( in, out, + input_profile_filename, NULL, intent )) || + icc_import( in, out, icc ) ) + return( -1 ); + + return( 0 ); +} + +int +im_icc_import_embedded( IMAGE *in, IMAGE *out, int intent ) +{ + Icc *icc; + void *data; + size_t data_length; + + if( im_header_get_type( in, IM_META_ICC_NAME ) == 0 ) { + im_error( "im_icc_import_embedded", + _( "no embedded profile" ) ); + return( -1 ); + } + + if( im_meta_get_blob( in, IM_META_ICC_NAME, &data, &data_length ) || + !(icc = icc_new_mem( in, out, data, data_length, intent )) || + icc_import( in, out, icc ) ) + return( -1 ); + + return( 0 ); +} + +static void +export_buf( float *in, PEL *out, int n, Icc *icc ) +{ + /* Buffer of encoded 16-bit pixels we transform. + */ + WORD encoded[3 * PIXEL_BUFFER_SIZE]; + + while( n > 0 ) { + const int chunk = IM_MIN( n, PIXEL_BUFFER_SIZE ); + + encode_lab( in, encoded, chunk ); + g_mutex_lock( icc->lock ); + cmsDoTransform( icc->trans, encoded, out, chunk ); + g_mutex_unlock( icc->lock ); + + in += chunk * 3; + out += chunk * IM_IMAGE_SIZEOF_PEL( icc->out ); + n -= chunk; + } +} + +int +im_icc_export_depth( IMAGE *in, IMAGE *out, int depth, + const char *output_profile_filename, int intent ) +{ + Icc *icc; + DWORD icc_format; + + /* Do IM_CODING_LABQ too. + */ + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t1 = im_open_local( out, "im_icc_export:1", "p" ); + + if( !t1 || im_LabQ2Lab( in, t1 ) ) + return( -1 ); + + in = t1; + } + + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_error( "im_icc_export", + _( "3-band uncoded Lab float only" ) ); + return( -1 ); + } + + if( depth != 8 && depth != 16 ) { + im_error( "im_icc_export", _( "unsupported bit depth" ) ); + return( -1 ); + } + + if( !(icc = icc_new_file( in, out, + NULL, output_profile_filename, intent )) ) + return( -1 ); + + if( !cmsIsIntentSupported( icc->out_profile, + intent, LCMS_USED_AS_OUTPUT ) ) + im_warning( "im_icc_export: intent %d (%s) not supported by " + "profile \"%s\"; falling back to default intent " + "(usually PERCEPTUAL)", + intent, decode_intent( intent ), + output_profile_filename ); + + /* Prepare the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + switch( cmsGetColorSpace( icc->out_profile ) ) { + case icSigCmykData: + out->Type = IM_TYPE_CMYK; + out->BandFmt = depth == 8 ? + IM_BANDFMT_UCHAR : IM_BANDFMT_USHORT; + out->Bbits = depth == 8 ? IM_BBITS_BYTE : IM_BBITS_SHORT; + out->Bands = 4; + icc_format = depth == 8 ? TYPE_CMYK_8 : TYPE_CMYK_16; + break; + + case icSigRgbData: + out->Type = depth == 8 ? + IM_TYPE_RGB : IM_TYPE_RGB16; + out->BandFmt = depth == 8 ? + IM_BANDFMT_UCHAR : IM_BANDFMT_USHORT; + out->Bbits = depth == 8 ? IM_BBITS_BYTE : IM_BBITS_SHORT; + out->Bands = 3; + icc_format = depth == 8 ? TYPE_RGB_8 : TYPE_RGB_16; + break; + + default: + im_error( "im_icc_export", _( "unimplemented output colour " + "space 0x%x" ), cmsGetColorSpace( icc->out_profile ) ); + return( -1 ); + } + + if( !(icc->trans = cmsCreateTransform( icc->in_profile, TYPE_Lab_16, + icc->out_profile, icc_format, intent, 0 )) ) + return( -1 ); + + /* Process! + */ + if( im_wrapone( in, out, (im_wrapone_fn) export_buf, icc, NULL ) ) + return( -1 ); + + if( attach_profile( out, output_profile_filename ) ) + return( -1 ); + + return( 0 ); +} + +int +im_icc_export( IMAGE *in, IMAGE *out, + const char *output_profile_filename, int intent ) +{ + return( im_icc_export_depth( in, out, + 8, output_profile_filename, intent ) ); +} + +int +im_icc_ac2rc( IMAGE *in, IMAGE *out, const char *profile_filename ) +{ + cmsHPROFILE profile; + cmsCIEXYZ media; + + double add[3]; + double mul[3]; + + IMAGE *t[2]; + + if( !(profile = cmsOpenProfileFromFile( profile_filename, "r" )) ) + return( -1 ); + + if( !cmsTakeMediaWhitePoint( &media, profile ) ) { + im_error( "im_icc_ac2rc", _( "unable to get media " + "white point" ) ); + return( -1 ); + } + + cmsCloseProfile( profile ); + + add[0] = 0.0; + add[1] = 0.0; + add[2] = 0.0; + + mul[0] = IM_D50_X0 / (media.X * 100.0); + mul[1] = IM_D50_Y0 / (media.Y * 100.0); + mul[2] = IM_D50_Z0 / (media.Z * 100.0); + + /* Do IM_CODING_LABQ too. + */ + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t1 = im_open_local( out, "im_icc_ac2rc-1", "p" ); + + if( !t1 || im_LabQ2Lab( in, t1 ) ) + return( -1 ); + + in = t1; + } + + if( im_open_local_array( out, t, 2, "im_icc_ac2rc-2", "p" ) || + im_Lab2XYZ_temp( in, t[0], IM_D50_X0, IM_D50_Y0, IM_D50_Z0 ) || + im_lintra_vec( 3, mul, t[0], add, t[1] ) || + im_XYZ2Lab_temp( t[1], out, IM_D50_X0, IM_D50_Y0, IM_D50_Z0 ) ) + return( -1 ); + + return( 0 ); +} + +#endif /*HAVE_LCMS*/ diff --git a/libsrc/colour/im_lab_morph.c b/libsrc/colour/im_lab_morph.c new file mode 100644 index 00000000..e4ac277c --- /dev/null +++ b/libsrc/colour/im_lab_morph.c @@ -0,0 +1,257 @@ +/* Morph a lab image ... adjust: + * + * - cast + * Pass in a MASK containing CIELAB readings for a neutral greyscale ... + * eg. + * + * 3 4 + * 14.23 4.8 -3.95 + * 18.74 2.76 -2.62 + * 23.46 1.4 -1.95 + * 27.53 1.76 -2.01 + * + * interpolation from this makes cast corrector ... interpolate top and + * tail towards [0,0,0] and [100,0,0] ... can be in any order (ie. need + * not be sorted on L*) + * + * - L* + * Pass in scale and offset for L* ... L*' = (L* + offset) * scale + * + * - saturation + * scale a and b by these amounts ... eg. 1.5 increases saturation ... + * useful for some gammut mapping + * + * Find the top two by generating and printing a greyscale ... find the bottom + * by printing a Macbeth and looking at a/b spread + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +typedef struct { + IMAGE *in, *out; + + double L_scale, L_offset; + + double a_offset[101], b_offset[101]; + double a_scale, b_scale; + +} Params; + +static int +morph_init( Params *parm, + IMAGE *in, IMAGE *out, + double L_scale, double L_offset, + DOUBLEMASK *mask, double a_scale, double b_scale ) +{ + int i, j; + + parm->in = in; + parm->out = out; + parm->L_scale = L_scale; + parm->L_offset = L_offset; + parm->a_scale = a_scale; + parm->b_scale = b_scale; + + if( mask->xsize != 3 || mask->ysize < 1 || mask->ysize > 100 ) { + im_errormsg( "im_lab_morph: bad greyscale mask size" ); + return( -1 ); + } + for( i = 0; i < mask->ysize; i++ ) { + double L = mask->coeff[i*3]; + double a = mask->coeff[i*3 + 1]; + double b = mask->coeff[i*3 + 2]; + + if( L < 0 || L > 100 || a < -120 || a > 120 || + b < -120 || b > 120 ) { + im_errormsg( "im_lab_morph: bad greyscale mask " + "value, row %d", i ); + return( -1 ); + } + } + + /* Generate a/b offsets. + */ + for( i = 0; i <= 100; i++ ) { + double L_low = 0; + double a_low = 0; + double b_low = 0; + + double L_high = 100; + double a_high = 0; + double b_high = 0; + + /* Search for greyscale L just below i. Don't assume sorted by + * L*. + */ + for( j = 0; j < mask->ysize; j++ ) { + double L = mask->coeff[j*3]; + double a = mask->coeff[j*3 + 1]; + double b = mask->coeff[j*3 + 2]; + + if( L < i && L > L_low ) { + L_low = L; + a_low = a; + b_low = b; + } + } + + /* Search for greyscale L just above i. + */ + for( j = mask->ysize - 1; j >= 0; j-- ) { + double L = mask->coeff[j*3]; + double a = mask->coeff[j*3 + 1]; + double b = mask->coeff[j*3 + 2]; + + if( L >= i && L < L_high ) { + L_high = L; + a_high = a; + b_high = b; + } + } + + /* Interpolate. + */ + parm->a_offset[i] = a_low + + (a_high - a_low) * ((i - L_low) / (L_high - L_low)); + parm->b_offset[i] = b_low + + (b_high - b_low) * ((i - L_low) / (L_high - L_low)); + } + + return( 0 ); +} + +#define loop( TYPE ) \ +{ \ + TYPE *p = (TYPE *) in; \ + TYPE *q = (TYPE *) out; \ + \ + for( x = 0; x < width; x++ ) { \ + double L = p[0]; \ + double a = p[1]; \ + double b = p[2]; \ + \ + L = IM_CLIP( 0, L, 100 ); \ + a -= parm->a_offset[(int) L]; \ + b -= parm->b_offset[(int) L]; \ + \ + L = (L + parm->L_offset) * parm->L_scale; \ + L = IM_CLIP( 0, L, 100 ); \ + \ + a *= parm->a_scale; \ + b *= parm->b_scale; \ + \ + q[0] = L; \ + q[1] = a; \ + q[2] = b; \ + \ + p += 3; \ + q += 3; \ + } \ +} + +static void +morph_buffer( float *in, float *out, int width, Params *parm ) +{ + int x; + + switch( parm->in->BandFmt ) { + case IM_BANDFMT_FLOAT: loop( float ); break; + case IM_BANDFMT_DOUBLE: loop( double ); break; + default: assert( 0 ); + } +} + +/* Morph an image. + */ +int +im_lab_morph( IMAGE *in, IMAGE *out, + DOUBLEMASK *mask, + double L_offset, double L_scale, + double a_scale, double b_scale ) +{ + Params *parm; + + /* Recurse for coded images. + */ + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t1 = im_open_local( out, "im_lab_morph:1", "p" ); + IMAGE *t2 = im_open_local( out, "im_lab_morph:2", "p" ); + + if( !t1 || !t2 || + im_LabQ2Lab( in, t1 ) || + im_lab_morph( t1, t2, + mask, L_offset, L_scale, a_scale, b_scale ) || + im_Lab2LabQ( t2, out ) ) + return( -1 ); + + return( 0 ); + } + + if( in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_lab_morph: must be uncoded or IM_CODING_LABQ" ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_FLOAT && in->BandFmt != IM_BANDFMT_DOUBLE ) { + im_errormsg( "im_lab_morph: must be uncoded float or double" ); + return( -1 ); + } + if( in->Bands != 3 ) { + im_errormsg( "im_lab_morph: must be 3 bands" ); + return( -1 ); + } + + if( !(parm = IM_NEW( out, Params )) || + morph_init( parm, + in, out, L_scale, L_offset, mask, a_scale, b_scale ) ) + return( -1 ); + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_LAB; + + if( im_wrapone( in, out, + (im_wrapone_fn) morph_buffer, parm, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/man3/Makefile.am b/libsrc/colour/man3/Makefile.am new file mode 100644 index 00000000..ff5338ac --- /dev/null +++ b/libsrc/colour/man3/Makefile.am @@ -0,0 +1,63 @@ +man_MANS = \ + im_LCh2Lab.3 \ + im_LCh2UCS.3 \ + im_Lab2LCh.3 \ + im_Lab2LabQ.3 \ + im_Lab2LabS.3 \ + im_Lab2UCS.3 \ + im_Lab2XYZ.3 \ + im_XYZ2Yxy.3 \ + im_Yxy2XYZ.3 \ + im_Lab2disp.3 \ + im_LabQ2Lab.3 \ + im_LabQ2LabS.3 \ + im_LabQ2XYZ.3 \ + im_LabQ2disp.3 \ + im_LabQ2disp_build_table.3 \ + im_LabQ2disp_table.3 \ + im_LabS2LabQ.3 \ + im_LabS2Lab.3 \ + im_UCS2LCh.3 \ + im_UCS2Lab.3 \ + im_UCS2XYZ.3 \ + im_XYZ2Lab.3 \ + im_XYZ2UCS.3 \ + im_XYZ2disp.3 \ + im_col_C2Cucs.3 \ + im_col_Ch2ab.3 \ + im_col_Ch2hucs.3 \ + im_col_Chucs2h.3 \ + im_col_Cucs2C.3 \ + im_col_L2Lucs.3 \ + im_col_Lab2XYZ.3 \ + im_col_Lucs2L.3 \ + im_col_XYZ2Lab.3 \ + im_col_XYZ2rgb.3 \ + im_col_ab2Ch.3 \ + im_col_dECMC.3 \ + im_col_display.3 \ + im_col_make_tables_RGB.3 \ + im_col_make_tables_UCS.3 \ + im_col_pythagoras.3 \ + im_col_rgb2XYZ.3 \ + im_lab_morph.3 \ + im_dE_fromLab.3 \ + im_dE00_fromLab.3 \ + im_dECMC_fromLab.3 \ + im_dECMC_fromdisp.3 \ + im_dE_fromXYZ.3 \ + im_dE_fromdisp.3 \ + im_disp2Lab.3 \ + im_disp2XYZ.3 \ + im_XYZ2sRGB.3 \ + im_icc_present.3 \ + im_icc_transform.3 \ + im_icc_import.3 \ + im_icc_import_embedded.3 \ + im_icc_export.3 \ + im_icc_export_depth.3 \ + im_icc_ac2rc.3 \ + im_sRGB2XYZ.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/colour/man3/im_LCh2Lab.3 b/libsrc/colour/man3/im_LCh2Lab.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_LCh2Lab.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_LCh2UCS.3 b/libsrc/colour/man3/im_LCh2UCS.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_LCh2UCS.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_Lab2LCh.3 b/libsrc/colour/man3/im_Lab2LCh.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_Lab2LCh.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_Lab2LabQ.3 b/libsrc/colour/man3/im_Lab2LabQ.3 new file mode 100644 index 00000000..97ddbcbb --- /dev/null +++ b/libsrc/colour/man3/im_Lab2LabQ.3 @@ -0,0 +1,61 @@ +.TH IM_XYZ2disp 3 "2 Decemder 1992" +.SH NAME +im_LabQ2Lab, im_Lab2LabQ, im_LabQ2LabS, im_LabS2LabQ, im_Lab2LabS, im_LabS2Lab \- pack and unpack LABPACK images. +.SH SYNOPSIS +#include +.br +#include + +int im_LabQ2Lab(in, out) +.br +IMAGE *in, *out; + +int im_Lab2LabQ(in, out) +.br +IMAGE *in, *out; + +int im_Lab2LabS(in, out) +.br +IMAGE *in, *out; + +int im_LabS2LabQ(in, out) +.br +IMAGE *in, *out; + +int im_LabS2Lab(in, out) +.br +IMAGE *in, *out; + +int im_LabQ2LabS(in, out) +.br +IMAGE *in, *out; + +.SH DESCRIPTION +These functions pack and unpack LAB images. + +LabQ is Lab packed in to 4 unsigned chars, with the Coding field set to +LABPACK. It counts as a coded type, since most operations will not give the +correct result on an image of this type. This is the MARC image type. Bits +are allocated as 10 for L and 11 for each of a and b. The first three bytes +contain the 8 most significant bits of Lab respectively, the final byte has +2/3/3 bits (MSB on left) of Lab respectively. + +im_LabQ2Lab() and im_Lab2LabQ() convert LABPACK images to three band float +images, scaled to look sensible to humans. This is the most convenient LAB +format for development work, but is rather slow. + +im_LabQ2LabS() and im_LabS2LabQ() convert LABPACK to and from three band +signed short images. L is shifted and masked to be in the range [0,32767], a +and b are shifted and masked to lie in [-32768,32767]. This is the best +computational LAB format, combining precision and speed. Programs such as +conv(1X) and similarity(1X), which can operate directly on LABPACK images, +unpack to LabS for computation. + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_col_XYZ2rgb(3), im_dE_fromdisp(3), im_XYZ2disp(3). +.SH COPYRIGHT +National Gallery, 1990 - 1993 +.SH AUTHOR +J. Cupitt \- 21/7/93 diff --git a/libsrc/colour/man3/im_Lab2LabS.3 b/libsrc/colour/man3/im_Lab2LabS.3 new file mode 100644 index 00000000..9c69e1f5 --- /dev/null +++ b/libsrc/colour/man3/im_Lab2LabS.3 @@ -0,0 +1 @@ +.so man3/im_Lab2LabQ.3 diff --git a/libsrc/colour/man3/im_Lab2UCS.3 b/libsrc/colour/man3/im_Lab2UCS.3 new file mode 100644 index 00000000..df1c0963 --- /dev/null +++ b/libsrc/colour/man3/im_Lab2UCS.3 @@ -0,0 +1,72 @@ +.TH IM_LAB2UCS 3 "2 December 1992" +.SH NAME +im_Lab2UCS, im_LabQ2XYZ, im_UCS2Lab, im_Lab2disp, im_disp2Lab, im_UCS2XYZ, +im_XYZ2UCS \- derived colour space conversion functions +.SH SYNOPSIS +#include +.br +#include + +int im_Lab2UCS(in, out) +.br +IMAGE *in, *out; + +int im_LabQ2XYZ(in, out) +.br +IMAGE *in, *out; + +int im_UCS2Lab(in, out) +.br +IMAGE *in, *out; + +int im_Lab2disp(in, out, display) +.br +IMAGE *in, *out; +.br +struct im_col_display *display; + +int im_disp2Lab(in, out, display) +.br +IMAGE *in, *out; +.br +struct im_col_display *display; + +int im_UCS2XYZ(in, out) +.br +IMAGE *in, *out; + +int im_XYZ2UCS(in, out) +.br +IMAGE *in, *out; + +.SH DESCRIPTION +These functions are built on the basic VIPS colour space transformations as a +convenience for the programmer. See im_Lab2XYZ(3) for an explanation of the +colour spaces and the basic conversion functions. + +im_Lab2UCS(3), for example, is defined as: + + int + im_Lab2UCS( IMAGE *in, IMAGE *out ) + { + IMAGE *t1 = im_open_local( out, + "im_Lab2UCS intermediate", "p" ); + + if( !t1 || + im_Lab2LCh( in, t1 ) || + im_LCh2UCS( t1, out ) ) + return( -1 ); + + return( 0 ); + } + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_col_XYZ2rgb(3), im_dE_fromLab(3), im_LabQ2Lab(3), im_Lab2disp(3). +.SH COPYRIGHT +National Gallery and Birkbeck College, 1990 - 1993 +.SH AUTHOR +K. Martinez \- 2/12/1992 +.br +J. Cupitt \- 21/7/93 diff --git a/libsrc/colour/man3/im_Lab2XYZ.3 b/libsrc/colour/man3/im_Lab2XYZ.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_Lab2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_Lab2disp.3 b/libsrc/colour/man3/im_Lab2disp.3 new file mode 100644 index 00000000..cb1e992d --- /dev/null +++ b/libsrc/colour/man3/im_Lab2disp.3 @@ -0,0 +1 @@ +.so man3/im_Lab2UCS.3 diff --git a/libsrc/colour/man3/im_LabQ2Lab.3 b/libsrc/colour/man3/im_LabQ2Lab.3 new file mode 100644 index 00000000..9c69e1f5 --- /dev/null +++ b/libsrc/colour/man3/im_LabQ2Lab.3 @@ -0,0 +1 @@ +.so man3/im_Lab2LabQ.3 diff --git a/libsrc/colour/man3/im_LabQ2LabS.3 b/libsrc/colour/man3/im_LabQ2LabS.3 new file mode 100644 index 00000000..9c69e1f5 --- /dev/null +++ b/libsrc/colour/man3/im_LabQ2LabS.3 @@ -0,0 +1 @@ +.so man3/im_Lab2LabQ.3 diff --git a/libsrc/colour/man3/im_LabQ2XYZ.3 b/libsrc/colour/man3/im_LabQ2XYZ.3 new file mode 100644 index 00000000..cb1e992d --- /dev/null +++ b/libsrc/colour/man3/im_LabQ2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_Lab2UCS.3 diff --git a/libsrc/colour/man3/im_LabQ2disp.3 b/libsrc/colour/man3/im_LabQ2disp.3 new file mode 100644 index 00000000..0743fe39 --- /dev/null +++ b/libsrc/colour/man3/im_LabQ2disp.3 @@ -0,0 +1,34 @@ +.TH IM_LABQ2DISP 3 "2 Decemder 1997" +.SH NAME +im_LabQ2disp, im_LabQ2disp_build_table, im_LabQ2disp_table \- convert LabQ to display rgb quickly and badly +.SH SYNOPSIS +#include +.br +#include + +int im_LabQ2disp( IMAGE *in, IMAGE *out, struct im_col_display *d ); + +void *im_LabQ2disp_build_table( IMAGE *out, struct im_col_display *d ); + +int im_LabQ2disp_table( IMAGE *in, IMAGE *out, void *table ); + +.SH DESCRIPTION +These functions convert LabQ images to displayable RGB as quickly as possible. + +im_LabQ2disp() converts in to out using the display profile d. It has to build +a large lookup table, so takes a while to start. + +im_LabQ2disp_build_table() is just the table-build phase of im_LabQ2disp(). It +returns a handle to the built table (or NULL for error). The memory for the +table is allocated local to out (ie is freed when out is closed). + +im_LabQ2disp_table() converts in to out using the supplied table. + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_LabQ2Lab(3), im_Lab2XYZ(3), im_XYZ2disp(3) +.SH COPYRIGHT +National Gallery, 1990 - 1997 +.SH AUTHOR +J. Cupitt \- 21/7/97 diff --git a/libsrc/colour/man3/im_LabQ2disp_build_table.3 b/libsrc/colour/man3/im_LabQ2disp_build_table.3 new file mode 100644 index 00000000..ce04ce4a --- /dev/null +++ b/libsrc/colour/man3/im_LabQ2disp_build_table.3 @@ -0,0 +1 @@ +.so man3/im_LabQ2disp.3 diff --git a/libsrc/colour/man3/im_LabQ2disp_table.3 b/libsrc/colour/man3/im_LabQ2disp_table.3 new file mode 100644 index 00000000..ce04ce4a --- /dev/null +++ b/libsrc/colour/man3/im_LabQ2disp_table.3 @@ -0,0 +1 @@ +.so man3/im_LabQ2disp.3 diff --git a/libsrc/colour/man3/im_LabS2Lab.3 b/libsrc/colour/man3/im_LabS2Lab.3 new file mode 100644 index 00000000..9c69e1f5 --- /dev/null +++ b/libsrc/colour/man3/im_LabS2Lab.3 @@ -0,0 +1 @@ +.so man3/im_Lab2LabQ.3 diff --git a/libsrc/colour/man3/im_LabS2LabQ.3 b/libsrc/colour/man3/im_LabS2LabQ.3 new file mode 100644 index 00000000..9c69e1f5 --- /dev/null +++ b/libsrc/colour/man3/im_LabS2LabQ.3 @@ -0,0 +1 @@ +.so man3/im_Lab2LabQ.3 diff --git a/libsrc/colour/man3/im_UCS2LCh.3 b/libsrc/colour/man3/im_UCS2LCh.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_UCS2LCh.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_UCS2Lab.3 b/libsrc/colour/man3/im_UCS2Lab.3 new file mode 100644 index 00000000..cb1e992d --- /dev/null +++ b/libsrc/colour/man3/im_UCS2Lab.3 @@ -0,0 +1 @@ +.so man3/im_Lab2UCS.3 diff --git a/libsrc/colour/man3/im_UCS2XYZ.3 b/libsrc/colour/man3/im_UCS2XYZ.3 new file mode 100644 index 00000000..cb1e992d --- /dev/null +++ b/libsrc/colour/man3/im_UCS2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_Lab2UCS.3 diff --git a/libsrc/colour/man3/im_XYZ2Lab.3 b/libsrc/colour/man3/im_XYZ2Lab.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_XYZ2Lab.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_XYZ2UCS.3 b/libsrc/colour/man3/im_XYZ2UCS.3 new file mode 100644 index 00000000..cb1e992d --- /dev/null +++ b/libsrc/colour/man3/im_XYZ2UCS.3 @@ -0,0 +1 @@ +.so man3/im_Lab2UCS.3 diff --git a/libsrc/colour/man3/im_XYZ2Yxy.3 b/libsrc/colour/man3/im_XYZ2Yxy.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_XYZ2Yxy.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_XYZ2disp.3 b/libsrc/colour/man3/im_XYZ2disp.3 new file mode 100644 index 00000000..3bfe3879 --- /dev/null +++ b/libsrc/colour/man3/im_XYZ2disp.3 @@ -0,0 +1,144 @@ +.TH IM_XYZ2disp 3 "2 Decemder 1992" +.SH NAME +im_XYZ2disp, im_disp2XYZ, im_Lab2XYZ, im_XYZ2Lab, im_XYZ2Yxy, im_Yxy2XYZ, +im_XYZ2sRGB, im_sRGB2XYZ, +im_Lab2LCh, im_LCh2Lab, im_LCh2UCS, im_UCS2LCh \- convert images between +various colour spaces +.SH SYNOPSIS +#include +.br +#include + +int im_XYZ2disp(in, out, display) +.br +IMAGE *in, *out; +.br +struct im_col_display *display; + +int im_disp2XYZ(in, out, display) +.br +IMAGE *in, *out; +.br +struct im_col_display *display; + +int im_Lab2XYZ(in, out) +.br +IMAGE *in, *out; + +int im_XYZ2Lab(in, out) +.br +IMAGE *in, *out; + +int im_XYZ2Yxy(in, out) +.br +IMAGE *in, *out; + +int im_Yxy2XYZ(in, out) +.br +IMAGE *in, *out; + +int im_XYZ2sRGB(in, out) +.br +IMAGE *in, *out; + +int im_sRGB2XYZ(in, out) +.br +IMAGE *in, *out; + +int im_Lab2LCh(in, out) +.br +IMAGE *in, *out; + +int im_LCh2Lab(in, out) +.br +IMAGE *in, *out; + +int im_LCh2UCS(in, out) +.br +IMAGE *in, *out; + +int im_UCS2LCh(in, out) +.br +IMAGE *in, *out; + +.SH DESCRIPTION +Functions to convert images between the different colour spaces supported by +VIPS: RGB, sRGB, XYZ, Yxy, Lab, LCh and UCS. RGB and sRGB are three band uchar +files. XYZ, Lab, LCh and UCS are three band float files. + +These are the basic conversion routines provided by VIPS. Other conversions, +such as im_Lab2disp(3), are built by composing these functions and are provided +as a convenience to the programmer. + +The VIPS colour spaces inter-convert as follows: + + +------- sRGB + | + LabQ ----- Lab ----- XYZ ----- RGB + | | | + | | +------- Yxy + | | + | +------- LCh ----- UCS + | + +-------- LabS + +The colour spaces are: + +LabQ --- This is the principal VIPS colorimetric storage format. See +im_LabQ2Lab(3) for an explanation. You cannot perform calculations on LabQ +images. They are for storage only. + +LabS --- This format represents coordinates in CIE Lab space as 16-bit +integers. It is the best format for computation, being relatively compact, +quick, and accurate. Colour values expressed in this way are hard to +visualise. See the page for im_LabQ2LabS(). + +Lab --- Coordinates in CIE Lab space are represented as float values in the +usual range. This is the easiest format for general work: adding 50 to the L +channel, for example, has the expected result. + +XYZ --- CIE XYZ colour space. + +Yxy --- CIE Yxy colour space. + +RGB --- This format is compatible with the RGB colour systems used in other +packages. If you want to export your image to a PC, for example, convert your +colorimetric image to RGB, then turn it to TIFF with vips2TIFF(1). You need to +supply a structure which characterises your display. See the manual page for +im_col_XYZ2rgb(3) for hints on these guys. + +sRGB --- This is a standard RGB, as defined by: + + http://www.color.org/contrib/sRGB.html + +it's handy for carrying colour information through JPEG compressors, for +example. + +LCh --- Like Lab, but the rectangular ab coordinates are replaced with the +polar Ch (Chroma and hue) coordinates. Hue angles are expressed in degrees. + +UCS --- A colour space based on the CMC(1:1) colour difference measurement. +This is a highly uniform colour space, much better than Lab for expressing +small differences. Conversions to and from UCS are extremely slow. + +These conversions set the Type field in the image header to LABQ, LABS, LAB, +XYZ, RGB, sRGB, LCH and UCS respectively. The Type field is for user +information only --- no function reads the Type field to determine its +behaviour. Visualisation programs, such as ip(1), use the Type field to help +present the information to the user. + +All VIPS colour spaces are based around D65. + +VIPS has functions for finding colour difference images. See +im_dE_fromLab(3). + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_col_XYZ2rgb(3), im_dE_fromLab(3), im_LabQ2Lab(3), im_Lab2disp(3). +.SH COPYRIGHT +National Gallery and Birkbeck College, 1990 - 1993 +.SH AUTHOR +K. Martinez \- 2/12/1992 +.br +J. Cupitt \- 21/7/93 diff --git a/libsrc/colour/man3/im_XYZ2sRGB.3 b/libsrc/colour/man3/im_XYZ2sRGB.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_XYZ2sRGB.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_Yxy2XYZ.3 b/libsrc/colour/man3/im_Yxy2XYZ.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_Yxy2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_col_C2Cucs.3 b/libsrc/colour/man3/im_col_C2Cucs.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_C2Cucs.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_Ch2ab.3 b/libsrc/colour/man3/im_col_Ch2ab.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_Ch2ab.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_Ch2hucs.3 b/libsrc/colour/man3/im_col_Ch2hucs.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_Ch2hucs.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_Chucs2h.3 b/libsrc/colour/man3/im_col_Chucs2h.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_Chucs2h.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_Cucs2C.3 b/libsrc/colour/man3/im_col_Cucs2C.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_Cucs2C.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_L2Lucs.3 b/libsrc/colour/man3/im_col_L2Lucs.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_L2Lucs.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_Lab2XYZ.3 b/libsrc/colour/man3/im_col_Lab2XYZ.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_Lab2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_Lucs2L.3 b/libsrc/colour/man3/im_col_Lucs2L.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_Lucs2L.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_XYZ2Lab.3 b/libsrc/colour/man3/im_col_XYZ2Lab.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_XYZ2Lab.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_XYZ2rgb.3 b/libsrc/colour/man3/im_col_XYZ2rgb.3 new file mode 100644 index 00000000..6c273ff2 --- /dev/null +++ b/libsrc/colour/man3/im_col_XYZ2rgb.3 @@ -0,0 +1,145 @@ +.TH IM_COL_XYZ2RGB 3 "2 December 1992" +.SH NAME +im_col_Lab2LCh, im_col_LCh2ab, im_col_Lab2XYZ, im_col_XYZ2Lab, +im_col_pythagoras, im_col_display, im_col_XYZ2rgb, im_col_rgb2XYZ, +im_col_L2Lucs, im_col_Lucs2L, im_col_C2Cucs, im_col_Cucs2C, im_col_Ch2hucs, +im_col_Chucs2h, im_col_make_tables_UCS, im_col_dECMC +\- colour space conversion +.SH SYNOPSIS +#include +.br +#include + + +int im_col_ab2Ch( a, b, C, h ) +.br +float a, b, *C, *h; + + +int im_col_Ch2ab( C, h, a, b ) +.br +float C, h, *a, *b; + +int im_col_Lab2XYZ( L, a, b, X, Y, Z ) +.br +float L, a, b, *X, *Y, *Z; + +int im_col_XYZ2Lab( X, Y, Z, L, a, b ) +.br +float X, Y, Z, *L, *a, *b; + +float im_col_pythagoras( L1, a1, b1, L2, a2, b2 ) +.br +float L1, a1, b1, L2, a2, b2; + +extern struct im_col_display *im_col_displays[]; + +struct im_col_tab_disp *im_col_make_tables_RGB( im, display ) +.br +IMAGE *im; +.br +struct im_col_display *display; + +int im_col_XYZ2rgb( display, table, X, Y, Z, r, g, b, oflow ) +.br +struct im_col_display *display; +.br +struct im_col_tab_disp *table; +.br +float X, Y, Z; +.br +int *r, *g, *b; +.br +int *oflow; + + +int im_col_rgb2XYZ( display, table, r, g, b, X, Y, Z ) +.br +struct im_col_display *display; +.br +struct im_col_tab_disp *table; +.br +int r, g, b; +.br +float *X, *Y, *Z; + +float im_col_L2Lucs( L ) +.br +float L; + +float im_col_Lucs2L( Lucs ) +.br +float Lucs; + +float im_col_C2Cucs( C ) +.br +float C; + +float im_col_Cucs2C( Cucs ) +.br +float Cucs; + +float im_col_Ch2hucs( C, h ) +.br +float h, C; + +float im_col_Chucs2h( C, hucs ) +.br +float hucs, C; + +void im_col_make_tables_UCS( void ) + +float im_col_dECMC( L1, a1, b1, L2, a2, b2 ) +.br +float L1, a1, b1, L2, a2, b2; + +.SH DESCRIPTION +Colour space conversion. +These functions convert colour values between four different formats: XYZ +(float), Lab (float), UCS (float), and RGB (unsigned char) +displayable. Additionally, functions are provided to move from (a,b)-style +rectangular colour coordinates to (C,h)-style coordinates. h is always +in degrees. + +UCS is a colour space derived from the CMC(1:1) equations. There is no easy +analytical conversion from UCS to Lab, so look-up tables are used. These have +to be built with a call to im_col_make_tables_UCS(). Once built, these +tables are shared by all UCS functions. You may call im_col_make_tables_UCS() +many times - tables are only built on the first call. + +im_col_pythagoras() returns the pythagoran distance between two points in a +colour space. It can be used for finding CIELAB delta E's. im_col_dECMC() +returns the colour difference between two LAB points in CMC(1:1). + +An im_col_display structure characterises a CRT screen (see ). +You can make up your own (if you can find a TV analyser), or use one of the +structures provied by VIPS in the NULL-terminated array im_col_displays[]. See +the source for disp2XYZ(1) for ideas on extracting a display struct from this +list. + +im_make_tables_RGB(3) has a display type as argument, and returns a pointer +to the structure im_col_tab_RGB. This latter contains the matrices to go from +XYZ to luminances (and back), and the tables to go from the luminances (in r, +g, b) to the effective signal values to be applied to the monitor input (and +back). The function returns NULL on error. The IMAGE argument is passed on to +im_malloc() to make the space required for the tables. Pass either NULL (if +you need to free the memory yourself) or an IMAGE descriptor (if you want the +memory to be freed automatically when that descriptor is closed). + +im_col_XYZ2rgb() takes a display, a look-up table and an XYZ coordinate are +returns three values in the range 0-255. The extra value oflow is set to 0 if +the specified XYZ position aflls within the display gamut, and to 1 if the +point lies outside the gamut. im_col_rgb2XYZ() is the reverse transformation. +.SH RETURN VALUE +The functions (usually) return 0 on success and -1 on error. +.SH SEE\ ALSO +.nf +im_XYZ2disp(3), im_dE_fromdisp(3). +.SH COPYRIGHT +National Gallery, 1990-1993. +.SH AUTHOR +D. Saunders \- 1988 +.br +J.Ph. Laurent \- 2/12/1992 +.br +J.Cupitt \- 21/7/93 diff --git a/libsrc/colour/man3/im_col_ab2Ch.3 b/libsrc/colour/man3/im_col_ab2Ch.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_ab2Ch.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_dECMC.3 b/libsrc/colour/man3/im_col_dECMC.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_dECMC.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_display.3 b/libsrc/colour/man3/im_col_display.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_display.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_make_tables_RGB.3 b/libsrc/colour/man3/im_col_make_tables_RGB.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_make_tables_RGB.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_make_tables_UCS.3 b/libsrc/colour/man3/im_col_make_tables_UCS.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_make_tables_UCS.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_pythagoras.3 b/libsrc/colour/man3/im_col_pythagoras.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_pythagoras.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_rgb2XYZ.3 b/libsrc/colour/man3/im_col_rgb2XYZ.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_rgb2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_dE00_fromLab.3 b/libsrc/colour/man3/im_dE00_fromLab.3 new file mode 100644 index 00000000..0f335f81 --- /dev/null +++ b/libsrc/colour/man3/im_dE00_fromLab.3 @@ -0,0 +1 @@ +.so man3/im_dE_fromLab.3 diff --git a/libsrc/colour/man3/im_dECMC_fromLab.3 b/libsrc/colour/man3/im_dECMC_fromLab.3 new file mode 100644 index 00000000..0f335f81 --- /dev/null +++ b/libsrc/colour/man3/im_dECMC_fromLab.3 @@ -0,0 +1 @@ +.so man3/im_dE_fromLab.3 diff --git a/libsrc/colour/man3/im_dECMC_fromdisp.3 b/libsrc/colour/man3/im_dECMC_fromdisp.3 new file mode 100644 index 00000000..0d3cc14a --- /dev/null +++ b/libsrc/colour/man3/im_dECMC_fromdisp.3 @@ -0,0 +1 @@ +.so man3/im_dE_fromdisp.3 diff --git a/libsrc/colour/man3/im_dE_fromLab.3 b/libsrc/colour/man3/im_dE_fromLab.3 new file mode 100644 index 00000000..e8c0291c --- /dev/null +++ b/libsrc/colour/man3/im_dE_fromLab.3 @@ -0,0 +1,44 @@ +.TH im_dE_fromLab 3 "2 December 1992" +.SH NAME +im_dE_fromLab, im_dECMC_fromLab, im_dE00_fromLab \- calculate colour differences +.SH SYNOPSIS +#include +.br +#include + +int im_dE_fromLab(in1, in2, out) +.br +IMAGE *in1, *in2, *out; + +int im_dECMC_fromLab(in1, in2, out) +.br +IMAGE *in1, *in2, *out; + +int im_dE00_fromLab(in1, in2, out) +.br +IMAGE *in1, *in2, *out; + +.SH DESCRIPTION +Given a pair of images in Lab format, these functions calculate CIE76, +CMC(1:1) and CIEDE2000 colour difference images. These are the basic colour +difference +calculators in VIPS --- other colour difference functions, such as +.B im_dE_fromXYZ(3), +are provided as a convenience to the programmer. + +See +.B im_Lab2XYZ(3) +for an explanation of the various colour spaces VIPS +supports and how to convert between them. + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_col_XYZ2rgb(3), im_XYZ2rgb(3), im_dE_fromXYZ(3). +.SH COPYRIGHT +National Gallery, 1990 - 1993 +.SH AUTHOR +J.Ph. Laurent \- 2/12/1992 +.br +J. Cupitt \- 21/7/93 + diff --git a/libsrc/colour/man3/im_dE_fromXYZ.3 b/libsrc/colour/man3/im_dE_fromXYZ.3 new file mode 100644 index 00000000..0d3cc14a --- /dev/null +++ b/libsrc/colour/man3/im_dE_fromXYZ.3 @@ -0,0 +1 @@ +.so man3/im_dE_fromdisp.3 diff --git a/libsrc/colour/man3/im_dE_fromdisp.3 b/libsrc/colour/man3/im_dE_fromdisp.3 new file mode 100644 index 00000000..02ae7763 --- /dev/null +++ b/libsrc/colour/man3/im_dE_fromdisp.3 @@ -0,0 +1,40 @@ +.TH IM_DE_FROMDISP 3 "2 December 1992" +.SH NAME +im_dE_fromdisp, im_dE_fromXYZ, im_dECMC_fromdisp \- derived colour difference +functions +.SH SYNOPSIS +#include +.br +#include + +im_dE_fromdisp( in1, in2, out, display ) +.br +IMAGE *in1, *in2, *out; +.br +struct im_col_display *display; + +int im_dE_fromXYZ(in, out) +.br +IMAGE *in, *out; + +im_dECMC_fromdisp( in1, in2, out, display ) +.br +IMAGE *in1, *in2, *out; +.br +struct im_col_display *display; + +.SH DESCRIPTION +These functions are built on the basic VIPS colour difference measurements as a +convenience for the programmer. See im_dE_fromLab(3) for an explanation of the +colour difference metrics and the basic comparision functions. + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_col_XYZ2rgb(3), im_dE_fromLab(3), im_LabQ2Lab(3), im_Lab2disp(3). +.SH COPYRIGHT +National Gallery and Birkbeck College, 1990 - 1993 +.SH AUTHOR +K. Martinez \- 2/12/1992 +.br +J. Cupitt \- 21/7/93 diff --git a/libsrc/colour/man3/im_disp2Lab.3 b/libsrc/colour/man3/im_disp2Lab.3 new file mode 100644 index 00000000..cb1e992d --- /dev/null +++ b/libsrc/colour/man3/im_disp2Lab.3 @@ -0,0 +1 @@ +.so man3/im_Lab2UCS.3 diff --git a/libsrc/colour/man3/im_disp2XYZ.3 b/libsrc/colour/man3/im_disp2XYZ.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_disp2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_icc_ac2rc.3 b/libsrc/colour/man3/im_icc_ac2rc.3 new file mode 100644 index 00000000..4e5ea9dc --- /dev/null +++ b/libsrc/colour/man3/im_icc_ac2rc.3 @@ -0,0 +1 @@ +.so man3/im_icc_transform.3 diff --git a/libsrc/colour/man3/im_icc_export.3 b/libsrc/colour/man3/im_icc_export.3 new file mode 100644 index 00000000..4e5ea9dc --- /dev/null +++ b/libsrc/colour/man3/im_icc_export.3 @@ -0,0 +1 @@ +.so man3/im_icc_transform.3 diff --git a/libsrc/colour/man3/im_icc_export_depth.3 b/libsrc/colour/man3/im_icc_export_depth.3 new file mode 100644 index 00000000..4e5ea9dc --- /dev/null +++ b/libsrc/colour/man3/im_icc_export_depth.3 @@ -0,0 +1 @@ +.so man3/im_icc_transform.3 diff --git a/libsrc/colour/man3/im_icc_import.3 b/libsrc/colour/man3/im_icc_import.3 new file mode 100644 index 00000000..4e5ea9dc --- /dev/null +++ b/libsrc/colour/man3/im_icc_import.3 @@ -0,0 +1 @@ +.so man3/im_icc_transform.3 diff --git a/libsrc/colour/man3/im_icc_import_embedded.3 b/libsrc/colour/man3/im_icc_import_embedded.3 new file mode 100644 index 00000000..4e5ea9dc --- /dev/null +++ b/libsrc/colour/man3/im_icc_import_embedded.3 @@ -0,0 +1 @@ +.so man3/im_icc_transform.3 diff --git a/libsrc/colour/man3/im_icc_present.3 b/libsrc/colour/man3/im_icc_present.3 new file mode 100644 index 00000000..4e5ea9dc --- /dev/null +++ b/libsrc/colour/man3/im_icc_present.3 @@ -0,0 +1 @@ +.so man3/im_icc_transform.3 diff --git a/libsrc/colour/man3/im_icc_transform.3 b/libsrc/colour/man3/im_icc_transform.3 new file mode 100644 index 00000000..24eae259 --- /dev/null +++ b/libsrc/colour/man3/im_icc_transform.3 @@ -0,0 +1,118 @@ +.TH IM_ICC_*() 3 "April 2002" +.SH NAME +im_icc_present, im_icc_transform, im_icc_import, im_icc_import_embedded, +im_icc_export, +im_icc_export_depth \- transform images using ICC profiles +.SH SYNOPSIS +#include +.br +#include + +#define VIPS_INTENT_PERCEPTUAL (0) +.br +#define VIPS_INTENT_RELATIVE_COLORIMETRIC (1) +.br +#define VIPS_INTENT_SATURATION (2) +.br +#define VIPS_INTENT_ABSOLUTE_COLORIMETRIC (3) + +int +.br +im_icc_present( void ) +.br +int +.br +im_icc_transform( IMAGE *in, IMAGE *out, +.br + const char *input_profile_filename, +.br + const char *output_profile_filename, +.br + int intent ) + +int +.br +im_icc_import( IMAGE *in, IMAGE *out, +.br + const char *input_profile_filename, int intent ) + +int +.br +im_icc_import_embedded( IMAGE *in, IMAGE *out, +.br + int intent ) + +int +.br +im_icc_export_depth( IMAGE *in, IMAGE *out, int depth, +.br + const char *output_profile_filename, int intent ) + +int +.br +im_icc_export( IMAGE *in, IMAGE *out, +.br + const char *output_profile_filename, int intent ) + +int +.br +im_icc_ac2rc( IMAGE *in, IMAGE *out, +.br + const char *profile_filename ) + +.SH DESCRIPTION +.B im_icc_present(3) +returns non-zero if there is an ICC library available. Calls to the other +VIPS ICC functions will all fail with an error message if there is no library. + +.B im_icc_transform(3) +maps between two images using an input and output profile and an intent. The +input image must have a format matching the input profile (eg. 4 bands for a +CMYK profile). The output image will have a form matching the output profile +(eg. 3 bands for an RGB output profile). The input image must be 8 or 16 bit +unsigned integer. The output image is always 8 bit unsigned int. The output +profile is attached to the output image under the name +.B "icc-profile-data". +Functions like +.B im_vips2jpeg(3) +will then attach the profile to the files they create. + +.B im_icc_import(3) +takes an image to D50 Lab float (profile interconnect space) from device space. +The input image must match the format expected by the profile: for example, a +printer profile will usually need 4 band CMYK data. The input image must be 8 +or 16 bit unsigned integer. + +.B im_icc_import_embedded(3) +takes an image to D50 Lab float (profile interconnect space) from device +space, using the profile embedded in the image. If there is no embedded +profile, an error is returned. Test for the presence of a profile with +.B im_header_get_type(3). + +.B im_icc_export_depth(3) +takes an image from D50 Lab float back to device space. The output image will +match the format of the profile: for example, a screen profile will write 3 +band RGB data. The output image biut depth can be set to 8 or 16 with the +depth parameter. The profile is attached to the output image under +the name +.B "icc-profile-data". +Functions like +.B im_vips2jpeg(3) +will then attach the profile to the files they create. + +.B im_icc_export(3) +behaves just as +.B im_icc_export_depth(), +but with depth always 8 bit. + +.B im_icc_ac2rc(3) +converts an image from D50 absolute to media relative colorimetry using the +media white point found in the ICC profile. + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_LabQ2Lab(3), im_Lab2XYZ(3). +.SH COPYRIGHT +The National Gallery + diff --git a/libsrc/colour/man3/im_lab_morph.3 b/libsrc/colour/man3/im_lab_morph.3 new file mode 100644 index 00000000..ac5563ba --- /dev/null +++ b/libsrc/colour/man3/im_lab_morph.3 @@ -0,0 +1,44 @@ +.TH IM_LAB_MORPH 3 "8 March 2001" +.SH NAME +im_lab_morph \- calculate colour differences +.SH SYNOPSIS +#include + +int im_lab_morph( IMAGE *in, IMAGE *out, +.br + DOUBLEMASK *mask, +.br + double L_offset, double L_scale, +.br + double a_scale, double b_scale ) + +.SH DESCRIPTION +This function tweaks the colour in an LAB image. It's useful for +making a 'tweaked' image for sending to a colour printer. + +It performs three corrections: first, it straightens the neutral axis (this is +useful if your printer tends to tint shadows slightly red, for example); it +moves L* by adding an offset and then multiplying by a scale (useful if your +printer thinks in relative colorimetry, for example), and finally scales a* +and b* by a factor (useful if your printer desaturates to avoid gamut +clipping). + +The neutral axis straightening is specified as a DOUBLEMASK containing L*, a* +and b* readings taken from a print of a neutral greyscale (one with a* and b* +zero). For example: + + 3 4 + 14.23 4.8 -3.95 + 18.74 2.76 -2.62 + 23.46 1.4 -1.95 + 27.53 1.76 -2.01 + +This is interpolated to make an a/b offset for each input value of L*. The top +and tail are interpolated towards [100,0,0] and [0,0,0]. Entries in the mask +can be in any order. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH COPYRIGHT +National Gallery, 2001 + diff --git a/libsrc/colour/man3/im_sRGB2XYZ.3 b/libsrc/colour/man3/im_sRGB2XYZ.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_sRGB2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/conversion/Makefile.am b/libsrc/conversion/Makefile.am new file mode 100644 index 00000000..0f16c537 --- /dev/null +++ b/libsrc/conversion/Makefile.am @@ -0,0 +1,62 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libconversion.la + +libconversion_la_SOURCES = \ + im_bernd.c \ + im_vips2tiff.c \ + im_tiff2vips.c \ + conver_dispatch.c \ + dbh.h \ + im_bandjoin.c \ + im_analyze2vips.c \ + im_black.c \ + im_c2amph.c \ + im_c2rect.c \ + im_c2imag.c \ + im_c2ps.c \ + im_c2real.c \ + im_clip.c \ + im_copy.c \ + im_csv2vips.c \ + im_vips2csv.c \ + im_extract.c \ + im_exr2vips.c \ + im_falsecolour.c \ + im_fliphor.c \ + im_flipver.c \ + im_gbandjoin.c \ + im_insert.c \ + im_jpeg2vips.c \ + im_lrjoin.c \ + im_magick2vips.c \ + im_mask2vips.c \ + im_msb.c \ + im_png2vips.c \ + im_ppm2vips.c \ + im_recomb.c \ + im_replicate.c \ + im_grid.c \ + im_raw2vips.c \ + im_ri2c.c \ + im_rightshift_size.c \ + im_rot180.c \ + im_rot270.c \ + im_rot90.c \ + im_scale.c \ + im_scaleps.c \ + im_slice.c \ + im_subsample.c \ + im_system.c \ + im_print.c \ + im_tbjoin.c \ + im_tile_cache.c \ + im_text.c \ + im_thresh.c \ + im_vips2mask.c \ + im_vips2jpeg.c \ + im_vips2ppm.c \ + im_vips2png.c \ + im_zoom.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/conversion/conver_dispatch.c b/libsrc/conversion/conver_dispatch.c new file mode 100644 index 00000000..089d7d5d --- /dev/null +++ b/libsrc/conversion/conver_dispatch.c @@ -0,0 +1,1964 @@ +/* VIPS function dispatch tables for conversion. + * + * J. Cupitt, 8/4/93. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +print_vec( im_object *argv ) +{ + const char *message = argv[0]; + char **out = (char **) &argv[1]; + + if( im_print( message ) ) + return( -1 ); + *out = im_strdup( NULL, "printed" ); + + return( 0 ); +} + +static im_arg_desc print_arg_types[] = { + IM_INPUT_STRING( "message" ), + IM_OUTPUT_STRING( "result" ) +}; + +static im_function print_desc = { + "im_print", /* Name */ + "print string to stdout", /* Description */ + 0, /* Flags */ + print_vec, /* Dispatch function */ + IM_NUMBER( print_arg_types ), /* Size of arg list */ + print_arg_types /* Arg list */ +}; + +static int +system_vec( im_object *argv ) +{ + IMAGE *in = argv[0]; + char *cmd = argv[1]; + char **out = (char **) &argv[2]; + + if( im_system( in, cmd, out ) ) + return( -1 ); + + return( 0 ); +} + +static im_arg_desc system_arg_types[] = { + IM_INPUT_IMAGE( "im" ), + IM_INPUT_STRING( "command" ), + IM_OUTPUT_STRING( "output" ) +}; + +static im_function system_desc = { + "im_system", /* Name */ + "run command on image", /* Description */ + 0, /* Flags */ + system_vec, /* Dispatch function */ + IM_NUMBER( system_arg_types ), /* Size of arg list */ + system_arg_types /* Arg list */ +}; + +static int +subsample_vec( im_object *argv ) +{ + IMAGE *in = argv[0]; + IMAGE *out = argv[1]; + int xsh = *((int *) argv[2]); + int ysh = *((int *) argv[3]); + + if( im_subsample( in, out, xsh, ysh ) ) + return( -1 ); + + return( 0 ); +} + +static im_arg_desc subsample_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xshrink" ), + IM_INPUT_INT( "yshrink" ) +}; + +static im_function subsample_desc = { + "im_subsample", /* Name */ + "subsample image by integer factors", /* Description */ + IM_FN_PIO, /* Flags */ + subsample_vec, /* Dispatch function */ + IM_NUMBER( subsample_args ), /* Size of arg list */ + subsample_args /* Arg list */ +}; + +/* Args to im_bernd. + */ +static im_arg_desc bernd_args[] = { + IM_INPUT_STRING( "tiffname" ), + IM_INPUT_INT( "left" ), + IM_INPUT_INT( "top" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ) +}; + +/* Call im_bernd via arg vector. + */ +static int +bernd_vec( im_object *argv ) +{ + char *name = argv[0]; + int left = *((int *) argv[1]); + int top = *((int *) argv[2]); + int width = *((int *) argv[3]); + int height = *((int *) argv[4]); + + return( im_bernd( name, left, top, width, height ) ); +} + +/* Description of im_bernd. + */ +static im_function bernd_desc = { + "im_bernd", /* Name */ + "extract from pyramid as jpeg", /* Description */ + 0, /* Flags */ + bernd_vec, /* Dispatch function */ + IM_NUMBER( bernd_args ), /* Size of arg list */ + bernd_args /* Arg list */ +}; + +/* Args to im_extract. + */ +static im_arg_desc extract_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "left" ), + IM_INPUT_INT( "top" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ), + IM_INPUT_INT( "band" ) +}; + +/* Call im_extract via arg vector. + */ +static int +extract_vec( im_object *argv ) +{ + IMAGE_BOX box; + + box.xstart = *((int *) argv[2]); + box.ystart = *((int *) argv[3]); + box.xsize = *((int *) argv[4]); + box.ysize = *((int *) argv[5]); + box.chsel = *((int *) argv[6]); + + return( im_extract( argv[0], argv[1], &box ) ); +} + +/* Description of im_extract. + */ +static im_function extract_desc = { + "im_extract", /* Name */ + "extract area/band", /* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + extract_vec, /* Dispatch function */ + IM_NUMBER( extract_args ), /* Size of arg list */ + extract_args /* Arg list */ +}; + +/* Args to im_extract_area. + */ +static im_arg_desc extract_area_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "left" ), + IM_INPUT_INT( "top" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ) +}; + +/* Call im_extract_area via arg vector. + */ +static int +extract_area_vec( im_object *argv ) +{ + int x = *((int *) argv[2]); + int y = *((int *) argv[3]); + int w = *((int *) argv[4]); + int h = *((int *) argv[5]); + + return( im_extract_area( argv[0], argv[1], x, y, w, h ) ); +} + +/* Description of im_extract_area. + */ +static im_function extract_area_desc = { + "im_extract_area", /* Name */ + "extract area", /* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + extract_area_vec, /* Dispatch function */ + IM_NUMBER( extract_area_args ), /* Size of arg list */ + extract_area_args /* Arg list */ +}; + +/* Args to im_extract_bands. + */ +static im_arg_desc extract_bands_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "band" ), + IM_INPUT_INT( "nbands" ), +}; + +/* Call im_extract_bands via arg vector. + */ +static int +extract_bands_vec( im_object *argv ) +{ + int chsel = *((int *) argv[2]); + int nbands = *((int *) argv[3]); + + return( im_extract_bands( argv[0], argv[1], chsel, nbands ) ); +} + +/* Description of im_extract_bands. + */ +static im_function extract_bands_desc = { + "im_extract_bands", /* Name */ + "extract several bands", /* Description */ + IM_FN_PIO, /* Flags */ + extract_bands_vec, /* Dispatch function */ + IM_NUMBER( extract_bands_args ),/* Size of arg list */ + extract_bands_args /* Arg list */ +}; + +/* Args to im_extract_band. + */ +static im_arg_desc extract_band_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "band" ) +}; + +/* Call im_extract_band via arg vector. + */ +static int +extract_band_vec( im_object *argv ) +{ + int chsel = *((int *) argv[2]); + + return( im_extract_band( argv[0], argv[1], chsel ) ); +} + +/* Description of im_extract_band. + */ +static im_function extract_band_desc = { + "im_extract_band", /* Name */ + "extract band", /* Description */ + IM_FN_PIO, /* Flags */ + extract_band_vec, /* Dispatch function */ + IM_NUMBER( extract_band_args ), /* Size of arg list */ + extract_band_args /* Arg list */ +}; + +/* Args to im_extract_areabands. + */ +static im_arg_desc extract_areabands_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "left" ), + IM_INPUT_INT( "top" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ), + IM_INPUT_INT( "band" ), + IM_INPUT_INT( "nbands" ) +}; + +/* Call im_extract_areabands via arg vector. + */ +static int +extract_areabands_vec( im_object *argv ) +{ + int left = *((int *) argv[2]); + int top = *((int *) argv[3]); + int width = *((int *) argv[4]); + int height = *((int *) argv[5]); + int band = *((int *) argv[6]); + int nbands = *((int *) argv[7]); + + return( im_extract_areabands( argv[0], argv[1], + left, top, width, height, band, nbands ) ); +} + +/* Description of im_extract_areabands. + */ +static im_function extract_areabands_desc = { + "im_extract_areabands", /* Name */ + "extract area and bands", /* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + extract_areabands_vec, /* Dispatch function */ + IM_NUMBER( extract_areabands_args ),/* Size of arg list */ + extract_areabands_args /* Arg list */ +}; + +/* One image in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Two images in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_bandjoin via arg vector. + */ +static int +bandjoin_vec( im_object *argv ) +{ + return( im_bandjoin( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_bandjoin. + */ +static im_function bandjoin_desc = { + "im_bandjoin", /* Name */ + "bandwise join of two images", /* Description */ + IM_FN_PIO, /* Flags */ + bandjoin_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +static im_arg_desc gbandjoin_args[] = { + IM_INPUT_IMAGEVEC( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +static int +gbandjoin_vec( im_object *argv ) +{ + im_imagevec_object *iv = (im_imagevec_object *) argv[0]; + + return( im_gbandjoin( iv->vec, argv[1], iv->n ) ); +} + +static im_function gbandjoin_desc = { + "im_gbandjoin", /* Name */ + "bandwise join of many images", /* Description */ + IM_FN_PIO, /* Flags */ + gbandjoin_vec, /* Dispatch function */ + IM_NUMBER( gbandjoin_args ), /* Size of arg list */ + gbandjoin_args /* Arg list */ +}; + +/* Args to im_text. + */ +static im_arg_desc text_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "text" ), + IM_INPUT_STRING( "font" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "alignment" ), + IM_INPUT_INT( "dpi" ) +}; + +/* Call im_text via arg vector. + */ +static int +text_vec( im_object *argv ) +{ + int width = *((int *) argv[3]); + int alignment = *((int *) argv[4]); + int dpi = *((int *) argv[5]); + + return( im_text( argv[0], argv[1], argv[2], width, alignment, dpi ) ); +} + +/* Description of im_text. + */ +static im_function text_desc = { + "im_text", /* Name */ + "generate text image", /* Description */ + IM_FN_PIO, /* Flags */ + text_vec, /* Dispatch function */ + IM_NUMBER( text_args ), /* Size of arg list */ + text_args /* Arg list */ +}; + +/* Args to im_black. + */ +static im_arg_desc black_args[] = { + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "x_size" ), + IM_INPUT_INT( "y_size" ), + IM_INPUT_INT( "bands" ) +}; + +/* Call im_black via arg vector. + */ +static int +black_vec( im_object *argv ) +{ + int xs = *((int *) argv[1]); + int ys = *((int *) argv[2]); + int bands = *((int *) argv[3]); + + return( im_black( argv[0], xs, ys, bands ) ); +} + +/* Description of im_black. + */ +static im_function black_desc = { + "im_black", /* Name */ + "generate black image", /* Description */ + IM_FN_PIO, /* Flags */ + black_vec, /* Dispatch function */ + IM_NUMBER( black_args ), /* Size of arg list */ + black_args /* Arg list */ +}; + +/* Args to im_clip2fmt. + */ +static im_arg_desc clip2fmt_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "ofmt" ) +}; + +/* Call im_clip2fmt via arg vector. + */ +static int +clip2fmt_vec( im_object *argv ) +{ + int ofmt = *((int *) argv[2]); + + return( im_clip2fmt( argv[0], argv[1], ofmt ) ); +} + +/* Description of im_clip2fmt. + */ +static im_function clip2fmt_desc = { + "im_clip2fmt", /* Name */ + "convert image format to ofmt", /* Description */ + IM_FN_PIO, /* Flags */ + clip2fmt_vec, /* Dispatch function */ + IM_NUMBER( clip2fmt_args ), /* Size of arg list */ + clip2fmt_args /* Arg list */ +}; + +/* Call im_c2rect via arg vector. + */ +static int +c2rect_vec( im_object *argv ) +{ + return( im_c2rect( argv[0], argv[1] ) ); +} + +/* Description of im_c2rect. + */ +static im_function c2rect_desc = { + "im_c2rect", /* Name */ + "convert phase and amplitude to real and imaginary", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + c2rect_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_c2amph via arg vector. + */ +static int +c2amph_vec( im_object *argv ) +{ + return( im_c2amph( argv[0], argv[1] ) ); +} + +/* Description of im_c2amph. + */ +static im_function c2amph_desc = { + "im_c2amph", /* Name */ + "convert real and imaginary to phase and amplitude", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + c2amph_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2dcm via arg vector. + */ +static int +clip2dcm_vec( im_object *argv ) +{ + return( im_clip2dcm( argv[0], argv[1] ) ); +} + +/* Description of im_clip2dcm. + */ +static im_function clip2dcm_desc = { + "im_clip2dcm", /* Name */ + "convert to double complex", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2dcm_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2cm via arg vector. + */ +static int +clip2cm_vec( im_object *argv ) +{ + return( im_clip2cm( argv[0], argv[1] ) ); +} + +/* Description of im_clip2cm. + */ +static im_function clip2cm_desc = { + "im_clip2cm", /* Name */ + "convert to complex", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2cm_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2us via arg vector. + */ +static int +clip2us_vec( im_object *argv ) +{ + return( im_clip2us( argv[0], argv[1] ) ); +} + +/* Description of im_clip2us. + */ +static im_function clip2us_desc = { + "im_clip2us", /* Name */ + "convert to unsigned 16-bit integer", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2us_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2ui via arg vector. + */ +static int +clip2ui_vec( im_object *argv ) +{ + return( im_clip2ui( argv[0], argv[1] ) ); +} + +/* Description of im_clip2ui. + */ +static im_function clip2ui_desc = { + "im_clip2ui", /* Name */ + "convert to unsigned 32-bit integer", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2ui_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2s via arg vector. + */ +static int +clip2s_vec( im_object *argv ) +{ + return( im_clip2s( argv[0], argv[1] ) ); +} + +/* Description of im_clip2s. + */ +static im_function clip2s_desc = { + "im_clip2s", /* Name */ + "convert to signed 16-bit integer", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2s_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2i via arg vector. + */ +static int +clip2i_vec( im_object *argv ) +{ + return( im_clip2i( argv[0], argv[1] ) ); +} + +/* Description of im_clip2i. + */ +static im_function clip2i_desc = { + "im_clip2i", /* Name */ + "convert to signed 32-bit integer", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2i_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2d via arg vector. + */ +static int +clip2d_vec( im_object *argv ) +{ + return( im_clip2d( argv[0], argv[1] ) ); +} + +/* Description of im_clip2d. + */ +static im_function clip2d_desc = { + "im_clip2d", /* Name */ + "convert to double-precision float", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2d_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2f via arg vector. + */ +static int +clip2f_vec( im_object *argv ) +{ + return( im_clip2f( argv[0], argv[1] ) ); +} + +/* Description of im_clip2f. + */ +static im_function clip2f_desc = { + "im_clip2f", /* Name */ + "convert to single-precision float", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2f_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2c via arg vector. + */ +static int +clip2c_vec( im_object *argv ) +{ + return( im_clip2c( argv[0], argv[1] ) ); +} + +/* Description of im_clip2c. + */ +static im_function clip2c_desc = { + "im_clip2c", /* Name */ + "convert to signed 8-bit integer", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2c_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip via arg vector. + */ +static int +clip_vec( im_object *argv ) +{ + return( im_clip( argv[0], argv[1] ) ); +} + +/* Description of im_clip. + */ +static im_function clip_desc = { + "im_clip", /* Name */ + "convert to unsigned 8-bit integer", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_ri2c via arg vector. + */ +static int +ri2c_vec( im_object *argv ) +{ + return( im_ri2c( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_ri2c. + */ +static im_function ri2c_desc = { + "im_ri2c", /* Name */ + "join two non-complex images to form complex", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + ri2c_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_c2imag via arg vector. + */ +static int +c2imag_vec( im_object *argv ) +{ + return( im_c2imag( argv[0], argv[1] ) ); +} + +/* Description of im_c2imag. + */ +static im_function c2imag_desc = { + "im_c2imag", /* Name */ + "extract imaginary part of complex image", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + c2imag_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_c2real via arg vector. + */ +static int +c2real_vec( im_object *argv ) +{ + return( im_c2real( argv[0], argv[1] ) ); +} + +/* Description of im_c2real. + */ +static im_function c2real_desc = { + "im_c2real", /* Name */ + "extract real part of complex image", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + c2real_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_c2ps via arg vector. + */ +static int +c2ps_vec( im_object *argv ) +{ + return( im_c2ps( argv[0], argv[1] ) ); +} + +/* Description of im_c2ps. + */ +static im_function c2ps_desc = { + "im_c2ps", /* Name */ + "find power spectrum of complex image", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + c2ps_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args to im_copy_set. + */ +static im_arg_desc copy_set_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "Type" ), + IM_INPUT_DOUBLE( "Xres" ), + IM_INPUT_DOUBLE( "Yres" ), + IM_INPUT_INT( "Xoffset" ), + IM_INPUT_INT( "Yoffset" ) +}; + +/* Call im_copy_set via arg vector. + */ +static int +copy_set_vec( im_object *argv ) +{ + int Type = *((int *) argv[2]); + float Xres = *((double *) argv[3]); + float Yres = *((double *) argv[4]); + int Xoffset = *((int *) argv[5]); + int Yoffset = *((int *) argv[6]); + + return( im_copy_set( argv[0], argv[1], + Type, Xres, Yres, Xoffset, Yoffset ) ); +} + +/* Description of im_copy_set. + */ +static im_function copy_set_desc = { + "im_copy_set", /* Name */ + "copy image, setting informational fields", + + /* Can't set PTOP ... we don't want to zap the LUT, we want the real + * image. + */ + IM_FN_PIO, /* Flags */ + + copy_set_vec, /* Dispatch function */ + IM_NUMBER( copy_set_args ), /* Size of arg list */ + copy_set_args /* Arg list */ +}; + +/* Args to im_copy_set_meta. + */ +static im_arg_desc copy_set_meta_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_STRING( "field" ), + IM_INPUT_GVALUE( "value" ) +}; + +/* Call im_copy_set_meta via arg vector. + */ +static int +copy_set_meta_vec( im_object *argv ) +{ + const char *field = argv[2]; + GValue *value = argv[3]; + + return( im_copy_set_meta( argv[0], argv[1], field, value ) ); +} + +/* Description of im_copy_set_meta. + */ +static im_function copy_set_meta_desc = { + "im_copy_set_meta", /* Name */ + "copy image, setting a meta field", + + /* Can't set PTOP ... we don't want to zap the LUT, we want the real + * image. + */ + IM_FN_PIO, /* Flags */ + + copy_set_meta_vec, /* Dispatch function */ + IM_NUMBER( copy_set_meta_args ),/* Size of arg list */ + copy_set_meta_args /* Arg list */ +}; + +/* Args to im_copy_morph. + */ +static im_arg_desc copy_morph_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "Bands" ), + IM_INPUT_INT( "BandFmt" ), + IM_INPUT_INT( "Coding" ) +}; + +/* Call im_copy_morph via arg vector. + */ +static int +copy_morph_vec( im_object *argv ) +{ + int Bands = *((int *) argv[2]); + int BandFmt = *((int *) argv[3]); + int Coding = *((int *) argv[4]); + + return( im_copy_morph( argv[0], argv[1], + Bands, BandFmt, Coding ) ); +} + +/* Description of im_copy_morph. + */ +static im_function copy_morph_desc = { + "im_copy_morph", /* Name */ + "copy image, setting pixel layout", + + /* Can't set PTOP ... we don't want to zap the LUT, we want the real + * image. + */ + IM_FN_PIO, /* Flags */ + + copy_morph_vec, /* Dispatch function */ + IM_NUMBER( copy_morph_args ), /* Size of arg list */ + copy_morph_args /* Arg list */ +}; + +/* Call im_copy via arg vector. + */ +static int +copy_vec( im_object *argv ) +{ + return( im_copy( argv[0], argv[1] ) ); +} + +/* Description of im_copy. + */ +static im_function copy_desc = { + "im_copy", /* Name */ + "copy image", + + /* Can't set PTOP ... we don't want to zap the LUT, we want the real + * image. + */ + IM_FN_PIO, /* Flags */ + + copy_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_copy_swap via arg vector. + */ +static int +copy_swap_vec( im_object *argv ) +{ + return( im_copy_swap( argv[0], argv[1] ) ); +} + +/* Description of im_copy_swap. + */ +static im_function copy_swap_desc = { + "im_copy_swap", /* Name */ + "copy image, swapping byte order", + + /* Can't set PTOP ... we don't want to zap the LUT, we want the real + * image. + */ + IM_FN_PIO, /* Flags */ + + copy_swap_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_fliphor via arg vector. + */ +static int +fliphor_vec( im_object *argv ) +{ + return( im_fliphor( argv[0], argv[1] ) ); +} + +/* Description of im_fliphor. + */ +static im_function fliphor_desc = { + "im_fliphor", /* Name */ + "flip image left-right", + IM_FN_PIO, /* Flags */ + fliphor_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_flipver via arg vector. + */ +static int +flipver_vec( im_object *argv ) +{ + return( im_flipver( argv[0], argv[1] ) ); +} + +/* Description of im_flipver. + */ +static im_function flipver_desc = { + "im_flipver", /* Name */ + "flip image top-bottom", + IM_FN_PIO, /* Flags */ + flipver_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_falsecolour via arg vector. + */ +static int +falsecolour_vec( im_object *argv ) +{ + return( im_falsecolour( argv[0], argv[1] ) ); +} + +/* Description of im_falsecolour. + */ +static im_function falsecolour_desc = { + "im_falsecolour", /* Name */ + "turn luminance changes into chrominance changes", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + falsecolour_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args for im_recomb. + */ +static im_arg_desc recomb_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DMASK( "matrix" ) +}; + +/* Call im_recomb via arg vector. + */ +static int +recomb_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_recomb( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_recomb. + */ +static im_function recomb_desc = { + "im_recomb", /* Name */ + "linear recombination with mask", + IM_FN_PIO, /* Flags */ + recomb_vec, /* Dispatch function */ + IM_NUMBER( recomb_args ), /* Size of arg list */ + recomb_args /* Arg list */ +}; + +/* Args for im_insert. + */ +static im_arg_desc insert_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_IMAGE( "sub" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "x" ), + IM_INPUT_INT( "y" ) +}; + +/* Call im_insert via arg vector. + */ +static int +insert_vec( im_object *argv ) +{ + int x = *((int *) argv[3]); + int y = *((int *) argv[4]); + + return( im_insert( argv[0], argv[1], argv[2], x, y ) ); +} + +/* Description of im_insert. + */ +static im_function insert_desc = { + "im_insert", /* Name */ + "insert sub-image into main image at position", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + insert_vec, /* Dispatch function */ + IM_NUMBER( insert_args ), /* Size of arg list */ + insert_args /* Arg list */ +}; + +/* Call im_insert_noexpand via arg vector. + */ +static int +insert_noexpand_vec( im_object *argv ) +{ + int x = *((int *) argv[3]); + int y = *((int *) argv[4]); + + return( im_insert_noexpand( argv[0], argv[1], argv[2], x, y ) ); +} + +/* Description of im_insert_noexpand. + */ +static im_function insert_noexpand_desc = { + "im_insert_noexpand", /* Name */ + "insert sub-image into main image at position, no expansion", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + insert_noexpand_vec, /* Dispatch function */ + IM_NUMBER( insert_args ), /* Size of arg list */ + insert_args /* Arg list */ +}; + +/* Call im_rot180 via arg vector. + */ +static int +rot180_vec( im_object *argv ) +{ + return( im_rot180( argv[0], argv[1] ) ); +} + +/* Description of im_rot180. + */ +static im_function rot180_desc = { + "im_rot180", /* Name */ + "rotate image 180 degrees", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + rot180_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_rot90 via arg vector. + */ +static int +rot90_vec( im_object *argv ) +{ + return( im_rot90( argv[0], argv[1] ) ); +} + +/* Description of im_rot90. + */ +static im_function rot90_desc = { + "im_rot90", /* Name */ + "rotate image 90 degrees clockwise", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + rot90_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_rot270 via arg vector. + */ +static int +rot270_vec( im_object *argv ) +{ + return( im_rot270( argv[0], argv[1] ) ); +} + +/* Description of im_rot270. + */ +static im_function rot270_desc = { + "im_rot270", /* Name */ + "rotate image 270 degrees clockwise", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + rot270_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_lrjoin via arg vector. + */ +static int +lrjoin_vec( im_object *argv ) +{ + return( im_lrjoin( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_lrjoin. + */ +static im_function lrjoin_desc = { + "im_lrjoin", /* Name */ + "join two images left-right", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + lrjoin_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_tbjoin via arg vector. + */ +static int +tbjoin_vec( im_object *argv ) +{ + return( im_tbjoin( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_tbjoin. + */ +static im_function tbjoin_desc = { + "im_tbjoin", /* Name */ + "join two images top-bottom", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + tbjoin_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Args to im_mask2vips. + */ +static im_arg_desc mask2vips_args[] = { + IM_INPUT_DMASK( "input" ), + IM_OUTPUT_IMAGE( "output" ), +}; + +/* Call im_mask2vips via arg vector. + */ +static int +mask2vips_vec( im_object *argv ) +{ + im_mask_object *mo = argv[0]; + + return( im_mask2vips( mo->mask, argv[1] ) ); +} + +/* Description of im_mask2vips. + */ +static im_function mask2vips_desc = { + "im_mask2vips", /* Name */ + "convert DOUBLEMASK to VIPS image", + 0, /* Flags */ + mask2vips_vec, /* Dispatch function */ + IM_NUMBER( mask2vips_args ), /* Size of arg list */ + mask2vips_args /* Arg list */ +}; + +/* Args to im_vips2mask. + */ +static im_arg_desc vips2mask_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_DMASK( "output" ), +}; + +/* Call im_vips2mask via arg vector. + */ +static int +vips2mask_vec( im_object *argv ) +{ + im_mask_object *mo = argv[1]; + + if( !(mo->mask = im_vips2mask( argv[0], mo->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_vips2mask. + */ +static im_function vips2mask_desc = { + "im_vips2mask", /* Name */ + "convert VIPS image to DOUBLEMASK", + 0, /* Flags */ + vips2mask_vec, /* Dispatch function */ + IM_NUMBER( vips2mask_args ), /* Size of arg list */ + vips2mask_args /* Arg list */ +}; + +/* Call im_scale via arg vector. + */ +static int +scale_vec( im_object *argv ) +{ + return( im_scale( argv[0], argv[1] ) ); +} + +/* Description of im_scale. + */ +static im_function scale_desc = { + "im_scale", /* Name */ + "scale image linearly to fit range 0-255", + IM_FN_PIO, /* Flags */ + scale_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_scaleps via arg vector. + */ +static int +scaleps_vec( im_object *argv ) +{ + return( im_scaleps( argv[0], argv[1] ) ); +} + +/* Description of im_scaleps. + */ +static im_function scaleps_desc = { + "im_scaleps", /* Name */ + "logarithmic scale of image to fit range 0-255", + 0, /* Flags */ + scaleps_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args to im_slice. + */ +static im_arg_desc slice_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_DOUBLE( "thresh1" ), + IM_INPUT_DOUBLE( "thresh2" ) +}; + +/* Call im_slice via arg vector. + */ +static int +slice_vec( im_object *argv ) +{ + double t1 = *((double *) argv[2]); + double t2 = *((double *) argv[3]); + + return( im_slice( argv[0], argv[1], t1, t2 ) ); +} + +/* Description of im_slice. + */ +static im_function slice_desc = { + "im_slice", /* Name */ + "slice an image using two thresholds", + 0, /* Flags */ + slice_vec, /* Dispatch function */ + IM_NUMBER( slice_args ), /* Size of arg list */ + slice_args /* Arg list */ +}; + +/* Args to im_thresh. + */ +static im_arg_desc thresh_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_DOUBLE( "threshold" ) +}; + +/* Call im_thresh via arg vector. + */ +static int +thresh_vec( im_object *argv ) +{ + double t1 = *((double *) argv[2]); + + return( im_thresh( argv[0], argv[1], t1 ) ); +} + +/* Description of im_thresh. + */ +static im_function thresh_desc = { + "im_thresh", /* Name */ + "slice an image at a threshold", + 0, /* Flags */ + thresh_vec, /* Dispatch function */ + IM_NUMBER( thresh_args ), /* Size of arg list */ + thresh_args /* Arg list */ +}; + +/* Args to im_grid. + */ +static im_arg_desc grid_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "tile_height" ), + IM_INPUT_INT( "across" ), + IM_INPUT_INT( "down" ) +}; + +/* Call im_grid via arg vector. + */ +static int +grid_vec( im_object *argv ) +{ + int tile_height = *((int *) argv[2]); + int across = *((int *) argv[3]); + int down = *((int *) argv[4]); + + return( im_grid( argv[0], argv[1], tile_height, across, down ) ); +} + +/* Description of im_grid. + */ +static im_function grid_desc = { + "im_grid", /* Name */ + "chop a tall thin image into a grid of images", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + grid_vec, /* Dispatch function */ + IM_NUMBER( grid_args ), /* Size of arg list */ + grid_args /* Arg list */ +}; + +/* Args to im_replicate. + */ +static im_arg_desc replicate_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "across" ), + IM_INPUT_INT( "down" ) +}; + +/* Call im_replicate via arg vector. + */ +static int +replicate_vec( im_object *argv ) +{ + int across = *((int *) argv[2]); + int down = *((int *) argv[3]); + + return( im_replicate( argv[0], argv[1], across, down ) ); +} + +/* Description of im_replicate. + */ +static im_function replicate_desc = { + "im_replicate", /* Name */ + "replicate an image horizontally and vertically", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + replicate_vec, /* Dispatch function */ + IM_NUMBER( replicate_args ), /* Size of arg list */ + replicate_args /* Arg list */ +}; + +/* Args to im_zoom. + */ +static im_arg_desc zoom_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "xfac" ), + IM_INPUT_INT( "yfac" ) +}; + +/* Call im_zoom via arg vector. + */ +static int +zoom_vec( im_object *argv ) +{ + int xfac = *((int *) argv[2]); + int yfac = *((int *) argv[3]); + + return( im_zoom( argv[0], argv[1], xfac, yfac ) ); +} + +/* Description of im_zoom. + */ +static im_function zoom_desc = { + "im_zoom", /* Name */ + "simple zoom of an image by integer factors", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + zoom_vec, /* Dispatch function */ + IM_NUMBER( zoom_args ), /* Size of arg list */ + zoom_args /* Arg list */ +}; + +static int +jpeg2vips_vec( im_object *argv ) +{ + char *in = argv[0]; + IMAGE *out = argv[1]; + + if( im_jpeg2vips( in, out ) ) + return( -1 ); + + return( 0 ); +} + +static im_arg_desc jpeg2vips_args[] = { + IM_INPUT_STRING( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +static im_function jpeg2vips_desc = { + "im_jpeg2vips", /* Name */ + "convert from jpeg", /* Description */ + 0, /* Flags */ + jpeg2vips_vec, /* Dispatch function */ + IM_NUMBER( jpeg2vips_args ), /* Size of arg list */ + jpeg2vips_args /* Arg list */ +}; + +static int +vips2jpeg_vec( im_object *argv ) +{ + IMAGE *in = argv[0]; + char *out = argv[1]; + + if( im_vips2jpeg( in, out ) ) + return( -1 ); + + return( 0 ); +} + +static im_arg_desc vips2jpeg_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_STRING( "out" ) +}; + +static im_function vips2jpeg_desc = { + "im_vips2jpeg", /* Name */ + "convert to jpeg", /* Description */ + 0, /* Flags */ + vips2jpeg_vec, /* Dispatch function */ + IM_NUMBER( vips2jpeg_args ), /* Size of arg list */ + vips2jpeg_args /* Arg list */ +}; + +static int +vips2mimejpeg_vec( im_object *argv ) +{ + IMAGE *in = argv[0]; + int qfac = *((int *) argv[1]); + + if( im_vips2mimejpeg( in, qfac ) ) + return( -1 ); + + return( 0 ); +} + +static im_arg_desc vips2mimejpeg_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_INT( "qfac" ) +}; + +static im_function vips2mimejpeg_desc = { + "im_vips2mimejpeg", /* Name */ + "convert to jpeg as mime type on stdout", /* Description */ + 0, /* Flags */ + vips2mimejpeg_vec, /* Dispatch function */ + IM_NUMBER( vips2mimejpeg_args ), /* Size of arg list */ + vips2mimejpeg_args /* Arg list */ +}; + +/* Args for vips2png. + */ +static im_arg_desc vips2png_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_STRING( "out" ) +}; + +/* Call im_vips2png via arg vector. + */ +static int +vips2png_vec( im_object *argv ) +{ + return( im_vips2png( argv[0], argv[1] ) ); +} + +/* Description of im_vips2png. + */ +static im_function vips2png_desc = { + "im_vips2png", /* Name */ + "convert VIPS image to PNG file", /* Description */ + 0, + vips2png_vec, /* Dispatch function */ + IM_NUMBER( vips2png_args ), /* Size of arg list */ + vips2png_args /* Arg list */ +}; + +/* Args for png2vips. + */ +static im_arg_desc png2vips_args[] = { + IM_INPUT_STRING( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_png2vips via arg vector. + */ +static int +png2vips_vec( im_object *argv ) +{ + return( im_png2vips( argv[0], argv[1] ) ); +} + +/* Description of im_png2vips. + */ +static im_function png2vips_desc = { + "im_png2vips", /* Name */ + "convert PNG file to VIPS image", /* Description */ + 0, + png2vips_vec, /* Dispatch function */ + IM_NUMBER( png2vips_args ), /* Size of arg list */ + png2vips_args /* Arg list */ +}; + +/* Args for exr2vips. + */ +static im_arg_desc exr2vips_args[] = { + IM_INPUT_STRING( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_exr2vips via arg vector. + */ +static int +exr2vips_vec( im_object *argv ) +{ + return( im_exr2vips( argv[0], argv[1] ) ); +} + +/* Description of im_exr2vips. + */ +static im_function exr2vips_desc = { + "im_exr2vips", /* Name */ + "convert an OpenEXR file to VIPS", /* Description */ + 0, + exr2vips_vec, /* Dispatch function */ + IM_NUMBER( exr2vips_args ), /* Size of arg list */ + exr2vips_args /* Arg list */ +}; + +/* Args for vips2tiff. + */ +static im_arg_desc vips2tiff_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_STRING( "out" ) +}; + +/* Call im_vips2tiff via arg vector. + */ +static int +vips2tiff_vec( im_object *argv ) +{ + return( im_vips2tiff( argv[0], argv[1] ) ); +} + +/* Description of im_vips2tiff. + */ +static im_function vips2tiff_desc = { + "im_vips2tiff", /* Name */ + "convert VIPS image to TIFF file", /* Description */ + 0, + vips2tiff_vec, /* Dispatch function */ + IM_NUMBER( vips2tiff_args ), /* Size of arg list */ + vips2tiff_args /* Arg list */ +}; + +/* Args for magick2vips. + */ +static im_arg_desc magick2vips_args[] = { + IM_INPUT_STRING( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_magick2vips via arg vector. + */ +static int +magick2vips_vec( im_object *argv ) +{ + return( im_magick2vips( argv[0], argv[1] ) ); +} + +/* Description of im_magick2vips. + */ +static im_function magick2vips_desc = { + "im_magick2vips", /* Name */ + "load file with libMagick", /* Description */ + 0, + magick2vips_vec, /* Dispatch function */ + IM_NUMBER( magick2vips_args ), /* Size of arg list */ + magick2vips_args /* Arg list */ +}; + +/* Args for tiff2vips. + */ +static im_arg_desc tiff2vips_args[] = { + IM_INPUT_STRING( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_tiff2vips via arg vector. + */ +static int +tiff2vips_vec( im_object *argv ) +{ + return( im_tiff2vips( argv[0], argv[1] ) ); +} + +/* Description of im_tiff2vips. + */ +static im_function tiff2vips_desc = { + "im_tiff2vips", /* Name */ + "convert TIFF file to VIPS image", /* Description */ + 0, + tiff2vips_vec, /* Dispatch function */ + IM_NUMBER( tiff2vips_args ), /* Size of arg list */ + tiff2vips_args /* Arg list */ +}; + +static int +analyze2vips_vec( im_object *argv ) +{ + const char *in = argv[0]; + IMAGE *out = argv[1]; + + return( im_analyze2vips( in, out ) ); +} + +static im_arg_desc analyze2vips_arg_types[] = { + IM_INPUT_STRING( "filename" ), + IM_OUTPUT_IMAGE( "im" ) +}; + +static im_function analyze2vips_desc = { + "im_analyze2vips", /* Name */ + "read a file in analyze format",/* Description */ + 0, /* Flags */ + analyze2vips_vec, /* Dispatch function */ + IM_NUMBER( analyze2vips_arg_types ),/* Size of arg list */ + analyze2vips_arg_types /* Arg list */ +}; + +static int +csv2vips_vec( im_object *argv ) +{ + const char *in = argv[0]; + IMAGE *out = argv[1]; + + return( im_csv2vips( in, out ) ); +} + +static im_arg_desc csv2vips_arg_types[] = { + IM_INPUT_STRING( "filename" ), + IM_OUTPUT_IMAGE( "im" ) +}; + +static im_function csv2vips_desc = { + "im_csv2vips", /* Name */ + "read a file in csv format",/* Description */ + 0, /* Flags */ + csv2vips_vec, /* Dispatch function */ + IM_NUMBER( csv2vips_arg_types ),/* Size of arg list */ + csv2vips_arg_types /* Arg list */ +}; + +static int +vips2csv_vec( im_object *argv ) +{ + IMAGE *in = argv[0]; + const char *filename = argv[1]; + + return( im_vips2csv( in, filename ) ); +} + +static im_arg_desc vips2csv_arg_types[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_STRING( "filename" ) +}; + +static im_function vips2csv_desc = { + "im_vips2csv", /* Name */ + "write an image in csv format", /* Description */ + 0, /* Flags */ + vips2csv_vec, /* Dispatch function */ + IM_NUMBER( vips2csv_arg_types ),/* Size of arg list */ + vips2csv_arg_types /* Arg list */ +}; + +static int +ppm2vips_vec( im_object *argv ) +{ + const char *in = argv[0]; + IMAGE *out = argv[1]; + + return( im_ppm2vips( in, out ) ); +} + +static im_arg_desc ppm2vips_arg_types[] = { + IM_INPUT_STRING( "filename" ), + IM_OUTPUT_IMAGE( "im" ) +}; + +static im_function ppm2vips_desc = { + "im_ppm2vips", /* Name */ + "read a file in pbm/pgm/ppm format", /* Description */ + 0, /* Flags */ + ppm2vips_vec, /* Dispatch function */ + IM_NUMBER( ppm2vips_arg_types ),/* Size of arg list */ + ppm2vips_arg_types /* Arg list */ +}; + +static int +vips2ppm_vec( im_object *argv ) +{ + IMAGE *im = argv[0]; + const char *filename = argv[1]; + + return( im_vips2ppm( im, filename ) ); +} + +static im_arg_desc vips2ppm_arg_types[] = { + IM_INPUT_IMAGE( "im" ), + IM_INPUT_STRING( "filename" ) +}; + +static im_function vips2ppm_desc = { + "im_vips2ppm", /* Name */ + "write a file in pbm/pgm/ppm format", /* Description */ + 0, /* Flags */ + vips2ppm_vec, /* Dispatch function */ + IM_NUMBER( vips2ppm_arg_types ),/* Size of arg list */ + vips2ppm_arg_types /* Arg list */ +}; + +/* Call im_msb via arg vector. + */ +static int +msb_vec (im_object * argv) +{ + return im_msb (argv[0], argv[1]); +} + +/* Description of im_msb. + */ +static im_function msb_desc = { + "im_msb", /* Name */ + "convert to uchar by discarding bits", + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + msb_vec, /* Dispatch function */ + IM_NUMBER (one_in_one_out), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args to im_msb_band. + */ +static im_arg_desc msb_band_args[] = { + IM_INPUT_IMAGE ("in"), + IM_OUTPUT_IMAGE ("out"), + IM_INPUT_INT ("band") +}; + +/* Call im_msb_band via arg vector. + */ +static int +msb_band_vec (im_object * argv) +{ + IMAGE *in = (IMAGE *) argv[0]; + IMAGE *out = (IMAGE *) argv[1]; + int *band = (int *) argv[2]; + + return im_msb_band (in, out, *band); +} + +/* Description of im_msb_band. + */ +static im_function msb_band_desc = { + "im_msb_band", /* Name */ + "convert to single band uchar by discarding bits", + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + msb_band_vec, /* Dispatch function */ + IM_NUMBER (msb_band_args), /* Size of arg list */ + msb_band_args /* Arg list */ +}; + +/* Args to im_rightshift_size. + */ +static im_arg_desc rightshift_size_args[] = { + IM_INPUT_IMAGE ("in"), + IM_OUTPUT_IMAGE ("out"), + IM_INPUT_INT ("xshift"), + IM_INPUT_INT ("yshift"), + IM_INPUT_INT ("band_fmt") +}; + +/* Call im_rightshift_size via arg vector. + */ +static int +rightshift_size_vec (im_object * argv) +{ + IMAGE *in = (IMAGE *) argv[0]; + IMAGE *out = (IMAGE *) argv[1]; + int *xshift = (int *) argv[2]; + int *yshift = (int *) argv[3]; + int *band_fmt = (int *) argv[4]; + + return im_rightshift_size (in, out, *xshift, *yshift, *band_fmt ); +} + +/* Description of im_rightshift_size. + */ +static im_function rightshift_size_desc = { + "im_rightshift_size", /* Name */ + "decrease size by a power-of-two factor", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + rightshift_size_vec, /* Dispatch function */ + IM_NUMBER (rightshift_size_args), /* Size of arg list */ + rightshift_size_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *conv_list[] = { + &bandjoin_desc, + &bernd_desc, + &black_desc, + &c2amph_desc, + &c2imag_desc, + &c2ps_desc, + &c2real_desc, + &c2rect_desc, + &clip2c_desc, + &clip2cm_desc, + &clip2d_desc, + &clip2dcm_desc, + &clip2f_desc, + &clip2fmt_desc, + &clip2i_desc, + &clip2s_desc, + &clip2ui_desc, + &clip2us_desc, + &clip_desc, + ©_desc, + ©_morph_desc, + ©_swap_desc, + ©_set_desc, + ©_set_meta_desc, + &csv2vips_desc, + &extract_area_desc, + &extract_areabands_desc, + &extract_band_desc, + &extract_bands_desc, + &extract_desc, + &falsecolour_desc, + &fliphor_desc, + &flipver_desc, + &gbandjoin_desc, + &grid_desc, + &insert_desc, + &insert_noexpand_desc, + &jpeg2vips_desc, + &lrjoin_desc, + &magick2vips_desc, + &mask2vips_desc, + &msb_desc, + &msb_band_desc, + &png2vips_desc, + &exr2vips_desc, + &ppm2vips_desc, + &analyze2vips_desc, + &print_desc, + &recomb_desc, + &replicate_desc, + &ri2c_desc, + &rot180_desc, + &rot270_desc, + &rot90_desc, + &scale_desc, + &scaleps_desc, + &rightshift_size_desc, + &slice_desc, + &subsample_desc, + &system_desc, + &tbjoin_desc, + &text_desc, + &thresh_desc, + &tiff2vips_desc, + &vips2csv_desc, + &vips2jpeg_desc, + &vips2mask_desc, + &vips2mimejpeg_desc, + &vips2png_desc, + &vips2ppm_desc, + &vips2tiff_desc, + &zoom_desc +}; + +/* Package of functions. + */ +im_package im__conversion = { + "conversion", + IM_NUMBER( conv_list ), + conv_list +}; diff --git a/libsrc/conversion/dbh.h b/libsrc/conversion/dbh.h new file mode 100644 index 00000000..782cbf67 --- /dev/null +++ b/libsrc/conversion/dbh.h @@ -0,0 +1,98 @@ +/* + * + * (c) Copyright, 1986-1991 + * Biodynamics Research Unit + * Mayo Foundation + * + * dbh.h + * + * + * database sub-definitions + */ + +/* + * + * The previous-generation header for Analyze images. Although (c) Mayo + * Foundation, it has been in the public domain for many years. + * + * Analyze images have a 348 byte header stored in a file with a .hdr suffix, + * and a corresponding .img file containing just the pixel data. + * + */ + +struct header_key /* header_key */ + { /* off + size*/ + int sizeof_hdr; /* 0 + 4 */ + char data_type[10]; /* 4 + 10 */ + char db_name[18]; /* 14 + 18 */ + int extents; /* 32 + 4 */ + short int session_error; /* 36 + 2 */ + char regular; /* 38 + 1 */ + char hkey_un0; /* 39 + 1 */ + }; /* total=40 */ + +struct image_dimension /* image_dimension */ + { /* off + size*/ + short int dim[8]; /* 0 + 16 */ + char vox_units[4]; /* 16 + 4 */ + char cal_units[8]; /* 20 + 4 */ + short int unused1; /* 24 + 2 */ + short int datatype; /* 30 + 2 */ + short int bitpix; /* 32 + 2 */ + short int dim_un0; /* 34 + 2 */ + float pixdim[8]; /* 36 + 32 */ + + /* pixdim[] specifies the voxel dimensions: + pixdim[1] - voxel width + pixdim[2] - voxel height + pixdim[3] - interslice distance + ..etc + */ + float vox_offset; /* 68 + 4 */ + float funused1; /* 72 + 4 */ + float funused2; /* 76 + 4 */ + float funused3; /* 80 + 4 */ + float cal_max; /* 84 + 4 */ + float cal_min; /* 88 + 4 */ + int compressed; /* 92 + 4 */ + int verified; /* 96 + 4 */ + int glmax, glmin; /* 100 + 8 */ + }; + +struct data_history /* data_history */ + { /* off + size*/ + char descrip[80]; /* 0 + 80 */ + char aux_file[24]; /* 80 + 24 */ + char orient; /* 104 + 1 */ + char originator[10]; /* 105 + 10 */ + char generated[10]; /* 115 + 10 */ + char scannum[10]; /* 125 + 10 */ + char patient_id[10]; /* 135 + 10 */ + char exp_date[10]; /* 145 + 10 */ + char exp_time[10]; /* 155 + 10 */ + char hist_un0[3]; /* 165 + 3 */ + int views; /* 168 + 4 */ + int vols_added; /* 172 + 4 */ + int start_field; /* 176 + 4 */ + int field_skip; /* 180 + 4 */ + int omax,omin; /* 184 + 8 */ + int smax,smin; /* 192 + 8 */ + }; /* total=200 */ + +struct dsr /* dsr */ + { /* off + size*/ + struct header_key hk; /* 0 + 40 */ + struct image_dimension dime; /* 40 + 108 */ + struct data_history hist; /* 148 + 200 */ + }; /* total=348 */ + +/* Acceptable values for hdr.dime.datatype */ +#define DT_UNKNOWN 0 +#define DT_BINARY 1 +#define DT_UNSIGNED_CHAR 2 +#define DT_SIGNED_SHORT 4 +#define DT_SIGNED_INT 8 +#define DT_FLOAT 16 +#define DT_COMPLEX 32 +#define DT_DOUBLE 64 +#define DT_RGB 128 diff --git a/libsrc/conversion/im_analyze2vips.c b/libsrc/conversion/im_analyze2vips.c new file mode 100644 index 00000000..d5f9eeb2 --- /dev/null +++ b/libsrc/conversion/im_analyze2vips.c @@ -0,0 +1,583 @@ +/* Read a Analyze file. Old-style header (so called 7.5 format). + * + * 3/8/05 + * - dbh.h header from Ralph Myers + * 22/8/05 + * - better byteswapper + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "dbh.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* The things we can have in header fields. Can't use GType, since we want a + * static value we can use in a declaration. + */ +typedef enum { + BYTE, + SHORT, + INT, + FLOAT, + STRING +} Type; + +/* A field in the dsr header. + */ +typedef struct { + const char *name; /* Eg. "header_key.sizeof_hdr" */ + Type type; + glong offset; /* Offset in struct */ + int len; /* Sizeof ... useful for string types */ +} Field; + +static Field dsr_header[] = { + { "dsr-header_key.sizeof_hdr", INT, + G_STRUCT_OFFSET( struct dsr, hk.sizeof_hdr ), 4 }, + { "dsr-header_key.data_type", STRING, + G_STRUCT_OFFSET( struct dsr, hk.data_type ), 10 }, + { "dsr-header_key.db_name", STRING, + G_STRUCT_OFFSET( struct dsr, hk.db_name ), 18 }, + { "dsr-header_key.extents", INT, + G_STRUCT_OFFSET( struct dsr, hk.extents ), 4 }, + { "dsr-header_key.session_error", SHORT, + G_STRUCT_OFFSET( struct dsr, hk.session_error ), 2 }, + { "dsr-header_key.regular", BYTE, + G_STRUCT_OFFSET( struct dsr, hk.regular ), 1 }, + { "dsr-header_key.hkey_un0", BYTE, + G_STRUCT_OFFSET( struct dsr, hk.hkey_un0 ), 1 }, + + { "dsr-image_dimension.dim[0]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[0] ), 2 }, + { "dsr-image_dimension.dim[1]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[1] ), 2 }, + { "dsr-image_dimension.dim[2]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[2] ), 2 }, + { "dsr-image_dimension.dim[3]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[3] ), 2 }, + { "dsr-image_dimension.dim[4]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[4] ), 2 }, + { "dsr-image_dimension.dim[5]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[5] ), 2 }, + { "dsr-image_dimension.dim[6]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[6] ), 2 }, + { "dsr-image_dimension.dim[7]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[7] ), 2 }, + { "dsr-image_dimension.vox_units[0]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.vox_units[0] ), 1 }, + { "dsr-image_dimension.vox_units[1]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.vox_units[1] ), 1 }, + { "dsr-image_dimension.vox_units[2]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.vox_units[2] ), 1 }, + { "dsr-image_dimension.vox_units[3]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.vox_units[3] ), 1 }, + { "dsr-image_dimension.cal_units[0]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[0] ), 1 }, + { "dsr-image_dimension.cal_units[1]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[1] ), 1 }, + { "dsr-image_dimension.cal_units[2]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[2] ), 1 }, + { "dsr-image_dimension.cal_units[3]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[3] ), 1 }, + { "dsr-image_dimension.cal_units[4]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[4] ), 1 }, + { "dsr-image_dimension.cal_units[5]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[5] ), 1 }, + { "dsr-image_dimension.cal_units[6]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[6] ), 1 }, + { "dsr-image_dimension.cal_units[7]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[7] ), 1 }, + { "dsr-image_dimension.data_type", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.datatype ), 2 }, + { "dsr-image_dimension.bitpix", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.bitpix ), 2 }, + { "dsr-image_dimension.dim_un0", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim_un0 ), 2 }, + { "dsr-image_dimension.pixdim[0]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[0] ), 4 }, + { "dsr-image_dimension.pixdim[1]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[1] ), 4 }, + { "dsr-image_dimension.pixdim[2]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[2] ), 4 }, + { "dsr-image_dimension.pixdim[3]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[3] ), 4 }, + { "dsr-image_dimension.pixdim[4]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[4] ), 4 }, + { "dsr-image_dimension.pixdim[5]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[5] ), 4 }, + { "dsr-image_dimension.pixdim[6]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[6] ), 4 }, + { "dsr-image_dimension.pixdim[7]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[7] ), 4 }, + { "dsr-image_dimension.vox_offset", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.vox_offset ), 4 }, + { "dsr-image_dimension.cal_max", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.cal_max ), 4 }, + { "dsr-image_dimension.cal_min", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.cal_min ), 4 }, + { "dsr-image_dimension.compressed", INT, + G_STRUCT_OFFSET( struct dsr, dime.compressed ), 4 }, + { "dsr-image_dimension.verified", INT, + G_STRUCT_OFFSET( struct dsr, dime.verified ), 4 }, + { "dsr-image_dimension.glmax", INT, + G_STRUCT_OFFSET( struct dsr, dime.glmax ), 4 }, + { "dsr-image_dimension.glmin", INT, + G_STRUCT_OFFSET( struct dsr, dime.glmin ), 4 }, + + { "dsr-data_history.descrip", STRING, + G_STRUCT_OFFSET( struct dsr, hist.descrip ), 80 }, + { "dsr-data_history.aux_file", STRING, + G_STRUCT_OFFSET( struct dsr, hist.aux_file ), 24 }, + { "dsr-data_history.orient", BYTE, + G_STRUCT_OFFSET( struct dsr, hist.orient ), 1 }, + { "dsr-data_history.originator", STRING, + G_STRUCT_OFFSET( struct dsr, hist.originator ), 10 }, + { "dsr-data_history.generated", STRING, + G_STRUCT_OFFSET( struct dsr, hist.generated ), 10 }, + { "dsr-data_history.scannum", STRING, + G_STRUCT_OFFSET( struct dsr, hist.scannum ), 10 }, + { "dsr-data_history.patient_id", STRING, + G_STRUCT_OFFSET( struct dsr, hist.patient_id ), 10 }, + { "dsr-data_history.exp_date", STRING, + G_STRUCT_OFFSET( struct dsr, hist.exp_date ), 10 }, + { "dsr-data_history.exp_time", STRING, + G_STRUCT_OFFSET( struct dsr, hist.exp_time ), 10 }, + { "dsr-data_history.hist_un0", STRING, + G_STRUCT_OFFSET( struct dsr, hist.hist_un0 ), 3 }, + { "dsr-data_history.views", INT, + G_STRUCT_OFFSET( struct dsr, hist.views ), 4 }, + { "dsr-data_history.vols_added", INT, + G_STRUCT_OFFSET( struct dsr, hist.vols_added ), 4 }, + { "dsr-data_history.start_field", INT, + G_STRUCT_OFFSET( struct dsr, hist.start_field ), 4 }, + { "dsr-data_history.field_skip", INT, + G_STRUCT_OFFSET( struct dsr, hist.field_skip ), 4 }, + { "dsr-data_history.omax", INT, + G_STRUCT_OFFSET( struct dsr, hist.omax ), 4 }, + { "dsr-data_history.omin", INT, + G_STRUCT_OFFSET( struct dsr, hist.omin ), 4 }, + { "dsr-data_history.smax", INT, + G_STRUCT_OFFSET( struct dsr, hist.smax ), 4 }, + { "dsr-data_history.smin", INT, + G_STRUCT_OFFSET( struct dsr, hist.smin ), 4 } +}; + +/* Given a filename, generate the names for the header and the image data. + * + * Eg. + * "fred" -> "fred.hdr", "fred.img" + * "fred.img" -> "fred.hdr", "fred.img" + */ +static void +generate_filenames( const char *path, char *header, char *image ) +{ + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + + const char *olds[] = { ".img", ".hdr" }; + + /* Take off any modifiers. + */ + im_filename_split( path, name, mode ); + + im__change_suffix( name, header, FILENAME_MAX, ".hdr", olds, 2 ); + im__change_suffix( name, image, FILENAME_MAX, ".img", olds, 2 ); +} + +/* str is a str which may not be NULL-terminated. Return a pointer to a static + * buffer with a NULL-terminated version so we can safely printf() the string. + * Also, make sure the string is plain ascii. + */ +static char * +getstr( int mx, const char *str ) +{ + static char buf[256]; + int i; + + assert( mx < 256 ); + + strncpy( buf, str, mx ); + buf[mx]= '\0'; + + /* How annoying, patient_id has some funny ctrlchars in that mess up + * xml encode later. + */ + for( i = 0; i < mx && buf[i]; i++ ) + if( !isascii( buf[i] ) || buf[i] < 32 ) + buf[i] = '@'; + + return( buf ); +} + +#ifdef DEBUG +static void +print_dsr( struct dsr *d ) +{ + int i; + + for( i = 0; i < IM_NUMBER( dsr_header ); i++ ) { + printf( "%s = ", dsr_header[i].name ); + + switch( dsr_header[i].type ) { + case BYTE: + printf( "%d\n", G_STRUCT_MEMBER( char, d, + dsr_header[i].offset ) ); + break; + + case SHORT: + printf( "%d\n", G_STRUCT_MEMBER( short, d, + dsr_header[i].offset ) ); + break; + + case INT: + printf( "%d\n", G_STRUCT_MEMBER( int, d, + dsr_header[i].offset ) ); + break; + + case FLOAT: + printf( "%g\n", G_STRUCT_MEMBER( float, d, + dsr_header[i].offset ) ); + break; + + case STRING: + printf( "\"%s\"\n", getstr( dsr_header[i].len, + &G_STRUCT_MEMBER( char, d, + dsr_header[i].offset ) ) ); + break; + + default: + assert( 0 ); + } + } +} +#endif /*DEBUG*/ + +static struct dsr * +read_header( const char *header ) +{ + struct dsr *d; + unsigned int len; + + if( !(d = (struct dsr *) im__file_read_name( header, &len )) ) + return( NULL ); + if( len != sizeof( struct dsr ) ) { + im_error( "im_analyze2vips", + _( "header file size incorrect" ) ); + im_free( d ); + return( NULL ); + } + + /* Ouch! Should check at configure time I guess. + */ + assert( sizeof( struct dsr ) == 348 ); + + /* dsr headers are always SPARC byte order (MSB first). Do we need to + * swap? + */ + if( !im_amiMSBfirst() ) { + int i; + + for( i = 0; i < IM_NUMBER( dsr_header ); i++ ) { + unsigned char *p; + + + switch( dsr_header[i].type ) { + case SHORT: + p = &G_STRUCT_MEMBER( unsigned char, d, + dsr_header[i].offset ); + im__read_2byte( 1, p, &p ); + break; + + case INT: + case FLOAT: + p = &G_STRUCT_MEMBER( unsigned char, d, + dsr_header[i].offset ); + im__read_4byte( 1, p, &p ); + break; + + case BYTE: + case STRING: + break; + + default: + assert( 0 ); + } + } + } + + if( len != d->hk.sizeof_hdr ) { + im_error( "im_analyze2vips", + _( "header file size incorrect" ) ); + im_free( d ); + return( NULL ); + } + + return( d ); +} + +/* Try to get VIPS header properties from a dsr. + */ +static int +get_vips_properties( struct dsr *d, + int *width, int *height, int *bands, int *fmt ) +{ + int i; + + if( d->dime.dim[0] < 2 || d->dime.dim[0] > 7 ) { + im_error( "im_analyze2vips", + _( "%d-dimensional images not supported" ), + d->dime.dim[0] ); + return( -1 ); + } + + /* Size of base 2d images. + */ + *width = d->dime.dim[1]; + *height = d->dime.dim[2]; + + for( i = 3; i <= d->dime.dim[0]; i++ ) + *height *= d->dime.dim[i]; + + /* Check it's a datatype we can handle. + */ + switch( d->dime.datatype ) { + case DT_UNSIGNED_CHAR: + *bands = 1; + *fmt = IM_BANDFMT_UCHAR; + break; + + case DT_SIGNED_SHORT: + *bands = 1; + *fmt = IM_BANDFMT_SHORT; + break; + + case DT_SIGNED_INT: + *bands = 1; + *fmt = IM_BANDFMT_INT; + break; + + case DT_FLOAT: + *bands = 1; + *fmt = IM_BANDFMT_FLOAT; + break; + + case DT_COMPLEX: + *bands = 1; + *fmt = IM_BANDFMT_COMPLEX; + break; + + case DT_DOUBLE: + *bands = 1; + *fmt = IM_BANDFMT_DOUBLE; + break; + + case DT_RGB: + *bands = 3; + *fmt = IM_BANDFMT_UCHAR; + break; + + default: + im_error( "im_analyze2vips", + _( "datatype %d not supported" ), d->dime.datatype ); + return( -1 ); + } + +#ifdef DEBUG + printf( "get_vips_properties: width = %d\n", *width ); + printf( "get_vips_properties: height = %d\n", *height ); + printf( "get_vips_properties: bands = %d\n", *bands ); + printf( "get_vips_properties: fmt = %d\n", *fmt ); +#endif /*DEBUG*/ + + return( 0 ); +} + +static void +attach_meta( IMAGE *out, struct dsr *d ) +{ + int i; + + im_meta_set_blob( out, "dsr", + (im_callback_fn) im_free, d, d->hk.sizeof_hdr ); + + for( i = 0; i < IM_NUMBER( dsr_header ); i++ ) { + switch( dsr_header[i].type ) { + case BYTE: + im_meta_set_int( out, dsr_header[i].name, + G_STRUCT_MEMBER( char, d, + dsr_header[i].offset ) ); + break; + + case SHORT: + im_meta_set_int( out, dsr_header[i].name, + G_STRUCT_MEMBER( short, d, + dsr_header[i].offset ) ); + break; + + case INT: + im_meta_set_int( out, dsr_header[i].name, + G_STRUCT_MEMBER( int, d, + dsr_header[i].offset ) ); + break; + + case FLOAT: + im_meta_set_double( out, dsr_header[i].name, + G_STRUCT_MEMBER( float, d, + dsr_header[i].offset ) ); + break; + + case STRING: + im_meta_set_string( out, dsr_header[i].name, + getstr( dsr_header[i].len, + &G_STRUCT_MEMBER( char, d, + dsr_header[i].offset ) ) ); + break; + + default: + assert( 0 ); + } + } +} + +int +im_isanalyze( const char *filename ) +{ + char header[FILENAME_MAX]; + char image[FILENAME_MAX]; + struct dsr *d; + int width, height; + int bands; + int fmt; + + generate_filenames( filename, header, image ); + if( !(d = read_header( header )) ) + return( 0 ); + +#ifdef DEBUG + print_dsr( d ); +#endif /*DEBUG*/ + + if( get_vips_properties( d, &width, &height, &bands, &fmt ) ) { + im_free( d ); + return( 0 ); + } + im_free( d ); + + return( 1 ); +} + +int +im_analyze2vips_header( const char *filename, IMAGE *out ) +{ + char header[FILENAME_MAX]; + char image[FILENAME_MAX]; + struct dsr *d; + int width, height; + int bands; + int fmt; + + generate_filenames( filename, header, image ); + if( !(d = read_header( header )) ) + return( -1 ); + +#ifdef DEBUG + print_dsr( d ); +#endif /*DEBUG*/ + + if( get_vips_properties( d, &width, &height, &bands, &fmt ) ) { + im_free( d ); + return( -1 ); + } + + im_initdesc( out, width, height, bands, im_bits_of_fmt( fmt ), fmt, + IM_CODING_NONE, + bands == 1 ? IM_TYPE_B_W : IM_TYPE_sRGB, + 1.0, 1.0, + 0, 0 ); + + attach_meta( out, d ); + + return( 0 ); +} + +int +im_analyze2vips( const char *filename, IMAGE *out ) +{ + char header[FILENAME_MAX]; + char image[FILENAME_MAX]; + struct dsr *d; + IMAGE *t[2]; + int width, height; + int bands; + int fmt; + im_arch_type arch = im_amiMSBfirst() ? + IM_ARCH_NATIVE : IM_ARCH_BYTE_SWAPPED; + + generate_filenames( filename, header, image ); + if( !(d = read_header( header )) ) + return( -1 ); + +#ifdef DEBUG + print_dsr( d ); +#endif /*DEBUG*/ + + if( get_vips_properties( d, &width, &height, &bands, &fmt ) || + im_open_local_array( out, t, 2, "im_analyze2vips", "p" ) || + im_raw2vips( image, t[0], width, height, + bands * im_bits_of_fmt( fmt ) / 8, 0 ) || + im_copy_morph( t[0], t[1], bands, fmt, IM_CODING_NONE ) || + im_copy_from( t[1], out, arch ) ) { + im_free( d ); + return( -1 ); + } + + attach_meta( out, d ); + + return( 0 ); +} + diff --git a/libsrc/conversion/im_bandjoin.c b/libsrc/conversion/im_bandjoin.c new file mode 100644 index 00000000..98da268e --- /dev/null +++ b/libsrc/conversion/im_bandjoin.c @@ -0,0 +1,161 @@ +/* @(#) Function to perform a band-wise join of two images. If the two images + * @(#) have n and m bands respectively, then the output image will have n+m + * @(#) bands, with the first n coming from the first image and the last m + * @(#) from the second. Works for any image type. + * @(#) + * @(#) Function im_bandjoin() assumes that the imin image + * @(#) is either memory mapped or in the buffer pimin->data. + * @(#) + * @(#) int im_bandjoin(imin1, imin2, imout) + * @(#) IMAGE *imin1, *imin2, *imout; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 12/02/1990 + * Modified on : 07/03/1991, by N. Dessipris, history removed + * 27/10/93 JC + * - adapted for partials + * - Nicos formatting removed + * - ANSIfied + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Bandjoin generate function. + */ +static int +bandjoin_gen( REGION *or, REGION **ir ) +{ + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int x, y, z; + int i1s = IM_IMAGE_SIZEOF_PEL( ir[0]->im ); /* sizeof LHS, RHS pels */ + int i2s = IM_IMAGE_SIZEOF_PEL( ir[1]->im ); + + /* Ask for input we need. + */ + if( im_prepare( ir[0], r ) ) + return( -1 ); + if( im_prepare( ir[1], r ) ) + return( -1 ); + + /* Perform join. + */ + for( y = to; y < bo; y++ ) { + PEL *i1 = (PEL *) IM_REGION_ADDR( ir[0], le, y ); + PEL *i2 = (PEL *) IM_REGION_ADDR( ir[1], le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + for( x = le; x < ri; x++ ) { + /* Copy bytes from first file. + */ + for( z = 0; z < i1s; z++ ) + *q++ = *i1++; + + /* Copy bytes from in2. + */ + for( z = 0; z < i2s; z++ ) + *q++ = *i2++; + } + } + + return( 0 ); +} + +/* Join two images. out->Bands = in1->Bands + in2->Bands. in1 goes first in + * the list. + */ +int +im_bandjoin( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE **in; + + /* Check our args. + */ + if( im_piocheck( in1, out ) ) + return( -1 ); + if( im_piocheck( in2, out ) ) + return( -1 ); + if( in1->Xsize != in2->Xsize || + in1->Ysize != in2->Ysize ) { + im_errormsg( "im_bandjoin: images not same size" ); + return( -1 ); + } + if( in1->BandFmt != in2->BandFmt ) { + im_errormsg( "im_bandjoin: images not same type" ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_errormsg( "im_bandjoin: input coded" ); + return( -1 ); + } + + /* Set up the output header. + */ + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + out->Bands = in1->Bands + in2->Bands; + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in1, in2, NULL ) ) + return( -1 ); + + /* Make input array. + */ + if( !(in = im_allocate_input_array( out, in1, in2, NULL )) ) + return( -1 ); + + /* Make output image. + */ + if( im_generate( out, + im_start_many, bandjoin_gen, im_stop_many, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_bernd.c b/libsrc/conversion/im_bernd.c new file mode 100644 index 00000000..c220e303 --- /dev/null +++ b/libsrc/conversion/im_bernd.c @@ -0,0 +1,94 @@ +/* @(#) Extract a tile from a pyramid as a jpeg + * @(#) + * @(#) int + * @(#) im_bernd( const char *tiffname, + * @(#) int x, int y, int w, int h ) + * @(#) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 7/5/99 JC + * - from im_tiff2vips and im_vips2jpeg, plus some stuff from Steve + * 11/7/01 JC + * - page number now in filename + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +extract( IMAGE *in, int x, int y, int w, int h ) +{ + int len; + char *buf; + IMAGE *t1 = im_open_local( in, "im_bernd:2", "p" ); + + if( im_extract_area( in, t1, x, y, w, h ) || + im_vips2bufjpeg( t1, in, 75, &buf, &len ) ) + return( -1 ); + + fwrite( buf, sizeof( char ), len, stdout ); + fflush( stdout ); + + if( ferror( stdout ) ) { + im_errormsg( "im_bernd: error writing output" ); + return( -1 ); + } + + return( 0 ); +} + +int +im_bernd( const char *tiffname, int x, int y, int w, int h ) +{ + IMAGE *in; + + if( !(in = im_open( "im_bernd:1", "p" )) ) + return( -1 ); + if( im_tiff2vips( tiffname, in ) || + extract( in, x, y, w, h ) ) { + im_close( in ); + return( -1 ); + } + im_close( in ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_black.c b/libsrc/conversion/im_black.c new file mode 100644 index 00000000..e9578991 --- /dev/null +++ b/libsrc/conversion/im_black.c @@ -0,0 +1,123 @@ +/* @(#) Make a black uchar image of a specified size. Sometimes useful for + * @(#) building masks. + * @(#) IMAGE out should nhave been set by the calling program + * @(#) + * @(#) int + * @(#) im_black(out, x, y, bands) + * @(#) IMAGE *out; + * @(#) int x, y; + * @(#) int bands; + * @(#) + * @(#) Returns 0 on success and -1 on error. + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/08/1990 + * Modified on : 16/04/1991 by N. Dessipris to work on a line by line basis + * 15/8/94 JC + * - adapted for partials + * - ANSIfied + * - memory leaks fixed! + * 2/3/98 JC + * - IM_ANY added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Generate function --- just black out the region. + */ +static int +black_gen( REGION *or ) +{ + int y; + int sz = IM_REGION_SIZEOF_LINE( or ); + int ls = IM_REGION_LSKIP( or ); + char *q = IM_REGION_ADDR( or, or->valid.left, or->valid.top ); + + for( y = 0; y < or->valid.height; y++, q += ls ) + memset( q, 0, sz ); + + return( 0 ); +} + +/* Make a one band black uchar image of a specified size. + */ +int +im_black( IMAGE *out, int x, int y, int bands ) +{ + int type; + + /* Check parameters. + */ + if( x < 0 || y < 0 || bands < 0 ) { + im_errormsg( "im_black: bad parameter" ); + return( -1 ); + } + + /* Check descriptor. + */ + if( im_poutcheck( out ) ) + return( -1 ); + + /* Set fields. + */ + if( bands == 1 ) + type = IM_TYPE_B_W; + else + type = IM_TYPE_MULTIBAND; + im_initdesc( out, + x, y, bands, + IM_BBITS_BYTE, IM_BANDFMT_UCHAR, IM_CODING_NONE, type, + 1.0, 1.0, 0, 0 ); + + /* Set hints - ANY is ok with us. + */ + if( im_demand_hint( out, IM_ANY, NULL ) ) + return( -1 ); + + /* Generate image. + */ + if( im_generate( out, NULL, black_gen, NULL, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_c2amph.c b/libsrc/conversion/im_c2amph.c new file mode 100644 index 00000000..112ce044 --- /dev/null +++ b/libsrc/conversion/im_c2amph.c @@ -0,0 +1,142 @@ +/* @(#) Functions which transforms the real and the imaginary parts of + * @(#) a complex image into amplitude and phase. + * @(#) Input image is either memory mapped or in a buffer. + * @(#) Used to display an inverse complex Fourier transform + * @(#) + * @(#) int im_c2amph(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : 09/05/1990 + * 15/6/93 JC + * - stupid stupid includes and externs fixed + * - I have been editing for 1 1/2 hours and I'm still drowning in + * rubbish extetrnshh + * 13/12/94 JC + * - modernised + * 9/7/02 JC + * - degree output, for consistency + * - slightly better behaviour in edge cases + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define loop(TYPE) \ +{\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + int x;\ + \ + for( x = 0; x < n; x++ ) {\ + double re = p[0];\ + double im = p[1];\ + double am, ph;\ + \ + am = sqrt( re * re + im * im );\ + \ + if( re == 0 ) { \ + if( im < 0.0 ) \ + ph = 270; \ + else if( im == 0.0 ) \ + ph = 0; \ + else \ + ph = 90; \ + } \ + else { \ + double t = atan( im / re ); \ + \ + if( re > 0.0 ) \ + if( im < 0.0 ) \ + ph = IM_DEG( t + IM_PI * 2.0 ); \ + else \ + ph = IM_DEG( t ); \ + else \ + ph = IM_DEG( t + IM_PI ); \ + } \ + \ + q[0] = am; \ + q[1] = ph; \ + \ + p += 2; \ + q += 2; \ + }\ +} + +/* c2amph buffer processor. + */ +static void +buffer_c2amph( void *in, void *out, int w, IMAGE *im ) +{ + int n = w * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_DPCOMPLEX: loop(double); break; + case IM_BANDFMT_COMPLEX: loop(float); break; + default: + error_exit( "buffer_c2amph: internal error" ); + } +} + +int +im_c2amph( IMAGE *in, IMAGE *out ) +{ + if( in->Coding != IM_CODING_NONE || !im_iscomplex( in ) ) { + im_errormsg( "im_c2amph: input should be uncoded complex" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) buffer_c2amph, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_c2imag.c b/libsrc/conversion/im_c2imag.c new file mode 100644 index 00000000..504d1853 --- /dev/null +++ b/libsrc/conversion/im_c2imag.c @@ -0,0 +1,117 @@ +/* @(#) Extract the imaginary part of a complex image. Output is float or + * @(#) double. + * @(#) + * @(#) int im_c2imag( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : 09/05/1990 + * 21/12/94 JC + * - im_c2amph() adapted to make im_c2real() and im_c2imag() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define loop(TYPE) \ +{\ + TYPE *p = (TYPE *) in + 1;\ + TYPE *q = (TYPE *) out;\ + int x;\ + \ + for( x = 0; x < n; x++ ) {\ + double re = *p;\ + \ + p += 2;\ + *q++ = re;\ + }\ +} + +/* c2imag buffer processor. + */ +static void +buffer_c2imag( void *in, void *out, int w, IMAGE *im ) +{ + int n = w * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_DPCOMPLEX: loop(double); break; + case IM_BANDFMT_COMPLEX: loop(float); break; + default: + error_exit( "buffer_c2imag: internal error" ); + } +} + +int +im_c2imag( IMAGE *in, IMAGE *out ) +{ + if( in->Coding != IM_CODING_NONE || !im_iscomplex( in ) ) { + im_errormsg( "im_c2imag: input should be uncoded complex" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Output will be float or double. + */ + if( in->BandFmt == IM_BANDFMT_DPCOMPLEX ) { + out->BandFmt = IM_BANDFMT_DOUBLE; + out->Bbits = IM_BBITS_DOUBLE; + } + else { + out->BandFmt = IM_BANDFMT_FLOAT; + out->Bbits = IM_BBITS_FLOAT; + } + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) buffer_c2imag, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_c2ps.c b/libsrc/conversion/im_c2ps.c new file mode 100644 index 00000000..75303191 --- /dev/null +++ b/libsrc/conversion/im_c2ps.c @@ -0,0 +1,64 @@ +/* @(#) Find the power spectrum of a complex image. + * @(#) + * @(#) int im_c2ps( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : 09/05/1990 + * 21/12/94 JC + * - im_c2imag() adapted to make new im_c2ps() + * 8/5/95 JC + * - now just calls im_abs() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_c2ps( IMAGE *in, IMAGE *out ) +{ + return( im_abs( in, out ) ); +} diff --git a/libsrc/conversion/im_c2real.c b/libsrc/conversion/im_c2real.c new file mode 100644 index 00000000..0a14a30b --- /dev/null +++ b/libsrc/conversion/im_c2real.c @@ -0,0 +1,122 @@ +/* @(#) Extract the real part of a complex image. Output is float or double. + * @(#) + * @(#) int im_c2real( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : 09/05/1990 + * 15/6/93 JC + * - stupid stupid includes and externs fixed + * - I have been editing for 1 1/2 hours and I'm still drowning in + * rubbish extetrnshh + * 13/12/94 JC + * - modernised + * 21/12/94 JC + * - im_c2amph() adapted to make im_c2real() and im_c2imag() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define loop(TYPE) \ +{\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + int x;\ + \ + for( x = 0; x < n; x++ ) {\ + double re = *p;\ + \ + p += 2;\ + *q++ = re;\ + }\ +} + +/* c2real buffer processor. + */ +static void +buffer_c2real( void *in, void *out, int w, IMAGE *im ) +{ + int n = w * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_DPCOMPLEX: loop(double); break; + case IM_BANDFMT_COMPLEX: loop(float); break; + default: + error_exit( "buffer_c2real: internal error" ); + } +} + +int +im_c2real( IMAGE *in, IMAGE *out ) +{ + if( in->Coding != IM_CODING_NONE || !im_iscomplex( in ) ) { + im_errormsg( "im_c2real: input should be uncoded complex" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Output will be float or double. + */ + if( in->BandFmt == IM_BANDFMT_DPCOMPLEX ) { + out->BandFmt = IM_BANDFMT_DOUBLE; + out->Bbits = IM_BBITS_DOUBLE; + } + else { + out->BandFmt = IM_BANDFMT_FLOAT; + out->Bbits = IM_BBITS_FLOAT; + } + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) buffer_c2real, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_c2rect.c b/libsrc/conversion/im_c2rect.c new file mode 100644 index 00000000..a4c1a13f --- /dev/null +++ b/libsrc/conversion/im_c2rect.c @@ -0,0 +1,108 @@ +/* @(#) Turn (amplitude, phase) image to (x, y) rectangular coordinates. + * @(#) + * @(#) int im_c2rect(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * 9/7/02 JC + * - from im_c2amph() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define loop(TYPE) \ +{\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + int x;\ + \ + for( x = 0; x < n; x++ ) {\ + double am = p[0];\ + double ph = p[1];\ + double re, im;\ + \ + re = am * cos( IM_RAD( ph ) ); \ + im = am * sin( IM_RAD( ph ) ); \ + \ + q[0] = re; \ + q[1] = im; \ + \ + p += 2; \ + q += 2; \ + }\ +} + +/* c2rect buffer processor. + */ +static void +buffer_c2rect( void *in, void *out, int w, IMAGE *im ) +{ + int n = w * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_DPCOMPLEX: loop(double); break; + case IM_BANDFMT_COMPLEX: loop(float); break; + default: + error_exit( "buffer_c2rect: internal error" ); + } +} + +int +im_c2rect( IMAGE *in, IMAGE *out ) +{ + if( in->Coding != IM_CODING_NONE || !im_iscomplex( in ) ) { + im_errormsg( "im_c2rect: input should be uncoded complex" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) buffer_c2rect, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_clip.c b/libsrc/conversion/im_clip.c new file mode 100644 index 00000000..d457afad --- /dev/null +++ b/libsrc/conversion/im_clip.c @@ -0,0 +1,484 @@ +/* @(#) Clip any down to 0-255. Call im_copy if the image is already uchar. + * @(#) + * @(#) int + * @(#) im_clip( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Author: Nicos Dessipris + * Written on: 07/03/1991 + * Modified on: + * 04/05/1992 JC + * - works for char, uchar too + * - floating point code removed from integer clip operations + * - uses nint() instead of own rounding code + * - calculated the number of >255 clips for float/double input + * incorrectly + * - rejects complex input correctly now + * 27/4/93 JC + * - adapted to work with partial images + * - nint() removed, now just +0.5 + * - im_warning code removed + * 30/6/93 JC + * - adapted for partial v2 + * 31/8/93 JC + * - now detects and prints over/underflows + * 27/10/93 JC + * - unsigned integer clips now faster! + * - falls back to im_copy() correctly + * 5/5/94 JC + * - switched to rint() + * 18/8/94 JC + * - now uses evalend callback + * 9/5/95 JC + * - now does complex too + * 11/7/95 JC + * - now uses IM_RINT() macro + * 10/3/01 JC + * - slightly faster and simpler + * - generalised to im_clip2fmt(), all other clippers now just call + * this + * 21/4/04 JC + * - now does floor(), not rint() ... you'll need to round yourself + * before calling this if you want round-to-nearest + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Global state. Track over/under-flows for all sequences in this. + */ +typedef struct { + IMAGE *in; /* Parameters */ + IMAGE *out; + int ofmt; + + int underflow; /* Number of underflows */ + int overflow; /* Number of overflows */ +} Clip; + +static int +clip_destroy( Clip *clip ) +{ + /* Print warnings, if necessary. + */ + if( clip->overflow || clip->underflow ) + im_warn( "im_clip", + _( "%d underflows and %d overflows detected" ), + clip->underflow, clip->overflow ); + + return( 0 ); +} + +/* Build a Clip. + */ +static Clip * +clip_new( IMAGE *in, IMAGE *out, int ofmt ) +{ + Clip *clip = IM_NEW( out, Clip ); + + if( !clip ) + return( NULL ); + + clip->in = in; + clip->out = out; + clip->ofmt = ofmt; + clip->underflow = 0; + clip->overflow = 0; + + if( im_add_close_callback( out, + (im_callback_fn) clip_destroy, clip, NULL ) ) + return( NULL ); + + return( clip ); +} + +/* Our sequence value: the region this sequence is using, and two local stats. + */ +typedef struct { + REGION *ir; /* Input region */ + int underflow; /* Number of underflows */ + int overflow; /* Number of overflows */ +} ClipSequence; + +/* Destroy a sequence value. + */ +static int +stop_clip( ClipSequence *seq, IMAGE *in, Clip *clip ) +{ + /* Add to global stats. + */ + clip->underflow += seq->underflow; + clip->overflow += seq->overflow; + + /* Junk our region too. + */ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Make a sequence value. + */ +static void * +start_clip( IMAGE *out, IMAGE *in, Clip *clip ) +{ + ClipSequence *seq = IM_NEW( out, ClipSequence ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->overflow = 0; + seq->underflow = 0; + + if( !(seq->ir = im_region_create( in )) ) + return( NULL ); + + return( (void *) seq ); +} + +/* Clip int types to an int type. + */ +#define IM_CLIP_INT_INT( ITYPE, OTYPE, IM_CLIP ) { \ + ITYPE *p = (ITYPE *) IM_REGION_ADDR( ir, le, y );\ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ ) {\ + int t = p[x];\ + \ + IM_CLIP( t, seq );\ + \ + q[x] = t;\ + }\ +} + +/* Clip float types to an int type. + */ +#define IM_CLIP_FLOAT_INT( ITYPE, OTYPE, IM_CLIP ) { \ + ITYPE *p = (ITYPE *) IM_REGION_ADDR( ir, le, y );\ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ ) {\ + ITYPE v = floor( p[x] );\ + \ + IM_CLIP( v, seq ); \ + \ + q[x] = v;\ + }\ +} + +/* Clip complex types to an int type. Just take the real part. + */ +#define IM_CLIP_COMPLEX_INT( ITYPE, OTYPE, IM_CLIP ) { \ + ITYPE *p = (ITYPE *) IM_REGION_ADDR( ir, le, y );\ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ ) {\ + ITYPE v = floor( p[0] );\ + p += 2;\ + \ + IM_CLIP( v, seq ); \ + \ + q[x] = v;\ + }\ +} + +/* Clip non-complex types to a float type. + */ +#define IM_CLIP_REAL_FLOAT( ITYPE, OTYPE ) { \ + ITYPE *p = (ITYPE *) IM_REGION_ADDR( ir, le, y );\ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = p[x];\ +} + +/* Clip complex types to a float type ... just take real. + */ +#define IM_CLIP_COMPLEX_FLOAT( ITYPE, OTYPE ) { \ + ITYPE *p = (ITYPE *) IM_REGION_ADDR( ir, le, y );\ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ ) {\ + q[x] = p[0];\ + p += 2;\ + }\ +} + +/* Clip any to a complex type ... set imaginary to zero. + */ +#define IM_CLIP_ANY_COMPLEX( ITYPE, OTYPE ) { \ + ITYPE *p = (ITYPE *) IM_REGION_ADDR( ir, le, y );\ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ ) {\ + q[0] = p[x];\ + q[1] = 0.0;\ + q += 2;\ + }\ +} + +#define BAND_SWITCH_INNER( ITYPE, INT, FLOAT, COMPLEX ) { \ + switch( clip->out->BandFmt ) { \ + case IM_BANDFMT_UCHAR: \ + INT( ITYPE, unsigned char, IM_CLIP_UCHAR ); \ + break; \ + case IM_BANDFMT_CHAR: \ + INT( ITYPE, signed char, IM_CLIP_CHAR ); \ + break; \ + case IM_BANDFMT_USHORT: \ + INT( ITYPE, unsigned short, IM_CLIP_USHORT ); \ + break; \ + case IM_BANDFMT_SHORT: \ + INT( ITYPE, signed short, IM_CLIP_SHORT ); \ + break; \ + case IM_BANDFMT_UINT: \ + INT( ITYPE, unsigned int, IM_CLIP_NONE ); \ + break; \ + case IM_BANDFMT_INT: \ + INT( ITYPE, signed int, IM_CLIP_NONE ); \ + break; \ + case IM_BANDFMT_FLOAT: \ + FLOAT( ITYPE, float ); \ + break; \ + case IM_BANDFMT_DOUBLE: \ + FLOAT( ITYPE, double ); \ + break; \ + case IM_BANDFMT_COMPLEX: \ + COMPLEX( ITYPE, float ); \ + break; \ + case IM_BANDFMT_DPCOMPLEX: \ + COMPLEX( ITYPE, double ); \ + break; \ + default: \ + assert( 0 ); \ + } \ +} + +/* Clip a small area. + */ +static int +clip_gen( REGION *or, ClipSequence *seq, IMAGE *in, Clip *clip ) +{ + REGION *ir = seq->ir; + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + int x, y; + + if( im_prepare( ir, r ) ) + return( -1 ); + + for( y = to; y < bo; y++ ) { + switch( clip->in->BandFmt ) { + case IM_BANDFMT_UCHAR: + BAND_SWITCH_INNER( unsigned char, + IM_CLIP_INT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_CHAR: + BAND_SWITCH_INNER( signed char, + IM_CLIP_INT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_USHORT: + BAND_SWITCH_INNER( unsigned short, + IM_CLIP_INT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_SHORT: + BAND_SWITCH_INNER( signed short, + IM_CLIP_INT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_UINT: + BAND_SWITCH_INNER( unsigned int, + IM_CLIP_INT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_INT: + BAND_SWITCH_INNER( signed int, + IM_CLIP_INT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_FLOAT: + BAND_SWITCH_INNER( float, + IM_CLIP_FLOAT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_DOUBLE: + BAND_SWITCH_INNER( double, + IM_CLIP_FLOAT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_COMPLEX: + BAND_SWITCH_INNER( float, + IM_CLIP_COMPLEX_INT, IM_CLIP_COMPLEX_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_DPCOMPLEX: + BAND_SWITCH_INNER( double, + IM_CLIP_COMPLEX_INT, IM_CLIP_COMPLEX_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + default: + assert( 0 ); + } + } + + return( 0 ); +} + +/* Clip to any format. + */ +int +im_clip2fmt( IMAGE *in, IMAGE *out, int ofmt ) +{ + Clip *clip; + + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_clip2fmt", _( "in must be uncoded" ) ); + return( -1 ); + } + if( ofmt < 0 || ofmt > IM_BANDFMT_DPCOMPLEX ) { + im_error( "im_clip2fmt", _( "ofmt out of range" ) ); + return( -1 ); + } + + /* Trivial case: fall back to im_copy(). + */ + if( in->BandFmt == ofmt ) + return( im_copy( in, out ) ); + + if( !(clip = clip_new( in, out, ofmt )) ) + return( -1 ); + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->BandFmt = ofmt; + out->Bbits = im_bits_of_fmt( ofmt ); + + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) || + im_generate( out, start_clip, clip_gen, stop_clip, in, clip ) ) + return( -1 ); + + return( 0 ); +} + +/* Legacy clippers. + */ +int +im_clip( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_UCHAR ) ); +} + +int +im_clip2c( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_CHAR ) ); +} + +int +im_clip2us( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_USHORT ) ); +} + +int +im_clip2s( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_SHORT ) ); +} + +int +im_clip2ui( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_UINT ) ); +} + +int +im_clip2i( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_INT ) ); +} + +int +im_clip2f( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_FLOAT ) ); +} + +int +im_clip2d( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_DOUBLE ) ); +} + +int +im_clip2cm( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_COMPLEX ) ); +} + +int +im_clip2dcm( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_DPCOMPLEX ) ); +} diff --git a/libsrc/conversion/im_copy.c b/libsrc/conversion/im_copy.c new file mode 100644 index 00000000..43326969 --- /dev/null +++ b/libsrc/conversion/im_copy.c @@ -0,0 +1,530 @@ +/* @(#) Copy an image. + * @(#) + * @(#) int + * @(#) im_copy( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Copy and set informational header fields + * @(#) + * @(#) int im_copy_set( in, out, type, xres, yres, xoff, yoff ) + * @(#) IMAGE *in, *out; + * @(#) int type; + * @(#) float xres, yres; + * @(#) int xoff, yoff; + * @(#) + * @(#) copy, swapping byte order + * @(#) + * @(#) int + * @(#) im_copy_swap( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 23/4/93 J.Cupitt + * - adapted to work with partial images + * 30/6/93 JC + * - adapted for partial v2 + * - and ANSI C + * 7/7/93 JC + * - now does IM_CODING_LABQ too + * 22/2/95 JC + * - new use of im_region_region() + * 25/6/02 JC + * - added im_copy_set() + * - hint is IM_ANY + * 5/9/02 JC + * - added xoff/yoff to copy_set + * 14/4/04 JC + * - im_copy() now zeros Xoffset/Yoffset (since origin is the same as + * input) + * 26/5/04 JC + * - added im_copy_swap() + * 1/6/05 + * - added im_copy_morph() + * 13/6/05 + * - oop, im_copy_set() was messed up + * 29/9/06 + * - added im_copy_set_meta(), handy wrapper for nip2 to set meta fields + * 2/11/06 + * - moved im__convert_saveable() here so it's always defined (was part + * of JPEG write code) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Copy a small area. + */ +static int +copy_gen( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + + /* Ask for input we need. + */ + if( im_prepare( ir, r ) ) + return( -1 ); + + /* Attach output region to that. + */ + if( im_region_region( or, ir, r, r->left, r->top ) ) + return( -1 ); + + return( 0 ); +} + +/* Copy image, changing header fields. + */ +static int +im_copy_set_all( IMAGE *in, IMAGE *out, + int Type, float Xres, float Yres, int Xoffset, int Yoffset, + int Bands, int BandFmt, int Coding ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_copy", _( "in must be uncoded" ) ); + return( -1 ); + } + if( Coding != IM_CODING_NONE && Coding != IM_CODING_LABQ ) { + im_error( "im_copy", _( "Coding must be NONE or LABQ" ) ); + return( -1 ); + } + if( BandFmt < 0 || BandFmt > IM_BANDFMT_DPCOMPLEX ) { + im_error( "im_copy", _( "BandFmt must be in range [0,%d]" ), + IM_BANDFMT_DPCOMPLEX ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = Type; + out->Xres = Xres; + out->Yres = Yres; + out->Xoffset = Xoffset; + out->Yoffset = Yoffset; + out->Bands = Bands; + out->BandFmt = BandFmt; + out->Coding = Coding; + out->Bbits = im_bits_of_fmt( BandFmt ); + + /* Sanity check: we (may) have changed bytes-per-pixel since we've + * changed Bands and BandFmt ... bad! + */ + if( IM_IMAGE_SIZEOF_PEL( in ) != IM_IMAGE_SIZEOF_PEL( out ) ) { + im_error( "im_copy", _( "sizeof( pixel ) has changed" ) ); + return( -1 ); + } + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, im_start_one, copy_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* Copy image, changing informational header fields. + */ +int +im_copy_set( IMAGE *in, IMAGE *out, + int Type, float Xres, float Yres, int Xoffset, int Yoffset ) +{ + return( im_copy_set_all( in, out, + Type, Xres, Yres, 0, 0, + in->Bands, in->BandFmt, in->Coding ) ); +} + +/* Copy image, changing nothing. + */ +int +im_copy( IMAGE *in, IMAGE *out ) +{ + return( im_copy_set( in, out, + in->Type, in->Xres, in->Yres, 0, 0 ) ); +} + +/* Copy image, changing fields which affect pixel layout. + */ +int +im_copy_morph( IMAGE *in, IMAGE *out, + int Bands, int BandFmt, int Coding ) +{ + return( im_copy_set_all( in, out, + in->Type, in->Xres, in->Yres, 0, 0, + Bands, BandFmt, Coding ) ); +} + +int +im_copy_set_meta( IMAGE *in, IMAGE *out, const char *field, GValue *value ) +{ + if( im_copy( in, out ) || + im_meta_set( out, field, value ) ) + return( 1 ); + + return( 0 ); +} + +/* Swap pairs of bytes. + */ +static void +im_copy_swap2_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = IM_IMAGE_SIZEOF_PEL( im ) * width; /* Bytes in buffer */ + + for( x = 0; x < sz; x += 2 ) { + out[x] = in[x + 1]; + out[x + 1] = in[x]; + } +} + +/* Swap 4- of bytes. + */ +static void +im_copy_swap4_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = IM_IMAGE_SIZEOF_PEL( im ) * width; /* Bytes in buffer */ + + for( x = 0; x < sz; x += 4 ) { + out[x] = in[x + 3]; + out[x + 1] = in[x + 2]; + out[x + 2] = in[x + 1]; + out[x + 3] = in[x]; + } +} + +/* Swap 8- of bytes. + */ +static void +im_copy_swap8_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = IM_IMAGE_SIZEOF_PEL( im ) * width; /* Bytes in buffer */ + + for( x = 0; x < sz; x += 8 ) { + out[x] = in[x + 7]; + out[x + 1] = in[x + 6]; + out[x + 2] = in[x + 5]; + out[x + 3] = in[x + 4]; + out[x + 4] = in[x + 3]; + out[x + 5] = in[x + 2]; + out[x + 6] = in[x + 1]; + out[x + 7] = in[x]; + } +} + +/* Copy, swapping byte order between little and big endian. + */ +int +im_copy_swap( IMAGE *in, IMAGE *out ) +{ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_copy_swap", _( "in must be uncoded" ) ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: + case IM_BANDFMT_UCHAR: + if( im_copy( in, out ) ) + return( -1 ); + break; + + case IM_BANDFMT_SHORT: + case IM_BANDFMT_USHORT: + if( im_wrapone( in, out, + (im_wrapone_fn) im_copy_swap2_gen, in, NULL ) ) + return( -1 ); + break; + + case IM_BANDFMT_INT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_COMPLEX: + if( im_wrapone( in, out, + (im_wrapone_fn) im_copy_swap4_gen, in, NULL ) ) + return( -1 ); + break; + + case IM_BANDFMT_DOUBLE: + case IM_BANDFMT_DPCOMPLEX: + if( im_wrapone( in, out, + (im_wrapone_fn) im_copy_swap8_gen, in, NULL ) ) + return( -1 ); + break; + + default: + im_error( "im_copy_swap", _( "unsupported image type" ) ); + return( -1 ); + } + + return( 0 ); +} + +int +im_copy_from( IMAGE *in, IMAGE *out, im_arch_type architecture ) +{ + switch( architecture ) { + case IM_ARCH_NATIVE: + return( im_copy( in, out ) ); + + case IM_ARCH_BYTE_SWAPPED: + return( im_copy_swap( in, out ) ); + + case IM_ARCH_LSB_FIRST: + return( im_amiMSBfirst() ? + im_copy_swap( in, out ) : im_copy( in, out ) ); + + case IM_ARCH_MSB_FIRST: + return( im_amiMSBfirst() ? + im_copy( in, out ) : im_copy_swap( in, out ) ); + + default: + im_error( "im_copy_from", + _( "bad architecture: %d" ), architecture ); + return( -1 ); + } +} + +/* Convert to 1 or 3 band uchar sRGB (or 2/4 band, if allow_alpha is set). + * Need to im_close() the return IMAGE. + */ +IMAGE * +im__convert_saveable( IMAGE *in, gboolean allow_alpha ) +{ + IMAGE *out; + + if( !(out = im_open( "im__convert_saveable", "p" )) ) + return( NULL ); + + /* If this is a IM_CODING_LABQ, we can go straight to RGB. + */ + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + static void *table = NULL; + + /* Make sure fast LabQ2disp tables are built. 7 is sRGB. + */ + if( !table ) + table = im_LabQ2disp_build_table( NULL, + im_col_displays( 7 ) ); + + if( !t || im_LabQ2disp_table( in, t, table ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + + /* Get the bands right. If we have >3, drop down to 3. If we have 2, + * drop down to 1. If allow_alpha is on, we can also have 2/4 bands. + */ + if( in->Coding == IM_CODING_NONE ) { + if( in->Bands == 2 && !allow_alpha ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + + if( !t || im_extract_band( in, t, 0 ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + else if( in->Bands > 3 && !allow_alpha ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + + if( !t || + im_extract_bands( in, t, 0, 3 ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + else if( in->Bands > 4 && allow_alpha ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + + if( !t || + im_extract_bands( in, t, 0, 4 ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + } + + /* Interpret the Type field for colorimetric images. + */ + if( in->Bands == 3 && in->BandFmt == IM_BANDFMT_SHORT && + in->Type == IM_TYPE_LABS ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + + if( !t || im_LabS2LabQ( in, t ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + + if( !t || im_LabQ2Lab( in, t ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + + if( in->Coding != IM_CODING_NONE ) { + im_close( out ); + return( NULL ); + } + + if( in->Bands == 3 && in->Type == IM_TYPE_LCH ) { + IMAGE *t[2]; + + if( im_open_local_array( out, t, 2, "conv-1", "p" ) || + im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || + im_LCh2Lab( t[0], t[1] ) ) { + im_close( out ); + return( NULL ); + } + + in = t[1]; + } + + if( in->Bands == 3 && in->Type == IM_TYPE_YXY ) { + IMAGE *t[2]; + + if( im_open_local_array( out, t, 2, "conv-1", "p" ) || + im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || + im_Yxy2XYZ( t[0], t[1] ) ) { + im_close( out ); + return( NULL ); + } + + in = t[1]; + } + + if( in->Bands == 3 && in->Type == IM_TYPE_UCS ) { + IMAGE *t[2]; + + if( im_open_local_array( out, t, 2, "conv-1", "p" ) || + im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || + im_UCS2XYZ( t[0], t[1] ) ) { + im_close( out ); + return( NULL ); + } + + in = t[1]; + } + + if( in->Bands == 3 && in->Type == IM_TYPE_LAB ) { + IMAGE *t[2]; + + if( im_open_local_array( out, t, 2, "conv-1", "p" ) || + im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || + im_Lab2XYZ( t[0], t[1] ) ) { + im_close( out ); + return( NULL ); + } + + in = t[1]; + } + + if( in->Bands == 3 && in->Type == IM_TYPE_XYZ ) { + IMAGE *t[2]; + + if( im_open_local_array( out, t, 2, "conv-1", "p" ) || + im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || + im_XYZ2disp( t[0], t[1], im_col_displays( 7 ) ) ) { + im_close( out ); + return( NULL ); + } + + in = t[1]; + } + + /* Clip to uchar if not there already. + */ + if( in->BandFmt != IM_BANDFMT_UCHAR ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + + if( !t || im_clip( in, t ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + + if( im_copy( in, out ) ) { + im_close( out ); + return( NULL ); + } + + return( out ); +} + diff --git a/libsrc/conversion/im_csv2vips.c b/libsrc/conversion/im_csv2vips.c new file mode 100644 index 00000000..8f07076e --- /dev/null +++ b/libsrc/conversion/im_csv2vips.c @@ -0,0 +1,316 @@ +/* Read a csv file. + * + * 19/12/05 JC + * - hacked from ppm reader + * 11/9/06 + * - now distingushes whitespace and separators, so we can have blank + * fields + * 20/9/06 + * - oop, unquoted trailing columns could get missed + * 17/5/07 + * - added im_csv2vips_header() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +skip_line( FILE *fp ) +{ + int ch; + + while( (ch = fgetc( fp )) != '\n' && ch != EOF ) + ; + + return( ch ); +} + +static int +skip_white( FILE *fp, const char whitemap[256] ) +{ + int ch; + + do { + ch = fgetc( fp ); + } while (ch != EOF && ch != '\n' && whitemap[ch] ); + + ungetc( ch, fp ); + + return( ch ); +} + +static int +skip_to_sep( FILE *fp, const char sepmap[256] ) +{ + int ch; + + do { + ch = fgetc( fp ); + } while (ch != EOF && ch != '\n' && !sepmap[ch] ); + + ungetc( ch, fp ); + + return( ch ); +} + +/* Read a single item. Syntax is: + * + * item : whitespace* double? whitespace* [EOF|EOL|separator] + * + * Return the char that caused failure on fail (EOF or \n). + */ +static int +read_double( FILE *fp, const char whitemap[256], const char sepmap[256], + int lineno, int colno, double *out ) +{ + int ch; + + /* The fscanf() may change this ... but all other cases need a zero. + */ + *out = 0; + + ch = skip_white( fp, whitemap ); + if( ch == EOF || ch == '\n' ) + return( ch ); + + if( !sepmap[ch] && fscanf( fp, "%lf", out ) != 1 ) { + /* Only a warning, since (for example) exported spreadsheets + * will often have text or date fields. + */ + im_warn( "im_csv2vips", + _( "error parsing number, line %d, column %d" ), + lineno, colno ); + + /* Step over the bad data to the next separator. + */ + ch = skip_to_sep( fp, sepmap ); + } + + /* Don't need to check result, we have read a field successfully. + */ + ch = skip_white( fp, whitemap ); + + /* If it's a separator, we have to step over it. + */ + if( ch != EOF && sepmap[ch] ) + (void) fgetc( fp ); + + return( 0 ); +} + +static int +read_csv( FILE *fp, IMAGE *out, + int start_skip, + const char *whitespace, const char *separator, + int lines ) +{ + int i; + char whitemap[256]; + char sepmap[256]; + const char *p; + fpos_t pos; + int columns; + int ch; + double d; + double *buf; + int y; + + /* Make our char maps. + */ + for( i = 0; i < 256; i++ ) { + whitemap[i] = 0; + sepmap[i] = 0; + } + for( p = whitespace; *p; p++ ) + whitemap[(int) *p] = 1; + for( p = separator; *p; p++ ) + sepmap[(int) *p] = 1; + + /* Skip first few lines. + */ + for( i = 0; i < start_skip; i++ ) + if( skip_line( fp ) == EOF ) { + im_error( "im_csv2vips", + _( "end of file while skipping start" ) ); + return( -1 ); + } + + /* Parse the first line to get number of columns. Only bother checking + * fgetpos() the first time we use it: assume it's working after this. + */ + if( fgetpos( fp, &pos ) ) { + im_error_system( errno, "im_csv2vips", _( "unable to seek" ) ); + return( -1 ); + } + for( columns = 0; (ch = read_double( fp, whitemap, sepmap, + start_skip + 1, columns + 1, &d )) == 0; columns++ ) + ; + fsetpos( fp, &pos ); + + if( columns == 0 ) { + im_error( "im_csv2vips", _( "empty line" ) ); + return( -1 ); + } + if( ch == -2 ) + /* Failed to parse a number. + */ + return( -1 ); + + /* If lines is -1, we have to parse the whole file to get the + * number of lines out. + */ + if( lines == -1 ) { + fgetpos( fp, &pos ); + for( lines = 0; skip_line( fp ) != EOF; lines++ ) + ; + fsetpos( fp, &pos ); + } + + im_initdesc( out, columns, lines, 1, + IM_BBITS_DOUBLE, IM_BANDFMT_DOUBLE, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + + if( im_outcheck( out ) || im_setupout( out ) || + !(buf = IM_ARRAY( out, IM_IMAGE_N_ELEMENTS( out ), double )) ) + return( -1 ); + + for( y = 0; y < lines; y++ ) { + int x; + + for( x = 0; x < columns; x++ ) { + ch = read_double( fp, whitemap, sepmap, + y + start_skip + 1, x + 1, &d ); + if( ch == EOF ) { + im_error( "im_csv2vips", + _( "unexpected end of file" ) ); + return( -1 ); + } + else if( ch == '\n' ) { + im_error( "im_csv2vips", + _( "unexpected end of line" ) ); + return( -1 ); + } + else if( ch ) + /* Parse error. + */ + return( -1 ); + + buf[x] = d; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + + /* Skip over the '\n' to the next line. + */ + skip_line( fp ); + } + + return( 0 ); +} + +int +im_csv2vips( const char *filename, IMAGE *out ) +{ + /* Read options. + */ + int start_skip = 0; + char *whitespace = " \""; + char *separator = ";,\t"; + int lines = -1; + + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char *p, *q, *r; + FILE *fp; + + /* Parse mode string. + */ + im_filename_split( filename, name, mode ); + p = &mode[0]; + while( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "ski", q ) && (r = im_getsuboption( q )) ) + start_skip = atoi( r ); + else if( im_isprefix( "whi", q ) && (r = im_getsuboption( q )) ) + whitespace = r; + else if( im_isprefix( "sep", q ) && (r = im_getsuboption( q )) ) + separator = r; + else if( im_isprefix( "lin", q ) && (r = im_getsuboption( q )) ) + lines = atoi( r ); + } + + if( !(fp = fopen( name, "r" )) ) { + im_error( "im_csv2vips", + _( "unable to open \"%s\"" ), name ); + return( -1 ); + } + + if( read_csv( fp, out, start_skip, whitespace, separator, lines ) ) { + fclose( fp ); + return( -1 ); + } + fclose( fp ); + + return( 0 ); +} + +/* We can't just read the header of a CSV. Instead, we read to a temp image, + * then copy just the header to the output. + */ +int +im_csv2vips_header( const char *filename, IMAGE *out ) +{ + IMAGE *t; + + if( !(t = im_open( "im_csv2vips_header", "p" )) ) + return( -1 ); + if( im_csv2vips( filename, t ) || + im_cp_desc( out, t ) ) { + im_close( t ); + return( -1 ); + } + im_close( t ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_exr2vips.c b/libsrc/conversion/im_exr2vips.c new file mode 100644 index 00000000..4e3d57b0 --- /dev/null +++ b/libsrc/conversion/im_exr2vips.c @@ -0,0 +1,424 @@ +/* Convert OpenEXR to VIPS + * + * 1/5/06 + * - from im_png2vips.c + * 17/5/06 + * - oops, buffer calcs were wrong + * 19/5/06 + * - added tiled read, with a separate cache + * - removed *255 we had before, better to do something clever with + * chromaticities + + - colour management + - attributes + - more of OpenEXR's pixel formats + - more than just RGBA channels + + the openexr C API is very limited ... it seems RGBA half pixels is + all you can do + + openexr lets you have different formats in different channels :-( + + there's no API to read the "chromaticities" attribute :-( + + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_OPENEXR + +#include + +int +im_exr2vips( const char *name, IMAGE *out ) +{ + im_error( "im_exr2vips", _( "OpenEXR support disabled" ) ); + return( -1 ); +} + +int +im_exr2vips_header( const char *name, IMAGE *out ) +{ + im_error( "im_exr2vips_header", _( "OpenEXR support disabled" ) ); + return( -1 ); +} + +#else /*HAVE_OPENEXR*/ + +#include +#include +#include +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* What we track during a OpenEXR read. + */ +typedef struct { + char *name; + IMAGE *out; + + ImfTiledInputFile *tiles; + ImfInputFile *lines; + const ImfHeader *header; + Rect window; + int tile_width; + int tile_height; + + /* Need to single-thread calls to ReadTile. + */ + GMutex *lock; +} Read; + +static void +get_imf_error( void ) +{ + im_error( "im_exr2vips", _( "EXR error: %s" ), ImfErrorMessage() ); +} + +static void +read_destroy( Read *read ) +{ + IM_FREE( read->name ); + + IM_FREEF( ImfCloseTiledInputFile, read->tiles ); + IM_FREEF( ImfCloseInputFile, read->lines ); + + IM_FREEF( g_mutex_free, read->lock ); + + im_free( read ); +} + +static Read * +read_new( const char *name, IMAGE *out ) +{ + Read *read; + int xmin, ymin; + int xmax, ymax; + + if( !(read = IM_NEW( NULL, Read )) ) + return( NULL ); + + read->name = im_strdup( NULL, name ); + read->out = out; + read->tiles = NULL; + read->lines = NULL; + read->lock = NULL; + + if( im_add_close_callback( out, + (im_callback_fn) read_destroy, read, NULL ) ) { + read_destroy( read ); + return( NULL ); + } + + /* Try to open tiled first ... if that fails, fall back to scanlines. + + FIXME ... seems a bit ugly, but how else can you spot a tiled + EXR image? + + */ + if( !(read->tiles = ImfOpenTiledInputFile( read->name )) ) { + if( !(read->lines = ImfOpenInputFile( read->name )) ) { + get_imf_error(); + return( NULL ); + } + } + +#ifdef DEBUG + if( read->tiles ) + printf( "im_exr2vips: opening in tiles mode\n" ); + else + printf( "im_exr2vips: opening in scanline mode\n" ); +#endif /*DEBUG*/ + + if( read->tiles ) { + read->header = ImfTiledInputHeader( read->tiles ); + read->lock = g_mutex_new(); + read->tile_width = ImfTiledInputTileXSize( read->tiles ); + read->tile_height = ImfTiledInputTileYSize( read->tiles ); + } + else + read->header = ImfInputHeader( read->lines ); + + ImfHeaderDataWindow( read->header, &xmin, &ymin, &xmax, &ymax ); + read->window.left = xmin; + read->window.top = ymin; + read->window.width = xmax - xmin + 1; + read->window.height = ymax - ymin + 1; + + return( read ); +} + +/* Read a OpenEXR file (header) into a VIPS (header). + */ +static int +exr2vips_header( Read *read, IMAGE *out ) +{ + /* + + FIXME ... not really sRGB. I think EXR is actually linear (no + gamma). We ought to read the chromaticities from the header, put + through a 3x3 matrix and output as XYZ + + */ + im_initdesc( out, + read->window.width, read->window.height, 4, + IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, + IM_CODING_NONE, IM_TYPE_sRGB, 1.0, 1.0, 0, 0 ); + + return( 0 ); +} + +/* Read a OpenEXR file header into a VIPS header. + */ +int +im_exr2vips_header( const char *name, IMAGE *out ) +{ + Read *read; + + if( !(read = read_new( name, out )) || + exr2vips_header( read, out ) ) + return( -1 ); + + return( 0 ); +} + +static int +fill_region( REGION *out, ImfRgba *imf_buffer, Read *read ) +{ + Rect *r = &out->valid; + + const int tw = read->tile_width; + const int th = read->tile_height; + + /* Find top left of tiles we need. + */ + const int xs = (r->left / tw) * tw; + const int ys = (r->top / th) * th; + + int x, y, z; + Rect image; + + /* Area of image. + */ + image.left = 0; + image.top = 0; + image.width = read->out->Xsize; + image.height = read->out->Ysize; + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += th ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += tw ) { + Rect tile; + Rect hit; + int result; + + if( !ImfTiledInputSetFrameBuffer( read->tiles, + imf_buffer - + (read->window.left + x) - + (read->window.top + y) * tw, + 1, tw ) ) { + get_imf_error(); + return( -1 ); + } + +#ifdef DEBUG + printf( "im_exr2vips: requesting tile %d x %d\n", + x / tw, y / th ); +#endif /*DEBUG*/ + + g_mutex_lock( read->lock ); + result = ImfTiledInputReadTile( read->tiles, + x / tw, y / th, 0, 0 ); + g_mutex_unlock( read->lock ); + + if( !result ) { + get_imf_error(); + return( -1 ); + } + + /* The tile in the file, in VIPS coordinates. + */ + tile.left = x; + tile.top = y; + tile.width = tw; + tile.height = th; + im_rect_intersectrect( &tile, &image, &tile ); + + /* The part of this tile that hits the region. + */ + im_rect_intersectrect( &tile, r, &hit ); + + /* Convert to float and write to the region. + */ + for( z = 0; z < hit.height; z++ ) { + ImfRgba *p = imf_buffer + + (hit.left - tile.left) + + (hit.top - tile.top + z) * tw; + float *q = (float *) IM_REGION_ADDR( out, + hit.left, hit.top + z ); + + ImfHalfToFloatArray( 4 * hit.width, + (ImfHalf *) p, q ); + } + } + + return( 0 ); +} + +/* Allocate a tile buffer. + */ +static void * +seq_start( IMAGE *out, Read *read ) +{ + ImfRgba *imf_buffer; + + if( !(imf_buffer = IM_ARRAY( out, + read->tile_width * read->tile_height, ImfRgba )) ) + return( NULL ); + + return( (void *) imf_buffer ); +} + +/* Read tilewise. + */ +static int +exr2vips_tiles( Read *read, IMAGE *out ) +{ + if( exr2vips_header( read, out ) || + im_poutcheck( out ) || + im_demand_hint( out, IM_SMALLTILE, NULL ) || + im_generate( out, seq_start, fill_region, NULL, read, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* Read scanlinewise. + */ +static int +exr2vips_lines( Read *read, IMAGE *out ) +{ + const int left = read->window.left; + const int top = read->window.top; + const int width = read->window.width; + const int height = read->window.height; + + ImfRgba *imf_buffer; + float *vips_buffer; + int y; + + if( !(imf_buffer = IM_ARRAY( out, width, ImfRgba )) || + !(vips_buffer = IM_ARRAY( out, 4 * width, float )) || + exr2vips_header( read, out ) || + im_outcheck( out ) || + im_setupout( out ) ) + return( -1 ); + + for( y = 0; y < height; y++ ) { + if( !ImfInputSetFrameBuffer( read->lines, + imf_buffer - left - (top + y) * width, + 1, width ) ) { + get_imf_error(); + return( -1 ); + } + if( !ImfInputReadPixels( read->lines, top + y, top + y ) ) { + get_imf_error(); + return( -1 ); + } + + ImfHalfToFloatArray( 4 * width, + (ImfHalf *) imf_buffer, vips_buffer ); + + if( im_writeline( y, out, (PEL *) vips_buffer ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +exr2vips( Read *read ) +{ + if( read->tiles ) { + IMAGE *raw; + + /* Tile cache: keep enough for two complete rows of tiles. + * This lets us do (smallish) area ops, like im_conv(), while + * still only hitting each OpenEXR tile once. + */ + if( !(raw = im_open_local( read->out, "cache", "p" )) ) + return( -1 ); + if( exr2vips_tiles( read, raw ) ) + return( -1 ); + if( im_tile_cache( raw, read->out, + read->tile_width, read->tile_height, + 2 * (1 + raw->Xsize / read->tile_width) ) ) + return( -1 ); + } + else { + if( exr2vips_lines( read, read->out ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Read a OpenEXR file into a VIPS image. + */ +int +im_exr2vips( const char *name, IMAGE *out ) +{ + Read *read; + +#ifdef DEBUG + printf( "im_exr2vips: reading \"%s\"\n", name ); +#endif /*DEBUG*/ + + if( !(read = read_new( name, out )) || + exr2vips( read ) ) + return( -1 ); + + return( 0 ); +} + +#endif /*HAVE_OPENEXR*/ diff --git a/libsrc/conversion/im_extract.c b/libsrc/conversion/im_extract.c new file mode 100644 index 00000000..b7ace8e7 --- /dev/null +++ b/libsrc/conversion/im_extract.c @@ -0,0 +1,261 @@ +/* @(#) Function to extract a portion of a Vasari format picture. + * @(#) Function im_extract() assumes that the imin image + * @(#) is either memory mapped or in the buffer pimin->data. + * @(#) + * @(#) int im_extract(pimin, pimout, pbox) + * @(#) IMAGE *pimin, *pimout; + * @(#) IMAGE_BOX *pbox; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 12/02/1990 + * Modified on: 4/6/92, J.Cupitt + * - speed up! why wasn't this done before? Why am I stupid? + * - layout, messages fixed + * now extracts IM_CODING_LABQ to IM_CODING_LABQ file: K.Martinez 1/7/93 + * 2/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 7/7/93 JC + * - behaviour for IM_CODING_LABQ fixed + * - better messages + * 7/10/94 JC + * - new IM_NEW() + * 22/2/95 JC + * - new use of im_region_region() + * 6/7/98 JC + * - im_extract_area() and im_extract_band() added + * 11/7/01 JC + * - im_extract_band() now numbers from zero + * 7/11/01 JC + * - oh what pain, im_extract now numbers bands from zero as well + * 6/9/02 JC + * - zero xoff/yoff for extracted area + * 14/4/04 JC + * - nope, -ve the origin + * 17/7/04 + * - added im_extract_bands(), remove many bands from image + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Extract one or more bands ... get number of output bands from + * or->im->Bands. + */ +static int +extract_band( REGION *or, REGION *ir, IMAGE *in, IMAGE_BOX *box ) +{ + Rect iarea; + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int es = IM_IMAGE_SIZEOF_ELEMENT( ir->im ); + int ipel = IM_IMAGE_SIZEOF_PEL( ir->im ); + int opel = IM_IMAGE_SIZEOF_PEL( or->im ); + char *p, *q; + int x, y, z; + + /* Ask for input we need. + */ + iarea = or->valid; + iarea.left += box->xstart; + iarea.top += box->ystart; + if( im_prepare( ir, &iarea ) ) + return( -1 ); + + for( y = to; y < bo; y++ ) { + p = IM_REGION_ADDR( ir, le + box->xstart, y + box->ystart ) + + box->chsel * es; + q = IM_REGION_ADDR( or, le, y ); + + for( x = le; x < ri; x++ ) { + for( z = 0; z < opel; z++ ) + q[z] = p[z]; + + p += ipel; + q += opel; + } + } + + return( 0 ); +} + +/* Extract an area. Can just use pointers. + */ +static int +extract_area( REGION *or, REGION *ir, IMAGE *in, IMAGE_BOX *box ) +{ + Rect iarea; + + /* Ask for input we need. Translate from demand in or's space to + * demand in ir's space. + */ + iarea = or->valid; + iarea.left += box->xstart; + iarea.top += box->ystart; + if( im_prepare( ir, &iarea ) ) + return( -1 ); + + /* Attach or to ir. + */ + if( im_region_region( or, ir, &or->valid, iarea.left, iarea.top ) ) + return( -1 ); + + return( 0 ); +} + +/* Extract an area and bands from an image. + */ +int +im_extract_areabands( IMAGE *in, IMAGE *out, + int left, int top, int width, int height, int band, int nbands ) +{ + IMAGE_BOX *box; + + if( im_piocheck( in, out ) ) + return( -1 ); + if( band < 0 || nbands < 1 || band + nbands > in->Bands ) { + im_error( "im_extract_areabands", + _( "band selection out of range" ) ); + return( -1 ); + } + if( left + width > in->Xsize || + top + height > in->Ysize || + left < 0 || top < 0 || + width <= 0 || height <= 0 ) { + im_error( "im_extract_areabands", _( "bad extract area" ) ); + return( -1 ); + } + if( in->Coding != IM_CODING_NONE ) { + if( in->Coding != IM_CODING_LABQ ) { + im_error( "im_extract_areabands", + _( "unknown coding" ) ); + return( -1 ); + } + + /* We only do area extract for coding == labq. + */ + if( band != 0 || nbands != in->Bands ) { + im_error( "im_extract_areabands", + _( "can only extract areas from LABQ" ) ); + return( -1 ); + } + } + + /* Set up the output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bands = nbands; + out->Xsize = width; + out->Ysize = height; + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + if( !(box = IM_NEW( out, IMAGE_BOX )) ) + return( -1 ); + box->xstart = left; + box->ystart = top; + box->xsize = width; + box->ysize = height; + box->chsel = band; + + /* Extracting all bands is a special case ... we can do it with + * pointers. + */ + if( band == 0 && nbands == in->Bands ) { + if( im_generate( out, + im_start_one, extract_area, im_stop_one, in, box ) ) + return( -1 ); + } + else { + if( im_generate( out, + im_start_one, extract_band, im_stop_one, in, box ) ) + return( -1 ); + } + + out->Xoffset = -left; + out->Yoffset = -top; + + return( 0 ); +} + +/* Legacy interface. + */ +int +im_extract( IMAGE *in, IMAGE *out, IMAGE_BOX *box ) +{ + if( box->chsel == -1 ) + return( im_extract_areabands( in, out, + box->xstart, box->ystart, box->xsize, box->ysize, + 0, in->Bands ) ); + else + return( im_extract_areabands( in, out, + box->xstart, box->ystart, box->xsize, box->ysize, + box->chsel, 1 ) ); +} + +/* Convenience functions. + */ +int +im_extract_area( IMAGE *in, IMAGE *out, + int left, int top, int width, int height ) +{ + return( im_extract_areabands( in, out, + left, top, width, height, 0, in->Bands ) ); +} + +int +im_extract_bands( IMAGE *in, IMAGE *out, int chsel, int nbands ) +{ + return( im_extract_areabands( in, out, + 0, 0, in->Xsize, in->Ysize, chsel, nbands ) ); +} + +int +im_extract_band( IMAGE *in, IMAGE *out, int chsel ) +{ + return( im_extract_bands( in, out, chsel, 1 ) ); +} diff --git a/libsrc/conversion/im_falsecolour.c b/libsrc/conversion/im_falsecolour.c new file mode 100644 index 00000000..5b4112b6 --- /dev/null +++ b/libsrc/conversion/im_falsecolour.c @@ -0,0 +1,355 @@ +/* @(#) Maps a one band uchar in image to a 3 band uchar out image. The + * @(#) colours are chosen to give an image of uniform luminance, but + * @(#) with a wide range of hues. + * @(#) + * @(#) Input should be either memory mapped or in buffer, out should have + * @(#) been set by a call to im_openout() or im_setbuf(). + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_falsecolour( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 23/6/95 JC + * - rewritten for PIO + * - now walks edges of colour cube to get more saturated appearance + * 21/8/05 + * - uses falsecolour scale from PET scanner + * 7/4/06 + * - hmm, reversed scale + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Falsecolour scale nicked from a PET scan. + */ +static unsigned char PET_colour[][3] = { + { 12, 0, 25 }, + { 17, 0, 34 }, + { 20, 0, 41 }, + { 22, 0, 45 }, + { 23, 0, 47 }, + { 27, 0, 55 }, + { 12, 0, 25 }, + { 5, 0, 11 }, + { 5, 0, 11 }, + { 5, 0, 11 }, + { 1, 0, 4 }, + { 1, 0, 4 }, + { 6, 0, 13 }, + { 15, 0, 30 }, + { 19, 0, 40 }, + { 23, 0, 48 }, + { 28, 0, 57 }, + { 36, 0, 74 }, + { 42, 0, 84 }, + { 46, 0, 93 }, + { 51, 0, 102 }, + { 59, 0, 118 }, + { 65, 0, 130 }, + { 69, 0, 138 }, + { 72, 0, 146 }, + { 81, 0, 163 }, + { 47, 0, 95 }, + { 12, 0, 28 }, + { 64, 0, 144 }, + { 61, 0, 146 }, + { 55, 0, 140 }, + { 52, 0, 137 }, + { 47, 0, 132 }, + { 43, 0, 128 }, + { 38, 0, 123 }, + { 30, 0, 115 }, + { 26, 0, 111 }, + { 23, 0, 108 }, + { 17, 0, 102 }, + { 9, 0, 94 }, + { 6, 0, 91 }, + { 2, 0, 87 }, + { 0, 0, 88 }, + { 0, 0, 100 }, + { 0, 0, 104 }, + { 0, 0, 108 }, + { 0, 0, 113 }, + { 0, 0, 121 }, + { 0, 0, 125 }, + { 0, 0, 129 }, + { 0, 0, 133 }, + { 0, 0, 141 }, + { 0, 0, 146 }, + { 0, 0, 150 }, + { 0, 0, 155 }, + { 0, 0, 162 }, + { 0, 0, 167 }, + { 0, 0, 173 }, + { 0, 0, 180 }, + { 0, 0, 188 }, + { 0, 0, 193 }, + { 0, 0, 197 }, + { 0, 0, 201 }, + { 0, 0, 209 }, + { 0, 0, 214 }, + { 0, 0, 218 }, + { 0, 0, 222 }, + { 0, 0, 230 }, + { 0, 0, 235 }, + { 0, 0, 239 }, + { 0, 0, 243 }, + { 0, 0, 247 }, + { 0, 4, 251 }, + { 0, 10, 255 }, + { 0, 14, 255 }, + { 0, 18, 255 }, + { 0, 24, 255 }, + { 0, 31, 255 }, + { 0, 36, 255 }, + { 0, 39, 255 }, + { 0, 45, 255 }, + { 0, 53, 255 }, + { 0, 56, 255 }, + { 0, 60, 255 }, + { 0, 66, 255 }, + { 0, 74, 255 }, + { 0, 77, 255 }, + { 0, 81, 255 }, + { 0, 88, 251 }, + { 0, 99, 239 }, + { 0, 104, 234 }, + { 0, 108, 230 }, + { 0, 113, 225 }, + { 0, 120, 218 }, + { 0, 125, 213 }, + { 0, 128, 210 }, + { 0, 133, 205 }, + { 0, 141, 197 }, + { 0, 145, 193 }, + { 0, 150, 188 }, + { 0, 154, 184 }, + { 0, 162, 176 }, + { 0, 167, 172 }, + { 0, 172, 170 }, + { 0, 180, 170 }, + { 0, 188, 170 }, + { 0, 193, 170 }, + { 0, 197, 170 }, + { 0, 201, 170 }, + { 0, 205, 170 }, + { 0, 211, 170 }, + { 0, 218, 170 }, + { 0, 222, 170 }, + { 0, 226, 170 }, + { 0, 232, 170 }, + { 0, 239, 170 }, + { 0, 243, 170 }, + { 0, 247, 170 }, + { 0, 251, 161 }, + { 0, 255, 147 }, + { 0, 255, 139 }, + { 0, 255, 131 }, + { 0, 255, 120 }, + { 0, 255, 105 }, + { 0, 255, 97 }, + { 0, 255, 89 }, + { 0, 255, 78 }, + { 0, 255, 63 }, + { 0, 255, 55 }, + { 0, 255, 47 }, + { 0, 255, 37 }, + { 0, 255, 21 }, + { 0, 255, 13 }, + { 0, 255, 5 }, + { 2, 255, 2 }, + { 13, 255, 13 }, + { 18, 255, 18 }, + { 23, 255, 23 }, + { 27, 255, 27 }, + { 35, 255, 35 }, + { 40, 255, 40 }, + { 43, 255, 43 }, + { 48, 255, 48 }, + { 55, 255, 55 }, + { 60, 255, 60 }, + { 64, 255, 64 }, + { 69, 255, 69 }, + { 72, 255, 72 }, + { 79, 255, 79 }, + { 90, 255, 82 }, + { 106, 255, 74 }, + { 113, 255, 70 }, + { 126, 255, 63 }, + { 140, 255, 56 }, + { 147, 255, 53 }, + { 155, 255, 48 }, + { 168, 255, 42 }, + { 181, 255, 36 }, + { 189, 255, 31 }, + { 197, 255, 27 }, + { 209, 255, 21 }, + { 224, 255, 14 }, + { 231, 255, 10 }, + { 239, 255, 7 }, + { 247, 251, 3 }, + { 255, 243, 0 }, + { 255, 239, 0 }, + { 255, 235, 0 }, + { 255, 230, 0 }, + { 255, 222, 0 }, + { 255, 218, 0 }, + { 255, 214, 0 }, + { 255, 209, 0 }, + { 255, 201, 0 }, + { 255, 197, 0 }, + { 255, 193, 0 }, + { 255, 188, 0 }, + { 255, 180, 0 }, + { 255, 176, 0 }, + { 255, 172, 0 }, + { 255, 167, 0 }, + { 255, 156, 0 }, + { 255, 150, 0 }, + { 255, 146, 0 }, + { 255, 142, 0 }, + { 255, 138, 0 }, + { 255, 131, 0 }, + { 255, 125, 0 }, + { 255, 121, 0 }, + { 255, 117, 0 }, + { 255, 110, 0 }, + { 255, 104, 0 }, + { 255, 100, 0 }, + { 255, 96, 0 }, + { 255, 90, 0 }, + { 255, 83, 0 }, + { 255, 78, 0 }, + { 255, 75, 0 }, + { 255, 71, 0 }, + { 255, 67, 0 }, + { 255, 65, 0 }, + { 255, 63, 0 }, + { 255, 59, 0 }, + { 255, 54, 0 }, + { 255, 52, 0 }, + { 255, 50, 0 }, + { 255, 46, 0 }, + { 255, 41, 0 }, + { 255, 39, 0 }, + { 255, 36, 0 }, + { 255, 32, 0 }, + { 255, 25, 0 }, + { 255, 22, 0 }, + { 255, 20, 0 }, + { 255, 17, 0 }, + { 255, 13, 0 }, + { 255, 10, 0 }, + { 255, 7, 0 }, + { 255, 4, 0 }, + { 255, 0, 0 }, + { 252, 0, 0 }, + { 251, 0, 0 }, + { 249, 0, 0 }, + { 248, 0, 0 }, + { 244, 0, 0 }, + { 242, 0, 0 }, + { 240, 0, 0 }, + { 237, 0, 0 }, + { 234, 0, 0 }, + { 231, 0, 0 }, + { 229, 0, 0 }, + { 228, 0, 0 }, + { 225, 0, 0 }, + { 222, 0, 0 }, + { 221, 0, 0 }, + { 219, 0, 0 }, + { 216, 0, 0 }, + { 213, 0, 0 }, + { 212, 0, 0 }, + { 210, 0, 0 }, + { 207, 0, 0 }, + { 204, 0, 0 }, + { 201, 0, 0 }, + { 199, 0, 0 }, + { 196, 0, 0 }, + { 193, 0, 0 }, + { 192, 0, 0 }, + { 190, 0, 0 }, + { 188, 0, 0 }, + { 184, 0, 0 }, + { 183, 0, 0 }, + { 181, 0, 0 }, + { 179, 0, 0 }, + { 175, 0, 0 }, + { 174, 0, 0 }, + { 174, 0, 0 } +}; + +/* False colour. One band uchar images only. + */ +int +im_falsecolour( IMAGE *in, IMAGE *out ) +{ + IMAGE *lut; + + /* Check our args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Bands != 1 || in->Coding != IM_CODING_NONE || + in->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_falsecolour", _( "input image not one band " + "uchar uncoded" ) ); + return( -1 ); + } + + if( !(lut = im_image( (PEL *) PET_colour, + 1, 256, 3, IM_BANDFMT_UCHAR )) ) + return( -1 ); + if( im_maplut( in, out, lut ) ) { + im_close( lut ); + return( -1 ); + } + + im_close( lut ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_fliphor.c b/libsrc/conversion/im_fliphor.c new file mode 100644 index 00000000..997d2513 --- /dev/null +++ b/libsrc/conversion/im_fliphor.c @@ -0,0 +1,150 @@ +/* @(#) Flips image left-right. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int im_fliphor( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * Copyright: 1990, N. Dessipris + * Written on: 28/10/91 + * Updated on: + * 19/7/93 JC + * - now allows IM_CODING_LABQ too + * - yuk! needs rewriting + * 21/12/94 JC + * - rewritten + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Flip a small area. + */ +static int +flip_gen( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + Rect in; + char *p, *q; + int x, y, z; + + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int ps = IM_IMAGE_SIZEOF_PEL( ir->im ); /* sizeof pel */ + + int hgt = ir->im->Xsize - r->width; + + int lastx; + + /* Transform to input coordinates. + */ + in = *r; + in.left = hgt - r->left; + + /* Find x of final pixel in input area. + */ + lastx = IM_RECT_RIGHT( &in ) - 1; + + /* Ask for input we need. + */ + if( im_prepare( ir, &in ) ) + return( -1 ); + + /* Loop, copying and reversing lines. + */ + for( y = to; y < bo; y++ ) { + p = IM_REGION_ADDR( ir, lastx, y ); + q = IM_REGION_ADDR( or, le, y ); + + for( x = le; x < ri; x++ ) { + /* Copy the pel. + */ + for( z = 0; z < ps; z++ ) + q[z] = p[z]; + + /* Skip forwards in out, back in in. + */ + q += ps; + p -= ps; + } + } + + return( 0 ); +} + +/* Copy image. + */ +int +im_fliphor( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_errormsg( "im_fliphor: in must be uncoded" ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, im_start_one, flip_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + out->Xoffset = in->Xsize; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/conversion/im_flipver.c b/libsrc/conversion/im_flipver.c new file mode 100644 index 00000000..99c4a311 --- /dev/null +++ b/libsrc/conversion/im_flipver.c @@ -0,0 +1,141 @@ +/* @(#) Flips image up-down. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int im_flipver( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * Copyright: 1990, N. Dessipris + * Written on: 28/10/91 + * Updated on: + * 21/12/94 JC + * - adapted from new im_fliphor(). + * 30/8/96 JC + * - ooops! IM_REGION_SIZEOF_LINE() not valid until im_prepare() has been + * called + * 7/3/03 JC + * - ahem, memcpy() line size calc was wrong, occasional segvs + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Flip a small area. + */ +static int +flip_gen( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + Rect in; + PEL *p, *q; + int y; + + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM( r ); + + int ls; + int psk, qsk; + + /* Transform to input coordinates. + */ + in = *r; + in.top = ir->im->Ysize - bo; + + /* Ask for input we need. + */ + if( im_prepare( ir, &in ) ) + return( -1 ); + + /* Loop, copying and reversing lines. + */ + p = (PEL *) IM_REGION_ADDR( ir, le, in.top + in.height - 1 ); + q = (PEL *) IM_REGION_ADDR( or, le, to ); + psk = IM_REGION_LSKIP( ir ); + qsk = IM_REGION_LSKIP( or ); + ls = IM_REGION_SIZEOF_LINE( or ); + + for( y = to; y < bo; y++ ) { + memcpy( q, p, ls ); + + p -= psk; + q += qsk; + } + + return( 0 ); +} + +/* Copy image. + */ +int +im_flipver( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_errormsg( "im_flipver: in must be uncoded" ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, im_start_one, flip_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = in->Ysize; + + return( 0 ); +} diff --git a/libsrc/conversion/im_gbandjoin.c b/libsrc/conversion/im_gbandjoin.c new file mode 100644 index 00000000..6ec3ee8f --- /dev/null +++ b/libsrc/conversion/im_gbandjoin.c @@ -0,0 +1,228 @@ +/* @(#) Function to perform a band-wise join of no images. + * @(#) Input images can have any number of bands; for instance if im[0] has j + * @(#) bands, im[1] k, ...., im[no-1] l bands, output has j+k+...+l bands + * @(#) respectively + * @(#) + * @(#) Function im_gbandjoin() assumes that the imin image + * @(#) is either memory mapped or in buffer + * @(#) + * @(#) int im_gbandjoin( imarray, imout, no ) + * @(#) IMAGE *imarray[], *imout; + * @(#) int no; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1991, N. Dessipris, modification of im_bandjoin() + * + * Author: N. Dessipris + * Written on: 17/04/1991 + * Modified on : + * 16/3/94 JC + * - rewritten for partials + * - now in ANSI C + * - now works for any number of input images, except zero + * 7/10/94 JC + * - new IM_NEW() + * 16/4/07 + * - fall back to im_copy() for 1 input image + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Struct we carry stuff around in. + */ +typedef struct joins { + int nim; /* Number of input images */ + IMAGE **in; /* Array of input images, NULL-terminated */ + int *is; /* An int for SIZEOF_PEL() for each image */ +} Join; + +/* Make a Join struct. + */ +static Join * +make_join( IMAGE *out, IMAGE **in, int nim ) +{ + Join *jn; + int i; + + if( !(jn = IM_NEW( out, Join )) ) + return( NULL ); + jn->nim = nim; + if( !(jn->in = IM_ARRAY( out, nim + 1, IMAGE * )) || + !(jn->is = IM_ARRAY( out, nim, int )) ) + return( NULL ); + + /* Remember to NULL-terminate. + */ + for( i = 0; i < nim; i++ ) { + jn->in[i] = in[i]; + jn->is[i] = IM_IMAGE_SIZEOF_PEL( in[i] ); + } + jn->in[nim] = NULL; + + return( jn ); +} + +/* Perform join. + */ +static int +join_bands( REGION *or, REGION **ir, IMAGE **in, Join *jn ) +{ + int x, y, z, i; + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int ps = IM_IMAGE_SIZEOF_PEL( or->im ); + + /* Prepare each input area. + */ + for( i = 0; i < jn->nim; i++ ) + if( im_prepare( ir[i], r ) ) + return( -1 ); + + /* Loop over output! + */ + for( y = to; y < bo; y++ ) { + PEL *qb = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Loop for each input image. + */ + for( i = 0; i < jn->nim; i++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir[i], le, y ); + PEL *q = qb; + int k = jn->is[i]; + + /* Copy all PELs from this line of this input image + * into the correct place in the output line. + */ + for( x = le; x < ri; x++ ) { + PEL *qn = q; + + /* Copy one PEL. + */ + for( z = 0; z < k; z++ ) + *q++ = *p++; + + /* Skip to the point at which the next PEL + * from this input should go. + */ + q = qn + ps; + } + + /* Move on to the line start for the next PEL. + */ + qb += k; + } + } + + return( 0 ); +} + +/* Band-wise join of a vector of image descriptors. + */ +int +im_gbandjoin( IMAGE **in, IMAGE *out, int nim ) +{ + int i; + Join *jn; + + /* Check it out! + */ + if( nim < 1 ) { + im_error( "im_gbandjoin", _( "zero input images!" ) ); + return( -1 ); + } + if( nim == 1 ) + return( im_copy( in[0], out ) ); + + /* Check our args. + */ + if( im_poutcheck( out ) ) + return( -1 ); + for( i = 0; i < nim; i++ ) { + if( im_pincheck( in[i] ) ) + return( -1 ); + + if( in[i]->Coding != IM_CODING_NONE ) { + im_error( "im_gbandjoin", _( "uncoded input only" ) ); + return( -1 ); + } + + if( in[0]->BandFmt != in[i]->BandFmt ) { + im_error( "im_gbandjoin", + _( "input images differ in format" ) ); + return( -1 ); + } + if( in[0]->Xsize != in[i]->Xsize || + in[0]->Ysize != in[i]->Ysize ) { + im_error( "im_gbandjoin", + _( "input images differ in size" ) ); + return( -1 ); + } + } + + /* Build a data area. + */ + if( !(jn = make_join( out, in, nim )) ) + return( -1 ); + + /* Prepare the output header. + */ + if( im_cp_desc_array( out, jn->in ) ) + return( -1 ); + out->Bands = 0; + for( i = 0; i < nim; i++ ) + out->Bands += in[i]->Bands; + + /* Set demand hints. + */ + if( im_demand_hint_array( out, IM_THINSTRIP, jn->in ) ) + return( -1 ); + + if( im_generate( out, + im_start_many, join_bands, im_stop_many, jn->in, jn ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_grid.c b/libsrc/conversion/im_grid.c new file mode 100644 index 00000000..465cf4c5 --- /dev/null +++ b/libsrc/conversion/im_grid.c @@ -0,0 +1,180 @@ +/* chop a tall thin image up into a grid ... useful for displaying volumetric + * images + * + * 4/8/05 + * 7/9/05 + * - oops, clipping was wrong + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on debugging output. +#define DEBUG +#define DEBUG_PAINT +#define DEBUG_MAKE + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +typedef struct _Grid { + IMAGE *in; + IMAGE *out; + int tile_height; + int across; + int down; +} Grid; + +static int +grid_gen( REGION *or, REGION *ir, IMAGE *in, Grid *grid ) +{ + Rect *r = &or->valid; + int twidth = grid->in->Xsize; + int theight = grid->tile_height; + int x, y; + Rect tile; + + /* Find top left of tiles we need. + */ + int xs = (r->left / twidth) * twidth; + int ys = (r->top / theight) * theight; + + /* The tile enclosing the top-left corner of the requested area. + */ + tile.left = xs; + tile.top = ys; + tile.width = twidth; + tile.height = theight; + + /* If the request fits inside a single tile, we can just pointer-copy. + */ + if( im_rect_includesrect( &tile, r ) ) { + Rect irect; + + /* Translate request to input space. + */ + irect = *r; + irect.left -= xs; + irect.top -= ys; + irect.top += grid->across * ys + theight * (xs / twidth); + + if( im_prepare( ir, &irect ) || + im_region_region( or, ir, r, irect.left, irect.top ) ) + return( -1 ); + + return( 0 ); + } + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += theight ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += twidth ) { + Rect paint; + Rect input; + + /* Whole tile at x, y + */ + tile.left = x; + tile.top = y; + tile.width = twidth; + tile.height = theight; + + /* Which parts touch the area of the output we are + * building. + */ + im_rect_intersectrect( &tile, r, &paint ); + + assert( !im_rect_isempty( &paint ) ); + + /* Translate back to ir coordinates. + */ + input = paint; + input.left -= x; + input.top -= y; + input.top += grid->across * y + theight * (x / twidth); + + /* Render into or. + */ + if( im_prepare_to( ir, or, &input, + paint.left, paint.top ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_grid( IMAGE *in, IMAGE *out, int tile_height, int across, int down ) +{ + Grid *grid = IM_NEW( out, Grid ); + + if( !grid || im_piocheck( in, out ) ) + return( -1 ); + if( across <= 0 || down <= 0 ) { + im_error( "im_grid", _( "bad parameters" ) ); + return( -1 ); + } + if( in->Ysize % tile_height != 0 || + in->Ysize / tile_height != across * down ) { + im_error( "im_grid", _( "bad grid geometry" ) ); + return( -1 ); + } + + grid->in = in; + grid->out = out; + grid->tile_height = tile_height; + grid->across = across; + grid->down = down; + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = in->Xsize * across; + out->Ysize = tile_height * down; + + /* We can render small tiles with pointer copies. + */ + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + + if( im_generate( out, + im_start_one, grid_gen, im_stop_one, in, grid ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/conversion/im_insert.c b/libsrc/conversion/im_insert.c new file mode 100644 index 00000000..c19adb84 --- /dev/null +++ b/libsrc/conversion/im_insert.c @@ -0,0 +1,379 @@ +/* @(#) Insert one image into another. ins is inserted into image in at + * @(#) position x, y relative to the top LH corner of in. out is made large + * @(#) enough to hold both in and ins. Any areas of out not coming from + * @(#) either in or ins are set to black (binary 0). If ins overlaps in, ins + * @(#) will appear on top of in. Both images must have the same number of + * @(#) bands and the same BandFmt. + * @(#) + * @(#) im_insert_noepand() always outputs an image the same size as in. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int im_insert(in, ins, out, x, y ) + * @(#) IMAGE *in, *ins, *out; + * @(#) int x, y; + * @(#) + * @(#) int im_insert_noexpand(in, ins, out, x, y ) + * @(#) IMAGE *in, *ins, *out; + * @(#) int x, y; + * @(#) + * @(#) Returns 0 on success and -1 on error. + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/08/1990 + * Modified on : + * 31/8/93 JC + * - ANSIfied + * - Nicos' reformatting undone. Grr! + * 22/12/94 + * - modernised + * - now does IM_CODING_LABQ too + * 22/6/95 JC + * - partialized + * 10/2/02 JC + * - adapted for im_prepare_to() stuff + * 14/4/04 + * - sets Xoffset / Yoffset + * 3/7/06 + * - add sanity range checks + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Hold our state in this. + */ +typedef struct { + /* Args. + */ + IMAGE *main; /* Main image */ + IMAGE *sub; /* Sub image */ + IMAGE *out; /* Output image */ + int x, y; /* Position of sub wrt. main */ + + /* Geometry. + */ + Rect rout; /* Output space */ + Rect rmain; /* Position of main in output */ + Rect rsub; /* Position of sub in output */ +} InsertState; + +/* Trivial case: we just need pels from one of the inputs. + */ +static int +just_one( REGION *or, REGION *ir, int x, int y ) +{ + Rect need; + + /* Find the part of pos we need. + */ + need = or->valid; + need.left -= x; + need.top -= y; + if( im_prepare( ir, &need ) ) + return( -1 ); + + /* Attach our output to it. + */ + if( im_region_region( or, ir, &or->valid, need.left, need.top ) ) + return( -1 ); + + return( 0 ); +} + +/* Black out a region. + */ +static void +black_region( REGION *reg ) +{ + PEL *q = (PEL *) IM_REGION_ADDR( reg, reg->valid.left, reg->valid.top ); + int wd = IM_REGION_SIZEOF_LINE( reg ); + int ls = IM_REGION_LSKIP( reg ); + int y; + + for( y = 0; y < reg->valid.height; y++, q += ls ) + memset( (char *) q, 0, wd ); +} + +/* Paste in parts of ir that fall within or --- ir is an input REGION for an + * image positioned at pos within or. + */ +static int +paste_region( REGION *or, REGION *ir, Rect *pos ) +{ + Rect ovl; + + /* Does any of the sub-image appear in the area we have been asked + * to make? + */ + im_rect_intersectrect( &or->valid, pos, &ovl ); + if( !im_rect_isempty( &ovl ) ) { + /* Find the part of in we need. + */ + ovl.left -= pos->left; + ovl.top -= pos->top; + + /* Paint this area of pixels into or. + */ + if( im_prepare_to( ir, or, &ovl, + ovl.left + pos->left, ovl.top + pos->top ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Insert generate function. + */ +static int +insert_gen( REGION *or, REGION **ir, IMAGE **vec, InsertState *ins ) +{ + Rect ovl; + + /* Does the rect we have been asked for fall entirely inside the + * sub-image? + */ + if( im_rect_includesrect( &ins->rsub, &or->valid ) ) + return( just_one( or, ir[1], + ins->rsub.left, ins->rsub.top ) ); + + /* Does it fall entirely inside the main, and not at all inside the + * sub? + */ + im_rect_intersectrect( &or->valid, &ins->rsub, &ovl ); + if( im_rect_includesrect( &ins->rmain, &or->valid ) && + im_rect_isempty( &ovl ) ) + return( just_one( or, ir[0], + ins->rmain.left, ins->rmain.top ) ); + + /* Output requires both (or neither) input. If it is not entirely + * inside both the main and the sub, then there is going to be some + * black. + */ + if( !(im_rect_includesrect( &ins->rsub, &or->valid ) && + im_rect_includesrect( &ins->rmain, &or->valid )) ) + /* Could be clever --- but just black the whole thing for + * simplicity. + */ + black_region( or ); + + /* Paste from main. + */ + if( paste_region( or, ir[0], &ins->rmain ) ) + return( -1 ); + + /* Paste from sub. + */ + if( paste_region( or, ir[1], &ins->rsub ) ) + return( -1 ); + + return( 0 ); +} + +/* xy range we sanity check on ... just to stop crazy numbers from 1/0 etc. + * causing assert() failuresd later. + */ +#define RANGE (10000000) + +/* Do an insert. + */ +int +im_insert( IMAGE *main, IMAGE *sub, IMAGE *out, int x, int y ) +{ + InsertState *ins = IM_NEW( out, InsertState ); + IMAGE **vec; + + /* Check args. + */ + if( !ins || im_piocheck( main, out ) || im_pincheck( sub ) ) + return( -1 ); + if( main->BandFmt != sub->BandFmt || main->Bands != sub->Bands || + main->Coding != sub->Coding ) { + im_error( "im_insert", _( "inputs differ in format" ) ); + return( -1 ); + } + if( main->Coding != IM_CODING_NONE && main->Coding != IM_CODING_LABQ ) { + im_error( "im_insert", + _( "input should be uncoded or IM_CODING_LABQ" ) ); + return( -1 ); + } + if( x > RANGE || x < -RANGE || y > RANGE || y < -RANGE ) { + im_error( "im_insert", _( "xy out of range" ) ); + return( -1 ); + } + + /* Save args. + */ + ins->main = main; + ins->sub = sub; + ins->out = out; + ins->x = x; + ins->y = y; + + /* Calculate geometry. First, position rmain and rsub with (0,0) at + * top LH corner of main. + */ + ins->rmain.left = 0; + ins->rmain.top = 0; + ins->rmain.width = main->Xsize; + ins->rmain.height = main->Ysize; + ins->rsub.left = x; + ins->rsub.top = y; + ins->rsub.width = sub->Xsize; + ins->rsub.height = sub->Ysize; + + /* Now: output is bounding box of these two. + */ + im_rect_unionrect( &ins->rmain, &ins->rsub, &ins->rout ); + + /* Translate origin to top LH corner of rout. + */ + ins->rmain.left -= ins->rout.left; + ins->rmain.top -= ins->rout.top; + ins->rsub.left -= ins->rout.left; + ins->rsub.top -= ins->rout.top; + ins->rout.left = 0; + ins->rout.top = 0; + + /* Set up the output header. + */ + if( im_cp_descv( out, main, sub, NULL ) ) + return( -1 ); + out->Xsize = ins->rout.width; + out->Ysize = ins->rout.height; + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, main, sub, NULL ) ) + return( -1 ); + + /* Make input array. + */ + if( !(vec = im_allocate_input_array( out, main, sub, NULL )) ) + return( -1 ); + + /* Make output image. + */ + if( im_generate( out, + im_start_many, insert_gen, im_stop_many, vec, ins ) ) + return( -1 ); + + out->Xoffset = ins->rmain.left; + out->Yoffset = ins->rmain.top; + + return( 0 ); +} + +/* As above, but don't expand to hold all of sub. + */ +int +im_insert_noexpand( IMAGE *main, IMAGE *sub, IMAGE *out, int x, int y ) +{ + InsertState *ins = IM_NEW( out, InsertState ); + IMAGE **vec; + + /* Check args. + */ + if( !ins || im_piocheck( main, out ) || im_pincheck( sub ) ) + return( -1 ); + if( main->BandFmt != sub->BandFmt || main->Bands != sub->Bands || + main->Coding != sub->Coding ) { + im_error( "im_insert_noexpand", + _( "inputs differ in format" ) ); + return( -1 ); + } + if( main->Coding != IM_CODING_NONE && main->Coding != IM_CODING_LABQ ) { + im_error( "im_insert_noexpand", + _( "input should be uncoded or IM_CODING_LABQ" ) ); + return( -1 ); + } + if( x > RANGE || x < -RANGE || y > RANGE || y < -RANGE ) { + im_error( "im_insert", _( "xy out of range" ) ); + return( -1 ); + } + + /* Save args. + */ + ins->main = main; + ins->sub = sub; + ins->out = out; + ins->x = x; + ins->y = y; + + /* Calculate geometry. + */ + ins->rmain.left = 0; + ins->rmain.top = 0; + ins->rmain.width = main->Xsize; + ins->rmain.height = main->Ysize; + ins->rsub.left = x; + ins->rsub.top = y; + ins->rsub.width = sub->Xsize; + ins->rsub.height = sub->Ysize; + ins->rout = ins->rmain; + + /* Set up the output header. + */ + if( im_cp_descv( out, main, sub, NULL ) ) + return( -1 ); + out->Xsize = ins->rout.width; + out->Ysize = ins->rout.height; + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, main, sub, NULL ) ) + return( -1 ); + + /* Make input array. + */ + if( !(vec = im_allocate_input_array( out, main, sub, NULL )) ) + return( -1 ); + + /* Make output image. + */ + if( im_generate( out, + im_start_many, insert_gen, im_stop_many, vec, ins ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_jpeg2vips.c b/libsrc/conversion/im_jpeg2vips.c new file mode 100644 index 00000000..f36fd70b --- /dev/null +++ b/libsrc/conversion/im_jpeg2vips.c @@ -0,0 +1,714 @@ +/* Convert 1 or 3-band 8-bit VIPS images to/from JPEG. + * + * 28/11/03 JC + * - better no-overshoot on tile loop + * 12/11/04 + * - better demand size choice for eval + * 30/6/05 JC + * - update im_error()/im_warn() + * - now loads and saves exif data + * 30/7/05 + * - now loads ICC profiles + * - now saves ICC profiles from the VIPS header + * 24/8/05 + * - jpeg load sets vips xres/yres from exif, if possible + * - jpeg save sets exif xres/yres from vips, if possible + * 29/8/05 + * - cut from old vips_jpeg.c + * 13/10/06 + * - add +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_JPEG + +#include + +int +im_jpeg2vips_header( const char *name, IMAGE *out ) +{ + im_error( "im_jpeg2vips_header", _( "JPEG support disabled" ) ); + + return( -1 ); +} + +int +im_jpeg2vips( const char *name, IMAGE *out ) +{ + im_error( "im_jpeg2vips", _( "JPEG support disabled" ) ); + + return( -1 ); +} + +#else /*HAVE_JPEG*/ + +#include +#include +#include +#include +#include + +#ifdef HAVE_EXIF +#ifdef UNTAGGED_EXIF +#include +#include +#include +#include +#else /*!UNTAGGED_EXIF*/ +#include +#include +#include +#include +#endif /*UNTAGGED_EXIF*/ +#endif /*HAVE_EXIF*/ + +#include +#include + +/* jpeglib includes jconfig.h, which can define HAVE_STDLIB_H ... which we + * also define. Make sure it's turned off. + */ +#ifdef HAVE_STDLIB_H +#undef HAVE_STDLIB_H +#endif /*HAVE_STDLIB_H*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define a new error handler for when we bomb out. + */ +typedef struct { + /* Public fields. + */ + struct jpeg_error_mgr pub; + + /* Private stuff for us. + */ + jmp_buf jmp; /* longjmp() here to get back to VIPS */ + FILE *fp; /* fclose() if non-NULL */ +} ErrorManager; + +/* New output message method - send to VIPS. + */ +METHODDEF(void) +new_output_message( j_common_ptr cinfo ) +{ + char buffer[JMSG_LENGTH_MAX]; + + (*cinfo->err->format_message)( cinfo, buffer ); + im_error( "vips_jpeg", _( "%s" ), buffer ); + +#ifdef DEBUG + printf( "vips_jpeg.c: new_output_message: \"%s\"\n", buffer ); +#endif /*DEBUG*/ +} + +/* New error_exit handler. + */ +METHODDEF(void) +new_error_exit( j_common_ptr cinfo ) +{ + ErrorManager *eman = (ErrorManager *) cinfo->err; + +#ifdef DEBUG + printf( "vips_jpeg.c: new_error_exit\n" ); +#endif /*DEBUG*/ + + /* Close the fp if necessary. + */ + if( eman->fp ) { + (void) fclose( eman->fp ); + eman->fp = NULL; + } + + /* Send the error message to VIPS. This method is overridden above. + */ + (*cinfo->err->output_message)( cinfo ); + + /* Jump back. + */ + longjmp( eman->jmp, 1 ); +} + +#ifdef HAVE_EXIF +#ifdef DEBUG_VERBOSE +/* Print exif for debugging ... hacked from exif-0.6.9/actions.c + */ +static void +show_tags( ExifData *data ) +{ + int i; + unsigned int tag; + const char *name; + + printf( "show EXIF tags:\n" ); + + for( i = 0; i < EXIF_IFD_COUNT; i++ ) + printf( "%-7.7s", exif_ifd_get_name( i ) ); + printf( "\n" ); + + for( tag = 0; tag < 0xffff; tag++ ) { + name = exif_tag_get_title( tag ); + if( !name ) + continue; + printf( " 0x%04x %-29.29s", tag, name ); + for( i = 0; i < EXIF_IFD_COUNT; i++ ) + if( exif_content_get_entry( data->ifd[i], tag ) ) + printf( " * " ); + else + printf( " - " ); + printf( "\n" ); + } +} + +static void +show_entry( ExifEntry *entry, void *client ) +{ + char exif_text[256]; + + printf( "%s", exif_tag_get_title( entry->tag ) ); + printf( "|" ); + printf( "%s", exif_entry_get_value( entry, exif_text, 256 ) ); + printf( "|" ); + printf( "%s", exif_format_get_name( entry->format ) ); + printf( "|" ); + printf( "%d bytes", entry->size ); + printf( "\n" ); +} + +static void +show_ifd( ExifContent *content, void *client ) +{ + exif_content_foreach_entry( content, show_entry, client ); + printf( "-\n" ); +} + +void +show_values( ExifData *data ) +{ + ExifByteOrder order; + + order = exif_data_get_byte_order( data ); + printf( "EXIF tags in '%s' byte order\n", + exif_byte_order_get_name( order ) ); + + printf( "%-20.20s", "Tag" ); + printf( "|" ); + printf( "%-58.58s", "Value" ); + printf( "\n" ); + + exif_data_foreach_content( data, show_ifd, NULL ); + + if( data->size ) + printf( "contains thumbnail of %d bytes\n", data->size ); +} +#endif /*DEBUG_VERBOSE*/ +#endif /*HAVE_EXIF*/ + +#ifdef HAVE_EXIF +static void +attach_exif_entry( ExifEntry *entry, IMAGE *im ) +{ + char name_text[256]; + VBuf name; + char value_text[256]; + VBuf value; + char exif_value[256]; + + im_buf_init_static( &name, name_text, 256 ); + im_buf_init_static( &value, value_text, 256 ); + + im_buf_appendf( &name, "exif-%s", exif_tag_get_title( entry->tag ) ); + im_buf_appendf( &value, "%s (%s, %d bytes)", + exif_entry_get_value( entry, exif_value, 256 ), + exif_format_get_name( entry->format ), + entry->size ); + + /* Can't do anything sensible with the error return. + */ + (void) im_meta_set_string( im, + im_buf_all( &name ), im_buf_all( &value ) ); +} + +static void +attach_exif_content( ExifContent *content, IMAGE *im ) +{ + exif_content_foreach_entry( content, + (ExifContentForeachEntryFunc) attach_exif_entry, im ); +} + +/* Just find the first occurence of the tag (is this correct?) + */ +static ExifEntry * +find_entry( ExifData *ed, ExifTag tag ) +{ + int i; + + for( i = 0; i < EXIF_IFD_COUNT; i++ ) { + ExifEntry *entry; + + if( (entry = exif_content_get_entry( ed->ifd[i], tag )) ) + return( entry ); + } + + return( NULL ); +} + +static int +get_entry_rational( ExifData *ed, ExifTag tag, double *out ) +{ + ExifEntry *entry; + ExifRational rational; + + if( !(entry = find_entry( ed, tag )) || + entry->format != EXIF_FORMAT_RATIONAL || + entry->components != 1 ) + return( -1 ); + + rational = exif_get_rational( entry->data, + exif_data_get_byte_order( ed ) ); + + *out = (double) rational.numerator / rational.denominator; + + return( 0 ); +} + +static int +get_entry_short( ExifData *ed, ExifTag tag, int *out ) +{ + ExifEntry *entry; + + if( !(entry = find_entry( ed, tag )) || + entry->format != EXIF_FORMAT_SHORT || + entry->components != 1 ) + return( -1 ); + + *out = exif_get_short( entry->data, + exif_data_get_byte_order( ed ) ); + + return( 0 ); +} + +static void +set_vips_resolution( IMAGE *im, ExifData *ed ) +{ + double xres, yres; + int unit; + + if( get_entry_rational( ed, EXIF_TAG_X_RESOLUTION, &xres ) || + get_entry_rational( ed, EXIF_TAG_Y_RESOLUTION, &yres ) || + get_entry_short( ed, EXIF_TAG_RESOLUTION_UNIT, &unit ) ) { + im_warn( "im_jpeg2vips", _( "error reading resolution" ) ); + return; + } + + switch( unit ) { + case 2: + /* In inches. + */ + xres /= 25.4; + yres /= 25.4; + break; + + case 3: + /* In cm. + */ + xres /= 10.0; + yres /= 10.0; + break; + + default: + im_warn( "im_jpeg2vips", _( "bad resolution unit" ) ); + return; + } + + im->Xres = xres; + im->Yres = yres; +} +#endif /*HAVE_EXIF*/ + +static int +read_exif( IMAGE *im, void *data, int data_length ) +{ + char *data_copy; + + /* Always attach a copy of the unparsed exif data. + */ + if( !(data_copy = im_malloc( NULL, data_length )) ) + return( -1 ); + memcpy( data_copy, data, data_length ); + if( im_meta_set_blob( im, IM_META_EXIF_NAME, + (im_callback_fn) im_free, data_copy, data_length ) ) { + im_free( data_copy ); + return( -1 ); + } + +#ifdef HAVE_EXIF +{ + ExifData *ed; + + if( !(ed = exif_data_new_from_data( data, data_length )) ) + return( -1 ); + + if( ed->size > 0 ) { +#ifdef DEBUG_VERBOSE + show_tags( ed ); + show_values( ed ); +#endif /*DEBUG_VERBOSE*/ + + /* Attach informational fields for what we find. + + FIXME ... better to have this in the UI layer? + + Or we could attach non-human-readable tags here (int, double + etc) and then move the human stuff to the UI layer? + + */ + exif_data_foreach_content( ed, + (ExifDataForeachContentFunc) attach_exif_content, im ); + + /* Look for resolution fields and use them to set the VIPS + * xres/yres fields. + */ + set_vips_resolution( im, ed ); + } + + exif_data_free( ed ); +} +#endif /*HAVE_EXIF*/ + + return( 0 ); +} + +/* Number of app2 sections we can capture. Each one can be 64k, so 640k should + * be enough for anyone (haha). + */ +#define MAX_APP2_SECTIONS (10) + +/* Read a cinfo to a VIPS image. + */ +static int +read_jpeg_header( struct jpeg_decompress_struct *cinfo, IMAGE *out ) +{ + jpeg_saved_marker_ptr p; + + /* Capture app2 sections here for assembly. + */ + void *app2_data[MAX_APP2_SECTIONS] = { 0 }; + int app2_data_length[MAX_APP2_SECTIONS] = { 0 }; + int data_length; + int i; + + /* Read JPEG header. + */ + jpeg_read_header( cinfo, TRUE ); + jpeg_calc_output_dimensions( cinfo ); + + /* Set VIPS header. + */ + im_initdesc( out, + cinfo->output_width, cinfo->output_height, + cinfo->output_components, + IM_BBITS_BYTE, IM_BANDFMT_UCHAR, IM_CODING_NONE, IM_TYPE_sRGB, + 1.0, 1.0, 0, 0 ); + + /* Look for EXIF and ICC profile. + */ + for( p = cinfo->marker_list; p; p = p->next ) { + switch( p->marker ) { + case JPEG_APP0 + 1: + /* EXIF data. + */ +#ifdef DEBUG + printf( "read_jpeg_header: seen %d bytes of EXIF\n", + p->data_length ); +#endif /*DEBUG*/ + if( read_exif( out, p->data, p->data_length ) ) + return( -1 ); + break; + + case JPEG_APP0 + 2: + /* ICC profile. + */ +#ifdef DEBUG + printf( "read_jpeg_header: seen %d bytes of ICC\n", + p->data_length ); +#endif /*DEBUG*/ + + if( p->data_length > 14 && + im_isprefix( "ICC_PROFILE", + (char *) p->data ) ) { + /* cur_marker numbers from 1, according to + * spec. + */ + int cur_marker = p->data[12] - 1; + + if( cur_marker >= 0 && + cur_marker < MAX_APP2_SECTIONS ) { + app2_data[cur_marker] = p->data + 14; + app2_data_length[cur_marker] = + p->data_length - 14; + } + } + break; + + default: +#ifdef DEBUG + printf( "read_jpeg_header: seen %d bytes of data\n", + p->data_length ); +#endif /*DEBUG*/ + break; + } + } + + /* Assemble ICC sections. + */ + data_length = 0; + for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) + data_length += app2_data_length[i]; + if( data_length ) { + unsigned char *data; + int p; + +#ifdef DEBUG + printf( "read_jpeg_header: assembled %d byte ICC profile\n", + data_length ); +#endif /*DEBUG*/ + + if( !(data = im_malloc( NULL, data_length )) ) + return( -1 ); + + p = 0; + for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) { + memcpy( data + p, app2_data[i], app2_data_length[i] ); + p += app2_data_length[i]; + } + + if( im_meta_set_blob( out, IM_META_ICC_NAME, + (im_callback_fn) im_free, data, data_length ) ) { + im_free( data ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Read a JPEG file header into a VIPS header. + */ +int +im_jpeg2vips_header( const char *name, IMAGE *out ) +{ + struct jpeg_decompress_struct cinfo; + ErrorManager eman; + FILE *fp; + + /* Make jpeg decompress object. + */ + cinfo.err = jpeg_std_error( &eman.pub ); + eman.pub.error_exit = new_error_exit; + eman.pub.output_message = new_output_message; + eman.fp = NULL; + if( setjmp( eman.jmp ) ) { + /* Here for longjmp() from new_error_exit(). + */ + jpeg_destroy_decompress( &cinfo ); + + return( -1 ); + } + jpeg_create_decompress( &cinfo ); + + /* Make input. + */ +#ifdef BINARY_OPEN + if( !(fp = fopen( name, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(fp = fopen( name, "r" )) ) { +#endif /*BINARY_OPEN*/ + jpeg_destroy_decompress( &cinfo ); + im_error( "im_jpeg2vips_header", + _( "unable to open \"%s\"" ), name ); + + return( -1 ); + } + eman.fp = fp; + jpeg_stdio_src( &cinfo, fp ); + + /* Need to read in APP1 (EXIF metadata) and APP2 (ICC profile). + */ + jpeg_save_markers( &cinfo, JPEG_APP0 + 1, 0xffff ); + jpeg_save_markers( &cinfo, JPEG_APP0 + 2, 0xffff ); + + /* Read! + */ + if( read_jpeg_header( &cinfo, out ) ) { + jpeg_destroy_decompress( &cinfo ); + fclose( fp ); + return( -1 ); + } + + /* Close and tidy. + */ + fclose( fp ); + eman.fp = NULL; + jpeg_destroy_decompress( &cinfo ); + + if( eman.pub.num_warnings != 0 ) { + im_warn( "im_jpeg2vips_header", + _( "read header gave %ld warnings" ), + eman.pub.num_warnings ); + im_warn( "im_jpeg2vips_header", "%s", im_error_buffer() ); + } + + return( 0 ); +} + +/* Read a cinfo to a VIPS image. + */ +static int +read_jpeg_image( struct jpeg_decompress_struct *cinfo, IMAGE *out ) +{ + int y, sz; + JSAMPROW row_pointer[1]; + + /* Check VIPS. + */ + if( im_outcheck( out ) || im_setupout( out ) ) + return( -1 ); + + /* Get size of output line and make a buffer. + */ + sz = cinfo->output_width * cinfo->output_components; + row_pointer[0] = (JSAMPLE *) (*cinfo->mem->alloc_large) + ( (j_common_ptr) cinfo, JPOOL_IMAGE, sz ); + + /* Start up decompressor. + */ + jpeg_start_decompress( cinfo ); + + /* Process image. + */ + for( y = 0; y < out->Ysize; y++ ) { + if( jpeg_read_scanlines( cinfo, &row_pointer[0], 1 ) != 1 ) { + im_error( "im_jpeg2vips", _( "truncated JPEG file" ) ); + return( -1 ); + } + if( im_writeline( y, out, row_pointer[0] ) ) + return( -1 ); + } + + /* Stop decompressor. + */ + jpeg_finish_decompress( cinfo ); + + return( 0 ); +} + +/* Read a JPEG file into a VIPS image. + */ +int +im_jpeg2vips( const char *name, IMAGE *out ) +{ + struct jpeg_decompress_struct cinfo; + ErrorManager eman; + FILE *fp; + + /* Make jpeg compression object. + */ + cinfo.err = jpeg_std_error( &eman.pub ); + eman.pub.error_exit = new_error_exit; + eman.pub.output_message = new_output_message; + eman.fp = NULL; + if( setjmp( eman.jmp ) ) { + /* Here for longjmp() from new_error_exit(). + */ + jpeg_destroy_decompress( &cinfo ); + + return( -1 ); + } + jpeg_create_decompress( &cinfo ); + + /* Make input. + */ +#ifdef BINARY_OPEN + if( !(fp = fopen( name, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(fp = fopen( name, "r" )) ) { +#endif /*BINARY_OPEN*/ + jpeg_destroy_decompress( &cinfo ); + im_error( "im_jpeg2vips", _( "unable to open \"%s\"" ), name ); + + return( -1 ); + } + eman.fp = fp; + jpeg_stdio_src( &cinfo, fp ); + + /* Need to read in APP1 (EXIF metadata) and APP2 (ICC profile). + */ + jpeg_save_markers( &cinfo, JPEG_APP0 + 1, 0xffff ); + jpeg_save_markers( &cinfo, JPEG_APP0 + 2, 0xffff ); + + /* Convert! + */ + if( read_jpeg_header( &cinfo, out ) || + read_jpeg_image( &cinfo, out ) ) { + jpeg_destroy_decompress( &cinfo ); + fclose( fp ); + return( -1 ); + } + + /* Close and tidy. + */ + fclose( fp ); + eman.fp = NULL; + jpeg_destroy_decompress( &cinfo ); + + if( eman.pub.num_warnings != 0 ) { + im_warn( "im_jpeg2vips", _( "read gave %ld warnings" ), + eman.pub.num_warnings ); + im_warn( "im_jpeg2vips", "%s", im_error_buffer() ); + } + + return( 0 ); +} + +#endif /*HAVE_JPEG*/ diff --git a/libsrc/conversion/im_lrjoin.c b/libsrc/conversion/im_lrjoin.c new file mode 100644 index 00000000..c4e11fcf --- /dev/null +++ b/libsrc/conversion/im_lrjoin.c @@ -0,0 +1,89 @@ +/* @(#) To join two images left right. The resultant image has + * @(#) Xsize = im1.Xsize + im2.Xsize and Ysize the min of im1.Ysize, im2.Ysize + * @(#) Input images should have the same number of Bands and BandFmt + * @(#) + * @(#) Usage: + * @(#) int im_lrjoin( IMAGE *left, IMAGE *right, IMAGE *out) + * @(#) IMAGE *left, *right, *out; + * @(#) + * @(#) + * + * Copyright 1990, 1991: Kirk Martinez, N. Dessipris + * Author: Kirk Martinez, N. Dessipris + * Written on: 9/6/90 + * Modified on: 17/04/1991 + * 31/8/93 JC + * - args to memcpy() were reversed + * 14/11/94 JC + * - tided up and ANSIfied + * - now accepts IM_CODING_LABQ + * - memory leaks removed + * - bug in calculation of output Xsize removed (thanks Thomson!) + * - bug in checking of image compatibility fixed + * 23/10/95 JC + * - rewritten in terms of im_insert() + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_lrjoin( IMAGE *left, IMAGE *right, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_lrjoin:1", "p" ); + + /* Paste right and left together. + */ + if( !t1 || im_insert( left, right, t1, left->Xsize, 0 ) ) + return( -1 ); + + /* Extract the part which the old im_lrjoin() would have made. + */ + if( im_extract_area( t1, out, + 0, 0, t1->Xsize, IM_MIN( left->Ysize, right->Ysize ) ) ) + return( -1 ); + + out->Xoffset = left->Xsize; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/conversion/im_magick2vips.c b/libsrc/conversion/im_magick2vips.c new file mode 100644 index 00000000..b6f6ea48 --- /dev/null +++ b/libsrc/conversion/im_magick2vips.c @@ -0,0 +1,621 @@ +/* Read a file using libMagick + * + * 7/1/03 JC + * - from im_tiff2vips + * 3/2/03 JC + * - some InitializeMagick() fail with NULL arg + * 2/11/04 + * - im_magick2vips_header() also checks sensible width/height + * 28/10/05 + * - copy attributes to meta + * - write many-frame images as a big column if all frames have identical + * width/height/bands/depth + * 31/3/06 + * - test for magick attr support + * 8/5/06 + * - set RGB16/GREY16 if appropriate + * 10/8/07 + * - support 32/64 bit imagemagick too + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on debugging output. +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_MAGICK + +#include + +int +im_magick2vips( const char *filename, IMAGE *im ) +{ + im_error( "im_magick2vips", _( "libMagick support disabled" ) ); + return( -1 ); +} + +int +im_magick2vips_header( const char *filename, IMAGE *im ) +{ + im_error( "im_magick2vips", _( "libMagick support disabled" ) ); + return( -1 ); +} + +#else /*HAVE_MAGICK*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* What we track during a read call. + */ +typedef struct _Read { + char *filename; + IMAGE *im; + + Image *image; + ImageInfo *image_info; + ExceptionInfo exception; + + int n_frames; + Image **frames; + int frame_height; + + /* Mutex to serialise calls to libMagick during threaded read. + */ + GMutex *lock; +} Read; + +static int +read_destroy( Read *read ) +{ +#ifdef DEBUG + printf( "im_magick2vips: read_destroy: %s\n", read->filename ); +#endif /*DEBUG*/ + + IM_FREEF( DestroyImage, read->image ); + IM_FREEF( DestroyImageInfo, read->image_info ); + IM_FREE( read->frames ); + IM_FREE( read->filename ); + DestroyExceptionInfo( &read->exception ); + IM_FREEF( g_mutex_free, read->lock ); + im_free( read ); + + return( 0 ); +} + +static Read * +read_new( const char *filename, IMAGE *im ) +{ + Read *read; + static int inited = 0; + + if( !inited ) { + InitializeMagick( "" ); + inited = 1; + } + + if( !(read = IM_NEW( NULL, Read )) ) + return( NULL ); + read->filename = im_strdup( NULL, filename ); + read->im = im; + read->image = NULL; + read->image_info = CloneImageInfo( NULL ); + GetExceptionInfo( &read->exception ); + read->n_frames = 0; + read->frames = NULL; + read->frame_height = 0; + read->lock = g_mutex_new(); + + if( im_add_close_callback( im, + (im_callback_fn) read_destroy, read, NULL ) ) { + read_destroy( read ); + return( NULL ); + } + + if( !read->filename || !read->image_info ) + return( NULL ); + + im_strncpy( read->image_info->filename, filename, MaxTextExtent ); + +#ifdef DEBUG + printf( "im_magick2vips: read_new: %s\n", read->filename ); +#endif /*DEBUG*/ + + return( read ); +} + +static int +get_bands( Image *image ) +{ + int bands; + + switch( image->colorspace ) { + case GRAYColorspace: + bands = 1; + break; + + case RGBColorspace: + bands = 3; + break; + + case sRGBColorspace: + bands = 3; + break; + + case CMYKColorspace: + bands = 4; + break; + + default: + im_error( "im_magick2vips", _( "unsupported colorspace %d" ), + (int) image->colorspace ); + return( -1 ); + } + + /* Alpha as well? + */ + if( image->matte ) { + assert( image->colorspace != CMYKColorspace ); + bands += 1; + } + + return( bands ); +} + +static int +parse_header( Read *read ) +{ + IMAGE *im = read->im; + Image *image = read->image; + + const ImageAttribute *attr; + Image *p; + int i; + + im->Xsize = image->columns; + im->Ysize = image->rows; + read->frame_height = image->rows; + if( (im->Bands = get_bands( image )) < 0 ) + return( -1 ); + + switch( image->depth ) { + case 8: + im->BandFmt = IM_BANDFMT_UCHAR; + im->Bbits = IM_BBITS_BYTE; + break; + + case 16: + im->BandFmt = IM_BANDFMT_USHORT; + im->Bbits = IM_BBITS_SHORT; + break; + +#ifdef UseHDRI + case 32: + im->BandFmt = IM_BANDFMT_FLOAT; + im->Bbits = IM_BBITS_FLOAT; + break; + + case 64: + im->BandFmt = IM_BANDFMT_DOUBLE; + im->Bbits = IM_BBITS_DOUBLE; + break; +#else /*!UseHDRI*/ + case 32: + im->BandFmt = IM_BANDFMT_UINT; + im->Bbits = IM_BBITS_INT; + break; + + /* No 64-bit int format in VIPS. + */ +#endif /*UseHDRI*/ + + default: + im_error( "im_magick2vips", _( "unsupported bit depth %d" ), + (int) image->depth ); + return( -1 ); + } + + switch( image->colorspace ) { + case GRAYColorspace: + if( im->BandFmt == IM_BANDFMT_USHORT ) + im->Type = IM_TYPE_GREY16; + else + im->Type = IM_TYPE_B_W; + break; + + case RGBColorspace: + if( im->BandFmt == IM_BANDFMT_USHORT ) + im->Type = IM_TYPE_RGB16; + else + im->Type = IM_TYPE_RGB; + break; + + case sRGBColorspace: + if( im->BandFmt == IM_BANDFMT_USHORT ) + im->Type = IM_TYPE_RGB16; + else + im->Type = IM_TYPE_sRGB; + break; + + case CMYKColorspace: + im->Type = IM_TYPE_CMYK; + break; + + default: + im_error( "im_magick2vips", _( "unsupported colorspace %d" ), + (int) image->colorspace ); + return( -1 ); + } + + switch( image->units ) { + case PixelsPerInchResolution: + im->Xres = image->x_resolution / 25.4; + im->Yres = image->y_resolution / 25.4; + break; + + case PixelsPerCentimeterResolution: + im->Xres = image->x_resolution / 10.0; + im->Yres = image->y_resolution / 10.0; + break; + + default: + im->Xres = 1.0; + im->Yres = 1.0; + break; + } + + /* Other fields. + */ + im->Coding = IM_CODING_NONE; + +#ifdef HAVE_GETNEXTIMAGEATTRIBUTE + /* Gah, magick6.something and later only. Attach any attributes. + */ + ResetImageAttributeIterator( image ); + while( (attr = GetNextImageAttribute( image )) ) { + char name_text[256]; + VBuf name; + + im_buf_init_static( &name, name_text, 256 ); + im_buf_appendf( &name, "magick-%s", attr->key ); + im_meta_set_string( im, im_buf_all( &name ), attr->value ); + +#ifdef DEBUG + printf( "key = \"%s\", value = \"%s\"\n", + attr->key, attr->value ); +#endif /*DEBUG*/ + } +#endif /*HAVE_GETNEXTIMAGEATTRIBUTE*/ + + /* Do we have a set of equal-sized frames? Append them. + + FIXME ... there must be an attribute somewhere from dicom read + which says this is a volumetric image + + */ + read->n_frames = 0; + for( p = image; p; (p = GetNextImageInList( p )) ) { + if( p->columns != im->Xsize || + p->rows != im->Ysize || + p->depth != im->Bbits || + get_bands( p ) != im->Bands ) + break; + + read->n_frames += 1; + } + if( p ) + /* Nope ... just do the first image in the list. + */ + read->n_frames = 1; + + /* Record frame pointers. + */ + im->Ysize *= read->n_frames; + if( !(read->frames = IM_ARRAY( NULL, read->n_frames, Image * )) ) + return( -1 ); + p = image; + for( i = 0; i < read->n_frames; i++ ) { + read->frames[i] = p; + p = GetNextImageInList( p ); + } + + return( 0 ); +} + +/* Divide by this to get 0 - MAX from a Quantum. Eg. consider QuantumRange == + * 65535, MAX == 255 (a Q16 ImageMagic representing an 8-bit image). Make sure + * this can't be zero (if QuantumRange < MAX) .. can happen if we have a Q8 + * ImageMagick trying to represent a 16-bit image. + */ +#define SCALE( MAX ) \ + (QuantumRange < (MAX) ? \ + 1 : \ + ((QuantumRange + 1) / ((MAX) + 1))) + +#define GRAY_LOOP( TYPE, MAX ) { \ + TYPE *q = (TYPE *) q8; \ + \ + for( x = 0; x < n; x++ ) \ + q[x] = pixels[x].green / SCALE( MAX ); \ +} + +#define GRAYA_LOOP( TYPE, MAX ) { \ + TYPE *q = (TYPE *) q8; \ + \ + for( x = 0; x < n; x++ ) { \ + q[0] = pixels[x].green / SCALE( MAX ); \ + q[1] = pixels[x].opacity / SCALE( MAX ); \ + \ + q += 2; \ + } \ +} + +#define RGB_LOOP( TYPE, MAX ) { \ + TYPE *q = (TYPE *) q8; \ + \ + for( x = 0; x < n; x++ ) { \ + q[0] = pixels[x].red / SCALE( MAX ); \ + q[1] = pixels[x].green / SCALE( MAX ); \ + q[2] = pixels[x].blue / SCALE( MAX ); \ + \ + q += 3; \ + } \ +} + +#define RGBA_LOOP( TYPE, MAX ) { \ + TYPE *q = (TYPE *) q8; \ + \ + for( x = 0; x < n; x++ ) { \ + q[0] = pixels[x].red / SCALE( MAX ); \ + q[1] = pixels[x].green / SCALE( MAX ); \ + q[2] = pixels[x].blue / SCALE( MAX ); \ + q[3] = pixels[x].opacity / SCALE( MAX ); \ + \ + q += 4; \ + } \ +} + +static void +unpack_pixels( IMAGE *im, PEL *q8, PixelPacket *pixels, int n ) +{ + int x; + + switch( im->Bands ) { + case 1: + /* Gray. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + GRAY_LOOP( unsigned char, 255 ); break; + case IM_BANDFMT_USHORT: + GRAY_LOOP( unsigned short, 65535 ); break; + case IM_BANDFMT_UINT: + GRAY_LOOP( unsigned int, 4294967295UL ); break; + case IM_BANDFMT_DOUBLE: + GRAY_LOOP( double, QuantumRange ); break; + + default: + assert( 0 ); + } + break; + + case 2: + /* Gray plus alpha. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + GRAYA_LOOP( unsigned char, 255 ); break; + case IM_BANDFMT_USHORT: + GRAYA_LOOP( unsigned short, 65535 ); break; + case IM_BANDFMT_UINT: + GRAYA_LOOP( unsigned int, 4294967295UL ); break; + case IM_BANDFMT_DOUBLE: + GRAYA_LOOP( double, QuantumRange ); break; + + default: + assert( 0 ); + } + break; + + case 3: + /* RGB. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + RGB_LOOP( unsigned char, 255 ); break; + case IM_BANDFMT_USHORT: + RGB_LOOP( unsigned short, 65535 ); break; + case IM_BANDFMT_UINT: + RGB_LOOP( unsigned int, 4294967295UL ); break; + case IM_BANDFMT_DOUBLE: + RGB_LOOP( double, QuantumRange ); break; + + default: + assert( 0 ); + } + break; + + case 4: + /* RGBA or CMYK. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + RGBA_LOOP( unsigned char, 255 ); break; + case IM_BANDFMT_USHORT: + RGBA_LOOP( unsigned short, 65535 ); break; + case IM_BANDFMT_UINT: + RGBA_LOOP( unsigned int, 4294967295UL ); break; + case IM_BANDFMT_DOUBLE: + RGBA_LOOP( double, QuantumRange ); break; + + default: + assert( 0 ); + } + break; + + default: + assert( 0 ); + } +} + +static PixelPacket * +get_pixels( Image *image, int left, int top, int width, int height ) +{ + PixelPacket *pixels; + + if( !(pixels = GetImagePixels( image, left, top, width, height )) ) + return( NULL ); + +/* Can't happen if red/green/blue are doubles. + */ +#ifndef UseHDRI + /* Unpack palette. + */ + if( image->storage_class == PseudoClass ) { + IndexPacket *indexes = GetIndexes( image ); + int i; + + for( i = 0; i < width * height; i++ ) { + IndexPacket x = indexes[i]; + + if( x < image->colors ) { + pixels[i].red = image->colormap[x].red; + pixels[i].green = image->colormap[x].green; + pixels[i].blue = image->colormap[x].blue; + } + } + } +#endif /*UseHDRI*/ + + return( pixels ); +} + +static int +magick_fill_region( REGION *out, void *dummy, Read *read ) +{ + Rect *r = &out->valid; + int y; + + for( y = 0; y < r->height; y++ ) { + int top = r->top + y; + int frame = top / read->frame_height; + int line = top % read->frame_height; + + PixelPacket *pixels; + + g_mutex_lock( read->lock ); + pixels = get_pixels( read->frames[frame], + r->left, line, r->width, 1 ); + g_mutex_unlock( read->lock ); + + if( !pixels ) { + im_error( "im_magick2vips", + _( "unable to read pixels" ) ); + return( -1 ); + } + + unpack_pixels( read->im, + (PEL *) IM_REGION_ADDR( out, r->left, top ), + pixels, r->width ); + } + + return( 0 ); +} + +int +im_magick2vips( const char *filename, IMAGE *im ) +{ + Read *read; + + if( !(read = read_new( filename, im )) ) + return( -1 ); + + read->image = ReadImage( read->image_info, &read->exception ); + if( !read->image ) { + im_error( "im_magick2vips", _( "unable to read file \"%s\"\n" + "libMagick error: %s %s" ), + filename, + read->exception.reason, read->exception.description ); + return( -1 ); + } + + if( parse_header( read ) || + im_poutcheck( im ) || + im_generate( im, NULL, magick_fill_region, NULL, read, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_magick2vips_header( const char *filename, IMAGE *im ) +{ + Read *read; + + if( !(read = read_new( filename, im )) ) + return( -1 ); + + read->image = PingImage( read->image_info, &read->exception ); + if( !read->image ) { + im_error( "im_magick2vips", _( "unable to ping file " + "\"%s\"\nlibMagick error: %s %s" ), + filename, + read->exception.reason, read->exception.description ); + return( -1 ); + } + + if( parse_header( read ) ) + return( -1 ); + + if( im->Xsize <= 0 || im->Ysize <= 0 ) { + im_error( "im_magick2vips", _( "bad image size" ) ); + return( -1 ); + } + + return( 0 ); +} + +#endif /*HAVE_MAGICK*/ diff --git a/libsrc/conversion/im_mask2vips.c b/libsrc/conversion/im_mask2vips.c new file mode 100644 index 00000000..8f864821 --- /dev/null +++ b/libsrc/conversion/im_mask2vips.c @@ -0,0 +1,97 @@ +/* @(#) Function to write a DOUBLEMASK to an IMAGE. + * @(#) + * @(#) int + * @(#) im_mask2vips( DOUBLEMASK *in, IMAGE *out ) + * @(#) + * @(#) The function returns -1 on error and 0 on success + * Author: J.Cupitt + * Written on: 6/6/94 + * Modified on: + * 7/10/94 JC + * - new IM_ARRAY() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_mask2vips( DOUBLEMASK *in, IMAGE *out ) +{ + int x, y; + double *buf, *p, *q; + + /* Check the mask. + */ + if( !in || !in->coeff ) { + im_errormsg( "im_mask2vips: bad input mask" ); + return( -1 ); + } + + /* Make the output image. + */ + im_initdesc( out, in->xsize, in->ysize, 1, + IM_BBITS_DOUBLE, IM_BANDFMT_DOUBLE, + IM_CODING_NONE, + IM_TYPE_B_W, + 1.0, 1.0, + 0, 0 ); + if( im_setupout( out ) ) + return( -1 ); + + /* Make an output buffer. + */ + if( !(buf = IM_ARRAY( out, in->xsize, double )) ) + return( -1 ); + + /* Write! + */ + for( p = in->coeff, y = 0; y < out->Ysize; y++ ) { + q = buf; + + for( x = 0; x < out->Xsize; x++ ) + *q++ = *p++; + + if( im_writeline( y, out, (void *) buf ) ) + return( -1 ); + } + + return( 0 ); +} + diff --git a/libsrc/conversion/im_msb.c b/libsrc/conversion/im_msb.c new file mode 100644 index 00000000..75d4f576 --- /dev/null +++ b/libsrc/conversion/im_msb.c @@ -0,0 +1,344 @@ +/* @(#) Convert a signed or unsigned, char or short or int image into unsigned + * @(#) char very quickly, by discarding the lower order bits. Signed values + * @(#) are converted to unsigned by adding 128. + * @(#) + * @(#) int + * @(#) im_msb( + * @(#) IMAGE *in, + * @(#) IMAGE *out + * @(#) ); + * @(#) + * @(#) As above, but also discard all but the specified band: + * @(#) + * @(#) int + * @(#) im_msb( + * @(#) IMAGE *in, + * @(#) IMAGE *out, + * @(#) int band + * @(#) ); + * + * Copyright: 2006, The Nottingham Trent University + * + * Author: Tom Vajzovic + * + * Written on: 2006-03-13 + * 27/9/06 + * - removed extra im_free() in im_copy() fallback + * 4/10/06 + * - removed warning on uchar fallback: it happens a lot with nip2 and + * isn't very serious + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H */ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + +/** MACROS **/ +/* BAND FORMAT TESTS */ + +#define SIZEOF_BAND(band_fmt) ( \ + ( IM_BANDFMT_UCHAR == (band_fmt) ) ? sizeof(unsigned char) \ + : ( IM_BANDFMT_CHAR == (band_fmt) ) ? sizeof(signed char) \ + : ( IM_BANDFMT_USHORT == (band_fmt) ) ? sizeof(unsigned short int) \ + : ( IM_BANDFMT_SHORT == (band_fmt) ) ? sizeof(signed short int) \ + : ( IM_BANDFMT_UINT == (band_fmt) ) ? sizeof(unsigned int) \ + : ( IM_BANDFMT_INT == (band_fmt) ) ? sizeof(signed int) \ + : ( IM_BANDFMT_FLOAT == (band_fmt) ) ? sizeof(float) \ + : ( IM_BANDFMT_DOUBLE == (band_fmt) ) ? sizeof(double) \ + : ( IM_BANDFMT_COMPLEX == (band_fmt) ) ? ( 2 * sizeof(float) ) \ + : ( 2 * sizeof(double) ) ) + +#define BANDFMT_SHORT_CHAR(band_fmt) ( \ + IM_BANDFMT_SHORT == (band_fmt) \ + || IM_BANDFMT_USHORT == (band_fmt) \ + || IM_BANDFMT_CHAR == (band_fmt) \ + || IM_BANDFMT_UCHAR == (band_fmt) ) + +#define BANDFMT_ANY_INT(band_fmt) ( \ + IM_BANDFMT_INT == (band_fmt) \ + || IM_BANDFMT_UINT == (band_fmt) \ + || BANDFMT_SHORT_CHAR(band_fmt) ) + +#define BANDFMT_UNSIGNED(band_fmt) ( \ + IM_BANDFMT_UINT == (band_fmt) \ + || IM_BANDFMT_USHORT == (band_fmt) \ + || IM_BANDFMT_UCHAR == (band_fmt) ) + +/* IMAGE TESTS */ + +#define IM_ANY_INT(im) BANDFMT_ANY_INT((im)-> BandFmt) +#define IM_UNSIGNED(im) BANDFMT_UNSIGNED((im)-> BandFmt) +#define IM_UNCODED(im) ( IM_CODING_NONE == (im)-> Coding ) + +/** LOCAL FUNCTIONS DECLARATIONS **/ +static void byte_select (unsigned char *in, unsigned char *out, int n, + size_t * params); +static void byte_select_flip (unsigned char *in, unsigned char *out, int n, + size_t * params); +static void msb_labq (unsigned char *in, unsigned char *out, int n); + +/** EXPORTED FUNCTIONS **/ + +int +im_msb (IMAGE * in, IMAGE * out) +{ +#define FUNCTION_NAME "im_msb" + + size_t *params; + im_wrapone_fn func; + +#define index (params[0]) +#define width (params[1]) +#define repeat (params[2]) + + if (im_piocheck (in, out)) + return -1; + + /* Stops a used-before-set warning. + */ + params = NULL; + + if (IM_UNCODED (in)) + { + + if (!IM_ANY_INT (in)) + { + im_error (FUNCTION_NAME, _("char, short or int only")); + return -1; + } + + params = IM_ARRAY (out, 3, size_t); + + if (!params) + return -1; + + width = SIZEOF_BAND (in->BandFmt); + +#ifdef WORDS_BIGENDIAN + index = 0; +#else + index = width - 1; +#endif + + repeat = in->Bands; + + if (IM_UNSIGNED (in)) + func = (im_wrapone_fn) byte_select; + else + func = (im_wrapone_fn) byte_select_flip; + + if (1 == width && (im_wrapone_fn) byte_select == func) + { + return im_copy (in, out); + } + } + + else if (IM_CODING_LABQ == in->Coding) + + func = (im_wrapone_fn) msb_labq; + + else + { + im_error (FUNCTION_NAME, _("unknown coding")); + return -1; + } + + if (im_cp_desc (out, in)) + return -1; + + out->Bbits = sizeof (unsigned char) << 3; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + + return im_wrapone (in, out, func, (void *) params, NULL); + +#undef index +#undef width +#undef repeat + +#undef FUNCTION_NAME +} + +int +im_msb_band (IMAGE * in, IMAGE * out, int band) +{ +#define FUNCTION_NAME "im_msb_band" + + size_t *params; + im_wrapone_fn func; + +#define index (params[0]) +#define width (params[1]) +#define repeat (params[2]) + + if (band < 0) + { + im_error (FUNCTION_NAME, _("bad arguments")); + return -1; + } + + if (im_piocheck (in, out)) + return -1; + + params = IM_ARRAY (out, 3, size_t); + + if (!params) + return -1; + + if (IM_UNCODED (in)) + { + + if (!IM_ANY_INT (in)) + { + im_error (FUNCTION_NAME, _("char, short or int only")); + return -1; + } + + if (band >= in->Bands) + { + im_error (FUNCTION_NAME, _("image does not have that many bands")); + return -1; + } + + width = SIZEOF_BAND (in->BandFmt); + +#ifdef WORDS_BIGENDIAN + index = width * band; +#else + index = (width * (band + 1)) - 1; +#endif + + width *= in->Bands; + repeat = 1; + + if (IM_UNSIGNED (in)) + func = (im_wrapone_fn) byte_select; + else + func = (im_wrapone_fn) byte_select_flip; + } + + else if (IM_CODING_LABQ == in->Coding) + { + + if (band > 2) + { + im_error (FUNCTION_NAME, _("image does not have that many bands")); + return -1; + } + width = 4; + repeat = 1; + index = band; + + if (band) + func = (im_wrapone_fn) byte_select_flip; + else + func = (im_wrapone_fn) byte_select; + } + else + { + im_error (FUNCTION_NAME, _("unknown coding")); + return -1; + } + + if (im_cp_desc (out, in)) + return -1; + + out->Bands = 1; + out->Bbits = sizeof (unsigned char) << 3; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + + return im_wrapone (in, out, func, (void *) params, NULL); + +#undef index +#undef width +#undef repeat + +#undef FUNCTION_NAME +} + +/** LOCAL FUNCTIONS DEFINITIONS **/ +static void +byte_select (unsigned char *in, unsigned char *out, int n, size_t * params) +{ +#define index (params[0]) +#define width (params[1]) +#define repeat (params[2]) + + unsigned char *stop = out + n * repeat; + + for (in += index; out < stop; in += width, ++out) + *out = *in; + +#undef index +#undef width +#undef repeat +} + +static void +byte_select_flip (unsigned char *in, unsigned char *out, int n, + size_t * params) +{ +#define index (params[0]) +#define width (params[1]) +#define repeat (params[2]) + + unsigned char *stop = out + n * repeat; + + for (in += index; out < stop; in += width, ++out) + *out = 0x80 ^ *in; + +#undef index +#undef width +#undef repeat +} + +static void +msb_labq (unsigned char *in, unsigned char *out, int n) +{ + unsigned char *stop = in + (n << 2); + + for (; in < stop; in += 4, out += 3) + { + out[0] = in[0]; + out[1] = 0x80 ^ in[1]; + out[2] = 0x80 ^ in[2]; + } +} diff --git a/libsrc/conversion/im_png2vips.c b/libsrc/conversion/im_png2vips.c new file mode 100644 index 00000000..a0d91ea8 --- /dev/null +++ b/libsrc/conversion/im_png2vips.c @@ -0,0 +1,381 @@ +/* Convert 1 to 4-band 8 or 16-bit VIPS images to/from PNG. + * + * 28/11/03 JC + * - better no-overshoot on tile loop + * 22/2/05 + * - read non-interlaced PNG with a line buffer (thanks Michel Brabants) + * 11/1/06 + * - read RGBA palette-ized images more robustly (thanks Tom) + * 20/4/06 + * - auto convert to sRGB/mono (with optional alpha) for save + * 1/5/06 + * - from vips_png.c + * 8/5/06 + * - set RGB16/GREY16 if appropriate + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_PNG + +#include + +int +im_png2vips( const char *name, IMAGE *out ) +{ + im_error( "im_png2vips", _( "PNG support disabled" ) ); + return( -1 ); +} + +int +im_png2vips_header( const char *name, IMAGE *out ) +{ + im_error( "im_png2vips_header", _( "PNG support disabled" ) ); + return( -1 ); +} + +#else /*HAVE_PNG*/ + +#include +#include +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#if PNG_LIBPNG_VER < 10003 +#error "PNG library too old." +#endif + +static void +user_error_function( png_structp png_ptr, png_const_charp error_msg ) +{ + im_error( "im_png2vips", _( "PNG error: \"%s\"" ), error_msg ); +} + +static void +user_warning_function( png_structp png_ptr, png_const_charp warning_msg ) +{ + im_error( "im_png2vips", _( "PNG warning: \"%s\"" ), warning_msg ); +} + +/* What we track during a PNG read. + */ +typedef struct { + char *name; + IMAGE *out; + + FILE *fp; + png_structp pPng; + png_infop pInfo; + png_bytep *row_pointer; + png_bytep data; +} Read; + +static void +read_destroy( Read *read ) +{ + if( read->name ) { + im_free( read->name ); + read->name = NULL; + } + if( read->fp ) { + fclose( read->fp ); + read->fp = NULL; + } + if( read->pPng ) + png_destroy_read_struct( &read->pPng, &read->pInfo, NULL ); + if( read->row_pointer ) { + im_free( read->row_pointer ); + read->row_pointer = NULL; + } + if( read->data ) { + im_free( read->data ); + read->data = NULL; + } + + im_free( read ); +} + +static Read * +read_new( const char *name, IMAGE *out ) +{ + Read *read; + + if( !(read = IM_NEW( NULL, Read )) ) + return( NULL ); + + read->name = im_strdup( NULL, name ); + read->out = out; + read->fp = NULL; + read->pPng = NULL; + read->pInfo = NULL; + read->row_pointer = NULL; + read->data = NULL; + +#ifdef BINARY_OPEN + if( !(read->fp = fopen( name, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(read->fp = fopen( name, "r" )) ) { +#endif /*BINARY_OPEN*/ + read_destroy( read ); + im_error( "im_png2vips", _( "unable to open \"%s\"" ), name ); + return( NULL ); + } + + if( !(read->pPng = png_create_read_struct( + PNG_LIBPNG_VER_STRING, NULL, + user_error_function, user_warning_function )) ) { + read_destroy( read ); + return( NULL ); + } + + /* Catch PNG errors from png_create_info_struct(). + */ + if( setjmp( read->pPng->jmpbuf ) ) { + read_destroy( read ); + return( NULL ); + } + + if( !(read->pInfo = png_create_info_struct( read->pPng )) ) { + read_destroy( read ); + return( NULL ); + } + + return( read ); +} + +/* Yuk! Have to malloc enough space for the whole image. Interlaced PNG + * is not really suitable for large objects ... + */ +static int +png2vips_interlace( Read *read ) +{ + const int rowbytes = IM_IMAGE_SIZEOF_LINE( read->out ); + int y; + + if( !(read->row_pointer = IM_ARRAY( NULL, + read->pInfo->height, png_bytep )) ) + return( -1 ); + if( !(read->data = (png_bytep) im_malloc( NULL, + read->pInfo->height * rowbytes )) ) + return( -1 ); + + for( y = 0; y < (int) read->pInfo->height; y++ ) + read->row_pointer[y] = read->data + y * rowbytes; + if( im_outcheck( read->out ) || + im_setupout( read->out ) || + setjmp( read->pPng->jmpbuf ) ) + return( -1 ); + + png_read_image( read->pPng, read->row_pointer ); + + for( y = 0; y < (int) read->pInfo->height; y++ ) + if( im_writeline( y, read->out, read->row_pointer[y] ) ) + return( -1 ); + + return( 0 ); +} + +/* Noninterlaced images can be read without needing enough RAM for the whole + * image. + */ +static int +png2vips_noninterlace( Read *read ) +{ + const int rowbytes = IM_IMAGE_SIZEOF_LINE( read->out ); + int y; + + if( !(read->data = (png_bytep) im_malloc( NULL, rowbytes )) ) + return( -1 ); + if( im_outcheck( read->out ) || + im_setupout( read->out ) || + setjmp( read->pPng->jmpbuf ) ) + return( -1 ); + + for( y = 0; y < (int) read->pInfo->height; y++ ) { + png_read_row( read->pPng, read->data, NULL ); + + if( im_writeline( y, read->out, read->data ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Read a PNG file (header) into a VIPS (header). + */ +static int +png2vips( Read *read, int header_only ) +{ + int bands, bpp, type; + + if( setjmp( read->pPng->jmpbuf ) ) + return( -1 ); + + png_init_io( read->pPng, read->fp ); + png_read_info( read->pPng, read->pInfo ); + + /* png_get_channels() gives us 1 band for palette images ... so look + * at colour_type for output bands. + */ + switch( read->pInfo->color_type ) { + case PNG_COLOR_TYPE_PALETTE: + bands = 3; + + /* Don't know if this is really correct. If there are + * transparent pixels, assume we're going to output RGBA. + */ + if( read->pInfo->num_trans ) + bands = 4; + + break; + + case PNG_COLOR_TYPE_GRAY: bands = 1; break; + case PNG_COLOR_TYPE_GRAY_ALPHA: bands = 2; break; + case PNG_COLOR_TYPE_RGB: bands = 3; break; + case PNG_COLOR_TYPE_RGB_ALPHA: bands = 4; break; + + default: + im_error( "im_png2vips", _( "unsupported colour type" ) ); + return( -1 ); + } + + /* 8 or 16 bit. + */ + bpp = read->pInfo->bit_depth > 8 ? 2 : 1; + + if( bpp > 1 ) { + if( bands < 3 ) + type = IM_TYPE_GREY16; + else + type = IM_TYPE_RGB16; + } + else { + if( bands < 3 ) + type = IM_TYPE_B_W; + else + type = IM_TYPE_sRGB; + } + + /* Expand palette images. + */ + if( read->pInfo->color_type == PNG_COLOR_TYPE_PALETTE ) + png_set_expand( read->pPng ); + + /* Expand <8 bit images to full bytes. + */ + if( read->pInfo->bit_depth < 8 ) + png_set_packing( read->pPng ); + + /* If we're an INTEL byte order machine and this is 16bits, we need + * to swap bytes. + */ + if( read->pInfo->bit_depth > 8 && !im_amiMSBfirst() ) + png_set_swap( read->pPng ); + + /* Set VIPS header. + */ + im_initdesc( read->out, + read->pInfo->width, read->pInfo->height, bands, + bpp == 1 ? IM_BBITS_BYTE : IM_BBITS_SHORT, + bpp == 1 ? IM_BANDFMT_UCHAR : IM_BANDFMT_USHORT, + IM_CODING_NONE, type, 1.0, 1.0, 0, 0 ); + + if( !header_only ) { + if( png_set_interlace_handling( read->pPng ) > 1 ) { + if( png2vips_interlace( read ) ) + return( -1 ); + } + else { + if( png2vips_noninterlace( read ) ) + return( -1 ); + } + } + + return( 0 ); +} + +/* Read a PNG file header into a VIPS header. + */ +int +im_png2vips_header( const char *name, IMAGE *out ) +{ + Read *read; + + if( !(read = read_new( name, out )) ) + return( -1 ); + + if( png2vips( read, 1 ) ) { + read_destroy( read ); + return( -1 ); + } + + read_destroy( read ); + + return( 0 ); +} + +/* Read a PNG file into a VIPS image. + */ +int +im_png2vips( const char *name, IMAGE *out ) +{ + Read *read; + +#ifdef DEBUG + printf( "im_png2vips: reading \"%s\"\n", name ); +#endif /*DEBUG*/ + + if( !(read = read_new( name, out )) ) + return( -1 ); + + if( png2vips( read, 0 ) ) { + read_destroy( read ); + return( -1 ); + } + + read_destroy( read ); + + return( 0 ); +} + +#endif /*HAVE_PNG*/ diff --git a/libsrc/conversion/im_ppm2vips.c b/libsrc/conversion/im_ppm2vips.c new file mode 100644 index 00000000..2e188dc8 --- /dev/null +++ b/libsrc/conversion/im_ppm2vips.c @@ -0,0 +1,440 @@ +/* Read a ppm file. + * + * Stephen Chan ... original code + * + * 21/11/00 JC + * - hacked for VIPS + * - reads ppm/pgm/pbm + * - mmaps binary pgm/ppm + * - reads all ascii formats (slowly!) + * 22/11/00 JC + * - oops, ascii read was broken + * - does 16/32 bit ascii now as well + * 24/5/01 + * - im_ppm2vips_header() added + * 22/5/04 + * - does 16/32 bit binary too + * - tiny fix for missing file close on read error + * 19/8/05 + * - use im_raw2vips() for binary read + * 9/9/05 + * - tiny cleanups + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* The largest number/field/whatever we can read. + */ +#define IM_MAX_THING (80) + +static void +skip_line( FILE *fp ) +{ + int ch; + + while( (ch = fgetc( fp )) != '\n' ) + ; +} + +static void +skip_white_space( FILE *fp ) +{ + int ch; + + while( isspace( ch = fgetc( fp ) ) ) + ; + ungetc( ch, fp ); + + if( ch == '#' ) { + skip_line( fp ); + skip_white_space( fp ); + } +} + +static int +read_uint( FILE *fp ) +{ + int i; + char buf[IM_MAX_THING]; + int ch; + + skip_white_space( fp ); + + /* Stop complaints about used-before-set on ch. + */ + ch = -1; + + for( i = 0; i < IM_MAX_THING - 1 && isdigit( ch = fgetc( fp ) ); i++ ) + buf[i] = ch; + buf[i] = '\0'; + + if( i == 0 ) { + im_error( "im_ppm2vips", _( "bad unsigned int" ) ); + return( -1 ); + } + + ungetc( ch, fp ); + + return( atoi( buf ) ); +} + +static int +read_header( FILE *fp, IMAGE *out, int *bits, int *ascii ) +{ + int width, height, bands, fmt, type; + int i; + char buf[IM_MAX_THING]; + int max_value; + + /* ppm types. + */ + static char *magic_names[] = { + "P1", /* pbm ... 1 band 1 bit, ascii */ + "P2", /* pgm ... 1 band many bit, ascii */ + "P3", /* ppm ... 3 band many bit, ascii */ + "P4", /* pbm ... 1 band 1 bit, binary */ + "P5", /* pgm ... 1 band 8 bit, binary */ + "P6" /* ppm ... 3 band 8 bit, binary */ + }; + + /* Characteristics, indexed by ppm type. + */ + static int lookup_bits[] = { + 1, 8, 8, 1, 8, 8 + }; + static int lookup_bands[] = { + 1, 1, 3, 1, 1, 3 + }; + static int lookup_ascii[] = { + 1, 1, 1, 0, 0, 0 + }; + + /* Read in the magic number. + */ + buf[0] = fgetc( fp ); + buf[1] = fgetc( fp ); + buf[2] = '\0'; + + for( i = 0; i < IM_NUMBER( magic_names ); i++ ) + if( strcmp( magic_names[i], buf ) == 0 ) + break; + if( i == IM_NUMBER( magic_names ) ) { + im_error( "im_ppm2vips", _( "bad magic number" ) ); + return( -1 ); + } + *bits = lookup_bits[i]; + bands = lookup_bands[i]; + *ascii = lookup_ascii[i]; + + /* Read in size. + */ + if( (width = read_uint( fp )) < 0 || + (height = read_uint( fp )) < 0 ) + return( -1 ); + + /* Read in max value for >1 bit images. + */ + if( *bits > 1 ) { + if( (max_value = read_uint( fp )) < 0 ) + return( -1 ); + + if( max_value > 255 ) + *bits = 16; + if( max_value > 65535 ) + *bits = 32; + } + + /* For binary images, there is always exactly 1 more whitespace + * character before the data starts. + */ + if( !*ascii && !isspace( fgetc( fp ) ) ) { + im_error( "im_ppm2vips", + _( "not whitespace before start of binary data" ) ); + return( -1 ); + } + + /* Choose a VIPS bandfmt. + */ + switch( *bits ) { + case 1: + case 8: + fmt = IM_BANDFMT_UCHAR; + break; + + case 16: + fmt = IM_BANDFMT_USHORT; + break; + + case 32: + fmt = IM_BANDFMT_UINT; + break; + + default: + assert( 0 ); + } + + if( bands == 1 ) { + if( fmt == IM_BANDFMT_USHORT ) + type = IM_TYPE_GREY16; + else + type = IM_TYPE_B_W; + } + else { + if( fmt == IM_BANDFMT_USHORT ) + type = IM_TYPE_RGB16; + else if( fmt == IM_BANDFMT_UINT ) + type = IM_TYPE_RGB; + else + type = IM_TYPE_sRGB; + } + + im_initdesc( out, width, height, bands, (*bits == 1) ? 8 : *bits, fmt, + IM_CODING_NONE, + type, + 1.0, 1.0, + 0, 0 ); + + return( 0 ); +} + +/* Read a ppm/pgm file using mmap(). + */ +static int +read_mmap( FILE *fp, const char *filename, IMAGE *out ) +{ + const int header_offset = ftell( fp ); + IMAGE *t[2]; + im_arch_type arch = im_amiMSBfirst() ? + IM_ARCH_NATIVE : IM_ARCH_BYTE_SWAPPED; + + if( im_open_local_array( out, t, 2, "im_ppm2vips", "p" ) || + im_raw2vips( filename, t[0], + out->Xsize, out->Ysize, + IM_IMAGE_SIZEOF_PEL( out ), header_offset ) || + im_copy_morph( t[0], t[1], + out->Bands, out->BandFmt, out->Coding ) || + im_copy_from( t[1], out, arch ) ) + return( -1 ); + + return( 0 ); +} + +/* Read an ascii ppm/pgm file. + */ +static int +read_ascii( FILE *fp, IMAGE *out ) +{ + int x, y; + PEL *buf; + + if( im_outcheck( out ) || im_setupout( out ) || + !(buf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + for( y = 0; y < out->Ysize; y++ ) { + for( x = 0; x < out->Xsize * out->Bands; x++ ) { + int val; + + if( (val = read_uint( fp )) < 0 ) + return( -1 ); + + switch( out->BandFmt ) { + case IM_BANDFMT_UCHAR: + buf[x] = IM_CLIP( 0, val, 255 ); + break; + + case IM_BANDFMT_USHORT: + ((unsigned short *) buf)[x] = + IM_CLIP( 0, val, 65535 ); + break; + + case IM_BANDFMT_UINT: + ((unsigned int *) buf)[x] = val; + break; + + default: + assert( 0 ); + } + } + + if( im_writeline( y, out, buf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Read an ascii 1 bit file. + */ +static int +read_1bit_ascii( FILE *fp, IMAGE *out ) +{ + int x, y; + PEL *buf; + + if( im_outcheck( out ) || im_setupout( out ) || + !(buf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + for( y = 0; y < out->Ysize; y++ ) { + for( x = 0; x < out->Xsize * out->Bands; x++ ) { + int val; + + if( (val = read_uint( fp )) < 0 ) + return( -1 ); + + if( val == 1 ) + buf[x] = 0; + else + buf[x] = 255; + } + + if( im_writeline( y, out, buf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Read a 1 bit binary file. + */ +static int +read_1bit_binary( FILE *fp, IMAGE *out ) +{ + int x, y, i; + int bits; + PEL *buf; + + if( im_outcheck( out ) || im_setupout( out ) || + !(buf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + bits = fgetc( fp ); + for( i = 0, y = 0; y < out->Ysize; y++ ) { + for( x = 0; x < out->Xsize * out->Bands; x++, i++ ) { + buf[x] = (bits & 128) ? 255 : 0; + bits <<= 1; + if( (i & 7) == 7 ) + bits = fgetc( fp ); + } + + if( im_writeline( y, out, buf ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +parse_ppm( FILE *fp, const char *filename, IMAGE *out ) +{ + int bits; + int ascii; + + if( read_header( fp, out, &bits, &ascii ) ) + return( -1 ); + + /* What sort of read are we doing? + */ + if( !ascii && bits >= 8 ) + return( read_mmap( fp, filename, out ) ); + else if( !ascii && bits == 1 ) + return( read_1bit_binary( fp, out ) ); + else if( ascii && bits == 1 ) + return( read_1bit_ascii( fp, out ) ); + else + return( read_ascii( fp, out ) ); +} + +int +im_ppm2vips_header( const char *filename, IMAGE *out ) +{ + FILE *fp; + int bits; + int ascii; + +#ifdef BINARY_OPEN + if( !(fp = fopen( filename, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(fp = fopen( filename, "r" )) ) { +#endif /*BINARY_OPEN*/ + im_error( "im_ppm2vips_header", + _( "unable to open \"%s\"" ), filename ); + return( -1 ); + } + + if( read_header( fp, out, &bits, &ascii ) ) { + fclose( fp ); + return( -1 ); + } + + fclose( fp ); + + return( 0 ); +} + +int +im_ppm2vips( const char *filename, IMAGE *out ) +{ + FILE *fp; + +#ifdef BINARY_OPEN + if( !(fp = fopen( filename, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(fp = fopen( filename, "r" )) ) { +#endif /*BINARY_OPEN*/ + im_error( "im_ppm2vips", + _( "unable to open \"%s\"" ), filename ); + return( -1 ); + } + + if( parse_ppm( fp, filename, out ) ) { + fclose( fp ); + return( -1 ); + } + + fclose( fp ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_print.c b/libsrc/conversion/im_print.c new file mode 100644 index 00000000..563bf0ed --- /dev/null +++ b/libsrc/conversion/im_print.c @@ -0,0 +1,52 @@ +/* im_print(): print a string to stdout + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Print a string to stdout, with a "\n". Sometimes useful for debugging + * language bindings. + */ +int +im_print( const char *message ) +{ + printf( "%s\n", message ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_raw2vips.c b/libsrc/conversion/im_raw2vips.c new file mode 100644 index 00000000..e8f50605 --- /dev/null +++ b/libsrc/conversion/im_raw2vips.c @@ -0,0 +1,64 @@ +/* Read raw image. Just a wrapper over im_binfile(). + * + * 3/8/05 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_raw2vips( const char *filename, IMAGE *out, + int width, int height, int bpp, int offset ) +{ + IMAGE *t; + + if( !(t = im_binfile( filename, width, height, bpp, offset )) ) + return( -1 ); + if( im_add_close_callback( out, + (im_callback_fn) im_close, t, NULL ) ) { + im_close( t ); + return( -1 ); + } + if( im_copy( t, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_recomb.c b/libsrc/conversion/im_recomb.c new file mode 100644 index 00000000..009e1772 --- /dev/null +++ b/libsrc/conversion/im_recomb.c @@ -0,0 +1,167 @@ +/* @(#) Recombination of bands of image: perform a matrix mult of the form + * @(#) + * @(#) a1 b11 b21 .. bm1 c1 + * @(#) a2 b12 b22 .. c2 + * @(#) . = . . x . + * @(#) . . . + * @(#) + * @(#) an b1n bmn cm + * @(#) + * @(#) Where A is an n band output image, C is an m band input image and B + * @(#) is an mxn matrix of floats. Can be used with 3x3 matrix to perform + * @(#) simple colour space transforms; 7x30 matrix to shrink 3rd order + * @(#) development of 3 filter system to IM_TYPE_XYZ etc. + * @(#) + * @(#) Output is always float, unless input is double, in which case output + * @(#) is double. Does not work for complex images. + * @(#) + * @(#) Usage: + * @(#) im_recomb( imagein, imageout, mat ) + * @(#) IMAGE *imagein, *imageout; + * @(#) DOUBLEMASK *mat; + * @(#) + * @(#) Returns: -1 on error, else 0 + * 21/6/95 JC + * - mildly modernised + * 14/3/96 JC + * - better error checks, partial + * - proper rounding behaviour for int types + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Inner loop. + */ +#define LOOP(INTYPE, OUTTYPE) \ +{\ + INTYPE *p = (INTYPE *) bin;\ + OUTTYPE *q = (OUTTYPE *) bout;\ + \ + for( i = 0; i < width; i++ ) {\ + double *m = mat->coeff;\ + \ + for( v = 0; v < mat->ysize; v++ ) {\ + double t = 0.0;\ + \ + for( u = 0; u < mat->xsize; u++ )\ + t += *m++ * p[u];\ + \ + *q++ = (OUTTYPE) t;\ + }\ + \ + p += mat->xsize;\ + }\ +} + +/* Process a buffer of PELs. + */ +static int +recomb_buf( void *bin, void *bout, int width, IMAGE *in, DOUBLEMASK *mat ) +{ + int i; + int u, v; + + /* Do the processing. + */ + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: LOOP( unsigned char, float ); break; + case IM_BANDFMT_CHAR: LOOP( signed char, float ); break; + case IM_BANDFMT_USHORT: LOOP( unsigned short, float ); break; + case IM_BANDFMT_SHORT: LOOP( signed short, float ); break; + case IM_BANDFMT_UINT: LOOP( unsigned int, float ); break; + case IM_BANDFMT_INT: LOOP( signed int, float ); break; + case IM_BANDFMT_FLOAT: LOOP( float, float ); break; + case IM_BANDFMT_DOUBLE: LOOP( double, double ); break; + + default: + im_errormsg( "im_recomb: unsupported input type" ); + return( -1 ); + } + + return( 0 ); +} + +/* Start here. + */ +int +im_recomb( IMAGE *in, IMAGE *out, DOUBLEMASK *mat ) +{ + DOUBLEMASK *mcpy; + + /* Check input image. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_errormsg( "im_recomb: uncoded non-complex only" ); + return( -1 ); + } + if( in->Bands != mat->xsize ) { + im_errormsg( "im_recomb: bands in must equal matrix width" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bands = mat->ysize; + if( im_isint( in ) ) { + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + } + + /* Take a copy of the matrix. + */ + if( !(mcpy = im_dup_dmask( mat, "conv_mask" )) ) + return( -1 ); + if( im_add_close_callback( out, + (im_callback_fn) im_free_dmask, mcpy, NULL ) ) { + im_free_dmask( mcpy ); + return( -1 ); + } + + /* And process! + */ + if( im_wrapone( in, out, (im_wrapone_fn) recomb_buf, in, mcpy ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_replicate.c b/libsrc/conversion/im_replicate.c new file mode 100644 index 00000000..a3b69c3b --- /dev/null +++ b/libsrc/conversion/im_replicate.c @@ -0,0 +1,156 @@ +/* replicate an image x times horizontally and vertically + * + * JC, 30 sep 03 + * + * 15/4/04 + * - some optimisations for some cases + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on debugging output. +#define DEBUG +#define DEBUG_PAINT +#define DEBUG_MAKE + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +replicate_gen( REGION *or, REGION *ir, IMAGE *in ) +{ + Rect *r = &or->valid; + int twidth = in->Xsize; + int theight = in->Ysize; + int x, y; + Rect tile; + + /* Find top left of tiles we need. + */ + int xs = (r->left / twidth) * twidth; + int ys = (r->top / theight) * theight; + + /* The tile enclosing the top-left corner of the requested area. + */ + tile.left = xs; + tile.top = ys; + tile.width = twidth; + tile.height = theight; + + /* If the request fits inside a single tile, we can just pointer-copy. + */ + if( im_rect_includesrect( &tile, r ) ) { + Rect irect; + + /* Translate request to input space. + */ + irect = *r; + irect.left -= xs; + irect.top -= ys; + if( im_prepare( ir, &irect ) ) + return( -1 ); + + if( im_region_region( or, ir, r, irect.left, irect.top ) ) + return( -1 ); + + return( 0 ); + } + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += theight ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += twidth ) { + Rect paint; + + /* Whole tile at x, y + */ + tile.left = x; + tile.top = y; + tile.width = twidth; + tile.height = theight; + + /* Which parts touch the area of the output we are + * building. + */ + im_rect_intersectrect( &tile, r, &paint ); + + /* Translate back to ir coordinates. + */ + paint.left -= x; + paint.top -= y; + + assert( !im_rect_isempty( &paint ) ); + + /* Render into or. + */ + if( im_prepare_to( ir, or, &paint, + paint.left + x, + paint.top + y ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_replicate( IMAGE *in, IMAGE *out, int across, int down ) +{ + if( across <= 0 || down <= 0 ) { + im_errormsg( "im_replicate: bad parameters" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize *= across; + out->Ysize *= down; + + /* We can render small tiles with pointer copies. + */ + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + + if( im_generate( out, + im_start_one, replicate_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/conversion/im_ri2c.c b/libsrc/conversion/im_ri2c.c new file mode 100644 index 00000000..3d5d2dfd --- /dev/null +++ b/libsrc/conversion/im_ri2c.c @@ -0,0 +1,170 @@ +/* @(#) Join two images to make a complex. If one of the inputs + * @(#) is a double, the output is IM_BANDFMT_DPCOMPLEX, otherwise it is IM_BANDFMT_COMPLEX. + * @(#) + * @(#) im_ri2c( IMAGE *in1, IMAGE *in2, IMAGE *out ) + * @(#) + * @(#) Returns: -1 on error, else 0 + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : 10/04/1990 + * 16/11/94 JC + * - rewritten with partials + * - more general + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Join two float buffers to make a complex. + */ +static void +join_float( float **p, float *q, int n, IMAGE *im ) +{ + int x; + int len = n * im->Bands; + float *p1 = p[0]; + float *p2 = p[1]; + + for( x = 0; x < len; x++ ) { + q[0] = *p1++; + q[1] = *p2++; + + q += 2; + } +} + +/* Join two double buffers to make a complex. + */ +static void +join_double( double **p, double *q, int n, IMAGE *im ) +{ + int x; + int len = n * im->Bands; + double *p1 = p[0]; + double *p2 = p[1]; + + for( x = 0; x < len; x++ ) { + q[0] = *p1++; + q[1] = *p2++; + + q += 2; + } +} + +/* Type conversion. + */ +static IMAGE * +convert( IMAGE *out, IMAGE *in, int (*cvt_fn)( IMAGE *, IMAGE * ) ) +{ + IMAGE *t1 = im_open_local( out, "Type conversion", "p" ); + + if( !t1 ) + return( NULL ); + + if( cvt_fn( in, t1 ) ) + return( NULL ); + + return( t1 ); +} + +int +im_ri2c( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + extern int im_clip2f( IMAGE *, IMAGE * ); + extern int im_clip2d( IMAGE *, IMAGE * ); + + /* Check input image. We don't need to check that sizes match -- + * im_wrapmany does this for us. + */ + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_errormsg( "im_ri2c: inputs should be uncoded" ); + return( -1 ); + } + if( im_iscomplex( in1 ) || im_iscomplex( in2 ) ) { + im_errormsg( "im_ri2c: inputs already complex" ); + return( -1 ); + } + + /* Prepare the output image. If either of the inputs is DOUBLE, we are + * DPCOMPLEX; otherwise we are COMPLEX. + */ + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + if( in1->BandFmt == IM_BANDFMT_DOUBLE || + in2->BandFmt == IM_BANDFMT_DOUBLE ) { + out->Bbits = IM_BBITS_DPCOMPLEX; + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + } + else { + out->Bbits = IM_BBITS_COMPLEX; + out->BandFmt = IM_BANDFMT_COMPLEX; + } + + /* Float inputs up to correct type. Note that if they are already the + * right type, this operation becomes a NOOP. + */ + if( out->BandFmt == IM_BANDFMT_COMPLEX ) { + in1 = convert( out, in1, im_clip2f ); + in2 = convert( out, in2, im_clip2f ); + } + else { + in1 = convert( out, in1, im_clip2d ); + in2 = convert( out, in2, im_clip2d ); + } + if( !in1 || !in2 ) + return( -1 ); + + /* Process! + */ + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( out->BandFmt == IM_BANDFMT_COMPLEX ) { + if( im_wrapmany( invec, out, + (im_wrapmany_fn) join_float, out, NULL ) ) + return( -1 ); + } + else { + if( im_wrapmany( invec, out, + (im_wrapmany_fn) join_double, out, NULL ) ) + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/conversion/im_rightshift_size.c b/libsrc/conversion/im_rightshift_size.c new file mode 100644 index 00000000..4ce46917 --- /dev/null +++ b/libsrc/conversion/im_rightshift_size.c @@ -0,0 +1,298 @@ +/* @(#) Decrease the size of an image by a power-of-two factor, summing the + * @(#) values of pixels, and shifting to give output of the specified band + * @(#) format. + * @(#) + * @(#) int + * @(#) im_rightshift_size( + * @(#) IMAGE *in, + * @(#) IMAGE *out, + * @(#) int xshift, + * @(#) int yshift, + * @(#) int band_fmt + * @(#) ); + * @(#) + * + * Copyright: 2006, Tom Vajzovic + * + * Author: Tom Vajzovic + * + * Written on: 2006-07-26 + * + * 2006-12-17 tcv: + * - rewrite generator function macros to produce many smaller functions + * - remove post-shift-up functionality + * 2007-02-02 jc + * - added return 0; on success + * - FUNCTION_NAME updated + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H */ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + + +/** LOCAL FUNCTION DECLARATIONS **/ + +/* function prototype macro */ +#define GEN_FUNC( SHIFT_MACRO, FROM_T, TO_T, SUM_T ) \ +static int gen_ ## SHIFT_MACRO ## _ ## FROM_T ## _to_ ## TO_T ## _with_ ## SUM_T( \ + REGION *to_make, REGION *make_from, void *unrequired, int *params ); + +/* macros to call function prototype macro for all possible combinations of types and macros */ +#define GEN_FUNCS_TO( SIGN, FROM_T, TO_T ) \ + GEN_FUNC( PRE_POST_SHIFT, FROM_T, TO_T, SIGN ## 64 ) \ + GEN_FUNC( POST_SHIFT, FROM_T, TO_T, SIGN ## 64 ) \ + GEN_FUNC( POST_SHIFT, FROM_T, TO_T, SIGN ## 32 ) \ + GEN_FUNC( NO_SHIFT, FROM_T, TO_T, SIGN ## 32 ) + +#define GEN_FUNCS_FROM( SIGN, FROM_T ) \ + GEN_FUNCS_TO( SIGN, FROM_T, SIGN ## 32 ) \ + GEN_FUNCS_TO( SIGN, FROM_T, SIGN ## 16 ) \ + GEN_FUNCS_TO( SIGN, FROM_T, SIGN ## 8 ) + +#define GEN_FUNCS_SIGN( SIGN ) \ + GEN_FUNCS_FROM( SIGN, SIGN ## 32 ) \ + GEN_FUNCS_FROM( SIGN, SIGN ## 16 ) \ + GEN_FUNCS_FROM( SIGN, SIGN ## 8 ) + +GEN_FUNCS_SIGN( gint ) +GEN_FUNCS_SIGN( guint ) + + +/** FUNCTION DEFINITIONS **/ + +int +im_rightshift_size( IMAGE *in, IMAGE *out, int xshift, int yshift, int band_fmt ){ +#define FUNCTION_NAME "im_rightshift_size" + int *params; + int needbits; + int outbits; + + if( im_piocheck( in, out ) ) + return -1; + + if( xshift < 0 || yshift < 0 ){ + im_error( FUNCTION_NAME, _( "bad arguments" ) ); + return -1; + } + if( ! xshift && ! yshift ){ + im_warn( FUNCTION_NAME, _( "shift by zero: falling back to im_copy" ) ); + return im_copy( in, out ); + } + if( ! in-> Xsize >> xshift || ! in-> Ysize >> yshift ){ + im_error( FUNCTION_NAME, _( "would result in zero size output image" ) ); + return -1; + } + if( ! im_isint( in ) ){ + im_error( FUNCTION_NAME, _( "integer type images only" ) ); + return -1; + } + if( IM_CODING_NONE != in->Coding ){ + im_error( FUNCTION_NAME, _( "uncoded images only" ) ); + return -1; + } + if( im_isuint( in ) ){ + if( IM_BANDFMT_UCHAR != band_fmt && IM_BANDFMT_USHORT != band_fmt && IM_BANDFMT_UINT != band_fmt ){ + im_error( FUNCTION_NAME, _( "unsigned input means that output must be unsigned int, short or char" ) ); + return -1; + } + } + else { + if( IM_BANDFMT_CHAR != band_fmt && IM_BANDFMT_SHORT != band_fmt && IM_BANDFMT_INT != band_fmt ){ + im_error( FUNCTION_NAME, _( "signed input means that output must be signed int, short or char" ) ); + return -1; + } + } + outbits= im_bits_of_fmt( band_fmt ); + + if( -1 == outbits ) + return -1; + + needbits= im_bits_of_fmt( in-> BandFmt ) + xshift + yshift; + + params= IM_ARRAY( out, 4, int ); + + if( ! params ) + return -1; + + params[ 0 ]= xshift; /* xshift */ + params[ 1 ]= yshift; /* yshift */ + + if( im_cp_desc( out, in ) ) + return -1; + + out-> Xsize >>= xshift; + out-> Ysize >>= yshift; + out-> Xres/= ( 1 << xshift ); /* x scale */ + out-> Yres/= ( 1 << yshift ); /* y scale */ + out-> BandFmt= band_fmt; + out-> Bbits= outbits; + + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return -1; + +#define RETURN_MACRO( MACRO, FROM_T, TO_T, SUM_T ) \ + return im_generate( out, im_start_one, gen_ ## MACRO ## _ ## FROM_T ## _to_ ## TO_T ## _with_ ## SUM_T, im_stop_one, in, params ); + +#define RETURN_MACRO_OUTS( MACRO, SUM_SIZE, OUT_SIZE ) switch( in-> BandFmt ){ \ + \ + case IM_BANDFMT_CHAR: \ + RETURN_MACRO( MACRO, gint8, gint ## OUT_SIZE, gint ## SUM_SIZE ) \ + \ + case IM_BANDFMT_UCHAR: \ + RETURN_MACRO( MACRO, guint8, guint ## OUT_SIZE, guint ## SUM_SIZE ) \ + \ + case IM_BANDFMT_SHORT: \ + RETURN_MACRO( MACRO, gint16, gint ## OUT_SIZE, gint ## SUM_SIZE ) \ + \ + case IM_BANDFMT_USHORT: \ + RETURN_MACRO( MACRO, guint16, guint ## OUT_SIZE, guint ## SUM_SIZE ) \ + \ + case IM_BANDFMT_INT: \ + RETURN_MACRO( MACRO, gint32, gint ## OUT_SIZE, gint ## SUM_SIZE ) \ + \ + case IM_BANDFMT_UINT: \ + RETURN_MACRO( MACRO, guint32, guint ## OUT_SIZE, guint ## SUM_SIZE ) \ +} + +#define RETURN_MACRO_SUMSS( MACRO, SUM_SIZE ) switch( out-> Bbits ){ \ + case 32: \ + RETURN_MACRO_OUTS( MACRO, SUM_SIZE, 32 ) \ + case 16: \ + RETURN_MACRO_OUTS( MACRO, SUM_SIZE, 16 ) \ + case 8: \ + RETURN_MACRO_OUTS( MACRO, SUM_SIZE, 8 ) \ +} + + if( needbits > 64 ){ + params[ 2 ]= needbits - 64; + params[ 3 ]= 64 - outbits; + RETURN_MACRO_SUMSS( PRE_POST_SHIFT, 64 ) + } + if( needbits > 32 ){ + params[ 3 ]= needbits - outbits; + RETURN_MACRO_SUMSS( POST_SHIFT, 64 ) + } + if( needbits > outbits ){ + params[ 3 ]= needbits - outbits; + RETURN_MACRO_SUMSS( POST_SHIFT, 32 ) + } + + RETURN_MACRO_SUMSS( NO_SHIFT, 32 ) + + return 0; + +#undef FUNCTION_NAME +} + +/** LOCAL FUNCTION DEFINITIONS **/ + +/* macros used in the function creating macro */ +#define PRE_POST_SHIFT() pixel+= from[ fx ] >> preshift; \ + to[ tx ]= pixel >> postshift; + +#define POST_SHIFT() pixel+= from[ fx ]; \ + to[ tx ]= pixel >> postshift; + +#define NO_SHIFT() pixel+= from[ fx ]; \ + to[ tx ]= pixel; + +/* function creating macro */ +#undef GEN_FUNC +#define GEN_FUNC( SHIFT_MACRO, FROM_T, TO_T, SUM_T ) \ +static int gen_ ## SHIFT_MACRO ## _ ## FROM_T ## _to_ ## TO_T ## _with_ ## SUM_T( \ + REGION *to_make, REGION *make_from, void *unrequired, int *params ){ \ + \ + int xshift= params[0]; \ + int yshift= params[1]; \ + int preshift= params[2]; \ + int postshift= params[3]; \ + Rect need= { \ + to_make-> valid. left << xshift, \ + to_make-> valid. top << yshift, \ + to_make-> valid. width << xshift, \ + to_make-> valid. height << yshift \ + }; \ + TO_T *to_start= (TO_T*) IM_REGION_ADDR( to_make, to_make-> valid. left, to_make-> valid. top ); \ + TO_T *to, *ty_stop; \ + SUM_T pixel; \ + FROM_T *from_start, *from_row, *from, *fy_stop; \ + \ + int band, tx, fx; \ + int tx_max= to_make-> valid. width * to_make-> im-> Bands; \ + int fx_max= to_make-> im-> Bands << xshift; \ + \ + size_t t_skip= IM_REGION_LSKIP( to_make ) / sizeof( TO_T ); \ + size_t f_skip; \ + \ + if( im_prepare( make_from, &need ) || ! im_rect_includesrect( &make_from-> valid, &need ) ) \ + return -1; \ + \ + from_start= (FROM_T*) IM_REGION_ADDR( make_from, need. left, need. top ); \ + f_skip= IM_REGION_LSKIP( make_from ) / sizeof( FROM_T ); \ + \ + for( band= 0; band < make_from-> im-> Bands; ++band ){ \ + \ + to= to_start + band; \ + ty_stop= to + t_skip * to_make-> valid. height; \ + \ + from_row= from_start + band; \ + \ + for( ; to < ty_stop; to+= t_skip, from_row+= f_skip << yshift ) \ + for( tx= 0; tx < tx_max; tx+= to_make-> im-> Bands ){ \ + \ + from= from_row + ( tx << xshift ); \ + fy_stop= from + ( f_skip << yshift ); \ + \ + pixel= 0; \ + \ + for( ; from < fy_stop; from+= f_skip ) \ + for( fx= 0; fx < fx_max; fx+= to_make-> im-> Bands ) \ + SHIFT_MACRO() \ + } \ + } \ + return 0; \ +} + +/* create functions for all possible combinations of types and macros */ +GEN_FUNCS_SIGN( gint ) +GEN_FUNCS_SIGN( guint ) diff --git a/libsrc/conversion/im_rot180.c b/libsrc/conversion/im_rot180.c new file mode 100644 index 00000000..2a13fd18 --- /dev/null +++ b/libsrc/conversion/im_rot180.c @@ -0,0 +1,158 @@ +/* @(#) rotates an image 180 degrees + * @(#) Usage: + * @(#) im_rot180(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * Copyright: 1991, N. Dessipris + * Written on: 28/10/91 + * Updated on: 2/4/92, J.Cupitt + * bugs in im_la90rot fixed, now works for any type. + * 19/7/93 JC + * - IM_CODING_LABQ allowed now + * 15/11/94 JC + * - name changed + * - memory leaks fixed + * 8/2/95 JC + * - oops! memory allocation problem fixed + * 18/5/95 JC + * - IM_MAXLINES increased + * 13/8/96 JC + * - rewritten for partials + * - adapted from im_rot90 + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Rotate a small piece. + */ +static int +rot180_gen( REGION *or, REGION *ir, IMAGE *in ) +{ + /* Output area. + */ + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int x, y; + + /* Pixel geometry. + */ + int ps; + + /* Find the area of the input image we need. + */ + Rect need; + + need.left = in->Xsize - ri; + need.top = in->Ysize - bo; + need.width = r->width; + need.height = r->height; + if( im_prepare( ir, &need ) ) + return( -1 ); + + /* Find PEL size and line skip for ir. + */ + ps = IM_IMAGE_SIZEOF_PEL( in ); + + /* Rotate the bit we now have. + */ + for( y = to; y < bo; y++ ) { + /* Start of this output line. + */ + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Corresponding position in ir. + */ + PEL *p = (PEL *) IM_REGION_ADDR( ir, + need.left + need.width - 1, + need.top + need.height - (y - to) - 1 ); + + /* Blap across! + */ + for( x = le; x < ri; x++ ) { + memcpy( q, p, ps ); + q += ps; + p -= ps; + } + } + + return( 0 ); +} + +int +im_rot180( IMAGE *in, IMAGE *out ) +{ + /* Make output image. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_rot180", _( "uncoded or IM_CODING_LABQ only" ) ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* We are fastest with thinstrip. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, rot180_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + out->Xoffset = in->Xsize; + out->Yoffset = in->Ysize; + + return( 0 ); +} + diff --git a/libsrc/conversion/im_rot270.c b/libsrc/conversion/im_rot270.c new file mode 100644 index 00000000..5e5bdd10 --- /dev/null +++ b/libsrc/conversion/im_rot270.c @@ -0,0 +1,162 @@ +/* @(#) rotates an image 270 degrees + * @(#) Usage: + * @(#) im_rot270(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * Copyright: 1991, N. Dessipris + * Written on: 28/10/91 + * Updated on: 2/4/92, J.Cupitt + * bugs in im_la90rot fixed, now works for any type. + * 19/7/93 JC + * - IM_CODING_LABQ allowed now + * 15/11/94 JC + * - name changed + * - memory leaks fixed + * 8/2/95 JC + * - oops! memory allocation problem fixed + * 18/5/95 JC + * - IM_MAXLINES increased + * 13/8/96 JC + * - rewritten for partials + * 6/11/02 JC + * - speed-up ... replace memcpy() with a loop for small pixels + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Rotate a small piece. + */ +static int +rot270_gen( REGION *or, REGION *ir, IMAGE *in ) +{ + /* Output area. + */ + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int x, y, i; + + /* Pixel geometry. + */ + int ps, ls; + + /* Find the area of the input image we need. + */ + Rect need; + + need.left = in->Xsize - bo; + need.top = le; + need.width = r->height; + need.height = r->width; + if( im_prepare( ir, &need ) ) + return( -1 ); + + /* Find PEL size and line skip for ir. + */ + ps = IM_IMAGE_SIZEOF_PEL( in ); + ls = IM_REGION_LSKIP( ir ); + + /* Rotate the bit we now have. + */ + for( y = to; y < bo; y++ ) { + /* Start of this output line. + */ + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Corresponding position in ir. + */ + PEL *p = (PEL *) IM_REGION_ADDR( ir, + need.left + need.width - (y - to) - 1, + need.top ); + + for( x = le; x < ri; x++ ) { + for( i = 0; i < ps; i++ ) + q[i] = p[i]; + + q += ps; + p += ls; + } + } + + return( 0 ); +} + +int +im_rot270( IMAGE *in, IMAGE *out ) +{ + /* Make output image. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_rot270", _( "uncoded or IM_CODING_LABQ only" ) ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = in->Ysize; + out->Ysize = in->Xsize; + + /* We want smalltile if possible. + */ + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, rot270_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = in->Xsize; + + return( 0 ); +} + diff --git a/libsrc/conversion/im_rot90.c b/libsrc/conversion/im_rot90.c new file mode 100644 index 00000000..9ee815ff --- /dev/null +++ b/libsrc/conversion/im_rot90.c @@ -0,0 +1,162 @@ +/* @(#) rotates an image 90 degrees + * @(#) Usage: + * @(#) im_rot90(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * Copyright: 1991, N. Dessipris + * Written on: 28/10/91 + * Updated on: 2/4/92, J.Cupitt + * bugs in im_la90rot fixed, now works for any type. + * 19/7/93 JC + * - IM_CODING_LABQ allowed now + * 15/11/94 JC + * - name changed + * - memory leaks fixed + * 8/2/95 JC + * - oops! memory allocation problem fixed + * 18/5/95 JC + * - IM_MAXLINES increased + * 13/8/96 JC + * - rewritten for partials + * 6/11/02 JC + * - speed-up ... replace memcpy() with a loop for small pixels + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Rotate a small piece. + */ +static int +rot90_gen( REGION *or, REGION *ir, IMAGE *in ) +{ + /* Output area. + */ + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int x, y, i; + + /* Pixel geometry. + */ + int ps, ls; + + /* Find the area of the input image we need. + */ + Rect need; + + need.left = to; + need.top = in->Ysize - ri; + need.width = r->height; + need.height = r->width; + if( im_prepare( ir, &need ) ) + return( -1 ); + + /* Find PEL size and line skip for ir. + */ + ps = IM_IMAGE_SIZEOF_PEL( in ); + ls = IM_REGION_LSKIP( ir ); + + /* Rotate the bit we now have. + */ + for( y = to; y < bo; y++ ) { + /* Start of this output line. + */ + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Corresponding position in ir. + */ + PEL *p = (PEL *) IM_REGION_ADDR( ir, + need.left + y - to, + need.top + need.height - 1 ); + + for( x = le; x < ri; x++ ) { + for( i = 0; i < ps; i++ ) + q[i] = p[i]; + + q += ps; + p -= ls; + } + } + + return( 0 ); +} + +int +im_rot90( IMAGE *in, IMAGE *out ) +{ + /* Make output image. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_rot90", _( "uncoded or IM_CODING_LABQ only" ) ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = in->Ysize; + out->Ysize = in->Xsize; + + /* We want smalltile if possible. + */ + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, rot90_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + out->Xoffset = in->Ysize; + out->Yoffset = 0; + + return( 0 ); +} + diff --git a/libsrc/conversion/im_scale.c b/libsrc/conversion/im_scale.c new file mode 100644 index 00000000..39427986 --- /dev/null +++ b/libsrc/conversion/im_scale.c @@ -0,0 +1,98 @@ +/* @(#) Scale an image so that min(im) maps to 0 and max(im) maps to 255. If + * @(#) min(im)==max(im), then we return black. Any non-complex type. + * @(#) + * @(#) int + * @(#) im_scale( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Author: John Cupitt + * Written on: 22/4/93 + * Modified on: + * 30/6/93 JC + * - adapted for partial v2 + * - ANSI + * 31/8/93 JC + * - calculation of offset now includes scale + * 8/5/06 + * - set Type on output too + * 16/10/06 + * - what? no, don't set Type, useful to be able to scale histograms, for + * example + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Scale, as noted above. + */ +int +im_scale( IMAGE *in, IMAGE *out ) +{ + DOUBLEMASK *stats; + IMAGE *t = im_open_local( out, "im_scale:1", "p" ); + double mx, mn; + double scale, offset; + + /* Measure min and max, calculate transform. + */ + if( !t || !(stats = im_stats( in )) ) + return( -1 ); + mn = stats->coeff[0]; + mx = stats->coeff[1]; + im_free_dmask( stats ); + + if( mn == mx ) + /* Range of zero: just return black. + */ + return( im_black( out, in->Xsize, in->Ysize, in->Bands ) ); + scale = 255.0 / (mx - mn); + offset = -(mn * scale); + + /* Transform! + */ + if( im_lintra( scale, in, offset, t ) || + im_clip( t, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_scaleps.c b/libsrc/conversion/im_scaleps.c new file mode 100644 index 00000000..5a0436d3 --- /dev/null +++ b/libsrc/conversion/im_scaleps.c @@ -0,0 +1,98 @@ +/* @(#) transform with log10(1.0 + pow(x, 0.25)) + .5, then scale so max + * @(#) == 255. + * @(#) + * @(#) int im_scaleps(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: 14/03/1991 + * 15/6/93 J.Cupitt + * - externs fixed + * - includes fixed + * 13/2/95 JC + * - ANSIfied + * - cleaned up + * 11/7/02 JC + * - rewritten ... got rid of the stuff for handling -ves, never used + * (and was broken anyway) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Scale, as noted above. + */ +int +im_scaleps( IMAGE *in, IMAGE *out ) +{ + IMAGE *t[4]; + double mx; + double scale; + + if( im_open_local_array( out, t, 4, "im_scaleps-1", "p" ) || + im_max( in, &mx ) ) + return( -1 ); + + if( mx <= 0.0 ) + /* Range of zero: just return black. + */ + return( im_black( out, in->Xsize, in->Ysize, in->Bands ) ); + + scale = 255.0 / log10( 1.0 + pow( mx, .25 ) ); + + /* Transform! + */ + if( im_powtra( in, t[0], 0.25 ) || + im_lintra( 1.0, t[0], 1.0, t[1] ) || + im_log10tra( t[1], t[2] ) || + im_lintra( scale, t[2], 0.0, t[3] ) || + im_clip( t[3], out ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/conversion/im_slice.c b/libsrc/conversion/im_slice.c new file mode 100644 index 00000000..2e1077aa --- /dev/null +++ b/libsrc/conversion/im_slice.c @@ -0,0 +1,157 @@ +/* @(#) Slices an image using two thresholds. Works for any non-complex type. + * @(#) Output has three levels 0 128 and 255. Values below or = t1 are 0, + * @(#) above t2 are 255 and the remaining are 128. + * @(#) Input is either memory mapped or in a buffer. + * @(#) It is implied that t1 is less than t2; however the program checks + * @(#) if they are the wrong way and swaps them + * @(#) + * @(#) int im_slice(in, out, t1, t2) + * @(#) IMAGE *in, *out; + * @(#) double t1, t2; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1991, N. Dessipris + * + * Author: N. Dessipris + * Written on: 15/03/1991 + * Modified on : + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define BRIGHT 255 +#define GREY 128 +#define DARK 0 + +/* Useful: Call a macro with the name, type pairs for all VIPS functions. + */ +#define im_for_all_types(A) \ + case IM_BANDFMT_UCHAR: A(unsigned char); break; \ + case IM_BANDFMT_CHAR: A(signed char); break; \ + case IM_BANDFMT_USHORT: A(unsigned short); break; \ + case IM_BANDFMT_SHORT: A(signed short); break; \ + case IM_BANDFMT_UINT: A(unsigned int); break; \ + case IM_BANDFMT_INT: A(signed int); break; \ + case IM_BANDFMT_FLOAT: A(float); break; + +/* Replacement for im_slice */ +int +im_slice( in, out, t1, t2 ) +IMAGE *in, *out; +double t1, t2; +{ + int x, y, z; + PEL *bu; /* Buffer we write to */ + int s, epl; /* Size and els per line */ + double thresh1, thresh2; + +/* Check our args. */ + if( im_iocheck( in, out ) ) + { + im_errormsg("im_slice: im_iocheck failed"); + return( -1 ); + } + if( in->Coding != IM_CODING_NONE ) + { + im_errormsg("im_slice: input should be uncoded"); + return( -1 ); + } + +/* Set up the output header. */ + if( im_cp_desc( out, in ) ) + { + im_errormsg("im_slice: im_cp_desc failed"); + return( -1 ); + } + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = IM_BBITS_BYTE; + if( im_setupout( out ) ) + { + im_errormsg("im_slice: im_setupout failed"); + return( -1 ); + } + + if ( t1 <= t2 ) + { thresh1 = t1; thresh2 = t2; } + else + { thresh1 = t2; thresh2 = t1; } +/* Make buffer for building o/p in. */ + epl = in->Xsize * in->Bands; + s = epl * sizeof( PEL ); + if( (bu = (PEL *) im_malloc( out, (unsigned)s )) == NULL ) + return( -1 ); + +/* Define what we do for each band element type. */ +#define im_slice_loop(TYPE)\ + { TYPE *a = (TYPE *) in->data;\ + \ + for( y = 0; y < in->Ysize; y++ ) {\ + PEL *b = bu;\ + \ + for( x = 0; x < in->Xsize; x++ )\ + for( z = 0; z < in->Bands; z++ ) {\ + double f = (double) *a++;\ + if ( f <= thresh1)\ + *b++ = (PEL)DARK;\ + else if ( f > thresh2 )\ + *b++ = (PEL)BRIGHT;\ + else \ + *b++ = (PEL)GREY;\ + }\ + \ + if( im_writeline( y, out, bu ) )\ + return( -1 );\ + }\ + } + +/* Do the above for all image types. */ + switch( in->BandFmt ) { + im_for_all_types( im_slice_loop ); + + default: + im_errormsg("im_slice: Unknown input format"); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/conversion/im_subsample.c b/libsrc/conversion/im_subsample.c new file mode 100644 index 00000000..5b162a6c --- /dev/null +++ b/libsrc/conversion/im_subsample.c @@ -0,0 +1,241 @@ +/* @(#) Shrink any image by some integer xy factor. Sub-samples! Partial, and + * @(#) quick as a result. + * @(#) + * @(#) int + * @(#) im_subsample( in, out, xshrink, yshrink ) + * @(#) IMAGE *in, *out; + * @(#) int xshrink, yshrink; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * + * 3/7/95 JC + * - adapted from im_shrink() + * 3/8/02 JC + * - fall back to im_copy() for x/y factors == 1 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Maximum width of input we ask for. + */ +#define IM_MAX_WIDTH (1000) + +/* Our main parameter struct. + */ +typedef struct { + int xshrink; /* Subsample factors */ + int yshrink; +} SubsampleInfo; + +/* Subsample a REGION. We fetch in IM_MAX_WIDTH pixel-wide strips, left-to-right + * across the input. + */ +static int +line_shrink_gen( REGION *or, REGION *ir, IMAGE *in, SubsampleInfo *st ) +{ + Rect *r = &or->valid; + + int le = r->left; + int ri = IM_RECT_RIGHT( r ); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int ps = IM_IMAGE_SIZEOF_PEL( in ); + + int owidth = IM_MAX_WIDTH / st->xshrink; + + Rect s; + int x, y; + int z, b; + + /* Loop down the region. + */ + for( y = to; y < bo; y++ ) { + char *q = IM_REGION_ADDR( or, le, y ); + char *p; + + /* Loop across the region, in owidth sized pieces. + */ + for( x = le; x < ri; x += owidth ) { + /* How many pixels do we make this time? + */ + int ow = IM_MIN( owidth, ri - x ); + + /* Ask for this many from input ... can save a + * little here! + */ + int iw = ow * st->xshrink - (st->xshrink - 1); + + /* Ask for input. + */ + s.left = x * st->xshrink; + s.top = y * st->yshrink; + s.width = iw; + s.height = 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Append new pels to output. + */ + p = IM_REGION_ADDR( ir, s.left, s.top ); + for( z = 0; z < ow; z++ ) { + for( b = 0; b < ps; b++ ) + q[b] = p[b]; + + q += ps; + p += ps * st->xshrink; + } + } + } + + return( 0 ); +} + +/* Fetch one pixel at a time ... good for very large shrinks, or for SMALLTILE + * pipes. + */ +static int +point_shrink_gen( REGION *or, REGION *ir, IMAGE *in, SubsampleInfo *st ) +{ + Rect *r = &or->valid; + + int le = r->left; + int ri = IM_RECT_RIGHT( r ); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int ps = IM_IMAGE_SIZEOF_PEL( in ); + + Rect s; + int x, y; + int b; + + /* Loop down the region. + */ + for( y = to; y < bo; y++ ) { + char *q = IM_REGION_ADDR( or, le, y ); + char *p; + + /* Loop across the region, in owidth sized pieces. + */ + for( x = le; x < ri; x++ ) { + /* Ask for input. + */ + s.left = x * st->xshrink; + s.top = y * st->yshrink; + s.width = 1; + s.height = 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Append new pels to output. + */ + p = IM_REGION_ADDR( ir, s.left, s.top ); + for( b = 0; b < ps; b++ ) + q[b] = p[b]; + q += ps; + } + } + + return( 0 ); +} + +int +im_subsample( IMAGE *in, IMAGE *out, int xshrink, int yshrink ) +{ + SubsampleInfo *st; + + /* Check parameters. + */ + if( xshrink < 1 || yshrink < 1 ) { + im_errormsg( "im_subsample: factors should both be >= 1" ); + return( -1 ); + } + if( xshrink == 1 && yshrink == 1 ) + return( im_copy( in, out ) ); + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Prepare output. Note: we round the output width down! + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = in->Xsize / xshrink; + out->Ysize = in->Ysize / yshrink; + out->Xres = in->Xres / xshrink; + out->Yres = in->Yres / yshrink; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_subsample: image has shrunk to nothing" ); + return( -1 ); + } + + /* Build and attach state struct. + */ + if( !(st = IM_NEW( out, SubsampleInfo )) ) + return( -1 ); + st->xshrink = xshrink; + st->yshrink = yshrink; + + /* Set demand hints. We want THINSTRIP, as we will be demanding a + * large area of input for each output line. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! If this is a very large shrink, then it's + * probably faster to do it a pixel at a time. If this is SMALLTILE, + * then it will hate long lines and we should always do 1 pixel at a + * time. + */ + if( in->dhint == IM_SMALLTILE || xshrink > 10 ) { + if( im_generate( out, + im_start_one, point_shrink_gen, im_stop_one, in, st ) ) + return( -1 ); + } + else { + if( im_generate( out, + im_start_one, line_shrink_gen, im_stop_one, in, st ) ) + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/conversion/im_system.c b/libsrc/conversion/im_system.c new file mode 100644 index 00000000..419379bb --- /dev/null +++ b/libsrc/conversion/im_system.c @@ -0,0 +1,226 @@ +/* im_system(): run a command on an image + * + * 7/3/00 JC + * - hacked it in + * 21/10/02 JC + * - use mktemp() if mkstemp() is not available + * 10/3/03 JC + * - out can be NULL + * 23/12/04 + * - use g_mkstemp() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define IM_MAX_STRSIZE (4096) + +#ifdef OS_WIN32 +#define popen(b,m) _popen(b,m) +#define pclose(f) _pclose(f) +#define mktemp(f) _mktemp(f) +#endif /*OS_WIN32*/ + +/* A string being written to ... multiple calls to buf_append add to it, on + * overflow append "..." and block further writes. + */ +typedef struct { + char *base; /* String base */ + int mx; /* Maximum length */ + int i; /* Current write point */ + int full; /* String has filled, block writes */ + int lasti; /* For read-recent */ +} BufInfo; + +/* Set to start state. + */ +static void +buf_rewind( BufInfo *buf ) +{ + buf->i = 0; + buf->lasti = 0; + buf->full = 0; + + strcpy( buf->base, "" ); +} + +/* Init a buf struct. + */ +static void +buf_init( BufInfo *buf, char *base, int mx ) +{ + buf->base = base; + buf->mx = mx; + buf_rewind( buf ); +} + +/* Append string to buf. Error on overflow. + */ +static int +buf_appends( BufInfo *buf, const char *str ) +{ + int len; + int avail; + int cpy; + + if( buf->full ) + return( 0 ); + + /* Amount we want to copy. + */ + len = strlen( str ); + + /* Space available. + */ + avail = buf->mx - buf->i - 4; + + /* Amount we actually copy. + */ + cpy = IM_MIN( len, avail ); + + strncpy( buf->base + buf->i, str, cpy ); + buf->i += cpy; + + if( buf->i >= buf->mx - 4 ) { + buf->full = 1; + strcpy( buf->base + buf->mx - 4, "..." ); + buf->i = buf->mx - 1; + return( 0 ); + } + + return( 1 ); +} + +/* Read all text from buffer. + */ +static char * +buf_all( BufInfo *buf ) +{ + buf->base[buf->i] = '\0'; + return( buf->base ); +} + +/* Do popen(), with printf-style args. + */ +static FILE * +popenf( const char *fmt, const char *mode, ... ) +{ + va_list args; + char buf[IM_MAX_STRSIZE]; + + va_start( args, mode ); + (void) im_vsnprintf( buf, IM_MAX_STRSIZE, fmt, args ); + va_end( args ); + + return( popen( buf, mode ) ); +} + +/* Run a command on an IMAGE ... copy to tmp (if necessary), run + * command on it, unlink (if we copied), return stdout from command. + */ +int +im_system( IMAGE *im, const char *cmd, char **out ) +{ + char *filename = im->filename; + int delete = 0; + FILE *fp; + + if( !im_isfile( im ) ) { + const char *tmpd; + char name[IM_MAX_STRSIZE]; + IMAGE *disc; + + if( !(tmpd = g_getenv( "TMPDIR" )) ) + tmpd = "/tmp"; + strcpy( name, tmpd ); + strcat( name, "/vips_XXXXXX" ); + + close( g_mkstemp( name ) ); + filename = im_strdup( NULL, name ); + + if( !(disc = im_open( filename, "w" )) ) { + unlink( filename ); + free( filename ); + return( -1 ); + } + if( im_copy( im, disc ) ) { + im_close( disc ); + unlink( filename ); + free( filename ); + return( -1 ); + } + im_close( disc ); + delete = 1; + } + + if( (fp = popenf( cmd, "r", filename )) ) { + char line[IM_MAX_STRSIZE]; + BufInfo buf; + char txt_buffer[IM_MAX_STRSIZE]; + + buf_init( &buf, txt_buffer, IM_MAX_STRSIZE ); + while( fgets( line, IM_MAX_STRSIZE, fp ) ) + if( !buf_appends( &buf, line ) ) + break; + pclose( fp ); + + if( out ) + *out = im_strdup( NULL, buf_all( &buf ) ); + } + + if( delete ) { + unlink( filename ); + im_free( filename ); + } + + if( !fp ) { + im_errormsg( "popen: %s", strerror( errno ) ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/conversion/im_tbjoin.c b/libsrc/conversion/im_tbjoin.c new file mode 100644 index 00000000..52831b8b --- /dev/null +++ b/libsrc/conversion/im_tbjoin.c @@ -0,0 +1,89 @@ +/* @(#) To join two images top bottom. The resultant image has + * @(#) Ysize = im1.Ysize + im2.Ysize and Xsize the min of im1.Xsize, im2.Xsize + * @(#) Input images should have the same number of Bands and BandFmt + * @(#) + * @(#) Usage: + * @(#) int im_tbjoin(top, bottom, out) + * @(#) IMAGE *top, *bottom, *out; + * @(#) + * @(#) + * + * Copyright: 1990, 1991 Kirk Martinez, N. Dessipris + * Author: Kirk Martinez, N. Dessipris + * Written on: 9/6/90 + * Updated: 15/03/1991, N. Dessipris + * 31/8/93 JC + * - externs removed + * 14/11/94 JC + * - tidied up + * - now works for IM_CODING_LABQ too + * - image compatibility bug fixed + * 28/4/95 JC + * - y arg to 2nd set of im_writeline()s was wrong + * 23/10/95 JC + * - rewritten in terms of im_insert() + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_tbjoin( IMAGE *top, IMAGE *bottom, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_tbjoin:1", "p" ); + + /* Paste top and bottom together. + */ + if( !t1 || im_insert( top, bottom, t1, 0, top->Ysize ) ) + return( -1 ); + + /* Extract the part which the old im_tbjoin() would have made. + */ + if( im_extract_area( t1, out, + 0, 0, IM_MIN( top->Xsize, bottom->Xsize ), t1->Ysize ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = top->Ysize; + + return( 0 ); +} diff --git a/libsrc/conversion/im_text.c b/libsrc/conversion/im_text.c new file mode 100644 index 00000000..37427e41 --- /dev/null +++ b/libsrc/conversion/im_text.c @@ -0,0 +1,237 @@ +/* @(#) Make an image containting text as a bitmap. One band uchar output, + * @(#) with 0 - 255 for black - white. + * @(#) + * @(#) int + * @(#) im_text( IMAGE *out, const char *text, const char *font, + * @(#) int width, int alignment, int dpi ) + * @(#) + * @(#) Font is a Pango font specification, eg. "Sans 12", or "Times News + * @(#) Roman Italic 12". text should be coded as utf-8. dpi is resolution + * @(#) in dots per inch. alignment is 0, 1 and 2 for right, centre and left + * @(#) alignment. width is the wrap width in pixels. + * @(#) + * @(#) Returns 0 on success and -1 on error. + * + * Written on: 20/5/04 + * 29/7/04 + * - !HAVE_PANGOFT2 was broken, thanks Kenneth + * 15/11/04 + * - gah, still broken, thanks Stefan + * 5/4/06 + * - return an error for im_text( "" ) rather than trying to make an + * empty image + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef HAVE_PANGOFT2 +#include +#include +#endif /*HAVE_PANGOFT2*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef HAVE_PANGOFT2 + +static PangoLayout * +text_layout_new( PangoContext *context, + const char *text, const char *font, int width, int alignment, int dpi ) +{ + PangoLayout *layout; + PangoFontDescription *font_description; + + layout = pango_layout_new( context ); + pango_layout_set_markup( layout, text, -1 ); + + font_description = pango_font_description_from_string( font ); + pango_layout_set_font_description( layout, font_description ); + pango_font_description_free( font_description ); + + if( width > 0 ) + pango_layout_set_width( layout, width * PANGO_SCALE ); + + if( alignment < 0 || alignment > 2 ) + alignment = PANGO_ALIGN_RIGHT; + pango_layout_set_alignment( layout, (PangoAlignment) alignment ); + + return( layout ); +} + +static int +text_ft_to_vips( FT_Bitmap *bitmap, IMAGE *out ) +{ + int y; + + if( im_outcheck( out ) ) + return( -1 ); + im_initdesc( out, bitmap->width, bitmap->rows, 1, + IM_BBITS_BYTE, IM_BANDFMT_UCHAR, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + if( im_setupout( out ) ) + return( -1 ); + + for( y = 0; y < bitmap->rows; y++ ) + if( im_writeline( y, out, + (PEL *) bitmap->buffer + y * bitmap->pitch ) ) + return( -1 ); + + return( 0 ); +} + +static int +text_layout_render_to_image( PangoLayout *layout, IMAGE *out ) +{ + PangoRectangle logical_rect; + FT_Bitmap bitmap; + int left; + int top; + int width; + int height; + + pango_layout_get_extents( layout, NULL, &logical_rect ); + +#ifdef DEBUG + printf( "logical left = %d, top = %d, width = %d, height = %d\n", + PANGO_PIXELS( logical_rect.x ), + PANGO_PIXELS( logical_rect.y ), + PANGO_PIXELS( logical_rect.width ), + PANGO_PIXELS( logical_rect.height ) ); +#endif /*DEBUG*/ + + left = PANGO_PIXELS( logical_rect.x ); + top = PANGO_PIXELS( logical_rect.y ); + width = PANGO_PIXELS( logical_rect.width ); + height = PANGO_PIXELS( logical_rect.height ); + + /* Can happen for "", for example. + */ + if( width == 0 || height == 0 ) { + im_error( "im_text", _( "no text to render" ) ); + return( -1 ); + } + + bitmap.width = width; + bitmap.pitch = (bitmap.width + 3) & ~3; + bitmap.rows = height; + if( !(bitmap.buffer = im_malloc( NULL, bitmap.pitch * bitmap.rows )) ) + return( -1 ); + bitmap.num_grays = 256; + bitmap.pixel_mode = ft_pixel_mode_grays; + memset( bitmap.buffer, 0x00, bitmap.pitch * bitmap.rows ); + + if( pango_layout_get_width( layout ) != -1 ) + pango_ft2_render_layout( &bitmap, layout, -left, -top ); + else + pango_ft2_render_layout( &bitmap, layout, 0, 0 ); + if( text_ft_to_vips( &bitmap, out ) ) { + im_free( bitmap.buffer ); + return( -1 ); + } + + im_free( bitmap.buffer ); + + return( 0 ); +} + +static int +text_render_to_image( PangoContext *context, IMAGE *out, + const char *text, const char *font, int width, int alignment, int dpi ) +{ + PangoLayout *layout; + + if( !(layout = text_layout_new( context, text, font, + width, alignment, dpi )) ) + return( -1 ); + + if( text_layout_render_to_image( layout, out ) ) { + g_object_unref( layout ); + return( -1 ); + } + + g_object_unref( layout ); + + return( 0 ); +} + +int +im_text( IMAGE *out, const char *text, const char *font, + int width, int alignment, int dpi ) +{ + static PangoFontMap *fontmap = NULL; + PangoContext *context; + + if( !pango_parse_markup( text, -1, 0, NULL, NULL, NULL, NULL ) ) { + im_error( "im_text", _( "invalid markup in text" ) ); + return( -1 ); + } + + /* Just have one of these, ever. It doesn't close properly when we + * _unref(), so keep it around for reuse. + */ + if( !fontmap ) + fontmap = pango_ft2_font_map_new(); + + pango_ft2_font_map_set_resolution( PANGO_FT2_FONT_MAP( fontmap ), + dpi, dpi ); + context = pango_ft2_font_map_create_context( + PANGO_FT2_FONT_MAP( fontmap ) ); + + if( text_render_to_image( context, out, text, font, + width, alignment, dpi ) ) { + g_object_unref( context ); + return( -1 ); + } + + g_object_unref( context ); + + return( 0 ); +} + +#else /*!HAVE_PANGOFT2*/ + +int +im_text( IMAGE *out, const char *text, const char *font, + int width, int alignment, int dpi ) +{ + im_error( "im_text", _( "pangoft2 support disabled" ) ); + + return( -1 ); +} + +#endif /*HAVE_PANGOFT2*/ diff --git a/libsrc/conversion/im_thresh.c b/libsrc/conversion/im_thresh.c new file mode 100644 index 00000000..3662fece --- /dev/null +++ b/libsrc/conversion/im_thresh.c @@ -0,0 +1,134 @@ +/* @(#) Thresholds an image. Works for any non-complex type. + * @(#) Output is a binary image with 0 and 255 only + * @(#) Input is either memory mapped or in a buffer. + * @(#) + * @(#) int im_thresh(imin, imout, threshold) + * @(#) IMAGE *imin, *imout; + * @(#) double threshold; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1991, N. Dessipris, J Cupitt + * + * Author: N. Dessipris, J. Cupitt + * Written on: 15/03/1991 + * Modified on : + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Useful: Call a macro with the name, type pairs for all VIPS functions. */ +#define BRIGHT 255 +#define DARK 0 +#define im_for_all_types(A) \ + case IM_BANDFMT_UCHAR: A(unsigned char); break; \ + case IM_BANDFMT_CHAR: A(signed char); break; \ + case IM_BANDFMT_USHORT: A(unsigned short); break; \ + case IM_BANDFMT_SHORT: A(signed short); break; \ + case IM_BANDFMT_UINT: A(unsigned int); break; \ + case IM_BANDFMT_INT: A(signed int); break; \ + case IM_BANDFMT_FLOAT: A(float); break; \ + case IM_BANDFMT_DOUBLE: A(double); break; + +/* Replacement for im_thresh */ +int +im_thresh( in, out, threshold ) +IMAGE *in, *out; +double threshold; +{ + int x, y; + PEL *bu; /* Buffer we write to */ + int s, epl; /* Size and els per line */ + +/* Check our args. */ + if( im_iocheck( in, out ) ) + { im_errormsg("im_thresh: im_iocheck failed"); return( -1 ); } + if( in->Coding != IM_CODING_NONE ) + { im_errormsg("im_thresh: input should be uncoded");return(-1);} + +/* Set up the output header. */ + if( im_cp_desc( out, in ) ) + { im_errormsg("im_thresh: im_cp_desc failed"); return( -1 ); } + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = IM_BBITS_BYTE; + if( im_setupout( out ) ) + { + im_errormsg("im_thresh: im_setupout failed"); + return( -1 ); + } + +/* Make buffer for building o/p in. */ + epl = in->Xsize * in->Bands; + s = epl * sizeof( PEL ); + if( (bu = (PEL *) im_malloc( out, (unsigned)s )) == NULL ) + return( -1 ); + +/* Define what we do for each band element type. */ +#define im_thresh_loop(TYPE)\ + { TYPE *a = (TYPE *) in->data;\ + \ + for( y = 0; y < in->Ysize; y++ ) {\ + PEL *b = bu;\ + \ + for( x = 0; x < epl; x++ ) {\ + double f = (double) *a++;\ + if ( f >= threshold)\ + *b++ = (PEL)BRIGHT;\ + else\ + *b++ = (PEL)DARK;\ + }\ + \ + if( im_writeline( y, out, bu ) ) \ + return( -1 );\ + }\ + } + +/* Do the above for all image types. */ + switch( in->BandFmt ) { + im_for_all_types( im_thresh_loop ); + + default: + im_errormsg("im_thresh: Unknown input format"); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/conversion/im_tiff2vips.c b/libsrc/conversion/im_tiff2vips.c new file mode 100644 index 00000000..0a62d019 --- /dev/null +++ b/libsrc/conversion/im_tiff2vips.c @@ -0,0 +1,1490 @@ +/* TIFF parts: Copyright (c) 1988, 1990 by Sam Leffler. + * All rights reserved. + * + * This file is provided for unrestricted use provided that this + * legend is included on all tape media and as a part of the + * software program in whole or part. Users may copy, modify or + * distribute this file at will. + * ----------------------------- + * Modifications for VIPS: Kirk Martinez 1994 + * 22/11/94 JC + * - more general + * - memory leaks fixed + * 20/3/95 JC + * - TIFF error handler added + * - read errors detected correctly + * + * Modified to handle LAB in tiff format. + * It convert LAB-tiff format to IM_TYPE_LABQ in vips format. + * Copyright July-1995 Ahmed Abbood. + * + * + * 19/9/95 JC + * - now calls TIFFClose ... stupid + * 25/1/96 JC + * - typo on MINISBLACK ... + * 7/4/97 JC + * - completely redone for TIFF 6 + * - now full baseline TIFF 6 reader, and does CIELAB as well + * 11/4/97 JC + * - added partial read for tiled images + * 23/4/97 JC + * - extra subsample parameter + * - im_istiffpyramid() added + * 5/12/97 JC + * - if loading YCbCr, convert to IM_CODING_LABQ + * 1/5/98 JC + * - now reads 16-bit greyscale and RGB + * 26/10/98 JC + * - now used "rb" mode on systems that need binary open + * 12/11/98 JC + * - no sub-sampling if sub == 1 + * 26/2/99 JC + * - ooops, else missing for subsample stuff above + * 2/10/99 JC + * - tiled 16-bit greyscale read was broken + * - added mutex for TIFF*() calls + * 11/5/00 JC + * - removed TIFFmalloc/TIFFfree usage + * 23/4/01 JC + * - HAVE_TIFF turns on TIFF goodness + * 24/5/01 JC + * - im_tiff2vips_header() added + * 11/7/01 JC + * - subsample now in input filename + * - ... and it's a page number (from 0) instead + * 21/8/02 JC + * - now reads CMYK + * - hmm, dpi -> ppm conversion was wrong! + * 10/9/02 JC + * - oops, handle TIFF errors better + * 2/12/02 JC + * - reads 8-bit RGBA + * 12/12/02 JC + * - reads 16-bit LAB + * 13/2/03 JC + * - pixels/cm res read was wrong + * 17/11/03 Andrey Kiselev + * - read 32-bit float greyscale and rgb + * 5/4/04 + * - better handling of edge tiles (thanks Ruven) + * 16/4/04 + * - cleanup + * - added broken tile read mode + * 18/5/04 Andrey Kiselev + * - better no resolution diagnostic + * 26/5/04 + * - reads 16 bit RGBA + * 28/7/04 + * - arrg, 16bit RGB was broken, thanks haida + * 26/11/04 + * - add a TIFF warning handler, stops occasional libMagick exceptions + * 9/3/05 + * - load 32-bit float LAB + * 8/4/05 + * - onebit read no longer reads one byte too many on multiple of 8 wide + * images + * 22/6/05 + * - 16 bit LAB read was broken + * 9/9/05 + * - read any ICCPROFILE tag + * 8/5/06 + * - set RGB16 and GREY16 Type + * 21/5/06 + * - use external im_tile_cache() operation for great code shrinkage + * - less RAM usage too, esp. with >1 CPU + * - should be slightly faster + * - removed 'broken' read option + * 18/7/07 Andrey Kiselev + * - remove "b" option on TIFFOpen() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on debugging output. +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_TIFF + +#include + +int +im_tiff2vips( const char *tiffile, IMAGE *im ) +{ + im_error( "im_tiff2vips", _( "TIFF support disabled" ) ); + return( -1 ); +} + +int +im_istiffpyramid( const char *name ) +{ + return( 0 ); +} + +int +im_tiff2vips_header( const char *tiffile, IMAGE *im ) +{ + im_error( "im_tiff2vips", _( "TIFF support disabled" ) ); + return( -1 ); +} + +#else /*HAVE_TIFF*/ + +#include +#include +#include +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Scanline-type process function. + */ +typedef void (*scanline_process_fn)( PEL *q, PEL *p, int n, void *user ); + +/* Stuff we track during a read. + */ +typedef struct { + /* Parameters. + */ + char *filename; + IMAGE *out; + + /* From filename. + */ + int page; + + /* The TIFF we read. + */ + TIFF *tiff; + + /* Process for this image type. + */ + scanline_process_fn sfn; + void *client; + + /* Geometry. + */ + int twidth, theight; /* Tile size */ + + /* Only need one of these, since we mutex around TIFF*(). + */ + GMutex *tlock; /* Lock for TIFF*() calls */ +} ReadTiff; + +/* Reading a YCbCr image ... parameters we use for conversion. + */ +typedef struct { + /* Input and output. + */ + TIFF *tif; /* From here */ + IMAGE *im; /* To here */ + + /* RGB <-> YCbCr conversion. + */ + float LumaRed, LumaGreen, LumaBlue; + + /* RGB -> LAB conversion. + */ + void *table; +} YCbCrParams; + +/* Handle TIFF errors here. + */ +static void +thandler_error( char *module, char *fmt, va_list ap ) +{ + im_verror( module, fmt, ap ); +} + +static void +thandler_warning( char *module, char *fmt, va_list ap ) +{ + char buf[256]; + + im_vsnprintf( buf, 256, fmt, ap ); + im_warn( module, "%s", buf ); +} + +/* Test for field exists. + */ +static int +tfexists( TIFF *tif, ttag_t tag ) +{ + uint32 a, b; + + if( TIFFGetField( tif, tag, &a, &b ) ) + return( 1 ); + else + return( 0 ); +} + +/* Test a uint16 field. Field must be defined and equal to the value. + */ +static int +tfequals( TIFF *tif, ttag_t tag, uint16 val ) +{ + uint16 fld; + + if( !TIFFGetFieldDefaulted( tif, tag, &fld ) ) { + im_error( "im_tiff2vips", + _( "required field %d missing" ), tag ); + return( 0 ); + } + if( fld != val ) { + im_error( "im_tiff2vips", _( "required field %d=%d, not %d" ), + tag, fld, val ); + return( 0 ); + } + + /* All ok. + */ + return( 1 ); +} + +/* Get a uint32 field. + */ +static int +tfget32( TIFF *tif, ttag_t tag, int *out ) +{ + uint32 fld; + + if( !TIFFGetFieldDefaulted( tif, tag, &fld ) ) { + im_error( "im_tiff2vips", + _( "required field %d missing" ), tag ); + return( 0 ); + } + + /* All ok. + */ + *out = fld; + return( 1 ); +} + +/* Get a uint16 field. + */ +static int +tfget16( TIFF *tif, ttag_t tag, int *out ) +{ + uint16 fld; + + if( !TIFFGetFieldDefaulted( tif, tag, &fld ) ) { + im_error( "im_tiff2vips", + _( "required field %d missing" ), tag ); + return( 0 ); + } + + /* All ok. + */ + *out = fld; + return( 1 ); +} + +/* Per-scanline process function for IM_CODING_LABQ. + */ +static void +labpack_line( PEL *q, PEL *p, int n, void *dummy ) +{ + int x; + + for( x = 0; x < n; x++ ) { + q[0] = p[0]; + q[1] = p[1]; + q[2] = p[2]; + q[3] = 0; + + q += 4; + p += 3; + } +} + +/* Read an 8-bit LAB image. + */ +static int +parse_labpack( ReadTiff *rtiff, IMAGE *out ) +{ + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 3 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 8 ) ) + return( -1 ); + + out->Bands = 4; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_LABQ; + out->Type = IM_TYPE_LAB; + + rtiff->sfn = labpack_line; + rtiff->client = NULL; + + return( 0 ); +} + +/* Per-scanline process function for IM_CODING_LABQ. + */ +static void +labs_line( PEL *q, PEL *p, int n, void *dummy ) +{ + int x; + unsigned short *p1 = (unsigned short *) p; + short *q1 = (short *) q; + + for( x = 0; x < n; x++ ) { + q1[0] = p1[0] >> 1; + q1[1] = p1[1]; + q1[2] = p1[2]; + + q1 += 3; + p1 += 3; + } +} + +/* Read a 16-bit LAB image. + */ +static int +parse_labs( ReadTiff *rtiff, IMAGE *out ) +{ + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 3 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 16 ) ) + return( -1 ); + + out->Bands = 3; + out->Bbits = 16; + out->BandFmt = IM_BANDFMT_SHORT; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_LABS; + + rtiff->sfn = labs_line; + rtiff->client = NULL; + + return( 0 ); +} + +/* Per-scanline process function for IM_CODING_LABQ. + */ +static void +ycbcr_line( PEL *q, PEL *p, int n, void *dummy ) +{ + int x; + + for( x = 0; x < n; x++ ) { + q[0] = p[0]; + q[1] = p[1]; + q[2] = p[2]; + q[3] = 0; + + q += 4; + p += 3; + } +} + +/* Read a YCbCr image. + */ +static int +parse_ycbcr( ReadTiff *rtiff, IMAGE *out ) +{ + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 3 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 8 ) ) + return( -1 ); + + out->Bands = 4; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_LABQ; + out->Type = IM_TYPE_LAB; + + rtiff->sfn = ycbcr_line; + rtiff->client = NULL; + + return( 0 ); +} + +/* Per-scanline process function for 1 bit images. + */ +static void +onebit_line( PEL *q, PEL *p, int n, void *flg ) +{ + /* Extract PHOTOMETRIC_INTERPRETATION. + */ + int pm = *((int *) flg); + int x, i, z; + PEL bits; + + int black = (pm == PHOTOMETRIC_MINISBLACK) ? 0 : 255; + int white = black ^ -1; + + /* (sigh) how many times have I written this? + */ + for( x = 0, i = 0; i < (n >> 3); i++ ) { + bits = (PEL) p[i]; + + for( z = 0; z < 8; z++, x++ ) { + q[x] = (bits & 128) ? white : black; + bits <<= 1; + } + } + + /* Do last byte in line. + */ + if( n & 7 ) { + bits = p[i]; + for( z = 0; z < (n & 7); z++ ) { + q[x + z] = (bits & 128) ? white : black; + bits <<= 1; + } + } +} + +/* Read a 1-bit TIFF image. Pass in pixel values to use for black and white. + */ +static int +parse_onebit( ReadTiff *rtiff, int pm, IMAGE *out ) +{ + int *ppm; + + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 1 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 1 ) ) + return( -1 ); + + out->Bands = 1; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_B_W; + + /* Note pm for later. + */ + if( !(ppm = IM_ARRAY( out, 1, int )) ) + return( -1 ); + *ppm = pm; + + rtiff->sfn = onebit_line; + rtiff->client = ppm; + + return( 0 ); +} + +/* Per-scanline process function for 8-bit greyscale images. + */ +static void +greyscale8_line( PEL *q, PEL *p, int n, void *flg ) +{ + /* Extract swap mask. + */ + PEL mask = *((PEL *) flg); + int x; + + /* Read bytes, swapping sense if necessary. + */ + for( x = 0; x < n; x++ ) + q[x] = p[x] ^ mask; +} + +/* Read a 8-bit grey-scale TIFF image. + */ +static int +parse_greyscale8( ReadTiff *rtiff, int pm, IMAGE *out ) +{ + PEL *mask; + + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 1 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 8 ) ) + return( -1 ); + + /* Eor each pel with this later. + */ + if( !(mask = IM_ARRAY( out, 1, PEL )) ) + return( -1 ); + *mask = (pm == PHOTOMETRIC_MINISBLACK) ? 0 : 255; + + out->Bands = 1; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_B_W; + + rtiff->sfn = greyscale8_line; + rtiff->client = mask; + + return( 0 ); +} + +/* Per-scanline process function for 16-bit greyscale images. + */ +static void +greyscale16_line( PEL *q, PEL *p, int n, void *flg ) +{ + /* Extract swap mask. + */ + unsigned short mask = *((unsigned short *) flg); + unsigned short *p1 = (unsigned short *) p; + unsigned short *q1 = (unsigned short *) q; + int x; + + /* Read bytes, swapping sense if necessary. + */ + for( x = 0; x < n; x++ ) + q1[x] = p1[x] ^ mask; +} + +/* Read a 16-bit grey-scale TIFF image. + */ +static int +parse_greyscale16( ReadTiff *rtiff, int pm, IMAGE *out ) +{ + unsigned short *mask; + + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 1 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 16 ) ) + return( -1 ); + + /* Eor each pel with this later. + */ + if( !(mask = IM_ARRAY( out, 1, unsigned short )) ) + return( -1 ); + mask[0] = (pm == PHOTOMETRIC_MINISBLACK) ? 0 : 65535; + + out->Bands = 1; + out->Bbits = 16; + out->BandFmt = IM_BANDFMT_USHORT; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_GREY16; + + rtiff->sfn = greyscale16_line; + rtiff->client = mask; + + return( 0 ); +} + +/* Per-scanline process function for 32-bit floating point greyscale images. + */ +static void +greyscale32f_line( PEL *q, PEL *p, int n ) +{ + float *p1 = (float *) p; + float *q1 = (float *) q; + int x; + + for( x = 0; x < n; x++ ) + q1[x] = p1[x]; +} + +/* Read a 32-bit floating point greyscale TIFF image. What do we do about + * MINISWHITE/MINISBLACK (pm)? Not sure ... just ignore it. + */ +static int +parse_greyscale32f( ReadTiff *rtiff, int pm, IMAGE *out ) +{ + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 1 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 32 ) ) + return( -1 ); + + out->Bands = 1; + out->Bbits = 32; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_B_W; + + rtiff->sfn = (scanline_process_fn) greyscale32f_line; + rtiff->client = NULL; + + return( 0 ); +} + +/* Per-scanline process function for palette images. + */ +static void +palette_line( PEL *q, PEL *p, int n, void *flg ) +{ + /* Extract maps. + */ + PEL *red = ((PEL **) flg)[0]; + PEL *green = ((PEL **) flg)[1]; + PEL *blue = ((PEL **) flg)[2]; + int x; + + /* Read bytes, generating colour. + */ + for( x = 0; x < n; x++ ) { + int i = *p++; + + q[0] = red[i]; + q[1] = green[i]; + q[2] = blue[i]; + + q += 3; + } +} + +/* Read a palette-ised TIFF image. Again, we only allow 8-bits for now. + */ +static int +parse_palette( ReadTiff *rtiff, IMAGE *out ) +{ + uint16 *tred, *tgreen, *tblue; + PEL *red, *green, *blue; + PEL **maps; + int i; + + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 1 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 8 ) ) + return( -1 ); + + /* Allocate mem for VIPS colour maps. + */ + if( !(red = IM_ARRAY( out, 256, PEL )) || + !(green = IM_ARRAY( out, 256, PEL )) || + !(blue = IM_ARRAY( out, 256, PEL )) || + !(maps = IM_ARRAY( out, 3, PEL * )) ) + return( -1 ); + + /* Get maps, convert to 8-bit data. + */ + if( !TIFFGetField( rtiff->tiff, + TIFFTAG_COLORMAP, &tred, &tgreen, &tblue ) ) { + im_error( "im_tiff2vips", _( "bad colormap" ) ); + return( -1 ); + } + for( i = 0; i < 256; i++ ) { + red[i] = tred[i] >> 8; + green[i] = tgreen[i] >> 8; + blue[i] = tblue[i] >> 8; + } + maps[0] = red; + maps[1] = green; + maps[2] = blue; + + out->Bands = 3; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_sRGB; + + rtiff->sfn = palette_line; + rtiff->client = maps; + + return( 0 ); +} + +/* Per-scanline process function for 8-bit RGB/RGBA. + */ +static void +rgb8_line( PEL *q, PEL *p, int n, IMAGE *im ) +{ + int x, b; + + for( x = 0; x < n; x++ ) { + for( b = 0; b < im->Bands; b++ ) + q[b] = p[b]; + + q += im->Bands; + p += im->Bands; + } +} + +/* Read an 8-bit RGB/RGBA image. + */ +static int +parse_rgb8( ReadTiff *rtiff, IMAGE *out ) +{ + int bands; + + /* Check other TIFF fields to make sure we can read this. Can have 4 + * bands for RGBA. + */ + if( !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 8 ) || + !tfget16( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, &bands ) ) + return( -1 ); + if( bands != 3 && bands != 4 ) { + im_error( "im_tiff2vips", _( "3 or 4 bands RGB TIFF only" ) ); + return( -1 ); + } + + out->Bands = bands; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_sRGB; + + rtiff->sfn = (scanline_process_fn) rgb8_line; + rtiff->client = out; + + return( 0 ); +} + +/* Per-scanline process function for RGB/RGBA 16. + */ +static void +rgb16_line( PEL *q, PEL *p, int n, IMAGE *im ) +{ + int x, b; + unsigned short *p1 = (unsigned short *) p; + unsigned short *q1 = (unsigned short *) q; + + for( x = 0; x < n; x++ ) { + for( b = 0; b < im->Bands; b++ ) + q1[b] = p1[b]; + + q1 += im->Bands; + p1 += im->Bands; + } +} + +/* Read a 16-bit RGB/RGBA image. + */ +static int +parse_rgb16( ReadTiff *rtiff, IMAGE *out ) +{ + int bands; + + /* Check other TIFF fields to make sure we can read this. Can have 4 + * bands for RGBA. + */ + if( !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 16 ) || + !tfget16( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, &bands ) ) + return( -1 ); + if( bands != 3 && bands != 4 ) { + im_error( "im_tiff2vips", _( "3 or 4 bands RGB TIFF only" ) ); + return( -1 ); + } + + out->Bands = bands; + out->Bbits = 16; + out->BandFmt = IM_BANDFMT_USHORT; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_RGB16; + + rtiff->sfn = (scanline_process_fn) rgb16_line; + rtiff->client = out; + + return( 0 ); +} + +/* Per-scanline process function for 32f. + */ +static void +r32f_line( PEL *q, PEL *p, int n, void *dummy ) +{ + int x; + float *p1 = (float *) p; + float *q1 = (float *) q; + + for( x = 0; x < n; x++ ) { + q1[0] = p1[0]; + q1[1] = p1[1]; + q1[2] = p1[2]; + + q1 += 3; + p1 += 3; + } +} + +/* Read a 32-bit float image. RGB or LAB, with or without alpha. + */ +static int +parse_32f( ReadTiff *rtiff, int pm, IMAGE *out ) +{ + int bands; + + if( !tfget16( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, &bands ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 32 ) ) + return( -1 ); + + /* Can be 4 for images with an alpha channel. + */ + assert( bands == 3 || bands == 4 ); + + out->Bands = bands; + out->Bbits = 32; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Coding = IM_CODING_NONE; + + switch( pm ) { + case PHOTOMETRIC_CIELAB: + out->Type = IM_TYPE_LAB; + break; + + case PHOTOMETRIC_RGB: + out->Type = IM_TYPE_sRGB; + break; + + default: + assert( 0 ); + } + + rtiff->sfn = r32f_line; + rtiff->client = NULL; + + return( 0 ); +} + +/* Per-scanline process function for CMYK8. + */ +static void +cmyk8_line( PEL *q, PEL *p, int n, void *dummy ) +{ + int x; + + for( x = 0; x < n; x++ ) { + q[0] = p[0]; + q[1] = p[1]; + q[2] = p[2]; + q[3] = p[3]; + + q += 4; + p += 4; + } +} + +/* Read a CMYK image. + */ +static int +parse_cmyk( ReadTiff *rtiff, IMAGE *out ) +{ + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 4 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 8 ) || + !tfequals( rtiff->tiff, TIFFTAG_INKSET, INKSET_CMYK ) ) + return( -1 ); + + out->Bands = 4; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_CMYK; + + rtiff->sfn = cmyk8_line; + rtiff->client = NULL; + + return( 0 ); +} + +/* Read resolution from a TIFF image. + */ +static int +parse_resolution( TIFF *tiff, IMAGE *out ) +{ + float x, y; + int ru; + + if( TIFFGetFieldDefaulted( tiff, TIFFTAG_XRESOLUTION, &x ) && + TIFFGetFieldDefaulted( tiff, TIFFTAG_YRESOLUTION, &y ) && + tfget16( tiff, TIFFTAG_RESOLUTIONUNIT, &ru ) ) { + switch( ru ) { + case RESUNIT_NONE: + break; + + case RESUNIT_INCH: + /* In pixels-per-inch ... convert to mm. + */ + x /= 10.0 * 2.54; + y /= 10.0 * 2.54; + break; + + case RESUNIT_CENTIMETER: + /* In pixels-per-centimetre ... convert to mm. + */ + x /= 10.0; + y /= 10.0; + break; + + default: + im_error( "im_tiff2vips", + _( "unknown resolution unit" ) ); + return( -1 ); + } + } + else { + im_warning( "im_tiff2vips: no resolution information for " + "TIFF image \"%s\" -- defaulting to 1 pixel per mm", + TIFFFileName( tiff ) ); + x = 1.0; + y = 1.0; + } + + out->Xres = x; + out->Yres = y; + + return( 0 ); +} + +/* Look at PhotometricInterpretation and BitsPerPixel, and try to figure out + * which of the image classes this is. + */ +static int +parse_header( ReadTiff *rtiff, IMAGE *out ) +{ + int pm, bps, format; + uint32 data_length; + void *data; + + /* Ban separate planes, too annoying. + */ + if( tfexists( rtiff->tiff, TIFFTAG_PLANARCONFIG ) && + !tfequals( rtiff->tiff, + TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ) ) + return( -1 ); + + /* Always need dimensions. + */ + if( !tfget32( rtiff->tiff, TIFFTAG_IMAGEWIDTH, &out->Xsize ) || + !tfget32( rtiff->tiff, TIFFTAG_IMAGELENGTH, &out->Ysize ) || + parse_resolution( rtiff->tiff, out ) ) + return( -1 ); + + /* Try to find out which type of TIFF image it is. + */ + if( !tfget16( rtiff->tiff, TIFFTAG_PHOTOMETRIC, &pm ) || + !tfget16( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, &bps ) ) + return( -1 ); + + switch( pm ) { + case PHOTOMETRIC_CIELAB: + switch( bps ) { + case 8: + if( parse_labpack( rtiff, out ) ) + return( -1 ); + break; + + case 16: + if( parse_labs( rtiff, out ) ) + return( -1 ); + break; + + case 32: + if( !tfget16( rtiff->tiff, + TIFFTAG_SAMPLEFORMAT, &format ) ) + return( -1 ); + + if( format == SAMPLEFORMAT_IEEEFP ) { + if( parse_32f( rtiff, pm, out ) ) + return( -1 ); + } + else { + im_error( "im_tiff2vips", + _( "unsupported sample " + "format %d for lab image" ), + format ); + return( -1 ); + } + + break; + + default: + im_error( "im_tiff2vips", + _( "unsupported depth %d for LAB image" ), + bps ); + return( -1 ); + } + + break; + + case PHOTOMETRIC_YCBCR: + /* Easy decision! + */ + if( parse_ycbcr( rtiff, out ) ) + return( -1 ); + + break; + + case PHOTOMETRIC_MINISWHITE: + case PHOTOMETRIC_MINISBLACK: + switch( bps ) { + case 1: + if( parse_onebit( rtiff, pm, out ) ) + return( -1 ); + + break; + + case 8: + if( parse_greyscale8( rtiff, pm, out ) ) + return( -1 ); + + break; + + case 16: + if( parse_greyscale16( rtiff, pm, out ) ) + return( -1 ); + + break; + + case 32: + if( !tfget16( rtiff->tiff, + TIFFTAG_SAMPLEFORMAT, &format ) ) + return( -1 ); + + if( format == SAMPLEFORMAT_IEEEFP ) { + if( parse_greyscale32f( rtiff, pm, out ) ) + return( -1 ); + } + else { + im_error( "im_tiff2vips", + _( "unsupported sample format " + "%d for greyscale image" ), + format ); + return( -1 ); + } + + break; + + default: + im_error( "im_tiff2vips", _( "unsupported depth %d " + "for greyscale image" ), bps ); + return( -1 ); + } + + break; + + case PHOTOMETRIC_PALETTE: + /* Full colour pallette. + */ + if( parse_palette( rtiff, out ) ) + return( -1 ); + + break; + + case PHOTOMETRIC_RGB: + /* Plain RGB. + */ + switch( bps ) { + case 8: + if( parse_rgb8( rtiff, out ) ) + return( -1 ); + break; + + case 16: + if( parse_rgb16( rtiff, out ) ) + return( -1 ); + break; + + case 32: + if( !tfget16( rtiff->tiff, + TIFFTAG_SAMPLEFORMAT, &format ) ) + return( -1 ); + + if( format == SAMPLEFORMAT_IEEEFP ) { + if( parse_32f( rtiff, pm, out ) ) + return( -1 ); + } + else { + im_error( "im_tiff2vips", + _( "unsupported sample " + "format %d for rgb image" ), + format ); + return( -1 ); + } + + break; + + default: + im_error( "im_tiff2vips", _( "unsupported depth %d " + "for RGB image" ), bps ); + return( -1 ); + } + + break; + + case PHOTOMETRIC_SEPARATED: + if( parse_cmyk( rtiff, out ) ) + return( -1 ); + + break; + + default: + im_error( "im_tiff2vips", _( "unknown photometric " + "interpretation %d" ), pm ); + return( -1 ); + } + + /* Read any ICC profile. + */ + if( TIFFGetField( rtiff->tiff, + TIFFTAG_ICCPROFILE, &data_length, &data ) ) { + void *data_copy; + + if( !(data_copy = im_malloc( NULL, data_length )) ) + return( -1 ); + memcpy( data_copy, data, data_length ); + if( im_meta_set_blob( out, IM_META_ICC_NAME, + (im_callback_fn) im_free, data_copy, data_length ) ) { + im_free( data_copy ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Allocate a tile buffer. Have one of these for each thread so we can unpack + * to vips in parallel. + */ +static void * +seq_start( IMAGE *out, ReadTiff *rtiff ) +{ + tdata_t *buf; + + if( !(buf = im_malloc( NULL, TIFFTileSize( rtiff->tiff ) )) ) + return( NULL ); + + return( (void *) buf ); +} + +/* Loop over the output region, painting in tiles from the file. + */ +static int +fill_region( REGION *out, tdata_t *buf, ReadTiff *rtiff ) +{ + Rect *r = &out->valid; + + /* Find top left of tiles we need. + */ + int xs = (r->left / rtiff->twidth) * rtiff->twidth; + int ys = (r->top / rtiff->theight) * rtiff->theight; + + /* Sizeof a line of bytes in the TIFF tile. + */ + int tls = TIFFTileSize( rtiff->tiff ) / rtiff->theight; + + /* Sizeof a pel in the TIFF file. This won't work for formats which + * are <1 byte per pel, like onebit :-( Fortunately, it's only used + * to calculate addresses within a tile, and because we are wrapped in + * im_tile_cache(), we will never have to calculate positions within a + * tile. + */ + int tps = tls / rtiff->twidth; + + int x, y, z; + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += rtiff->theight ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += rtiff->twidth ) { + Rect tile; + Rect hit; + + /* Read that tile. + */ + g_mutex_lock( rtiff->tlock ); + if( TIFFReadTile( rtiff->tiff, buf, + x, y, 0, 0 ) < 0 ) { + g_mutex_unlock( rtiff->tlock ); + return( -1 ); + } + g_mutex_unlock( rtiff->tlock ); + + /* The tile we read. + */ + tile.left = x; + tile.top = y; + tile.width = rtiff->twidth; + tile.height = rtiff->twidth; + + /* The section that hits the region we are building. + */ + im_rect_intersectrect( &tile, r, &hit ); + + /* Unpack to VIPS format. We can do this in parallel. + * Just unpack the section of the tile we need. + */ + for( z = 0; z < hit.height; z++ ) { + PEL *p = (PEL *) buf + + (hit.left - tile.left) * tps + + (hit.top - tile.top + z) * tls; + PEL *q = (PEL *) IM_REGION_ADDR( out, + hit.left, hit.top + z ); + + rtiff->sfn( q, p, hit.width, rtiff->client ); + } + } + + return( 0 ); +} + +/* Tile-type TIFF reader core - pass in a per-tile transform. Generate into + * the im and do it all partially. + */ +static int +read_tilewise( ReadTiff *rtiff, IMAGE *out ) +{ + IMAGE *raw; + + /* Tile cache: keep enough for two complete rows of tiles. + * This lets us do (smallish) area ops, like im_conv(), while + * still only hitting each TIFF tile once. + */ + if( !(raw = im_open_local( out, "cache", "p" )) ) + return( -1 ); + + /* Get tiling geometry. + */ + if( !tfget32( rtiff->tiff, TIFFTAG_TILEWIDTH, &rtiff->twidth ) || + !tfget32( rtiff->tiff, TIFFTAG_TILELENGTH, &rtiff->theight ) ) + return( -1 ); + + /* Make sure we can write PIO-style. + */ + if( im_poutcheck( raw ) ) + return( -1 ); + + /* Parse the TIFF header and set up raw. + */ + if( parse_header( rtiff, raw ) ) + return( -1 ); + + /* Process and save as VIPS. + */ + if( im_demand_hint( raw, IM_SMALLTILE, NULL ) || + im_generate( raw, + seq_start, fill_region, im_free, rtiff, NULL ) ) + return( -1 ); + + /* Copy to out, adding a cache. Enough tiles for two complete rows. + */ + if( im_tile_cache( raw, out, + rtiff->twidth, rtiff->theight, + 2 * (1 + raw->Xsize / rtiff->twidth) ) ) + return( -1 ); + + return( 0 ); +} + +/* Scanline-type TIFF reader core - pass in a per-scanline transform. + */ +static int +read_scanlinewise( ReadTiff *rtiff, IMAGE *out ) +{ + PEL *vbuf; + tdata_t tbuf; + int y; + + if( parse_header( rtiff, out ) ) + return( -1 ); + + /* Make sure we can write WIO-style. + */ + if( im_outcheck( out ) || im_setupout( out ) ) + return( -1 ); + + /* Make VIPS output buffer. + */ + if( !(vbuf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Make TIFF line input buffer. + */ + if( !(tbuf = im_malloc( out, TIFFScanlineSize( rtiff->tiff ) )) ) + return( -1 ); + + for( y = 0; y < out->Ysize; y++ ) { + /* Read TIFF scanline. + */ + if( TIFFReadScanline( rtiff->tiff, tbuf, y, 0 ) < 0 ) { + im_error( "im_tiff2vips", _( "read error" ) ); + return( -1 ); + } + + /* Process and save as VIPS. + */ + rtiff->sfn( vbuf, tbuf, out->Xsize, rtiff->client ); + if( im_writeline( y, out, vbuf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Free a ReadTiff. + */ +static int +readtiff_destroy( ReadTiff *rtiff ) +{ + IM_FREEF( TIFFClose, rtiff->tiff ); + IM_FREEF( g_mutex_free, rtiff->tlock ); + + return( 0 ); +} + +/* Make a ReadTiff. + */ +static ReadTiff * +readtiff_new( const char *filename, IMAGE *out ) +{ + ReadTiff *rtiff; + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char *p, *q; + + if( !(rtiff = IM_NEW( out, ReadTiff )) ) + return( NULL ); + rtiff->filename = NULL; + rtiff->out = out; + im_filename_split( filename, name, mode ); + rtiff->filename = im_strdup( out, name ); + rtiff->page = 0; + rtiff->tiff = NULL; + rtiff->sfn = NULL; + rtiff->client = NULL; + rtiff->twidth = 0; + rtiff->theight = 0; + rtiff->tlock = g_mutex_new(); + + if( im_add_close_callback( out, + (im_callback_fn) readtiff_destroy, rtiff, NULL ) ) { + readtiff_destroy( rtiff ); + return( NULL ); + } + + /* Parse out params. + */ + p = &mode[0]; + if( (q = im_getnextoption( &p )) ) { + rtiff->page = atoi( q ); + + if( rtiff->page < 0 || rtiff->page > 1000 ) { + im_error( "im_tiff2vips", _( "bad page number %d" ), + rtiff->page ); + return( NULL ); + } + } + + return( rtiff ); +} + +/* Pull out the nth directory from a TIFF file. + */ +static TIFF * +get_directory( const char *filename, int page ) +{ + TIFF *tif; + int i; + + /* No need to use "b" and it means something different anyway. + */ + if( !(tif = TIFFOpen( filename, "r" )) ) { + im_error( "im_tiff2vips", + _( "unable to open \"%s\" for input" ), + filename ); + return( NULL ); + } + + for( i = 0; i < page; i++ ) + if( !TIFFReadDirectory( tif ) ) { + /* Run out of directories. + */ + TIFFClose( tif ); + return( NULL ); + } + + return( tif ); +} + +int +im_istiffpyramid( const char *name ) +{ + TIFF *tif; + + TIFFSetErrorHandler( (TIFFErrorHandler) thandler_error ); + TIFFSetWarningHandler( (TIFFErrorHandler) thandler_warning ); + + if( (tif = get_directory( name, 2 )) ) { + /* We can see page 2 ... assume it is. + */ + TIFFClose( tif ); + return( 1 ); + } + + return( 0 ); +} + +int +im_tiff2vips( const char *filename, IMAGE *out ) +{ + ReadTiff *rtiff; + +#ifdef DEBUG + printf( "im_tiff2vips: libtiff version is \"%s\"\n", TIFFGetVersion() ); +#endif /*DEBUG*/ + + TIFFSetErrorHandler( (TIFFErrorHandler) thandler_error ); + TIFFSetWarningHandler( (TIFFErrorHandler) thandler_warning ); + + if( !(rtiff = readtiff_new( filename, out )) ) + return( -1 ); + + if( !(rtiff->tiff = get_directory( rtiff->filename, rtiff->page )) ) { + im_error( "im_tiff2vips", _( "TIFF file does not " + "contain page %d" ), rtiff->page ); + return( -1 ); + } + + if( TIFFIsTiled( rtiff->tiff ) ) { + if( read_tilewise( rtiff, out ) ) + return( -1 ); + } + else { + if( read_scanlinewise( rtiff, out ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Just parse the header. + */ +int +im_tiff2vips_header( const char *filename, IMAGE *out ) +{ + ReadTiff *rtiff; + + TIFFSetErrorHandler( (TIFFErrorHandler) thandler_error ); + TIFFSetWarningHandler( (TIFFErrorHandler) thandler_warning ); + + if( !(rtiff = readtiff_new( filename, out )) ) + return( -1 ); + + if( !(rtiff->tiff = get_directory( rtiff->filename, rtiff->page )) ) { + im_error( "im_tiff2vips", + _( "TIFF file does not contain page %d" ), + rtiff->page ); + return( -1 ); + } + + if( parse_header( rtiff, out ) ) + return( -1 ); + + return( 0 ); +} + +#endif /*HAVE_TIFF*/ diff --git a/libsrc/conversion/im_tile_cache.c b/libsrc/conversion/im_tile_cache.c new file mode 100644 index 00000000..f7f30b9d --- /dev/null +++ b/libsrc/conversion/im_tile_cache.c @@ -0,0 +1,390 @@ +/* Tile tile cache from tiff2vips ... broken out so it can be shared with + * openexr read. + * + * This isn't the same as im_cache(): we don't sub-divide, and we + * single-thread our callee. + * + * 23/8/06 + * - take ownership of reused tiles in case the cache is being shared + * 13/2/07 + * - relase ownership after fillng with pixels in case we read across + * threads + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on debugging output. +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Lower and upper bounds for tile cache size. Choose an exact number based on + * tile size. + */ +#define IM_MAX_TILE_CACHE (250) +#define IM_MIN_TILE_CACHE (5) + +/* A tile in our cache. + */ +typedef struct { + struct _Read *read; + + REGION *region; /* REGION with private mem for data */ + int time; /* Time of last use for flush */ + int x; /* xy pos in VIPS image cods */ + int y; +} Tile; + +/* Stuff we track during a read. + */ +typedef struct _Read { + /* Parameters. + */ + IMAGE *in; + IMAGE *out; + int tile_width; + int tile_height; + int max_tiles; + + /* Cache. + */ + int time; /* Update ticks for LRU here */ + int ntiles; /* Current cache size */ + GMutex *lock; /* Lock everything here */ + GSList *cache; /* List of tiles */ +} Read; + +static void +tile_destroy( Tile *tile ) +{ + Read *read = tile->read; + + read->cache = g_slist_remove( read->cache, tile ); + read->ntiles -= 1; + assert( read->ntiles >= 0 ); + tile->read = NULL; + + IM_FREEF( im_region_free, tile->region ); + + im_free( tile ); +} + +static void +read_destroy( Read *read ) +{ + IM_FREEF( g_mutex_free, read->lock ); + + while( read->cache ) { + Tile *tile = (Tile *) read->cache->data; + + tile_destroy( tile ); + } + + im_free( read ); +} + +static Read * +read_new( IMAGE *in, IMAGE *out, + int tile_width, int tile_height, int max_tiles ) +{ + Read *read; + + if( !(read = IM_NEW( NULL, Read )) ) + return( NULL ); + read->in = in; + read->out = out; + read->tile_width = tile_width; + read->tile_height = tile_height; + read->max_tiles = max_tiles; + read->time = 0; + read->ntiles = 0; + read->lock = g_mutex_new(); + read->cache = NULL; + + if( im_add_close_callback( out, + (im_callback_fn) read_destroy, read, NULL ) ) { + read_destroy( read ); + return( NULL ); + } + + return( read ); +} + +static Tile * +tile_new( Read *read ) +{ + Tile *tile; + + if( !(tile = IM_NEW( NULL, Tile )) ) + return( NULL ); + + tile->read = read; + tile->region = NULL; + tile->time = read->time; + tile->x = -1; + tile->y = -1; + read->cache = g_slist_prepend( read->cache, tile ); + assert( read->ntiles >= 0 ); + read->ntiles += 1; + + if( !(tile->region = im_region_create( read->in )) ) { + tile_destroy( tile ); + return( NULL ); + } + + return( tile ); +} + +/* Do we have a tile in the cache? + */ +static Tile * +tile_search( Read *read, int x, int y ) +{ + GSList *p; + + for( p = read->cache; p; p = p->next ) { + Tile *tile = (Tile *) p->data; + + if( tile->x == x && tile->y == y ) + return( tile ); + } + + return( NULL ); +} + +static void +tile_touch( Tile *tile ) +{ + assert( tile->read->ntiles >= 0 ); + + tile->time = tile->read->time++; +} + +/* Fill a tile with pixels. + */ +static int +tile_fill( Tile *tile, int x, int y ) +{ + Rect area; + + tile->x = x; + tile->y = y; + +#ifdef DEBUG + printf( "im_tile_cache: filling tile %d x %d\n", tile->x, tile->y ); +#endif /*DEBUG*/ + + area.left = x; + area.top = y; + area.width = tile->read->tile_width; + area.height = tile->read->tile_height; + if( im_prepare( tile->region, &area ) ) + return( -1 ); + + /* Make sure these pixels aren't part of this thread's buffer cache + * ... they may be read out by another thread. + */ + im__region_no_ownership( tile->region ); + + tile_touch( tile ); + + return( 0 ); +} + +/* Find existing tile, make a new tile, or if we have a full set of tiles, + * reuse LRU. + */ +static Tile * +tile_find( Read *read, int x, int y ) +{ + Tile *tile; + int oldest; + GSList *p; + + /* In cache already? + */ + if( (tile = tile_search( read, x, y )) ) { + tile_touch( tile ); + + return( tile ); + } + + /* Cache not full? + */ + if( read->max_tiles == -1 || + read->ntiles < read->max_tiles ) { + if( !(tile = tile_new( read )) || + tile_fill( tile, x, y ) ) + return( NULL ); + + return( tile ); + } + + /* Reuse an old one. + */ + oldest = read->time; + tile = NULL; + for( p = read->cache; p; p = p->next ) { + Tile *t = (Tile *) p->data; + + if( t->time < oldest ) { + oldest = t->time; + tile = t; + } + } + + assert( tile ); + + /* The tile may have been created by another thread if we are sharing + * the tile cache between several readers. Take ownership of the tile + * to stop assert() failures in im_prepare(). This is safe, since we + * are in a mutex. + */ + im__region_take_ownership( tile->region ); + +#ifdef DEBUG + printf( "im_tile_cache: reusing tile %d x %d\n", tile->x, tile->y ); +#endif /*DEBUG*/ + + if( tile_fill( tile, x, y ) ) + return( NULL ); + + return( tile ); +} + +/* Copy rect from from to to. + */ +static void +copy_region( REGION *from, REGION *to, Rect *area ) +{ + int y; + + /* Area should be inside both from and to. + */ + assert( im_rect_includesrect( &from->valid, area ) ); + assert( im_rect_includesrect( &to->valid, area ) ); + + /* Loop down common area, copying. + */ + for( y = area->top; y < IM_RECT_BOTTOM( area ); y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( from, area->left, y ); + PEL *q = (PEL *) IM_REGION_ADDR( to, area->left, y ); + + memcpy( q, p, IM_IMAGE_SIZEOF_PEL( from->im ) * area->width ); + } +} + +/* Loop over the output region, filling with data from cache. + */ +static int +fill_region( REGION *out, void *seq, Read *read ) +{ + const int tw = read->tile_width; + const int th = read->tile_height; + Rect *r = &out->valid; + + /* Find top left of tiles we need. + */ + int xs = (r->left / tw) * tw; + int ys = (r->top / th) * th; + + int x, y; + + g_mutex_lock( read->lock ); + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += th ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += tw ) { + Tile *tile; + Rect tarea; + Rect hit; + + if( !(tile = tile_find( read, x, y )) ) { + g_mutex_unlock( read->lock ); + return( -1 ); + } + + /* The area of the tile. + */ + tarea.left = x; + tarea.top = y; + tarea.width = tw; + tarea.height = th; + + /* The part of the tile that we need. + */ + im_rect_intersectrect( &tarea, r, &hit ); + + copy_region( tile->region, out, &hit ); + } + + g_mutex_unlock( read->lock ); + + return( 0 ); +} + +int +im_tile_cache( IMAGE *in, IMAGE *out, + int tile_width, int tile_height, int max_tiles ) +{ + Read *read; + + if( tile_width <= 0 || tile_height <= 0 || max_tiles < -1 ) { + im_error( "im_tile_cache", _( "bad parameters" ) ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + + if( !(read = read_new( in, out, + tile_width, tile_height, max_tiles )) ) + return( -1 ); + if( im_generate( out, + NULL, fill_region, NULL, read, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_vips2csv.c b/libsrc/conversion/im_vips2csv.c new file mode 100644 index 00000000..6d19f9dd --- /dev/null +++ b/libsrc/conversion/im_vips2csv.c @@ -0,0 +1,146 @@ +/* Write a csv file. + * + * 9/6/06 + * - hacked from im_debugim + * 23/10/06 + * - allow separator to be specified (default "\t", ) + * 17/11/06 + * - oops, was broken + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define PRINT_INT( TYPE ) fprintf( fp, "%d", *((TYPE*)p) ); +#define PRINT_FLOAT( TYPE ) fprintf( fp, "%g", *((TYPE*)p) ); +#define PRINT_COMPLEX( TYPE ) fprintf( fp, "(%g, %g)", \ + ((TYPE*)p)[0], ((TYPE*)p)[1] ); + +static int +vips2csv( IMAGE *in, FILE *fp, const char *sep ) +{ + int w = IM_IMAGE_N_ELEMENTS( in ); + int es = IM_IMAGE_SIZEOF_ELEMENT( in ); + + int x, y; + PEL *p; + + p = (PEL *) in->data; + for( y = 0; y < in->Ysize; y++ ) { + for( x = 0; x < w; x++ ) { + if( x > 0 ) + fprintf( fp, "%s", sep ); + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + PRINT_INT( unsigned char ); break; + case IM_BANDFMT_CHAR: + PRINT_INT( char ); break; + case IM_BANDFMT_USHORT: + PRINT_INT( unsigned short ); break; + case IM_BANDFMT_SHORT: + PRINT_INT( short ); break; + case IM_BANDFMT_UINT: + PRINT_INT( unsigned int ); break; + case IM_BANDFMT_INT: + PRINT_INT( int ); break; + case IM_BANDFMT_FLOAT: + PRINT_FLOAT( float ); break; + case IM_BANDFMT_DOUBLE: + PRINT_FLOAT( double ); break; + case IM_BANDFMT_COMPLEX: + PRINT_COMPLEX( float ); break; + case IM_BANDFMT_DPCOMPLEX: + PRINT_COMPLEX( double ); break; + + default: + assert( 0 ); + } + + p += es; + } + + fprintf( fp, "\n" ); + } + + return( 0 ); +} + +int +im_vips2csv( IMAGE *in, const char *filename ) +{ + char *separator = "\t"; + + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + FILE *fp; + char *p, *q, *r; + + /* Parse mode string. + */ + im_filename_split( filename, name, mode ); + p = &mode[0]; + while( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "sep", q ) && (r = im_getsuboption( q )) ) + separator = r; + } + + if( im_incheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_vips2csv", _( "input must be uncoded" ) ); + return( -1 ); + } + + if( !(fp = fopen( name, "w" )) ) { + im_error( "im_cvips2csv", _( "unable to open \"%s\"" ), + name ); + return( -1 ); + } + + if( vips2csv( in, fp, separator ) ) { + fclose( fp ); + return( -1 ); + } + + fclose( fp ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_vips2jpeg.c b/libsrc/conversion/im_vips2jpeg.c new file mode 100644 index 00000000..69f83d2c --- /dev/null +++ b/libsrc/conversion/im_vips2jpeg.c @@ -0,0 +1,867 @@ +/* Convert 1 or 3-band 8-bit VIPS images to/from JPEG. + * + * 28/11/03 JC + * - better no-overshoot on tile loop + * 12/11/04 + * - better demand size choice for eval + * 30/6/05 JC + * - update im_error()/im_warn() + * - now loads and saves exif data + * 30/7/05 + * - now loads ICC profiles + * - now saves ICC profiles from the VIPS header + * 24/8/05 + * - jpeg load sets vips xres/yres from exif, if possible + * - jpeg save sets exif xres/yres from vips, if possible + * 29/8/05 + * - cut from old vips_jpeg.c + * 20/4/06 + * - auto convert to sRGB/mono for save + * 13/10/06 + * - add +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_JPEG + +#include + +int +im_vips2jpeg( IMAGE *in, const char *filename ) +{ + im_error( "im_vips2jpeg", _( "JPEG support disabled" ) ); + + return( -1 ); +} + +int +im_vips2bufjpeg( IMAGE *in, IMAGE *out, int qfac, char **obuf, int *olen ) +{ + im_error( "im_vips2bufjpeg", _( "JPEG support disabled" ) ); + + return( -1 ); +} + +int +im_vips2mimejpeg( IMAGE *in, int qfac ) +{ + im_error( "im_vips2mimejpeg", _( "JPEG support disabled" ) ); + + return( -1 ); +} + +#else /*HAVE_JPEG*/ + +#include +#include +#include +#include +#include + +#ifdef HAVE_EXIF +#ifdef UNTAGGED_EXIF +#include +#include +#include +#include +#else /*!UNTAGGED_EXIF*/ +#include +#include +#include +#include +#endif /*UNTAGGED_EXIF*/ +#endif /*HAVE_EXIF*/ + +#include +#include +#include + +/* jpeglib includes jconfig.h, which can define HAVE_STDLIB_H ... which we + * also define. Make sure it's turned off. + */ +#ifdef HAVE_STDLIB_H +#undef HAVE_STDLIB_H +#endif /*HAVE_STDLIB_H*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define a new error handler for when we bomb out. + */ +typedef struct { + /* Public fields. + */ + struct jpeg_error_mgr pub; + + /* Private stuff for us. + */ + jmp_buf jmp; /* longjmp() here to get back to VIPS */ + FILE *fp; /* fclose() if non-NULL */ +} ErrorManager; + +/* New output message method - send to VIPS. + */ +METHODDEF(void) +new_output_message( j_common_ptr cinfo ) +{ + char buffer[JMSG_LENGTH_MAX]; + + (*cinfo->err->format_message)( cinfo, buffer ); + im_error( "vips_jpeg", _( "%s" ), buffer ); + +#ifdef DEBUG + printf( "vips_jpeg.c: new_output_message: \"%s\"\n", buffer ); +#endif /*DEBUG*/ +} + +/* New error_exit handler. + */ +METHODDEF(void) +new_error_exit( j_common_ptr cinfo ) +{ + ErrorManager *eman = (ErrorManager *) cinfo->err; + +#ifdef DEBUG + printf( "vips_jpeg.c: new_error_exit\n" ); +#endif /*DEBUG*/ + + /* Close the fp if necessary. + */ + if( eman->fp ) { + (void) fclose( eman->fp ); + eman->fp = NULL; + } + + /* Send the error message to VIPS. This method is overridden above. + */ + (*cinfo->err->output_message)( cinfo ); + + /* Jump back. + */ + longjmp( eman->jmp, 1 ); +} + +/* What we track during a JPEG write. + */ +typedef struct { + IMAGE *in; + struct jpeg_compress_struct cinfo; + ErrorManager eman; + REGION *reg; + im_threadgroup_t *tg; + JSAMPROW *row_pointer; + char *profile_bytes; + unsigned int profile_length; +} Write; + +static void +write_destroy( Write *write ) +{ + jpeg_destroy_compress( &write->cinfo ); + IM_FREEF( im_threadgroup_free, write->tg ); + IM_FREEF( im_region_free, write->reg ); + IM_FREEF( im_close, write->in ); + IM_FREEF( fclose, write->eman.fp ); + IM_FREE( write->row_pointer ); + IM_FREE( write->profile_bytes ); + im_free( write ); +} + +static Write * +write_new( IMAGE *in ) +{ + Write *write; + + if( !(write = IM_NEW( NULL, Write )) ) + return( NULL ); + memset( write, 0, sizeof( Write ) ); + + if( !(write->in = im__convert_saveable( in, FALSE )) ) { + im_error( "im_vips2jpeg", + _( "unable to convert to RGB for save" ) ); + write_destroy( write ); + return( NULL ); + } + + write->reg = im_region_create( write->in ); + write->tg = im_threadgroup_create( write->in ); + + write->row_pointer = IM_ARRAY( NULL, write->tg->nlines, JSAMPROW ); + + write->cinfo.err = jpeg_std_error( &write->eman.pub ); + write->eman.pub.error_exit = new_error_exit; + write->eman.pub.output_message = new_output_message; + write->eman.fp = NULL; + write->profile_bytes = NULL; + write->profile_length = 0; + + if( !write->reg || !write->tg || !write->row_pointer ) { + write_destroy( write ); + return( NULL ); + } + + return( write ); +} + +#ifdef HAVE_EXIF +static void +write_rational( ExifEntry *entry, ExifByteOrder bo, void *data ) +{ + ExifRational *v = (ExifRational *) data; + + exif_set_rational( entry->data, bo, *v ); +} + +static void +write_short( ExifEntry *entry, ExifByteOrder bo, void *data ) +{ + ExifShort *v = (ExifShort *) data; + + exif_set_short( entry->data, bo, *v ); +} + +typedef void (*write_fn)( ExifEntry *, ExifByteOrder, void * ); + +static int +write_tag( ExifData *ed, ExifTag tag, ExifFormat f, write_fn fn, void *data ) +{ + ExifByteOrder bo; + int found; + int i; + + bo = exif_data_get_byte_order( ed ); + + /* Need to set the tag in all sections which have it :-( + */ + found = 0; + for( i = 0; i < EXIF_IFD_COUNT; i++ ) { + ExifEntry *entry; + + if( (entry = exif_content_get_entry( ed->ifd[i], tag )) && + entry->format == f && + entry->components == 1 ) { + fn( entry, bo, data ); + found = 1; + } + } + + if( !found ) { + /* There was no tag we could update ... make one in ifd[0]. + */ + ExifEntry *entry; + + entry = exif_entry_new(); + exif_content_add_entry( ed->ifd[0], entry ); + exif_entry_initialize( entry, tag ); + fn( entry, bo, data ); + } + + return( 0 ); +} + +static int +set_exif_resolution( ExifData *ed, IMAGE *im ) +{ + double xres, yres; + ExifRational xres_rational, yres_rational; + ExifShort unit; + + /* Always save as inches - more progs support it for read. + */ + xres = im->Xres * 25.4; + yres = im->Yres * 25.4; + unit = 2; + + /* Wow, how dumb, fix this. + */ + xres_rational.numerator = xres * 100000; + xres_rational.denominator = 100000; + yres_rational.numerator = yres * 100000; + yres_rational.denominator = 100000; + + if( write_tag( ed, EXIF_TAG_X_RESOLUTION, EXIF_FORMAT_RATIONAL, + write_rational, &xres_rational ) || + write_tag( ed, EXIF_TAG_Y_RESOLUTION, EXIF_FORMAT_RATIONAL, + write_rational, &yres_rational ) || + write_tag( ed, EXIF_TAG_RESOLUTION_UNIT, EXIF_FORMAT_SHORT, + write_short, &unit ) ) { + im_error( "im_jpeg2vips", + _( "error setting JPEG resolution" ) ); + return( -1 ); + } + + return( 0 ); +} +#endif /*HAVE_EXIF*/ + +static int +write_exif( Write *write ) +{ + unsigned char *data; + size_t data_length; + unsigned int idl; +#ifdef HAVE_EXIF + ExifData *ed; + + /* Either parse from the embedded EXIF, or if there's none, make + * some fresh EXIF we can write the resolution to. + */ + if( im_header_get_type( write->in, IM_META_EXIF_NAME ) ) { + if( im_meta_get_blob( write->in, IM_META_EXIF_NAME, + (void *) &data, &data_length ) ) + return( -1 ); + + if( !(ed = exif_data_new_from_data( data, data_length )) ) + return( -1 ); + } + else + ed = exif_data_new(); + + /* Set EXIF resolution from VIPS. + */ + if( set_exif_resolution( ed, write->in ) ) { + exif_data_free( ed ); + return( -1 ); + } + + /* Reserialise and write. exif_data_save_data() returns an int for some + * reason. + */ + exif_data_save_data( ed, &data, &idl ); + if( !idl ) { + im_error( "im_jpeg2vips", _( "error saving EXIF" ) ); + exif_data_free( ed ); + return( -1 ); + } + data_length = idl; + +#ifdef DEBUG + printf( "im_vips2jpeg: attaching %d bytes of EXIF\n", data_length ); +#endif /*DEBUG*/ + + exif_data_free( ed ); + jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, data, data_length ); + free( data ); +#else /*!HAVE_EXIF*/ + /* No libexif ... just copy the embedded EXIF over. + */ + if( im_header_get_type( write->in, IM_META_EXIF_NAME ) ) { + if( im_meta_get_blob( write->in, IM_META_EXIF_NAME, + (void *) &data, &data_length ) ) + return( -1 ); + +#ifdef DEBUG + printf( "im_vips2jpeg: attaching %d bytes of EXIF\n", + data_length ); +#endif /*DEBUG*/ + + jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, + data, data_length ); + } +#endif /*!HAVE_EXIF*/ + + return( 0 ); +} + +/* ICC writer from lcms, slight tweaks. + */ + +#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */ +#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */ +#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */ +#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN) + +/* + * This routine writes the given ICC profile data into a JPEG file. + * It *must* be called AFTER calling jpeg_start_compress() and BEFORE + * the first call to jpeg_write_scanlines(). + * (This ordering ensures that the APP2 marker(s) will appear after the + * SOI and JFIF or Adobe markers, but before all else.) + */ + +static void +write_profile_data (j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len) +{ + unsigned int num_markers; /* total number of markers we'll write */ + int cur_marker = 1; /* per spec, counting starts at 1 */ + unsigned int length; /* number of bytes to write in this marker */ + + /* rounding up will fail for length == 0 */ + assert( icc_data_len > 0 ); + + /* Calculate the number of markers we'll need, rounding up of course */ + num_markers = (icc_data_len + MAX_DATA_BYTES_IN_MARKER - 1) / + MAX_DATA_BYTES_IN_MARKER; + + while (icc_data_len > 0) { + /* length of profile to put in this marker */ + length = icc_data_len; + if (length > MAX_DATA_BYTES_IN_MARKER) + length = MAX_DATA_BYTES_IN_MARKER; + icc_data_len -= length; + + /* Write the JPEG marker header (APP2 code and marker length) */ + jpeg_write_m_header(cinfo, ICC_MARKER, + (unsigned int) (length + ICC_OVERHEAD_LEN)); + + /* Write the marker identifying string "ICC_PROFILE" (null-terminated). + * We code it in this less-than-transparent way so that the code works + * even if the local character set is not ASCII. + */ + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x5F); + jpeg_write_m_byte(cinfo, 0x50); + jpeg_write_m_byte(cinfo, 0x52); + jpeg_write_m_byte(cinfo, 0x4F); + jpeg_write_m_byte(cinfo, 0x46); + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x4C); + jpeg_write_m_byte(cinfo, 0x45); + jpeg_write_m_byte(cinfo, 0x0); + + /* Add the sequencing info */ + jpeg_write_m_byte(cinfo, cur_marker); + jpeg_write_m_byte(cinfo, (int) num_markers); + + /* Add the profile data */ + while (length--) { + jpeg_write_m_byte(cinfo, *icc_data_ptr); + icc_data_ptr++; + } + cur_marker++; + } +} + +/* Write an ICC Profile from a file into the JPEG stream. + */ +static int +write_profile_file( Write *write, const char *profile ) +{ + if( !(write->profile_bytes = + im__file_read_name( profile, &write->profile_length )) ) + return( -1 ); + write_profile_data( &write->cinfo, + (JOCTET *) write->profile_bytes, write->profile_length ); + +#ifdef DEBUG + printf( "im_vips2jpeg: attached profile \"%s\"\n", profile ); +#endif /*DEBUG*/ + + return( 0 ); +} + +static int +write_profile_meta( Write *write ) +{ + void *data; + size_t data_length; + + if( im_meta_get_blob( write->in, IM_META_ICC_NAME, + &data, &data_length ) ) + return( -1 ); + + write_profile_data( &write->cinfo, data, data_length ); + +#ifdef DEBUG + printf( "im_vips2jpeg: attached %d byte profile from VIPS header\n", + data_length ); +#endif /*DEBUG*/ + + return( 0 ); +} + +/* Write a VIPS image to a JPEG compress struct. + */ +static int +write_vips( Write *write, int qfac, const char *profile ) +{ + IMAGE *in = write->in; + + Rect area; + int y, i; + + /* Should have been converted for save. + */ + assert( in->BandFmt == IM_BANDFMT_UCHAR ); + assert( in->Coding == IM_CODING_NONE ); + assert( in->Bands == 1 || in->Bands == 3 ); + + /* Check input image. + */ + if( im_pincheck( in ) ) + return( -1 ); + if( qfac < 0 || qfac > 100 ) { + im_error( "im_vips2jpeg", _( "qfac should be in 0-100" ) ); + return( -1 ); + } + + /* Set compression parameters. + */ + write->cinfo.image_width = in->Xsize; + write->cinfo.image_height = in->Ysize; + if( in->Bands == 3 ) { + write->cinfo.input_components = 3; + write->cinfo.in_color_space = JCS_RGB; + } + else if( in->Bands == 1 ) { + write->cinfo.input_components = 1; + write->cinfo.in_color_space = JCS_GRAYSCALE; + } + + /* Rest to default. + */ + jpeg_set_defaults( &write->cinfo ); + jpeg_set_quality( &write->cinfo, qfac, TRUE ); + + /* Build compress tables. + */ + jpeg_start_compress( &write->cinfo, TRUE ); + + /* Write any APP markers we need. + */ + if( write_exif( write ) ) + return( -1 ); + + /* A profile supplied as an argument overrides an embedded profile. + */ + if( profile && + write_profile_file( write, profile ) ) + return( -1 ); + if( !profile && + im_header_get_type( in, IM_META_ICC_NAME ) && + write_profile_meta( write ) ) + return( -1 ); + + /* Write data. + */ + for( y = 0; y < in->Ysize; y += write->tg->nlines ) { + area.left = 0; + area.top = y; + area.width = in->Xsize; + area.height = IM_MIN( write->tg->nlines, in->Ysize - y ); + + if( im_prepare_thread( write->tg, write->reg, &area ) ) + return( -1 ); + + for( i = 0; i < area.height; i++ ) + write->row_pointer[i] = (JSAMPROW) + IM_REGION_ADDR( write->reg, 0, y + i ); + + jpeg_write_scanlines( &write->cinfo, + write->row_pointer, area.height ); + } + + jpeg_finish_compress( &write->cinfo ); + + return( 0 ); +} + +/* Write a VIPS image to a file as JPEG. + */ +int +im_vips2jpeg( IMAGE *in, const char *filename ) +{ + Write *write; + int qfac = 75; + char *profile = NULL; + + char *p, *q; + + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char buf[FILENAME_MAX]; + + /* Parse mode from filename. + */ + im_filename_split( filename, name, mode ); + strcpy( buf, mode ); + p = &buf[0]; + if( (q = im_getnextoption( &p )) ) { + if( strcmp( q, "" ) != 0 ) + qfac = atoi( mode ); + } + if( (q = im_getnextoption( &p )) ) { + if( strcmp( q, "" ) != 0 ) + profile = q; + } + if( (q = im_getnextoption( &p )) ) { + im_error( "im_vips2jpeg", + _( "unknown extra options \"%s\"" ), q ); + return( -1 ); + } + + if( !(write = write_new( in )) ) + return( -1 ); + + if( setjmp( write->eman.jmp ) ) { + /* Here for longjmp() from new_error_exit(). + */ + write_destroy( write ); + + return( -1 ); + } + + /* Can't do this in write_new(), has to be after we've made the + * setjmp(). + */ + jpeg_create_compress( &write->cinfo ); + + /* Make output. + */ +#ifdef BINARY_OPEN + if( !(write->eman.fp = fopen( name, "wb" )) ) { +#else /*BINARY_OPEN*/ + if( !(write->eman.fp = fopen( name, "w" )) ) { +#endif /*BINARY_OPEN*/ + write_destroy( write ); + im_error( "im_vips2jpeg", + _( "unable to open \"%s\"" ), name ); + + return( -1 ); + } + jpeg_stdio_dest( &write->cinfo, write->eman.fp ); + + /* Convert! + */ + if( write_vips( write, qfac, profile ) ) { + write_destroy( write ); + return( -1 ); + } + write_destroy( write ); + + return( 0 ); +} + +/* Just like the above, but we write to a memory buffer. + * + * A memory buffer for the compressed image. + */ +typedef struct { + /* Public jpeg fields. + */ + struct jpeg_destination_mgr pub; + + /* Private stuff during write. + */ + JOCTET *data; /* Allocated area */ + int used; /* Number of bytes written so far */ + int size; /* Max size */ + + /* Copy the compressed area here. + */ + IMAGE *out; /* Allocate relative to this */ + char **obuf; /* Allocated buffer, and size */ + int *olen; +} OutputBuffer; + +/* Init dest method. + */ +METHODDEF(void) +init_destination( j_compress_ptr cinfo ) +{ + OutputBuffer *buf = (OutputBuffer *) cinfo->dest; + int mx = cinfo->image_width * cinfo->image_height * + cinfo->input_components * sizeof( JOCTET ); + + /* Allocate relative to the image we are writing .. freed when we junk + * this output. + */ + buf->data = (JOCTET *) (*cinfo->mem->alloc_large) + ( (j_common_ptr) cinfo, JPOOL_IMAGE, mx ); + buf->used = 0; + buf->size = mx; + + /* Set buf pointers for library. + */ + buf->pub.next_output_byte = buf->data; + buf->pub.free_in_buffer = mx; +} + +/* Buffer full method ... should never get this. + */ +METHODDEF(boolean) +empty_output_buffer( j_compress_ptr cinfo ) +{ + /* Not really a file write error, but why not. Should never happen. + */ + ERREXIT( cinfo, JERR_FILE_WRITE ); + + return( 0 ); +} + +/* Cleanup. Write entire buffer as a MIME type. + */ +METHODDEF(void) +term_destination( j_compress_ptr cinfo ) +{ + OutputBuffer *buf = (OutputBuffer *) cinfo->dest; + int len = buf->size - buf->pub.free_in_buffer; + void *obuf; + + /* Allocate and copy to the VIPS output area. + */ + if( !(obuf = im_malloc( buf->out, len )) ) + ERREXIT( cinfo, JERR_FILE_WRITE ); + memcpy( obuf, buf->data, len ); + *(buf->obuf) = obuf; + *(buf->olen) = len; +} + +/* Set dest to one of our objects. + */ +static void +buf_dest( j_compress_ptr cinfo, IMAGE *out, char **obuf, int *olen ) +{ + OutputBuffer *buf; + + /* The destination object is made permanent so that multiple JPEG + * images can be written to the same file without re-executing + * jpeg_stdio_dest. This makes it dangerous to use this manager and + * a different destination manager serially with the same JPEG object, + * because their private object sizes may be different. + * + * Caveat programmer. + */ + if( !cinfo->dest ) { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small) + ( (j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof( OutputBuffer ) ); + } + + buf = (OutputBuffer *) cinfo->dest; + buf->pub.init_destination = init_destination; + buf->pub.empty_output_buffer = empty_output_buffer; + buf->pub.term_destination = term_destination; + + /* Save output parameters. + */ + buf->out = out; + buf->obuf = obuf; + buf->olen = olen; +} + +/* As above, but save to a buffer. The buffer is allocated relative to out. + * On success, buf is set to the output buffer and len to the size of the + * compressed image. + */ +int +im_vips2bufjpeg( IMAGE *in, IMAGE *out, int qfac, char **obuf, int *olen ) +{ + Write *write; + + if( !(write = write_new( in )) ) + return( -1 ); + + /* Clear output parameters. + */ + *obuf = NULL; + *olen = 0; + + /* Make jpeg compression object. + */ + if( setjmp( write->eman.jmp ) ) { + /* Here for longjmp() from new_error_exit(). + */ + write_destroy( write ); + + return( -1 ); + } + jpeg_create_compress( &write->cinfo ); + + /* Attach output. + */ + buf_dest( &write->cinfo, out, obuf, olen ); + + /* Convert! + */ + if( write_vips( write, qfac, NULL ) ) { + write_destroy( write ); + + return( -1 ); + } + write_destroy( write ); + + return( 0 ); +} + +/* As above, but save as a mime jpeg on stdout. + */ +int +im_vips2mimejpeg( IMAGE *in, int qfac ) +{ + IMAGE *base; + int len; + char *buf; + + if( !(base = im_open( "im_vips2mimejpeg:1", "p" )) ) + return( -1 ); + if( im_vips2bufjpeg( in, base, qfac, &buf, &len ) ) { + im_close( base ); + return( -1 ); + } + + /* Write as a MIME type. + */ + printf( "Content-length: %d\r\n", len ); + printf( "Content-type: image/jpeg\r\n" ); + printf( "\r\n" ); + fwrite( buf, sizeof( char ), len, stdout ); + fflush( stdout ); + + im_close( base ); + + if( ferror( stdout ) ) { + im_error( "im_vips2mimejpeg", _( "error writing output" ) ); + return( -1 ); + } + + return( 0 ); +} + +#endif /*HAVE_JPEG*/ diff --git a/libsrc/conversion/im_vips2mask.c b/libsrc/conversion/im_vips2mask.c new file mode 100644 index 00000000..49c33b0d --- /dev/null +++ b/libsrc/conversion/im_vips2mask.c @@ -0,0 +1,126 @@ +/* @(#) Function to write an IMAGE to a DOUBLEMASK. One band IM_BANDFMT_DOUBLE + * @(#) images, or n-band by 1 pixel double images. + * @(#) + * @(#) DOUBLEMASK * + * @(#) im_vips2mask( IMAGE *in, char *out ) + * @(#) + * @(#) The function returns NULL on error and a new DOUBLEMASK on success + * Author: J.Cupitt + * Written on: 6/6/94 + * Modified on: + * + * 16/10/06 + * - allow 1xn-band images too + * 23/2/07 + * - oop, broken for nx1 m-band images + * - now casts to double for you + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +DOUBLEMASK * +im_vips2mask( IMAGE *in, const char *outname ) +{ + int width, height; + DOUBLEMASK *out; + + /* double* only: cast if necessary. + */ + if( in->BandFmt != IM_BANDFMT_DOUBLE ) { + IMAGE *t; + + if( !(t = im_open( "im_vips2mask", "p" )) ) + return( NULL ); + if( im_clip2d( in, t ) || + !(out = im_vips2mask( t, outname )) ) { + im_close( t ); + return( NULL ); + } + im_close( t ); + + return( out ); + } + + /* Check the image. + */ + if( im_incheck( in ) ) + return( NULL ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_vips2mask", _( "uncoded images only" ) ); + return( NULL ); + } + if( in->Bands == 1 ) { + width = in->Xsize; + height = in->Ysize; + } + else if( in->Xsize == 1 ) { + width = in->Bands; + height = in->Ysize; + } + else if( in->Ysize == 1 ) { + width = in->Xsize; + height = in->Bands; + } + else { + im_error( "im_vips2mask", + _( "one band, nx1, or 1xn images only" ) ); + return( NULL ); + } + + if( !(out = im_create_dmask( outname, width, height )) ) + return( NULL ); + if( in->Bands > 1 && in->Ysize == 1 ) { + double *data = (double *) in->data; + int x, y; + + /* Need to transpose: the image is RGBRGBRGB, we need RRRGGGBBB. + */ + for( y = 0; y < height; y++ ) + for( x = 0; x < width; x++ ) + out->coeff[x + y * width] = + data[x * height + y]; + } + else + memcpy( out->coeff, in->data, + width * height * sizeof( double ) ); + + return( out ); +} + diff --git a/libsrc/conversion/im_vips2png.c b/libsrc/conversion/im_vips2png.c new file mode 100644 index 00000000..20ee37f4 --- /dev/null +++ b/libsrc/conversion/im_vips2png.c @@ -0,0 +1,311 @@ +/* Convert 1 to 4-band 8 or 16-bit VIPS images to/from PNG. + * + * 28/11/03 JC + * - better no-overshoot on tile loop + * 22/2/05 + * - read non-interlaced PNG with a line buffer (thanks Michel Brabants) + * 11/1/06 + * - read RGBA palette-ized images more robustly (thanks Tom) + * 20/4/06 + * - auto convert to sRGB/mono (with optional alpha) for save + * 1/5/06 + * - from vips_png.c + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_PNG + +#include + +int +im_vips2png( IMAGE *in, const char *filename ) +{ + im_error( "im_vips2png", _( "PNG support disabled" ) ); + return( -1 ); +} + +#else /*HAVE_PNG*/ + +#include +#include +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#if PNG_LIBPNG_VER < 10003 +#error "PNG library too old." +#endif + +static void +user_error_function( png_structp png_ptr, png_const_charp error_msg ) +{ + im_error( "im_vips2png", _( "PNG error: \"%s\"" ), error_msg ); +} + +static void +user_warning_function( png_structp png_ptr, png_const_charp warning_msg ) +{ + im_error( "im_vips2png", _( "PNG warning: \"%s\"" ), warning_msg ); +} + +/* What we track during a PNG write. + */ +typedef struct { + IMAGE *in; + REGION *reg; + im_threadgroup_t *tg; + + FILE *fp; + png_structp pPng; + png_infop pInfo; + png_bytep *row_pointer; +} Write; + +static void +write_destroy( Write *write ) +{ + IM_FREEF( im_region_free, write->reg ); + IM_FREEF( im_threadgroup_free, write->tg ); + IM_FREEF( im_close, write->in ); + IM_FREEF( fclose, write->fp ); + if( write->pPng ) + png_destroy_write_struct( &write->pPng, &write->pInfo ); + IM_FREE( write->row_pointer ); + + im_free( write ); +} + +static Write * +write_new( IMAGE *in ) +{ + Write *write; + + if( !(write = IM_NEW( NULL, Write )) ) + return( NULL ); + memset( write, 0, sizeof( Write ) ); + + if( !(write->in = im__convert_saveable( in, TRUE )) ) { + im_error( "im_vips2png", + _( "unable to convert to RGB for save" ) ); + write_destroy( write ); + return( NULL ); + } + + write->reg = im_region_create( write->in ); + write->tg = im_threadgroup_create( write->in ); + write->row_pointer = IM_ARRAY( NULL, write->tg->nlines, png_bytep ); + write->fp = NULL; + write->pPng = NULL; + write->pInfo = NULL; + + if( !write->reg || !write->tg || !write->row_pointer ) { + write_destroy( write ); + return( NULL ); + } + + if( !(write->pPng = png_create_write_struct( + PNG_LIBPNG_VER_STRING, NULL, + user_error_function, user_warning_function )) ) { + write_destroy( write ); + return( NULL ); + } + + /* Catch PNG errors from png_create_info_struct(). + */ + if( setjmp( write->pPng->jmpbuf ) ) { + write_destroy( write ); + return( NULL ); + } + + if( !(write->pInfo = png_create_info_struct( write->pPng )) ) { + write_destroy( write ); + return( NULL ); + } + + return( write ); +} + +/* Write a VIPS image to PNG. + */ +static int +write_vips( Write *write, int compress, int interlace ) +{ + IMAGE *in = write->in; + + Rect area; + int j, y, i, nb_passes; + + assert( in->BandFmt == IM_BANDFMT_UCHAR ); + assert( in->Coding == IM_CODING_NONE ); + assert( in->Bands > 0 && in->Bands < 5 ); + + /* Catch PNG errors. + */ + if( setjmp( write->pPng->jmpbuf ) ) + return( -1 ); + + /* Check input image. + */ + if( im_pincheck( in ) ) + return( -1 ); + if( compress < 0 || compress > 9 ) { + im_error( "im_vips2png", _( "compress should be in [0,9]" ) ); + return( -1 ); + } + + /* Set compression parameters. + */ + png_set_compression_level( write->pPng, compress ); + + write->pInfo->width = in->Xsize; + write->pInfo->height = in->Ysize; + write->pInfo->bit_depth = (in->BandFmt == IM_BANDFMT_UCHAR ? 8 : 16); + write->pInfo->gamma = (float) 1.0; + + switch( in->Bands ) { + case 1: write->pInfo->color_type = PNG_COLOR_TYPE_GRAY; break; + case 2: write->pInfo->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; + case 3: write->pInfo->color_type = PNG_COLOR_TYPE_RGB; break; + case 4: write->pInfo->color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; + + default: + assert( 0 ); + } + + png_write_info( write->pPng, write->pInfo ); + + /* If we're an intel byte order CPU and this is a 16bit image, we need + * to swap bytes. + */ + if( write->pInfo->bit_depth > 8 && !im_amiMSBfirst() ) + png_set_swap( write->pPng ); + + if( interlace ) + nb_passes = png_set_interlace_handling( write->pPng ); + else + nb_passes = 1; + + /* Write data. + */ + for( i = 0; i < nb_passes; i++ ) + for( y = 0; y < in->Ysize; y += write->tg->nlines ) { + area.left = 0; + area.top = y; + area.width = in->Xsize; + area.height = IM_MIN( write->tg->nlines, + in->Ysize - y ); + + if( im_prepare_thread( write->tg, write->reg, &area ) ) + return( -1 ); + + for( j = 0; j < area.height; j++ ) + write->row_pointer[j] = (png_bytep) + IM_REGION_ADDR( write->reg, 0, y + j ); + png_write_rows( write->pPng, + write->row_pointer, area.height ); + } + + png_write_end( write->pPng, write->pInfo ); + + return( 0 ); +} + +/* Write a VIPS image to a file as PNG. + */ +int +im_vips2png( IMAGE *in, const char *filename ) +{ + Write *write; + int compress; + int interlace; + + char *p, *q; + + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char buf[FILENAME_MAX]; + + if( !(write = write_new( in )) ) + return( -1 ); + + /* Extract write mode from filename and parse. + */ + im_filename_split( filename, name, mode ); + strcpy( buf, mode ); + p = &buf[0]; + compress = 6; + interlace = 0; + if( (q = im_getnextoption( &p )) ) + compress = atoi( q ); + if( (q = im_getnextoption( &p )) ) + interlace = atoi( q ); + + /* Make output. + */ +#ifdef BINARY_OPEN + if( !(write->fp = fopen( name, "wb" )) ) { +#else /*BINARY_OPEN*/ + if( !(write->fp = fopen( name, "w" )) ) { +#endif /*BINARY_OPEN*/ + write_destroy( write ); + im_error( "im_vips2png", _( "unable to open \"%s\"" ), name ); + + return( -1 ); + } + png_init_io( write->pPng, write->fp ); + + /* Convert it! + */ + if( write_vips( write, compress, interlace ) ) { + write_destroy( write ); + im_error( "im_vips2png", _( "unable to write \"%s\"" ), name ); + + return( -1 ); + } + write_destroy( write ); + + return( 0 ); +} + +#endif /*HAVE_PNG*/ diff --git a/libsrc/conversion/im_vips2ppm.c b/libsrc/conversion/im_vips2ppm.c new file mode 100644 index 00000000..da378b16 --- /dev/null +++ b/libsrc/conversion/im_vips2ppm.c @@ -0,0 +1,293 @@ +/* Write a ppm file. + * + * 28/11/03 JC + * - better no-overshoot on tile loop + * 9/9/05 + * - tiny cleanups + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* What we track during a PPM write. + */ +typedef struct { + IMAGE *in; + REGION *reg; + im_threadgroup_t *tg; + FILE *fp; + char *name; +} Write; + +static void +write_destroy( Write *write ) +{ + IM_FREEF( im_threadgroup_free, write->tg ); + IM_FREEF( im_region_free, write->reg ); + IM_FREEF( fclose, write->fp ); + IM_FREE( write->name ); + + im_free( write ); +} + +static Write * +write_new( IMAGE *in, const char *name ) +{ + Write *write; + + if( !(write = IM_NEW( NULL, Write )) ) + return( NULL ); + + write->in = in; + write->reg = im_region_create( write->in ); + write->tg = im_threadgroup_create( write->in ); + write->name = im_strdup( NULL, name ); + +#ifdef BINARY_OPEN + if( !(write->fp = fopen( name, "wb" )) ) { +#else /*BINARY_OPEN*/ + if( !(write->fp = fopen( name, "w" )) ) { +#endif /*BINARY_OPEN*/ + im_error( "im_vips2ppm", + _( "unable to open \"%s\" for output" ), name ); + } + + if( !write->reg || !write->tg || !write->name || !write->fp ) { + write_destroy( write ); + return( NULL ); + } + + return( write ); +} + +typedef int (*write_fn)( IMAGE *in, FILE *fp, PEL *p ); + +static int +write_ppm_line_ascii( IMAGE *in, FILE *fp, PEL *p ) +{ + const int sk = IM_IMAGE_SIZEOF_PEL( in ); + const int nb = IM_MIN( 3, in->Bands ); + int x, k; + + /* If IM_CODING_LABQ, write 3 bands. + */ + + for( x = 0; x < in->Xsize; x++ ) { + for( k = 0; k < nb; k++ ) { + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + fprintf( fp, "%d ", p[k] ); + break; + + case IM_BANDFMT_USHORT: + fprintf( fp, "%d ", ((unsigned short *) p)[k] ); + break; + + case IM_BANDFMT_UINT: + fprintf( fp, "%d ", ((unsigned int *) p)[k] ); + break; + + default: + assert( 0 ); + } + } + + fprintf( fp, " " ); + + p += sk; + } + + if( !fprintf( fp, "\n" ) ) { + im_error( "im_vips2ppm", _( "write error ... disc full?" ) ); + return( -1 ); + } + + return( 0 ); +} + +static int +write_ppm_line_binary( IMAGE *in, FILE *fp, PEL *p ) +{ + const int sk = IM_IMAGE_SIZEOF_PEL( in ); + const int nb = IM_MIN( 3, in->Bands ); + int x; + + for( x = 0; x < in->Xsize; x++ ) { + if( !fwrite( p, 1, nb, fp ) ) { + im_error( "im_vips2ppm", + _( "write error ... disc full?" ) ); + return( -1 ); + } + + p += sk; + } + + return( 0 ); +} + +static int +write_ppm( Write *write, int ascii ) +{ + IMAGE *in = write->in; + write_fn fn = ascii ? write_ppm_line_ascii : write_ppm_line_binary; + + int max_value; + char *magic; + time_t timebuf; + int y, i; + Rect area; + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + max_value = UCHAR_MAX; + break; + + case IM_BANDFMT_USHORT: + max_value = USHRT_MAX; + break; + + case IM_BANDFMT_UINT: + max_value = UINT_MAX; + break; + + default: + assert( 0 ); + } + + if( in->Bands == 1 && ascii ) + magic = "P2"; + else if( in->Bands == 1 && !ascii ) + magic = "P5"; + else if( (in->Bands == 3 || in->Bands == 4) && ascii ) + magic = "P3"; + else if( (in->Bands == 3 || in->Bands == 4) && !ascii ) + magic = "P6"; + else + assert( 0 ); + + fprintf( write->fp, "%s\n", magic ); + time( &timebuf ); + fprintf( write->fp, "#im_vips2ppm - %s\n", ctime( &timebuf ) ); + fprintf( write->fp, "%d %d\n", in->Xsize, in->Ysize ); + fprintf( write->fp, "%d\n", max_value ); + + for( y = 0; y < in->Ysize; y += write->tg->nlines ) { + area.left = 0; + area.top = y; + area.width = in->Xsize; + area.height = IM_MIN( write->tg->nlines, in->Ysize - y ); + + if( im_prepare_thread( write->tg, write->reg, &area ) ) + return( -1 ); + + for( i = 0; i < area.height; i++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( write->reg, 0, y + i ); + + if( fn( write->in, write->fp, p ) ) + return( -1 ); + } + } + + return( 0 ); +} + +int +im_vips2ppm( IMAGE *in, const char *filename ) +{ + Write *write; + int ascii; + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + + /* Default to binary output ... much smaller. + */ + ascii = 0; + + /* Extract write mode from filename. + */ + im_filename_split( filename, name, mode ); + if( strcmp( mode, "" ) != 0 ) { + if( im_isprefix( "binary", mode ) ) + ascii = 0; + else if( im_isprefix( "ascii", mode ) ) + ascii = 1; + else { + im_error( "im_vips2ppm", + _( "bad mode string, " + "should be \"binary\" or \"ascii\"" ) ); + return( -1 ); + } + } + + if( in->Bbits > 8 && !ascii ) { + im_error( "im_vips2ppm", + _( "can't write binary >8 bit images" ) ); + return( -1 ); + } + if( !im_isuint( in ) ) { + im_error( "im_vips2ppm", _( "unsigned int formats only" ) ); + return( -1 ); + } + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_vips2ppm", + _( "uncoded or IM_CODING_LABQ only" ) ); + return( -1 ); + } + if( in->Coding == IM_CODING_NONE && in->Bands != 1 && in->Bands != 3 ) { + im_error( "im_vips2ppm", _( "1 or 3 band images only" ) ); + return( -1 ); + } + + if( im_pincheck( in ) || !(write = write_new( in, name )) ) + return( -1 ); + + if( write_ppm( write, ascii ) ) { + write_destroy( write ); + return( -1 ); + } + write_destroy( write ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_vips2tiff.c b/libsrc/conversion/im_vips2tiff.c new file mode 100644 index 00000000..d2111af5 --- /dev/null +++ b/libsrc/conversion/im_vips2tiff.c @@ -0,0 +1,1605 @@ +/* TIFF PARTS: + * Copyright (c) 1988, 1990 by Sam Leffler. + * All rights reserved. + * + * This file is provided for unrestricted use provided that this + * legend is included on all tape media and as a part of the + * software program in whole or part. Users may copy, modify or + * distribute this file at will. + + MODIFICATION FOR VIPS Copyright 1991, K.Martinez + * software may be distributed FREE, with these copyright notices + * no responsibility/warantee is implied or given + * + * + * Modified and added im_LabQ2LabC() function. It can write IM_TYPE_LABQ image + * in vips format to LAB in tiff format. + * Copyright 1994 Ahmed Abbood. + * + * 19/9/95 JC + * - calls TIFFClose() more reliably + * - tidied up + * 12/4/97 JC + * - thrown away and rewritten for TIFF 6 lib + * 22/4/97 JC + * - writes a pyramid! + * - to separate TIFF files tho' + * 23/4/97 JC + * - does 2nd gather pass to put pyramid into a single TIFF file + * - ... and shrinks IM_CODING_LABQ too + * 26/10/98 JC + * - binary open for stupid systems + * 7/6/99 JC + * - 16bit TIFF write too + * 9/7/99 JC + * - ZIP tiff added + * 11/5/00 JC + * - removed TIFFmalloc/TIFFfree + * 5/8/00 JC + * - mode string now part of filename + * 23/4/01 JC + * - HAVE_TIFF turns on TIFFness + * 19/3/02 ruven + * - pyramid stops at tile size, not 64x64 + * 29/4/02 JC + * - write any number of bands (but still with photometric RGB, so not + * very useful) + * 10/9/02 JC + * - oops, handle TIFF errors better + * - now writes CMYK correctly + * 13/2/03 JC + * - tries not to write mad resolutions + * 7/5/03 JC + * - only write CMYK if Type == CMYK + * - writes EXTRASAMPLES ALPHA for bands == 2 or 4 (if we're writing RGB) + * 17/11/03 JC + * - write float too + * 28/11/03 JC + * - read via a "p" so we work from mmap window images + * - uses threadgroups for speedup + * 9/3/04 JC + * - 1 bit write mode added + * 5/4/04 + * - better handling of edge tiles (thanks Ruven) + * 18/5/04 Andrey Kiselev + * - added res_inch/res_cm option + * 20/5/04 JC + * - allow single res number too + * 19/7/04 + * - write several scanlines at once, good speed up for some cases + * 22/9/04 + * - got rid of wrapper image so nip gets progress feedback + * - fixed tiny read-beyond-buffer issue for edge tiles + * 7/10/04 + * - added ICC profile embedding + * 13/12/04 + * - can now pyramid any non-complex type (thanks Ruven) + * 27/1/05 + * - added ccittfax4 as a compression option + * 9/3/05 + * - set PHOTOMETRIC_CIELAB for vips TYPE_LAB images ... so we can write + * float LAB as well as float RGB + * - also LABS images + * 22/6/05 + * - 16 bit LAB write was broken + * 9/9/05 + * - write any icc profile from meta + * 3/3/06 + * - raise tile buffer limit (thanks Ruven) + * 11/11/06 + * - set ORIENTATION_TOPLEFT (thanks Josef) + * 18/7/07 Andrey Kiselev + * - remove "b" option on TIFFOpen() + * - support TIFFTAG_PREDICTOR types for lzw and deflate compression + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on IM_REGION_ADDR() range checks, don't delete intermediates. +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_TIFF + +#include + +int +im_vips2tiff( IMAGE *im, const char *filename ) +{ + im_error( "im_vips2tiff", _( "TIFF support disabled" ) ); + + return( -1 ); +} + +#else /*HAVE_TIFF*/ + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Max no of tiles we buffer in a layer. Enough to buffer a line of 64x64 + * tiles on a 100k pixel across image. + */ +#define IM_MAX_LAYER_BUFFER (1000) + +/* Bits we OR together for quadrants in a tile. + */ +typedef enum pyramid_bits { + PYR_TL = 1, /* Top-left etc. */ + PYR_TR = 2, + PYR_BL = 4, + PYR_BR = 8, + PYR_ALL = 15, + PYR_NONE = 0 +} PyramidBits; + +/* A tile in our pyramid. + */ +typedef struct pyramid_tile { + REGION *tile; + PyramidBits bits; +} PyramidTile; + +/* A layer in the pyramid. + */ +typedef struct pyramid_layer { + /* Parameters. + */ + struct tiff_write *tw; /* Main TIFF write struct */ + int width, height; /* Layer size */ + int sub; /* Subsample factor for this layer */ + + char *lname; /* Name of this TIFF file */ + TIFF *tif; /* TIFF file we write this layer to */ + PEL *tbuf; /* TIFF output buffer */ + PyramidTile tiles[IM_MAX_LAYER_BUFFER]; + + struct pyramid_layer *below; /* Tiles go to here */ + struct pyramid_layer *above; /* Tiles come from here */ +} PyramidLayer; + +/* A TIFF image in the process of being written. + */ +typedef struct tiff_write { + IMAGE *im; /* Original input image */ + char *name; /* Final name we write to */ + char *mode; /* Mode string */ + + /* Read from im with these. + */ + REGION *reg; + im_threadgroup_t *tg; + + char *bname; /* Name for base layer */ + TIFF *tif; /* Image we write to */ + + PyramidLayer *layer; /* Top of pyramid, if in use */ + PEL *tbuf; /* TIFF output buffer */ + int tls; /* Tile line size */ + + int compression; /* Compression type */ + int jpqual; /* JPEG q-factor */ + int predictor; /* Predictor value */ + int tile; /* Tile or not */ + int tilew, tileh; /* Tile size */ + int pyramid; /* Write pyramid */ + int onebit; /* Write as 1-bit TIFF */ + int resunit; /* Resolution unit (inches or cm) */ + float xres; /* Resolution in X */ + float yres; /* Resolution in Y */ + int embed; /* Embed ICC profile */ + char *icc_profile; /* Profile to embed */ +} TiffWrite; + +/* Handle TIFF errors here. + */ +static void +vhandle( char *module, char *fmt, va_list ap ) +{ + im_error( "im_vips2tiff", _( "TIFF error in \"%s\": " ), module ); + im_verror( "im_vips2tiff", fmt, ap ); +} + +/* Open TIFF for output. + */ +static TIFF * +tiff_openout( const char *name ) +{ + TIFF *tif; + + if( !(tif = TIFFOpen( name, "w" )) ) { + im_error( "im_vips2tiff", + _( "unable to open \"%s\" for output" ), name ); + return( NULL ); + } + + return( tif ); +} + +/* Open TIFF for input. + */ +static TIFF * +tiff_openin( const char *name ) +{ + TIFF *tif; + + if( !(tif = TIFFOpen( name, "r" )) ) { + im_error( "im_vips2tiff", + _( "unable to open \"%s\" for input" ), name ); + return( NULL ); + } + + return( tif ); +} + +/* Convert VIPS LabQ to TIFF LAB. Just take the first three bands. + */ +static void +LabQ2LabC( PEL *q, PEL *p, int n ) +{ + int x; + + for( x = 0; x < n; x++ ) { + /* Get most significant 8 bits of lab. + */ + q[0] = p[0]; + q[1] = p[1]; + q[2] = p[2]; + + p += 4; + q += 3; + } +} + +/* Pack 8 bit VIPS to 1 bit TIFF. + */ +static void +eightbit2onebit( PEL *q, PEL *p, int n ) +{ + int x; + PEL bits; + + bits = 0; + for( x = 0; x < n; x++ ) { + bits <<= 1; + if( p[x] ) + bits |= 1; + + if( (x & 0x7) == 0x7 ) { + *q++ = bits; + bits = 0; + } + } + + /* Any left-over bits? Need to be left-aligned. + */ + if( (x & 0x7) != 0 ) + *q++ = bits << (8 - (x & 0x7)); +} + +/* Convert VIPS LABS to TIFF 16 bit LAB. + */ +static void +LabS2Lab16( PEL *q, PEL *p, int n ) +{ + int x; + short *p1 = (short *) p; + unsigned short *q1 = (unsigned short *) q; + + for( x = 0; x < n; x++ ) { + /* TIFF uses unsigned 16 bit ... move zero, scale up L. + */ + q1[0] = (int) p1[0] << 1; + q1[1] = p1[1]; + q1[2] = p1[2]; + + p1 += 3; + q1 += 3; + } +} + +/* Pack a VIPS region into a TIFF tile buffer. + */ +static void +pack2tiff( TiffWrite *tw, REGION *in, PEL *q, Rect *area ) +{ + int y; + + for( y = area->top; y < IM_RECT_BOTTOM( area ); y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( in, area->left, y ); + + if( in->im->Coding == IM_CODING_LABQ ) + LabQ2LabC( q, p, area->width ); + else if( tw->onebit ) + eightbit2onebit( q, p, area->width ); + else if( in->im->BandFmt == IM_BANDFMT_SHORT && + in->im->Type == IM_TYPE_LABS ) + LabS2Lab16( q, p, area->width ); + else + memcpy( q, p, + area->width * IM_IMAGE_SIZEOF_PEL( in->im ) ); + + q += tw->tls; + } +} + +/* Embed an ICC profile. + */ +static int +embed_profile( TIFF *tif, const char *profile ) +{ + char *buffer; + unsigned int length; + + if( !(buffer = im__file_read_name( profile, &length )) ) + return( -1 ); + TIFFSetField( tif, TIFFTAG_ICCPROFILE, length, buffer ); + im_free( buffer ); + +#ifdef DEBUG + printf( "im_vips2tiff: attached profile \"%s\"\n", profile ); +#endif /*DEBUG*/ + + return( 0 ); +} + +/* Embed an ICC profile from IMAGE metadata. + */ +static int +embed_profile_meta( TIFF *tif, IMAGE *im ) +{ + void *data; + size_t data_length; + + if( im_meta_get_blob( im, IM_META_ICC_NAME, &data, &data_length ) ) + return( -1 ); + TIFFSetField( tif, TIFFTAG_ICCPROFILE, data_length, data ); + +#ifdef DEBUG + printf( "im_vips2tiff: attached profile from meta\n" ); +#endif /*DEBUG*/ + + return( 0 ); +} + +/* Write a TIFF header. width and height are the size of the IMAGE we are + * writing (may have been shrunk!). + */ +static int +write_tiff_header( TiffWrite *tw, TIFF *tif, int width, int height ) +{ + uint16 v[1]; + + /* Output base header fields. + */ + TIFFSetField( tif, TIFFTAG_IMAGEWIDTH, width ); + TIFFSetField( tif, TIFFTAG_IMAGELENGTH, height ); + TIFFSetField( tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ); + TIFFSetField( tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT ); + TIFFSetField( tif, TIFFTAG_COMPRESSION, tw->compression ); + + /* Don't write mad resolutions (eg. zero), it confuses some programs. + */ + TIFFSetField( tif, TIFFTAG_RESOLUTIONUNIT, tw->resunit ); + TIFFSetField( tif, TIFFTAG_XRESOLUTION, + IM_CLIP( 0.01, tw->xres, 10000 ) ); + TIFFSetField( tif, TIFFTAG_YRESOLUTION, + IM_CLIP( 0.01, tw->yres, 10000 ) ); + + if( tw->compression == COMPRESSION_JPEG ) { + TIFFSetField( tif, TIFFTAG_JPEGQUALITY, tw->jpqual ); + TIFFSetField( tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB ); + } + + if( tw->predictor != -1 ) + TIFFSetField( tif, TIFFTAG_PREDICTOR, tw->predictor ); + + /* Attach ICC profile. + */ + if( tw->embed && embed_profile( tif, tw->icc_profile ) ) + return( -1 ); + if( !tw->embed && im_header_get_type( tw->im, IM_META_ICC_NAME ) && + embed_profile_meta( tif, tw->im ) ) + return( -1 ); + + /* And colour fields. + */ + if( tw->im->Coding == IM_CODING_LABQ ) { + TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, 3 ); + TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, 8 ); + TIFFSetField( tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB ); + } + else if( tw->onebit ) { + TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, 1 ); + TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, 1 ); + TIFFSetField( tif, + TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK ); + } + else { + int photometric; + + TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, tw->im->Bands ); + TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, tw->im->Bbits ); + + switch( tw->im->Bands ) { + case 1: + case 2: + photometric = PHOTOMETRIC_MINISBLACK; + if( tw->im->Bands == 2 ) { + v[0] = EXTRASAMPLE_ASSOCALPHA; + TIFFSetField( tif, TIFFTAG_EXTRASAMPLES, 1, v ); + } + break; + + case 3: + case 4: + if( tw->im->Type == IM_TYPE_LAB || + tw->im->Type == IM_TYPE_LABS ) + photometric = PHOTOMETRIC_CIELAB; + else if( tw->im->Type == IM_TYPE_CMYK ) { + photometric = PHOTOMETRIC_SEPARATED; + TIFFSetField( tif, + TIFFTAG_INKSET, INKSET_CMYK ); + } + else + photometric = PHOTOMETRIC_RGB; + + if( tw->im->Type != IM_TYPE_CMYK && + tw->im->Bands == 4 ) { + v[0] = EXTRASAMPLE_ASSOCALPHA; + TIFFSetField( tif, TIFFTAG_EXTRASAMPLES, 1, v ); + } + break; + + default: + assert( 0 ); + } + + TIFFSetField( tif, TIFFTAG_PHOTOMETRIC, photometric ); + } + + /* Layout. + */ + if( tw->tile ) { + TIFFSetField( tif, TIFFTAG_TILEWIDTH, tw->tilew ); + TIFFSetField( tif, TIFFTAG_TILELENGTH, tw->tileh ); + } + else + TIFFSetField( tif, TIFFTAG_ROWSPERSTRIP, 16 ); + + /* Sample format ... for float, we write IEEE. + */ + if( tw->im->BandFmt == IM_BANDFMT_FLOAT ) + TIFFSetField( tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP ); + + return( 0 ); +} + +/* Free a pyramid layer. + */ +static void +free_layer( PyramidLayer *layer ) +{ + int i; + + for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ ) + if( layer->tiles[i].tile ) { + im_region_free( layer->tiles[i].tile ); + layer->tiles[i].tile = NULL; + } + + /* And close the TIFF file we are writing to. + */ + if( layer->tbuf ) { + im_free( layer->tbuf ); + layer->tbuf = NULL; + } + if( layer->tif ) { + TIFFClose( layer->tif ); + layer->tif = NULL; + } +} + +/* Free an entire pyramid. + */ +static void +free_pyramid( PyramidLayer *layer ) +{ + if( layer->below ) + free_pyramid( layer->below ); + + free_layer( layer ); +} + +/* Make a name for a new TIFF layer. Base it on sub factor. + */ +static char * +new_tiff_name( TiffWrite *tw, char *name, int sub ) +{ + char buf[FILENAME_MAX]; + char buf2[FILENAME_MAX]; + + /* Remove existing .tif/.tiff suffix, if any. + */ + strcpy( buf, name ); + if( im_ispostfix( buf, ".tif" ) ) + buf[strlen( buf ) - 4] = '\0'; + if( im_ispostfix( buf, ".tiff" ) ) + buf[strlen( buf ) - 5] = '\0'; + + im_snprintf( buf2, FILENAME_MAX, "%s.%d.tif", buf, sub ); + + return( im_strdup( tw->im, buf2 ) ); +} + +/* Build a pyramid. w & h are size of layer above this layer. Write new layer + * struct into *zap, return 0/-1 for success/fail. + */ +static int +build_pyramid( TiffWrite *tw, PyramidLayer *above, + PyramidLayer **zap, int w, int h ) +{ + PyramidLayer *layer = IM_NEW( tw->im, PyramidLayer ); + int i; + + if( !layer ) + return( -1 ); + layer->tw = tw; + layer->width = w / 2; + layer->height = h / 2; + + if( !above ) + /* Top of pyramid. + */ + layer->sub = 2; + else + layer->sub = above->sub * 2; + + layer->lname = NULL; + layer->tif = NULL; + layer->tbuf = NULL; + + for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ ) { + layer->tiles[i].tile = NULL; + layer->tiles[i].bits = PYR_NONE; + } + + layer->below = NULL; + layer->above = above; + + /* Save layer, to make sure it gets freed properly. + */ + *zap = layer; + + if( layer->width > tw->tilew || layer->height > tw->tileh ) + if( build_pyramid( tw, layer, + &layer->below, layer->width, layer->height ) ) + return( -1 ); + + if( !(layer->lname = new_tiff_name( tw, tw->name, layer->sub )) ) + return( -1 ); + + /* Make output image. + */ + if( !(layer->tif = tiff_openout( layer->lname )) ) + return( -1 ); + + /* Write the TIFF header for this layer. + */ + if( write_tiff_header( tw, layer->tif, layer->width, layer->height ) ) + return( -1 ); + + if( !(layer->tbuf = im_malloc( NULL, TIFFTileSize( layer->tif ) )) ) + return( -1 ); + + return( 0 ); +} + +/* Pick a new tile to write to in this layer. Either reuse a tile we have + * previously filled, or make a new one. + */ +static int +find_new_tile( PyramidLayer *layer ) +{ + int i; + + /* Exisiting buffer we have finished with? + */ + for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ ) + if( layer->tiles[i].bits == PYR_ALL ) + return( i ); + + /* Have to make a new one. + */ + for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ ) + if( !layer->tiles[i].tile ) { + if( !(layer->tiles[i].tile = + im_region_create( layer->tw->im )) ) + return( -1 ); + return( i ); + } + + /* Out of space! + */ + im_error( "im_vips2tiff", _( "layer buffer exhausted -- " + "try making TIFF output tiles smaller" ) ); + + return( -1 ); +} + +/* Find a tile in the layer buffer - if it's not there, make a new one. + */ +static int +find_tile( PyramidLayer *layer, Rect *pos ) +{ + int i; + Rect quad; + Rect image; + Rect inter; + + /* Do we have a REGION for this position? + */ + for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ ) { + REGION *reg = layer->tiles[i].tile; + + if( reg && reg->valid.left == pos->left && + reg->valid.top == pos->top ) + return( i ); + } + + /* Make a new one. + */ + if( (i = find_new_tile( layer )) < 0 ) + return( -1 ); + if( im_region_buffer( layer->tiles[i].tile, pos ) ) + return( -1 ); + layer->tiles[i].bits = PYR_NONE; + + /* Do any quadrants of this tile fall entirely outside the image? + * If they do, set their bits now. + */ + quad.width = layer->tw->tilew / 2; + quad.height = layer->tw->tileh / 2; + image.left = 0; + image.top = 0; + image.width = layer->width; + image.height = layer->height; + + quad.left = pos->left; + quad.top = pos->top; + im_rect_intersectrect( &quad, &image, &inter ); + if( im_rect_isempty( &inter ) ) + layer->tiles[i].bits |= PYR_TL; + + quad.left = pos->left + quad.width; + quad.top = pos->top; + im_rect_intersectrect( &quad, &image, &inter ); + if( im_rect_isempty( &inter ) ) + layer->tiles[i].bits |= PYR_TR; + + quad.left = pos->left; + quad.top = pos->top + quad.height; + im_rect_intersectrect( &quad, &image, &inter ); + if( im_rect_isempty( &inter ) ) + layer->tiles[i].bits |= PYR_BL; + + quad.left = pos->left + quad.width; + quad.top = pos->top + quad.height; + im_rect_intersectrect( &quad, &image, &inter ); + if( im_rect_isempty( &inter ) ) + layer->tiles[i].bits |= PYR_BR; + + return( i ); +} + +/* Shrink a region by a factor of two, writing the result to a specified + * offset in another region. IM_CODING_LABQ only. + */ +static void +shrink_region_labpack( REGION *from, Rect *area, + REGION *to, int xoff, int yoff ) +{ + int ls = IM_REGION_LSKIP( from ); + Rect *t = &to->valid; + + int x, y; + Rect out; + + /* Calculate output size and position. + */ + out.left = t->left + xoff; + out.top = t->top + yoff; + out.width = area->width / 2; + out.height = area->height / 2; + + /* Shrink ... ignore the extension byte for speed. + */ + for( y = 0; y < out.height; y++ ) { + PEL *p = (PEL *) + IM_REGION_ADDR( from, area->left, area->top + y * 2 ); + PEL *q = (PEL *) + IM_REGION_ADDR( to, out.left, out.top + y ); + + for( x = 0; x < out.width; x++ ) { + signed char *sp = (signed char *) p; + unsigned char *up = (unsigned char *) p; + + int l = up[0] + up[4] + + up[ls] + up[ls + 4]; + int a = sp[1] + sp[5] + + sp[ls + 1] + sp[ls + 5]; + int b = sp[2] + sp[6] + + sp[ls + 2] + sp[ls + 6]; + + q[0] = l >> 2; + q[1] = a >> 2; + q[2] = b >> 2; + q[3] = 0; + + q += 4; + p += 8; + } + } +} + +#define SHRINK_TYPE_INT( TYPE ) \ + for( x = 0; x < out.width; x++ ) { \ + TYPE *tp = (TYPE *) p; \ + TYPE *tp1 = (TYPE *) (p + ls); \ + TYPE *tq = (TYPE *) q; \ + \ + for( z = 0; z < nb; z++ ) { \ + int tot = tp[z] + tp[z + nb] + \ + tp1[z] + tp1[z + nb]; \ + \ + tq[z] = tot >> 2; \ + } \ + \ + /* Move on two pels in input. \ + */ \ + p += ps << 1; \ + q += ps; \ + } + +#define SHRINK_TYPE_FLOAT( TYPE ) \ + for( x = 0; x < out.width; x++ ) { \ + TYPE *tp = (TYPE *) p; \ + TYPE *tp1 = (TYPE *) (p + ls); \ + TYPE *tq = (TYPE *) q; \ + \ + for( z = 0; z < nb; z++ ) { \ + double tot = (double) tp[z] + tp[z + nb] + \ + tp1[z] + tp1[z + nb]; \ + \ + tq[z] = tot / 4; \ + } \ + \ + /* Move on two pels in input. \ + */ \ + p += ps << 1; \ + q += ps; \ + } + +/* Shrink a region by a factor of two, writing the result to a specified + * offset in another region. n-band, non-complex. + */ +static void +shrink_region( REGION *from, Rect *area, + REGION *to, int xoff, int yoff ) +{ + int ls = IM_REGION_LSKIP( from ); + int ps = IM_IMAGE_SIZEOF_PEL( from->im ); + int nb = from->im->Bands; + Rect *t = &to->valid; + + int x, y, z; + Rect out; + + /* Calculate output size and position. + */ + out.left = t->left + xoff; + out.top = t->top + yoff; + out.width = area->width / 2; + out.height = area->height / 2; + + for( y = 0; y < out.height; y++ ) { + PEL *p = (PEL *) + IM_REGION_ADDR( from, area->left, area->top + y * 2 ); + PEL *q = (PEL *) + IM_REGION_ADDR( to, out.left, out.top + y ); + + /* Process this line of pels. + */ + switch( from->im->BandFmt ) { + case IM_BANDFMT_UCHAR: + SHRINK_TYPE_INT( unsigned char ); break; + case IM_BANDFMT_CHAR: + SHRINK_TYPE_INT( signed char ); break; + case IM_BANDFMT_USHORT: + SHRINK_TYPE_INT( unsigned short ); break; + case IM_BANDFMT_SHORT: + SHRINK_TYPE_INT( signed short ); break; + case IM_BANDFMT_UINT: + SHRINK_TYPE_INT( unsigned int ); break; + case IM_BANDFMT_INT: + SHRINK_TYPE_INT( signed int ); break; + case IM_BANDFMT_FLOAT: + SHRINK_TYPE_FLOAT( float ); break; + case IM_BANDFMT_DOUBLE: + SHRINK_TYPE_FLOAT( double ); break; + + default: + assert( 0 ); + } + } +} + +/* Write a tile from a layer. + */ +static int +save_tile( TiffWrite *tw, TIFF *tif, PEL *tbuf, REGION *reg, Rect *area ) +{ + /* Have to repack pixels. + */ + pack2tiff( tw, reg, tbuf, area ); + +#ifdef DEBUG + printf( "Writing %dx%d pixels at position %dx%d to image %s\n", + tw->tilew, tw->tileh, area->left, area->top, + TIFFFileName( tif ) ); +#endif /*DEBUG*/ + + /* Write to TIFF! easy. + */ + if( TIFFWriteTile( tif, tbuf, area->left, area->top, 0, 0 ) < 0 ) { + im_error( "im_vips2tiff", _( "TIFF write tile failed" ) ); + return( -1 ); + } + + return( 0 ); +} + +/* A new tile has arrived! Shrink into this layer, if we fill a region, write + * it and recurse. + */ +static int +new_tile( PyramidLayer *layer, REGION *tile, Rect *area ) +{ + TiffWrite *tw = layer->tw; + int xoff, yoff; + + int t, ri, bo; + Rect out, new; + PyramidBits bit; + + /* Calculate pos and size of new pixels we make inside this layer. + */ + new.left = area->left / 2; + new.top = area->top / 2; + new.width = area->width / 2; + new.height = area->height / 2; + + /* Has size fallen to zero? Can happen if this is a one-pixel-wide + * strip. + */ + if( im_rect_isempty( &new ) ) + return( 0 ); + + /* Offset into this tile ... ie. which quadrant we are writing. + */ + xoff = new.left % layer->tw->tilew; + yoff = new.top % layer->tw->tileh; + + /* Calculate pos for tile we shrink into in this layer. + */ + out.left = new.left - xoff; + out.top = new.top - yoff; + + /* Clip against edge of image. + */ + ri = IM_MIN( layer->width, out.left + layer->tw->tilew ); + bo = IM_MIN( layer->height, out.top + layer->tw->tileh ); + out.width = ri - out.left; + out.height = bo - out.top; + + if( (t = find_tile( layer, &out )) < 0 ) + return( -1 ); + + /* Shrink into place. + */ + if( tw->im->Coding == IM_CODING_NONE ) + shrink_region( tile, area, + layer->tiles[t].tile, xoff, yoff ); + else + shrink_region_labpack( tile, area, + layer->tiles[t].tile, xoff, yoff ); + + /* Set that bit. + */ + if( xoff ) + if( yoff ) + bit = PYR_BR; + else + bit = PYR_TR; + else + if( yoff ) + bit = PYR_BL; + else + bit = PYR_TL; + if( layer->tiles[t].bits & bit ) { + im_error( "im_vips2tiff", _( "internal error #9876345" ) ); + return( -1 ); + } + layer->tiles[t].bits |= bit; + + if( layer->tiles[t].bits == PYR_ALL ) { + /* Save this complete tile. + */ + if( save_tile( tw, layer->tif, layer->tbuf, + layer->tiles[t].tile, &layer->tiles[t].tile->valid ) ) + return( -1 ); + + /* And recurse down the pyramid! + */ + if( layer->below && + new_tile( layer->below, + layer->tiles[t].tile, + &layer->tiles[t].tile->valid ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Write as tiles. + */ +static int +write_tif_tile( TiffWrite *tw ) +{ + IMAGE *im = tw->im; + Rect area; + int x, y; + + if( !(tw->tbuf = im_malloc( NULL, TIFFTileSize( tw->tif ) )) ) + return( -1 ); + + /* Write pyramid too? Only bother if bigger than tile size. + */ + if( tw->pyramid && + (im->Xsize > tw->tilew || im->Ysize > tw->tileh) && + build_pyramid( tw, NULL, &tw->layer, im->Xsize, im->Ysize ) ) + return( -1 ); + + for( y = 0; y < im->Ysize; y += tw->tileh ) + for( x = 0; x < im->Xsize; x += tw->tilew ) { + /* Set up rect we write. + */ + area.left = x; + area.top = y; + area.width = IM_MIN( tw->tilew, im->Xsize - x ); + area.height = IM_MIN( tw->tileh, im->Ysize - y ); + + if( im_prepare_thread( tw->tg, tw->reg, &area ) ) + return( -1 ); + + /* Write to TIFF. + */ + if( save_tile( tw, tw->tif, tw->tbuf, tw->reg, &area ) ) + return( -1 ); + + /* Is there a pyramid? Write to that too. + */ + if( tw->layer && new_tile( tw->layer, tw->reg, &area ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Write as scan-lines. + */ +static int +write_tif_strip( TiffWrite *tw ) +{ + IMAGE *im = tw->im; + Rect area; + int y, y2; + + if( !(tw->tbuf = im_malloc( NULL, TIFFScanlineSize( tw->tif ) )) ) + return( -1 ); + + /* Set up rect we ask for. + */ + area.left = 0; + area.width = im->Xsize; + + for( y = 0; y < im->Ysize; y += tw->tg->nlines ) { + area.top = y; + area.height = IM_MIN( tw->tg->nlines, im->Ysize - y ); + if( im_prepare_thread( tw->tg, tw->reg, &area ) ) + return( -1 ); + + for( y2 = 0; y2 < area.height; y2++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( tw->reg, 0, y + y2 ); + + /* Any repacking necessary. + */ + if( im->Coding == IM_CODING_LABQ ) { + LabQ2LabC( tw->tbuf, p, im->Xsize ); + p = tw->tbuf; + } + else if( im->BandFmt == IM_BANDFMT_SHORT && + im->Type == IM_TYPE_LABS ) { + LabS2Lab16( tw->tbuf, p, im->Xsize ); + p = tw->tbuf; + } + else if( tw->onebit ) { + eightbit2onebit( tw->tbuf, p, im->Xsize ); + p = tw->tbuf; + } + + /* Write to TIFF! easy. + */ + if( TIFFWriteScanline( tw->tif, p, y + y2, 0 ) < 0 ) { + im_error( "im_vips2tiff", + _( "TIFF write failed" ) ); + return( -1 ); + } + } + } + + return( 0 ); +} + +/* Delete any temp files we wrote. + */ +static void +delete_files( TiffWrite *tw ) +{ + PyramidLayer *layer = tw->layer; + + if( tw->bname ) { + unlink( tw->bname ); + tw->bname = NULL; + } + + for( layer = tw->layer; layer; layer = layer->below ) + if( layer->lname ) { + unlink( layer->lname ); + layer->lname = NULL; + } +} + +/* Free a TiffWrite. + */ +static void +free_tiff_write( TiffWrite *tw ) +{ +#ifndef DEBUG + delete_files( tw ); +#endif /*DEBUG*/ + + if( tw->tg ) { + im_threadgroup_free( tw->tg ); + tw->tg = NULL; + } + if( tw->reg ) { + im_region_free( tw->reg ); + tw->reg = NULL; + } + if( tw->tif ) { + TIFFClose( tw->tif ); + tw->tif = NULL; + } + if( tw->tbuf ) { + im_free( tw->tbuf ); + tw->tbuf = NULL; + } + if( tw->layer ) + free_pyramid( tw->layer ); + if( tw->icc_profile ) { + im_free( tw->icc_profile ); + tw->icc_profile = NULL; + } +} + +/* Round N down to P boundary. + */ +#define ROUND_DOWN(N,P) ((N) - ((N) % P)) + +/* Round N up to P boundary. + */ +#define ROUND_UP(N,P) (ROUND_DOWN( (N) + (P) - 1, (P) )) + +/* Make and init a TiffWrite. + */ +static TiffWrite * +make_tiff_write( IMAGE *im, const char *filename ) +{ + TiffWrite *tw; + char *p, *q, *r; + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char buf[FILENAME_MAX]; + + if( !(tw = IM_NEW( im, TiffWrite )) ) + return( NULL ); + tw->im = im; + im_filename_split( filename, name, mode ); + tw->name = im_strdup( im, name ); + tw->mode = im_strdup( im, mode ); + tw->tg = NULL; + tw->reg = NULL; + tw->bname = NULL; + tw->tif = NULL; + tw->layer = NULL; + tw->tbuf = NULL; + tw->compression = COMPRESSION_NONE; + tw->jpqual = 75; + tw->predictor = -1; + tw->tile = 0; + tw->tilew = 128; + tw->tileh = 128; + tw->pyramid = 0; + tw->onebit = 0; + tw->embed = 0; + tw->icc_profile = NULL; + + /* Output resolution settings default to VIPS-alike. + */ + tw->resunit = RESUNIT_CENTIMETER; + tw->xres = im->Xres * 10; + tw->yres = im->Yres * 10; + + /* Parse mode string. + */ + strcpy( buf, mode ); + p = &buf[0]; + if( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "none", q ) ) + tw->compression = COMPRESSION_NONE; + else if( im_isprefix( "packbits", q ) ) + tw->compression = COMPRESSION_PACKBITS; + else if( im_isprefix( "ccittfax4", q ) ) + tw->compression = COMPRESSION_CCITTFAX4; + else if( im_isprefix( "lzw", q ) ) { + tw->compression = COMPRESSION_LZW; + + if( (r = im_getsuboption( q )) ) + if( sscanf( r, "%d", &tw->predictor ) != 1 ) { + im_error( "im_vips2tiff", + _( "bad predictor " + "parameter" ) ); + return( NULL ); + } + } + else if( im_isprefix( "deflate", q ) ) { + tw->compression = COMPRESSION_ADOBE_DEFLATE; + + if( (r = im_getsuboption( q )) ) + if( sscanf( r, "%d", &tw->predictor ) != 1 ) { + im_error( "im_vips2tiff", + _( "bad predictor " + "parameter" ) ); + return( NULL ); + } + } + else if( im_isprefix( "jpeg", q ) ) { + tw->compression = COMPRESSION_JPEG; + + if( (r = im_getsuboption( q )) ) + if( sscanf( r, "%d", &tw->jpqual ) != 1 ) { + im_error( "im_vips2tiff", + _( "bad JPEG quality " + "parameter" ) ); + return( NULL ); + } + } + else { + im_error( "im_vips2tiff", _( "unknown compression mode " + "\"%s\"\nshould be one of \"none\", " + "\"packbits\", \"ccittfax4\", \"lzw\", " + "\"deflate\" or \"jpeg\"" ), q ); + return( NULL ); + } + } + if( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "tile", q ) ) { + tw->tile = 1; + + if( (r = im_getsuboption( q )) ) { + if( sscanf( r, "%dx%d", + &tw->tilew, &tw->tileh ) != 2 ) { + im_error( "im_vips2tiff", _( "bad tile " + "sizes" ) ); + return( NULL ); + } + + if( tw->tilew < 10 || tw->tileh < 10 || + tw->tilew > 1000 || tw->tileh > 1000 ) { + im_error( "im_vips2tiff", _( "bad tile " + "size %dx%d" ), + tw->tilew, tw->tileh ); + return( NULL ); + } + + if( (tw->tilew & 0xf) != 0 || + (tw->tileh & 0xf) != 0 ) { + im_error( "im_vips2tiff", _( "tile " + "size not a multiple of 16" ) ); + return( NULL ); + } + } + } + else if( im_isprefix( "strip", q ) ) + tw->tile = 0; + else { + im_error( "im_vips2tiff", _( "unknown layout mode " + "\"%s\"\nshould be one of \"tile\" or " + "\"strip\"" ), q ); + return( NULL ); + } + } + if( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "pyramid", q ) ) + tw->pyramid = 1; + else if( im_isprefix( "flat", q ) ) + tw->pyramid = 0; + else { + im_error( "im_vips2tiff", _( "unknown multi-res mode " + "\"%s\"\nshould be one of \"flat\" or " + "\"pyramid\"" ), q ); + return( NULL ); + } + } + if( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "onebit", q ) ) + tw->onebit = 1; + else if( im_isprefix( "manybit", q ) ) + tw->onebit = 0; + else { + im_error( "im_vips2tiff", _( "unknown format " + "\"%s\"\nshould be one of \"onebit\" or " + "\"manybit\"" ), q ); + return( NULL ); + } + } + if( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "res_cm", q ) ) + tw->resunit = RESUNIT_CENTIMETER; + else if( im_isprefix( "res_inch", q ) ) + tw->resunit = RESUNIT_INCH; + else { + im_error( "im_vips2tiff", _( "unknown resolution unit " + "\"%s\"\nshould be one of \"res_cm\" or " + "\"res_inch\"" ), q ); + return( NULL ); + } + + if( (r = im_getsuboption( q )) ) { + if( sscanf( r, "%fx%f", &tw->xres, &tw->yres ) != 2 ) { + if( sscanf( r, "%f", &tw->xres ) != 1 ) { + im_error( "im_vips2tiff", _( "bad " + "resolution values" ) ); + return( NULL ); + } + + tw->yres = tw->xres; + } + } + } + if( (q = im_getnextoption( &p )) && strcmp( q, "" ) != 0 ) { + tw->embed = 1; + tw->icc_profile = im_strdup( NULL, q ); + } + if( (q = im_getnextoption( &p )) ) { + im_error( "im_vips2tiff", + _( "unknown extra options \"%s\"" ), q ); + return( NULL ); + } + if( !tw->tile && tw->pyramid ) { + im_warn( "im_vips2tiff", _( "can't have strip pyramid -- " + "enabling tiling" ) ); + tw->tile = 1; + } + + /* We can only pyramid LABQ and non-complex images. + */ + if( tw->pyramid ) { + if( im->Coding == IM_CODING_NONE && im_iscomplex( im ) ) { + im_error( "im_vips2tiff", + _( "can only pyramid LABQ and " + "non-complex images" ) ); + return( NULL ); + } + } + + /* Only 1-bit-ize 8 bit mono images. + */ + if( tw->onebit ) { + if( im->Coding != IM_CODING_NONE || + im->BandFmt != IM_BANDFMT_UCHAR || + im->Bands != 1 ) + tw->onebit = 0; + } + + if( tw->onebit && tw->compression == COMPRESSION_JPEG ) { + im_warn( "im_vips2tiff", _( "can't have 1-bit JPEG -- " + "disabling JPEG" ) ); + tw->compression = COMPRESSION_NONE; + } + + /* Make region and threadgroup. + */ + if( !(tw->reg = im_region_create( tw->im )) || + !(tw->tg = im_threadgroup_create( tw->im )) ) { + free_tiff_write( tw ); + return( NULL ); + } + + /* Sizeof a line of bytes in the TIFF tile. + */ + if( im->Coding == IM_CODING_LABQ ) + tw->tls = tw->tilew * 3; + else if( tw->onebit ) + tw->tls = ROUND_UP( tw->tilew, 8 ) / 8; + else + tw->tls = IM_IMAGE_SIZEOF_PEL( im ) * tw->tilew; + + return( tw ); +} + +/* Copy fields. + */ +#define CopyField( tag, v ) \ + if( TIFFGetField( in, tag, &v ) ) TIFFSetField( out, tag, v ) + +/* Copy a TIFF file ... we know we wrote it, so just copy the tags we know + * we might have set. + */ +static int +tiff_copy( TIFF *out, TIFF *in ) +{ + uint32 i32, i32a; + uint16 i16; + int i; + float f; + tdata_t buf; + ttile_t tile; + ttile_t n; + + /* All the fields we might have set. + */ + CopyField( TIFFTAG_IMAGEWIDTH, i32 ); + CopyField( TIFFTAG_IMAGELENGTH, i32 ); + CopyField( TIFFTAG_PLANARCONFIG, i16 ); + CopyField( TIFFTAG_ORIENTATION, i16 ); + CopyField( TIFFTAG_COMPRESSION, i16 ); + CopyField( TIFFTAG_XRESOLUTION, f ); + CopyField( TIFFTAG_YRESOLUTION, f ); + CopyField( TIFFTAG_RESOLUTIONUNIT, i16 ); + CopyField( TIFFTAG_JPEGQUALITY, i ); + CopyField( TIFFTAG_PREDICTOR, i16 ); + CopyField( TIFFTAG_SAMPLESPERPIXEL, i16 ); + CopyField( TIFFTAG_BITSPERSAMPLE, i16 ); + CopyField( TIFFTAG_PHOTOMETRIC, i16 ); + CopyField( TIFFTAG_TILEWIDTH, i32 ); + CopyField( TIFFTAG_TILELENGTH, i32 ); + CopyField( TIFFTAG_ROWSPERSTRIP, i32 ); + + if( TIFFGetField( in, TIFFTAG_ICCPROFILE, &i32, &i32a ) ) + TIFFSetField( out, TIFFTAG_ICCPROFILE, i32, i32a ); + + buf = im_malloc( NULL, TIFFTileSize( in ) ); + n = TIFFNumberOfTiles( in ); + for( tile = 0; tile < n; tile++ ) { + tsize_t len; + + len = TIFFReadEncodedTile( in, tile, buf, (tsize_t) -1 ); + if( len < 0 || + TIFFWriteEncodedTile( out, tile, buf, len ) < 0 ) { + im_free( buf ); + return( -1 ); + } + } + im_free( buf ); + + return( 0 ); +} + +/* Append a file to a TIFF file. + */ +static int +tiff_append( TIFF *out, const char *name ) +{ + TIFF *in; + + if( !(in = tiff_openin( name )) ) + return( -1 ); + + if( tiff_copy( out, in ) ) { + TIFFClose( in ); + return( -1 ); + } + TIFFClose( in ); + + if( !TIFFWriteDirectory( out ) ) + return( -1 ); + + return( 0 ); +} + +/* Gather all of the files we wrote into single output file. + */ +static int +gather_pyramid( TiffWrite *tw ) +{ + PyramidLayer *layer; + TIFF *out; + +#ifdef DEBUG + printf( "Starting pyramid gather ...\n" ); +#endif /*DEBUG*/ + + if( !(out = tiff_openout( tw->name )) ) + return( -1 ); + + if( tiff_append( out, tw->bname ) ) { + TIFFClose( out ); + return( -1 ); + } + + for( layer = tw->layer; layer; layer = layer->below ) + if( tiff_append( out, layer->lname ) ) { + TIFFClose( out ); + return( -1 ); + } + + TIFFClose( out ); + +#ifdef DEBUG + printf( "Pyramid built\n" ); +#endif /*DEBUG*/ + + return( 0 ); +} + +int +im_vips2tiff( IMAGE *im, const char *filename ) +{ + TiffWrite *tw; + int res; + +#ifdef DEBUG + printf( "im_tiff2vips: libtiff version is \"%s\"\n", TIFFGetVersion() ); +#endif /*DEBUG*/ + + /* Override the default TIFF error handler. + */ + TIFFSetErrorHandler( (TIFFErrorHandler) vhandle ); + + /* Check input image. + */ + if( im_pincheck( im ) ) + return( -1 ); + if( im->Coding != IM_CODING_LABQ && im->Coding != IM_CODING_NONE ) { + im_error( "im_vips2tiff", _( "unknown coding type" ) ); + return( -1 ); + } + if( im->BandFmt != IM_BANDFMT_UCHAR && + !(im->BandFmt == IM_BANDFMT_SHORT && + im->Type == IM_TYPE_LABS) && + im->BandFmt != IM_BANDFMT_USHORT && + im->BandFmt != IM_BANDFMT_FLOAT ) { + im_error( "im_vips2tiff", _( "unsigned 8-bit int, 16-bit int, " + "and 32-bit float only" ) ); + return( -1 ); + } + if( im->Coding == IM_CODING_NONE ) { + if( im->Bands < 1 || im->Bands > 4 ) { + im_error( "im_vips2tiff", _( "1 to 4 bands only" ) ); + return( -1 ); + } + } + + /* Make output image. If this is a pyramid, write the base image to + * fred.1.tif rather than fred.tif. + */ + if( !(tw = make_tiff_write( im, filename )) ) + return( -1 ); + if( tw->pyramid ) { + if( !(tw->bname = new_tiff_name( tw, tw->name, 1 )) || + !(tw->tif = tiff_openout( tw->bname )) ) { + free_tiff_write( tw ); + return( -1 ); + } + } + else { + /* No pyramid ... write straight to name. + */ + if( !(tw->tif = tiff_openout( tw->name )) ) { + free_tiff_write( tw ); + return( -1 ); + } + } + + /* Write the TIFF header for the full-res file. + */ + if( write_tiff_header( tw, tw->tif, im->Xsize, im->Ysize ) ) { + free_tiff_write( tw ); + return( -1 ); + } + + if( tw->tile ) + res = write_tif_tile( tw ); + else + res = write_tif_strip( tw ); + if( res ) { + free_tiff_write( tw ); + return( -1 ); + } + + /* Free pyramid resources ... this will TIFFClose() the intermediates, + * ready for us to read from them again. + */ + if( tw->layer ) + free_pyramid( tw->layer ); + if( tw->tif ) { + TIFFClose( tw->tif ); + tw->tif = NULL; + } + + /* Gather layers together into final pyramid file. + */ + if( tw->pyramid && gather_pyramid( tw ) ) { + free_tiff_write( tw ); + return( -1 ); + } + + free_tiff_write( tw ); + + return( 0 ); +} + +#endif /*HAVE_TIFF*/ diff --git a/libsrc/conversion/im_zoom.c b/libsrc/conversion/im_zoom.c new file mode 100644 index 00000000..e6c0dacc --- /dev/null +++ b/libsrc/conversion/im_zoom.c @@ -0,0 +1,371 @@ +/* @(#) Zoom an image by pixel replication. Any non-coded type, also works for + * @(#) IM_CODING_LABQ. + * @(#) + * @(#) int im_zoom( in, out, factor ) + * @(#) IMAGE *in, *out; + * @(#) int factor; + * @(#) + * @(#) Returns: -1 on error, else 0 + * Author: N. Martinez 1991 + * 6/6/94 JC + * - rewritten to ANSI-C + * - now works for any type, including IM_CODING_LABQ + * 7/10/94 JC + * - new IM_ARRAY() macro + * 26/1/96 JC + * - separate x and y zoom factors + * 21/8/96 JC + * - partial, yuk! this is so complicated ... + * 30/8/96 JC + * - sets demand_hint + * 10/2/00 JC + * - check for integer overflow in zoom facs ... was happening with ip's + * zoom on large images + * 3/8/02 JC + * - fall back to im_copy() for x & y factors == 1 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* + * TODO: + * Test for pixel size and use memcpy() on individual pixels once they reach + * sizes of the order of tens of bytes. char-wise copy is quiker than + * memcpy() for smaller pixels. + * + * Also, I haven't tested it but int-wise copying may be faster still, as + * long as alignment permits it. + * + * tcv. 2006-09-01 + */ + +/* Turn off assert(). +#define NDEBUG 1 + */ + +/* Turn on IM_REGION_ADDR() range checks. +#define DEBUG 1 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Round N down to P boundary. + */ +#define ROUND_DOWN(N,P) ((N) - ((N) % P)) + +/* Round N up to P boundary. + */ +#define ROUND_UP(N,P) (ROUND_DOWN( (N) + (P) - 1, (P) )) + +/* Our main parameter struct. + */ +typedef struct { + int xfac; /* Scale factors */ + int yfac; +} ZoomInfo; + +/* Paint the part of the region containing only whole pels. + */ +static void +paint_whole( REGION *or, REGION *ir, ZoomInfo *zm, + const int left, const int right, const int top, const int bottom ) +{ + const int ps = IM_IMAGE_SIZEOF_PEL( ir->im ); + const int ls = IM_REGION_LSKIP( or ); + const int rs = ps * (right - left); + + /* Transform to ir coordinates. + */ + const int ileft = left / zm->xfac; + const int iright = right / zm->xfac; + const int itop = top / zm->yfac; + const int ibottom = bottom / zm->yfac; + + int x, y, z, i; + + /* We know this! + */ + assert( right > left && bottom > top && + right % zm->xfac == 0 && + left % zm->xfac == 0 && + top % zm->yfac == 0 && + bottom % zm->yfac == 0 ); + + /* Loop over input, as we know we are all whole. + */ + for( y = itop; y < ibottom; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir, ileft, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, left, y * zm->yfac ); + PEL *r; + + /* Expand the first line of pels. + */ + r = q; + for( x = ileft; x < iright; x++ ) { + /* Copy each pel xfac times. + */ + for( z = 0; z < zm->xfac; z++ ) { + for( i = 0; i < ps; i++ ) + r[i] = p[i]; + + r += ps; + } + + p += ps; + } + + /* Copy the expanded line yfac-1 times. + */ + r = q + ls; + for( z = 1; z < zm->yfac; z++ ) { + memcpy( r, q, rs ); + r += ls; + } + } +} + +/* Paint the part of the region containing only part-pels. + */ +static void +paint_part( REGION *or, REGION *ir, const ZoomInfo *zm, + const int left, const int right, const int top, const int bottom ) +{ + const int ps = IM_IMAGE_SIZEOF_PEL( ir->im ); + const int ls = IM_REGION_LSKIP( or ); + const int rs = ps * (right - left); + + /* Start position in input. + */ + const int ix = left / zm->xfac; + const int iy = top / zm->yfac; + + /* Pels down to yfac boundary, pels down to bottom. Do the smallest of + * these for first y loop. + */ + const int ptbound = (iy + 1) * zm->yfac - top; + const int ptbot = bottom - top; + + int yt = IM_MIN( ptbound, ptbot ); + + int x, y, z, i; + + /* Only know this. + */ + assert( right - left >= 0 && bottom - top >= 0 ); + + /* Have to loop over output. + */ + for( y = top; y < bottom; ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir, ix, y / zm->yfac ); + PEL *q = (PEL *) IM_REGION_ADDR( or, left, y ); + PEL *r; + + /* Output pels until we jump the input pointer. + */ + int xt = (ix + 1) * zm->xfac - left; + + /* Loop for this output line. + */ + r = q; + for( x = left; x < right; x++ ) { + /* Copy 1 pel. + */ + for( i = 0; i < ps; i++ ) + r[i] = p[i]; + r += ps; + + /* Move input if on boundary. + */ + --xt; + if( xt == 0 ) { + xt = zm->xfac; + p += ps; + } + } + + /* Repeat that output line until the bottom of this pixel + * boundary, or we hit bottom. + */ + r = q + ls; + for( z = 1; z < yt; z++ ) { + memcpy( r, q, rs ); + r += ls; + } + + /* Move y on by the number of lines we wrote. + */ + y += yt; + + /* Reset yt for next iteration. + */ + yt = zm->yfac; + } +} + +/* Zoom a REGION. + */ +static int +zoom_gen( REGION *or, REGION *ir, IMAGE *in, ZoomInfo *zm ) +{ + /* Output area we are building. + */ + const Rect *r = &or->valid; + const int ri = IM_RECT_RIGHT( r ); + const int bo = IM_RECT_BOTTOM(r); + + Rect s; + int left, right, top, bottom; + int width, height; + + /* Area of input we need. We have to round out, as we may have + * part-pixels all around the edges. + */ + left = ROUND_DOWN( r->left, zm->xfac ); + right = ROUND_UP( ri, zm->xfac ); + top = ROUND_DOWN( r->top, zm->yfac ); + bottom = ROUND_UP( bo, zm->yfac ); + width = right - left; + height = bottom - top; + s.left = left / zm->xfac; + s.top = top / zm->yfac; + s.width = width / zm->xfac; + s.height = height / zm->yfac; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Find the part of the output (if any) which uses only whole pels. + */ + left = ROUND_UP( r->left, zm->xfac ); + right = ROUND_DOWN( ri, zm->xfac ); + top = ROUND_UP( r->top, zm->yfac ); + bottom = ROUND_DOWN( bo, zm->yfac ); + width = right - left; + height = bottom - top; + + /* Stage 1: we just paint the whole pels in the centre of the region. + * As we know they are not clipped, we can do it quickly. + */ + if( width > 0 && height > 0 ) + paint_whole( or, ir, zm, left, right, top, bottom ); + + /* Just fractional pixels left. Paint in the top, left, right and + * bottom parts. + */ + if( top - r->top > 0 ) + /* Some top pixels. + */ + paint_part( or, ir, zm, + r->left, ri, r->top, IM_MIN( top, bo ) ); + if( left - r->left > 0 && height > 0 ) + /* Left pixels. + */ + paint_part( or, ir, zm, + r->left, IM_MIN( left, ri ), top, bottom ); + if( ri - right > 0 && height > 0 ) + /* Right pixels. + */ + paint_part( or, ir, zm, + IM_MAX( right, r->left ), ri, top, bottom ); + if( bo - bottom > 0 && height >= 0 ) + /* Bottom pixels. + */ + paint_part( or, ir, zm, + r->left, ri, IM_MAX( bottom, r->top ), bo ); + + return( 0 ); +} + +int +im_zoom( IMAGE *in, IMAGE *out, int xfac, int yfac ) +{ + ZoomInfo *zm; + + /* Check arguments. + */ + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_zoom", _( "unknown coding type" ) ); + return( -1 ); + } + if( xfac <= 0 || yfac <= 0 ) { + im_error( "im_zoom", _( "zoom factors should be >= 0" ) ); + return( -1 ); + } + if( (double) in->Xsize * xfac > (double) INT_MAX / 2 || + (double) in->Ysize * yfac > (double) INT_MAX / 2 ) { + /* Make sure we won't get integer overflow. + */ + im_error( "im_zoom", _( "zoom factors too large" ) ); + return( -1 ); + } + if( xfac == 1 && yfac == 1 ) + return( im_copy( in, out ) ); + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Make output. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = in->Xsize * xfac; + out->Ysize = in->Ysize * yfac; + + /* Save parameters. + */ + if( !(zm = IM_NEW( out, ZoomInfo )) ) + return( -1 ); + zm->xfac = xfac; + zm->yfac = yfac; + + /* Set demand hints. THINSTRIP will prevent us from using + * paint_whole() too much ... so go for FATSTRIP. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, zoom_gen, im_stop_one, in, zm ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/man3/Makefile.am b/libsrc/conversion/man3/Makefile.am new file mode 100644 index 00000000..a0444356 --- /dev/null +++ b/libsrc/conversion/man3/Makefile.am @@ -0,0 +1,84 @@ +man_MANS = \ + im_analyze2vips.3 \ + im_bandjoin.3 \ + im_black.3 \ + im_c2amph.3 \ + im_c2imag.3 \ + im_c2ps.3 \ + im_c2real.3 \ + im_c2rect.3 \ + im_clip.3 \ + im_clip2c.3 \ + im_clip2cm.3 \ + im_clip2d.3 \ + im_clip2dcm.3 \ + im_clip2f.3 \ + im_clip2fmt.3 \ + im_clip2i.3 \ + im_clip2s.3 \ + im_clip2ui.3 \ + im_clip2us.3 \ + im_copy.3 \ + im_copy_set.3 \ + im_copy_set_meta.3 \ + im_copy_swap.3 \ + im_copy_morph.3 \ + im_copy_from.3 \ + im_csv2vips.3 \ + im_csv2vips_header.3 \ + im_magick2vips.3 \ + im_magick2vips_header.3 \ + im_extract.3 \ + im_extract_area.3 \ + im_extract_areabands.3 \ + im_extract_bands.3 \ + im_falsecolour.3 \ + im_fliphor.3 \ + im_flipver.3 \ + im_gbandjoin.3 \ + im_insert.3 \ + im_istiff.3 \ + im_jpeg2vips.3 \ + im_jpeg2vips_header.3 \ + im_lrjoin.3 \ + im_mask2vips.3 \ + im_png2vips.3 \ + im_png2vips_header.3 \ + im_exr2vips.3 \ + im_exr2vips_header.3 \ + im_ppm2vips.3 \ + im_ppm2vips_header.3 \ + im_print.3 \ + im_raw2vips.3 \ + im_recomb.3 \ + im_replicate.3 \ + im_grid.3 \ + im_ri2c.3 \ + im_rot180.3 \ + im_rot270.3 \ + im_rot90.3 \ + im_scale.3 \ + im_scaleps.3 \ + im_rightshift_size.3 \ + im_slice.3 \ + im_subsample.3 \ + im_system.3 \ + im_tbjoin.3 \ + im_thresh.3 \ + im_text.3 \ + im_tiff2vips.3 \ + im_tiff2vips_header.3 \ + im_tile_cache.3 \ + im_vips2bufjpeg.3 \ + im_vips2csv.3 \ + im_vips2jpeg.3 \ + im_vips2mask.3 \ + im_vips2mimejpeg.3 \ + im_vips2png.3 \ + im_vips2ppm.3 \ + im_vips2tiff.3 \ + im_zoom.3 \ + im_msb.3 \ + im_msb_band.3 + +EXTRA_DIST = ${man_MANS} diff --git a/libsrc/conversion/man3/im_analyze2vips.3 b/libsrc/conversion/man3/im_analyze2vips.3 new file mode 100644 index 00000000..b3bbf50c --- /dev/null +++ b/libsrc/conversion/man3/im_analyze2vips.3 @@ -0,0 +1,39 @@ +.TH IM_ANALYZE2VIPS 3 "4 August 2005" +.SH NAME +im_analyze2vips \- convert Analyze 7.5 images to VIPS format +.SH SYNOPSIS +#include + +int im_analyze2vips( const char *filename, IMAGE *out ) + +int im_analyze2vips_header( const char *filename, IMAGE *out ) + +.SH DESCRIPTION +.B im_analyze2vips(3) +reads the Analyze image in +.B filename, +and writes the image out +in VIPS format. It can read (almost) any Analyze format image. Images with +more than two dimensions simply appear as a very tall, thin strip. Use +.B im_grid(3) +to lay the tiles out as your prefer. + +It reads the old-style 7.5 format, where the header and the image data are +stored in separate files. You can give the name of either the header (for +example, "fred.hdr") the image data (for example, "fred.img"), or neither (eg. +"fred"). + +The fields in the Analyze header appear in the VIPS header with a "dsr-" +prefix. So the Analyze field "patient_id", which is part of data_history, may +be retrieved with + + im_header_string "dsr-data_history.patient_id" fred.v + +.B im_analyze2vips_header() +reads the just the header of the image into the VIPS image. You can't read any +pixels! + +.SH SEE ALSO +im_grid(3) +.SH COPYRIGHT +Imperial College 2005 diff --git a/libsrc/conversion/man3/im_bandjoin.3 b/libsrc/conversion/man3/im_bandjoin.3 new file mode 100644 index 00000000..f6d0d5d9 --- /dev/null +++ b/libsrc/conversion/man3/im_bandjoin.3 @@ -0,0 +1,39 @@ +.TH IM_BANDJOIN 3 "28 June 1990" +.SH NAME +im_bandjoin, im_gbandjoin \- join two or more images +.SH SYNOPSIS +.B #include + +.B int im_bandjoin(im1, im2, imout) +.br +.B IMAGE *im1, *im2, *imout; + +.B int im_gbandjoin(imarray, imout, no) +.br +.B IMAGE *imarray[], *imout; +.br +.B int no; +.SH DESCRIPTION +These function perform a band-wise join of two or more images. Input +images should be of the same type and should have the same sizes. + +.B im_bandjoin() +performs a band-wise join of two images. If the two images +have n and m bands respectively, then the output image will have n+m +bands, with the first n coming from the first image and the last m +from the second. + +.B im_gbandjoin() +performs a generalised band-wise join of no images. +Input images can have any number of bands; for instance if imarray[0] has j +bands, imarray[1] has k bands, ...., imarray[no-1] has z bands, output +has j+k+...+z bands. +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_lrjoin(3), im_lrmerge(3), im_insert(3). +.SH COPYRIGHT +.br +J. Cupitt, N. Dessipris +.SH AUTHOR +J. Cupitt, N. Dessipris \- 25/04/1991 diff --git a/libsrc/conversion/man3/im_black.3 b/libsrc/conversion/man3/im_black.3 new file mode 100644 index 00000000..20a9331f --- /dev/null +++ b/libsrc/conversion/man3/im_black.3 @@ -0,0 +1,23 @@ +.TH IM_BLACK 3 "11 April 1990" +.SH NAME +im_black \- make a black image +.SH SYNOPSIS +.B #include + +.B int im_black(out, x, y, bands) +.br +.B IMAGE *out; +.br +.B int x, y, bands; +.SH DESCRIPTION +im_black() +makes a black uchar image of the specified size and number of bands. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_openin(3), im_openout(3), im_setbuf(3). +.SH COPYRIGHT +.br +National Gallery 1990 - 1993 +.SH AUTHOR +J. Cupitt \- 11/04/1990 diff --git a/libsrc/conversion/man3/im_c2amph.3 b/libsrc/conversion/man3/im_c2amph.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_c2amph.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_c2imag.3 b/libsrc/conversion/man3/im_c2imag.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_c2imag.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_c2ps.3 b/libsrc/conversion/man3/im_c2ps.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_c2ps.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_c2real.3 b/libsrc/conversion/man3/im_c2real.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_c2real.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_c2rect.3 b/libsrc/conversion/man3/im_c2rect.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_c2rect.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip.3 b/libsrc/conversion/man3/im_clip.3 new file mode 100644 index 00000000..6096b537 --- /dev/null +++ b/libsrc/conversion/man3/im_clip.3 @@ -0,0 +1,90 @@ +.TH CONVERSIONS 3 "15 April 1991" +.SH NAME +im_c2amph, im_c2imag, im_c2ps, im_c2real, im_clip2fmt, +im_ri2c \- conversion between image types +.SH SYNOPSIS +#include + +int im_c2amph(in, out) +.br +IMAGE *in, *out; + +int im_c2rect(in, out) +.br +IMAGE *in, *out; + +int im_c2imag(in, out) +.br +IMAGE *in, *out; + +int im_c2ps(in, out) +.br +IMAGE *in, *out; + +int im_c2real(in, out) +.br +IMAGE *in, *out; + +int im_clip2fmt(in, out, ofmt) +.br +IMAGE *in, *out; +.br +int ofmt; + +int im_ri2c(in1, in2, out) +.br +IMAGE *in1, *in2, *out; +.SH DESCRIPTION +Each of the above functions converts the image held by the image descriptor in +to another type image and writes the result on the image descriptor out. +Sizes and the number of channels are identical to those of input. Whenever +required by the conversion, the element values are rounded by calling floor(). + +.B im_c2amph() +converts a complex (float or double) input to (amplitude, phase), with phase +in degrees. Output has the same format as input. + +.B im_c2rect() +converts a complex (float or double) input in (amplitude, phase) form back to +rectangular coordinates. Again, phase should be expressed in degrees. + +.B im_c2ps() +convert a (float or double) complex image to amplitude (float or +double) image. Complex input (a,b) is mapped to sqrt( a*a+b*b ). + +.B im_clip2fmt() +converts the input image to 'ofmt', where ofmt is the new value for +BandFmt. For example, im_clip2fmt(in, out, FMTUSHORT) converts any image to +unsigned short. + +The number of +clipped values (if any), are printed in the standard error output. Before +clipping rounding is carried out if necessary. When converting between real +and complex types with these functions, the imaginary part of the output is +set to zero. When converting from complex to real types, the imaginary part is +ignored. Use im_c2ps(), im_c2real() etc. to get other behaviour. + +Legacy functions are available, called im_clip(), im_clip2c(), im_clip2d(), +im_clip2f(), im_clip2i(), im_clip2s(), im_clip2ui(), im_clip2us(), +im_clip2cm() and im_clip2dcm() which convert the input image to unsigned char, +signed char, double, float, int, short, unsigned int, unsigned short, +complex and double complex respectively. + +.B im_ri2c() +takes as inputs two non-complex images of equal sizes and number of +channels. The output is a complex image with the real part coming from in1 +and the imaginary part coming from in2. Output is float complex (FMTCOMPLEX) +only if none of the inputs is double. If one or both inputs are double the +result is double complex (FMTDPCOMPLEX). + +.B im_c2real() +and +.B im_c2imag() +extract the real or the imaginary part of a +complex image respectively. If input is be float complex or double complex, +then output is float or double respectively. + +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_scale(3). diff --git a/libsrc/conversion/man3/im_clip2c.3 b/libsrc/conversion/man3/im_clip2c.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2c.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2cm.3 b/libsrc/conversion/man3/im_clip2cm.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2cm.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2d.3 b/libsrc/conversion/man3/im_clip2d.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2d.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2dcm.3 b/libsrc/conversion/man3/im_clip2dcm.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2dcm.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2f.3 b/libsrc/conversion/man3/im_clip2f.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2f.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2fmt.3 b/libsrc/conversion/man3/im_clip2fmt.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2fmt.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2i.3 b/libsrc/conversion/man3/im_clip2i.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2i.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2s.3 b/libsrc/conversion/man3/im_clip2s.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2s.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2ui.3 b/libsrc/conversion/man3/im_clip2ui.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2ui.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2us.3 b/libsrc/conversion/man3/im_clip2us.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2us.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_copy.3 b/libsrc/conversion/man3/im_copy.3 new file mode 100644 index 00000000..4e33f68d --- /dev/null +++ b/libsrc/conversion/man3/im_copy.3 @@ -0,0 +1,79 @@ +.TH IM_COPY 3 "11 April 1990" +.SH NAME +im_copy, im_copy_set, im_copy_swap, im_copy_morph \- copy an image +.SH SYNOPSIS +.B #include + +int im_copy(in, out) +.br +IMAGE *in, *out; + +int im_copy_set( in, out, type, xres, yres ) +.br +IMAGE *in, *out; +.br +int type; +.br +float xres, yres; + +int im_copy_swap( in, out ) +.br +IMAGE *in, *out; + +int im_copy_morph( in, out, Bands, BandFmt, Coding ) +.br +IMAGE *in, *out; +.br +int Bands, BandFmt, Coding; + +typedef enum { +.br + IM_ARCH_NATIVE, +.br + IM_ARCH_BYTE_SWAPPED, +.br + IM_ARCH_LSB_FIRST, +.br + IM_ARCH_MSB_FIRST +.br +} im_arch_type; + +int im_copy_from( in, out, architecture ) +.br +IMAGE *in, *out; +.br +im_arch_type architecture; + +.SH DESCRIPTION +.B im_copy(3) +copies the image held by the image descriptor in +and writes the result to the image descriptor out. The input can be of any +size and have any type. Does LABPACK coded images too! + +.B im_copy_set(3) +behaves exactly as +.B im_copy(3), +but lets you set informational fields in the +header on the way through. + +.B im_copy_swap(3) +copies an uncoded image, swapping between SPARC and Intel byte order on the +way. + +.B im_copy_morph(3) +behaves exactly as +.B im_copy(3), +but lets you set fields which affect pixel format +on the way through. + +.B im_copy_from(3) +calls either +.B im_copy(3) +or +.B im_copy_swap(3) +as necessary to copy from the specified architecture. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_extract(3), im_open(3) diff --git a/libsrc/conversion/man3/im_copy_from.3 b/libsrc/conversion/man3/im_copy_from.3 new file mode 100644 index 00000000..8e0c909e --- /dev/null +++ b/libsrc/conversion/man3/im_copy_from.3 @@ -0,0 +1 @@ +.so man3/im_copy.3 diff --git a/libsrc/conversion/man3/im_copy_morph.3 b/libsrc/conversion/man3/im_copy_morph.3 new file mode 100644 index 00000000..8e0c909e --- /dev/null +++ b/libsrc/conversion/man3/im_copy_morph.3 @@ -0,0 +1 @@ +.so man3/im_copy.3 diff --git a/libsrc/conversion/man3/im_copy_set.3 b/libsrc/conversion/man3/im_copy_set.3 new file mode 100644 index 00000000..8e0c909e --- /dev/null +++ b/libsrc/conversion/man3/im_copy_set.3 @@ -0,0 +1 @@ +.so man3/im_copy.3 diff --git a/libsrc/conversion/man3/im_copy_set_meta.3 b/libsrc/conversion/man3/im_copy_set_meta.3 new file mode 100644 index 00000000..8e0c909e --- /dev/null +++ b/libsrc/conversion/man3/im_copy_set_meta.3 @@ -0,0 +1 @@ +.so man3/im_copy.3 diff --git a/libsrc/conversion/man3/im_copy_swap.3 b/libsrc/conversion/man3/im_copy_swap.3 new file mode 100644 index 00000000..8e0c909e --- /dev/null +++ b/libsrc/conversion/man3/im_copy_swap.3 @@ -0,0 +1 @@ +.so man3/im_copy.3 diff --git a/libsrc/conversion/man3/im_csv2vips.3 b/libsrc/conversion/man3/im_csv2vips.3 new file mode 100644 index 00000000..f80aea48 --- /dev/null +++ b/libsrc/conversion/man3/im_csv2vips.3 @@ -0,0 +1,76 @@ +.TH IM_CSV 3 "November 2000" +.SH NAME +im_csv2vips, im_vips2csv \- read and write CSV (comma separated values) files +.SH SYNOPSIS +#include + +int im_csv2vips( const char *filename, IMAGE *out ) + +int im_csv2vips_header( const char *filename, IMAGE *out ); + +int im_vips2csv( IMAGE *in, const char *filename ) + +.SH DESCRIPTION +.B im_csv2vips(3) +reads the CSV (comma separated values) data in filename, and writes the image +out in VIPS format. The output image is always 1 band (monochrome), +IM_BANDFMT_DOUBLE. + +The reader is deliberately rather fussy: it will fail if there are any short +lines, or if the file is too short. It will ignore lines that are too long. + +Read options can be embedded in the filename. The options can be given in any +order and are: + + skip:lines-to-skip + +The number of lines to skip at the start of the file. Default zero. + + whi:whitespace-characters + +The skippable whitespace characters. Default and double quotes ("). +Whitespace characters are always run together. + + sep:separator-characters + +The characters that separate fields. Default ;,. Separators are +never run together. + + line:lines-to-read + +The number of lines to read from the file. Default -1, meaning read to end of +file. + +.B im_csv2vips_header(3) +works exactly as +.B im_csv2vips(3) +but only sets the header of the output image. It is rather slow, since it has +to read the whole input file. + +.B im_vips2csv(3) +writes the VIPS image to the file as ascii text, one line of text per +scanline. Complex numbers are written as "(real,imaginary)" and will need +extra parsing I guess. + +Write options can be embedded in the filename. The options can be given in any +order and are: + + sep:separator-string + +The string to use to separate numbers in the output. The default is "\\t" (tab). + +.SH EXAMPLES + + im_csv2vips( "fred.csv:skip:58,sep:\,,line:3", out ); + +Will read three lines starting at line 59, with space, comma and tab as the +allowed separators. Note that the ',' has to be escaped with a backslash. + + im_vips2csv fred.jpg fred.csv:sep:\t + +Convert a jpeg to CSV, separating numbers with tab characters. + +.SH SEE ALSO +im_read_dmask(3), im_ppm2vips(3) +.SH COPYRIGHT +Hey, you want this? You have it! diff --git a/libsrc/conversion/man3/im_csv2vips_header.3 b/libsrc/conversion/man3/im_csv2vips_header.3 new file mode 100644 index 00000000..e4558b4a --- /dev/null +++ b/libsrc/conversion/man3/im_csv2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_csv2vips.3 diff --git a/libsrc/conversion/man3/im_exr2vips.3 b/libsrc/conversion/man3/im_exr2vips.3 new file mode 100644 index 00000000..2a930078 --- /dev/null +++ b/libsrc/conversion/man3/im_exr2vips.3 @@ -0,0 +1,23 @@ +.TH IM_EXR 3 "3 Jan 2003" +.SH NAME +im_exr2vips, im_exr2vips_header \- convert OpenEXR images to VIPS format +.SH SYNOPSIS +#include + +int im_exr2vips( const char *filename, IMAGE *out ) + +int im_exr2vips_header( const char *filename, IMAGE *out ) + +.SH DESCRIPTION +.B im_exr2vips() +reads the OpenEXR image in filename, and writes the image out +in VIPS format. + +.B im_png2vips_header() +reads just the header information from the OpenEXR file. You can't read any +pixels! + +.SH SEE ALSO +im_isexr(3), im_vips2tiff(3) +.SH COPYRIGHT +Hey, you want this? You have it! diff --git a/libsrc/conversion/man3/im_exr2vips_header.3 b/libsrc/conversion/man3/im_exr2vips_header.3 new file mode 100644 index 00000000..719c91d6 --- /dev/null +++ b/libsrc/conversion/man3/im_exr2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_exr2vips.3 diff --git a/libsrc/conversion/man3/im_extract.3 b/libsrc/conversion/man3/im_extract.3 new file mode 100644 index 00000000..fe920239 --- /dev/null +++ b/libsrc/conversion/man3/im_extract.3 @@ -0,0 +1,60 @@ +.TH IM_EXTRACT 3 "11 April 1990" +.SH NAME +im_extract_areabands, im_extract_bands, im_extract_area \- extract a portion of an image +.SH SYNOPSIS +.B #include + +int im_extract_areabands( IMAGE *in, IMAGE *out, +.br + int left, int top, int width, int height, int band, int nbands ) + +int im_extract_area( IMAGE *in, IMAGE *out, +.br + int left, int top, int width, int height ) + +int im_extract_bands( IMAGE *in, IMAGE *out, +.br + int chsel, int nbands ) + +.SH DESCRIPTION +.B im_extract_areabands(3) +extracts the rectangular portion of the image defined by +.B left, +.B top, +.B width, +and +.B height of image +.B in +and writes the result to image +.B out. +The area must lie entirely within in the image. Selects the set of +.B nbands +bands +starting at band number +.B band +(numbering bands from zero). + +Works for any size image, any number of bands, any type. Works for LABPACK +coded images too! But disallows band extraction in this case. + +.B im_extract_area(3) +is a convenience function which extracts an area from an +image, leaving the bands the same. + +.B im_extract_bands(3) +takes +.B nbands +out of an image, starting from band +.B band. +So, for example, nbands == 2, bands == 1 will form a two band image from an RGB +image, where the two bands are the G and the B bands. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_insert(3), im_lrjoin(3), im_lrmerge(3), im_stats(3), +im_region_region(3). +.SH COPYRIGHT +J. Cupitt, +.SH AUTHOR +J. Cupitt \- 11/04/1990 diff --git a/libsrc/conversion/man3/im_extract_area.3 b/libsrc/conversion/man3/im_extract_area.3 new file mode 100644 index 00000000..09e88df3 --- /dev/null +++ b/libsrc/conversion/man3/im_extract_area.3 @@ -0,0 +1 @@ +.so man3/im_extract.3 diff --git a/libsrc/conversion/man3/im_extract_areabands.3 b/libsrc/conversion/man3/im_extract_areabands.3 new file mode 100644 index 00000000..09e88df3 --- /dev/null +++ b/libsrc/conversion/man3/im_extract_areabands.3 @@ -0,0 +1 @@ +.so man3/im_extract.3 diff --git a/libsrc/conversion/man3/im_extract_bands.3 b/libsrc/conversion/man3/im_extract_bands.3 new file mode 100644 index 00000000..09e88df3 --- /dev/null +++ b/libsrc/conversion/man3/im_extract_bands.3 @@ -0,0 +1 @@ +.so man3/im_extract.3 diff --git a/libsrc/conversion/man3/im_falsecolour.3 b/libsrc/conversion/man3/im_falsecolour.3 new file mode 100644 index 00000000..1549752e --- /dev/null +++ b/libsrc/conversion/man3/im_falsecolour.3 @@ -0,0 +1,23 @@ +.TH IM_FALSECOLOUR 3 "30 October 1992" +.SH NAME +im_falsecolour \- colour a mono image +.SH SYNOPSIS +.B #include + +.B int im_falsecolour(in, out) +.br +.B IMAGE *in, *out; + +.SH DESCRIPTION +Turns a one-band unsigned char image into a three band unsigned char image. +The colour scale is stolen from a PET scan and has red for hot, green for +normal and blue for cold. +.SH RETURN VALUE +0 on success and -1 on error. +.SH SEE ALSO +im_disp2XYZ(3), etc. +.SH COPYRIGHT +.br +National Gallery +.SH AUTHOR +J. Cupitt diff --git a/libsrc/conversion/man3/im_fliphor.3 b/libsrc/conversion/man3/im_fliphor.3 new file mode 100644 index 00000000..bf72dac4 --- /dev/null +++ b/libsrc/conversion/man3/im_fliphor.3 @@ -0,0 +1 @@ +.so man3/im_rot180.3 diff --git a/libsrc/conversion/man3/im_flipver.3 b/libsrc/conversion/man3/im_flipver.3 new file mode 100644 index 00000000..bf72dac4 --- /dev/null +++ b/libsrc/conversion/man3/im_flipver.3 @@ -0,0 +1 @@ +.so man3/im_rot180.3 diff --git a/libsrc/conversion/man3/im_gbandjoin.3 b/libsrc/conversion/man3/im_gbandjoin.3 new file mode 100644 index 00000000..15713fe8 --- /dev/null +++ b/libsrc/conversion/man3/im_gbandjoin.3 @@ -0,0 +1 @@ +.so man3/im_bandjoin.3 diff --git a/libsrc/conversion/man3/im_grid.3 b/libsrc/conversion/man3/im_grid.3 new file mode 100644 index 00000000..f3597c00 --- /dev/null +++ b/libsrc/conversion/man3/im_grid.3 @@ -0,0 +1,42 @@ +.TH IM_GRID 3 "4 August 2005" +.SH NAME +im_grid \- split a vertical image into a grid of smaller images +.SH SYNOPSIS +.B #include + +int +.br +im_grid( IMAGE *in, IMAGE *out, +.br + int tile_height, int across, int down ) +.SH DESCRIPTION +.B im_grid(3) +slices image +.B in +into a set of tiles, each the same width as +.B in +and of height +.B tile_height +and rearranges the tiles into a grid with +.B across +tiles across and +.B down +tiles down. + +It is useful for loading volumetric images (for example, CT or PET scans) +where more than 2 dimensions need to be displayed. + +The current implementation is optimised for the case where image +.B in +is thin and tall and +.B across +and +.B down +are large. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_extract_area(3), im_zoom(3) +.SH COPYRIGHT +Imperial College 2005 diff --git a/libsrc/conversion/man3/im_insert.3 b/libsrc/conversion/man3/im_insert.3 new file mode 100644 index 00000000..7b4e69b6 --- /dev/null +++ b/libsrc/conversion/man3/im_insert.3 @@ -0,0 +1,29 @@ +.TH IM_INSERT 3 "11 April 1990" +.SH NAME +im_insert \- insert one image into another +.SH SYNOPSIS +#include + +int im_insert(in, ins, out, x, y) +.br +IMAGE *in, *insub, *out; +.br +int x, y; +.SH DESCRIPTION +im_insert() +inserts one image into another. ins is inserted into image in at +position x, y relative to the top left hand corner of in. out is made large +enough to hold all of both in and ins. Any areas of out not coming from +either in or ins are set to black (binary 0). If ins overlaps in, +ins +will appear on top of in. Both images must have the same number of +bands and the same BandFmt. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_extract(3), im_lrjoin(3), im_lrmerge(3) +.SH COPYRIGHT +.br +J. Cupitt, +.SH AUTHOR +J. Cupitt \- 11/04/1990 diff --git a/libsrc/conversion/man3/im_istiff.3 b/libsrc/conversion/man3/im_istiff.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/conversion/man3/im_istiff.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/conversion/man3/im_jpeg2vips.3 b/libsrc/conversion/man3/im_jpeg2vips.3 new file mode 100644 index 00000000..8d5d3adb --- /dev/null +++ b/libsrc/conversion/man3/im_jpeg2vips.3 @@ -0,0 +1,70 @@ +.TH IM_JPEG2VIPS 3 "6 June 1994" +.SH NAME +im_jpeg2vips, im_vips2jpeg, im_vips2bufjpeg, im_vips2mimejpeg \- convert JPEG images to and from VIPS format +.SH SYNOPSIS +#include + +int im_jpeg2vips( char *filename, IMAGE *out ) + +int im_jpeg2vips_header( char *filename, IMAGE *out ) + +int im_vips2jpeg( IMAGE *in, char *filename ) + +int im_vips2bufjpeg( IMAGE *in, IMAGE *base, char **obuf, int *olen ) + +int im_vips2mimejpeg( IMAGE *in ) + +.SH DESCRIPTION +.B im_jpeg2vips() +reads the named jpeg file and writes it to the specified +IMAGE. The entire image is read before returning. It will handle 1 and 3 band +8-bit images only. + +Any embedded ICC profiles are ignored: you always just get the RGB from the +file. Instead, the embedded profile will be attached to the image as metadata. +Any EXIF data is also attached as VIPS metadata. + +.B im_jpeg2vips_header() +reads just the header of the JPEG file (including any EXIF data), and sets +the fields in the VIPS image. +You can't read any pixels! This is only useful for printing header +information. + +.B im_vips2jpeg() +writes the IMAGE to filename in JPEG format. It uses the +default settings of the IJG library. + +A compression factor may be encoded in the filename: for example, +"fred.jpg" will write with the default compression factor (75), +"fred.jpg:25" will write with factor 25. + +An ICC profile may also be specified. For example, +"fred.jpg:25,/home/john/srgb.icc" will embed the profile stored in the file +"/home/john/srgb.icc" into the JPEG image. This does not affect the pixels +which are written; just the way they are tagged. + +If no profile is specified in the save string and the VIPS header contains an +ICC profile named IM_META_ICC_NAME ("icc-profile-data"), the +profile from the header will be attached. + +The image is automatically converted to RGB or Monochrome before saving. Any +metadata attached to the image is saved as EXIF, if possible. + +.B im_vips2bufjpeg() +returns the compressed image in a memory buffer. The buffer +is allocated for you, local to IMAGE descriptor +.B base. +The size of the +allocated buffer is returned in the +.B olen +parameter. You are responsible for +freeing the buffer. The buffer is only allocated if the function returns +successfully. + +.B im_vips2mimejpeg() +writes the image to stdout as a MIME image/jpeg type. It +outputs Content-Length and Content-Type fields making the result suitable for +sending to a web browser. + +.SH SEE ALSO +im_isjpeg(3). diff --git a/libsrc/conversion/man3/im_jpeg2vips_header.3 b/libsrc/conversion/man3/im_jpeg2vips_header.3 new file mode 100644 index 00000000..d7fbd38d --- /dev/null +++ b/libsrc/conversion/man3/im_jpeg2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_jpeg2vips.3 diff --git a/libsrc/conversion/man3/im_lrjoin.3 b/libsrc/conversion/man3/im_lrjoin.3 new file mode 100644 index 00000000..83de90c3 --- /dev/null +++ b/libsrc/conversion/man3/im_lrjoin.3 @@ -0,0 +1,38 @@ +.TH JOIN 3 "25 April 1991" +.SH NAME +im_lrjoin, im_tbjoin \- join two images into one +.SH SYNOPSIS +#include + +int im_lrjoin( IMAGE *im1, IMAGE *im2, IMAGE *imout ) + +int im_tbjoin( IMAGE *im1, IMAGE *im2, IMAGE *imout ) +.SH DESCRIPTION +These functions join two image left-right or top-bottom. Both input images +should have the same no of bands and the same BandFmt. Output has the same no +of bands and BandFmt as input. Only the history of the first image is kept by +the output image. + +im_lrjoin() joins two images held by image descriptors im1 and im2 and writes +the resultant byte image on the image descriptor imout. The number of the +extracted channels are identical for all images. The Xsize of imout is the +sum of the Xsizes of im1 and im2; the Ysize of imout is the min of the Ysizes +of im1 and im2. When joining im1 is on the left side of imout and im2 is on +the right side of imout. + +im_tbjoin() joins two images held by image descriptors im1 and im2 and writes +the resultant byte image on the image descriptor imout. The number of the +extracted channels are identical for all images. The Xsize of imout is the +min of the Xsizes of im1 and im2; the Ysize of imout is the sum of the Ysizes +of im1 and im2. When joining im1 is on the top side of imout and im2 is on +the bottom side of imout. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_extract(3), im_lrmerge(3), im_insert(3) +.SH COPYRIGHT +.br +K. Martinez, N. Dessipris, +.SH AUTHOR +K. Martinez and N. Dessipris \- 25/04/1991 diff --git a/libsrc/conversion/man3/im_magick2vips.3 b/libsrc/conversion/man3/im_magick2vips.3 new file mode 100644 index 00000000..f16166ea --- /dev/null +++ b/libsrc/conversion/man3/im_magick2vips.3 @@ -0,0 +1,25 @@ +.TH IM_MAGICK 3 "January 2003" +.SH NAME +im_magick2vips, im_magick2vips_header \- read images with the libMagick +library +.SH SYNOPSIS +#include + +int im_magick2vips( const char *filename, IMAGE *out ) + +int im_magick2vips_header( const char *filename, IMAGE *out ) + +.SH DESCRIPTION +.B im_magick2vips() +reads the image in filename using libMagick, and writes the image out +in VIPS format. It should be able to read any ImageMagick image, including +the float and double formats. + +.B im_magick2vips_header() +reads the just the header into the VIPS image. You can't read any +pixels! + +.SH SEE ALSO +im_ismagick(3) +.SH COPYRIGHT +Hey, you want this? You have it! diff --git a/libsrc/conversion/man3/im_magick2vips_header.3 b/libsrc/conversion/man3/im_magick2vips_header.3 new file mode 100644 index 00000000..651b1cf2 --- /dev/null +++ b/libsrc/conversion/man3/im_magick2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_magick2vips.3 diff --git a/libsrc/conversion/man3/im_mask2vips.3 b/libsrc/conversion/man3/im_mask2vips.3 new file mode 100644 index 00000000..48cca147 --- /dev/null +++ b/libsrc/conversion/man3/im_mask2vips.3 @@ -0,0 +1 @@ +.so man3/im_vips2mask.3 diff --git a/libsrc/conversion/man3/im_msb.3 b/libsrc/conversion/man3/im_msb.3 new file mode 100644 index 00000000..35e45abc --- /dev/null +++ b/libsrc/conversion/man3/im_msb.3 @@ -0,0 +1,63 @@ +.TH IM_MSB 3 "06 May 2006" +.SH NAME + im_msb, im_msb_band \- Convert to uchar by discarding bits +.SH SYNOPSIS +.nf +.B #include +.sp +.BI "int im_msb( IMAGE " "*in" ", IMAGE " "*out" " ); +.br + +.BI "int im_msb_band( IMAGE " "*in" ", IMAGE " "*out" ", int " "band" " ); +.fi +.SH DESCRIPTION +.B im_msb(3) +converts char, short, or int images (including LABQ coded ones) into unsigned +char images, very quickly, by discarding the lower order bits. Once scaled to +char, signed values are converted to unsigned by adding 128. +.PP +For a signed short (16 bit) image, +.PP +im_msb( in, out ); +.PP +is equivalent to: +.PP +im_lintra( (1.0/256.0), in, 128.0, temp ); +.br +im_clip2fmt( temp, out, IM_BANDFMT_UCHAR ); +.PP +but much faster. +.PP +For any image which uses the whole range of values for its band format, +.PP +im_msb( in, out ); +.PP +is equivalent to: +.PP +im_scale( in, out ); +.PP +but a great deal faster, and without evaluating the input twice. +.PP +.B im_msb_band(3) +is as +.BR im_msb(3) , +except that all but one of the bands are also discarded. +.PP +im_msb_band( in, out, i ); +.PP +is equivalent to: +.PP +im_msb( in, temp ); +.br +im_extract_bands( temp, out, i, 1 ); +.PP +but again, faster. +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_lintra(3), im_scale(3), im_clip(3) +.SH COPYRIGHT +.br +Copyright 2006, The Nottingham Trent University. +.SH AUTHOR +Tom Vajzovic diff --git a/libsrc/conversion/man3/im_msb_band.3 b/libsrc/conversion/man3/im_msb_band.3 new file mode 100644 index 00000000..7c697b04 --- /dev/null +++ b/libsrc/conversion/man3/im_msb_band.3 @@ -0,0 +1 @@ +.so man3/im_msb.3 diff --git a/libsrc/conversion/man3/im_png2vips.3 b/libsrc/conversion/man3/im_png2vips.3 new file mode 100644 index 00000000..9b7ef388 --- /dev/null +++ b/libsrc/conversion/man3/im_png2vips.3 @@ -0,0 +1,56 @@ +.TH IM_PNG 3 "3 Jan 2003" +.SH NAME +im_png2vips, im_png2vips_header, im_png2vips \- convert PNG images to and from VIPS format +.SH SYNOPSIS +#include + +int im_png2vips( const char *filename, IMAGE *out ) + +int im_png2vips_header( const char *filename, IMAGE *out ) + +int im_vips2png( IMAGE *in, const char *filename ) + +.SH DESCRIPTION +.B im_png2vips() +reads the png image in filename, and writes the image out +in VIPS format. + +.B im_png2vips_header() +reads just the header information from the PNG file. You can't read any +pixels! + +.B im_vips2png() +reads the image in and writes a PNG file to the specified +filename. The filename may include an optional mode string, following a ':' +character, which you can use to specify compression and interlace. + +The mode string has the following syntax: + + , + +where is an integer between 0 and 9 specifying the amount of +compression (6 is the default), and is 0 for no interlace (this is +the default) and 1 for ADAM7 interlace. + +Beware that writing an interlaced image is potentially up to 7 times slower +than writing a non-interlaced image. + +The image is automatically converted to RGB or Monochrome (with an optional +alpha channel) before saving. + +.SH EXAMPLES + +The call: + + im_vips2png fred.v fred.png + +Writes a compression 6, uninterlaced PNG image. + + im_vips2png fred.v fred.tif:,1 + +Writes an interlaced image. + +.SH SEE ALSO +im_ispng(3), im_vips2tiff(3) +.SH COPYRIGHT +Hey, you want this? You have it! diff --git a/libsrc/conversion/man3/im_png2vips_header.3 b/libsrc/conversion/man3/im_png2vips_header.3 new file mode 100644 index 00000000..4a4b4cd7 --- /dev/null +++ b/libsrc/conversion/man3/im_png2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_png2vips.3 diff --git a/libsrc/conversion/man3/im_ppm2vips.3 b/libsrc/conversion/man3/im_ppm2vips.3 new file mode 100644 index 00000000..8ade759e --- /dev/null +++ b/libsrc/conversion/man3/im_ppm2vips.3 @@ -0,0 +1,38 @@ +.TH IM_PPM 3 "November 2000" +.SH NAME +im_ppm2vips, im_ppm2vips_header, im_vips2ppm \- convert PPM/PGM/PBM images to and from VIPS format +.SH SYNOPSIS +#include + +int im_ppm2vips( const char *filename, IMAGE *out ) + +int im_ppm2vips_header( const char *filename, IMAGE *out ) + +int im_vips2ppm( IMAGE *in, const char *filename ) + +.SH DESCRIPTION +.B im_ppm2vips() +reads the PPM/PGM/PBM image in filename, and writes the image out +in VIPS format. It can read 1, 8, 16 and 32 bit images, colour or monochrome, +stored in binary or in ASCII. One bit images become 8 bit VIPS images, with 0 +and 255 for 0 and 1. + +.B im_ppm2vips_header() +reads the just the header of the image into the VIPS image. You can't read any +pixels! + +.B im_vips2ppm() +writes the VIPS image to the named file in PPM format. It can write 8, 16 or +32 bit integer images, colour or monochrome, stored as binary or ASCII. Images +of more than 8 bits can only be stored in ASCII. + +The storage format is indicated by a filename extension. + + im_vips2ppm( im, "fred.ppm:ascii" ) + +will write to "fred.ppm" in ascii format. The default is binary. + +.SH SEE ALSO +im_isppm(3) +.SH COPYRIGHT +Hey, you want this? You have it! diff --git a/libsrc/conversion/man3/im_ppm2vips_header.3 b/libsrc/conversion/man3/im_ppm2vips_header.3 new file mode 100644 index 00000000..de0d1282 --- /dev/null +++ b/libsrc/conversion/man3/im_ppm2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_ppm2vips.3 diff --git a/libsrc/conversion/man3/im_print.3 b/libsrc/conversion/man3/im_print.3 new file mode 100644 index 00000000..e1ebc80f --- /dev/null +++ b/libsrc/conversion/man3/im_print.3 @@ -0,0 +1,21 @@ +.TH IM_PRINT 3 "January 2002" +.SH NAME +im_print \- print a string to stdout +.SH SYNOPSIS +#include + +int +.br +im_print( const char *message ) + +.SH DESCRIPTION +.B im_print() +prints the message to stdout, with a newline. Sometimes useful for debugging +language bindings. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_system(3) +.SH COPYRIGHT +2002 The National Gallery diff --git a/libsrc/conversion/man3/im_raw2vips.3 b/libsrc/conversion/man3/im_raw2vips.3 new file mode 100644 index 00000000..e50d822a --- /dev/null +++ b/libsrc/conversion/man3/im_raw2vips.3 @@ -0,0 +1,30 @@ +.TH IM_RAW2VIPS 3 "4 August 2005" +.SH NAME +im_raw2vips \- wrap a raw binary file inside an IMAGE descriptor +.SH SYNOPSIS +.B #include + +int +.br +im_raw2vips( const char *filename, IMAGE *out, +.br + int width, int height, int bpp, int offset ) + +.SH DESCRIPTION +.B im_raw2vips(3) +mmaps the file named, setting image +.B out +so that access to that image will read from the file. +The parameters specify the image width, height, bytes per pixel and offset +in bytes from the start of the file. + +Use functions like +.B im_copy_morph(3) +to set the pixel type, byte ordering and so on. + +.SH RETURN VALUE +The functions return NULL on error. +.SH SEE ALSO +im_copy_morph(3), im_copy_swap(3). +.SH COPYRIGHT +Imperial College, 2005 diff --git a/libsrc/conversion/man3/im_recomb.3 b/libsrc/conversion/man3/im_recomb.3 new file mode 100644 index 00000000..c0481d11 --- /dev/null +++ b/libsrc/conversion/man3/im_recomb.3 @@ -0,0 +1,36 @@ +.TH IM_RECOMB 3 "11 April 1990" +.SH NAME +im_recomb \- matrix recombination of image +.SH SYNOPSIS +#include + +int im_recomb(imagein, imageout, mat) +.br +IMAGE *imagein, *imageout; +.br +DOUBLEMASK *mat; + +.SH DESCRIPTION +im_recomb() recombines the elements of an m band image to form an n band image +using mat, an m by n matrix of floating point numbers. + +It calculates + + A = B x C + +where A is an n band output image, C is an m band input image and B +is an m by n matrix of floats. Can be used with a 3 by 3 matrix to perform +simple colour space transforms; 7 by 30 matrix to shrink 3rd order +development of 3 filter system to XYZ etc. + +The output type is float unless the input type is double, in which case the +output type is double. It does not work for complex image types. All +intermediates are calculated as double. Note that the width of the matrix +should be equal to the number of bands in the input image. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_read_dmask(3), im_matinv(3), im_lintra(3) +.SH COPYRIGHT +National Gallery and Birkbeck College, 1990 - 1996 diff --git a/libsrc/conversion/man3/im_replicate.3 b/libsrc/conversion/man3/im_replicate.3 new file mode 100644 index 00000000..d05a7817 --- /dev/null +++ b/libsrc/conversion/man3/im_replicate.3 @@ -0,0 +1,31 @@ +.TH IM_REPLICATE 3 "6 October 2003" +.SH NAME +im_replicate \- replicate an image horizontally and vertically +.SH SYNOPSIS +.B #include + +int +.br +im_replicate( IMAGE *in, IMAGE *out, +.br + int across, int down ) +.SH DESCRIPTION +.B im_replicate(3) +repeats image +.B in +across and down a fixed number of times to make a new larger image. + +The current implementation is optimised for the case where image +.B in +is small and +.B across +and +.B down +are large. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_extract_area(3), im_zoom(3) +.SH COPYRIGHT +National Gallery 2003 diff --git a/libsrc/conversion/man3/im_ri2c.3 b/libsrc/conversion/man3/im_ri2c.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_ri2c.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_rightshift_size.3 b/libsrc/conversion/man3/im_rightshift_size.3 new file mode 100644 index 00000000..52b6782f --- /dev/null +++ b/libsrc/conversion/man3/im_rightshift_size.3 @@ -0,0 +1,30 @@ +.TH IM_RIGHTSHIFT_SIZE 3 "12 August 2006" +.SH NAME + im_rightshift_size \- Decrease size by a power-of-two factor +.SH SYNOPSIS +.nf +.B #include +.sp +.BI "int im_rightshift_size( IMAGE " "*in" ", IMAGE " "*out" ", int " "xshift" ", int " "yshift" ", int " "band_fmt" " ); +.fi +.SH DESCRIPTION +.B im_rightshift_size(3) +decreases the size of an integer type image by a power-of-two factor, very quickly, by summing the values of +adjacent pixels. The sum is shifted to produce output of the specified band format. +.IR xshift " and "yshift +are positive and give the base-two logarithms of the scale factors. +.PP +If the input image is a signed type, then +.I band_fmt +must be one of IM_BANDFMT_CHAR, IM_BANDFMT_SHORT or IM_BANDFMT_INT. If it is an unsigned type, then +.I band_fmt +must be one of IM_BANDFMT_UCHAR, IM_BANDFMT_USHORT or IM_BANDFMT_UINT. +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_affine(3) +.SH COPYRIGHT +.br +Copyright 2006, Tom Vajzovic. +.SH AUTHOR +Tom Vajzovic diff --git a/libsrc/conversion/man3/im_rot180.3 b/libsrc/conversion/man3/im_rot180.3 new file mode 100644 index 00000000..94fba66f --- /dev/null +++ b/libsrc/conversion/man3/im_rot180.3 @@ -0,0 +1,38 @@ +.TH IM_ROTATION 3 "5 December 1991" +.SH NAME +im_rot180, im_rot270, im_rot90, im_fliphor, im_flipver \- rotate and flip +an image +.SH SYNOPSIS +.B #include + +.B int im_rot180(in, out) +.br +.B IMAGE *in, *out; + +.B int im_rot270(in, out) +.br +.B IMAGE *in, *out; + +.B int im_rot90(in, out) +.br +.B IMAGE *in, *out; + +.B int im_fliphor(in, out) +.br +.B IMAGE *in, *out; + +.B int im_flipver(in, out) +.br +.B IMAGE *in, *out; +.SH DESCRIPTION +im_rot180(), im_rot270() and im_rot90(), rotate the input image in by 180, +270 and 90 degrees clockwise. im_fliphor() and im_flipver(), flip in +horizontally and vertically respectively. All functions work for any size +image, any type and any number of bands. All also work for LABPACK coded +images. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE\ ALSO +rot180(1) +.SH AUTHOR +N. Dessipris \- 5/12/1991 diff --git a/libsrc/conversion/man3/im_rot270.3 b/libsrc/conversion/man3/im_rot270.3 new file mode 100644 index 00000000..bf72dac4 --- /dev/null +++ b/libsrc/conversion/man3/im_rot270.3 @@ -0,0 +1 @@ +.so man3/im_rot180.3 diff --git a/libsrc/conversion/man3/im_rot90.3 b/libsrc/conversion/man3/im_rot90.3 new file mode 100644 index 00000000..bf72dac4 --- /dev/null +++ b/libsrc/conversion/man3/im_rot90.3 @@ -0,0 +1 @@ +.so man3/im_rot180.3 diff --git a/libsrc/conversion/man3/im_scale.3 b/libsrc/conversion/man3/im_scale.3 new file mode 100644 index 00000000..29915b4b --- /dev/null +++ b/libsrc/conversion/man3/im_scale.3 @@ -0,0 +1,40 @@ +.TH CONVERSIONS 3 "28 June 1990" +.SH NAME +im_scale, im_scaleps \- scale an image to unsigned char image +.SH SYNOPSIS +#include + +int im_scale(in, out) +.br +IMAGE *in, *out; + +int im_scaleps(in, out) +.br +IMAGE *in, *out; +.SH DESCRIPTION +These functions scale a non-complex image to a displayable +byte (unsigned char) image. + +im_scale() scales the image held by image descriptor to a byte (unsigned char) +image and writes the result on the image descriptor out. Sizes and the number +of the bands of out are identical to those of input. +Input image should be non complex and can have any number of channels. +The output is scaled between 0 and 255; if the image is multiband the maximum +value of all channels is set to 255 and the minimum to 0. In all cases, +rounding is performed by adding .5 to the scaled values. + +.B im_scaleps() +scales to 0 - 255 by mapping each pixel through the equation + + log10(1.0 + pow(x, 0.25)) + +and then multiplying by a factor so that the image maximum is 255. + +This transformation highlights both low and high spatial +frequencies in the power spectrum. +The images produced by this non-linear transformation are only +for display and not for further processing. +.SH RETURN VALUE +Each function returns 0 on success and -1 on error. +.SH SEE ALSO +im_any2c(3), im_c2amph(3), im_clip(3) diff --git a/libsrc/conversion/man3/im_scaleps.3 b/libsrc/conversion/man3/im_scaleps.3 new file mode 100644 index 00000000..ed46d616 --- /dev/null +++ b/libsrc/conversion/man3/im_scaleps.3 @@ -0,0 +1 @@ +.so man3/im_scale.3 diff --git a/libsrc/conversion/man3/im_slice.3 b/libsrc/conversion/man3/im_slice.3 new file mode 100644 index 00000000..2e4675e8 --- /dev/null +++ b/libsrc/conversion/man3/im_slice.3 @@ -0,0 +1 @@ +.so man3/im_thresh.3 diff --git a/libsrc/conversion/man3/im_subsample.3 b/libsrc/conversion/man3/im_subsample.3 new file mode 100644 index 00000000..c8cefe3b --- /dev/null +++ b/libsrc/conversion/man3/im_subsample.3 @@ -0,0 +1,24 @@ +.TH IM_SUBSAMPLE 3 "19 Aug 1996" +.SH NAME +im_subsample \- subsample image by integer factor +.SH SYNOPSIS +#include + +int im_subsample(in, out, xsub, ysub) +.br +IMAGE *in, *out; +.br +int xsub, ysub; + +.SH DESCRIPTION +.B im_subsample() +sub-samples image in by an x and y integer factor. It is much faster than +im_shrink(), especially for large partial images. + +It works for any image type. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_shrink(3), im_similarity(3), im_zoom(3) +.SH COPYRIGHT +1989-1996 The National Gallery and Birkbeck College diff --git a/libsrc/conversion/man3/im_system.3 b/libsrc/conversion/man3/im_system.3 new file mode 100644 index 00000000..d0adc95f --- /dev/null +++ b/libsrc/conversion/man3/im_system.3 @@ -0,0 +1,48 @@ +.TH IM_SYSTEM 3 "7 Mar 2000" +.SH NAME +im_system \- run a command on an image +.SH SYNOPSIS +#include + +int +im_system(im, cmd, out) +.br +IMAGE *im; +.br +const char *cmd; +.br +char **out; + +.SH DESCRIPTION +.B im_system() +runs a command on an image, returning the command's output as a string. This +string should be freed with +.B im_free() +when you've finished with it. + +The command is executed with the +.B system(3) +call; the first '%s' in the command being substituted for a filename. + +For example: + +im_system( im, "vips2dj %s | lpr", &result ) + +will run the command vips2dj(1) on the image, piping the result to the +printer. + +If the IMAGE is a file on disc, then the filename will be the name of the real +file. If the image is in memory, or the result of a computation, then a new +file is created in the temporary area called something like "vips_XXXXXX", and +that filename given to the command. The file is deleted when the command +finishes. + +The environment variable TMPDIR can be used to set the temporary directory. If +it is not set, it defaults to "/tmp". + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +system(3), vips2dj(1) +.SH COPYRIGHT +2000 The National Gallery and Birkbeck College diff --git a/libsrc/conversion/man3/im_tbjoin.3 b/libsrc/conversion/man3/im_tbjoin.3 new file mode 100644 index 00000000..82c54e22 --- /dev/null +++ b/libsrc/conversion/man3/im_tbjoin.3 @@ -0,0 +1 @@ +.so man3/im_lrjoin.3 diff --git a/libsrc/conversion/man3/im_text.3 b/libsrc/conversion/man3/im_text.3 new file mode 100644 index 00000000..c1b7be46 --- /dev/null +++ b/libsrc/conversion/man3/im_text.3 @@ -0,0 +1,47 @@ +.TH IM_TEXT 3 "20 May 2004" +.SH NAME +im_text \- render a utf-8 text string into an image +.SH SYNOPSIS +.B #include + +.B int im_text( out, text, font, width, alignment, dpi ) +.br +.B IMAGE *out; +.br +.B const char *text; +.br +.B const char *font; +.br +.B int width; +.br +.B int alignment; +.br +.B int dpi; + +.SH DESCRIPTION +.B im_text(3) +makes an image containing the text string rendered as a bitmap with Pango. +The result is a one band 8 bit image with 0 - 255 as black to white. The +string may contain Pango markup, for example "TheGuardian". + +Fonts are specified pango-style as "family style size", for example "sans 12" +or "times italic 14". + +The +.B width +parameter, if greater than zero, gives the line width to wrap at. + +.B alignment +can be 0, 1 or 2 for right, centre and left alignment. + +.B dpi +is the resolution to render the text at. 300 is good for print, 100 for +displays. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_plotmask(3), im_insertplace(3) +.SH COPYRIGHT +.br +National Gallery 2004 diff --git a/libsrc/conversion/man3/im_thresh.3 b/libsrc/conversion/man3/im_thresh.3 new file mode 100644 index 00000000..345fdb57 --- /dev/null +++ b/libsrc/conversion/man3/im_thresh.3 @@ -0,0 +1,46 @@ +.TH IM_THRESH 3 "26 April 1991" +.SH NAME +im_thresh, im_slice \- threshold an image +.SH SYNOPSIS +#include + +int im_thresh(in, out, threshold) +.br +IMAGE *in, *out; +.br +double threshold; + +int im_slice(in, out, threshold1, threshold2) +.br +IMAGE *in, *out; +.br +double threshold1, threshold2; +.SH DESCRIPTION +These functions have been replaced with the relational and boolean +packages - see +.B im_lessconst() +and +.B im_and() +for much better ways of doing this. + +These functions operate on any non-complex input. The output image is a +unsigned char image with the same sizes and the same number of channels as +input. + +im_slice() thresholds the image held by image descriptor in and writes the +result on the image descriptor out. Output is a byte image with values less +than threshold1) set to 0, values in [threshold1, threshold2) set to 128 and +values greater than threshold2 set to 255 (x in range [a,b) means a<=x + +int im_tiff2vips( const char *filename, IMAGE *out ) + +int im_tiff2vips_header( const char *filename, IMAGE *out ) + +int im_vips2tiff( IMAGE *in, const char *filename ) + +.SH DESCRIPTION +.B im_tiff2vips(3) +reads the tiff image in filename, and writes the image out +in VIPS format. It is a full baseline TIFF 6 reader, with extensions for +tiled images, multipage images, LAB colour space, pyramidal images and +JPEG compression. + +You can embed options in the filename. They have the form: + + filename.tif: + +.B page-number +lets you read a particular page out of a multipage TIFF file. For +example: + + "fred.tif:12" + +will read page 12 (numbering from page zero) from the TIFF image. + +Use +.B im_istifftiled(3) +and +.B im_istiffpyramid(3) +to find the TIFF image type before calling. + +.B im_tiff2vips_header(3) +reads just the header information from the TIFF file. You can't read any +pixels! + +.B im_vips2tiff(3) +reads the image in and writes a TIFF file to the specified +filename. The filename may include an optional mode string, following a ':' +character. For example, "fred.tif" would write a default TIFF file (flat, +strips, no compression). Writing to "fred.tif:deflate,tile:64x64" would write a +ZIP-coded image, split into 64 by 64 pixel tiles. + +The mode string has the following syntax: + + ,,,,, + +where is one of: + + "none" - no compression + "jpeg" - JPEG compression + "deflate" - ZIP (deflate) compression + "packbits" - TIFF packbits compression + "ccittfax4" - CCITT Group 4 fax encoding + "lzw" - Lempel-Ziv compression + +"jpeg" compression can be followed by a ":" character and a JPEG quality +level; "lzw" and "deflate" can be followed by a ":" and predictor value. The +default compression type is "none", the default JPEG quality factor is 75. + +Predictor is not set by default. There are three predictor values recognised +at the moment (2007, July): 1 is no prediction, 2 is a horizontal differencing +and 3 is a floating point predictor. Refer to the libtiff specifications for +further discussion of various predictors. In short, predictor helps to better +compress image, especially in case of digital photos or scanned images and bit +depths > 8. Try it to find whether it works for your images. + +JPEG compression is a good lossy compressor for photographs, packbits is good +for 1-bit images, and deflate is the best lossless compression TIFF can do. +LZW has patent problems and is no longer recommended. + + is one of: + + "strip" - strip layout + "tile" - tiled layout + +"tile" layout can be followed by a ":" character and the horizontal and +vertical tile size, separated by a "x" character. The default layout is +"strip", and the default tile size is 128 by 128 pixels. + + is one of: + + "flat" - single image + "pyramid" - many images arranged in a pyramid + +The default multi-res mode is "flat". + + is one of: + + "manybit" - don't bit-reduce images + "onebit" - one band 8 bit images are saved as 1 bit + +The default format is "multibit". + + is one of: + + "res_cm" - output resolution unit is pixels per centimetre + "res_inch" - output resolution unit is pixels per inch + +The default format is "res_cm". The unit can optionally be followed by a +":" character and the horizontal and vertical resolution, separated by a "x" +character. You can have a single number with no "x" and set the horizontal +and vertical resolutions together. The default unit is cm, and the default +resolution is taken from the VIPS header. + + is the filename of an ICC profile to embed in the TIFF file + +The TIFF reader and writer are based on Sam Leffler's TIFF library, and the IJG +JPEG coder. + +.SH EXAMPLES + +The call: + + im_vips2tiff fred.v fred.tif + +Writes a striped, uncompressed TIFF image. Almost anything should be able to +read this. + + im_vips2tiff fred.v fred.tif:jpeg,tile,pyramid + +Writes a tiled JPEG pyramid image. Although VIPS tries to follow the TIFF +specification carefully, you may have trouble reading this on any system other +than VIPS. + + im_vips2tiff fred.v fred.tif:jpeg:25,tile:64x64 + +Writes a highly compressed JPEG image, with a tile size of 64 by 64 pixels. + + im_vips2tiff fred.v fred.tif:,tile + +Writes an uncompressed tiled image. + + im_vips2tiff fred.v fred.tif:packbits,tile,,onebit + +Writes a tiled one bit TIFF image (provided fred.v is a one band 8 bit image) +compressed with packbits. + + im_vips2tiff fred.v fred.tif:,,,,res_inch:300 + +Writes fred.v as a tiff file, with the resolution in the tiff header set to +300 dpi. + +.SH SEE ALSO +im_istiff(3), im_istifftiled(3), im_istiffpyramid(3) +.SH COPYRIGHT +Hey, you want this? You have it! diff --git a/libsrc/conversion/man3/im_tiff2vips_header.3 b/libsrc/conversion/man3/im_tiff2vips_header.3 new file mode 100644 index 00000000..78ab0c6a --- /dev/null +++ b/libsrc/conversion/man3/im_tiff2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_tiff2vips.3 diff --git a/libsrc/conversion/man3/im_tile_cache.3 b/libsrc/conversion/man3/im_tile_cache.3 new file mode 100644 index 00000000..181c253b --- /dev/null +++ b/libsrc/conversion/man3/im_tile_cache.3 @@ -0,0 +1,45 @@ +.TH IM_TILE_CACHE 3 "19 May 2006" +.SH NAME +im_tile_cache \- cache an image, tilewise +.SH SYNOPSIS +#include + +int +.br +im_tile_cache( IMAGE *in, IMAGE *out, +.br + int tile_width, int tile_height, int max_tiles ); + +.SH DESCRIPTION +.B tile_cache(3) +behaves rather like +.B im_copy(3) +between images +.B in +and +.B out, +except that it keeps a cache of computed pixels. This cache is made of up to +.B max_tiles +tiles (a value of -1 for +.B max +means any number of tiles), and each tile is of size +.B tile_width +by +.B tile_height +pixels. Each cache tile is made with a single call to +.B im_prepare(3). + +This is a lower-level operation than +.B im_cache(3) +since it does no subdivision. It is suitable for caching the output of +operations like +.B im_exr2vips(3) +on tiled images. + +.SH RETURN VALUE +The function returns 0 on success, and non-zero on error, setting +im_error(). +.SH SEE ALSO +im_prepare(3) +.SH AUTHOR +J Cupitt, 2006 diff --git a/libsrc/conversion/man3/im_vips2bufjpeg.3 b/libsrc/conversion/man3/im_vips2bufjpeg.3 new file mode 100644 index 00000000..d7fbd38d --- /dev/null +++ b/libsrc/conversion/man3/im_vips2bufjpeg.3 @@ -0,0 +1 @@ +.so man3/im_jpeg2vips.3 diff --git a/libsrc/conversion/man3/im_vips2csv.3 b/libsrc/conversion/man3/im_vips2csv.3 new file mode 100644 index 00000000..e4558b4a --- /dev/null +++ b/libsrc/conversion/man3/im_vips2csv.3 @@ -0,0 +1 @@ +.so man3/im_csv2vips.3 diff --git a/libsrc/conversion/man3/im_vips2jpeg.3 b/libsrc/conversion/man3/im_vips2jpeg.3 new file mode 100644 index 00000000..d7fbd38d --- /dev/null +++ b/libsrc/conversion/man3/im_vips2jpeg.3 @@ -0,0 +1 @@ +.so man3/im_jpeg2vips.3 diff --git a/libsrc/conversion/man3/im_vips2mask.3 b/libsrc/conversion/man3/im_vips2mask.3 new file mode 100644 index 00000000..9be32f55 --- /dev/null +++ b/libsrc/conversion/man3/im_vips2mask.3 @@ -0,0 +1,26 @@ +.TH IM_VIPS2MASK 3 "6 June 1994" +.SH NAME +im_vips2mask, im_mask2vips \- convert between masks and images +.SH SYNOPSIS +.B #include + +DOUBLEMASK *im_vips2mask( IMAGE *in, char *out_name ) + +int im_mask2vips( DOUBLEMASK *in, IMAGE *out ) +.SH DESCRIPTION +.B im_vips2mask(3) +returns a DOUBLEMASK of name out_name containing the data from IMAGE in. +Images must have either one band, a width of 1, or a height of 1. +Returns NULL on error, and a new mask on success. +The scale field is set to 1.0 and the offset to 0.0. + +.B im_mask2vips(3) +writes the doubles held in mask in to the IMAGE out. It creates a one-band +image of the same size as in. It returns 0 on success and -1 on error. The +scale and offset fields are ignored. + +.SH SEE ALSO +im_openin(3), im_openout(3), im_setbuf(3), edvips(3), im_create_dmask(3). +.SH COPYRIGHT +.br +National Gallery, 1994 diff --git a/libsrc/conversion/man3/im_vips2mimejpeg.3 b/libsrc/conversion/man3/im_vips2mimejpeg.3 new file mode 100644 index 00000000..d7fbd38d --- /dev/null +++ b/libsrc/conversion/man3/im_vips2mimejpeg.3 @@ -0,0 +1 @@ +.so man3/im_jpeg2vips.3 diff --git a/libsrc/conversion/man3/im_vips2png.3 b/libsrc/conversion/man3/im_vips2png.3 new file mode 100644 index 00000000..4a4b4cd7 --- /dev/null +++ b/libsrc/conversion/man3/im_vips2png.3 @@ -0,0 +1 @@ +.so man3/im_png2vips.3 diff --git a/libsrc/conversion/man3/im_vips2ppm.3 b/libsrc/conversion/man3/im_vips2ppm.3 new file mode 100644 index 00000000..de0d1282 --- /dev/null +++ b/libsrc/conversion/man3/im_vips2ppm.3 @@ -0,0 +1 @@ +.so man3/im_ppm2vips.3 diff --git a/libsrc/conversion/man3/im_vips2tiff.3 b/libsrc/conversion/man3/im_vips2tiff.3 new file mode 100644 index 00000000..78ab0c6a --- /dev/null +++ b/libsrc/conversion/man3/im_vips2tiff.3 @@ -0,0 +1 @@ +.so man3/im_tiff2vips.3 diff --git a/libsrc/conversion/man3/im_zoom.3 b/libsrc/conversion/man3/im_zoom.3 new file mode 100644 index 00000000..440fdd84 --- /dev/null +++ b/libsrc/conversion/man3/im_zoom.3 @@ -0,0 +1,23 @@ +.TH IM_ZOOM 3 "11 April 1990" +.SH NAME +im_zoom \- zoom an image +.SH SYNOPSIS +.B #include + +.B int im_zoom(in, out, xfac, yfac) +.br +.B IMAGE *in, *out; +.B int xfac, yfac; + +.SH DESCRIPTION +.B im_zoom() +zooms (ie. nearest neighbour upsampling) the image held by the image +descriptor in by factors xfac and yfac and writes the result to the image +descriptor out. Works for any type of image, even LABPACK. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_copy(3), im_affine(3). +.SH COPYRIGHT +National Gallery, 1994. diff --git a/libsrc/convolution/Makefile.am b/libsrc/convolution/Makefile.am new file mode 100644 index 00000000..fb6e4f54 --- /dev/null +++ b/libsrc/convolution/Makefile.am @@ -0,0 +1,33 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libconvolution.la + +libconvolution_la_SOURCES = \ + rotmask.c \ + rw_mask.c \ + convol_dispatch.c \ + im_addgnoise.c \ + im_compass.c \ + im_conv.c \ + im_convf.c \ + im_convsep.c \ + im_convsepf.c \ + im_convsub.c \ + im_contrast_surface.c \ + im_embed.c \ + im_fastcor.c \ + im_gaussmasks.c \ + im_gaussnoise.c \ + im_gradcor.c \ + im_logmasks.c \ + im_rank_image.c \ + im_mpercent.c \ + im_rank.c \ + im_resize_linear.c \ + im_sharpen.c \ + im_shrink.c \ + im_spcor.c \ + im_stretch3.c \ + im_zerox.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/convolution/convol_dispatch.c b/libsrc/convolution/convol_dispatch.c new file mode 100644 index 00000000..0bf098e2 --- /dev/null +++ b/libsrc/convolution/convol_dispatch.c @@ -0,0 +1,1407 @@ +/* VIPS function dispatch tables for convolution. + * + * J. Cupitt, 14/2/95. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* One image in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Two images in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Args to im_stretch3. + */ +static im_arg_desc stretch3_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "xdisp" ), + IM_INPUT_DOUBLE( "ydisp" ) +}; + +/* Call im_stretch3 via arg vector. + */ +static int +stretch3_vec( im_object *argv ) +{ + double xdisp = *((int *) argv[2]); + double ydisp = *((int *) argv[3]); + + return( im_stretch3( argv[0], argv[1], xdisp, ydisp ) ); +} + +/* Description of im_stretch3. + */ +static im_function stretch3_desc = { + "im_stretch3", /* Name */ + "stretch 3%, sub-pixel displace by xdisp/ydisp", + IM_FN_PIO, /* Flags */ + stretch3_vec, /* Dispatch function */ + IM_NUMBER( stretch3_args ), /* Size of arg list */ + stretch3_args /* Arg list */ +}; + +/* Args to im_contrast_surface. + */ +static im_arg_desc contrast_surface_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "half_win_size" ), + IM_INPUT_INT( "spacing" ) +}; + +/* Call im_contrast_surface via arg vector. + */ +static int +contrast_surface_vec( im_object *argv ) +{ + int half_win_size = *((int *) argv[2]); + int spacing = *((int *) argv[3]); + + return( im_contrast_surface( argv[0], argv[1], + half_win_size, spacing ) ); +} + +/* Description of im_contrast_surface. + */ +static im_function contrast_surface_desc = { + "im_contrast_surface", /* Name */ + "find high-contrast points in an image", + IM_FN_PIO, /* Flags */ + contrast_surface_vec, /* Dispatch function */ + IM_NUMBER( contrast_surface_args ),/* Size of arg list */ + contrast_surface_args /* Arg list */ +}; + +/* Args to im_contrast_surface_raw. + */ +static im_arg_desc contrast_surface_raw_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "half_win_size" ), + IM_INPUT_INT( "spacing" ) +}; + +/* Call im_contrast_surface_raw via arg vector. + */ +static int +contrast_surface_raw_vec( im_object *argv ) +{ + int half_win_size = *((int *) argv[2]); + int spacing = *((int *) argv[3]); + + return( im_contrast_surface_raw( argv[0], argv[1], + half_win_size, spacing ) ); +} + +/* Description of im_contrast_surface_raw. + */ +static im_function contrast_surface_raw_desc = { + "im_contrast_surface_raw", /* Name */ + "find high-contrast points in an image", + IM_FN_PIO, /* Flags */ + contrast_surface_raw_vec, /* Dispatch function */ + IM_NUMBER( contrast_surface_raw_args ),/* Size of arg list */ + contrast_surface_raw_args /* Arg list */ +}; + +/* Args to im_rank. + */ +static im_arg_desc rank_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xsize" ), + IM_INPUT_INT( "ysize" ), + IM_INPUT_INT( "n" ) +}; + +/* Call im_rank via arg vector. + */ +static int +rank_vec( im_object *argv ) +{ + int xsize = *((int *) argv[2]); + int ysize = *((int *) argv[3]); + int n = *((int *) argv[4]); + + return( im_rank( argv[0], argv[1], xsize, ysize, n ) ); +} + +/* Description of im_rank. + */ +static im_function rank_desc = { + "im_rank", /* Name */ + "rank filter nth element of xsize/ysize window", + IM_FN_PIO, /* Flags */ + rank_vec, /* Dispatch function */ + IM_NUMBER( rank_args ), /* Size of arg list */ + rank_args /* Arg list */ +}; + +/* Call im_rank_raw via arg vector. + */ +static int +rank_raw_vec( im_object *argv ) +{ + int xsize = *((int *) argv[2]); + int ysize = *((int *) argv[3]); + int n = *((int *) argv[4]); + + return( im_rank_raw( argv[0], argv[1], xsize, ysize, n ) ); +} + +/* Description of im_rank_raw. + */ +static im_function rank_raw_desc = { + "im_rank_raw", /* Name */ + "rank filter nth element of xsize/ysize window, no border", + IM_FN_PIO, /* Flags */ + rank_raw_vec, /* Dispatch function */ + IM_NUMBER( rank_args ), /* Size of arg list */ + rank_args /* Arg list */ +}; + +/* Args to im_sharpen. + */ +static im_arg_desc sharpen_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "mask_size" ), + IM_INPUT_DOUBLE( "x1" ), + IM_INPUT_DOUBLE( "y2" ), + IM_INPUT_DOUBLE( "y3" ), + IM_INPUT_DOUBLE( "m1" ), + IM_INPUT_DOUBLE( "m2" ) +}; + +/* Call im_sharpen via arg vector. + */ +static int +sharpen_vec( im_object *argv ) +{ + int mask_size = *((int *) argv[2]); + double x1 = *((double *) argv[3]); + double x2 = *((double *) argv[4]); + double x3 = *((double *) argv[5]); + double m1 = *((double *) argv[6]); + double m2 = *((double *) argv[7]); + + return( im_sharpen( argv[0], argv[1], mask_size, x1, x2, x3, m1, m2 ) ); +} + +/* Description of im_sharpen. + */ +static im_function sharpen_desc = { + "im_sharpen", /* Name */ + "sharpen high frequencies of L channel of LabQ", + IM_FN_PIO, /* Flags */ + sharpen_vec, /* Dispatch function */ + IM_NUMBER( sharpen_args ), /* Size of arg list */ + sharpen_args /* Arg list */ +}; + +/* Args to im_addgnoise. + */ +static im_arg_desc addgnoise_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "sigma" ) +}; + +/* Call im_addgnoise via arg vector. + */ +static int +addgnoise_vec( im_object *argv ) +{ + double sigma = *((double *) argv[2]); + + return( im_addgnoise( argv[0], argv[1], sigma ) ); +} + +/* Description of im_addgnoise. + */ +static im_function addgnoise_desc = { + "im_addgnoise", /* Name */ + "add gaussian noise with mean 0 and std. dev. sigma", + IM_FN_PIO, /* Flags */ + addgnoise_vec, /* Dispatch function */ + IM_NUMBER( addgnoise_args ), /* Size of arg list */ + addgnoise_args /* Arg list */ +}; + +/* Args for im_read_dmask() + */ +static im_arg_desc read_dmask_args[] = { + IM_INPUT_STRING( "filename" ), + IM_OUTPUT_DMASK( "mask" ) +}; + +/* Call im_read_dmask via arg vector. + */ +static int +read_dmask_vec( im_object *argv ) +{ + im_mask_object *mo = argv[1]; + + if( !(mo->mask = im_read_dmask( argv[0] )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_read_dmask(). + */ +static im_function read_dmask_desc = { + "im_read_dmask", /* Name */ + "read matrix of double from file", + 0, /* Flags */ + read_dmask_vec, /* Dispatch function */ + IM_NUMBER( read_dmask_args ), /* Size of arg list */ + read_dmask_args /* Arg list */ +}; + +/* Args for convolver with imask. + */ +static im_arg_desc conv_imask[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_IMASK( "matrix" ) +}; + +/* Args for convolver with dmask. + */ +static im_arg_desc conv_dmask[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DMASK( "matrix" ) +}; + +/* Call im_compass via arg vector. + */ +static int +compass_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_compass( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_compass. + */ +static im_function compass_desc = { + "im_compass", /* Name */ + "convolve with 8-way rotating integer mask", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + compass_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Call im_conv via arg vector. + */ +static int +conv_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_conv( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_conv. + */ +static im_function conv_desc = { + "im_conv", /* Name */ + "convolve", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + conv_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Call im_conv_raw via arg vector. + */ +static int +conv_raw_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_conv_raw( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_conv_raw. + */ +static im_function conv_raw_desc = { + "im_conv_raw", /* Name */ + "convolve, no border", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + conv_raw_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Call im_convf via arg vector. + */ +static int +convf_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_convf( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_convf. + */ +static im_function convf_desc = { + "im_convf", /* Name */ + "convolve, with DOUBLEMASK", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + convf_vec, /* Dispatch function */ + IM_NUMBER( conv_dmask ), /* Size of arg list */ + conv_dmask /* Arg list */ +}; + +/* Call im_convf_raw via arg vector. + */ +static int +convf_raw_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_convf_raw( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_convf_raw. + */ +static im_function convf_raw_desc = { + "im_convf_raw", /* Name */ + "convolve, with DOUBLEMASK, no border", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + convf_raw_vec, /* Dispatch function */ + IM_NUMBER( conv_dmask ), /* Size of arg list */ + conv_dmask /* Arg list */ +}; + +/* Call im_convsep via arg vector. + */ +static int +convsep_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_convsep( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_convsep. + */ +static im_function convsep_desc = { + "im_convsep", /* Name */ + "seperable convolution", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + convsep_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Call im_convsep_raw via arg vector. + */ +static int +convsep_raw_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_convsep_raw( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_convsep_raw. + */ +static im_function convsep_raw_desc = { + "im_convsep_raw", /* Name */ + "seperable convolution, no border", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + convsep_raw_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Call im_convsepf via arg vector. + */ +static int +convsepf_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_convsepf( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_convsepf. + */ +static im_function convsepf_desc = { + "im_convsepf", /* Name */ + "seperable convolution, with DOUBLEMASK", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + convsepf_vec, /* Dispatch function */ + IM_NUMBER( conv_dmask ), /* Size of arg list */ + conv_dmask /* Arg list */ +}; + +/* Call im_convsepf_raw via arg vector. + */ +static int +convsepf_raw_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_convsepf_raw( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_convsepf_raw. + */ +static im_function convsepf_raw_desc = { + "im_convsepf_raw", /* Name */ + "seperable convolution, with DOUBLEMASK, no border", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + convsepf_raw_vec, /* Dispatch function */ + IM_NUMBER( conv_dmask ), /* Size of arg list */ + conv_dmask /* Arg list */ +}; + +/* Args for im_convsub. + */ +static im_arg_desc convsub_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_IMASK( "matrix" ), + IM_INPUT_INT( "xskip" ), + IM_INPUT_INT( "yskip" ) +}; + +/* Call im_convsub via arg vector. + */ +static int +convsub_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + int xskip = *((int *) argv[3]); + int yskip = *((int *) argv[4]); + + return( im_convsub( argv[0], argv[1], mo->mask, xskip, yskip ) ); +} + +/* Description of im_convsub. + */ +static im_function convsub_desc = { + "im_convsub", /* Name */ + "convolve uchar to uchar, sub-sampling by xskip, yskip", + IM_FN_TRANSFORM, /* Flags */ + convsub_vec, /* Dispatch function */ + IM_NUMBER( convsub_args ), /* Size of arg list */ + convsub_args /* Arg list */ +}; + +/* Call im_fastcor via arg vector. + */ +static int +fastcor_vec( im_object *argv ) +{ + return( im_fastcor( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_fastcor. + */ +static im_function fastcor_desc = { + "im_fastcor", /* Name */ + "fast correlate in2 within in1", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + fastcor_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_fastcor_raw via arg vector. + */ +static int +fastcor_raw_vec( im_object *argv ) +{ + return( im_fastcor_raw( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_fastcor_raw. + */ +static im_function fastcor_raw_desc = { + "im_fastcor_raw", /* Name */ + "fast correlate in2 within in1, no border", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + fastcor_raw_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Args for im_gauss_dmask. + */ +static im_arg_desc gauss_dmask_args[] = { + IM_OUTPUT_DMASK( "mask" ), + IM_INPUT_DOUBLE( "sigma" ), + IM_INPUT_DOUBLE( "min_amp" ) +}; + +/* Call im_gauss_dmask via arg vector. + */ +static int +gauss_dmask_vec( im_object *argv ) +{ + im_mask_object *mo = argv[0]; + double sigma = *((double *) argv[1]); + double min_amp = *((double *) argv[2]); + + if( !(mo->mask = + im_gauss_dmask( mo->name, sigma, min_amp )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_gauss_dmask. + */ +static im_function gauss_dmask_desc = { + "im_gauss_dmask", /* Name */ + "generate gaussian DOUBLEMASK", + 0, /* Flags */ + gauss_dmask_vec, /* Dispatch function */ + IM_NUMBER( gauss_dmask_args ), /* Size of arg list */ + gauss_dmask_args /* Arg list */ +}; + +/* Args for im_gauss_imask. + */ +static im_arg_desc gauss_imask_args[] = { + IM_OUTPUT_IMASK( "mask" ), + IM_INPUT_DOUBLE( "sigma" ), + IM_INPUT_DOUBLE( "min_amp" ) +}; + +/* Call im_gauss_imask via arg vector. + */ +static int +gauss_imask_vec( im_object *argv ) +{ + im_mask_object *mo = argv[0]; + double sigma = *((double *) argv[1]); + double min_amp = *((double *) argv[2]); + + if( !(mo->mask = + im_gauss_imask( mo->name, sigma, min_amp )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_gauss_imask. + */ +static im_function gauss_imask_desc = { + "im_gauss_imask", /* Name */ + "generate gaussian INTMASK", + 0, /* Flags */ + gauss_imask_vec, /* Dispatch function */ + IM_NUMBER( gauss_imask_args ), /* Size of arg list */ + gauss_imask_args /* Arg list */ +}; + +/* Args for im_gaussnoise. + */ +static im_arg_desc gaussnoise_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xsize" ), + IM_INPUT_INT( "ysize" ), + IM_INPUT_DOUBLE( "mean" ), + IM_INPUT_DOUBLE( "sigma" ) +}; + +/* Call im_gaussnoise via arg vector. + */ +static int +gaussnoise_vec( im_object *argv ) +{ + int xsize = *((int *) argv[1]); + int ysize = *((int *) argv[2]); + double mean = *((double *) argv[3]); + double sigma = *((double *) argv[4]); + + if( im_gaussnoise( argv[0], xsize, ysize, mean, sigma ) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_gaussnoise. + */ +static im_function gaussnoise_desc = { + "im_gaussnoise", /* Name */ + "generate image of gaussian noise with specified statistics", + IM_FN_PIO, /* Flags */ + gaussnoise_vec, /* Dispatch function */ + IM_NUMBER( gaussnoise_args ), /* Size of arg list */ + gaussnoise_args /* Arg list */ +}; + +/* Call im_grad_x via arg vector. + */ +static int +grad_x_vec( im_object *argv ) +{ + return( im_grad_x( argv[0], argv[1] ) ); +} + +/* Description of im_grad_x. + */ +static im_function grad_x_desc = { + "im_grad_x", /* Name */ + "x component of gradient of image", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + grad_x_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_grad_y via arg vector. + */ +static int +grad_y_vec( im_object *argv ) +{ + return( im_grad_y( argv[0], argv[1] ) ); +} + +/* Description of im_grad_y. + */ +static im_function grad_y_desc = { + "im_grad_y", /* Name */ + "y component of gradient of image", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + grad_y_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_gradcor via arg vector. + */ +static int +gradcor_vec( im_object *argv ) +{ + return( im_gradcor( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_gradcor. + */ +static im_function gradcor_desc = { + "im_gradcor", /* Name */ + "non-normalised correlation of gradient of in2 within in1", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + gradcor_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_gradcor_raw via arg vector. + */ +static int +gradcor_raw_vec( im_object *argv ) +{ + return( im_gradcor_raw( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_gradcor_raw. + */ +static im_function gradcor_raw_desc = { + "im_gradcor_raw", /* Name */ + "non-normalised correlation of gradient of in2 within in1, no padding", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + gradcor_raw_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_gradient via arg vector. + */ +static int +gradient_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_gradient( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_gradient. + */ +static im_function gradient_desc = { + "im_gradient", /* Name */ + "convolve with 2-way rotating mask", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + gradient_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Call im_lindetect via arg vector. + */ +static int +lindetect_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_lindetect( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_lindetect. + */ +static im_function lindetect_desc = { + "im_lindetect", /* Name */ + "convolve with 4-way rotating mask", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + lindetect_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Args for im_log_imask. + */ +static im_arg_desc log_imask_args[] = { + IM_OUTPUT_IMASK( "mask" ), + IM_INPUT_DOUBLE( "sigma" ), + IM_INPUT_DOUBLE( "min_amp" ) +}; + +/* Call im_log_imask via arg vector. + */ +static int +log_imask_vec( im_object *argv ) +{ + im_mask_object *mo = argv[0]; + double sigma = *((double *) argv[1]); + double min_amp = *((double *) argv[2]); + + if( !(mo->mask = + im_log_imask( mo->name, sigma, min_amp )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_log_imask. + */ +static im_function log_imask_desc = { + "im_log_imask", /* Name */ + "generate laplacian of gaussian INTMASK", + 0, /* Flags */ + log_imask_vec, /* Dispatch function */ + IM_NUMBER( log_imask_args ), /* Size of arg list */ + log_imask_args /* Arg list */ +}; + +/* Args for im_log_dmask. + */ +static im_arg_desc log_dmask_args[] = { + IM_OUTPUT_DMASK( "maskfile" ), + IM_INPUT_DOUBLE( "sigma" ), + IM_INPUT_DOUBLE( "min_amp" ) +}; + +/* Call im_log_dmask via arg vector. + */ +static int +log_dmask_vec( im_object *argv ) +{ + im_mask_object *mo = argv[0]; + double sigma = *((double *) argv[1]); + double min_amp = *((double *) argv[2]); + + if( !(mo->mask = + im_log_dmask( mo->name, sigma, min_amp )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_log_dmask. + */ +static im_function log_dmask_desc = { + "im_log_dmask", /* Name */ + "generate laplacian of gaussian DOUBLEMASK", + 0, /* Flags */ + log_dmask_vec, /* Dispatch function */ + IM_NUMBER( log_dmask_args ), /* Size of arg list */ + log_dmask_args /* Arg list */ +}; + +/* Args for im_resize_linear. + */ +static im_arg_desc resize_linear_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "X" ), + IM_INPUT_INT( "Y" ) +}; + +/* Call im_resize_linear via arg vector. + */ +static int +resize_linear_vec( im_object *argv ) +{ + int X = *((int *) argv[2]); + int Y = *((int *) argv[3]); + + return( im_resize_linear( argv[0], argv[1], X, Y ) ); +} + +/* Description of im_resize_linear. + */ +static im_function resize_linear_desc = { + "im_resize_linear", /* Name */ + "resize to X by Y pixels with linear interpolation", + 0, /* Flags */ + resize_linear_vec, /* Dispatch function */ + IM_NUMBER( resize_linear_args ), /* Size of arg list */ + resize_linear_args /* Arg list */ +}; + +/* Args for im_mpercent. + */ +static im_arg_desc mpercent_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_DOUBLE( "percent" ), + IM_OUTPUT_INT( "thresh" ) +}; + +/* Call im_mpercent via arg vector. + */ +static int +mpercent_vec( im_object *argv ) +{ + double percent = *((double *) argv[1]); + + return( im_mpercent( argv[0], percent, argv[2] ) ); +} + +/* Description of im_mpercent. + */ +static im_function mpercent_desc = { + "im_mpercent", /* Name */ + "find threshold above which there are percent values", + 0, /* Flags */ + mpercent_vec, /* Dispatch function */ + IM_NUMBER( mpercent_args ), /* Size of arg list */ + mpercent_args /* Arg list */ +}; + +/* Args for im_shrink. + */ +static im_arg_desc shrink_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "xfac" ), + IM_INPUT_DOUBLE( "yfac" ) +}; + +/* Call im_shrink via arg vector. + */ +static int +shrink_vec( im_object *argv ) +{ + double xshrink = *((double *) argv[2]); + double yshrink = *((double *) argv[3]); + + return( im_shrink( argv[0], argv[1], xshrink, yshrink ) ); +} + +/* Description of im_shrink. + */ +static im_function shrink_desc = { + "im_shrink", /* Name */ + "shrink image by xfac, yfac times", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + shrink_vec, /* Dispatch function */ + IM_NUMBER( shrink_args ), /* Size of arg list */ + shrink_args /* Arg list */ +}; + +/* Call im_spcor via arg vector. + */ +static int +spcor_vec( im_object *argv ) +{ + return( im_spcor( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_spcor. + */ +static im_function spcor_desc = { + "im_spcor", /* Name */ + "normalised correlation of in2 within in1", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + spcor_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_spcor_raw via arg vector. + */ +static int +spcor_raw_vec( im_object *argv ) +{ + return( im_spcor_raw( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_spcor_raw. + */ +static im_function spcor_raw_desc = { + "im_spcor_raw", /* Name */ + "normalised correlation of in2 within in1, no black padding", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + spcor_raw_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_spcor2 via arg vector. + */ +static int +spcor2_vec( im_object *argv ) +{ + return( im_spcor2( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_spcor2. + */ +static im_function spcor2_desc = { + "im_spcor2", /* Name */ + "normalised correlation of in2 within in1", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + spcor2_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_spcor2_raw via arg vector. + */ +static int +spcor2_raw_vec( im_object *argv ) +{ + return( im_spcor2_raw( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_spcor2_raw. + */ +static im_function spcor2_raw_desc = { + "im_spcor2_raw", /* Name */ + "normalised correlation of in2 within in1, no black padding", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + spcor2_raw_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Args for im_zerox. + */ +static im_arg_desc zerox_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "flag" ) +}; + +/* Call im_zerox via arg vector. + */ +static int +zerox_vec( im_object *argv ) +{ + int flag = *((int *) argv[2]); + + return( im_zerox( argv[0], argv[1], flag ) ); +} + +/* Description of im_zerox. + */ +static im_function zerox_desc = { + "im_zerox", /* Name */ + "find +ve or -ve zero crossings in image", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + zerox_vec, /* Dispatch function */ + IM_NUMBER( zerox_args ), /* Size of arg list */ + zerox_args /* Arg list */ +}; + +/* Args for im_embed. + */ +static im_arg_desc embed_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "type" ), + IM_INPUT_INT( "x" ), + IM_INPUT_INT( "y" ), + IM_INPUT_INT( "w" ), + IM_INPUT_INT( "h" ) +}; + +/* Call im_embed via arg vector. + */ +static int +embed_vec( im_object *argv ) +{ + int type = *((int *) argv[2]); + int x = *((int *) argv[3]); + int y = *((int *) argv[4]); + int w = *((int *) argv[5]); + int h = *((int *) argv[6]); + + return( im_embed( argv[0], argv[1], type, x, y, w, h ) ); +} + +/* Description of im_embed. + */ +static im_function embed_desc = { + "im_embed", /* Name */ + "embed in within a set of borders", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + embed_vec, /* Dispatch function */ + IM_NUMBER( embed_args ), /* Size of arg list */ + embed_args /* Arg list */ +}; + +/* Mask functions! + */ +static im_arg_desc imask_args[] = { + IM_INPUT_IMASK( "in" ), + IM_OUTPUT_IMASK( "out" ) +}; + +static im_arg_desc dmask_args[] = { + IM_INPUT_DMASK( "in" ), + IM_OUTPUT_DMASK( "out" ) +}; + +/* Call im_rotate_imask45 via arg vector. + */ +static int +rotate_imask45_vec( im_object *argv ) +{ + im_mask_object *min = argv[0]; + im_mask_object *mout = argv[1]; + + if( !(mout->mask = im_rotate_imask45( min->mask, mout->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_rotate_imask45. + */ +static im_function rotate_imask45_desc = { + "im_rotate_imask45", /* Name */ + "rotate INTMASK clockwise by 45 degrees", + 0, /* Flags */ + rotate_imask45_vec, /* Dispatch function */ + IM_NUMBER( imask_args ), /* Size of arg list */ + imask_args /* Arg list */ +}; + +/* Call im_rotate_imask90 via arg vector. + */ +static int +rotate_imask90_vec( im_object *argv ) +{ + im_mask_object *min = argv[0]; + im_mask_object *mout = argv[1]; + + if( !(mout->mask = im_rotate_imask90( min->mask, mout->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_rotate_imask90. + */ +static im_function rotate_imask90_desc = { + "im_rotate_imask90", /* Name */ + "rotate INTMASK clockwise by 90 degrees", + 0, /* Flags */ + rotate_imask90_vec, /* Dispatch function */ + IM_NUMBER( imask_args ), /* Size of arg list */ + imask_args /* Arg list */ +}; + +/* Call im_rotate_dmask45 via arg vector. + */ +static int +rotate_dmask45_vec( im_object *argv ) +{ + im_mask_object *min = argv[0]; + im_mask_object *mout = argv[1]; + + if( !(mout->mask = im_rotate_dmask45( min->mask, mout->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_rotate_dmask45. + */ +static im_function rotate_dmask45_desc = { + "im_rotate_dmask45", /* Name */ + "rotate DOUBLEMASK clockwise by 45 degrees", + 0, /* Flags */ + rotate_dmask45_vec, /* Dispatch function */ + IM_NUMBER( dmask_args ), /* Size of arg list */ + dmask_args /* Arg list */ +}; + +/* Call im_rotate_dmask90 via arg vector. + */ +static int +rotate_dmask90_vec( im_object *argv ) +{ + im_mask_object *min = argv[0]; + im_mask_object *mout = argv[1]; + + if( !(mout->mask = im_rotate_dmask90( min->mask, mout->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_rotate_dmask90. + */ +static im_function rotate_dmask90_desc = { + "im_rotate_dmask90", /* Name */ + "rotate DOUBLEMASK clockwise by 90 degrees", + 0, /* Flags */ + rotate_dmask90_vec, /* Dispatch function */ + IM_NUMBER( dmask_args ), /* Size of arg list */ + dmask_args /* Arg list */ +}; + +static im_arg_desc maxvalue_args[] = { + IM_INPUT_IMAGEVEC( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +static int +maxvalue_vec( im_object *argv ) +{ + im_imagevec_object *iv = (im_imagevec_object *) argv[0]; + + return( im_maxvalue( iv->vec, argv[1], iv->n ) ); +} + +static im_function maxvalue_desc = { + "im_maxvalue", /* Name */ + "point-wise maximum value", /* Description */ + IM_FN_PIO, /* Flags */ + maxvalue_vec, /* Dispatch function */ + IM_NUMBER( maxvalue_args ), /* Size of arg list */ + maxvalue_args /* Arg list */ +}; + +static im_arg_desc rank_image_args[] = { + IM_INPUT_IMAGEVEC( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "index" ) +}; + +static int +rank_image_vec( im_object *argv ) +{ + im_imagevec_object *iv = (im_imagevec_object *) argv[0]; + int index = *((int *) argv[2]); + + return( im_rank_image( iv->vec, argv[1], iv->n, index ) ); +} + +static im_function rank_image_desc = { + "im_rank_image", /* Name */ + "point-wise pixel rank", /* Description */ + IM_FN_PIO, /* Flags */ + rank_image_vec, /* Dispatch function */ + IM_NUMBER( rank_image_args ), /* Size of arg list */ + rank_image_args /* Arg list */ +}; + +static int +imask_xsize_vec( im_object *argv ) +{ + *( (int*) argv[1] )= ( (INTMASK*) ( ( (im_mask_object*) argv[0] )-> mask ) )-> xsize; + return 0; +} + +static int +imask_ysize_vec( im_object *argv ) +{ + *( (int*) argv[1] )= ( (INTMASK*) ( ( (im_mask_object*) argv[0] )-> mask ) )-> ysize; + return 0; +} + +static int +dmask_xsize_vec( im_object *argv ) +{ + *( (int*) argv[1] )= ( (DOUBLEMASK*) ( ( (im_mask_object*) argv[0] )-> mask ) )-> xsize; + return 0; +} + +static int +dmask_ysize_vec( im_object *argv ) +{ + *( (int*) argv[1] )= ( (DOUBLEMASK*) ( ( (im_mask_object*) argv[0] )-> mask ) )-> ysize; + return 0; +} + +static im_arg_desc imask_size_args[] = { + IM_INPUT_IMASK( "mask" ), + IM_OUTPUT_INT( "size" ) +}; + +static im_arg_desc dmask_size_args[] = { + IM_INPUT_DMASK( "mask" ), + IM_OUTPUT_INT( "size" ) +}; + +static im_function imask_xsize_desc = { + "im_imask_xsize", /* Name */ + "horizontal size of an intmask", /* Description */ + 0, /* Flags */ + imask_xsize_vec, /* Dispatch function */ + IM_NUMBER( imask_size_args ), /* Size of arg list */ + imask_size_args /* Arg list */ +}; + +static im_function imask_ysize_desc = { + "im_imask_ysize", /* Name */ + "vertical size of an intmask", /* Description */ + 0, /* Flags */ + imask_ysize_vec, /* Dispatch function */ + IM_NUMBER( imask_size_args ), /* Size of arg list */ + imask_size_args /* Arg list */ +}; + +static im_function dmask_xsize_desc = { + "im_dmask_xsize", /* Name */ + "horizontal size of a doublemask", /* Description */ + 0, /* Flags */ + dmask_xsize_vec, /* Dispatch function */ + IM_NUMBER( dmask_size_args ), /* Size of arg list */ + dmask_size_args /* Arg list */ +}; + +static im_function dmask_ysize_desc = { + "im_dmask_ysize", /* Name */ + "vertical size of a doublemask", /* Description */ + 0, /* Flags */ + dmask_ysize_vec, /* Dispatch function */ + IM_NUMBER( dmask_size_args ), /* Size of arg list */ + dmask_size_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *convol_list[] = { + &addgnoise_desc, + &compass_desc, + &contrast_surface_desc, + &contrast_surface_raw_desc, + &conv_desc, + &conv_raw_desc, + &convf_desc, + &convf_raw_desc, + &convsep_desc, + &convsep_raw_desc, + &convsepf_desc, + &convsepf_raw_desc, + &convsub_desc, + &dmask_xsize_desc, + &dmask_ysize_desc, + &embed_desc, + &fastcor_desc, + &fastcor_raw_desc, + &gauss_dmask_desc, + &gauss_imask_desc, + &gaussnoise_desc, + &grad_x_desc, + &grad_y_desc, + &gradcor_desc, + &gradcor_raw_desc, + &gradient_desc, + &imask_xsize_desc, + &imask_ysize_desc, + &rank_image_desc, + &lindetect_desc, + &log_dmask_desc, + &log_imask_desc, + &maxvalue_desc, + &mpercent_desc, + &rank_desc, + &rank_raw_desc, + &read_dmask_desc, + &resize_linear_desc, + &rotate_dmask45_desc, + &rotate_dmask90_desc, + &rotate_imask45_desc, + &rotate_imask90_desc, + &sharpen_desc, + &shrink_desc, + &spcor_desc, + &spcor_raw_desc, + &spcor2_desc, + &spcor2_raw_desc, + &stretch3_desc, + &zerox_desc +}; + +/* Package of functions. + */ +im_package im__convolution = { + "convolution", + IM_NUMBER( convol_list ), + convol_list +}; diff --git a/libsrc/convolution/im_addgnoise.c b/libsrc/convolution/im_addgnoise.c new file mode 100644 index 00000000..2b6d93c2 --- /dev/null +++ b/libsrc/convolution/im_addgnoise.c @@ -0,0 +1,96 @@ +/* @(#) Add gaussian noise with mean 0 and variance sigma to image + * @(#) The noise is generated by averaging 12 random numbers + * @(#) page 78 PIETGEN 1989 n = 12 + * @(#) Input image is any, output is float + * @(#) If running on SYSTEM V CONSTANT should be replaced by 2**15 - 1 + * @(#) Usage + * @(#) + * @(#) int im_addgnoise(imin, imout, sigma) + * @(#) IMAGE *imin, *imout; + * @(#) double sigma; + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright 1990, N. Dessipris. + * + * File written on 2/12/1986 + * Author : N. Dessipris + * Updated : 22/01/1991 + * 1/2/95 JC + * - junked! + * - now uses partial im_gaussnoise() and partial im_add() to do the + * same job + & 1/5/01 JC + * - oops, failed for not-1-band images + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define MAX_BANDS (200) + +int +im_addgnoise( IMAGE *in, IMAGE *out, double sigma ) +{ + IMAGE *noise[MAX_BANDS]; + IMAGE *nb; + int i; + + if( in->Bands > MAX_BANDS ) { + im_error( "im_addgnoise", _( "too many bands" ) ); + return( -1 ); + } + + /* Make n-band noise image. + */ + for( i = 0; i < in->Bands; i++ ) + if( !(noise[i] = im_open_local( out, "im_addgnoise:2", "p" )) || + im_gaussnoise( noise[i], in->Xsize, in->Ysize, + 0.0, sigma ) ) + return( -1 ); + + if( !(nb = im_open_local( out, "im_addgnoise:3", "p" )) || + im_gbandjoin( noise, out, in->Bands ) || + im_add( in, nb, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/convolution/im_compass.c b/libsrc/convolution/im_compass.c new file mode 100644 index 00000000..7b357da7 --- /dev/null +++ b/libsrc/convolution/im_compass.c @@ -0,0 +1,148 @@ +/* @(#) im_complass: Optimised convolution for line detection + * @(#) Uses the entered mask and 7 rotated versions of it (each by 45 degrees) + * @(#) + * @(#) Usage + * @(#) int im_compass( in, out, m ) + * @(#) IMAGE *in, *out; + * @(#) INTMASK *m; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * @(#) Returns an int pointer to valid offsets for rotating a square mask + * @(#) of odd size by 45 degrees. + * @(#) + * @(#) Usage + * @(#) int *im_offsets45( size ) + * @(#) int size; + * @(#) + * @(#) Returns an int pointer to valid offsets on sucess and -1 on error + * @(#) + * + * Author: N. Dessipris (Copyright, N. Dessipris 1991) + * Written on: 08/05/1991 + * Modified on: + * 11/3/01 JC + * - rewritten, calling im_conv() and im_maxvalue() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_compass( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + IMAGE *filtered[8]; + IMAGE *absed[8]; + int i; + + if( im_open_local_array( out, filtered, 8, "im_compass:1", "p" ) || + im_open_local_array( out, absed, 8, "im_compass:2", "p" ) ) + return( -1 ); + + for( i = 0; i < 8; i++ ) { + if( im_conv( in, filtered[i], mask ) || + !(mask = (INTMASK *) im_local( out, + (im_construct_fn) im_rotate_imask45, + (im_callback_fn) im_free_imask, + mask, mask->filename, NULL )) ) + return( -1 ); + } + + for( i = 0; i < 8; i++ ) + if( im_abs( filtered[i], absed[i] ) ) + return( -1 ); + + return( im_maxvalue( absed, out, 8 ) ); +} + +int +im_lindetect( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + IMAGE *filtered[4]; + IMAGE *absed[4]; + int i; + + if( im_open_local_array( out, filtered, 4, "im_lindetect:1", "p" ) || + im_open_local_array( out, absed, 4, "im_lindetect:2", "p" ) ) + return( -1 ); + + for( i = 0; i < 4; i++ ) { + if( im_conv( in, filtered[i], mask ) || + !(mask = (INTMASK *) im_local( out, + (im_construct_fn) im_rotate_imask45, + (im_callback_fn) im_free_imask, + mask, mask->filename, NULL )) ) + return( -1 ); + } + + for( i = 0; i < 4; i++ ) + if( im_abs( filtered[i], absed[i] ) ) + return( -1 ); + + return( im_maxvalue( absed, out, 4 ) ); +} + +#define GTEMPS (4) + +int +im_gradient( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + IMAGE *t[GTEMPS]; + INTMASK *rmask; + + if( im_open_local_array( out, t, GTEMPS, "im_gradient", "p" ) ) + return( -1 ); + + if( !(rmask = (INTMASK *) im_local( out, + (im_construct_fn) im_rotate_imask90, + (im_callback_fn) im_free_imask, mask, mask->filename, NULL )) ) + return( -1 ); + + if( im_conv( in, t[0], mask ) || + im_conv( in, t[1], rmask ) || + im_abs( t[0], t[2] ) || + im_abs( t[1], t[3] ) || + im_add( t[2], t[3], out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/convolution/im_contrast_surface.c b/libsrc/convolution/im_contrast_surface.c new file mode 100644 index 00000000..f10faf07 --- /dev/null +++ b/libsrc/convolution/im_contrast_surface.c @@ -0,0 +1,274 @@ +/* @(#) Generate an image where the value of each pixel represents the + * @(#) contrast within a window of half_win_size from the corresponsing + * @(#) point in the input image. Sub-sample by a factor of spacing. + * @(#) + * @(#) Pixels beyond the edges of the image are considered to be have the + * @(#) value zero (black). + * @(#) + * @(#) Input must be single-band uncoded uchar, WIO or PIO. + * @(#) + * @(#) Output is single-band uncoded uint, WIO or PIO. + * @(#) + * @(#) int + * @(#) im_contrast_surface( + * @(#) IMAGE *in, + * @(#) IMAGE *out, + * @(#) int half_win_size, + * @(#) int spacing + * @(#) ); + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * @(#) Also: im_contrast_surface_raw(). As above, but pixels within + * @(#) half_win_size of the edge are not calculated, and output is smaller + * @(#) accordingly. + * + * Copyright: 2006, The Nottingham Trent University + * + * Author: Tom Vajzovic + * (based on algorithm by Nicos Dessipris & John Cupitt) + * + * Written on: 2006-03-13 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H */ + +#ifdef NOT_IN_VIPS +#define _(s) (s) +#else +#include +#endif + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + +/** MACROS **/ + +/* from simple_macros.h */ +#define LESSER(a,b) ((a)<(b)?(a):(b)) +#define DOUBLE(a) ( (a)<<1 ) +#define DOUBLE_ADD_ONE(a) ( 1 | ( (a)<<1 ) ) + +/** LOCAL TYPES **/ + +typedef struct cont_surf_params_s +{ + int half_win_size; + int spacing; + +} cont_surf_params_t; + +/** LOCAL FUNCTIONS DECLARATIONS **/ + +int +im_contrast_surface (IMAGE * in, IMAGE * out, int half_win_size, int spacing); + +int +im_contrast_surface_raw (IMAGE * in, IMAGE * out, int half_win_size, + int spacing); + +static int cont_surf_gen (REGION * to_make, REGION * make_from, + void *unrequired, cont_surf_params_t * params); + +static unsigned int calc_cont (REGION * reg, int win_size_less_one, + int x_left, int y_top); + +/** EXPORTED FUNCTIONS **/ + +int +im_contrast_surface (IMAGE * in, IMAGE * out, int half_win_size, int spacing) +{ + IMAGE *t1 = im_open_local (out, "im_contrast_surface intermediate", "p"); + + if (!t1 + || im_embed (in, t1, 1, half_win_size, half_win_size, + in->Xsize + DOUBLE (half_win_size), + in->Ysize + DOUBLE (half_win_size)) + || im_contrast_surface_raw (t1, out, half_win_size, spacing)) + + return -1; + + out->Xoffset = 0; + out->Yoffset = 0; + + return 0; +} + +int +im_contrast_surface_raw (IMAGE * in, IMAGE * out, int half_win_size, + int spacing) +{ +#define FUNCTION_NAME "im_contrast_surface_raw" + + cont_surf_params_t *params; + + if (im_piocheck (in, out)) + return -1; + + if (IM_CODING_NONE != in->Coding || IM_BANDFMT_UCHAR != in->BandFmt + || 1 != in->Bands) + { + im_error (FUNCTION_NAME, _("one band uncoded uchar only")); + return -1; + } + + if (half_win_size < 1 || spacing < 1) + { + im_error (FUNCTION_NAME, _("bad parameters")); + return -1; + } + + if (DOUBLE (half_win_size) >= LESSER (in->Xsize, in->Ysize)) + { + im_error (FUNCTION_NAME, + _("parameters would result in zero size output image")); + return -1; + } + + params = IM_NEW (out, cont_surf_params_t); + + if (!params) + return -1; + + params->half_win_size = half_win_size; + params->spacing = spacing; + + if (im_cp_desc (out, in)) + return -1; + + out->BandFmt = IM_BANDFMT_UINT; + out->Bbits = sizeof (unsigned int) << 3; + + out->Xsize = 1 + ((in->Xsize - DOUBLE_ADD_ONE (half_win_size)) / spacing); + out->Ysize = 1 + ((in->Ysize - DOUBLE_ADD_ONE (half_win_size)) / spacing); + + out->Xoffset = -half_win_size; + out->Yoffset = -half_win_size; + + if (im_demand_hint (out, IM_FATSTRIP, in, NULL)) + return -1; + + return im_generate (out, im_start_one, cont_surf_gen, im_stop_one, in, + params); + +#undef FUNCTION_NAME +} + +/** LOCAL FUNCTIONS DEFINITIONS **/ +static int +cont_surf_gen (REGION * to_make, REGION * make_from, void *unrequired, + cont_surf_params_t * params) +{ + /* I don't need *in, but I will recieve it anyway since im_start_one() needs it */ + + unsigned int *row = + (unsigned int *) IM_REGION_ADDR (to_make, to_make->valid.left, + to_make->valid.top); + int xoff; + int y; + int bottom = to_make->valid.top + to_make->valid.height; + size_t lskip = IM_REGION_LSKIP (to_make) / sizeof (unsigned int); + + Rect area = { + params->spacing * to_make->valid.left, + params->spacing * to_make->valid.top, + DOUBLE_ADD_ONE (params->half_win_size) + + (params->spacing * (to_make->valid.width - 1)), + DOUBLE_ADD_ONE (params->half_win_size) + + (params->spacing * (to_make->valid.height - 1)) + }; + + if (im_prepare (make_from, &area) + || !im_rect_equalsrect (&make_from->valid, &area)) + return -1; + + for (y = to_make->valid.top; y < bottom; ++y, row += lskip) + + for (xoff = 0; xoff < to_make->valid.width; ++xoff) + + row[xoff] = + calc_cont (make_from, DOUBLE (params->half_win_size), + (xoff + to_make->valid.left) * params->spacing, + y * params->spacing); + + return 0; +} + +static unsigned int +calc_cont (REGION * reg, int win_size_less_one, int x_left, int y_top) +{ + unsigned char val; + unsigned char all_black = 1; + unsigned char *row; + unsigned int contrast = 0; + int xoff; + int yoff; + size_t lskip = IM_REGION_LSKIP (reg) / sizeof (unsigned char); + + row = (unsigned char *) IM_REGION_ADDR (reg, x_left, y_top); + val = *row; + + for (yoff = 0; yoff <= win_size_less_one && all_black; ++yoff, row += lskip) + for (xoff = 0; xoff <= win_size_less_one; ++xoff) + if (row[xoff] != val) + { + all_black = 0; + break; + } + + if (all_black) + return contrast; + + row = (unsigned char *) IM_REGION_ADDR (reg, x_left, y_top); + + for (yoff = 0; yoff < win_size_less_one; ++yoff, row += lskip) + { + for (xoff = 0; xoff < win_size_less_one; ++xoff) + contrast += + abs (row[xoff + 1] - row[xoff]) + abs (row[xoff + lskip] - + row[xoff]); + + contrast += abs (row[xoff + lskip] - row[xoff]); + } + + for (xoff = 0; xoff < win_size_less_one; ++xoff) + contrast += abs (row[xoff + 1] - row[xoff]); + + return contrast; +} diff --git a/libsrc/convolution/im_conv.c b/libsrc/convolution/im_conv.c new file mode 100644 index 00000000..891ae643 --- /dev/null +++ b/libsrc/convolution/im_conv.c @@ -0,0 +1,538 @@ +/* @(#) Convolve an image with an INTMASK. Image can have any number of bands, + * @(#) any non-complex type. Size and type of output image matches type of + * @(#) input image. + * @(#) + * @(#) int + * @(#) im_conv( in, out, mask ) + * @(#) IMAGE *in, *out; + * @(#) INTMASK *mask; + * @(#) + * @(#) Also: im_conv_raw(). As above, but does not add a black border. + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * @(#) Old code, kept for use of other old code in this package: + * @(#) + * @(#) Creates int luts for all non zero elm of the original mask; + * @(#) which is kept in buffer of length buffersize + * @(#) cnt is needed for freeing luts. Called by the above. + * @(#) + * @(#) int im__create_int_luts( buffer, buffersize, orig_luts, luts, cnt ) + * @(#) int *buffer, buffersize; + * @(#) int **orig_luts, **luts, *cnt; + * @(#) + * @(#) Returns either 0 (sucess) or -1 (fail) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris & Kirk Martinez + * Written on: 29/04/1991 + * Modified on: 19/05/1991 + * 8/7/93 JC + * - adapted for partial v2 + * - memory leaks fixed + * - ANSIfied + * 23/7/93 JC + * - inner loop unrolled with a switch - 25% speed-up! + * 13/12/93 JC + * - tiny rounding error removed + * 7/10/94 JC + * - new IM_ARRAY() macro + * - various simplifications + * - evalend callback added + * 1/2/95 JC + * - use of IM_REGION_ADDR() updated + * - output size was incorrect! see comment below + * - bug with large non-square matricies fixed too + * - uses new im_embed() function + * 13/7/98 JC + * - wierd bug ... im_free_imask is no longer directly called for close + * callback, caused SIGKILL on solaris 2.6 ... linker bug? + * 9/3/01 JC + * - reworked and simplified, about 10% faster + * - slightly better range clipping + * 27/7/01 JC + * - rejects masks with scale == 0 + * 7/4/04 + * - im_conv() now uses im_embed() with edge stretching on the input, not + * the output + * - sets Xoffset / Yoffset + * 11/11/05 + * - simpler inner loop avoids gcc4 bug + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our parameters ... we take a copy of the mask argument, plus we make a + * smaller version with the zeros squeezed out. + */ +typedef struct { + IMAGE *in; + IMAGE *out; + INTMASK *mask; /* Copy of mask arg */ + + int nnz; /* Number of non-zero mask elements */ + int *coeff; /* Array of non-zero mask coefficients */ + + int underflow; /* Global underflow/overflow counts */ + int overflow; +} Conv; + +static int +conv_destroy( Conv *conv ) +{ + /* Print underflow/overflow count. + */ + if( conv->overflow || conv->underflow ) + im_warning( "im_conv: %d overflows and %d underflows detected", + conv->overflow, conv->underflow ); + + if( conv->mask ) { + (void) im_free_imask( conv->mask ); + conv->mask = NULL; + } + + return( 0 ); +} + +static Conv * +conv_new( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + Conv *conv = IM_NEW( out, Conv ); + const int ne = mask->xsize * mask->ysize; + int i; + + if( !conv ) + return( NULL ); + + conv->in = in; + conv->out = out; + conv->mask = NULL; + conv->nnz = 0; + conv->coeff = NULL; + conv->underflow = 0; + conv->overflow = 0; + + if( im_add_close_callback( out, + (im_callback_fn) conv_destroy, conv, NULL ) || + !(conv->coeff = IM_ARRAY( out, ne, int )) || + !(conv->mask = im_dup_imask( mask, "conv_mask" )) ) + return( NULL ); + + /* Find non-zero mask elements. + */ + for( i = 0; i < ne; i++ ) + if( mask->coeff[i] ) + conv->coeff[conv->nnz++] = mask->coeff[i]; + + return( conv ); +} + +/* Our sequence value. + */ +typedef struct { + Conv *conv; + REGION *ir; /* Input region */ + + int *offsets; /* Offsets for each non-zero matrix element */ + PEL **pts; /* Per-non-zero mask element image pointers */ + + int underflow; /* Underflow/overflow counts */ + int overflow; +} ConvSequence; + +/* Free a sequence value. + */ +static int +stop_conv( ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + /* Add local under/over counts to global counts. + */ + conv->overflow += seq->overflow; + conv->underflow += seq->underflow; + + /* Free attached objects. + */ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Convolution start function. + */ +static void * +start_conv( IMAGE *out, IMAGE *in, Conv *conv ) +{ + ConvSequence *seq = IM_NEW( out, ConvSequence ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->conv = conv; + seq->ir = NULL; + seq->pts = NULL; + seq->underflow = 0; + seq->overflow = 0; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + seq->offsets = IM_ARRAY( out, conv->nnz, int ); + seq->pts = IM_ARRAY( out, conv->nnz, PEL * ); + if( !seq->ir || !seq->offsets || !seq->pts ) { + stop_conv( seq, in, conv ); + return( NULL ); + } + + return( (void *) seq ); +} + +#define INNER sum += *t++ * (*p++)[x] + +/* INT and FLOAT inner loops. + */ +#define CONV_INT( TYPE, IM_CLIP ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + int sum = 0; \ + int *t = conv->coeff; \ + TYPE **p = (TYPE **) seq->pts; \ + \ + z = 0; \ + IM_UNROLL( conv->nnz, INNER ); \ + \ + sum = ((sum + rounding) / mask->scale) + mask->offset; \ + \ + IM_CLIP; \ + \ + q[x] = sum; \ + } \ +} + +#define CONV_FLOAT( TYPE ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + double sum = 0; \ + int *t = conv->coeff; \ + TYPE **p = (TYPE **) seq->pts; \ + \ + z = 0; \ + IM_UNROLL( conv->nnz, INNER ); \ + \ + sum = (sum / mask->scale) + mask->offset; \ + \ + q[x] = sum; \ + } \ +} + +/* Convolve! + */ +static int +gen_conv( REGION *or, ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + REGION *ir = seq->ir; + INTMASK *mask = conv->mask; + int rounding = (mask->scale + 1)/2; + + Rect *r = &or->valid; + Rect s; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM( r ); + int sz = IM_REGION_N_ELEMENTS( or ); + + int x, y, z, i; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += mask->xsize - 1; + s.height += mask->ysize - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Fill offset array. + */ + z = 0; + for( i = 0, y = 0; y < mask->ysize; y++ ) + for( x = 0; x < mask->xsize; x++, i++ ) + if( mask->coeff[i] ) + seq->offsets[z++] = + IM_REGION_ADDR( ir, x + le, y + to ) - + IM_REGION_ADDR( ir, le, to ); + + for( y = to; y < bo; y++ ) { + /* Init pts for this line of PELs. + */ + for( z = 0; z < conv->nnz; z++ ) + seq->pts[z] = seq->offsets[z] + + (PEL *) IM_REGION_ADDR( ir, le, y ); + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + CONV_INT( unsigned char, IM_CLIP_UCHAR( sum, seq ) ); + break; + + case IM_BANDFMT_CHAR: + CONV_INT( signed char, IM_CLIP_CHAR( sum, seq ) ); + break; + + case IM_BANDFMT_USHORT: + CONV_INT( unsigned short, IM_CLIP_USHORT( sum, seq ) ); + break; + + case IM_BANDFMT_SHORT: + CONV_INT( signed short, IM_CLIP_SHORT( sum, seq ) ); + break; + + case IM_BANDFMT_UINT: + CONV_INT( unsigned int, IM_CLIP_NONE( sum, seq ) ); + break; + + case IM_BANDFMT_INT: + CONV_INT( signed int, IM_CLIP_NONE( sum, seq ) ); + break; + + case IM_BANDFMT_FLOAT: + CONV_FLOAT( float ); + break; + + case IM_BANDFMT_DOUBLE: + CONV_FLOAT( double ); + break; + + default: + assert( 0 ); + } + } + + return( 0 ); +} + +int +im_conv_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + Conv *conv; + + /* Check parameters. + */ + if( !in || in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_errormsg( "im_conv: input non-complex uncoded please!"); + return( -1 ); + } + if( !mask || mask->xsize > 1000 || mask->ysize > 1000 || + mask->xsize <= 0 || mask->ysize <= 0 || !mask->coeff || + mask->scale == 0 ) { + im_errormsg( "im_conv: nonsense mask parameters" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( !(conv = conv_new( in, out, mask )) ) + return( -1 ); + + /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= mask->xsize - 1; + out->Ysize -= mask->ysize - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_conv: image too small for mask" ); + return( -1 ); + } + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + if( im_generate( out, start_conv, gen_conv, stop_conv, in, conv ) ) + return( -1 ); + + out->Xoffset = -mask->xsize / 2; + out->Yoffset = -mask->ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_conv( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + IMAGE *t1 = im_open_local( out, "im_conv intermediate", "p" ); + + if( !t1 || + im_embed( in, t1, 1, mask->xsize / 2, mask->ysize / 2, + in->Xsize + mask->xsize - 1, + in->Ysize + mask->ysize - 1 ) || + im_conv_raw( t1, out, mask ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} + +/* im__create_int_luts is not used in this file. We have to keep it for the use + * of other conv functions in this directory which have not yet been + * rewritten. + + FIXME ... the only one left is im_convsub() which I'm sure no one + uses. Scrap this junk in the next version. Kill off the old gradient + and lindetect things too. + + */ + +/* Create multiplication luts for all non zero elements of the original mask; + * which is kept in buffer of length buffersize + * cnt is needed for freeing luts + */ +int +im__create_int_luts( int *buffer, int buffersize, + int **orig_luts, int **luts, int *cnt ) +{ + int *pbuffer; + int *buf1, *buf2, *pbuf1, *pbuf2; + int i, j; + int min, max; + int mark; /* used to mark the buffer mark = max+1 */ + int counter; /* counts the no of unique elms in mask; returned in cnt*/ + + buf1 = (int*)calloc( (unsigned)buffersize, sizeof(int) ); + buf2 = (int*)calloc( (unsigned)buffersize, sizeof(int) ); + if ( ( buf1 == NULL ) || ( buf2 == NULL ) ) + { + im_errormsg("im_create_int_luts: calloc failed (1)"); + return( -1 ); + } + + pbuffer = buffer; + pbuf1 = buf1; + /* find max and copy mask to buf1 */ + max = *pbuffer; + for ( i=0; i < buffersize; i++ ) + { + if ( *pbuffer > max ) + max = *pbuffer; + *pbuf1++ = *pbuffer++; + } + mark = max + 1; + pbuf1 = buf1; + pbuf2 = buf2; + counter = 0; +/* find a min at a time; put it into buf2 and mark all values of + * buf1 equal to found min, to INT_MAX + */ + for ( i=0; i < buffersize; i++ ) + { + min = mark + 1; /* force min to be greater than mark */ + pbuf1 = buf1; + /* find a min */ + for ( j=0; j < buffersize; j++ ) + { + if ( *pbuf1 < min ) + min = *pbuf1; + pbuf1++; + } + if ( min == mark ) /* all min are found */ + break; + *pbuf2++ = min; + counter++; + pbuf1 = buf1; + for ( j=0; j < buffersize; j++ ) /* mark values equal to min */ + { + if ( *pbuf1 == min ) + *pbuf1 = mark; + pbuf1++; + } + } +/* buf2 should keep now counter unique values of the mask, descending order + * Malloc counter luts and initialise them + */ + pbuf2 = buf2; + for ( i=0; i +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our parameters ... we take a copy of the mask argument, plus we make a + * smaller version with the zeros squeezed out. + */ +typedef struct { + IMAGE *in; + IMAGE *out; + DOUBLEMASK *mask; /* Copy of mask arg */ + + int nnz; /* Number of non-zero mask elements */ + double *coeff; /* Array of non-zero mask coefficients */ +} Conv; + +static int +conv_destroy( Conv *conv ) +{ + if( conv->mask ) { + (void) im_free_dmask( conv->mask ); + conv->mask = NULL; + } + + return( 0 ); +} + +static Conv * +conv_new( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + Conv *conv = IM_NEW( out, Conv ); + const int ne = mask->xsize * mask->ysize; + int i; + + if( !conv ) + return( NULL ); + + conv->in = in; + conv->out = out; + conv->mask = NULL; + conv->nnz = 0; + conv->coeff = NULL; + + if( im_add_close_callback( out, + (im_callback_fn) conv_destroy, conv, NULL ) || + !(conv->coeff = IM_ARRAY( out, ne, double )) || + !(conv->mask = im_dup_dmask( mask, "conv_mask" )) ) + return( NULL ); + + /* Find non-zero mask elements. + */ + for( i = 0; i < ne; i++ ) + if( mask->coeff[i] ) + conv->coeff[conv->nnz++] = mask->coeff[i]; + + return( conv ); +} + +/* Our sequence value. + */ +typedef struct { + Conv *conv; + REGION *ir; /* Input region */ + + int *offsets; /* Offsets for each non-zero matrix element */ + PEL **pts; /* Per-non-zero mask element image pointers */ +} ConvSequence; + +/* Free a sequence value. + */ +static int +stop_conv( ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + /* Free attached objects. + */ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Convolution start function. + */ +static void * +start_conv( IMAGE *out, IMAGE *in, Conv *conv ) +{ + ConvSequence *seq = IM_NEW( out, ConvSequence ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->conv = conv; + seq->ir = NULL; + seq->pts = NULL; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + seq->offsets = IM_ARRAY( out, conv->nnz, int ); + seq->pts = IM_ARRAY( out, conv->nnz, PEL * ); + if( !seq->ir || !seq->offsets || !seq->pts ) { + stop_conv( seq, in, conv ); + return( NULL ); + } + + return( (void *) seq ); +} + +#define INNER sum += *t++ * (*p++)[x] + +#define CONV_FLOAT( ITYPE, OTYPE ) { \ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + double sum = 0; \ + double *t = conv->coeff; \ + ITYPE **p = (ITYPE **) seq->pts; \ + \ + z = 0; \ + IM_UNROLL( conv->nnz, INNER ); \ + \ + sum = (sum / mask->scale) + mask->offset; \ + \ + q[x] = sum; \ + } \ +} + +/* Convolve! + */ +static int +gen_conv( REGION *or, ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + REGION *ir = seq->ir; + DOUBLEMASK *mask = conv->mask; + + Rect *r = &or->valid; + Rect s; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + + int x, y, z, i; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += mask->xsize - 1; + s.height += mask->ysize - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Fill offset array. + */ + z = 0; + for( i = 0, y = 0; y < mask->ysize; y++ ) + for( x = 0; x < mask->xsize; x++, i++ ) + if( mask->coeff[i] ) + seq->offsets[z++] = + IM_REGION_ADDR( ir, x + le, y + to ) - + IM_REGION_ADDR( ir, le, to ); + + for( y = to; y < bo; y++ ) { + /* Init pts for this line of PELs. + */ + for( z = 0; z < conv->nnz; z++ ) + seq->pts[z] = seq->offsets[z] + + (PEL *) IM_REGION_ADDR( ir, le, y ); + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + CONV_FLOAT( unsigned char, float ); break; + case IM_BANDFMT_CHAR: + CONV_FLOAT( signed char, float ); break; + case IM_BANDFMT_USHORT: + CONV_FLOAT( unsigned short, float ); break; + case IM_BANDFMT_SHORT: + CONV_FLOAT( signed short, float ); break; + case IM_BANDFMT_UINT: + CONV_FLOAT( unsigned int, float ); break; + case IM_BANDFMT_INT: + CONV_FLOAT( signed int, float ); break; + case IM_BANDFMT_FLOAT: + CONV_FLOAT( float, float ); break; + case IM_BANDFMT_DOUBLE: + CONV_FLOAT( double, double ); break; + + default: + assert( 0 ); + } + } + + return( 0 ); +} + +int +im_convf_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + Conv *conv; + + /* Check parameters. + */ + if( !in || in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_errormsg( "im_convf: input non-complex uncoded please!"); + return( -1 ); + } + if( !mask || mask->xsize > 1000 || mask->ysize > 1000 || + mask->xsize <= 0 || mask->ysize <= 0 || !mask->coeff || + mask->scale == 0 ) { + im_errormsg( "im_convf: nonsense mask parameters" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( !(conv = conv_new( in, out, mask )) ) + return( -1 ); + + /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_isint( in ) ) { + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + } + out->Xsize -= mask->xsize - 1; + out->Ysize -= mask->ysize - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_convf: image too small for mask" ); + return( -1 ); + } + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + if( im_generate( out, start_conv, gen_conv, stop_conv, in, conv ) ) + return( -1 ); + + out->Xoffset = -mask->xsize / 2; + out->Yoffset = -mask->ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_convf( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + IMAGE *t1 = im_open_local( out, "im_convf intermediate", "p" ); + + if( !t1 || + im_embed( in, t1, 1, mask->xsize / 2, mask->ysize / 2, + in->Xsize + mask->xsize - 1, + in->Ysize + mask->ysize - 1 ) || + im_convf_raw( t1, out, mask ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/convolution/im_convsep.c b/libsrc/convolution/im_convsep.c new file mode 100644 index 00000000..19cd010a --- /dev/null +++ b/libsrc/convolution/im_convsep.c @@ -0,0 +1,429 @@ +/* @(#) Convolve an image with a seperable (1xN, or Nx1) INTMASK. Image can + * @(#) have any number of bands, any non-complex type. Size and type of + * @(#) output image matches type of input image. + * @(#) + * @(#) int + * @(#) im_convsep( in, out, mask ) + * @(#) IMAGE *in, *out; + * @(#) INTMASK *mask; + * @(#) + * @(#) Also: im_convsep_raw(). As above, but does not add a black border. + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 29/04/1991 + * Modified on: 29/4/93 K.Martinez for Sys5 + * 9/3/01 JC + * - rewritten using im_conv() + * 27/7/01 JC + * - rejects masks with scale == 0 + * 7/4/04 + * - now uses im_embed() with edge stretching on the input, not + * the output + * - sets Xoffset / Yoffset + * 21/4/04 + * - scale down int convolves at 1/2 way mark, much less likely to integer + * overflow on intermediates + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our parameters ... we take a copy of the mask argument. + */ +typedef struct { + IMAGE *in; + IMAGE *out; + INTMASK *mask; /* Copy of mask arg */ + + int size; /* N for our 1xN or Nx1 mask */ + int scale; /* Our scale ... we have to square mask->scale */ + + int underflow; /* Global underflow/overflow counts */ + int overflow; +} Conv; + +/* End of evaluation --- print overflows and underflows. + */ +static int +conv_destroy( Conv *conv ) +{ + /* Print underflow/overflow count. + */ + if( conv->overflow || conv->underflow ) + im_warn( "im_convsep", _( "%d overflows and %d underflows " + "detected" ), conv->overflow, conv->underflow ); + + if( conv->mask ) { + (void) im_free_imask( conv->mask ); + conv->mask = NULL; + } + + return( 0 ); +} + +static Conv * +conv_new( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + Conv *conv = IM_NEW( out, Conv ); + + if( !conv ) + return( NULL ); + + conv->in = in; + conv->out = out; + conv->mask = NULL; + conv->size = mask->xsize * mask->ysize; + conv->scale = mask->scale * mask->scale; + conv->underflow = 0; + conv->overflow = 0; + + if( im_add_close_callback( out, + (im_callback_fn) conv_destroy, conv, NULL ) || + !(conv->mask = im_dup_imask( mask, "conv_mask" )) ) + return( NULL ); + + return( conv ); +} + +/* Our sequence value. + */ +typedef struct { + Conv *conv; + REGION *ir; /* Input region */ + + PEL *sum; /* Line buffer */ + + int underflow; /* Underflow/overflow counts */ + int overflow; +} ConvSequence; + +/* Free a sequence value. + */ +static int +stop_conv( ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + /* Add local under/over counts to global counts. + */ + conv->overflow += seq->overflow; + conv->underflow += seq->underflow; + + IM_FREEF( im_region_free, seq->ir ); + + return( 0 ); +} + +/* Convolution start function. + */ +static void * +start_conv( IMAGE *out, IMAGE *in, Conv *conv ) +{ + ConvSequence *seq = IM_NEW( out, ConvSequence ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->conv = conv; + seq->ir = NULL; + seq->sum = NULL; + seq->underflow = 0; + seq->overflow = 0; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + if( im_isint( conv->out ) ) + seq->sum = (PEL *) + IM_ARRAY( out, IM_IMAGE_N_ELEMENTS( in ), int ); + else + seq->sum = (PEL *) + IM_ARRAY( out, IM_IMAGE_N_ELEMENTS( in ), double ); + if( !seq->ir || !seq->sum ) { + stop_conv( seq, in, conv ); + return( NULL ); + } + + return( (void *) seq ); +} + +/* What we do for every point in the mask, for each pixel. + */ +#define VERTICAL_CONV { z -= 1; li -= lskip; sum += coeff[z] * vfrom[li]; } +#define HORIZONTAL_CONV { z -= 1; li -= bands; sum += coeff[z] * hfrom[li]; } + +/* INT and FLOAT inner loops. + */ +#define CONV_INT( TYPE, IM_CLIP ) { \ + TYPE *vfrom; \ + int *vto; \ + int *hfrom; \ + TYPE *hto; \ + \ + /* Convolve to sum array. We convolve the full width of \ + * this input line. \ + */ \ + vfrom = (TYPE *) IM_REGION_ADDR( ir, le, y ); \ + vto = (int *) seq->sum; \ + for( x = 0; x < isz; x++ ) { \ + int sum; \ + \ + z = conv->size; \ + li = lskip * z; \ + sum = 0; \ + \ + IM_UNROLL( z, VERTICAL_CONV ); \ + \ + sum = ((sum + rounding) / mask->scale) + mask->offset; \ + \ + vto[x] = sum; \ + vfrom += 1; \ + } \ + \ + /* Convolve sums to output. \ + */ \ + hfrom = (int *) seq->sum; \ + hto = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + for( x = 0; x < osz; x++ ) { \ + int sum; \ + \ + z = conv->size; \ + li = bands * z; \ + sum = 0; \ + \ + IM_UNROLL( z, HORIZONTAL_CONV ); \ + \ + sum = ((sum + rounding) / mask->scale) + mask->offset; \ + \ + IM_CLIP; \ + \ + hto[x] = sum; \ + hfrom += 1; \ + } \ +} + +#define CONV_FLOAT( TYPE ) { \ + TYPE *vfrom; \ + double *vto; \ + double *hfrom; \ + TYPE *hto; \ + \ + /* Convolve to sum array. We convolve the full width of \ + * this input line. \ + */ \ + vfrom = (TYPE *) IM_REGION_ADDR( ir, le, y ); \ + vto = (double *) seq->sum; \ + for( x = 0; x < isz; x++ ) { \ + double sum; \ + \ + z = conv->size; \ + li = lskip * z; \ + sum = 0; \ + \ + IM_UNROLL( z, VERTICAL_CONV ); \ + \ + vto[x] = sum; \ + vfrom += 1; \ + } \ + \ + /* Convolve sums to output. \ + */ \ + hfrom = (double *) seq->sum; \ + hto = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + for( x = 0; x < osz; x++ ) { \ + double sum; \ + \ + z = conv->size; \ + li = bands * z; \ + sum = 0; \ + \ + IM_UNROLL( z, HORIZONTAL_CONV ); \ + \ + sum = (sum / conv->scale) + mask->offset; \ + \ + hto[x] = sum; \ + hfrom += 1; \ + } \ +} + +/* Convolve! + */ +static int +gen_conv( REGION *or, ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + REGION *ir = seq->ir; + INTMASK *mask = conv->mask; + int rounding = (mask->scale + 1)/2; + int bands = in->Bands; + int *coeff = conv->mask->coeff; + + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int osz = IM_REGION_N_ELEMENTS( or ); + + Rect s; + int lskip; + int isz; + int x, y, z, li; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += conv->size - 1; + s.height += conv->size - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + lskip = IM_REGION_LSKIP( ir ) / IM_IMAGE_SIZEOF_ELEMENT( in ); + isz = IM_REGION_N_ELEMENTS( ir ); + + for( y = to; y < bo; y++ ) { + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + CONV_INT( unsigned char, IM_CLIP_UCHAR( sum, seq ) ); + break; + case IM_BANDFMT_CHAR: + CONV_INT( signed char, IM_CLIP_CHAR( sum, seq ) ); + break; + case IM_BANDFMT_USHORT: + CONV_INT( unsigned short, IM_CLIP_USHORT( sum, seq ) ); + break; + case IM_BANDFMT_SHORT: + CONV_INT( signed short, IM_CLIP_SHORT( sum, seq ) ); + break; + case IM_BANDFMT_UINT: + CONV_INT( unsigned int, IM_CLIP_NONE( sum, seq ) ); + break; + case IM_BANDFMT_INT: + CONV_INT( signed int, IM_CLIP_NONE( sum, seq ) ); + break; + case IM_BANDFMT_FLOAT: + CONV_FLOAT( float ); + break; + case IM_BANDFMT_DOUBLE: + CONV_FLOAT( double ); + break; + + default: + assert( 0 ); + } + } + + return( 0 ); +} + +int +im_convsep_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + Conv *conv; + + /* Check parameters. + */ + if( !in || in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_error( "im_convsep", _( "non-complex uncoded only" ) ); + return( -1 ); + } + if( !mask || mask->xsize > 1000 || mask->ysize > 1000 || + mask->xsize <= 0 || mask->ysize <= 0 || !mask->coeff || + mask->scale == 0 ) { + im_error( "im_convsep", _( "nonsense mask parameters" ) ); + return( -1 ); + } + if( mask->xsize != 1 && mask->ysize != 1 ) { + im_error( "im_convsep", _( "expect 1xN or Nx1 input mask" ) ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( !(conv = conv_new( in, out, mask )) ) + return( -1 ); + + /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= conv->size - 1; + out->Ysize -= conv->size - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_error( "im_convsep", _( "image too small for mask" ) ); + return( -1 ); + } + + /* SMALLTILE seems the fastest in benchmarks. + */ + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) || + im_generate( out, start_conv, gen_conv, stop_conv, in, conv ) ) + return( -1 ); + + out->Xoffset = -mask->xsize / 2; + out->Yoffset = -mask->ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_convsep( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + IMAGE *t1 = im_open_local( out, "im_convsep intermediate", "p" ); + int size = mask->xsize * mask->ysize; + + if( !t1 || + im_embed( in, t1, 1, size / 2, size / 2, + in->Xsize + size - 1, + in->Ysize + size - 1 ) || + im_convsep_raw( t1, out, mask ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/convolution/im_convsepf.c b/libsrc/convolution/im_convsepf.c new file mode 100644 index 00000000..4eb1bf12 --- /dev/null +++ b/libsrc/convolution/im_convsepf.c @@ -0,0 +1,347 @@ +/* @(#) Convolve an image with a DOUBLEMASK. Image can have any number of bands, + * @(#) any non-complex type. Output is IM_BANDFMT_FLOAT for all non-complex inputs + * @(#) except IM_BANDFMT_DOUBLE, which gives IM_BANDFMT_DOUBLE. + * @(#) Separable mask of sizes 1xN or Nx1 + * @(#) + * @(#) int im_convsepf( in, out, mask ) + * @(#) IMAGE *in, *out; + * @(#) DOUBLEMASK *mask; details in mask.h + * @(#) + * @(#) Returns either 0 (sucess) or -1 (fail) + * @(#) Picture can have any number of channels (max 64). + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 29/04/1991 + * Modified on: 29/4/93 K.Martinez for sys5 + * 9/3/01 JC + * - rewritten using im_conv() + * 7/4/04 + * - now uses im_embed() with edge stretching on the input, not + * the output + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our parameters ... we take a copy of the mask argument. + */ +typedef struct { + IMAGE *in; + IMAGE *out; + DOUBLEMASK *mask; /* Copy of mask arg */ + + int size; /* N for our 1xN or Nx1 mask */ + int scale; /* Our scale ... we have to ^2 mask->scale */ +} Conv; + +/* End of evaluation. + */ +static int +conv_destroy( Conv *conv ) +{ + if( conv->mask ) { + (void) im_free_dmask( conv->mask ); + conv->mask = NULL; + } + + return( 0 ); +} + +static Conv * +conv_new( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + Conv *conv = IM_NEW( out, Conv ); + + if( !conv ) + return( NULL ); + + conv->in = in; + conv->out = out; + conv->mask = NULL; + conv->size = mask->xsize * mask->ysize; + conv->scale = mask->scale * mask->scale; + + if( im_add_close_callback( out, + (im_callback_fn) conv_destroy, conv, NULL ) || + !(conv->mask = im_dup_dmask( mask, "conv_mask" )) ) + return( NULL ); + + return( conv ); +} + +/* Our sequence value. + */ +typedef struct { + Conv *conv; + REGION *ir; /* Input region */ + + PEL *sum; /* Line buffer */ +} ConvSequence; + +/* Free a sequence value. + */ +static int +stop_conv( ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + IM_FREEF( im_region_free, seq->ir ); + + return( 0 ); +} + +/* Convolution start function. + */ +static void * +start_conv( IMAGE *out, IMAGE *in, Conv *conv ) +{ + ConvSequence *seq = IM_NEW( out, ConvSequence ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->conv = conv; + seq->ir = NULL; + seq->sum = NULL; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + if( im_isint( conv->out ) ) + seq->sum = (PEL *) + IM_ARRAY( out, IM_IMAGE_N_ELEMENTS( in ), int ); + else + seq->sum = (PEL *) + IM_ARRAY( out, IM_IMAGE_N_ELEMENTS( in ), double ); + if( !seq->ir || !seq->sum ) { + stop_conv( seq, in, conv ); + return( NULL ); + } + + return( (void *) seq ); +} + +/* What we do for every point in the mask, for each pixel. + */ +#define VERTICAL_CONV { z -= 1; li -= lskip; sum += coeff[z] * vfrom[li]; } +#define HORIZONTAL_CONV { z -= 1; li -= bands; sum += coeff[z] * hfrom[li]; } + +#define CONV_FLOAT( ITYPE, OTYPE ) { \ + ITYPE *vfrom; \ + double *vto; \ + double *hfrom; \ + OTYPE *hto; \ + \ + /* Convolve to sum array. We convolve the full width of \ + * this input line. \ + */ \ + vfrom = (ITYPE *) IM_REGION_ADDR( ir, le, y ); \ + vto = (double *) seq->sum; \ + for( x = 0; x < isz; x++ ) { \ + double sum; \ + \ + z = conv->size; \ + li = lskip * z; \ + sum = 0; \ + \ + IM_UNROLL( z, VERTICAL_CONV ); \ + \ + vto[x] = sum; \ + vfrom += 1; \ + } \ + \ + /* Convolve sums to output. \ + */ \ + hfrom = (double *) seq->sum; \ + hto = (OTYPE *) IM_REGION_ADDR( or, le, y ); \ + for( x = 0; x < osz; x++ ) { \ + double sum; \ + \ + z = conv->size; \ + li = bands * z; \ + sum = 0; \ + \ + IM_UNROLL( z, HORIZONTAL_CONV ); \ + \ + sum = (sum / conv->scale) + mask->offset; \ + \ + hto[x] = sum; \ + hfrom += 1; \ + } \ +} + +/* Convolve! + */ +static int +gen_conv( REGION *or, ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + REGION *ir = seq->ir; + DOUBLEMASK *mask = conv->mask; + double *coeff = conv->mask->coeff; + int bands = in->Bands; + + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int osz = IM_REGION_N_ELEMENTS( or ); + + Rect s; + int lskip; + int isz; + int x, y, z, li; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += conv->size - 1; + s.height += conv->size - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + lskip = IM_REGION_LSKIP( ir ) / IM_IMAGE_SIZEOF_ELEMENT( in ); + isz = IM_REGION_N_ELEMENTS( ir ); + + for( y = to; y < bo; y++ ) { + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + CONV_FLOAT( unsigned char, float ); break; + case IM_BANDFMT_CHAR: + CONV_FLOAT( signed char, float ); break; + case IM_BANDFMT_USHORT: + CONV_FLOAT( unsigned short, float ); break; + case IM_BANDFMT_SHORT: + CONV_FLOAT( signed short, float ); break; + case IM_BANDFMT_UINT: + CONV_FLOAT( unsigned int, float ); break; + case IM_BANDFMT_INT: + CONV_FLOAT( signed int, float ); break; + case IM_BANDFMT_FLOAT: + CONV_FLOAT( float, float ); break; + case IM_BANDFMT_DOUBLE: + CONV_FLOAT( double, double ); break; + + default: + assert( 0 ); + } + } + + return( 0 ); +} + +int +im_convsepf_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + Conv *conv; + + /* Check parameters. + */ + if( !in || in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_error( "im_convsepf", _( "non-complex uncoded only" ) ); + return( -1 ); + } + if( !mask || mask->xsize > 1000 || mask->ysize > 1000 || + mask->xsize <= 0 || mask->ysize <= 0 || !mask->coeff || + mask->scale == 0 ) { + im_error( "im_convsepf", _( "bad mask parameters" ) ); + return( -1 ); + } + if( mask->xsize != 1 && mask->ysize != 1 ) { + im_error( "im_convsepf", _( "expect 1xN or Nx1 input mask" ) ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( !(conv = conv_new( in, out, mask )) ) + return( -1 ); + + /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_isint( in ) ) { + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + } + out->Xsize -= conv->size - 1; + out->Ysize -= conv->size - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_error( "im_convsepf", _( "image too small for mask" ) ); + return( -1 ); + } + + /* SMALLTILE seems fastest. + */ + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) || + im_generate( out, start_conv, gen_conv, stop_conv, in, conv ) ) + return( -1 ); + + out->Xoffset = -conv->size / 2; + out->Yoffset = -conv->size / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_convsepf( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + IMAGE *t1 = im_open_local( out, "im_convsepf intermediate", "p" ); + int size = mask->xsize * mask->ysize; + + if( !t1 || + im_embed( in, t1, 1, size / 2, size / 2, + in->Xsize + size - 1, + in->Ysize + size - 1 ) || + im_convsepf_raw( t1, out, mask ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/convolution/im_convsub.c b/libsrc/convolution/im_convsub.c new file mode 100644 index 00000000..738d240f --- /dev/null +++ b/libsrc/convolution/im_convsub.c @@ -0,0 +1,264 @@ +/* @(#) Function which convolves and subsamples VASARI format picture + * @(#) with a mask stored in a file argument. + * @(#) + * @(#) int im_convsub( in, out, mask, xskip, yskip ) + * @(#) IMAGE *in, *out; + * @(#) INTMASK *mask; details in vips.h + * @(#) int xskip, yskip; is the subsamping factor along both directions + * @(#) + * @(#) Returns either 0 (sucess) or -1 (fail) + * @(#) + * @(#) Picture can have any number of channels (max 64). + * @(#) It is assummed that the output picture is subsampled on + * @(#) both directions by a factor of xskip horizontally and yskip vertically. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 29/04/1991 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + + +int im_convsub( in, out, m, xskip, yskip ) +IMAGE *in, *out; +INTMASK *m; +int xskip, yskip; +{ + + + int x; /* horizontal direction */ + int y; /* vertical direction */ + int n_clipped = 0; + int p_clipped = 0; + int i, b; + PEL **pnts, **cpnt1s, **cpnt2s; /* to keep pointers to data */ + PEL **pnt, **cpnt1, **cpnt2; /* to keep pointers to data */ + PEL *input, *line, *cpline; + int *pm; /* pointer to mask coefficients */ + int count; /* no of non zero elms of the original mask */ + int *newm, *pnewm; /* pointer to non zero mask coefficients */ + int os; /* size of an input line of data */ + int ms; /* is m->xsize * m->ysize */ + int **lut_orig, **lut; + int lutcnt = 0; + int rounding, sum; + int tempsize; + +/* Check input, output and vars */ + if ((xskip < 1)||(yskip < 1)) + { + im_errormsg("im_convsub: xskip and yskip must be >= 1"); + return(-1); + } + if (im_iocheck(in, out) == -1) + { im_errormsg("im_convsub: Unable to im_iocheck"); return(-1); } + + if ( (in->Coding != IM_CODING_NONE)||(in->Bbits != IM_BBITS_BYTE) + ||(in->BandFmt != IM_BANDFMT_UCHAR) ) + { + im_errormsg("im_convsub:input should be unsigned char uncoded"); + return(-1); + } + +/* Prepare output */ + if (im_cp_desc(out, in) == -1) + { im_errormsg("im_convsub: im_cp_desc failed"); return(-1); } + tempsize = in->Xsize/xskip; + while ( 1 ) + { + if ( tempsize * xskip + m->xsize < in->Xsize ) + break; + else + tempsize--; + if ( tempsize < 0 ) + break; + } + out->Xsize = tempsize; + tempsize = in->Ysize/yskip; + while ( 1 ) + { + if ( tempsize * yskip + m->ysize < in->Ysize ) + break; + else + tempsize--; + if ( tempsize < 0 ) + break; + } + out->Ysize = tempsize; + if ( ( out->Xsize < 2 )||( out->Ysize < 2 ) ) + {im_errormsg("im_convsub: too small output sizes");return(-1); } + + if( im_setupout(out) == -1) + {im_errormsg("im_convsub: im_setupout failed"); return(-1); } + +/* Malloc one line of output data */ + os = out->Xsize * out->Bands; + if ( (line=(PEL*)calloc( (unsigned)os, sizeof(char))) == NULL) + { im_errormsg("im_convsub: unable to calloc(1)"); return(-1); } + +/* Malloc pointers and put them at correct location */ + ms = m->xsize * m->ysize; + count = 0; /* exclude the non-zero elms */ + pm = m->coeff; + for ( i=0; idata; + pm = m->coeff; + pnewm = newm; + for (y=0; yysize; y++) + { + for (x=0; xxsize; x++) + { + if ( *pm != 0 ) + { + *pnewm++ = *pm; + pnt[i] = (input +(x + y*in->Xsize) * in->Bands); + i++; + } + pm++; + } + } + + if ( i != count ) + { im_errormsg("im_convsub: impossible state"); return(-1); } + +/* Malloc pointers; not all lut_orig are used necessarily */ + lut_orig = (int**)calloc((unsigned)count, sizeof(int**) ); + lut = (int**)calloc((unsigned)count, sizeof(int**) ); + if ( (lut == NULL) || (lut_orig == NULL) ) + { im_errormsg("im_conv: unable to calloc(1)"); return(-1); } + +/* Create luts; count is needed for freeing pointers. Not all lut_orig are used + * if zero elms are detected. + */ + if ( im__create_int_luts(newm, count, lut_orig, lut, &lutcnt ) == -1 ) + { + im_errormsg("im_convsub: im_create_int_luts failed"); + return(-1); + } + + rounding = m->scale/2; + +/* Output out->Ysize processed lines */ + for(y=0; y < out->Ysize; y++) + { + cpline = line; + for (i=0; iXsize * in->Bands * yskip ); + } + + /* process out->Xsize points */ + for( x = 0; x < out->Xsize; x++ ) + { + for (i=0; iBands; + } + for ( b=0; bBands; b++ ) + { + sum = 0; + for (i=0; iscale ) + m->offset; + + if ( sum < (int)0 ) + { n_clipped++; sum = (int)0; } + else if ( sum > (int)255) + { p_clipped++; sum = (int)255; } + *cpline++ = (unsigned char)sum; + } + } + + /* Output the calculated line */ + if ( im_writeline(y, out, (PEL*)line) == -1 ) + { + im_errormsg("im_convsub: im_writeline failed(2)"); + free((char*)line); free((char*)newm); + free((char*)pnts); + free((char*)cpnt1s); free((char*)cpnt2s); + for ( i=0; i +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Per-call struct. + */ +typedef struct _Embed { + IMAGE *in; + IMAGE *out; + int flag; + int x, y, w, h; + + /* Geometry calculations. + */ + Rect rout; /* Whole output area */ + Rect rsub; /* Rect occupied by image */ + + /* The 8 border pieces. The 4 borders strictly up/down/left/right of + * the main image, and the 4 corner pieces. + */ + Rect border[8]; +} Embed; + +/* Paint 'value' into an area of a region. 0/255 for value usually. + */ +static void +embed_paint_rect( REGION *or, Rect *r, int value ) +{ + Rect ovl; + + im_rect_intersectrect( r, &or->valid, &ovl ); + if( !im_rect_isempty( &ovl ) ) { + PEL *q = (PEL *) IM_REGION_ADDR( or, ovl.left, ovl.top ); + int wd = ovl.width * IM_IMAGE_SIZEOF_PEL( or->im ); + int ls = IM_REGION_LSKIP( or ); + int y; + + for( y = 0; y < ovl.height; y++ ) { + memset( (char *) q, value, wd ); + q += ls; + } + } +} + +/* r is the bit we are trying to paint, guaranteed to be entirely within + * border area i. Set out to be the edge of the image we need to paint the + * pixels in r. + */ +static void +embed_find_edge( Embed *embed, Rect *r, int i, Rect *out ) +{ + /* Expand the border by 1 pixel, intersect with the image area, and we + * get the edge. Usually too much though: eg. we could make the entire + * right edge. + */ + *out = embed->border[i]; + im_rect_marginadjust( out, 1 ); + im_rect_intersectrect( out, &embed->rsub, out ); + + /* Usually too much though: eg. we could make the entire + * right edge. If we're strictly up/down/left/right of the image, we + * can trim. + */ + if( i == 0 || i == 2 ) { + Rect extend; + + /* Above or below. + */ + extend = *r; + extend.top = 0; + extend.height = embed->h; + im_rect_intersectrect( out, &extend, out ); + } + if( i == 1 || i == 3 ) { + Rect extend; + + /* Left or right. + */ + extend = *r; + extend.left = 0; + extend.width = embed->w; + im_rect_intersectrect( out, &extend, out ); + } +} + +/* Copy a single pixel sideways into a line of pixels. + */ +static void +embed_copy_pixel( Embed *embed, PEL *q, PEL *p, int n ) +{ + const int bs = IM_IMAGE_SIZEOF_PEL( embed->in ); + + int x, b; + + for( x = 0; x < n; x++ ) + for( b = 0; b < bs; b++ ) + *q++ = p[b]; +} + +/* Paint r of region or. It's a border area, lying entirely within + * embed->border[i]. p points to the top-left source pixel to fill with. + * plsk is the line stride. + */ +static void +embed_paint_edge( Embed *embed, REGION *or, int i, Rect *r, PEL *p, int plsk ) +{ + const int bs = IM_IMAGE_SIZEOF_PEL( embed->in ); + + Rect todo; + PEL *q; + int y; + + /* Pixels left to paint. + */ + todo = *r; + + /* Corner pieces ... copy the single pixel to paint the top line of + * todo, then use the line copier below to paint the rest of it. + */ + if( i > 3 ) { + q = (PEL *) IM_REGION_ADDR( or, todo.left, todo.top ); + embed_copy_pixel( embed, q, p, todo.width ); + + p = q; + todo.top += 1; + todo.height -= 1; + } + + if( i == 1 || i == 3 ) { + /* Vertical line of pixels to copy. + */ + for( y = 0; y < todo.height; y++ ) { + q = (PEL *) IM_REGION_ADDR( or, + todo.left, todo.top + y ); + embed_copy_pixel( embed, q, p, todo.width ); + p += plsk; + } + } + else { + /* Horizontal line of pixels to copy. + */ + for( y = 0; y < todo.height; y++ ) { + q = (PEL *) IM_REGION_ADDR( or, + todo.left, todo.top + y ); + memcpy( q, p, bs * todo.width ); + } + } +} + +static int +embed_gen( REGION *or, REGION *ir, IMAGE *in, Embed *embed ) +{ + Rect *r = &or->valid; + + Rect ovl; + int i; + PEL *p; + int plsk; + + /* Entirely within the input image? Generate the subimage and copy + * pointers. + */ + if( im_rect_includesrect( &embed->rsub, r ) ) { + Rect need; + + need = *r; + need.left -= embed->x; + need.top -= embed->y; + if( im_prepare( ir, &need ) || + im_region_region( or, ir, r, need.left, need.top ) ) + return( -1 ); + + return( 0 ); + } + + /* Does any of the input image appear in the area we have been asked + * to make? Paste it in. + */ + im_rect_intersectrect( r, &embed->rsub, &ovl ); + if( !im_rect_isempty( &ovl ) ) { + /* Paint the bits coming from the input image. + */ + ovl.left -= embed->x; + ovl.top -= embed->y; + if( im_prepare_to( ir, or, &ovl, + ovl.left + embed->x, ovl.top + embed->y ) ) + return( -1 ); + ovl.left += embed->x; + ovl.top += embed->y; + } + + switch( embed->flag ) { + case 0: + case 4: + /* Paint the borders a solid value. + */ + for( i = 0; i < 8; i++ ) + embed_paint_rect( or, &embed->border[i], + embed->flag == 0 ? 0 : 255 ); + break; + + case 1: + /* Extend the borders. + */ + for( i = 0; i < 8; i++ ) { + Rect todo; + Rect edge; + + im_rect_intersectrect( r, &embed->border[i], &todo ); + if( !im_rect_isempty( &todo ) ) { + embed_find_edge( embed, &todo, i, &edge ); + + /* Did we paint any of the input image? If we + * did, we can fetch the edge pixels from + * that. + */ + if( !im_rect_isempty( &ovl ) ) { + p = (PEL *) IM_REGION_ADDR( or, + edge.left, edge.top ); + plsk = IM_REGION_LSKIP( or ); + } + else { + /* No pixels painted ... fetch + * directly from the input image. + */ + edge.left -= embed->x; + edge.top -= embed->y; + if( im_prepare( ir, &edge ) ) + return( -1 ); + p = (PEL *) IM_REGION_ADDR( ir, + edge.left, edge.top ); + plsk = IM_REGION_LSKIP( ir ); + } + + embed_paint_edge( embed, + or, i, &todo, p, plsk ); + } + } + + break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +static Embed * +embed_new( IMAGE *in, IMAGE *out, int flag, int x, int y, int w, int h ) +{ + Embed *embed = IM_NEW( out, Embed ); + Rect want; + + /* Take a copy of args. + */ + embed->in = in; + embed->out = out; + embed->flag = flag; + embed->x = x; + embed->y = y; + embed->w = w; + embed->h = h; + + /* Whole output area. + */ + embed->rout.left = 0; + embed->rout.top = 0; + embed->rout.width = out->Xsize; + embed->rout.height = out->Ysize; + + /* Rect occupied by image (can be clipped to nothing). + */ + want.left = x; + want.top = y; + want.width = in->Xsize; + want.height = in->Ysize; + im_rect_intersectrect( &want, &embed->rout, &embed->rsub ); + + /* FIXME ... actually, it can't. embed_find_edge() will fail if rsub + * is empty. Make this more general at some point and remove this + * test. + */ + if( im_rect_isempty( &embed->rsub ) ) { + im_error( "im_embed", _( "bad dimensions" ) ); + return( NULL ); + } + + /* Edge rects of new pixels ... top, right, bottom, left. Order + * important. Can be empty. + */ + embed->border[0].left = embed->rsub.left; + embed->border[0].top = 0; + embed->border[0].width = embed->rsub.width; + embed->border[0].height = embed->rsub.top; + + embed->border[1].left = IM_RECT_RIGHT( &embed->rsub ); + embed->border[1].top = embed->rsub.top; + embed->border[1].width = out->Xsize - IM_RECT_RIGHT( &embed->rsub ); + embed->border[1].height = embed->rsub.height; + + embed->border[2].left = embed->rsub.left; + embed->border[2].top = IM_RECT_BOTTOM( &embed->rsub ); + embed->border[2].width = embed->rsub.width; + embed->border[2].height = out->Ysize - IM_RECT_BOTTOM( &embed->rsub ); + + embed->border[3].left = 0; + embed->border[3].top = embed->rsub.top; + embed->border[3].width = embed->rsub.left; + embed->border[3].height = embed->rsub.height; + + /* Corner rects. Top-left, top-right, bottom-right, bottom-left. Order + * important. + */ + embed->border[4].left = 0; + embed->border[4].top = 0; + embed->border[4].width = embed->rsub.left; + embed->border[4].height = embed->rsub.top; + + embed->border[5].left = IM_RECT_RIGHT( &embed->rsub ); + embed->border[5].top = 0; + embed->border[5].width = out->Xsize - IM_RECT_RIGHT( &embed->rsub ); + embed->border[5].height = embed->rsub.top; + + embed->border[6].left = IM_RECT_RIGHT( &embed->rsub ); + embed->border[6].top = IM_RECT_BOTTOM( &embed->rsub ); + embed->border[6].width = out->Xsize - IM_RECT_RIGHT( &embed->rsub ); + embed->border[6].height = out->Ysize - IM_RECT_BOTTOM( &embed->rsub ); + + embed->border[7].left = 0; + embed->border[7].top = IM_RECT_BOTTOM( &embed->rsub ); + embed->border[7].width = embed->rsub.left; + embed->border[7].height = out->Ysize - IM_RECT_BOTTOM( &embed->rsub ); + + return( embed ); +} + +/* Do flag 0/4 (black/white) and 1 (extend). + */ +static int +embed( IMAGE *in, IMAGE *out, int flag, int x, int y, int w, int h ) +{ + Embed *embed; + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = w; + out->Ysize = h; + + if( !(embed = embed_new( in, out, flag, x, y, w, h )) || + im_demand_hint( out, IM_SMALLTILE, in, NULL ) || + im_generate( out, + im_start_one, embed_gen, im_stop_one, + in, embed ) ) + return( -1 ); + + return( 0 ); +} + +int +im_embed( IMAGE *in, IMAGE *out, int flag, int x, int y, int w, int h ) +{ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_embed", _( "unknown image coding type" ) ); + return( -1 ); + } + if( flag < 0 || flag > 4 ) { + im_error( "im_embed", _( "unknown flag" ) ); + return( -1 ); + } + if( w <= 0 || h <= 0 ) { + im_error( "im_embed", _( "bad dimensions" ) ); + return( -1 ); + } + + /* nip can generate this quite often ... just copy. + */ + if( x == 0 && y == 0 && w == in->Xsize && h == in->Ysize ) + return( im_copy( in, out ) ); + + switch( flag ) { + case 0: + case 1: + case 4: + if( embed( in, out, flag, x, y, w, h ) ) + return( -1 ); + break; + + case 2: +{ + /* Clock arithmetic: we want negative x/y to wrap around + * nicely. + */ + const int nx = x < 0 ? + -x % in->Xsize : + in->Xsize - x % in->Xsize; + const int ny = y < 0 ? + -y % in->Ysize : + in->Ysize - y % in->Ysize; + + IMAGE *t[1]; + + if( im_open_local_array( out, t, 1, "embed-flag2", "p" ) || + im_replicate( in, t[0], + w / in->Xsize + 2, h / in->Ysize + 2 ) || + im_extract_area( t[0], out, nx, ny, w, h ) ) + return( -1 ); +} + break; + + case 3: +{ + /* As case 2, but the tiles are twice the size because of + * mirroring. + */ + const int w2 = in->Xsize * 2; + const int h2 = in->Ysize * 2; + + const int nx = x < 0 ? -x % w2 : w2 - x % w2; + const int ny = y < 0 ? -y % h2 : h2 - y % h2; + + IMAGE *t[7]; + + if( im_open_local_array( out, t, 7, "embed-flag3", "p" ) || + /* Cache the edges of in, since we may well be reusing + * them repeatedly. Will only help for tiny borders + * (up to 20 pixels?), but that's our typical case + * with im_conv() etc. + im_cache( in, t[0], IM__TILE_WIDTH, IM__TILE_HEIGHT, + 3 * (in->Xsize / IM__TILE_WIDTH + 1) + + 3 * (in->Ysize / IM__TILE_HEIGHT + 1) ) || + */ + + /* + + FIXME ... alternatively, don't cache, hmm, + need to time this for typical cases + + */ + im_copy( in, t[0] ) || + + /* Make a 2x2 mirror tile. + */ + im_fliphor( t[0], t[1] ) || + im_lrjoin( t[0], t[1], t[2] ) || + im_flipver( t[2], t[3] ) || + im_tbjoin( t[2], t[3], t[4] ) || + + /* Repeat, then cut out the centre. + */ + im_replicate( t[4], t[5], + w / t[4]->Xsize + 2, h / t[4]->Ysize + 2 ) || + im_extract_area( t[5], t[6], nx, ny, w, h ) || + + /* Overwrite the centre with the input, much faster + * for centre pixels. + */ + im_insert_noexpand( t[6], in, out, x, y ) ) + return( -1 ); +} + break; + + default: + assert( 0 ); + } + + out->Xoffset = x; + out->Yoffset = y; + + return( 0 ); +} diff --git a/libsrc/convolution/im_fastcor.c b/libsrc/convolution/im_fastcor.c new file mode 100644 index 00000000..6669ce5b --- /dev/null +++ b/libsrc/convolution/im_fastcor.c @@ -0,0 +1,209 @@ +/* @(#) Functions which calculates spatial correlation between two images. + * @(#) by taking absolute differences pixel by pixel without calculating + * @(#) the correlation coefficient. + * @(#) + * @(#) The function works as follows: + * @(#) + * @(#) int im_fastcor( im, ref, out ) + * @(#) IMAGE *im, *ref, *out; + * @(#) + * @(#) ref must be smaller than in. The correlation is + * @(#) calculated by overlaping im on the top left corner of ref + * @(#) and moving it all over ref calculating the correlation coefficient + * @(#) at each point. The resultant coefficients are written as unsigned int + * @(#) numbers in out which has the size of im. + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on : 15/03/1991 + * 20/2/95 JC + * - ANSIfied + * - in1 and in2 swapped, to match order for im_spcor + * - memory leaks fixed + * 21/2/95 JC + * - partialed + * - speed-ups + * 7/4/04 + * - now uses im_embed() with edge stretching on the output + * - sets Xoffset / Yoffset + * 8/3/06 JC + * - use im_embed() with edge stretching on the input, not the output + * - calculate sum of squares of differences, rather than abs of + * difference + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Fastcor generate function. + */ +static int +fastcor_gen( REGION *or, REGION *ir, IMAGE *in, IMAGE *ref ) +{ + Rect irect; + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int ri = IM_RECT_RIGHT(r); + + int x, y, i, j; + int lsk; + + /* What part of ir do we need? + */ + irect.left = or->valid.left; + irect.top = or->valid.top; + irect.width = or->valid.width + ref->Xsize - 1; + irect.height = or->valid.height + ref->Ysize - 1; + + if( im_prepare( ir, &irect ) ) + return( -1 ); + lsk = IM_REGION_LSKIP( ir ); + + /* Loop over or. + */ + for( y = to; y < bo; y++ ) { + PEL *a = (PEL *) IM_REGION_ADDR( ir, le, y ); + unsigned int *q = (unsigned int *) IM_REGION_ADDR( or, le, y ); + + for( x = le; x < ri; x++ ) { + int sum = 0; + PEL *b = (PEL *) ref->data; + PEL *a1 = a; + + for( j = 0; j < ref->Ysize; j++ ) { + PEL *a2 = a1; + + for( i = 0; i < ref->Xsize; i++ ) { + int t = *b++ - *a2++; + + sum += t * t; + } + + a1 += lsk; + } + + *q++ = sum; + a += 1; + } + } + + return( 0 ); +} + +/* Raw fastcor, with no borders. + */ +int +im_fastcor_raw( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + /* PIO between in and out; WIO from ref. + */ + if( im_piocheck( in, out ) || im_incheck( ref ) ) + return( -1 ); + + /* Check sizes. + */ + if( in->Xsize < ref->Xsize || in->Ysize < ref->Ysize ) { + im_errormsg( "im_fastcor: ref not smaller than in" ); + return( -1 ); + } + + /* Check types. + */ + if( in->Coding != IM_CODING_NONE || in->Bands != 1 || + in->BandFmt != IM_BANDFMT_UCHAR || + ref->Coding != IM_CODING_NONE || ref->Bands != 1 || + ref->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im_fastcor_raw: input not uncoded 1 band uchar" ); + return( -1 ); + } + + /* Prepare the output image. + */ + if( im_cp_descv( out, in, ref, NULL ) ) + return( -1 ); + out->Bbits = IM_BBITS_INT; + out->BandFmt = IM_BANDFMT_UINT; + out->Xsize = in->Xsize - ref->Xsize + 1; + out->Ysize = in->Ysize - ref->Ysize + 1; + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Write the correlation. + */ + if( im_generate( out, + im_start_one, fastcor_gen, im_stop_one, in, ref ) ) + return( -1 ); + + out->Xoffset = -ref->Xsize / 2; + out->Yoffset = -ref->Ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_fastcor( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_fastcor intermediate", "p" ); + + if( !t1 || + im_embed( in, t1, 1, + ref->Xsize / 2, ref->Ysize / 2, + in->Xsize + ref->Xsize - 1, + in->Ysize + ref->Ysize - 1 ) || + im_fastcor_raw( t1, ref, out ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/convolution/im_gaussmasks.c b/libsrc/convolution/im_gaussmasks.c new file mode 100644 index 00000000..3402ccab --- /dev/null +++ b/libsrc/convolution/im_gaussmasks.c @@ -0,0 +1,182 @@ +/* @(#) Returns a circularly symmetric Gaussian mask + * @(#) min_amplitude should be greater than 0.0 and less than 1.0 + * @(#) min_amplitude determines the size of the mask; if for instance + * @(#) the value .1 is entered this means that the produced mask is clipped + * @(#) at values less than 10 percent of the minimum negative amplitude. + * @(#) If the value of min_amplitude is too small, then the filter coefficients + * @(#) are calculated for masksize equal to the min of 8 * sigma or 256. + * @(#) The mask can be directly used with the vasari convolution programs, + * @(#) the default offset set is 0 + * @(#) + * @(#) DOUBLEMASK *im_gauss_dmask( filename, sigma, min_amplitude ) + * @(#) char *filename; + * @(#) double sigma, min_amplitude; + * @(#) + * @(#) Returns a laplacian of Gaussian square double mask or NULL on error + * @(#) + * @(#) DOUBLEMASK *im_gauss_imask( filename, sigma, min_amplitude ) + * @(#) char *filename; + * @(#) double sigma, min_amplitude; + * @(#) + * @(#) Returns a laplacian of Gaussian square int mask or NULL on error + */ + +/* Written on: 30/11/1989 by Nicos + * Updated on: 6/12/1991 + * 7/8/96 JC + * - ansified, mem leaks plugged + * 20/11/98 JC + * - mask too large check added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define IM_MAXMASK 256 + +DOUBLEMASK * +im_gauss_dmask( const char *filename, double sigma, double min_ampl ) +{ + int x, y, k; + double distance; + double temp; + double *pt1, *pt2, *pt3, *pt4; + int max_x; + int xm, ym; + int xm2, ym2; /* xm2 = xm/2 */ + int offset; + double *cf, *cfs, *mc; + DOUBLEMASK *m; + double sig2, sum; /* sig2 = 2. * sigma * sigma */ + + /* Find the size of the mask depending on the entered data + */ + sig2 = 2. * sigma * sigma; + max_x = 8 * sigma > IM_MAXMASK ? IM_MAXMASK : 8 * sigma ; + for( x = 0; x < max_x; x++ ) { + temp = exp( - ((double)(x * x))/sig2 ); + if( temp < min_ampl ) + break; + } + if( x == max_x ) { + im_errormsg( "im_gauss_dmask: mask too large" ); + return( NULL ); + } + + xm2 = x; + ym2 = x; + xm = xm2 * 2 + 1; + ym = ym2 * 2 + 1; + + if( !(cfs = IM_ARRAY( NULL, (xm2+1)*(ym2+1), double )) ) + return( NULL ); + + for( k = 0, y = 0; y <= ym2; y++ ) { + for( x = 0; x <= xm2; x++, k++ ) { + distance = x*x + y*y; + cfs[k] = exp( -distance / sig2 ); + } + } + +#ifdef PIM_RINT + for( k = 0, y = 0; y <= ymask_2; y++ ) { + for( x = 0; x <= xmask_2; x++, k++ ) + fprintf(stderr, "%3.2f ", cfs[k] ); + fprintf(stderr, "\n"); + } +#endif + + if( !(m = im_create_dmask( filename, xm, ym )) ) { + im_free( cfs ); + return( NULL ); + } + + /* copy the 1/4 cfs into the m + */ + cf = cfs; + offset = xm2 * (xm + 1); + mc = m->coeff + offset; + for( y = 0; y <= ym2; y++ ) { + for( x = 0; x <= xm2; x++ ) { + pt1 = mc + (y * xm) + x; + pt2 = mc - (y * xm) + x; + pt3 = mc + (y * xm) - x; + pt4 = mc - (y * xm) - x; + + *pt1 = cf[x]; + *pt2 = cf[x]; + *pt3 = cf[x]; + *pt4 = cf[x]; + } + + cf += (xm2 + 1); + } + im_free( cfs ); + + sum = 0.0; + for( k = 0, y = 0; y < m->ysize; y++ ) + for( x = 0; x < m->xsize; x++, k++ ) + sum += m->coeff[k]; + m->scale = sum; + m->offset = 0.0; + +#ifdef PIM_RINT + im_print_dmask( m ); +#endif + return( m ); +} + +INTMASK * +im_gauss_imask( const char *filename, double sigma, double min_amplitude ) +{ + DOUBLEMASK *dm; + INTMASK *im; + + if( !(dm = im_gauss_dmask( filename, sigma, min_amplitude )) ) + return( NULL ); + + if( !(im = im_scale_dmask( dm, dm->filename )) ) { + im_free_dmask( dm ); + return( NULL ); + } + im_free_dmask( dm ); + + return( im ) ; +} diff --git a/libsrc/convolution/im_gaussnoise.c b/libsrc/convolution/im_gaussnoise.c new file mode 100644 index 00000000..d3da077c --- /dev/null +++ b/libsrc/convolution/im_gaussnoise.c @@ -0,0 +1,157 @@ +/* @(#) Creates a Gaussian noisy float image with mean of 0 and variance of 1 + * @(#) by averaging 12 random numbers + * @(#) Creates one band float image + * @(#) page 78 PIETGEN 1989 n = 12 + * @(#) Usage + * @(#) + * @(#) int im_gaussnoise(image, xsize, ysize, mean, sigma) + * @(#) IMAGE *image; + * @(#) int xsize, ysize; + * @(#) double mean, sigma; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright 1990, N. Dessipris. + * + * File written on 2/12/1986 + * Author : N. Dessipris + * Updated : 6/6/1991 + * 21/7/93 JC + * - im_outcheck() call added + * 1/2/95 JC + * - declaration for drand48() added + * - partialised, adapting im_black() + * 23/10/98 JC + * - drand48() chaged to random() for poartability + * 21/10/02 JC + * - tries rand() if random() is not available + * - uses RAND_MAX, d'oh + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Keep parameters here. + */ +typedef struct { + double mean; + double sigma; +} GnoiseInfo; + +/* Generate function --- just fill the region with noise. "dummy" is our + * sequence value: we don't need one. + */ +/*ARGSUSED*/ +static int +gnoise_gen( REGION *or, void *dummy, GnoiseInfo *gin ) +{ + int x, y, i; + int sz = IM_REGION_N_ELEMENTS( or ); + + for( y = 0; y < or->valid.height; y++ ) { + float *q = (float *) + IM_REGION_ADDR( or, or->valid.left, y + or->valid.top ); + + for( x = 0; x < sz; x++ ) { + double sum = 0.0; + + for( i = 0; i < 12; i++ ) +#ifdef HAVE_RANDOM + sum += (double) random() / RAND_MAX; +#else /*HAVE_RANDOM*/ +#ifdef HAVE_RAND + sum += (double) rand() / RAND_MAX; +#else /*HAVE_RAND*/ +#error "no random number generator found" +#endif /*HAVE_RAND*/ +#endif /*HAVE_RAND*/ + + q[x] = (sum - 6.0) * gin->sigma + gin->mean; + } + } + + return( 0 ); +} + +/* Make a one band float image of gaussian noise. + */ +int +im_gaussnoise( IMAGE *out, int x, int y, double mean, double sigma ) +{ + GnoiseInfo *gin; + + /* Check parameters. + */ + if( x < 0 || y < 0 ) { + im_errormsg( "im_gaussnoise: bad parameter" ); + return( -1 ); + } + + /* Check descriptor. + */ + if( im_poutcheck( out ) ) + return( -1 ); + + /* Set fields. + */ + im_initdesc( out, + x, y, 1, + IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, IM_CODING_NONE, IM_TYPE_B_W, + 1.0, 1.0, 0, 0 ); + + /* Set hints - ANY is ok with us. + */ + if( im_demand_hint( out, IM_ANY, NULL ) ) + return( -1 ); + + /* Save parameters. + */ + if( !(gin = IM_NEW( out, GnoiseInfo )) ) + return( -1 ); + gin->mean = mean; + gin->sigma = sigma; + + /* Generate image. + */ + if( im_generate( out, NULL, gnoise_gen, NULL, gin, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/convolution/im_gradcor.c b/libsrc/convolution/im_gradcor.c new file mode 100644 index 00000000..1d7743ad --- /dev/null +++ b/libsrc/convolution/im_gradcor.c @@ -0,0 +1,536 @@ +/* @(#) Like im_spcor(), but with a new metric. Docs to follow. + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 2007 Nottingham Trent University + * + * Author: Tom Vajzovic + * Written on: 2007-06-07 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + + +/** LOCAL TYPES **/ + +typedef struct { + REGION *reg; + int *region_xgrad; + int *region_ygrad; + size_t region_xgrad_area; + size_t region_ygrad_area; +} +gradcor_seq_t; + + +/** LOCAL FUNCTION DECLARATIONS **/ + +static void *gradcor_start( IMAGE *out, void *vptr_large, void *unrequired ); +static int gradcor_stop( void *vptr_seq, void *unrequired, void *unreq2 ); +static int gradcor_gen( REGION *to_make, void *vptr_seq, void *unrequired, void *vptr_grads ); + +#define XGRAD_GEN_DECLARATION( TYPE ) static int xgrad_gen_ ## TYPE( REGION *to_make, void *vptr_make_from, void *unrequired, void *unreq2 ) +#define YGRAD_GEN_DECLARATION( TYPE ) static int ygrad_gen_ ## TYPE( REGION *to_make, void *vptr_make_from, void *unrequired, void *unreq2 ) + +XGRAD_GEN_DECLARATION( guint8 ); +YGRAD_GEN_DECLARATION( guint8 ); +XGRAD_GEN_DECLARATION( gint8 ); +YGRAD_GEN_DECLARATION( gint8 ); +XGRAD_GEN_DECLARATION( guint16 ); +YGRAD_GEN_DECLARATION( guint16 ); +XGRAD_GEN_DECLARATION( gint16 ); +YGRAD_GEN_DECLARATION( gint16 ); +XGRAD_GEN_DECLARATION( guint32 ); +YGRAD_GEN_DECLARATION( guint32 ); +XGRAD_GEN_DECLARATION( gint32 ); +YGRAD_GEN_DECLARATION( gint32 ); +#if 0 +XGRAD_GEN_DECLARATION( float ); +YGRAD_GEN_DECLARATION( float ); +XGRAD_GEN_DECLARATION( double ); +YGRAD_GEN_DECLARATION( double ); +#endif + + +/** EXPORTED FUNCTION DEFINITIONS **/ + +int +im_gradcor( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ +#define FUNCTION_NAME "im_gradcor" + IMAGE *t1 = im_open_local( out, FUNCTION_NAME " intermediate", "p" ); + + if( !t1 || + im_embed( in, t1, 1, + ref->Xsize / 2, ref->Ysize / 2, + in->Xsize + ref->Xsize - 1, + in->Ysize + ref->Ysize - 1 ) || + im_gradcor_raw( t1, ref, out ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +#undef FUNCTION_NAME +} + +int im_gradcor_raw( IMAGE *large, IMAGE *small, IMAGE *out ){ +#define FUNCTION_NAME "im_gradcor_raw" + + if( im_piocheck( large, out ) || im_pincheck( small ) ) + return -1; + + if( ! im_isint( large ) || ! im_isint( small ) ){ + im_error( FUNCTION_NAME, "image does not have integer band format" ); + return -1; + } + if( large-> Coding || small-> Coding ){ + im_error( FUNCTION_NAME, "image is not uncoded" ); + return -1; + } + if( 1 != large-> Bands || 1 != small-> Bands ){ + im_error( FUNCTION_NAME, "image is multi-band" ); + return -1; + } + if( large-> Xsize < small-> Xsize || large-> Ysize < small-> Ysize ){ + im_error( FUNCTION_NAME, "second image must be smaller than first" ); + return -1; + } + if( im_cp_desc( out, large ) ) + return -1; + + out-> Xsize= 1 + large-> Xsize - small-> Xsize; + out-> Ysize= 1 + large-> Ysize - small-> Ysize; + out-> BandFmt= IM_BANDFMT_FLOAT; + out-> Bbits= IM_BBITS_FLOAT; + + if( im_demand_hint( out, IM_FATSTRIP, large, NULL ) ) + return -1; + + { + IMAGE *xgrad= im_open_local( out, FUNCTION_NAME ": xgrad", "t" ); + IMAGE *ygrad= im_open_local( out, FUNCTION_NAME ": ygrad", "t" ); + IMAGE **grads= im_allocate_input_array( out, xgrad, ygrad, NULL ); + + return im_grad_x( small, xgrad ) + || im_grad_y( small, ygrad ) + || im_generate( out, gradcor_start, gradcor_gen, gradcor_stop, (void*) large, (void*) grads ); + } +#undef FUNCTION_NAME +} + +int im_grad_x( IMAGE *in, IMAGE *out ){ +#define FUNCTION_NAME "im_grad_x" + + if( im_piocheck( in, out ) ) + return -1; + + if( ! im_isint( in ) ){ + im_error( FUNCTION_NAME, "image does not have integer band format" ); + return -1; + } + if( in-> Coding ){ + im_error( FUNCTION_NAME, "image is not uncoded" ); + return -1; + } + if( 1 != in-> Bands ){ + im_error( FUNCTION_NAME, "image is multi-band" ); + return -1; + } + if( im_cp_desc( out, in ) ) + return -1; + + -- out-> Xsize; + out-> BandFmt= IM_BANDFMT_INT; /* do not change without updating im_gradcor() */ + out-> Bbits= IM_BBITS_INT; + + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return -1; + +#define RETURN_GENERATE( TYPE ) return im_generate( out, im_start_one, xgrad_gen_ ## TYPE, im_stop_one, (void*) in, NULL ) + + switch( in-> BandFmt ){ + + case IM_BANDFMT_UCHAR: + RETURN_GENERATE( guint8 ); + + case IM_BANDFMT_CHAR: + RETURN_GENERATE( gint8 ); + + case IM_BANDFMT_USHORT: + RETURN_GENERATE( guint16 ); + + case IM_BANDFMT_SHORT: + RETURN_GENERATE( gint16 ); + + case IM_BANDFMT_UINT: + RETURN_GENERATE( guint32 ); + + case IM_BANDFMT_INT: + RETURN_GENERATE( gint32 ); +#if 0 + case IM_BANDFMT_FLOAT: + RETURN_GENERATE( float ); + case IM_BANDFMT_DOUBLE: + RETURN_GENERATE( double ); +#endif +#undef RETURN_GENERATE + } +#undef FUNCTION_NAME +} + +int im_grad_y( IMAGE *in, IMAGE *out ){ +#define FUNCTION_NAME "im_grad_y" + + if( im_piocheck( in, out ) ) + return -1; + + if( ! im_isint( in ) ){ + im_error( FUNCTION_NAME, "image does not have integer band format" ); + return -1; + } + if( in-> Coding ){ + im_error( FUNCTION_NAME, "image is not uncoded" ); + return -1; + } + if( 1 != in-> Bands ){ + im_error( FUNCTION_NAME, "image is multi-band" ); + return -1; + } + if( im_cp_desc( out, in ) ) + return -1; + + -- out-> Ysize; + out-> BandFmt= IM_BANDFMT_INT; /* do not change without updating im_gradcor() */ + out-> Bbits= IM_BBITS_INT; + + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return -1; + +#define RETURN_GENERATE( TYPE ) return im_generate( out, im_start_one, ygrad_gen_ ## TYPE, im_stop_one, (void*) in, NULL ) + + switch( in-> BandFmt ){ + + case IM_BANDFMT_UCHAR: + RETURN_GENERATE( guint8 ); + + case IM_BANDFMT_CHAR: + RETURN_GENERATE( gint8 ); + + case IM_BANDFMT_USHORT: + RETURN_GENERATE( guint16 ); + + case IM_BANDFMT_SHORT: + RETURN_GENERATE( gint16 ); + + case IM_BANDFMT_UINT: + RETURN_GENERATE( guint32 ); + + case IM_BANDFMT_INT: + RETURN_GENERATE( gint32 ); +#if 0 + case IM_BANDFMT_FLOAT: + RETURN_GENERATE( float ); + case IM_BANDFMT_DOUBLE: + RETURN_GENERATE( double ); +#endif +#undef RETURN_GENERATE + } +#undef FUNCTION_NAME +} + + +/** LOCAL FUNCTION DEFINITIONS **/ + +static void *gradcor_start( IMAGE *out, void *vptr_large, void *unrequired ){ + + gradcor_seq_t *seq= IM_NEW( NULL, gradcor_seq_t ); + if( ! seq ) + return NULL; + + seq-> region_xgrad= (int*) NULL; + seq-> region_ygrad= (int*) NULL; + seq-> region_xgrad_area= 0; + seq-> region_ygrad_area= 0; + + seq-> reg= im_region_create( (IMAGE*) vptr_large ); + if( ! seq-> reg ){ + im_free( (void*) seq ); + return NULL; + } + return (void*) seq; +} + +static int gradcor_stop( void *vptr_seq, void *unrequired, void *unreq2 ){ + + gradcor_seq_t *seq= (gradcor_seq_t*) vptr_seq; + if( seq ){ + im_free( (void*) seq-> region_xgrad ); + im_free( (void*) seq-> region_ygrad ); + im_region_free( seq-> reg ); + seq-> region_xgrad= (int*) NULL; + seq-> region_ygrad= (int*) NULL; + seq-> reg= (REGION*) NULL; + im_free( (void*) seq ); + } + return 0; +} + +static int gradcor_gen( REGION *to_make, void *vptr_seq, void *unrequired, void *vptr_grads ){ + + gradcor_seq_t *seq= (gradcor_seq_t*) vptr_seq; + REGION *make_from= seq-> reg; + + IMAGE **grads= (IMAGE**) vptr_grads; + IMAGE *small_xgrad= grads[0]; + IMAGE *small_ygrad= grads[1]; + + Rect require= { + to_make-> valid. left, + to_make-> valid. top, + to_make-> valid. width + small_xgrad-> Xsize, + to_make-> valid. height + small_ygrad-> Ysize + }; + size_t region_xgrad_width= require. width - 1; + size_t region_ygrad_height= require. height - 1; + + if( im_prepare( make_from, &require ) ) + return -1; + +#define FILL_BUFFERS( TYPE ) /* fill region_xgrad */ \ + { \ + TYPE *reading= (TYPE*) IM_REGION_ADDR( make_from, require. left, require. top ); \ + size_t read_skip= ( IM_REGION_LSKIP( make_from ) / sizeof(TYPE) ) - region_xgrad_width; \ + size_t area_need= region_xgrad_width * require. height; \ + \ + if( seq-> region_xgrad_area < area_need ){ \ + free( seq-> region_xgrad ); \ + seq-> region_xgrad= malloc( area_need * sizeof(int) ); \ + if( ! seq-> region_xgrad ) \ + return -1; \ + seq-> region_xgrad_area= area_need; \ + } \ + { \ + int *writing= seq-> region_xgrad; \ + int *write_end= writing + area_need; \ + int *write_stop; \ + for( ; writing < write_end; reading+= read_skip ) \ + for( write_stop= writing + region_xgrad_width; writing < write_stop; ++reading, ++writing ) \ + *writing= reading[1] - reading[0]; \ + } \ + } \ + { /* fill region_ygrad */ \ + TYPE *reading= (TYPE*) IM_REGION_ADDR( make_from, require. left, require. top ); \ + size_t read_line= IM_REGION_LSKIP( make_from ) / sizeof(TYPE); \ + size_t read_skip= read_line - require. width; \ + size_t area_need= require. width * region_ygrad_height; \ + \ + if( seq-> region_ygrad_area < area_need ){ \ + free( seq-> region_ygrad ); \ + seq-> region_ygrad= malloc( area_need * sizeof(int) ); \ + if( ! seq-> region_ygrad ) \ + return -1; \ + seq-> region_ygrad_area= area_need; \ + } \ + { \ + int *writing= seq-> region_ygrad; \ + int *write_end= writing + area_need; \ + int *write_stop; \ + for( ; writing < write_end; reading+= read_skip ) \ + for( write_stop= writing + require. width; writing < write_stop; ++reading, ++writing ) \ + *writing= reading[ read_line ] - reading[0]; \ + } \ + } + switch( make_from-> im-> BandFmt ){ + case IM_BANDFMT_UCHAR: + FILL_BUFFERS( unsigned char ) + break; + case IM_BANDFMT_CHAR: + FILL_BUFFERS( signed char ) + break; + case IM_BANDFMT_USHORT: + FILL_BUFFERS( unsigned short int ) + break; + case IM_BANDFMT_SHORT: + FILL_BUFFERS( signed short int ) + break; + case IM_BANDFMT_UINT: + FILL_BUFFERS( unsigned int ) + break; + case IM_BANDFMT_INT: + FILL_BUFFERS( signed int ) + break; + } + { /* write to output */ + size_t write_skip= IM_REGION_LSKIP( to_make ) / sizeof( float ); + float *writing= (float*) IM_REGION_ADDR_TOPLEFT( to_make ); + float *write_end= writing + write_skip * to_make-> valid. height; + float *write_stop; + size_t write_width= to_make-> valid. width; + + size_t small_xgrad_width= small_xgrad-> Xsize; + size_t small_ygrad_width= small_ygrad-> Xsize; + int *small_xgrad_end= (int*) small_xgrad-> data + small_xgrad_width * small_xgrad-> Ysize; + int *small_ygrad_end= (int*) small_ygrad-> data + small_ygrad_width * small_ygrad-> Ysize; + + int *region_xgrad_start= seq-> region_xgrad; + int *region_ygrad_start= seq-> region_ygrad; + size_t region_xgrad_start_skip= region_xgrad_width - write_width; + size_t region_ygrad_start_skip= require. width - write_width; + + size_t region_xgrad_read_skip= region_xgrad_width - small_xgrad_width; + size_t region_ygrad_read_skip= require. width - small_ygrad_width; + + write_skip-= write_width; + + for( ; writing < write_end; writing+= write_skip, region_xgrad_start+= region_xgrad_start_skip, region_ygrad_start+= region_ygrad_start_skip ) + for( write_stop= writing + write_width; writing < write_stop; ++writing, ++region_xgrad_start, ++region_ygrad_start ){ + gint64 sum= 0; + { + int *small_xgrad_read= (int*) small_xgrad-> data; + int *small_xgrad_stop; + int *region_xgrad_read= region_xgrad_start; + + for( ; small_xgrad_read < small_xgrad_end; region_xgrad_read+= region_xgrad_read_skip ) + for( small_xgrad_stop= small_xgrad_read + small_xgrad_width; small_xgrad_read < small_xgrad_stop; ++small_xgrad_read, ++region_xgrad_read ) + sum+= *small_xgrad_read * *region_xgrad_read; + } + { + int *small_ygrad_read= (int*) small_ygrad-> data; + int *small_ygrad_stop; + int *region_ygrad_read= region_ygrad_start; + + for( ; small_ygrad_read < small_ygrad_end; region_ygrad_read+= region_ygrad_read_skip ) + for( small_ygrad_stop= small_ygrad_read + small_ygrad_width; small_ygrad_read < small_ygrad_stop; ++small_ygrad_read, ++region_ygrad_read ) + sum+= *small_ygrad_read * *region_ygrad_read; + } + *writing= sum; + } + } + return 0; +} + +#define XGRAD_GEN_DEFINITION( TYPE ) \ +static int xgrad_gen_ ## TYPE( REGION *to_make, void *vptr_make_from, void *unrequired, void *unreq2 ){ \ + \ + REGION *make_from= (REGION*) vptr_make_from; \ + Rect require= { \ + to_make-> valid. left, \ + to_make-> valid. top, \ + to_make-> valid. width + 1, \ + to_make-> valid. height \ + }; \ + if( im_prepare( make_from, &require ) ) \ + return -1; \ + \ + { \ + int *writing= (int*) IM_REGION_ADDR_TOPLEFT( to_make ); \ + size_t write_skip= IM_REGION_LSKIP( to_make ) / sizeof(int); \ + int *write_end= writing + write_skip * to_make-> valid. height; \ + size_t write_width= to_make-> valid. width; \ + int *write_stop; \ + \ + TYPE *reading= (TYPE*) IM_REGION_ADDR( make_from, require. left, require. top ); \ + size_t read_skip= ( IM_REGION_LSKIP( make_from ) / sizeof(TYPE) ) - write_width; \ + \ + write_skip-= write_width; \ + \ + for( ; writing < write_end; writing+= write_skip, reading+= read_skip ) \ + for( write_stop= writing + write_width; writing < write_stop; ++writing, ++reading ) \ + *writing= (int)( reading[1] - reading[0] ); \ + } \ + return 0; \ +} + +#define YGRAD_GEN_DEFINITION( TYPE ) \ +static int ygrad_gen_ ## TYPE( REGION *to_make, void *vptr_make_from, void *unrequired, void *unreq2 ){ \ + \ + REGION *make_from= (REGION*) vptr_make_from; \ + Rect require= { \ + to_make-> valid. left, \ + to_make-> valid. top, \ + to_make-> valid. width, \ + to_make-> valid. height + 1 \ + }; \ + if( im_prepare( make_from, &require ) ) \ + return -1; \ + \ + { \ + int *writing= (int*) IM_REGION_ADDR_TOPLEFT( to_make ); \ + size_t write_skip= IM_REGION_LSKIP( to_make ) / sizeof(int); \ + int *write_end= writing + write_skip * to_make-> valid. height; \ + size_t write_width= to_make-> valid. width; \ + int *write_stop; \ + \ + TYPE *reading= (TYPE*) IM_REGION_ADDR( make_from, require. left, require. top ); \ + size_t read_line= IM_REGION_LSKIP( make_from ) / sizeof(TYPE); \ + size_t read_skip= read_line - write_width; \ + \ + write_skip-= write_width; \ + \ + for( ; writing < write_end; writing+= write_skip, reading+= read_skip ) \ + for( write_stop= writing + write_width; writing < write_stop; ++writing, ++reading ) \ + *writing= (int)( reading[ read_line ] - reading[0] ); \ + } \ + return 0; \ +} + +XGRAD_GEN_DEFINITION( guint8 ) +YGRAD_GEN_DEFINITION( guint8 ) +XGRAD_GEN_DEFINITION( gint8 ) +YGRAD_GEN_DEFINITION( gint8 ) +XGRAD_GEN_DEFINITION( guint16 ) +YGRAD_GEN_DEFINITION( guint16 ) +XGRAD_GEN_DEFINITION( gint16 ) +YGRAD_GEN_DEFINITION( gint16 ) +XGRAD_GEN_DEFINITION( guint32 ) +YGRAD_GEN_DEFINITION( guint32 ) +XGRAD_GEN_DEFINITION( gint32 ) +YGRAD_GEN_DEFINITION( gint32 ) +#if 0 +XGRAD_GEN_DEFINITION( float ) +YGRAD_GEN_DEFINITION( float ) +XGRAD_GEN_DEFINITION( double ) +YGRAD_GEN_DEFINITION( double ) +#endif diff --git a/libsrc/convolution/im_logmasks.c b/libsrc/convolution/im_logmasks.c new file mode 100644 index 00000000..4aec46f6 --- /dev/null +++ b/libsrc/convolution/im_logmasks.c @@ -0,0 +1,220 @@ +/* @(#) Returns a circularly symmetric difference of Gaussian mask + * @(#) min_amplitude should be greater than 0.0 and less than 1.0 + * @(#) min_amplitude determines the size of the mask; if for instance + * @(#) the value .1 is entered this means that the produced mask is clipped + * @(#) at values less than 10 percent of the minimum negative amplitude. + * @(#) If the value of min_amplitude is too small, then the filter coefficients + * @(#) are calculated for masksize equal to the min of 8 * sigma or 256. + * @(#) The mask can be directly used with the vasari convolution programs, + * @(#) the default offset set is 0 + * @(#) + * @(#) DOUBLEMASK *im_log_dmask( filename, sigma, min_amplitude ) + * @(#) char *filename; + * @(#) double sigma, min_amplitude; + * @(#) + * @(#) Returns a laplacian of Gaussian square double mask or NULL on error + * @(#) + * @(#) DOUBLEMASK *im_log_imask( filename, sigma, min_amplitude ) + * @(#) char *filename; + * @(#) double sigma, min_amplitude; + * @(#) + * @(#) Returns a laplacian of Gaussian square int mask or NULL on error + */ + +/* Written on: 30/11/1989 + * Updated on: 6/12/1991 + * 7/8/96 JC + * - ansified, mem leaks plugged + * 20/11/98 JC + * - mask too large check added + * 26/3/02 JC + * - ahem, was broken since '96, thanks matt + * 16/7/03 JC + * - makes mask out to zero, not out to minimum, thanks again matt + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define PIM_RINT 1 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define IM_MAXMASK 256 + +DOUBLEMASK * +im_log_dmask( const char *filename, double sigma, double min_ampl ) +{ + const double sig2 = sigma * sigma; + + double last; + int x, y, k; + + double *pt1, *pt2, *pt3, *pt4; + int xm, ym; + int xm2, ym2; /* xm2 = xm/2 */ + int offset; + double *cf, *cfs, *mc; + DOUBLEMASK *m; + double sum; + + /* Stop used-before-set warnings. + */ + last = 0.0; + + /* Find the size of the mask depending on the entered data. We want to + * eval the mask out to the flat zero part, ie. beyond the minimum and + * to the point where it comes back up towards zero. + */ + for( x = 0; x < IM_MAXMASK; x++ ) { + const double distance = x * x; + double val; + + /* Handbook of Pattern Recognition and image processing + * by Young and Fu AP 1986 pp 220-221 + * temp = (1.0 / (2.0 * IM_PI * sig4)) * + (2.0 - (distance / sig2)) * + exp( (-1.0) * distance / (2.0 * sig2) ) + + .. use 0.5 to normalise + */ + val = 0.5 * + (2.0 - (distance / sig2)) * + exp( -distance / (2.0 * sig2) ); + + /* Stop when change in temp (ie. difference from the last + * point) and absolute value are both less than the min. + */ + if( x > 0 && + fabs( val ) < min_ampl && + fabs( val - last ) < min_ampl ) + break; + + last = val; + } + if( x == IM_MAXMASK ) { + im_errormsg( "im_log_dmask: mask too large" ); + return( NULL ); + } + + xm2 = x; ym2 = x; + xm = xm2 * 2 + 1; ym = ym2 * 2 + 1; + + if( !(cfs = IM_ARRAY( NULL, (xm2 + 1) * (ym2 + 1), double )) ) + return( NULL ); + + /* Make 1/4 of the mask. + */ + for( k = 0, y = 0; y <= ym2; y++ ) + for( x = 0; x <= xm2; x++, k++ ) { + const double distance = x * x + y * y; + + cfs[k] = 0.5 * + (2.0 - (distance / sig2)) * + exp( -distance / (2.0 * sig2) ); + } + +#ifdef PIM_RINT + for( k = 0, y = 0; y <= ym2; y++ ) { + for( x = 0; x <= xm2; x++, k++ ) + fprintf( stderr, "%3.2f ", cfs[k] ); + fprintf( stderr, "\n" ); + } +#endif + + if( !(m = im_create_dmask( filename, xm, ym )) ) { + im_free( cfs ); + return( NULL ); + } + + /* Copy the 1/4 cfs into the m + */ + cf = cfs; + offset = xm2 * (xm + 1); + mc = m->coeff + offset; + for( y = 0; y <= ym2; y++ ) { + for( x = 0; x <= xm2; x++ ) { + pt1 = mc + (y * xm) + x; + pt2 = mc - (y * xm) + x; + pt3 = mc + (y * xm) - x; + pt4 = mc - (y * xm) - x; + + *pt1 = cf[x]; + *pt2 = cf[x]; + *pt3 = cf[x]; + *pt4 = cf[x]; + } + + cf += (xm2 + 1); + } + im_free( cfs ); + + sum = 0.0; + for( k = 0, y = 0; y < m->ysize; y++ ) + for( x = 0; x < m->xsize; x++, k++ ) + sum += m->coeff[k]; + m->scale = sum; + m->offset = 0.0; + +#ifdef PIM_RINT + im_print_dmask( m ); +#endif + + return( m ); +} + +INTMASK * +im_log_imask( const char *filename, double sigma, double min_ampl ) +{ + DOUBLEMASK *dm; + INTMASK *im; + + if( !(dm = im_log_dmask( filename, sigma, min_ampl )) ) + return( NULL ); + + if( !(im = im_scale_dmask( dm, dm->filename )) ) { + im_free_dmask( dm ); + return( NULL ); + } + im_free_dmask( dm ); + + return( im ) ; +} diff --git a/libsrc/convolution/im_mpercent.c b/libsrc/convolution/im_mpercent.c new file mode 100644 index 00000000..0c6dc56e --- /dev/null +++ b/libsrc/convolution/im_mpercent.c @@ -0,0 +1,101 @@ +/* @(#) Function which returns an int. This integer is the threshold + * @(#) above which there are percent values of the input images + * @(#) If for instance precent=.1, the number of pels of the input image + * @(#) with values greater than the returned int will correspond to + * @(#) 10% of all pels of the image. + * @(#) One band IM_BANDFMT_UCHAR images only. + * @(#) + * @(#) Function im_mpercent() assumes that input + * @(#) is either memory mapped or in a buffer. + * @(#) + * @(#) int im_percent(in, percent) + * @(#) IMAGE *in; + * @(#) double percent; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, N. Dessipris + * + * Author: N. Dessipris + * Written on: 02/08/1990 + * Modified on : 29/4/93 K.Martinez for Sys5 + * 20/2/95 JC + * - now returns result through parameter + * - ANSIfied a little + * 19/1/07 + * - redone with the vips hist operators + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* What threshold will select a specified percentage of the pixels in the + * image. + */ +int +im_mpercent( IMAGE *in, double percent, int *out ) +{ + IMAGE *base; + IMAGE *t[6]; + double pos; + + if( !(base = im_open( "im_mpercent1", "p" )) ) + return( -1 ); + if( im_open_local_array( base, t, 6, "im_mpercent", "p" ) ) { + im_close( base ); + return( -1 ); + } + + if( im_histgr( in, t[0], -1 ) || + im_histcum( t[0], t[1] ) || + im_histnorm( t[1], t[2] ) || + im_lessconst( t[2], t[3], percent * t[2]->Xsize ) || + im_fliphor( t[3], t[4] ) || + im_profile( t[4], t[5], 1 ) || + im_avg( t[5], &pos ) ) { + im_close( base ); + return( -1 ); + } + im_close( base ); + + *out = pos; + + return( 0 ); +} diff --git a/libsrc/convolution/im_rank.c b/libsrc/convolution/im_rank.c new file mode 100644 index 00000000..7d81b033 --- /dev/null +++ b/libsrc/convolution/im_rank.c @@ -0,0 +1,424 @@ +/* @(#) Rank filter. + * @(#) + * @(#) int + * @(#) im_rank( in, out, xsize, ysize, order ) + * @(#) IMAGE *in, *out; + * @(#) int xsize, ysize; + * @(#) int order; + * @(#) + * @(#) Also: im_rank_raw(). As above, but does not add a black border. + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * + * Author: JC + * Written on: 19/8/96 + * Modified on: + * JC 20/8/96 + * - now uses insert-sort rather than bubble-sort + * - now works for any non-complex type + * JC 22/6/01 + * - oops, sanity check on n wrong + * JC 28/8/03 + * - cleanups + * - better selection algorithm ... same speed for 3x3, about 3x faster + * for 5x5, faster still for larger windows + * - index from zero for consistency with other parts of vips + * 7/4/04 + * - now uses im_embed() with edge stretching on the input, not + * the output + * - sets Xoffset / Yoffset + * 7/10/04 + * - oops, im_embed() size was wrong + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Global state: save our parameters here. + */ +typedef struct { + IMAGE *in, *out; /* Images we run */ + int xsize, ysize; /* Window size */ + int order; /* Element select */ + int n; /* xsize * ysize */ +} RankInfo; + +/* Sequence value: just the array we sort in. + */ +typedef struct { + REGION *ir; + PEL *sort; +} SeqInfo; + +/* Free a sequence value. + */ +static int +stop_rank( SeqInfo *seq, IMAGE *in, RankInfo *rnk ) +{ + /* Free attached objects. + */ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Rank start function. + */ +static void * +start_rank( IMAGE *out, IMAGE *in, RankInfo *rnk ) +{ + SeqInfo *seq = IM_NEW( out, SeqInfo ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->sort = NULL; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + seq->sort = IM_ARRAY( out, + IM_IMAGE_SIZEOF_ELEMENT( in ) * rnk->n, PEL ); + if( !seq->ir || !seq->sort ) { + stop_rank( seq, in, rnk ); + return( NULL ); + } + + return( (void *) seq ); +} + +#define SWAP( TYPE, A, B ) { \ + TYPE t = (A); \ + (A) = (B); \ + (B) = t; \ +} + +/* Inner loop for select-sorting TYPE. + */ +#define LOOP_SELECT( TYPE ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + TYPE *p = (TYPE *) IM_REGION_ADDR( ir, le, y ); \ + TYPE *sort = (TYPE *) seq->sort; \ + TYPE a; \ + \ + for( x = 0; x < sz; x++ ) { \ + TYPE *d = p + x; \ + \ + /* Copy window into sort[]. + */ \ + for( k = 0, j = 0; j < rnk->ysize; j++ ) { \ + for( i = 0; i < eaw; i += bands, k++ ) \ + sort[k] = d[i]; \ + d += ls; \ + } \ + \ + /* Rearrange sort[] to make the order-th element the order-th + * smallest, adapted from Numerical Recipes in C. + */ \ + lower = 0; /* Range we know the result lies in */ \ + upper = rnk->n - 1; \ + for(;;) { \ + if( upper - lower < 2 ) { \ + /* 1 or 2 elements left. + */ \ + if( upper - lower == 1 && \ + sort[lower] > sort[upper] ) \ + SWAP( TYPE, \ + sort[lower], sort[upper] ); \ + break; \ + } \ + else { \ + /* Pick mid-point of remaining elements. + */ \ + mid = (lower + upper) >> 1; \ + \ + /* Sort lower/mid/upper elements, hold + * midpoint in sort[lower + 1] for + * partitioning. + */ \ + SWAP( TYPE, sort[lower + 1], sort[mid] ); \ + if( sort[lower] > sort[upper] ) \ + SWAP( TYPE, \ + sort[lower], sort[upper] ); \ + if( sort[lower + 1] > sort[upper] ) \ + SWAP( TYPE, \ + sort[lower + 1], sort[upper] );\ + if( sort[lower] > sort[lower + 1] ) \ + SWAP( TYPE, \ + sort[lower], sort[lower + 1] ) \ + \ + i = lower + 1; \ + j = upper; \ + a = sort[lower + 1]; \ + \ + for(;;) { \ + /* Search for out of order elements. + */ \ + do \ + i++; \ + while( sort[i] < a ); \ + do \ + j--; \ + while( sort[j] > a ); \ + if( j < i ) \ + break; \ + SWAP( TYPE, sort[i], sort[j] ); \ + } \ + \ + /* Replace mid element. + */ \ + sort[lower + 1] = sort[j]; \ + sort[j] = a; \ + \ + /* Move to partition with the kth element. + */ \ + if( j >= rnk->order ) \ + upper = j - 1; \ + if( j <= rnk->order ) \ + lower = i; \ + } \ + } \ + \ + q[x] = sort[rnk->order]; \ + } \ +} + +/* Loop for find max of window. + */ +#define LOOP_MAX( TYPE ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + TYPE *p = (TYPE *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + TYPE *d = &p[x]; \ + TYPE max; \ + \ + max = *d; \ + for( j = 0; j < rnk->ysize; j++ ) { \ + TYPE *e = d; \ + \ + for( i = 0; i < rnk->xsize; i++ ) { \ + if( *e > max ) \ + max = *e; \ + \ + e += bands; \ + } \ + \ + d += ls; \ + } \ + \ + q[x] = max; \ + } \ +} + +/* Loop for find min of window. + */ +#define LOOP_MIN( TYPE ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + TYPE *p = (TYPE *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + TYPE *d = &p[x]; \ + TYPE min; \ + \ + min = *d; \ + for( j = 0; j < rnk->ysize; j++ ) { \ + TYPE *e = d; \ + \ + for( i = 0; i < rnk->xsize; i++ ) { \ + if( *e < min ) \ + min = *e; \ + \ + e += bands; \ + } \ + \ + d += ls; \ + } \ + \ + q[x] = min; \ + } \ +} + +#define SWITCH( OPERATION ) \ + switch( rnk->out->BandFmt ) { \ + case IM_BANDFMT_UCHAR: OPERATION( unsigned char ); break; \ + case IM_BANDFMT_CHAR: OPERATION( signed char ); break; \ + case IM_BANDFMT_USHORT: OPERATION( unsigned short ); break; \ + case IM_BANDFMT_SHORT: OPERATION( signed short ); break; \ + case IM_BANDFMT_UINT: OPERATION( unsigned int ); break; \ + case IM_BANDFMT_INT: OPERATION( signed int ); break; \ + case IM_BANDFMT_FLOAT: OPERATION( float ); break; \ + case IM_BANDFMT_DOUBLE: OPERATION( double ); break; \ + \ + default: \ + assert( 0 ); \ + } + +/* Rank of a REGION. + */ +static int +gen_rank( REGION *or, SeqInfo *seq, IMAGE *in, RankInfo *rnk ) +{ + REGION *ir = seq->ir; + + Rect *r = &or->valid; + Rect s; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + + int ls; + int bands = in->Bands; + int eaw = rnk->xsize * bands; /* elements across window */ + + int x, y; + int i, j, k; + int upper, lower, mid; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += rnk->xsize - 1; + s.height += rnk->ysize - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + ls = IM_REGION_LSKIP( ir ) / IM_IMAGE_SIZEOF_ELEMENT( in ); + + for( y = to; y < bo; y++ ) { + if( rnk->order == 0 ) + SWITCH( LOOP_MIN ) + else if( rnk->order == rnk->n - 1 ) + SWITCH( LOOP_MAX ) + else + SWITCH( LOOP_SELECT ) } + + return( 0 ); +} + +/* Rank filter. + */ +int +im_rank_raw( IMAGE *in, IMAGE *out, int xsize, int ysize, int order ) +{ + RankInfo *rnk; + + /* Check parameters. + */ + if( !in || in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_errormsg( "im_rank: input non-complex uncoded only" ); + return( -1 ); + } + if( xsize > 1000 || ysize > 1000 || xsize <= 0 || ysize <= 0 || + order < 0 || order > xsize * ysize - 1 ) { + im_errormsg( "im_rank: bad parameters" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Save parameters. + */ + if( !(rnk = IM_NEW( out, RankInfo )) ) + return( -1 ); + rnk->in = in; + rnk->out = out; + rnk->xsize = xsize; + rnk->ysize = ysize; + rnk->order = order; + rnk->n = xsize * ysize; + + /* Prepare output. Consider a 7x7 window and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= xsize - 1; + out->Ysize -= ysize - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_rank: image too small for window" ); + return( -1 ); + } + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, start_rank, gen_rank, stop_rank, in, rnk ) ) + return( -1 ); + + out->Xoffset = -xsize / 2; + out->Yoffset = -ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_rank( IMAGE *in, IMAGE *out, int xsize, int ysize, int order ) +{ + IMAGE *t1 = im_open_local( out, "im_rank:1", "p" ); + + if( !t1 || + im_embed( in, t1, 1, + xsize/2, ysize/2, + in->Xsize + xsize - 1, in->Ysize + ysize - 1 ) || + im_rank_raw( t1, out, xsize, ysize, order ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/convolution/im_rank_image.c b/libsrc/convolution/im_rank_image.c new file mode 100644 index 00000000..6ba67f6f --- /dev/null +++ b/libsrc/convolution/im_rank_image.c @@ -0,0 +1,327 @@ +/* @(#) Sort a set of images, pixelwise, and pick out the index at each point. + * @(#) + * @(#) int im_rank_image( imarray, imout, no, index ) + * @(#) IMAGE *imarray[], *imout; + * @(#) int no, index; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * 19/8/03 + * - from im_maxvalue(), via im_gbandjoin() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Parameters. + */ +typedef struct Rank { + IMAGE **in; /* Array of input images, NULL-terminated */ + IMAGE *out; + int n; /* Number of input images */ + int index; /* Pick out this one */ +} Rank; + +/* Make a Rank struct. + */ +static Rank * +rank_new( IMAGE **in, IMAGE *out, int n, int index ) +{ + int i; + Rank *rank; + + if( !(rank = IM_NEW( out, Rank )) ) + return( NULL ); + + rank->n = n; + rank->index = index; + rank->out = out; + if( !(rank->in = IM_ARRAY( out, n + 1, IMAGE * )) ) + return( NULL ); + for( i = 0; i < n; i++ ) + rank->in[i] = in[i]; + rank->in[n] = NULL; + + return( rank ); +} + +/* Our sequence value. + */ +typedef struct { + Rank *rank; + + REGION **ir; /* Input regions */ + PEL **pts; /* Per-input region data pointer */ + PEL *sort; /* Sort pixels here */ +} RankSequence; + +/* Free a sequence value. + */ +static int +stop_rank( RankSequence *seq, IMAGE **in, Rank *rank ) +{ + int i; + + for( i = 0; i < rank->n; i++ ) + if( seq->ir[i] ) { + im_region_free( seq->ir[i] ); + seq->ir[i] = NULL; + } + + return( 0 ); +} + +/* Make a sequence value. + */ +static void * +start_rank( IMAGE *out, IMAGE **in, Rank *rank ) +{ + RankSequence *seq; + int i; + + if( !(seq = IM_NEW( out, RankSequence )) ) + return( NULL ); + + /* Init! + */ + seq->rank = rank; + seq->ir = NULL; + seq->pts = NULL; + + /* Attach regions and arrays. + */ + seq->ir = IM_ARRAY( out, rank->n + 1, REGION * ); + seq->pts = IM_ARRAY( out, rank->n + 1, PEL * ); + seq->sort = IM_ARRAY( out, + rank->n * IM_IMAGE_SIZEOF_ELEMENT( in[0] ), PEL ); + if( !seq->ir || !seq->pts || !seq->sort ) { + stop_rank( seq, in, rank ); + return( NULL ); + } + + for( i = 0; i < rank->n; i++ ) + if( !(seq->ir[i] = im_region_create( in[i] )) ) { + stop_rank( seq, in, rank ); + return( NULL ); + } + seq->ir[i] = NULL; + + return( (void *) seq ); +} + +/* Special-case max and min (rather common). + */ +#define FIND_IM_MAX( TYPE ) { \ + for( x = 0; x < sz; x++ ) { \ + TYPE top = ((TYPE *) seq->pts[0])[x]; \ + \ + for( i = 1; i < rank->n; i++ ) { \ + TYPE v = ((TYPE *) seq->pts[i])[x]; \ + \ + if( v > top ) \ + top = v; \ + } \ + \ + ((TYPE *) q)[x] = top; \ + } \ +} + +#define FIND_IM_MIN( TYPE ) { \ + for( x = 0; x < sz; x++ ) { \ + TYPE bot = ((TYPE *) seq->pts[0])[x]; \ + \ + for( i = 1; i < rank->n; i++ ) { \ + TYPE v = ((TYPE *) seq->pts[i])[x]; \ + \ + if( v < bot ) \ + bot = v; \ + } \ + \ + ((TYPE *) q)[x] = bot; \ + } \ +} + +/* Inner loop for sorting. + */ +#define FIND_IM_RANK( TYPE ) { \ + TYPE *sort = (TYPE *) seq->sort; \ + \ + for( x = 0; x < sz; x++ ) { \ + for( i = 0; i < rank->n; i++ ) { \ + TYPE v = ((TYPE *) seq->pts[i])[x]; \ + \ + /* Search for element >v. + */\ + for( j = 0; j < i; j++ ) \ + if( sort[j] > v ) \ + break; \ + \ + /* Move remaining elements down. + */ \ + for( k = i; k > j; k-- ) \ + sort[k] = sort[k - 1]; \ + \ + /* Insert this element. + */ \ + sort[j] = v; \ + } \ + \ + ((TYPE *) q)[x] = sort[rank->index]; \ + } \ +} + +#define SWITCH( OPERATION ) \ + switch( rank->out->BandFmt ) { \ + case IM_BANDFMT_UCHAR: OPERATION( unsigned char ); break; \ + case IM_BANDFMT_CHAR: OPERATION( signed char ); break; \ + case IM_BANDFMT_USHORT: OPERATION( unsigned short ); break; \ + case IM_BANDFMT_SHORT: OPERATION( signed short ); break; \ + case IM_BANDFMT_UINT: OPERATION( unsigned int ); break; \ + case IM_BANDFMT_INT: OPERATION( signed int ); break; \ + case IM_BANDFMT_FLOAT: OPERATION( float ); break; \ + case IM_BANDFMT_DOUBLE: OPERATION( double ); break; \ + \ + default: \ + assert( 0 ); \ + } + +static int +find_rank( REGION *or, RankSequence *seq, IMAGE **in, Rank *rank ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + + int x, y, i, j, k; + + /* Prepare each input area. + */ + for( i = 0; i < rank->n; i++ ) + if( im_prepare( seq->ir[i], r ) ) + return( -1 ); + + /* Loop over output! + */ + for( y = to; y < bo; y++ ) { + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + for( i = 0; i < rank->n; i++ ) + seq->pts[i] = (PEL *) + IM_REGION_ADDR( seq->ir[i], le, y ); + + /* Special-case max and min. + */ + if( rank->index == 0 ) + SWITCH( FIND_IM_MIN ) + else if( rank->index == rank->n - 1 ) + SWITCH( FIND_IM_MAX ) + else + SWITCH( FIND_IM_RANK ) + } + + return( 0 ); +} + +/* pair-wise rank of a vector of image descriptors. + */ +int +im_rank_image( IMAGE **in, IMAGE *out, int n, int index ) +{ + int i; + Rank *rank; + + if( n < 1 ) { + im_errormsg( "im_rank_image: zero input images!" ); + return( -1 ); + } + if( index < 0 || index > n - 1 ) { + im_errormsg( "im_rank_image: " + "index should be in range 0 - %d", n - 1 ); + return( -1 ); + } + if( im_poutcheck( out ) ) + return( -1 ); + for( i = 0; i < n; i++ ) { + if( im_pincheck( in[i] ) ) + return( -1 ); + + if( in[i]->Coding != IM_CODING_NONE || im_iscomplex( in[i] ) ) { + im_errormsg( "im_rank_image: " + "uncoded non-complex only" ); + return( -1 ); + } + + if( in[0]->BandFmt != in[i]->BandFmt ) { + im_errormsg( "im_rank_image: " + "input images differ in format" ); + return( -1 ); + } + if( in[0]->Xsize != in[i]->Xsize || + in[0]->Ysize != in[i]->Ysize ) { + im_errormsg( "im_rank_image: " + "input images differ in size" ); + return( -1 ); + } + if( in[0]->Bands != in[i]->Bands ) { + im_errormsg( "im_rank_image: " + "input images differ in number of bands" ); + return( -1 ); + } + } + + if( !(rank = rank_new( in, out, n, index )) || + im_cp_desc_array( out, rank->in ) || + im_demand_hint_array( out, IM_THINSTRIP, rank->in ) || + im_generate( out, + start_rank, find_rank, stop_rank, rank->in, rank ) ) + return( -1 ); + + return( 0 ); +} + +int +im_maxvalue( IMAGE **in, IMAGE *out, int n ) +{ + return( im_rank_image( in, out, n, n - 1 ) ); +} diff --git a/libsrc/convolution/im_resize_linear.c b/libsrc/convolution/im_resize_linear.c new file mode 100644 index 00000000..fe371fd5 --- /dev/null +++ b/libsrc/convolution/im_resize_linear.c @@ -0,0 +1,194 @@ +/* im_lowpass() + * History: + * 27/10/94 JC + * - IM_ARRAY modified to use local allocation + * - im_iscomplex() call added + * 17/2/95 JC + * - modernised a little + * 18/8/95 JC + * - name changed to reflect function more closely + * 2/6/04 + * - was detecting edges incorrectly, segv for some images (thanks Javi) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* What we do for each pel. + */ +#define LOOP( TYPE ) \ + if( Xint >= 0 && Yint >=0 && \ + Xint < in->Xsize-1 && Yint < in->Ysize-1 ) \ + for( bb = 0; bb < in->Bands; bb++ ) { \ + TYPE s1 = *((TYPE *) p); \ + TYPE s2 = *((TYPE *) (p + ips)); \ + TYPE s3 = *((TYPE *) (p + ils)); \ + TYPE s4 = *((TYPE *) (p + ips + ils)); \ + TYPE *t = (TYPE *) q; \ + \ + *t = (1-dx)*(1-dy)*s1 + dx*(1-dy)*s2 + \ + dy*(1-dx)*s3 + dx*dy*s4; \ + \ + p += ies; \ + q += oes; \ + } \ + else if( Xint == in->Xsize-1 && Yint >= 0 && Yint < in->Ysize - 1 ) \ + for( bb = 0; bb < in->Bands; bb++ ) { \ + TYPE s1 = *((TYPE *) p); \ + TYPE s3 = *((TYPE *) (p + ils)); \ + TYPE *t = (TYPE *) q; \ + \ + *t = (1-dy)*s1 + dy*s3; \ + \ + p += ies; \ + q += oes; \ + } \ + else if( Yint == in->Ysize-1 && Xint >= 0 && Xint < in->Xsize - 1 ) \ + for( bb = 0; bb < in->Bands; bb++ ) { \ + TYPE s1 = *((TYPE *) p); \ + TYPE s2 = *((TYPE *) (p + ips)); \ + TYPE *t = (TYPE *) q; \ + \ + *t = (1-dx)*s1 + dx*s2; \ + \ + p += ies; \ + q += oes; \ + } \ + else \ + for( bb = 0; bb < in->Bands; bb++ ) { \ + unsigned char s1 = *((unsigned char *) p); \ + TYPE *t = (TYPE *) q; \ + \ + *t = s1; \ + \ + p += ies; \ + q += oes; \ + } + +int +im_resize_linear( IMAGE *in, IMAGE *out, int X, int Y ) +{ + double dx, dy, xscale, yscale; + double Xnew, Ynew; /* inv. coord. of the interpolated pt */ + + int x, y; + int Xint, Yint; + int bb; + + PEL *input, *opline; + PEL *q, *p; + + int ils, ips, ies; /* Input and output line, pel and */ + int ols, ops, oes; /* element sizes */ + + if( im_iocheck( in, out ) ) + return( -1 ); + if( im_iscomplex( in ) ) { + im_errormsg( "im_lowpass: non-complex input only" ); + return( -1 ); + } + if( in->Coding != IM_CODING_NONE ) { + im_errormsg("im_lowpass: input should be uncoded"); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + out->Xsize = X; + out->Ysize = Y; + + if( im_setupout( out ) ) + return( -1 ); + + ils = IM_IMAGE_SIZEOF_LINE( in ); + ips = IM_IMAGE_SIZEOF_PEL( in ); + ies = IM_IMAGE_SIZEOF_ELEMENT( in ); + + ols = IM_IMAGE_SIZEOF_LINE( out ); + ops = IM_IMAGE_SIZEOF_PEL( out ); + oes = IM_IMAGE_SIZEOF_ELEMENT( out ); + +/* buffer lines +***************/ + if( !(opline = IM_ARRAY( out, ols, PEL )) ) + return( -1 ); + +/* Resampling +*************/ + input = (PEL*) in->data; + xscale = ((double)in->Xsize-1)/(X-1); + yscale = ((double)in->Ysize-1)/(Y-1); + +for (y=0; yBandFmt ) { + case IM_BANDFMT_UCHAR: LOOP( unsigned char); break; + case IM_BANDFMT_USHORT: LOOP( unsigned short ); break; + case IM_BANDFMT_UINT: LOOP( unsigned int ); break; + case IM_BANDFMT_CHAR: LOOP( signed char ); break; + case IM_BANDFMT_SHORT: LOOP( signed short ); break; + case IM_BANDFMT_INT: LOOP( signed int ); break; + case IM_BANDFMT_FLOAT: LOOP( float ); break; + case IM_BANDFMT_DOUBLE: LOOP( double ); break; + + default: + im_errormsg( "im_lowpass: unsupported image type" ); + return( -1 ); + /*NOTREACHED*/ + } + } + + if (im_writeline(y, out, opline) ) + return(-1); + } +return(0); +} diff --git a/libsrc/convolution/im_sharpen.c b/libsrc/convolution/im_sharpen.c new file mode 100644 index 00000000..e30fb7c3 --- /dev/null +++ b/libsrc/convolution/im_sharpen.c @@ -0,0 +1,308 @@ +/* Cored sharpen of LABQ image. + * + * Usage: + * + * int im_sharpen( IMAGE *in, IMAGE *out, + * int mask_size, + * int x1, int x2, + * double m1, double m2 ) + * + * Returns 0 on success and -1 on error + * + * Copyright: 1995 A. Abbood + * Author: A. Abbood + * Written on: 30/01/1995 + * 15/5/95 JC + * - updated for latest 7.3 mods + * - m3 parameter removed + * - bug fixes and speed-ups + * 4/7/95 JC + * - x3 parameter added + * - xs are now double + * 6/7/95 JC + * - xs are now ys + * - better LUT generation + * 12/3/01 JC + * - uses seperable convolution for umask + * - tiny clean ups + * 23/7/01 JC + * - fix for band extract index changed + * 21/4/04 + * - switched to gaussian mask and radius + * 20/11/04 + * - uses extract_bands() to remove and reattach ab for slight speedup + * - accepts LabS as well as LabQ for slight speedup + * - small code tidies + * - ~15% speed up in total + * 29/11/06 + * - convolve first to help region sharing + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* A lut --- we need indexes in the range [-x3,x2], so add x3 to indexes + * before starting to index table. + */ +typedef struct { + int *lut; /* Start of lut */ + int x1, x2, x3; /* Parameters scaled up to int */ +} SharpenLut; + +/* Make a lut. + */ +static SharpenLut * +build_lut( IMAGE *out, int x1, int x2, int x3, double m1, double m2 ) +{ + int i; + SharpenLut *slut = IM_NEW( out, SharpenLut ); + + if( !slut ) + return( NULL ); + + if( !(slut->lut = IM_ARRAY( out, x2 + x3 + 1, int )) ) + return( NULL ); + slut->x1 = x1; + slut->x2 = x2; + slut->x3 = x3; + + for( i = 0; i < x1; i++ ) { + slut->lut[x3 + i] = i*m1; + slut->lut[x3 - i] = -i*m1; + } + for( i = x1; i <= x2; i++ ) + slut->lut[x3 + i] = x1*m1 + (i-x1)*m2; + for( i = x1; i <= x3; i++ ) + slut->lut[x3 - i] = -(x1*m1 + (i-x1)*m2); + + return( slut ); +} + +/* Take the difference of in1 and in2 and LUT it. + */ +static void +buf_difflut( short **in, short *out, int n, SharpenLut *slut ) +{ + int range = slut->x2 + slut->x3; + int *lut = slut->lut; + int x3 = slut->x3; + short *p1 = in[1]; + short *p2 = in[0]; + int i; + + for( i = 0; i < n; i++ ) { + int v1 = p1[i]; + int v2 = p2[i]; + + /* v2 is the area average. If this is zero, then we pass the + * original image through unaltered. + */ + if( v2 == 0 ) + out[i] = v1; + else { + /* Find difference. Offset by x3 to get the expected + * range of values. + */ + int s1 = x3 + (v1 - v2); + int s2; + + /* Clip to LUT range. + */ + if( s1 < 0 ) + s1 = 0; + else if( s1 > range ) + s1 = range; + + /* Transform! + */ + s2 = v1 + lut[s1]; + + /* Clip to LabS range. + */ + if( s2 < 0 ) + s2 = 0; + else if( s2 > 32767 ) + s2 = 32767; + + /* And write. + */ + out[i] = s2; + } + } +} + +/* Make a 1 line gaussian of a specified radius. + */ +static INTMASK * +sharpen_mask_new( int radius ) +{ + INTMASK *base; + INTMASK *line; + int total; + int i; + + /* Stop at 20% of max ... bit mean, but means mask radius is roughly + * right. + */ + if( !(base = im_gauss_imask( "big1", radius / 2, 0.2 )) ) + return( NULL ); + + if( !(line = im_create_imask( "sharpen-line", base->xsize, 1 )) ) { + im_free_imask( base ); + return( NULL ); + } + + total = 0; + for( i = 0; i < base->xsize; i++ ) { + line->coeff[i] = + base->coeff[base->xsize * (base->ysize / 2) + i]; + total += line->coeff[i]; + } + line->scale = total; + + im_free_imask( base ); + +#ifdef DEBUG + printf( "sharpen_mask_new: created mask:\n" ); + im_print_imask( line ); +#endif /*DEBUG*/ + + return( line ); +} + +int +im_sharpen( IMAGE *in, IMAGE *out, + int mask_size, + double x1, double y2, double y3, + double m1, double m2 ) +{ + IMAGE *arry[3]; + IMAGE *t[4]; + INTMASK *mask; + SharpenLut *slut; + + /* Turn y parameters into xs. + */ + double x2 = (y2 - x1 * (m1 - m2)) / m2; + double x3 = (y3 - x1 * (m1 - m2)) / m2; + + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *tc[2]; + + if( im_open_local_array( out, tc, 2, "im_sharpen:1", "p" ) || + im_LabQ2LabS( in, tc[0] ) || + im_sharpen( tc[0], tc[1], + mask_size, x1, y2, y3, m1, m2 ) || + im_LabS2LabQ( tc[1], out ) ) + return( -1 ); + + return( 0 ); + } + + /* Check IMAGE parameters + */ + if( in->Coding != IM_CODING_NONE || + in->Bands != 3 || + in->BandFmt != IM_BANDFMT_SHORT ) { + im_error( "im_sharpen", _( "input not 3-band short" ) ); + return( -1 ); + } + + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Check number range. + */ + if( x1 < 0 || x2 < 0 || x1 > 99 || x2 > 99 || x1 > x2 || + x3 < 0 || x3 > 99 || x1 > x3 ) { + im_error( "im_sharpen", _( "parameters out of range" ) ); + return( -1 ); + } + + /* Set up data structures we need. First, the convolution mask we will + * use. + */ + if( !(mask = (INTMASK *) im_local( out, + (im_construct_fn) sharpen_mask_new, + (im_callback_fn) im_free_imask, + GINT_TO_POINTER( mask_size ), NULL, NULL )) ) + return( -1 ); + + /* Make the lut we will use. We need to scale up x1, x2, x3 to the + * LabS range. + */ + if( !(slut = build_lut( out, + x1 * 327.67, x2 * 327.67, x3 * 327.67, m1, m2 )) ) + return( -1 ); + + /* Open a set of local image descriptors. + */ + if( im_open_local_array( out, t, 4, "im_sharpen:2", "p" ) ) + return( -1 ); + + /* Extract L and ab, convolve L. + */ + if( im_extract_band( in, t[0], 0 ) || + im_extract_bands( in, t[1], 1, 2 ) || + im_convsep( t[0], t[2], mask ) ) + return( -1 ); + + /* Find difference of L channel and convolved L channel, and pass + * through LUT. + */ + if( im_cp_desc( t[3], t[2] ) ) + return( -1 ); + arry[0] = t[2]; arry[1] = t[0]; arry[2] = NULL; + if( im_wrapmany( arry, t[3], + (im_wrapmany_fn) buf_difflut, slut, NULL ) ) + return( -1 ); + + /* Reattach ab. + */ + if( im_bandjoin( t[3], t[1], out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/convolution/im_shrink.c b/libsrc/convolution/im_shrink.c new file mode 100644 index 00000000..2d39a5e8 --- /dev/null +++ b/libsrc/convolution/im_shrink.c @@ -0,0 +1,313 @@ +/* @(#) Shrink any non-complex image by some x, y, factor. No interpolation! + * @(#) Just average an area. Suitable for making quicklooks only! + * @(#) + * @(#) int + * @(#) im_shrink( in, out, xshrink, yshrink ) + * @(#) IMAGE *in, *out; + * @(#) double xshrink, yshrink; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Authors: Nicos Dessipris and Kirk Martinez + * Written on: 29/04/1991 + * Modified on: 2/11/92, 22/2/93 Kirk Martinez - Xres Yres & cleanup + incredibly inefficient for box filters as LUTs are used instead of + + Needs converting to a smoother filter: eg Gaussian! KM + * 15/7/93 JC + * - rewritten for partial v2 + * - ANSIfied + * - now shrinks any non-complex type + * - no longer cloned from im_convsub() + * - could be much better! see km comments above + * 3/8/93 JC + * - rounding bug fixed + * 11/1/94 JC + * - problems with .000001 and round up/down ignored! Try shrink 3738 + * pixel image by 9.345000000001 + * 7/10/94 JC + * - IM_NEW and IM_ARRAY added + * - more typedef + * 3/7/95 JC + * - IM_CODING_LABQ handling added here + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our main parameter struct. + */ +typedef struct { + double xshrink; /* Shrink factors */ + double yshrink; + int mw; /* Size of area we average */ + int mh; + int np; /* Number of pels we average */ +} ShrinkInfo; + +/* Our per-sequence parameter struct. We hold an offset for each pel we + * average. + */ +typedef struct { + REGION *ir; + int *off; +} SeqInfo; + +/* Free a sequence value. + */ +static int +shrink_stop( SeqInfo *seq, IMAGE *in, ShrinkInfo *st ) +{ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Make a sequence value. + */ +static void * +shrink_start( IMAGE *out, IMAGE *in, ShrinkInfo *st ) +{ + SeqInfo *seq = IM_NEW( out, SeqInfo ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->off = NULL; + seq->ir = im_region_create( in ); + seq->off = IM_ARRAY( out, st->np, int ); + if( !seq->off || !seq->ir ) { + shrink_stop( seq, in, st ); + return( NULL ); + } + + return( (void *) seq ); +} + +/* Integer shrink. + */ +#define ishrink(TYPE) \ + for( y = to; y < bo; y++ ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = le; x < ri; x++ ) { \ + int ix = x * st->xshrink; \ + int iy = y * st->yshrink; \ + TYPE *p = (TYPE *) IM_REGION_ADDR( ir, ix, iy ); \ + \ + for( b = 0; b < ir->im->Bands; b++ ) { \ + int sum = 0; \ + int *t = seq->off; \ + \ + for( z = 0; z < st->np; z++ ) \ + sum += p[*t++]; \ + \ + *q++ = sum / st->np; \ + p++; \ + } \ + } \ + } + +/* FP shrink. + */ +#define fshrink(TYPE) \ + for( y = to; y < bo; y++ ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = le; x < ri; x++ ) { \ + int ix = x * st->xshrink; \ + int iy = y * st->yshrink; \ + TYPE *p = (TYPE *) IM_REGION_ADDR( ir, ix, iy ); \ + \ + for( b = 0; b < ir->im->Bands; b++ ) { \ + double sum = 0; \ + int *t = seq->off; \ + \ + for( z = 0; z < st->np; z++ ) \ + sum += p[*t++]; \ + \ + *q++ = sum / st->np; \ + p++; \ + } \ + } \ + } + +/* Shrink a REGION. + */ +static int +shrink_gen( REGION *or, SeqInfo *seq, IMAGE *in, ShrinkInfo *st ) +{ + REGION *ir = seq->ir; + + Rect *r = &or->valid; + Rect s; + int le = r->left; + int ri = IM_RECT_RIGHT( r ); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int x, y, z, b; + + /* What part of the input image do we need? Very careful: round left + * down, round right up. + */ + s.left = r->left * st->xshrink; + s.top = r->top * st->yshrink; + s.width = ceil( IM_RECT_RIGHT( r ) * st->xshrink ) - s.left; + s.height = ceil( IM_RECT_BOTTOM( r ) * st->yshrink ) - s.top; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Init offsets for pel addressing. Note that offsets must be for the + * type we will address the memory array with. + */ + for( z = 0, y = 0; y < st->mh; y++ ) + for( x = 0; x < st->mw; x++ ) + seq->off[z++] = (IM_REGION_ADDR( ir, x, y ) - IM_REGION_ADDR( ir, 0, 0 )) / + IM_IMAGE_SIZEOF_ELEMENT( ir->im ); + + switch( ir->im->BandFmt ) { + case IM_BANDFMT_UCHAR: ishrink(unsigned char); break; + case IM_BANDFMT_CHAR: ishrink(char); break; + case IM_BANDFMT_USHORT: ishrink(unsigned short); break; + case IM_BANDFMT_SHORT: ishrink(short); break; + case IM_BANDFMT_UINT: ishrink(unsigned int); break; + case IM_BANDFMT_INT: ishrink(int); break; + case IM_BANDFMT_FLOAT: fshrink(float); break; + case IM_BANDFMT_DOUBLE: fshrink(double); break; + + default: + im_errormsg( "im_shrink: unsupported input format" ); + return( -1 ); + } + + return( 0 ); +} + +static int +shrink( IMAGE *in, IMAGE *out, double xshrink, double yshrink ) +{ + ShrinkInfo *st; + + /* Check parameters. + */ + if( !in || im_iscomplex( in ) ) { + im_errormsg( "im_shrink: non-complex input only" ); + return( -1 ); + } + if( xshrink < 1.0 || yshrink < 1.0 ) { + im_errormsg( "im_shrink: shrink factors should both be >1" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Prepare output. Note: we round the output width down! + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = in->Xsize / xshrink; + out->Ysize = in->Ysize / yshrink; + out->Xres = in->Xres / xshrink; + out->Yres = in->Yres / yshrink; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_shrink: image has shrunk to nothing" ); + return( -1 ); + } + + /* Build and attach state struct. + */ + if( !(st = IM_NEW( out, ShrinkInfo )) ) + return( -1 ); + st->xshrink = xshrink; + st->yshrink = yshrink; + st->mw = ceil( xshrink ); + st->mh = ceil( yshrink ); + st->np = st->mw * st->mh; + + /* Set demand hints. We want THINSTRIP, as we will be demanding a + * large area of input for each output line. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + shrink_start, shrink_gen, shrink_stop, in, st ) ) + return( -1 ); + + return( 0 ); +} + +/* Wrap up the above: do IM_CODING_LABQ as well. + */ +int +im_shrink( IMAGE *in, IMAGE *out, double xshrink, double yshrink ) +{ + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t[2]; + + if( im_open_local_array( out, t, 2, "im_shrink:1", "p" ) || + im_LabQ2LabS( in, t[0] ) || + shrink( t[0], t[1], xshrink, yshrink ) || + im_LabS2LabQ( t[1], out ) ) + return( -1 ); + } + else if( in->Coding == IM_CODING_NONE ) { + if( shrink( in, out, xshrink, yshrink ) ) + return( -1 ); + } + else { + im_errormsg( "im_shrink: unknown coding type" ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/convolution/im_spcor.c b/libsrc/convolution/im_spcor.c new file mode 100644 index 00000000..f54e415d --- /dev/null +++ b/libsrc/convolution/im_spcor.c @@ -0,0 +1,550 @@ +/* @(#) Functions which calculates the correlation coefficient between two + * @(#) images. + * @(#) + * @(#) int im_spcor( IMAGE *in, IMAGE *ref, IMAGE *out ) + * @(#) + * @(#) We calculate: + * @(#) + * @(#) sumij (ref(i,j)-mean(ref))(inkl(i,j)-mean(inkl)) + * @(#) c(k,l) = ------------------------------------------------ + * @(#) sqrt(sumij (ref(i,j)-mean(ref))^2) * + * @(#) sqrt(sumij (inkl(i,j)-mean(inkl))^2) + * @(#) + * @(#) where inkl is the area of in centred at position (k,l). + * @(#) + * @(#) Writes float to out. in and ref must be 1 band uchar, or 1 band + * @(#) ushort. + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on : + * 20/2/95 JC + * - updated + * - ANSIfied, a little + * 21/2/95 JC + * - rewritten + * - partialed + * - speed-ups + * - new correlation coefficient (see above), from Niblack "An + * Introduction to Digital Image Processing,", Prentice/Hall, pp 138. + * 4/9/97 JC + * - now does short/ushort as well + * 13/2/03 JC + * - oops, could segv for short images + * 14/4/04 JC + * - sets Xoffset / Yoffset + * 8/3/06 JC + * - use im_embed() with edge stretching on the input, not the output + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Hold global stuff here. + */ +typedef struct { + IMAGE *ref; /* Image we are searching for */ + double rmean; /* Mean of search window */ + double c1; /* sqrt(sumij (ref(i,j)-mean(ref))^2) */ +} SpcorInfo; + +typedef struct { + + REGION *f; + int *f_cols; + size_t max_cols; + +} spcor2_seq; + +typedef struct { + + IMAGE *w; + gint64 area; + double recip_area; + double mean; + double n_var; + +} spcor2_w_inf; + +static spcor2_seq *spcor2_start( IMAGE *r, IMAGE *f ); +static int spcor2_gen( REGION *r, spcor2_seq *seq, void *unrequired, spcor2_w_inf *w_inf ); +static int spcor2_stop( spcor2_seq *seq ); + +#define LOOP(IN) \ +{ \ + IN *a = (IN *) p; \ + IN *b = (IN *) ref->data; \ + int in_lsk = lsk / sizeof( IN ); \ + IN *a1, *b1; \ + \ + /* For each pel in or, loop over ref. First, \ + * calculate mean of area in ir corresponding to ref. \ + */ \ + for( a1 = a, sum1 = 0, j = 0; j < ref->Ysize; j++, a1 += in_lsk ) \ + for( i = 0; i < ref->Xsize; i++ ) \ + sum1 += a1[i]; \ + imean = (double) sum1 / (ref->Xsize * ref->Ysize); \ + \ + /* Loop over ir again, this time calculating \ + * sum-of-squares-of-differences for this window on \ + * ir, and also sum-of-products-of-differences. \ + */ \ + for( a1 = a, b1 = b, sum2 = 0.0, sum3 = 0.0, j = 0; \ + j < ref->Ysize; j++, a1 += in_lsk, b1 += ref->Xsize ) { \ + for( i = 0; i < ref->Xsize; i++ ) { \ + /* Reference pel, and input pel. \ + */ \ + IN rp = b1[i]; \ + IN ip = a1[i]; \ + \ + /* Accumulate sum-of-squares-of- \ + * differences for input image. \ + */ \ + double t = ip - imean; \ + sum2 += t * t; \ + \ + /* Accumulate product-of-differences. \ + */ \ + sum3 += (rp - inf->rmean) * (ip - imean); \ + } \ + } \ +} + +/* spcor generate function. + */ +static int +spcor_gen( REGION *or, REGION *ir, IMAGE *in, SpcorInfo *inf ) +{ + IMAGE *ref = inf->ref; + Rect irect; + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int ri = IM_RECT_RIGHT(r); + + int x, y, i, j; + int lsk; + + double imean; + double sum1; + double sum2, sum3; + double c2, cc; + + /* What part of ir do we need? + */ + irect.left = or->valid.left; + irect.top = or->valid.top; + irect.width = or->valid.width + ref->Xsize - 1; + irect.height = or->valid.height + ref->Ysize - 1; + + if( im_prepare( ir, &irect ) ) + return( -1 ); + lsk = IM_REGION_LSKIP( ir ); + + /* Loop over or. + */ + for( y = to; y < bo; y++ ) { + float *q = (float *) IM_REGION_ADDR( or, le, y ); + + for( x = le; x < ri; x++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir, x, y ); + + /* Find sums for this position. + */ + switch( ref->BandFmt ) { + case IM_BANDFMT_UCHAR: LOOP(unsigned char); break; + case IM_BANDFMT_USHORT: LOOP(unsigned short); break; + case IM_BANDFMT_SHORT: LOOP(signed short); break; + default: + error_exit( "im_spcor: internal error #7934" ); + + /* Keep gcc -Wall happy. + */ + return( -1 ); + } + + /* Now: calculate correlation coefficient! + */ + c2 = sqrt( sum2 ); + cc = sum3 / (inf->c1 * c2); + + *q++ = cc; + } + } + + return( 0 ); +} + +/* Pre-calculate stuff for our reference image. + */ +static SpcorInfo * +make_inf( IMAGE *out, IMAGE *ref ) +{ + SpcorInfo *inf = IM_NEW( out, SpcorInfo ); + int sz = ref->Xsize * ref->Ysize; + PEL *p = (PEL *) ref->data; + double s; + int i; + + if( !inf ) + return( NULL ); + + /* Pre-calculate stuff on our reference image. + */ + inf->ref = ref; + if( im_avg( inf->ref, &inf->rmean ) ) + return( NULL ); + + /* Find sqrt-of-sum-of-squares-of-differences. + */ + for( s = 0.0, i = 0; i < sz; i++ ) { + double t = (int) p[i] - inf->rmean; + s += t * t; + } + inf->c1 = sqrt( s ); + + return( inf ); +} + +int +im_spcor_raw( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + SpcorInfo *inf; + + /* PIO between in and out; WIO from ref. + */ + if( im_piocheck( in, out ) || im_incheck( ref ) ) + return( -1 ); + + /* Check sizes. + */ + if( in->Xsize < ref->Xsize || in->Ysize < ref->Ysize ) { + im_errormsg( "im_spcor_raw: ref not smaller than in" ); + return( -1 ); + } + + /* Check types. + */ + if( in->Coding != IM_CODING_NONE || in->Bands != 1 || + ref->Coding != IM_CODING_NONE || ref->Bands != 1 || + in->BandFmt != ref->BandFmt ) { + im_errormsg( "im_spcor_raw: input not uncoded 1 band" ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_UCHAR && + in->BandFmt != IM_BANDFMT_SHORT && + in->BandFmt != IM_BANDFMT_USHORT ) { + im_errormsg( "im_spcor_raw: input not char/short/ushort" ); + return( -1 ); + } + + /* Prepare the output image. + */ + if( im_cp_descv( out, in, ref, NULL ) ) + return( -1 ); + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Xsize = in->Xsize - ref->Xsize + 1; + out->Ysize = in->Ysize - ref->Ysize + 1; + + /* Pre-calculate some stuff. + */ + if( !(inf = make_inf( out, ref )) ) + return( -1 ); + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Write the correlation. + */ + if( im_generate( out, + im_start_one, spcor_gen, im_stop_one, in, inf ) ) + return( -1 ); + + out->Xoffset = -ref->Xsize / 2; + out->Yoffset = -ref->Ysize / 2; + + return( 0 ); +} + +/* The above, with the input expanded to make out the same size as in. + */ +int +im_spcor( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_spcor intermediate", "p" ); + + if( !t1 || + im_embed( in, t1, 1, + ref->Xsize / 2, ref->Ysize / 2, + in->Xsize + ref->Xsize - 1, + in->Ysize + ref->Ysize - 1 ) || + im_spcor_raw( t1, ref, out ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} + +int +im_spcor2_raw( + IMAGE *f, + IMAGE *w, + IMAGE *r +){ +#define FUNCTION_NAME "im_spcor_raw" + + DOUBLEMASK *w_stats; + spcor2_w_inf *w_inf; + + if( im_piocheck( f, r ) || im_incheck( w ) ) + return -1; + + if( f-> Xsize < w-> Xsize || f-> Ysize < w-> Ysize ){ + im_error( FUNCTION_NAME, "window must be smaller than search area" ); + return -1; + } + if( f-> Coding || w-> Coding ){ + im_error( FUNCTION_NAME, "uncoded images only" ); + return -1; + } + if( 1 != f-> Bands || 1 != w-> Bands ){ + im_error( FUNCTION_NAME, "single band images only" ); + return -1; + } + if( !( IM_BANDFMT_UCHAR == f-> BandFmt + || IM_BANDFMT_CHAR == f-> BandFmt + || IM_BANDFMT_USHORT == f-> BandFmt + || IM_BANDFMT_SHORT == f-> BandFmt ) ){ + im_error( FUNCTION_NAME, "short or char images only" ); + return -1; + } + if( f-> BandFmt != w-> BandFmt ){ + im_error( FUNCTION_NAME, "band formats must match" ); + return -1; + } + if( im_cp_descv( r, f, w, NULL ) ) + return -1; + + r-> Xsize-= ( w-> Xsize - 1 ); + r-> Ysize-= ( w-> Ysize - 1 ); + r-> BandFmt= IM_BANDFMT_FLOAT; + r-> Bbits= IM_BBITS_FLOAT; + r-> Xoffset= - w-> Xsize / 2; + r-> Yoffset= - w-> Ysize / 2; + + if( im_demand_hint( r, IM_FATSTRIP, f, NULL ) ) + return -1; + + w_inf= IM_NEW( r, spcor2_w_inf ); + w_stats= im_stats( w ); + + if( ! w_inf || ! w_stats ) + return -1; + + w_inf-> w= w; + w_inf-> area= w-> Xsize * w-> Ysize; + w_inf-> recip_area= 1.0 / (double) w_inf-> area; + w_inf-> mean= w_stats-> coeff[ 4 ]; + w_inf-> n_var= w_stats-> coeff[ 3 ] - w_stats-> coeff[ 2 ] * w_stats-> coeff[ 2 ] * w_inf-> recip_area; + + im_free_dmask( w_stats ); + + return im_generate( r, (void*)spcor2_start, spcor2_gen, spcor2_stop, f, w_inf ); + +#undef FUNCTION_NAME +} + +static spcor2_seq * +spcor2_start( IMAGE *r, IMAGE *f ){ + + REGION *reg= im_region_create( f ); + spcor2_seq *seq; + + if( ! reg ) + return NULL; + + seq= IM_NEW( NULL, spcor2_seq ); + if( ! seq ) + return NULL; + + seq-> f= reg; + seq-> f_cols= NULL; + seq-> max_cols= 0; + + return seq; +} + +static int +spcor2_gen( + REGION *r, + spcor2_seq *seq, + void *unrequired, + spcor2_w_inf *w_inf +){ + + Rect need= { + r-> valid. left, + r-> valid. top, + r-> valid. width + w_inf-> w-> Xsize - 1, + r-> valid. height + w_inf-> w-> Ysize - 1 + }; + int j; + float *r_data= (float*) IM_REGION_ADDR( r, r-> valid. left, r-> valid. top ); + size_t r_skip= IM_REGION_LSKIP( r ) / sizeof( float ); + float *r_end= r_data + r-> valid. height * r_skip; + + r_skip-= r-> valid. width; + + if( im_prepare( seq-> f, & need ) ) + return -1; + + if( need. width > seq-> max_cols ){ + im_free( seq-> f_cols ); + + seq-> f_cols= IM_ARRAY( NULL, need. width + 1, int ); /* one spare for the last right move */ + if( ! seq-> f_cols ) + return -1; + + seq-> max_cols= need. width; + } + memset( seq-> f_cols, 0, seq-> max_cols * sizeof( int ) ); + +#define LOOPS(TYPE) { \ + TYPE *f_start= (TYPE*) IM_REGION_ADDR( seq-> f, need. left, need. top ); \ + size_t f_skip= IM_REGION_LSKIP( seq-> f ) / sizeof( TYPE ); \ + size_t f_row_skip= f_skip - r-> valid. width; \ + size_t f_win_skip= f_skip - w_inf-> w-> Xsize; \ + \ + TYPE *f_win_end= f_start; \ + TYPE *f_stop= f_win_end + f_skip * w_inf-> w-> Ysize; \ + \ + for( ; f_win_end < f_stop; f_win_end+= f_skip ) \ + for( j= 0; j < need. width; ++j ) \ + seq-> f_cols[ j ]+= f_win_end[ j ]; \ + \ + for( ; r_data < r_end; r_data+= r_skip, f_start+= f_row_skip, f_win_end+= f_skip ){ \ + double f_mean= 0.0; \ + \ + for( j= 0; j < w_inf-> w-> Xsize; ++j ) \ + f_mean+= seq-> f_cols[ j ]; \ + \ + f_mean*= w_inf-> recip_area; \ + \ + for( j= 0; j < r-> valid. width; ++f_start, ++r_data, \ + f_mean+= ( seq-> f_cols[ w_inf-> w-> Xsize + j ] - seq-> f_cols[ j ] ) * w_inf-> recip_area, \ + ++j ){ \ + \ + double num_sum= 0.0; \ + double den_sum= 0.0; \ + TYPE *w_data= (TYPE*) w_inf-> w-> data; \ + TYPE *w_end= w_data + w_inf-> area; \ + TYPE *w_stop; \ + TYPE *f_data= f_start; \ + \ + for( ; w_data < w_end; f_data+= f_win_skip ) \ + for( w_stop= w_data + w_inf-> w-> Xsize; w_data < w_stop; ++w_data, ++f_data ){ \ + \ + double f_term= *f_data - f_mean; \ + \ + num_sum+= f_term * ( *w_data - w_inf-> mean ); \ + den_sum+= f_term * f_term; \ + } \ + \ + *r_data= num_sum * pow( den_sum * w_inf-> n_var, -0.5 ); \ + } \ + \ + if( r_data + r_skip < r_end ) \ + for( j= 0; j < need. width; ++j ) \ + seq-> f_cols[ j ]+= f_win_end[ j ] - f_start[ j ]; \ + } \ + } + + switch( w_inf-> w-> BandFmt ){ + case IM_BANDFMT_UCHAR: LOOPS( guint8 ) break; + case IM_BANDFMT_CHAR: LOOPS( gint8 ) break; + case IM_BANDFMT_USHORT: LOOPS( guint16 ) break; + case IM_BANDFMT_SHORT: LOOPS( gint16 ) break; + } + +#undef LOOPS + return 0; +} + +static int +spcor2_stop( spcor2_seq *seq ){ + + im_region_free( seq-> f ); + im_free( seq-> f_cols ); + im_free( seq ); + + return 0; +} + +int +im_spcor2( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_spcor2 intermediate", "p" ); + + if( !t1 || + im_embed( in, t1, 1, + ref->Xsize / 2, ref->Ysize / 2, + in->Xsize + ref->Xsize - 1, + in->Ysize + ref->Ysize - 1 ) || + im_spcor2_raw( t1, ref, out ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/convolution/im_stretch3.c b/libsrc/convolution/im_stretch3.c new file mode 100644 index 00000000..3eec8f93 --- /dev/null +++ b/libsrc/convolution/im_stretch3.c @@ -0,0 +1,323 @@ +/* Function to stretch an image by 3%, and displace in x and y. Cubic + * interpolation with a seperable mask. Displacements are: + * + * 0 <= xdisp < 1.0. + * 0 <= ydisp < 1.0. + * + * Each horizontal block of 33 pixels is stretched to 34. + * + * Written by Ahmed Abbood + * August-1994 + * + * Any unsigned short image. Output image is 3 pixels smaller because of + * convolution, but x is larger by 3%: + * + * out->Xsize = 34*(in->Xsize / 33) + in->Xsize%33 - 3; + * out->Ysize = in->Ysize - 3; + * + * 20/10/95 JC + * - was not freeing regions correctly + * - tidied up + * 29/3/96 JC + * - completely rewritten ... now produces correct result, and is 2x + * faster + * 18/9/97 JC + * - added to VIPS library as im_stretch3 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Data for the cubic interpolation function. + */ +typedef struct { + IMAGE *in; + double dx, dy; + + int xoff, yoff; /* Mask we start with for this disp. */ + int mask[34][4]; /* Fixed-point masks for each output pixel */ +} StretchInfo; + +/* Per-thread info. + */ +typedef struct seq_info { + StretchInfo *sin; + REGION *ir; + unsigned short *buf; + int lsk; +} SeqInfo; + +static int +stop_stretch( SeqInfo *seq ) +{ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +static void * +start_stretch( IMAGE *out, IMAGE *in, StretchInfo *sin ) +{ + SeqInfo *seq = IM_NEW( out, SeqInfo ); + + if( !seq ) + return( NULL ); + + seq->sin = sin; + seq->ir = im_region_create( in ); + seq->lsk = IM_IMAGE_N_ELEMENTS( out ); + seq->buf = IM_ARRAY( out, 4*seq->lsk, unsigned short ); + + if( !seq->buf || !seq->ir ) { + stop_stretch( seq ); + return( NULL ); + } + + return( (void *)seq ); +} + +/* Stretch a line of pels into a line in the buffer. + */ +static void +make_xline( StretchInfo *sin, + unsigned short *p, unsigned short *q, int w, int m ) +{ + int bands = sin->in->Bands; + int tot; + int x, b; + + /* Offsets for subsequent pixels. + */ + int o1 = 1*bands; + int o2 = 2*bands; + int o3 = 3*bands; + + for( x = 0; x < w; x++ ) { + int *mask = &sin->mask[m][0]; + unsigned short *p1 = p; + + /* Loop for this pel. + */ + for( b = 0; b < bands; b++ ) { + tot = p1[0]*mask[0] + p1[o1]*mask[1] + + p1[o2]*mask[2] + p1[o3]*mask[3]; + tot = IM_MAX( 0, tot ); + p1++; + *q++ = (tot + 16384) >> 15; + } + + /* Move to next mask. + */ + m++; + if( m == 34 ) + /* Back to mask 0, reuse this input pel. + */ + m = 0; + else + /* Move to next input pel. + */ + p += bands; + } +} + +/* As above, but do the vertical resample. lsk is how much we add to move down + * a line in p, boff is [0,1,2,3] for which buffer line is mask[3]. + */ +static void +make_yline( StretchInfo *sin, int lsk, int boff, + unsigned short *p, unsigned short *q, int w, int m ) +{ + int bands = sin->in->Bands; + int we = w * bands; + int *mask = &sin->mask[m][0]; + int tot; + int x; + + /* Offsets for subsequent pixels. Down a line each time. + */ + int o0 = lsk*boff; + int o1 = lsk*((boff + 1) % 4); + int o2 = lsk*((boff + 2) % 4); + int o3 = lsk*((boff + 3) % 4); + + for( x = 0; x < we; x++ ) { + tot = p[o0]*mask[0] + p[o1]*mask[1] + + p[o2]*mask[2] + p[o3]*mask[3]; + tot = IM_MAX( 0, tot ); + p++; + *q++ = (tot + 16384) >> 15; + } +} + +static int +stretch_gen( REGION *or, SeqInfo *seq, IMAGE *in, StretchInfo *sin ) +{ + REGION *ir = seq->ir; + Rect *r = &or->valid; + Rect r1; + int x, y; + + /* What mask do we start with? + */ + int xstart = (r->left + sin->xoff) % 34; + + /* What part of input do we need for this output? + */ + r1.left = r->left - (r->left + sin->xoff) / 34; + r1.top = r->top; + x = IM_RECT_RIGHT( r ); + x = x - (x + sin->xoff) / 34 + 3; + r1.width = x - r1.left; + r1.height = r->height + 3; + if( im_prepare( ir, &r1 ) ) + return( -1 ); + + /* Fill the first three lines of the buffer. + */ + for( y = 0; y < 3; y++ ) { + unsigned short *p = (unsigned short *) + IM_REGION_ADDR( ir, r1.left, y + r1.top ); + unsigned short *q = seq->buf + seq->lsk*y; + + make_xline( sin, p, q, r->width, xstart ); + } + + /* Loop for subsequent lines: stretch a new line of x pels, and + * interpolate a line of output from the 3 previous xes plus this new + * one. + */ + for( y = 0; y < r->height; y++ ) { + /* Next line of fresh input pels. + */ + unsigned short *p = (unsigned short *) + IM_REGION_ADDR( ir, r1.left, y + r1.top + 3 ); + + /* Next line we fill in the buffer. + */ + int boff = (y + 3)%4; + unsigned short *q = seq->buf + boff*seq->lsk; + + /* Line we write in output. + */ + unsigned short *q1 = (unsigned short *) + IM_REGION_ADDR( or, r->left, y + r->top ); + + /* Process this new xline. + */ + make_xline( sin, p, q, r->width, xstart ); + + /* Generate new output line. + */ + make_yline( sin, seq->lsk, boff, + seq->buf, q1, r->width, sin->yoff ); + } + + return( 0 ); +} + +int +im_stretch3( IMAGE *in, IMAGE *out, double dx, double dy ) +{ + StretchInfo *sin; + int i; + + /* Check our args. + */ + if( in->Coding != IM_CODING_NONE || in->BandFmt != IM_BANDFMT_USHORT ) { + im_errormsg( "im_stretch3: not uncoded unsigned short" ); + return( -1 ); + } + if( dx < 0 || dx >= 1.0 || dy < 0 || dy >= 1.0 ) { + im_errormsg( "im_stretch3: displacements out of range [0,1)" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Prepare the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = 34*(in->Xsize / 33) + in->Xsize%33 - 3; + out->Ysize = in->Ysize - 3; + + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + if( !(sin = IM_NEW( out, StretchInfo )) ) + return( -1 ); + + /* Save parameters. + */ + sin->in = in; + sin->dx = dx; + sin->dy = dy; + + /* Generate masks. + */ + for( i = 0; i < 34; i++ ) { + double d = (34.0 - i)/34.0; + + double y0 = 2.0*d*d - d - d*d*d; + double y1 = 1.0 - 2.0*d*d + d*d*d; + double y2 = d + d*d - d*d*d; + double y3 = -d*d + d*d*d; + + sin->mask[i][0] = IM_RINT( y0 * 32768 ); + sin->mask[i][1] = IM_RINT( y1 * 32768 ); + sin->mask[i][2] = IM_RINT( y2 * 32768 ); + sin->mask[i][3] = IM_RINT( y3 * 32768 ); + } + + /* Which mask do we start with to apply these offsets? + */ + sin->xoff = (dx * 33.0) + 0.5; + sin->yoff = (dy * 33.0) + 0.5; + + if( im_generate( out, + start_stretch, stretch_gen, stop_stretch, in, sin ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/convolution/im_zerox.c b/libsrc/convolution/im_zerox.c new file mode 100644 index 00000000..a76adbcb --- /dev/null +++ b/libsrc/convolution/im_zerox.c @@ -0,0 +1,178 @@ +/* @(#) Functions which detects the +ve and -ve edges of + * @(#) zero crossings of an image depending on the flag + * @(#) Function im_zerox() assumes that the imin file is an integer image + * @(#) either memory mapped or in a buffer. + * @(#) The output image is byte with + * @(#) zero crossing set to 255 and all othre values set to zero + * @(#) + * @(#) int im_zerox(pimin, pimout, flag) + * @(#) IMAGE *pimin, *pimout; + * @(#) int flag; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : + * 1/2/95 JC + * - rewritten for PIO + * - some bugs removed + * 11/5/06 + * - small clean ups + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define loop( TYPE ) \ + for( i = 0; i < ne; i++ ) { \ + TYPE p1 = ((TYPE *)p)[i]; \ + TYPE p2 = ((TYPE *)p)[i + b]; \ + \ + if( flag == 1 && p1 > 0 && p2 <= 0 ) \ + q[i] = 255; \ + else if( flag == -1 && p1 < 0 && p2 >= 0 ) \ + q[i] = 255; \ + else \ + q[i] = 0; \ + } + +/* Zerox generate function. + */ +static int +zerox_gen( REGION *or, REGION *ir, IMAGE *in, int flag ) +{ + Rect irect; + Rect *r = &or->valid; + + /* Range of pixels we loop over. + */ + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM( r ); + int b = in->Bands; + int ne = b * r->width; + + int i, y; + + /* We need to be able to see one pixel to the right. + */ + irect.top = r->top; + irect.left = r->left; + irect.width = r->width + 1; + irect.height = r->height; + if( im_prepare( ir, &irect ) ) + return( -1 ); + + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: loop( signed char ); break; + case IM_BANDFMT_SHORT: loop( signed short ); break; + case IM_BANDFMT_INT: loop( signed int ); break; + case IM_BANDFMT_FLOAT: loop( float ); break; + case IM_BANDFMT_DOUBLE: loop( double ); break; + + default: + error_exit( "im_zerox: internal error" ); + /*NOTREACHED*/ + } + } + + return( 0 ); +} + +int +im_zerox( IMAGE *in, IMAGE *out, int flag ) +{ + IMAGE *t1 = im_open_local( out, "im_zerox#1" , "p" ); + + if( !t1 ) + return( -1 ); + if( flag != -1 && flag != 1 ) { + im_error( "im_zerox", _( "flag not -1 ot 1" ) ); + return( -1 ); + } + if( im_piocheck( in, t1 ) ) + return( -1 ); + if( im_iscomplex( in ) || in->Coding != IM_CODING_NONE ) { + im_error( "im_zerox", _( "non-complex uncoded only" ) ); + return( -1 ); + } + if( in->Xsize < 2 ) { + im_error( "im_zerox", _( "image too narrow" ) ); + return( -1 ); + } + if( im_isuint( in ) ) + /* Unsigned type, therefore there will be no zero-crossings. + */ + return( im_black( out, in->Xsize, in->Ysize, in->Bands ) ); + + /* Force output to be BYTE. Output is narrower than input by 1 pixel. + */ + if( im_cp_desc( t1, in ) ) + return( -1 ); + t1->Bbits = IM_BBITS_BYTE; + t1->BandFmt = IM_BANDFMT_UCHAR; + t1->Xsize -= 1; + + /* Set hints - THINSTRIP is ok with us. + */ + if( im_demand_hint( t1, IM_THINSTRIP, NULL ) ) + return( -1 ); + + /* Generate image. + */ + if( im_generate( t1, im_start_one, zerox_gen, im_stop_one, + in, GINT_TO_POINTER( flag ) ) ) + return( -1 ); + + /* Now embed it in a larger image. + */ + if( im_embed( t1, out, 0, 0, 0, in->Xsize, in->Ysize ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/convolution/man3/Makefile.am b/libsrc/convolution/man3/Makefile.am new file mode 100644 index 00000000..d66f0ec7 --- /dev/null +++ b/libsrc/convolution/man3/Makefile.am @@ -0,0 +1,58 @@ +man_MANS = \ + im_addgnoise.3 \ + im_compass.3 \ + im_conv.3 \ + im_conv_raw.3 \ + im_convf.3 \ + im_convf_raw.3 \ + im_convsep.3 \ + im_convsep_raw.3 \ + im_convsepf.3 \ + im_convsepf_raw.3 \ + im_convsub.3 \ + im_contrast_surface.3 \ + im_contrast_surface_raw.3 \ + im_create_dmask.3 \ + im_create_imask.3 \ + im_dup_dmask.3 \ + im_dup_imask.3 \ + im_embed.3 \ + im_fastcor.3 \ + im_free_dmask.3 \ + im_free_imask.3 \ + im_gauss_dmask.3 \ + im_gauss_imask.3 \ + im_gaussnoise.3 \ + im_gradient.3 \ + im_lindetect.3 \ + im_log_dmask.3 \ + im_log_imask.3 \ + im_lowpass.3 \ + im_maxvalue.3 \ + im_mpercent.3 \ + im_offsets45.3 \ + im_offsets90.3 \ + im_print_dmask.3 \ + im_print_imask.3 \ + im_rank.3 \ + im_rank_image.3 \ + im_read_dmask.3 \ + im_norm_dmask.3 \ + im_read_imask.3 \ + im_rotate_dmask45.3 \ + im_rotate_dmask90.3 \ + im_rotate_imask45.3 \ + im_rotate_imask90.3 \ + im_scale_dmask.3 \ + im_sharpen.3 \ + im_shrink.3 \ + im_spcor.3 \ + im_stretch3.3 \ + im_write_dmask.3 \ + im_write_dmask_name.3 \ + im_write_imask.3 \ + im_write_imask_name.3 \ + im_zerox.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/convolution/man3/im_addgnoise.3 b/libsrc/convolution/man3/im_addgnoise.3 new file mode 100644 index 00000000..f41ed39f --- /dev/null +++ b/libsrc/convolution/man3/im_addgnoise.3 @@ -0,0 +1,27 @@ +.TH IM_ADDGNOISE 3 "14 May 1991" +.SH NAME +im_addgnoise \- add gaussian noise to an image +.SH SYNOPSIS +#include + +int im_addgnoise(in, out, sigma) +.br +IMAGE *in, *out; +.br +double sigma; + +.SH DESCRIPTION +im_addgnoise() adds gaussian noise with mean 0 and standard deviation sigma to +the image held by the image descriptor in and writes the result on the image +descriptor out. The function works on any non-complex input image. +Input can have any no of bands. The noise is generated by adding +12 random numbers. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_add(3), im_lintra(3), im_multiply(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 25/04/1991 diff --git a/libsrc/convolution/man3/im_compass.3 b/libsrc/convolution/man3/im_compass.3 new file mode 100644 index 00000000..5679d2bc --- /dev/null +++ b/libsrc/convolution/man3/im_compass.3 @@ -0,0 +1,106 @@ +.TH IM_COMPASS 3 "2 May 1991" +.SH NAME +im_compass, im_rank_image, im_maxvalue, im_lindetect, im_gradient \- extract features from an image with +a rotating input mask +.SH SYNOPSIS +#include + +int im_rank_image(in, out, n, index) +.br +IMAGE **in; +.br +IMAGE *out; +.br +int n; +.br +int index; + +int im_maxvalue(in, out, n) +.br +IMAGE **in; +.br +IMAGE *out; +.br +int n; + +int im_compass(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_lindetect(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_gradient(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; +.SH DESCRIPTION +.B im_rank_image() +sorts the input images pixel-wise, then outputs an image in which each pixel +is selected from the sorted list by the +.B index +parameter. For example, if +.B index +is zero, then each output pixel will be the minimum of all the corresponding +input pixels. +It works for any uncoded, non-complex +image type. All input images must match in size, format, and number of bands. + +These functions convolve the image pointed by the image descriptor in with the +mask pointed by mask and put the result in the image pointed by out. + +The mask structure INTMASK is returned by the function im_read_imask(3), for +integer mask only. Input should be unsigned char; processing is carried out +using look-up tables. The output is integer. coefficients. The size of the +output image and the number of channels are the same as the corresponding of +the input. + +If the sizes of the mask are xm and ym and the sizes of the image are xs and ys, +then there is a black border at the output image as follows: + +The top ym/2 lines black, bottom ys-ym/2-ym lines black; each of the remaining +lines has the initial xm/2 pels blank, and the final +xs-xm/2-xm pels black (division over 2 is integer division). +The output at each point is divided by scale and then the offset is added. +Both offset and scale are held in mask. + +.B im_maxvalue() +is a convenience function: it is simply: + + im_rank_image( in, out, n, n - 1 ) + +.B im_compass() +convolves each point of the input byte image pointed by in with 8 masks. +The first mask is the entered mask and the seven remaining masks are produced +by successive rotations of the entered mask by 45 degrees. The maximum output +of all masks at each point is written to the output integer IMAGE descriptor. +The function expects as input a square mask of odd size. + +.B im_lindetect() +convolves each point of the input byte image pointed by in with 4 masks. +The first mask is the entered mask and the three remaining masks are produced +by successive rotations of the entered mask by 45 degrees. The maximum output +of all masks at each point is written to the output integer IMAGE descriptor. +The function expects as input a square mask of odd size. + +.B im_gradient() +convolves each point of the input byte image pointed by in with 2 masks; +the entered mask and with a 90 degrees rotation of the +entered mask. If g1, g2 are the result of each convolution then for each point +abs(g1) + abs(g2) is written to the output integer IMAGE descriptor. +The function expects as input a square mask of any size. +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_readmask(3), im_conv(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 02/05/1991 diff --git a/libsrc/convolution/man3/im_contrast_surface.3 b/libsrc/convolution/man3/im_contrast_surface.3 new file mode 100644 index 00000000..20d8fe8b --- /dev/null +++ b/libsrc/convolution/man3/im_contrast_surface.3 @@ -0,0 +1,40 @@ +.TH IM_CONTRAST_SURFACE 3 "01 May 2006" +.SH NAME + im_contrast_surface, im_contrast_surface_raw \- Generate contrast surface +.SH SYNOPSIS +.nf +.B #include +.sp +.BI "int im_contrast_surface( IMAGE " "*in" ", IMAGE " "*out" ", int " "half_win_size" ", int " "spacing" " ); +.br + +.BI "int im_contrast_surface_raw( IMAGE " "*in" ", IMAGE " "*out" ", int " "half_win_size" ", int " "spacing" " ); +.fi +.SH DESCRIPTION +These functions generate an image where the value of each pixel represents the +contrast within a square window of size 2 * half_win_size + 1 centred on the +corresponsing point in the input image. +.PP +The output is sub-sampled by a factor of spacing. +.PP +Input must be single-band uncoded uchar, WIO or PIO. +.PP +Output is single-band uncoded uint, WIO or PIO. +.PP +In +.BR im_contrast_surface(3) , +pixels beyond the edges of the input image are considered to be have the value +of the nearest pixel which is in the image. +.PP +Alternatively, in +.BR im_contrast_surface_raw(3) , +pixels within half_win_size of the edge are not calculated, and output is +smaller accordingly. +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH COPYRIGHT +.br +Copyright 2006, The Nottingham Trent University. +.SH AUTHOR +Tom Vajzovic + diff --git a/libsrc/convolution/man3/im_contrast_surface_raw.3 b/libsrc/convolution/man3/im_contrast_surface_raw.3 new file mode 100644 index 00000000..bf67695a --- /dev/null +++ b/libsrc/convolution/man3/im_contrast_surface_raw.3 @@ -0,0 +1 @@ +.so man3/im_contrast_surface.3 diff --git a/libsrc/convolution/man3/im_conv.3 b/libsrc/convolution/man3/im_conv.3 new file mode 100644 index 00000000..02839b24 --- /dev/null +++ b/libsrc/convolution/man3/im_conv.3 @@ -0,0 +1,149 @@ +.TH CONVOLUTION 3 "2 May 1991" +.SH NAME +im_conv, im_conv_raw, im_convf, im_convf_raw, +im_convsep, im_convsep_raw, im_convsepf, im_convsepf_raw, +im_convsub, im_shrink \- convolves an image with a generalised mask +.SH SYNOPSIS +#include + +int im_conv(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_conv_raw(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_convf(in, out, mask) +.br +IMAGE *in, *out; +.br +DOUBLEMASK *mask; + +int im_convf_raw(in, out, mask) +.br +IMAGE *in, *out; +.br +DOUBLEMASK *mask; + +int im_convsep(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_convsep_raw(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_convsepf(in, out, mask) +.br +IMAGE *in, *out; +.br +DOUBLEMASK *mask; + +int im_convsepf_raw(in, out, mask) +.br +IMAGE *in, *out; +.br +DOUBLEMASK *mask; + +int im_convsub(in, out, mask, xskip, yskip) +.br +IMAGE *in, *out; +.br +INTMASK *mask; +.br +int xskip, yskip; + +int im_shrink(in, out, xfactor, yfactor) +.br +IMAGE *in, *out; +.br +double xfactor, yfactor; + +.SH DESCRIPTION +These functions +convolve the image +.B in +with the matrix +.B mask +and put the result in the image +.B out. + +Input should be non-complex. The size and type of the output image are +the same as the size of the input. To output a larger type (for example, +to output an int image from convolution of a byte image, avoiding clipping), +cast the input image up with +.B im_clip2fmt(3). + +The output image is the same size as the input. The edge pixels are calculated +by expanding the input image using +.B im_embed(3) +in mode 1 (replicating edge pixels) just enough so that the output can match +the input. + +The output at each point is divided by scale and then the offset is added. +Both offset and scale are held in mask. + +The +.B im_conv*(3) +functions have 'raw' versions which do not add the border: instead the +output image is smaller than the input. + +.B im_conv(3) +and +.B im_conv_raw(3) +convolve any non-complex input image to make an output image of the same +type. Rounding is appropriate to the image type. + +.B im_convf(3) +and +.B im_convf_raw(3) +convolves to float (double if the input is double). +The function expects a double mask. + +.B im_convsep(3) +and +.B im_convsep_raw(3) +carry out convolution using an 1xN or Nx1 separable mask. + +The function scales the output result by dividing it with scale*scale. The +scale factor should therefore be the sqrt of the scale of the square NxN mask. + +Rounding is appropriate to the image type. It works on any non-complex image, +and writes the output in the same format as the input. +The function expects integer mask. + +.B im_convsepf(3) +and +.B im_convsepf_raw(3) +convolves to float (double if the input is double). +The function expects a double mask. + +.B im_convsub(3) +convolves the byte image pointed by in and writes the result as a byte output. +Using this function the input image is subsampled on +both directions by an integer factor of xskip horizontally and +an integer factor of yskip vertically. +During the covolution, values are rounded before division. +Both input and output are bytes. Output is clipped between 0 and 255. +The function expects an integer mask. + +.B im_shrink(3) +shrink the input image file by xfactor along the horizontal and +yfactor along the vertical direction. The function does not perform subpixel +interpolation and therefore the resultant image can present aliasing especially +for small x and y factors. Any size image, any non-complex type, any number of +bands. +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_read_imask(3), im_compass(3), im_fastcor(3), im_log_dmask(3), +im_clip2fmt(3) diff --git a/libsrc/convolution/man3/im_conv_raw.3 b/libsrc/convolution/man3/im_conv_raw.3 new file mode 100644 index 00000000..da5db97a --- /dev/null +++ b/libsrc/convolution/man3/im_conv_raw.3 @@ -0,0 +1,123 @@ +.TH CONVOLUTION 3 "2 May 1991" +.SH NAME +im_conv, im_convf, im_convsep, im_convsepf, +im_convsub, im_shrink \- convolves an image with a generalised +mask +.SH SYNOPSIS +#include + +int im_conv(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_convf(in, out, mask) +.br +IMAGE *in, *out; +.br +DOUBLEMASK *mask; + +int im_convsep(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_convsepf(in, out, mask) +.br +IMAGE *in, *out; +.br +DOUBLEMASK *mask; + +int im_convsub(in, out, mask, xskip, yskip) +.br +IMAGE *in, *out; +.br +INTMASK *mask; +.br +int xskip, yskip; + +int im_shrink(in, out, xfactor, yfactor) +.br +IMAGE *in, *out; +.br +double xfactor, yfactor; + +.SH DESCRIPTION +These functions +convolve the image pointed by the image descriptor in with the file +pointed by mask and puts the result in the image pointed by out. +The mask structures INTMASK and DOUBLEMASK are +returned by the function im_read_imask(3) or im_read_dmask(3), +for integer and double masks respectively. + +Input should be non-complex. +The size and type of the output image are the same as the size of the input. +To +output a larger type (for example, to output an int image from convolution +of a byte image, avoiding clipping), cast the input image up with one of +the im_clip2*() functions. + +If the sizes of the mask are xm and ym and the sizes of the image are xs and ys, +then there is a black border at the output image as follows: + +The top ym/2 lines black, bottom ys-ym/2-ym lines black; each of the remaining +lines has the initial xm/2 pels blank, and the final +xs-xm/2-xm pels (division over 2 is integer division). + +The output at each point is divided by scale and then the offset is added. +Both offset and scale are held in mask. + +.B im_conv() +and +.B im_convsep() +have 'raw' versions which do not add the black border: instead the output +image is smaller than the input. + +.B im_conv() +and +.B im_conv_raw() +convolve any non-complex input image to make an output image of the same +type. Rounding is appropriate to the image type. + +.B im_convf() +convolves to float (double if the input is double). +The function expects a double mask. + +.B im_convsep() +and +.B im_convsep_raw() +carry out convolution using an 1xN or Nx1 separable mask. + +The function +scales the output result by dividing it with scale*scale. The scale +factor should therefore be the sqrt of the scale of the square NxN mask. + +Rounding is appropriate to the image type. It works on any non-complex image, +and writes the output in the same format as the input. +The function expects integer mask. + +.B im_convsepf() +convolves to float (double if the input is double). +The function expects a double mask. + +.B im_convsub() +convolves the byte image pointed by in and writes the result as a byte output. +Using this function the input image is subsampled on +both directions by an integer factor of xskip horizontally and +an integer factor of yskip vertically. +During the covolution, values are rounded before division. +Both input and output are bytes. Output is clipped between 0 and 255. +The function expects an integer mask. + +.B im_shrink() +shrink the input image file by xfactor along the horizontal and +yfactor along the vertical direction. The function doesnot perform subpixel +interpolation and therefore the resultant image can present aliasing especially +for small x and y factors. Any size image, any non-complex type, any number of +bands. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_read_imask(3), im_compass(3), im_fastcor(3). diff --git a/libsrc/convolution/man3/im_convf.3 b/libsrc/convolution/man3/im_convf.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convf.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_convf_raw.3 b/libsrc/convolution/man3/im_convf_raw.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convf_raw.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_convsep.3 b/libsrc/convolution/man3/im_convsep.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convsep.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_convsep_raw.3 b/libsrc/convolution/man3/im_convsep_raw.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convsep_raw.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_convsepf.3 b/libsrc/convolution/man3/im_convsepf.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convsepf.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_convsepf_raw.3 b/libsrc/convolution/man3/im_convsepf_raw.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convsepf_raw.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_convsub.3 b/libsrc/convolution/man3/im_convsub.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convsub.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_create_dmask.3 b/libsrc/convolution/man3/im_create_dmask.3 new file mode 100644 index 00000000..df8ebcfd --- /dev/null +++ b/libsrc/convolution/man3/im_create_dmask.3 @@ -0,0 +1,224 @@ +.TH IM_READMASK 3 "2 May 1991" +.SH NAME +im_create_dmask, im_create_imask, im_create_dmaskv, im_create_imaskv, +im_dup_dmask, im_dup_imask, im_free_dmask, +im_free_imask, im_print_imask, im_print_dmask, im_read_dmask, im_read_imask, +im_scale_dmask, im_write_dmask, im_write_imask \- operations on double or int +masks +.SH SYNOPSIS +#include + +DOUBLEMASK *im_create_dmask( name, xs, ys ) +.br +char *name; +.br +int xs, ys; + +INTMASK *im_create_imask( name, xs, ys ) +.br +char *name; +.br +int xs, ys; + +DOUBLEMASK *im_create_dmaskv( name, xs, ys, ... ) +.br +char *name; +.br +int xs, ys; + +INTMASK *im_create_imaskv( name, xs, ys, ... ) +.br +char *name; +.br +int xs, ys; + +DOUBLEMASK *im_dup_dmask( m, name ) +.br +DOUBLEMASK *m; +.br +char *name; + +INTMASK *im_dup_imask( m, name ) +.br +INTMASK *m; +.br +char *name; + +void im_free_dmask( mask ) +.br +DOUBLEMASK *mask; + +void im_free_imask( mask ) +.br +INTMASK *mask; + +DOUBLEMASK *im_read_dmask( name ) +.br +char *name; + +INTMASK *im_read_imask( name ) +.br +char *name; + +void im_print_dmask( mask ) +.br +DOUBLEMASK *mask; + +void im_print_imask( mask ) +.br +INTMASK *mask; + +INTMASK im_scale_dmask( mask, name ) +.br +DOUBLEMASK *mask; +.br +char *name + +void im_norm_dmask( DOUBLEMASK *mask ); + +int im_write_imask( m ) +.br +INTMASK *m; + +int im_write_imask_name( m, name ) +.br +INTMASK *m; +.br +char *name; + +int im_write_dmask_name( m, name ) +.br +DOUBLEMASK *m; +.br +char *name; + +.SH DESCRIPTION +Masks are (see ): + + typedef struct { typedef struct { + int xsize; int xsize; + int ysize; int ysize; + int scale; double scale; + int offset; double offset; + int *coeff; double *coeff; + char *filename; char *filename; + } INTMASK ; } DOUBLEMASK ; + +By mask files have the following structure: + + x y [scale] [offset] + a11 a12 ... a1y + a21 a22 ... a2y] + ... ... ... ... + ax1 ax2 ... axy + +The mask sizes x and y are always integer whereas all the remaining coefficients +are either int for INTMASK and double for the DOUBLEMASK. All numbers are +written in ASCII, for convenience. scale and offset are +always optional. If missing, they default to 1 and 0 respectively. This laxity +makes mask files useful for matrix operations, see im_matinv(3), etc. + +im_create_dmask() +returns an DOUBLEMASK with the filename xsize and ysize set to name xs and ys +respectively. The function allocates memory for the coefficients which are +all initialised to zero. In case of error the function returns NULL. + +im_create_dmaskv() operates exactly as im_create_dmask(), except that the mask +is initialised by the parameters following the width and height. These +parameters must all be doubles. For example: + + DOUBLEMASK *m = im_create_dmaskv( "untitled", 3, 3, + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0 ); + +creates an identity matrix. + +im_create_imask() +returns an INTMASK with the filename xsize and ysize set to name xs and ys +respectively. The function allocates memory for the coefficients which are +all initialised to zero. In case of error the function returns NULL. + +im_create_imaskv() operates exactly as im_create_imask(), except that the mask +is initialised by the parameters following the width and height. These +parameters must all be ints. For example: + + INTMASK *m = im_create_imaskv( "untitled", 3, 3, + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 ); + +creates an identity matrix. + +im_dup_dmask() +duplicates the DOUBLEMASK mask pointed by m and returns a pointer to the +new mask. The filename member of the returned mask is set to the entered +name. The function returns either a DOUBLEMASK pointer or NULL on error. + +im_dup_imask() +duplicates the INTMASK mask pointed by m and returns a pointer to the +new mask. The filename member of the returned mask is set to the entered +name. The function returns either an INTMASK pointer or NULL on error. + +im_free_dmask() +frees the memory allocated for the DOUBLEMASK mask. + +im_free_imask() +frees the memory allocated for the INTMASK mask. + +im_print_dmask() +prints at the sterr the DOUBLEMASK pointed by mask. + +im_print_imask() +prints at the sterr the INTMASK pointed by mask. + +im_read_dmask() +opens the argument name and reads the mask into a DOUBLEMASK structure. +The function returns a pointer to an DOUBLEMASK or NULL on error. + +im_read_imask() +opens the argument name and reads the mask into a INTMASK structure. +If the input +file has non-integer coefficients then the function im_read_dmask() should +be used instead. There is a built-in checking for all mask members. +If the mask has coefficients which are integers or they are floats +with the decimal part equal to zero, the function reads the mask +properly and puts the resultant coefficients in a valid INTMASK. +The function returns a pointer to an INTMASK or NULL on error. + +im_scale_dmask() +returns an INTMASK of the DOUBLE MASK m. The maximum value of the DOUBLEMASK +is scaled to 100 in the returned mask. The scale member of the INTMASK is +scaled accordingly to the scale member of m. +The function returns a pointer to an INTMASK or NULL on error. + +.B im_norm_dmask(3) +applies the scale and offset members of +.I mask +to each of its coefficients, and then sets them to 1.0 and zero respectively. + +im_write_imask() +writes the INTMASK pointed by m to the filename member of m. The function +checks whether the filename is valid and it expects only an INTMASK. +The function returns 0 on success and -1 on error. +For writing a DOUBLEMASK use im_write_dmask() instead. + +im_write_imask_name() +operates as im_write_imask(), but the mask is written to the named file. + +im_write_dmask() +writes the DOUBLEMASK pointed by m to the filename member of m. The function +checks whether the filename is valid and it expects only an DOUBLEMASK. +The mask is written in a file with the format given above. The function uses +fprintf with the conversion character %g, to write the non integer +mask coefficients. It returns 0 on success and -1 on error. + +im_write_dmask_name() +operates as im_write_dmask(), but the mask is written to the named file. + +.SH SEE ALSO +im_lindetect(3), im_compass(3), im_conv(3), im_matinv(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 02/05/1991 diff --git a/libsrc/convolution/man3/im_create_imask.3 b/libsrc/convolution/man3/im_create_imask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_create_imask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_dup_dmask.3 b/libsrc/convolution/man3/im_dup_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_dup_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_dup_imask.3 b/libsrc/convolution/man3/im_dup_imask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_dup_imask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_embed.3 b/libsrc/convolution/man3/im_embed.3 new file mode 100644 index 00000000..d09de3bf --- /dev/null +++ b/libsrc/convolution/man3/im_embed.3 @@ -0,0 +1,36 @@ +.TH IM_EMBED 3 "11 April 1995" +.SH NAME +im_embed \- extract a portion of an image +.SH SYNOPSIS +.B #include + +int im_embed( in, out, type, x, y, w, h ) +.br +IMAGE *in, *out; +.br +int type; +.br +int x, y, w, h; + +.SH DESCRIPTION +im_embed() embeds in within a larger image (size w by h), with in's top +left-hand corner at position (x,y) within the output image. The value of type +controls what appears in the new pels. + + 0 - black pels (all bytes 0) + 1 - extend pels from image to edge + 2 - tile pels from image + 3 - mirror pels from image + 4 - white pels (all bytes 255) + +Works for any size image, any number of bands, any type. Works for LABPACK +coded images too. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_insert(3), im_extract(3), im_region_region(3), im_fill_copy(3). +.SH COPYRIGHT +National Gallery, 1995. +.SH AUTHOR +J. Cupitt \- 11/04/1995 diff --git a/libsrc/convolution/man3/im_fastcor.3 b/libsrc/convolution/man3/im_fastcor.3 new file mode 100644 index 00000000..6d02e7ad --- /dev/null +++ b/libsrc/convolution/man3/im_fastcor.3 @@ -0,0 +1,67 @@ +.TH IM_FASTCOR 3 "14 May 1991" +.SH NAME +im_fastcor, im_spcor \- correlate two images +.SH SYNOPSIS +.B #include + +.B int im_fastcor(in, ref, out) +.br +.B IMAGE *in, *ref, *out; + +.B int im_spcor(in, ref, out) +.br +.B IMAGE *in, *ref, *out; +.SH DESCRIPTION +These functions calculate spatial correlation between two +one-band images held +by the image descriptors +.B in +and +.B ref. +The sizes of +.B ref +should be smaller than +the sizes of +.B in. +The correlation is carried out by overlapping +.B ref +on the top +left corner of +.B in +and moving it over +.B in. + +The output image is the same size as the input. The edge pixels are calculated +by expanding the input image using +.B im_embed(3) +in mode 1 (replicating edge pixels) just enough so that the output can match +the input. + +.B im_spcor(3) +calculates the spatial correlation between +.B in +and +.B ref +using the +correlation coefficient from Niblack "An Introduction to Digital Image +Processing,", Prentice/Hall, pp 138. The resultant coefficients are written +as float numbers in +.B out. +The images must be char, short or ushort. + +.B im_fastcor(3) +simply returns the sum of squares of differences between +.B in +and +.B ref. +This is much faster, but less useful. The resultant coefficients are written +as unsigned int numbers in out which has a size of in. + +.SH BUGS +The functions do not check for integer overflow. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_conv(3), im_lrmosaic(3). +.SH COPYRIGHT +The National Gallery and Birkbeck College, 1990-1997. diff --git a/libsrc/convolution/man3/im_free_dmask.3 b/libsrc/convolution/man3/im_free_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_free_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_free_imask.3 b/libsrc/convolution/man3/im_free_imask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_free_imask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_gauss_dmask.3 b/libsrc/convolution/man3/im_gauss_dmask.3 new file mode 100644 index 00000000..37c3a33f --- /dev/null +++ b/libsrc/convolution/man3/im_gauss_dmask.3 @@ -0,0 +1,48 @@ +.TH GAUSS_MASKS 3 "6 December 1991" +.SH NAME +im_gauss_dmask, im_gauss_imask \- create a gaussian DOUBLEMASK or INTMASK +.SH SYNOPSIS +.B #include + +.B DOUBLEMASK im_gauss_dmask( name, sigma, min_amplitude ) +.br +char *name; +.br +double sigma, min_amplitude; + +.B INTMASK im_gauss_imask( name, sigma, min_amplitude ) +.br +char *name; +.br +double sigma, min_amplitude; +.SH DESCRIPTION +Both functions create a circularly symmetric Gaussian mask of sigma. The size +of the mask is determined by the variable min_amplitude; if for instance the +value .1 is entered this means that the produced mask is clipped at values +less than 10 percent of the maximum amplitude. +The mask can be directly used with the vasari convolution +programs, the default offset set is 0. + +The program uses the following equation: + + H(r) = exp( -(r * r) / (2 * sigma * sigma) ). + +The generated mask has odd size and its maximum value is normalised to +either 100 (gauss_imask) or to 1.0 (gauss_dmask). + +.B im_gauss_dmask() +creates a DOUBLEMASK laplacian of Gaussian mask with maximum value normalised +to 1.0. + +.B im_gauss_imask() +creates a INTMASK laplacian of Gaussian mask with maximum value normalised +to 100. +.SH RETURNED VALUE: +The functions return NULL on erorr. +.SH SEE ALSO +im_log_dmask(3), im_conv(3) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 06/12/1991 diff --git a/libsrc/convolution/man3/im_gauss_imask.3 b/libsrc/convolution/man3/im_gauss_imask.3 new file mode 100644 index 00000000..dca741eb --- /dev/null +++ b/libsrc/convolution/man3/im_gauss_imask.3 @@ -0,0 +1 @@ +.so man3/im_gauss_dmask.3 diff --git a/libsrc/convolution/man3/im_gaussnoise.3 b/libsrc/convolution/man3/im_gaussnoise.3 new file mode 100644 index 00000000..a7b05c24 --- /dev/null +++ b/libsrc/convolution/man3/im_gaussnoise.3 @@ -0,0 +1,27 @@ +.TH IM_GAUSNOISE 3 "10 May 1991" +.SH NAME +im_gaussnoise \- creates a gaussian noisy picture +.SH SYNOPSIS +#include + +int im_gaussnoise(image, xsize, ysize, mean, sigma) +.br +IMAGE *image; +.br +int xsize, ysize; +.br +double mean, sigma; + +.SH DESCRIPTION +im_gaussnoise() creates a float one band gaussian noise picture of size xsize +by ysize. The created image has mean mean and square root of variance equal +to sigma. The noise is generated by averaging 12 random numbers. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_grey(3), im_addgnoise(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/convolution/man3/im_gradient.3 b/libsrc/convolution/man3/im_gradient.3 new file mode 100644 index 00000000..5f146a90 --- /dev/null +++ b/libsrc/convolution/man3/im_gradient.3 @@ -0,0 +1,2 @@ +.so man3/im_compass.3 + diff --git a/libsrc/convolution/man3/im_lindetect.3 b/libsrc/convolution/man3/im_lindetect.3 new file mode 100644 index 00000000..34274649 --- /dev/null +++ b/libsrc/convolution/man3/im_lindetect.3 @@ -0,0 +1 @@ +.so man3/im_compass.3 diff --git a/libsrc/convolution/man3/im_log_dmask.3 b/libsrc/convolution/man3/im_log_dmask.3 new file mode 100644 index 00000000..3bc62f48 --- /dev/null +++ b/libsrc/convolution/man3/im_log_dmask.3 @@ -0,0 +1 @@ +.so man3/im_log_imask.3 diff --git a/libsrc/convolution/man3/im_log_imask.3 b/libsrc/convolution/man3/im_log_imask.3 new file mode 100644 index 00000000..15e4cb2d --- /dev/null +++ b/libsrc/convolution/man3/im_log_imask.3 @@ -0,0 +1,56 @@ +.TH LOG_MASKS 3 "6 December 1991" +.SH NAME +im_log_dmask, im_log_imask \- create a laplacian of gaussian (log) +DOUBLEMASK or INTMASK +.SH SYNOPSIS +.B +#include + + +.B DOUBLEMASK *im_log_dmask( name, sigma, min_amplitude ) +.br +char *name; +.br +double sigma, min_amplitude; + +.B INTMASK *im_log_imask( name, sigma, min_amplitude ) +.br +char *name; +.br +double sigma, min_amplitude; +.SH DESCRIPTION +Both functions create a circularly symmetric laplacian of Gaussian mask. The +size of the mask is determined by the variable min_amplitude; if for instance +the value .1 is entered this means that the produced mask is clipped at values +within 10 persent of zero, and where the change between mask elements is less +than 10%. +The mask can be directly used with the vasari convolution +programs, the default offset set is 0. + +The program uses the following equation: (from Handbook of Pattern Recognition +and image processing by Young and Fu, AP 1986 pages 220-221): + + H(r) = (1 / (2 * M_PI * s4)) * + (2 - (r2 / s2)) * + exp(-r2 / (2 * s2)) + +where s2 = sigma * sigma, s4=s2 * s2, r2 = r * r. The generated mask has odd +size and its +maximum value is normalised to either 100 (log_imask) or to 1.0 (log_dmask) + +.B im_log_dmask() +creates a DOUBLEMASK laplacian of Gaussian mask with maximum value normalised +to 1.0. + +.B im_log_imask() +creates a INTMASK laplacian of Gaussian mask with maximum value normalised +to 100. +.SH RETURNED VALUE: +The functions return NULL on erorr. +.SH SEE ALSO +im_gauss_dmask(3), im_conv(3) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 06/12/1991 diff --git a/libsrc/convolution/man3/im_lowpass.3 b/libsrc/convolution/man3/im_lowpass.3 new file mode 100644 index 00000000..38a25465 --- /dev/null +++ b/libsrc/convolution/man3/im_lowpass.3 @@ -0,0 +1,26 @@ +.TH IM_CONV 3 "2 May 1991" +.SH NAME +im_lowpass \- remove high frequencies quickly +.SH SYNOPSIS +#include + +int im_lowpass(in, out, x, y) +.br +IMAGE *in, *out; +.br +int x, y; +.SH DESCRIPTION +Expand image up to X by Y pixels. Linear interpolation. Used by lowpass(1X) +for simple and nasty low-pass filtering. The result is very poor, but good +enough for smoothing out whites. Any number of bands, output size == input +size, any non-complex type. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_shrink(3), im_similarity_area(3). +.SH COPYRIGHT +.br +National Gallery +.SH AUTHOR +Too embarrassed to say diff --git a/libsrc/convolution/man3/im_maxvalue.3 b/libsrc/convolution/man3/im_maxvalue.3 new file mode 100644 index 00000000..34274649 --- /dev/null +++ b/libsrc/convolution/man3/im_maxvalue.3 @@ -0,0 +1 @@ +.so man3/im_compass.3 diff --git a/libsrc/convolution/man3/im_mpercent.3 b/libsrc/convolution/man3/im_mpercent.3 new file mode 100644 index 00000000..1f5ec9dc --- /dev/null +++ b/libsrc/convolution/man3/im_mpercent.3 @@ -0,0 +1,30 @@ +.TH IM_MPERCENT 3 "14 May 1991" +.SH NAME +im_mpercent \- find threshold corresponding to a percentage of an image values +.SH SYNOPSIS +#include + +int im_mpercent( in, percent, thresh ) +.br +IMAGE *in; +.br +int percent; +.br +int *thresh; + +.SH DESCRIPTION +im_mpercent() returns (through the thresh parameter) the threshold above which +there are percent values of the input image. If for example percent=.1, the +number of pels of the input image with values greater than the returned int +will correspond to 10% of all pels of the image. + +The function is applied on one band FMTUCHAR images only. It can be +used in order to threshold the scaled result of a filtering operation. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_conv(3), im_zerox(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 14/05/1991 diff --git a/libsrc/convolution/man3/im_norm_dmask.3 b/libsrc/convolution/man3/im_norm_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_norm_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_offsets45.3 b/libsrc/convolution/man3/im_offsets45.3 new file mode 100644 index 00000000..5504772e --- /dev/null +++ b/libsrc/convolution/man3/im_offsets45.3 @@ -0,0 +1,99 @@ +.TH IM_OFFSETS45 3 "28 May 1991" +.SH NAME +im_offsets45, im_offsets90, im_rotate_imask45, im_rotate_dmask45, +im_rotate_imask90, im_rotate_dmask90 \- rotate square masks +.SH SYNOPSIS + +.B int *im_offsets45( size ) +.br +.B int size; + +.B int *im_offsets90( size ) +.br +.B int size; + +.B INTMASK *im_rotate_imask45( m, name ) +.br +.B INTMASK *m; +.br +.B char *name; + +.B DOUBLEMASK *im_rotate_dmask45( m, name ) +.br +.B DOUBLEMASK *m; +.br +.B char *name; + +.B INTMASK *im_rotate_imask90( m, name ) +.br +.B INTMASK *m; +.br +.B char *name; + +.B DOUBLEMASK *im_rotate_dmask90( m, name ) +.br +.B DOUBLEMASK *m; +.br +.B char *name; + +.SH DESCRIPTION +These functions can be used to produce the integer offsets needed to rotate +masks by 45 or 90 degrees clockwise. + +.B im_offsets45() +accepts only even size and returns a pointer to an int buffer of size*size. +The program mallocs the buffer and puts into it the integer offsets needed to +rotate a mask of even size size by 45 degrees. For instance if size is 3 the +program returns the buffer 3 0 1 6 4 2 7 8 5 which is the offsets needed to +rotate a mask with offsets 0 1 2 3 4 5 6 7 8. The function return a +pointer to valid data on success or NULL on error. Since the program mallocs +a buffer, the user should free the returned pointer accordingly. + +.B im_offsets90() +accepts any size and returns a pointer to an int buffer of size*size. The +program mallocs the buffer and puts into it the integer offsets needed to +rotate a mask of size size by 90 degrees. For instance if size is 3 the +program returns the buffer 6 3 0 7 4 1 8 5 2 which is the offsets needed to +rotate a mask with offsets 0 1 2 3 4 5 6 7 8. The function return a +pointers to valid data on success or NULL on error. Since the program mallocs +a buffer, the user should free the returned pointer accordingly. + +.B im_rotate_imask45() +rotates the INTMASK m by 45 degrees and returns the rotated mask. The filename +member of the returned structure is set to name. The program allocates memory +for the new mask and therefore the user must free the returned mask by a call +to im_free_imask(3). The returned mask is rotated by 45 degrees clockwise. +The function return a valid INTMASK pointer on success or NULL on error. + +.B im_rotate_dmask45() +rotates the DOUBLEMASK m by 45 degrees +and returns the rotated mask. The filename +member of the returned structure is set to name. The program allocates memory +for the new mask and therefore the user must free the returned mask by a call +to im_free_dmask(3). The returned mask is rotated by 45 degrees clockwise. +The function return a valid DOUBLEMASK pointer on success or NULL on error. + +.B im_rotate_imask90() +rotates the INTMASK m by 90 degrees and returns the rotated mask. The filename +member of the returned structure is set to name. The program allocates memory +for the new mask and therefore the user must free the returned mask by a call +to im_free_imask(3). The returned mask is rotated by 90 degrees clockwise. +The function return a valid INTMASK pointer on success or NULL on error. + +.B im_rotate_dmask90() +rotates the DOUBLEMASK m by 90 degrees +and returns the rotated mask. The filename +member of the returned structure is set to name. The program allocates memory +for the new mask and therefore the user must free the returned mask by a call +to im_free_dmask(3). The returned mask is rotated by 90 degrees clockwise. +The function return a valid DOUBLEMASK pointer on success or NULL on error. + +.SH RETURN VALUE +.SH SEE ALSO +im_read_imask(3), im_read_dmask(3), im_free_imask(3), im_free_dmask(3), +im_gradient(3), im_compass(3), im_conv(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \-28/05/1991 diff --git a/libsrc/convolution/man3/im_offsets90.3 b/libsrc/convolution/man3/im_offsets90.3 new file mode 100644 index 00000000..bf93b39a --- /dev/null +++ b/libsrc/convolution/man3/im_offsets90.3 @@ -0,0 +1 @@ +.so man3/im_offsets45.3 diff --git a/libsrc/convolution/man3/im_print_dmask.3 b/libsrc/convolution/man3/im_print_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_print_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_print_imask.3 b/libsrc/convolution/man3/im_print_imask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_print_imask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_rank.3 b/libsrc/convolution/man3/im_rank.3 new file mode 100644 index 00000000..ea870068 --- /dev/null +++ b/libsrc/convolution/man3/im_rank.3 @@ -0,0 +1,45 @@ +.TH IM_RANK 3 "19 Aug 1996" +.SH NAME +im_rank, im_rank_raw \- rank filter +.SH SYNOPSIS +#include + +int im_rank(in, out, xsize, ysize, n) +.br +IMAGE *in, *out; +.br +int xsize, ysize, n; + +int im_rank_raw(in, out, xsize, ysize, n) +.br +IMAGE *in, *out; +.br +int xsize, ysize, n; + +.SH DESCRIPTION +.B im_rank() +does rank filtering on an image. A window of size xsize by ysize +is passed over the image. At each position, the pixels inside the window are +sorted into ascending order and the pixel at the nth position is output. n +numbers from 0. + +It works for any non-complex image type, with any number of bands. A black +border is added to the output image to make it the same size as the +input. + +.B im_rank_raw() +works just as im_rank(), but does not add the border. +.SH EXAMPLES +For a median filter with mask size m (3 for 3x3, 5 for 5x5, etc.) use + + im_rank( in, out, m, m, m * m / 2 ); + +The special cases n == 0 and n == m * m - 1 are useful dilate and expand +operators. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_conv(3), im_fastcor(3). +.SH COPYRIGHT +1989-1996 The National Gallery and Birkbeck College diff --git a/libsrc/convolution/man3/im_rank_image.3 b/libsrc/convolution/man3/im_rank_image.3 new file mode 100644 index 00000000..34274649 --- /dev/null +++ b/libsrc/convolution/man3/im_rank_image.3 @@ -0,0 +1 @@ +.so man3/im_compass.3 diff --git a/libsrc/convolution/man3/im_read_dmask.3 b/libsrc/convolution/man3/im_read_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_read_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_read_imask.3 b/libsrc/convolution/man3/im_read_imask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_read_imask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_rotate_dmask45.3 b/libsrc/convolution/man3/im_rotate_dmask45.3 new file mode 100644 index 00000000..bf93b39a --- /dev/null +++ b/libsrc/convolution/man3/im_rotate_dmask45.3 @@ -0,0 +1 @@ +.so man3/im_offsets45.3 diff --git a/libsrc/convolution/man3/im_rotate_dmask90.3 b/libsrc/convolution/man3/im_rotate_dmask90.3 new file mode 100644 index 00000000..bf93b39a --- /dev/null +++ b/libsrc/convolution/man3/im_rotate_dmask90.3 @@ -0,0 +1 @@ +.so man3/im_offsets45.3 diff --git a/libsrc/convolution/man3/im_rotate_imask45.3 b/libsrc/convolution/man3/im_rotate_imask45.3 new file mode 100644 index 00000000..bf93b39a --- /dev/null +++ b/libsrc/convolution/man3/im_rotate_imask45.3 @@ -0,0 +1 @@ +.so man3/im_offsets45.3 diff --git a/libsrc/convolution/man3/im_rotate_imask90.3 b/libsrc/convolution/man3/im_rotate_imask90.3 new file mode 100644 index 00000000..bf93b39a --- /dev/null +++ b/libsrc/convolution/man3/im_rotate_imask90.3 @@ -0,0 +1 @@ +.so man3/im_offsets45.3 diff --git a/libsrc/convolution/man3/im_scale_dmask.3 b/libsrc/convolution/man3/im_scale_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_scale_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_sharpen.3 b/libsrc/convolution/man3/im_sharpen.3 new file mode 100644 index 00000000..a6bd295d --- /dev/null +++ b/libsrc/convolution/man3/im_sharpen.3 @@ -0,0 +1,163 @@ +.TH IM_SHARPEN 3 "16 May 1995" +.SH NAME +im_sharpen \- simple coring edge enhancement +.SH SYNOPSIS +#include + +int +.br +im_sharpen( in, out, mask_radius, x1, y2, y3, m1, m2 ) +.br +IMAGE *in, *out; +.br +int mask_radius; +.br +double x1, y2, y3; +.br +double m1, m2; + +.SH DESCRIPTION +This function selectively sharpens the L* channel of a LABPACK coded image. It +is suitable for preparing an image for printing, where edges need to be +boosted to compensate for blurring introduced by the half-tone process, and +also for giving any additional `crispening' required. + +The function operates as: + + in gaussian out + --+-> blur with ----> subtract --> LUT --> add ----> + | mask_radius ^ ^ + | | | + +----------------------+ | + | | + +-----------------------------------------+ + +In other words, the L* channel is smoothed with a gaussian average function of +radius mask_radius and this smoothed image is subtracted from the +original L* to generate a high-frequency signal. + +This high-frequency signal is passed through a look-up table specified by the +x1, y2, y3, m1 and m2 parameters and added back to the original L* channel to +generate the sharpened image. + +The LUT is shaped as: + + ^ + y2 |- - - - - ----------- + | / + | / slope m2 + | .../ + -x1 | ... | + -------------------...----------------------> + | ... | x1 + |... slope m1 + / | + / m2 | + / | + / | + / | + / | + ______/ _ _ _ _ _ _ | -y3 + | + +When trying to understand the meaning of these parameters, it is helpful to +imagine a cross-section through an image. Sharpening filters boost apparent +sharpness by performing the following transformation. Consider a cross-section +through a soft edge: + + ^ + | .......... + | .... + | ... + | .. + |. + --------------------+-------------------> + .| + .. | + ... | + .... | + .......... | + | + +This becomes an enhanced edge, perhaps: + + ^ + | ... overshoot + | . . + | . ............ + | . + |. + . + . + --------------------+-------------------> + . + .| + .| + . | + ............. . | + . . | + undershoot ... | + | + +There are two features: the transition from black to white has become +steeper, and there are positive and negative undershoots and overshoots. + +As a general guide, some overshoot is good for printing (it helps exaggerate +the edge), but not too much, as you will start to see strong white fringes. +Undershoot introduces black lines, which are less intrusive, so you can allow +more under- than over-shoot. + +im_sharpen uses the x1 parameter to distinguish between low and high frequency +areas. Pixels which differ by less than x1 L* units from their local average +are sharpened by factor 1+m1, and pixels which differ by more than x1 are +sharpened by factor 1+m2. y2 and y3 set limits on the amount of positive and +negative sharpening we allow. + +For printing, we recommend the following settings: + + mask_radius == 7 + x1 == 1.5 + y2 == 20 (don't brighten by more than 20 L*) + y3 == 50 (can darken by up to 50 L*) + + m1 == 1 (some sharpening in flat areas) + m2 == 2 (more sharpening in jaggy areas) + +If you want more or less sharpening, we suggest you just change the m1 and m2 +parameters. For an extreme sharpen, you might try: + + m1 == 2 + m2 == 4 + +And for a relatively gentle sharpen, perhaps: + + m1 == 0.5 + m2 == 1.5 + +If you want to adjust the x1 parameter, it can be helpful visualise its +effect by setting: + + m1 == -2 + m2 == 4 + +In other words, heavily blur flat areas, and heavily sharpen elsewhere. This +creates a terrible-looking image, but you will be able to see clearly what +parts of your image are being classified as flat. + +The y2 and y3 parameters need not usually be adjusted, unless you wish to +reduce the strength of the finges. + +The mask_radius parameter changes the width of the fringe and can be adjusted +according to the output printing resolution. As an approximate guideline, use +3 for 4 pixels/mm (CRT display resolution), 5 for 8 pixels/mm, 7 for 12 +pixels/mm and 9 for 16 pixels/mm (300 dpi == 12 pixels/mm). These figures +refer to the image raster, not the half-tone resolution. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_conv(3), im_compass(3), im_fastcor(3). +.SH COPYRIGHT +National Gallery and Birkbeck College, 1995 +.SH THANKS TO +Lindsay! diff --git a/libsrc/convolution/man3/im_shrink.3 b/libsrc/convolution/man3/im_shrink.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_shrink.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_spcor.3 b/libsrc/convolution/man3/im_spcor.3 new file mode 100644 index 00000000..e22eb0f2 --- /dev/null +++ b/libsrc/convolution/man3/im_spcor.3 @@ -0,0 +1 @@ +.so man3/im_fastcor.3 diff --git a/libsrc/convolution/man3/im_stretch3.3 b/libsrc/convolution/man3/im_stretch3.3 new file mode 100644 index 00000000..3b08c3e4 --- /dev/null +++ b/libsrc/convolution/man3/im_stretch3.3 @@ -0,0 +1,28 @@ +.TH IM_STRETCH3 3 "Sep 18 1997" +.SH NAME +im_stretch3 \- stretch horizontally by 3%, displace sub-pixel +mask +.SH SYNOPSIS +#include + +int im_stretch3( in, out, xdisp, ydisp ) +.br +IMAGE *in, *out; +.br +double xdisp, ydisp; + +.SH DESCRIPTION +im_stretch3() stretches the input image by 3% horizontally, and displaces it +by xdisp/ydisp. It uses bi-cubic interpolation, but runs quickly. It works +only for unsigned short images. + +This function is part of the MARC acquisition software, but is generally +useful for squaring up the pixels in images from the Kontron ProgRes camera +range. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_conv(3), im_shrink(3). +.SH COPYRIGHT +The National Gallery, 1997 diff --git a/libsrc/convolution/man3/im_write_dmask.3 b/libsrc/convolution/man3/im_write_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_write_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_write_dmask_name.3 b/libsrc/convolution/man3/im_write_dmask_name.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_write_dmask_name.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_write_imask.3 b/libsrc/convolution/man3/im_write_imask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_write_imask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_write_imask_name.3 b/libsrc/convolution/man3/im_write_imask_name.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_write_imask_name.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_zerox.3 b/libsrc/convolution/man3/im_zerox.3 new file mode 100644 index 00000000..5678af66 --- /dev/null +++ b/libsrc/convolution/man3/im_zerox.3 @@ -0,0 +1,30 @@ +.TH IM_ZEROX 3 "14 May 1991" +.SH NAME +im_zerox \- find the zero crossings of an image +.SH SYNOPSIS +.B #include + +int im_zerox(in, out, flag) +.br +IMAGE *in, *out; +.br +int flag; + +.SH DESCRIPTION +im_zerox() detects the positive and negative edges of zero crossings of an +image held by the image descriptor in, depending on the flag. + +If flag is -1 the negative zero crossings are returned. +If flag is 1 the positive zero crossings are returned. + +The output image is byte with zero crossing set to 255 and all other values +set to zero. Input can have any number of channels, and be any non-complex type. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_conv(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 14/05/1991 diff --git a/libsrc/convolution/rotmask.c b/libsrc/convolution/rotmask.c new file mode 100644 index 00000000..85c1057e --- /dev/null +++ b/libsrc/convolution/rotmask.c @@ -0,0 +1,356 @@ +/* @(#) Functions to create offsets for rotating square masks. + * @(#) Usage + * @(#) int *im_offsets45( size ) + * @(#) int size; + * @(#) + * @(#) Returns an int pointer to valid offsets on sucess and -1 on error + * @(#) + * + * @(#) Usage + * @(#) int *im_offsets90( size ) + * @(#) int size; + * @(#) + * @(#) Returns an int pointer to valid offsets on sucess and -1 on error + * @(#) + * + * @(#) Functions to rotate square masks + * @(#) + * @(#) Usage + * @(#) INTMASK *im_rotate_imask45( mask, name ) + * @(#) INTMASK *mask; + * @(#) + * @(#) Returns an pointer to INTMASK which keeps the original mask rotated + * @(#) by 45 degrees clockwise. + * @(#) The filename member of the returned mask is set to name + * @(#) + * @(#) Usage + * @(#) DOUBLEMASK *im_rotate_dmask45( mask, name ) + * @(#) DOUBLEMASK *mask; + * @(#) + * @(#) Returns an pointer to INTMASK which keeps the original mask rotated + * @(#) by 45 degrees clockwise. + * @(#) The filename member of the returned mask is set to name + * @(#) + * @(#) Usage + * @(#) INTMASK *im_rotate_imask90( mask, name ) + * @(#) INTMASK *mask; + * @(#) + * @(#) Returns an pointer to INTMASK which keeps the original mask rotated + * @(#) by 90 degrees clockwise. + * @(#) The filename member of the returned mask is set to name + * @(#) + * @(#) Usage + * @(#) DOUBLEMASK *im_rotate_dmask90( mask, name ) + * @(#) DOUBLEMASK *mask; + * @(#) + * @(#) Returns an pointer to DOUBLEMASK which keeps the original mask rotated + * @(#) by 90 degrees clockwise. + * @(#) The filename member of the returned mask is set to name + * @(#) + * @(#) Prints a mask. Used mainly for debugging purposes + * @(#) + * @(#) Usage + * @(#) void im_print_dmask( m ) + * @(#) DOUBLEMASK *m; + * @(#) + * @(#) Usage + * @(#) void im_print_imask( m ) + * @(#) INTMASK *m; + * @(#) + * + * + * Author: N. Dessipris (Copyright, N. Dessipris 1991) + * Written on: 08/05/1991 + * Modified on: 28/05/1991 + * 12/10/95 JC + * - small revisions, needs rewriting really + * 7/8/96 JC + * - absolutely foul desp code revised + * - many bugs and mem leaks fixed + * 1/3/99 JC + * - oops, fns were not preserving scale and offset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define PIM_RINT 1 +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Creates the offsets to rotate by 45 degrees an odd size square mask + */ +int * +im_offsets45( int size ) +{ + int temp; + int x, y; + int size2 = size * size; + int size_2 = size / 2; + int *pnt, *cpnt1, *cpnt2; + + if( size%2 == 0 ) { + im_errormsg( "im_offsets45: size not odd" ); + return( NULL ); + } + if( !(pnt = IM_ARRAY( NULL, size2, int )) ) + return( NULL ); + + /* point at the beginning and end of the buffer + */ + cpnt1 = pnt; cpnt2 = pnt + size2 - 1; + + for( y = 0; y < size_2; y++ ) { + temp = (size_2 + y) * size; + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + + for( x = 0; x < y; x++ ) { + temp -= (size-1); + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + } + + for( x = 0; x < size_2 - y; x++ ) { + temp -= size; + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + } + + for( x = 0; x < size_2 - y; x++ ) { + temp++; + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + } + + for( x = 0; x < y; x++ ) { + temp -= ( size - 1 ); + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + } + } + + /* the diagonal now + */ + temp = size * (size - 1); + cpnt1 = pnt + size_2 * size; + for( x = 0; x < size; x++ ) { + *cpnt1++ = temp; + temp -= (size-1); + } + +#ifdef PIM_RINT + temp = 0; + for( y = 0; y < size; y++ ) { + for( x = 0; x < size; x++ ) { + fprintf( stderr, "%4d", *(pnt+temp) ); + temp++; + } + fprintf(stderr, "\n"); + } + fprintf(stderr, "\n"); +#endif + + return( pnt ); +} + + +/* Creates the offsets to rotate any mask by 90 degrees. + */ +int * +im_offsets90( int size ) +{ + int temp; + int x, y, k; + int *offsets; + + if( !(offsets = IM_ARRAY( NULL, size*size, int )) ) + return( NULL ); + + for( k = 0, y = 0; y < size; y++ ) { + temp = size * (size - 1) + y; + + for( x = 0; x < size; x++, k++ ) { + offsets[k] = temp; + temp -= size; + } + } + + return( offsets ); +} + +/* Tye pf offset-generating function. + */ +typedef int *(*offset_fn)( int ); + +/* Rotate a dmask with a set of offsets. + */ +static DOUBLEMASK * +rotdmask( offset_fn fn, DOUBLEMASK *m, const char *name ) +{ + DOUBLEMASK *out; + int size = m->xsize * m->ysize; + int *offsets; + int i; + + if( m->xsize != m->ysize || (m->xsize % 2) == 0 ) { + im_errormsg( "im_rotate_*mask*: mask should " + "be square of even size" ); + return( NULL ); + } + if( !(offsets = fn( m->xsize )) ) + return( NULL ); + if( !(out = im_create_dmask( name, m->xsize, m->ysize )) ) { + im_free( offsets ); + return( NULL ); + } + out->scale = m->scale; + out->offset = m->offset; + + for( i = 0; i < size; i++ ) + out->coeff[i] = m->coeff[offsets[i]]; + + im_free( offsets ); + + return( out ); +} + +/* Rotate an imask with a set of offsets. + */ +static INTMASK * +rotimask( offset_fn fn, INTMASK *m, const char *name ) +{ + INTMASK *out; + int size = m->xsize * m->ysize; + int *offsets; + int i; + + if( m->xsize != m->ysize || (m->xsize % 2) == 0 ) { + im_errormsg( "im_rotate_*mask*: mask should " + "be square of even size" ); + return( NULL ); + } + if( !(offsets = fn( m->xsize )) ) + return( NULL ); + if( !(out = im_create_imask( name, m->xsize, m->ysize )) ) { + im_free( offsets ); + return( NULL ); + } + out->scale = m->scale; + out->offset = m->offset; + + for( i = 0; i < size; i++ ) + out->coeff[i] = m->coeff[offsets[i]]; + + im_free( offsets ); + + return( out ); +} + +/* Returns a mask which is the argument mask rotated by 90 degrees. Filename + * of the returned mask is name. + */ +DOUBLEMASK * +im_rotate_dmask90( DOUBLEMASK *m, const char *name ) +{ + return( rotdmask( im_offsets90, m, name ) ); +} + +/* Returns a mask which is the argument mask rotated by 45 degrees. Filename + * of the returned mask is name. + */ +DOUBLEMASK * +im_rotate_dmask45( DOUBLEMASK *m, const char *name ) +{ + return( rotdmask( im_offsets45, m, name ) ); +} + +/* Returns a mask which is the argument mask rotated by 90 degrees. Filename + * of the returned mask is name. + */ +INTMASK * +im_rotate_imask90( INTMASK *m, const char *name ) +{ + return( rotimask( im_offsets90, m, name ) ); +} + +/* Returns a mask which is the argument mask rotated by 45 degrees. Filename + * of the returned mask is name. + */ +INTMASK * +im_rotate_imask45( INTMASK *m, const char *name ) +{ + return( rotimask( im_offsets45, m, name ) ); +} + +void +im_print_imask( INTMASK *m ) +{ + int i, j, k; + int *pm = m->coeff; + + fprintf( stderr, " %s: %d %d %d %d\n", + m->filename, m->xsize, m->ysize, m->scale, m->offset ); + + for( k = 0, j = 0; j < m->ysize; j++ ) { + for( i = 0; i < m->xsize; i++, k++ ) + fprintf( stderr, "%d\t", pm[k] ); + + fprintf( stderr, "\n" ); + } +} + +void +im_print_dmask( DOUBLEMASK *m ) +{ + int i, j, k; + double *pm = m->coeff; + + fprintf( stderr, " %s: %d %d %f %f\n", + m->filename, m->xsize, m->ysize, m->scale, m->offset ); + + for( k = 0, j = 0; j < m->ysize; j++ ) { + for( i = 0; i < m->xsize; i++, k++ ) + fprintf( stderr, "%f\t", pm[k] ); + + fprintf( stderr, "\n" ); + } +} diff --git a/libsrc/convolution/rw_mask.c b/libsrc/convolution/rw_mask.c new file mode 100644 index 00000000..7abffbbc --- /dev/null +++ b/libsrc/convolution/rw_mask.c @@ -0,0 +1,725 @@ +/* @(#) Function which read a mask from a file. + * @(#) Result is written in the structure IMASK or DMASK depending on whether + * @(#) the input mask is integer or double. The structure of the mask is + * @(#) given in mask.h + * @(#) The mask coefficients can be either int (INTMASK) + * @(#) or double (DOUBLEMASK). + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 29/04/1991 + * Modified on: 10/8/1992, J.Cupitt + * - Mask reading routines no longer fail if scale and offset are missing. + * Instead, they set default values of scale=1, offset=0. + * - Code tidied up, better error recovery. + * - Bugs fixed in im_dup_*mask. No longer coredump. + * - Bugs fixed in im_write_*mask. Now work for non-square matricies. + * - im_copy_*mask_matrix, im_copy_matrix_*mask added: copy VIPS mask + * structures into Numerical Recipies in C style matricies and vice + * versa. Both structures should have been built before copy attempted. + * See im_create_*mask, im_*mat_alloc. The matrix should be indexed by 0 + * to size-1. + * 9/7/93 JC + * - some ANSIfication and tidies + * - im_free_*mask() now return zero, so they can be used as close + * callbacks. + * 7/10/94 JC + * - new IM_NEW(), IM_ARRAY() macros added + * 27/4/95 JC + * - oops! forgot to init IM_ARRAY() memory to zero + * 7/8/96 JC + * - im_scale_dmask rewritten + * 7/5/98 JC + * - im_read_*mask() rewritten, now more robust + * - im_write_*mask() rewritten + * - new functions im_write_*mask_name() + * 28/7/99 JC + * - im_create_imaskv(), im_create_dmaskv() make masks and init from + * varargs + * - tabs allowed as column separators + * 9/2/05 + * - "," allowed as column separator ... helps CSV read + * 31/5/06 + * - use g_ascii_strtod() and friends + * 2006-09-08 tcv + * - add im_norm_dmask() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Size of line buffer for reading. + */ +#define IM_MAX_LINE (4096) + +/* Free mask structure and any attached arrays. Return zero, so we can use + * these functions as close callbacks. + */ +int +im_free_imask( INTMASK *m ) +{ + if( ! m ) + return 0; + + if( m->coeff ) + im_free( m->coeff ); + if( m->filename ) + im_free( m->filename ); + im_free( m ); + + return( 0 ); +} + +int +im_free_dmask( DOUBLEMASK *m ) +{ + if( ! m ) + return 0; + + if( m->coeff ) + im_free( m->coeff ); + if( m->filename ) + im_free( m->filename ); + im_free( m ); + + return( 0 ); +} + +/* Create structures. + */ +INTMASK * +im_create_imask( const char *filename, int xs, int ys ) +{ + INTMASK *m; + int size = xs * ys; + + /* Check args. + */ + if( xs <= 0 || ys <= 0 || filename == NULL ) { + im_error( "im_create_imask", _( "bad arguments" ) ); + return( NULL ); + } + + /* Allocate and initialise structure. + */ + if( !(m = IM_NEW( NULL, INTMASK )) ) + return( NULL ); + m->coeff = NULL; + m->filename = NULL; + m->scale = 1; + m->offset = 0; + m->xsize = 0; + m->ysize = 0; + + if( !(m->coeff = IM_ARRAY( NULL, size, int )) ) { + im_free_imask( m ); + return( NULL ); + } + (void) memset( (char *) m->coeff, 0, size * sizeof( int ) ); + if( !(m->filename = im_strdup( NULL, filename )) ) { + im_free_imask( m ); + return( NULL ); + } + m->xsize = xs; m->ysize = ys; + + return( m ); +} + +INTMASK * +im_create_imaskv( const char *filename, int xs, int ys, ... ) +{ + va_list ap; + + INTMASK *m; + int i; + + if( !(m = im_create_imask( filename, xs, ys )) ) + return( NULL ); + + va_start( ap, ys ); + for( i = 0; i < xs * ys; i++ ) + m->coeff[i] = va_arg( ap, int ); + va_end( ap ); + + return( m ); +} + +DOUBLEMASK * +im_create_dmask( const char *filename, int xs, int ys ) +{ + DOUBLEMASK *m; + int size = xs * ys; + + /* Check args. + */ + if( xs <= 0 || ys <= 0 || filename == NULL ) { + im_error( "im_create_dmask", _( "bad arguments" ) ); + return( NULL ); + } + + /* Allocate and initialise structure. + */ + if( !(m = IM_NEW( NULL, DOUBLEMASK )) ) + return( NULL ); + m->coeff = NULL; + m->filename = NULL; + m->scale = 1.0; + m->offset = 0.0; + m->xsize = 0; + m->ysize = 0; + + if( !(m->coeff = IM_ARRAY( NULL, size, double )) ) { + im_free_dmask( m ); + return( NULL ); + } + (void) memset( (char *) m->coeff, 0, size * sizeof( double ) ); + if( !(m->filename = im_strdup( NULL, filename )) ) { + im_free_dmask( m ); + return( NULL ); + } + m->xsize = xs; m->ysize = ys; + + return( m ); +} + +DOUBLEMASK * +im_create_dmaskv( const char *filename, int xs, int ys, ... ) +{ + va_list ap; + + DOUBLEMASK *m; + int i; + + if( !(m = im_create_dmask( filename, xs, ys )) ) + return( NULL ); + + va_start( ap, ys ); + for( i = 0; i < xs * ys; i++ ) + m->coeff[i] = va_arg( ap, double ); + va_end( ap ); + + return( m ); +} + +/* Open for read. + */ +static FILE * +open_read( const char *name ) +{ + FILE *fp; + + if( !(fp = fopen( name, "r" )) ) { + im_error( "read_mask", _( "Unable to open \"%s\" for input" ), + name ); + return( NULL ); + } + + return( fp ); +} + +/* Read a line from a file! + */ +static int +get_line( FILE *fp, char *buf ) +{ + if( !fgets( buf, IM_MAX_LINE, fp ) ) { + im_error( "read_mask", _( "unexpected EOF" ) ); + return( -1 ); + } + + return( 0 ); +} + +/* width, height, optional scale, optional offset. + */ +static int +read_header( FILE *fp, int *xs, int *ys, double *scale, double *offset ) +{ + char buf[IM_MAX_LINE]; + char *p, *q; + double v[4]; + int i; + + /* Read the first line: should contain size and optional + * scale + offset. + */ + if( get_line( fp, buf ) ) + return( -1 ); + + /* Read as space separated doubles. \n is in the break list because + * our line will (usually) have a trailing \n which we want to count + * as whitespace. + */ + p = buf; + for( i = 0, p = buf; + i < 4 && (q = im_break_token( p, " \t\n" )); + i++, p = q ) + v[i] = g_ascii_strtod( p, NULL ); + + if( (i != 2 && i != 4) || + ceil( v[0] ) != v[0] || + ceil( v[1] ) != v[1] || + v[0] <= 0 || + v[1] <= 0 ) { + im_error( "read_header", _( "error reading matrix header" ) ); + return( -1 ); + } + if( i == 4 && v[2] == 0 ) { + im_error( "read_header", _( "scale should be non-zero" ) ); + return( -1 ); + } + + *xs = v[0]; + *ys = v[1]; + if( i == 2 ) { + *scale = 1.0; + *offset = 0.0; + } + else { + *scale = v[2]; + *offset = v[3]; + } + + return( 0 ); +} + +/* Read matrix files. + */ +DOUBLEMASK * +im_read_dmask( const char *maskfile ) +{ + FILE *fp; + double sc, off; + int xs, ys; + DOUBLEMASK *m; + int x, y, i, size; + char buf[IM_MAX_LINE]; + + if( !(fp = open_read( maskfile )) ) + return( NULL ); + + if( read_header( fp, &xs, &ys, &sc, &off ) ) { + fclose( fp ); + return( NULL ); + } + + if( !(m = im_create_dmask( maskfile, xs, ys )) ) { + fclose( fp ); + return( NULL ); + } + m->scale = sc; + m->offset = off; + size = xs * ys; + + for( i = 0, y = 0; y < ys; y++ ) { + char *p; + + if( get_line( fp, buf ) ) { + im_free_dmask( m ); + fclose( fp ); + return( NULL ); + } + + for( p = buf, x = 0; p && x < xs; + x++, i++, p = im_break_token( p, " \t,\";" ) ) + m->coeff[i] = g_ascii_strtod( p, NULL ); + } + fclose( fp ); + + return( m ); +} + +/* INTMASK ... read as double, check for intness. + */ +INTMASK * +im_read_imask( const char *maskfile ) +{ + DOUBLEMASK *dmask; + INTMASK *imask; + int i; + + if( !(dmask = im_read_dmask( maskfile )) ) + return( NULL ); + + if( ceil( dmask->scale ) != dmask->scale || + ceil( dmask->offset ) != dmask->offset ) { + im_free_dmask( dmask ); + im_error( "im_read_imask", + _( "scale and offset should be int" ) ); + + return( NULL ); + } + + for( i = 0; i < dmask->xsize * dmask->ysize; i++ ) + if( ceil( dmask->coeff[i] ) != dmask->coeff[i] ) { + im_free_dmask( dmask ); + im_error( "im_read_imask", _( "cofficient at " + "position (%d, %d) is not int" ), + i % dmask->xsize, + i / dmask->xsize ); + + return( NULL ); + } + + if( !(imask = im_create_imask( maskfile, + dmask->xsize, dmask->ysize )) ) { + im_free_dmask( dmask ); + return( NULL ); + } + imask->scale = dmask->scale; + imask->offset = dmask->offset; + for( i = 0; i < dmask->xsize * dmask->ysize; i++ ) + imask->coeff[i] = dmask->coeff[i]; + + im_free_dmask( dmask ); + + return( imask ); +} + +INTMASK * +im_scale_dmask( DOUBLEMASK *m, const char *name ) +{ + const int size = m->xsize * m->ysize; + + INTMASK *out; + double maxval, dsum; + int i; + int isum; + + if( !name || m->xsize <= 0 || m->ysize <= 0 ) { + im_error( "im_scale_dmask", _( "bad args" ) ); + return( NULL ); + } + if( !(out = im_create_imask( name, m->xsize, m->ysize )) ) + return( NULL ); + + /* Find mask max. + */ + maxval = m->coeff[0]; + for( i = 0; i < size; i++ ) + if( m->coeff[i] > maxval ) + maxval = m->coeff[i]; + + /* Copy and scale, setting max to 100. + */ + for( i = 0; i < size; i++ ) + out->coeff[i] = IM_RINT( m->coeff[i] * 100.0 / maxval ); + out->offset = m->offset; + + /* Set the scale to match the adjustment to max. + */ + isum = 0; + dsum = 0.0; + for( i = 0; i < size; i++ ) { + isum += out->coeff[i]; + dsum += m->coeff[i]; + } + + if( dsum == m->scale ) + out->scale = isum; + else + out->scale = IM_RINT( m->scale * isum / dsum ); + + return( out ); +} + +void +im_norm_dmask( DOUBLEMASK *mask ) +{ + const int n = mask->xsize * mask->ysize; + const double scale = 1.0 / mask->scale; + + int i; + + if( 1.0 == scale && 0.0 == mask->offset ) + return; + + for( i = 0; i < n; i++ ) + mask->coeff[i] = mask->coeff[i] * scale + mask->offset; + + mask->scale = 1.0; + mask->offset = 0.0; +} + +INTMASK * +im_dup_imask( INTMASK *m, const char *name ) +{ + const int xs = m->xsize; + const int ys = m->ysize; + const int size = xs * ys; + + INTMASK *new; + int i; + + if( !(new = im_create_imask( name, xs, ys )) ) + return( NULL ); + + new->offset = m->offset; + new->scale = m->scale; + + for( i = 0; i < size; i++ ) + new->coeff[i] = m->coeff[i]; + + return( new ); +} + +DOUBLEMASK * +im_dup_dmask( DOUBLEMASK *m, const char *name ) +{ + DOUBLEMASK *new; + int xs = m->xsize; + int ys = m->ysize; + int size = xs * ys; + int i; + double *pnt1, *pnt2; + + if( !(new = im_create_dmask( name, xs, ys )) ) + return( NULL ); + + new->offset = m->offset; + new->scale = m->scale; + + pnt1 = m->coeff; + pnt2 = new->coeff; + for( i = 0; i < size; i++ ) + *pnt2++ = *pnt1++; + + return( new ); +} + +/* Open for write. + */ +static FILE * +open_write( const char *name ) +{ + FILE *fp; + + if( !(fp = fopen( name, "w" )) ) { + im_error( "write_mask", _( "unable to open \"%s\" for output" ), + name ); + return( NULL ); + } + + return( fp ); +} + +/* Write to file. + */ +static int +write_line( FILE *fp, const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + if( !vfprintf( fp, fmt, ap ) ) { + im_error( "write_mask", _( "write error, disc full?" ) ); + return( -1 ); + } + va_end( ap ); + + return( 0 ); +} + +static int +write_double( FILE *fp, double d ) +{ + char buf[G_ASCII_DTOSTR_BUF_SIZE]; + + fprintf( fp, "%s", g_ascii_dtostr( buf, sizeof( buf ), d ) ); + + return( 0 ); +} + +/* Write the INTMASK m into name. + */ +int +im_write_imask_name( INTMASK *m, const char *name ) +{ + FILE *fp; + int x, y, i; + + if( !(fp = open_write( name )) ) + return( -1 ); + + if( write_line( fp, "%d %d %d %d\n", + m->xsize, m->ysize, m->scale, m->offset ) ) { + fclose( fp ); + return( -1 ); + } + + for( i = 0, y = 0; y < m->ysize; y++ ) { + for( x = 0; x < m->xsize; x++, i++ ) + if( write_line( fp, "%d ", m->coeff[i] ) ) { + fclose( fp ); + return( -1 ); + } + + if( write_line( fp, "\n" ) ) { + fclose( fp ); + return( -1 ); + } + } + fclose( fp ); + + return( 0 ); +} + +/* Write the INTMASK m into m->filename + */ +int +im_write_imask( INTMASK *m ) +{ + if( !m->filename ) { + im_error( "im_write_imask", _( "filename not set" ) ); + return( -1 ); + } + + return( im_write_imask_name( m, m->filename ) ); +} + +/* Write the DOUBLEMASK m into name. + */ +int +im_write_dmask_name( DOUBLEMASK *m, const char *name ) +{ + FILE *fp; + int x, y, i; + + if( !(fp = open_write( name )) ) + return( -1 ); + + if( write_line( fp, "%d %d ", m->xsize, m->ysize ) || + write_double( fp, m->scale ) || + write_line( fp, " " ) || + write_double( fp, m->offset ) || + write_line( fp, "\n" ) ) { + fclose( fp ); + return( -1 ); + } + + for( i = 0, y = 0; y < m->ysize; y++ ) { + for( x = 0; x < m->xsize; x++, i++ ) + if( write_double( fp, m->coeff[i] ) || + write_line( fp, " " ) ) { + fclose( fp ); + return( -1 ); + } + + if( write_line( fp, "\n" ) ) { + fclose( fp ); + return( -1 ); + } + } + fclose( fp ); + + return( 0 ); +} + +/* Write the DOUBLEMASK m into m->filename + */ +int +im_write_dmask( DOUBLEMASK *m ) +{ + if( !m->filename ) { + im_error( "im_write_dmask", _( "filename not set" ) ); + return( -1 ); + } + + return( im_write_dmask_name( m, m->filename ) ); +} + +/* Copy an imask into a matrix. Only used internally by matrix package for + * invert. + */ +void +im_copy_imask_matrix( INTMASK *mask, int **matrix ) +{ + int x, y; + int *p = mask->coeff; + + for( y = 0; y < mask->ysize; y++ ) + for( x = 0; x < mask->xsize; x++ ) + matrix[x][y] = *p++; +} + + +/* Copy a matrix into an imask. + */ +void +im_copy_matrix_imask( int **matrix, INTMASK *mask ) +{ + int x, y; + int *p = mask->coeff; + + for( y = 0; y < mask->ysize; y++ ) + for( x = 0; x < mask->xsize; x++ ) + *p++ = matrix[x][y]; +} + +/* Copy a dmask into a matrix. + */ +void +im_copy_dmask_matrix( DOUBLEMASK *mask, double **matrix ) +{ + int x, y; + double *p = mask->coeff; + + for( y = 0; y < mask->ysize; y++ ) + for( x = 0; x < mask->xsize; x++ ) + matrix[x][y] = *p++; +} + +/* Copy a matrix to a dmask. + */ +void +im_copy_matrix_dmask( double **matrix, DOUBLEMASK *mask ) +{ + int x, y; + double *p = mask->coeff; + + for( y = 0; y < mask->ysize; y++ ) + for( x = 0; x < mask->xsize; x++ ) + *p++ = matrix[x][y]; +} diff --git a/libsrc/dummy.c b/libsrc/dummy.c new file mode 100644 index 00000000..066b323d --- /dev/null +++ b/libsrc/dummy.c @@ -0,0 +1,3 @@ +/* mac os x libtool hates empty link sections in convenience libraries + */ +const int im__dummy_value = 42; diff --git a/libsrc/freq_filt/Makefile.am b/libsrc/freq_filt/Makefile.am new file mode 100644 index 00000000..d8bb54a2 --- /dev/null +++ b/libsrc/freq_filt/Makefile.am @@ -0,0 +1,19 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libfreq_filt.la + +libfreq_filt_la_SOURCES = \ + fft_sp.c \ + fmask4th.c \ + fmaskcir.c \ + freq_dispatch.c \ + im_disp_ps.c \ + im_fractsurf.c \ + im_freq_mask.c \ + im_freqflt.c \ + im_fwfft.c \ + im_invfft.c \ + im_invfftr.c \ + im_rotquad.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/freq_filt/fft_sp.c b/libsrc/freq_filt/fft_sp.c new file mode 100644 index 00000000..7cda5267 --- /dev/null +++ b/libsrc/freq_filt/fft_sp.c @@ -0,0 +1,213 @@ +/* Copyright (c) 1982 Michael Landy, Yoav Cohen, and George Sperling + +Disclaimer: No guarantees of performance accompany this software, +nor is any responsibility assumed on the part of the authors. All the +software has been tested extensively and every effort has been made to +insure its reliability. */ + +/* 1991 Modified by N. Dessipris to return a valid code on error */ + +/* fft -- fast fourier transform, adapted from +** Gonzalez & Wintz p.87. +** +** No division by N is performed. +** +** Timing: rough estimates: +** two-dimensional arrays, (including copying columns +** back and forth): +** for arrays up to 16X16: less than 1 sec. +** for 32X32 arrays: about 3.0 sec. +** for 64X64 arrays: about 9.0 sec. +** for 128X128 arrays: about 31.0 sec. +** +** Calling sequence: +** +** float *rvec,*ivec; +** int loglen,skip; +** +** fft_2d(rvec,ivec,loglen) +** performs a 2-dimensional fft where loglen is the log of the length of +** a side of the array +** +** fft_2dgen(rvec,ivec,logrows,logcols) +** performs a 2-dimensional fft where logrows is the log of the number of +** rows, and logcols is the log of the number of columns +** +** fftn(rvec,ivec,loglen,skip) +** performs a 1-dimensional fft on every skip-th entry +*/ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +/* Only compile this if we're missing the fftw library. + */ +#if !HAVE_FFTW && !HAVE_FFTW3 + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static float *Const = NULL, *iConst = NULL; +static int storesize = 0, Constsize = 0; + +static int +fftn(rvec,ivec,loglen,nskip) + +float *rvec,*ivec; +int loglen,nskip; + +{ + int n,nv2,nm1,i,j,k,l,le,le1,c,nle; + float *rveci , *rvecj , *iveci , *ivecj ; + float t,wr,wi,tr,ti ; + + if(loglen==0) + return(-1); + n=1<> 1 ; nm1=n-1 ; j=0 ; + if (storesize>=1 ; + } + j+=k ; + } + le=1 ; + for (l=0;l=n) { + im_warning("index=%d\n",i+le1); + return(-1); + } + rveci=rvec+i*nskip ; rvecj=rvec+(i+le1)*nskip; + iveci=ivec+i*nskip ; ivecj=ivec+(i+le1)*nskip; + + if (c==0) { + tr = *rvecj; + ti = *ivecj; + } + else { + tr = *rvecj*Const[c] - *ivecj*iConst[c]; + ti = *rvecj*iConst[c] + *ivecj*Const[c]; + } + *rvecj = *rveci - tr; + *ivecj = *iveci - ti; + + *rveci += tr; + *iveci += ti; + } + c += nle; + } + } +/** Division by n + for(i=0;i idealhpf, parameters: frequency cutoff + * @(#) 1 -\> ideallpf, parameters: frequency cutoff + * @(#) 2 -\> buthpf, parameters: order, frequency cutoff, amplitude cutoff + * @(#) 3 -\> butlpf, parameters: order, frequency cutoff, amplitude cutoff + * @(#) 4 -\> gaussianlpf, parameters: frequency cutoff, amplitude cutoff + * @(#) 5 -\> gaussianhpf, parameters: frequency cutoff, amplitude cutoff + * @(#) ring pass ring reject filters + * @(#) 6 -\> idealrpf, parameters: frequency cutoff, width + * @(#) 7 -\> idealrrf, parameters: frequency cutoff, width + * @(#) 8 -\> butrpf, parameters: order, freq cutoff, width, ampl cutoff + * @(#) 9 -\> butrrf, parameters: order, freq cutoff, width, ampl cutoff + * @(#) 10 -\> gaussianrpf, parameters: frequency cutoff, width, ampl cutoff + * @(#) 11 -\> gaussianrrf, parameters: frequency cutoff, width, ampl cutoff + * @(#) fractal filters (for filtering gaussian noises only) + * @(#) 18 -> fractal, parameters: fractal dimension + * @(#) + * @(#) Initially one forth of the coefficients is created and it is copied over + * @(#) the four quadrants for faster processing + * @(#) + * @(#) Functions in this file; for explanations see each function + * @(#) + * @(#) float * + * @(#) im__create_quarter( out, xs, ys, flag, ap ) + * @(#) IMAGE *out; + * @(#) int xs, ys; + * @(#) enum mask_type flag; + * @(#) va_list ap; + * @(#) + * + * Written on: Nov 1991 + * Updated on: Dec 1991 + * 20/9/95 JC + * - modernised + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/************************************************************************/ +/* malloc space and create normalised coefficients accross */ +/* the x (horizontal) and y (vertical) direction. */ +/************************************************************************/ +static int +alloc( IMAGE *out, int xs, int ys, double **xd, double **yd, float **coeff ) +{ + int i; + double *x, *y; + float *c; + + x = IM_ARRAY( out, xs/2 + 1, double ); + y = IM_ARRAY( out, ys/2 + 1, double ); + c = IM_ARRAY( out, (xs/2 + 1)*(ys/2 + 1), float ); + if( !x || !y || !c ) + return( -1 ); + + for( i = 0; i < ys/2 + 1; i++ ) + y[i] = (i * i) / ((double) (ys*ys/4)); + for( i = 0; i < xs/2 + 1; i++ ) + x[i] = (i * i) / ((double) (xs*xs/4)); + *xd = x; *yd = y; *coeff = c; + + return( 0 ); +} + +/* xs and ys are the sizes of the final mask; all functions returns + * the coefficients for one forth of the final mask + */ + +/************************************************************************/ +/* FLAG = 0 */ +/* Creates an ideal high pass filter mask */ +/************************************************************************/ +static float * +ideal_hpf( IMAGE *out, int xs, int ys, double fc ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, fc2, distance2; + + if( xs != ys || fc < 0.0 ) { + im_errormsg( "ideal_hpf: bad args" ); + return( NULL ); + } + + if( fc > 1.0 && fc <= xs/2 ) + fc2 = fc * fc * 4.0 / (double)(xs * ys); + else if( fc <= 1.0 && fc > 0.0 ) + fc2 = fc * fc; + else { + im_errormsg( "ideal_hpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = xd[x] + yd[y]; + if( distance2 > fc2 ) + *cpcoeff++ = 1.0; + else + *cpcoeff++ = 0.0; + } + + *coeff = 1.0; + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 1 */ +/* Creates an ideal low pass filter mask */ +/************************************************************************/ +static float * +ideal_lpf( IMAGE *out, int xs, int ys, double fc ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, fc2, distance2; + + if( xs != ys || fc <= 0.0 ) { + im_errormsg( "ideal_lpf: bad args" ); + return( NULL ); + } + + if( fc > 1.0 && fc <= xs/2 ) + fc2 = fc * fc * 4.0 / (double)(xs * ys); + else if( fc <= 1.0 && fc > 0.0 ) + fc2 = fc * fc; + else { + im_errormsg( "ideal_lpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = xd[x] + yd[y]; + if( distance2 <= fc2 ) + *cpcoeff++ = 1.0; + else + *cpcoeff++ = 0.0; + } + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 2 */ +/* Creates an Butterworth high pass filter mask */ +/************************************************************************/ +static float * +butterworth_hpf( IMAGE *out, int xs, int ys, + double order, double fc, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, fc2, distance2, cnst; + + if( xs != ys || fc < 0.0 || order < 1.0 || ac <= 0.0 || ac >= 1.0 ) { + im_errormsg( "butterworth_hpf: bad args" ); + return( NULL ); + } + + if( fc > 1.0 && fc <= xs/2 ) + fc2 = fc * fc * 4.0 / (double)(xs * ys); + else if( fc <= 1.0 && fc > 0.0 ) + fc2 = fc * fc; + else { + im_errormsg( "butterworth_hpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff) ) + return( NULL ); + + cpcoeff = coeff; + cnst = (1.0 / ac) - 1.0; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + /* Leave the dc component unaltered + */ + if( x == 0 && y == 0 ) + *cpcoeff++ = 1.0; + else { + distance2 = fc2 / (xd[x] + yd[y]); + *cpcoeff++ = 1.0 / + (1.0 + cnst * pow( distance2, order )); + } + } + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 3 */ +/* Creates an Butterworth low pass filter mask */ +/************************************************************************/ +static float * +butterworth_lpf( IMAGE *out, int xs, int ys, + double order, double fc, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, fc2, distance2, cnst; + + if( xs != ys || fc <= 0.0 || order < 1.0 || ac >= 1.0 || ac <= 0.0 ) { + im_errormsg( "butterworth_lpf: bad args" ); + return( NULL ); + } + + if( fc > 1.0 && fc <= xs/2 ) + fc2 = fc * fc * 4.0 / (double)(xs * ys); + else if( fc <= 1.0 && fc > 0.0 ) + fc2 = fc * fc; + else { + im_errormsg( "butterworth_lpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = (1.0/ac) - 1.0; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = (xd[x] + yd[y])/fc2; + *cpcoeff++ = 1.0 / + (1.0 + cnst * pow( distance2, order )); + } + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 4 */ +/* Creates a gaussian high pass filter mask */ +/************************************************************************/ +static float * +gaussian_hpf( IMAGE *out, int xs, int ys, double fc, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, fc2, distance2, cnst; + + if( xs != ys || fc <= 0.0 || ac >= 1.0 || ac <= 0.0 ) { + im_errormsg( "gaussian_hpf: bad args" ); + return( NULL ); + } + + if( fc > 1.0 && fc <= xs/2 ) + fc2 = fc * fc * 4.0 / (double)(xs * ys); + else if( fc <= 1.0 && fc > 0.0 ) + fc2 = fc * fc; + else { + im_errormsg( "gaussian_hpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = -log( ac ); + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = (xd[x] + yd[y])/fc2; + *cpcoeff++ = 1.0 - exp( -cnst * distance2 ); + } + + *coeff = 1.0; + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 5 */ +/* Creates a gaussian low pass filter mask */ +/************************************************************************/ +static float * +gaussian_lpf( IMAGE *out, int xs, int ys, double fc, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, fc2, distance2, cnst; + + if( xs != ys || fc < 0.0 || ac >= 1.0 || ac <= 0.0 ) { + im_errormsg( "gaussian_lpf: bad args" ); + return( NULL ); + } + + if( fc > 1.0 && fc <= xs/2 ) + fc2 = fc * fc * 4.0 / (double)(xs * ys); + else if( fc <= 1.0 && fc > 0.0 ) + fc2 = fc * fc; + else { + im_errormsg( "gaussian_lpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = -log( ac ); + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = (xd[x] + yd[y])/fc2; + *cpcoeff++ = exp( - cnst * distance2 ); + } + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 6 */ +/* Creates an ideal ring pass filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static float * +ideal_rpf( IMAGE *out, int xs, int ys, double fc, double width ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, df, distance2, radius1_2, radius2_2; + + if( xs != ys || fc <= 0 || width <= 0 ) { + im_errormsg( "ideal_rpf: bad args" ); + return( NULL ); + } + + df = width/2.0; + if( fc <= 1.0 && df < 1.0 && fc - df > 0.0 ) { + radius1_2 = (fc-df)*(fc-df); + radius2_2 = (fc+df)*(fc+df); + } + else if( fc - df > 1.0 && df >= 1.0 && fc <= xs/2 ) { + radius1_2 = (fc - df) * (fc - df) * 4.0 / ((double)(xs * xs)); + radius2_2 = (fc + df) * (fc + df) * 4.0 / ((double)(xs * xs)); + } + else { + im_errormsg( "ideal_rpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = xd[x] + yd[y]; + if( distance2 < radius2_2 && distance2 > radius1_2 ) + *cpcoeff++ = 1.0; + else + *cpcoeff++ = 0.0; + } + + *coeff = 1.0; + + return( coeff ); +} + + +/************************************************************************/ +/* FLAG = 7 */ +/* Creates an ideal band reject filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static float * +ideal_rrf( IMAGE *out, int xs, int ys, double fc, double width ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, df, distance2, radius1_2, radius2_2; + + if( xs != ys || fc < 0.0 || width <= 0.0 ) { + im_errormsg( "ideal_rrf: bad args" ); + return( NULL ); + } + + df = width/2.0; + if( fc <= 1.0 && df < 1.0 && fc - df > 0.0 ) { + radius1_2 = (fc-df)*(fc-df); + radius2_2 = (fc+df)*(fc+df); + } + else if( fc - df > 1.0 && df >= 1.0 && fc <= xs/2 ) { + radius1_2 = (fc - df) * (fc - df) * 4.0 / ((double)(xs * xs)); + radius2_2 = (fc + df) * (fc + df) * 4.0 / ((double)(xs * xs)); + } + else { + im_errormsg( "ideal_rrf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = xd[x] + yd[y]; + if( distance2 < radius2_2 && distance2 > radius1_2 ) + *cpcoeff++ = 0.0; + else + *cpcoeff++ = 1.0; + } + + return( coeff ); +} + + +/************************************************************************/ +/* FLAG = 8 */ +/* Creates a butterworth band pass filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static float * +butterworth_rpf( IMAGE *out, int xs, int ys, + double order, double fc, double width, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, d, df, ndf, ndf2, nfc, cnst; + + if( xs != ys || fc <= 0.0 || width <= 0.0 || + order < 1.0 || ac >= 1.0 || ac <= 0.0 ) { + im_errormsg( "butterworth_rpf: bad args" ); + return( NULL ); + } + + df = width/2.0; + if( fc <= 1.0 && df < 1.0 && fc-df > 0.0 ) { + nfc = fc; + ndf = width/2.0; + } + else if( fc - df > 1.0 && df >= 1.0 && fc <= xs/2 ) { + nfc = fc * 2.0 /(double)xs; + ndf = width /(double)ys; + } + else { + im_errormsg( "butterworth_rpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = (1.0/ac) - 1.0; + ndf2 = ndf * ndf; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + d = sqrt( xd[x] + yd[y] ); + *cpcoeff++ = 1.0 / + (1.0 + cnst * + pow( (d-nfc)*(d-nfc)/ndf2, order )); + } + + *coeff = 1.0; + + return( coeff ); +} + + + +/************************************************************************/ +/* FLAG = 9 */ +/* Creates a butterworth ring reject filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static float * +butterworth_rrf( IMAGE *out, int xs, int ys, + double order, double fc, double width, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, d, df, ndf, ndf2, nfc, cnst; + + if( xs != ys || fc <= 0.0 || width <= 0.0 || + order < 1.0 || ac >= 1.0 || ac <= 0.0 ) { + im_errormsg( "butterworth_rrf: bad args" ); + return( NULL ); + } + + df = width/2.0; + if( fc <= 1.0 && df < 1.0 && fc-df > 0.0 ) { + nfc = fc; + ndf = width/2.0; + } + else if( fc - df > 1.0 && df >= 1.0 && fc <= xs/2 ) { + nfc = fc * 2.0 /(double)xs; + ndf = width /(double)ys; + } + else { + im_errormsg( "butterworth_rrf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = (1.0/ac) - 1.0; + ndf2 = ndf * ndf; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + d = sqrt( xd[x] + yd[y] ); + if( d == 0.0 ) + *cpcoeff++ = 1.0; + else + *cpcoeff++ = 1.0 / + (1.0 + cnst * pow( + ndf2/((d-nfc)*(d-nfc)), order )); + } + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 10 */ +/* Creates a gaussian band pass filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static float * +gaussian_rpf( IMAGE *out, int xs, int ys, double fc, double width, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, d, df, ndf, ndf2, nfc, cnst; + + if( xs != ys || fc < 0.0 || width <= 0.0 || ac <= 0.0 || ac > 1.0 ) { + im_errormsg( "gaussian_rpf: bad args" ); + return( NULL ); + } + + df = width/2.0; + if( fc <= 1.0 && df < 1.0 && fc - df > 0.0 ) { + nfc = fc; + ndf = width/2.0; + } + else if( fc - df > 1.0 && df >= 1.0 && fc <= xs/2 ) { + nfc = fc * 2.0 /(double) xs; + ndf = width /(double)ys; + } + else { + im_errormsg( "gaussian_rpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = -log( ac ); + ndf2 = ndf * ndf; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + d = sqrt( xd[x] + yd[y] ); + *cpcoeff++ = exp( -cnst * (d-nfc) * (d-nfc)/ndf2 ); + } + + *coeff = 1.0; + + return( coeff ); +} + + + + +/************************************************************************/ +/* FLAG = 11 */ +/* Creates a gaussian band reject filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static float * +gaussian_rrf( IMAGE *out, int xs, int ys, double fc, double width, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, d, df, ndf, ndf2, nfc, cnst; + + if( xs != ys || fc < 0.0 || width <= 0.0 || ac <= 0.0 || ac > 1.0 ) { + im_errormsg( "gaussian_rrf: bad args" ); + return( NULL ); + } + + df = width/2.0; + if( fc <= 1.0 && df < 1.0 && fc - df > 0.0 ) { + nfc = fc; + ndf = width/2.0; + } + else if( fc - df > 1.0 && df >= 1.0 && fc <= xs/2 ) { + nfc = fc * 2.0 /(double) xs; + ndf = width / (double)ys; + } + else { + im_errormsg( "gaussian_rrf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = -log( ac ); + ndf2 = ndf * ndf; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + d = sqrt( xd[x] + yd[y] ); + *cpcoeff++ = 1.0 - + exp( -cnst * (d-nfc) * (d-nfc) / ndf2 ); + } + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 18 */ +/* Theoretically the power spectrum of a fractal surface should decay + * according to its fractal dimension + * This program should be used to create fractal images by filtering the + * power spectrum of Gaussian white noise + * More specifically according to PIET: + * since the coefficients of fractal noise + * < |vsubk|^2 > decay as 1/( |f|^(beta+1) ) + * or since beta=2*H + 1, beta= 7-2*D + * < |vsubk|^2 > decay as 1/( |f|^(8-2*D) ) + * and the fractal filter which should produce vsubk + * should have transfer function decaying as 1/( |f|^((beta+1)/2) ) + * where f = sqrt(fsubx * fsubx + fsuby *fsuby) + * Finally the filter has transfer function decaying as + * sqrt(fsubx*fsubx+fsuby*fsuby)^(D-4) or + * (fsubx*fsubx+fsuby*fsuby)^((D-4)/2) <--- This relation is used. + * On the other hand if D=3-H, the filtermask should decay as + * (fsubx*fsubx+fsuby*fsuby)^(-(H+1)/2) , 0= 3.0 ) { + im_errormsg( "fractal_flt: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = (frdim - 4.0)/2.0; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = xd[x] + yd[y]; + if( distance2 == 0.0 ) + *cpcoeff++ = 1.0; + else + *cpcoeff++ = pow( distance2, cnst ); + } + + return( coeff ); +} + +/* Creates one forth of the mask coefficients. If the final mask is + * xsize by xsize, one forth should have sizes (xsize/2 + 1) by (ysize/2 + 1) + * This happens because the horizontal spatial frequencies extend + * from -xsize/2 up to (xsize/2 - 1) inclusive and + * the vertical spatial frequencies + * from -ysize/2 up to (ysize/2 - 1) inclusive + * In order to calculate the spatial frequencies at location (x, y) + * the maximum spatial frequency at the horizontal direction xsize/2 and + * the maximum spatial frequency at the vertical direction ysize/2 have + * been normalised to 1.0. + * All arithmetic internally has been carried out in double precision; + * however all masks are written as floats with maximum value normalised to 1.0 + */ + +float * +im__create_quarter( IMAGE *out, int xs, int ys, MaskType flag, va_list ap ) +{ + /* May be fewer than 4 args ... but extract them all anyway. Should be + * safe. + */ + double p0 = va_arg( ap, double ); + double p1 = va_arg( ap, double ); + double p2 = va_arg( ap, double ); + double p3 = va_arg( ap, double ); + + switch( flag ) { + /* High pass - low pass + */ + case MASK_IDEAL_HIGHPASS: + return( ideal_hpf( out, xs, ys, p0 ) ); + + case MASK_IDEAL_LOWPASS: + return( ideal_lpf( out, xs, ys, p0 ) ); + + case MASK_BUTTERWORTH_HIGHPASS: + return( butterworth_hpf( out, xs, ys, p0, p1, p2 ) ); + + case MASK_BUTTERWORTH_LOWPASS: + return( butterworth_lpf( out, xs, ys, p0, p1, p2 ) ); + + case MASK_GAUSS_HIGHPASS: + return( gaussian_hpf( out, xs, ys, p0, p1 ) ); + + case MASK_GAUSS_LOWPASS: + return( gaussian_lpf( out, xs, ys, p0, p1 ) ); + + /* Ring pass - ring reject. + */ + case MASK_IDEAL_RINGPASS: + return( ideal_rpf( out, xs, ys, p0, p1 ) ); + + case MASK_IDEAL_RINGREJECT: + return( ideal_rrf( out, xs, ys, p0, p1 ) ); + + case MASK_BUTTERWORTH_RINGPASS: + return( butterworth_rpf( out, + xs, ys, p0, p1, p2, p3 ) ); + + case MASK_BUTTERWORTH_RINGREJECT: + return( butterworth_rrf( out, + xs, ys, p0, p1, p2, p3 ) ); + + case MASK_GAUSS_RINGPASS: + return( gaussian_rpf( out, xs, ys, p0, p1, p2 ) ); + + case MASK_GAUSS_RINGREJECT: + return( gaussian_rrf( out, xs, ys, p0, p1, p2 ) ); + + case MASK_FRACTAL_FLT: + return( fractal_flt( out, xs, ys, p0 ) ); + + default: + im_errormsg( "create_quarter: unimplemented mask" ); + return( NULL ); + } + + /*NOTREACHED*/ +} diff --git a/libsrc/freq_filt/fmaskcir.c b/libsrc/freq_filt/fmaskcir.c new file mode 100644 index 00000000..98b7fc27 --- /dev/null +++ b/libsrc/freq_filt/fmaskcir.c @@ -0,0 +1,664 @@ +/* @(#) Typical filter function + * @(#) va_list is flag, filter parameters + * @(#) + * @(#) The following masks are implemented in this file + * @(#) flag, filter shape, parameters + * @(#) band pass ring reject filters + * @(#) 12 -\> idealbpf, parameters: frequency cutoff, width + * @(#) 13 -\> idealbrf, parameters: frequency cutoff, width + * @(#) 14 -\> butbpf, parameters: order, freq cutoff, width, ampl cutoff + * @(#) 15 -\> butbrf, parameters: order, freq cutoff, width, ampl cutoff + * @(#) 16 -\> gaussianbpf, parameters: frequency cutoff, width, ampl cutoff + * @(#) 17 -\> gaussianbrf, parameters: frequency cutoff, width, ampl cutoff + * @(#) + * @(#) The whole mask is created at once and written into the image file + * @(#) + * @(#) The following functions are contained within this file: + * @(#) Details are preceding the source code of each function + * @(#) + * @(#) int im__fmaskcir( out, flag, ap) + * @(#) IMAGE *out; + * @(#) enum mask_type flag; + * @(#) va_list ap; + * @(#) + * + * Copyright: N. Dessipris, 1991 + * Written on: Nov 1991 + * Updated on: Dec 1991 + * 20/9/95 JC + * - modernised + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + + +/************************************************************************ + * malloc space and create normalised coefficients accross + * the x (horizontal) and y (vertical) direction. + * xs, ys are the image sizes + * xd and yd are the scrambled distributions of x and y in the rotated + * Fourier transform + * xplusd is the non scrambled distribution of (x+x0)*(x+x0) centred at 0 + * xminus is the non scrambled distribution of (x-x0)*(x-x0) centred at 0 + * similar for yplusd and yminusd + ************************************************************************/ +static int +alloc( IMAGE *out, + int xs, int ys, + int **xd, int **yd, + int **xplusd, int **xminusd, int **yplusd, int **yminusd, + int x0, int y0, + float **line ) +{ + int i; + int *x, *y, *xp, *xm, *yp, *ym; + int *pp, *pm; + float *l; + + x = IM_ARRAY( out, xs, int ); + y = IM_ARRAY( out, ys, int ); + xp = IM_ARRAY( out, xs, int ); + xm = IM_ARRAY( out, xs, int ); + yp = IM_ARRAY( out, ys, int ); + ym = IM_ARRAY( out, ys, int ); + l = IM_ARRAY( out, xs, float ); + + if( !x || !y || !xp || !xm || !yp || !ym || !l ) + return( -1 ); + + /* if ys = 8 then y = {0,1,2,3,-4,-3,-2,-1}. + */ + for( i = 0; i < ys/2; i++ ) { + y[i] = i; + y[i+ys/2] = -ys/2 + i; + } + for( i = 0; i < xs/2; i++ ) { + x[i] = i; + x[i+xs/2] = -xs/2 + i; + } + *xd = x; + *yd = y; + + pp = yp + ys/2; + pm = ym + ys/2; + for( i = -ys/2; i < ys/2; i++ ) { + pp[i] = (i + y0)*(i + y0); + pm[i] = (i - y0)*(i - y0); + } + *yplusd = yp + ys/2; + *yminusd = ym + ys/2; + + pp = xp + xs/2; + pm = xm + xs/2; + for( i = -xs/2; i < xs/2; i++ ) { + pp[i] = (i+x0)*(i+x0); + pm[i] = (i-x0)*(i-x0); + } + *xplusd = xp + xs/2; + *xminusd = xm + xs/2; + + *line = l; + + return( 0 ); +} + +/************************************************************************/ +/* FLAG = 12 */ +/* Creates an ideal band pass filter mask */ +/* The band is two CIRCLEs of radius r centred */ +/* at (fcx, fcy) and (-fcx, -fcy) */ +/************************************************************************/ +static int +ideal_bpf( IMAGE *out, double fcx, double fcy, double r ) +{ + int x, y; + int xs = out->Xsize; + int ys = out->Ysize; + float *line, *cpline; + int *xd, *yd, *xplusx0d, *xminusx0d, *yplusy0d, *yminusy0d; + int x0, y0, d1_2, d2_2, r2; + int y2plus, y2minus; + + if( xs != ys ) { + im_errormsg( "ideal_bpf: bad sizes" ); + return( -1 ); + } + if( fabs(fcx) <= 1.0 && fabs(fcy) < 1.0 && r > 0.0 && r < 1.0 ) { + x0 = fcx*xs / 2.0; + y0 = fcy*ys / 2.0; + r2 = r*r*xs / 4.0; + } + else if( fabs(fcx) < xs/2 && fabs(fcy) < ys/2 && r >= 1.0 ) { + x0 = fcx; + y0 = fcy; + r2 = r*r; + } + else { + im_errormsg( "ideal_bpf: bad args" ); + return( -1 ); + } + + if( alloc( out, xs, ys, + &xd, &yd, &xplusx0d, &xminusx0d, + &yplusy0d, &yminusy0d, x0, y0, &line ) ) + return( -1 ); + + for( y = 0; y < ys; y++ ) { + cpline = line; + y2plus = yplusy0d[yd[y]]; + y2minus = yminusy0d[yd[y]]; + + for( x = 0; x < xs; x++ ) { + d1_2 = xminusx0d[xd[x]] + y2minus; + d2_2 = xplusx0d[xd[x]] + y2plus; + + if( d1_2 <= r2 ) + *cpline = 1.0; + else if( d2_2 <= r2 ) + *cpline = 1.0; + else + *cpline = 0.0; + + if( x == 0 && y == 0 ) + *cpline = 1.0; /* allow the dc component */ + + cpline++; + } + + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + + +/************************************************************************/ +/* FLAG = 13 */ +/* Creates an ideal band reject filter mask */ +/* The band is a CIRCLE of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static int +ideal_brf( IMAGE *out, double fcx, double fcy, double r ) +{ + int x, y; + int xs = out->Xsize; + int ys = out->Ysize; + float *line, *cpline; + int *xd, *yd, *xplusx0d, *xminusx0d, *yplusy0d, *yminusy0d; + int x0, y0, d1_2, d2_2, r2; + int y2plus, y2minus; + + if( xs != ys ) { + im_errormsg( "ideal_brf: bad args" ); + return( -1 ); + } + if( fabs(fcx) <= 1.0 && fabs(fcy) <= 1.0 && r > 0.0 && r < 1.0 ) { + x0 = fcx*xs / 2.0; + y0 = fcy*ys / 2.0; + r2 = r*r*xs / 4.0; + } + else if( fabs(fcx) < xs/2 && fabs(fcy) < ys/2 && r >= 1.0 ) { + x0 = fcx; + y0 = fcy; + r2 = r*r; + } + else { + im_errormsg( "ideal_brf: bad args" ); + return( -1 ); + } + + if( alloc( out, xs, ys, + &xd, &yd, &xplusx0d, &xminusx0d, + &yplusy0d, &yminusy0d, x0, y0, &line ) ) + return( -1 ); + + for( y = 0; y < ys; y++ ) { + cpline = line; + y2plus = yplusy0d[yd[y]]; + y2minus = yminusy0d[yd[y]]; + + for( x = 0; x < xs; x++ ) { + d1_2 = xminusx0d[xd[x]] + y2minus; + d2_2 = xplusx0d[xd[x]] + y2plus; + + if( d1_2 <= r2 ) + *cpline = 0.0; + else if( d2_2 <= r2 ) + *cpline = 0.0; + else + *cpline = 1.0; + + if( x == 0 && y == 0 ) + *cpline = 1.0; + + cpline++; + } + + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + +/************************************************************************/ +/* FLAG = 14 */ +/* Creates a butterworth band pass filter mask */ +/* The band is two CIRCLES centred at (fcx, fcy) and (-fcx, -fcy) */ +/* The program assummes that the peaks of the 2d mask are at the */ +/* centres above and are set to 1.0. The amplitude of both circle mask */ +/* are added and the cuttof frequency is calculated on the plane */ +/* which passes though the centre of the circles and the 0 point */ +/************************************************************************/ +static int +butterworth_bpf( IMAGE *out, + double order, double fcx, double fcy, double r, double ac ) +{ + int x, y; + int xs = out->Xsize; + int ys = out->Ysize; + float *line, *cpline; + int *xd, *yd, *xplusx0d, *xminusx0d, *yplusy0d, *yminusy0d; + int x0, y0; + double cnst, cnsta, d1_2, d2_2, nr2; /* nr2 is new r squared */ + int y2plus, y2minus; + + if( xs != ys || order < 1.0 ) { + im_errormsg( "butterworth_bpf: bad sizes" ); + return( -1 ); + } + if( fabs(fcx) <= 1.0 && fabs(fcy) <= 1.0 && r > 0.0 && r < 1.0 ) { + x0 = fcx*xs / 2.0; + y0 = fcy*ys / 2.0; + nr2 = r*r*xs*xs / 4.0; + } + else if( fabs(fcx) < xs/2 && fabs(fcy) < ys/2 && r >= 1.0 ) { + x0 = fcx; + y0 = fcy; + nr2 = r*r; + } + else { + im_errormsg( "butterworth_bpf: bad args" ); + return( -1 ); + } + if( ac >= 1.0 || ac < 0.0) { + im_errormsg( "butterworth_bpf: bad args" ); + return( -1 ); + } + + if( alloc( out, xs, ys, + &xd, &yd, &xplusx0d, &xminusx0d, + &yplusy0d, &yminusy0d, x0, y0, &line ) ) + return( -1 ); + + /* Filter shape: radius d0, centres at (x0, y0), (-x0,-y0) + * H(d) = H1(d) + H2(d) + * H(d) = cnst1/(1 + cnst2 * pow((d-d0)/d0, 2*order)) + + * cnst1/(1 + cnst2 * pow((d+d0)/d0, 2*order)); + * for d=+d0 H(+d0) = 1.0; for d=-d0 H(-d0) = 1.0; + * for d=+da H(+da) = ampl_cutof; for d=-da H1(-da) = ampl_cutof; + * da = (xa, ya) + * xa = x0*(1 - radius/sqrt(x0*x0+y0*y0)) + * ya = y0*(1 - radius/sqrt(x0*x0+y0*y0)) + */ + cnst = (1.0/ac) - 1.0; + + /* normalise the amplitude at (x0,y0) to 1.0 + */ + cnsta = 1.0 / (1.0 + 1.0 / + (1.0 + cnst*pow( 4.0*(x0*x0 + y0*y0)/nr2, order ))); + + for( y = 0; y < ys; y++ ) { + cpline = line; + y2plus = yplusy0d[yd[y]]; + y2minus = yminusy0d[yd[y]]; + + for( x = 0; x < xs; x++ ) { + d1_2 = xminusx0d[xd[x]] + y2minus; + d2_2 = xplusx0d[xd[x]] + y2plus; + + *cpline = cnsta * ( + 1.0 / (1.0 + cnst * pow( d1_2/nr2, order )) + + 1.0 / (1.0 + cnst * pow( d2_2/nr2, order )) ); + + if( x == 0 && y == 0 ) + *cpline = 1.0; + + cpline++; + } + + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + + + +/************************************************************************/ +/* FLAG = 15 */ +/* Creates a butterworth band pass filter mask */ +/* The band is a the 1-H(f) of above */ +/************************************************************************/ +static int +butterworth_brf( IMAGE *out, + double order, double fcx, double fcy, double r, double ac ) +{ + int x, y; + int xs = out->Xsize; + int ys = out->Ysize; + float *line, *cpline; + int *xd, *yd, *xplusx0d, *xminusx0d, *yplusy0d, *yminusy0d; + int x0, y0; + double cnst, cnsta, d1_2, d2_2, nr2; /* nr2 is new r squared */ + int y2plus, y2minus; + + if( xs != ys || order < 1.0 ) { + im_errormsg( "butterworth_brf: bad sizes" ); + return( -1 ); + } + if( fabs(fcx) <= 1.0 && fabs(fcy) <= 1.0 && r > 0.0 && r < 1.0 ) { + x0 = fcx * xs / 2.0; + y0 = fcy * ys / 2.0; + nr2 = r*r*xs*xs / 4.0; + } + else if( fabs(fcx) < xs/2 && fabs(fcy) < ys/2 && r >= 1.0 ) { + x0 = fcx; + y0 = fcy; + nr2 = r*r; + } + else { + im_errormsg( "butterworth_brf: bad args" ); + return( -1 ); + } + if( ac >= 1.0 || ac < 0.0) { + im_errormsg( "butterworth_brf: bad args" ); + return( -1 ); + } + + if( alloc( out, xs, ys, + &xd, &yd, &xplusx0d, &xminusx0d, + &yplusy0d, &yminusy0d, x0, y0, &line ) ) + return( -1 ); + + cnst = (1.0/ac) - 1.0; + + /* normalise the amplitude at (x0,y0) to 1.0 + */ + cnsta = 1.0 / (1.0 + 1.0 / (1.0 + + cnst * pow( 4.0*(x0*x0 + y0*y0)/nr2, order ))); + + for( y = 0; y < ys; y++ ) { + cpline = line; + y2plus = yplusy0d[yd[y]]; + y2minus = yminusy0d[yd[y]]; + + for( x = 0; x < xs; x++ ) { + d1_2 = xminusx0d[xd[x]] + y2minus; + d2_2 = xplusx0d[xd[x]] + y2plus; + + if( d1_2 == 0.0 || d2_2 == 0.0 ) + *cpline = 0; + else + *cpline = 1.0 - cnsta * + ( 1.0/(1.0 + cnst*pow( d1_2/nr2, order )) + + 1.0/(1.0 + cnst*pow( d2_2/nr2, order )) ); + + if( x == 0 && y == 0 ) + *cpline = 1.0; + + cpline++; + } + + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + + +/************************************************************************/ +/* FLAG = 16 */ +/* Creates a gaussian band pass filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static int +gaussian_bpf( IMAGE *out, double fcx, double fcy, double r, double ac ) +{ + int x, y; + int xs = out->Xsize; + int ys = out->Ysize; + float *line, *cpline; + int *xd, *yd, *xplusx0d, *xminusx0d, *yplusy0d, *yminusy0d; + int x0, y0; + double cnst, cnsta, d1_2, d2_2, nr2; /* nr2 is new r squared */ + int y2plus, y2minus; + + if( xs != ys ) { + im_errormsg( "gauss_bpf: bad sizes" ); + return( -1 ); + } + if( fabs(fcx) <= 1.0 && fabs(fcy) <= 1.0 && r > 0.0 && r < 1.0 ) { + x0 = fcx*xs / 2.0; + y0 = fcy*ys / 2.0; + nr2 = r*r*xs*xs / 4.0; + } + else if( fabs(fcx) < xs/2 && fabs(fcy) < ys/2 && r >= 1.0 ) { + x0 = fcx; + y0 = fcy; + nr2 = r*r; + } + else { + im_errormsg( "gauss_bpf: bad args (f)" ); + return( -1 ); + } + if( ac >= 1.0 || ac < 0.0 ) { + im_errormsg( "gauss_bpf: bad args (ac)" ); + return( -1 ); + } + + if( alloc( out, xs, ys, + &xd, &yd, &xplusx0d, &xminusx0d, + &yplusy0d, &yminusy0d, x0, y0, &line ) ) + return( -1 ); + + cnst = -log( ac ); + + /* normalise the amplitude at (x0,y0) to 1.0 + */ + cnsta = 1.0/(1.0 + exp( - cnst * 4.0 * (x0*x0+y0*y0) / nr2 )); + + for( y = 0; y < ys; y++ ) { + cpline = line; + y2plus = yplusy0d[yd[y]]; + y2minus = yminusy0d[yd[y]]; + + for( x = 0; x < xs; x++ ) { + d1_2 = xminusx0d[xd[x]] + y2minus; + d2_2 = xplusx0d[xd[x]] + y2plus; + + *cpline = cnsta * + (exp( -cnst * d1_2/nr2 ) + + exp( -cnst * d2_2/nr2 )); + + if( x == 0 && y == 0 ) + *cpline = 1.0; + + cpline++; + } + + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + + + + +/************************************************************************/ +/* FLAG = 17 */ +/* Creates a gaussian band reject filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static int +gaussian_brf( IMAGE *out, double fcx, double fcy, double r, double ac ) +{ + int x, y; + int xs = out->Xsize; + int ys = out->Ysize; + float *line, *cpline; + int *xd, *yd, *xplusx0d, *xminusx0d, *yplusy0d, *yminusy0d; + int x0, y0; + double cnst, cnsta, d1_2, d2_2, nr2; /* nr2 is new r squared */ + int y2plus, y2minus; + + if( xs != ys ) { + im_errormsg( "gauss_brf: bad sizes" ); + return( -1 ); + } + if( fabs(fcx) <= 1.0 && fabs(fcy) <= 1.0 && r > 0.0 && r < 1.0 ) { + x0 = fcx*xs / 2.0; + y0 = fcy*ys / 2.0; + nr2 = r*r*xs*xs / 4.0; + } + else if( fabs(fcx) < xs/2 && fabs(fcy) < ys/2 && r >= 1.0 ) { + x0 = fcx; + y0 = fcy; + nr2 = r * r; + } + else { + im_errormsg( "gauss_brf: bad args" ); + return( -1 ); + } + if( ac >= 1.0 || ac < 0.0 ) { + im_errormsg( "gauss_brf: bad args" ); + return( -1 ); + } + + if( alloc( out, xs, ys, + &xd, &yd, &xplusx0d, &xminusx0d, + &yplusy0d, &yminusy0d, x0, y0, &line ) ) + return( -1 ); + + cnst = -log( ac ); + + /* normalise the amplitude at (x0,y0) to 1.0 + */ + cnsta = 1.0/(1.0 + exp( - cnst * 4.0 * (x0*x0+y0*y0) / nr2 )); + + for( y = 0; y < ys; y++ ) { + cpline = line; + y2plus = yplusy0d[yd[y]]; + y2minus = yminusy0d[yd[y]]; + + for( x = 0; x < xs; x++ ) { + d1_2 = xminusx0d[xd[x]] + y2minus; + d2_2 = xplusx0d[xd[x]] + y2plus; + + *cpline = 1.0 - cnsta * + (exp( -cnst * d1_2/nr2 ) + + exp( -cnst * d2_2/nr2 )); + + if( x == 0 && y == 0 ) + *cpline = 1.0; + + cpline++; + } + + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + + +/* Creates bandpass filter masks + * All arithmetic internally has been carried out in double precision; + * however all masks are written as floats with maximum value normalised to 1.0 + */ +int +im__fmaskcir( IMAGE *out, MaskType flag, va_list ap ) +{ + /* May be fewer than 5 args ... but extract them all anyway. Should be + * safe. + */ + double p0 = va_arg( ap, double ); + double p1 = va_arg( ap, double ); + double p2 = va_arg( ap, double ); + double p3 = va_arg( ap, double ); + double p4 = va_arg( ap, double ); + + switch( flag ) { + /* Band pass - band reject. + */ + case MASK_IDEAL_BANDPASS: + return( ideal_bpf( out, p0, p1, p2 ) ); + + case MASK_IDEAL_BANDREJECT: + return( ideal_brf( out, p0, p1, p2 ) ); + + case MASK_BUTTERWORTH_BANDPASS: + return( butterworth_bpf( out, p0, p1, p2, p3, p4 ) ); + + case MASK_BUTTERWORTH_BANDREJECT: + return( butterworth_brf( out, p0, p1, p2, p3, p4 ) ); + + case MASK_GAUSS_BANDPASS: + return( gaussian_bpf( out, p0, p1, p2, p3 ) ); + + case MASK_GAUSS_BANDREJECT: + return( gaussian_brf( out, p0, p1, p2, p3 ) ); + + default: + im_errormsg( "im__fmaskcir: unimplemented mask" ); + return( -1 ); + } + + /*NOTREACHED*/ +} diff --git a/libsrc/freq_filt/freq_dispatch.c b/libsrc/freq_filt/freq_dispatch.c new file mode 100644 index 00000000..bf6bb73c --- /dev/null +++ b/libsrc/freq_filt/freq_dispatch.c @@ -0,0 +1,311 @@ +/* Function dispatch tables for freq_filt. + * + * J. Cupitt, 23/2/95 + * 22/4/97 JC + * - oops, im_freqflt() was wrong + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* One image in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Args to im_create_fmask(). + */ +static im_arg_desc create_fmask_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ), + IM_INPUT_INT( "type" ), + IM_INPUT_DOUBLE( "p1" ), + IM_INPUT_DOUBLE( "p2" ), + IM_INPUT_DOUBLE( "p3" ), + IM_INPUT_DOUBLE( "p4" ), + IM_INPUT_DOUBLE( "p5" ) +}; + +/* Call im_create_fmask via arg vector. + */ +static int +create_fmask_vec( im_object *argv ) +{ + int width = *((int *) argv[1]); + int height = *((int *) argv[2]); + int type = *((int *) argv[3]); + double p1 = *((double *) argv[4]); + double p2 = *((double *) argv[5]); + double p3 = *((double *) argv[6]); + double p4 = *((double *) argv[7]); + double p5 = *((double *) argv[8]); + + return( im_create_fmask( argv[0], width, height, + type, p1, p2, p3, p4, p5 ) ); +} + +/* Description of im_create_fmask. + */ +static im_function create_fmask_desc = { + "im_create_fmask", /* Name */ + "create frequency domain filter mask", + 0, /* Flags */ + create_fmask_vec, /* Dispatch function */ + IM_NUMBER( create_fmask_args ), /* Size of arg list */ + create_fmask_args /* Arg list */ +}; + +/* Args to im_flt_image_freq(). + */ +static im_arg_desc flt_image_freq_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "type" ), + IM_INPUT_DOUBLE( "p1" ), + IM_INPUT_DOUBLE( "p2" ), + IM_INPUT_DOUBLE( "p3" ), + IM_INPUT_DOUBLE( "p4" ), + IM_INPUT_DOUBLE( "p5" ) +}; + +/* Call im_flt_image_freq via arg vector. + */ +static int +flt_image_freq_vec( im_object *argv ) +{ + int type = *((int *) argv[2]); + double p1 = *((double *) argv[3]); + double p2 = *((double *) argv[4]); + double p3 = *((double *) argv[5]); + double p4 = *((double *) argv[6]); + double p5 = *((double *) argv[7]); + + return( im_flt_image_freq( argv[0], argv[1], + type, p1, p2, p3, p4, p5 ) ); +} + +/* Description of im_flt_image_freq. + */ +static im_function flt_image_freq_desc = { + "im_flt_image_freq", /* Name */ + "frequency domain filter image", + 0, /* Flags */ + flt_image_freq_vec, /* Dispatch function */ + IM_NUMBER( flt_image_freq_args ), /* Size of arg list */ + flt_image_freq_args /* Arg list */ +}; + +/* Args to im_fractsurf(). + */ +static im_arg_desc fractsurf_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "size" ), + IM_INPUT_DOUBLE( "dimension" ) +}; + +/* Call im_fractsurf via arg vector. + */ +static int +fractsurf_vec( im_object *argv ) +{ + int size = *((int *) argv[1]); + double dim = *((double *) argv[2]); + + return( im_fractsurf( argv[0], size, dim ) ); +} + +/* Description of im_fractsurf. + */ +static im_function fractsurf_desc = { + "im_fractsurf", /* Name */ + "generate a fractal surface of given dimension", + IM_FN_TRANSFORM, /* Flags */ + fractsurf_vec, /* Dispatch function */ + IM_NUMBER( fractsurf_args ), /* Size of arg list */ + fractsurf_args /* Arg list */ +}; + +/* Args to im_freqflt(). + */ +static im_arg_desc freqflt_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_IMAGE( "mask" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_freqflt via arg vector. + */ +static int +freqflt_vec( im_object *argv ) +{ + return( im_freqflt( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_freqflt. + */ +static im_function freqflt_desc = { + "im_freqflt", /* Name */ + "frequency-domain filter of in with mask", + IM_FN_TRANSFORM, /* Flags */ + freqflt_vec, /* Dispatch function */ + IM_NUMBER( freqflt_args ), /* Size of arg list */ + freqflt_args /* Arg list */ +}; + +/* Call im_disp_ps via arg vector. + */ +static int +disp_ps_vec( im_object *argv ) +{ + return( im_disp_ps( argv[0], argv[1] ) ); +} + +/* Description of im_disp_ps. + */ +static im_function disp_ps_desc = { + "im_disp_ps", /* Name */ + "make displayable power spectrum", + IM_FN_TRANSFORM, /* Flags */ + disp_ps_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_rotquad via arg vector. + */ +static int +rotquad_vec( im_object *argv ) +{ + return( im_rotquad( argv[0], argv[1] ) ); +} + +/* Description of im_rotquad. + */ +static im_function rotquad_desc = { + "im_rotquad", /* Name */ + "rotate image quadrants to move origin to centre", + IM_FN_TRANSFORM, /* Flags */ + rotquad_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_fwfft via arg vector. + */ +static int +fwfft_vec( im_object *argv ) +{ + return( im_fwfft( argv[0], argv[1] ) ); +} + +/* Description of im_fwfft. + */ +static im_function fwfft_desc = { + "im_fwfft", /* Name */ + "forward fast-fourier transform", + IM_FN_TRANSFORM, /* Flags */ + fwfft_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_invfft via arg vector. + */ +static int +invfft_vec( im_object *argv ) +{ + return( im_invfft( argv[0], argv[1] ) ); +} + +/* Description of im_invfft. + */ +static im_function invfft_desc = { + "im_invfft", /* Name */ + "inverse fast-fourier transform", + IM_FN_TRANSFORM, /* Flags */ + invfft_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_invfftr via arg vector. + */ +static int +invfftr_vec( im_object *argv ) +{ + return( im_invfftr( argv[0], argv[1] ) ); +} + +/* Description of im_invfftr. + */ +static im_function invfftr_desc = { + "im_invfftr", /* Name */ + "real part of inverse fast-fourier transform", + IM_FN_TRANSFORM, /* Flags */ + invfftr_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *freq_list[] = { + &create_fmask_desc, + &disp_ps_desc, + &flt_image_freq_desc, + &fractsurf_desc, + &freqflt_desc, + &fwfft_desc, + &rotquad_desc, + &invfft_desc, + &invfftr_desc +}; + +/* Package of functions. + */ +im_package im__freq_filt = { + "freq_filt", + IM_NUMBER( freq_list ), + freq_list +}; diff --git a/libsrc/freq_filt/im_disp_ps.c b/libsrc/freq_filt/im_disp_ps.c new file mode 100644 index 00000000..9e278622 --- /dev/null +++ b/libsrc/freq_filt/im_disp_ps.c @@ -0,0 +1,107 @@ +/* @(#) Makes a displayable uchar power spectrum of an input one band image + * @(#) Input should be float complex + * @(#) All images are kept in RAM; so only square arrays of powers of + * @(#) 2 as inputs. + * @(#) Functions im_fwfft, im_c2ps, im_scaleps and im_rotquad are used + * @(#) Image descriptors should have been set properly by the calling program + * @(#) + * @(#) int im_disp_ps(in, out) + * @(#) IMAGE *in, *out; + * @(#) int bandno; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 27/03/1991 + * Modified on : + * 16/6/93 J.Cupitt + * - im_ioflag() changed to im_iocheck() + * 23/2/95 JC + * - rewritten for partials + * 10/9/98 JC + * - frees memory more quickly + * 2/4/02 JC + * - any number of bands + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +disp_ps( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + IMAGE *t[3]; + + if( im_open_local_array( out, t, 3, "im_disp_ps temp 1", "p" ) ) + return( -1 ); + + if( in->BandFmt == IM_BANDFMT_COMPLEX ) { + if( im_c2ps( in, t[1] ) ) + return( -1 ); + } + else { + if( im_fwfft( in, t[0] ) || im_c2ps( t[0], t[1] ) ) + return( -1 ); + } + + if( im_scaleps( t[1], t[2] ) || im_rotquad( t[2], out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_disp_ps( IMAGE *in, IMAGE *out ) +{ + IMAGE *dummy = im_open( "memory:1", "p" ); + + if( !dummy ) + return( -1 ); + if( disp_ps( dummy, in, out ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + return( 0 ); +} diff --git a/libsrc/freq_filt/im_fractsurf.c b/libsrc/freq_filt/im_fractsurf.c new file mode 100644 index 00000000..fa85bd1d --- /dev/null +++ b/libsrc/freq_filt/im_fractsurf.c @@ -0,0 +1,87 @@ +/* @(#) Creates a vasari fractal surface of a given dimension by + * @(#) filtering white gaussian noise (function im_gaussnoise(3X)) + * @(#) using the function im_fltimage_freq(3X) + * @(#) + * @(#) Usage: + * @(#) int im_fractsurf(im, frd, size) + * @(#) double frd; + * @(#) int size; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 10/09/1991 + * Modified on: + * 20/9/95 JC + * - modernised, a little + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include /* for MASK_FRACTAL_FLT */ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_fractsurf( IMAGE *out, int size, double frd ) +{ + IMAGE *noise = im_open_local( out, "noise.v", "p" ); + + if( !noise ) + return( -1 ); + if( frd <= 2.0 || frd >= 3.0 ) { + im_errormsg( "im_fractsurf: dimension shuld be in (2,3)" ); + return( -1 ); + } + + if( im_gaussnoise( noise, size, size, 0.0, 1.0 ) ) + return( -1 ); + + /* create the fractal filter mask, and perform filtering on noise + * Note that the result is in im, stored as float since + * the original noise is in float. It needs scaling for display + */ + if( im_flt_image_freq( noise, out, MASK_FRACTAL_FLT, frd ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/freq_filt/im_freq_mask.c b/libsrc/freq_filt/im_freq_mask.c new file mode 100644 index 00000000..fe734bb1 --- /dev/null +++ b/libsrc/freq_filt/im_freq_mask.c @@ -0,0 +1,237 @@ +/* @(#) Filter functions + * &(#) va_alist is a series of double variables + * @(#) + * @(#) Used to filter image in in the frequency domain, writes + * @(#) the result in image out + * @(#) + * @(#) int im_flt_image_freq( in, out, flag, num_args, va_alist ) + * @(#) IMAGE *in, *out; + * @(#) enum mask_type flag; + * @(#) int num_args; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * @(#) Creates a filter mask used for filtering in the frequency domain + * @(#) The resultant mask is held by image + * @(#) + * @(#) int im_create_fmask(image, xsize, ysize, flag, num_args, va_alist) + * @(#) IMAGE *image; + * @(#) int xsize, ysize; + * @(#) enum mask_type flag; + * @(#) int num_args; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * @(#) Creates a filter mask used for filtering in the frequency domain + * @(#) The resultant mask is held by image + * @(#) Function im_freq_mask() differs from im_create_fmask() in the last + * @(#) argument only: the latter accepts a va_dcl whereas the former + * @(#) accepts a va_list pointer pointing to the read arguments of va_dcl + * @(#) + * @(#) int im_freq_mask(image, xs, ys, flag, num_args, ap) + * @(#) IMAGE *image; + * @(#) int xs, ys; + * @(#) enum mask_type flag; + * @(#) int num_args; + * @(#) va_list ap; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * Copyright: N. Dessipris 1991, + * Written on: Nov 1991 + * Updated on: Dec 1991 + * 20/9/95 JC + * - modernised + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Create the final mask by copying the 1/4 of the mask held by coeff + * The final mask is written onto image on a line by line basis + * The buffer coeff should hold (xsize/2+1)*(ysize/2+1) elms + * The created mask is not rotated; so the center is at (0, 0) + */ +static int +copy_quarter( IMAGE *out, float *coeff_s ) +{ + float *line, *cpline; + float *coeff, *cpcoeff; + int x, y; + int hxsplus1; + + if( !(line = IM_ARRAY( out, out->Xsize, float )) ) + return( -1 ); + + hxsplus1 = out->Xsize/2 + 1; + coeff = coeff_s; + for( y = 0; y < out->Ysize/2; y++ ) { + cpline = line; + cpcoeff = coeff; coeff += hxsplus1; + + for( x = 0; x < out->Xsize/2; x++ ) + *cpline++ = *cpcoeff++; + for( x = out->Xsize/2; x < out->Xsize; x++ ) + *cpline++ = *cpcoeff--; + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + for( y = out->Ysize/2; y < out->Ysize; y++ ) { + cpline = line; + cpcoeff = coeff; coeff -= hxsplus1; + + for( x = 0; x < out->Xsize/2; x++ ) + *cpline++ = *cpcoeff++; + for( x = out->Xsize/2; x < out->Xsize; x++ ) + *cpline++ = *cpcoeff--; + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Make a mask image. + */ +static int +build_freq_mask( IMAGE *out, int xs, int ys, MaskType flag, va_list ap ) +{ + float *coeff; + extern float *im__create_quarter( IMAGE *, + int, int, MaskType, va_list ); + + /* Check sizes and create one quarter of the final mask + */ + if( !im_ispoweroftwo( xs ) || !im_ispoweroftwo( ys ) ) { + im_errormsg( "im_freq_mask: mask sizes power of 2 only" ); + return( -1 ); + } + + /* Create the output image. + */ + im_initdesc( out, xs, ys, 1, IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + if( im_setupout( out ) ) + return( -1 ); + + switch( flag ) { + case MASK_IDEAL_HIGHPASS: + case MASK_IDEAL_LOWPASS: + case MASK_BUTTERWORTH_HIGHPASS: + case MASK_BUTTERWORTH_LOWPASS: + case MASK_GAUSS_HIGHPASS: + case MASK_GAUSS_LOWPASS: + + case MASK_IDEAL_RINGPASS: + case MASK_IDEAL_RINGREJECT: + case MASK_BUTTERWORTH_RINGPASS: + case MASK_BUTTERWORTH_RINGREJECT: + case MASK_GAUSS_RINGPASS: + case MASK_GAUSS_RINGREJECT: + + case MASK_FRACTAL_FLT: + /* All these are created as a quarter and duplicated. + */ + if( !(coeff = im__create_quarter( out, xs, ys, flag, ap )) || + copy_quarter( out, coeff ) ) + return( -1 ); + break; + + case MASK_IDEAL_BANDPASS: + case MASK_IDEAL_BANDREJECT: + case MASK_BUTTERWORTH_BANDPASS: + case MASK_BUTTERWORTH_BANDREJECT: + case MASK_GAUSS_BANDPASS: + case MASK_GAUSS_BANDREJECT: + /* Created all in one go. + */ + if( im__fmaskcir( out, flag, ap ) ) + return( -1 ); + break; + + default: + im_errormsg( "im_freq_mask: unimplemented mask type" ); + return( -1 ); + } + + return( 0 ); +} + +/* Create a mask, and filter an image with it. + */ +int +im_flt_image_freq( IMAGE *in, IMAGE *out, MaskType flag, ... ) +{ + IMAGE *mask = im_open_local( out, "tempmask", "p" ); + va_list ap; + + if( !mask ) + return( -1 ); + + /* Generate mask. + */ + va_start( ap, flag ); + if( build_freq_mask( mask, in->Xsize, in->Ysize, flag, ap ) ) + return( -1 ); + va_end( ap ); + + if( im_freqflt( in, mask, out ) ) + return( -1 ); + + return( 0 ); +} + +/* Create a filter mask. + */ +int +im_create_fmask( IMAGE *out, int xsize, int ysize, MaskType flag, ... ) +{ + va_list ap; + + va_start( ap, flag ); + if( build_freq_mask( out, xsize, ysize, flag, ap ) ) + return( -1 ); + va_end( ap ); + + return( 0 ); +} diff --git a/libsrc/freq_filt/im_freqflt.c b/libsrc/freq_filt/im_freqflt.c new file mode 100644 index 00000000..45fe0e1e --- /dev/null +++ b/libsrc/freq_filt/im_freqflt.c @@ -0,0 +1,124 @@ +/* @(#) Functions which takes as input a valid image and filters it + * @(#) in the fourier domain with the filter mask + * @(#) Input can have any format ; output is the same as input + * @(#) imin can be char uchar, short, ushort, int, uint, float, double + * @(#) or complex float; result is the same as input, clipped if necessary. + * @(#) mask can have any format but the sizes of input and mask are equal + * @(#) The function performs float fft and if the input is not complex float + * @(#) the output is casted to the type of input according to im_clip2..() + * @(#) Since buffer images are involved the size, is restricted to 512x512 + * @(#) for the SUN4 SPARC workstation + * @(#) + * @(#) int im_freqflt(imin, mask, imout) + * @(#) IMAGE *imin, *mask, *imout; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on : 08/03/1991 + * 16/6/93 J.Cupitt + * - im_multiply() called, rather than im_cmultim() + * 27/10/93 JC + * - im_clip2*() called, rather than im_any2*() + * 20/9/95 JC + * - rewritten + * 10/9/98 JC + * - frees memory more quickly + * 4/3/03 JC + * - use im_invfftr() to get real back for speedup + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_freqflt( IMAGE *in, IMAGE *mask, IMAGE *out ) +{ + IMAGE *dummy; + + /* Placeholder for memory free. + */ + if( !(dummy = im_open( "memory-1", "p" )) ) + return( -1 ); + + if( im_iscomplex( in ) ) { + /* Easy case! Assume it has already been transformed. + */ + IMAGE *t1 = im_open_local( dummy, "im_freqflt-1", "p" ); + + if( !t1 || + im_multiply( in, mask, t1 ) || + im_invfftr( t1, out ) ) { + im_close( dummy ); + return( -1 ); + } + } + else { + /* Harder - fft first, then mult, then force back to start + * type. + * + * Optimisation: output of im_invfft() is float buffer, we + * will usually chagetype to char, so rather than keeping a + * large float buffer and partial to char from that, do + * changetype to a memory buffer, and copy to out from that. + */ + IMAGE *t[3]; + IMAGE *t3; + + if( im_open_local_array( dummy, t, 3, "im_freqflt-1", "p" ) || + !(t3 = im_open_local( out, "im_freqflt-3", "t" )) || + im_fwfft( in, t[0] ) || + im_multiply( t[0], mask, t[1] ) || + im_invfftr( t[1], t[2] ) || + im_clip2fmt( t[2], t3, in->BandFmt ) || + im_copy( t3, out ) ) { + im_close( dummy ); + return( -1 ); + } + } + + im_close( dummy ); + + return( 0 ); +} + diff --git a/libsrc/freq_filt/im_fwfft.c b/libsrc/freq_filt/im_fwfft.c new file mode 100644 index 00000000..b828308a --- /dev/null +++ b/libsrc/freq_filt/im_fwfft.c @@ -0,0 +1,638 @@ +/* @(#) Does a forward fft on an input image descriptor + * @(#) using the function fft_sp. (float fft) + * @(#) Input can be any complex or no-complex; output is complex (two floats) + * @(#) + * @(#) Usage: + * @(#) int im_fwfft(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/04/1990 + * Modified on : 09/05/1990 to cope with float input + * Modified on : 08/03/1991 history removed + * Modified on : 03/04/1991 to cope with any input + * + * 28/6/95 JC + * - rewritten to use im_clip2f() rather than own code + * - memory leaks fixed + * 10/9/98 JC + * - frees memory more quickly + * 2/4/02 JC + * - fftw code added + * 13/7/02 JC + * - output Type set to IM_TYPE_FOURIER to help nip + * 27/2/03 JC + * - exploits real_to_complex() path in libfftw for real input (thanks + * Matt) for a 2x speed-up + * 17/11/03 JC + * - fix a segv for wider than high images in the real_to_complex() path + * (thanks Andrey) + * - fixes to real_to_complex() path to give the correct result for + * non-square images, including odd widths and heights + * 3/11/04 + * - added fftw3 support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#ifdef HAVE_FFTW +#include +#endif /*HAVE_FFTW*/ + +#ifdef HAVE_FFTW3 +#include +#endif /*HAVE_FFTW3*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef HAVE_FFTW +/* Call rfftw for a 1 band real image. + */ +static int +rfwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + const int size = in->Xsize * in->Ysize; + const int half_width = in->Xsize / 2 + 1; + + /* Pack to double real here. + */ + IMAGE *real = im_open_local( dummy, "fwfft1:1", "t" ); + + /* Transform to halfcomplex here. + */ + double *half_complex = IM_ARRAY( dummy, + in->Ysize * half_width * 2, double ); + + rfftwnd_plan plan; + double *buf, *q, *p; + int x, y; + + if( !real || !half_complex || im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_fwfft", _( "one band uncoded only" ) ); + return( -1 ); + } + if( im_clip2d( in, real ) ) + return( -1 ); + + /* Make the plan for the transform. Yes, they really do use nx for + * height and ny for width. + */ + if( !(plan = rfftw2d_create_plan( in->Ysize, in->Xsize, + FFTW_FORWARD, FFTW_MEASURE | FFTW_USE_WISDOM )) ) { + im_error( "im_fwfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + rfftwnd_one_real_to_complex( plan, + (fftw_real *) real->data, (fftw_complex *) half_complex ); + + rfftwnd_destroy_plan( plan ); + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_DPCOMPLEX; + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (double *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Copy to out and normalise. The right half is the up/down and + * left/right flip of the left, but conjugated. Do the first + * row separately, then mirror around the centre row. + */ + p = half_complex; + q = buf; + + for( x = 0; x < half_width; x++ ) { + q[0] = p[0] / size; + q[1] = p[1] / size; + p += 2; + q += 2; + } + + p = half_complex + ((in->Xsize + 1) / 2 - 1) * 2; + + for( x = half_width; x < out->Xsize; x++ ) { + q[0] = p[0] / size; + q[1] = -1.0 * p[1] / size; + p -= 2; + q += 2; + } + + if( im_writeline( 0, out, (PEL *) buf ) ) + return( -1 ); + + for( y = 1; y < out->Ysize; y++ ) { + p = half_complex + y * half_width * 2; + q = buf; + + for( x = 0; x < half_width; x++ ) { + q[0] = p[0] / size; + q[1] = p[1] / size; + p += 2; + q += 2; + } + + /* Good grief. + */ + p = half_complex + 2 * + ((out->Ysize - y + 1) * half_width - 2 + + (in->Xsize & 1)); + + for( x = half_width; x < out->Xsize; x++ ) { + q[0] = p[0] / size; + q[1] = -1.0 * p[1] / size; + p -= 2; + q += 2; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Call fftw for a 1 band complex image. + */ +static int +cfwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + fftwnd_plan plan; + double *buf, *q, *p; + int x, y; + + IMAGE *cmplx = im_open_local( dummy, "fwfft1:1", "t" ); + + /* Make dp complex image. + */ + if( !cmplx || im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_fwfft", _( "one band uncoded only" ) ); + return( -1 ); + } + if( im_clip2dcm( in, cmplx ) ) + return( -1 ); + + /* Make the plan for the transform. + */ + if( !(plan = fftw2d_create_plan( in->Ysize, in->Xsize, + FFTW_FORWARD, + FFTW_MEASURE | FFTW_USE_WISDOM | FFTW_IN_PLACE )) ) { + im_error( "im_fwfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + fftwnd_one( plan, (fftw_complex *) cmplx->data, NULL ); + + fftwnd_destroy_plan( plan ); + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_DPCOMPLEX; + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (double *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Copy to out, normalise. + */ + for( p = (double *) cmplx->data, y = 0; y < out->Ysize; y++ ) { + int size = out->Xsize * out->Ysize; + + q = buf; + + for( x = 0; x < out->Xsize; x++ ) { + q[0] = p[0] / size; + q[1] = p[1] / size; + p += 2; + q += 2; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +fwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + if( im_iscomplex( in ) ) + return( cfwfft1( dummy, in, out ) ); + else + return( rfwfft1( dummy, in, out ) ); +} +#else /*!HAVE_FFTW*/ +#ifdef HAVE_FFTW3 +/* Real to complex forward transform. + */ +static int +rfwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + const int size = in->Xsize * in->Ysize; + const int half_width = in->Xsize / 2 + 1; + + /* Pack to double real here. + */ + IMAGE *real = im_open_local( dummy, "fwfft1:1", "t" ); + + /* Transform to halfcomplex here. + */ + double *half_complex = IM_ARRAY( dummy, + in->Ysize * half_width * 2, double ); + + /* We have to have a separate real buffer for the planner to work on. + */ + double *planner_scratch = IM_ARRAY( dummy, + in->Xsize * in->Ysize, double ); + + fftw_plan plan; + double *buf, *q, *p; + int x, y; + + if( !real || !half_complex || im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_fwfft", _( "one band uncoded only" ) ); + return( -1 ); + } + if( im_clip2d( in, real ) ) + return( -1 ); + + /* Make the plan for the transform. Yes, they really do use nx for + * height and ny for width. Use a separate scratch buffer for the + * planner, we can't overwrite real->data + */ + if( !(plan = fftw_plan_dft_r2c_2d( in->Ysize, in->Xsize, + planner_scratch, (fftw_complex *) half_complex, + 0 )) ) { + im_error( "im_fwfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + fftw_execute_dft_r2c( plan, + (double *) real->data, (fftw_complex *) half_complex ); + + fftw_destroy_plan( plan ); + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_DPCOMPLEX; + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (double *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Copy to out and normalise. The right half is the up/down and + * left/right flip of the left, but conjugated. Do the first + * row separately, then mirror around the centre row. + */ + p = half_complex; + q = buf; + + for( x = 0; x < half_width; x++ ) { + q[0] = p[0] / size; + q[1] = p[1] / size; + p += 2; + q += 2; + } + + p = half_complex + ((in->Xsize + 1) / 2 - 1) * 2; + + for( x = half_width; x < out->Xsize; x++ ) { + q[0] = p[0] / size; + q[1] = -1.0 * p[1] / size; + p -= 2; + q += 2; + } + + if( im_writeline( 0, out, (PEL *) buf ) ) + return( -1 ); + + for( y = 1; y < out->Ysize; y++ ) { + p = half_complex + y * half_width * 2; + q = buf; + + for( x = 0; x < half_width; x++ ) { + q[0] = p[0] / size; + q[1] = p[1] / size; + p += 2; + q += 2; + } + + /* Good grief. + */ + p = half_complex + 2 * + ((out->Ysize - y + 1) * half_width - 2 + + (in->Xsize & 1)); + + for( x = half_width; x < out->Xsize; x++ ) { + q[0] = p[0] / size; + q[1] = -1.0 * p[1] / size; + p -= 2; + q += 2; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Complex to complex forward transform. + */ +static int +cfwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + fftw_plan plan; + double *buf, *q, *p; + int x, y; + + IMAGE *cmplx = im_open_local( dummy, "fwfft1:1", "t" ); + + /* We have to have a separate buffer for the planner to work on. + */ + double *planner_scratch = IM_ARRAY( dummy, + in->Xsize * in->Ysize * 2, double ); + + /* Make dp complex image. + */ + if( !cmplx || im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_fwfft", _( "one band uncoded only" ) ); + return( -1 ); + } + if( im_clip2dcm( in, cmplx ) ) + return( -1 ); + + /* Make the plan for the transform. + */ + if( !(plan = fftw_plan_dft_2d( in->Ysize, in->Xsize, + (fftw_complex *) planner_scratch, + (fftw_complex *) planner_scratch, + FFTW_FORWARD, + 0 )) ) { + im_error( "im_fwfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + fftw_execute_dft( plan, + (fftw_complex *) cmplx->data, (fftw_complex *) cmplx->data ); + + fftw_destroy_plan( plan ); + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_DPCOMPLEX; + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (double *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Copy to out, normalise. + */ + for( p = (double *) cmplx->data, y = 0; y < out->Ysize; y++ ) { + int size = out->Xsize * out->Ysize; + + q = buf; + + for( x = 0; x < out->Xsize; x++ ) { + q[0] = p[0] / size; + q[1] = p[1] / size; + p += 2; + q += 2; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +fwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + if( im_iscomplex( in ) ) + return( cfwfft1( dummy, in, out ) ); + else + return( rfwfft1( dummy, in, out ) ); +} +#else /*!HAVE_FFTW3*/ +/* Transform a 1 band image with vips's built-in fft routine. + */ +static int +fwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + int size = in->Xsize * in->Ysize; + int bpx = im_ispoweroftwo( in->Xsize ); + int bpy = im_ispoweroftwo( in->Ysize ); + float *buf, *q, *p1, *p2; + int x, y; + + /* Buffers for real and imaginary parts. + */ + IMAGE *real = im_open_local( dummy, "fwfft1:1", "t" ); + IMAGE *imag = im_open_local( dummy, "fwfft1:2", "t" ); + + /* Temporaries. + */ + IMAGE *t1 = im_open_local( dummy, "fwfft1:3", "p" ); + + if( !real || !imag || !t1 ) + return( -1 ); + if( im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 || + im_iscomplex( in ) ) { + im_error( "im_fwfft", + _( "one band non-complex uncoded only" ) ); + return( -1 ); + } + if( !bpx || !bpy ) { + im_error( "im_fwfft", _( "sides must be power of 2" ) ); + return( -1 ); + } + + /* Make sure we have a float input image. + */ + if( im_clip2f( in, real ) ) + return( -1 ); + + /* Make a buffer of 0 floats of the same size for the imaginary part. + */ + if( im_black( t1, in->Xsize, in->Ysize, 1 ) ) + return( -1 ); + if( im_clip2f( t1, imag ) ) + return( -1 ); + + /* Transform! + */ + if( im__fft_sp( (float *) real->data, (float *) imag->data, + bpx - 1, bpy - 1 ) ) { + im_error( "im_fwfft", _( "fft_sp failed" ) ); + return( -1 ); + } + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_COMPLEX; + out->BandFmt = IM_BANDFMT_COMPLEX; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (float *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Gather together real and imag parts. We have to normalise output! + */ + for( p1 = (float *) real->data, p2 = (float *) imag->data, + y = 0; y < out->Ysize; y++ ) { + q = buf; + + for( x = 0; x < out->Xsize; x++ ) { + q[0] = *p1++ / size; + q[1] = *p2++ / size; + q += 2; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} +#endif /*HAVE_FFTW3*/ +#endif /*HAVE_FFTW*/ + +/* Transform an n-band image with a 1-band processing function. + */ +int +im__fftproc( IMAGE *dummy, IMAGE *in, IMAGE *out, im__fftproc_fn fn ) +{ + if( im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + + if( in->Bands == 1 ) { + if( fn( dummy, in, out ) ) + return( -1 ); + } + else { + IMAGE *acc; + int b; + + for( acc = NULL, b = 0; b < in->Bands; b++ ) { + IMAGE *t1 = im_open_local( dummy, + "fwfftn:1", "p" ); + IMAGE *t2 = im_open_local( dummy, + "fwfftn:2", "p" ); + + if( !t1 || !t2 || + im_extract_band( in, t1, b ) || + fn( dummy, t1, t2 ) ) + return( -1 ); + + if( !acc ) + acc = t2; + else { + IMAGE *t3 = im_open_local( dummy, + "fwfftn:3", "p" ); + + if( !t3 || im_bandjoin( acc, t2, t3 ) ) + return( -1 ); + + acc = t3; + } + } + + if( im_copy( acc, out ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_fwfft( IMAGE *in, IMAGE *out ) +{ + IMAGE *dummy = im_open( "im_fwfft:1", "p" ); + + if( !dummy ) + return( -1 ); + if( im__fftproc( dummy, in, out, fwfft1 ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + /* Set type hint. + */ + out->Type = IM_TYPE_FOURIER; + + return( 0 ); +} diff --git a/libsrc/freq_filt/im_invfft.c b/libsrc/freq_filt/im_invfft.c new file mode 100644 index 00000000..289e7119 --- /dev/null +++ b/libsrc/freq_filt/im_invfft.c @@ -0,0 +1,280 @@ +/* @(#) Does a inverse fft on an input image descriptor + * @(#) using the function fft_sp. + * @(#) Input complex (2 floats) output complex (2 floats) + * @(#) + * @(#) Usage: + * @(#) int im_invfft(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/04/1990 + * Modified on : + * 28/6/95 JC + * - rewritten, based on new im_fwfft() code + * 10/9/98 JC + * - frees memory more quickly + * 2/4/02 JC + * - fftw code added + * 13/7/02 JC + * - Type reset + * 27/2/03 JC + * - tiny speed-up ... save 1 copy on write + * 22/1/04 JC + * - oops, fix for segv on wider than high fftw transforms + * 3/11/04 + * - added fftw3 support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#ifdef HAVE_FFTW +#include +#endif /*HAVE_FFTW*/ + +#ifdef HAVE_FFTW3 +#include +#endif /*HAVE_FFTW3*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef HAVE_FFTW +/* Call fftw for a 1 band image. + */ +static int +invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + fftwnd_plan plan; + + IMAGE *cmplx = im_open_local( out, "invfft1:1", "t" ); + + /* Make dp complex image. + */ + if( !cmplx || im_pincheck( in ) || im_poutcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_invfft", _( "one band uncoded only" ) ); + return( -1 ); + } + if( im_clip2dcm( in, cmplx ) ) + return( -1 ); + + /* Make the plan for the transform. Yes, they really do use nx for + * height and ny for width. + */ + if( !(plan = fftw2d_create_plan( in->Ysize, in->Xsize, + FFTW_BACKWARD, + FFTW_MEASURE | FFTW_USE_WISDOM | FFTW_IN_PLACE )) ) { + im_error( "im_invfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + fftwnd_one( plan, (fftw_complex *) cmplx->data, NULL ); + + fftwnd_destroy_plan( plan ); + + /* Copy to out. + */ + if( im_copy( cmplx, out ) ) + return( -1 ); + + return( 0 ); +} +#else /*!HAVE_FFTW*/ +#ifdef HAVE_FFTW3 +/* Complex to complex inverse transform. + */ +static int +invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + fftw_plan plan; + + IMAGE *cmplx = im_open_local( out, "invfft1:1", "t" ); + + /* We have to have a separate buffer for the planner to work on. + */ + double *planner_scratch = IM_ARRAY( dummy, + in->Xsize * in->Ysize * 2, double ); + + /* Make dp complex image. + */ + if( !cmplx || im_pincheck( in ) || im_poutcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_invfft", _( "one band uncoded only" ) ); + return( -1 ); + } + if( im_clip2dcm( in, cmplx ) ) + return( -1 ); + + /* Make the plan for the transform. Yes, they really do use nx for + * height and ny for width. + */ + if( !(plan = fftw_plan_dft_2d( in->Ysize, in->Xsize, + (fftw_complex *) planner_scratch, + (fftw_complex *) planner_scratch, + FFTW_BACKWARD, + 0 )) ) { + im_error( "im_invfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + fftw_execute_dft( plan, + (fftw_complex *) cmplx->data, (fftw_complex *) cmplx->data ); + + fftw_destroy_plan( plan ); + + /* Copy to out. + */ + if( im_copy( cmplx, out ) ) + return( -1 ); + + return( 0 ); +} +#else /*!HAVE_FFTW3*/ +/* Fall back to VIPS's built-in fft + */ +static int +invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + int bpx = im_ispoweroftwo( in->Xsize ); + int bpy = im_ispoweroftwo( in->Ysize ); + float *buf, *q, *p1, *p2; + int x, y; + + /* Buffers for real and imaginary parts. + */ + IMAGE *real = im_open_local( dummy, "invfft1:1", "t" ); + IMAGE *imag = im_open_local( dummy, "invfft1:2", "t" ); + + /* Temps. + */ + IMAGE *t1 = im_open_local( dummy, "invfft1:3", "p" ); + IMAGE *t2 = im_open_local( dummy, "invfft1:4", "p" ); + + if( !real || !imag || !t1 ) + return( -1 ); + if( im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || + in->Bands != 1 || !im_iscomplex( in ) ) { + im_error( "im_invfft", _( "one band complex uncoded only" ) ); + return( -1 ); + } + if( !bpx || !bpy ) { + im_error( "im_invfft", _( "sides must be power of 2" ) ); + return( -1 ); + } + + /* Make sure we have a single-precision complex input image. + */ + if( im_clip2cm( in, t1 ) ) + return( -1 ); + + /* Extract real and imag parts. We have to complement the imaginary. + */ + if( im_c2real( t1, real ) ) + return( -1 ); + if( im_c2imag( t1, t2 ) || im_lintra( -1.0, t2, 0.0, imag ) ) + return( -1 ); + + /* Transform! + */ + if( im__fft_sp( (float *) real->data, (float *) imag->data, + bpx - 1, bpy - 1 ) ) { + im_error( "im_invfft", _( "fft_sp failed" ) ); + return( -1 ); + } + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_COMPLEX; + out->BandFmt = IM_BANDFMT_COMPLEX; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (float *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Gather together real and imag parts. + */ + for( p1 = (float *) real->data, p2 = (float *) imag->data, + y = 0; y < out->Ysize; y++ ) { + q = buf; + + for( x = 0; x < out->Xsize; x++ ) { + q[0] = *p1++; + q[1] = *p2++; + q += 2; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} +#endif /*HAVE_FFTW3*/ +#endif /*HAVE_FFTW*/ + +int +im_invfft( IMAGE *in, IMAGE *out ) +{ + IMAGE *dummy = im_open( "im_invfft:1", "p" ); + + if( !dummy ) + return( -1 ); + if( im__fftproc( dummy, in, out, invfft1 ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + if( out->Bands == 1 ) + out->Type = IM_TYPE_B_W; + else + out->Type = IM_TYPE_MULTIBAND; + + return( 0 ); +} diff --git a/libsrc/freq_filt/im_invfftr.c b/libsrc/freq_filt/im_invfftr.c new file mode 100644 index 00000000..2d458d46 --- /dev/null +++ b/libsrc/freq_filt/im_invfftr.c @@ -0,0 +1,330 @@ +/* @(#) Does a inverse fft on an input image descriptor + * @(#) Input complex (2 floats) output real + * @(#) + * @(#) Usage: + * @(#) int im_invfftr(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * + * Modified on : + * 27/2/03 JC + * - from im_invfft.c + * 22/1/04 JC + * - oops, fix for segv on wider than high fftw transforms + * 3/11/04 + * - added fftw3 support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#ifdef HAVE_FFTW +#include +#endif /*HAVE_FFTW*/ + +#ifdef HAVE_FFTW3 +#include +#endif /*HAVE_FFTW3*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef HAVE_FFTW +/* Use fftw2. + */ +static int +invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + IMAGE *cmplx = im_open_local( dummy, "invfft1-1", "t" ); + IMAGE *real = im_open_local( out, "invfft1-2", "t" ); + const int half_width = in->Xsize / 2 + 1; + + /* Transform to halfcomplex here. + */ + double *half_complex = IM_ARRAY( dummy, + in->Ysize * half_width * 2, double ); + + rfftwnd_plan plan; + int x, y; + double *q, *p; + + if( !cmplx || !real || !half_complex || im_pincheck( in ) || + im_poutcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_invfft", _( "one band uncoded only" ) ); + return( -1 ); + } + + /* Make dp complex image for input. + */ + if( im_clip2dcm( in, cmplx ) ) + return( -1 ); + + /* Make mem buffer real image for output. + */ + if( im_cp_desc( real, in ) ) + return( -1 ); + real->Bbits = IM_BBITS_DOUBLE; + real->BandFmt = IM_BANDFMT_DOUBLE; + if( im_setupout( real ) ) + return( -1 ); + + /* Build half-complex image. + */ + q = half_complex; + for( y = 0; y < cmplx->Ysize; y++ ) { + p = ((double *) cmplx->data) + y * in->Xsize * 2; + + for( x = 0; x < half_width; x++ ) { + q[0] = p[0]; + q[1] = p[1]; + p += 2; + q += 2; + } + } + + /* Make the plan for the transform. Yes, they really do use nx for + * height and ny for width. + */ + if( !(plan = rfftw2d_create_plan( in->Ysize, in->Xsize, + FFTW_BACKWARD, FFTW_MEASURE | FFTW_USE_WISDOM )) ) { + im_error( "im_invfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + rfftwnd_one_complex_to_real( plan, + (fftw_complex *) half_complex, (fftw_real *) real->data ); + + rfftwnd_destroy_plan( plan ); + + /* Copy to out. + */ + if( im_copy( real, out ) ) + return( -1 ); + + return( 0 ); +} +#else /*!HAVE_FFTW*/ +#ifdef HAVE_FFTW3 +/* Complex to real inverse transform. + */ +static int +invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + IMAGE *cmplx = im_open_local( dummy, "invfft1-1", "t" ); + IMAGE *real = im_open_local( out, "invfft1-2", "t" ); + const int half_width = in->Xsize / 2 + 1; + + /* Transform to halfcomplex here. + */ + double *half_complex = IM_ARRAY( dummy, + in->Ysize * half_width * 2, double ); + + /* We have to have a separate real buffer for the planner to work on. + */ + double *planner_scratch = IM_ARRAY( dummy, + in->Ysize * half_width * 2, double ); + + fftw_plan plan; + int x, y; + double *q, *p; + + if( !cmplx || !real || !half_complex || im_pincheck( in ) || + im_poutcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_invfft", _( "one band uncoded only" ) ); + return( -1 ); + } + + /* Make dp complex image for input. + */ + if( im_clip2dcm( in, cmplx ) ) + return( -1 ); + + /* Make mem buffer real image for output. + */ + if( im_cp_desc( real, in ) ) + return( -1 ); + real->Bbits = IM_BBITS_DOUBLE; + real->BandFmt = IM_BANDFMT_DOUBLE; + if( im_setupout( real ) ) + return( -1 ); + + /* Build half-complex image. + */ + q = half_complex; + for( y = 0; y < cmplx->Ysize; y++ ) { + p = ((double *) cmplx->data) + y * in->Xsize * 2; + + for( x = 0; x < half_width; x++ ) { + q[0] = p[0]; + q[1] = p[1]; + p += 2; + q += 2; + } + } + + /* Make the plan for the transform. Yes, they really do use nx for + * height and ny for width. + */ + if( !(plan = fftw_plan_dft_c2r_2d( in->Ysize, in->Xsize, + (fftw_complex *) planner_scratch, (double *) real->data, + 0 )) ) { + im_error( "im_invfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + fftw_execute_dft_c2r( plan, + (fftw_complex *) half_complex, (double *) real->data ); + + fftw_destroy_plan( plan ); + + /* Copy to out. + */ + if( im_copy( real, out ) ) + return( -1 ); + + return( 0 ); +} +#else /*!HAVE_FFTW3*/ +/* Fall back to vips's built-in fft. + */ +static int +invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + int bpx = im_ispoweroftwo( in->Xsize ); + int bpy = im_ispoweroftwo( in->Ysize ); + float *buf, *q, *p1; + int x, y; + + /* Buffers for real and imaginary parts. + */ + IMAGE *real = im_open_local( dummy, "invfft1:1", "t" ); + IMAGE *imag = im_open_local( dummy, "invfft1:2", "t" ); + + /* Temps. + */ + IMAGE *t1 = im_open_local( dummy, "invfft1:3", "p" ); + IMAGE *t2 = im_open_local( dummy, "invfft1:4", "p" ); + + if( !real || !imag || !t1 ) + return( -1 ); + if( im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || + in->Bands != 1 || !im_iscomplex( in ) ) { + im_error( "im_invfft", _( "one band complex uncoded only" ) ); + return( -1 ); + } + if( !bpx || !bpy ) { + im_error( "im_invfft", _( "sides must be power of 2" ) ); + return( -1 ); + } + + /* Make sure we have a single-precision complex input image. + */ + if( im_clip2cm( in, t1 ) ) + return( -1 ); + + /* Extract real and imag parts. We have to complement the imaginary. + */ + if( im_c2real( t1, real ) ) + return( -1 ); + if( im_c2imag( t1, t2 ) || im_lintra( -1.0, t2, 0.0, imag ) ) + return( -1 ); + + /* Transform! + */ + if( im__fft_sp( (float *) real->data, (float *) imag->data, + bpx - 1, bpy - 1 ) ) { + im_error( "im_invfft", _( "fft_sp failed" ) ); + return( -1 ); + } + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (float *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Just write real part. + */ + for( p1 = (float *) real->data, y = 0; y < out->Ysize; y++ ) { + q = buf; + + for( x = 0; x < out->Xsize; x++ ) { + q[x] = *p1++; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} +#endif /*HAVE_FFTW3*/ +#endif /*HAVE_FFTW*/ + +int +im_invfftr( IMAGE *in, IMAGE *out ) +{ + IMAGE *dummy = im_open( "im_invfft:1", "p" ); + + if( !dummy ) + return( -1 ); + if( im__fftproc( dummy, in, out, invfft1 ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + if( out->Bands == 1 ) + out->Type = IM_TYPE_B_W; + else + out->Type = IM_TYPE_MULTIBAND; + + return( 0 ); +} diff --git a/libsrc/freq_filt/im_rotquad.c b/libsrc/freq_filt/im_rotquad.c new file mode 100644 index 00000000..d4361356 --- /dev/null +++ b/libsrc/freq_filt/im_rotquad.c @@ -0,0 +1,103 @@ +/* @(#) Shifts the four quadrants of a fourier transform for display + * @(#) Any number of bands, any coding, any band format + * @(#) Works on images with even sizes + * @(#) Output is the same as the input + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_rotquad( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/04/1990 + * Modified on : 09/05/1991 + * Modified on : 09/06/1992, J.Cupitt. + * - now works for any type, any number of bands. + * - uses bcopy instead of a loop: mucho faster. + * now uses memcpy - for Sys5 compat K.Martinez 29/4/92 + * 5/8/93 JC + * - some ANSIfication + * 28/6/95 JC + * - some more modernisation + * 11/7/02 JC + * - redone in term of extract()/insert(), for great partialisation + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_rotquad( IMAGE *in, IMAGE *out ) +{ + IMAGE *t[6]; + int xd = in->Xsize / 2; + int yd = in->Ysize / 2; + + if( in->Xsize < 2 || in->Ysize < 2 ) + return( im_copy( in, out ) ); + + if( im_open_local_array( out, t, 6, "im_rotquad-1", "p" ) || + /* Extract 4 areas. + */ + im_extract_area( in, t[0], 0, 0, xd, yd ) || + im_extract_area( in, t[1], xd, 0, in->Xsize - xd, yd ) || + im_extract_area( in, t[2], 0, yd, xd, in->Ysize - yd ) || + im_extract_area( in, t[3], xd, yd, + in->Xsize - xd, in->Ysize - yd ) || + + /* Reassemble, rotated. + */ + im_insert( t[3], t[2], t[4], in->Xsize - xd, 0 ) || + im_insert( t[1], t[0], t[5], in->Xsize - xd, 0 ) || + im_insert( t[4], t[5], out, 0, in->Ysize - yd ) ) + return( -1 ); + + out->Xoffset = xd; + out->Yoffset = yd; + + return( 0 ); +} diff --git a/libsrc/freq_filt/man3/Makefile.am b/libsrc/freq_filt/man3/Makefile.am new file mode 100644 index 00000000..4ed28f0b --- /dev/null +++ b/libsrc/freq_filt/man3/Makefile.am @@ -0,0 +1,13 @@ +man_MANS = \ + im_create_fmask.3 \ + im_disp_ps.3 \ + im_flt_imag_freq.3 \ + im_fractsurf.3 \ + im_freqflt.3 \ + im_fwfft.3 \ + im_invfft.3 \ + im_invfftr.3 \ + im_rotquad.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/freq_filt/man3/im_create_fmask.3 b/libsrc/freq_filt/man3/im_create_fmask.3 new file mode 100644 index 00000000..a10c37b3 --- /dev/null +++ b/libsrc/freq_filt/man3/im_create_fmask.3 @@ -0,0 +1,359 @@ +.TH IM_FMASKPROF 3 "8 Oct 1991" +.SH NAME +im_create_fmask \- create a frequency domain filter mask according to args +.SH SYNOPSIS +.B #include + +int +.br +im_create_fmask( IMAGE *out, int xs, int ys, int type, double p1, ... ) + +.SH DESCRIPTION +im_create_fmask() +creates a float one band image mask. Sizes xs and ys must be powers of 2, +and must be square. Non-square masks may be added in a future version. + +There are 18 types of filter mask supported in this VIPS - they are listed +below. For each type, you are expected to give the correct number of +additional parameters. See the table. + +.br +----------------------------------------------------------- +.br +| Filter mask; type; num_args; parameters | +.br +----------------------------------------------------------- +.br +| Ideal high pass; 0; 1; fc | +.br +| Ideal low pass; 1; 1; fc | +.br +| Butterworth high pass; 2; 3; order, fc, ac | +.br +| Butterworth low pass; 3; 3; order, fc, ac | +.br +| Gaussian low pass; 4; 2; fc, ac | +.br +| Gaussian high pass; 5; 2; fc, ac | +.br +| | +.br +| Ideal ring pass; 6; 2; fc, width | +.br +| Ideal ring reject; 7; 2; fc, width | +.br +| Butterworth ring pass; 8; 4; order, fc, width, ac | +.br +| Butterworth ring reject; 9; 4; order, fc, width, ac | +.br +| Gaussian ring pass; 10; 3; fc, width, ac | +.br +| Gaussian ring reject; 11; 3; fc, width, ac | +.br +| | +.br +| Ideal band pass; 12; 3; fcx, fcy, r | +.br +| Ideal band reject; 13; 3; fcx, fcy, r | +.br +| Butterworth band pass; 14; 5; order, fcx, fcy, r, ac | +.br +| Butterworth band reject; 15; 5; order, fcx, fcy, r, ac | +.br +| Gaussian band pass; 16; 4; fcx, fcy, r, ac | +.br +| Gaussian band reject; 17; 4; fcx, fcy, r, ac | +.br +| | +.br +| fractal filter mask; 18; 1; fractal_dimension | +.br +----------------------------------------------------------- + +All masks are created with the four quadrants rotated so the (0,0) dc component +is at the top left corner of the image. In order to view a mask, +the four quadrants must be rotated +(im_rotquad(3)) and scaled (im_scale(3)). If the masks +are used for filtering in the frequency domain, there is no need for rotation. +Function im_flt_imag_freq(3) creates a mask and filter a square image in the +frequency domain. + +As a matter of convention the positive x axis is from left to right while the +positive y axis is from top to bottom (on the image with the frequency (0,0) +close to the centre i.e the four quadrants rotated). +All produced filters are float images with the maximum value normalised to 1.0. +Ideal and Butterworth filters are given in the book by Gonzalez and Wintz. + +HIGH PASS - LOW PASS FILTER MASKS (flag: 0 to 5) + +A high pass filter mask filters the low frequencies while allowing the high +frequencies to get through. The reverse happens with a low pass +filter mask. The transition is controlled by the frequency +cutoff (fc). All masks are circularly symmetric and they are creating +by duplicating one forth of them. + +Ideal high pass/low pass (argno=1): + +The variable fc determines the frequency cutoff which can be given either as +percentage of the max spatial frequency (normalised by convention to 1.0) or +in pixels. In the latter case it is assumed that the input image is +square and that the maximum spatial frequency +corresponds to xs/2 points horizontally and and ys/2 points vertically. +The following line of code creates an ideal circularly symmetric +high pass filter mask: + +im_create_fmask(im, 128, 128, 0, .5); + +with all values above half the max spatial frequency +(corresponding to 32 pixels) set to 1.0 and the remaining set to 0.0. +The dc value (corresponding to the frequency (0,0)) is set to 1.0. +When the mask is properly scaled and has its four quadrants rotated it is a +black circle within a white square. The radius of the circle is +determined by fc which is .5*max_spatial_frequency that is, for the example +above .5*64=32. +The centre of the circle is set to 1.0 (white), in order to allow +the dc component to pass unaltered. +A circularly symmetric ideal low pass filter mask is constructed in a similar +way. + +Butterworth high pass/low pass (argno=3): + +Each mask needs three arguments: the order, fc and ac. Order corresponds to +the order of the Butterworth filter mask, fc is the frequency cutoff and +ac is the amplitude cutoff. The same conventions are valid for both fc and ac +as for the ideal high pass and low pass filter mask. +The amplitude cutoff is determined by ac and corresponds to the percentage +of the maximum amplitude at fc. The maximum amplitude is always +normalised to 1.0. +If the transfer function of the filter is H(r) then H(fc) = ac*H(0). +The transfer function at frequency (0,0) is also set to 1.0. + +The transfer function of the Butterworth high pass is: +.br +H(r)=1.0/(1.0+(1.0/ac-1.0)*pow((fc*fc)/(r*r),order)). +.br +For a Butterworth low pass: +.br +H(r)=1.0/(1.0+(1.0/ac-1.0)*pow((r*r)/(fc*fc),order)). +.br +Both masks are given in Gonzalez and Wintz (Digital Image Processing, 2nd edn, +1987). +By increasing the order, the filter becomes steeper introducing ringing. + +Gaussian high pass/low pass (argno=2): + +Each of these masks needs 2 arguments: fc and ac. For both arguments the same +conventions as for the Butterworth mask are valid. The transfer function +of a Gaussian high pass filter mask is given by the equation: +.br +H(r) = 1.0 - exp(log(ac)*r*r/(fc*fc)). +.br +The corresponding mask for a Gaussian high pass is: +.br +H(f) = exp(log(ac)*r*r/(fc*fc)). +.br +ac being the amplitude cutoff. +.br + + +RING PASS - RING REJECT FILTER MASKS (flag: 6 to 11) + +A circularly symmetric ring pass filter mask allows all +frequencies within a ring, to pass while blocking all other frequencies. +The ring is specified by its width and it radius which corresponds to fc +the frequency cutoff. The fc is centred within the width and, therefore, +the ring starts at point fc-width/2 up to fc+width/2 along the positive +horizontal x axis. The reverse happens with a low pass +filter mask. The transition is controlled by the frequency +cutoff (fc). All masks are circularly symmetric and they are creating +by duplicating one forth of them. + +Ideal ring pass/ring reject filter masks (argno=2): + +An ideal ring pass filter mask has two arguments, the width and the frequency +cutoff. The created mask when properly rotated, +is a white ring of internal radius fc-df +and external radius fc+df, on a black square. All band pass values +within the ring are set to 1.0 while the remaining band reject frequencies +are set to 0.0. The (0,0) frequency component is set to 1.0. +Both fc and width must be either between 0.0 and 1.0, or between 1.0 and +xs/2. If both are between 0.0 and 1.0 then the program normalises then to the +maximum spatial frequency which is xs/2=ys/2. + +An ideal ring reject filter mask is the reverse of the ideal ring pass filter +mask, that is it allows all frequencies to get through apart from the +frequencies within a ring specified by the args of the function, +in a similar way as the ideal ring pass filter. + +Butterworth ring pass/ring reject filter masks (argno=4): + +.br +Each of these masks has 4 arguments: the order of the filter (order), +the frequency cutoff (fc), the width (width) and the amplitude cutoff (ac). +.br +A Butterworth ring pass filter mask is a circularly symmetric ring shape mask. +The profile of the mask along the horizontal positive axis is a shifted +low pass Butterworth filter mask, with the maximum value set to 1.0. +This mask is similar to the ideal ring pass but the transition +between band pass and band reject zones instead of a sharp brick +wall, is a shifted Butterworth low pass filter +mask. The transfer function of the mask is given by the equation: +.br +H(r)=1./(1.+cnst*pow(((r-fc)*(r-fc)/(w2)),order)) +.br +where cnst=1/ac, w2 = width*width/4. +.br +Both fc and width should be either between 0.0 and 1.0 or between 1.0 and xs/2 +as in the case of the ideal ring pass or ring reject mask. The amplitude +cutoff should be always between 0.0 and 1.0. It should be noted that: +.br +H(fc+df)=H(fc-df)=ac*H(fc) +.br +The value of H(0) at frequency (0,0) has been set to 1.0 in order to allow +the dc component of the image to pass unaltered. + +For the case of the Butterworth ring reject filter mask, its transfer function +is given by the equation: +.br +H(r)=1./(1.+cnst*pow((w2/((r-fc)*(r-fc))),order)) +.br +where cnst=1/ac, w2 = width*width/4. +.br + +Gaussian ring pass/ring reject filter masks (argno=3): + +Each of these masks takes three arguments: the frequency cutoff (fc), the width +(width) and the amplitude cutoff (ac). The conventions for the arguments +are the same as for the Butterworth ring pass and ring reject masks above; +however the order is not needed. + +The transfer function of a Gaussian ring pass filter mask is: +.br +H(r)=exp(log(ac)*(r-fc) * (r-fc)/w2) +.br +where w2 = width*width/4. +.br + +BAND PASS - BAND REJECT MASKS (flag:13 to 17) + +These filter masks are used in order to eliminate spatial frequencies +around a given frequency. Since the masks must be symmetrical +with respect to the origin, they cannot be realised by creating +one forth and replicating it four times. + +Ideal band pass/band reject filter masks (argno=3): + +An ideal band reject filter mask takes three arguments: the coordinates +of the centre of the one circle (fcx,fcy) and its radius r. The produced +filter mask has all values 0.0 except two disks centred at (fcx,fcy), +(-fcx,-fcy) each one having radius r. The two disks have values of 1.0. +The value of the mask corresponding to (0,0) spatial frequency, as also +set to 1.0. + +All three arguments fcx, fcy and r should be either between 0 and 1 or +between 1 and the max spatial frequency which is xs/2=ys/2. In the +case that the arguments are between 0.0 and 1.0 they are interpreted +as percentage of the maximum spatial frequency. For the case of band +pass filter masks the value of the (0,0) frequency is set to 1.0 so that +the dc component can pass unaltered. + +Butterworth band pass/band reject filter masks (argno=4): + +A Butterworth band pass/band reject +filter mask allows/rejects spatial frequencies +around a given frequency. The mask consists of the sum of two +Butterworth shape filters centered at (fcx,fcy) and (-fcx,-fcy). +The shape of each mask is determined by the parameters of the function. +The arguments fcx, fcy and r obey the same conventions as for those +of the ideal band pass / band reject masks. The transfer function of the +filter at point (0,0) is set to 1.0. + +The function works by adding the two Butterworth masks. +As a result, if the whole mask is normalised with respect to +frequency (fcx,fcy), the cutoff frequency at (fcx+||r||,fcy+||r||) will +be different to that of (fcx-||r||,fcy-||r||), since the tail of +the mask centered at (-fcx,-fcy) will give a different contribution +to (fcx+||r||,fcy+||r||) than that to (fcx-||r||,fcy-||r||). +In order to simplify the calculations, the function estimates the +amplitude at a cutoff frequency ((fcx-||r||,fcy-||r||) as if the contribution +comes only from the mask centred at (fcx,fcy). The side effect of this +approach is that for big values of r the cutoff frequency of the filter mask +is different at frequencies (fcx+||r||,fcy+||r||) and (fcx+||r||,fcy+||r||). + +More specifically, given that each disk has a Butterworth shape of radius r +with centres at (x0, y0) and (-x0,-y0), +the transfer function of a Butterworth band pass filter +mask is given by the equation: +.br +H(d)= { H1(d) + H2(d) } +.br +H1(d) = cnst1/(1 + cnst2 * pow((d-d0)/r, 2*order)) +.br +H2(d) = cnst1/(1 + cnst2 * pow((d+d0)/r, 2*order)) +.br +where +.br +cnst1=1./(1.+1./(1.+cnst1*pow(d02/((r/2)*(r/2)),order))) +.br +cnst2=1./ac - 1., +.br +d02 = x0*x0+y0*y0. +.br +With this configuration for d=+d0, H(+d0) = 1.0; for d=-d0 H(-d0) = 1.0. +If da=(xa,ya), then for d=+da, H1(+da)=ac and for d=-da, H1(-da)=ac. In the +latter case it is assumed that xa=x0*(1-radius/sqrt(x0*x0+y0*y0)) and that +ya=y0*(1-radius/sqrt(x0*x0+y0*y0)). + +The transfer function of a Butterworth band reject filter H_bbr(d) is given +by the equation: +.br +H_bbr(d) = 1.0 - H_bbp(d), +.br +where H_bbp(d) is the transfer function of the Butterworth bandpass filter +defined above. + +Gaussian band pass/band reject filter masks (argno=3): + +For a Gaussian band pass or band reject filter mask, similar conventions +to those of the Butterworth filter masks, are valid however the order as an +argument is not needed. + +The transfer function of a Gaussian band pass filter mask is given by the +equation +.br +H(d)= { H1(d) + H2(d) } +.br +H1(d) = cnst1 * exp(-cnst2 * (d-d0)*(d-d0)/(r*r)) +.br +H1(d) = cnst1 * exp(-cnst2 * (d+d0)*(d+d0)/(r*r)) +.br +where +.br +cnst1=1/( 1+exp(-cnst*d02/((r/2)*(r/2))) ), +.br +d02 = x0*x0+y0*y0 and cnst2=-log(ac). + +The transfer function of a Gaussian band reject filter H_gbr(d) is given +by the equation: +.br +H_gbr(d) = 1.0 - H_gbp(d), +.br +where H_gbp(d) is the transfer function of the Gaussian bandpass filter +defined above. + +FRACTAL FILTER MASK (flag:18) + +The fractal filter mask should be used only to filter square images of +white Gaussian noise in order to create fractal surfaces of a given fractal +dimension. The fractal dimension should be between 2.0 and 3.0. The produced +mask has a power spectrum which decays according to the rule entered by the +parameter fractal dimension. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_flt_image_freq(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/08/1991 diff --git a/libsrc/freq_filt/man3/im_disp_ps.3 b/libsrc/freq_filt/man3/im_disp_ps.3 new file mode 100644 index 00000000..33040ded --- /dev/null +++ b/libsrc/freq_filt/man3/im_disp_ps.3 @@ -0,0 +1,32 @@ +.TH IM_DISP_PS 3 "23 May 1991" +.SH NAME +im_disp_ps \- creates a displayable power spectrum of an one band image +.SH SYNOPSIS +.B #include + +.B int im_disp_ps(in, out) +.br +.B IMAGE *in, *out; +.SH DESCRIPTION +im_disp_ps() creates a displayable power spectrum of the image held by the +image descriptor in. The resultant unsigned char image is written on the +image descriptor out. Input should be one band image of any non-complex +type. + +The function finds the fourier transform of the input (if in is not complex) +and then the amplitude power spectrum is created (im_c2ps(3)). The power +spectrum is passed through a non-linear transformation (im_scaleps(3)). The +final image has the four quadrants rotated in such a way that the coordinate +(0,0) is near the centre of the image (im_rotquad(3)). + +Input should be a square image with size power of 2. +The maximum size of the input image is dictated by the available RAM. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_fwfft(3), im_c2ps(3), im_scaleps(3), im_rotquad(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 23/05/1991 diff --git a/libsrc/freq_filt/man3/im_flt_imag_freq.3 b/libsrc/freq_filt/man3/im_flt_imag_freq.3 new file mode 100644 index 00000000..3242a947 --- /dev/null +++ b/libsrc/freq_filt/man3/im_flt_imag_freq.3 @@ -0,0 +1,27 @@ +.TH IM_FMASKPROF 3 "8 Oct 1991" +.SH NAME +im_flt_imag_freq \- filter a square image in the frequency domain +.SH SYNOPSIS +#include + +int +.br +im_flt_imag_freq( IMAGE *in, IMAGE *out, int flag, double p1, ... ) + +.SH DESCRIPTION +im_flt_imag_freq() +filters an image in the frequency domain using a mask dictated by the flag +and the parameters p1, ... The mask is created by using the +function im_create_fmask(3). After creating the mask the program filters +the input image using the function im_freqflt(3). + +For details about the arguments refer to the function im_create_fmask(3). + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_freqflt(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/08/1991 diff --git a/libsrc/freq_filt/man3/im_fractsurf.3 b/libsrc/freq_filt/man3/im_fractsurf.3 new file mode 100644 index 00000000..3fdeca34 --- /dev/null +++ b/libsrc/freq_filt/man3/im_fractsurf.3 @@ -0,0 +1,29 @@ +.TH IM_FRACTSURF 3 "10 May 1991" +.SH NAME +im_fractsurf \- creates a fractal surface +.SH SYNOPSIS +.B #include + +.B int im_fractsurf(in, frdim, size) +.br +.B IMAGE *in; +.br +.B double frdim; +.br +.B int size; +.SH DESCRIPTION +.B im_fractsurf +creates a fracta surface. The program initially creates a gaussian +noise image (im_gausnoise(3)) of sizes size x size. The image is then +filtered (im_flt_image_freq(3)) in order to force the power spectrum to +decay according to the desired fractal dimension frdim. The result is +a one band float image and should be scaled for display. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_flt_image_freq(3), im_gausnoise(3) +.SH COPYRIGHT +.br +N. Dessipris, +.SH AUTHOR +N. Dessipris \- 06/07/1990 diff --git a/libsrc/freq_filt/man3/im_freqflt.3 b/libsrc/freq_filt/man3/im_freqflt.3 new file mode 100644 index 00000000..5f6a5f41 --- /dev/null +++ b/libsrc/freq_filt/man3/im_freqflt.3 @@ -0,0 +1,35 @@ +.TH IM_FOURFLT 3 "10 May 1991" +.SH NAME +im_freqflt \- filters an image with a float filter mask in the frequency domain +.SH SYNOPSIS +.B #include + +.B int im_freqflt(in, filtermask, out) +.br +IMAGE *in, *filtermask, *out; +.SH DESCRIPTION +im_freqflt() performs filtering in the frequency domain of the input image +held by the image descriptor in with a mask held by the descriptor filtermask +and writes the result on the image descriptor out. + +Image sizes should be power of two and less or equal to 512. All images +should be one channel square images. Image filtermask is a non-complex one +channel image created by im_create_fmask(). Input image can be any type. If +input is complex, in and filtermask are multiplied using the function +im_cmultim(3). + +If input is not complex then it is transformed into the frequency domain and +then it is multiplied with the filtermask. In the latter case the result is +inverse fourier transformed and clipped to the input image format using the +function im_clip2fmt(3). +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH BUGS +The function has not been fully tested. +.SH SEE\ ALSO +im_fwfft(3), im_invfft(3), im_create_fmask(3), im_cmultim(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/freq_filt/man3/im_fwfft.3 b/libsrc/freq_filt/man3/im_fwfft.3 new file mode 100644 index 00000000..ba26cbeb --- /dev/null +++ b/libsrc/freq_filt/man3/im_fwfft.3 @@ -0,0 +1,60 @@ +.TH IM_FWFFT 3 "14 May 1991" +.SH NAME +im_fwfft, im_invfft, im_invfftr \- forward and inverse fft on an image +.SH SYNOPSIS +.B #include + +.B int im_fwfft(in, out) +.br +.B IMAGE *in, *out; + +.B int im_invfft(in, out) +.br +.B IMAGE *in, *out; + +.B int im_invfftr(in, out) +.br +.B IMAGE *in, *out; + +.SH DESCRIPTION +.B im_fwfft() +performs a forward fast Fourier Transform on the image held by the +image descriptor in and writes the result to the image descriptor out. +The image can be in any format and have any number of bands. The output is +always complex. + +If VIPS has been built with support for libfftw, a high-speed FFT library, +then fftwnd_one() is used to compute the transform. This produces a double +precision complex result. The first transformation at a particular image +size will be very slow as libfftw optimises itself for your machine, +but subsequent transforms of images of that size are extremely fast. +Unfortunately, libfftw does not have good out-of-memory behaviour. If you +try to transform a very large image, your program will exit abruptly. + +If VIPS has not been built with libfftw support, VIPS uses its own fft +routines. These are rather slow, are single precision only, and can only +transform images whose sides are a power of two. + +.B im_invfft() +performs the reverse transform. +The input image must be complex, the output is always complex. The image may +have any number of bands. + +Again, if libfftw was present when VIPS was +compiled, that library is used to calculate the transform. + +.B im_invfftr() +performs the reverse transform. +The input image must be complex, the output is always real. The image may +have any number of bands. It is about 2 x faster than +.B im_invfft(). + +Again, if libfftw was present when VIPS was +compiled, that library is used to calculate the transform. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_rotquad(3), im_c2ps(3), im_scaleps(3), im_disp_ps(3). +.SH COPYRIGHT +1995, National Gallery and Birkbeck College diff --git a/libsrc/freq_filt/man3/im_invfft.3 b/libsrc/freq_filt/man3/im_invfft.3 new file mode 100644 index 00000000..1111213f --- /dev/null +++ b/libsrc/freq_filt/man3/im_invfft.3 @@ -0,0 +1 @@ +.so man3/im_fwfft.3 diff --git a/libsrc/freq_filt/man3/im_invfftr.3 b/libsrc/freq_filt/man3/im_invfftr.3 new file mode 100644 index 00000000..1111213f --- /dev/null +++ b/libsrc/freq_filt/man3/im_invfftr.3 @@ -0,0 +1 @@ +.so man3/im_fwfft.3 diff --git a/libsrc/freq_filt/man3/im_rotquad.3 b/libsrc/freq_filt/man3/im_rotquad.3 new file mode 100644 index 00000000..d2fc6bea --- /dev/null +++ b/libsrc/freq_filt/man3/im_rotquad.3 @@ -0,0 +1,28 @@ +.TH IM_ROTQUAD 3 "07 July 1990" +.SH NAME +im_rotquad \- rotates the four quadrants of an image +.SH SYNOPSIS +.B #include + +.B int im_rotquad(in, out) +.br +.B IMAGE *in, *out; +.SH DESCRIPTION +im_rotquad rotates the four quadrants of the image held by the image +descriptor in and writes the result on the image descriptor out. + +The function is used primarily to rotate a fourier transform in such a way +that the coordinate (0,0) is near the centre of the image, the so-called +optical transform. + +The function operates on any input; +output has the same format, sizes and bands as input. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_fwfft(3), im_invfft(3), im_scaleps(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 06/07/1990 diff --git a/libsrc/histograms_lut/Makefile.am b/libsrc/histograms_lut/Makefile.am new file mode 100644 index 00000000..b98669f0 --- /dev/null +++ b/libsrc/histograms_lut/Makefile.am @@ -0,0 +1,25 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libhistograms_lut.la + +libhistograms_lut_la_SOURCES = \ + hist_dispatch.c \ + im_gammacorrect.c \ + im_heq.c \ + im_hist.c \ + im_histeq.c \ + im_histgr.c \ + im_histnD.c \ + im_histplot.c \ + im_histspec.c \ + im_hsp.c \ + im_identity.c \ + im_invertlut.c \ + im_lhisteq.c \ + im_maplut.c \ + im_buildlut.c \ + im_project.c \ + im_stdif.c \ + tone.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/histograms_lut/hist_dispatch.c b/libsrc/histograms_lut/hist_dispatch.c new file mode 100644 index 00000000..f49044b6 --- /dev/null +++ b/libsrc/histograms_lut/hist_dispatch.c @@ -0,0 +1,811 @@ +/* VIPS function dispatch tables for histogram_lut. + * + * J. Cupitt, 24/5/95. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* One image in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Args for im_gammacorrect. + */ +static im_arg_desc gammacorrect_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "exponent" ) +}; + +/* Call im_gammacorrect via arg vector. + */ +static int +gammacorrect_vec( im_object *argv ) +{ + double exp = *((double *) argv[2]); + + return( im_gammacorrect( argv[0], argv[1], exp ) ); +} + +/* Description of im_gammacorrect. + */ +static im_function gammacorrect_desc = { + "im_gammacorrect", /* Name */ + "gamma-correct image", /* Description */ + IM_FN_PIO, /* Flags */ + gammacorrect_vec, /* Dispatch function */ + IM_NUMBER( gammacorrect_args ), /* Size of arg list */ + gammacorrect_args /* Arg list */ +}; + +/* Image plus number in, image out. + */ +static im_arg_desc heq_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "band_number" ) +}; + +/* Call im_heq via arg vector. + */ +static int +heq_vec( im_object *argv ) +{ + int bn = *((int *) argv[2]); + + return( im_heq( argv[0], argv[1], bn ) ); +} + +/* Description of im_heq. + */ +static im_function heq_desc = { + "im_heq", /* Name */ + "histogram-equalise image", /* Description */ + IM_FN_PIO, /* Flags */ + heq_vec, /* Dispatch function */ + IM_NUMBER( heq_args ), /* Size of arg list */ + heq_args /* Arg list */ +}; + +/* Call im_hist via arg vector. + */ +static int +hist_vec( im_object *argv ) +{ + int bn = *((int *) argv[2]); + + return( im_hist( argv[0], argv[1], bn ) ); +} + +/* Description of im_hist. + */ +static im_function hist_desc = { + "im_hist", /* Name */ + "find and graph histogram of image", /* Description */ + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + hist_vec, /* Dispatch function */ + IM_NUMBER( heq_args ), /* Size of arg list */ + heq_args /* Arg list */ +}; + +/* Call im_histcum via arg vector. + */ +static int +histcum_vec( im_object *argv ) +{ + return( im_histcum( argv[0], argv[1] ) ); +} + +/* Description of im_histcum. + */ +static im_function histcum_desc = { + "im_histcum", /* Name */ + "turn histogram to cumulative histogram",/* Description */ + IM_FN_PIO, /* Flags */ + histcum_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_histnorm via arg vector. + */ +static int +histnorm_vec( im_object *argv ) +{ + return( im_histnorm( argv[0], argv[1] ) ); +} + +/* Description of im_histcum. + */ +static im_function histnorm_desc = { + "im_histnorm", /* Name */ + "form normalised histogram",/* Description */ + IM_FN_PIO, /* Flags */ + histnorm_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_histeq via arg vector. + */ +static int +histeq_vec( im_object *argv ) +{ + return( im_histeq( argv[0], argv[1] ) ); +} + +/* Description of im_histeq. + */ +static im_function histeq_desc = { + "im_histeq", /* Name */ + "form histogram equalistion LUT",/* Description */ + IM_FN_PIO, /* Flags */ + histeq_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_histgr via arg vector. + */ +static int +histgr_vec( im_object *argv ) +{ + int bn = *((int *) argv[2]); + + return( im_histgr( argv[0], argv[1], bn ) ); +} + +/* Description of im_histgr. + */ +static im_function histgr_desc = { + "im_histgr", /* Name */ + "find histogram of image", /* Description */ + IM_FN_TRANSFORM, /* Flags */ + histgr_vec, /* Dispatch function */ + IM_NUMBER( heq_args ), /* Size of arg list */ + heq_args /* Arg list */ +}; + +/* Call im_histnD() via arg vector. + */ +static int +histnD_vec( im_object *argv ) +{ + int bins = *((int *) argv[2]); + + return( im_histnD( argv[0], argv[1], bins ) ); +} + +/* Args for im_histnD(). + */ +static im_arg_desc histnD_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "bins" ) +}; + +/* Description of im_histnD(). + */ +static im_function histnD_desc = { + "im_histnD", /* Name */ + "find 1D, 2D or 3D histogram of image", /* Description */ + IM_FN_TRANSFORM, /* Flags */ + histnD_vec, /* Dispatch function */ + IM_NUMBER( histnD_args ), /* Size of arg list */ + histnD_args /* Arg list */ +}; + +/* Call im_histplot via arg vector. + */ +static int +histplot_vec( im_object *argv ) +{ + return( im_histplot( argv[0], argv[1] ) ); +} + +/* Description of im_histplot. + */ +static im_function histplot_desc = { + "im_histplot", /* Name */ + "plot graph of histogram", /* Description */ + IM_FN_PIO, /* Flags */ + histplot_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args for im_histspec. + */ +static im_arg_desc histspec_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_IMAGE( "ref" ), + IM_OUTPUT_IMAGE( "out" ), +}; + +/* Call im_histspec via arg vector. + */ +static int +histspec_vec( im_object *argv ) +{ + return( im_histspec( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_histspec. + */ +static im_function histspec_desc = { + "im_histspec", /* Name */ + "find histogram which will make pdf of in match ref", + 0, /* Flags */ + histspec_vec, /* Dispatch function */ + IM_NUMBER( histspec_args ), /* Size of arg list */ + histspec_args /* Arg list */ +}; + +/* Call im_hsp via arg vector. + */ +static int +hsp_vec( im_object *argv ) +{ + return( im_hsp( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_hsp. + */ +static im_function hsp_desc = { + "im_hsp", /* Name */ + "match stats of in to stats of ref", + 0, /* Flags */ + hsp_vec, /* Dispatch function */ + IM_NUMBER( histspec_args ), /* Size of arg list */ + histspec_args /* Arg list */ +}; + +/* Args for im_identity. + */ +static im_arg_desc identity_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "nbands" ) +}; + +/* Call im_identity via arg vector. + */ +static int +identity_vec( im_object *argv ) +{ + int nb = *((int *) argv[1]); + + return( im_identity( argv[0], nb ) ); +} + +/* Description of im_identity. + */ +static im_function identity_desc = { + "im_identity", /* Name */ + "generate identity histogram", + 0, /* Flags */ + identity_vec, /* Dispatch function */ + IM_NUMBER( identity_args ), /* Size of arg list */ + identity_args /* Arg list */ +}; + +/* Args for im_identity_ushort. + */ +static im_arg_desc identity_ushort_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "nbands" ), + IM_INPUT_INT( "size" ) +}; + +/* Call im_identity_ushort via arg vector. + */ +static int +identity_ushort_vec( im_object *argv ) +{ + int nb = *((int *) argv[1]); + int sz = *((int *) argv[2]); + + return( im_identity_ushort( argv[0], nb, sz ) ); +} + +/* Description of im_identity_ushort. + */ +static im_function identity_ushort_desc = { + "im_identity_ushort", /* Name */ + "generate ushort identity histogram", + 0, /* Flags */ + identity_ushort_vec, /* Dispatch function */ + IM_NUMBER( identity_ushort_args ), /* Size of arg list */ + identity_ushort_args /* Arg list */ +}; + +/* Args for im_lhisteq. + */ +static im_arg_desc lhisteq_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ) +}; + +/* Call im_lhisteq via arg vector. + */ +static int +lhisteq_vec( im_object *argv ) +{ + int xw = *((int *) argv[2]); + int yw = *((int *) argv[3]); + + return( im_lhisteq( argv[0], argv[1], xw, yw ) ); +} + +/* Description of im_lhisteq. + */ +static im_function lhisteq_desc = { + "im_lhisteq", /* Name */ + "local histogram equalisation", + IM_FN_PIO, /* Flags */ + lhisteq_vec, /* Dispatch function */ + IM_NUMBER( lhisteq_args ), /* Size of arg list */ + lhisteq_args /* Arg list */ +}; + +/* Call im_lhisteq_raw via arg vector. + */ +static int +lhisteq_raw_vec( im_object *argv ) +{ + int xw = *((int *) argv[2]); + int yw = *((int *) argv[3]); + + return( im_lhisteq_raw( argv[0], argv[1], xw, yw ) ); +} + +/* Description of im_lhisteq_raw. + */ +static im_function lhisteq_raw_desc = { + "im_lhisteq_raw", /* Name */ + "local histogram equalisation, no border", + IM_FN_PIO, /* Flags */ + lhisteq_raw_vec, /* Dispatch function */ + IM_NUMBER( lhisteq_args ), /* Size of arg list */ + lhisteq_args /* Arg list */ +}; + +/* Args for im_maplut. + */ +static im_arg_desc maplut_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_IMAGE( "lut" ) +}; + +/* Call im_maplut via arg vector. + */ +static int +maplut_vec( im_object *argv ) +{ + return( im_maplut( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_maplut. + */ +static im_function maplut_desc = { + "im_maplut", /* Name */ + "map image through LUT", + IM_FN_PIO, /* Flags */ + maplut_vec, /* Dispatch function */ + IM_NUMBER( maplut_args ), /* Size of arg list */ + maplut_args /* Arg list */ +}; + +/* Call im_project() via arg vector. + */ +static int +project_vec( im_object *argv ) +{ + return( im_project( argv[0], argv[1], argv[2] ) ); +} + +/* Args for im_project(). + */ +static im_arg_desc project_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "hout" ), + IM_OUTPUT_IMAGE( "vout" ) +}; + +/* Description of im_project(). + */ +static im_function project_desc = { + "im_project", /* Name */ + "find horizontal and vertical projections of an image", + IM_FN_TRANSFORM, /* Flags */ + project_vec, /* Dispatch function */ + IM_NUMBER( project_args ), /* Size of arg list */ + project_args /* Arg list */ +}; + +/* Args for im_stdif. + */ +static im_arg_desc stdif_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "a" ), + IM_INPUT_DOUBLE( "m0" ), + IM_INPUT_DOUBLE( "b" ), + IM_INPUT_DOUBLE( "s0" ), + IM_INPUT_INT( "xw" ), + IM_INPUT_INT( "yw" ) +}; + +/* Call im_stdif via arg vector. + */ +static int +stdif_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + double m0 = *((double *) argv[3]); + double b = *((double *) argv[4]); + double s0 = *((double *) argv[5]); + int xw = *((int *) argv[6]); + int yw = *((int *) argv[7]); + + return( im_stdif( argv[0], argv[1], a, m0, b, s0, xw, yw ) ); +} + +/* Description of im_stdif. + */ +static im_function stdif_desc = { + "im_stdif", /* Name */ + "statistical differencing", + IM_FN_PIO, /* Flags */ + stdif_vec, /* Dispatch function */ + IM_NUMBER( stdif_args ), /* Size of arg list */ + stdif_args /* Arg list */ +}; + +/* Call im_stdif_raw via arg vector. + */ +static int +stdif_raw_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + double m0 = *((double *) argv[3]); + double b = *((double *) argv[4]); + double s0 = *((double *) argv[5]); + int xw = *((int *) argv[6]); + int yw = *((int *) argv[7]); + + return( im_stdif_raw( argv[0], argv[1], a, m0, b, s0, xw, yw ) ); +} + +/* Description of im_stdif. + */ +static im_function stdif_raw_desc = { + "im_stdif_raw", /* Name */ + "statistical differencing, no border", + IM_FN_PIO, /* Flags */ + stdif_raw_vec, /* Dispatch function */ + IM_NUMBER( stdif_args ), /* Size of arg list */ + stdif_args /* Arg list */ +}; + +/* Args for im_buildlut. + */ +static im_arg_desc buildlut_args[] = { + IM_INPUT_DMASK( "xyes" ), + IM_OUTPUT_IMAGE( "lut" ) +}; + +/* Call im_buildlut via arg vector. + */ +static int +buildlut_vec( im_object *argv ) +{ + im_mask_object *mi = argv[0]; + + return( im_buildlut( mi->mask, argv[1] ) ); +} + +/* Description of im_buildlut. + */ +static im_function buildlut_desc = { + "im_buildlut", /* Name */ + "generate LUT table from set of x/y positions", + 0, /* Flags */ + buildlut_vec, /* Dispatch function */ + IM_NUMBER( buildlut_args ),/* Size of arg list */ + buildlut_args /* Arg list */ +}; + +/* Args for im_invertlut. + */ +static im_arg_desc invertlut_args[] = { + IM_INPUT_DMASK( "measures" ), + IM_OUTPUT_IMAGE( "lut" ), + IM_INPUT_INT( "lut_size" ) +}; + +/* Call im_invertlut via arg vector. + */ +static int +invertlut_vec( im_object *argv ) +{ + im_mask_object *mi = argv[0]; + int lut_size = *((int *) argv[2]); + + return( im_invertlut( mi->mask, argv[1], lut_size ) ); +} + +/* Description of im_invertlut. + */ +static im_function invertlut_desc = { + "im_invertlut", /* Name */ + "generate correction table from set of measures", + 0, /* Flags */ + invertlut_vec, /* Dispatch function */ + IM_NUMBER( invertlut_args ),/* Size of arg list */ + invertlut_args /* Arg list */ +}; + +/* Args for im_tone_build. + */ +static im_arg_desc tone_build_args[] = { + IM_OUTPUT_IMAGE( "hist" ), + IM_INPUT_DOUBLE( "Lb" ), + IM_INPUT_DOUBLE( "Lw" ), + IM_INPUT_DOUBLE( "Ps" ), + IM_INPUT_DOUBLE( "Pm" ), + IM_INPUT_DOUBLE( "Ph" ), + IM_INPUT_DOUBLE( "S" ), + IM_INPUT_DOUBLE( "M" ), + IM_INPUT_DOUBLE( "H" ) +}; + +/* Call im_tone_build via arg vector. + */ +static int +tone_build_vec( im_object *argv ) +{ + double Lb = *((double *) argv[1]); + double Lw = *((double *) argv[2]); + double Ps = *((double *) argv[3]); + double Pm = *((double *) argv[4]); + double Ph = *((double *) argv[5]); + double S = *((double *) argv[6]); + double M = *((double *) argv[7]); + double H = *((double *) argv[8]); + + return( im_tone_build( argv[0], Lb, Lw, Ps, Pm, Ph, S, M, H ) ); +} + +/* Description of im_tone_build. + */ +static im_function tone_build_desc = { + "im_tone_build", /* Name */ + "create LUT for tone adjustment of LabS images", + 0, /* Flags */ + tone_build_vec, /* Dispatch function */ + IM_NUMBER( tone_build_args ), /* Size of arg list */ + tone_build_args /* Arg list */ +}; + +/* Args for im_tone_build_range. + */ +static im_arg_desc tone_build_range_args[] = { + IM_OUTPUT_IMAGE( "hist" ), + IM_INPUT_INT( "in_max" ), + IM_INPUT_INT( "out_max" ), + IM_INPUT_DOUBLE( "Lb" ), + IM_INPUT_DOUBLE( "Lw" ), + IM_INPUT_DOUBLE( "Ps" ), + IM_INPUT_DOUBLE( "Pm" ), + IM_INPUT_DOUBLE( "Ph" ), + IM_INPUT_DOUBLE( "S" ), + IM_INPUT_DOUBLE( "M" ), + IM_INPUT_DOUBLE( "H" ) +}; + +/* Call im_tone_build_range via arg vector. + */ +static int +tone_build_range_vec( im_object *argv ) +{ + int in_max = *((int *) argv[1]); + int out_max = *((int *) argv[2]); + double Lb = *((double *) argv[3]); + double Lw = *((double *) argv[4]); + double Ps = *((double *) argv[5]); + double Pm = *((double *) argv[6]); + double Ph = *((double *) argv[7]); + double S = *((double *) argv[8]); + double M = *((double *) argv[9]); + double H = *((double *) argv[10]); + + return( im_tone_build_range( argv[0], in_max, out_max, + Lb, Lw, Ps, Pm, Ph, S, M, H ) ); +} + +/* Description of im_tone_build_range. + */ +static im_function tone_build_range_desc = { + "im_tone_build_range", /* Name */ + "create LUT for tone adjustment", + 0, /* Flags */ + tone_build_range_vec, /* Dispatch function */ + IM_NUMBER( tone_build_range_args ),/* Size of arg list */ + tone_build_range_args /* Arg list */ +}; + +/* Args for im_tone_analyse. + */ +static im_arg_desc tone_analyse_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "hist" ), + IM_INPUT_DOUBLE( "Ps" ), + IM_INPUT_DOUBLE( "Pm" ), + IM_INPUT_DOUBLE( "Ph" ), + IM_INPUT_DOUBLE( "S" ), + IM_INPUT_DOUBLE( "M" ), + IM_INPUT_DOUBLE( "H" ) +}; + +/* Call im_tone_analyse via arg vector. + */ +static int +tone_analyse_vec( im_object *argv ) +{ + double Ps = *((double *) argv[2]); + double Pm = *((double *) argv[3]); + double Ph = *((double *) argv[4]); + double S = *((double *) argv[5]); + double M = *((double *) argv[6]); + double H = *((double *) argv[7]); + + return( im_tone_analyse( argv[0], argv[1], Ps, Pm, Ph, S, M, H ) ); +} + +/* Description of im_tone_analyse. + */ +static im_function tone_analyse_desc = { + "im_tone_analyse", /* Name */ + "analyse in and create LUT for tone adjustment", + 0, /* Flags */ + tone_analyse_vec, /* Dispatch function */ + IM_NUMBER( tone_analyse_args ), /* Size of arg list */ + tone_analyse_args /* Arg list */ +}; + +/* Args for im_ismonotonic. + */ +static im_arg_desc ismonotonic_args[] = { + IM_INPUT_IMAGE( "lut" ), + IM_OUTPUT_INT( "mono" ) +}; + +/* Call im_ismonotonic via arg vector. + */ +static int +ismonotonic_vec( im_object *argv ) +{ + int *res = (int *) argv[1]; + + return( im_ismonotonic( argv[0], res ) ); +} + +/* Description of im_ismonotonic. + */ +static im_function ismonotonic_desc = { + "im_ismonotonic", /* Name */ + "test LUT for monotonicity", + 0, /* Flags */ + ismonotonic_vec, /* Dispatch function */ + IM_NUMBER( ismonotonic_args ), /* Size of arg list */ + ismonotonic_args /* Arg list */ +}; + +/* Args for im_tone_map + */ +static im_arg_desc tone_map_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_IMAGE( "lut" ) +}; + +/* Call im_tone_map via arg vector. + */ +static int +tone_map_vec( im_object *argv ) +{ + return( im_tone_map( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_tone_map. + */ +static im_function tone_map_desc = { + "im_tone_map", /* Name */ + "map L channel of LabS or LabQ image through LUT", + IM_FN_PIO, /* Flags */ + tone_map_vec, /* Dispatch function */ + IM_NUMBER( tone_map_args ),/* Size of arg list */ + tone_map_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *hist_list[] = { + &gammacorrect_desc, + &heq_desc, + &hist_desc, + &histcum_desc, + &histeq_desc, + &histgr_desc, + &histnD_desc, + &histnorm_desc, + &histplot_desc, + &histspec_desc, + &hsp_desc, + &identity_desc, + &identity_ushort_desc, + &ismonotonic_desc, + &lhisteq_desc, + &lhisteq_raw_desc, + &invertlut_desc, + &buildlut_desc, + &maplut_desc, + &project_desc, + &stdif_desc, + &stdif_raw_desc, + &tone_analyse_desc, + &tone_build_desc, + &tone_build_range_desc, + &tone_map_desc +}; + +/* Package of functions. + */ +im_package im__histograms_lut = { + "histograms_lut", + IM_NUMBER( hist_list ), + hist_list +}; diff --git a/libsrc/histograms_lut/im_buildlut.c b/libsrc/histograms_lut/im_buildlut.c new file mode 100644 index 00000000..5c878936 --- /dev/null +++ b/libsrc/histograms_lut/im_buildlut.c @@ -0,0 +1,241 @@ +/* @(#) Build a LUT from a set of x/y points. Eg. if input is + * @(#) + * @(#) 12 100 + * @(#) 14 110 + * @(#) 18 120 + * @(#) + * @(#) we generate + * @(#) + * @(#) 100 (12) + * @(#) 105 + * @(#) 110 + * @(#) 112.5 + * @(#) 115 + * @(#) 117.5 + * @(#) 120 (18) + * @(#) + * @(#) The x/y points don't need to be sorted: we do that. You can have + * @(#) several Ys ... each becomes a band in the output LUT. + * + * Written on: 26/9/06 + * - from im_invertlut() + * 9/10/06 + * - don't output x values + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* +#define DEBUG + */ + +/* Our state. + */ +typedef struct _State { + DOUBLEMASK *input; /* Input mask */ + int lut_size; /* Number of output elements to generate */ + double **data; /* Rows of unpacked matrix */ +} State; + +/* Use this to sort our input rows by the first column. + */ +static int +compare( const void *a, const void *b ) +{ + double **r1 = (double **) a; + double **r2 = (double **) b; + + double diff = r1[0][0] - r2[0][0]; + + if( diff > 0 ) + return( 1 ); + else if( diff == 0 ) + return( 0 ); + else + return( -1 ); +} + +/* Free our state. + */ +static void +free_state( State *state ) +{ + if( state->data ) { + int i; + + for( i = 0; i < state->input->ysize; i++ ) + if( state->data[i] ) { + im_free( state->data[i] ); + state->data[i] = NULL; + } + + im_free( state->data ); + state->data = NULL; + } +} + +/* Fill our state. + */ +static int +build_state( State *state, DOUBLEMASK *input ) +{ + int x, y, i; + int xlow, xhigh; + + state->input = input; + state->data = NULL; + + /* Need xlow and xhigh to get the size of the LUT we build. + */ + xlow = xhigh = input->coeff[0]; + for( y = 0; y < input->ysize; y++ ) { + double v = input->coeff[y * input->xsize]; + + if( floor( v ) != v ) { + im_error( "im_buildlut", _( "x value not an int" ) ); + return( -1 ); + } + + if( v < xlow ) + xlow = v; + if( v > xhigh ) + xhigh = v; + } + state->lut_size = xhigh - xlow; + + if( state->lut_size < 1 ) { + im_error( "im_buildlut", _( "x range too small" ) ); + return( -1 ); + } + + if( !(state->data = IM_ARRAY( NULL, input->ysize, double * )) ) + return( -1 ); + for( y = 0; y < input->ysize; y++ ) + state->data[y] = NULL; + + for( y = 0; y < input->ysize; y++ ) + if( !(state->data[y] = IM_ARRAY( NULL, input->xsize, double )) ) + return( -1 ); + + for( i = 0, y = 0; y < input->ysize; y++ ) + for( x = 0; x < input->xsize; x++, i++ ) + state->data[y][x] = input->coeff[i]; + + /* Sort by 1st column in input. + */ + qsort( state->data, input->ysize, sizeof( double * ), compare ); + +#ifdef DEBUG + printf( "Input table, sorted by 1st column\n" ); + for( y = 0; y < input->ysize; y++ ) { + printf( "%.4d ", y ); + + for( x = 0; x < input->xsize; x++ ) + printf( "%.9f ", state->data[y][x] ); + + printf( "\n" ); + } +#endif /*DEBUG*/ + + return( 0 ); +} + +static int +buildlut( State *state, IMAGE *output ) +{ + const DOUBLEMASK *input = state->input; + const int ysize = input->ysize; + const int xsize = input->xsize; + + double *odata = (double *) output->data; + + int b, y, i, x; + + /* Do each output channel separately. + */ + for( b = 0; b < xsize - 1; b++ ) + for( x = b, y = 0; y < ysize - 1; y++ ) { + const int x1 = state->data[y][0]; + const int x2 = state->data[y + 1][0]; + const double y1 = state->data[y][b + 1]; + const double y2 = state->data[y + 1][b + 1]; + const int dx = x2 - x1; + const double dy = y2 - y1; + + for( i = 0; i < dx; i++, x += xsize - 1 ) + odata[x] = y1 + i * dy / dx; + } + + return( 0 ); +} + +int +im_buildlut( DOUBLEMASK *input, IMAGE *output ) +{ + State state; + + if( !input || input->xsize < 2 || input->ysize < 1 ) { + im_error( "im_buildlut", _( "bad input matrix size" ) ); + return( -1 ); + } + + if( build_state( &state, input ) ) { + free_state( &state ); + return( -1 ); + } + + im_initdesc( output, + state.lut_size, 1, input->xsize - 1, + IM_BBITS_DOUBLE, IM_BANDFMT_DOUBLE, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + if( im_setupout( output ) ) + return( -1 ); + + if( buildlut( &state, output ) ) { + free_state( &state ); + return( -1 ); + } + + free_state( &state ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_gammacorrect.c b/libsrc/histograms_lut/im_gammacorrect.c new file mode 100644 index 00000000..14e4ad7a --- /dev/null +++ b/libsrc/histograms_lut/im_gammacorrect.c @@ -0,0 +1,82 @@ +/* @(#) Gamma-correct uchar image with factor gammafactor. + * @(#) + * @(#) int im_gammacorrect(in, out, exponent) + * @(#) IMAGE *in, *out; + * @(#) double exponent; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * + * + * Copyright: 1990, N. Dessipris. + * + * Written on: 19/07/1990 + * Modified on: + * 19/6/95 JC + * - redone as library function + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_gammacorrect( IMAGE *in, IMAGE *out, double exponent ) +{ + IMAGE *t1 = im_open_local( out, "im_gammacorrect:#1", "p" ); + IMAGE *t2 = im_open_local( out, "im_gammacorrect:#2", "p" ); + IMAGE *t3 = im_open_local( out, "im_gammacorrect:#2", "p" ); + + if( !t1 || !t2 || !t3 ) + return( -1 ); + + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im_gammacorrect: uchar images only" ); + return( -1 ); + } + + if( im_identity( t1, 1 ) || + im_powtra( t1, t2, exponent ) || + im_scale( t2, t3 ) || + im_maplut( in, out, t3 ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_heq.c b/libsrc/histograms_lut/im_heq.c new file mode 100644 index 00000000..aaca77f8 --- /dev/null +++ b/libsrc/histograms_lut/im_heq.c @@ -0,0 +1,79 @@ +/* @(#) Histogram equalises the input uchar image; result in uchar + * @(#) If bandno=0 all bands are equilised independantly + * @(#) else input image is equilised using the histogram of bandno only. + * @(#) Image descriptors should have been set properly by the calling program + * @(#) + * @(#) Usage: heq imagein imageout bandno + * @(#) int im_heq(in, out, bandno) + * @(#) IMAGE *in, *out; + * @(#) int bandno; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 27/03/1991 + * Modified on : + * 16/6/93 J.Cupitt + * - im_ioflag() changed to im_iocheck() + * 24/5/95 JC + * - ANSIfied and tidied up + * 3/3/01 JC + * - more cleanup + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_heq( IMAGE *in, IMAGE *out, int bandno ) +{ + IMAGE *t1 = im_open_local( out, "im_heq:1", "p" ); + IMAGE *t2 = im_open_local( out, "im_heq:2", "p" ); + + if( !t1 || !t2 || + im_histgr( in, t1, bandno ) || + im_histeq( t1, t2 ) || + im_maplut( in, out, t2 ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_hist.c b/libsrc/histograms_lut/im_hist.c new file mode 100644 index 00000000..89d63d04 --- /dev/null +++ b/libsrc/histograms_lut/im_hist.c @@ -0,0 +1,75 @@ +/* @(#) Creates a displayable historam of in result in hist + * @(#) If bandno=0 histogram of all bands is created + * @(#) else the histogram of bandno only is created. + * @(#) + * @(#) Usage: + * @(#) int im_hist(in, out, bandno) + * @(#) IMAGE *in, *out; + * @(#) int bandno; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 27/03/1991 + * Modified on : + * 16/6/93 J.Cupitt + * - im_ioflag() call changed to im_iocheck() + * 24/5/95 JC + * - ANSIfied and tidied up + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_hist( IMAGE *in, IMAGE *out, int bandno ) +{ + IMAGE *hist = im_open_local( out, "im_hist:#1", "p" ); + + if( !hist || + im_iocheck( in, out ) || + im_histgr( in, hist, bandno ) || + im_histplot( hist, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_histeq.c b/libsrc/histograms_lut/im_histeq.c new file mode 100644 index 00000000..e84ede49 --- /dev/null +++ b/libsrc/histograms_lut/im_histeq.c @@ -0,0 +1,212 @@ +/* @(#) Takes as input a histogram and creates a lut which when applied + * @(#) on the original image, histogram equilises it. + * @(#) + * @(#) Histogram equalisation is carried out for each band of hist + * @(#) individually. + * @(#) + * @(#) int im_histeq( IMAGE *hist, IMAGE *lut ) + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * + * Copyright: 1991, N. Dessipris + * + * Author: N. Dessipris + * Written on: 02/08/1990 + * 24/5/95 JC + * - tidied up and ANSIfied + * 20/7/95 JC + * - smartened up again + * - now works for hists >256 elements + * 3/3/01 JC + * - broken into cum and norm ... helps im_histspec() + * - better behaviour for >8 bit hists + * 31/10/05 JC + * - was broken for vertical histograms, gah + * - neater im_histnorm() + * 23/7/07 + * - eek, off by 1 for more than 1 band hists + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define ACCUMULATE( ITYPE, OTYPE ) { \ + for( b = 0; b < nb; b++ ) { \ + ITYPE *p = (ITYPE *) in->data; \ + OTYPE *q = (OTYPE *) outbuf; \ + OTYPE total; \ + \ + total = 0; \ + for( x = b; x < mx; x += nb ) { \ + total += p[x]; \ + q[x] = total; \ + } \ + } \ +} + +/* Form cumulative histogram. + */ +int +im_histcum( IMAGE *in, IMAGE *out ) +{ + const int px = in->Xsize * in->Ysize; + const int nb = im_iscomplex( in ) ? in->Bands * 2 : in->Bands; + const int mx = px * nb; + + PEL *outbuf; + int b, x; + + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_histcum", _( "input coded" ) ); + return( -1 ); + } + if( px > 65536 ) { + im_error( "im_histcum", _( "input too large" ) ); + return( -1 ); + } + if( im_incheck( in ) ) + return( -1 ); + + /* int types -> uint, float/double stay as they are. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = px; + out->Ysize = 1; + if( im_isint( in ) ) + out->BandFmt = IM_BANDFMT_UINT; + out->Bbits = im_bits_of_fmt( out->BandFmt ); + + if( !(outbuf = im_malloc( out, IM_IMAGE_SIZEOF_LINE( out ))) ) + return( -1 ); + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: + ACCUMULATE( signed char, unsigned int ); break; + case IM_BANDFMT_UCHAR: + ACCUMULATE( unsigned char, unsigned int ); break; + case IM_BANDFMT_SHORT: + ACCUMULATE( signed short, unsigned int ); break; + case IM_BANDFMT_USHORT: + ACCUMULATE( unsigned short, unsigned int ); break; + case IM_BANDFMT_INT: + ACCUMULATE( signed int, unsigned int ); break; + case IM_BANDFMT_UINT: + ACCUMULATE( unsigned int, unsigned int ); break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_COMPLEX: + ACCUMULATE( float, float ); break; + case IM_BANDFMT_DOUBLE: + case IM_BANDFMT_DPCOMPLEX: + ACCUMULATE( double, double ); break; + + default: + assert( 0 ); + } + + if( im_setupout( out ) || im_writeline( 0, out, outbuf ) ) + return( -1 ); + + return( 0 ); +} + +/* Normalise histogram ... normalise range to make it square (ie. max == + * number of elements). Normalise each band separately. + */ +int +im_histnorm( IMAGE *in, IMAGE *out ) +{ + const int px = in->Xsize * in->Ysize; + DOUBLEMASK *stats; + double *a, *b; + int i; + IMAGE *t1; + int fmt; + + /* Need max for each channel. + */ + if( !(a = IM_ARRAY( out, in->Bands, double )) || + !(b = IM_ARRAY( out, in->Bands, double )) || + !(stats = im_stats( in )) ) + return( -1 ); + + /* Scale each channel by px / channel max + */ + for( i = 0; i < in->Bands; i++ ) { + a[i] = px / stats->coeff[6 + 1 + 6*i]; + b[i] = 0; + } + + im_free_dmask( stats ); + + if( !(t1 = im_open_local( out, "im_histnorm:2", "p" )) || + im_lintra_vec( in->Bands, a, in, b, t1 ) ) + return( -1 ); + + /* Make output format as small as we can. + */ + if( px <= 256 ) + fmt = IM_BANDFMT_UCHAR; + else if( px <= 65536 ) + fmt = IM_BANDFMT_USHORT; + else + fmt = IM_BANDFMT_UINT; + + if( im_clip2fmt( t1, out, fmt ) ) + return( -1 ); + + return( 0 ); +} + +/* Histogram equalisation. + */ +int +im_histeq( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_histeq:1", "p" ); + + /* Normalised cumulative. + */ + if( !t1 || im_histcum( in, t1 ) || im_histnorm( t1, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_histgr.c b/libsrc/histograms_lut/im_histgr.c new file mode 100644 index 00000000..0faad82d --- /dev/null +++ b/libsrc/histograms_lut/im_histgr.c @@ -0,0 +1,379 @@ +/* @(#) im_histgr: make a histogram of an image and saves it into hist. + * @(#) If input is uchar, output is 256 by 1 image of uint. If input is + * @(#) ushort, output is max(image) + 1 by 1 image of uint. If bandno is + * @(#) zero, then output is has same number of bands as input, with each + * @(#) band being a separate histogram. Otherwise, bandno selects a band + * @(#) to find the histogram of. + * @(#) + * @(#) Usage: + * @(#) int im_histgr(image, hist, bandno) + * @(#) IMAGE *image, *hist; + * @(#) int bandno; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, 1991, N. Dessipris. + * + * Author: Nicos Dessipris. + * Written on: 09/07/1990 + * Modified on : 11/03/1991 + * 19/7/93 JC + * - test for Coding type added + * 26/10/94 JC + * - rewritten for ANSI + * - now does USHORT too + * - 5 x faster! + * 2/6/95 JC + * - rewritten for partials + * 3/3/01 JC + * - tiny speed ups + * 21/1/07 + * - number bands from zero + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Accumulate a histogram in one of these. + */ +typedef struct { + int bands; /* Number of bands in output */ + int which; /* If one band in out, which band of input */ + int size; /* Length of bins */ + int mx; /* Maximum value we have seen */ + unsigned int **bins; /* All the bins! */ +} Histogram; + +/* Build a Histogram. + */ +static Histogram * +build_hist( IMAGE *out, int bands, int which, int size ) +{ + int i; + Histogram *hist = IM_NEW( out, Histogram ); + + if( !hist || !(hist->bins = IM_ARRAY( out, bands, unsigned int * )) ) + return( NULL ); + for( i = 0; i < bands; i++ ) { + if( !(hist->bins[i] = IM_ARRAY( out, size, unsigned int )) ) + return( NULL ); + memset( hist->bins[i], 0, size * sizeof( unsigned int ) ); + } + + hist->bands = bands; + hist->which = which; + hist->size = size; + hist->mx = 0; + + return( hist ); +} + +/* Build a sub-hist, based on the main hist. + */ +static void * +build_subhist( IMAGE *out, Histogram *mhist ) +{ + return( (void *) + build_hist( out, mhist->bands, mhist->which, mhist->size ) ); +} + +/* Join a sub-hist onto the main hist. + */ +static int +merge_subhist( Histogram *shist, Histogram *mhist ) +{ + int i, j; + + /* Sanity! + */ + if( shist->bands != mhist->bands || shist->size != mhist->size ) + error_exit( "sanity failure in merge_subhist" ); + + /* Add on sub-data. + */ + mhist->mx = IM_MAX( mhist->mx, shist->mx ); + for( i = 0; i < mhist->bands; i++ ) + for( j = 0; j < mhist->size; j++ ) + mhist->bins[i][j] += shist->bins[i][j]; + + /* Blank out sub-hist to make sure we can't add it again. + */ + shist->mx = 0; + for( i = 0; i < shist->bands; i++ ) + shist->bins[i] = NULL; + + return( 0 ); +} + +/* Histogram of all bands of a uchar image. + */ +static int +find_uchar_hist( REGION *reg, Histogram *hist ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int nb = im->Bands; + int x, y, z; + + /* Accumulate! + */ + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( reg, le, y ); + int i; + + for( i = 0, x = 0; x < r->width; x++ ) + for( z = 0; z < nb; z++, i++ ) + hist->bins[z][p[i]]++; + } + + /* Note the maximum. + */ + hist->mx = 255; + + return( 0 ); +} + +/* Histogram of a selected band of a uchar image. + */ +static int +find_uchar_hist_extract( REGION *reg, Histogram *hist ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + unsigned int *bins = hist->bins[0]; + int nb = im->Bands; + int max = r->width * nb; + int x, y; + + /* Accumulate! + */ + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( reg, le, y ); + + for( x = hist->which; x < max; x += nb ) + bins[p[x]]++; + } + + /* Note the maximum. + */ + hist->mx = 255; + + return( 0 ); +} + +/* Histogram of all bands of a ushort image. + */ +static int +find_ushort_hist( REGION *reg, Histogram *hist ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int mx = hist->mx; + int nb = im->Bands; + int x, y, z; + + /* Accumulate! + */ + for( y = to; y < bo; y++ ) { + unsigned short *p = (unsigned short *) + IM_REGION_ADDR( reg, le, y ); + int i; + + for( i = 0, x = 0; x < r->width; x++ ) + for( z = 0; z < nb; z++, i++ ) { + int v = p[i]; + + /* Adjust maximum. + */ + if( v > mx ) + mx = v; + + hist->bins[z][v]++; + } + } + + /* Note the maximum. + */ + hist->mx = mx; + + return( 0 ); +} + +/* Histogram of all bands of a ushort image. + */ +static int +find_ushort_hist_extract( REGION *reg, Histogram *hist ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int mx = hist->mx; + unsigned int *bins = hist->bins[0]; + int nb = im->Bands; + int max = nb * r->width; + int x, y; + + /* Accumulate! + */ + for( y = to; y < bo; y++ ) { + unsigned short *p = (unsigned short *) + IM_REGION_ADDR( reg, le, y ) + hist->which; + + for( x = hist->which; x < max; x += nb ) { + int v = p[x]; + + /* Adjust maximum. + */ + if( v > mx ) + mx = v; + + bins[v]++; + } + } + + /* Note the maximum. + */ + hist->mx = mx; + + return( 0 ); +} + +int +im_histgr( IMAGE *in, IMAGE *out, int bandno ) +{ + int size; /* Length of hist */ + int bands; /* Number of bands in output */ + Histogram *mhist; + int (*scanfn)( REGION *, Histogram * ); + int i, j; + unsigned int *obuffer, *q; + + /* Check images. PIO from in, WIO to out. + */ + if( im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_histgr", _( "uncoded images only" ) ); + return( -1 ); + } + + /* Find the range of pixel values we must handle. + */ + if( in->Bbits == IM_BBITS_BYTE && in->BandFmt == IM_BANDFMT_UCHAR ) + size = 256; + else if( in->Bbits == IM_BBITS_SHORT && + in->BandFmt == IM_BANDFMT_USHORT ) + size = 65536; + else { + im_error( "im_histgr", _( "input not uchar or ushort" ) ); + return( -1 ); + } + + /* How many output bands? + */ + if( bandno > in->Bands || bandno < -1 ) { + im_error( "im_histgr", _( "bad band parameter" ) ); + return( -1 ); + } + if( bandno == -1 ) + bands = in->Bands; + else + bands = 1; + + /* Build main hist we accumulate data in. + */ + if( !(mhist = build_hist( out, bands, bandno, size )) ) + return( -1 ); + + /* Select scan function. + */ + if( in->BandFmt == IM_BANDFMT_UCHAR && bandno == -1 ) + scanfn = find_uchar_hist; + else if( in->BandFmt == IM_BANDFMT_UCHAR ) + scanfn = find_uchar_hist_extract; + else if( in->BandFmt == IM_BANDFMT_USHORT && bandno == -1 ) + scanfn = find_ushort_hist; + else + scanfn = find_ushort_hist_extract; + + /* Accumulate data. + */ + if( im_iterate( in, + build_subhist, scanfn, merge_subhist, mhist, NULL ) ) + return( -1 ); + + /* Make the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + im_initdesc( out, + mhist->mx + 1, 1, bands, IM_BBITS_INT, IM_BANDFMT_UINT, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + if( im_setupout( out ) ) + return( -1 ); + + /* Interleave for output. + */ + if( !(obuffer = IM_ARRAY( out, + IM_IMAGE_N_ELEMENTS( out ), unsigned int )) ) + return( -1 ); + for( q = obuffer, j = 0; j < out->Xsize; j++ ) + for( i = 0; i < out->Bands; i++ ) + *q++ = mhist->bins[i][j]; + + /* Write interleaved buffer into hist. + */ + if( im_writeline( 0, out, (PEL *) obuffer ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_histnD.c b/libsrc/histograms_lut/im_histnD.c new file mode 100644 index 00000000..6bd24bc2 --- /dev/null +++ b/libsrc/histograms_lut/im_histnD.c @@ -0,0 +1,267 @@ +/* @(#) im_histnD: make a one, two or three dimensional histogram of a 1, 2 or + * @(#) 3 band image. Divide each axis into a certain number of bins .. ie. + * @(#) output is 1 x bins, binx x bins, or bins x bins x bins bands. + * @(#) uchar and ushort only. + * @(#) + * @(#) Usage: + * @(#) int im_histnD( image, hist, bins ) + * @(#) IMAGE *image, *hist; + * @(#) int bins; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Written on: 8/7/03 + * 10/11/04 + * - oops, was not checking the bandfmt coming in + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Accumulate a histogram in one of these. + */ +typedef struct { + IMAGE *in; + IMAGE *out; + int bins; + + unsigned int ***data; /* Gather stats here */ +} Histogram; + +/* Build a Histogram. + */ +static Histogram * +build_hist( IMAGE *in, IMAGE *out, int bins ) +{ + /* How many dimensions we we need to allocate? + */ + int ilimit = in->Bands > 2 ? bins : 1; + int jlimit = in->Bands > 1 ? bins : 1; + + int i, j; + Histogram *hist; + + if( !(hist = IM_NEW( out, Histogram )) ) + return( NULL ); + + hist->in = in; + hist->out = out; + hist->bins = bins; + + if( !(hist->data = IM_ARRAY( out, bins, unsigned int ** )) ) + return( NULL ); + memset( hist->data, 0, bins * sizeof( unsigned int ** ) ); + + for( i = 0; i < ilimit; i++ ) { + if( !(hist->data[i] = IM_ARRAY( out, bins, unsigned int * )) ) + return( NULL ); + memset( hist->data[i], 0, bins * sizeof( unsigned int * ) ); + for( j = 0; j < jlimit; j++ ) { + if( !(hist->data[i][j] = IM_ARRAY( out, + bins, unsigned int )) ) + return( NULL ); + memset( hist->data[i][j], + 0, bins * sizeof( unsigned int ) ); + } + } + + return( hist ); +} + +/* Build a sub-hist, based on the main hist. + */ +static void * +build_subhist( IMAGE *out, Histogram *mhist ) +{ + return( (void *) + build_hist( mhist->in, mhist->out, mhist->bins ) ); +} + +/* Join a sub-hist onto the main hist. + */ +static int +merge_subhist( Histogram *shist, Histogram *mhist ) +{ + int i, j, k; + + /* Sanity! + */ + if( shist->in != mhist->in || shist->out != mhist->out ) + error_exit( "sanity failure in merge_subhist" ); + + /* Add on sub-data. + */ + for( i = 0; i < mhist->bins; i++ ) + for( j = 0; j < mhist->bins; j++ ) + for( k = 0; k < mhist->bins; k++ ) + if( mhist->data[i] && mhist->data[i][j] ) { + mhist->data[i][j][k] += + shist->data[i][j][k]; + + /* Zap sub-hist to make sure we + * can't add it again. + */ + shist->data[i][j][k] = 0; + } + + return( 0 ); +} + +#define LOOP( TYPE ) { \ + TYPE *p = (TYPE *) line;\ + \ + for( i = 0, x = 0; x < r->width; x++ ) { \ + for( z = 0; z < nb; z++, i++ ) \ + index[z] = p[i] / scale; \ + \ + hist->data[index[2]][index[1]][index[0]]++; \ + } \ +} + +static int +find_hist( REGION *reg, Histogram *hist ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int nb = im->Bands; + int max_val = im->BandFmt == IM_BANDFMT_UCHAR ? 256 : 65536; + int scale = max_val / hist->bins; + int x, y, z, i; + int index[3]; + + /* Fill these with dimensions, backwards. + */ + index[0] = index[1] = index[2] = 0; + + /* Accumulate! + */ + for( y = to; y < bo; y++ ) { + char *line = IM_REGION_ADDR( reg, le, y ); + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + LOOP( unsigned char ); + break; + + case IM_BANDFMT_USHORT: + LOOP( unsigned char ); + break; + + default: + error_exit( "panic #34847563245" ); + } + } + + return( 0 ); +} + +int +im_histnD( IMAGE *in, IMAGE *out, int bins ) +{ + int max_val; + Histogram *mhist; + int x, y, z, i; + unsigned int *obuffer; + + /* Check images. PIO from in, WIO to out. + */ + if( im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_histnD", _( " uncoded images only" ) ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_UCHAR && + in->BandFmt != IM_BANDFMT_USHORT ) { + im_error( "im_histnD", + _( " unsigned 8 or 16 bit images only" ) ); + return( -1 ); + } + + max_val = in->BandFmt == IM_BANDFMT_UCHAR ? 256 : 65536; + if( bins < 1 || bins > max_val ) { + im_error( "im_histnD", + _( " bins out of range [1,%d]" ), max_val ); + return( -1 ); + } + + /* Build main hist we accumulate to. + */ + if( !(mhist = build_hist( in, out, bins )) ) + return( -1 ); + + /* Accumulate data. + */ + if( im_iterate( in, + build_subhist, find_hist, merge_subhist, mhist, NULL ) ) + return( -1 ); + + /* Make the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + im_initdesc( out, + bins, in->Bands > 1 ? bins : 1, in->Bands > 2 ? bins : 1, + IM_BBITS_INT, IM_BANDFMT_UINT, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + if( im_setupout( out ) ) + return( -1 ); + + /* Interleave to output buffer. + */ + if( !(obuffer = IM_ARRAY( out, + IM_IMAGE_N_ELEMENTS( out ), unsigned int )) ) + return( -1 ); + + for( y = 0; y < out->Ysize; y++ ) { + for( i = 0, x = 0; x < out->Xsize; x++ ) + for( z = 0; z < out->Bands; z++, i++ ) + obuffer[i] = mhist->data[z][y][x]; + + if( im_writeline( y, out, (PEL *) obuffer ) ) + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_histplot.c b/libsrc/histograms_lut/im_histplot.c new file mode 100644 index 00000000..6592d54d --- /dev/null +++ b/libsrc/histograms_lut/im_histplot.c @@ -0,0 +1,336 @@ +/* @(#) im_histplot: plot a 1xany or anyx1 image file as a max x any or + * @(#) any x max graph using these rules: + * @(#) + * @(#) - unsigned char + * @(#) always output 256 + * @(#) - other unsigned integer types + * @(#) output 0 - max + * @(#) - signed int types + * @(#) min moved to 0, max moved to max + min. + * @(#) - float types + * @(#) min moved to 0, max moved to any (square output) + * @(#) + * @(#) usage: + * @(#) + * @(#) int + * @(#) im_histplot( hist, histplot ) + * @(#) IMAGE *hist, *histplot; + * @(#) + * @(#) Returns non-zero on error + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris. + * Written on: 09/07/1990 + * Modified on : 12/03/1991 + * 20/6/95 JC + * - rules rationalised + * - im_lineprof removed + * - rewritten + * 13/8/99 JC + * - rewritten again for partial, rules redone + * 19/9/99 JC + * - oooops, broken for >1 band + * 26/9/99 JC + * - oooops, graph float was wrong + * 17/11/99 JC + * - oops, failed for all 0's histogram + * 14/12/05 + * - redone plot function in C, also use incheck() to cache calcs + * - much, much faster! + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Normalise an image using the rules noted above. + */ +static int +normalise( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_histplot:2", "p" ); + double min, max; + + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_histplot", _( "uncoded only" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_histplot", _( "non-complex only" ) ); + return( -1 ); + } + + if( im_isuint( in ) ) { + /* Trivial case. + */ + if( im_copy( in, out ) ) + return( -1 ); + } + else if( im_isint( in ) ) { + /* Move min up to 0. incheck(), because we have to min() so we + * might as well save the calcs. + */ + if( !t1 || + im_incheck( in ) || + im_min( in, &max ) || + im_lintra( 1.0, in, -min, t1 ) ) + return( -1 ); + } + else { + /* Float image: scale min--max to 0--any. Output square + * graph. + */ + int any; + + if( in->Xsize == 1 ) + any = in->Ysize; + else + any = in->Xsize; + + /* incheck(), because we have to min()/max() so we + * might as well save the calcs. + */ + if( !t1 || + im_incheck( in ) || + im_min( in, &min ) || + im_max( in, &max ) || + im_lintra( any / (max - min), in, + -min * any / (max - min), out ) ) + return( -1 ); + } + + return( 0 ); +} + +#define VERT( TYPE ) { \ + TYPE *p1 = (TYPE *) p; \ + \ + for( x = le; x < ri; x++ ) { \ + for( b = 0; b < nb; b++ ) \ + q[b] = p1[b] < x ? 0 : 255; \ + \ + q += nb; \ + } \ +} + + +/* Generate function. + */ +static int +make_vert_gen( REGION *or, void *dummy, IMAGE *in ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int ri = IM_RECT_RIGHT( r ); + int bo = IM_RECT_BOTTOM( r ); + int nb = in->Bands; + + int x, y, b; + + for( y = to; y < bo; y++ ) { + unsigned char *q = (unsigned char *) + IM_REGION_ADDR( or, le, y ); + unsigned char *p = (unsigned char *) + IM_IMAGE_ADDR( in, 0, y ); + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: VERT( unsigned char ); break; + case IM_BANDFMT_CHAR: VERT( signed char ); break; + case IM_BANDFMT_USHORT: VERT( unsigned short ); break; + case IM_BANDFMT_SHORT: VERT( signed short ); break; + case IM_BANDFMT_UINT: VERT( unsigned int ); break; + case IM_BANDFMT_INT: VERT( signed int ); break; + case IM_BANDFMT_FLOAT: VERT( float ); break; + case IM_BANDFMT_DOUBLE: VERT( double ); break; + + default: + im_error( "im_histplot", _( "internal error #8255" ) ); + return( -1 ); + } + } + + return( 0 ); +} + +#define HORZ( TYPE ) { \ + TYPE *p1 = (TYPE *) p; \ + \ + for( y = to; y < bo; y++ ) { \ + for( b = 0; b < nb; b++ ) \ + q[b] = p1[b] < (ht - y) ? 0 : 255; \ + \ + q += lsk; \ + } \ +} + + +/* Generate function. + */ +static int +make_horz_gen( REGION *or, void *dummy, IMAGE *in ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int ri = IM_RECT_RIGHT( r ); + int bo = IM_RECT_BOTTOM( r ); + int nb = in->Bands; + int lsk = IM_REGION_LSKIP( or ); + int ht = or->im->Ysize; + + int x, y, b; + + for( x = le; x < ri; x++ ) { + unsigned char *q = (unsigned char *) + IM_REGION_ADDR( or, x, to ); + unsigned char *p = (unsigned char *) + IM_IMAGE_ADDR( in, x, 0 ); + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: HORZ( unsigned char ); break; + case IM_BANDFMT_CHAR: HORZ( signed char ); break; + case IM_BANDFMT_USHORT: HORZ( unsigned short ); break; + case IM_BANDFMT_SHORT: HORZ( signed short ); break; + case IM_BANDFMT_UINT: HORZ( unsigned int ); break; + case IM_BANDFMT_INT: HORZ( signed int ); break; + case IM_BANDFMT_FLOAT: HORZ( float ); break; + case IM_BANDFMT_DOUBLE: HORZ( double ); break; + + default: + im_error( "im_histplot", _( "internal error #8255" ) ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Plot image. + */ +static int +plot( IMAGE *in, IMAGE *out ) +{ + IMAGE *t[5]; + double max; + int tsize; + int xsize; + int ysize; + + if( im_incheck( in ) || + im_poutcheck( out ) ) + return( -1 ); + + /* Find range we will plot. + */ + if( im_open_local_array( out, t, 5, "im_histplot", "p" ) || + im_max( in, &max ) ) + return( -1 ); + if( max < 0 ) { + im_errormsg( "im_histplot: internal error #8254" ); + return( -1 ); + } + if( in->BandFmt == IM_BANDFMT_UCHAR ) + tsize = 256; + else + tsize = ceil( max ); + + /* Make sure we don't make a zero height image. + */ + if( tsize == 0 ) + tsize = 1; + + if( in->Xsize == 1 ) { + /* Vertical graph. + */ + xsize = tsize; + ysize = in->Ysize; + } + else { + /* Horizontal graph. + */ + xsize = in->Xsize; + ysize = tsize; + } + + /* Set image. + */ + im_initdesc( out, xsize, ysize, in->Bands, + IM_BBITS_BYTE, IM_BANDFMT_UCHAR, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + + /* Set hints - ANY is ok with us. + */ + if( im_demand_hint( out, IM_ANY, NULL ) ) + return( -1 ); + + /* Generate image. + */ + if( in->Xsize == 1 ) { + if( im_generate( out, NULL, make_vert_gen, NULL, in, NULL ) ) + return( -1 ); + } + else { + if( im_generate( out, NULL, make_horz_gen, NULL, in, NULL ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_histplot( IMAGE *hist, IMAGE *histplot ) +{ + IMAGE *norm = im_open_local( histplot, "im_histplot:1", "p" ); + + if( !norm ) + return( -1 ); + if( hist->Xsize != 1 && hist->Ysize != 1 ) { + im_error( "im_histplot", _( "Xsize or Ysize not 1" ) ); + return( -1 ); + } + + if( normalise( hist, norm ) || + plot( norm, histplot ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_histspec.c b/libsrc/histograms_lut/im_histspec.c new file mode 100644 index 00000000..55dce220 --- /dev/null +++ b/libsrc/histograms_lut/im_histspec.c @@ -0,0 +1,217 @@ +/* @(#) Creates a lut for transforming imagein (with histin) according to + * @(#) the pdf of imageref (with histref). The lut should have been set + * @(#) by a call to im_setbuf() or im_openout(). histin and histref + * @(#) should have been set by a call to im_mmapin() or they are buffer images + * @(#) + * @(#) Usage: int im_histspec(in, ref, out) + * @(#) IMAGE *histin, *histref, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 19/07/1990 + * Modified on: 26/03/1991 + * 1/3/01 JC + * - bleurg! rewritten, now does 16 bits as well, bugs removed, faster, + * smaller + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* +#define DEBUG + */ + +/* +#define PIM_RINT + */ + +/* Match two normalised cumulative histograms. + */ +static int +match( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + const int inpx = in->Xsize * in->Ysize; + const int refpx = ref->Xsize * ref->Ysize; + const int bands = in->Bands; + + unsigned int *inbuf; /* in and ref, padded to same size */ + unsigned int *refbuf; + unsigned int *outbuf; /* Always output as uint */ + + int px; /* Number of pixels */ + int max; /* px * bands */ + + int i, j; + + if( im_iocheck( in, out ) || im_iocheck( ref, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || ref->Coding != IM_CODING_NONE ) { + im_errormsg( "im_histspec: not uncoded" ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_UINT || + ref->BandFmt != IM_BANDFMT_UINT ) { + im_errormsg( "im_histspec: bad band format" ); + return( -1 ); + } + if( in->Bands != ref->Bands ) { + im_errormsg( "im_histspec: input histograms differ in " + "number of bands" ); + return( -1 ); + } + + /* How big? + */ + if( inpx <= 256 && refpx <= 256 ) + px = 256; + else if( inpx <= 65536 && refpx <= 65536 ) + px = 65536; + else { + im_errormsg( "im_histspec: luts too large" ); + return( -1 ); + } + max = px * bands; + + /* Unpack to equal sized buffers. + */ + inbuf = IM_ARRAY( out, max, unsigned int ); + refbuf = IM_ARRAY( out, max, unsigned int ); + outbuf = IM_ARRAY( out, max, unsigned int ); + if( !inbuf || !refbuf || !outbuf ) + return( -1 ); + for( i = 0; i < inpx * bands; i++ ) + inbuf[i] = ((unsigned int *)in->data)[i]; + for( ; i < max; i++ ) + inbuf[i] = 0; + for( i = 0; i < refpx * bands; i++ ) + refbuf[i] = ((unsigned int *)ref->data)[i]; + for( ; i < max; i++ ) + refbuf[i] = 0; + + for( j = 0; j < bands; j++ ) { + /* Track up refbuf[] with this. + */ + int ri = j; + int limit = max - bands; + + for( i = j; i < max; i += bands ) { + unsigned int inv = inbuf[i]; + + for( ; ri < limit; ri += bands ) + if( inv <= refbuf[ri] ) + break; + + if( ri < limit ) { + /* Simple rounding. + */ + double mid = refbuf[ri] + + refbuf[ri + bands] / 2.0; + + if( inv < mid ) + outbuf[i] = ri/bands; + else + outbuf[i] = ri/bands + 1; + } + else + outbuf[i] = refbuf[ri]; + } + } + + if( im_cp_descv( out, in, ref, NULL ) ) + return( -1 ); + out->Xsize = px; + out->Ysize = 1; + out->Type = IM_TYPE_HISTOGRAM; + + if( im_setupout( out ) || im_writeline( 0, out, (PEL *) outbuf ) ) + return( -1 ); + + return( 0 ); +} + +int +im_histspec( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_histspec:1", "p" ); + IMAGE *t2 = im_open_local( out, "im_histspec:2", "p" ); + IMAGE *t3 = im_open_local( out, "im_histspec:3", "p" ); + IMAGE *t4 = im_open_local( out, "im_histspec:4", "p" ); + IMAGE *t5 = im_open_local( out, "im_histspec:5", "p" ); + + int px; + int fmt; + + if( !t1 || !t2 || !t2 || !t4 || !t5 ) + return( -1 ); + if( !im_isuint( in ) || !im_isuint( ref ) ) { + im_errormsg( "im_histspec: input luts are not some unsigned " + "integer type" ); + return( -1 ); + } + + /* Match hists. + */ + if( im_histeq( in, t1 ) || im_clip2ui( t1, t2 ) || + im_histeq( ref, t3 ) || im_clip2ui( t3, t4 ) || + match( t2, t4, t5 ) ) + return( -1 ); + + /* Clip type down. + */ + px = t5->Xsize * t5->Ysize; + if( px <= 256 ) + fmt = IM_BANDFMT_UCHAR; + else if( px <= 65536 ) + fmt = IM_BANDFMT_USHORT; + else + fmt = IM_BANDFMT_UINT; + + if( im_clip2fmt( t5, out, fmt ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/histograms_lut/im_hsp.c b/libsrc/histograms_lut/im_hsp.c new file mode 100644 index 00000000..79af9c2b --- /dev/null +++ b/libsrc/histograms_lut/im_hsp.c @@ -0,0 +1,77 @@ +/* @(#) Maps imagein to imageout with histogram specified by imageref + * @(#) Both images should have the same number of bands + * @(#) Each band of the output image is specified according to the distribution + * @(#) of grey levels of the reference image + * @(#) + * @(#) Usage: + * @(#) int im_hsp(in, ref, out) + * @(#) IMAGE *in, *ref, *out; + * @(#) + * @(#) Return 0 on success and -1 on error + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 08/05/1990 + * Modified on : + * 16/6/93 J.Cupitt + * - im_ioflag() call changed to im_iocheck() + * 25/5/95 JC + * - revised + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_hsp( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + IMAGE *histin = im_open_local( out, "im_hsp:#1", "p" ); + IMAGE *histref = im_open_local( out, "im_hsp:#2", "p" ); + IMAGE *lut = im_open_local( out, "im_hsp:#3", "p" ); + + if( !histin || !histref || !lut || + im_histgr( in, histin, -1 ) || + im_histgr( ref, histref, -1 ) || + im_histspec( histin, histref, lut ) || + im_maplut( in, out, lut ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_identity.c b/libsrc/histograms_lut/im_identity.c new file mode 100644 index 00000000..030781df --- /dev/null +++ b/libsrc/histograms_lut/im_identity.c @@ -0,0 +1,163 @@ +/* @(#) Creates a image file with Xsize=256, Ysize=1, Bands=bands, + * @(#) Bbits=IM_BBITS_BYTE, BandFmt=IM_BANDFMT_UCHAR, Type=IM_TYPE_HISTOGRAM; + * @(#) + * @(#) The created image consist a n bands linear lut and is the basis + * @(#) for building up look-up tables. + * @(#) + * @(#) int im_identity(lut, bands) + * @(#) IMAGE *lut; + * @(#) int bands; + * @(#) + * @(#) As above, but make a ushort LUT. ushort LUTs can be less than 65536 + * @(#) elements - sz is the number of elements required. + * @(#) + * @(#) int im_identity_ushort(lut, bands, sz) + * @(#) IMAGE *lut; + * @(#) int bands; + * @(#) int sz; + * @(#) + * @(#) Returns -1 on error and 0 on success + * @(#) + * + * Copyright 1991, N. Dessipris. + * + * Author N. Dessipris + * Written on 11/03/1991 + * Updated on: + * 18/6/93 JC + * - im_outcheck() call added + * - ANSIfied + * 24/8/94 JC + * - im_identity_ushort() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_identity( IMAGE *lut, int bands ) +{ + unsigned char *buf, *p; + int x, z; + + /* Check input args + */ + if( im_outcheck( lut ) ) + return( -1 ); + if( bands < 0 ) { + im_errormsg( "im_identity: bad bands" ); + return( -1 ); + } + + /* Set new image properly. + */ + im_initdesc( lut, + 256, 1, bands, IM_BBITS_BYTE, IM_BANDFMT_UCHAR, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + + /* Make output. + */ + if( im_setupout( lut ) ) + return( -1 ); + + /* Create output buffer. + */ + if( !(buf = (unsigned char *) im_malloc( lut, bands * 256 )) ) + return( -1 ); + + /* Write ramp. + */ + for( p = buf, x = 0; x < 256; x++ ) + for( z = 0; z < bands; z++ ) + *p++ = x; + + if( im_writeline( 0, lut, buf ) ) + return( -1 ); + + return( 0 ); +} + +int +im_identity_ushort( IMAGE *lut, int bands, int sz ) +{ + unsigned short *buf, *p; + int x, z; + + /* Check input args + */ + if( im_outcheck( lut ) ) + return( -1 ); + if( sz > 65536 || sz < 0 ) { + im_errormsg( "im_identity_ushort: bad size" ); + return( -1 ); + } + if( bands < 0 ) { + im_errormsg( "im_identity_ushort: bad bands" ); + return( -1 ); + } + + /* Set new image. + */ + im_initdesc( lut, + sz, 1, bands, IM_BBITS_SHORT, IM_BANDFMT_USHORT, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + + /* Make output. + */ + if( im_setupout( lut ) ) + return( -1 ); + + /* Create output buffer. + */ + if( !(buf = (unsigned short *) + im_malloc( lut, sz * bands * sizeof( unsigned short ) )) ) + return( -1 ); + + /* Write ramp. + */ + for( p = buf, x = 0; x < sz; x++ ) + for( z = 0; z < bands; z++ ) + *p++ = x; + if( im_writeline( 0, lut, (PEL *) buf ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_invertlut.c b/libsrc/histograms_lut/im_invertlut.c new file mode 100644 index 00000000..26d6ffb6 --- /dev/null +++ b/libsrc/histograms_lut/im_invertlut.c @@ -0,0 +1,268 @@ +/* @(#) Given a mask of target values and real values, generate a LUT which + * @(#) will map reals to targets. Handy for linearising images from + * @(#) measurements of a colour chart. All values in [0,1]. Piecewise linear + * @(#) interpolation, extrapolate head and tail to 0 and 1. + * @(#) + * @(#) Eg. input line like: + * @(#) + * @(#) 0.1 0.2 0.3 0.1 + * @(#) + * @(#) means a patch with 10% reflectance produces an image with 20% in + * @(#) channel 1, 30% in channel 2, and 10% in channel 3. + * @(#) + * @(#) Inputs don't need to be sorted (we do that). Generate any precision + * @(#) LUT ... typically ask for 256 elements. + * + * Written on: 5/6/01 + * Modified on : + * 7/7/03 JC + * - generate image rather than doublemask (arrg) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* +#define DEBUG + */ + +/* Our state. + */ +typedef struct { + DOUBLEMASK *input; /* Input mask */ + IMAGE *output; /* Output lut */ + int lut_size; /* Number of output elements to generate */ + + double **data; /* Rows of unpacked matrix */ +} State; + +/* Use this to sort our input rows by the first column. + */ +static int +compare( const void *a, const void *b ) +{ + double **r1 = (double **) a; + double **r2 = (double **) b; + + double diff = r1[0][0] - r2[0][0]; + + if( diff > 0 ) + return( 1 ); + else if( diff == 0 ) + return( 0 ); + else + return( -1 ); +} + +/* Free our state. + */ +static void +free_state( State *state ) +{ + if( state->data ) { + int i; + + for( i = 0; i < state->input->ysize; i++ ) + if( state->data[i] ) { + im_free( state->data[i] ); + state->data[i] = NULL; + } + + im_free( state->data ); + state->data = NULL; + } +} + +/* Fill our state. + */ +static int +build_state( State *state, DOUBLEMASK *input, IMAGE *output, int lut_size ) +{ + int x, y, i; + + state->input = input; + state->output = output; + state->lut_size = lut_size; + state->data = NULL; + + if( !(state->data = IM_ARRAY( NULL, input->ysize, double * )) ) + return( -1 ); + for( y = 0; y < input->ysize; y++ ) + state->data[y] = NULL; + + for( y = 0; y < input->ysize; y++ ) + if( !(state->data[y] = IM_ARRAY( NULL, input->xsize, double )) ) + return( -1 ); + + for( i = 0, y = 0; y < input->ysize; y++ ) + for( x = 0; x < input->xsize; x++, i++ ) + state->data[y][x] = input->coeff[i]; + + /* Sanity check for data range. + */ + for( y = 0; y < input->ysize; y++ ) + for( x = 0; x < input->xsize; x++ ) + if( state->data[y][x] > 1.0 || + state->data[y][x] < 0.0 ) { + im_errormsg( "im_invertlut: element out of " + "range [0,1]" ); + return( -1 ); + } + + /* Sort by 1st column in input. + */ + qsort( state->data, input->ysize, sizeof( double * ), compare ); + +#ifdef DEBUG + printf( "Input table, sorted by 1st column\n" ); + for( y = 0; y < input->ysize; y++ ) { + printf( "%.4d ", y ); + + for( x = 0; x < input->xsize; x++ ) + printf( "%.9f ", state->data[y][x] ); + + printf( "\n" ); + } +#endif /*DEBUG*/ + + return( 0 ); +} + +static int +invertlut( State *state ) +{ + DOUBLEMASK *input = state->input; + int ysize = input->ysize; + int xsize = input->xsize; + IMAGE *output = state->output; + double *odata = (double *) output->data; + int bands = xsize - 1; + + double **data = state->data; + int lut_size = state->lut_size; + + int i; + + /* Do each output channel separately. + */ + for( i = 0; i < bands; i++ ) { + /* The first and last lut positions we know real values for. + */ + int first = data[0][i + 1] * (lut_size - 1); + int last = data[ysize - 1][i + 1] * (lut_size - 1); + + int k; + double fac; + + /* Extrapolate bottom and top segments to (0,0) and (1,1). + */ + fac = data[0][0] / first; + for( k = 0; k < first; k++ ) + odata[i + k * bands] = k * fac; + + fac = (1 - data[ysize - 1][0]) / ((lut_size - 1) - last); + for( k = last + 1; k < lut_size; k++ ) + odata[i + k * bands] = + data[ysize - 1][0] + (k - last) * fac; + + /* Interpolate the data setions. + */ + for( k = first; k <= last; k++ ) { + /* Where we're at in the [0,1] range. + */ + double ki = (double) k / (lut_size - 1); + + double irange, orange; + int j; + + /* Search for the lowest real value < ki. There may + * not be one: if not, just use 0. Tiny error. + */ + for( j = ysize - 1; j >= 0; j-- ) + if( data[j][i + 1] < ki ) + break; + if( j == -1 ) + j = 0; + + /* Interpolate k as being between row data[j] and row + * data[j + 1]. + */ + irange = data[j + 1][i + 1] - data[j][i + 1]; + orange = data[j + 1][0] - data[j][0]; + + odata[i + k * bands] = data[j][0] + + orange * ((ki - data[j][i + 1]) / irange); + } + } + + return( 0 ); +} + +int +im_invertlut( DOUBLEMASK *input, IMAGE *output, int lut_size ) +{ + State state; + + if( !input || input->xsize < 2 || input->ysize < 1 ) { + im_errormsg( "im_invertlut: bad input matrix" ); + return( -1 ); + } + if( lut_size < 1 || lut_size > 65536 ) { + im_errormsg( "im_invertlut: bad lut_size" ); + return( -1 ); + } + + im_initdesc( output, + lut_size, 1, input->xsize - 1, + IM_BBITS_DOUBLE, IM_BANDFMT_DOUBLE, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + if( im_setupout( output ) ) + return( -1 ); + + if( build_state( &state, input, output, lut_size ) || + invertlut( &state ) ) { + free_state( &state ); + return( -1 ); + } + free_state( &state ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_lhisteq.c b/libsrc/histograms_lut/im_lhisteq.c new file mode 100644 index 00000000..8cab4c9e --- /dev/null +++ b/libsrc/histograms_lut/im_lhisteq.c @@ -0,0 +1,212 @@ +/* @(#) Performs local histogram equalisation on an image using a + * @(#) window of size xw by yw + * @(#) Works only on monochrome images + * @(#) + * @(#) int im_lhisteq(in, out, xwin, ywin) + * @(#) IMAGE *in, *out; + * @(#) int xwin, ywin; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * + * Copyright: 1991, N. Dessipris + * + * Author: N. Dessipris + * Written on: 24/10/1991 + * Modified on : + * 25/1/96 JC + * - rewritten, adapting im_spcor() + * - correct result, 2x faster, partial, simpler, better arg checking + * 8/7/04 + * - expand input rather than output with new im_embed() mode + * - _raw() output is one pixel larger + * - sets Xoffset/Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Hold global stuff here. + */ +typedef struct { + int xwin, ywin; /* Parameters */ + + int npels; /* Pels in window */ +} LhistInfo; + +/* lhist generate function. + */ +static int +lhist_gen( REGION *or, REGION *ir, IMAGE *in, LhistInfo *inf ) +{ + Rect irect; + + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int ri = IM_RECT_RIGHT(r); + + int x, y, i, j; + int lsk; + + int coff; /* Offset to move to centre of window */ + + /* What part of ir do we need? + */ + irect.left = or->valid.left; + irect.top = or->valid.top; + irect.width = or->valid.width + inf->xwin; + irect.height = or->valid.height + inf->ywin; + + if( im_prepare( ir, &irect ) ) + return( -1 ); + lsk = IM_REGION_LSKIP( ir ); + coff = lsk * (inf->ywin/2) + inf->xwin/2; + + for( y = to; y < bo; y++ ) { + /* Get input and output pointers for this line. + */ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + PEL *p1, *p2; + int hist[ 256 ]; + + /* Find histogram for start of this line. + */ + memset( hist, 0, 256 * sizeof(int) ); + for( p1 = p, j = 0; j < inf->ywin; j++, p1 += lsk ) + for( p2 = p1, i = 0; i < inf->xwin; i++, p2++ ) + hist[*p2]++; + + /* Loop for output pels. + */ + for( x = le; x < ri; x++, p++ ) { + /* Sum histogram up to current pel. + */ + int target = p[coff]; + int sum = 0; + + for( sum = 0, i = 0; i < target; i++ ) + sum += hist[i]; + + /* Transform. + */ + *q++ = sum * 256 / inf->npels; + + /* Adapt histogram - remove the pels from the left hand + * column, add in pels for a new right-hand column. + */ + for( p1 = p, j = 0; j < inf->ywin; j++, p1 += lsk ) { + hist[p1[0]]--; + hist[p1[inf->xwin]]++; + } + } + } + + return( 0 ); +} + +int +im_lhisteq_raw( IMAGE *in, IMAGE *out, int xwin, int ywin ) +{ + LhistInfo *inf; + + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Bbits != IM_BBITS_BYTE || in->BandFmt != IM_BANDFMT_UCHAR || + in->Bands != 1 || in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_lhisteq: one band uchar uncoded only" ); + return( -1 ); + } + if( xwin > in->Xsize || ywin > in->Ysize ) { + im_errormsg( "im_lhisteq: window too large" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= xwin - 1; + out->Ysize -= ywin - 1; + + /* Save parameters. + */ + if( !(inf = IM_NEW( out, LhistInfo )) ) + return( -1 ); + inf->xwin = xwin; + inf->ywin = ywin; + inf->npels = xwin * ywin; + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Write the hist. + */ + if( im_generate( out, + im_start_one, lhist_gen, im_stop_one, in, inf ) ) + return( -1 ); + + out->Xoffset = -xwin / 2; + out->Yoffset = -xwin / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_lhisteq( IMAGE *in, IMAGE *out, int xwin, int ywin ) +{ + IMAGE *t1 = im_open_local( out, "im_lhisteq:1", "p" ); + + if( !t1 || + im_embed( in, t1, 1, + xwin / 2, ywin / 2, + in->Xsize + xwin - 1, in->Ysize + ywin - 1 ) || + im_lhisteq_raw( t1, out, xwin, ywin ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_maplut.c b/libsrc/histograms_lut/im_maplut.c new file mode 100644 index 00000000..8cdc3d22 --- /dev/null +++ b/libsrc/histograms_lut/im_maplut.c @@ -0,0 +1,623 @@ +/* @(#) Map an image through another image, acting as a LUT (Look Up Table). + * @(#) The lut may have any type, and the output image will be that type. + * @(#) + * @(#) The input image must be an unsigned integer types, that is, it must + * @(#) be one of IM_BANDFMT_UCHAR, IM_BANDFMT_USHORT or IM_BANDFMT_UINT. + * @(#) + * @(#) If the input is IM_BANDFMT_UCHAR, then the LUT must have 256 elements, + * @(#) in other words, lut->Xsize * lut->Ysize == 256. + * @(#) + * @(#) If the input is IM_BANDFMT_USHORT or IM_BANDFMT_UINT, then the lut + * @(#) may have any number of elements, and input pels whose value is + * @(#) greater than lut->Xsize * lut->Ysize are mapped with the last LUT + * @(#) element. + * @(#) + * @(#) If LUT has one band, then all bands of input pass through it. If LUT + * @(#) has same number of bands as input, then each band is LUTed + * @(#) separately. If input has one band, then LUT may have many bands and + * @(#) the output will have the same number of bands as the LUT. + * @(#) + * @(#) int + * @(#) im_maplut( in, out, lut ) + * @(#) IMAGE *in, *out, *lut; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Modified: + * 18/6/93 JC + * - oops! im_incheck() added for LUT image + * - some ANSIfication + * 15/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * - now does complex LUTs too + * 10/3/94 JC + * - more helpful error messages, slight reformatting + * 24/8/94 JC + * - now allows non-uchar image input + * 7/10/94 JC + * - uses im_malloc(), IM_NEW() etc. + * 13/3/95 JC + * - now takes a private copy of LUT, so user can im_close() LUT image + * after im_maplut() without fear of coredumps + * 23/6/95 JC + * - lut may now have many bands if image has just one band + * 3/3/01 JC + * - small speed ups + * 30/6/04 + * - heh, 1 band image + 3 band lut + >8bit output has been broken for 9 + * years :-) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Struct we carry for LUT operations. + */ +typedef struct { + int fmt; /* LUT image BandFmt */ + int nb; /* Number of bands in lut */ + int es; /* IM_IMAGE_SIZEOF_ELEMENT() for lut image */ + int sz; /* Number of elements in minor dimension */ + int clp; /* Value we clip against */ + PEL **table; /* Lut converted to 2d array */ + int overflow; /* Number of overflows for non-uchar lut */ +} LutInfo; + +/* Print overflows, if any. + */ +static int +end_lut( LutInfo *st ) +{ + if( st->overflow ) { + im_warn( "im_maplut", _( "%d overflows detected" ), + st->overflow ); + st->overflow = 0; + } + + return( 0 ); +} + +/* Build a lut table. + */ +static LutInfo * +build_luts( IMAGE *out, IMAGE *lut ) +{ + LutInfo *st = IM_NEW( out, LutInfo ); + int i, x; + PEL *q; + + if( !st ) + return( NULL ); + + /* Make luts. We unpack the LUT image into a C 2D array to speed + * processing. + */ + st->fmt = lut->BandFmt; + st->es = IM_IMAGE_SIZEOF_ELEMENT( lut ); + st->nb = lut->Bands; + st->sz = lut->Xsize * lut->Ysize; + st->clp = st->sz - 1; + st->overflow = 0; + st->table = NULL; + if( im_add_evalend_callback( out, + (im_callback_fn) end_lut, st, NULL ) ) + return( NULL ); + + /* Attach tables. + */ + if( !(st->table = IM_ARRAY( out, lut->Bands, PEL * )) ) + return( NULL ); + for( i = 0; i < lut->Bands; i++ ) + if( !(st->table[i] = IM_ARRAY( out, st->sz * st->es, PEL )) ) + return( NULL ); + + /* Scan LUT and fill table. + */ + q = (PEL *) lut->data; + for( x = 0; x < st->sz; x++ ) + for( i = 0; i < st->nb; i++ ) { + memcpy( st->table[i] + x * st->es, q, st->es ); + q += st->es; + } + + return( st ); +} + +/* Our sequence value: the region this sequence is using, and local stats. + */ +typedef struct { + REGION *ir; /* Input region */ + int overflow; /* Number of overflows */ +} Seq; + +/* Destroy a sequence value. + */ +static int +stop_maplut( Seq *seq, IMAGE *in, LutInfo *st ) +{ + /* Add to global stats. + */ + st->overflow += seq->overflow; + + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Our start function. + */ +static void * +start_maplut( IMAGE *out, IMAGE *in, LutInfo *st ) +{ + Seq *seq = IM_NEW( out, Seq ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->overflow = 0; + + if( !(seq->ir = im_region_create( in )) ) + return( NULL ); + + return( (void *) seq ); +} + +/* Map through n non-complex luts. + */ +#define loop(OUT) { \ + int b = st->nb; \ + \ + for( y = to; y < bo; y++ ) { \ + for( z = 0; z < b; z++ ) { \ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + OUT *tlut = (OUT *) st->table[z]; \ + \ + for( x = z; x < ne; x += b ) \ + q[x] = tlut[p[x]]; \ + } \ + } \ +} + +/* Map through n complex luts. + */ +#define loopc(OUT) { \ + int b = in->Bands; \ + \ + for( y = to; y < bo; y++ ) { \ + for( z = 0; z < b; z++ ) { \ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ) + z; \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ) + z*2; \ + OUT *tlut = (OUT *) st->table[z]; \ + \ + for( x = 0; x < ne; x += b ) { \ + int n = p[x]*2; \ + \ + q[0] = tlut[n]; \ + q[1] = tlut[n + 1]; \ + q += b*2; \ + } \ + } \ + } \ +} + +#define loopg(IN,OUT) { \ + int b = st->nb; \ + \ + for( y = to; y < bo; y++ ) { \ + for( z = 0; z < b; z++ ) { \ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + OUT *tlut = (OUT *) st->table[z]; \ + \ + for( x = z; x < ne; x += b ) { \ + int index = p[x]; \ + \ + if( index > st->clp ) { \ + index = st->clp; \ + seq->overflow++; \ + } \ + \ + q[x] = tlut[index]; \ + } \ + } \ + } \ +} + +#define loopcg(IN,OUT) { \ + int b = in->Bands; \ + \ + for( y = to; y < bo; y++ ) { \ + for( z = 0; z < b; z++ ) { \ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y ) + z; \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ) + z*2; \ + OUT *tlut = (OUT *) st->table[z]; \ + \ + for( x = 0; x < ne; x += b ) { \ + int index = p[x]; \ + \ + if( index > st->clp ) { \ + index = st->clp; \ + seq->overflow++; \ + } \ + \ + q[0] = tlut[ index*2 ]; \ + q[1] = tlut[ index*2+1 ]; \ + \ + q += b*2; \ + } \ + } \ + } \ +} + +/* Map image through one non-complex lut. + */ +#define loop1(OUT) { \ + OUT *tlut = (OUT *) st->table[0]; \ + \ + for( y = to; y < bo; y++ ) { \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < ne; x++ ) \ + q[x] = tlut[p[x]]; \ + } \ +} + +/* Map image through one complex lut. + */ +#define loop1c(OUT) { \ + OUT *tlut = (OUT *) st->table[0]; \ + \ + for( y = to; y < bo; y++ ) { \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < ne; x++ ) { \ + int n = p[x] * 2; \ + \ + q[0] = tlut[n]; \ + q[1] = tlut[n + 1]; \ + q += 2; \ + } \ + } \ +} + +/* As above, but the input image may be any unsigned integer type. We have to + * index the lut carefully, and record the number of overflows we detect. + */ +#define loop1g(IN,OUT) { \ + OUT *tlut = (OUT *) st->table[0]; \ + \ + for( y = to; y < bo; y++ ) { \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < ne; x++ ) { \ + int index = p[x]; \ + \ + if( index > st->clp ) { \ + index = st->clp; \ + seq->overflow++; \ + } \ + \ + q[x] = tlut[index]; \ + } \ + } \ +} + +#define loop1cg(IN,OUT) { \ + OUT *tlut = (OUT *) st->table[0]; \ + \ + for( y = to; y < bo; y++ ) { \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < ne; x++ ) { \ + int index = p[x]; \ + \ + if( index > st->clp ) { \ + index = st->clp; \ + seq->overflow++; \ + } \ + \ + q[0] = tlut[index*2]; \ + q[1] = tlut[index*2 + 1]; \ + q += 2; \ + } \ + } \ +} + +/* Map 1-band image through a many-band non-complex lut. + */ +#define loop1m(OUT) { \ + OUT **tlut = (OUT **) st->table; \ + \ + for( y = to; y < bo; y++ ) { \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( i = 0, x = 0; x < np; x++ ) { \ + int n = p[x]; \ + \ + for( z = 0; z < st->nb; z++, i++ ) \ + q[i] = tlut[z][n]; \ + } \ + } \ +} + +/* Map 1-band image through many-band complex lut. + */ +#define loop1cm(OUT) { \ + OUT **tlut = (OUT **) st->table; \ + \ + for( y = to; y < bo; y++ ) { \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < np; x++ ) { \ + int n = p[x] * 2; \ + \ + for( z = 0; z < st->nb; z++ ) { \ + q[0] = tlut[z][n]; \ + q[1] = tlut[z][n+1]; \ + q += 2; \ + } \ + } \ + } \ +} + +/* Map 1-band uint or ushort image through a many-band non-complex LUT. + */ +#define loop1gm(IN,OUT) { \ + OUT **tlut = (OUT **) st->table; \ + \ + for( y = to; y < bo; y++ ) { \ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + \ + for( i = 0, x = 0; x < np; x++ ) { \ + int n = p[x]; \ + \ + if( n > st->clp ) { \ + n = st->clp; \ + seq->overflow++; \ + } \ + \ + for( z = 0; z < st->nb; z++, i++ ) \ + q[i] = tlut[z][n]; \ + } \ + } \ +} + +/* Map 1-band uint or ushort image through a many-band complex LUT. + */ +#define loop1cgm(IN,OUT) { \ + OUT **tlut = (OUT **) st->table; \ + \ + for( y = to; y < bo; y++ ) { \ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = 0; x < np; x++ ) { \ + int n = p[x]; \ + \ + if( n > st->clp ) { \ + n = st->clp; \ + seq->overflow++; \ + } \ + \ + for( z = 0; z < st->nb; z++ ) { \ + q[0] = tlut[z][n*2]; \ + q[1] = tlut[z][n*2 + 1]; \ + q += 2; \ + } \ + } \ + } \ +} + +/* Switch for input types. Has to be uint type! + */ +#define inner_switch( UCHAR, GEN, OUT ) \ + switch( ir->im->BandFmt ) { \ + case IM_BANDFMT_UCHAR: UCHAR( OUT ); break; \ + case IM_BANDFMT_USHORT: GEN( unsigned short, OUT ); break; \ + case IM_BANDFMT_UINT: GEN( unsigned int, OUT ); break; \ + default: \ + im_error( "im_maplut", _( "bad input file" ) ); \ + return( -1 ); \ + } + +/* Switch for LUT types. One function for non-complex images, a + * variant for complex ones. Another pair as well, in case the input is not + * uchar. + */ +#define outer_switch( UCHAR_F, UCHAR_FC, GEN_F, GEN_FC ) \ + switch( st->fmt ) { \ + case IM_BANDFMT_UCHAR: inner_switch( UCHAR_F, GEN_F, \ + unsigned char ); break; \ + case IM_BANDFMT_CHAR: inner_switch( UCHAR_F, GEN_F, \ + char ); break; \ + case IM_BANDFMT_USHORT: inner_switch( UCHAR_F, GEN_F, \ + unsigned short ); break; \ + case IM_BANDFMT_SHORT: inner_switch( UCHAR_F, GEN_F, \ + short ); break; \ + case IM_BANDFMT_UINT: inner_switch( UCHAR_F, GEN_F, \ + unsigned int ); break; \ + case IM_BANDFMT_INT: inner_switch( UCHAR_F, GEN_F, \ + int ); break; \ + case IM_BANDFMT_FLOAT: inner_switch( UCHAR_F, GEN_F, \ + float ); break; \ + case IM_BANDFMT_DOUBLE: inner_switch( UCHAR_F, GEN_F, \ + double ); break; \ + case IM_BANDFMT_COMPLEX: inner_switch( UCHAR_FC, GEN_FC, \ + float ); break; \ + case IM_BANDFMT_DPCOMPLEX: inner_switch( UCHAR_FC, GEN_FC, \ + double ); break; \ + default: \ + im_error( "im_maplut", _( "bad lut file" ) ); \ + return( -1 ); \ + } + +/* Do a map. + */ +static int +gen_map( REGION *or, Seq *seq, IMAGE *in, LutInfo *st ) +{ + REGION *ir = seq->ir; + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int np = r->width; /* Pels across region */ + int ne = IM_REGION_N_ELEMENTS( or ); /* Number of elements */ + int x, y, z, i; + + /* Get input ready. + */ + if( im_prepare( ir, r ) ) + return( -1 ); + + /* Process! + */ + if( st->nb == 1 ) + /* One band lut. + */ + outer_switch( loop1, loop1c, loop1g, loop1cg ) + else + /* Many band lut. + */ + if( ir->im->Bands == 1 ) + /* ... but 1 band input. + */ + outer_switch( loop1m, loop1cm, loop1gm, loop1cgm ) + else + outer_switch( loop, loopc, loopg, loopcg ) + + return( 0 ); +} + +int +im_maplut( IMAGE *in, IMAGE *out, IMAGE *lut ) +{ + LutInfo *st; + + /* Check lut. + */ + if( lut->Coding != IM_CODING_NONE ) { + im_error( "im_maplut", _( "lut is not uncoded" ) ); + return( -1 ); + } + if( lut->Xsize * lut->Ysize > 100000 ) { + im_error( "im_maplut", _( "lut seems very large!" ) ); + return( -1 ); + } + + /* Check input output. Old-style IO from lut, for simplicity. + */ + if( im_piocheck( in, out ) || im_incheck( lut ) ) + return( -1 ); + + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_maplut", _( "input is not uncoded" ) ); + return( -1 ); + } + if( !im_isuint( in ) ) { + im_error( "im_maplut", _( "input is not some unsigned " + "integer type" ) ); + return( -1 ); + } + if( in->Bands != 1 && lut->Bands != 1 && lut->Bands != in->Bands ) { + im_error( "im_maplut", _( "lut should have 1 band, or same " + "number of bands as input, or any number of bands " + "if input has 1 band" ) ); + return( -1 ); + } + if( in->BandFmt == IM_BANDFMT_UCHAR && + lut->Xsize * lut->Ysize != 256 ) { + im_error( "im_maplut", _( "input is uchar and lut does not " + "have 256 elements" ) ); + return( -1 ); + } + + /* Prepare the output header. + */ + if( im_cp_descv( out, in, lut, NULL ) ) + return( -1 ); + + /* Force output to be the same type as lut. + */ + out->Bbits = lut->Bbits; + out->BandFmt = lut->BandFmt; + + /* Output has same number of bands as LUT, unless LUT has 1 band, in + * which case output has same number of bands as input. + */ + if( lut->Bands != 1 ) + out->Bands = lut->Bands; + + /* Make tables. + */ + if( !(st = build_luts( out, lut )) ) + return( -1 ); + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Process! + */ + if( im_generate( out, start_maplut, gen_map, stop_maplut, in, st ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_project.c b/libsrc/histograms_lut/im_project.c new file mode 100644 index 00000000..ecda7aea --- /dev/null +++ b/libsrc/histograms_lut/im_project.c @@ -0,0 +1,299 @@ +/* @(#) Find the horizontal and vertical projections of an image, ie. the sum + * @(#) of pixels in each row and column. Two output images, 1xheight and + * @(#) widthx1, with the largest required bandfmt. + * @(#) + * @(#) int im_project( in, columns, rows ) + * @(#) IMAGE *in; + * @(#) IMAGE *columns, *rows; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * 20/4/06 + * - from im_histgr() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Accumulate a projection in one of these. + */ +typedef struct { + IMAGE *in; + IMAGE *hout; + IMAGE *vout; + void *columns; + void *rows; +} Project; + +/* For each input bandfmt, the type we accumulate pixels in. + */ +static int project_type[] = { + IM_BANDFMT_UINT, /* IM_BANDFMT_UCHAR */ + IM_BANDFMT_INT, /* IM_BANDFMT_CHAR */ + IM_BANDFMT_UINT, /* IM_BANDFMT_USHORT */ + IM_BANDFMT_INT, /* IM_BANDFMT_SHORT */ + IM_BANDFMT_UINT, /* IM_BANDFMT_UINT */ + IM_BANDFMT_INT, /* IM_BANDFMT_INT */ + IM_BANDFMT_DOUBLE, /* IM_BANDFMT_FLOAT */ + IM_BANDFMT_NOTSET, /* IM_BANDFMT_COMPLEX */ + IM_BANDFMT_DOUBLE, /* IM_BANDFMT_DOUBLE */ + IM_BANDFMT_NOTSET /* IM_BANDFMT_DPCOMPLEX */ +}; + +static Project * +project_new( IMAGE *in, IMAGE *hout, IMAGE *vout ) +{ + Project *project; + int psize = IM_IMAGE_SIZEOF_PEL( hout ); + + if( !(project = IM_NEW( hout, Project )) ) + return( NULL ); + project->in = in; + project->hout = hout; + project->vout = vout; + project->columns = IM_ARRAY( hout, psize * in->Xsize, guchar ); + project->rows = IM_ARRAY( hout, psize * in->Ysize, guchar ); + if( !project->columns || !project->rows ) + return( NULL ); + + memset( project->columns, 0, psize * in->Xsize ); + memset( project->rows, 0, psize * in->Ysize ); + + return( project ); +} + +/* Build a sub-project, based on the main project. + */ +static void * +project_new_sub( IMAGE *out, Project *mproject ) +{ + return( (void *) + project_new( mproject->in, mproject->hout, mproject->vout ) ); +} + +#define ADD_BUFFER( TYPE, Q, P, N ) { \ + TYPE *p = (TYPE *) (P); \ + TYPE *q = (TYPE *) (Q); \ + int n = (N); \ + int i; \ + \ + for( i = 0; i < n; i++ ) \ + q[i] += p[i]; \ +} + +/* Join a sub-project onto the main project. + */ +static int +project_merge( Project *sproject, Project *mproject ) +{ + IMAGE *in = mproject->in; + IMAGE *out = mproject->hout; + int hsz = in->Xsize * in->Bands; + int vsz = in->Ysize * in->Bands; + + assert( sproject->hout == mproject->hout ); + assert( sproject->vout == mproject->vout ); + + /* Add on sub-data. + */ + switch( out->BandFmt ) { + case IM_BANDFMT_UINT: + ADD_BUFFER( guint, mproject->columns, sproject->columns, hsz ); + ADD_BUFFER( guint, mproject->rows, sproject->rows, vsz ); + break; + + case IM_BANDFMT_INT: + ADD_BUFFER( int, mproject->columns, sproject->columns, hsz ); + ADD_BUFFER( int, mproject->rows, sproject->rows, vsz ); + break; + + case IM_BANDFMT_DOUBLE: + ADD_BUFFER( double, mproject->columns, sproject->columns, hsz ); + ADD_BUFFER( double, mproject->rows, sproject->rows, vsz ); + break; + + default: + assert( 0 ); + } + + /* Blank out sub-project to make sure we can't add it again. + */ + memset( sproject->columns, 0, IM_IMAGE_SIZEOF_ELEMENT( out ) * hsz ); + memset( sproject->rows, 0, IM_IMAGE_SIZEOF_ELEMENT( out ) * vsz ); + + return( 0 ); +} + +/* Add an area of pixels. + */ +#define ADD_PIXELS( OUTTYPE, INTYPE ) { \ + OUTTYPE *rows; \ + OUTTYPE *columns; \ + INTYPE *p; \ + \ + rows = ((OUTTYPE *) project->rows) + to * nb; \ + for( y = 0; y < r->height; y++ ) { \ + columns = ((OUTTYPE *) project->columns) + le * nb; \ + p = (INTYPE *) IM_REGION_ADDR( reg, le, y + to ); \ + \ + for( x = 0; x < r->width; x++ ) { \ + for( z = 0; z < nb; z++ ) { \ + columns[z] += p[z]; \ + rows[z] += p[z]; \ + } \ + \ + p += nb; \ + columns += nb; \ + } \ + \ + rows += nb; \ + } \ +} + +/* Add a region to a project. + */ +static int +project_scan( REGION *reg, Project *project ) +{ + Rect *r = ®->valid; + int le = r->left; + int to = r->top; + int nb = project->in->Bands; + int x, y, z; + + switch( project->in->BandFmt ) { + case IM_BANDFMT_UCHAR: + ADD_PIXELS( guint, guchar ); + break; + + case IM_BANDFMT_CHAR: + ADD_PIXELS( int, char ); + break; + + case IM_BANDFMT_USHORT: + ADD_PIXELS( guint, gushort ); + break; + + case IM_BANDFMT_SHORT: + ADD_PIXELS( int, short ); + break; + + case IM_BANDFMT_UINT: + ADD_PIXELS( guint, guint ); + break; + + case IM_BANDFMT_INT: + ADD_PIXELS( int, int ); + break; + + case IM_BANDFMT_FLOAT: + ADD_PIXELS( double, float ); + break; + + case IM_BANDFMT_DOUBLE: + ADD_PIXELS( double, double ); + break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +int +im_project( IMAGE *in, IMAGE *hout, IMAGE *vout ) +{ + Project *mproject; + int y; + + /* Check images. PIO from in, WIO to out. + */ + if( im_pincheck( in ) || im_outcheck( hout ) || im_outcheck( vout ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_project", _( "uncoded images only" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_project", _( "non-complex images only" ) ); + return( -1 ); + } + + /* Make the output images. + */ + if( im_cp_desc( hout, in ) || im_cp_desc( vout, in ) ) + return( -1 ); + + hout->Xsize = 1; + hout->BandFmt = project_type[in->BandFmt]; + hout->Bbits = im_bits_of_fmt( hout->BandFmt ); + hout->Type = IM_TYPE_HISTOGRAM; + + vout->Ysize = 1; + vout->BandFmt = project_type[in->BandFmt]; + vout->Bbits = im_bits_of_fmt( hout->BandFmt ); + vout->Type = IM_TYPE_HISTOGRAM; + + /* Build the main project we accumulate data in. + */ + if( !(mproject = project_new( in, hout, vout )) ) + return( -1 ); + + /* Accumulate data. + */ + if( im_iterate( in, + project_new_sub, project_scan, project_merge, mproject, NULL ) ) + return( -1 ); + + if( im_setupout( hout ) || im_setupout( vout ) ) + return( -1 ); + + if( im_writeline( 0, vout, (PEL *) mproject->columns ) ) + return( -1 ); + for( y = 0; y < in->Ysize; y++ ) + if( im_writeline( y, hout, (PEL *) mproject->rows + + y * IM_IMAGE_SIZEOF_PEL( hout ) ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_stdif.c b/libsrc/histograms_lut/im_stdif.c new file mode 100644 index 00000000..7450e856 --- /dev/null +++ b/libsrc/histograms_lut/im_stdif.c @@ -0,0 +1,260 @@ +/* @(#) Functions which calculates statistical differenciating according to + * @(#) the formula given in page 45 of the book "An intro to digital image + * @(#) processing" by Wayne Niblack + * @(#) + * @(#) At point (i,j) the output is given by the eqn: + * @(#) + * @(#) vout(i,j) = a*m0 +(1-a)*meanv + + * @(#) (vin(i,j) - meanv) * beta*sigma0/(sigma0+beta*stdv) + * @(#) + * @(#) Values a, m0, beta and sigma0 are entered + * @(#) meanv and stdv are the values calculated over a moving window + * @(#) xwin and ywin are the sizes of the used window + * @(#) The resultant coefficients are written as floats + * @(#) in out which has a size of in + * @(#) + * @(#) int im_stdif(in, im, alpha, mean0, beta, sigma0, xwin, ywin) + * @(#) IMAGE *in, *out; + * @(#) int xwin, ywin; + * @(#) double alpha, mean0, beta, sigma0; + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on : + * 6/8/93 JC + * - now works for odd window sizes + * - ANSIfication + * 25/5/95 JC + * - new IM_ARRAY() macro + * 25/1/96 JC + * - im_lhisteq() adapted to make new im_stdif() + * - now partial, plus rolling window + * - 5x faster, amazingly + * - works + * 7/4/04 + * - now uses im_embed() with edge stretching on the input, not + * the output + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Hold global stuff here. + */ +typedef struct { + int xwin, ywin; /* Parameters */ + double a, m0, b, s0; +} StdifInfo; + +/* stdif generate function. + */ +static int +stdif_gen( REGION *or, REGION *ir, IMAGE *in, StdifInfo *inf ) +{ + Rect irect; + + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int ri = IM_RECT_RIGHT(r); + + int x, y, i, j; + int lsk; + + int coff; /* Offset to move to centre of window */ + int npel = inf->xwin * inf->ywin; + + /* What part of ir do we need? + */ + irect.left = or->valid.left; + irect.top = or->valid.top; + irect.width = or->valid.width + inf->xwin; + irect.height = or->valid.height + inf->ywin; + if( im_prepare( ir, &irect ) ) + return( -1 ); + + lsk = IM_REGION_LSKIP( ir ); + coff = lsk * (inf->ywin/2) + inf->xwin/2; + + for( y = to; y < bo; y++ ) { + /* Get input and output pointers for this line. + */ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + PEL *p1, *p2; + int sum = 0; + int sum2 = 0; + + /* Precompute some factors. + */ + double f1 = inf->a * inf->m0; + double f2 = 1.0 - inf->a; + double f3 = inf->b * inf->s0; + + /* Find sum, sum of squares for the start of this line. + */ + for( p1 = p, j = 0; j < inf->ywin; j++, p1 += lsk ) + for( p2 = p1, i = 0; i < inf->xwin; i++, p2++ ) { + int t = *p2; + + sum += t; + sum2 += t * t; + } + + /* Loop for output pels. + */ + for( x = le; x < ri; x++, p++ ) { + /* Find stats. + */ + double mean = (double)sum / npel; + double var = (double)sum2 / npel - (mean * mean); + double sig = sqrt( var ); + + /* Transform. + */ + double res = f1 + f2*mean + ((double) p[coff] - mean) * + (f3 / (inf->s0 + inf->b*sig)); + + /* And write. + */ + if( res < 0.0 ) + *q++ = 0; + else if( res >= 256.0 ) + *q++ = 255; + else + *q++ = res + 0.5; + + /* Adapt sums - remove the pels from the left hand + * column, add in pels for a new right-hand column. + */ + for( p1 = p, j = 0; j < inf->ywin; j++, p1 += lsk ) { + int t1 = p1[0]; + int t2 = p1[inf->xwin]; + + sum -= t1; + sum2 -= t1 * t1; + + sum += t2; + sum2 += t2 * t2; + } + } + } + + return( 0 ); +} + +int +im_stdif_raw( IMAGE *in, IMAGE *out, + double a, double m0, double b, double s0, + int xwin, int ywin ) +{ + StdifInfo *inf; + + if( m0 < 0 || m0 > 255 || a < 0 || a > 1.0 || b < 0 || b > 2 || + s0 < 0 || s0 > 255 ) { + im_errormsg( "im_stdif: parameters out of range" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Bbits != IM_BBITS_BYTE || in->BandFmt != IM_BANDFMT_UCHAR || + in->Bands != 1 || in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_stdif: one band uchar uncoded only" ); + return( -1 ); + } + if( xwin > in->Xsize || ywin > in->Ysize ) { + im_errormsg( "im_stdif: window too large" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= xwin; + out->Ysize -= ywin; + + /* Save parameters. + */ + if( !(inf = IM_NEW( out, StdifInfo )) ) + return( -1 ); + inf->xwin = xwin; + inf->ywin = ywin; + inf->a = a; + inf->m0 = m0; + inf->b = b; + inf->s0 = s0; + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Write the hist. + */ + if( im_generate( out, + im_start_one, stdif_gen, im_stop_one, in, inf ) ) + return( -1 ); + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_stdif( IMAGE *in, IMAGE *out, + double a, double m0, double b, double s0, + int xwin, int ywin ) +{ + IMAGE *t1 = im_open_local( out, "im_stdif:1", "p" ); + + if( !t1 || + im_embed( in, t1, 1, xwin / 2, ywin / 2, + in->Xsize + xwin - 1, + in->Ysize + ywin - 1 ) || + im_stdif_raw( t1, out, a, m0, b, s0, xwin, ywin ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/man3/Makefile.am b/libsrc/histograms_lut/man3/Makefile.am new file mode 100644 index 00000000..4310d6f3 --- /dev/null +++ b/libsrc/histograms_lut/man3/Makefile.am @@ -0,0 +1,27 @@ +man_MANS = \ + im_gammacorrect.3 \ + im_heq.3 \ + im_hist.3 \ + im_histeq.3 \ + im_histcum.3 \ + im_histnorm.3 \ + im_histgr.3 \ + im_histnD.3 \ + im_histplot.3 \ + im_histspec.3 \ + im_hsp.3 \ + im_identity.3 \ + im_identity_ushort.3 \ + im_invertlut.3 \ + im_buildlut.3 \ + im_lhisteq.3 \ + im_lhisteq_raw.3 \ + im_maplut.3 \ + im_project.3 \ + im_stdif.3 \ + im_tone_analyse.3 \ + im_tone_build.3 \ + im_tone_map.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/histograms_lut/man3/im_buildlut.3 b/libsrc/histograms_lut/man3/im_buildlut.3 new file mode 100644 index 00000000..22809486 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_buildlut.3 @@ -0,0 +1,38 @@ +.TH IM_BUILDLUT 3 "June 2006" +.SH NAME +im_buildlut \- build a LUT from a set of x/y points +.SH SYNOPSIS +#include + +int +.br +im_buildlut( DOUBLEMASK *input, IMAGE *output ) + +.SH DESCRIPTION +.B im_buildlut(3) +constructs a LUT, interpolating a set of x/y points. Interpolation is strictly +piecewise linear. For example, if the input is: + + 12 100 + 14 110 + 18 120 + +we generate + + 100 (12) + 105 + 110 + 112.5 + 115 + 117.5 (17) + +the x axis (12 .. 17) is implied. The x/y points don't need to be +sorted: we do that. You can have several Ys ... each becomes a band in the +output LUT. + +.SH RETURN VALUE +-1 on error, otherwise 0 +.SH SEE ALSO +im_invertlut(3), im_identity(3). +.SH COPYRIGHT +2006, Imperial College diff --git a/libsrc/histograms_lut/man3/im_gammacorrect.3 b/libsrc/histograms_lut/man3/im_gammacorrect.3 new file mode 100644 index 00000000..ca4a8fa5 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_gammacorrect.3 @@ -0,0 +1,24 @@ +.TH GAMMA 3 "10 May 1991" +.SH NAME +im_gammacorrect \- carry out gamma correction +.SH SYNOPSIS +#include + +int im_gammacorrect(in, out, exponent) +.br +IMAGE *in, *out; +.br +double exponent; + +.SH DESCRIPTION +im_gammacorrect performs gamma correction to in. The result in written in out. +The correction is carried out by creating a lookup table using the double +exponent and mapping in through it. The exponent is applied on a ramp lut and +the resultant lut is scaled. All channels of im are mapped through the lookup +table. +.SH SEE ALSO +im_histgr(3), im_heq(3), im_histeq(3), im_identity(3), im_maplut(3) +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/histograms_lut/man3/im_heq.3 b/libsrc/histograms_lut/man3/im_heq.3 new file mode 100644 index 00000000..4715cd8d --- /dev/null +++ b/libsrc/histograms_lut/man3/im_heq.3 @@ -0,0 +1,53 @@ +.TH IM_HEQ 3 "10 May 1991" +.SH NAME +im_heq, im_lhisteq, im_lhisteq_raw, im_hsp \- process an image using grey level transformations +.SH SYNOPSIS +#include + +int im_heq( in, out, bandno ) +.br +IMAGE *in, *out; +.br +int bandno; + +int im_lhisteq( in, out, xw, yw ) +.br +IMAGE *in, *out; +.br +int xw, yw; + +int im_hsp( in, ref, out ) +.br +IMAGE *in, *ref, *out; + +.SH DESCRIPTION +im_heq() +histogram equalises the unsigned char image held by the IMAGE descriptor +in. The result is written to the IMAGE descriptor out. +If bandno is -1 then all input bands are equalised independently. In all +other cases the input image is equalised using the histogram of bandno only. +The latter processing produces better results. + +im_hsp() +maps in to out with histogram specified by the ref. All images should be +unsigned char. Each band of the output image is specified according to the +distribution of grey levels of the reference image according to +im_histspec(3). + +im_lhisteq() +histogram equalises the one channel unsigned char image pointed to by the +Image descriptor in. The result is written to the IMAGE descriptor out. +The histogram equalisation is based on a window of size xw by yw centered at +the current location of each input pixel. The produced image has a black +border of sizes xw/2 (left), xw-xw/2 (right), yw/2 (top) and yw-yw/2 (bottom). + +im_lhisteq_raw() +is as above, but does not add the black border to the output image. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_histgr(3), im_histplot(3), im_histspec(3), im_lineprof(3), +im_stdif(3). +.SH COPYRIGHT +1991--1996 The National Gallery and Birkbeck College diff --git a/libsrc/histograms_lut/man3/im_hist.3 b/libsrc/histograms_lut/man3/im_hist.3 new file mode 100644 index 00000000..32170a06 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_hist.3 @@ -0,0 +1,36 @@ +.TH IM_HIST 3 "10 May 1991" +.SH NAME +im_hist \- create a displayable histogram from an image +.SH SYNOPSIS +.B #include + +.B int im_hist( in, plotofhist, bandno ) +.br +.B IMAGE *in, *plotofhist; +.br +.B int bandno; +.SH DESCRIPTION +.B im_hist() +creates a displayable histogram file for the image held by the image +descriptor +.B in. +The created displayable histogram is held in the IMAGE +descriptor +.B plotofhist. +If +.B bandno +== -1 a displayable histogram of all input bands +is created else the histogram of bandno only is created. + +See +.B im_histplot(3) +for rules on the size and scaling og the result. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_histgr(3), im_histplot(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/histograms_lut/man3/im_histcum.3 b/libsrc/histograms_lut/man3/im_histcum.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histcum.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_histeq.3 b/libsrc/histograms_lut/man3/im_histeq.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histeq.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_histgr.3 b/libsrc/histograms_lut/man3/im_histgr.3 new file mode 100644 index 00000000..67fb50e5 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histgr.3 @@ -0,0 +1,142 @@ +.TH IM_HISTGR 3 "10 May 1991" +.SH NAME +im_histcum, +im_histeq, +im_histgr, +im_histnD, +im_histnorm, +im_histspec, +im_identity, +im_identity_ushort, +im_ismonotonic +\- create, display and process histograms or luts +.SH SYNOPSIS +#include + +int im_histgr( in, out, bandno ) +.br +IMAGE *in, *out; +.br +int bandno; + +int im_histnD( in, out, bins ) +.br +IMAGE *in, *out; +.br +int bins; + +int im_histcum(hist, lut) +.br +IMAGE *hist, *lut; + +int im_histnorm(hist, lut) +.br +IMAGE *hist, *lut; + +int im_histeq(hist, lut) +.br +IMAGE *hist, *lut; + +int im_histspec(histin, histref, lut) +.br +IMAGE *histin, *histref, *lut; + +int im_identity(lut, bands) +.br +IMAGE *lut; +.br +int bands; + +int im_identity_ushort(lut, bands, sz) +.br +IMAGE *lut; +.br +int bands; +.br +int sz; + +int im_ismonotonic( IMAGE *lut, int *out ) + +.SH DESCRIPTION +.B im_histgr(3) +writes the histogram of image in to image out. If bandno is -1, +then out will have the same number of bands as in, and each band of out will +have the histogram of the corresponding band of in. Otherwise, bandno selects +the band of the image for which the histogram will be found, numbering from +zero. + +Image in may be either FMTUCHAR or FMTUSHORT. If in is uchar, then out will +have 256 elements, one for each possible pixel value. If in is ushort, then +.B im_histgr(3) +finds the maximum of in, and outputs a histogram with max + 1 +elements. + +For example, suppose you have an image from a 12-bit camera, where +each pixel is in the range [0,4095]. Calling +.B im_histgr(3) +for this image will +make a histogram with at most 4096 elements. If the histogram is smaller than +this, then it means that the right hand end of the histogram was all zero, and +has not been generated. + +Also check +.B im_histnD(3) +below for another way to make histograms. + +.B im_histnD(3) +makes a n-dimensional histogram from an n-band image (1, 2 and 3 bands only). +Because 3D histograms can get very large very quickly, the +.B bins +parameter sets the length of each dimension, that is, the number of bins the +possible numeric range of the image is divided into. + +Unsigned 8 and 16 bit images only. + +Use +.B im_histplot(3) +to graph the histogram for visualisation. See the separate manpage. + +.B im_histcum(3) +forms a cumulative histogram. It works for any unsigned integer format. + +.B im_histnorm(3) +normalises a histogram. The maximum histogram value becomes equal to the +number of pixels in the histogram. In effect, the histogram +becomes 'square'. Each channel is normalised separately. + +.B im_histeq(3) +takes as input a histogram held by the IMAGE descriptor hist and creates an +unsigned char look up table (held by the IMAGE descriptor lut) which when +applied on the original image, (with histogram held by hist) histogram +equalises it. Histogram equalisation is carried out for each band of hist +individually. + +.B im_histspec(3) +creates a lut for transforming an image with histogram held by histin +according to the pdf (probability density function) of a reference image with +histogram held by histref. histin and histref should have the same number +of bands. + +.B im_identity(3) +creates an look-up-table with Xsize=256, Ysize=1, Bands=bands, Bbits=BBBYTE, +BandFmt=FMTUCHAR, Type=LUT, which is held by the IMAGE descriptor lut. The +created image consist a bands linear (ramp) lut and is the basis for building +look-up tables. + +.B im_identity_ushort(3) +creates an look-up-table with Xsize=sz, Ysize=1, Bands=bands, Bbits=BBSHORT, +BandFmt=FMTUSHORT, Type=LUT, which is held by the IMAGE descriptor lut. The +created image consist of a linear (ramp) lut and is the basis for building +look-up tables. + +.B im_ismonotonic(3) +sets out to non-zero if the look-up table (or histogram) in lut is monotonic, +that is, if it's slope is always >0. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_hist(3), im_hsp(3), im_heq(3), im_maplut(3), im_tone_map(3), +im_histplot(3). +.SH COPYRIGHT +The National Gallery and Birkbeck College, 1991 \- 1994. diff --git a/libsrc/histograms_lut/man3/im_histnD.3 b/libsrc/histograms_lut/man3/im_histnD.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histnD.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_histnorm.3 b/libsrc/histograms_lut/man3/im_histnorm.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histnorm.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_histplot.3 b/libsrc/histograms_lut/man3/im_histplot.3 new file mode 100644 index 00000000..121995eb --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histplot.3 @@ -0,0 +1,47 @@ +.TH IM_HISTPLOT 3 "10 May 1991" +.SH NAME +im_histplot \- graph an image of one horizontal or vertical line +.SH SYNOPSIS +#include + +int im_histplot(in, out) +.br +IMAGE *in, *out; + +.SH DESCRIPTION +.B im_histplot(3) +plots a graph of a horizontal or vertical image file with +one line. It is suitable for displaying the output of histogram functions +such as im_histgr(3) and for displaying line profiles. + +Note that if you try to directly plot the result of +.B im_histgr(3) +you will often get a very, very large image, since +.B im_histplot(3) +will draw an image as high (or as wide) as the largest pixel +value in the image (potentially huge). Use +.B im_histnorm(3) +to normalise the histogram maximum before plotting. + +The input image should be either one horizontal line (Ysize=1) or +one vertical line (Xsize=1). If the image is FMTUCHAR, the graph is drawn +as 256 by line-length pixels. For all other unsigned integer types, +.B im_histplot(3) +finds the image maximum, and draws the graph as max by line-length pixels. + +For signed integer types, +.B im_histplot(3) +finds minimum and maximum, moves min up to zero, and +draws the graph as min + max by line-lenth pixels. + +For float types, +.B im_histplot(3) +finds minimum and maximum, and scales the image range so as to make the +graph square. + +.SH RETURNED VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_histgr(3), im_histnorm(3), im_extract(3). +.SH COPYRIGHT +National Gallery and Birkbeck College, 1995 diff --git a/libsrc/histograms_lut/man3/im_histspec.3 b/libsrc/histograms_lut/man3/im_histspec.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histspec.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_hsp.3 b/libsrc/histograms_lut/man3/im_hsp.3 new file mode 100644 index 00000000..0d4a60ee --- /dev/null +++ b/libsrc/histograms_lut/man3/im_hsp.3 @@ -0,0 +1 @@ +.so man3/im_heq.3 diff --git a/libsrc/histograms_lut/man3/im_identity.3 b/libsrc/histograms_lut/man3/im_identity.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_identity.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_identity_ushort.3 b/libsrc/histograms_lut/man3/im_identity_ushort.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_identity_ushort.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_invertlut.3 b/libsrc/histograms_lut/man3/im_invertlut.3 new file mode 100644 index 00000000..e1719ca0 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_invertlut.3 @@ -0,0 +1,41 @@ +.TH IM_INVERTLUT 3 "June 2001" +.SH NAME +im_invertlut \- turn a set of greyscale measurements into a gamma-correcting +LUT +.SH SYNOPSIS +#include + +int +.br +im_invertlut( DOUBLEMASK *input, IMAGE *output, int lut_size ) + +.SH DESCRIPTION +Given a mask of target values and real values, generate a LUT which +will map reals to targets. Handy for linearising images from +measurements of a colour chart. All values in [0,1]. +Extrapolate head and tail to 0 and 1. + +Eg. input like: + + 4 3 + 0.1 0.2 0.3 0.1 + 0.2 0.4 0.4 0.2 + 0.7 0.5 0.6 0.3 + +means a patch with 10% reflectance produces an image with 20% in +channel 1, 30% in channel 2, and 10% in channel 3. A patch with 20% +reflectance makes an image with 40% red, 40% green and 20% blue, and so on. + +Inputs don't need to be sorted (we do that). Generate any precision +LUT ... typically ask for 256 elements. + +It won't work too well for non-monotonic camera responses (should fix this). + +Interpolation is simple piecewise linear; ought to do something better really. + +.SH RETURN VALUE +-1 on error, otherwise 0 +.SH SEE ALSO +im_histgr(3), im_hsp(3), im_heq(3), im_identity(3). +.SH COPYRIGHT +2001, National Gallery diff --git a/libsrc/histograms_lut/man3/im_lhisteq.3 b/libsrc/histograms_lut/man3/im_lhisteq.3 new file mode 100644 index 00000000..0d4a60ee --- /dev/null +++ b/libsrc/histograms_lut/man3/im_lhisteq.3 @@ -0,0 +1 @@ +.so man3/im_heq.3 diff --git a/libsrc/histograms_lut/man3/im_lhisteq_raw.3 b/libsrc/histograms_lut/man3/im_lhisteq_raw.3 new file mode 100644 index 00000000..0d4a60ee --- /dev/null +++ b/libsrc/histograms_lut/man3/im_lhisteq_raw.3 @@ -0,0 +1 @@ +.so man3/im_heq.3 diff --git a/libsrc/histograms_lut/man3/im_maplut.3 b/libsrc/histograms_lut/man3/im_maplut.3 new file mode 100644 index 00000000..a5eb245b --- /dev/null +++ b/libsrc/histograms_lut/man3/im_maplut.3 @@ -0,0 +1,46 @@ +.TH IM_MAPLUT 3 "10 May 1991" +.SH NAME +im_maplut \- map an image through a lookup table +.SH SYNOPSIS +#include + +int im_maplut(in, out, lut) +.br +IMAGE *in, *out, *lut; + +.SH DESCRIPTION +im_maplut() maps an image through another image, acting as a LUT (Look Up +Table). The lut may have any type, and the output image will be of that type. + +The input image must be an unsigned integer types, that is, it must be one of +FMTUCHAR, FMTUSHORT or FMTUINT. + +If the input is FMTUCHAR, then the LUT must have 256 elements, in other words, +lut->Xsize * lut->Ysize == 256. + +If the input is FMTUSHORT or FMTUINT, then the lut may have any number of +elements, and input pels whose value is greater than lut->Xsize * lut->Ysize +are mapped with the last LUT element. The function counts and prints the +number of image elements which overflow in this way. + +As regards bands, there are three cases: + + - If LUT has one band, then the input may have any number of bands, and + each band will pass through the same LUT. + + - If LUT has the same number of bands as the input, then each band of the + input will be LUTed separately. + + - If the input has one band, then the LUT may have any number of bands, and + the output will have the same number of bands as the LUT. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_histgr(3), im_hsp(3), im_heq(3), im_identity(3). +.SH COPYRIGHT +1995, National Gallery and Birkbeck College +.SH AUTHORS +J. Cupitt, 1995 +.br +N. Dessipris \- 10/05/1991 diff --git a/libsrc/histograms_lut/man3/im_project.3 b/libsrc/histograms_lut/man3/im_project.3 new file mode 100644 index 00000000..9dd16f4c --- /dev/null +++ b/libsrc/histograms_lut/man3/im_project.3 @@ -0,0 +1,34 @@ +.TH IM_PROJECT 3 "10 April 2006" +.SH NAME +im_project \- find horizontal and vertical projections +.SH SYNOPSIS +#include + +int im_histplot(IMAGE *in, IMAGE *hout, IMAGE *vout) + +.SH DESCRIPTION +.B im_project(3) +finds the horizontal and vertical projections of an image, that is, the sum of +each row and the sum of each column. + +The +.B hout +result image is 1 x in->Ysize pixels, where each pixel is the sum of all the +pixels in that row of the input image. +The +.B vout +result image is in->Xsize x 1 pixels, where each pixel is the sum of all the +pixels in that column of the input image. + +The output images are of the largest appropriate type for the input image: so +unsigned integer images produce UINT output, and so on. The output images have +the same number of bands as the input. + +The operation does not work for complex images, or for coded images. + +.SH RETURNED VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_histplot(3), im_histnorm(3), im_extract(3). +.SH COPYRIGHT +Imperial College, 2006 diff --git a/libsrc/histograms_lut/man3/im_stdif.3 b/libsrc/histograms_lut/man3/im_stdif.3 new file mode 100644 index 00000000..d13a29a7 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_stdif.3 @@ -0,0 +1,54 @@ +.TH IM_STDIF 3 "10 May 1991" +.SH NAME +im_stdif, im_stdif_raw \- statistical differentiation of an image +.SH SYNOPSIS +.B #include + +int im_stdif(in, out, a, m0, b, s0, xw, yw) +.br +IMAGE *in, *out; +.br +double a, m0, b, s0; +.br +int xw, yw; + +int im_stdif_raw(in, out, a, m0, b, s0, xw, yw) +.br +IMAGE *in, *out; +.br +double a, m0, b, s0; +.br +int xw, yw; + +.SH DESCRIPTION +im_stdif() preforms statistical differencing according to the formula +given in page 45 of the book "An Introduction to Digital Image Processing" by +Wayne Niblack. This transformation emphasises the way in which a pel differs +statistically from its neighbours. It is useful for enhancing low-contrast +images with lots of detail, such as X-ray plates. + +At point (i,j) the output is given by the eqn: + + vout(i,j) = a*m0 + (1-a)*meanv + + (vin(i,j) - meanv) * (b*s0) / (s0+b*stdv) + +Values a, m0, b and s0 are entered, while meanv and stdv are the values +calculated over a moving window of size xw, yw centred on pixel (i,j). m0 is the +new mean, a is the weight given to it. s0 is the new standard deviation, b is +the weight given to it. Try: + + im_stdif $VIPSHOME/pics/huysum.v fred.v 0.5 128 0.5 50 11 11 + +The opreation works on one-band UCHAR images only, and writes a one-band UCHAR +image as its result. The output image has the same size as the input - a black +border is added to mark uncomputable pixels. + +im_stdif_raw() behaves exactly as im_stdif(), but does not add the border of +black pixels. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_lhisteq(3), im_histgr(3), im_hsp(3), im_heq(3). +.SH COPYRIGHT +1991-1996, The National Gallery and Birkbeck College diff --git a/libsrc/histograms_lut/man3/im_tone_analyse.3 b/libsrc/histograms_lut/man3/im_tone_analyse.3 new file mode 100644 index 00000000..adbb59b6 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_tone_analyse.3 @@ -0,0 +1 @@ +.so man3/im_tone_build.3 diff --git a/libsrc/histograms_lut/man3/im_tone_build.3 b/libsrc/histograms_lut/man3/im_tone_build.3 new file mode 100644 index 00000000..2ef1615c --- /dev/null +++ b/libsrc/histograms_lut/man3/im_tone_build.3 @@ -0,0 +1,151 @@ +.TH TONE 3 "10 May 1991" +.SH NAME +im_tone_build, im_tone_map, im_tone_analyse, im_tone_build_range \- tone-curve adjustment of LAB +images +.SH SYNOPSIS + +int +.br +im_tone_build_range( +.br + int in_max, int out_max, +.br + IMAGE *lut, +.br + double Lb, double Lw, +.br + double Ps, double Pm, double Ph, +.br + double S, double M, double H ) + +int +.br +im_tone_build( +.br + IMAGE *lut, +.br + double Lb, double Lw, +.br + double Ps, double Pm, double Ph, +.br + double S, double M, double H ) + +int +.br +im_tone_analyse( +.br + IMAGE *in, +.br + IMAGE *lut, +.br + double Ps, double Pm, double Ph, +.br + double S, double M, double H ) + +int +.br +im_tone_map( IMAGE *in, IMAGE *out, IMAGE *lut ) + +.SH DESCRIPTION +Various functions relating to tone curve adjustment. Example tone curve: + + repro L* out + ^ + | + 100 | . . . . . . . . . . . . . . . .* + | * . + Lw | . . . . . . . . . . . . . .* . + | * . . + | /* . . + | / * . . + | / .* . . + | * * /* *. . . + | * * / . . . + | * / . . . . + | * / . . . . + | * / . . . . + | * / . . . . . + | */ . . . . . + Lb | . .* . . . . . + | * . . . . . . + |* . . . . . . + 0 +---------------------------------------> + 0 Lb Ls Lm Lh Lw 100 L* in + +.B im_tone_build_range(3) +generates a tone curve for the adjustment of image levels. The curve is +an unsigned 16-bit image with (in_max + 1) entries, each in the range [0, +out_max]. + +The parameters are +expressed as 0-100, as in LAB colour space, but you specify the scaling for the +input and output images with the in_max and out_max parameters. + +Parameters: + + Lb - black point + Lw - white point + +Both in L* units, ie. in the range [0,100]. These should be set by histogram +analysis of the image to be transformed to 0.1% and 99.9% of the full range of +the image. See im_tone_analyse() below. + + Ps - shadow point + Pm - mid-tone point + Ph - highlight point + +All in [0,1], meaning max of shadow section of curve should be positioned +at Lb+Ps(Lw-Lb), etc. Suggested values: Ps, Pm, Ph should be 0.2, 0.5 and 0.8. +Ps is limited to the range [0.1,0.3], Pm to the range [0.4,0.6] and Ph to +[0.7,0.9]. + + S - shadow adjustment factor (+/- 15) + M - mid-tone adjustment factor (+/- 30) + H - highlight adjustment factor (+/- 15) + +These are the principal parameters, controlling the brightness in the shadow, +mid-tone and highlight areas. Suggested values: + + 0, 0, 0 - no change to input image + 5, 0, -2 - boost shadows a little, depress highlights slightly + +.B im_tone_build(3) +is a convenience function that calls +.B im_tone_build_range(3) +with ranges suitable for tone correcting a LABQ image to a LABS image. + +Use +.B im_ismonotonic(3) +to check that the slope of your tone curve is always >0, +use +.B im_histplot(3) +to graph the curve, use +.B im_tone_map(3) +to apply your curve to an image. + +.B im_tone_map(3) +map just the L channel of a LabQ or LabS image through a tone +curve. + +.B im_tone_analyse(3) +find the histogram of a LabS or LabQ image and use that to +set the Ln and Lw parameters of +.B im_tone_build(3). +All other parameters as above. + +Example: + + example% im_tone_analyse $VIPSHOME/pics/master.v /tmp/lut.v \ + 0.2 0.5 0.8 6.3 0.8 -3 + example% im_ismonotonic /tmp/lut.v + 255 + example% im_tone_map $VIPSHOME/pics/master.v /tmp/master2.v /tmp/lut.v + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_histplot(3), im_ismonotonic(3). +.SH COPYRIGHT +1995, National Gallery +.SH AUTHORS +J. Cupitt diff --git a/libsrc/histograms_lut/man3/im_tone_map.3 b/libsrc/histograms_lut/man3/im_tone_map.3 new file mode 100644 index 00000000..adbb59b6 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_tone_map.3 @@ -0,0 +1 @@ +.so man3/im_tone_build.3 diff --git a/libsrc/histograms_lut/tone.c b/libsrc/histograms_lut/tone.c new file mode 100644 index 00000000..e0016d97 --- /dev/null +++ b/libsrc/histograms_lut/tone.c @@ -0,0 +1,528 @@ +/* @(#) Various functions relating to tone curve adjustment. + * @(#) + * @(#) im_tone_build: generate tone curve for adjustment of LabQ image. LUT we + * @(#) make is always 1024 elements, each element is a LabS L value. + * @(#) + * @(#) Base parameters: + * @(#) + * @(#) Lb - black point + * @(#) Lw - white point (both [0,100]) + * @(#) + * @(#) Ps - shadow point + * @(#) Pm - mid-tone point + * @(#) Ph - highlight point + * @(#) + * @(#) All 0-1, meaning max of shadow section of curve should be positioned + * @(#) at Lb+Ps(Lw-Lb), etc. Suggest Lb, Lw should be set by histogram + * @(#) analysis to 0.1% and 99.9%; Ps, Pm, Ph should be 0.2, 0.5, 0.8. + * @(#) + * @(#) Main parameters: + * @(#) + * @(#) S - shadow adjustment factor (+/- 30) + * @(#) M - mid-tone adjustment factor (+/- 30) + * @(#) H - highlight adjustment factor (+/- 30) + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_tone_build( + * @(#) IMAGE *lut, + * @(#) double Lb, double Lw, + * @(#) double Ps, double Pm, double Ph, + * @(#) double S, double M, double H ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * @(#) im_ismonotonic: test any LUT for monotonicity --- set out to non-zero + * @(#) if lut is monotonic. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_ismonotonic( IMAGE *lut, int *out ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * @(#) im_tone_map: map just the L channel of a LabQ or LabS image through + * @(#) a LUT. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_tone_map( IMAGE *in, IMAGE *out, IMAGE *lut ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * @(#) im_tone_analyse: find the histogram of a LabS or LabQ image and use + * @(#) that to set the Ln and Lw parameters of im_tone_build() + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_tone_analyse( + * @(#) IMAGE *in, + * @(#) IMAGE *lut, + * @(#) double Ps, double Pm, double Ph, + * @(#) double S, double M, double H ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Author: John Cupitt + * Written on: 18/7/1995 + * 17/9/96 JC + * - restrictions on Ps, Pm, Ph relaxed + * - restrictions on S, M, H relaxed + * 25/7/01 JC + * - patched for im_extract_band() change + * 11/7/04 + * - generalised to im_tone_build_range() ... so you can use it for any + * image ,not just LabS + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Parameters for tone curve formation. + */ +typedef struct { + /* Parameters. + */ + double Lb, Lw; + double Ps, Pm, Ph; + double S, M, H; + + /* Derived values. + */ + double Ls, Lm, Lh; +} ToneShape; + +/* Calculate shadow curve. + */ +static double +shad( ToneShape *ts, double x ) +{ + double x1 = (x - ts->Lb) / (ts->Ls - ts->Lb); + double x2 = (x - ts->Ls) / (ts->Lm - ts->Ls); + double out; + + if( x < ts->Lb ) + out = 0; + else if( x < ts->Ls ) + out = 3.0 * x1 * x1 - 2.0 * x1 * x1 * x1; + else if( x < ts->Lm ) + out = 1.0 - 3.0 * x2 * x2 + 2.0 * x2 * x2 * x2; + else + out = 0; + + return( out ); +} + +/* Calculate mid-tone curve. + */ +static double +mid( ToneShape *ts, double x ) +{ + double x1 = (x - ts->Ls) / (ts->Lm - ts->Ls); + double x2 = (x - ts->Lm) / (ts->Lh - ts->Lm); + double out; + + if( x < ts->Ls ) + out = 0; + else if( x < ts->Lm ) + out = 3.0 * x1 * x1 - 2.0 * x1 * x1 * x1; + else if( x < ts->Lh ) + out = 1.0 - 3.0 * x2 * x2 + 2.0 * x2 * x2 * x2; + else + out = 0; + + return( out ); +} + +/* Calculate highlight curve. + */ +static double +high( ToneShape *ts, double x ) +{ + double x1 = (x - ts->Lm) / (ts->Lh - ts->Lm); + double x2 = (x - ts->Lh) / (ts->Lw - ts->Lh); + double out; + + if( x < ts->Lm ) + out = 0; + else if( x < ts->Lh ) + out = 3.0 * x1 * x1 - 2.0 * x1 * x1 * x1; + else if( x < ts->Lw ) + out = 1.0 - 3.0 * x2 * x2 + 2.0 * x2 * x2 * x2; + else + out = 0; + + return( out ); +} + +/* Generate a point on the tone curve. Everything is 0-100. + */ +static double +tone_curve( ToneShape *ts, double x ) +{ + double out; + + out = x + + ts->S * shad( ts, x ) + ts->M * mid( ts, x ) + + ts->H * high( ts, x ); + + return( out ); +} + +int +im_tone_build_range( IMAGE *out, + int in_max, int out_max, + double Lb, double Lw, + double Ps, double Pm, double Ph, + double S, double M, double H ) +{ + ToneShape *ts = IM_NEW( out, ToneShape ); + unsigned short lut[65536]; + int i; + + /* Check args. + */ + if( !ts || im_outcheck( out ) ) + return( -1 ); + if( in_max < 0 || in_max > 65535 || + out_max < 0 || out_max > 65535 ) { + im_error( "im_tone_build", + _( "bad in_max, out_max parameters" ) ); + return( -1 ); + } + if( Lb < 0 || Lb > 100 || Lw < 0 || Lw > 100 || Lb > Lw ) { + im_error( "im_tone_build", _( "bad Lb, Lw parameters" ) ); + return( -1 ); + } + if( Ps < 0.0 || Ps > 1.0 ) { + im_error( "im_tone_build", _( "Ps not in range [0.0,1.0]" ) ); + return( -1 ); + } + if( Pm < 0.0 || Pm > 1.0 ) { + im_error( "im_tone_build", _( "Pm not in range [0.0,1.0]" ) ); + return( -1 ); + } + if( Ph < 0.0 || Ph > 1.0 ) { + im_error( "im_tone_build", _( "Ph not in range [0.0,1.0]" ) ); + return( -1 ); + } + if( S < -30 || S > 30 ) { + im_error( "im_tone_build", _( "S not in range [-30,+30]" ) ); + return( -1 ); + } + if( M < -30 || M > 30 ) { + im_error( "im_tone_build", _( "M not in range [-30,+30]" ) ); + return( -1 ); + } + if( H < -30 || H > 30 ) { + im_error( "im_tone_build", _( "H not in range [-30,+30]" ) ); + return( -1 ); + } + + /* Note params. + */ + ts->Lb = Lb; + ts->Lw = Lw; + ts->Ps = Ps; + ts->Pm = Pm; + ts->Ph = Ph; + ts->S = S; + ts->M = M; + ts->H = H; + + /* Note derived params. + */ + ts->Ls = Lb + Ps * (Lw - Lb); + ts->Lm = Lb + Pm * (Lw - Lb); + ts->Lh = Lb + Ph * (Lw - Lb); + + /* Generate curve. + */ + for( i = 0; i <= in_max; i++ ) { + int v = (out_max / 100.0) * + tone_curve( ts, 100.0 * i / in_max ); + + if( v < 0 ) + v = 0; + else if( v > out_max ) + v = out_max; + + lut[i] = v; + } + + /* Make the output image. + */ + im_initdesc( out, + in_max + 1, 1, 1, IM_BBITS_SHORT, IM_BANDFMT_USHORT, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + if( im_setupout( out ) ) + return( -1 ); + if( im_writeline( 0, out, (PEL *) lut ) ) + return( -1 ); + + return( 0 ); +} + +int +im_tone_build( IMAGE *out, + double Lb, double Lw, + double Ps, double Pm, double Ph, + double S, double M, double H ) +{ + IMAGE *t1 = im_open_local( out, "im_tone_build", "p" ); + + if( !t1 || + im_tone_build_range( t1, 1023, 32767, + Lb, Lw, Ps, Pm, Ph, S, M, H ) || + im_clip2s( t1, out ) ) + return( -1 ); + + return( 0 ); +} + +/* Test a lut or histogram for monotonicity. + */ +int +im_ismonotonic( IMAGE *lut, int *out ) +{ + IMAGE *t1 = im_open_local( lut, "im_ismonotonic:1", "p" ); + IMAGE *t2 = im_open_local( lut, "im_ismonotonic:2", "p" ); + IMAGE *t3 = im_open_local( lut, "im_ismonotonic:3", "p" ); + double m; + + if( !t1 || !t2 || !t3 ) + return( -1 ); + + /* Can be either a horizontal or vertical LUT. + */ + if( lut->Xsize != 1 && lut->Ysize != 1 ) { + im_error( "im_ismonotonic", _( "not 1 by n or n by 1 image" ) ); + return( -1 ); + } + + /* Extract two areas, offset by 1 pixel. + */ + if( lut->Xsize == 1 ) { + if( im_extract_area( lut, t1, 0, 0, 1, lut->Ysize - 1 ) || + im_extract_area( lut, t2, 0, 1, 1, lut->Ysize - 1 ) ) + return( -1 ); + } + else { + if( im_extract_area( lut, t1, 0, 0, lut->Xsize - 1, 1 ) || + im_extract_area( lut, t2, 1, 0, lut->Xsize - 1, 1 ) ) + return( -1 ); + } + + /* Now t2 should be >= than t1 everywhere! + */ + if( im_moreeq( t2, t1, t3 ) || im_min( t3, &m ) ) + return( -1 ); + + *out = m; + + return( 0 ); +} + +/* Map the L channel of a LabQ or LabS channel of an image through a LUT. + */ +int +im_tone_map( IMAGE *in, IMAGE *out, IMAGE *lut ) +{ + IMAGE *t1 = im_open_local( out, "im_tone_map:1", "p" ); + IMAGE *t2 = im_open_local( out, "im_tone_map:2", "p" ); + IMAGE *t3 = im_open_local( out, "im_tone_map:3", "p" ); + IMAGE *t4 = im_open_local( out, "im_tone_map:4", "p" ); + IMAGE *t5 = im_open_local( out, "im_tone_map:5", "p" ); + IMAGE *t6 = im_open_local( out, "im_tone_map:6", "p" ); + IMAGE *t7 = im_open_local( out, "im_tone_map:7", "p" ); + IMAGE *t8 = im_open_local( out, "im_tone_map:8", "p" ); + IMAGE *imarray[3]; + + if( !t1 || !t2 || !t3 || !t4 || !t5 || !t6 || !t7 ) + return( -1 ); + + /* Need a 1024-point IM_BANDFMT_SHORT lut. + */ + if( lut->Xsize != 1 && lut->Ysize != 1 ) { + im_error( "im_tone_map", _( "not 1 by n or n by 1 image" ) ); + return( -1 ); + } + if( lut->Xsize*lut->Ysize != 1024 || + lut->BandFmt != IM_BANDFMT_SHORT ) { + im_error( "im_tone_map", + _( "not 1024-point IM_BANDFMT_SHORT lut" ) ); + return( -1 ); + } + + /* If in is IM_CODING_LABQ, unpack. + */ + if( in->Coding == IM_CODING_LABQ ) { + if( im_LabQ2LabS( in, t1 ) ) + return( -1 ); + } + else + t1 = in; + + /* Should now be 3-band short. + */ + if( t1->Coding != IM_CODING_NONE || t1->BandFmt != IM_BANDFMT_SHORT || + t1->Bands != 3 ) { + im_error( "im_tone_map", _( "input not LabS or LabQ" ) ); + return( -1 ); + } + + /* Split into bands. + */ + if( im_extract_band( t1, t2, 0 ) || im_extract_band( t1, t3, 1 ) || + im_extract_band( t1, t4, 2 ) ) + return( -1 ); + + /* Scale L down to 10 bits so we can use it to index LUT. And amke + * sure we have an unsigned type we can use for indexing. + */ + if( im_shiftright( t2, t8, 5 ) || + im_clip2us( t8, t5 ) ) + return( -1 ); + + /* Replace L. + */ + if( im_maplut( t5, t6, lut ) ) + return( -1 ); + + /* Recombine bands. If input was LabQ, repack. + */ + imarray[0] = t6; imarray[1] = t3; imarray[2] = t4; + if( in->Coding == IM_CODING_LABQ ) { + if( im_gbandjoin( imarray, t7, 3 ) || + im_LabS2LabQ( t7, out ) ) + return( -1 ); + } + else { + if( im_gbandjoin( imarray, out, 3 ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Find histogram of in, and use that to set Lb, Lw levels. + */ +int +im_tone_analyse( + IMAGE *in, + IMAGE *lut, + double Ps, double Pm, double Ph, + double S, double M, double H ) +{ + gint64 sum = in->Xsize * in->Ysize; + int *p; + int i, j; + double Lb, Lw; + + IMAGE *t1 = im_open_local( lut, "im_tone_analyse:1", "p" ); + IMAGE *t2 = im_open_local( lut, "im_tone_analyse:2", "p" ); + IMAGE *t3 = im_open_local( lut, "im_tone_analyse:3", "p" ); + IMAGE *t4 = im_open_local( lut, "im_tone_analyse:4", "p" ); + IMAGE *t6 = im_open_local( lut, "im_tone_analyse:6", "p" ); + + if( !t1 || !t2 || !t3 || !t4 || !t6 ) + return( -1 ); + + /* If in is IM_CODING_LABQ, unpack. + */ + if( in->Coding == IM_CODING_LABQ ) { + if( im_LabQ2LabS( in, t1 ) ) + return( -1 ); + } + else + t1 = in; + + /* Should now be 3-band short. + */ + if( t1->Coding != IM_CODING_NONE || t1->BandFmt != IM_BANDFMT_SHORT || + t1->Bands != 3 ) { + im_error( "im_tone_analyse", _( "input not LabS or LabQ" ) ); + return( -1 ); + } + + /* Extract and scale L. + */ + if( im_extract_band( t1, t2, 0 ) || + im_shiftright( t2, t3, 5 ) || + im_clip2us( t3, t4 ) ) + return( -1 ); + + /* Take histogram, and make it a cumulative hist. + */ + if( im_histgr( t4, t6, -1 ) ) + return( -1 ); + + /* Search for 0.1% mark. + */ + if( im_incheck( t6 ) ) + return( -1 ); + p = (int *) t6->data; + for( j = 0, i = 0; i < t6->Xsize; i++ ) { + j += p[i]; + if( j > sum * (0.1 / 100.0) ) + break; + } + Lb = i / 10.24; + + /* Search for 99.9% mark. + */ + p = (int *) t6->data; + for( j = 0, i = t6->Xsize - 1; i > 0; i-- ) { + j += p[i]; + if( j > sum * (0.1 / 100.0) ) + break; + } + Lw = i / 10.24; + + im_diagnostics( "im_tone_analyse: set Lb = %g, Lw = %g", Lb, Lw ); + + return( im_tone_build( lut, Lb, Lw, Ps, Pm, Ph, S, M, H ) ); +} diff --git a/libsrc/inplace/Makefile.am b/libsrc/inplace/Makefile.am new file mode 100644 index 00000000..73fd5301 --- /dev/null +++ b/libsrc/inplace/Makefile.am @@ -0,0 +1,17 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libinplace.la + +libinplace_la_SOURCES = \ + im_circle.c \ + im_flood.c \ + im_insertplace.c \ + im_line.c \ + im_paintrect.c \ + im_plotmask.c \ + inplace_dispatch.c \ + line_draw.c \ + plot_point.c \ + smudge_area.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/inplace/im_circle.c b/libsrc/inplace/im_circle.c new file mode 100644 index 00000000..5dc92536 --- /dev/null +++ b/libsrc/inplace/im_circle.c @@ -0,0 +1,134 @@ +/* @(#) writes a circle in a vasari file + * @(#) The circle is centred in the middle of the file (xsize/2, ysize/2) + * @(#) im must be a valid image + * @(#) int im_circle(pim, cx, cy, radius, intensity) + * @(#) IMAGE *pim; + * @(#) int cx, cy, radius, intensity; + * @(#) + * @(#) Return -1 on error 0 on sucess. + * + * Copyright 1990, N. Dessipris. + * + * Author N. Dessipris + * Written on 30/05/1990 + * Updated on: + * 22/7/93 JC + * - im_incheck() call added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_circle( IMAGE *im, int cx, int cy, int radius, int intensity) +{ + PEL *start; + int size = 0; + int x, y, d, offset; + + if( im_rwcheck( im ) ) + return( -1 ); + +/* Check args */ + if ( (im->data == NULL)||(im->BandFmt != IM_BANDFMT_UCHAR)||(im->Bands != 1) + ||(im->Bbits != IM_BBITS_BYTE) ) + { + im_errormsg("im_circle: unable to write input image"); + return(-1); + } + if ((intensity > 255)||(intensity <= 0)) + { + im_errormsg("im_circle: intensity between 0 and 255"); + return(-1); + } +/* Check if circle fits into image */ + if ( ((radius+cy)> im->Ysize - 1) || ((cy-radius)< 0 ) || + ((radius+cx)> im->Xsize - 1) || ((cx-radius) < 0 ) ) + { + im_errormsg("im_circle: The circle doesnot fit in image"); + return(-1); + } +/* Draw the circle */ + size = im->Xsize; + start = (PEL*)im->data; + offset = cy * im->Xsize + cx; /* point at the center of the circle */ + x = 0; + y = radius; + d = 3 - 2 * radius; + while ( x < y ) + { + *(start + offset + size * y + x) = (PEL)intensity; + *(start + offset + size * x + y) = (PEL)intensity; + *(start + offset + size * y - x) = (PEL)intensity; + *(start + offset + size * x - y) = (PEL)intensity; + *(start + offset - size * y - x) = (PEL)intensity; + *(start + offset - size * x - y) = (PEL)intensity; + *(start + offset - size * y + x) = (PEL)intensity; + *(start + offset - size * x + y) = (PEL)intensity; + if (d < 0 ) + d += ( 4 * x + 6 ); + else + { + d += ( 4 * ( x - y ) + 10 ); + y--; + } + x++; + } + if ( x== y ) + { + *(start + offset + size * y + x) = (PEL)intensity; + *(start + offset + size * x + y) = (PEL)intensity; + *(start + offset + size * y - x) = (PEL)intensity; + *(start + offset + size * x - y) = (PEL)intensity; + *(start + offset - size * y - x) = (PEL)intensity; + *(start + offset - size * x - y) = (PEL)intensity; + *(start + offset - size * y + x) = (PEL)intensity; + *(start + offset - size * x + y) = (PEL)intensity; + } + + im_invalidate( im ); + + return(0); +} diff --git a/libsrc/inplace/im_flood.c b/libsrc/inplace/im_flood.c new file mode 100644 index 00000000..9254b649 --- /dev/null +++ b/libsrc/inplace/im_flood.c @@ -0,0 +1,421 @@ +/* int im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout ) + * + * Flood fill from point (x,y) with colour ink. Flood up to boundary == ink. + * Any type, any number of bands, IM_CODING_LABQ too. Returns the bounding box + * of the modified pixels in dout, whether it succeeds or not. + * + * Currently a rather inefficient pixel-based algorithm, should put something + * better in, really. Speed isn't likely to be a problem, except for very + * large images. + * + * JC 30/8/97 + * - VIPSified, cleaned up, from "John Robinson's prog to fill + * enclosed areas" + * - something Kirk gave me, so thanks John + * JC 1/10/97 + * - swapped inner memcmp/cpy for a loop ... faster for small pixels + * 13/7/02 JC + * - im_flood_blob() added + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Size of a point buffer. We allocate a list of these to hold points we need + * to visit. + */ +#define PBUFSIZE (1000) + +/* An xy position. + */ +typedef struct { + int x, y; +} Point; + +/* A buffer of points, and how many of them have been used. When full, alloc a + * new buffer, and link it on. + */ +typedef struct _Buffer { + struct _Buffer *next; + int n; + Point points[PBUFSIZE]; +} Buffer; + +/* Our state. + */ +typedef struct { + /* Parameters. + */ + IMAGE *im; + int x, y; + PEL *ink; /* Copy of ink param */ + Rect *dout; /* Write dirty here at end */ + + /* Derived stuff. + */ + PEL *edge; /* Boundary colour */ + int equal; /* Fill to == edge, or != edge */ + int ps; /* sizeof( one pel ) */ + int ls; /* sizeof( one line ) */ + int left, right; /* Area will fill within */ + int top, bottom; + Rect dirty; /* Bounding box of pixels we have changed */ + + /* Two buffers of points which we know need checking. + */ + Buffer *buf1; + Buffer *buf2; +} State; + +/* Alloc a new buffer. + */ +static Buffer * +build_buffer( void ) +{ + Buffer *buf = IM_NEW( NULL, Buffer ); + + if( !buf ) + return( NULL ); + buf->next = NULL; + buf->n = 0; + + return( buf ); +} + +/* Free a chain of buffers. + */ +static void +free_buffer( Buffer *buf ) +{ + IM_FREE( buf->next ); + im_free( buf ); +} + +/* Free a state. + */ +static void +free_state( State *st ) +{ + /* Write dirty back to caller. + */ + if( st->dout ) + *st->dout = st->dirty; + + /* Free our stuff. + */ + IM_FREE( st->ink ); + IM_FREEF( free_buffer, st->buf1 ); + IM_FREEF( free_buffer, st->buf2 ); + im_free( st ); +} + +/* Build a state. + */ +static State * +build_state( IMAGE *im, int x, int y, PEL *ink, Rect *dout ) +{ + State *st = IM_NEW( NULL, State ); + + if( !st ) + return( NULL ); + st->im = im; + st->x = x; + st->y = y; + st->ink = NULL; + st->dout = dout; + st->ps = IM_IMAGE_SIZEOF_PEL( im ); + st->ls = IM_IMAGE_SIZEOF_LINE( im ); + st->buf1 = NULL; + st->buf2 = NULL; + st->left = 0; + st->top = 0; + st->right = im->Xsize; + st->bottom = im->Ysize; + st->dirty.left = x; + st->dirty.top = y; + st->dirty.width = 0; + st->dirty.height = 0; + + if( !(st->ink = (PEL *) im_malloc( NULL, st->ps )) || + !(st->edge = (PEL *) im_malloc( NULL, st->ps )) || + !(st->buf1 = build_buffer()) || + !(st->buf2 = build_buffer()) ) { + free_state( st ); + return( NULL ); + } + memcpy( st->ink, ink, st->ps ); + + return( st ); +} + +/* Add xy to buffer, move buffer on on overflow. + */ +#define ADD( BUF, X, Y ) { \ + BUF->points[BUF->n].x = X; \ + BUF->points[BUF->n].y = Y; \ + BUF->n++; \ + if( BUF->n == PBUFSIZE ) { \ + if( !BUF->next ) { \ + if( !(BUF->next = build_buffer()) ) \ + return( -1 ); \ + } \ + BUF = BUF->next; \ + BUF->n = 0; \ + } \ +} + +/* If point != edge, add it to out. + */ +#define ADDIFNOTEDGE( P, X, Y ) { \ + PEL *p1 = (P); \ + \ + for( j = 0; j < st->ps; j++ ) \ + if( p1[j] != st->edge[j] ) { \ + ADD( out, X, Y ); \ + break; \ + } \ +} + +/* If point == edge, add it to out. + */ +#define ADDIFEDGE( P, X, Y ) { \ + PEL *p1 = (P); \ + \ + for( j = 0; j < st->ps; j++ ) \ + if( p1[j] != st->edge[j] ) \ + break; \ + if( j == st->ps ) \ + ADD( out, X, Y ); \ +} + +/* Read points to fill from in, write new points to out. + */ +static int +dofill( State *st, Buffer *in, Buffer *out ) +{ + int i, j; + + /* Clear output buffer. + */ + out->n = 0; + + /* Loop over chain of input buffers. + */ + for(;;) { + /* Loop for this buffer. + */ + for( i = 0; i < in->n; i++ ) { + /* Find this pixel. + */ + int x = in->points[i].x; + int y = in->points[i].y; + PEL *p = (PEL *) st->im->data + x*st->ps + y*st->ls; + + /* Is it still not fore? May have been set by us + * earlier. + */ + for( j = 0; j < st->ps; j++ ) + if( p[j] != st->ink[j] ) + break; + if( j == st->ps ) + continue; + + /* Set this pixel. + */ + for( j = 0; j < st->ps; j++ ) + p[j] = st->ink[j]; + + /* Changes bb of dirty area? + */ + if( x < st->dirty.left ) { + st->dirty.left -= x; + st->dirty.width += x; + } + else if( x > st->dirty.left + st->dirty.width ) + st->dirty.width += x; + + if( y < st->dirty.top ) { + st->dirty.top -= y; + st->dirty.height += y; + } + else if( y > st->dirty.top + st->dirty.height ) + st->dirty.height += y; + + /* Propogate to neighbours. + */ + if( st->equal ) { + if( x < st->right - 1 ) + ADDIFEDGE( p + st->ps, x + 1, y ); + if( x > st->left ) + ADDIFEDGE( p - st->ps, x - 1, y ); + if( y < st->bottom - 1 ) + ADDIFEDGE( p + st->ls, x, y + 1 ); + if( y > st->top ) + ADDIFEDGE( p - st->ls, x, y - 1 ); + } + else { + if( x < st->right - 1 ) + ADDIFNOTEDGE( p + st->ps, x + 1, y ); + if( x > st->left ) + ADDIFNOTEDGE( p - st->ps, x - 1, y ); + if( y < st->bottom - 1 ) + ADDIFNOTEDGE( p + st->ls, x, y + 1 ); + if( y > st->top ) + ADDIFNOTEDGE( p - st->ls, x, y - 1 ); + } + } + + if( in->n == PBUFSIZE ) + /* Buffer full ... must be another one. + */ + in = in->next; + else + break; + } + + return( 0 ); +} + +int +im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout ) +{ + State *st; + Buffer *in, *out, *t; + PEL *p; + + if( im_rwcheck( im ) ) + return( -1 ); + if( im->Coding != IM_CODING_NONE && im->Coding != IM_CODING_LABQ ) { + im_error( "im_flood", _( "uncoded or IM_CODING_LABQ only" ) ); + return( -1 ); + } + if( !(st = build_state( im, x, y, ink, dout )) ) + return( -1 ); + + /* Test start pixel ... nothing to do? + */ + p = (PEL *) im->data + x*st->ps + y*st->ls; + if( memcmp( p, ink, st->ps ) == 0 ) { + free_state( st ); + return( 0 ); + } + + /* Flood to != ink. + */ + memcpy( st->edge, ink, st->ps ); + st->equal = 0; + + /* Add start pixel to the work buffer, and loop. + */ + ADD( st->buf1, x, y ) + for( in = st->buf1, out = st->buf2; + in->n > 0; t = in, in = out, out = t ) + if( dofill( st, in, out ) ) { + free_state( st ); + return( -1 ); + } + + free_state( st ); + + im_invalidate( im ); + + return( 0 ); +} + +int +im_flood_blob( IMAGE *im, int x, int y, PEL *ink, Rect *dout ) +{ + State *st; + Buffer *in, *out, *t; + PEL *p; + + if( im_rwcheck( im ) ) + return( -1 ); + if( im->Coding != IM_CODING_NONE && im->Coding != IM_CODING_LABQ ) { + im_error( "im_flood", _( "uncoded or IM_CODING_LABQ only" ) ); + return( -1 ); + } + if( !(st = build_state( im, x, y, ink, dout )) ) + return( -1 ); + + /* Edge is set by colour of start pixel. + */ + p = (PEL *) im->data + x*st->ps + y*st->ls; + memcpy( st->edge, p, st->ps ); + st->equal = 1; + + /* Add start pixel to the work buffer, and loop. + */ + ADD( st->buf1, x, y ) + for( in = st->buf1, out = st->buf2; + in->n > 0; t = in, in = out, out = t ) + if( dofill( st, in, out ) ) { + free_state( st ); + return( -1 ); + } + + free_state( st ); + + im_invalidate( im ); + + return( 0 ); +} + +/* A Flood blob we can call from nip. Grr! Should be a way to wrap these + * automatically. Maybe nip could do it if it seems a RW image argument? + */ +int +im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink ) +{ + IMAGE *t; + + if( !(t = im_open_local( out, "im_flood_blob_copy", "t" )) || + im_copy( in, t ) || + im_flood_blob( t, x, y, ink, NULL ) || + im_copy( t, out ) ) + return( -1 ); + + return( 0 ); +} + + diff --git a/libsrc/inplace/im_insertplace.c b/libsrc/inplace/im_insertplace.c new file mode 100644 index 00000000..46cc1d6a --- /dev/null +++ b/libsrc/inplace/im_insertplace.c @@ -0,0 +1,123 @@ +/* @(#) Insert an image into another. Like im_insert, but an `in-place' + * @(#) operation. small must fit entirely inside big - no clipping is + * @(#) performed. + * @(#) + * @(#) int + * @(#) im_insertplace( big, small, x, y ) + * @(#) IMAGE *big, *small; + * @(#) int x, y; + * @(#) + * + * Copyright: J. Cupitt + * Written: 15/06/1992 + * 22/7/93 JC + * - im_incheck() added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 1/9/04 JC + * - checks bands/types/etc match (thanks Matt) + * - smarter pixel size calculations + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Like im_insert, but perform an in-place insertion. + */ +int +im_insertplace( IMAGE *big, IMAGE *small, int x, int y ) +{ + Rect br, sr; + PEL *p, *q; + int z; + + /* Check IO. + */ + if( im_rwcheck( big ) || im_incheck( small ) ) + return( -1 ); + + /* Check compatibility. + */ + if( big->BandFmt != small->BandFmt || big->Bands != small->Bands || + big->Coding != small->Coding ) { + im_error( "im_insertplace", _( "inputs differ in format" ) ); + return( -1 ); + } + if( big->Coding != IM_CODING_NONE && big->Coding != IM_CODING_LABQ ) { + im_error( "im_insertplace", _( "input should be uncoded " + "or IM_CODING_LABQ" ) ); + return( -1 ); + } + + /* Make rects for big and small. + */ + br.left = 0; + br.top = 0; + br.width = big->Xsize; + br.height = big->Ysize; + sr.left = x; + sr.top = y; + sr.width = small->Xsize; + sr.height = small->Ysize; + + /* Small fits inside big? + */ + if( !im_rect_includesrect( &br, &sr ) ) { + im_error( "im_insertplace", _( "small not inside big" ) ); + return( -1 ); + } + + /* Loop, memcpying small to big. + */ + p = (PEL *) IM_IMAGE_ADDR( small, 0, 0 ); + q = (PEL *) IM_IMAGE_ADDR( big, x, y ); + for( z = 0; z < small->Ysize; z++ ) { + memcpy( (char *) q, (char *) p, IM_IMAGE_SIZEOF_LINE( small ) ); + p += IM_IMAGE_SIZEOF_LINE( small ); + q += IM_IMAGE_SIZEOF_LINE( big ); + } + + im_invalidate( big ); + + return( 0 ); +} diff --git a/libsrc/inplace/im_line.c b/libsrc/inplace/im_line.c new file mode 100644 index 00000000..925846ca --- /dev/null +++ b/libsrc/inplace/im_line.c @@ -0,0 +1,153 @@ +/* @#) line drawer. adapted to draw for graphics system + * @(#) Modified to be compatible with the vasari library + * @(#) In order to use this function, the input file should have been set by + * @(#) im_mmapinrw() + * + * Copyright: N. Dessipris + * Written: 02/01/1990 + * Modified : + * 22/7/93 JC + * - im_incheck() added + * - externs removed + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int im_line(image, x1, y1, x2, y2, pelval) +IMAGE *image; +int x1, x2, y1, y2, pelval; +{ + +double x, y, dx, dy, m; +long offset; +double signx, signy; + + if( im_rwcheck( image ) ) + return( -1 ); +/* check coordinates */ +if ( (x1 > image->Xsize)||(x1<0)||(y1 > image->Ysize)||(y1<0) + ||(x2 > image->Xsize)||(x2<0)||(y2 > image->Ysize)||(y2<0) ) + { im_errormsg("im_line: invalid line cooordinates"); return(-1); } +if ((pelval > 255)||(pelval < 0)) + {im_errormsg("im_line: line intensity between 0 and 255"); return(-1); } + +if (image->Bands != 1) + { im_errormsg("im_line: image should have one band only");return(-1); } + +dx = (double)(x2 - x1); +dy = (double)(y2 - y1); + +if (dx < 0.0) + signx = -1.0; +else + signx = 1.0; + +if (dy < 0.0) + signy = -1.0; +else + signy = 1.0; + +if (dx == 0.0) + { + x = x1; y = y1; + while (y != y2) + { + offset = (int)(x+.5) + ((int)(y +.5)) * image->Xsize; + *(image->data + offset) = (PEL)pelval; + y += signy; + } + /* Draw point (x2, y2) */ + offset = x2 + y2 * image->Xsize; + *(image->data + offset) = (PEL)pelval; + return(0); + } + +if (dy == 0.0) + { + y = y1; x = x1; + while (x != x2) + { + offset = (int)(x+.5) + ((int)(y +.5)) * image->Xsize; + *(image->data + offset) = (PEL)pelval; + x += signx; + } + /* Draw point (x2, y2) */ + offset = x2 + y2 * image->Xsize; + *(image->data + offset) = (PEL)pelval; + return(0); + } + +if (fabs(dy) < fabs(dx)) + { + m = fabs(dy/dx)*signy; + y = y1; + x = x1; + while (x != x2) + { + offset = (int)(x+.5) + ((int)(y +.5)) * image->Xsize; + *(image->data + offset) = (PEL)pelval; + x += signx; + y += m; + } + } +else + { + m = fabs(dx/dy)*signx; + x = x1; y = y1; + while (y != y2) + { + offset = (int)(x+.5) + ((int)(y +.5)) * image->Xsize; + *(image->data + offset) = (PEL)pelval; + x += m; + y += signy; + } + } +/* Draw point (x2, y2) */ +offset = x2 + y2 * image->Xsize; +*(image->data + offset) = (PEL)pelval; + im_invalidate( image ); +return(0); +} diff --git a/libsrc/inplace/im_paintrect.c b/libsrc/inplace/im_paintrect.c new file mode 100644 index 00000000..cfad16d3 --- /dev/null +++ b/libsrc/inplace/im_paintrect.c @@ -0,0 +1,105 @@ +/* @(#) Fill Rect r of image im with pels of colour ink. r can be any size and + * @(#) any position, we clip against the image size. + * @(#) + * @(#) int + * @(#) im_paintrect( IMAGE *im, Rect *r, PEL *ink ) + * @(#) + * @(#) + * + * Copyright: J. Cupitt + * Written: 15/06/1992 + * 22/7/93 JC + * - im_incheck() added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Paint a rect of colour into an image. + */ +int +im_paintrect( IMAGE *im, Rect *r, PEL *ink ) +{ + int es = im->Bbits >> 3; + int ps = es * im->Bands; + int ls = ps * im->Xsize; + Rect image, clipped; + int x, y, b; + PEL *to; + PEL *q; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Find area we plot. + */ + image.left = 0; + image.top = 0; + image.width = im->Xsize; + image.height = im->Ysize; + im_rect_intersectrect( r, &image, &clipped ); + + /* Any points left to plot? + */ + if( im_rect_isempty( &clipped ) ) + return( 0 ); + + /* Loop through image plotting where required. + */ + to = (PEL *) im->data + clipped.left * ps + clipped.top * ls; + for( y = 0; y < clipped.height; y++ ) { + q = to; + + for( x = 0; x < clipped.width; x++ ) + for( b = 0; b < ps; b++ ) + *q++ = ink[b]; + + to += ls; + } + + im_invalidate( im ); + + return( 0 ); +} + diff --git a/libsrc/inplace/im_plotmask.c b/libsrc/inplace/im_plotmask.c new file mode 100644 index 00000000..c171086e --- /dev/null +++ b/libsrc/inplace/im_plotmask.c @@ -0,0 +1,239 @@ +/* @(#) Plot many points in a single call. Pass ink, array containing + * @(#) 0/255 showing where to plot and Rect showing size of array and + * @(#) offset to get to centre of array. ix and iy are where to plot. Rect + * @(#) can be any size, any position - we clip against the edges of the + * @(#) image. + * @(#) + * @(#) int + * @(#) im_plotmask( IMAGE *im, int ix, int iy, PEL *ink, PEL *mask, Rect *r ) + * @(#) + * + * Copyright: J. Cupitt + * Written: 15/06/1992 + * 22/7/93 JC + * - im_incheck() added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 24/10/03 JC + * - now blends with 0-255 mask + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Paint ink into an 8 or 16 bit integer image. + */ +#define IBLEND( TYPE, TO, INK, B, W ) { \ + TYPE *tto = (TYPE *) (TO); \ + TYPE *tink = (TYPE *) (INK); \ + \ + int x, i, j; \ + \ + for( j = 0, x = 0; x < (W); x++ ) \ + for( i = 0; i < (B); i++, j++ ) \ + tto[j] = (tink[i] * mask_line[x] + \ + tto[j] * (255 - mask_line[x])) / 255; \ +} + +/* Do the blend with doubles. + */ +#define DBLEND( TYPE, TO, INK, B, W ) { \ + TYPE *tto = (TYPE *) (TO); \ + TYPE *tink = (TYPE *) (INK); \ + \ + int x, i, j; \ + \ + for( j = 0, x = 0; x < (W); x++ ) \ + for( i = 0; i < (B); i++, j++ ) \ + tto[j] = ((double) tink[i] * mask_line[x] + \ + (double) tto[j] * (255 - mask_line[x])) / 255;\ +} + +/* Blend of complex. + */ +#define CBLEND( TYPE, TO, INK, B, W ) { \ + TYPE *tto = (TYPE *) (TO); \ + TYPE *tink = (TYPE *) (INK); \ + \ + int x, i, j; \ + \ + for( j = 0, x = 0; x < (W); x++ ) \ + for( i = 0; i < (B) * 2; i += 2, j += 2 ) { \ + tto[j] = ((double) tink[i] * mask_line[x] + \ + (double) tto[j] * (255 - mask_line[x])) / 255;\ + tto[j + 1] = ((double) tink[i + 1] * mask_line[x] + \ + (double) tto[j + 1] * (255 - mask_line[x])) / \ + 255;\ + } \ +} + +/* Plot lots of points! Pass ink, array of 0/255 showing where to plot, rect + * showing size and offset for array. Used for fat lines and text. + */ +int +im_plotmask( IMAGE *im, int ix, int iy, PEL *ink, PEL *mask, Rect *r ) +{ + Rect area, image, clipped; + int y; + int mx, my; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Find area we plot. + */ + area = *r; + area.left += ix; + area.top += iy; + image.left = 0; + image.top = 0; + image.width = im->Xsize; + image.height = im->Ysize; + im_rect_intersectrect( &area, &image, &clipped ); + + /* Any points left to plot? + */ + if( im_rect_isempty( &clipped ) ) + return( 0 ); + + /* Find area of mask we use. + */ + mx = IM_MAX( 0, clipped.left - area.left ); + my = IM_MAX( 0, clipped.top - area.top ); + + /* Loop through image plotting where required. + */ + if( im->Coding == IM_CODING_LABQ ) { + float *lab_buffer; + float ink_buffer[3]; + + if( !(lab_buffer = + IM_ARRAY( NULL, clipped.width * 3, float )) ) + return( -1 ); + + imb_LabQ2Lab( ink, ink_buffer, 1 ); + + for( y = 0; y < clipped.height; y++ ) { + PEL *to = (PEL *) IM_IMAGE_ADDR( im, + clipped.left, y + clipped.top ); + PEL *mask_line = mask + + mx + (y + my) * area.width; + + imb_LabQ2Lab( to, lab_buffer, clipped.width ); + DBLEND( float, + lab_buffer, ink_buffer, 3, clipped.width ); + imb_Lab2LabQ( lab_buffer, to, clipped.width ); + } + + im_free( lab_buffer ); + } + else { + for( y = 0; y < clipped.height; y++ ) { + PEL *to = (PEL *) IM_IMAGE_ADDR( im, + clipped.left, y + clipped.top ); + PEL *mask_line = mask + + mx + (y + my) * area.width; + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + IBLEND( unsigned char, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_CHAR: + IBLEND( signed char, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_USHORT: + IBLEND( unsigned short, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_SHORT: + IBLEND( signed short, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_UINT: + DBLEND( unsigned int, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_INT: + DBLEND( signed int, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_FLOAT: + DBLEND( float, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_DOUBLE: + DBLEND( double, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_COMPLEX: + CBLEND( float, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_DPCOMPLEX: + CBLEND( double, + to, ink, im->Bands, clipped.width ); + break; + + default: + im_error( "im_plotmask", + _( "internal error" ) ); + return( -1 ); + } + } + } + + im_invalidate( im ); + + return( 0 ); +} + diff --git a/libsrc/inplace/inplace_dispatch.c b/libsrc/inplace/inplace_dispatch.c new file mode 100644 index 00000000..4e8adb6c --- /dev/null +++ b/libsrc/inplace/inplace_dispatch.c @@ -0,0 +1,287 @@ +/* Function dispatch tables for inplace. + * + * J. Cupitt, 8/2/95 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Args for im_circle. + */ +static im_arg_desc circle_args[] = { + IM_RW_IMAGE( "image" ), + IM_INPUT_INT( "cx" ), + IM_INPUT_INT( "cy" ), + IM_INPUT_INT( "radius" ), + IM_INPUT_INT( "intensity" ) +}; + +/* Call im_circle via arg vector. + */ +static int +circle_vec( im_object *argv ) +{ + int cx = *((int *) argv[1]); + int cy = *((int *) argv[2]); + int radius = *((int *) argv[3]); + int intensity = *((int *) argv[4]); + + return( im_circle( argv[0], cx, cy, radius, intensity ) ); +} + +/* Description of im_circle. + */ +static im_function circle_desc = { + "im_circle", /* Name */ + "plot circle on image", + 0, /* Flags */ + circle_vec, /* Dispatch function */ + IM_NUMBER( circle_args ), /* Size of arg list */ + circle_args /* Arg list */ +}; + +/* Args for im_insertplace. + */ +static im_arg_desc insertplace_args[] = { + IM_RW_IMAGE( "main" ), + IM_INPUT_IMAGE( "sub" ), + IM_INPUT_INT( "x" ), + IM_INPUT_INT( "y" ) +}; + +/* Call im_insertplace via arg vector. + */ +static int +insertplace_vec( im_object *argv ) +{ + int x = *((int *) argv[2]); + int y = *((int *) argv[3]); + + return( im_insertplace( argv[0], argv[1], x, y ) ); +} + +/* Description of im_insertplace. + */ +static im_function insertplace_desc = { + "im_insertplace", /* Name */ + "draw image sub inside image main at position (x,y)", + 0, /* Flags */ + insertplace_vec, /* Dispatch function */ + IM_NUMBER( insertplace_args ), /* Size of arg list */ + insertplace_args /* Arg list */ +}; + +/* Args for im_line. + */ +static im_arg_desc line_args[] = { + IM_RW_IMAGE( "im" ), + IM_INPUT_INT( "x1" ), + IM_INPUT_INT( "y1" ), + IM_INPUT_INT( "x2" ), + IM_INPUT_INT( "y2" ), + IM_INPUT_INT( "pelval" ) +}; + +/* Call im_line via arg vector. + */ +static int +line_vec( im_object *argv ) +{ + int x1 = *((int *) argv[1]); + int y1 = *((int *) argv[2]); + int x2 = *((int *) argv[3]); + int y2 = *((int *) argv[4]); + int pel = *((int *) argv[5]); + + return( im_line( argv[0], x1, y1, x2, y2, pel ) ); +} + +/* Description of im_line. + */ +static im_function line_desc = { + "im_line", /* Name */ + "draw line between points (x1,y1) and (x2,y2)", + 0, /* Flags */ + line_vec, /* Dispatch function */ + IM_NUMBER( line_args ), /* Size of arg list */ + line_args /* Arg list */ +}; + +/* Args for im_lineset. + */ +static im_arg_desc lineset_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_IMAGE( "mask" ), + IM_INPUT_IMAGE( "ink" ), + IM_INPUT_INTVEC( "x1" ), + IM_INPUT_INTVEC( "y1" ), + IM_INPUT_INTVEC( "x2" ), + IM_INPUT_INTVEC( "y2" ) +}; + +/* Call im_lineset via arg vector. + */ +static int +lineset_vec( im_object *argv ) +{ + im_intvec_object *x1v = (im_intvec_object *) argv[4]; + im_intvec_object *y1v = (im_intvec_object *) argv[5]; + im_intvec_object *x2v = (im_intvec_object *) argv[6]; + im_intvec_object *y2v = (im_intvec_object *) argv[7]; + + if( x1v->n != y1v->n || x1v->n != x2v->n || x1v->n != y2v->n ) { + im_error( "im_lineset", _( "vectors not same length" ) ); + return( -1 ); + } + + return( im_lineset( argv[0], argv[1], argv[2], argv[3], + x1v->n, x1v->vec, y1v->vec, x2v->vec, y2v->vec ) ); +} + +/* Description of im_lineset. + */ +static im_function lineset_desc = { + "im_lineset", /* Name */ + "draw line between points (x1,y1) and (x2,y2)", + 0, /* Flags */ + lineset_vec, /* Dispatch function */ + IM_NUMBER( lineset_args ), /* Size of arg list */ + lineset_args /* Arg list */ +}; + +/* Calculate a pixel for an image from a vec of double. Valid while im is + * valid. + */ +static PEL * +vector_to_ink( IMAGE *im, double *vec ) +{ + const int n = im->Bands; + + IMAGE *t[3]; + double *zeros; + int i; + + if( im_open_local_array( im, t, 3, "vector_to_ink", "t" ) || + !(zeros = IM_ARRAY( im, n, double )) ) + return( NULL ); + for( i = 0; i < n; i++ ) + zeros[i] = 0.0; + + if( im_black( t[0], 1, 1, n ) || + im_lintra_vec( n, zeros, t[0], vec, t[1] ) || + im_clip2fmt( t[1], t[2], im->BandFmt ) ) + return( NULL ); + + return( (PEL *) t[2]->data ); +} + +/* Args for im_flood_blob_copy(). + */ +static im_arg_desc flood_blob_copy_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "start_x" ), + IM_INPUT_INT( "start_y" ), + IM_INPUT_DOUBLEVEC( "ink" ) +}; + +/* Call im_flood_blob_copy() via arg vector. + */ +static int +flood_blob_copy_vec( im_object *argv ) +{ + IMAGE *in = argv[0]; + IMAGE *out = argv[1]; + int start_x = *((int *) argv[2]); + int start_y = *((int *) argv[3]); + im_doublevec_object *dv = (im_doublevec_object *) argv[4]; + + PEL *ink; + + if( dv->n != in->Bands ) { + im_error( "im_flood_blob_copy", _( "bad vector length" ) ); + return( -1 ); + } + if( !(ink = vector_to_ink( in, dv->vec )) ) + return( -1 ); + + return( im_flood_blob_copy( in, out, start_x, start_y, ink ) ); +} + +/* Description of im_flood_blob_copy(). + */ +static im_function flood_blob_copy_desc = { + "im_flood_blob_copy", /* Name */ + "flood with ink from start_x, start_y while pixel == start pixel", + 0, /* Flags */ + flood_blob_copy_vec, /* Dispatch function */ + IM_NUMBER( flood_blob_copy_args ),/* Size of arg list */ + flood_blob_copy_args /* Arg list */ +}; + +/* To do: + * these all need some kind of pel type + * + im_flood.c + im_paintrect.c + im_plotmask.c + line_draw.c + plot_point.c + smudge_area.c + * + */ + +/* Package up all these functions. + */ +static im_function *inplace_list[] = { + &circle_desc, + &flood_blob_copy_desc, + &insertplace_desc, + &line_desc, + &lineset_desc +}; + +/* Package of functions. + */ +im_package im__inplace = { + "inplace", + IM_NUMBER( inplace_list ), + inplace_list +}; diff --git a/libsrc/inplace/line_draw.c b/libsrc/inplace/line_draw.c new file mode 100644 index 00000000..c83ea94c --- /dev/null +++ b/libsrc/inplace/line_draw.c @@ -0,0 +1,449 @@ +/* @(#) Line drawer. Faster than the old im_line. Any number of bands, + * @(#) any type including complex. Instead of passing a PEL value, pass a + * @(#) pointer to the pel value you wish to plot. The correct number of + * @(#) bytes must be there! Both start and end points should be in the + * @(#) image. + * @(#) + * @(#) int + * @(#) im_fastline( im, x1, y1, x2, y2, pel ) + * @(#) IMAGE *im; + * @(#) int x1, x2, y1, y2; + * @(#) PEL *pel; + * @(#) + * @(#) As above, but rather than plotting a point, call a passed function + * @(#) for every point on the line. Up to three extra args passed down too. + * @(#) If the passed function returns non-zero, im_fastlineuser stops and + * @(#) returns non-zero. Start and end points may be outside the image - + * @(#) clipping is the responsibility of the user function. + * @(#) + * @(#) int + * @(#) im_fastlineuser( im, x1, y1, x2, y2, plot_fn, + * @(#) client1, client2, client3 ) + * @(#) IMAGE *im; + * @(#) int x1, x2, y1, y2; + * @(#) int (*plot_fn)(); + * @(#) void *client1, *client2, *client3; + * @(#) + * @(#) int + * @(#) plot_fn( im, x, y, client1, client2, client3 ) + * @(#) IMAGE *im; + * @(#) int x, y; + * @(#) void *client1, *client2, *client3; + * @(#) + * + * Copyright: J. Cupitt + * Written: 15/06/1992 + * Modified : 22/10/92 - clipping constraints changed + * 22/7/93 JC + * - im_incheck() added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define SWAP(A,B) {int t; t = (A); (A) = (B); (B) = t;} + +/* Draw a line on a image. + */ +int +im_fastline( IMAGE *im, int x1, int y1, int x2, int y2, PEL *pel ) +{ + int es = im->Bbits >> 3; + int ps = es * im->Bands; + int ls = ps * im->Xsize; + PEL *p; + + int x, y, dx, dy; + int err; + int b; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Check coordinates in range. + */ + if( x1 > im->Xsize || x1 < 0 || + y1 > im->Ysize || y1 < 0 || + x2 > im->Xsize || x2 < 0 || + y2 > im->Ysize || y2 < 0 ) { + im_errormsg( "im_fastline: invalid line cooordinates" ); + return( -1 ); + } + + /* Find offsets. + */ + dx = x2 - x1; + dy = y2 - y1; + + /* Swap endpoints to reduce number of cases. + */ + if( abs( dx ) >= abs( dy ) && dx < 0 ) { + /* Swap to get all x greater or equal cases going to the + * right. Do diagonals here .. just have up and right and down + * and right now. + */ + SWAP( x1, x2 ); + SWAP( y1, y2 ); + } + else if( abs( dx ) < abs( dy ) && dy < 0 ) { + /* Swap to get all y greater cases going down the screen. + */ + SWAP( x1, x2 ); + SWAP( y1, y2 ); + } + + /* Recalculate dx, dy. + */ + dx = x2 - x1; + dy = y2 - y1; + + /* Start point and offset. + */ + x = x1; + y = y1; + p = (PEL *) im->data + x * ps + y * ls; + + /* Plot point macro. + */ +#define PLOT \ + for( b = 0; b < ps; b++ ) \ + p[b] = pel[b]; + + /* Special case: zero width and height is single point. + */ + if( dx == 0 && dy == 0 ) { + PLOT; + } + /* Special case vertical and horizontal lines for speed. + */ + else if( dx == 0 ) { + /* Vertical line going down. + */ + for( ; y <= y2; y++ ) { + PLOT; + p += ls; + } + } + else if( dy == 0 ) { + /* Horizontal line to the right. + */ + for( ; x <= x2; x++ ) { + PLOT; + p += ps; + } + } + /* Special case diagonal lines. + */ + else if( abs( dy ) == abs( dx ) && dy > 0 ) { + /* Diagonal line going down and right. + */ + for( ; x <= x2; x++ ) { + PLOT; + p += ps + ls; + } + } + else if( abs( dy ) == abs( dx ) && dy < 0 ) { + /* Diagonal line going up and right. + */ + for( ; x <= x2; x++ ) { + PLOT; + p += ps - ls; + } + } + else if( abs( dy ) < abs( dx ) && dy > 0 ) { + /* Between -45 and 0 degrees. + */ + for( err = 0; x <= x2; x++ ) { + PLOT; + p += ps; + err += dy; + if( err >= dx ) { + err -= dx; + p += ls; + } + } + } + else if( abs( dy ) < abs( dx ) && dy < 0 ) { + /* Between 0 and 45 degrees. + */ + for( err = 0; x <= x2; x++ ) { + PLOT; + p += ps; + err -= dy; + if( err >= dx ) { + err -= dx; + p -= ls; + } + } + } + else if( abs( dy ) > abs( dx ) && dx > 0 ) { + /* Between -45 and -90 degrees. + */ + for( err = 0; y <= y2; y++ ) { + PLOT; + p += ls; + err += dx; + if( err >= dy ) { + err -= dy; + p += ps; + } + } + } + else if( abs( dy ) > abs( dx ) && dx < 0 ) { + /* Between -90 and -135 degrees. + */ + for( err = 0; y <= y2; y++ ) { + PLOT; + p += ls; + err -= dx; + if( err >= dy ) { + err -= dy; + p -= ps; + } + } + } + else + error_exit( "internal error #9872659823475982375" ); + + im_invalidate( im ); + + return( 0 ); +} + +/* Draw a line on a image with a user plot function. We do no clipping: the + * user function should check ranges for each pixel when it is called. + */ +int +im_fastlineuser( IMAGE *im, + int x1, int y1, int x2, int y2, + int (*fn)(), void *client1, void *client2, void *client3 ) +{ + int x, y, dx, dy; + int err; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Find offsets. + */ + dx = x2 - x1; + dy = y2 - y1; + + /* Swap endpoints to reduce number of cases. + */ + if( abs( dx ) >= abs( dy ) && dx < 0 ) { + /* Swap to get all x greater or equal cases going to the + * right. Do diagonals here .. just have up and right and down + * and right now. + */ + SWAP( x1, x2 ); + SWAP( y1, y2 ); + } + else if( abs( dx ) < abs( dy ) && dy < 0 ) { + /* Swap to get all y greater cases going down the screen. + */ + SWAP( x1, x2 ); + SWAP( y1, y2 ); + } + + /* Recalculate dx, dy. + */ + dx = x2 - x1; + dy = y2 - y1; + + /* Start point and offset. + */ + x = x1; + y = y1; + + /* Special case: zero width and height is single point. + */ + if( dx == 0 && dy == 0 ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + } + /* Special case vertical and horizontal lines for speed. + */ + else if( dx == 0 ) { + /* Vertical line going down. + */ + for( ; y <= y2; y++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + } + } + else if( dy == 0 ) { + /* Horizontal line to the right. + */ + for( ; x <= x2; x++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + } + } + /* Special case diagonal lines. + */ + else if( abs( dy ) == abs( dx ) && dy > 0 ) { + /* Diagonal line going down and right. + */ + for( ; x <= x2; x++, y++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + } + } + else if( abs( dy ) == abs( dx ) && dy < 0 ) { + /* Diagonal line going up and right. + */ + for( ; x <= x2; x++, y-- ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + } + } + else if( abs( dy ) < abs( dx ) && dy > 0 ) { + /* Between -45 and 0 degrees. + */ + for( err = 0; x <= x2; x++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + err += dy; + if( err >= dx ) { + err -= dx; + y++; + } + } + } + else if( abs( dy ) < abs( dx ) && dy < 0 ) { + /* Between 0 and 45 degrees. + */ + for( err = 0; x <= x2; x++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + err -= dy; + if( err >= dx ) { + err -= dx; + y--; + } + } + } + else if( abs( dy ) > abs( dx ) && dx > 0 ) { + /* Between -45 and -90 degrees. + */ + for( err = 0; y <= y2; y++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + err += dx; + if( err >= dy ) { + err -= dy; + x++; + } + } + } + else if( abs( dy ) > abs( dx ) && dx < 0 ) { + /* Between -90 and -135 degrees. + */ + for( err = 0; y <= y2; y++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + err -= dx; + if( err >= dy ) { + err -= dy; + x--; + } + } + } + else + error_exit( "internal error #9872659823475982375" ); + + im_invalidate( im ); + + return( 0 ); +} + +/* Draw a set of lines with an ink and a mask. A non-inplace operation, handy + * for nip2. + */ +int +im_lineset( IMAGE *in, IMAGE *out, IMAGE *mask, IMAGE *ink, + int n, int *x1v, int *y1v, int *x2v, int *y2v ) +{ + Rect mask_rect; + int i; + + if( mask->Bands != 1 || mask->BandFmt != IM_BANDFMT_UCHAR || + mask->Coding != IM_CODING_NONE ) { + im_error( "im_lineset", + _( "mask image not 1 band 8 bit uncoded" ) ); + return( -1 ); + } + if( ink->Bands != in->Bands || ink->BandFmt != in->BandFmt || + ink->Coding != in->Coding ) { + im_error( "im_lineset", + _( "ink image does not match in image" ) ); + return( -1 ); + } + if( ink->Xsize != 1 || ink->Ysize != 1 ) { + im_error( "im_lineset", _( "ink image not 1x1 pixels" ) ); + return( -1 ); + } + + /* Copy the image thenm fastline to it ... this will render to a "t" + * usually. + */ + if( im_incheck( mask ) || + im_incheck( ink ) || + im_copy( in, out ) ) + return( -1 ); + + mask_rect.left = mask->Xsize / 2; + mask_rect.top = mask->Ysize / 2; + mask_rect.width = mask->Xsize; + mask_rect.height = mask->Ysize; + + for( i = 0; i < n; i++ ) { + if( im_fastlineuser( out, x1v[i], y1v[i], x2v[i], y2v[i], + im_plotmask, ink->data, mask->data, &mask_rect ) ) + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/inplace/man3/Makefile.am b/libsrc/inplace/man3/Makefile.am new file mode 100644 index 00000000..d8379a52 --- /dev/null +++ b/libsrc/inplace/man3/Makefile.am @@ -0,0 +1,18 @@ +man_MANS = \ + im_circle.3 \ + im_fastline.3 \ + im_fastlineuser.3 \ + im_flood.3 \ + im_flood_blob.3 \ + im_insertplace.3 \ + im_line.3 \ + im_lineset.3 \ + im_paintrect.3 \ + im_plotmask.3 \ + im_plotpoint.3 \ + im_readpoint.3 \ + im_smear.3 \ + im_smudge.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/inplace/man3/im_circle.3 b/libsrc/inplace/man3/im_circle.3 new file mode 100644 index 00000000..0306aad5 --- /dev/null +++ b/libsrc/inplace/man3/im_circle.3 @@ -0,0 +1,33 @@ +.TH IM_CIRCLE 3 "10 May 1991" +.SH NAME +im_circle \- draws a circle within an image file +.SH SYNOPSIS +.B #include + +.B int im_circle(im, cx, cy, radius, intensity) +.br +.B IMAGE *im; +.br +.B int cx, cy; +.br +.B int radius, intensity; +.SH DESCRIPTION +.B im_circle() +draws a circle on top of an image pointed by the image descriptor im. +Input im should be one band unsigned char image. + +It must be stressed that a call to this function overwrites the content +of the original image at the points corresponding to the drawn circle. +The centre of the circle is at point (cx, cy) and has radius radius and +the intensity of the produced circle is given by the int intensity (between +0 and 255). If the circle does not fit in the original image, an error code +is returned. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_line(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/inplace/man3/im_fastline.3 b/libsrc/inplace/man3/im_fastline.3 new file mode 100644 index 00000000..24a57b00 --- /dev/null +++ b/libsrc/inplace/man3/im_fastline.3 @@ -0,0 +1 @@ +.so man3/im_paintrect.3 diff --git a/libsrc/inplace/man3/im_fastlineuser.3 b/libsrc/inplace/man3/im_fastlineuser.3 new file mode 100644 index 00000000..24a57b00 --- /dev/null +++ b/libsrc/inplace/man3/im_fastlineuser.3 @@ -0,0 +1 @@ +.so man3/im_paintrect.3 diff --git a/libsrc/inplace/man3/im_flood.3 b/libsrc/inplace/man3/im_flood.3 new file mode 100644 index 00000000..a261e53e --- /dev/null +++ b/libsrc/inplace/man3/im_flood.3 @@ -0,0 +1,41 @@ +.TH IM_FLOOD 3 "30 October 1992" +.SH NAME +im_flood, im_flood_blob \- flood a area +.SH SYNOPSIS +.B #include +.B #include + +int im_flood( im, x, y, ink, dout ) +.br +IMAGE *im; +.br +int x, y; +.br +PEL *ink; +.br +Rect *dout; + +int im_flood_blob( im, x, y, ink, dout ) +.br +IMAGE *im; +.br +int x, y; +.br +PEL *ink; +.br +Rect *dout; + +.SH DESCRIPTION +.B im_flood() +fills an enclosed area from a starting point, painting ink into 4-way +connected pels whose colour is not equal to ink. + +.B im_flood_blob() +floods with the ink colour, finding pels 4-way connected to the start pel +which are the same colour as the start pel. It is useful for changing the +colour of a blob of pels which all have the same value. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_insertplace(3), im_smudge(3). diff --git a/libsrc/inplace/man3/im_flood_blob.3 b/libsrc/inplace/man3/im_flood_blob.3 new file mode 100644 index 00000000..dac0a2ee --- /dev/null +++ b/libsrc/inplace/man3/im_flood_blob.3 @@ -0,0 +1 @@ +.so man3/im_flood.3 diff --git a/libsrc/inplace/man3/im_insertplace.3 b/libsrc/inplace/man3/im_insertplace.3 new file mode 100644 index 00000000..c3cb9dba --- /dev/null +++ b/libsrc/inplace/man3/im_insertplace.3 @@ -0,0 +1,27 @@ +.TH IM_INSERTPLACE 3 "30 October 1992" +.SH NAME +im_insertplace \- paste small images into big images +.SH SYNOPSIS +.B #include + +im_insertplace( big, small, x, y ) +.br +IMAGE *big, *small; +.br +int x, y; + +.SH DESCRIPTION +Paste image small into image big, with small's left-left-hand corner at (x,y) +in image big. Image big must be large enough to hold all of small! No +clipping. Images may have any type, but must both have the same type. + +This is an in-place operation. Big is damaged! Careful. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE\ ALSO +im_fastline(3), im_smudge(3). +.SH COPYRIGHT +.br +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/inplace/man3/im_line.3 b/libsrc/inplace/man3/im_line.3 new file mode 100644 index 00000000..51f5a998 --- /dev/null +++ b/libsrc/inplace/man3/im_line.3 @@ -0,0 +1,29 @@ +.TH IM_LINE 3 "10 May 1991" +.SH NAME +im_line \- writes a line on a vasari image +.SH SYNOPSIS +.B #include + +.B int im_line(image, x1, y1, x2, y2, pelval) +.br +.B IMAGE *image; +.br +.B int x1, x2, y1, y2, pelval; +.SH DESCRIPTION +im_line() +draws a line in the image held by image. The start of the line is at point +(x1,y1) and the end is at (x2,y2). The intensity of the drawn line is pelval. +Input image should be one byte unsigned char. The function overwrites any +data in the file, so take care when applying it. + +This function is here for compatibility only. You should use im_fastline(3) or +im_fastlineuser(3). +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_grey(3), im_fastline(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/inplace/man3/im_lineset.3 b/libsrc/inplace/man3/im_lineset.3 new file mode 100644 index 00000000..24a57b00 --- /dev/null +++ b/libsrc/inplace/man3/im_lineset.3 @@ -0,0 +1 @@ +.so man3/im_paintrect.3 diff --git a/libsrc/inplace/man3/im_paintrect.3 b/libsrc/inplace/man3/im_paintrect.3 new file mode 100644 index 00000000..2c9381d6 --- /dev/null +++ b/libsrc/inplace/man3/im_paintrect.3 @@ -0,0 +1,126 @@ +.TH IM_AND 3 "30 October 1992" +.SH NAME +im_paintrect, im_plotmask, im_readpoint, im_plotpoint, im_fastline, +im_fastlineuser \- suck pels up, and paint them down somewhere else +.SH SYNOPSIS +.B #include +.br +.B #include + +int im_readpoint( im, x, y, ink ) +.br +IMAGE *im; +.br +int x, y; +.br +PEL *ink; + +int im_plotpoint( im, x, y, ink ) +.br +IMAGE *im; +.br +int x, y; +.br +PEL *ink; + +int im_plotmask( im, ix, iy, ink, mask, r ) +.br +IMAGE *im; +.br +int ix, iy; +.br +PEL *ink; +.br +PEL *mask; +.br +Rect *r; + +int im_paintrect( im, r, ink ) +.br +IMAGE *im; +.br +Rect *r; +.br +PEL *ink; + +int im_fastline( im, x1, y1, x2, y2, ink ) +.br +IMAGE *im; +.br +int x1, y1, x2, y2; +.br +PEL *ink; + +int im_fastlineuser( im, x1, y1, x2, y2, +.br + plot_fn, client1, client2, client3 ) +.br +IMAGE *im; +.br +int x1, y1, x2, y2; +.br +int (*plot_fn)(); +.br +void *client1, *client2, *client3; + +int plot_fn( im, x, y, client1, client2, client3 ) +.br +IMAGE *im; +.br +int x, y; +.br +void *client1, *client2, *client3; + +int im_lineset( IMAGE *in, IMAGE *out, IMAGE *mask, IMAGE *ink, +.br + int n, int *x1v, int *y1v, int *x2v, int *y2v ) + +.SH DESCRIPTION +.B im_readpoint(3) +sucks a pel from position (x,y) into a buffer; all other +functions paint that buffer back again in various ways. These functions will +work for an image of any type, since they rely on +.B im_readpoint(3) +for their +data. If you read a pel from one image and paint with it to another, it is +your responsibility to check that the two images have identical pels. + +All these functions are `in place,' that is, they write directly to the +output image! Be very careful, you can destroy data. + +.B im_plotmask(3) +takes an array of 0/255s and a Rect describing the size and +offset of the array. It adds (ix,iy) to the array offset, and then paint pels +into all array positions which are non-zero. Rect need not lie inside the +image - the function clips carefully at the edges. + +.B im_paintrect(3) +fills the rectangle described with ink. The Rect can be any +size and at any position - the function clipps against the edges of the +image. + +.B im_fastline(3) +is a replacement for +.B im_line(3) +which plots the specified ink in +the image. The start and end points must lie entirely inside the image. + +.B im_fastlineuser(3) +does not plot points, instead it calls a user plot function +for every position along the line. There is no clipping - the endpoints may be +anywhere at all. Three client values are carried around and passed into the +user function. + +.B im_lineset(3) +copies the image and draws a set of lines on the copy. The lines are drawn +using the mask image as a brush to apply the ink image. Useful for nip2. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE\ ALSO +im_insertplace(3), im_smudge(3). +.SH COPYRIGHT +.br +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/inplace/man3/im_plotmask.3 b/libsrc/inplace/man3/im_plotmask.3 new file mode 100644 index 00000000..24a57b00 --- /dev/null +++ b/libsrc/inplace/man3/im_plotmask.3 @@ -0,0 +1 @@ +.so man3/im_paintrect.3 diff --git a/libsrc/inplace/man3/im_plotpoint.3 b/libsrc/inplace/man3/im_plotpoint.3 new file mode 100644 index 00000000..24a57b00 --- /dev/null +++ b/libsrc/inplace/man3/im_plotpoint.3 @@ -0,0 +1 @@ +.so man3/im_paintrect.3 diff --git a/libsrc/inplace/man3/im_readpoint.3 b/libsrc/inplace/man3/im_readpoint.3 new file mode 100644 index 00000000..24a57b00 --- /dev/null +++ b/libsrc/inplace/man3/im_readpoint.3 @@ -0,0 +1 @@ +.so man3/im_paintrect.3 diff --git a/libsrc/inplace/man3/im_smear.3 b/libsrc/inplace/man3/im_smear.3 new file mode 100644 index 00000000..b180aebc --- /dev/null +++ b/libsrc/inplace/man3/im_smear.3 @@ -0,0 +1 @@ +.so man3/im_smudge.3 diff --git a/libsrc/inplace/man3/im_smudge.3 b/libsrc/inplace/man3/im_smudge.3 new file mode 100644 index 00000000..d7825ca4 --- /dev/null +++ b/libsrc/inplace/man3/im_smudge.3 @@ -0,0 +1,42 @@ +.TH IM_SMUDGE 3 "30 October 1992" +.SH NAME +im_smudge, im_smear \- filter and smudge in place +.SH SYNOPSIS +.B #include +.br +.B #include + +int +.br +im_smudge( im, ix, iy, r ) +.br +IMAGE *im; +.br +int ix, iy; +.br +Rect *r; + +int +.br +im_smear( im, ix, iy, r ) +.br +IMAGE *im; +.br +int ix, iy; +.br +Rect *r; +.SH DESCRIPTION +im_smudge() performs a low-pass filter of the pels inside rect r. Rect r is +clipped against the edges of the image. im_smear() is not very useful. + +Both these functions are `in place,' that is, they write directly to the +output image! Be very careful, you can destroy data. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE\ ALSO +im_insertplace(3), im_readpoint(3). +.SH COPYRIGHT +.br +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/inplace/plot_point.c b/libsrc/inplace/plot_point.c new file mode 100644 index 00000000..379f3157 --- /dev/null +++ b/libsrc/inplace/plot_point.c @@ -0,0 +1,119 @@ +/* @(#) Read a pel out of an image and into a buffer, plot a pel back into + * @(#) the image again. + * @(#) + * @(#) int + * @(#) im_readpoint( IMAGE *im, int x, int y, PEL *ink ) + * @(#) + * @(#) int + * @(#) im_plotpoint( IMAGE *im, int x, int y, PEL *ink ) + * @(#) + * + * Copyright: J. Cupitt + * Written: 15/06/1992 + * 22/7/93 JC + * - im_incheck() added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Read a colour from an image. + */ +int +im_readpoint( IMAGE *im, int x, int y, PEL *pel ) +{ + int es = im->Bbits >> 3; + int ps = es * im->Bands; + int ls = ps * im->Xsize; + int b; + PEL *from; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Check coordinates in range. + */ + if( x > im->Xsize || x < 0 || y > im->Ysize || y < 0 ) { + im_errormsg( "im_readpoint: invalid cooordinates" ); + return( 1 ); + } + + /* Suck single pixel. + */ + from = (PEL *) im->data + x * ps + y * ls; + for( b = 0; b < ps; b++ ) + *pel++ = *from++; + + return( 0 ); +} + +/* Plot a point in an image. + */ +int +im_plotpoint( IMAGE *im, int x, int y, PEL *pel ) +{ + int es = im->Bbits >> 3; + int ps = es * im->Bands; + int ls = ps * im->Xsize; + int b; + PEL *to; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Check coordinates in range. + */ + if( x > im->Xsize || x < 0 || y > im->Ysize || y < 0 ) + return( 0 ); + + /* Paint single pixel. + */ + to = (PEL *) im->data + x * ps + y * ls; + for( b = 0; b < ps; b++ ) + *to++ = *pel++; + + im_invalidate( im ); + + return( 0 ); +} diff --git a/libsrc/inplace/smudge_area.c b/libsrc/inplace/smudge_area.c new file mode 100644 index 00000000..73730fa9 --- /dev/null +++ b/libsrc/inplace/smudge_area.c @@ -0,0 +1,319 @@ +/* @(#) Smudge and smear a piece of image. Smudge is a low-pass filter, + * @(#) smear is an early version of smudge which contains a bug: it + * @(#) pushes pels to the left a little too. Looks cute though. + * @(#) + * @(#) r is the area to smudge/smear. Clipped against the image edges + * @(#) properly. + * @(#) + * @(#) int + * @(#) im_smudge( IMAGE *im, int ix, int iy, Rect *r ) + * @(#) + * @(#) int + * @(#) im_smear( IMAGE *im, int ix, int iy, Rect *r ) + * @(#) + * + * Copyright: J. Cupitt + * Written: 15/06/1992 + * 22/7/93 JC + * - im_incheck() added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * ? JC + * - im_makerw() changed to im_rwcheck() + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Smudge a section of an IMAGE. Smudge area r offset by x, y. Take average + * of pixels in 3x3 area surrounding current pixel for every pixel in r. We do + * not change the outermost pixels in the image, although we do read them. + */ +int +im_smudge( IMAGE *im, int ix, int iy, Rect *r ) +{ + int x, y, a, b, c; + int ba = im->Bands; + int el = ba * im->Xsize; + Rect area, image, clipped; + double total[ 256 ]; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Don't do the margins. + */ + area = *r; + area.left += ix; + area.top += iy; + image.left = 0; + image.top = 0; + image.width = im->Xsize; + image.height = im->Ysize; + im_rect_marginadjust( &image, -1 ); + im_rect_intersectrect( &area, &image, &clipped ); + + /* Any left? + */ + if( im_rect_isempty( &clipped ) ) + return( 0 ); + +/* What we do for each type. + */ +#define SMUDGE(TYPE) \ + for( y = clipped.top; y < clipped.top + clipped.height; y++ ) \ + for( x = clipped.left; \ + x < clipped.left + clipped.width; x++ ) { \ + TYPE *to = (TYPE *) im->data + x * ba + y * el; \ + TYPE *from = to - el - ba; \ + TYPE *f; \ + \ + for( a = 0; a < ba; a++ ) \ + total[a] = 0.0; \ + \ + for( a = 0; a < 3; a++ ) { \ + f = from; \ + for( b = 0; b < 3; b++ ) \ + for( c = 0; c < ba; c++ ) \ + total[c] += *f++; \ + from += el; \ + } \ + \ + for( a = 0; a < ba; a++ ) \ + to[a] = (16 * (double) to[a] + total[a]) \ + / 25.0; \ + } + + /* Loop through the remaining pixels. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + SMUDGE(unsigned char); + break; + + case IM_BANDFMT_CHAR: + SMUDGE(char); + break; + + case IM_BANDFMT_USHORT: + SMUDGE(unsigned short); + break; + + case IM_BANDFMT_SHORT: + SMUDGE(short); + break; + + case IM_BANDFMT_UINT: + SMUDGE(unsigned int); + break; + + case IM_BANDFMT_INT: + SMUDGE(int); + break; + + case IM_BANDFMT_FLOAT: + SMUDGE(float); + break; + + case IM_BANDFMT_DOUBLE: + SMUDGE(double); + break; + + /* Do complex types too. Just treat as float and double, but with + * twice the number of bands. + */ + case IM_BANDFMT_COMPLEX: + /* Twice number of bands: double size and bands. + */ + ba *= 2; + el *= 2; + + SMUDGE(float); + + break; + + case IM_BANDFMT_DPCOMPLEX: + /* Twice number of bands: double size and bands. + */ + ba *= 2; + el *= 2; + + SMUDGE(double); + + break; + + default: + im_error( "im_smudge", _( "unknown band format" ) ); + return( -1 ); + + } + + im_invalidate( im ); + + return( 0 ); +} + +/* Smear a section of an IMAGE. As above, but shift it left a bit. + */ +int +im_smear( IMAGE *im, int ix, int iy, Rect *r ) +{ + int x, y, a, b, c; + int ba = im->Bands; + int el = ba * im->Xsize; + Rect area, image, clipped; + double total[ 256 ]; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Don't do the margins. + */ + area = *r; + area.left += ix; + area.top += iy; + image.left = 0; + image.top = 0; + image.width = im->Xsize; + image.height = im->Ysize; + im_rect_marginadjust( &image, -1 ); + image.left--; + im_rect_intersectrect( &area, &image, &clipped ); + + /* Any left? + */ + if( im_rect_isempty( &clipped ) ) + return( 0 ); + +/* What we do for each type. + */ +#define SMEAR(TYPE) \ + for( y = clipped.top; y < clipped.top + clipped.height; y++ ) \ + for( x = clipped.left; \ + x < clipped.left + clipped.width; x++ ) { \ + TYPE *to = (TYPE *) im->data + x * ba + y * el; \ + TYPE *from = to - el; \ + TYPE *f; \ + \ + for( a = 0; a < ba; a++ ) \ + total[a] = 0.0; \ + \ + for( a = 0; a < 3; a++ ) { \ + f = from; \ + for( b = 0; b < 3; b++ ) \ + for( c = 0; c < ba; c++ ) \ + total[c] += *f++; \ + from += el; \ + } \ + \ + for( a = 0; a < ba; a++ ) \ + to[a] = (40 * (double) to[a+ba] + total[a]) \ + / 49.0; \ + } + + /* Loop through the remaining pixels. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + SMEAR(unsigned char); + break; + + case IM_BANDFMT_CHAR: + SMEAR(char); + break; + + case IM_BANDFMT_USHORT: + SMEAR(unsigned short); + break; + + case IM_BANDFMT_SHORT: + SMEAR(short); + break; + + case IM_BANDFMT_UINT: + SMEAR(unsigned int); + break; + + case IM_BANDFMT_INT: + SMEAR(int); + break; + + case IM_BANDFMT_FLOAT: + SMEAR(float); + break; + + case IM_BANDFMT_DOUBLE: + SMEAR(double); + break; + + /* Do complex types too. Just treat as float and double, but with + * twice the number of bands. + */ + case IM_BANDFMT_COMPLEX: + /* Twice number of bands: double size and bands. + */ + ba *= 2; + el *= 2; + + SMEAR(float); + + break; + + case IM_BANDFMT_DPCOMPLEX: + /* Twice number of bands: double size and bands. + */ + ba *= 2; + el *= 2; + + SMEAR(double); + + break; + + default: + im_error( "im_smear", _( "unknown band format" ) ); + return( -1 ); + } + + im_invalidate( im ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/Makefile.am b/libsrc/iofuncs/Makefile.am new file mode 100644 index 00000000..0a1c17f0 --- /dev/null +++ b/libsrc/iofuncs/Makefile.am @@ -0,0 +1,64 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libiofuncs.la + +libiofuncs_la_SOURCES = \ + meta.c \ + base64.h \ + base64.c \ + callback.c \ + debug.c \ + dispatch_types.c \ + error.c \ + error_exit.c \ + im_binfile.c \ + im_bits_of_fmt.c \ + im_close.c \ + im_cp_desc.c \ + im_debugim.c \ + im_demand_hint.c \ + im_desc_hd.c \ + im_generate.c \ + im_header.c \ + im_histlin.c \ + im_image.c \ + im_init.c \ + im_initdesc.c \ + im_iocheck.c \ + im_iterate.c \ + im_makerw.c \ + im_mapfile.c \ + im_openin.c \ + im_open.c \ + im_openout.c \ + im_partial.c \ + im_piocheck.c \ + im_prepare.c \ + im_printdesc.c \ + im_printlines.c \ + im_readhist.c \ + im_render.c \ + im_setbox.c \ + im_setbuf.c \ + im_setupout.c \ + im_unmapfile.c \ + im_updatehist.c \ + im_guess_prefix.c \ + im_wrapmany.c \ + im_wrapone.c \ + im_writeline.c \ + memory.c \ + package.c \ + predicate.c \ + region.c \ + rect.c \ + semaphore.c \ + threadgroup.c \ + util.c \ + im_init_world.c \ + vbuf.c \ + window.c \ + buffer.c \ + time.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/iofuncs/base64.c b/libsrc/iofuncs/base64.c new file mode 100644 index 00000000..d0479229 --- /dev/null +++ b/libsrc/iofuncs/base64.c @@ -0,0 +1,285 @@ +/* base64.c -- Encode/decode integers in base64 format + * Created: Mon Sep 23 16:55:12 1996 by faith@dict.org + * Revised: Sat Mar 30 12:02:36 2002 by faith@dict.org + * Copyright 1996, 2002 Rickard E. Faith (faith@dict.org) + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: base64.c,v 1.5 2007/01/03 18:31:34 jcupitt Exp $ + * + * \section{Base-64 Routines} + * + * \intro These routines use the 64-character subset of International + * Alphabet IA5 discussed in RFC 1421 (printeable encoding) and RFC 1522 + * (base64 MIME). + * + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + * + */ + +/* + + Hacked for VIPS ... does any length object (not just ints), formats + base64 into 70 character lines, output to a malloc'd buffer. + + VIPS uses this to write BLOBs (like ICC profiles, for example) to the + XML that follows an image. + +Modified on: +23/7/07 JC + - oop, needed a slightly larger worst-case buffer in im__b64_encode() + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#include "base64.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static unsigned char b64_list[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +#define XX 100 + +static unsigned char b64_index[256] = { + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63, + 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX, + XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX, + XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, +}; + +/* Read (up to) 3 bytes from in. Be careful about byte ordering :-/ we need to + * end up with in[2] in the bottom few bits. + */ +static int +read24( const unsigned char *in, size_t remaining ) +{ + int bits; + int i; + + bits = 0; + for( i = 0; i < 3; i++ ) { + bits <<= 8; + if( remaining > 0 ) { + bits |= in[i]; + remaining -= 1; + } + } + + return( bits ); +} + +/* Output (up to) 24 bits as four base64 chars. Pad with '=' characters. + */ +static void +encode24( char *p, int bits, size_t remaining ) +{ + int i; + + for( i = 0; i < 4; i++ ) { + if( remaining <= 0 ) + p[i] = '='; + else { + /* Take the top 6 bits of 24. + */ + p[i] = b64_list[(bits >> 18) & 63]; + bits <<= 6; + remaining -= 6; + } + } +} + +/* Output to a malloc'd buffer, NULL on error. Try to be simple and reliable, + * rather than quick. + */ +char * +im__b64_encode( const unsigned char *data, size_t data_length ) +{ + /* Worst case: 1.333 chars per byte, plus 10% for extra carriage + * returns and stuff. And the \n\0 at the end. + */ + const size_t output_data_length = data_length * 44 / 30 + 2; + + char *buffer; + char *p; + size_t i; + int cursor; + + if( data_length <= 0 ) { + im_error( "im__b64_encode", _( "too little data" ) ); + return( NULL ); + } + if( output_data_length > 1024 * 1024 ) { + /* We shouldn't really be used for large amounts of data. + */ + im_error( "im__b64_encode", _( "too much data" ) ); + return( NULL ); + } + if( !(buffer = im_malloc( NULL, output_data_length )) ) + return( NULL ); + + p = buffer; + *p++ = '\n'; + cursor = 0; + + for( i = 0; i < data_length; i += 3 ) { + size_t remaining = data_length - i; + int bits; + + bits = read24( data + i, remaining ); + encode24( p, bits, remaining * 8 ); + p += 4; + cursor += 4; + + if( cursor >= 76 ) { + *p++ = '\n'; + cursor = 0; + } + } + if( cursor > 0 ) + *p++ = '\n'; + *p++ = '\0'; + +#ifdef DEBUG +{ + unsigned int total; + + /* Calculate a very simple checksum for debugging. + */ + for( total = 0, i = 0; i < data_length; i++ ) + total += data[i]; + + printf( "im__b64_encode: length = %d, checksum 0x%x\n", + data_length, total & 0xffff ); +} +#endif /*DEBUG*/ + + return( buffer ); +} + +/* Decode base64 back to binary in a malloc'd buffer. NULL on error. + */ +unsigned char * +im__b64_decode( const char *buffer, size_t *data_length ) +{ + const size_t buffer_length = strlen( buffer ); + + /* Worst case. + */ + const size_t output_data_length = buffer_length * 3 / 4; + + unsigned char *data; + unsigned char *p; + unsigned int bits; + int nbits; + size_t i; + + if( output_data_length > 1024 * 1024 ) { + /* We shouldn't really be used for large amounts of data. + */ + im_error( "im__b64_decode", _( "too much data" ) ); + return( NULL ); + } + + if( !(data = im_malloc( NULL, output_data_length )) ) + return( NULL ); + + p = data; + bits = 0; + nbits = 0; + + for( i = 0; i < buffer_length; i++ ) { + unsigned int val; + + if( (val = b64_index[(int) buffer[i]]) != XX ) { + bits <<= 6; + bits |= val; + nbits += 6; + + if( nbits >= 8 ) { + *p++ = (bits >> (nbits - 8)) & 0xff; + nbits -= 8; + } + } + } + + assert( p - data < output_data_length ); + + if( data_length ) + *data_length = p - data; + +#ifdef DEBUG +{ + unsigned int total; + + /* Calculate a very simple checksum for debugging. + */ + for( total = 0, i = 0; i < p - data; i++ ) + total += data[i]; + + printf( "im__b64_decode: length = %d, checksum 0x%x\n", + p - data, total & 0xffff ); +} +#endif /*DEBUG*/ + + return( data ); +} diff --git a/libsrc/iofuncs/base64.h b/libsrc/iofuncs/base64.h new file mode 100644 index 00000000..ffe09f28 --- /dev/null +++ b/libsrc/iofuncs/base64.h @@ -0,0 +1,25 @@ +/* + + Copyright (C) 1991-2005 The National Gallery + + 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 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 + + */ + +/* base64 encode/decode functions. + */ + +char *im__b64_encode( const unsigned char *data, size_t data_length ); +unsigned char *im__b64_decode( const char *buffer, size_t *data_length ); diff --git a/libsrc/iofuncs/buffer.c b/libsrc/iofuncs/buffer.c new file mode 100644 index 00000000..03dd439a --- /dev/null +++ b/libsrc/iofuncs/buffer.c @@ -0,0 +1,579 @@ +/* Manage sets of pixel buffers on an image. + * + * 30/10/06 + * - from window.c + * 2/2/07 + * - speed up the search, use our own lock (thanks Christian) + * 5/2/07 + * - split to many buffer lists per image + * 11/2/07 + * - split to a buffer hash per thread + * - reuse buffer mallocs when we can + * 20/2/07 + * - add im_buffer_cache_list_t and we can avoid some hash ops on + * done/undone + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG +#define DEBUG_CREATE + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef DEBUG +/* Track all regions here for debugging. + */ +static GSList *im__buffers_all = NULL; +#endif /*DEBUG*/ + +#ifdef DEBUG_CREATE +static int buffer_cache_n = 0; +#endif /*DEBUG_CREATE*/ + +#ifdef HAVE_THREADS +static GPrivate *thread_buffer_cache_key = NULL; +#else /*!HAVE_THREADS*/ +static im_buffer_cache_t *thread_buffer_cache = NULL; +#endif /*HAVE_THREADS*/ + +/* Only need this if we're threading and need to do a lot of start/stop. + */ +#ifdef HAVE_THREADS +static void +buffer_cache_free( im_buffer_cache_t *cache ) +{ +#ifdef DEBUG_CREATE + buffer_cache_n -= 1; + + printf( "buffer_cache_free: freeing cache %p on thread %p\n", + cache, g_thread_self() ); + printf( "\t(%d cachees left)\n", buffer_cache_n ); +#endif /*DEBUG_CREATE*/ + + IM_FREEF( g_hash_table_destroy, cache->hash ); + IM_FREE( cache ); +} +#endif /*HAVE_THREADS*/ + +static void +buffer_cache_list_free( im_buffer_cache_list_t *cache_list ) +{ + GSList *p; + + /* Need to mark undone so we don't try and take them off this hash on + * unref. + */ + for( p = cache_list->buffers; p; p = p->next ) { + im_buffer_t *buffer = (im_buffer_t *) p->data; + + buffer->done = FALSE; + } + + g_slist_free( cache_list->buffers ); + im_free( cache_list ); +} + +static im_buffer_cache_list_t * +buffer_cache_list_new( im_buffer_cache_t *cache, IMAGE *im ) +{ + im_buffer_cache_list_t *cache_list; + + if( !(cache_list = IM_NEW( NULL, im_buffer_cache_list_t )) ) + return( NULL ); + cache_list->buffers = NULL; + cache_list->thread = g_thread_self(); + cache_list->cache = cache; + cache_list->im = im; + +#ifdef DEBUG_CREATE + printf( "buffer_cache_list_new: new cache %p for thread %p\n", + cache, g_thread_self() ); + printf( "\t(%d cachees now)\n", buffer_cache_n ); +#endif /*DEBUG_CREATE*/ + + return( cache_list ); +} + +static im_buffer_cache_t * +buffer_cache_new( void ) +{ + im_buffer_cache_t *cache; + + if( !(cache = IM_NEW( NULL, im_buffer_cache_t )) ) + return( NULL ); + + cache->hash = g_hash_table_new_full( g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) buffer_cache_list_free ); + cache->thread = g_thread_self(); + +#ifdef DEBUG_CREATE + buffer_cache_n += 1; + + printf( "buffer_cache_new: new cache %p for thread %p\n", + cache, g_thread_self() ); + printf( "\t(%d cachees now)\n", buffer_cache_n ); +#endif /*DEBUG_CREATE*/ + + return( cache ); +} + +/* Get the buffer cache. + */ +static im_buffer_cache_t * +buffer_cache_get( void ) +{ + im_buffer_cache_t *cache; + +#ifdef HAVE_THREADS + if( !(cache = g_private_get( thread_buffer_cache_key )) ) { + cache = buffer_cache_new(); + g_private_set( thread_buffer_cache_key, cache ); + } +#else /*!HAVE_THREADS*/ + if( !thread_buffer_cache ) + thread_buffer_cache = buffer_cache_new(); + cache = thread_buffer_cache; +#endif /*HAVE_THREADS*/ + + return( cache ); +} + +/* Pixels have been calculated: publish for other parts of this thread to see. + */ +void +im_buffer_done( im_buffer_t *buffer ) +{ + if( !buffer->done ) { + IMAGE *im = buffer->im; + im_buffer_cache_t *cache = buffer_cache_get(); + im_buffer_cache_list_t *cache_list; + +#ifdef DEBUG + printf( "im_buffer_done: thread %p adding " + "buffer %p to cache %p\n", + g_thread_self(), buffer, cache ); +#endif /*DEBUG*/ + + /* Look up and update the buffer list. + */ + if( !(cache_list = g_hash_table_lookup( cache->hash, im )) ) { + cache_list = buffer_cache_list_new( cache, im ); + g_hash_table_insert( cache->hash, im, cache_list ); + } + + assert( !g_slist_find( cache_list->buffers, buffer ) ); + assert( cache_list->thread == cache->thread ); + + cache_list->buffers = + g_slist_prepend( cache_list->buffers, buffer ); + buffer->done = TRUE; + buffer->cache = cache; + } +} + +/* Take off the public 'done' list. + */ +void +im_buffer_undone( im_buffer_t *buffer ) +{ + if( buffer->done ) { + IMAGE *im = buffer->im; + im_buffer_cache_t *cache = buffer->cache; + im_buffer_cache_list_t *cache_list; + +#ifdef DEBUG + printf( "im_buffer_undone: thread %p removing " + "buffer %p from cache %p\n", + g_thread_self(), buffer, cache ); +#endif /*DEBUG*/ + + assert( cache->thread == g_thread_self() ); + + cache_list = g_hash_table_lookup( cache->hash, im ); + + assert( cache_list ); + assert( cache_list->thread == cache->thread ); + assert( g_slist_find( cache_list->buffers, buffer ) ); + + cache_list->buffers = + g_slist_remove( cache_list->buffers, buffer ); + buffer->done = FALSE; + buffer->cache = NULL; + +#ifdef DEBUG + printf( "im_buffer_undone: %d buffers left\n", + g_slist_length( buffers ) ); +#endif /*DEBUG*/ + } +} + +void +im_buffer_unref( im_buffer_t *buffer ) +{ +#ifdef DEBUG + printf( "** im_buffer_unref: left = %d, top = %d, " + "width = %d, height = %d (%p)\n", + buffer->area.left, buffer->area.top, + buffer->area.width, buffer->area.height, + buffer ); +#endif /*DEBUG*/ + + assert( buffer->ref_count > 0 ); + + buffer->ref_count -= 1; + + if( buffer->ref_count == 0 ) { +#ifdef DEBUG + if( !buffer->done ) + printf( "im_buffer_unref: buffer was not done\n" ); +#endif /*DEBUG*/ + + im_buffer_undone( buffer ); + + buffer->im = NULL; + IM_FREE( buffer->buf ); + buffer->bsize = 0; + im_free( buffer ); + +#ifdef DEBUG + g_mutex_lock( im__global_lock ); + assert( g_slist_find( im__buffers_all, buffer ) ); + im__buffers_all = g_slist_remove( im__buffers_all, buffer ); + printf( "%d buffers in vips\n", + g_slist_length( im__buffers_all ) ); + g_mutex_unlock( im__global_lock ); +#endif /*DEBUG*/ + } +} + +/* Make a new buffer. + */ +static im_buffer_t * +buffer_new( IMAGE *im, Rect *area ) +{ + im_buffer_t *buffer; + + if( !(buffer = IM_NEW( NULL, im_buffer_t )) ) + return( NULL ); + + buffer->ref_count = 1; + buffer->im = im; + buffer->area = *area; + buffer->done = FALSE; + buffer->cache = NULL; + buffer->invalid = FALSE; + buffer->bsize = (size_t) IM_IMAGE_SIZEOF_PEL( im ) * + area->width * area->height; + if( !(buffer->buf = im_malloc( NULL, buffer->bsize )) ) { + im_buffer_unref( buffer ); + return( NULL ); + } + +#ifdef DEBUG + printf( "** buffer_new: left = %d, top = %d, " + "width = %d, height = %d (%p)\n", + buffer->area.left, buffer->area.top, + buffer->area.width, buffer->area.height, + buffer ); +#endif /*DEBUG*/ + +#ifdef DEBUG + g_mutex_lock( im__global_lock ); + im__buffers_all = g_slist_prepend( im__buffers_all, buffer ); + printf( "%d buffers in vips\n", g_slist_length( im__buffers_all ) ); + g_mutex_unlock( im__global_lock ); +#endif /*DEBUG*/ + + return( buffer ); +} + +static int +buffer_move( im_buffer_t *buffer, Rect *area ) +{ + IMAGE *im = buffer->im; + size_t new_bsize; + + assert( buffer->ref_count == 1 ); + + buffer->area = *area; + im_buffer_undone( buffer ); + assert( !buffer->done ); + buffer->invalid = FALSE; + + new_bsize = (size_t) IM_IMAGE_SIZEOF_PEL( im ) * + area->width * area->height; + if( buffer->bsize < new_bsize ) { + buffer->bsize = new_bsize; + IM_FREE( buffer->buf ); + if( !(buffer->buf = im_malloc( NULL, buffer->bsize )) ) + return( -1 ); + } + + return( 0 ); +} + +/* Find an existing buffer that encloses area and return a ref. + */ +static im_buffer_t * +buffer_find( IMAGE *im, Rect *r ) +{ + im_buffer_cache_t *cache = buffer_cache_get(); + im_buffer_cache_list_t *cache_list; + im_buffer_t *buffer; + GSList *p; + Rect *area; + + cache_list = g_hash_table_lookup( cache->hash, im ); + p = cache_list ? cache_list->buffers : NULL; + + /* This needs to be quick :-( don't use + * im_slist_map2()/im_rect_includesrect(), do the search inline. + */ + for( ; p; p = p->next ) { + buffer = (im_buffer_t *) p->data; + area = &buffer->area; + + if( area->left <= r->left && + area->top <= r->top && + area->left + area->width >= r->left + r->width && + area->top + area->height >= r->top + r->height ) { + buffer->ref_count += 1; + +#ifdef DEBUG + printf( "im_buffer_find: left = %d, top = %d, " + "width = %d, height = %d, count = %d (%p)\n", + buffer->area.left, buffer->area.top, + buffer->area.width, buffer->area.height, + buffer->ref_count, + buffer ); +#endif /*DEBUG*/ + + break; + } + } + + if( p ) + return( buffer ); + else + return( NULL ); +} + +/* Return a ref to a buffer that encloses area. + */ +im_buffer_t * +im_buffer_ref( IMAGE *im, Rect *area ) +{ + im_buffer_t *buffer; + + if( !(buffer = buffer_find( im, area )) ) + /* No existing buffer ... make a new one. + */ + if( !(buffer = buffer_new( im, area )) ) + return( NULL ); + + return( buffer ); +} + +/* Unref old, ref new, in a single operation. Move the buffer if we can. + */ +im_buffer_t * +im_buffer_unref_ref( im_buffer_t *old_buffer, IMAGE *im, Rect *area ) +{ + im_buffer_t *buffer; + + assert( !old_buffer || old_buffer->im == im ); + + if( (buffer = buffer_find( im, area )) && !buffer->invalid ) { + /* The new area has an OK buffer already: use that. + */ + IM_FREEF( im_buffer_unref, old_buffer ); + } + else if( old_buffer && old_buffer->ref_count == 1 ) { + /* The old buffer is not shared ... we can reuse it. + */ + buffer = old_buffer; + if( buffer_move( buffer, area ) ) { + im_buffer_unref( buffer ); + return( NULL ); + } + } + else { + /* Old buffer in use ... need another. + */ + IM_FREEF( im_buffer_unref, old_buffer ); + if( !(buffer = buffer_new( im, area )) ) + return( NULL ); + } + + return( buffer ); +} + +void +im_buffer_print( im_buffer_t *buffer ) +{ + printf( "im_buffer_t: %p ref_count = %d, ", buffer, buffer->ref_count ); + printf( "im = %p, ", buffer->im ); + printf( "area.left = %d, ", buffer->area.left ); + printf( "area.top = %d, ", buffer->area.top ); + printf( "area.width = %d, ", buffer->area.width ); + printf( "area.height = %d, ", buffer->area.height ); + printf( "done = %d, ", buffer->done ); + printf( "invalid = %d, ", buffer->invalid ); + printf( "buf = %p, ", buffer->buf ); + printf( "bsize = %zd\n", buffer->bsize ); +} + +/* Make a parent/child link. + */ +void +im__link_make( IMAGE *parent, IMAGE *child ) +{ + assert( parent ); + assert( child ); + + parent->children = g_slist_prepend( parent->children, child ); + child->parents = g_slist_prepend( child->parents, parent ); +} + +/* Break link. + */ +static void * +im__link_break( IMAGE *parent, IMAGE *child ) +{ + assert( parent ); + assert( child ); + assert( g_slist_find( parent->children, child ) ); + assert( g_slist_find( child->parents, parent ) ); + + parent->children = g_slist_remove( parent->children, child ); + child->parents = g_slist_remove( child->parents, parent ); + + return( NULL ); +} + +static void * +im__link_break_rev( IMAGE *child, IMAGE *parent ) +{ + return( im__link_break( parent, child ) ); +} + +/* An IMAGE is going ... break all links. + */ +void +im__link_break_all( IMAGE *im ) +{ + im_slist_map2( im->parents, + (VSListMap2Fn) im__link_break, im, NULL ); + im_slist_map2( im->children, + (VSListMap2Fn) im__link_break_rev, im, NULL ); +} + +static void * +im__link_mapp( IMAGE *im, VSListMap2Fn fn, int *serial, void *a, void *b ) +{ + void *res; + + /* Loop? + */ + if( im->serial == *serial ) + return( NULL ); + im->serial = *serial; + + if( (res = fn( im, a, b )) ) + return( res ); + + return( im_slist_map4( im->parents, + (VSListMap4Fn) im__link_mapp, fn, serial, a, b ) ); +} + +/* Apply a function to an image and all it's parents, direct and indirect. + */ +void * +im__link_map( IMAGE *im, VSListMap2Fn fn, void *a, void *b ) +{ + static int serial = 0; + + serial += 1; + return( im__link_mapp( im, fn, &serial, a, b ) ); +} + +static void * +im_invalidate_region( REGION *reg ) +{ + if( reg->buffer ) + reg->buffer->invalid = TRUE; + + return( NULL ); +} + +static void * +im_invalidate_image( IMAGE *im ) +{ + (void) im_slist_map2( im->regions, + (VSListMap2Fn) im_invalidate_region, NULL, NULL ); + + return( NULL ); +} + +/* Invalidate all pixel caches on an IMAGE and any parents. + */ +void +im_invalidate( IMAGE *im ) +{ + (void) im__link_map( im, + (VSListMap2Fn) im_invalidate_image, NULL, NULL ); +} + +/* Init the buffer cache system. + */ +void +im__buffer_init( void ) +{ +#ifdef HAVE_THREADS + if( !thread_buffer_cache_key ) + thread_buffer_cache_key = g_private_new( + (GDestroyNotify) buffer_cache_free ); +#endif /*HAVE_THREADS*/ +} diff --git a/libsrc/iofuncs/callback.c b/libsrc/iofuncs/callback.c new file mode 100644 index 00000000..8e3d82c1 --- /dev/null +++ b/libsrc/iofuncs/callback.c @@ -0,0 +1,145 @@ +/* Close and generate callbacks. + * + * 1/7/93 JC + * 20/7/93 JC + * - eval callbacks added + * 16/8/94 JC + * - evalend callbacks added + * 16/1/04 JC + * - now always calls all callbacks, even if some fail + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Callback struct. We attach a list of callbacks to images to be invoked when + * the image is closed. These do things like closing previous elements in a + * chain of operations, freeing client data, etc. + */ +typedef struct { + IMAGE *im; /* IMAGE we are attached to */ + int (*fn)(); /* callback function */ + void *a, *b; /* arguments to callback */ +} VCallback; + +/* Add a callback to an IMAGE. We can't use IM_NEW(), note! Freed eventually by + * im__close(), or by im_generate(), etc. for evalend callbacks. + */ +static int +add_callback( IMAGE *im, GSList **cblist, int (*fn)(), void *a, void *b ) +{ + VCallback *cbs; + + if( !(cbs = IM_NEW( NULL, VCallback )) ) + return( -1 ); + + cbs->fn = fn; + cbs->a = a; + cbs->b = b; + cbs->im = im; + *cblist = g_slist_prepend( *cblist, cbs ); + + return( 0 ); +} + +/* Add a close callback to an IMAGE. + */ +int +im_add_close_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + return( add_callback( im, &im->closefns, fn, a, b ) ); +} + +/* Add an eval callback to an IMAGE. + */ +int +im_add_eval_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + return( add_callback( im, &im->evalfns, fn, a, b ) ); +} + +/* Add an eval end callback to an IMAGE. + */ +int +im_add_evalend_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + return( add_callback( im, &im->evalendfns, fn, a, b ) ); +} + +/* Perform a user callback. + */ +static void * +call_callback( VCallback *cbs, int *result ) +{ + int res; + + if( (res = cbs->fn( cbs->a, cbs->b )) ) { + im_error( "im__trigger_callbacks", _( "user callback " + "failed for %s" ), cbs->im->filename ); + *result = res; + +#ifdef DEBUG_IO + printf( "im__trigger_callbacks: user callback " + "failed for %s\n", cbs->im->filename ); +#endif /*DEBUG_IO*/ + } + + return( NULL ); +} + +/* Perform a list of user callbacks. + */ +int +im__trigger_callbacks( GSList *cblist ) +{ + int result; + +#ifdef DEBUG_IO + printf( "im__trigger_callbacks: calling %d user callbacks ..\n", + g_slist_length( cblist ) ); +#endif /*DEBUG_IO*/ + + result = 0; + (void) im_slist_map2( cblist, + (VSListMap2Fn) call_callback, &result, NULL ); + + return( result ); +} diff --git a/libsrc/iofuncs/debug.c b/libsrc/iofuncs/debug.c new file mode 100644 index 00000000..5370ae2b --- /dev/null +++ b/libsrc/iofuncs/debug.c @@ -0,0 +1,134 @@ +/* debug.c: support for debugging + * + * 24/10/95 JC + * - first version + * 24/2/05 + * - print more mem allocation info + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Track all open images in this. + */ +GSList *im__open_images = NULL; + +static void * +print_one_line_region( REGION *r, int *n2, int *total ) +{ + if( r->type == IM_REGION_BUFFER && r->buffer ) { + printf( "\t*** %d) %zd malloced bytes\n", + *n2, r->buffer->bsize ); + *total += r->buffer->bsize; + } + + *n2 += 1; + + return( NULL ); +} + +/* Print a one-line description of an image, with an index. + */ +static void * +print_one_line( IMAGE *im, int *n, int *total ) +{ + printf( "%2d) %p, %s, %s: %dx%d, %d bands, %s\n", + *n, + im, + im_dtype2char( im->dtype ), im->filename, + im->Xsize, im->Ysize, im->Bands, + im_BandFmt2char( im->BandFmt ) ); + *n += 1; + + if( im->dtype == IM_SETBUF && im->data ) { + int size = IM_IMAGE_SIZEOF_LINE( im ) * im->Ysize; + + printf( "\t*** %d malloced bytes\n", size ); + *total += size; + } + + if( im->regions ) { + int n2; + int total2; + + printf( "\t%d regions\n", g_slist_length( im->regions ) ); + n2 = 0; + total2 = 0; + (void) im_slist_map2( im->regions, + (VSListMap2Fn) print_one_line_region, &n2, &total2 ); + if( total2 ) + printf( "\t*** using total of %d bytes\n", total2 ); + *total += total2; + } + + return( NULL ); +} + +/* Print one line for each open descriptor. + */ +void +im__print_all( void ) +{ + int n = 0; + int total = 0; + + if( im__open_images ) { + printf( "%d images\n", g_slist_length( im__open_images ) ); + (void) im_slist_map2( im__open_images, + (VSListMap2Fn) print_one_line, &n, &total ); + if( total ) + printf( "\n\t*** all-image total = %d bytes\n", total ); + } +} + +/* Debugging: given an index, print everything we know about that descriptor. + */ +void +im__print_one( int n ) +{ + IMAGE *im = g_slist_nth_data( im__open_images, n ); + + if( !im ) { + printf( "bad index: %d\n", n ); + return; + } + + im_printdesc( im ); +} diff --git a/libsrc/iofuncs/dispatch_types.c b/libsrc/iofuncs/dispatch_types.c new file mode 100644 index 00000000..c723c888 --- /dev/null +++ b/libsrc/iofuncs/dispatch_types.c @@ -0,0 +1,829 @@ +/* Define built-in VIPS types. + * + * J. Cupitt, 8/4/93. + * + * Modified: + * 21/5/07 + * - any length vector (Tom) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Max str we parse. + */ +#define IM_MAX_STR (4096) + +/* String containing each of the characters which can be used within a + * single command line argument to separate the elements of a vector. + */ +#define VEC_SEPS " " + +/* Init function for input displays. + */ +static int +input_display_init( im_object *obj, char *str ) +{ + struct im_col_display *scr = im_col_display_name( str ); + + if( !scr ) { + int i; + + im_error( "input_display", + _( "unknown display type \"%s\"" ), str ); + im_error( "input_display", _( "display should be one of:\n" ) ); + for( i = 0; (scr = im_col_displays( i )); i++ ) + im_error( "input_display", + " '%s'\n", scr->d_name ); + + return( -1 ); + } + + *obj = scr; + + return( 0 ); +} + +/* Input display type. + */ +im_type_desc im__input_display = { + IM_TYPE_DISPLAY, /* Its a display */ + 0, /* No storage needed */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_display_init, /* Init function */ + NULL /* Destroy function */ +}; + +/* Output display type. + */ +im_type_desc im__output_display = { + IM_TYPE_DISPLAY, /* Its a display */ + sizeof( struct im_col_display ),/* Memory to allocate */ + IM_TYPE_OUTPUT, /* Output object */ + NULL, /* Init function */ + NULL /* Destroy function */ +}; + +/* Init function for input images. + */ +static int +input_image_init( im_object *obj, char *str ) +{ + IMAGE **im = (IMAGE **) obj; + + return( !(*im = im_open( str, "r" )) ); +} + +/* Input image type. + */ +im_type_desc im__input_image = { + IM_TYPE_IMAGE, /* Its an image */ + 0, /* No storage needed */ + IM_TYPE_ARG, /* It requires a command-line arg */ + (im_init_obj_fn) input_image_init, /* Init function */ + (im_dest_obj_fn) im_close /* Destroy function */ +}; + +/* Init function for output images. + */ +static int +output_image_init( im_object *obj, char *str ) +{ + IMAGE **im = (IMAGE **) obj; + + return( !(*im = im_open( str, "w" )) ); +} + +/* Output image type. + */ +im_type_desc im__output_image = { + IM_TYPE_IMAGE, /* Its an image */ + 0, /* No storage to be allocated */ + IM_TYPE_OUTPUT | IM_TYPE_ARG, /* Flags! */ + (im_init_obj_fn) output_image_init, /* Init function */ + (im_dest_obj_fn) im_close /* Destroy function */ +}; + +/* Init function for RW images. + */ +static int +rw_image_init( im_object *obj, char *str ) +{ + IMAGE **im = (IMAGE **) obj; + + return( !(*im = im_open( str, "rw" )) ); +} + +/* RW image type. + */ +im_type_desc im__rw_image = { + IM_TYPE_IMAGE, /* Its an image */ + 0, /* No storage to be allocated */ + IM_TYPE_ARG, /* Flags! Pretend its an input type */ + (im_init_obj_fn) rw_image_init, /* Init function */ + (im_dest_obj_fn) im_close /* Destroy function */ +}; + +/* im_imagevec_object destroy function. + */ +static int +imagevec_dest( im_object obj ) +{ + im_imagevec_object *iv = obj; + + if( iv->vec ) { + int i; + + for( i = 0; i < iv->n; i++ ) + if( iv->vec[i] ) { + im_close( iv->vec[i] ); + iv->vec[i] = NULL; + } + + im_free( iv->vec ); + iv->vec = NULL; + iv->n = 0; + } + + return( 0 ); +} + +/* Init function for imagevec input. + */ +static int +input_imagevec_init( im_object *obj, char *str ) +{ + im_imagevec_object *iv = *obj; + char **strv; + int nargs; + int i; + + strv = g_strsplit( str, VEC_SEPS, -1 ); + nargs = g_strv_length( strv ); + + if( !(iv->vec = IM_ARRAY( NULL, nargs, IMAGE * )) ) { + g_strfreev( strv ); + return( -1 ); + } + iv->n = nargs; + + /* Must NULL them out in case we fail halfway though opening them all. + */ + for( i = 0; i < nargs; i++ ) + iv->vec[i] = NULL; + + for( i = 0; i < nargs; i++ ) + if( !(iv->vec[i] = im_open( strv[i], "r" )) ) { + g_strfreev( strv ); + return( -1 ); + } + + g_strfreev( strv ); + + return( 0 ); +} + +/* Input image vector type. + */ +im_type_desc im__input_imagevec = { + IM_TYPE_IMAGEVEC, /* Its an array of IMAGE */ + sizeof( im_imagevec_object ), /* Memory to allocate in vec build */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_imagevec_init, /* Init function */ + imagevec_dest /* Destroy function */ +}; + +/* Init function for masks. "str" can be NULL for output masks. + */ +static int +mask_init( im_object *obj, char *str ) +{ + im_mask_object *mo = *obj; + + /* Install string, clear mask. + */ + if( str && !(mo->name = im_strdup( NULL, str )) ) + return( -1 ); + mo->mask = NULL; + + return( 0 ); +} + +/* Init function for input dmasks. As above, but read in the mask. + */ +static int +dmask_init( im_object *obj, char *str ) +{ + im_mask_object *mo = *obj; + + if( mask_init( obj, str ) ) + return( -1 ); + if( !(mo->mask = im_read_dmask( str )) ) + return( -1 ); + + return( 0 ); +} + +/* Init function for input imasks. + */ +static int +imask_init( im_object *obj, char *str ) +{ + im_mask_object *mo = *obj; + + if( mask_init( obj, str ) ) + return( -1 ); + if( !(mo->mask = im_read_imask( str )) ) + return( -1 ); + + return( 0 ); +} + +/* DOUBLEMASK destroy function. + */ +static int +dmask_dest( im_object obj ) +{ + im_mask_object *mo = obj; + + if( mo->name ) { + im_free( mo->name ); + mo->name = NULL; + } + if( mo->mask ) { + im_free_dmask( (DOUBLEMASK *) mo->mask ); + mo->mask = NULL; + } + + return( 0 ); +} + +/* INTMASK destroy function. + */ +static int +imask_dest( im_object obj ) +{ + im_mask_object *mo = obj; + + if( mo->name ) { + im_free( mo->name ); + mo->name = NULL; + } + if( mo->mask ) { + im_free_imask( (INTMASK *) mo->mask ); + mo->mask = NULL; + } + + return( 0 ); +} + +/* As above, but save the mask first. + */ +static int +save_dmask_dest( im_object obj ) +{ + im_mask_object *mo = obj; + + if( mo->mask && im_write_dmask( mo->mask ) ) + return( -1 ); + return( dmask_dest( obj ) ); +} + +/* As above, but save the mask first. + */ +static int +save_imask_dest( im_object obj ) +{ + im_mask_object *mo = obj; + + if( mo->mask && im_write_imask( mo->mask ) ) + return( -1 ); + return( imask_dest( obj ) ); +} + +/* Output dmask type. + */ +im_type_desc im__output_dmask = { + IM_TYPE_DMASK, /* Its a mask */ + sizeof( im_mask_object ),/* Storage for mask object */ + IM_TYPE_OUTPUT | IM_TYPE_ARG, /* Flags */ + mask_init, /* Init function */ + save_dmask_dest /* Save and destroy function */ +}; + +/* Input dmask type. + */ +im_type_desc im__input_dmask = { + IM_TYPE_DMASK, /* Its a mask */ + sizeof( im_mask_object ),/* Storage for mask object */ + IM_TYPE_ARG, /* It requires a command-line arg */ + dmask_init, /* Init function */ + dmask_dest /* Destroy function */ +}; + +/* Output imask type. + */ +im_type_desc im__output_imask = { + IM_TYPE_IMASK, /* Its a mask */ + sizeof( im_mask_object ),/* Storage for mask object */ + IM_TYPE_OUTPUT | IM_TYPE_ARG, /* Flags */ + mask_init, /* Init function */ + save_imask_dest /* Save and destroy function */ +}; + +/* Input imask type. + */ +im_type_desc im__input_imask = { + IM_TYPE_IMASK, /* Its a mask */ + sizeof( im_mask_object ),/* Storage for mask object */ + IM_TYPE_ARG, /* It requires a command-line arg */ + imask_init, /* Init function */ + imask_dest /* Destroy function */ +}; + +/* Output dmask to screen type. Set a `print' function to get actual output. + * Used for things like "stats". + */ +im_type_desc im__output_dmask_screen = { + IM_TYPE_DMASK, /* Its a mask */ + sizeof( im_mask_object ),/* Storage for mask object */ + IM_TYPE_OUTPUT, /* Its an output argument */ + mask_init, /* Init function */ + dmask_dest /* Destroy function */ +}; + +/* Init function for double input. + */ +static int +input_double_init( im_object *obj, char *str ) +{ + double *d = (double *) *obj; + + *d = g_ascii_strtod( str, NULL ); + + return( 0 ); +} + +/* Input double type. + */ +im_type_desc im__input_double = { + IM_TYPE_DOUBLE, /* Its a double */ + sizeof( double ), /* Memory to allocate */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_double_init, /* Init function */ + NULL /* Destroy function */ +}; + +/* im_doublevec_object destroy function. + */ +static int +doublevec_dest( im_object obj ) +{ + im_doublevec_object *dv = obj; + + if( dv->vec ) { + im_free( dv->vec ); + dv->vec = NULL; + dv->n = 0; + } + + return( 0 ); +} + +/* Init function for doublevec input. + */ +static int +input_doublevec_init( im_object *obj, char *str ) +{ + im_doublevec_object *dv = *obj; + char **strv; + int nargs; + int i; + + strv = g_strsplit( str, VEC_SEPS, -1 ); + nargs = g_strv_length( strv ); + + if( !(dv->vec = IM_ARRAY( NULL, nargs, double )) ) { + g_strfreev( strv ); + return( -1 ); + } + dv->n = nargs; + + for( i = 0; i < nargs; i++ ) { + dv->vec[i] = g_ascii_strtod( strv[i], NULL ); + if( errno ) { + im_error_system( errno, "input_doublevec_init", + _( "bad double \"%s\"" ), strv[i] ); + g_strfreev( strv ); + return( -1 ); + } + } + + g_strfreev( strv ); + + return( 0 ); +} + +/* Input double vector type. + */ +im_type_desc im__input_doublevec = { + IM_TYPE_DOUBLEVEC, /* Its an array of double */ + sizeof( im_doublevec_object ), /* Memory to allocate in vec build */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_doublevec_init, /* Init function */ + doublevec_dest /* Destroy function */ +}; + +/* im_intvec_object destroy function. + */ +static int +intvec_dest( im_object obj ) +{ + im_intvec_object *iv = obj; + + if( iv->vec ) { + im_free( iv->vec ); + iv->vec = NULL; + iv->n = 0; + } + + return( 0 ); +} + +/* Init function for intvec input. + */ +static int +input_intvec_init( im_object *obj, char *str ) +{ + im_intvec_object *iv = *obj; + char **strv; + int nargs; + int i; + + strv = g_strsplit( str, VEC_SEPS, -1 ); + nargs = g_strv_length( strv ); + + if( !(iv->vec = IM_ARRAY( NULL, nargs, int )) ) { + g_strfreev( strv ); + return( -1 ); + } + iv->n = nargs; + + for( i = 0; i < nargs; i++ ) { + long int val= strtol( strv[i], NULL, 10 ); + + if( errno ) { + im_error_system( errno, "input_intvec_init", + _( "bad integer \"%s\"" ), strv[i] ); + g_strfreev( strv ); + return( -1 ); + } + if( INT_MAX < val || INT_MIN > val ) { + im_error( "input_intvec_init", + "%ld overflows integer type", val ); + } + iv->vec[i] = (int) val; + } + + g_strfreev( strv ); + + return( 0 ); +} + +/* Input int vector type. + */ +im_type_desc im__input_intvec = { + IM_TYPE_INTVEC, /* It's an array of int */ + sizeof( im_intvec_object ), /* Memory to allocate in vec build */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_intvec_init, /* Init function */ + intvec_dest /* Destroy function */ +}; + +/* Init function for int input. + */ +static int +input_int_init( im_object *obj, char *str ) +{ + int *i = (int *) *obj; + + if( sscanf( str, "%d", i ) != 1 ) { + im_error( "input_int", _( "bad format" ) ); + return( -1 ); + } + + return( 0 ); +} + +/* Input int type. + */ +im_type_desc im__input_int = { + IM_TYPE_INT, /* Its an int */ + sizeof( int ), /* Memory to allocate */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_int_init, /* Init function */ + NULL /* Destroy function */ +}; + +/* Init function for string input. + */ +static int +input_string_init( im_object *obj, char *str ) +{ + if( !(*obj = (im_object) im_strdup( NULL, str )) ) + return( -1 ); + + return( 0 ); +} + +/* Input string type. + */ +im_type_desc im__input_string = { + IM_TYPE_STRING, /* Its a string */ + 0, /* Memory to allocate */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_string_init, /* Init function */ + im_free /* Destroy function */ +}; + +/* Output string type. + */ +im_type_desc im__output_string = { + IM_TYPE_STRING, /* Its a string */ + 0, /* Memory to allocate */ + IM_TYPE_OUTPUT, /* Its an output argument */ + NULL, /* Init function */ + im_free /* Destroy function */ +}; + +/* Output double type. + */ +im_type_desc im__output_double = { + IM_TYPE_DOUBLE, /* Its a double */ + sizeof( double ), /* Memory to allocate */ + IM_TYPE_OUTPUT, /* Its an output argument */ + NULL, /* Init function */ + NULL /* Destroy function */ +}; + +/* Output complex type. + */ +im_type_desc im__output_complex = { + IM_TYPE_COMPLEX, /* Its a complex */ + 2 * sizeof( double ), /* Memory to allocate */ + IM_TYPE_OUTPUT, /* Its an output argument */ + NULL, /* Init function */ + NULL /* Destroy function */ +}; + +/* Output int type. + */ +im_type_desc im__output_int = { + IM_TYPE_INT, /* Its an int */ + sizeof( int ), /* Memory to allocate */ + IM_TYPE_OUTPUT, /* Its an output argument */ + NULL, /* Init function */ + NULL /* Destroy function */ +}; + +/* Print function for int output. + */ +int +im__iprint( im_object obj ) +{ + int *i = (int *) obj; + + printf( "%d\n", *i ); + + return( 0 ); +} + +/* Print function for string output. + */ +int +im__sprint( im_object obj ) +{ + char *s = (char *) obj; + + printf( "%s\n", s ); + + return( 0 ); +} + +/* Print function for double output. + */ +int +im__dprint( im_object obj ) +{ + double *d = (double *) obj; + + printf( "%G\n", *d ); + + return( 0 ); +} + +/* Print function for complex output. + */ +int +im__cprint( im_object obj ) +{ + double *d = (double *) obj; + + printf( "%G %G\n", d[0], d[1] ); + + return( 0 ); +} + +/* Statistics to stdout. + */ +int +im__dmsprint( im_object obj ) +{ + DOUBLEMASK *mask = ((im_mask_object *) obj)->mask; + double *row; + int i, j; + + /* Print statistics band stats eg: 2 bands:b 0,1 + */ + printf( "\ +band minimum maximum sum sum^2 mean deviation\ +\n" ); + for( j = 0; j < mask->ysize; j++ ) { + row = mask->coeff + j * 6; + if( j == 0 ) + printf( "all" ); + else + printf( "%2d ", j ); + + for( i = 0; i < 6; i++ ) + printf( "%12g", row[i] ); + printf( "\n" ); + } + + return( 0 ); +} + +static char *decode_dtype( enum im_col_disp_type type ) +{ + switch( type ) { + case DISP_BARCO: + return( "DISP_BARCO" ); + case DISP_DUMB: + return( "DISP_DUMB" ); + default: + return( "" ); + } +} + +/* Print display stuff. + */ +int +im__displayprint( im_object obj ) +{ + struct im_col_display *scr = (struct im_col_display *) obj; + + printf( "im_col_display:\n" ); + printf( "\td_name: %s\n", scr->d_name ); + printf( "\td_type: %s\n", decode_dtype( scr->d_type ) ); + printf( "\td_mat:\n" ); + printf( "\t\t %g %g %g\n", + scr->d_mat[0][0], scr->d_mat[0][1], scr->d_mat[0][2] ); + printf( "\t\t %g %g %g\n", + scr->d_mat[1][0], scr->d_mat[1][1], scr->d_mat[1][2] ); + printf( "\t\t %g %g %g\n", + scr->d_mat[2][0], scr->d_mat[2][1], scr->d_mat[2][2] ); + + printf( "\td_YCW: %g\n", scr->d_YCW ); + printf( "\td_xCW: %g\n", scr->d_xCW ); + printf( "\td_yCW: %g\n", scr->d_yCW ); + + printf( "\td_YCR: %g\n", scr->d_YCR ); + printf( "\td_YCG: %g\n", scr->d_YCG ); + printf( "\td_YCB: %g\n", scr->d_YCB ); + + printf( "\td_Vrwr: %d\n", scr->d_Vrwr ); + printf( "\td_Vrwg: %d\n", scr->d_Vrwg ); + printf( "\td_Vrwb: %d\n", scr->d_Vrwb ); + + printf( "\td_Y0R: %g\n", scr->d_Y0R ); + printf( "\td_Y0G: %g\n", scr->d_Y0G ); + printf( "\td_Y0B: %g\n", scr->d_Y0B ); + + printf( "\td_gammaR: %g\n", scr->d_gammaR ); + printf( "\td_gammaG: %g\n", scr->d_gammaG ); + printf( "\td_gammaB: %g\n", scr->d_gammaB ); + + printf( "\td_B: %g\n", scr->d_B ); + printf( "\td_P: %g\n", scr->d_P ); + + return( 0 ); +} + +/* GValue + */ + +/* Init function for input gvalue. Just make a string ... will get cast to + * whatever later. + */ +static int +input_gvalue_init( im_object *obj, char *str ) +{ + GValue *value = *obj; + + g_value_init( value, G_TYPE_STRING ); + g_value_set_string( value, str ); + + return( 0 ); +} + +static int +gvalue_free( im_object obj ) +{ + GValue *value = obj; + + g_value_unset( value ); + + return( 0 ); +} + +/* Input GValue type. + */ +im_type_desc im__input_gvalue = { + IM_TYPE_GVALUE, + sizeof( GValue ), /* Need some storage */ + IM_TYPE_ARG, /* It requires a command-line arg */ + (im_init_obj_fn) input_gvalue_init, /* Init function */ + (im_dest_obj_fn) gvalue_free /* Destroy function */ +}; + +int +im__gprint( im_object obj ) +{ + GValue *value = obj; + char *str_value; + + str_value = g_strdup_value_contents( value ); + printf( "%s\n", str_value ); + g_free( str_value ); + + return( 0 ); +} + +/* Init function for output gvalue. Just init to zero. + */ +static int +output_gvalue_init( im_object *obj ) +{ + GValue *value = *obj; + + memset( value, 0, sizeof( GValue ) ); + + return( 0 ); +} + +im_type_desc im__output_gvalue = { + IM_TYPE_GVALUE, + sizeof( GValue ), /* Need some storage */ + IM_TYPE_OUTPUT, /* No arg needed (just print) */ + (im_init_obj_fn) output_gvalue_init, /* Init function */ + (im_dest_obj_fn) gvalue_free /* Destroy function */ +}; diff --git a/libsrc/iofuncs/error.c b/libsrc/iofuncs/error.c new file mode 100644 index 00000000..7da6b2b4 --- /dev/null +++ b/libsrc/iofuncs/error.c @@ -0,0 +1,248 @@ +/* @(#) error handling + * @(#) + * @(#) Usage: + * @(#) void im_errormsg(variable_list) + * @(#) (variable_list) is (format, arg1, arg2, ...) + * @(#) format, arg1, arg2, etc are the same as in fprintf + * @(#) + * Copyright: N. Dessipris + * Written on: 18/03/1991 + * Updated on: 9/7/92 KM + * 20/12/2003 JC + * - i18n added, domain now separate arg + * 14/2/07 + * - lock around error buffer changes + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef OS_WIN32 +#include +#include +#endif /*OS_WIN32*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Make global array to keep the error message buffer. + */ +#define IM_MAX_ERROR (10240) +static char im_error_text[IM_MAX_ERROR] = ""; +static VBuf im_error_buf = + IM_BUF_STATIC( im_error_text, IM_MAX_ERROR ); + +#define IM_DIAGNOSTICS "IM_DIAGNOSTICS" +#define IM_WARNING "IM_WARNING" + +const char * +im_error_buffer( void ) +{ + const char *msg; + + g_mutex_lock( im__global_lock ); + msg = im_buf_all( &im_error_buf ); + g_mutex_unlock( im__global_lock ); + + return( msg ); +} + +void +im_verror( const char *domain, const char *fmt, va_list ap ) +{ + g_mutex_lock( im__global_lock ); + im_buf_appendf( &im_error_buf, "%s: ", domain ); + im_buf_vappendf( &im_error_buf, fmt, ap ); + im_buf_appends( &im_error_buf, "\n" ); + g_mutex_unlock( im__global_lock ); +} + +void +im_error( const char *domain, const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_verror( domain, fmt, ap ); + va_end( ap ); +} + +void +im_verror_system( int err, const char *domain, const char *fmt, va_list ap ) +{ + im_verror( domain, fmt, ap ); + +#ifdef OS_WIN32 +{ + char *buf; + + if( FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err, + MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), + (LPSTR) &buf, 0, NULL ) ) { + im_error( _( "windows error" ), "%s", buf ); + LocalFree( buf ); + } +} +#else /*OS_WIN32*/ +{ + char *buf; + + buf = g_locale_to_utf8( strerror( err ), -1, NULL, NULL, NULL ); + im_error( _( "unix error" ), "%s", buf ); + g_free( buf ); +} +#endif /*OS_WIN32*/ +} + +void +im_error_system( int err, const char *domain, const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_verror_system( err, domain, fmt, ap ); + va_end( ap ); +} + +void +im_error_clear( void ) +{ + g_mutex_lock( im__global_lock ); + im_buf_rewind( &im_error_buf ); + g_mutex_unlock( im__global_lock ); +} + +void +im_vdiag( const char *domain, const char *fmt, va_list ap ) +{ + if( !g_getenv( IM_DIAGNOSTICS ) ) { + (void) fprintf( stderr, _( "%s: " ), _( "vips diagnostic" ) ); + (void) fprintf( stderr, _( "%s: " ), domain ); + (void) vfprintf( stderr, fmt, ap ); + (void) fprintf( stderr, "\n" ); + } +} + +void +im_diag( const char *domain, const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_vdiag( domain, fmt, ap ); + va_end( ap ); +} + +void +im_vwarn( const char *domain, const char *fmt, va_list ap ) +{ + if( !g_getenv( IM_WARNING ) ) { + (void) fprintf( stderr, _( "%s: " ), _( "vips warning" ) ); + (void) fprintf( stderr, _( "%s: " ), domain ); + (void) vfprintf( stderr, fmt, ap ); + (void) fprintf( stderr, "\n" ); + } +} + +void +im_warn( const char *domain, const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_vwarn( domain, fmt, ap ); + va_end( ap ); +} + +/* Compatibility with pre-7.10 ... can't portably do these as macros sadly. + */ + +void +im_errormsg( const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_verror( "untranslated", fmt, ap ); + va_end( ap ); +} + +void +im_verrormsg( const char *fmt, va_list ap ) +{ + im_verror( "untranslated", fmt, ap ); +} + +void +im_errormsg_system( int err, const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_verror_system( err, "untranslated", fmt, ap ); + va_end( ap ); +} + +void +im_diagnostics( const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_vdiag( "untranslated", fmt, ap ); + va_end( ap ); +} + +void +im_warning( const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_vwarn( "untranslated", fmt, ap ); + va_end( ap ); +} diff --git a/libsrc/iofuncs/error_exit.c b/libsrc/iofuncs/error_exit.c new file mode 100644 index 00000000..da2a3064 --- /dev/null +++ b/libsrc/iofuncs/error_exit.c @@ -0,0 +1,75 @@ +/* @(#) print error mesg on stderr and exit(1) + * @(#) It also prints any additional error messages set by the routines + * @(#) + * @(#) Usage: + * @(#) void error_exit(variable_arg_list) + * + * Copyright: N. Dessipris + * Written on: 19/03/1991 + * Modified on: + * 11/5/93 J.Cupitt + * - strange extra newlines removed - see im_errormsg() + * - strange tests removed + * 28-4-99 JC + * - ansified + * 2/8/06 + * - print prgname + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +void +error_exit( const char *fmt, ... ) +{ + va_list ap; + + fprintf( stderr, "%s: ", g_get_prgname() ); + + va_start( ap, fmt ); + (void) vfprintf( stderr, fmt, ap ); + va_end( ap ); + + fprintf( stderr, "\n" ); + fprintf( stderr, "%s", im_errorstring() ); + + exit( 1 ); +} diff --git a/libsrc/iofuncs/im_binfile.c b/libsrc/iofuncs/im_binfile.c new file mode 100644 index 00000000..e55f7156 --- /dev/null +++ b/libsrc/iofuncs/im_binfile.c @@ -0,0 +1,182 @@ +/* @(#) Function to read a binary file with no header to vasari file + * @(#) Usage: + * @(#) + * @(#) IMAGE * + * @(#) im_binfile(in, xs, ys, bands, offset) + * @(#) char *in; + * @(#) int xs, ys, bands, offset; + * @(#) + * @(#) The function returns NULL on error. + * @(#) Works for uchar input only. + * Author: N. Dessipris + * Written on: 31/7/91 + * Modified on: + * 15/6/93 JC + * - includes fixed + * - externs fixed + * 7/6/94 JC + * - im_outcheck() added + * 13/12/94 JC + * - now uses im_mapfile(), rather than read() and copy + * - hence returns a new IMAGE descriptor + * 25/5/95 JC + * - oops! mess up with fd on errors + * 5/2/04 JC + * - added offset param + * 26/5/04 + * - data init was broken by im_mapfile() change + * 30/9/05 + * - use int64 for size calcs so we can map >31 bit files on 64 bit + * machines + * - delay mmap() for large files, cf. im_openin() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +IMAGE * +im_binfile( const char *name, int xs, int ys, int bands, int offset ) +{ + IMAGE *im; + gint64 length; + gint64 psize; + + /* Check parameters. + */ + if( xs <= 0 || ys <= 0 || bands <=0 ) { + im_error( "im_binfile", _( "bad parameters" ) ); + return( NULL ); + } + + /* Make new output image for us. + */ + if( !(im = im_init( name )) ) + return( NULL ); + if( (im->fd = im__open_image_file( name )) == -1 ) { + im_close( im ); + return( NULL ); + } + im->dtype = IM_OPENIN; + im->sizeof_header = offset; + + /* Predict file size. + */ + psize = (gint64) xs * ys * bands + offset; + + /* Read the file length and check against what we think + * the size should be. + */ + if( (length = im_file_length( im->fd )) == -1 ) { + im_close( im ); + return( NULL ); + } + + /* Very common, so special message. + */ + if( psize > length ) { + im_error( "im_binfile", _( "unable to open %s: " + "file has been truncated" ), im->filename ); + im_close( im ); + return( NULL ); + } + + /* Just wierd. Only print a warning for this, since we should + * still be able to process it without coredumps. + */ + if( psize < length ) + im_warn( "im_binfile", _( "%s is longer than expected" ), + im->filename ); + + /* If the predicted size is under our mmap threshold, mmap the whole + * thing now. Otherwise, delay the map until region create and we'll + * use a rolling window. See also im_openin(). + */ + if( psize < im__mmap_limit ) { + if( im_mapfile( im ) ) { + im_close( im ); + return( NULL ); + } + im->data = im->baseaddr + offset; + im->dtype = IM_MMAPIN; + } + + /* Set header fields. + */ + im->Xsize = xs; + im->Ysize = ys; + im->Bands = bands; + + /* Set others to standard values. + */ + im->Bbits = IM_BBITS_BYTE; + im->BandFmt = IM_BANDFMT_UCHAR; + im->Coding = IM_CODING_NONE; + + if( bands == 1 ) + im->Type = IM_TYPE_B_W; + else if( bands == 3 ) + im->Type = IM_TYPE_RGB; + else + im->Type = IM_TYPE_MULTIBAND; + + im->Xres = 1.0; + im->Yres = 1.0; + + im->Length = 0; + im->Compression = 0; + im->Level = 0; + + im->Xoffset = 0; + im->Yoffset = 0; + + /* Init others too. + */ + im->dhint = IM_THINSTRIP; + + return( im ); +} diff --git a/libsrc/iofuncs/im_bits_of_fmt.c b/libsrc/iofuncs/im_bits_of_fmt.c new file mode 100644 index 00000000..c2abe152 --- /dev/null +++ b/libsrc/iofuncs/im_bits_of_fmt.c @@ -0,0 +1,64 @@ +/* Return number of bits of band format or -1 on error. + * + * 02/06/05 JF + * - original code + * 12/1/06 + * - use a table + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static const int bits[] = { + IM_BBITS_BYTE, + IM_BBITS_BYTE, + IM_BBITS_SHORT, + IM_BBITS_SHORT, + IM_BBITS_INT, + IM_BBITS_INT, + IM_BBITS_FLOAT, + IM_BBITS_COMPLEX, + IM_BBITS_DOUBLE, + IM_BBITS_DPCOMPLEX +}; + +/* Return number of pels bits for band format or -1 on error. + */ +int +im_bits_of_fmt( int bandfmt ) +{ + return( bandfmt < 0 || bandfmt > IM_BANDFMT_DPCOMPLEX ? + im_error( "im_bits_of_fmt", + _( "unsupported band format: %d" ), bandfmt ), + -1 : + bits[bandfmt] ); +} diff --git a/libsrc/iofuncs/im_close.c b/libsrc/iofuncs/im_close.c new file mode 100644 index 00000000..d7353b8a --- /dev/null +++ b/libsrc/iofuncs/im_close.c @@ -0,0 +1,303 @@ +/* @(#) Frees all resources associated with IMAGE, and frees the memory + * @(#) occupied by IMAGE. + * @(#) + * @(#) int + * @(#) im_close( image ) + * @(#) IMAGE *image; + * @(#) + * @(#) As above, but just free resources attached to IMAGE. After call, IMAGE + * @(#) is as if im_init() had just been called. Useful for closing and + * @(#) re-opening as another image type. See im_piocheck() etc. + * @(#) + * @(#) int + * @(#) im__close( image ) + * @(#) IMAGE *image; + * @(#) + * @(#) Returns 0 on success and 1 on error. + * + * Copyright: Nicos Dessipris + * Written on: 12/04/1990 + * Modified on : + * 24/7/92 JC + * - im_update_descfile code tidied up + * - free on NULL string when junking Hist fixed + * - now calls im_unmapfile + * - better behaviour if image has been opened and closed with + * no im_setupout call + * - better behaviour for half-made IMAGE descriptors + * 15/4/93 JC + * - additions for freeing partial images + * 29/4/93 JC + * - close callback list added + * 10/5/93 JC + * - im__close() added + * 9/11/93 JC + * - im_update_descfile -> write_descfile + * - if Hist is NULL, no longer makes up and writes .desc file + * 16/8/94 JC + * - evalend callbacks added + * - ANSIfied + * 24/10/95 JC + * - now tracks open images ... see also im_init() and debug.c + * 11/7/00 JC + * - SETBUF_FOREIGN added + * 16/1/04 JC + * - frees as much as possible on im_close() failure + * 6/6/05 Markus Wollgarten + * - free Meta on close + * 30/6/05 JC + * - actually, free Meta on final close, so we carry meta over on an + * im__close()/im_openin() pair (eg. see im_pincheck()) + * 11/7/05 + * - call im__writehist() to send history to XML after image data + * 3/1/07 + * - free history_list + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_IO +#define DEBUG_NEW + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#ifdef HAVE_IO_H +#include +#endif /*HAVE_IO_H*/ +#include + +#include +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Maximum file name length. + */ +#define NAMELEN 1024 + +/* Free any resources owned by this descriptor. The descriptor is left as if a + * call to im_init had just happened - ie. the filename is set, but no other + * resources are attached. Information is lost if this is a im_setbuf() + * image! On an error, return non-zero and leave the image in an indeterminate + * state. Too hard to recover gracefully. + */ +int +im__close( IMAGE *im ) +{ + int result = 0; + + /* No action for NULL image. + */ + if( !im ) + return( result ); + +#ifdef DEBUG_IO + printf( "im__close: starting for %s ..\n", im->filename ); +#endif /*DEBUG_IO*/ + + /* Free any regions defined on this image. This will, in turn, call + * all stop functions still running, freeing all regions we have on + * other images, etc. + */ +#ifdef DEBUG_IO + printf( "im__close: freeing %d regions ..\n", + g_slist_length( (List *) im->regions ) ); +#endif /*DEBUG_IO*/ + while( im->regions ) + im_region_free( (REGION *) im->regions->data ); + + /* That should mean we have no windows. + */ + if( im->windows ) { + GSList *p; + + printf( "** im__close: leaked windows!\n" ); + for( p = im->windows; p; p = p->next ) + im_window_print( (im_window_t *) p->data ); + } + + /* Make sure all evalend functions have been called, perform all close + * callbacks, and free eval callbacks. + */ + result |= im__trigger_callbacks( im->evalendfns ); + IM_FREEF( im_slist_free_all, im->evalendfns ); + IM_FREEF( im_slist_free_all, im->evalfns ); + result |= im__trigger_callbacks( im->closefns ); + IM_FREEF( im_slist_free_all, im->closefns ); + + /* Junk generate functions. + */ + im->start = NULL; + im->generate = NULL; + im->stop = NULL; + + /* No more parent/child links. + */ + im__link_break_all( im ); + + /* What resources are associated with this IMAGE descriptor? + */ + if( im->baseaddr ) { + /* MMAP file. + */ +#ifdef DEBUG_IO + printf( "im__close: unmapping file ..\n" ); +#endif /*DEBUG_IO*/ + + if( im_unmapfile( im ) ) + return( -1 ); + im->data = NULL; + } + + /* Is there a file descriptor? + */ + if( im->fd != -1 ) { +#ifdef DEBUG_IO + printf( "im__close: closing output file ..\n" ); +#endif /*DEBUG_IO*/ + + if( im->dtype == IM_OPENOUT && im__writehist( im ) ) { + im_errormsg( "im_close: unable to write metadata " + "for %s", im->filename ); + result = -1; + } + if( close( im->fd ) == -1 ) { + im_errormsg( "im_close: unable to close fd (2) " + "for %s", im->filename ); + result = -1; + } + im->fd = -1; + } + + /* Any image data? + */ + if( im->data ) { + /* Buffer image. Only free stuff we know we allocated. + */ + if( im->dtype == IM_SETBUF ) { +#ifdef DEBUG_IO + printf( "im__close: freeing buffer ..\n" ); +#endif /*DEBUG_IO*/ + im_free( im->data ); + im->dtype = IM_NONE; + } + + im->data = NULL; + } + + /* Reset other state. + */ + im->dtype = IM_NONE; + im->dhint = IM_SMALLTILE; + im->kill = 0; + im->close_pending = 0; + im->sizeof_header = IM_SIZEOF_HEADER; + +#ifdef DEBUG_IO + printf( "im__close: final success for %s (%p)\n", + im->filename, im ); +#endif /*DEBUG_IO*/ + + return( result ); +} + +/* Free resources and close descriptor. + */ +int +im_close( IMAGE *im ) +{ + int result = 0; + + /* No action for NULL image. + */ + if( !im ) + return( result ); + + /* Are there any regions left on this image? If there are, just set + * close_pending and return. The image will be then be closed when + * the last region is freed (see im_region_free()). This prevents + * some dangling region pointers. + */ +#ifdef DEBUG_IO + if( im->close_pending ) + /* Strange! Just print a warning. + */ + printf( "im_close: im_close called twice on \"%s\"\n", + im->filename ); +#endif /*DEBUG_IO*/ + if( im->regions ) { +#ifdef DEBUG_IO + printf( "im_close: pending close for \"%s\"\n", im->filename ); +#endif /*DEBUG_IO*/ + im->close_pending = 1; + return( result ); + } + + /* Is this descriptor currently being closed somewhere else? Immediate + * return if it is. This prevents infinite descent if a close callback + * includes an im_close for this image. This can happen sometimes! + */ + if( im->closing ) + return( result ); + im->closing = 1; + + /* Free IMAGE resources. + */ + if( im__close( im ) ) + result = -1; + +#ifdef DEBUG_NEW + printf( "im_close: freeing IMAGE 0x%p, \"%s\"\n", im, im->filename ); +#endif /*DEBUG_NEW*/ + + /* Final cleanup. + */ + IM_FREEF( g_mutex_free, im->sslock ); + IM_FREE( im->filename ); + IM_FREE( im->Hist ); + IM_FREEF( im__gslist_gvalue_free, im->history_list ); + im__meta_destroy( im ); + im__open_images = g_slist_remove( im__open_images, im ); + IM_FREE( im ); + + return( result ); +} diff --git a/libsrc/iofuncs/im_cp_desc.c b/libsrc/iofuncs/im_cp_desc.c new file mode 100644 index 00000000..c18536b9 --- /dev/null +++ b/libsrc/iofuncs/im_cp_desc.c @@ -0,0 +1,152 @@ +/* @(#) Function which copies IMAGE descriptor image2 to image1; + * @(#) data, fd and filename are not copied + * @(#) used to make programs simpler by copying most parameters + * @(#) + * @(#) int + * @(#) im_cp_desc( image1, image2 ) + * @(#) IMAGE *image1, *image2; + * @(#) + * @(#) Returns 0 on success or -1 on fail. + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 09/02/1990 + * Modified on : 22/2/93 By Kirk Martinez: v6.3 + * 28/10/1992 J.Cupitt + * - now calls im_cp_Hist, and hence frees old history correctly + * 10/5/93 J.Cupitt + * - checks return result from im_cp_Hist() + * 22/11/00 JC + * - ANSIfied + * 5/9/02 JC + * - copy Xoffset/Yoffset too + * 14/4/04 JC + * - hmm, in fact no, zero them + * 6/6/05 Markus Wollgarten + * - copy Meta + * 29/8/05 + * - added im_cp_descv() and im_cp_desc_array() + * 2/9/05 + * - simplified ... no more skip the first line stuff + * 4/1/07 + * - merge hists with history_list instead + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* in is a NULL-termnated array of input images. Always at least one image + * there. + */ +int +im_cp_desc_array( IMAGE *out, IMAGE *in[] ) +{ + int i; + int ni; + + assert( in[0] ); + + out->Xsize = in[0]->Xsize; + out->Ysize = in[0]->Ysize; + out->Bands = in[0]->Bands; + out->Bbits = in[0]->Bbits; + out->BandFmt = in[0]->BandFmt; + out->Type = in[0]->Type; + out->Coding = in[0]->Coding; + out->Xres = in[0]->Xres; + out->Yres = in[0]->Yres; + out->Xoffset = 0; + out->Yoffset = 0; + + /* Count number of images. + */ + for( ni = 0; in[ni]; ni++ ) + ; + + /* Need to copy last-to-first so that in0 meta will override any + * earlier meta. + */ + im__meta_destroy( out ); + for( i = ni - 1; i >= 0; i-- ) + if( im_image_sanity( in[i] ) || + im__meta_cp( out, in[i] ) ) + return( -1 ); + + /* Merge hists first to last. + */ + for( i = 0; in[i]; i++ ) + out->history_list = im__gslist_gvalue_merge( out->history_list, + in[i]->history_list ); + + return( 0 ); +} + +/* Max number of images we can handle. + */ +#define MAX_IMAGES (1000) + +int +im_cp_descv( IMAGE *out, IMAGE *in1, ... ) +{ + va_list ap; + int i; + IMAGE *in[MAX_IMAGES]; + + in[0] = in1; + va_start( ap, in1 ); + for( i = 1; i < MAX_IMAGES && (in[i] = va_arg( ap, IMAGE * )); i++ ) + ; + va_end( ap ); + if( i == MAX_IMAGES ) { + im_error( "im_cp_descv", _( "too many images" ) ); + return( -1 ); + } + + return( im_cp_desc_array( out, in ) ); +} + +int +im_cp_desc( IMAGE *dest, IMAGE *src ) +{ + return( im_cp_descv( dest, src, NULL ) ); +} diff --git a/libsrc/iofuncs/im_debugim.c b/libsrc/iofuncs/im_debugim.c new file mode 100644 index 00000000..59d1c6b3 --- /dev/null +++ b/libsrc/iofuncs/im_debugim.c @@ -0,0 +1,138 @@ +/* @(#) Function which prints in stdout the values of a picture + * @(#) + * @(#) For debuging only + * @(#) is either memory mapped or in a buffer. + * @(#) + * @(#) void + * @(#) im_debugim( in ) + * @(#) IMAGE *in; + * @(#) + * + * Copyright: 1991 N. Dessipris + * + * Author: N. Dessipris + * Written on: 18/03/1991 + * Modified on: + * 15/4/93 J.Cupitt + * - returns int, not void now, so error messages work + * - detects im->data invalid. + * 15/4/93 J.Cupitt + * - uses %g format, not %f for printf() + * 23/7/93 JC + * - im_incheck() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_debugim( IMAGE *in ) +{ +/* Check our args. */ + if( im_incheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_debugim: input must be uncoded" ); + return( -1 ); + } + +/* What type? First define the loop we want to perform for all types. */ +#define loopuc(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y, z; \ + \ + for ( y=0; yYsize; y++ ) {\ + for ( x=0; xXsize; x++ ) {\ + for ( z=0; zBands; z++ ) {\ + fprintf(stderr, "%4d", (TYPE)*p++ );\ + } \ + } \ + fprintf(stderr, "\n");\ + } \ + } + +#define loop(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y, z; \ + \ + for ( y=0; yYsize; y++ ) {\ + for ( x=0; xXsize; x++ ) {\ + for ( z=0; zBands; z++ ) {\ + fprintf(stderr, "%g\t", (double)*p++ );\ + } \ + } \ + fprintf(stderr, "\n");\ + } \ + } + +#define loopcmplx(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y, z; \ + \ + for ( y=0; yYsize; y++ ) {\ + for ( x=0; xXsize; x++ ) {\ + for ( z=0; zBands; z++ ) {\ + fprintf(stderr,"re=%g\t",(double)*p++);\ + fprintf(stderr,"im=%g\t",(double)*p++);\ + } \ + } \ + fprintf(stderr, "\n");\ + } \ + } + +/* Now generate code for all types. */ + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: loopuc(unsigned char); break; + case IM_BANDFMT_CHAR: loop(char); break; + case IM_BANDFMT_USHORT: loop(unsigned short); break; + case IM_BANDFMT_SHORT: loop(short); break; + case IM_BANDFMT_UINT: loop(unsigned int); break; + case IM_BANDFMT_INT: loop(int); break; + case IM_BANDFMT_FLOAT: loop(float); break; + case IM_BANDFMT_DOUBLE: loop(double); break; + case IM_BANDFMT_COMPLEX: loopcmplx(float); break; + case IM_BANDFMT_DPCOMPLEX: loopcmplx(double); break; + + default: + im_errormsg("im_debugim: unknown input format"); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_demand_hint.c b/libsrc/iofuncs/im_demand_hint.c new file mode 100644 index 00000000..628e4fd2 --- /dev/null +++ b/libsrc/iofuncs/im_demand_hint.c @@ -0,0 +1,188 @@ +/* @(#) Hint to the evaluation mechanism that it should ask for output from + * @(#) this image with a certain shape of patch. + * @(#) + * @(#) int + * @(#) im_demand_hint( im, hint, in1, in2, ..., NULL ) + * @(#) IMAGE *im, *in1, *in2, ...; + * @(#) im_demand_type hint; + * @(#) + * @(#) hint may be one of + * @(#) + * @(#) IM_THINSTRIP + * @(#) This operation would like to output strips the width of the + * @(#) image and a few pels high. This is option suitable for + * @(#) point-to-point operations, such as those in the arithmetic + * @(#) package. + * @(#) + * @(#) This is the fastest style for most simple operations. + * @(#) + * @(#) IM_FATSTRIP + * @(#) This operation would like to output strips the width of the + * @(#) image and as high as possible. This option is suitable for + * @(#) area operations which do not violently transform coordinates, + * @(#) such as im_conv(). + * @(#) + * @(#) IM_SMALLTILE + * @(#) This is the most general demand format, and is the default. + * @(#) Output is demanded in small (around 100x100 pel) sections. + * @(#) This style works reasonably efficiently, even for bizzare + * @(#) operations like 45 degree rotate. + * @(#) + * @(#) IM_ANY + * @(#) Not from a disc file, so any geometry is OK. + * @(#) + * @(#) NOTE: demand style falls back to the most restrictive in the pipeline. + * @(#) All pipeline elements in the pipeline must agree on IM_THINSTRIP + * @(#) before output will be asked for in this manner. If you do not set a + * @(#) hint, you will get IM_SMALLTILE. + * @(#) + * @(#) in1, in2, ... are the images on which out will make demands. You + * @(#) should terminate the list with NULL. + * @(#) + * @(#) int + * @(#) im_demand_hint_array( im, hint, in ) + * @(#) IMAGE *im, **in; + * @(#) im_demand_type hint; + * @(#) + * @(#) As above, but in is a NULL-terminated array of input images. Use + * @(#) im_allocate_input_array() to build the input array. + * @(#) + * @(#) Returns non-zero on failure. + * @(#) + * + * Copyright: The National Gallery, 1993 + * Written on: 6/9/93 + * Modified on : + * 2/3/98 JC + * - IM_ANY added + * 19/5/06 + * - minor change to rules: don't force ANY on no-input operations ... + * fails for image import + * 1/12/06 + * - build parent/child links as well + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Max number of images we can handle. + */ +#define MAX_IMAGES (1000) + +/* Given two im_demand_type, return the most restrictive. + */ +static im_demand_type +find_least( im_demand_type a, im_demand_type b ) +{ + return( (im_demand_type) IM_MIN( (int) a, (int) b ) ); +} + +/* Set hint for this image. + */ +int +im_demand_hint_array( IMAGE *im, im_demand_type hint, IMAGE **in ) +{ + int i, len, nany; + + /* How many input images are there? And how many are IM_ANY? + */ + for( i = 0, len = 0, nany = 0; in[i]; i++, len++ ) + if( in[i]->dhint == IM_ANY ) + nany++; + + if( len == 0 ) + /* No input images? Just set the requested hint. We don't + * force ANY, since the operation might be something like + * tiled read of an EXR image, where we certainly don't want + * ANY. + */ + ; + else if( nany == len ) + /* Special case: if all the inputs are IM_ANY, then output can + * be IM_ANY regardless of what this function wants. + */ + hint = IM_ANY; + else + /* Find the most restrictive of all the hints available to us. + */ + for( i = 0; i < len; i++ ) + hint = find_least( hint, in[i]->dhint ); + + im->dhint = hint; + +#ifdef DEBUG + printf( "im_demand_hint_array: set dhint for \"%s\" to %s\n", + im->filename, im_dhint2char( im->dhint ) ); +#endif /*DEBUG*/ + + /* im depends on all these ims. + */ + for( i = 0; i < len; i++ ) + im__link_make( im, in[i] ); + + return( 0 ); +} + +/* Build an array, and call the above. + */ +int +im_demand_hint( IMAGE *im, im_demand_type hint, ... ) +{ + va_list ap; + int i; + IMAGE *ar[MAX_IMAGES]; + + va_start( ap, hint ); + for( i = 0; i < MAX_IMAGES && (ar[i] = va_arg( ap, IMAGE * )); i++ ) + ; + va_end( ap ); + if( i == MAX_IMAGES ) { + im_error( "im_demand_hint", _( "too many images" ) ); + return( -1 ); + } + + return( im_demand_hint_array( im, hint, ar ) ); +} diff --git a/libsrc/iofuncs/im_desc_hd.c b/libsrc/iofuncs/im_desc_hd.c new file mode 100644 index 00000000..4db8046f --- /dev/null +++ b/libsrc/iofuncs/im_desc_hd.c @@ -0,0 +1,204 @@ +/* @(#) im_desc_hd: Copies vips file header to an IMAGE descriptor. + * @(#) + * @(#) int + * @(#) im__read_header_bytes( IMAGE *im, unsigned char *from ) + * @(#) + * @(#) int + * @(#) im__write_header_bytes( IMAGE *im, unsigned char *to ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: Nicos Dessipris + * Written on: 13/02/1990 + * Modified on : 22/2/92 v6.3 Kirk Martinez + * 17/11/94 JC + * - read compression fields too + * 28/10/98 JC + * - byteswap stuff added + * 21/8/02 JC + * - oops, was truncating float + * 22/8/05 + * - slightly less stupid + * 30/12/06 + * - use gunit32/16 for 2 and 4 byte quantities + * 12/1/07 + * - override bbits in the file ... it's now deprecated + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Read short/int/float LSB and MSB first. + */ +void +im__read_4byte( int msb_first, unsigned char *to, unsigned char **from ) +{ + unsigned char *p = *from; + int out; + + if( msb_first ) + out = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + else + out = p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0]; + + *from += 4; + *((guint32 *) to) = out; +} + +void +im__read_2byte( int msb_first, unsigned char *to, unsigned char **from ) +{ + int out; + unsigned char *p = *from; + + if( msb_first ) + out = p[0] << 8 | p[1]; + else + out = p[1] << 8 | p[0]; + + *from += 2; + *((guint16 *) to) = out; +} + +/* We always write in native byte order. + */ +void +im__write_4byte( unsigned char **to, unsigned char *from ) +{ + *((guint32 *) *to) = *((guint32 *) from); + *to += 4; +} + +void +im__write_2byte( unsigned char **to, unsigned char *from ) +{ + *((guint16 *) *to) = *((guint16 *) from); + *to += 2; +} + +/* offset, read, write functions. + */ +typedef struct _FieldIO { + glong offset; + void (*read)( int msb_first, unsigned char *to, unsigned char **from ); + void (*write)( unsigned char **to, unsigned char *from ); +} FieldIO; + +static FieldIO fields[] = { + { G_STRUCT_OFFSET( IMAGE, Xsize ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Ysize ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Bands ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Bbits ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, BandFmt ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Coding ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Type ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Xres ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Yres ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Length ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Compression ), + im__read_2byte, im__write_2byte }, + { G_STRUCT_OFFSET( IMAGE, Level ), + im__read_2byte, im__write_2byte }, + { G_STRUCT_OFFSET( IMAGE, Xoffset ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Yoffset ), + im__read_4byte, im__write_4byte } +}; + +int +im__read_header_bytes( IMAGE *im, unsigned char *from ) +{ + int msb_first; + int i; + + im__read_4byte( 1, (unsigned char *) &im->magic, &from ); + if( im->magic != IM_MAGIC_INTEL && im->magic != IM_MAGIC_SPARC ) { + im_error( "im_open", _( "\"%s\" is not a VIPS image" ), + im->filename ); + return( -1 ); + } + msb_first = im->magic == IM_MAGIC_SPARC; + + for( i = 0; i < IM_NUMBER( fields ); i++ ) + fields[i].read( msb_first, + &G_STRUCT_MEMBER( unsigned char, im, fields[i].offset ), + &from ); + + /* Set this ourselves ... bbits is deprecated in the file format. + */ + im->Bbits = im_bits_of_fmt( im->BandFmt ); + + return( 0 ); +} + +int +im__write_header_bytes( IMAGE *im, unsigned char *to ) +{ + guint32 magic; + int i; + unsigned char *q; + + /* How odd, you'd think it would be the other way around. + */ + magic = im_amiMSBfirst() ? IM_MAGIC_INTEL : IM_MAGIC_SPARC; + q = to; + im__write_4byte( &q, (unsigned char *) &magic ); + + for( i = 0; i < IM_NUMBER( fields ); i++ ) + fields[i].write( &q, + &G_STRUCT_MEMBER( unsigned char, im, + fields[i].offset ) ); + + /* Pad spares with zeros. + */ + while( q - to < im->sizeof_header ) + *q++ = 0; + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_generate.c b/libsrc/iofuncs/im_generate.c new file mode 100644 index 00000000..ff616a0a --- /dev/null +++ b/libsrc/iofuncs/im_generate.c @@ -0,0 +1,850 @@ +/* Manage pipelines of partial images. + * + * J.Cupitt, 17/4/93. + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 6/7/93 JC + * - im_setupout() conventions clarified - see autorewind in + * im_iocheck(). + * 20/7/93 JC + * - eval callbacks added + * 7/9/93 JC + * - demand hint mechanism added + * 25/10/93 + * - asynchronous output mechanisms removed, as no observable speed-up + * 9/5/94 + * - new thread stuff added, with a define to turn it off + * 15/8/94 + * - start & stop functions can now be NULL for no-op + * 7/10/94 JC + * - evalend callback system added + * 23/12/94 JC + * - IM_ARRAY uses added + * 22/2/95 JC + * - im_fill_copy() added + * - im_region_region() uses modified + * 24/4/95 JC & KM + * - im_fill_lines() bug removed + * 30/8/96 JC + * - revised and simplified ... some code shared with im_iterate() + * - new im_generate_region() added + * 2/3/98 JC + * - IM_ANY added + * 20/7/99 JC + * - tile geometry made into ints for easy tuning + * 30/7/99 RP JC + * - threads reorganised for POSIX + * 29/9/99 JC + * - threadgroup stuff added + * 15/4/04 + * - better how-many-pixels-calculated + * 27/11/06 + * - merge background write stuff + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_IO + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Start and stop functions for one image in, input image is first user data. + */ +void * +im_start_one( IMAGE *out, IMAGE *in, void *dummy ) +{ + return( im_region_create( in ) ); +} + +int +im_stop_one( REGION *reg, void *dummy1, void *dummy2 ) +{ + im_region_free( reg ); + + return( 0 ); +} + +/* Stop and start functions for many images in. First client is pointer to + * null-terminated array of input images. + */ +int +im_stop_many( REGION **ar, void *dummy1, void *dummy2 ) +{ + int i; + + if( ! ar ) + return 0; + + for( i = 0; ar[i]; i++ ) + im_region_free( ar[i] ); + im_free( (char *) ar ); + + return( 0 ); +} + +void * +im_start_many( IMAGE *out, IMAGE **in, void *dummy ) +{ + int i, n; + REGION **ar; + + /* How many images? + */ + for( n = 0; in[n]; n++ ) + ; + + /* Alocate space for region array. + */ + if( !(ar = IM_ARRAY( NULL, n + 1, REGION * )) ) + return( NULL ); + + /* Create a set of regions. + */ + for( i = 0; i < n; i++ ) + if( !(ar[i] = im_region_create( in[i] )) ) { + im_stop_many( ar, NULL, NULL ); + return( NULL ); + } + ar[n] = NULL; + + return( ar ); +} + +/* Convenience function - make a null-terminated array of input images. + * Use with im_start_many. + */ +IMAGE ** +im_allocate_input_array( IMAGE *out, ... ) +{ + va_list ap; + IMAGE **ar; + IMAGE *im; + int i, n; + + /* Count input images. + */ + va_start( ap, out ); + for( n = 0; (im = va_arg( ap, IMAGE * )); n++ ) + ; + va_end( ap ); + + /* Allocate array. + */ + if( !(ar = IM_ARRAY( out, n + 1, IMAGE * )) ) + return( NULL ); + + /* Fill array. + */ + va_start( ap, out ); + for( i = 0; i < n; i++ ) + ar[i] = va_arg( ap, IMAGE * ); + va_end( ap ); + ar[n] = NULL; + + return( ar ); +} + +/* Loop over a big region, filling it in many small pieces with threads. + */ +static int +eval_to_region( REGION *or, im_threadgroup_t *tg ) +{ + Rect *r = &or->valid; + Rect image; + + int x, y; + + image.left = 0; + image.top = 0; + image.width = or->im->Xsize; + image.height = or->im->Ysize; + + /* Note we'll be working to fill a contigious area. + */ + tg->inplace = 1; + + /* Loop over or, attaching to all sub-parts in turn. + */ + for( y = r->top; y < IM_RECT_BOTTOM( r ); y += tg->ph ) + for( x = r->left; x < IM_RECT_RIGHT( r ); x += tg->pw ) { + im_thread_t *thr; + Rect pos; + Rect clipped; + + /* thrs appear on idle when the child thread does + * threadgroup_idle_add and hits the 'go' semaphore. + */ + thr = im_threadgroup_get( tg ); + + /* Set the position we want to generate with this + * thread. Clip against the size of the image and the + * space available in or. + */ + pos.left = x; + pos.top = y; + pos.width = tg->pw; + pos.height = tg->ph; + im_rect_intersectrect( &pos, &image, &clipped ); + im_rect_intersectrect( &clipped, r, &clipped ); + + /* Note params and start work. + */ + thr->oreg = or; + thr->pos = clipped; + thr->x = clipped.left; + thr->y = clipped.top; + im_threadgroup_trigger( thr ); + + /* Trigger any eval callbacks on our source image. + */ + im__handle_eval( tg->im, tg->pw, tg->ph ); + + /* Check for errors. + */ + if( im_threadgroup_iserror( tg ) ) { + /* Don't kill threads yet ... we may want to + * get some error stuff out of them. + */ + im_threadgroup_wait( tg ); + return( -1 ); + } + } + + /* Wait for all threads to hit 'go' again. + */ + im_threadgroup_wait( tg ); + + if( im_threadgroup_iserror( tg ) ) + return( -1 ); + + return( 0 ); +} + +/* Output to a memory area. Might be im_setbuf(), im_mmapin()/im_makerw() or + * im_mmapinrw(). + */ +static int +eval_to_memory( im_threadgroup_t *tg, REGION *or ) +{ + int y, chunk; + IMAGE *im = or->im; + +#ifdef DEBUG_IO + int ntiles = 0; + printf( "eval_to_memory: partial image output to memory area\n" ); +#endif /*DEBUG_IO*/ + + /* Choose a chunk size ... 1/100th of the height of the image, about. + * This sets the granularity of user feedback on eval progress, but + * does not affect mem requirements etc. + */ + chunk = (im->Ysize / 100) + 1; + + /* Loop down the output image, evaling each chunk. + */ + for( y = 0; y < im->Ysize; y += chunk ) { + Rect pos; + + /* Attach or to this position in image. + */ + pos.left = 0; + pos.top = y; + pos.width = im->Xsize; + pos.height = chunk; + if( im_region_image( or, &pos ) ) + return( -1 ); + + /* Ask for evaluation of this area. + */ + if( eval_to_region( or, tg ) ) + return( -1 ); + +#ifdef DEBUG_IO + ntiles++; +#endif /*DEBUG_IO*/ + } + +#ifdef DEBUG_IO + printf( "eval_to_memory: success! %d patches written\n", ntiles ); +#endif /*DEBUG_IO*/ + + return( 0 ); +} + +/* A buffer we are going to write to disc in a background thread. + */ +typedef struct _WriteBuffer { + im_threadgroup_t *tg; /* What makes the pixels */ + REGION *region; /* Pixels */ + Rect area; /* Part of image this region covers */ + im_semaphore_t go; /* Start bg thread loop */ + im_semaphore_t nwrite; /* Number of threads writing to region */ + im_semaphore_t done; /* Bg thread has done write */ + int write_errno; /* Save write errors here */ + GThread *thread; /* BG writer thread */ + gboolean kill; /* Set to ask thread to exit */ +} WriteBuffer; + +static void +wbuffer_free( WriteBuffer *wbuffer ) +{ + /* Is there a thread running this region? Kill it! + */ + if( wbuffer->thread ) { + wbuffer->kill = TRUE; + im_semaphore_up( &wbuffer->go ); + + /* Return value is always NULL (see wbuffer_write_thread). + */ + (void) g_thread_join( wbuffer->thread ); +#ifdef DEBUG_CREATE + printf( "wbuffer_free: g_thread_join()\n" ); +#endif /*DEBUG_CREATE*/ + + wbuffer->thread = NULL; + } + + IM_FREEF( im_region_free, wbuffer->region ); + im_semaphore_destroy( &wbuffer->go ); + im_semaphore_destroy( &wbuffer->nwrite ); + im_semaphore_destroy( &wbuffer->done ); + im_free( wbuffer ); +} + +static void +wbuffer_write( WriteBuffer *wbuffer ) +{ + im_threadgroup_t *tg = wbuffer->tg; + IMAGE *im = tg->im; + REGION *region = wbuffer->region; + Rect *area = &wbuffer->area; + size_t nwritten, count; + void *buf; + + count = region->bpl * area->height; + buf = IM_REGION_ADDR( region, 0, area->top ); + do { + nwritten = write( im->fd, buf, count ); + + /* Write failed? Note in wbuffer errno for the main + * thread to pick up. + */ + if( nwritten == (size_t) -1 ) { + wbuffer->write_errno = errno; + break; + } + + buf = (void *) ((char *) buf + nwritten); + count -= nwritten; + } while( count > 0 ); + +#ifdef DEBUG_IO + printf( "wbuffer_write: %d bytes from wbuffer %p\n", + region->bpl * area->height, wbuffer ); +#endif /*DEBUG_IO*/ +} + +#ifdef HAVE_THREADS +/* Run this as a thread to do a BG write. + */ +static void * +wbuffer_write_thread( void *data ) +{ + WriteBuffer *wbuffer = (WriteBuffer *) data; + + for(;;) { + im_semaphore_down( &wbuffer->go ); + + if( wbuffer->kill ) + break; + + /* Wait for all writer threads to leave this wbuffer. + */ + im_semaphore_downn( &wbuffer->nwrite, 0 ); + + wbuffer_write( wbuffer ); + + /* Signal write complete. + */ + im_semaphore_up( &wbuffer->done ); + } + + return( NULL ); +} +#endif /*HAVE_THREADS*/ + +static WriteBuffer * +wbuffer_new( im_threadgroup_t *tg ) +{ + WriteBuffer *wbuffer; + + if( !(wbuffer = IM_NEW( NULL, WriteBuffer )) ) + return( NULL ); + wbuffer->tg = tg; + wbuffer->region = NULL; + im_semaphore_init( &wbuffer->go, 0, "go" ); + im_semaphore_init( &wbuffer->nwrite, 0, "nwrite" ); + im_semaphore_init( &wbuffer->done, 0, "done" ); + wbuffer->write_errno = 0; + wbuffer->thread = NULL; + wbuffer->kill = FALSE; + + if( !(wbuffer->region = im_region_create( tg->im )) ) { + wbuffer_free( wbuffer ); + return( NULL ); + } + +#ifdef HAVE_THREADS + /* Make this last (picks up parts of wbuffer on startup). + */ + if( !(wbuffer->thread = g_thread_create( wbuffer_write_thread, wbuffer, + TRUE, NULL )) ) { + im_error( "wbuffer_new", _( "unable to create thread" ) ); + wbuffer_free( wbuffer ); + return( NULL ); + } +#endif /*HAVE_THREADS*/ + + return( wbuffer ); +} + +/* At end of work_fn ... need to tell wbuffer write thread that we're done. + */ +static int +wbuffer_work_fn( REGION *region, WriteBuffer *wbuffer ) +{ + im_semaphore_upn( &wbuffer->nwrite, 1 ); + + return( 0 ); +} + +/* Attach a wbuffer to a position. + */ +static int +wbuffer_position( WriteBuffer *wbuffer, + int left, int top, int width, int height ) +{ + Rect image, area; + + image.left = 0; + image.top = 0; + image.width = wbuffer->tg->im->Xsize; + image.height = wbuffer->tg->im->Ysize; + + area.left = left; + area.top = top; + area.width = width; + area.height = height; + + im_rect_intersectrect( &area, &image, &wbuffer->area ); + if( im_region_buffer( wbuffer->region, &wbuffer->area ) ) + return( -1 ); + + /* This should be an exclusive buffer, hopefully. + */ + assert( !wbuffer->region->buffer->done ); + + return( 0 ); +} + +/* Loop over a wbuffer filling it threadily. + */ +static int +wbuffer_fill( WriteBuffer *wbuffer ) +{ + Rect *area = &wbuffer->area; + im_threadgroup_t *tg = wbuffer->tg; + IMAGE *im = tg->im; + Rect image; + + int x, y; + +#ifdef DEBUG_IO + printf( "wbuffer_fill: starting for wbuffer %p at line %d\n", + wbuffer, area->top ); +#endif /*DEBUG_IO*/ + + image.left = 0; + image.top = 0; + image.width = im->Xsize; + image.height = im->Ysize; + + /* Loop over area, sparking threads for all sub-parts in turn. + */ + for( y = area->top; y < IM_RECT_BOTTOM( area ); y += tg->ph ) + for( x = area->left; x < IM_RECT_RIGHT( area ); x += tg->pw ) { + im_thread_t *thr; + Rect pos; + Rect clipped; + + /* thrs appear on idle when the child thread does + * threadgroup_idle_add and hits the 'go' semaphore. + */ + thr = im_threadgroup_get( tg ); + + /* Set the position we want to generate with this + * thread. Clip against the size of the image and the + * space available in or. + */ + pos.left = x; + pos.top = y; + pos.width = tg->pw; + pos.height = tg->ph; + im_rect_intersectrect( &pos, &image, &clipped ); + im_rect_intersectrect( &clipped, area, &clipped ); + + /* Note params. + */ + thr->oreg = wbuffer->region; + thr->pos = clipped; + thr->x = clipped.left; + thr->y = clipped.top; + thr->a = wbuffer; + +#ifdef DEBUG_IO + printf( "wbuffer_fill: starting for tile at %d x %d\n", + x, y ); +#endif /*DEBUG_IO*/ + + /* Add writer to n of writers on wbuffer, set it going. + */ + im_semaphore_upn( &wbuffer->nwrite, -1 ); + im_threadgroup_trigger( thr ); + + /* Trigger any eval callbacks on our source image. + */ + im__handle_eval( tg->im, tg->pw, tg->ph ); + + /* Check for errors. + */ + if( im_threadgroup_iserror( tg ) ) { + /* Don't kill threads yet ... we may want to + * get some error stuff out of them. + */ + im_threadgroup_wait( tg ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Eval to file. + */ +static int +wbuffer_eval_to_file( WriteBuffer *b1, WriteBuffer *b2 ) +{ + im_threadgroup_t *tg = b1->tg; + IMAGE *im = tg->im; + int y; + + assert( b1->tg == b2->tg ); + +#ifdef DEBUG_IO + int nstrips; + + nstrips = 0; + printf( "wbuffer_eval_to_file: partial image output to file\n" ); +#endif /*DEBUG_IO*/ + + /* Note we'll be working to fill a contigious area. + */ + tg->inplace = 1; + + /* What threads do at the end of each tile ... decrement the nwrite + * semaphore. + */ + tg->work = (im__work_fn) wbuffer_work_fn; + + /* Fill to in steps, write each to the output. + */ + for( y = 0; y < im->Ysize; y += tg->nlines ) { + /* Attach to this position in image. + */ + if( wbuffer_position( b1, 0, y, im->Xsize, tg->nlines ) ) + return( -1 ); + + /* Spark off threads to fill with data. + */ + if( wbuffer_fill( b1 ) ) + return( -1 ); + + /* We have to keep the ordering on wbuffer writes, so we can't + * have more than one background write going at once. Plus we + * want to make sure write()s don't get interleaved. Wait for + * the previous BG write (if any) to finish. + */ + if( y > 0 ) { + im_semaphore_down( &b2->done ); + + /* Previous write suceeded? + */ + if( b2->write_errno ) { + im_error_system( b2->write_errno, + "im__eval_to_file", + _( "write failed" ) ); + return( -1 ); + } + } + + /* b1 write can go. + */ + im_semaphore_up( &b1->go ); + +#ifndef HAVE_THREADS + /* No threading ... just write. + */ + wbuffer_write( b1 ); +#endif /*HAVE_THREADS*/ + + /* Rotate wbuffers. + */ + { + WriteBuffer *t; + + t = b1; b1 = b2; b2 = t; + } + +#ifdef DEBUG_IO + nstrips++; +#endif /*DEBUG_IO*/ + } + + /* Wait for all threads to finish, check for any errors. + */ + im_threadgroup_wait( tg ); + im_semaphore_down( &b2->done ); + if( im_threadgroup_iserror( tg ) ) + return( -1 ); + if( b1->write_errno || b2->write_errno ) { + im_error_system( + b1->write_errno ? b1->write_errno : b2->write_errno, + "im__eval_to_file", _( "write failed" ) ); + return( -1 ); + } + +#ifdef DEBUG_IO + printf( "wbuffer_eval_to_file: success! %d strips written\n", nstrips ); +#endif /*DEBUG_IO*/ + + return( 0 ); +} + +static int +eval_to_file( im_threadgroup_t *tg ) +{ + WriteBuffer *b1, *b2; + + b1 = wbuffer_new( tg ); + b2 = wbuffer_new( tg ); + + if( !b1 || !b2 || wbuffer_eval_to_file( b1, b2 ) ) { + IM_FREEF( wbuffer_free, b1 ); + IM_FREEF( wbuffer_free, b2 ); + + return( -1 ); + } + + wbuffer_free( b1 ); + wbuffer_free( b2 ); + + return( 0 ); +} + +/* Attach a generate function to an image. + */ +int +im_generate( IMAGE *im, + void *(*start_fn)(), int (*gen_fn)(), int (*stop_fn)(), + void *a, void *b ) +{ + int res; + REGION *or; + im_threadgroup_t *tg; + + if( im_image_sanity( im ) ) + return( -1 ); + if( im->Xsize <= 0 || im->Ysize <= 0 || im->Bands <= 0 ) { + im_error( "im_generate", _( "bad dimensions" ) ); + return( -1 ); + } + + /* Look at output type to decide our action. + */ + switch( im->dtype ) { + case IM_PARTIAL: + /* Output to partial image. Just attach functions and return. + */ + if( im->generate || im->start || im->stop ) { + im_error( "im_generate", _( "func already attached" ) ); + return( -1 ); + } + + im->start = start_fn; + im->generate = gen_fn; + im->stop = stop_fn; + im->client1 = a; + im->client2 = b; + +#ifdef DEBUG_IO + printf( "im_generate: attaching partial callbacks\n" ); +#endif /*DEBUG_IO*/ + + break; + + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_MMAPINRW: + case IM_OPENOUT: + /* Eval now .. sanity check. + */ + if( im->generate || im->start || im->stop ) { + im_error( "im_generate", _( "func already attached" ) ); + return( -1 ); + } + + /* Get output ready. + */ + if( im_setupout( im ) ) + return( -1 ); + + /* Attach callbacks. + */ + im->start = start_fn; + im->generate = gen_fn; + im->stop = stop_fn; + im->client1 = a; + im->client2 = b; + + /* Evaluate. Two output styles: to memory area (im_setbuf() + * or im_mmapinrw()) or to file (im_openout()). + */ + if( !(or = im_region_create( im )) ) + return( -1 ); + if( !(tg = im_threadgroup_create( im )) ) { + im_region_free( or ); + return( -1 ); + } + if( im->dtype == IM_OPENOUT ) + res = eval_to_file( tg ); + else + res = eval_to_memory( tg, or ); + + /* Clean up. + */ + im_threadgroup_free( tg ); + im_region_free( or ); + + /* Evaluation is now complete, with all sequences finished. + * Trigger evalend callbacks, then free them to make sure we + * don't trigger twice. + */ + res |= im__trigger_callbacks( im->evalendfns ); + IM_FREEF( im_slist_free_all, im->evalendfns ); + + /* Error? + */ + if( res ) + return( -1 ); + + break; + + default: + /* Not a known output style. + */ + im_error( "im_generate", _( "unable to output to a %s image" ), + im_dtype2char( im->dtype ) ); + return( -1 ); + } + + return( 0 ); +} + +/* Generate a region of pixels ... with threads! Very like im_prepare(), but + * threaded and does sub-division. + */ +int +im_prepare_thread( im_threadgroup_t *tg, REGION *or, Rect *r ) +{ + IMAGE *im = or->im; + + if( im_image_sanity( im ) ) + return( -1 ); + + switch( im->dtype ) { + case IM_PARTIAL: + if( im_region_fill( or, r, + (im_region_fill_fn) eval_to_region, tg ) ) + return( -1 ); + + break; + + case IM_OPENIN: + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_MMAPIN: + case IM_MMAPINRW: + /* Attach to existing buffer. + */ + if( im_region_image( or, r ) ) + return( -1 ); + + break; + + default: + im_error( "im_prepare_thread", _( "unable to input from a %s " + "image" ), im_dtype2char( im->dtype ) ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_guess_prefix.c b/libsrc/iofuncs/im_guess_prefix.c new file mode 100644 index 00000000..0281e6c5 --- /dev/null +++ b/libsrc/iofuncs/im_guess_prefix.c @@ -0,0 +1,398 @@ +/* @(#) Guess a value for install prefix. Pass in argv[0] (or NULL) as a clue, + * @(#) plus the name of the controlling environment variable (eg. VIPSHOME). + * @(#) + * @(#) const char * + * @(#) im_guess_prefix( const char *argv0, const char *env_name ) + * @(#) + * @(#) Don't free the string you get back (treat as result of g_getenv()). + * @(#) The function returns NULL on error. + * + * Written on: 5/2/01 + * Modified on: + * 3/3/01 JC + * - better behaviour for relative paths in argv0 + * 22/9/01 JC + * - oops, SEGV in some cases for argv0 contains relative path + * 26/9/01 JC + * - reworked for new prefix scheme + * 9/11/01 JC + * - grr! added strdup() on putenv() for newer linuxes + * 14/12/01 JC + * - now uses realpath() for better relative pathname guessing + * 21/10/02 JC + * - turn off realpath() if not available + * - path_is_absolute() from glib + * - append ".exe" to name on w32 + * - prefix cwd() to path on w32 + * 31/7/03 JC + * - better relative path handling + * 23/12/04 + * - use g_setenv()/g_getenv() + * 29/4/05 + * - gah, back to plain setenv() so we work with glib-2.2 + * 5/10/05 + * - phew, now we can use g_setenv() again + * 18/8/06 + * - use IM_EXEEXT + * 6/2/07 CB + * - move trailing '\0' too in extract_prefix + * 21/7/07 + * - fall back to configure-time prefix rather than returning an error + * (thanks Jay) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#ifdef HAVE_SYS_PARAM_H +#include +#endif /*HAVE_SYS_PARAM_H*/ +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#ifdef HAVE_DIRECT_H +#include +#endif /*HAVE_DIRECT_H*/ +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Strip off any of a set of old suffixes (eg. [".v", ".jpg"]), add a single + * new suffix (eg. ".tif"). + */ +void +im__change_suffix( const char *name, char *out, int mx, + const char *new, const char **olds, int nolds ) +{ + char *p; + int i; + int len; + + /* Copy start string. + */ + im_strncpy( out, name, mx ); + + /* Drop all matching suffixes. + */ + while( (p = strrchr( out, '.' )) ) { + /* Found suffix - test against list of alternatives. Ignore + * case. + */ + for( i = 0; i < nolds; i++ ) + if( g_ascii_strcasecmp( p, olds[i] ) == 0 ) { + *p = '\0'; + break; + } + + /* Found match? If not, break from loop. + */ + if( *p ) + break; + } + + /* Add new suffix. + */ + len = strlen( out ); + im_strncpy( out + len, new, mx - len ); +} + +static char * +get_current_dir( void ) +{ + static char buffer[PATH_MAX]; + char *dir; + + /* We don't use getcwd(3) on SUNOS, because, it does a popen("pwd") + * and, if that wasn't bad enough, hangs in doing so. + */ +#if defined( sun ) && !defined( __SVR4 ) + dir = getwd( buffer ); +#else /* !sun */ + dir = getcwd( buffer, PATH_MAX ); +#endif /* !sun */ + + if( !dir ) { + buffer[0] = G_DIR_SEPARATOR; + buffer[1] = '\0'; + dir = buffer; + } + + return( dir ); +} + +/* Find the prefix part of a dir ... name is the name of this prog from argv0. + * + * dir name guess prefix + * + * /home/john/vips-7.6.4/bin/vips-7.6 vips-7.6 /home/john/vips-7.6.4 + * /usr/local/bin/ip ip /usr/local + * + * all other forms ... return NULL. + */ +static char * +extract_prefix( const char *dir, const char *name ) +{ + char edir[PATH_MAX]; + char vname[PATH_MAX]; + int i; + +#ifdef DEBUG + printf( "extract_prefix: trying for dir = \"%s\", name = \"%s\"\n", + dir, name ); +#endif /*DEBUG*/ + + /* Is dir relative? Prefix with cwd. + */ + if( !g_path_is_absolute( dir ) ) { + im_snprintf( edir, PATH_MAX, "%s" G_DIR_SEPARATOR_S "%s", + get_current_dir(), dir ); + } + else { + im_strncpy( edir, dir, PATH_MAX ); + } + + /* Chop off the trailing prog name, plus the trailing + * G_DIR_SEPARATOR_S. + */ + if( !im_ispostfix( edir, name ) ) + return( NULL ); + im_strncpy( vname, edir, PATH_MAX ); + vname[strlen( edir ) - strlen( name ) - 1] = '\0'; + + /* Remove any "/./", any trailing "/.", any trailing "/". + */ + for( i = 0; i < (int) strlen( vname ); i++ ) + if( im_isprefix( G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, + vname + i ) ) + memcpy( vname + i, vname + i + 2, + strlen( vname + i + 2 ) + 1 ); + if( im_ispostfix( vname, G_DIR_SEPARATOR_S "." ) ) + vname[strlen( vname ) - 2] = '\0'; + if( im_ispostfix( vname, G_DIR_SEPARATOR_S ) ) + vname[strlen( vname ) - 1] = '\0'; + +#ifdef DEBUG + printf( "extract_prefix: canonicalised path = \"%s\"\n", vname ); +#endif /*DEBUG*/ + + /* Ought to be a "/bin" at the end now. + */ + if( !im_ispostfix( vname, G_DIR_SEPARATOR_S "bin" ) ) + return( NULL ); + vname[strlen( vname ) - strlen( G_DIR_SEPARATOR_S "bin" )] = '\0'; + +#ifdef DEBUG + printf( "extract_prefix: found \"%s\"\n", vname ); +#endif /*DEBUG*/ + + return( im_strdup( NULL, vname ) ); +} + +/* Search a path for a file ... we overwrite the PATH string passed in. + */ +static char * +scan_path( char *path, const char *name ) +{ + char *p, *q; + char *prefix; + + for( p = path; + (q = im_break_token( p, G_SEARCHPATH_SEPARATOR_S )); p = q ) { + char str[PATH_MAX]; + + /* Form complete path. + */ + im_snprintf( str, PATH_MAX, + "%s" G_DIR_SEPARATOR_S "%s", p, name ); + +#ifdef DEBUG + printf( "scan_path: looking in \"%s\" for \"%s\"\n", + p, name ); +#endif /*DEBUG*/ + + if( im_existsf( "%s", str ) && + (prefix = extract_prefix( str, name )) ) { + return( prefix ); + } + } + + return( NULL ); +} + +/* Look for a file along PATH. If we find it, look for an enclosing prefix. + */ +static char * +find_file( const char *name ) +{ + const char *path = g_getenv( "PATH" ); + char *prefix; + char full_path[PATH_MAX]; + + if( !path ) + return( NULL ); + +#ifdef DEBUG + printf( "im_guess_prefix: g_getenv( \"PATH\" ) == \"%s\"\n", path ); +#endif /*DEBUG*/ + +#ifdef OS_WIN32 + /* Windows always searches '.' first, so prepend cwd to path. + */ + im_snprintf( full_path, PATH_MAX, "%s" G_SEARCHPATH_SEPARATOR_S "%s", + get_current_dir(), path ); +#else /*!OS_WIN32*/ + im_strncpy( full_path, path, PATH_MAX ); +#endif /*OS_WIN32*/ + + if( (prefix = scan_path( full_path, name )) ) + return( prefix ); + + return( NULL ); +} + +/* Guess a value for the install PREFIX. + */ +static const char * +guess_prefix( const char *argv0, const char *name ) +{ + char *prefix; + + /* Try to guess from argv0. + */ + if( argv0 ) { + if( g_path_is_absolute( argv0 ) ) { + /* Must point to our executable. + */ + if( (prefix = extract_prefix( argv0, name )) ) { +#ifdef DEBUG + printf( "im_guess_prefix: found \"%s\" from " + "argv0\n", prefix ); +#endif /*DEBUG*/ + return( prefix ); + } + } + + /* Look along path for name. + */ + if( (prefix = find_file( name )) ) { +#ifdef DEBUG + printf( "im_guess_prefix: found \"%s\" from " + "PATH\n", prefix ); +#endif /*DEBUG*/ + return( prefix ); + } + } + +#ifdef HAVE_REALPATH + /* Try to guess from cwd. Only if this is a relative path, though. No + * realpath on winders, but fortunately it seems to always generate + * a full path in argv[0]. + */ + if( !g_path_is_absolute( argv0 ) ) { + char full_path[PATH_MAX]; + char resolved[PATH_MAX]; + + im_snprintf( full_path, PATH_MAX, "%s" G_DIR_SEPARATOR_S "%s", + get_current_dir(), argv0 ); + + if( realpath( full_path, resolved ) ) { + if( (prefix = extract_prefix( resolved, name )) ) { + +#ifdef DEBUG + printf( "im_guess_prefix: found \"%s\" " + "from cwd\n", prefix ); +#endif /*DEBUG*/ + return( prefix ); + } + } + } +#endif /*HAVE_REALPATH*/ + + /* Fall back to the configure-time prefix. + */ + return( IM_PREFIX ); +} + +/* Guess a value for the install PREFIX. + */ +const char * +im_guess_prefix( const char *argv0, const char *env_name ) +{ + const char *prefix; + const char *p; + char name[PATH_MAX]; + + /* Already set? + */ + if( (prefix = g_getenv( env_name )) ) { +#ifdef DEBUG + printf( "im_guess_prefix: found \"%s\" in environment\n", + prefix ); +#endif /*DEBUG*/ + return( prefix ); + } + + /* Get the program name from argv0. + */ + p = im_skip_dir( argv0 ); + + /* Add the exe suffix, if it's missing. + */ + if( strlen( IM_EXEEXT ) > 0 ) { + const char *olds[] = { IM_EXEEXT }; + + im__change_suffix( p, name, PATH_MAX, IM_EXEEXT, olds, 1 ); + } + else + im_strncpy( name, p, PATH_MAX ); + +#ifdef DEBUG + printf( "im_guess_prefix: argv0 = %s\n", argv0 ); + printf( "im_guess_prefix: name = %s\n", name ); + printf( "im_guess_prefix: cwd = %s\n", get_current_dir() ); +#endif /*DEBUG*/ + + prefix = guess_prefix( argv0, name ); + g_setenv( env_name, prefix, TRUE ); + + return( prefix ); +} diff --git a/libsrc/iofuncs/im_header.c b/libsrc/iofuncs/im_header.c new file mode 100644 index 00000000..9175afa1 --- /dev/null +++ b/libsrc/iofuncs/im_header.c @@ -0,0 +1,265 @@ +/* im_header_int, im_header_double, im_header_string: output various fields + * from the VIPS header + * + * 9/7/02 JC + * - first version + * 7/6/05 + * - now reads meta fields too + * - cleaned up + * - added im_header_exists(), im_header_map() + * 1/8/05 + * - now im_header_get_type() and im_header_get() rather than + * im_header_exists() + * 4/1/07 + * - removed Hist from standard fields ... now a separate function + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Name, offset pair. + */ +typedef struct _HeaderField { + const char *field; + glong offset; +} HeaderField; + +/* Built in fields and struct offsets. + */ +static HeaderField int_field[] = { + { "Xsize", G_STRUCT_OFFSET( IMAGE, Xsize ) }, + { "Ysize", G_STRUCT_OFFSET( IMAGE, Ysize ) }, + { "Bands", G_STRUCT_OFFSET( IMAGE, Bands ) }, + { "Bbits", G_STRUCT_OFFSET( IMAGE, Bbits ) }, + { "BandFmt", G_STRUCT_OFFSET( IMAGE, BandFmt ) }, + { "Coding", G_STRUCT_OFFSET( IMAGE, Coding ) }, + { "Type", G_STRUCT_OFFSET( IMAGE, Type ) }, + { "Xoffset", G_STRUCT_OFFSET( IMAGE, Xoffset ) }, + { "Yoffset", G_STRUCT_OFFSET( IMAGE, Yoffset ) } +}; + +/* These are actually floats :-( how annoying. We report them as doubles for + * consistency with the im_meta_*() functions. + */ +static HeaderField double_field[] = { + { "Xres", G_STRUCT_OFFSET( IMAGE, Xres ) }, + { "Yres", G_STRUCT_OFFSET( IMAGE, Yres ) } +}; + +static HeaderField string_field[] = { + { "filename", G_STRUCT_OFFSET( IMAGE, filename ) } +}; + +int +im_header_int( IMAGE *im, const char *field, int *out ) +{ + int i; + + for( i = 0; i < IM_NUMBER( int_field ); i++ ) + if( strcmp( field, int_field[i].field ) == 0 ) { + *out = G_STRUCT_MEMBER( int, im, + int_field[i].offset ); + break; + } + + if( i == IM_NUMBER( int_field ) && + im_meta_get_int( im, field, out ) ) { + im_error( "im_header_int", + _( "no such int field \"%s\"" ), field ); + return( -1 ); + } + + return( 0 ); +} + +int +im_header_double( IMAGE *im, const char *field, double *out ) +{ + int i; + + for( i = 0; i < IM_NUMBER( double_field ); i++ ) + if( strcmp( field, double_field[i].field ) == 0 ) { + *out = G_STRUCT_MEMBER( float, im, + double_field[i].offset ); + break; + } + + if( i == IM_NUMBER( double_field ) && + im_meta_get_double( im, field, out ) ) { + im_error( "im_header_double", + _( "no such double field \"%s\"" ), field ); + return( -1 ); + } + + return( 0 ); +} + +int +im_header_string( IMAGE *im, const char *field, char **out ) +{ + int i; + + for( i = 0; i < IM_NUMBER( string_field ); i++ ) + if( strcmp( field, string_field[i].field ) == 0 ) { + *out = G_STRUCT_MEMBER( char *, im, + string_field[i].offset ); + break; + } + + if( i == IM_NUMBER( string_field ) && + im_meta_get_string( im, field, out ) ) { + im_error( "im_header_string", + _( "no such string field \"%s\"" ), field ); + return( -1 ); + } + + return( 0 ); +} + +GType +im_header_get_type( IMAGE *im, const char *field ) +{ + int i; + GType type; + + for( i = 0; i < IM_NUMBER( int_field ); i++ ) + if( strcmp( field, int_field[i].field ) == 0 ) + return( G_TYPE_INT ); + for( i = 0; i < IM_NUMBER( double_field ); i++ ) + if( strcmp( field, double_field[i].field ) == 0 ) + return( G_TYPE_DOUBLE ); + for( i = 0; i < IM_NUMBER( string_field ); i++ ) + if( strcmp( field, string_field[i].field ) == 0 ) + return( G_TYPE_STRING ); + if( (type = im_meta_get_type( im, field )) ) + return( type ); + + return( 0 ); +} + +/* Fill value_copy with a copy of the value, -1 on error. value_copy must be + * zeroed but uninitialised. User must g_value_unset( value ). + */ +int +im_header_get( IMAGE *im, const char *field, GValue *value_copy ) +{ + int i; + + for( i = 0; i < IM_NUMBER( int_field ); i++ ) + if( strcmp( field, int_field[i].field ) == 0 ) { + g_value_init( value_copy, G_TYPE_INT ); + g_value_set_int( value_copy, + G_STRUCT_MEMBER( int, im, + int_field[i].offset ) ); + return( 0 ); + } + + for( i = 0; i < IM_NUMBER( double_field ); i++ ) + if( strcmp( field, double_field[i].field ) == 0 ) { + g_value_init( value_copy, G_TYPE_DOUBLE ); + g_value_set_double( value_copy, + G_STRUCT_MEMBER( float, im, + double_field[i].offset ) ); + return( 0 ); + } + + for( i = 0; i < IM_NUMBER( string_field ); i++ ) + if( strcmp( field, string_field[i].field ) == 0 ) { + g_value_init( value_copy, G_TYPE_STRING ); + g_value_set_static_string( value_copy, + G_STRUCT_MEMBER( char *, im, + string_field[i].offset ) ); + return( 0 ); + } + + if( !im_meta_get( im, field, value_copy ) ) + return( 0 ); + + return( -1 ); +} + +static void * +header_map_fn( Meta *meta, im_header_map_fn fn, void *a ) +{ + return( fn( meta->im, meta->field, &meta->value, a ) ); +} + +void * +im_header_map( IMAGE *im, im_header_map_fn fn, void *a ) +{ + int i; + GValue value = { 0 }; + void *result; + + for( i = 0; i < IM_NUMBER( int_field ); i++ ) { + im_header_get( im, int_field[i].field, &value ); + result = fn( im, int_field[i].field, &value, a ); + g_value_unset( &value ); + + if( result ) + return( result ); + } + + for( i = 0; i < IM_NUMBER( double_field ); i++ ) { + im_header_get( im, double_field[i].field, &value ); + result = fn( im, double_field[i].field, &value, a ); + g_value_unset( &value ); + + if( result ) + return( result ); + } + + for( i = 0; i < IM_NUMBER( string_field ); i++ ) { + im_header_get( im, string_field[i].field, &value ); + result = fn( im, string_field[i].field, &value, a ); + g_value_unset( &value ); + + if( result ) + return( result ); + } + + if( im->Meta_traverse && + (result = im_slist_map2( im->Meta_traverse, + (VSListMap2Fn) header_map_fn, fn, a )) ) + return( result ); + + return( NULL ); +} diff --git a/libsrc/iofuncs/im_histlin.c b/libsrc/iofuncs/im_histlin.c new file mode 100644 index 00000000..5e3fea4e --- /dev/null +++ b/libsrc/iofuncs/im_histlin.c @@ -0,0 +1,120 @@ +/* @(#) Appends one line of history consisting of a buffer of data, + * @(#) time, history and CR in history section of the image descriptor + * @(#) The history variable list must be declared properly + * @(#) by the calling function + * @(#) + * @(#) int im_histlin(variable_list) + * @(#) (variable_list) is (imagedescriptor, format, arg1, arg2, ...) + * @(#) format, arg1, arg2, ... are the same as in printf + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 16/01/1990 + * Modified on : 21/03/1991 + * 28/10/92 JC + * - if Hist is NULL, no longer returns error code. Now makes a history + * line (just the file name) and continues + * - does not overwrite the end of strdup buffers any more! + * - bugs in calls to time/ctime fixed + * - no longer free's ctime's static buffer! + * - frees old Hist correctly + * 22/12/94 + * - ANSIfied with stdarg + * 2/9/05 + * - no more first line of Hist means something special nonsense + * 4/1/07 + * - added im_history_get() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_histlin( IMAGE *im, const char *fmt, ... ) +{ + va_list args; + char line[4096]; + time_t timebuf; + + /* Format command. -40, to leave 26 for the ctime, three for the # and + * a bit. + */ + va_start( args, fmt ); + (void) im_vsnprintf( line, 4096 - 40, fmt, args ); + va_end( args ); + strcat( line, " # " ); + + /* Add the date. ctime always attaches a '\n', gah. + */ + time( &timebuf ); + strcat( line, ctime( &timebuf ) ); + line[strlen( line ) - 1] = '\0'; + +#ifdef DEBUG + printf( "im_histlin: adding:\n\t%s\nto history on image %p\n", + line, im ); +#endif /*DEBUG*/ + + im->history_list = g_slist_append( im->history_list, + im__gvalue_ref_string_new( line ) ); + + return( 0 ); +} + +/* Read an image's history. + */ +const char * +im_history_get( IMAGE *im ) +{ + if( !im->Hist ) + im->Hist = im__gslist_gvalue_get( im->history_list ); + + return( im->Hist ? im->Hist : "" ); +} diff --git a/libsrc/iofuncs/im_image.c b/libsrc/iofuncs/im_image.c new file mode 100644 index 00000000..d44adf3d --- /dev/null +++ b/libsrc/iofuncs/im_image.c @@ -0,0 +1,92 @@ +/* @(#) Make a memory area of pixels into a VIPS image ... we don't free() on + * @(#) im_close(), that's up to the caller ... format is BandFmt + * @(#) + * @(#) Usage: + * @(#) + * @(#) IMAGE * + * @(#) im_image( void *buffer, int width, int height, int bands, int format ) + * @(#) + * @(#) The function returns NULL on error. + * Written on: 11/7/00 + * Modified on: + * 20/3/01 JC + * - oops, broken for IM_BANDFMT_UCHAR + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +IMAGE * +im_image( void *buffer, int width, int height, int bands, int format ) +{ + IMAGE *im; + + if( width <= 0 || height <= 0 || bands <= 0 || + format < 0 || format > IM_BANDFMT_DPCOMPLEX ) { + im_errormsg( "im_image: bad parameters" ); + return( NULL ); + } + + /* Make new output image for us. + */ + if( !(im = im_init( "untitled" )) ) + return( NULL ); + + /* Set header fields. + */ + im->Xsize = width; + im->Ysize = height; + im->Bands = bands; + im->BandFmt = format; + im->Bbits = im_bits_of_fmt( format ); + im->Coding = IM_CODING_NONE; + + if( bands == 1 ) + im->Type = IM_TYPE_B_W; + else if( bands == 3 ) + im->Type = IM_TYPE_RGB; + else + im->Type = IM_TYPE_MULTIBAND; + + im->data = (char *) buffer; + im->dtype = IM_SETBUF_FOREIGN; + + return( im ); +} diff --git a/libsrc/iofuncs/im_init.c b/libsrc/iofuncs/im_init.c new file mode 100644 index 00000000..65271879 --- /dev/null +++ b/libsrc/iofuncs/im_init.c @@ -0,0 +1,168 @@ +/* @(#) Initialise the IMAGE to impossible startup values. Install the + * @(#) filename. + * @(#) + * @(#) IMAGE *im_init( char *filename ) + * + * Copyright: Nicos Dessipris & Kirk Martinez, 1990 + * Written on: 13/02/1990 + * Modified on : 3/6/92 22/2/93 + * 15/4/93 J.Cupitt + * - init for partial image buffers added + * - init for type field + * - filename added + * 10/5/93 J.Cupitt + * - allocates space for IMAGE too, and returns new data + * 23/2/94 JC + * - ANSIfied, man page revised + * 16/8/94 JC + * - evalend callbacks added + * 28/11/94 JC + * - new compression fields added, thr added + * 24/10/95 JC + * - now tracks open images ... see also im_close() and debug.c + * 1/12/04 JC + * - added an im_init_world() to help old progs + * 30/9/05 + * - added sizeof_header + * 2/1/07 + * - init magic + * - init history_list + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_NEW + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Make a new IMAGE structure, set fields to an initial state. We set the + * filename field only. + */ +IMAGE * +im_init( const char *filename ) +{ + IMAGE *im; + + /* Pass in a nonsense name for argv0 ... this init world is only here + * for old programs which are missing an im_init_world() call. We must + * have threads set up before we can process. + */ + if( im_init_world( "vips" ) ) + im_error_clear(); + + if( !(im = IM_NEW( NULL, IMAGE )) ) + return( NULL ); + +#ifdef DEBUG_NEW + printf( "im_init: new IMAGE 0x%p, \"%s\"\n", im, filename ); +#endif /*DEBUG_NEW*/ + + im->Xsize = -1; + im->Ysize = -1; + im->Bands = -1; + im->Bbits = -1; + im->BandFmt = -1; + im->Coding = -1; + im->Type = -1; + im->Xres = 1.0; + im->Yres = 1.0; + im->Length = 0; + im->Compression = 0; + im->Level = 0; + im->Xoffset = 0; + im->Yoffset = 0; + + im->Hist = NULL; + + im->data = NULL; + im->time = NULL; + im->kill = 0; + + im->dtype = IM_NONE; + im->fd = -1; + im->baseaddr = NULL; + im->length = 0; + im->closefns = NULL; + im->evalfns = NULL; + im->evalendfns = NULL; + im->closing = 0; + im->close_pending = 0; + + /* Default to native order. + */ + im->magic = im_amiMSBfirst() ? IM_MAGIC_SPARC : IM_MAGIC_INTEL; + + im->start = NULL; + im->generate = NULL; + im->stop = NULL; + im->client1 = NULL; + im->client2 = NULL; + im->sslock = g_mutex_new(); + im->regions = NULL; + im->dhint = IM_SMALLTILE; + + im->Meta = NULL; + im->Meta_traverse = NULL; + + /* Default to the VIPS header size. Can be changed later. + */ + im->sizeof_header = IM_SIZEOF_HEADER; + + im->windows = NULL; + + im->parents = NULL; + im->children = NULL; + im->serial = 0; + + im->history_list = NULL; + + if( !(im->filename = im_strdup( NULL, filename )) ) { + im_close( im ); + return( NULL ); + } + + im__open_images = g_slist_prepend( im__open_images, im ); + + return( im ); +} diff --git a/libsrc/iofuncs/im_init_world.c b/libsrc/iofuncs/im_init_world.c new file mode 100644 index 00000000..dbe4fd3c --- /dev/null +++ b/libsrc/iofuncs/im_init_world.c @@ -0,0 +1,221 @@ +/* Start up the world of vips. + * + * 7/1/04 JC + * - 1st version + * 7/6/05 + * - g_type_init() too, so we can use gobject + * 2/9/06 + * - also set g_prg_name() and load plugins + * 8/12/06 + * - add liboil support + * 5/2/07 + * - stop a loop if we're called recursively during VIPS startup ... it + * can happen if (for example) im_guess_prefix() fails and tries to + * i18n an error message (thanks Christian) + * 8/6/07 + * - just warn if plugins fail to load correctly: too annoying to have + * VIPS refuse to start because of a dodgy plugin + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Use in various small places where we need a mutex and it's not worth + * making a private one. + */ +GMutex *im__global_lock = NULL; + +int +im_init_world( const char *argv0 ) +{ + static gboolean started = FALSE; + static gboolean done = FALSE; + char *prgname; + const char *prefix; + char name[256]; + + /* Two stage done handling: 'done' means we've completed, 'started' + * means we're currently initialising. Use this to prevent recursive + * invocation. + */ + if( done ) + /* Called more than once, we succeeded, just return OK. + */ + return( 0 ); + if( started ) + /* Recursive invocation, something has broken horribly. + * Hopefully the first init will handle it. + */ + return( 0 ); + started = TRUE; + + /* Need gobject etc. + */ + g_type_init(); + +#ifdef G_THREADS_ENABLED + if( !g_thread_supported() ) + g_thread_init( NULL ); +#endif /*G_THREADS_ENABLED*/ + + if( !im__global_lock ) + im__global_lock = g_mutex_new(); + + prgname = g_path_get_basename( argv0 ); + g_set_prgname( prgname ); + g_free( prgname ); + + /* Try to discover our prefix. + */ + if( !(prefix = im_guess_prefix( argv0, "VIPSHOME" )) ) + return( -1 ); + + /* Get i18n .mo files from $VIPSHOME/share/locale/. + */ + im_snprintf( name, 256, + /* + "%s" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S "locale", + */ + "%s" G_DIR_SEPARATOR_S "share", + prefix ); + bindtextdomain( GETTEXT_PACKAGE, name ); + bind_textdomain_codeset( GETTEXT_PACKAGE, "UTF-8" ); + + /* Start up converters for builtin types. + */ + im__meta_init_types(); + + /* Load up any plugins in $VIPSHOME/lib. We don't error on failure, + * it's too annoying to have VIPS refuse to start because of a broken + * plugin. + */ + if( im_load_plugins( "%s/lib", prefix ) ) { + im_warn( "im_init_world", "%s", im_errorstring() ); + im_error_clear(); + } + + /* Start up the buffer cache. + */ + im__buffer_init(); + +#ifdef HAVE_LIBOIL +{ +#ifdef DEBUG + GTimer *timer = g_timer_new(); +#endif /*DEBUG*/ + + oil_init(); + +#ifdef DEBUG + /* 0.3 is only about 0.1s on my laptop, but this may take longer in + * future. + */ + printf( "oil_init: %gs\n", g_timer_elapsed( timer, NULL ) ); + g_timer_destroy( timer ); +#endif /*DEBUG*/ +} +#endif /*HAVE_LIBOIL*/ + + done = TRUE; + + return( 0 ); +} + +const char * +im__gettext( const char *msgid ) +{ + /* Pass in a nonsense name for argv0 ... this init path is only here + * for old programs which are missing an im_init_world() call. We need + * i18n set up before we can translate. + */ + if( im_init_world( "giant_banana" ) ) + im_error_clear(); + + return( dgettext( GETTEXT_PACKAGE, msgid ) ); +} + +const char * +im__ngettext( const char *msgid, const char *plural, unsigned long int n ) +{ + if( im_init_world( "giant_banana" ) ) + im_error_clear(); + + return( dngettext( GETTEXT_PACKAGE, msgid, plural, n ) ); +} + +static GOptionEntry option_entries[] = { + { "vips-concurrency", 'c', 0, G_OPTION_ARG_INT, &im__concurrency, + N_( "evaluate with N concurrent threads" ), "N" }, + { "vips-tile-width", 'c', 0, G_OPTION_ARG_INT, &im__tile_width, + N_( "set tile width to N (DEBUG)" ), "N" }, + { "vips-tile-height", 'c', 0, G_OPTION_ARG_INT, &im__tile_height, + N_( "set tile height to N (DEBUG)" ), "N" }, + { "vips-thinstrip-height", 'c', 0, + G_OPTION_ARG_INT, &im__thinstrip_height, + N_( "set thinstrip height to N (DEBUG)" ), "N" }, + { "vips-fatstrip-height", 'c', 0, + G_OPTION_ARG_INT, &im__fatstrip_height, + N_( "set fatstrip height to N (DEBUG)" ), "N" }, + { NULL } +}; + +/* The cmd-line options we support. + */ +GOptionGroup * +im_get_option_group( void ) +{ + static GOptionGroup *option_group = NULL; + + if( !option_group ) { + option_group = g_option_group_new( + "vips", _( "VIPS Options" ), _( "Show VIPS options" ), + NULL, NULL ); + g_option_group_add_entries( option_group, option_entries ); + } + + return( option_group ); +} diff --git a/libsrc/iofuncs/im_initdesc.c b/libsrc/iofuncs/im_initdesc.c new file mode 100644 index 00000000..7c6833cf --- /dev/null +++ b/libsrc/iofuncs/im_initdesc.c @@ -0,0 +1,81 @@ +/* @(#) Initialises an image descriptor to entered values + * @(#) fd, baseaddr, data and filename are not handled by this function + * @(#) The order of the args is the same as in vips/vips.h + * + * @(#) Right call: + * @(#) void im_initdesc(image, xsize, ysize, bands, bandbits, bandfmt, + coding, type, xres, yres) + * @(#) IMAGE *image; + * @(#) int xsize, ysize, bands, bandbits, bandfmt, coding, type; + * @(#) float xres, yres; + * HANDLESHEADER + * Copyright: Nicos Dessipris, 1991 + * Written on: 02/04/1991 + * Modified on : 3/6/92 Kirk Martinez + * 23/2/94 JC + * - ANSIfied + * 2/6/07 + * - ignore bandbits + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +void +im_initdesc( IMAGE *image, + int xsize, int ysize, + int bands, int bandbits, int bandfmt, + int coding, int type, + float xres, float yres, + int xo, int yo ) +{ + image->Xsize = xsize; + image->Ysize = ysize; + image->Bands = bands; + /* bandbits is deprecated ... set to whatever the format requires. + */ + image->Bbits = im_bits_of_fmt( bandfmt ); + image->BandFmt = bandfmt; + image->Coding = coding; + image->Type = type; + image->Xres = xres; + image->Yres = yres; + image->Xoffset = xo; + image->Yoffset = yo; +} diff --git a/libsrc/iofuncs/im_iocheck.c b/libsrc/iofuncs/im_iocheck.c new file mode 100644 index 00000000..3fac63fc --- /dev/null +++ b/libsrc/iofuncs/im_iocheck.c @@ -0,0 +1,313 @@ +/* @(#) Function which checks the structures imagein and imageout + * @(#) It returns a valid value only if ip and op are set properly + * @(#) Cases of returned integer value + * @(#) + * @(#) int im_iocheck(imagein, imageout) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Returns -1 on fail + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : + * 15/4/93 JC + * - im_incheck(), im_outcheck() added. + * - type field now checked. + * 10/6/93 JC + * - auto-fallback to old-style input added + * 6/6/95 JC + * - revised and improved fallback code + */ + +/* @(#) Try to make an IMAGE writeable. im_mmapin files become im_mmapinrw + * @(#) files, buffers are left alone and output files and partial images + * @(#) generate an error. + * @(#) + * @(#) int im_rwcheck( im ) + * @(#) IMAGE *im; + * @(#) + * @(#) Returns non-zero on error. + * @(#) + * Copyright: John Cupitt + * Written on: 17/6/92 + * Updated on: + * 15/4/93 + * - checks for partial images added + * - now uses type field + * 31/8/93 JC + * - returns ok for IM_MMAPINRW type files now too + * - returns -1 rather than 1 on error + * - ANSIfied + * 1/10/97 JC + * - moved here, and renamed im_rwcheck() + * 13/2/01 JC + * - im_image_sanity() checks added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include +#include +#ifdef HAVE_SYS_FILE_H +#include +#endif /*HAVE_SYS_FILE_H*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Convert a partial to a setbuf. + */ +static int +convert_ptob( IMAGE *im ) +{ + IMAGE *t1; + + /* Change to IM_SETBUF. First, make a memory buffer and copy into that. + */ + if( !(t1 = im_open( "im_incheck:1", "t" )) ) + return( -1 ); + if( im_copy( im, t1 ) ) { + im_close( t1 ); + return( -1 ); + } + + /* Copy new stuff in. We can't im__close( im ) and free stuff, as this + * would kill of lots of regions and cause dangling pointers + * elsewhere. + */ + im->dtype = IM_SETBUF; + im->data = t1->data; + t1->data = NULL; + + /* Close temp image. + */ + if( im_close( t1 ) ) + return( -1 ); + + return( 0 ); +} + +/* Convert an openin to a mmapin. + */ +static int +convert_otom( IMAGE *im ) +{ + /* just mmap() the whole thing. + */ + if( im_mapfile( im ) ) + return( -1 ); + im->data = im->baseaddr + im->sizeof_header; + im->dtype = IM_MMAPIN; + + return( 0 ); +} + +/* Check that an image is readable by old-style VIPS functions. + */ +int +im_incheck( IMAGE *im ) +{ + if( im_image_sanity( im ) ) + return( -1 ); + +#ifdef DEBUG_IO + printf( "im_incheck: old-style input for %s\n", im->filename ); +#endif/*DEBUG_IO*/ + + switch( im->dtype ) { + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + /* Should have been written to. + */ + if( !im->data ) { + im_errormsg( "im_incheck: no image data" ); + return( -1 ); + } + + break; + + case IM_MMAPIN: + case IM_MMAPINRW: + /* Can read from all these, in principle anyway. + */ + break; + + case IM_PARTIAL: +#ifdef DEBUG_IO + printf( "im_incheck: converting partial image for old-style input\n" ); +#endif/*DEBUG_IO*/ + + /* Change to a setbuf, so our caller can use it. + */ + if( convert_ptob( im ) ) + return( -1 ); + + break; + + case IM_OPENIN: +#ifdef DEBUG_IO + printf( "im_incheck: converting openin image for old-style input\n" ); +#endif/*DEBUG_IO*/ + + /* Change to a MMAPIN. + */ + if( convert_otom( im ) ) + return( -1 ); + + break; + + case IM_OPENOUT: + /* Close file down and reopen as im_mmapin. + */ +#ifdef DEBUG_IO + printf( "im_incheck: auto-rewind of %s\n", im->filename ); +#endif/*DEBUG_IO*/ + if( im__close( im ) || im_openin( im ) ) { + im_errormsg( "im_incheck: auto-rewind for %s failed", + im->filename ); + return( -1 ); + } + + break; + + default: + im_errormsg( "im_incheck: image not readable" ); + return( -1 ); + } + + return( 0 ); +} + +/* Check that an image is writeable. + */ +int +im_outcheck( IMAGE *im ) +{ +#ifdef DEBUG_IO + printf( "im_outcheck: old-style output for %s\n", im->filename ); +#endif/*DEBUG_IO*/ + + switch( im->dtype ) { + case IM_PARTIAL: + /* Make sure nothing is attached. + */ + if( im->generate ) { + im_errormsg( "im_outcheck: image already written" ); + return( -1 ); + } + + /* Cannot do old-style write to PARTIAL. Turn to SETBUF. + */ + im->dtype = IM_SETBUF; + + /* Fall through to SETBUF case. + */ + + case IM_SETBUF: + /* Check that it has not been im_setupout(). + */ + if( im->data ) { + im_errormsg( "im_outcheck: image already written" ); + return( -1 ); + } + + break; + + case IM_OPENOUT: + case IM_SETBUF_FOREIGN: + /* Can write to this ok. + */ + break; + + default: + im_errormsg( "im_outcheck: image not writeable" ); + return( -1 ); + } + + return( 0 ); +} + +/* Check a pair of fds for IO. + */ +int +im_iocheck( IMAGE *in, IMAGE *out ) +{ + return( im_incheck( in ) || im_outcheck( out ) ); +} + +int +im_rwcheck( IMAGE *im ) +{ + /* Do an im_incheck(). This will rewind im_openout() files, and + * generate im_partial() files. + */ + if( im_incheck( im ) ) { + im_errormsg( "im_rwcheck: unable to rewind file" ); + return( -1 ); + } + + /* Look at the type. + */ + switch( im->dtype ) { + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_MMAPINRW: + /* No action necessary. + */ + break; + + case IM_MMAPIN: + /* Try to remap read-write. + */ + if( im_remapfilerw( im ) ) + return( -1 ); + + break; + + default: + im_errormsg( "im_rwcheck: bad file type" ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_iterate.c b/libsrc/iofuncs/im_iterate.c new file mode 100644 index 00000000..36487437 --- /dev/null +++ b/libsrc/iofuncs/im_iterate.c @@ -0,0 +1,230 @@ +/* Manage pipelines of partial images. + * + * J.Cupitt, 17/4/93. + * 1/7/93 JC + * - adapted for partial v2 + * 9/5/94 + * - new thread stuff added, with a define to turn it off + * 21/11/94 JC + * - pw and ph wrong way round! + * 24/5/95 JC + * - redone, now works in pipelines! + * 30/8/96 JC + * - more sharing with im_generate() + * 2/3/98 JC + * - IM_ANY added + * 19/1/99 JC + * - oops, threads were broken :( + * 30/7/99 RP JC + * - threads reorganised for POSIX + * 25/10/03 JC + * - read via a buffer image so we work with mmap window images + * 27/11/06 + * - merge threadgroup stuff + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_IO + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Loop over an image, preparing in parts with threads. + */ +static int +eval_to_image( im_threadgroup_t *tg, IMAGE *im ) +{ + int x, y; + Rect image; + + /* Set up. + */ + tg->inplace = 0; + + image.left = 0; + image.top = 0; + image.width = im->Xsize; + image.height = im->Ysize; + + /* Loop over or, attaching to all sub-parts in turn. + */ + for( y = 0; y < im->Ysize; y += tg->ph ) + for( x = 0; x < im->Xsize; x += tg->pw ) { + im_thread_t *thr; + Rect pos; + Rect clipped; + + /* thrs appear on idle when the child thread does + * threadgroup_idle_add and hits the 'go' semaphore. + */ + thr = im_threadgroup_get( tg ); + + /* Set the position we want to generate with this + * thread. + */ + pos.left = x; + pos.top = y; + pos.width = tg->pw; + pos.height = tg->ph; + im_rect_intersectrect( &image, &pos, &clipped ); + + thr->pos = clipped; + + /* Start worker going. + */ + im_threadgroup_trigger( thr ); + + /* Trigger any eval callbacks on our source image. + */ + im__handle_eval( im, tg->pw, tg->ph ); + + /* Check for errors. + */ + if( im_threadgroup_iserror( tg ) ) { + /* Don't kill threads yet ... we may want to + * get some error stuff out of them. + */ + im_threadgroup_wait( tg ); + return( -1 ); + } + } + + /* Wait for all threads to hit 'go' again. + */ + im_threadgroup_wait( tg ); + + /* Test for any errors. + */ + if( im_threadgroup_iserror( tg ) ) + return( -1 ); + + return( 0 ); +} + +static int +iterate( im_threadgroup_t *tg, IMAGE *im, + void *(*start)(), int (*generate)(), int (*stop)(), void *b, void *c ) +{ + int i; + int res; + +#ifdef DEBUG_IO + if( tg && tg->nthr > 1 ) + im_diagnostics( "im_iterate: using %d threads", tg->nthr ); +#endif /*DEBUG_IO*/ + + /* Call all the start functions, and pop in the sequence values. + */ + for( i = 0; i < tg->nthr; i++ ) { + if( start && !(tg->thr[i]->a = start( im, b, c )) ) { + im_error( "im_iterate", + _( "start function failed for image \"%s\"" ), + im->filename ); + return( -1 ); + } + tg->thr[i]->b = b; + tg->thr[i]->c = c; + } + + /* Loop and generate multi-thread. + */ + res = eval_to_image( tg, im ); + + /* Call all stop functions. + */ + for( i = 0; i < tg->nthr; i++ ) { + if( tg->thr[i]->a && stop ) { + /* Trigger the stop function. + */ + if( stop( tg->thr[i]->a, b, c ) ) + /* Drastic! + */ + im_error( "im_iterate", + _( "stop function failed " + "for image \"%s\"" ), + im->filename ); + tg->thr[i]->a = NULL; + } + } + + return( res ); +} + +/* Scan region over image in small pieces. + */ +int +im_iterate( IMAGE *im, + void *(*start)(), int (*generate)(), int (*stop)(), void *b, void *c ) +{ + IMAGE *t; + im_threadgroup_t *tg; + int res; + + if( im_image_sanity( im ) ) + return( -1 ); + + if( !(t = im_open( "im_iterate_buffer", "p" )) ) + return( -1 ); + if( im_copy( im, t ) ) { + im_close( t ); + return( -1 ); + } + + if( !(tg = im_threadgroup_create( t )) ) { + im_close( t ); + return( -1 ); + } + tg->work = generate; + tg->inplace = 0; + +#ifdef DEBUG_IO + if( tg && tg->nthr > 1 ) + im_diagnostics( "im_iterate: using %d threads", tg->nthr ); +#endif /*DEBUG_IO*/ + + res = iterate( tg, t, start, generate, stop, b, c ); + + im_threadgroup_free( tg ); + im_close( t ); + + return( res ); +} diff --git a/libsrc/iofuncs/im_makerw.c b/libsrc/iofuncs/im_makerw.c new file mode 100644 index 00000000..49342e70 --- /dev/null +++ b/libsrc/iofuncs/im_makerw.c @@ -0,0 +1,86 @@ +/* @(#) Try to make an IMAGE writeable. im_mmapin files become im_mmapinrw + * @(#) files, buffers are left alone and output files and partial images + * @(#) generate an error. + * @(#) + * @(#) int im_makerw( im ) + * @(#) IMAGE *im; + * @(#) + * @(#) Returns non-zero on error. + * @(#) + * Copyright: John Cupitt + * Written on: 17/6/92 + * Updated on: + * 15/4/93 + * - checks for partial images added + * - now uses type field + * 31/8/93 JC + * - returns ok for IM_MMAPINRW type files now too + * - returns -1 rather than 1 on error + * - ANSIfied + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_makerw( IMAGE *im ) +{ + /* This will rewind im_openout() files, and generate im_partial() files. + */ + if( im_incheck( im ) ) { + im_errormsg( "im_makerw: unable to rewind file" ); + return( -1 ); + } + + switch( im->dtype ) { + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_MMAPINRW: + break; + + case IM_MMAPIN: + if( im_remapfilerw( im ) ) + return( -1 ); + break; + + default: + im_errormsg( "im_makerw: bad file type" ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_mapfile.c b/libsrc/iofuncs/im_mapfile.c new file mode 100644 index 00000000..cce255fc --- /dev/null +++ b/libsrc/iofuncs/im_mapfile.c @@ -0,0 +1,351 @@ +/* @(#) Function which returns a char pointer at the beginning of the file + * @(#) corresponding to fd. + * @(#) + * @(#) int + * @(#) im_mapfile( im ) + * @(#) IMAGE im; + * @(#) + * @(#) As above, but map read-write. + * @(#) + * @(#) int + * @(#) im_mapfilerw( im ) + * @(#) IMAGE im; + * @(#) + * @(#) Return -1 on error, 0 for success. + * + * Copyright: Nicos Dessipris + * Wriiten on: 13/02/1990 + * Updated on: + * 10/5/93 J.Cupitt + * - im_mapfilerw() added + * 13/12/94 JC + * - ANSIfied + * 5/7/99 JC + * - better error if unable to map rw + * 31/3/02 JC + * - better mmap() fails error + * 19/9/02 JC + * - added im__mmap()/im__munmap() with windows versions + * 5/1/04 Lev Serebryakov + * - patched for freebsd compatibility + * 5/2/04 JC + * - now records length as well as base, so we unmap the right amount of + * memory even if files change behind our back + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#ifdef HAVE_SYS_MMAN_H +#include +#endif /*HAVE_SYS_MMAN_H*/ +#ifdef HAVE_SYS_FILE_H +#include +#endif /*HAVE_SYS_FILE_H*/ +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#ifdef OS_WIN32 +#ifndef S_ISREG +#define S_ISREG(m) (!!(m & _S_IFREG)) +#endif +#endif /*OS_WIN32*/ + +#include + +#ifdef OS_WIN32 +#include +#endif /*OS_WIN32*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +void * +im__mmap( int fd, int writeable, size_t length, gint64 offset ) +{ + void *baseaddr; + +#ifdef DEBUG + printf( "im__mmap: length = %d, offset = %lld\n", length, offset ); +#endif /*DEBUG*/ + +#ifdef OS_WIN32 +{ + HANDLE hFile = (HANDLE) _get_osfhandle( fd ); + HANDLE hMMFile; + DWORD flProtect; + DWORD dwDesiredAccess; + DWORD dwFileOffsetHigh; + DWORD dwFileOffsetLow; + + /* woah, slightly gross + */ + int dws = sizeof( DWORD ); + int shift = 8 * dws; + gint64 mask = ((gint64) -1) >> shift; + + if( writeable ) { + flProtect = PAGE_READWRITE; + dwDesiredAccess = FILE_MAP_WRITE; + } + else { + flProtect = PAGE_READONLY; + dwDesiredAccess = FILE_MAP_READ; + } + + if( !(hMMFile = CreateFileMapping( hFile, + NULL, flProtect, 0, 0, NULL )) ) { + im_error_system( GetLastError(), "im_mapfile", + _( "unable to CreateFileMapping" ) ); + return( NULL ); + } + + dwFileOffsetHigh = (offset >> shift) & mask; + dwFileOffsetLow = offset & mask; + + if( !(baseaddr = (char *)MapViewOfFile( hMMFile, dwDesiredAccess, + dwFileOffsetHigh, dwFileOffsetLow, length )) ) { + im_error_system( GetLastError(), "im_mapfile", + _( "unable to MapViewOfFile" ) ); + CloseHandle( hMMFile ); + return( NULL ); + } + + /* Can close mapping now ... view stays until UnmapViewOfFile(). + + FIXME ... is this a performance problem? + + */ + CloseHandle( hMMFile ); +} +#else /*!OS_WIN32*/ +{ + int prot; + + if( writeable ) + prot = PROT_WRITE; + else + prot = PROT_READ; + + /* Casting gint64 to off_t should be safe, even on *nixes without + * LARGEFILE. + */ + + baseaddr = mmap( 0, length, prot, MAP_SHARED, fd, (off_t) offset ); + if( baseaddr == MAP_FAILED ) { + im_error_system( errno, "im_mapfile", _( "unable to mmap" ) ); + im_warn( "im_mapfile", _( "map failed (%s), " + "running very low on system resources, " + "expect a crash soon" ), strerror( errno ) ); + return( NULL ); + } +} +#endif /*OS_WIN32*/ + + return( baseaddr ); +} + +int +im__munmap( void *start, size_t length ) +{ +#ifdef OS_WIN32 + if( !UnmapViewOfFile( start ) ) { + im_error_system( GetLastError(), "im_mapfile", + _( "unable to UnmapViewOfFile" ) ); + return( -1 ); + } +#else /*!OS_WIN32*/ + if( munmap( start, length ) < 0 ) { + im_error_system( errno, "im_mapfile", + _( "unable to munmap file" ) ); + return( -1 ); + } +#endif /*OS_WIN32*/ + + return( 0 ); +} + +int +im_mapfile( IMAGE *im ) +{ + gint64 length; + struct stat st; + mode_t m; + + assert( !im->baseaddr ); + + /* Check the size of the file; if it is less than 64 bytes, then flag + * an error. + */ + if( (length = im_file_length( im->fd )) == -1 ) + return( -1 ); + if( fstat( im->fd, &st ) == -1 ) { + im_error( "im_mapfile", _( "unable to get file status" ) ); + return( -1 ); + } + m = (mode_t) st.st_mode; + if( length < 64 ) { + im_error( "im_mapfile", _( "file is less than 64 bytes" ) ); + return( -1 ); + } + if( !S_ISREG( m ) ) { + im_error( "im_mapfile", _( "not a regular file" ) ); + return( -1 ); + } + + if( !(im->baseaddr = im__mmap( im->fd, 0, length, 0 )) ) + return( -1 ); + + /* im__mmap() will fail for >2GB, so this is safe even for large + * files. + */ + im->length = length; + + return( 0 ); +} + +/* As above, but map read/write. + */ +int +im_mapfilerw( IMAGE *im ) +{ + gint64 length; + struct stat st; + mode_t m; + + assert( !im->baseaddr ); + + /* Check the size of the file if it is less than 64 bytes return + * make also sure that it is a regular file + */ + if( (length = im_file_length( im->fd )) == -1 ) + return( -1 ); + if( fstat( im->fd, &st ) == -1 ) { + im_error( "im_mapfilerw", _( "unable to get file status" ) ); + return( -1 ); + } + m = (mode_t) st.st_mode; + if( length < 64 || !S_ISREG( m ) ) { + im_error( "im_mapfile", _( "unable to read data" ) ); + return( -1 ); + } + + if( !(im->baseaddr = im__mmap( im->fd, 1, length, 0 )) ) + return( -1 ); + + /* im__mmap() will fail for >2GB, so this is safe even for large + * files. + */ + im->length = length; + + return( 0 ); +} + +/* From im_rwcheck() ... image needs to be a completely mapped read-only file, + * we try to remap it read-write. + */ +int +im_remapfilerw( IMAGE *image ) +{ + void *baseaddr; + +#ifdef OS_WIN32 +{ + HANDLE hFile = (HANDLE) _get_osfhandle( image->fd ); + HANDLE hMMFile; + + if( !(hMMFile = CreateFileMapping( hFile, + NULL, PAGE_READWRITE, 0, 0, NULL )) ) { + im_error_system( GetLastError(), "im_mapfile", + _( "unable to CreateFileMapping" ) ); + return( -1 ); + } + + if( !UnmapViewOfFile( image->baseaddr ) ) { + im_error_system( GetLastError(), "im_mapfile", + _( "unable to UnmapViewOfFile" ) ); + return( -1 ); + } + if( !(baseaddr = (char *)MapViewOfFileEx( hMMFile, FILE_MAP_WRITE, + 0, 0, 0, image->baseaddr )) ) { + im_error_system( GetLastError(), "im_mapfile", + _( "unable to MapViewOfFile" ) ); + CloseHandle( hMMFile ); + return( -1 ); + } + + /* Can close mapping now ... view stays until UnmapViewOfFile(). + + FIXME ... is this a performance problem? + + */ + CloseHandle( hMMFile ); +} +#else /*!OS_WIN32*/ +{ + assert( image->dtype == IM_MMAPIN ); + + baseaddr = mmap( image->baseaddr, image->length, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, + image->fd, 0 ); + if( baseaddr == (void *)-1 ) { + im_error( "im_mapfile", _( "unable to mmap: \"%s\" - %s" ), + image->filename, strerror( errno ) ); + return( -1 ); + } +} +#endif /*OS_WIN32*/ + + image->dtype = IM_MMAPINRW; + + if( baseaddr != image->baseaddr ) { + im_error( "im_mapfile", _( "unable to mmap \"%s\" to same " + "address" ), image->filename ); + image->baseaddr = baseaddr; + return( -1 ); + } + + return( 0 ); +} + diff --git a/libsrc/iofuncs/im_open.c b/libsrc/iofuncs/im_open.c new file mode 100644 index 00000000..085c6dfc --- /dev/null +++ b/libsrc/iofuncs/im_open.c @@ -0,0 +1,605 @@ +/* @(#) im_open: vips front end to file open functions +ARGS: filename and mode which is one of: +"r" read by mmap (im_mmapin) +"rw" read and write by mmap (im_mmapinrw) +"w" write for write (im_writeline) +"t" for temporary memory image +"p" for partial memory image +RETURNS: IMAGE pointer or 0 on error +Copyright: Kirk Martinez 29/5/92 + +Modified: + * 8/8/94 JC + * - ANSIfied + * - im_open_local added + * 16/8/94 JC + * - im_malloc() added + * 22/11/94 JC & KM + * - tiff added + * 1/2/95 JC + * - tiff removed again! + * - now just im_istiff(), im_tiff2vips() and im_vips2tiff() + * - applications have responsibility for making the translation + * 26/3/96 JC + * - im_open_local() now closes on close, not evalend + * 14/11/96 JC + * - tiff and jpeg added again + * - open for read used im_istiff() and im_isjpeg() to switch + * - open for write looks at suffix + * 23/4/97 JC + * - im_strdup() now allows NULL IMAGE parameter + * - subsample parameter added to im_tiff2vips() + * 29/10/98 JC + * - byte-swap stuff added + * 16/6/99 JC + * - 8x byte swap added for double/double complex + * - better error message if file does not exist + * - ignore case when testing suffix for save + * - fix im_mmapinrw() return test to stop coredump in edvips if + * unwritable + * 2/11/99 JC + * - malloc/strdup stuff moved to memory.c + * 5/8/00 JC + * - fixes for im_vips2tiff() changes + * 13/10/00 JC + * - ooops, missing 'else' + * 22/11/00 JC + * - ppm read/write added + * 12/2/01 JC + * - im__image_sanity() added + * 16/5/01 JC + * - now allows RW for non-native byte order, provided it's an 8-bit + * image + * 11/7/01 JC + * - im_tiff2vips() no longer has subsample option + * 25/3/02 JC + * - better im_open() error message + * 12/12/02 JC + * - sanity no longer returns errors, just warns + * 28/12/02 HB + * - Added PNG support + * 6/5/03 JC + * - added im_open_header() (from header.c) + * 22/5/03 JC + * - im__skip_dir() knows about ':' in filenames for vips2tiff etc. + * 27/11/03 JC + * - im_image_sanity() now insists x/y/bands all >0 + * 6/1/04 JC + * - moved util funcs out to util.c + * 18/2/04 JC + * - added an im_init_world() to help old progs + * 16/4/04 + * - oop, im_open() didn't know about input options in filenames + * 2/11/04 + * - im_open( "r" ) is now lazier ... for non-VIPS formats, we delay + * read until the first ->generate() + * - so im_open_header() is now a no-op + * 22/6/05 + * - if TIFF open fails, try going through libmagick + * 4/8/05 + * - added analyze read + * 30/9/05 + * - oops, lazy read error recovery didn't clear a pointer + * 1/5/06 + * - added OpenEXR read + * 9/6/06 + * - added CSV read/write + * 20/9/06 + * - test for NULL filename/mode, common if you forget to check argc + * (thanks bruno) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_IO + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Suffix sets. + */ +static const char *im_suffix_vips[] = { + "v", "", + NULL +}; +static const char *im_suffix_tiff[] = { + "tif", "tiff", + NULL +}; +static const char *im_suffix_jpeg[] = { + "jpeg", "jpg", "jfif", "jpe", + NULL +}; +static const char *im_suffix_ppm[] = { + "ppm", "pbm", "pgm", + NULL +}; +static const char *im_suffix_png[] = { + "png", + NULL +}; +static const char *im_suffix_csv[] = { + "csv", + NULL +}; + +/* Open a VIPS image and byte-swap the image data if necessary. + */ +static IMAGE * +read_vips( const char *filename ) +{ + IMAGE *im, *im2; + + if( !(im = im_init( filename )) ) + return( NULL ); + if( im_openin( im ) ) { + im_close( im ); + return( NULL ); + } + + /* Already in native format? + */ + if( im_isMSBfirst( im ) == im_amiMSBfirst() ) + return( im ); + + /* Not native ... but maybe does not need swapping? + */ + if( im->Coding == IM_CODING_LABQ ) + return( im ); + if( im->Coding != IM_CODING_NONE ) { + im_close( im ); + im_error( "im_open", _( "unknown coding type" ) ); + return( NULL ); + } + if( im->BandFmt == IM_BANDFMT_CHAR || im->BandFmt == IM_BANDFMT_UCHAR ) + return( im ); + + /* Needs swapping :( make a little pipeline up to do this for us. + */ + if( !(im2 = im_open( filename, "p" )) ) + return( NULL ); + if( im_add_close_callback( im2, + (im_callback_fn) im_close, im, NULL ) ) { + im_close( im ); + im_close( im2 ); + return( NULL ); + } + if( im_copy_swap( im, im2 ) ) { + im_close( im2 ); + return( NULL ); + } + + return( im2 ); +} + +/* Delayed save: if we write to TIFF or to JPEG format, actually do the write + * to a "p" and on evalend do im_vips2tiff() or whatever. Track save + * parameters here. + */ +typedef struct { + int (*save_fn)(); /* Save function */ + IMAGE *im; /* Image to save */ + char *filename; /* Save args */ +} SaveBlock; + +/* From evalend callback: invoke a delayed save. + */ +static int +invoke_sb( SaveBlock *sb ) +{ + if( sb->save_fn( sb->im, sb->filename ) ) + return( -1 ); + + return( 0 ); +} + +/* Attach a SaveBlock to an image. + */ +static int +attach_sb( IMAGE *out, int (*save_fn)(), const char *filename ) +{ + SaveBlock *sb = IM_NEW( out, SaveBlock ); + + if( !sb ) + return( -1 ); + sb->im = out; + sb->save_fn = save_fn; + sb->filename = im_strdup( out, filename ); + + if( im_add_evalend_callback( out, + (im_callback_fn) invoke_sb, (void *) sb, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* Lazy open: init a descriptor from a filename (just read the header) but + * delay actually decoding pixels until the first call to a start function. + */ + +typedef int (*OpenLazyFn)( const char *filename, IMAGE *im ); + +/* What we track during a delayed open. + */ +typedef struct _OpenLazy { + char *filename; + + OpenLazyFn read_pixels; /* Read in pixels with this */ + IMAGE *lazy_im; /* Image we read to .. copy from this */ +} OpenLazy; + +/* Our start function ... do the lazy open, if necessary, and return a region + * on the new image. + */ +static void * +open_lazy_start( IMAGE *out, OpenLazy *lazy, void *dummy ) +{ + if( !lazy->lazy_im ) { + if( !(lazy->lazy_im = im_open_local( out, + "open_lazy_start", "p" )) || + lazy->read_pixels( lazy->filename, lazy->lazy_im ) ) { + IM_FREEF( im_close, lazy->lazy_im ); + return( NULL ); + } + } + + return( im_region_create( lazy->lazy_im ) ); +} + +/* Just copy. + */ +static int +open_lazy_generate( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + + /* Ask for input we need. + */ + if( im_prepare( ir, r ) ) + return( -1 ); + + /* Attach output region to that. + */ + if( im_region_region( or, ir, r, r->left, r->top ) ) + return( -1 ); + + return( 0 ); +} + +/* Lazy open ... init the header with the first OpenLazyFn, delay actually + * decoding pixels with the second OpenLazyFn until the first generate(). + */ +static int +open_lazy( OpenLazyFn read_header, OpenLazyFn read_pixels, + const char *filename, IMAGE *out ) +{ + OpenLazy *lazy = IM_NEW( out, OpenLazy ); + + if( !lazy || + !(lazy->filename = im_strdup( out, filename )) ) + return( -1 ); + lazy->read_pixels = read_pixels; + lazy->lazy_im = NULL; + + if( read_header( filename, out ) ) + return( -1 ); + + if( im_generate( out, + open_lazy_start, open_lazy_generate, im_stop_one, + lazy, NULL ) ) + return( -1 ); + + return( 0 ); +} + +static IMAGE * +open_sub( OpenLazyFn read_header, OpenLazyFn read_pixels, const char *filename ) +{ + IMAGE *im; + + if( !(im = im_open( filename, "p" )) || + open_lazy( read_header, read_pixels, filename, im ) ) { + im_close( im ); + return( NULL ); + } + + return( im ); +} + +IMAGE * +im_open( const char *filename, const char *mode ) +{ + IMAGE *im; + + /* Pass in a nonsense name for argv0 ... this init world is only here + * for old programs which are missing an im_init_world() call. We must + * have threads set up before we can process. + */ + if( im_init_world( "vips" ) ) + im_error_clear(); + + if( !filename || !mode ) { + im_error( "im_open", _( "NULL filename or mode" ) ); + return( NULL ); + } + + switch( mode[0] ) { + case 'r': +{ + char name[FILENAME_MAX]; + char options[FILENAME_MAX]; + + /* Break any options off the name ... eg. "fred.tif:jpeg,tile" + * etc. + */ + im_filename_split( filename, name, options ); + + /* Check for other formats. + + FIXME ... should have a table to avoid all this + repetition + + */ + if( !im_existsf( "%s", name ) ) { + im_error( "im_open", + _( "\"%s\" is not readable" ), name ); + return( NULL ); + } + else if( im_istiff( name ) ) { + /* If TIFF open fails, try going through libmagick. + */ + if( !(im = open_sub( + im_tiff2vips_header, im_tiff2vips, + filename )) && + !(im = open_sub( + im_magick2vips_header, im_magick2vips, + filename )) ) + return( NULL ); + } + else if( im_isjpeg( name ) ) { + if( !(im = open_sub( + im_jpeg2vips_header, im_jpeg2vips, filename )) ) + return( NULL ); + } + else if( im_isexr( name ) ) { + if( !(im = open_sub( + im_exr2vips_header, im_exr2vips, filename )) ) + return( NULL ); + } + else if( im_isppm( name ) ) { + if( !(im = open_sub( + im_ppm2vips_header, im_ppm2vips, filename )) ) + return( NULL ); + } + else if( im_ispng( name ) ) { + if( !(im = open_sub( + im_png2vips_header, im_png2vips, filename )) ) + return( NULL ); + } + else if( im_filename_suffix_match( name, im_suffix_csv ) ) { + if( !(im = open_sub( + im_csv2vips_header, im_csv2vips, filename )) ) + return( NULL ); + } + else if( im_isvips( name ) ) { + if( mode[1] == 'w' ) { + /* Has to be native format for >8 bits. + */ + if( !(im = im_init( filename )) ) + return( NULL ); + if( im_openinrw( im ) ) { + im_close( im ); + return( NULL ); + } + if( im->Bbits != IM_BBITS_BYTE && + im_isMSBfirst( im ) != + im_amiMSBfirst() ) { + im_close( im ); + im_error( "im_open", _( "open for read-" + "write for native format " + "images only" ) ); + return( NULL ); + } + } + else + im = read_vips( filename ); + } + else if( im_isanalyze( name ) ) { + if( !(im = open_sub( + im_analyze2vips_header, im_analyze2vips, + filename )) ) + return( NULL ); + } + else if( im_ismagick( name ) ) { + /* Have this last as it can be very slow to detect + * failure. + */ + if( !(im = open_sub( + im_magick2vips_header, im_magick2vips, + filename )) ) + return( NULL ); + } + else { + im_error( "im_open", _( "\"%s\" is not " + "a supported format" ), filename ); + return( NULL ); + } +} + + break; + + case 'w': + /* Look at the suffix for format write. + */ + if( im_filename_suffix_match( filename, im_suffix_vips ) ) + im = im_openout( filename ); + else if( im_filename_suffix_match( filename, + im_suffix_tiff ) ) { + /* TIFF write. Save to a partial, and on evalend + * im_vips2tiff from that. + */ + if( !(im = im_open( "im_open:vips2tiff:1", "p" )) ) + return( NULL ); + if( attach_sb( im, im_vips2tiff, filename ) ) { + im_close( im ); + return( NULL ); + } + } + else if( im_filename_suffix_match( filename, + im_suffix_jpeg ) ) { + /* JPEG write. + */ + if( !(im = im_open( "im_open:vips2jpeg:1", "p" )) ) + return( NULL ); + if( attach_sb( im, im_vips2jpeg, filename ) ) { + im_close( im ); + return( NULL ); + } + } + else if( im_filename_suffix_match( filename, im_suffix_ppm ) ) { + /* ppm write. + */ + if( !(im = im_open( "im_open:vips2ppm:1", "p" )) ) + return( NULL ); + if( attach_sb( im, im_vips2ppm, filename ) ) { + im_close( im ); + return( NULL ); + } + } + else if( im_filename_suffix_match( filename, im_suffix_png ) ) { + /* png write. + */ + if( !(im = im_open( "im_open:vips2png:1", "p" )) ) + return( NULL ); + if( attach_sb( im, im_vips2png, filename ) ) { + im_close( im ); + return( NULL ); + } + } + else if( im_filename_suffix_match( filename, im_suffix_csv ) ) { + /* csv write. + */ + if( !(im = im_open( "im_open:vips2csv:1", "p" )) ) + return( NULL ); + if( attach_sb( im, im_vips2csv, filename ) ) { + im_close( im ); + return( NULL ); + } + } + else { + im_error( "im_open", _( "unknown suffix for save" ) ); + return( NULL ); + } + break; + + case 't': + im = im_setbuf( filename ); + break; + + case 'p': + im = im_partial( filename ); + break; + + default: + /* Getting here means bad mode + */ + im_error( "im_open", _( "bad mode \"%s\"" ), mode ); + return( NULL ); + } + +#ifdef DEBUG_IO + printf( "im_open: success for %s (%p)\n", im->filename, im ); +#endif /*DEBUG_IO*/ + + return( im ); +} + +/* Test an image for sanity. We could add many more tests here. + */ +static const char * +image_sanity( IMAGE *im ) +{ + if( !im ) + return( "NULL descriptor" ); + if( !im->filename ) + return( "NULL filename" ); + if( !g_slist_find( im__open_images, im ) ) + return( "not on open image list" ); + + if( im->Xsize <= 0 || im->Ysize <= 0 || im->Bands <= 0 ) + return( "bad dimensions" ); + if( im->BandFmt < -1 || im->BandFmt > IM_BANDFMT_DPCOMPLEX || + (im->Coding != -1 && + im->Coding != IM_CODING_NONE && + im->Coding != IM_CODING_LABQ) || + im->Type < -1 || im->Type > IM_TYPE_GREY16 || + im->dtype > IM_PARTIAL || im->dhint > IM_ANY ) + return( "bad enum value" ); + if( im->Xres < 0 || im->Xres < 0 ) + return( "bad resolution" ); + + return( NULL ); +} + +int +im_image_sanity( IMAGE *im ) +{ + const char *msg; + + if( (msg = image_sanity( im )) ) { + im_warn( "im_image_sanity", "\"%s\" (%p) %s", + im ? (im->filename ? im->filename : "") : "", + im, msg ); + im_printdesc( im ); + } + + return( 0 ); +} + +/* Just here for compatibility. + */ +IMAGE * +im_open_header( const char *file ) +{ + return( im_open( file, "r" ) ); +} diff --git a/libsrc/iofuncs/im_openin.c b/libsrc/iofuncs/im_openin.c new file mode 100644 index 00000000..1a948015 --- /dev/null +++ b/libsrc/iofuncs/im_openin.c @@ -0,0 +1,293 @@ +/* @(#) Open a VIPS image file for reading. The IMAGE should be as made by + * @(#) im_init(), or after im__close. + * @(#) + * @(#) If the file is small, we mmap() it + * @(#) and make an image of type IM_MMAP. If the file is large, we don't + * @(#) mmap(); instead we just open a file descriptor and wait for any regions + * @(#) defined on the image to make small mmap() windows. Also read the + * @(#) history. + * @(#) + * @(#) int + * @(#) im_openin( IMAGE *image ) + * @(#) + * @(#) As above, but always mmap() the whole file, and do it read/write. + * @(#) + * @(#) int + * @(#) im_openinrw( IMAGE *image ) + * + * Copyright: Nicos Dessipris + * Written on: 13/02/1990 + * Modified on : 27/02/1991 + * 17/6/92 J.Cupitt + * - Now opens read-write if possible. This allows later calls to + * im_makerw. We mmap with PROT_READ, so there is no danger of + * scribbling over owned images. + * 16/4/93 J.Cupitt + * - adapted to use type field + * 10/5/93 J.Cupitt + * - split into im__mmapin() and im_mmapin() for im_openout() convenience + * - functions of im_mmapin.c and im_mmapinrw.c combined + * 7/9/93 JC + * - now sets dhint field + * 17/11/94 JC + * - checks length of compressed files too + * 19/8/98 JC + * - uses strerror() to print system error messages + * 28/10/98 JC + * - _INTEL and _SPARC auto byte-swap added + * 6/8/02 JC + * - redone for mmap() window stuff + * 13/3/06 JC + * - don't abort load if we can't get the XML + * 16/8/06 + * - more O_BINARY nonsense to help cygwin + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#ifdef HAVE_IO_H +#include +#endif + +/* Try to make an O_BINARY ... sometimes need the leading '_'. + */ +#ifdef BINARY_OPEN +#ifndef O_BINARY +#ifdef _O_BINARY +#define O_BINARY _O_BINARY +#endif /*_O_BINARY*/ +#endif /*!O_BINARY*/ +#endif /*BINARY_OPEN*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* mmap() whole vs. window threshold ... an int, so we can tune easily from a + * debugger. + */ +#ifdef DEBUG +int im__mmap_limit = 1; +#else +int im__mmap_limit = IM__MMAP_LIMIT; +#endif /*DEBUG*/ + +/* Sort of open for read for image files. + */ +int +im__open_image_file( const char *filename ) +{ + int fd; + + /* Try to open read-write, so that calls to im_makerw() will work. + * When we later mmap this file, we set read-only, so there + * is little danger of scrubbing over files we own. + */ +#ifdef BINARY_OPEN + if( (fd = open( filename, O_RDWR | O_BINARY )) == -1 ) { +#else /*BINARY_OPEN*/ + if( (fd = open( filename, O_RDWR )) == -1 ) { +#endif /*BINARY_OPEN*/ + /* Open read-write failed. Fall back to open read-only. + */ +#ifdef BINARY_OPEN + if( (fd = open( filename, O_RDONLY | O_BINARY )) == -1 ) { +#else /*BINARY_OPEN*/ + if( (fd = open( filename, O_RDONLY )) == -1 ) { +#endif /*BINARY_OPEN*/ + im_error( "im__open_image_file", + _( "unable to open \"%s\", %s" ), + filename, strerror( errno ) ); + return( -1 ); + } + } + + return( fd ); +} + +/* Predict the size of the header plus pixel data. Don't use off_t, + * it's sometimes only 32 bits (eg. on many windows build environments) and we + * want to always be 64 bit. + */ +gint64 +im__image_pixel_length( IMAGE *im ) +{ + gint64 psize; + + switch( im->Coding ) { + case IM_CODING_LABQ: + case IM_CODING_NONE: + psize = (gint64) IM_IMAGE_SIZEOF_LINE( im ) * im->Ysize; + break; + + default: + psize = im->Length; + break; + } + + return( psize + im->sizeof_header ); +} + +/* Open the filename, read the header, some sanity checking. + */ +int +im__read_header( IMAGE *image ) +{ + /* We don't use im->sizeof_header here, but we know we're reading a + * VIPS image anyway. + */ + unsigned char header[IM_SIZEOF_HEADER]; + + gint64 length; + gint64 psize; + + image->dtype = IM_OPENIN; + if( (image->fd = im__open_image_file( image->filename )) == -1 ) + return( -1 ); + if( read( image->fd, header, IM_SIZEOF_HEADER ) != IM_SIZEOF_HEADER || + im__read_header_bytes( image, header ) ) { + im_error( "im_openin", + _( "unable to read header for \"%s\", %s" ), + image->filename, strerror( errno ) ); + return( -1 ); + } + + /* Predict and check the file size. + */ + psize = im__image_pixel_length( image ); + if( (length = im_file_length( image->fd )) == -1 ) + return( -1 ); + if( psize > length ) { + im_error( "im_openin", _( "unable to open \"%s\", %s" ), + image->filename, _( "file has been truncated" ) ); + return( -1 ); + } + + /* Set demand style. Allow the most permissive sort. + */ + image->dhint = IM_THINSTRIP; + + /* Set the history part of im descriptor. Don't return an error if this + * fails (due to eg. corrupted XML) because it's probably mostly + * harmless. + */ + if( im__readhist( image ) ) { + im_warn( "im_openin", _( "error reading XML: %s" ), + im_error_buffer() ); + im_error_clear(); + } + + return( 0 ); +} + +/* Open, then mmap() small images, leave large images to have a rolling mmap() + * window for each region. + */ +int +im_openin( IMAGE *image ) +{ + gint64 size; + +#ifdef DEBUG + char *str; + + if( (str = g_getenv( "IM_MMAP_LIMIT" )) ) { + im__mmap_limit = atoi( str ); + printf( "im_openin: setting maplimit to %d from environment\n", + im__mmap_limit ); + } +#endif /*DEBUG*/ + + if( im__read_header( image ) ) + return( -1 ); + + size = (gint64) IM_IMAGE_SIZEOF_LINE( image ) * image->Ysize + + image->sizeof_header; + if( size < im__mmap_limit ) { + if( im_mapfile( image ) ) + return( -1 ); + image->data = image->baseaddr + image->sizeof_header; + image->dtype = IM_MMAPIN; + +#ifdef DEBUG + printf( "im_openin: completely mmap()ing \"%s\": it's small\n", + image->filename ); +#endif /*DEBUG*/ + } + else { +#ifdef DEBUG + printf( "im_openin: delaying mmap() of \"%s\": it's big!\n", + image->filename ); +#endif /*DEBUG*/ + } + + return( 0 ); +} + +/* Open, then mmap() read/write. + */ +int +im_openinrw( IMAGE *image ) +{ + if( im__read_header( image ) ) + return( -1 ); + + if( im_mapfilerw( image ) ) + return( -1 ); + image->data = image->baseaddr + image->sizeof_header; + image->dtype = IM_MMAPINRW; + +#ifdef DEBUG + printf( "im_openin: completely mmap()ing \"%s\" read-write\n", + image->filename ); +#endif /*DEBUG*/ + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_openout.c b/libsrc/iofuncs/im_openout.c new file mode 100644 index 00000000..ba646e35 --- /dev/null +++ b/libsrc/iofuncs/im_openout.c @@ -0,0 +1,75 @@ +/* @(#) im_openout: associates an IMAGE with an image file for output + * @(#) IMAGE should be closed with im_close() + * @(#) Usage: + * @(#) IMAGE *im_openout(file_name) + * @(#) char *file_name; + * @(#) + * @(#) Returns *IMAGE or NULL on error. + * + * Copyright: Nicos Dessipris + * Written on: 13/02/1990 + * Modified on : 26/04/1990 by KM + * 16/4/93 JC + * - uses new init, type style + * - memory leak fixed + * 11/5/93 JC + * - newer im_init() style + * 23/10/98 JC + * - new BINARY_OPEN define + * 4/7/01 JC + * - delay open() until im_setupout() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +IMAGE * +im_openout( const char *file_name ) +{ + IMAGE *image; + + if( !(image = im_init( file_name )) ) + return( NULL ); + image->dtype = IM_OPENOUT; + + return( image ); +} diff --git a/libsrc/iofuncs/im_partial.c b/libsrc/iofuncs/im_partial.c new file mode 100644 index 00000000..d0492abc --- /dev/null +++ b/libsrc/iofuncs/im_partial.c @@ -0,0 +1,64 @@ +/* @(#) im_partial: initialise a partial IMAGE. Just set im->dtype. + * @(#) + * @(#) IMAGE * + * @(#) im_partial( file_name ) + * @(#) char *file_name; + * @(#) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +IMAGE * +im_partial( const char *filename ) +{ + IMAGE *im = im_init( filename ); + + if( !im ) + return(NULL); + im->dtype = IM_PARTIAL; + + /* No need to set demand style - im_demand_hint() from application + * will do this. + */ + + return( im ); +} diff --git a/libsrc/iofuncs/im_piocheck.c b/libsrc/iofuncs/im_piocheck.c new file mode 100644 index 00000000..6e94c35d --- /dev/null +++ b/libsrc/iofuncs/im_piocheck.c @@ -0,0 +1,197 @@ +/* @(#) Function which checks the structures imagein and imageout and gets + * @(#) ready for partial input. If a function calls this in its setup stage, + * @(#) we assume it is partial-ready. If it calls im_iocheck(), we fall back + * @(#) to old-style behaviour. + * @(#) + * @(#) int + * @(#) im_piocheck( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) int + * @(#) im_pincheck( imagein ) + * @(#) IMAGE *imagein; + * @(#) + * @(#) int + * @(#) im_piocheck( imageout ) + * @(#) IMAGE *imageout; + * @(#) + * @(#) Returns -1 on fail + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : + * 15/4/93 J.Cupitt + * - im_incheck(), im_outcheck() added. + * - type field now checked. + * 10/6/93 J.Cupitt + * - im_iocheck() adapted to make im_piocheck() + * - auto-rewind feature added + * 27/10/95 JC + * - im_pincheck() on a setbuf now zaps generate function so as not to + * confuse any later calls to im_prepare() or im_prepare_inplace() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Check that an image is readable. + */ +int +im_pincheck( IMAGE *im ) +{ + if( im_image_sanity( im ) ) + return( -1 ); + +#ifdef DEBUG_IO + printf( "im_pincheck: enabling partial input for %s\n", im->filename ); +#endif /*DEBUG_IO*/ + + switch( im->dtype ) { + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + /* Should have been written to. + */ + if( !im->data ) { + im_errormsg( "im_pincheck: no image data" ); + return( -1 ); + } + + /* Should be no generate functions now. + */ + im->start = NULL; + im->generate = NULL; + im->stop = NULL; + + break; + + case IM_PARTIAL: + /* Should have had generate functions attached. + */ + if( !im->generate ) { + im_errormsg( "im_pincheck: no image data" ); + return( -1 ); + } + + break; + + case IM_MMAPIN: + case IM_MMAPINRW: + case IM_OPENIN: + break; + + case IM_OPENOUT: + /* Close file down and reopen as im_mmapin. + */ +#ifdef DEBUG_IO + printf( "im_pincheck: auto-rewind of %s\n", im->filename ); +#endif/*DEBUG_IO*/ + if( im__close( im ) || im_openin( im ) ) { + im_errormsg( "im_pincheck: auto-rewind for %s failed", + im->filename ); + return( -1 ); + } + + break; + + default: + im_errormsg( "im_pincheck: image not readable" ); + return( -1 ); + } + + return( 0 ); +} + +/* Check that an image is writeable. + */ +int +im_poutcheck( IMAGE *im ) +{ + if( !im ) { + im_errormsg( "im_poutcheck: null image descriptor" ); + return( -1 ); + } + +#ifdef DEBUG_IO + printf( "im_pincheck: enabling partial output for %s\n", im->filename ); +#endif /*DEBUG_IO*/ + + switch( im->dtype ) { + case IM_SETBUF: + /* Check that it has not been im_setupout(). + */ + if( im->data ) { + im_errormsg( "im_poutcheck: image already written" ); + return( -1 ); + } + + break; + + case IM_PARTIAL: + /* Make sure nothing is attached. + */ + if( im->generate ) { + im_errormsg( "im_poutcheck: image already written" ); + return( -1 ); + } + + break; + + case IM_OPENOUT: + case IM_SETBUF_FOREIGN: + /* Okeydoke. Not much checking here. + */ + break; + + default: + im_errormsg( "im_poutcheck: image not writeable" ); + return( -1 ); + } + + return( 0 ); +} + +/* Check a pair of fds for IO. + */ +int +im_piocheck( IMAGE *in, IMAGE *out ) +{ + return( im_pincheck( in ) || im_poutcheck( out ) ); +} diff --git a/libsrc/iofuncs/im_prepare.c b/libsrc/iofuncs/im_prepare.c new file mode 100644 index 00000000..6871eb56 --- /dev/null +++ b/libsrc/iofuncs/im_prepare.c @@ -0,0 +1,384 @@ +/* Request an area of an image for input. + * + * J.Cupitt, 17/4/93. + * + * Modified: + * 22/11/94 JC + * - check for start and stop functions removed, as can now be NULL + * 22/2/95 JC + * - im_fill_copy() added + * 18/4/95 JC + * - kill flag added for compute cases + * 27/10/95 JC + * - im_fill_copy() now only uses im_generate() mechanism if it has to + * - im_fill_copy() renamed as im_prepare_inplace() + * 18/8/99 JC + * - oops ... cache stuff removed, interacted badly with inplace ops + * 26/3/02 JC + * - better error message for im_prepare() + * 9/8/02 JC + * - im_prepare_inplace() broke with mmap() windows ... im_prepare_to() + * replaces it and is nicer + * 30/9/05 + * - hmm, did not stop if a start function failed + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Generate into a region. + */ +static int +fill_region( REGION *reg ) +{ + IMAGE *im = reg->im; + + /* Start new sequence, if necessary. + */ + if( im__call_start( reg ) ) + return( -1 ); + + /* Ask for evaluation. + */ + if( im->generate( reg, reg->seq, im->client1, im->client2 ) ) + return( -1 ); + + return( 0 ); +} + +int +im__test_kill( IMAGE *im ) +{ + /* Has kill been set for this image? If yes, abort evaluation. + */ + if( im->kill ) { + im_error( "im__test_kill", _( "killed for image \"%s\"" ), + im->filename ); + return( -1 ); + } + + return( 0 ); +} + +/* Make REGION reg ready for input of area r. For most image types, we can + * just im_attach, for PARTIAL though we may need to generate. + */ +int +im_prepare( REGION *reg, Rect *r ) +{ + IMAGE *im = reg->im; + + Rect save = *r; + + im__region_check_ownership( reg ); + + if( im__test_kill( im ) ) + return( -1 ); + + /* We use save for sanity checking valid: we test at the end that the + * pixels we have generated are indeed all the ones that were asked + * for. + * + * However, r may be clipped by the image size, so we need to clip + * save as well to make sure we don't fail the assert due to that. + */ +{ + Rect image; + + image.left = 0; + image.top = 0; + image.width = reg->im->Xsize; + image.height = reg->im->Ysize; + im_rect_intersectrect( &save, &image, &save ); +} + +#ifdef DEBUG + printf( "im_prepare: left = %d, top = %d, width = %d, height = %d\n", + r->left, r->top, r->width, r->height ); +#endif /*DEBUG*/ + + switch( im->dtype ) { + case IM_PARTIAL: + if( im_region_fill( reg, r, + (im_region_fill_fn) fill_region, NULL ) ) + return( -1 ); + + break; + + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_MMAPIN: + case IM_MMAPINRW: + case IM_OPENIN: + /* Attach to existing buffer. + */ + if( im_region_image( reg, r ) ) + return( -1 ); + + break; + + default: + im_error( "im_prepare", _( "unable to input from a %s image" ), + im_dtype2char( im->dtype ) ); + return( -1 ); + } + + /* valid should now include all the pixels that were asked for. + */ + assert( im_rect_includesrect( ®->valid, &save ) ); + + return( 0 ); +} + +/* Copy from one region to another. Copy area r from inside reg to dest, + * positioning the area of pixels at x, y. + */ +void +im__copy_region( REGION *reg, REGION *dest, Rect *r, int x, int y ) +{ + int z; + int len = IM_IMAGE_SIZEOF_PEL( reg->im ) * r->width; + char *p = IM_REGION_ADDR( reg, r->left, r->top ); + char *q = IM_REGION_ADDR( dest, x, y ); + int plsk = IM_REGION_LSKIP( reg ); + int qlsk = IM_REGION_LSKIP( dest ); + +#ifdef DEBUG + /* Find the area we will write to in dest. + */ + Rect output; + + printf( "im__copy_region: sanity check\n" ); + + output.left = x; + output.top = y; + output.width = r->width; + output.height = r->height; + + /* Must be inside dest->valid. + */ + assert( im_rect_includesrect( &dest->valid, &output ) ); + + /* Check the area we are reading from in reg. + */ + assert( im_rect_includesrect( ®->valid, r ) ); +#endif /*DEBUG*/ + + for( z = 0; z < r->height; z++ ) { + memcpy( q, p, len ); + + p += plsk; + q += qlsk; + } +} + +/* We need to make pixels using reg's generate function, and write the result + * to dest. + */ +static int +im_prepare_to_generate( REGION *reg, REGION *dest, Rect *r, int x, int y ) +{ + IMAGE *im = reg->im; + char *p; + + if( !im->generate ) { + im_error( "im_prepare_to", _( "incomplete header" ) ); + return( -1 ); + } + + if( im_region_region( reg, dest, r, x, y ) ) + return( -1 ); + + /* Remember where reg is pointing now. + */ + p = IM_REGION_ADDR( reg, reg->valid.left, reg->valid.top ); + + /* Run sequence into reg. + */ + if( fill_region( reg ) ) + return( -1 ); + + /* The generate function may not have actually made any pixels ... it + * might just have redirected reg to point somewhere else. If it has, + * we need an extra copy operation. + */ + if( IM_REGION_ADDR( reg, reg->valid.left, reg->valid.top ) != p ) + im__copy_region( reg, dest, r, x, y ); + + return( 0 ); +} + +/* Like im_prepare(): fill reg with data, ready to be read from by our caller. + * Unlike im_prepare(), rather than allocating memory local to reg for the + * result, we guarantee that we will fill the pixels in dest at offset x, y. + * In other words, we generate an extra copy operation if necessary. + */ +int +im_prepare_to( REGION *reg, REGION *dest, Rect *r, int x, int y ) +{ + IMAGE *im = reg->im; + Rect image; + Rect wanted; + Rect clipped; + Rect clipped2; + Rect final; + + if( im__test_kill( im ) ) + return( -1 ); + + /* Sanity check. + */ + if( !dest->data || dest->im->BandFmt != reg->im->BandFmt || + dest->im->Bands != reg->im->Bands ) { + im_error( "im_prepare_to", _( "inappropriate region type" ) ); + return( -1 ); + } + + /* clip r first against the size of reg->im, then again against the + * memory we have available to write to on dest. Just like + * im_region_region() + */ + image.top = 0; + image.left = 0; + image.width = reg->im->Xsize; + image.height = reg->im->Ysize; + im_rect_intersectrect( r, &image, &clipped ); + + assert( clipped.left == r->left ); + assert( clipped.top == r->top ); + + wanted.left = x + (clipped.left - r->left); + wanted.top = y + (clipped.top - r->top); + wanted.width = clipped.width; + wanted.height = clipped.height; + + /* Test that dest->valid is large enough. + */ + if( !im_rect_includesrect( &dest->valid, &wanted ) ) { + im_error( "im_prepare_to", _( "dest too small" ) ); + return( -1 ); + } + + im_rect_intersectrect( &wanted, &dest->valid, &clipped2 ); + + /* Translate back to reg's coordinate space and set as valid. + */ + final.left = r->left + (clipped2.left - wanted.left); + final.top = r->top + (clipped2.top - wanted.top); + final.width = clipped2.width; + final.height = clipped2.height; + + x = clipped2.left; + y = clipped2.top; + + if( im_rect_isempty( &final ) ) { + im_error( "im_prepare_to", _( "valid clipped to nothing" ) ); + return( -1 ); + } + +#ifdef DEBUG + printf( "im_prepare_to: left = %d, top = %d, width = %d, height = %d\n", + final.left, final.top, final.width, final.height ); +#endif /*DEBUG*/ + + /* Input or output image type? + */ + switch( im->dtype ) { + case IM_OPENOUT: + case IM_PARTIAL: + /* We are generating with a sequence. + */ + if( im_prepare_to_generate( reg, dest, &final, x, y ) ) + return( -1 ); + + break; + + case IM_MMAPIN: + case IM_MMAPINRW: + case IM_OPENIN: + /* Attach to existing buffer and copy to dest. + */ + if( im_region_image( reg, &final ) ) + return( -1 ); + im__copy_region( reg, dest, &final, x, y ); + + break; + + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + /* Could be either input or output. If there is a generate + * function, we are outputting. + */ + if( im->generate ) { + if( im_prepare_to_generate( reg, dest, &final, x, y ) ) + return( -1 ); + } + else { + if( im_region_image( reg, &final ) ) + return( -1 ); + im__copy_region( reg, dest, &final, x, y ); + } + + break; + + default: + im_error( "im_prepare_to", _( "unable to input from a " + "%s image" ), im_dtype2char( im->dtype ) ); + return( -1 ); + } + + return( 0 ); +} + +int +im_prepare_many( REGION **reg, Rect *r ) +{ + for( ; *reg; ++reg ) + if( im_prepare( *reg, r ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_printdesc.c b/libsrc/iofuncs/im_printdesc.c new file mode 100644 index 00000000..d865113b --- /dev/null +++ b/libsrc/iofuncs/im_printdesc.c @@ -0,0 +1,346 @@ +/* @(#) display VIPS IMAGE descriptor for debugging + * @(#) a valid IMAGE descriptor is needed + * @(#) HANDLESIMAGE + * @(#) Static strings must be modified if header files are changed + * @(#) void im_printdesc(image) + * @(#) IMAGE *image; + * + * Copyright: Nicos Dessipris + * Written on: 15/01/1990 + * Modified on : 3/6/92 Kirk Martinez + * 15/4/93 JC + * - partial regions dumped too + * - resolution bug fixed + * 11/5/93 JC + * - formatting changed for ctags + * 1/7/93 JC + * - adapted for partial v2 + * 15/11/94 JC + * - new Coding types added + * - new Type values added + * - new Compression type added + * 2/3/98 JC + * - IM_ANY added + * 5/11/98 JC + * - prints byte-order too + * 1/8/05 + * - prints meta header fields too + * 9/9/05 + * - oops, shouldn't print filename ourselves + * 4/1/07 + * - use new im_history_get() thing + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static const char *im_Type[] = { + "IM_TYPE_MULTIBAND", /* 0 */ + "IM_TYPE_B_W", /* 1 */ + "LUMINACE", /* 2 */ + "XRAY", /* 3 */ + "IR", /* 4 */ + "YUV", /* 5 */ + "RED_ONLY", /* 6 */ + "GREEN_ONLY", /* 7 */ + "BLUE_ONLY", /* 8 */ + "POWER_SPECTRUM", /* 9 */ + "IM_TYPE_HISTOGRAM", /* 10 */ + "LUT", /* 11 */ + "IM_TYPE_XYZ", /* 12 */ + "IM_TYPE_LAB", /* 13 */ + "CMC", /* 14 */ + "IM_TYPE_CMYK", /* 15 */ + "IM_TYPE_LABQ", /* 15 */ + "IM_TYPE_RGB", /* 17 */ + "IM_TYPE_UCS", /* 18 */ + "IM_TYPE_LCH", /* 19 */ + "IM_TYPE_LABS", /* 20 */ + "", /* 21 */ + "IM_TYPE_sRGB", /* 22 */ + "IM_TYPE_YXY", /* 23 */ + "IM_TYPE_FOURIER", /* 24 */ + "IM_TYPE_RGB16", /* 25 */ + "IM_TYPE_GREY16" /* 26 */ +}; + +static const char *im_BandFmt[] = { + "IM_BANDFMT_UCHAR", + "IM_BANDFMT_CHAR", + "IM_BANDFMT_USHORT", + "IM_BANDFMT_SHORT", + "IM_BANDFMT_UINT", + "IM_BANDFMT_INT", + "IM_BANDFMT_FLOAT", + "IM_BANDFMT_COMPLEX", + "IM_BANDFMT_DOUBLE", + "IM_BANDFMT_DPCOMPLEX" +}; + +static const char *im_Coding[] = { + "IM_CODING_NONE", + "COLQUANT8", + "IM_CODING_LABQ", + "IM_CODING_LABQ_COMPRESSED" +}; + +static const char *im_Compression[] = { + "NO_COMPRESSION", + "TCSF_COMPRESSION", + "JPEG_COMPRESSION" +}; + +static const char *im_dtype[] = { + "IM_NONE", + "IM_SETBUF", + "IM_SETBUF_FOREIGN", + "IM_OPENIN", + "IM_MMAPIN", + "IM_MMAPINRW", + "IM_OPENOUT", + "IM_PARTIAL" +}; + +static const char *im_dhint[] = { + "IM_SMALLTILE", + "IM_FATSTRIP", + "IM_THINSTRIP", + "IM_ANY" +}; + +/* Stuff to decode an enum. + */ +typedef struct _EnumTable { + const char *error; /* eg. "" */ + const char **names; /* eg. {"IM_CODING_NONE",..} */ + int nnames; +} EnumTable; + +static EnumTable enumType = { + N_( "" ), + im_Type, + IM_NUMBER( im_Type ) +}; + +static EnumTable enumBandFmt = { + N_( "" ), + im_BandFmt, + IM_NUMBER( im_BandFmt ) +}; + +static EnumTable enumCoding = { + N_( "" ), + im_Coding, + IM_NUMBER( im_Coding ) +}; + +static EnumTable enumCompression = { + N_( "" ), + im_Compression, + IM_NUMBER( im_Compression ) +}; + +static EnumTable enumdtype = { + N_( "" ), + im_dtype, + IM_NUMBER( im_dtype ) +}; + +static EnumTable enumdhint = { + N_( "" ), + im_dhint, + IM_NUMBER( im_dhint ) +}; + +static const char * +enum2char( EnumTable *etable, int n ) +{ + if( n < 0 || n > etable->nnames ) + return( _( etable->error ) ); + else + return( etable->names[n] ); +} + +static int +char2enum( EnumTable *etable, const char *name ) +{ + int i; + + for( i = 0; i < etable->nnames; i++ ) + if( g_ascii_strcasecmp( etable->names[i], name ) == 0 ) + return( i ); + + im_error( "char2enum", _( etable->error ) ); + + return( -1 ); +} + + +/* Prettyprint various header fields. + */ +const char *im_Type2char( int n ) + { return( enum2char( &enumType, n ) ); } +const char *im_BandFmt2char( int n ) + { return( enum2char( &enumBandFmt, n ) ); } +const char *im_Coding2char( int n ) + { return( enum2char( &enumCoding, n ) ); } +const char *im_Compression2char( int n ) + { return( enum2char( &enumCompression, n ) ); } +const char *im_dtype2char( im_desc_type n ) + { return( enum2char( &enumdtype, n ) ); } +const char *im_dhint2char( im_demand_type n ) + { return( enum2char( &enumdhint, n ) ); } + +int im_char2Type( const char *str ) + { return( char2enum( &enumType, str ) ); } +int im_char2BandFmt( const char *str ) + { return( char2enum( &enumBandFmt, str ) ); } +int im_char2Coding( const char *str ) + { return( char2enum( &enumCoding, str ) ); } +int im_char2Compression( const char *str ) + { return( char2enum( &enumCompression, str ) ); } +im_desc_type im_char2dtype( const char *str ) + { return( char2enum( &enumdtype, str ) ); } +im_demand_type im_char2dhint( const char *str ) + { return( char2enum( &enumdhint, str ) ); } + +static void * +print_field_fn( IMAGE *im, const char *field, GValue *value ) +{ + const char *extra; + char *str_value; + + str_value = g_strdup_value_contents( value ); + printf( "%s: %s", field, str_value ); + g_free( str_value ); + + /* Replace NULL static strings with "(null)". + */ +#define NN( X ) ((X) ? (X) : "(null)") + + /* Look for known enums and decode them. + */ + extra = NULL; + if( strcmp( field, "Coding" ) == 0 ) + extra = NN( im_Coding2char( g_value_get_int( value ) ) ); + else if( strcmp( field, "BandFmt" ) == 0 ) + extra = NN( im_BandFmt2char( g_value_get_int( value ) ) ); + else if( strcmp( field, "Type" ) == 0 ) + extra = NN( im_Type2char( g_value_get_int( value ) ) ); + else if( strcmp( field, "Compression" ) == 0 ) + extra = NN( im_Compression2char( g_value_get_int( value ) ) ); + + if( extra ) + printf( " - %s", extra ); + + printf( "\n" ); + + return( NULL ); +} + +static void * +print_region( REGION *reg, void *a, void *b ) +{ + printf( "Region defined for area at %dx%d size %dx%d\n", + reg->valid.left, reg->valid.top, + reg->valid.width, reg->valid.height ); + if( reg->seq ) + printf( "sequence in progress on region\n" ); + if( reg->buffer ) + printf( "local memory allocated\n" ); + + return( NULL ); +} + +void +im_printdesc( IMAGE *image ) +{ + if( !image ) { + printf( "NULL descriptor\n" ); + return; + } + + printf( "IMAGE* %p\n", image ); + + if( im_isMSBfirst( image ) ) + printf( "SPARC (MSB first) " ); + else + printf( "Intel (LSB first) " ); + printf( "byte order image, on a " ); + if( im_amiMSBfirst() ) + printf( "SPARC (MSB first) " ); + else + printf( "Intel (LSB first) " ); + printf( "byte order machine\n" ); + + (void) im_header_map( image, (im_header_map_fn) print_field_fn, NULL ); + + printf( "Hist: %s", im_history_get( image ) ); + + /* Print other (non-header) fields. + */ + if( image->generate ) + printf( "user generate function attached\n" ); + if( image->closefns ) + printf( "user close callbacks attached\n" ); + if( image->evalfns ) + printf( "user eval callbacks attached\n" ); + if( image->evalendfns ) + printf( "user evalend callbacks attached\n" ); + if( image->regions ) { + printf( "%d regions present\n", + g_slist_length( image->regions ) ); + im_slist_map2( image->regions, + (VSListMap2Fn) print_region, NULL, NULL ); + } + if( image->kill ) + printf( "kill flag set\n" ); + if( image->closing ) + printf( "closing flag set\n" ); + if( image->close_pending ) + printf( "close_pending flag set\n" ); + +#ifdef DEBUG + /* Can't get these with im_header_get(), so only show for debugging. + */ + printf( "dhint: %s\n", im_dhint2char( image->dhint ) ); + printf( "dtype: %s\n", im_dtype2char( image->dtype ) ); +#endif /*DEBUG*/ +} diff --git a/libsrc/iofuncs/im_printlines.c b/libsrc/iofuncs/im_printlines.c new file mode 100644 index 00000000..4f2c5caf --- /dev/null +++ b/libsrc/iofuncs/im_printlines.c @@ -0,0 +1,150 @@ +/* @(#) Function which prints in stdout the values of a picture + * @(#) + * @(#) For debuging only + * @(#) is either memory mapped or in a buffer. + * @(#) + * @(#) void im_printlines( in ) + * @(#) IMAGE *in; + * @(#) + * + * Copyright: 1991 N. Dessipris + * + * Author: N. Dessipris + * Written on: 18/03/1991 + * Modified on: + * 15/4/93 J.Cupitt + * - returns int, not void now, so error messages work + * - detects im->data invalid. + * 23/7/93 JC + * - im_incheck() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Useful: Call a macro with the name, type pairs for all VIPS functions. + */ +#define im_for_all_types() \ + case IM_BANDFMT_UCHAR: loopuc(unsigned char); break; \ + case IM_BANDFMT_CHAR: loop(char); break; \ + case IM_BANDFMT_USHORT: loop(unsigned short); break; \ + case IM_BANDFMT_SHORT: loop(short); break; \ + case IM_BANDFMT_UINT: loop(unsigned int); break; \ + case IM_BANDFMT_INT: loop(int); break; \ + case IM_BANDFMT_FLOAT: loop(float); break; \ + case IM_BANDFMT_DOUBLE: loop(double); break; \ + case IM_BANDFMT_COMPLEX: loopcmplx(float); break; \ + case IM_BANDFMT_DPCOMPLEX: loopcmplx(double); break; + +int +im_printlines( IMAGE *in ) +{ + if( im_incheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_printlines: input must be uncoded" ); + return( -1 ); + } + if( !in->data ) { + im_errormsg( "im_debugim: unsuitable image type" ); + return( -1 ); + } + +/* What type? First define the loop we want to perform for all types. */ +#define loopuc(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y, z; \ + \ + for ( y=0; yYsize; y++ ) {\ + fprintf(stderr, "line:%5d\n", y); \ + for ( x=0; xXsize; x++ ) {\ + fprintf(stderr, "%5d", x); \ + for ( z=0; zBands; z++ ) {\ + fprintf(stderr, "\t%4d", (TYPE)*p++ );\ + } \ + fprintf(stderr, "\n"); \ + } \ + } \ + } + +#define loop(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y, z; \ + \ + for ( y=0; yYsize; y++ ) {\ + fprintf(stderr, "line:%5d\n", y); \ + for ( x=0; xXsize; x++ ) {\ + fprintf(stderr, "%5d", x); \ + for ( z=0; zBands; z++ ) {\ + fprintf(stderr, "\t%f", (double)*p++ );\ + } \ + fprintf(stderr, "\n"); \ + } \ + } \ + } + +#define loopcmplx(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y, z; \ + \ + for ( y=0; yYsize; y++ ) {\ + fprintf(stderr, "line:%5d\n", y); \ + for ( x=0; xXsize; x++ ) {\ + fprintf(stderr, "%5d", x); \ + for ( z=0; zBands; z++ ) {\ + fprintf(stderr,"\t%f",(double)*p++);\ + fprintf(stderr,"\t%f",(double)*p++);\ + } \ + fprintf(stderr, "\n");\ + } \ + } \ + } + +/* Now generate code for all types. */ + switch( in->BandFmt ) { + im_for_all_types(); + default: { + im_errormsg( "im_printlines: unknown input format" ); + return( -1 ); + } + } + + return( 0 ); + +} diff --git a/libsrc/iofuncs/im_readhist.c b/libsrc/iofuncs/im_readhist.c new file mode 100644 index 00000000..4e29c985 --- /dev/null +++ b/libsrc/iofuncs/im_readhist.c @@ -0,0 +1,753 @@ +/* @(#) Reads one line of description and the history of the filename + * @(#) This is done by replacing the ending .v of the filename with .desc + * @(#) and trying to read the new file. If the file ending in .desc + * @(#) does exist it is read and put into the Hist pointer of the + * @(#) image descriptor + * @(#) If the .desc file does not exist or if the input file is not + * @(#) ending with .v the Hist pointer in initialised to "filename\n" + * @(#) and history is kept from the current processing stage. + * @(#) + * @(#) int im_readhist(image) + * @(#) IMAGE *image; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * Copyright: Nicos Dessipris + * Written on: 15/01/1990 + * Modified on : + * 28/10/92 JC + * - no more wild freeing! + * - behaves itself, thank you + * 13/1/94 JC + * - array-bounds write found and fixed + * 26/10/98 JC + * - binary open for stupid systems + * 24/9/01 JC + * - slight clean up + * 6/8/02 JC + * - another cleanup + * 11/7/05 + * - now read XML from after the image data rather than a separate + * annoying file + * - added im__writehist() to write XML to the image + * 5/10/05 + * - added wrappers for seek/truncate with native win32 calls for long + * file support + * 3/1/07 + * - set history_list instead + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_FILE_H +#include +#endif /*HAVE_SYS_FILE_H*/ +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#ifdef HAVE_IO_H +#include +#endif /*HAVE_IO_H*/ +#include +#include + +#ifdef OS_WIN32 +#include +#endif /*OS_WIN32*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our XML namespace. + */ +#define NAMESPACE "http://www.vips.ecs.soton.ac.uk/vips" + +/* Need our own seek(), since lseek() on win32 can't do long files. + */ +static int +im__seek( int fd, gint64 pos ) +{ +#ifdef OS_WIN32 +{ + HANDLE hFile = (HANDLE) _get_osfhandle( fd ); + LARGE_INTEGER p; + + p.QuadPart = pos; + if( !SetFilePointerEx( hFile, p, NULL, FILE_BEGIN ) ) { + im_error_system( GetLastError(), "im__seek", + _( "unable to seek" ) ); + return( -1 ); + } +} +#else /*!OS_WIN32*/ + if( lseek( fd, pos, SEEK_SET ) == (off_t) -1 ) { + im_error( "im__seek", _( "unable to seek" ) ); + return( -1 ); + } +#endif /*OS_WIN32*/ + + return( 0 ); +} + +/* Need our own ftruncate(), since ftruncate() on win32 can't do long files. + + DANGER ... this moves the file pointer to the end of file on win32, + but not on *nix; don't make any assumptions about the file pointer + position after calling this + + */ +static int +im__ftruncate( int fd, gint64 pos ) +{ +#ifdef OS_WIN32 +{ + HANDLE hFile = (HANDLE) _get_osfhandle( fd ); + LARGE_INTEGER p; + + p.QuadPart = pos; + if( im__seek( fd, pos ) ) + return( -1 ); + if( !SetEndOfFile( hFile ) ) { + im_error_system( GetLastError(), "im__ftruncate", + _( "unable to truncate" ) ); + return( -1 ); + } +} +#else /*!OS_WIN32*/ + if( ftruncate( fd, pos ) ) { + im_error_system( errno, "im__ftruncate", + _( "unable to truncate" ) ); + return( -1 ); + } +#endif /*OS_WIN32*/ + + return( 0 ); +} + +/* Read a chunk of an fd into memory. Add a '\0' at the end. + */ +static char * +read_chunk( int fd, gint64 offset, size_t length ) +{ + char *buf; + + if( im__seek( fd, offset ) ) + return( NULL ); + if( !(buf = im_malloc( NULL, length + 1 )) ) + return( NULL ); + if( read( fd, buf, length ) != length ) { + im_free( buf ); + im_error( "im_readhist", _( "unable to read history" ) ); + return( NULL ); + } + buf[length] = '\0'; + + return( buf ); +} + +/* Does it look like an image has an extension block? + */ +int +im__has_extension_block( IMAGE *im ) +{ + gint64 length; + gint64 psize; + + psize = im__image_pixel_length( im ); + if( (length = im_file_length( im->fd )) == -1 ) + return( 0 ); + + return( length - psize > 0 ); +} + +/* Read everything after the pixels into memory. + */ +void * +im__read_extension_block( IMAGE *im, int *size ) +{ + gint64 length; + gint64 psize; + void *buf; + + psize = im__image_pixel_length( im ); + if( (length = im_file_length( im->fd )) == -1 ) + return( NULL ); + if( length - psize > 10 * 1024 * 1024 ) { + im_error( "im_readhist", + _( "more than a 10 megabytes of XML? " + "sufferin' succotash!" ) ); + return( NULL ); + } + if( length - psize == 0 ) + return( NULL ); + if( !(buf = read_chunk( im->fd, psize, length - psize )) ) + return( NULL ); + if( size ) + *size = length - psize; + +#ifdef DEBUG + printf( "im__read_extension_block: read %d bytes from %s\n", + (int) (length - psize), im->filename ); + printf( "data: \"%s\"\n", (char *) buf ); +#endif /*DEBUG*/ + + return( buf ); +} + +/* Read everything after the pixels into memory. + + FIXME ... why can't we use xmlParserInputBufferCreateFd and parse + directly from the fd rather than having to read the stupid thing into + memory + + the libxml API docs are impossible to decipher + + */ +static xmlDoc * +read_xml( IMAGE *im ) +{ + void *buf; + int size; + xmlDoc *doc; + xmlNode *node; + + if( !(buf = im__read_extension_block( im, &size )) ) + return( NULL ); + if( !(doc = xmlParseMemory( buf, size )) ) { + im_free( buf ); + return( NULL ); + } + im_free( buf ); + if( !(node = xmlDocGetRootElement( doc )) || + !node->nsDef || + !im_isprefix( NAMESPACE, (char *) node->nsDef->href ) ) { + im_error( "im__readhist", _( "incorrect namespace in XML" ) ); + xmlFreeDoc( doc ); + return( NULL ); + } + +#ifdef DEBUG + printf( "read_xml: namespace == %s\n", node->nsDef->href ); +#endif /*DEBUG*/ + + return( doc ); +} + +/* Find the first child node with a name. + */ +static xmlNode * +get_node( xmlNode *base, const char *name ) +{ + xmlNode *i; + + for( i = base->children; i; i = i->next ) + if( strcmp( (char *) i->name, name ) == 0 ) + return( i ); + + return( NULL ); +} + +/* Read a string property to a buffer. TRUE for success. + */ +static int +get_sprop( xmlNode *xnode, const char *name, char *buf, int sz ) +{ + char *value = (char *) xmlGetProp( xnode, (xmlChar *) name ); + + if( !value ) + return( 0 ); + + im_strncpy( buf, value, sz ); + IM_FREEF( xmlFree, value ); + + return( 1 ); +} + +/* Chop history into lines, add each one as a refstring. + */ +static void +set_history( IMAGE *im, char *history ) +{ + GSList *history_list; + char *p, *q; + + /* There can be history there already if we're rewinding. + */ + IM_FREEF( im__gslist_gvalue_free, im->history_list ); + + history_list = NULL; + + for( p = history; *p; p = q ) { + if( (q = strchr( p, '\n' )) ) + *q = '\0'; + else + q = p + strlen( p ); + + history_list = g_slist_prepend( history_list, + im__gvalue_ref_string_new( p ) ); + } + + im->history_list = g_slist_reverse( history_list ); +} + +/* Load header fields. + */ +static int +rebuild_header_builtin( IMAGE *im, xmlNode *i ) +{ + char name[256]; + + if( get_sprop( i, "name", name, 256 ) ) { + if( strcmp( name, "Hist" ) == 0 ) { + char *history; + + /* Have to take (another) copy, since we need to free + * with xmlFree(). + */ + history = (char *) xmlNodeGetContent( i ); + set_history( im, history ); + xmlFree( history ); + } + } + + return( 0 ); +} + +/* Load meta fields. + */ +static int +rebuild_header_meta( IMAGE *im, xmlNode *i ) +{ + char name[256]; + char type[256]; + + if( get_sprop( i, "name", name, 256 ) && + get_sprop( i, "type", type, 256 ) ) { + GType gtype = g_type_from_name( type ); + + /* Can we convert from IM_SAVE_STRING to type? + */ + if( gtype && + g_value_type_transformable( + IM_TYPE_SAVE_STRING, gtype ) ) { + char *content; + GValue save_value = { 0 }; + GValue value = { 0 }; + + content = (char *) xmlNodeGetContent( i ); + g_value_init( &save_value, IM_TYPE_SAVE_STRING ); + im_save_string_set( &save_value, content ); + xmlFree( content ); + + g_value_init( &value, gtype ); + if( !g_value_transform( &save_value, &value ) ) { + g_value_unset( &save_value ); + im_error( "im__readhist", _( "error " + "transforming from save format" ) ); + return( -1 ); + } + if( im_meta_set( im, name, &value ) ) { + g_value_unset( &save_value ); + g_value_unset( &value ); + return( -1 ); + } + g_value_unset( &save_value ); + g_value_unset( &value ); + } + } + + return( 0 ); +} + +static xmlDoc * +get_xml( IMAGE *im ) +{ + if( im_header_get_type( im, IM_META_XML ) ) { + xmlDoc *doc; + + if( im_meta_get_area( im, IM_META_XML, (void *) &doc ) ) + return( NULL ); + + return( doc ); + } + + return( NULL ); +} + +/* Rebuild header fields that depend on stuff saved in xml. + */ +static int +rebuild_header( IMAGE *im ) +{ + xmlDoc *doc; + + if( (doc = get_xml( im )) ) { + xmlNode *root; + xmlNode *block; + + if( !(root = xmlDocGetRootElement( doc )) ) + return( -1 ); + if( (block = get_node( root, "header" )) ) { + xmlNode *i; + + for( i = block->children; i; i = i->next ) + if( strcmp( (char *) i->name, "field" ) == 0 ) + if( rebuild_header_builtin( im, i ) ) + return( -1 ); + } + if( (block = get_node( root, "meta" )) ) { + xmlNode *i; + + for( i = block->children; i; i = i->next ) + if( strcmp( (char *) i->name, "field" ) == 0 ) + if( rebuild_header_meta( im, i ) ) + return( -1 ); + } + } + + return( 0 ); +} + +/* Called at the end of im__read_header ... get any XML after the pixel data + * and read it in. + */ +int +im__readhist( IMAGE *im ) +{ + /* Junk any old xml meta. + */ + if( im_header_get_type( im, IM_META_XML ) ) + im_meta_set_area( im, IM_META_XML, NULL, NULL ); + + if( im__has_extension_block( im ) ) { + xmlDoc *doc; + + if( !(doc = read_xml( im )) ) + return( -1 ); + if( im_meta_set_area( im, IM_META_XML, + (im_callback_fn) xmlFreeDoc, doc ) ) { + xmlFreeDoc( doc ); + return( -1 ); + } + } + + if( rebuild_header( im ) ) + return( -1 ); + + return( 0 ); +} + +#define MAX_STRSIZE (32768) /* Max size of text for stack strings */ + +static int +set_prop( xmlNode *node, const char *name, const char *fmt, ... ) +{ + va_list ap; + char value[MAX_STRSIZE]; + + va_start( ap, fmt ); + (void) im_vsnprintf( value, MAX_STRSIZE, fmt, ap ); + va_end( ap ); + + if( !xmlSetProp( node, (xmlChar *) name, (xmlChar *) value ) ) { + im_error( "im_writehist", _( "unable to set property \"%s\" " + "to value \"%s\"." ), + name, value ); + return( -1 ); + } + + return( 0 ); +} + +static int +set_sprop( xmlNode *node, const char *name, const char *value ) +{ + if( value && set_prop( node, name, "%s", value ) ) + return( -1 ); + + return( 0 ); +} + +static int +set_field( xmlNode *node, + const char *name, const char *type, const char *content ) +{ + xmlNode *field; + + if( !(field = xmlNewChild( node, NULL, (xmlChar *) "field", NULL )) || + set_sprop( field, "type", type ) || + set_sprop( field, "name", name ) ) + return( -1 ); + xmlNodeSetContent( field, (xmlChar *) content ); + + return( 0 ); +} + +static void * +save_fields_meta( Meta *meta, xmlNode *node ) +{ + GType type = G_VALUE_TYPE( &meta->value ); + + /* If we can transform to IM_TYPE_SAVE_STRING and back, we can save and + * restore. + */ + if( g_value_type_transformable( type, IM_TYPE_SAVE_STRING ) && + g_value_type_transformable( IM_TYPE_SAVE_STRING, type ) ) { + GValue save_value = { 0 }; + + g_value_init( &save_value, IM_TYPE_SAVE_STRING ); + if( !g_value_transform( &meta->value, &save_value ) ) { + im_error( "im__writehist", + _( "error transforming to save format" ) ); + return( node ); + } + if( set_field( node, meta->field, g_type_name( type ), + im_save_string_get( &save_value ) ) ) { + g_value_unset( &save_value ); + return( node ); + } + g_value_unset( &save_value ); + } + + return( NULL ); +} + +static int +save_fields( IMAGE *im, xmlNode *node ) +{ + xmlNode *this; + + /* Save header fields. + */ + if( !(this = xmlNewChild( node, NULL, (xmlChar *) "header", NULL )) ) + return( -1 ); + if( set_field( this, "Hist", + g_type_name( IM_TYPE_REF_STRING ), im_history_get( im ) ) ) + return( -1 ); + + if( !(this = xmlNewChild( node, NULL, (xmlChar *) "meta", NULL )) ) + return( -1 ); + if( im->Meta_traverse && + im_slist_map2( im->Meta_traverse, + (VSListMap2Fn) save_fields_meta, this, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im__write_extension_block( IMAGE *im, void *buf, int size ) +{ + gint64 length; + gint64 psize; + + psize = im__image_pixel_length( im ); + if( (length = im_file_length( im->fd )) == -1 ) + return( -1 ); + if( length - psize < 0 ) { + im_error( "im__write_extension_block", + _( "file has been truncated" ) ); + return( -1 ); + } + + if( im__ftruncate( im->fd, psize ) || + im__seek( im->fd, psize ) ) + return( -1 ); + if( im__write( im->fd, buf, size ) ) + return( -1 ); + +#ifdef DEBUG + printf( "im__write_extension_block: written %d bytes of XML to %s\n", + size, im->filename ); +#endif /*DEBUG*/ + + return( 0 ); +} + +#ifdef DEBUG +/* Return a string of n characters. Buffer is zapped each time! + */ +const char * +rpt( char ch, int n ) +{ + int i; + static char buf[200]; + + n = IM_MIN( 190, n ); + + for( i = 0; i < n; i++ ) + buf[i] = ch; + buf[i] = '\0'; + + return( buf ); +} + +/* Return a string of n spaces. Buffer is zapped each time! + */ +const char * +spc( int n ) +{ + return( rpt( ' ', n ) ); +} + +static void +prettify_tree_sub( xmlNode *xnode, int indent ) +{ + xmlNode *txt; + xmlNode *next; + + for(;;) { + next = xnode->next; + + /* According to memprof, this leaks :-( If you cut it out into + * a separate prog though, it's OK + + FIXME ... how odd + + */ + txt = xmlNewText( "\n" ); + xmlAddPrevSibling( xnode, txt ); + txt = xmlNewText( spc( indent ) ); + xmlAddPrevSibling( xnode, txt ); + + if( xnode->children ) + prettify_tree_sub( xnode->children, indent + 2 ); + + if( !next ) + break; + + xnode = next; + } + + txt = xmlNewText( spc( indent - 2 ) ); + xmlAddNextSibling( xnode, txt ); + txt = xmlNewText( "\n" ); + xmlAddNextSibling( xnode, txt ); +} + +/* Walk an XML document, adding extra blank text elements so that it's easier + * to read. Don't call me twice! + */ +void +prettify_tree( xmlDoc *xdoc ) +{ + xmlNode *xnode = xmlDocGetRootElement( xdoc ); + + prettify_tree_sub( xnode, 0 ); +} +#endif /*DEBUG*/ + +/* Append XML to output fd. + */ +int +im__writehist( IMAGE *im ) +{ + xmlDoc *doc; + char namespace[256]; + char *dump; + int dump_size; + + assert( im->dtype == IM_OPENOUT ); + assert( im->fd != -1 ); + + if( !(doc = xmlNewDoc( (xmlChar *) "1.0" )) ) + return( -1 ); + + im_snprintf( namespace, 256, "%s/%d.%d.%d", + NAMESPACE, + IM_MAJOR_VERSION, IM_MINOR_VERSION, IM_MICRO_VERSION ); + if( !(doc->children = xmlNewDocNode( doc, + NULL, (xmlChar *) "root", NULL )) || + set_sprop( doc->children, "xmlns", namespace ) || + save_fields( im, doc->children ) ) { + im_error( "im__writehist", _( "xml save error" ) ); + xmlFreeDoc( doc ); + return( -1 ); + } + + /* Bizarre double-cast stops a bogus gcc 4.1 compiler warning. + */ + xmlDocDumpMemory( doc, (xmlChar **) ((char *) &dump), &dump_size ); + if( !dump ) { + im_error( "im__writehist", _( "xml save error" ) ); + xmlFreeDoc( doc ); + return( -1 ); + } + + if( im__write_extension_block( im, dump, dump_size ) ) { + xmlFreeDoc( doc ); + xmlFree( dump ); + return( -1 ); + } + +#ifdef DEBUG +{ + char *dump2; + int dump_size2; + + /* Uncomment to have XML pretty-printed. Can be annoying during + * debugging tho' + */ + prettify_tree( doc ); + + xmlDocDumpMemory( doc, (xmlChar **) &dump2, &dump_size2 ); + if( !dump2 ) { + im_error( "im__writehist", _( "xml save error" ) ); + xmlFreeDoc( doc ); + xmlFree( dump ); + return( -1 ); + } + + printf( "im__writehist: saved XML is: \"%s\"", dump2 ); + xmlFree( dump2 ); +} +#endif /*DEBUG*/ + + xmlFreeDoc( doc ); + xmlFree( dump ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_render.c b/libsrc/iofuncs/im_render.c new file mode 100644 index 00000000..a3d3bc1f --- /dev/null +++ b/libsrc/iofuncs/im_render.c @@ -0,0 +1,1181 @@ +/* render an image in the background as a set of tiles + * + * don't read from mask after closing out + * + * JC, 30 sep 03 + * + * 22/10/03 JC + * - now uses threadgroup kill system, avoiding race condition + * 2/2/04 JC + * - cache failed for large images + * 8/4/04 + * - touch reused tiles so they don't get reused again too soon ... helps + * stop thrashing when we've many pending paints and lots of threads + * 15/4/04 + * - added im_cache() convenience function + * 26/1/05 + * - added im_render_fade() ... fade tiles display for nip2 + * - mask can now be NULL for no mask output + * 11/2/05 + * - tidies + * 27/2/05 + * - limit the number of simultaneous renders + * - kill threadgroups when no dirties left + * - max == -1 means unlimited cache size + * - 'priority' marks non-suspendable renders + * 1/4/05 + * - rewritten for a few global threads instead, and a job queue ... + * should be simpler & more reliable + * 23/4/07 + * - oop, race condition fixed + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on debugging output. +#define DEBUG +#define DEBUG_REUSE +#define DEBUG_TG +#define DEBUG_MAKE +#define DEBUG_PAINT + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ + +/* Need Sleep() on win32. + */ +#ifdef OS_WIN32 +#include +#endif /*OS_WIN32*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef HAVE_THREADS +static const int have_threads = 1; +#else /*!HAVE_THREADS*/ +static const int have_threads = 0; +#endif /*HAVE_THREADS*/ + +/* A manager thread. We have a fixed number of these taking jobs off the list + * of current renders with dirty tiles, doing a tile, and putting the render + * back. + */ +typedef struct _RenderThread { + GThread *gthread; + struct _Render *render; /* The last render we worked on */ +} RenderThread; + +/* Notify caller through this. + */ +typedef void (*notify_fn)( IMAGE *, Rect *, void * ); + +/* The states a tile can be in. + */ +typedef enum { + TILE_DIRTY, /* On the dirty list .. contains no pixels */ + TILE_WORKING, /* Currently being worked on */ + TILE_PAINTED_FADING, /* Painted, on fade list */ + TILE_PAINTED /* Painted, ready for reuse */ +} TileState; + +/* A tile in our cache. + */ +typedef struct { + struct _Render *render; + + Rect area; /* Place here (unclipped) */ + REGION *region; /* REGION with the pixels */ + + int access_ticks; /* Time of last use for LRU flush */ + int time; /* Time when we finished painting */ + + TileState state; +} Tile; + +/* + + FIXME ... should have an LRU queue rather than this thing with times + + FIXME ... could also hash from tile xy to tile pointer + + */ + +/* Per-call state. + */ +typedef struct _Render { + /* Reference count this, since we use these things from several + * threads. Can't easily use the gobject ref count system, since we + * need a lock around operations. + */ + int ref_count; + GMutex *ref_count_lock; + + /* Parameters. + */ + IMAGE *in; /* Image we render */ + IMAGE *out; /* Write tiles here on demand */ + IMAGE *mask; /* Set valid pixels here */ + int width, height; /* Tile size */ + int max; /* Maximum number of tiles */ + int fps; /* FPS for fade in */ + int steps; /* Steps fade occurs in */ + int priority; /* Larger numbers done sooner */ + notify_fn notify; /* Tell caller about paints here */ + void *client; + + /* Make readers single thread with this. No point allowing + * multi-thread read. + */ + GMutex *read_lock; + + /* Tile cache. + */ + GSList *cache; /* List of all our tiles */ + int ntiles; /* Number of cache tiles */ + int access_ticks; /* Inc. on each access ... used for LRU */ + + /* List of tiles which are to be painted. + */ + GMutex *dirty_lock; /* Lock before we read/write the dirty list */ + GSList *dirty; /* Tiles which need painting */ + + /* List of painted tiles being faded in. Plus a fade-in thread. + */ + GMutex *fade_lock; /* Lock before we read/write the fade list */ + GSList *fade; /* Tiles which are being faded */ + GThread *fade_gthread; /* Fade tiles in thread */ + int time; /* Increment each frame */ + int fade_kill; /* Set to ask fade thread to exit */ + + /* Render thread stuff. + */ + im_threadgroup_t *tg; /* Render with this threadgroup */ + int render_kill; /* This render is dying */ +} Render; + +/* Number of RenderThread we create. + */ +static const int render_thread_max = 1; + +static GSList *render_thread_all = NULL; + +/* Number of renders with dirty tiles. RenderThreads queue up on this. + */ +static im_semaphore_t render_dirty_sem; + +/* All the renders with dirty tiles. + */ +static GMutex *render_dirty_lock = NULL; +static GSList *render_dirty_all = NULL; + +static void +render_dirty_remove( Render *render ) +{ + g_mutex_lock( render_dirty_lock ); + + if( g_slist_find( render_dirty_all, render ) ) { + render_dirty_all = g_slist_remove( render_dirty_all, render ); + + /* We know this can't block, since there is at least 1 item in + * the render_dirty_all list (us). Except that + * render_dirty_get() does a _down() before it locks, so this + * could block if we run inbetween :-( possible deadlock. + */ + im_semaphore_down( &render_dirty_sem ); + } + + g_mutex_unlock( render_dirty_lock ); +} + +static void * +tile_free( Tile *tile ) +{ +#ifdef DEBUG_MAKE + printf( "tile_free\n" ); +#endif /*DEBUG_MAKE*/ + + if( tile->region ) { + (void) im_region_free( tile->region ); + tile->region = NULL; + } + + im_free( tile ); + + return( NULL ); +} + +static int +render_free( Render *render ) +{ +#ifdef DEBUG_MAKE + printf( "render_free: %p\n", render ); +#endif /*DEBUG_MAKE*/ + + assert( render->ref_count == 0 ); + + render_dirty_remove( render ); + + /* Kill fade thread. + */ + if( render->fade_gthread ) { + render->fade_kill = 1; + (void) g_thread_join( render->fade_gthread ); + render->fade_gthread = NULL; + render->fade_kill = 0; + } + + IM_FREEF( im_threadgroup_free, render->tg ); + + /* Free cache. + */ + im_slist_map2( render->cache, + (VSListMap2Fn) tile_free, NULL, NULL ); + IM_FREEF( g_slist_free, render->cache ); + render->ntiles = 0; + IM_FREEF( g_slist_free, render->dirty ); + IM_FREEF( g_slist_free, render->fade ); + + g_mutex_free( render->ref_count_lock ); + g_mutex_free( render->dirty_lock ); + g_mutex_free( render->read_lock ); + g_mutex_free( render->fade_lock ); + + im_free( render ); + + return( 0 ); +} + +/* Ref and unref a Render ... free on last unref. + */ +static int +render_ref( Render *render ) +{ + g_mutex_lock( render->ref_count_lock ); + assert( render->ref_count != 0 ); + render->ref_count += 1; + g_mutex_unlock( render->ref_count_lock ); + + return( 0 ); +} + +static int +render_unref( Render *render ) +{ + int kill; + + g_mutex_lock( render->ref_count_lock ); + assert( render->ref_count > 0 ); + render->ref_count -= 1; + kill = render->ref_count == 0; + g_mutex_unlock( render->ref_count_lock ); + + if( kill ) + render_free( render ); + + return( 0 ); +} + +/* Wait for a render with dirty tiles. + */ +static Render * +render_dirty_get( void ) +{ + Render *render; + + /* Wait for a render with dirty tiles. + */ + im_semaphore_down( &render_dirty_sem ); + + g_mutex_lock( render_dirty_lock ); + + /* Just take the head of the jobs list ... we sort when we add. If + * render_dirty_remove() is called between our semaphore letting us in + * and the _lock(), render_dirty_all can be NULL. + */ + render = NULL; + if( render_dirty_all ) { + render = (Render *) render_dirty_all->data; + + assert( render->ref_count == 1 ); + + /* Ref the render to make sure it can't die while we're + * working on it. + */ + render_ref( render ); + + render_dirty_all = g_slist_remove( render_dirty_all, render ); + } + + g_mutex_unlock( render_dirty_lock ); + + return( render ); +} + +/* Do a single tile. Take a dirty tile from the dirty list, fill with pixels, + * and add to the fade list. + */ +static void +render_dirty_process( Render *render ) +{ + Tile *tile; + + /* Take a tile off the dirty list. + */ + g_mutex_lock( render->dirty_lock ); + if( render->dirty ) { + tile = (Tile *) render->dirty->data; + assert( tile->state == TILE_DIRTY ); + render->dirty = g_slist_remove( render->dirty, tile ); + tile->state = TILE_WORKING; + } + else + tile = NULL; + g_mutex_unlock( render->dirty_lock ); + + if( tile ) { + int result; + + result = -1; + if( !render->tg ) { + render->tg = im_threadgroup_create( render->in ); + +#ifdef DEBUG_TG + printf( "render_paint_tile: " + "%p starting threadgroup\n", + render ); +#endif /*DEBUG_TG*/ + } + + if( render->tg ) { +#ifdef DEBUG_PAINT + printf( "render_fill_tile: " + "%p paint of tile %dx%d\n", + render, + tile->area.left, tile->area.top ); +#endif /*DEBUG_PAINT*/ + + /* We're writing to the tile region, but we didn't + * make it. + */ + im__region_take_ownership( tile->region ); + + result = im_prepare_thread( render->tg, + tile->region, &tile->area ); + } + + /* + + FIXME ... nice if we did something with the error return + + */ +#ifdef DEBUG_PAINT + if( result ) + printf( "render_fill_tile: " + "im_prepare_thread() failed!\n\t%s\n", + im_error_buffer() ); +#endif /*DEBUG_PAINT*/ + + g_mutex_lock( render->fade_lock ); + + render->fade = g_slist_prepend( render->fade, tile ); + tile->time = render->time; + tile->state = TILE_PAINTED_FADING; + + /* Hand tile over to another thread. + */ + im__region_no_ownership( tile->region ); + + g_mutex_unlock( render->fade_lock ); + } +} + +static int +render_dirty_sort( Render *a, Render *b ) +{ + return( b->priority - a->priority ); +} + +/* Add to the jobs list, if it has work to be done. + */ +static void +render_dirty_put( Render *render ) +{ + g_mutex_lock( render_dirty_lock ); + + if( render->dirty && !render->render_kill ) { + if( !g_slist_find( render_dirty_all, render ) ) { + render_dirty_all = g_slist_prepend( render_dirty_all, + render ); + render_dirty_all = g_slist_sort( render_dirty_all, + (GCompareFunc) render_dirty_sort ); + im_semaphore_up( &render_dirty_sem ); + } + } + else { + /* Coming off the jobs list ... shut down the render pipeline. + */ +#ifdef DEBUG_TG + printf( "render_dirty_put: %p stopping threadgroup\n", render ); +#endif /*DEBUG_TG*/ + IM_FREEF( im_threadgroup_free, render->tg ); + } + + g_mutex_unlock( render_dirty_lock ); +} + +/* Main loop for RenderThreads. + */ +static void * +render_thread_main( void *client ) +{ + /* Could use this if we want per-thread state in the future. + RenderThread *thread = (RenderThread *) client; + */ + + for(;;) { + Render *render; + + if( (render = render_dirty_get()) ) { + render_dirty_process( render ); + render_dirty_put( render ); + + assert( render->ref_count == 1 || + render->ref_count == 2 ); + + /* _get() does a ref to make sure we keep the render + * alive during processing ... unref before we loop. + * This can kill off the render. + */ + render_unref( render ); + } + } +} + +/* Create our set of RenderThread. Assume we're single-threaded here. + */ +static int +render_thread_create( void ) +{ + int len = g_slist_length( render_thread_all ); + int i; + + if( !have_threads ) + return( 0 ); + + /* 1st time through only. + */ + if( !render_dirty_lock ) { + render_dirty_lock = g_mutex_new(); + im_semaphore_init( &render_dirty_sem, 0, "render_dirty_sem" ); + } + + for( i = len; i < render_thread_max; i++ ) { + RenderThread *thread = IM_NEW( NULL, RenderThread ); + + thread->gthread = NULL; + thread->render = NULL; + + if( !(thread->gthread = g_thread_create_full( + render_thread_main, thread, + IM__DEFAULT_STACK_SIZE, TRUE, FALSE, + G_THREAD_PRIORITY_NORMAL, NULL )) ) { + im_free( thread ); + im_error( "im_render", _( "unable to create thread" ) ); + return( -1 ); + } + + render_thread_all = g_slist_prepend( render_thread_all, + thread ); + } + + return( 0 ); +} + +#ifdef DEBUG +static char * +tile_state_name( TileState state ) +{ + switch( state ) { + case TILE_DIRTY: return( "TILE_DIRTY" ); + case TILE_WORKING: return( "TILE_WORKING" ); + case TILE_PAINTED_FADING: return( "TILE_PAINTED_FADING" ); + case TILE_PAINTED: return( "TILE_PAINTED" ); + + default: + assert( FALSE ); + } +} + +static void * +tile_print( Tile *tile ) +{ + printf( "tile: %dx%d, access_ticks = %d, time = %d,\n" + "\tstate = %s\n", + tile->area.left, tile->area.top, + tile->access_ticks, tile->time, + tile_state_name( tile->state ) ); + + return( NULL ); +} +#endif /*DEBUG*/ + +/* Come here for the fade work thread. + */ +static void * +render_fade( void *client ) +{ + Render *render = (Render *) client; + + for(;;) { + GSList *p; + GSList *next; + +#ifdef OS_WIN32 + Sleep( 1000 / render->fps ); +#else /*!OS_WIN32*/ + usleep( 1000000 / render->fps ); +#endif /*!OS_WIN32*/ + + if( render->fade_kill ) + break; + + /* Increment frame counter. Used to set bg/fg ratio for fade. + */ + render->time += 1; + + g_mutex_lock( render->fade_lock ); + for( p = render->fade; p; p = next ) { + Tile *tile = (Tile *) p->data; + + assert( tile->state == TILE_PAINTED_FADING ); + + /* We remove the current element as we scan :-( so get + * next beforehand. + */ + next = p->next; + + if( render->time - tile->time > render->steps ) { + /* Tile is solid ... comes off the fade list. + */ + render->fade = g_slist_remove( render->fade, + tile ); + tile->state = TILE_PAINTED; + } + + /* Ask client to update. + */ + if( render->notify ) + render->notify( render->out, + &tile->area, render->client ); + } + g_mutex_unlock( render->fade_lock ); + } + + return( NULL ); +} + +static Render * +render_new( IMAGE *in, IMAGE *out, IMAGE *mask, + int width, int height, int max, + int fps, int steps, + int priority, + notify_fn notify, void *client ) +{ + Render *render; + + /* Don't use auto-free for render, we do our own lifetime management + * with _ref() and _unref(). + */ + if( !(render = IM_NEW( NULL, Render )) ) + return( NULL ); + + render->ref_count = 1; + render->ref_count_lock = g_mutex_new(); + + render->in = in; + render->out = out; + render->mask = mask; + render->width = width; + render->height = height; + render->max = max; + render->fps = fps; + render->steps = steps; + render->priority = priority; + render->notify = notify; + render->client = client; + + render->read_lock = g_mutex_new(); + + render->cache = NULL; + render->ntiles = 0; + render->access_ticks = 0; + + render->dirty_lock = g_mutex_new(); + render->dirty = NULL; + + render->fade_lock = g_mutex_new(); + render->fade = NULL; + render->fade_gthread = NULL; + render->time = 0; + render->fade_kill = 0; + + render->tg = NULL; + render->render_kill = FALSE; + + if( im_add_close_callback( out, + (im_callback_fn) render_unref, render, NULL ) ) { + (void) render_unref( render ); + return( NULL ); + } + + /* Only need the fade thread for fps != 0. + */ + if( have_threads && fps && + !(render->fade_gthread = g_thread_create_full( + render_fade, render, + IM__DEFAULT_STACK_SIZE, TRUE, FALSE, + G_THREAD_PRIORITY_NORMAL, NULL )) ) { + im_error( "im_render", _( "unable to create thread" ) ); + return( NULL ); + } + + return( render ); +} + +/* Make a Tile. + */ +static Tile * +tile_new( Render *render ) +{ + Tile *tile; + +#ifdef DEBUG_MAKE + printf( "tile_new\n" ); +#endif /*DEBUG_MAKE*/ + + /* Don't use auto-free: we need to make sure we free the tile after + * Render. + */ + if( !(tile = IM_NEW( NULL, Tile )) ) + return( NULL ); + + tile->render = render; + tile->region = NULL; + tile->area.left = 0; + tile->area.top = 0; + tile->area.width = 0; + tile->area.height = 0; + tile->access_ticks = render->access_ticks; + tile->time = 0; + tile->state = TILE_WORKING; + + if( !(tile->region = im_region_create( render->in )) ) { + (void) tile_free( tile ); + return( NULL ); + } + + render->cache = g_slist_prepend( render->cache, tile ); + render->ntiles += 1; + + return( tile ); +} + +static void * +tile_test_area( Tile *tile, Rect *area ) +{ + if( im_rect_equalsrect( &tile->area, area ) ) + return( tile ); + + return( NULL ); +} + +/* Search the cache for a tile, NULL if not there. Could be *much* faster. If + * we find a tile, bump to front. + */ +static Tile * +render_tile_lookup( Render *render, Rect *area ) +{ + Tile *tile; + + tile = (Tile *) im_slist_map2( render->cache, + (VSListMap2Fn) tile_test_area, area, NULL ); + + /* We've looked at a tile ... bump to end of LRU and front of dirty. + */ + if( tile ) { + tile->access_ticks = render->access_ticks; + render->access_ticks += 1; + + g_mutex_lock( render->dirty_lock ); + if( tile->state == TILE_DIRTY ) { +#ifdef DEBUG + printf( "tile_bump_dirty: bumping tile %dx%d\n", + tile->area.left, tile->area.top ); +#endif /*DEBUG*/ + + render->dirty = g_slist_remove( render->dirty, tile ); + render->dirty = g_slist_prepend( render->dirty, tile ); + } + g_mutex_unlock( render->dirty_lock ); + } + + return( tile ); +} + +/* Add a tile to the dirty list. + */ +static void +tile_set_dirty( Tile *tile, Rect *area ) +{ + Render *render = tile->render; + +#ifdef DEBUG_PAINT + printf( "tile_set_dirty: adding tile %dx%d to dirty\n", + area->left, area->top ); +#endif /*DEBUG_PAINT*/ + + assert( tile->state == TILE_WORKING ); + + /* Touch the ticks ... we want to make sure this tile will not be + * reused too soon, so it gets a chance to get painted. + */ + tile->access_ticks = render->access_ticks; + render->access_ticks += 1; + + g_mutex_lock( render->dirty_lock ); + + tile->state = TILE_DIRTY; + tile->area = *area; + render->dirty = g_slist_prepend( render->dirty, tile ); + + /* Someone else will write to it now. + */ + im__region_no_ownership( tile->region ); + + /* Can't unlock render->dirty_lock here, we need to render_dirty_put() + * before the tile is processed. + */ + + if( render->notify && have_threads ) + /* Add to the list of renders with dirty tiles. One of our bg + * threads will pick it up and paint it. + */ + render_dirty_put( render ); + else { + /* No threads, or no notify ... paint the tile ourselves, + * sychronously. No need to notify the client, since they'll + * never see black tiles. + */ +#ifdef DEBUG_PAINT + printf( "tile_set_dirty: painting tile %dx%d synchronously\n", + area->left, area->top ); +#endif /*DEBUG_PAINT*/ + + if( !render->tg ) + render->tg = im_threadgroup_create( render->in ); + + /* We're writing to the tile region, but we didn't make it. + */ + im__region_take_ownership( tile->region ); + + if( render->tg ) + im_prepare_thread( render->tg, + tile->region, &tile->area ); + + /* Can't fade in synchronous mode .. straight to painted. + */ + tile->state = TILE_PAINTED; + render->dirty = g_slist_remove( render->dirty, tile ); + } + + g_mutex_unlock( render->dirty_lock ); +} + +static void * +tile_test_clean_ticks( Tile *this, Tile **best ) +{ + if( this->state == TILE_PAINTED ) + if( !*best || this->access_ticks < (*best)->access_ticks ) + *best = this; + + return( NULL ); +} + +/* Pick a painted tile to reuse. Search for LRU (slow!). + */ +static Tile * +render_tile_get_painted( Render *render ) +{ + Tile *tile; + + tile = NULL; + im_slist_map2( render->cache, + (VSListMap2Fn) tile_test_clean_ticks, &tile, NULL ); + + if( tile ) { + assert( tile->state == TILE_PAINTED ); + +#ifdef DEBUG_REUSE + printf( "render_tile_get_painted: reusing painted %p\n", tile ); + + g_mutex_lock( render->dirty_lock ); + assert( !g_slist_find( render->dirty, tile ) ); + g_mutex_unlock( render->dirty_lock ); + + g_mutex_lock( render->fade_lock ); + assert( !g_slist_find( render->fade, tile ) ); + g_mutex_unlock( render->fade_lock ); +#endif /*DEBUG_REUSE*/ + + tile->state = TILE_WORKING; + } + + return( tile ); +} + +/* Take a tile off the end of the dirty list. + */ +static Tile * +render_tile_get_dirty( Render *render ) +{ + Tile *tile; + + g_mutex_lock( render->dirty_lock ); + if( !render->dirty ) + tile = NULL; + else { + tile = (Tile *) g_slist_last( render->dirty )->data; + render->dirty = g_slist_remove( render->dirty, tile ); + tile->state = TILE_WORKING; + } + g_mutex_unlock( render->dirty_lock ); + +#ifdef DEBUG_REUSE + if( tile ) + printf( "render_tile_get_dirty: reusing dirty %p\n", tile ); +#endif /*DEBUG_REUSE*/ + + return( tile ); +} + +static Tile * +render_tile_get( Render *render, Rect *area ) +{ + Tile *tile; + + /* Got this tile already? + */ + if( (tile = render_tile_lookup( render, area )) ) { +#ifdef DEBUG_PAINT + printf( "render_tile_get: found %dx%d in cache\n", + area->left, area->top ); +#endif /*DEBUG_PAINT*/ + + return( tile ); + } + + /* Have we fewer tiles than teh max? Can just make a new tile. + */ + if( render->ntiles < render->max || render->max == -1 ) { + if( !(tile = tile_new( render )) ) + return( NULL ); + } + else { + /* Need to reuse a tile. Try for an old painted tile first, + * then if that fails, reuse a dirty tile. Don't search the + * fading list, though we could maybe scavenge there. + */ + if( !(tile = render_tile_get_painted( render )) && + !(tile = render_tile_get_dirty( render )) ) + return( NULL ); + +#ifdef DEBUG_REUSE + printf( "(render_tile_get: was at %dx%d, moving to %dx%d)\n", + tile->area.left, tile->area.top, + area->left, area->top ); +#endif /*DEBUG_REUSE*/ + } + +#ifdef DEBUG_PAINT + printf( "render_tile_get: sending %dx%d for calc\n", + area->left, area->top ); +#endif /*DEBUG_PAINT*/ + + tile_set_dirty( tile, area ); + + return( tile ); +} + +/* Copy what we can from the tile into the region. + */ +static void +tile_copy( Tile *tile, REGION *to ) +{ + Rect ovlap; + int y; + int len; + + /* Find common pixels. + */ + im_rect_intersectrect( &tile->area, &to->valid, &ovlap ); + assert( !im_rect_isempty( &ovlap ) ); + len = IM_IMAGE_SIZEOF_PEL( to->im ) * ovlap.width; + + /* If the tile is painted, copy over the pixels. Otherwise, fill with + * zero. + */ + if( tile->state == TILE_PAINTED || + tile->state == TILE_PAINTED_FADING ) { +#ifdef DEBUG_PAINT + printf( "tile_copy: copying calculated pixels for %dx%d\n", + tile->area.left, tile->area.top ); +#endif /*DEBUG_PAINT*/ + + for( y = ovlap.top; y < IM_RECT_BOTTOM( &ovlap ); y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( tile->region, + ovlap.left, y ); + PEL *q = (PEL *) IM_REGION_ADDR( to, ovlap.left, y ); + + memcpy( q, p, len ); + } + } + else { +#ifdef DEBUG_PAINT + printf( "tile_copy: zero filling for %dx%d\n", + tile->area.left, tile->area.top ); +#endif /*DEBUG_PAINT*/ + + for( y = ovlap.top; + y < IM_RECT_BOTTOM( &ovlap ); y++ ) { + PEL *q = (PEL *) + IM_REGION_ADDR( to, ovlap.left, y ); + + memset( q, 0, len ); + } + } +} + +/* Loop over the output region, filling with data from cache. + */ +static int +region_fill( REGION *out, void *seq, Render *render ) +{ + Rect *r = &out->valid; + int x, y; + + /* Find top left of tiles we need. + */ + int xs = (r->left / render->width) * render->width; + int ys = (r->top / render->height) * render->height; + + /* Only allow one reader. No point threading this, calculation is + * decoupled anyway. + */ + g_mutex_lock( render->read_lock ); + + /* + + FIXME ... if r fits inside a single tile, could skip the copy. + + */ + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += render->height ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += render->width ) { + Rect area; + Tile *tile; + + area.left = x; + area.top = y; + area.width = render->width; + area.height = render->height; + + if( (tile = render_tile_get( render, &area )) ) + tile_copy( tile, out ); + } + + g_mutex_unlock( render->read_lock ); + + return( 0 ); +} + +/* Paint the state of the 'painted' flag for a tile. Fade too. + */ +static void +tile_paint_mask( Tile *tile, REGION *to ) +{ + int mask; + + Rect ovlap; + int y; + int len; + + /* Find common pixels. + */ + im_rect_intersectrect( &tile->area, &to->valid, &ovlap ); + assert( !im_rect_isempty( &ovlap ) ); + len = IM_IMAGE_SIZEOF_PEL( to->im ) * ovlap.width; + + if( tile->state == TILE_PAINTED_FADING ) { + Render *render = tile->render; + int age = render->time - tile->time; + double opacity = IM_CLIP( 0, (double) age / render->steps, 1 ); + + mask = 255 * pow( opacity, 4 ); + } + else if( tile->state == TILE_PAINTED ) + mask = 255; + else + mask = 0; + + for( y = ovlap.top; y < IM_RECT_BOTTOM( &ovlap ); y++ ) { + PEL *q = (PEL *) IM_REGION_ADDR( to, ovlap.left, y ); + + memset( q, mask, len ); + } +} + +/* The mask image is 255 .. 0 for the state of painted for each tile. + */ +static int +mask_fill( REGION *out, void *seq, Render *render ) +{ + Rect *r = &out->valid; + int x, y; + + /* Find top left of tiles we need. + */ + int xs = (r->left / render->width) * render->width; + int ys = (r->top / render->height) * render->height; + + g_mutex_lock( render->read_lock ); + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += render->height ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += render->width ) { + Rect area; + Tile *tile; + + area.left = x; + area.top = y; + area.width = render->width; + area.height = render->height; + + if( (tile = render_tile_get( render, &area )) ) + tile_paint_mask( tile, out ); + } + + g_mutex_unlock( render->read_lock ); + + return( 0 ); +} + +int +im_render_fade( IMAGE *in, IMAGE *out, IMAGE *mask, + int width, int height, int max, + int fps, int steps, + int priority, + notify_fn notify, void *client ) +{ + Render *render; + + /* Make sure the bg work threads are ready. + */ + if( render_thread_create() ) + return( -1 ); + + if( width <= 0 || height <= 0 || max < -1 || fps <= 0 || steps < 0 ) { + im_error( "im_render", _( "bad parameters" ) ); + return( -1 ); + } + if( im_pincheck( in ) || im_poutcheck( out ) ) + return( -1 ); + if( mask ) { + if( im_poutcheck( mask ) || + im_cp_desc( mask, in ) ) + return( -1 ); + + mask->Bands = 1; + mask->BandFmt = IM_BANDFMT_UCHAR; + mask->Bbits = IM_BBITS_BYTE; + mask->Type = IM_TYPE_B_W; + mask->Coding = IM_CODING_NONE; + } + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) || + im_demand_hint( mask, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + + if( !(render = render_new( in, out, mask, + width, height, max, fps, steps, priority, notify, client )) ) + return( -1 ); + +#ifdef DEBUG_MAKE + printf( "im_render: max = %d, %p\n", max, render ); +#endif /*DEBUG_MAKE*/ + + if( im_generate( out, NULL, region_fill, NULL, render, NULL ) ) + return( -1 ); + if( mask && + im_generate( mask, NULL, mask_fill, NULL, render, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_render( IMAGE *in, IMAGE *out, IMAGE *mask, + int width, int height, int max, notify_fn notify, void *client ) +{ + return( im_render_fade( in, out, mask, + width, height, max, 10, 0, 0, notify, client ) ); +} + +int +im_cache( IMAGE *in, IMAGE *out, int width, int height, int max ) +{ + if( im_render( in, out, NULL, width, height, max, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_setbox.c b/libsrc/iofuncs/im_setbox.c new file mode 100644 index 00000000..1ba8fde5 --- /dev/null +++ b/libsrc/iofuncs/im_setbox.c @@ -0,0 +1,64 @@ +/* @(#) Copies the coordinates of a box to an IMAGE_BOX + * @(#) + * @(#) Right call: + * @(#) void im_setbox(pbox, xst, yst, xsiz, ysiz, ch_select) + * @(#) IMAGE_BOX *pbox; + * @(#) int xst, yst, xsiz, ysiz, ch_select; + * @(#) ch_select could be 0, 1, 2 or 3 corresponding to + * @(#) a, r , g or b respectively. + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 13/02/1990 + * Modified on : 04/04/1990 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +void im_setbox(pbox, xst, yst, xsiz, ysiz, ch_select) +IMAGE_BOX *pbox; +int xst, yst, xsiz, ysiz, ch_select; +{ + pbox->xstart = xst; + pbox->ystart = yst; + pbox->xsize = xsiz; + pbox->ysize = ysiz; + pbox->chsel = ch_select; +} diff --git a/libsrc/iofuncs/im_setbuf.c b/libsrc/iofuncs/im_setbuf.c new file mode 100644 index 00000000..43f3b091 --- /dev/null +++ b/libsrc/iofuncs/im_setbuf.c @@ -0,0 +1,73 @@ +/* @(#) im_setbuf: initialise a buffer IMAGE + * @(#) Initialise the data pointer of the image descriptor to an arbitrary + * @(#) non NULL value and copies the file_name onto the filename of the + * @(#) image structure. + * @(#) + * @(#) Right call: + * @(#) IMAGE *im_setbuf(file_name) + * @(#) char *file_name; + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 13/02/1990 + * Modified on : 25/04/1990 KM, 20/03/1991 ND + * 16/4/93 J.Cupitt + * - support for type field added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +IMAGE * +im_setbuf( const char *file_name ) +{ + IMAGE *im; + + if( !(im = im_init( file_name )) ) + return( NULL ); + im->dtype = IM_SETBUF; + + /* Set demand style. Allow the most permissive sort. + */ + im->dhint = IM_ANY; + + return( im ); +} diff --git a/libsrc/iofuncs/im_setupout.c b/libsrc/iofuncs/im_setupout.c new file mode 100644 index 00000000..a930b498 --- /dev/null +++ b/libsrc/iofuncs/im_setupout.c @@ -0,0 +1,159 @@ +/* @(#) + * @(#) Function which sets up the output as follows + * @(#) If the output is a buffer, it allocates memory according to image sizes + * @(#) If the output is a file, it write a header + * @(#) to the file pointed by image.fd + * @(#) If the output is a partial image, then `magically' turn this from an + * @(#) im_partial() image into an im_setbuf() image. If im_setupout() is + * @(#) called, we take it as a sign that we are dealing with pre-partial + * @(#) images code. + * @(#) When exiting, image.fd points at the end of the header info. + * @(#) expecting raw image data. + * @(#) No description or history is involved + * @(#) Called by all im_funcs + * @(#) + * @(#) int im_setupout(image) + * @(#) IMAGE *image; + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * Copyright: Nicos Dessipris + * Written on: 16/01/1990 + * Modified on : 04/04/1990, 28/02/1991 + * 15/4/93 JC + * - partial image support added + * 18/6/93 JC + * - ANSIfied + * 4/7/01 JC + * - OPENOUT open delayed until here + * 21/8/01 ruven + * - stat/file needed + * 22/8/05 + * * - less stupid header write + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#ifdef HAVE_SYS_FILE_H +#include +#endif /*HAVE_SYS_FILE_H*/ +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Open mode for write ... on some systems, have to set BINARY too. + */ +#ifdef BINARY_OPEN +#define MODE (O_WRONLY | O_CREAT | O_TRUNC | O_BINARY) +#else +#define MODE (O_WRONLY | O_CREAT | O_TRUNC) +#endif /*BINARY_OPEN*/ + +int +im_setupout( IMAGE *im ) +{ + if( im_image_sanity( im ) ) + return( -1 ); + if( im->Xsize <= 0 || im->Ysize <= 0 || im->Bands <= 0 ) { + im_error( "im_setupout", _( "bad dimensions" ) ); + return( -1 ); + } + + if( im->dtype == IM_PARTIAL ) { + /* Make it into a im_setbuf() image. + */ +#ifdef DEBUG_IO + printf( "im_setupout: old-style output for %s\n", + im->filename ); +#endif /*DEBUG_IO*/ + im->dtype = IM_SETBUF; + } + + switch( im->dtype ) { + case IM_MMAPINRW: + case IM_SETBUF_FOREIGN: + /* No action. + */ + break; + + case IM_SETBUF: + /* Allocate memory. + */ + if( im->data ) { + /* Sanity failure! + */ + im_error( "im_setupout", _( "called twice!" ) ); + return( -1 ); + } + if( !(im->data = im_malloc( NULL, + IM_IMAGE_SIZEOF_LINE( im ) * im->Ysize )) ) + return( -1 ); + + break; + + case IM_OPENOUT: + { + /* Don't use im->sizeof_header here, but we know we're + * writing a VIPS image anyway. + */ + unsigned char header[IM_SIZEOF_HEADER]; + + if( (im->fd = open( im->filename, MODE, 0666 )) < 0 ) { + im_error( "im_setupout", + _( "unable to write to \"%s\"" ), + im->filename ); + return( -1 ); + } + if( im__write_header_bytes( im, header ) || + im__write( im->fd, header, IM_SIZEOF_HEADER ) ) + return( -1 ); + + break; + } + + default: + im_error( "im_setupout", _( "bad image descriptor" ) ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_unmapfile.c b/libsrc/iofuncs/im_unmapfile.c new file mode 100644 index 00000000..1c5c6df2 --- /dev/null +++ b/libsrc/iofuncs/im_unmapfile.c @@ -0,0 +1,71 @@ +/* @(#) Function which unmaps a file memory mapped by mapfile() + * @(#) The argument baseaddress should be the pointer returned by mapfile(); + * @(#) The function finds the size of the file from + * @(#) + * @(#) int im_unmapfile(fd, baseaddress) + * @(#) int fd; + * @(#) char *baseaddress; + * @(#) + * @(#) Returns 0 on success and -1 on error. + * @(#) + * Copyright: Nicos Dessipris + * Wriiten on: 13/02/1990 + * Updated on: + * 18/4/97 JC + * - ANSIfied + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#ifdef HAVE_SYS_MMAN_H +#include +#endif +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_unmapfile( IMAGE *im ) +{ + if( im__munmap( im->baseaddr, im->length ) ) + return( -1 ); + im->baseaddr = NULL; + im->length = 0; + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_updatehist.c b/libsrc/iofuncs/im_updatehist.c new file mode 100644 index 00000000..2e2481f3 --- /dev/null +++ b/libsrc/iofuncs/im_updatehist.c @@ -0,0 +1,80 @@ +/* @(#) + * @(#) int + * @(#) im_updatehist( IMAGE *out, const char *name, int argc, char *argv[] ) + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 16/01/1990 + * Modified on : 28/10/1992 J. Cupitt + * - now calls im_histlin, much simpler + * - many bugs in old version ... + * 22/8/05 + * - pass argv0 separately + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define IM_MAX_LINE (4096) + +int +im_updatehist( IMAGE *out, const char *name, int argc, char *argv[] ) +{ + int i; + char txt[IM_MAX_LINE]; + VBuf buf; + + im_buf_init_static( &buf, txt, IM_MAX_LINE ); + im_buf_appends( &buf, name ); + + for( i = 0; i < argc; i++ ) { + im_buf_appends( &buf, " " ); + im_buf_appends( &buf, argv[i] ); + } + + if( im_histlin( out, "%s", im_buf_all( &buf ) ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_wrapmany.c b/libsrc/iofuncs/im_wrapmany.c new file mode 100644 index 00000000..23909f46 --- /dev/null +++ b/libsrc/iofuncs/im_wrapmany.c @@ -0,0 +1,202 @@ +/* Wrap-up a buffer processing function as a PIO VIPS function. + * + * Given a NULL-terminated list of input images all of the same size, an + * output image and a buffer processing function, make a PIO image processing + * operation. + * + * int im_wrapmany( IMAGE **in, IMAGE *out, + * im_wrapmany_fn fn, void *a, void *b ) + * + * where im_wrapmany_fn has type: + * + * process_buffer( void **in, void *out, int n, + * void *a, void *b ) + * + * in is a NULL-terminated array of input buffers, out is an output buffer, n + * is the number of pixels (note! not band-elements) and a and b are extra + * arguments carried for the function + * + * Modified: + * 1/8/95 JC + * - buffer functions now get their own copies of the input pointer + * array + * 28/7/97 JC + * - amazing error ... only worked if ir and or had same valid + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +typedef struct { + im_wrapmany_fn fn; /* Function we call */ + void *a, *b; /* User values for function */ +} UserBundle; + +/* Maximum number of input images -- why not? + */ +#define IM_MAX_INPUT_IMAGES (64) + +/* Convert a REGION. + */ +static int +process_region( REGION *or, REGION **ir, IMAGE *im, UserBundle *bun ) +{ + PEL *p[IM_MAX_INPUT_IMAGES], *q; + int i, y; + + /* Prepare all input regions and make buffer pointers. + */ + for( i = 0; ir[i]; i++ ) { + if( im_prepare( ir[i], &or->valid ) ) + return( -1 ); + p[i] = (PEL *) IM_REGION_ADDR( ir[i], + or->valid.left, or->valid.top ); + } + p[i] = NULL; + q = (PEL *) IM_REGION_ADDR( or, or->valid.left, or->valid.top ); + + /* Convert linewise. + */ + for( y = 0; y < or->valid.height; y++ ) { + PEL *p1[IM_MAX_INPUT_IMAGES]; + + /* Make a copy of p[] which the buffer function can mess up if + * it wants. + */ + for( i = 0; ir[i]; i++ ) + p1[i] = p[i]; + + /* Bizarre double-cast stops a bogus gcc 4.1 compiler warning. + */ + bun->fn( (void **) ((void *)p1), q, + or->valid.width, bun->a, bun->b ); + + /* Move pointers on. + */ + for( i = 0; ir[i]; i++ ) + p[i] += IM_REGION_LSKIP( ir[i] ); + q += IM_REGION_LSKIP( or ); + } + + return( 0 ); +} + +/* Make a copy of an array of input images. + */ +static IMAGE ** +dupims( IMAGE *out, IMAGE **in ) +{ + IMAGE **new; + int i, n; + + /* Count input images. + */ + for( n = 0; in[n]; n++ ) + ; + + /* Allocate new array. + */ + if( !(new = IM_ARRAY( out, n + 1, IMAGE * )) ) + return( NULL ); + + /* Copy. + */ + for( i = 0; i < n; i++ ) + new[i] = in[i]; + new[n] = NULL; + + return( new ); +} + +/* Wrap up as a partial. + */ +int +im_wrapmany( IMAGE **in, IMAGE *out, im_wrapmany_fn fn, void *a, void *b ) +{ + UserBundle *bun = IM_NEW( out, UserBundle ); + int i, n; + + /* Count input images. + */ + for( n = 0; in[n]; n++ ) + ; + if( n >= IM_MAX_INPUT_IMAGES - 1 ) { + im_error( "im_wrapmany", _( "too many input images" ) ); + return( -1 ); + } + + /* Save args. + */ + if( !bun || !(in = dupims( out, in )) ) + return( -1 ); + bun->fn = fn; + bun->a = a; + bun->b = b; + + /* Check descriptors --- make sure that our caller has done this + * correctly. + */ + for( i = 0; i < n; i++ ) { + if( in[i]->Xsize != out->Xsize || in[i]->Ysize != out->Ysize ) { + im_error( "im_wrapmany", + _( "descriptors differ in size" ) ); + return( -1 ); + } + + /* Check io style. + */ + if( im_piocheck( in[i], out ) ) + return( -1 ); + } + + /* Hint demand style. Being a buffer processor, we are happiest with + * thin strips. + */ + if( im_demand_hint_array( out, IM_THINSTRIP, in ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_many, process_region, im_stop_many, in, bun ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_wrapone.c b/libsrc/iofuncs/im_wrapone.c new file mode 100644 index 00000000..7f997a37 --- /dev/null +++ b/libsrc/iofuncs/im_wrapone.c @@ -0,0 +1,121 @@ +/* As im_wrapmany, but just allow one input and one output. + * + * The types become: + * + * int im_wrapone( IMAGE *in, IMAGE *out, + * im_wrapone_fn fn, void *a, void *b ) + * + * where im_wrapone_fn has type: + * + * process_buffer( void *in, void *out, int n, + * void *a, void *b ) + * 28/7/97 JC + * - amazing error ... failed if or and ir were different sizes + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +typedef struct { + im_wrapone_fn fn; /* Function we call */ + void *a, *b; /* User values for function */ +} UserBundle; + +/* Build or->valid a line at a time from ir. + */ +static int +process_region( REGION *or, REGION *ir, IMAGE *im, UserBundle *bun ) +{ + PEL *p, *q; + int y; + + /* Prepare input region and make buffer pointers. + */ + if( im_prepare( ir, &or->valid ) ) + return( -1 ); + p = (PEL *) IM_REGION_ADDR( ir, or->valid.left, or->valid.top ); + q = (PEL *) IM_REGION_ADDR( or, or->valid.left, or->valid.top ); + + /* Convert linewise. + */ + for( y = 0; y < or->valid.height; y++ ) { + bun->fn( p, q, or->valid.width, bun->a, bun->b ); + p += IM_REGION_LSKIP( ir ); + q += IM_REGION_LSKIP( or ); + } + + return( 0 ); +} + +/* Wrap up as a partial. + */ +int +im_wrapone( IMAGE *in, IMAGE *out, im_wrapone_fn fn, void *a, void *b ) +{ + UserBundle *bun = IM_NEW( out, UserBundle ); + + /* Save args. + */ + if( !bun ) + return( -1 ); + bun->fn = fn; + bun->a = a; + bun->b = b; + + /* Check descriptors. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Hint demand style. Being a buffer processor, we are happiest with + * thin strips. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, process_region, im_stop_one, + in, bun ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_writeline.c b/libsrc/iofuncs/im_writeline.c new file mode 100644 index 00000000..37325acd --- /dev/null +++ b/libsrc/iofuncs/im_writeline.c @@ -0,0 +1,113 @@ +/* @(#) Functions which writes the yth buffer line to either the output file + * @(#) or the output buffer. + * @(#) It is the responsibility of the user to create a buffer line + * @(#) and write the data to it before calling this function. + * @(#) No checking is carried out for image + * @(#) + * @(#) int im_writeline(ypos, image, linebuffer) + * @(#) int ypos; + * @(#) IMAGE *image; + * @(#) char *linebuffer; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 04/04/1990 + * Modified on : + * 15/4/93 JC + * - support for partial images + * 13/12/93 JC + * - now triggers eval callbacks for the output image. + * 26/3/02 JC + * - better error messages + * 31/10/03 JC + * - stop early on kill + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#ifdef HAVE_IO_H +#include +#endif /*HAVE_IO_H*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_writeline( int ypos, IMAGE *image, PEL *linebuffer ) +{ + int linesize = IM_IMAGE_SIZEOF_LINE( image ); + char *tmp; + + /* Possible cases for output: FILE or SETBUF. + */ + switch( image->dtype ) { + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + tmp = image->data + ypos * linesize; + memcpy( tmp, linebuffer, linesize ); + + break; + + case IM_OPENOUT: + if( im__write( image->fd, linebuffer, linesize ) ) + return( -1 ); + + break; + + default: + im_errormsg( "im_writeline: unable to output to a %s image", + im_dtype2char( image->dtype ) ); + return( -1 ); + } + + /* Trigger evaluation callbacks for this image. + */ + if( im__handle_eval( image, image->Xsize, 1 ) ) + return( -1 ); + if( im__test_kill( image ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/man3/IM_ARRAY.3 b/libsrc/iofuncs/man3/IM_ARRAY.3 new file mode 100644 index 00000000..2ed9a74b --- /dev/null +++ b/libsrc/iofuncs/man3/IM_ARRAY.3 @@ -0,0 +1,80 @@ +.TH IM_ARRAY 3 "11 April 1993" +.SH NAME +IM_ARRAY, IM_NEW, IM_NUMBER \- memory allocation macros +.SH SYNOPSIS + +#include +.br +#include + +type-name *IM_NEW( IMAGE *im, type-name ) +.br +type-name *IM_ARRAY( IMAGE *im, int number, type-name ) +.br +int IM_NUMBER( array ) + +.SH DESCRIPTION + +NEW, NUMBER and ARRAY are macros built on im_malloc(3) which make memory +allocation slightly easier. Given a type name, NEW returns a pointer to a +piece of memory large enough to hold an object of that type. ARRAY works as +NEW, but allocates space for a number of objects. Given an array, NUMBER +returns the number of elements in that array. + + #define IM_NEW(IM,A) ((A *)im_malloc((IM),sizeof(A))) + #define IM_NUMBER(R) (sizeof(R)/sizeof(R[0])) + #define IM_ARRAY(IM,N,T) ((T *)im_malloc((IM),(N) * sizeof(T))) + +Both IM_ARRAY and IM_NEW take an image descriptor as their first +parameter. Memory is allocated local to this descriptor, that is, when the +descriptor is closed, the memory is automatically freed for you. If you +pass NULL instead of an image descriptor, memory is allocated globally and +is not automatically freed. + +(NOTE: in versions of VIPS before 7.3, NEW(3) and ARRAY(3) did not have the +initial IMAGE parameter. If you are converting an old VIPS7.2 program, you +will need to add a NULL parameter to the start of all NEW(3) and ARRAY(3) +parameter lists.) + +Both functions return NULL on error, setting im_errorstring. + +Example: + + #include + #include + + /* A structure we want to carry about. + */ + typedef struct { + ... + } Wombat; + + /* A static array of them. + */ + static Wombat swarm[] = { + { ... }, + { ... }, + { ... } + }; + static int swarm_size = IM_NUMBER( swarm ); + + int + transform_wombat( IMAGE *in, IMAGE *out ) + { + /* Allocate space for a Wombat. + */ + Wombat *mar = IM_NEW( out, Wombat ); + + /* Allocate space for a copy of swarm. + */ + Wombat *mar = IM_ARRAY( out, swarm_size, Wombat ); + + .... + } + +.SH COPYRIGHT +National Gallery, 1993 +.SH SEE ALSO +im_malloc(3), im_open_local(3). +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/IM_IMAGE_ADDR.3 b/libsrc/iofuncs/man3/IM_IMAGE_ADDR.3 new file mode 100644 index 00000000..bdbc8709 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_IMAGE_ADDR.3 @@ -0,0 +1,66 @@ +.TH MACROS 3 "11 April 1990" +.SH NAME +IM_IMAGE_ADDR, IM_IMAGE_SIZEOF_ELEMENT, IM_IMAGE_SIZEOF_PEL, +IM_IMAGE_SIZEOF_LINE, IM_IMAGE_N_ELEMENTS \- +macros for images +.SH SYNOPSIS +.B #include + +int IM_IMAGE_SIZEOF_ELEMENT( im ) +.br +IMAGE *im; + +int IM_IMAGE_SIZEOF_PEL( im ) +.br +IMAGE *im; + +int IM_IMAGE_SIZEOF_LINE( im ) +.br +IMAGE *im; + +int IM_IMAGE_N_ELEMENTS( im ) +.br +IMAGE *im; + +char *IM_IMAGE_ADDR( im, x, y ) +.br +IMAGE *im; +.br +int x; +.br +int y; + +.SH DESCRIPTION +These macros help to simplify address arithmetic for images. + +IM_IMAGE_SIZEOF_ELEMENT(3) returns sizeof( one band element ). + +IM_IMAGE_SIZEOF_PEL(3) returns sizeof( one pel ). + +IM_IMAGE_SIZEOF_LINE(3) returns sizeof( one horizontal line of pels ). + +IM_IMAGE_N_ELEMENTS(3) returns the number of band elements across a horizontal line. + +IM_IMAGE_ADDR(3) returns a pointer to the pixel at position (x,y) in the +image. The point (x,y) should lie within the image. + +If the macro DEBUG has been defined, then IM_IMAGE_ADDR(3) will also +perform bounds checking. If you ask for the address of a pel outside the +image, +then IM_IMAGE_ADDR(3) will print an error message of the form: + + IM_IMAGE_ADDR: point out of bounds, file "test.c", line 18 + (point x=50, y=0 + should have been within Rect left=0, top=0, width=50, height=50) + +and call abort(3). + +DEBUG needs to be defined *before* vips.h is included. Either define DEBUG +with -D in your Makefile, or have a #define DEBUG right at the top of your +file. +.SH COPYRIGHT +National Gallery, 1993 +.SH SEE ALSO +im_malloc(3), im_open_local(3). +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/IM_IMAGE_N_ELEMENTS.3 b/libsrc/iofuncs/man3/IM_IMAGE_N_ELEMENTS.3 new file mode 100644 index 00000000..0e4c07b3 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_IMAGE_N_ELEMENTS.3 @@ -0,0 +1 @@ +.so man3/IM_IMAGE_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_ELEMENT.3 b/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_ELEMENT.3 new file mode 100644 index 00000000..0e4c07b3 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_ELEMENT.3 @@ -0,0 +1 @@ +.so man3/IM_IMAGE_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_LINE.3 b/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_LINE.3 new file mode 100644 index 00000000..0e4c07b3 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_LINE.3 @@ -0,0 +1 @@ +.so man3/IM_IMAGE_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_PEL.3 b/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_PEL.3 new file mode 100644 index 00000000..0e4c07b3 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_PEL.3 @@ -0,0 +1 @@ +.so man3/IM_IMAGE_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_MAX.3 b/libsrc/iofuncs/man3/IM_MAX.3 new file mode 100644 index 00000000..b83855ef --- /dev/null +++ b/libsrc/iofuncs/man3/IM_MAX.3 @@ -0,0 +1 @@ +.so man3/IM_RINT.3 diff --git a/libsrc/iofuncs/man3/IM_MIN.3 b/libsrc/iofuncs/man3/IM_MIN.3 new file mode 100644 index 00000000..b83855ef --- /dev/null +++ b/libsrc/iofuncs/man3/IM_MIN.3 @@ -0,0 +1 @@ +.so man3/IM_RINT.3 diff --git a/libsrc/iofuncs/man3/IM_NEW.3 b/libsrc/iofuncs/man3/IM_NEW.3 new file mode 100644 index 00000000..c891c6dd --- /dev/null +++ b/libsrc/iofuncs/man3/IM_NEW.3 @@ -0,0 +1 @@ +.so man3/IM_ARRAY.3 diff --git a/libsrc/iofuncs/man3/IM_NUMBER.3 b/libsrc/iofuncs/man3/IM_NUMBER.3 new file mode 100644 index 00000000..c891c6dd --- /dev/null +++ b/libsrc/iofuncs/man3/IM_NUMBER.3 @@ -0,0 +1 @@ +.so man3/IM_ARRAY.3 diff --git a/libsrc/iofuncs/man3/IM_RECT_BOTTOM.3 b/libsrc/iofuncs/man3/IM_RECT_BOTTOM.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_RECT_BOTTOM.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/IM_RECT_HCENTRE.3 b/libsrc/iofuncs/man3/IM_RECT_HCENTRE.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_RECT_HCENTRE.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/IM_RECT_RIGHT.3 b/libsrc/iofuncs/man3/IM_RECT_RIGHT.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_RECT_RIGHT.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/IM_RECT_VCENTRE.3 b/libsrc/iofuncs/man3/IM_RECT_VCENTRE.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_RECT_VCENTRE.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/IM_REGION_ADDR.3 b/libsrc/iofuncs/man3/IM_REGION_ADDR.3 new file mode 100644 index 00000000..206f8c0a --- /dev/null +++ b/libsrc/iofuncs/man3/IM_REGION_ADDR.3 @@ -0,0 +1,65 @@ +.TH MACROS 3 "11 April 1990" +.SH NAME +IM_REGION_ADDR, +IM_REGION_LSKIP, +IM_REGION_N_ELEMENTS, IM_REGION_SIZEOF_LINE \- +macros for regions +.SH SYNOPSIS +.B #include +.br +.B #include + +int IM_REGION_LSKIP( reg ) +.br +REGION *reg; + +int IM_REGION_N_ELEMENTS( reg ) +.br +REGION *reg; + +int IM_REGION_SIZEOF_LINE( reg ) +.br +REGION *reg; + +char *IM_REGION_ADDR( reg, x, y ) +.br +REGION *reg; +.br +int x, y; + +.SH DESCRIPTION +These macros help to simplify address arithmetic for regions. + +IM_REGION_LSKIP(3) returns the number of *bytes* you should add to move +down a scan line. Remember that if your pointer has been cast to the type +of the image pels, this will not be the correct amount to add! The value +lskip returns can be changed by a call to im_prepare(3). + +IM_REGION_N_ELEMENTS(3) returns the number of band elements across the region. + +IM_REGION_SIZEOF_LINE(3) returns sizeof( horizontal line across region ). + +IM_REGION_ADDR(3) returns a pointer to the pixel at position (x,y) in the +image on which reg has been defined. The point (x,y) should lie within the +valid area for this region. + +If the macro DEBUG has been defined, then IM_REGION_ADDR(3) will also +perform bounds checking. If you ask for the address of a pel outside the rect +reg->valid, then IM_REGION_ADDR(3) will print an error message of the form: + + IM_REGION_ADDR: point out of bounds, file "test.c", line 18 + (point x=50, y=0 + should have been within Rect left=0, top=0, width=50, height=50) + +and call abort(3). + +DEBUG needs to be defined *before* region.h is included. Either define DEBUG +with -D in your Makefile, or have a #define DEBUG right at the top of your +file. + +.SH COPYRIGHT +National Gallery, 1993 +.SH SEE ALSO +im_malloc(3), im_open_local(3). +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/IM_REGION_LSKIP.3 b/libsrc/iofuncs/man3/IM_REGION_LSKIP.3 new file mode 100644 index 00000000..9fc98c69 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_REGION_LSKIP.3 @@ -0,0 +1 @@ +.so man3/IM_REGION_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_REGION_N_ELEMENTS.3 b/libsrc/iofuncs/man3/IM_REGION_N_ELEMENTS.3 new file mode 100644 index 00000000..9fc98c69 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_REGION_N_ELEMENTS.3 @@ -0,0 +1 @@ +.so man3/IM_REGION_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_REGION_SIZEOF_LINE.3 b/libsrc/iofuncs/man3/IM_REGION_SIZEOF_LINE.3 new file mode 100644 index 00000000..9fc98c69 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_REGION_SIZEOF_LINE.3 @@ -0,0 +1 @@ +.so man3/IM_REGION_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_RINT.3 b/libsrc/iofuncs/man3/IM_RINT.3 new file mode 100644 index 00000000..210c399c --- /dev/null +++ b/libsrc/iofuncs/man3/IM_RINT.3 @@ -0,0 +1,36 @@ +.TH MACROS 3 "11 April 1990" +.SH NAME +IM_RINT, IM_MAX, IM_MIN \- misc math macros +.SH SYNOPSIS + +.B #include +.br +.B #include + +int IM_RINT( float ) +.br +any IM_MAX( any, any ) +.br +any IM_MIN( any, any ) + +.SH DESCRIPTION + +These macros provide some simple but fast math functions --- IM_MAX(3) +returns the maximum of its two arguments, IM_MIN(3) the smallest, and +IM_RINT(3) rounds a float or double to the nearest integer. + +Beware: these macros may evaluate their argument more than once, so you MUST +NOT use ++,--, or a function call in their argument lists. + +They are defined as: + + #define IM_MAX(A,B) ((A)>(B)?(A):(B)) + #define IM_MIN(A,B) ((A)<(B)?(A):(B)) + #define IM_RINT(R) ((int)((R)>0?((R)+0.5):((R)-0.5))) + +.SH COPYRIGHT +National Gallery, 1993 +.SH SEE ALSO +im_malloc(3), im_open_local(3). +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/Makefile.am b/libsrc/iofuncs/man3/Makefile.am new file mode 100644 index 00000000..576eaa52 --- /dev/null +++ b/libsrc/iofuncs/man3/Makefile.am @@ -0,0 +1,165 @@ +man_MANS = \ + IM_ARRAY.3 \ + IM_IMAGE_ADDR.3 \ + IM_IMAGE_N_ELEMENTS.3 \ + IM_IMAGE_SIZEOF_ELEMENT.3 \ + IM_IMAGE_SIZEOF_LINE.3 \ + IM_IMAGE_SIZEOF_PEL.3 \ + IM_MAX.3 \ + IM_MIN.3 \ + IM_NEW.3 \ + IM_NUMBER.3 \ + IM_REGION_ADDR.3 \ + IM_REGION_LSKIP.3 \ + IM_REGION_N_ELEMENTS.3 \ + IM_REGION_SIZEOF_LINE.3 \ + IM_RINT.3 \ + IM_RECT_RIGHT.3 \ + IM_RECT_BOTTOM.3 \ + IM_RECT_HCENTRE.3 \ + IM_RECT_VCENTRE.3 \ + error_exit.3 \ + im_BandFmt2char.3 \ + im_Coding2char.3 \ + im_Compression2char.3 \ + im_Type2char.3 \ + im_add_close_callback.3 \ + im_add_eval_callback.3 \ + im_add_evalend_callback.3 \ + im_allocate_input_array.3 \ + im_amiMSBfirst.3 \ + im_binfile.3 \ + im_bits_of_fmt.3 \ + im_char2BandFmt.3 \ + im_char2Coding.3 \ + im_char2Compression.3 \ + im_char2Type.3 \ + im_concurrency_set.3 \ + im_concurrency_get.3 \ + im_error_buffer.3 \ + im_verror.3 \ + im_error_clear.3 \ + im_close.3 \ + im_cp_desc.3 \ + im_cp_descv.3 \ + im_cp_desc_array.3 \ + im_debugim.3 \ + im_demand_hint.3 \ + im_demand_hint_array.3 \ + im_diag.3 \ + im_error.3 \ + im_generate.3 \ + im_guess_prefix.3 \ + im_header.3 \ + im_header_double.3 \ + im_header_int.3 \ + im_header_string.3 \ + im_header_get.3 \ + im_header_get_type.3 \ + im_header_map.3 \ + im_histlin.3 \ + im_history_get.3 \ + im_image.3 \ + im_image_sanity.3 \ + im_incheck.3 \ + im_init.3 \ + im_init_world.3 \ + im_get_option_group.3 \ + im_initdesc.3 \ + im_iocheck.3 \ + im_isMSBfirst.3 \ + im_iscomplex.3 \ + im_isfile.3 \ + im_isfloat.3 \ + im_isint.3 \ + im_isscalar.3 \ + im_isjpeg.3 \ + im_ispartial.3 \ + im_ispng.3 \ + im_isppm.3 \ + im_istifftiled.3 \ + im_isuint.3 \ + im_isvips.3 \ + im_iterate.3 \ + im_list_add.3 \ + im_list_append.3 \ + im_list_eq.3 \ + im_list_fix.3 \ + im_list_fold.3 \ + im_list_free.3 \ + im_list_index.3 \ + im_list_insert.3 \ + im_list_len.3 \ + im_list_map.3 \ + im_render.3 \ + im_render_fade.3 \ + im_cache.3 \ + im_list_map_rev.3 \ + im_list_member.3 \ + im_list_pos.3 \ + im_list_remove.3 \ + im_makerw.3 \ + im_malloc.3 \ + im_free.3 \ + im_mmapin.3 \ + im_mmapinrw.3 \ + im_open.3 \ + im_open_local.3 \ + im_open_local_array.3 \ + im_openout.3 \ + im_outcheck.3 \ + im_partial.3 \ + im_pincheck.3 \ + im_piocheck.3 \ + im_poutcheck.3 \ + im_prepare.3 \ + im_prepare_many.3 \ + im_prepare_to.3 \ + im_printdesc.3 \ + im_printlines.3 \ + im_rect_dup.3 \ + im_rect_equalsrect.3 \ + im_rect_includespoint.3 \ + im_rect_includesrect.3 \ + im_rect_intersectrect.3 \ + im_rect_isempty.3 \ + im_rect_marginadjust.3 \ + im_rect_normalise.3 \ + im_rect_unionrect.3 \ + im_region_create.3 \ + im_region_free.3 \ + im_region_image.3 \ + im_region_buffer.3 \ + im_region_position.3 \ + im_region_region.3 \ + im_setbuf.3 \ + im_setupout.3 \ + im_start_many.3 \ + im_start_one.3 \ + im_stop_many.3 \ + im_stop_one.3 \ + im_updatehist.3 \ + im_version.3 \ + im_version_string.3 \ + im_warn.3 \ + im_wrapmany.3 \ + im_wrapone.3 \ + im_writeline.3 \ + im_meta.3 \ + im_meta_get.3 \ + im_meta_get_area.3 \ + im_meta_get_double.3 \ + im_meta_get_int.3 \ + im_meta_get_string.3 \ + im_meta_get_type.3 \ + im_meta_get_blob.3 \ + im_meta_set.3 \ + im_meta_set_area.3 \ + im_meta_set_double.3 \ + im_meta_set_int.3 \ + im_meta_set_blob.3 \ + im_meta_set_string.3 \ + im_invalidate.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/iofuncs/man3/error_exit.3 b/libsrc/iofuncs/man3/error_exit.3 new file mode 100644 index 00000000..7db4b63b --- /dev/null +++ b/libsrc/iofuncs/man3/error_exit.3 @@ -0,0 +1 @@ +.so man3/im_error.3 diff --git a/libsrc/iofuncs/man3/im_BandFmt2char.3 b/libsrc/iofuncs/man3/im_BandFmt2char.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_BandFmt2char.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_Coding2char.3 b/libsrc/iofuncs/man3/im_Coding2char.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_Coding2char.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_Compression2char.3 b/libsrc/iofuncs/man3/im_Compression2char.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_Compression2char.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_Type2char.3 b/libsrc/iofuncs/man3/im_Type2char.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_Type2char.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_add_close_callback.3 b/libsrc/iofuncs/man3/im_add_close_callback.3 new file mode 100644 index 00000000..0be9c41f --- /dev/null +++ b/libsrc/iofuncs/man3/im_add_close_callback.3 @@ -0,0 +1 @@ +.so man3/im_malloc.3 diff --git a/libsrc/iofuncs/man3/im_add_eval_callback.3 b/libsrc/iofuncs/man3/im_add_eval_callback.3 new file mode 100644 index 00000000..0be9c41f --- /dev/null +++ b/libsrc/iofuncs/man3/im_add_eval_callback.3 @@ -0,0 +1 @@ +.so man3/im_malloc.3 diff --git a/libsrc/iofuncs/man3/im_add_evalend_callback.3 b/libsrc/iofuncs/man3/im_add_evalend_callback.3 new file mode 100644 index 00000000..0be9c41f --- /dev/null +++ b/libsrc/iofuncs/man3/im_add_evalend_callback.3 @@ -0,0 +1 @@ +.so man3/im_malloc.3 diff --git a/libsrc/iofuncs/man3/im_allocate_input_array.3 b/libsrc/iofuncs/man3/im_allocate_input_array.3 new file mode 100644 index 00000000..c124af1e --- /dev/null +++ b/libsrc/iofuncs/man3/im_allocate_input_array.3 @@ -0,0 +1 @@ +.so man3/im_generate.3 diff --git a/libsrc/iofuncs/man3/im_amiMSBfirst.3 b/libsrc/iofuncs/man3/im_amiMSBfirst.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_amiMSBfirst.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_binfile.3 b/libsrc/iofuncs/man3/im_binfile.3 new file mode 100644 index 00000000..1c7d7f93 --- /dev/null +++ b/libsrc/iofuncs/man3/im_binfile.3 @@ -0,0 +1,42 @@ +.TH IM_BINFILE 3 "11 April 1990" +.SH NAME +im_binfile, im_image \- wrap a raw binary file inside an IMAGE descriptor +.SH SYNOPSIS +.B #include + +IMAGE *im_binfile( name, xs, ys, b, off ) +.br +char *in; +.br +IMAGE *out; +.br +int xs, ys, b, off; + +IMAGE * +.br +im_image( void *buffer, int width, int height, int bands, int format ) + +.SH DESCRIPTION +.B im_binfile(3) +maps the file named, and returns an image descriptor which looks +very like the sort of thing that +.B im_mmapin(3) +returns. + +The parameters specify the image width, height, number of bands and offset +in bytes from the start of the file. + +.B im_image(3) +makes an IMAGE deriptor from an area of pixels in memory. The memory +buffer will not be freed when the IMAGE is closed, use +.B im_add_close_callback() +if you want this. + +.SH RETURN VALUE +The functions return NULL on error. +.SH SEE ALSO +im_mmapin(3), im_openout(3), im_setbuf(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_bits_of_fmt.3 b/libsrc/iofuncs/man3/im_bits_of_fmt.3 new file mode 100644 index 00000000..f4ee23ed --- /dev/null +++ b/libsrc/iofuncs/man3/im_bits_of_fmt.3 @@ -0,0 +1,16 @@ +.TH IM_BITS_OF_FMT 3 "2 June 2005" +.SH NAME +im_bits_of_fmt \- return number of bits of band format +.SH SYNOPSIS +.B #include + +.B int im_bits_of_fmt( int bandfmt ) + +.SH DESCRIPTION +.B im_bits_of_fmt(3) +calculates the number of bits of band format. + +.SH RETURN VALUE +On success this function returns the number of bits of band format and +-1 is returned on error. + diff --git a/libsrc/iofuncs/man3/im_cache.3 b/libsrc/iofuncs/man3/im_cache.3 new file mode 100644 index 00000000..b7647daa --- /dev/null +++ b/libsrc/iofuncs/man3/im_cache.3 @@ -0,0 +1 @@ +.so man3/im_render.3 diff --git a/libsrc/iofuncs/man3/im_char2BandFmt.3 b/libsrc/iofuncs/man3/im_char2BandFmt.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_char2BandFmt.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_char2Coding.3 b/libsrc/iofuncs/man3/im_char2Coding.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_char2Coding.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_char2Compression.3 b/libsrc/iofuncs/man3/im_char2Compression.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_char2Compression.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_char2Type.3 b/libsrc/iofuncs/man3/im_char2Type.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_char2Type.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_close.3 b/libsrc/iofuncs/man3/im_close.3 new file mode 100644 index 00000000..d7e21787 --- /dev/null +++ b/libsrc/iofuncs/man3/im_close.3 @@ -0,0 +1,35 @@ +.TH IM_CLOSE 3 "11 April 1990" +.SH NAME +im_close \- close an image descriptor +.SH SYNOPSIS +#include + +int im_close(image) +.br +IMAGE *image; +.SH DESCRIPTION +im_close(3) frees all the resources attached to the image descriptor. This may +involve closing files, freeing memory buffers, triggering close callback +lists, unmapping files, freeing regions, and so on. If all this succeeds, then +the function returns zero. If something goes wrong, the function returns +non-zero and sets im_errormsg(3). If im_close(3) fails, the image descriptor is +left in an undefined state. + +In the case that the image descriptor corresponds to a file opened by +im_openout(3) that has been written to, the function sets an output +description file as follows: If the output image filename is terminated with +".v", the string held by the Hist member of the image descriptor is copied to +a corresponding file ending with ".desc" in the same directory. In all other +cases, an output .desc file is not created. + +If a NULL pointer is passed to im_close(3), it returns successfully +immediately. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH COPYRIGHT +N. Dessipris, K. Martinez, J. Cupitt +.SH SEE ALSO +im_mmapin(3), im_openin(3), im_openout(3), im_setbuf(3), im_open(3), +im_incheck(3), im_pincheck(3). +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_concurrency_get.3 b/libsrc/iofuncs/man3/im_concurrency_get.3 new file mode 100644 index 00000000..9c8864dd --- /dev/null +++ b/libsrc/iofuncs/man3/im_concurrency_get.3 @@ -0,0 +1 @@ +.so man3/im_concurrency_set.3 diff --git a/libsrc/iofuncs/man3/im_concurrency_set.3 b/libsrc/iofuncs/man3/im_concurrency_set.3 new file mode 100644 index 00000000..5b9938f6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_concurrency_set.3 @@ -0,0 +1,32 @@ +.TH IM_CONCURRENCY 3 "8 January 2007" +.SH NAME +im_concurrency_set, im_concurrency_get \- set and get the number of threads to +use for evaluation +.SH SYNOPSIS +.B #include + +void im_concurrency_set( int concurrency ) +.br +int im_concurrency_get( void ) + +.SH DESCRIPTION + +.B im_concurrency_set(3) +and +.B im_concurrency_get(3) +set and get the number of parallel threads VIPS should use to calculate +pixels. + +A value of zero (the default) means to get the number of threads +from the environment variable IM_CONCURRENCY. If that is not set, the number +of threads defaults to one. + +Setting the number of threads only affects image evaluations which start after +that point, it will not change the behaviour of existing evaluations. + +Most command-line vips programs support the --vips-concurrency flag, which can +also be used to set the concurrency. + +.SH SEE ALSO +im_get_option_group(3), +`VIPS manual,' in accompanying documentation. diff --git a/libsrc/iofuncs/man3/im_cp_desc.3 b/libsrc/iofuncs/man3/im_cp_desc.3 new file mode 100644 index 00000000..89627abb --- /dev/null +++ b/libsrc/iofuncs/man3/im_cp_desc.3 @@ -0,0 +1,48 @@ +.TH IM_CP_DESC 3 "11 April 1990" +.SH NAME +im_cp_desc, im_append_Hist \- copy most of an image descriptor to another +image descriptor. +.SH SYNOPSIS +.B #include + +int im_cp_desc_array( IMAGE *out, IMAGE *in[] ); + +int im_cp_descv( IMAGE *out, IMAGE *in1, ... ); + +.B int im_cp_desc( IMAGE *out, IMAGE *in ); + +.B int im_append_Hist( IMAGE *out, IMAGE *in ); + +.SH DESCRIPTION +.B im_cp_desc_array(3) +takes an output image and a NULL-terminated list of input images, which must +contain at least one input image. + +It copies the fields describing the size, bands, type, resolution, and +coding from the first input image to the output image. History and meta from +all images are copied over. If two input images have the same meta tag, the +earlier one takes precedence. + +.B im_cp_descv(3) +is a varargs conveniece function for +.B im_cp_desc_array(3). + +.B im_cp_desc(3) +is a convenience function which calls +.B im_cp_descv( image1, image2, NULL ). + +.B im_append_Hist(3) +appends the history attached to image2 to the end of the history on image1. It +is used by image processing functions which take more than one image as input, +and which need to make sure that all the input history appears in the output. + +The first line of image2 history is not copied, as this conventionally holds +background information which is not part of the file history. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_cp_desc_array.3 b/libsrc/iofuncs/man3/im_cp_desc_array.3 new file mode 100644 index 00000000..33ff1a6b --- /dev/null +++ b/libsrc/iofuncs/man3/im_cp_desc_array.3 @@ -0,0 +1 @@ +.so man3/im_cp_desc.3 diff --git a/libsrc/iofuncs/man3/im_cp_descv.3 b/libsrc/iofuncs/man3/im_cp_descv.3 new file mode 100644 index 00000000..33ff1a6b --- /dev/null +++ b/libsrc/iofuncs/man3/im_cp_descv.3 @@ -0,0 +1 @@ +.so man3/im_cp_desc.3 diff --git a/libsrc/iofuncs/man3/im_debugim.3 b/libsrc/iofuncs/man3/im_debugim.3 new file mode 100644 index 00000000..c2712a8a --- /dev/null +++ b/libsrc/iofuncs/man3/im_debugim.3 @@ -0,0 +1,31 @@ +.TH IM_DEBUG 3 "22 April 1991" +.SH NAME +im_debug, im_printlines \- print raw image data pointed by an image descriptor +.SH SYNOPSIS +.B #include + +.B void im_debugim( image ) +.br +.B IMAGE *image; + +.B void im_printlines( image ) +.br +.B IMAGE *image; +.SH DESCRIPTION +.B im_debugim(3) +prints to the standard error output raw data pointed by image sequentially. +Data are printed as float numbers and the function interprets the input format +properly. If input is complex then for each pixel the real part followed +by the imaginary is printed. It can be used for debugging preferably on small +images. If input is uchar then the printed values are integers. + +.B im_printlines +printes in the standard error output the no of line followed by CR, followed +by the line pixel values. For each pixel, the x location +followed by the pixel value(s) is printed. The location and the pixels value(s) +are separated by a tab. Each pixel is printed on a separate line. +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 22/04/1991 diff --git a/libsrc/iofuncs/man3/im_demand_hint.3 b/libsrc/iofuncs/man3/im_demand_hint.3 new file mode 100644 index 00000000..0b7623e0 --- /dev/null +++ b/libsrc/iofuncs/man3/im_demand_hint.3 @@ -0,0 +1,132 @@ +.TH IM_IOCHECK 3 "11 April 1990" +.SH NAME +im_demand_hint \- hint on demand style for im_generate(3) +.SH SYNOPSIS +#include +.br +#include + +int im_demand_hint( im, hint, in1, in2, ..., NULL ) +.br +IMAGE *im, *in1, *in2, ...; +.br +im_demand_type hint; + +int im_demand_hint_array( im, hint, in ) +.br +IMAGE *im, **in; +.br +im_demand_type hint; +.SH DESCRIPTION +.B im_demand_hint(3) +suggests to im_generate(3) the sorts of demand with which this image processing +operation would be happiest. + +.B im +is the image this operation is generating. +.B hint +is the demand style this operation would like (see below), and +.B in1 ... +is a NULL-terminated list of the image upon which this output image directly +depends, that is, the images which this operation will call im_prepare(3) for. + +This list of parent images is necessary, as im_demand_hint(3) needs to know +what demand style this operation's ancestors have requested. If an ancestor of +this operation has specified a very restrictive demand style, then this +operation must fall back to that restrictive style and ignore the hint given +in this call to im_demand_hint(3). + +VIPS currently supports three demand styles. More may be added in the future. +These demand styles are given below in order of increasing restrictiveness. +When demanding output from a pipeline, im_generate(3) will use the most +restrictive of the styles requested by the operations in the pipeline. + +IM_THINSTRIP +.br +This operation would like to output strips the width of the image and a few +pels high. This is option suitable for point-to-point operations, such as +those in the arithmetic package. + +This option is only efficient for cases where each output pel depends upon the +pel in the corresponding position in the input image. + +IM_FATSTRIP +.br +This operation would like to output strips the width of the image and as high +as possible. This option is suitable for area operations which do not +violently transform coordinates, such as im_conv(3). + +IM_SMALLTILE +.br +This is the most general demand format, and is the default. Output is +demanded in small (around 100x100 pel) sections. This style works reasonably +efficiently, even for bizzare operations like 45 degree rotate. + +IM_ANY +.br +This image is not being demand-read from a disc file (even indirectly) so any +demand style is OK. It's used for things like +.B im_black(3) +where the pixels are calculated. + +.B +im_demand_hint_array(3) +works exactly as im_demand_hint(3), but expects a pointer to a NULL-terminated +array of parent images as its third argument. You may use +im_allocate_input_array(3), if you wish, to build this structure. + +As an example, here is part of the code for im_invert(3). In this operation, +each output pel depends upon the corresponding input pel. In other words, +there is no coordinate transformation in im_prepare(3). This style of +operation is most efficient with IM_THINSTRIP IO. + +int im_invert( IMAGE *in, IMAGE *out ) +.br +{ +.br + if( in->Coding != NOCODING ) { +.br + im_errormsg( "im_invert: input coded" ); +.br + return( -1 ); +.br + } +.br + if( in->BandFmt != FMTUCHAR ) { +.br + im_errormsg( "im_invert: input not UCHAR" ); +.br + return( -1 ); +.br + } +.br + if( im_piocheck( in, out ) ) +.br + return( -1 ); +.br + if( im_cp_desc( out, in ) ) +.br + return( -1 ); +.br + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) +.br + return( -1 ); +.br + if( im_generate( out, +.br + im_start_one, inv_gen, im_stop_one, in, NULL ) ) +.br + return( -1 ); +.br + return( 0 ); +.br +} + +.SH RETURN VALUE +All functions returns 0 on success and non-zero on error. +.SH SEE ALSO +im_generate(3), im_prepare(3). +.SH COPYRIGHT +National Gallery +.SH AUTHOR +J. Cupitt \- 3/9/93 diff --git a/libsrc/iofuncs/man3/im_demand_hint_array.3 b/libsrc/iofuncs/man3/im_demand_hint_array.3 new file mode 100644 index 00000000..124bcb4a --- /dev/null +++ b/libsrc/iofuncs/man3/im_demand_hint_array.3 @@ -0,0 +1 @@ +.so man3/im_demand_hint.3 diff --git a/libsrc/iofuncs/man3/im_diag.3 b/libsrc/iofuncs/man3/im_diag.3 new file mode 100644 index 00000000..7db4b63b --- /dev/null +++ b/libsrc/iofuncs/man3/im_diag.3 @@ -0,0 +1 @@ +.so man3/im_error.3 diff --git a/libsrc/iofuncs/man3/im_error.3 b/libsrc/iofuncs/man3/im_error.3 new file mode 100644 index 00000000..884328a5 --- /dev/null +++ b/libsrc/iofuncs/man3/im_error.3 @@ -0,0 +1,84 @@ +.TH IM_ERRORMSG 3 "22 April 1991" +.SH NAME +im_error_buffer, im_verror, im_error, im_error_clear, im_warn, im_diag, +error_exit \- handle error messages from VIPS +.SH SYNOPSIS +.B #include +.B #include + +.B const char *im_error_buffer( void ) + +.B void im_verror( const char *domain, const char *fmt, va_list ap ) + +.B void im_error( const char *domain, const char *fmt, ... ) + +.B void im_error_system( int errno, const char *domain, const char *fmt, ... ) + +.B void im_error_clear() + +.B void im_warn( const char *domain, const char *fmt, ... ) + +.B void im_diag( const char *domain, const char *fmt, ... ) + +.B void error_exit( const char *fmt, ... ) + +.SH DESCRIPTION +.B im_error(3) +formats its arguments as printf and appends the string, with a newline, to +the error buffer. The +.B domain +argument indicates the error source and should not be marked for translation. + +For example, the call: + + im_error( "mystuff", _( "bad argument %d" ), a ); + +might appear in the error buffer as: + + mystuff: bad argument 12 + +.B im_verror(3) +works exactly as +.B im_error(3) +but takes stdarg arguments. + +.B im_error_system(3) +works exactly as +.B im_error(3) +but additionally will translate and append a system error code. + +.B im_error_buffer(3) +returns a pointer to the start of the error buffer. + +.B im_error_clear(3) +empties the error buffer. + +.B error_exit(3) +formats its arguments as printf and sends the result to the error output, +together with the contents of the error log, before terminating with an error +status. + +.B im_warn(3) +works as +.B im_error(), +but output is sent to the list of warnings. + +If an environment variable IM_WARNING exists, messages are suppressed. +Warnings should be used for non-critical recoverable errors such as values +being clipped. + +.B im_diag(3) +works as +.B im_error(), +but output is sent to the list of diagnosic errors. + +If an environment variable IM_DIAGNOSTICS exists, messages are suppressed. +Diagnostics should be used to give extra feedback about the result of the +operation. +.SH SEE ALSO +error_exit(3), im_intro(3). +.SH COPYRIGHT +.br +Birkbeck College +.SH AUTHOR +N. Dessipris \- 22/04/1991 diff --git a/libsrc/iofuncs/man3/im_error_buffer.3 b/libsrc/iofuncs/man3/im_error_buffer.3 new file mode 100644 index 00000000..7db4b63b --- /dev/null +++ b/libsrc/iofuncs/man3/im_error_buffer.3 @@ -0,0 +1 @@ +.so man3/im_error.3 diff --git a/libsrc/iofuncs/man3/im_error_clear.3 b/libsrc/iofuncs/man3/im_error_clear.3 new file mode 100644 index 00000000..7db4b63b --- /dev/null +++ b/libsrc/iofuncs/man3/im_error_clear.3 @@ -0,0 +1 @@ +.so man3/im_error.3 diff --git a/libsrc/iofuncs/man3/im_free.3 b/libsrc/iofuncs/man3/im_free.3 new file mode 100644 index 00000000..0be9c41f --- /dev/null +++ b/libsrc/iofuncs/man3/im_free.3 @@ -0,0 +1 @@ +.so man3/im_malloc.3 diff --git a/libsrc/iofuncs/man3/im_generate.3 b/libsrc/iofuncs/man3/im_generate.3 new file mode 100644 index 00000000..0b183a4e --- /dev/null +++ b/libsrc/iofuncs/man3/im_generate.3 @@ -0,0 +1,215 @@ +.TH IM_GENERATE 3 "11 April 1993" +.SH NAME +im_generate, im_start_one, im_stop_one, im_allocate_input_array, +im_start_many, im_stop_many \- generate image pixels +.SH SYNOPSIS +.B #include +.br +.B #include + +void *im_start_one( out, in ) +.br +IMAGE *out, *in; + +int im_stop_one( reg ) +.br +REGION *reg; + +IMAGE **im_allocate_input_array( IMAGE *out, ... ) + +void *im_start_many( out, in ) +.br +IMAGE *out, **in; + +int im_stop_many( REGION **out ) +.br +REGION **out; + +int im_generate( im, + start_fn, gen_fn, stop_fn, void *a, void *b ) +.br +IMAGE *im; +.br +void *(*start_fn)(); +.br +int (*gen_fn)(); +.br +int (*stop_fn)(); +.br +void *a, void *b; + +where, typically, + +void *start_fn( im, a, b ) +.br +IMAGE *im; +.br +void *a, *b; + +int gen_fn( or, seq, a, b ) +.br +REGION *or; +.br +void *seq; +.br +void *a, *b; + +int stop_fn( seq, a, b ) +.br +void *seq; +.br +void *a, *b; +.SH DESCRIPTION +.B im_generate(3), +with its supporting convenience functions, is used for +PIO image output. See also +.B im_wrapone(3) +for an easy alternative to +.B im_generate(3) +for simple image +processing operations. + +.B im_start_one(3) +and +.B im_stop_one(3) +are convenience functions, useful for +simple one-image-in, one-image-out operations. +.B im_start_one(3) +assumes the +first of the two user arguments (a, above) is the input image. It creates a +REGION on this image and returns a pointer to the region as a sequence value. + +.B im_stop_one(3) +assumes the sequence value is a REGION pointer, and frees it. + +.B im_allocate_input_array(3) takes as arguments the output image and a list of +input images, terminated with a NULL. It allocates a NULL-terminated array to +hold the images, and attaches a close callback to the output image to free +that array. Example: + + IMAGE *in, *in2, *in3, *in4; + IMAGE **arry; + + if( !(arry = im_allocate_input_array( out, + in1, in2, in3, in4, NULL )) ) + return( -1 ); + +builds the structure + + IMAGE *arry[] = { in1, in2, in3, in4, NULL }; + +and makes sure it will be freed. + +.B im_start_many(3) +and +.B im_stop_many(3) +work exactly as +.B im_start_one(3) +and +.B im_stop_one(3), +but with NULL-terminated arrays of IMAGEs and REGIONs. +They are useful for many-images-in, one-image-out operations. +.B im_start_many(3) +assumes that the first of the two user arguments is a pointer +to a NULL-terminates array of IMAGEs. It builds and returns as the sequence +value a NULL-terminated array of REGIONs. + +.B im_stop_many(3) +assumes the sequence value is a pointer to a NULL-terminated +array of REGIONs. It frees all the regions in turn. See +.B im_add(3) +for an +example of this pair of functions in action. + +.B im_generate(3) +looks at the type of im and acts accordingly: + + IM_PARTIAL: the start, process and stop functions are attached to the +region, and im_generate returns immediately. See +.B im_prepare(3). + + IM_SETBUF: memory for the output image is created and sequences +started to fill it. It is an error to write to the same buffer twice. + + IM_MMAPINRW: sequences are started, and asked to fill the image in patches. + + IM_OPENOUT: The output file is created and a header written to disc. A +buffer +large enough to hold GENERATE_TILE_HEIGHT complete horizontal lines is +created, and sequences started to fill this buffer. When the buffer has been +filled, the whole set of lines are flushed to disc in a single write(2) +operation, and work starts on the next set of lines. + +Any other image type is an error. +.B im_generate(3) +returns 0 for complete +success, and non-zero on failure. + + static int + wombat_gen( or, ir, in ) + REGION *or, *ir; + IMAGE *in; + { + ... process! + + return( 0 ); + } + + int + im_wombat( in, out ) + IMAGE *in, *out; + { + if( im_iocheck( in, out ) ) + return( -1 ); + + ... check parametersm check image descriptors + ... for type-compatibility, etc. etc. + + if( im_cp_desc( out, in ) ) + return( -1 ); + + ... set fields in out for the type of image you + ... wish to write + + if( im_generate( out, + im_start_one, wombat_gen, im_stop_one, + in, NULL ) ) + return( -1 ); + + return( 0 ); + } + +See also the source to +.B im_invert(3), +.B im_exptra(3), +and, if you are brave, +.B im_conv(3) +or +.B im_add(3). + +On machines with several CPUs, +.B im_generate(3) +and +.B im_iterate(3) +automatically parallelise programs. You can set the desired +concurrency level with the environment variable IM_CONCURRENCY, for example + + example% export IM_CONCURRENCY=2 + example% lintra 2.0 fred.v 0.0 fred2.v + +will run lintra with enough concurrency to keep 2 CPUs fully occupied. +If IM_CONCURRENCY is not set, then it defaults to 1. See also +im_concurrency_set(3). + +Most programs which use VIPS will also let you use the command-line argument +--vips-concurrency to set parallelisation, see im_get_option_group(3). + +.SH COPYRIGHT +National Gallery, 1993 +.SH SEE ALSO +im_wrapone(3), im_add_eval_callback(3), im_iterate(3), im_piocheck(3), +im_concurrency_set(3), +im_get_option_group(3), +`VIPS manual,' in accompanying documentation. +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_get_option_group.3 b/libsrc/iofuncs/man3/im_get_option_group.3 new file mode 100644 index 00000000..73c527a0 --- /dev/null +++ b/libsrc/iofuncs/man3/im_get_option_group.3 @@ -0,0 +1 @@ +.so man3/im_init_world.3 diff --git a/libsrc/iofuncs/man3/im_guess_prefix.3 b/libsrc/iofuncs/man3/im_guess_prefix.3 new file mode 100644 index 00000000..785d56b6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_guess_prefix.3 @@ -0,0 +1,29 @@ +.TH IM_VIPSHOME 3 "8 March 2001" +.SH NAME +im_guess_prefix \- try to guess install directory +.SH SYNOPSIS +.B #include + +.B const char *im_guess_prefix( const char *argv0, +.B const char *env_name ) + +.SH DESCRIPTION +.B im_guess_prefix(3) +tries to guess the install directory. You should pass in the value of +argv[0] (the name your program was run as) as a clue to help it out, plus +the name of the environment variable you let the user override your package +install area with (eg. "VIPSHOME"). + +On success, +.B im_guess_prefix(3) +returns the prefix it discovered, and as a side effect, sets the environment +variable (if it's not set). + +.B im_guess_prefix(3) +is useful for programs in the VIPS distribution and contrib which need to be +able to find data files. + +Don't free the return string! + +.SH RETURN VALUE +The function returns NULL on failure. diff --git a/libsrc/iofuncs/man3/im_header.3 b/libsrc/iofuncs/man3/im_header.3 new file mode 100644 index 00000000..88c4a435 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header.3 @@ -0,0 +1,57 @@ +.TH IM_HEADER 3 "7 May 2002" +.SH NAME +im_header_int, im_header_double, im_header_string, im_header_get, +im_header_get_type, im_header_map \- read fields from the image header +.SH SYNOPSIS +#include + +int im_header_int( IMAGE *image, const char *field, int *out ) +.br +int im_header_double( IMAGE *image, const char *field, double *out ) +.br +int im_header_string( IMAGE *image, const char *field, char **out ) +.br +GType im_header_get_type( IMAGE *im, const char *field ); +.br +int im_header_get( IMAGE *im, const char *field, GValue *value_copy ); +.br +typedef void *(*im_header_map_fn)( IMAGE *, + const char *, GValue *, void *, void * ); +.br +void *im_header_map( IMAGE *im, + im_header_map_fn fn, void *a, void *b ); + +.SH DESCRIPTION +.B im_header_int(3) +reads the value of an integer header field. These are +"Xsize", "Ysize", "Bands", "Bbits", "BandFmt", "Coding", and "Type", or any +integer meta field. + +.B im_header_double(3) +reads the value of the integer header fields. These are +"Xres", and "Yres", or any double meta field. + +.B im_header_string(3) +reads the value of the integer header fields. These are +"Hist", and "filename" or any string meta field. + +.B im_header_get_type(3) +returns the GType (eg. G_TYPE_INT) for a field. It returns zero if the field +does not exist. It does not set +.B im_error(3), +so it's useful for test for a field's existence. + +.B im_header_get(3) +fills the GValue with a copy of the field value, if the field exists. The +value should be zeroed but otherwise uninitialised. The value should be unset +once the user has finished with it. + +.B im_header_map(3) +maps a function over all header fields, presenting the value of each field as +a GValue. Return non-NULL from the map function to stop iteration early. It +maps over the builtin fields first, then any meta fields. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_close(3), im_open(3). diff --git a/libsrc/iofuncs/man3/im_header_double.3 b/libsrc/iofuncs/man3/im_header_double.3 new file mode 100644 index 00000000..eec90686 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header_double.3 @@ -0,0 +1 @@ +.so man3/im_header.3 diff --git a/libsrc/iofuncs/man3/im_header_get.3 b/libsrc/iofuncs/man3/im_header_get.3 new file mode 100644 index 00000000..eec90686 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header_get.3 @@ -0,0 +1 @@ +.so man3/im_header.3 diff --git a/libsrc/iofuncs/man3/im_header_get_type.3 b/libsrc/iofuncs/man3/im_header_get_type.3 new file mode 100644 index 00000000..eec90686 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header_get_type.3 @@ -0,0 +1 @@ +.so man3/im_header.3 diff --git a/libsrc/iofuncs/man3/im_header_int.3 b/libsrc/iofuncs/man3/im_header_int.3 new file mode 100644 index 00000000..eec90686 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header_int.3 @@ -0,0 +1 @@ +.so man3/im_header.3 diff --git a/libsrc/iofuncs/man3/im_header_map.3 b/libsrc/iofuncs/man3/im_header_map.3 new file mode 100644 index 00000000..eec90686 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header_map.3 @@ -0,0 +1 @@ +.so man3/im_header.3 diff --git a/libsrc/iofuncs/man3/im_header_string.3 b/libsrc/iofuncs/man3/im_header_string.3 new file mode 100644 index 00000000..eec90686 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header_string.3 @@ -0,0 +1 @@ +.so man3/im_header.3 diff --git a/libsrc/iofuncs/man3/im_histlin.3 b/libsrc/iofuncs/man3/im_histlin.3 new file mode 100644 index 00000000..cd5bc3e0 --- /dev/null +++ b/libsrc/iofuncs/man3/im_histlin.3 @@ -0,0 +1,34 @@ +.TH IM_HISTORY 3 "22 April 1991" +.SH NAME +im_histlin, im_updatehist, im_history_get \- manage image history +.SH SYNOPSIS +.B #include + +int im_histlin( IMAGE *im, const char *fmt, ... ); +.br +int im_updatehist( IMAGE *out, const char *name, int argc, char *argv[] ); +.br +const char *im_history_get( IMAGE *im ); + +.br +.SH DESCRIPTION +.B im_histlin(3) +formats its arguments as printf(3), appends " # time-and-date", and appends +the whole line of text to the image history. The string is typically the +command-line action that would be required to do whatever it is that you've +just done to the image. + +.B im_updatehist(3) +is given a standard argc/argv, formats them appropriately, and calls +im_histlin(3) for you. Note that the program name is passed separately. + +.B im_history_get(3) +returns the entire history of an image as a single C string, one action per +line. No need to free, but you mustn't modify either. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_cp_desc(3). +.SH COPYRIGHT +Imperial College, London, 2007. diff --git a/libsrc/iofuncs/man3/im_history_get.3 b/libsrc/iofuncs/man3/im_history_get.3 new file mode 100644 index 00000000..d421d4b0 --- /dev/null +++ b/libsrc/iofuncs/man3/im_history_get.3 @@ -0,0 +1 @@ +.so man3/im_histlin.3 diff --git a/libsrc/iofuncs/man3/im_image.3 b/libsrc/iofuncs/man3/im_image.3 new file mode 100644 index 00000000..597a5913 --- /dev/null +++ b/libsrc/iofuncs/man3/im_image.3 @@ -0,0 +1 @@ +.so man3/im_binfile.3 diff --git a/libsrc/iofuncs/man3/im_image_sanity.3 b/libsrc/iofuncs/man3/im_image_sanity.3 new file mode 100644 index 00000000..f29d2047 --- /dev/null +++ b/libsrc/iofuncs/man3/im_image_sanity.3 @@ -0,0 +1,19 @@ +.TH IM_IMAGE_SANITY 3 "Feb 2001" +.SH NAME +im_image_sanity \- check image descriptors for sanity +.SH SYNOPSIS +.B #include + +int im_image_sanity( IMAGE *im ) + +.SH DESCRIPTION +.B im_image_sanity(3) +performs a few simple checks on the IMAGE for bad fields. If it finds a +problem, it returns -1, serts im_errorstring, and prints a warning message. + +It is called from various places within VIPS to try to catch errors early. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_incheck(3). diff --git a/libsrc/iofuncs/man3/im_incheck.3 b/libsrc/iofuncs/man3/im_incheck.3 new file mode 100644 index 00000000..e0092c33 --- /dev/null +++ b/libsrc/iofuncs/man3/im_incheck.3 @@ -0,0 +1 @@ +.so man3/im_iocheck.3 diff --git a/libsrc/iofuncs/man3/im_init.3 b/libsrc/iofuncs/man3/im_init.3 new file mode 100644 index 00000000..df1539fe --- /dev/null +++ b/libsrc/iofuncs/man3/im_init.3 @@ -0,0 +1,22 @@ +.TH IM_INIT 3 "11 April 1990" +.SH NAME +im_init \- make an IMAGE descriptor +.SH SYNOPSIS +#include + +IMAGE *im_init( char *filename ) +.SH DESCRIPTION +im_init(3) allocates space for an IMAGE descriptor, sets all fields to +initial values, and returns the descriptor. A copy is made of the filename +argument. The IMAGE returned by im_init(3) should be passed to im_close(3) to +free it. + +This function is used internally by VIPS and should not be called by users. +.SH SEE ALSO +im_mmapin(3), im_openin(3), im_setbuf(3), im_close(3). +.SH COPYRIGHT +Birkbeck College and the National Gallery (c) 1994 +.SH AUTHOR +N. Dessipris \- 11/04/1990 +.br +J.Cupitt \- 23/2/94 diff --git a/libsrc/iofuncs/man3/im_init_world.3 b/libsrc/iofuncs/man3/im_init_world.3 new file mode 100644 index 00000000..4374f029 --- /dev/null +++ b/libsrc/iofuncs/man3/im_init_world.3 @@ -0,0 +1,47 @@ +.TH IM_INIT_WORLD 3 "11 April 1990" +.SH NAME +im_init_world, im_get_option_group \- start up VIPS +.SH SYNOPSIS +#include + +int im_init_world( const char *argv0 ) +.br +GOptionGroup *im_get_option_group( void ); +.SH DESCRIPTION +.B im_init_world(3) +starts up the VIPS library. It: + + - initialises any libraries that VIPS is using, including GObject + - starts up the threading system + - guesses where the VIPS data files are and sets up i18n + - loads any plugins + +The +.B argv0 +argument is the value of +.B argv[0] +your program was passed by the host operating system. VIPS uses this with +.B im_guess_prefix(3) +to try to find the various VIPS data files. + +.SH EXAMPLE + + int + main( int argc, char **argv ) + { + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + return( 0 ); + } + +.B im_get_option_group(3) +returns a +.B GOptionGroup +containing various VIPS command-line options. It can be used with GOption to +help parse argc/argv. + +.SH SEE ALSO +im_guess_prefix(3), GOption(3) +.SH COPYRIGHT +Birkbeck College and the National Gallery (c) 1994 diff --git a/libsrc/iofuncs/man3/im_initdesc.3 b/libsrc/iofuncs/man3/im_initdesc.3 new file mode 100644 index 00000000..a531cf32 --- /dev/null +++ b/libsrc/iofuncs/man3/im_initdesc.3 @@ -0,0 +1,45 @@ +.TH IM_INITDESC 3 "22 April 1991" +.SH NAME +im_initdesc \- initialises an image descriptor to specific values +.SH SYNOPSIS +#include + +void im_initdesc( IMAGE *image, + int xsize, int ysize, + int bands, int bandbits, int bandfmt, + int coding, int type, + float xres, float yres, + float xo, float yo ) +.SH DESCRIPTION +.B im_initdesc(3) +initialises the image descriptor according to the entered values. +More specifically the Xsize, Ysize, Bands, BandFmt, Coding, Type, +Xres, Yres, Xoffset and Yoffset members of the descriptor are initialised by the +correspondingly entered arguments. The members fd, baseaddr, data and +filename are not handled by this function. The order of the args is +the same as in vips/vips.h. + +The +.B bandbits +parameter is deprecated and ignored by this function: you can always pass zero. + +Example: make a 512 by 512 one-band memory buffer. + + if( !(im = im_open( "temp", "t" )) ) + /* Error ... + im_initdesc( im, + 512, 512, + 1, 0, FMTUCHAR, + NOCODING, B_W, + 1.0, 1.0, + 0, 0 ); + if( im_setupout( im ) ) + /* Error ... + +.SH ALSO +im_open(3), im_setupout(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 22/04/1991 diff --git a/libsrc/iofuncs/man3/im_invalidate.3 b/libsrc/iofuncs/man3/im_invalidate.3 new file mode 100644 index 00000000..487e9587 --- /dev/null +++ b/libsrc/iofuncs/man3/im_invalidate.3 @@ -0,0 +1,16 @@ +.TH IM_INVALIDATE 3 "5 December 2006" +.SH NAME +im_invalidate \- flush caches related to an image +.SH SYNOPSIS +.B #include + +.B void im_invalidate( IMAGE *im ) + +.SH DESCRIPTION +.B im_invalidate(3) +marks all caches related to the image as invalid and requring recalculation. +It needs to be called if an image changes after being made: for example, after +a paint action, or perhaps after a new frame has arrived from a video source. + +.SH SEE\ ALSO +im_prepare(3), im_region_buffer(3). diff --git a/libsrc/iofuncs/man3/im_iocheck.3 b/libsrc/iofuncs/man3/im_iocheck.3 new file mode 100644 index 00000000..921fa570 --- /dev/null +++ b/libsrc/iofuncs/man3/im_iocheck.3 @@ -0,0 +1,50 @@ +.TH IM_IOCHECK 3 "11 April 1990" +.SH NAME +im_incheck, im_outcheck, im_iocheck \- checks image descriptors +for WIO +.SH SYNOPSIS +.B #include + +int im_incheck( in ) +.br +IMAGE *in; + +int im_outcheck( out ) +.br +IMAGE *out; + +.B int im_iocheck( in, out) +.br +.B IMAGE *in, *out; + +.SH DESCRIPTION +im_incheck(3) +checks that an image descriptor is suitable for WIO input (ie. all of +its pels can be found from im\-\>data). If possible, it transforms the +descriptor to make WIO input ok using the following rules: + +IM_PARTIAL: the descriptor is magically turned into an IM_SETBUF descriptor. +Memory is allocated and the image generated into that. The old partial +callbacks are closed down, and the descriptor reformed as a IM_SETBUF. + +IM_OPENOUT: if the descriptor has been written to, it is automatically +`rewound,` that is, it is closed and reopened as an IM_MMAPIN descriptor. + +IM_SETBUF: just checks that the descriptor has been written to. + +See the manual page for im_setupout(3) for a skeleton program. + +im_outcheck(3) checks that a descriptor is suitable for WIO output with +im_writeline(3). If it sees an IM_PARTIAL image, it turns it magically into an +IM_SETBUF image. + +im_iocheck(3) simply calls in_incheck(3) for image in and im_outcheck(3) for +image out. +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_open(3), im_cp_desc(3), im_setupout(3), im_makerw(3). +.SH COPYRIGHT +National Gallery +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_isMSBfirst.3 b/libsrc/iofuncs/man3/im_isMSBfirst.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isMSBfirst.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_iscomplex.3 b/libsrc/iofuncs/man3/im_iscomplex.3 new file mode 100644 index 00000000..5d330032 --- /dev/null +++ b/libsrc/iofuncs/man3/im_iscomplex.3 @@ -0,0 +1,121 @@ +.TH IM_ISINT 3 "11 April 1990" +.SH NAME +im_isint, im_ispng, im_isuint, im_isfloat, im_isscalar, im_iscomplex, im_istiff, +istifftiled, im_isjpeg, im_isfile, im_ispartial, im_isvips, im_isMSBfirst, +im_fexists, im_amiMSBfirst \- classify image types +.SH SYNOPSIS +.B #include + +int im_isMSBfirst( im ) +.br +IMAGE *im; + +int im_amiMSBfirst( void ) + +int im_fexists( const char *filename, ... ) + +int im_istiff( filename ) +.br +char *filename; + +int im_istifftiled( filename ) +.br +char *filename; + +int im_isjpeg( filename ) +.br +char *filename; + +int im_ispng( filename ) +.br +char *filename; + +int im_isppm( filename ) +.br +char *filename; + +int im_isvips( filename ) +.br +char *filename; + +int im_isfile( im ) +.br +IMAGE *im; + +int im_ispartial( im ) +.br +IMAGE *im; + +int im_isint( im ) +.br +IMAGE *im; + +int im_isuint( im ) +.br +IMAGE *im; + +int im_isfloat( im ) +.br +IMAGE *im; + +int im_isscalar( im ) +.br +IMAGE *im; + +int im_iscomplex( im ) +.br +IMAGE *im; + +.SH DESCRIPTION +im_fexists(3) returns non-zero if the named file exists and is readable. Args +as printf(3). + +im_istiff(3) tests a file for tiff-ness. + +im_istifftiled(3) tests a file for tiff-tiled-ness. + +im_isjpeg(3) tests a file for jpeg-ness. + +im_ispng(3) tests a file for png-ness. + +im_isppm(3) tests a file for PPM/PGM/PBM-ness. + +im_isvips(3) tests a file for vips-ness. + +im_isMSBfirst(3) returns true (1) if the file is in most-significant-byte first +form. This is the byte order used on the SPARC architecture, and others. It +returns false (0)for intel-order images. + +im_amiMSBfirst(3) returns true (1) if this processor is MSB first. + +im_isfile(3) returns true if the descriptor corresponds to some disc object, +that is, was opened with modes "r", "rw" or "w". + +im_ispartial(3) returns true if the descriptor is partial, +that is, was opened with mode "p". + +The rest of these functions test im\-\>BandFmt, returning logical truth +(non-zero) if BandFmt is one of a number of possibles, and returning zero if +it is not. + +im_isint(3) (is integer type) returns true if im\-\>BandFmt is one of FMTUCHAR, +FMTCHAR, FMTUSHORT, FMTSHORT, FMTUINT or FMTINT. + +im_isuint(3) (is unsigned integer type) returns true if im\-\>BandFmt is one of +FMTUCHAR, FMTUSHORT or FMTUINT. + +im_isfloat(3) (is floating point type) returns true if im\-\>BandFmt is one of +FMTFLOAT or FMTDOUBLE. + +im_isscalar(3) (is scalar type) returns true if im\-\>BandFmt is one of FMTUCHAR, +FMTCHAR, FMTUSHORT, FMTSHORT, FMTUINT, FMTINT, FMTFLOAT or FMTDOUBLE. + +im_iscomplex(3) (is complex type) returns true if im\-\>BandFmt is one of +FMTCOMPLEX or FMTDPCOMPLEX. + +.SH COPYRIGHT +National Gallery, 1993 +.SH SEE ALSO +im_tiff2vips(3), im_jpeg2vips(3) +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_isfile.3 b/libsrc/iofuncs/man3/im_isfile.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isfile.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isfloat.3 b/libsrc/iofuncs/man3/im_isfloat.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isfloat.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isint.3 b/libsrc/iofuncs/man3/im_isint.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isint.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isjpeg.3 b/libsrc/iofuncs/man3/im_isjpeg.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isjpeg.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_ispartial.3 b/libsrc/iofuncs/man3/im_ispartial.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_ispartial.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_ispng.3 b/libsrc/iofuncs/man3/im_ispng.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_ispng.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isppm.3 b/libsrc/iofuncs/man3/im_isppm.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isppm.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isscalar.3 b/libsrc/iofuncs/man3/im_isscalar.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isscalar.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_istifftiled.3 b/libsrc/iofuncs/man3/im_istifftiled.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_istifftiled.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isuint.3 b/libsrc/iofuncs/man3/im_isuint.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isuint.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isvips.3 b/libsrc/iofuncs/man3/im_isvips.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isvips.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_iterate.3 b/libsrc/iofuncs/man3/im_iterate.3 new file mode 100644 index 00000000..1735b64e --- /dev/null +++ b/libsrc/iofuncs/man3/im_iterate.3 @@ -0,0 +1,72 @@ +.TH IM_ITERATE 3 "30 October 1992" +.SH NAME +im_iterate \- PIO input from image +.SH SYNOPSIS +.B #include +.br +.B #include + +int im_iterate( im, start_fn, scan_fn, stop_fn, a, b ) +.br +IMAGE *im; +.br +void *(*start_fn)(); +.br +int (*scan_fn)(); +.br +int (*stop_fn)(); +.br +void *a, *b; + +where, typically, + +void *start_fn( im, a, b ) +.br +IMAGE *im; +.br +void *a, *b; + +int scan_fn( or, seq, a, b ) +.br +REGION *or; +.br +void *seq; +.br +void *a, *b; + +int stop_fn( seq, a, b ) +.br +void *seq; +.br +void *a, *b; +.SH DESCRIPTION +im_iterate(3) is used for PIO image input. See `VIPS Library Programmers' +guide,' in the accompanying documentation, for an introduction to this +function. + +im_iterate(3) makes one or more regions on im, and starts one or more sequences +running over the image. im_iterate(3) guarantees that + + - scan_fn() will see each of the pels in im exactly once + - start_fn() and stop_fn() are both exclusive + +See the guide, the man page for im_generate(3), and the source to im_deviate(3) +for examples. + +On machines with SVR4 threads and several CPUs, im_generate(3) and +im_iterate(3) automatically parallelise programs. You can set the desired +concurrency level with the environment variable IM_CONCURRENCY, for example + + example% setenv IM_CONCURRENCY 2 + example% stats fred.v + +will run stats with enough concurrency to keep 2 CPUs fully occupied. +If IM_CONCURRENCY is not set, then it defaults to 1. +.SH RETURN VALUE +All functions return 0 on success and non-zero on error. +.SH SEE ALSO +im_generate(3). +.SH COPYRIGHT +National Gallery, 1993 +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_list_add.3 b/libsrc/iofuncs/man3/im_list_add.3 new file mode 100644 index 00000000..1f901b08 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_add.3 @@ -0,0 +1,229 @@ +.TH IM_LIST_ADD 3 "2 May 1991" +.SH NAME +im_list_add, im_list_len, im_list_pos, im_list_member, im_list_append, +im_list_remove, +im_list_eq, im_list_map, im_list_map_rev, im_list_fold, im_list_fix, +im_list_free, im_list_insert \- linked list functions +.SH SYNOPSIS +#include +.br +#include + +typedef struct list_type { +.br + struct list_type *next; +.br + void *this; +.br +} List; + +#define hd(L) ((L)->this) +.br +#define tl(L) ((L)->next) + +typedef void *(*im_list_map_fn)( void *, void *, void * ); +.br +typedef void (*im_list_free_fn)( void *, void *, void * ); +.br +typedef void *(*im_list_fold_fn)( void *, void *, +.br + void *, void * ); + +int im_list_len( List *l ); +.br +int im_list_pos( List *l, void *t ); +.br +int im_list_member( List *l, void *t ); +.br +void *im_list_index( List *l, int n ); +.br +int im_list_add( List **base, void *new ); +.br +int im_list_insert( List **base, void *new, void *old ); +.br +int im_list_append( List **base, void *new ); +.br +int im_list_remove( List **base, void *t ); + +void *im_list_eq( void *a, void *b ); +.br +void *im_list_map( List *l, +.br + im_list_map_fn fn, void *a, void *b ); +.br +void *im_list_map_rev( List *l, +.br + im_list_map_fn fn, void *a, void *b ); +.br +void *im_list_fold( List *l, +.br + void *start, im_list_fold_fn fn, void *a, void *b ); +.br +void im_list_fix( List **base, +.br + im_list_map_fn fn, void *a, void *b ); +.br +void im_list_free( List **base, +.br + im_list_free_fn fn, void *a, void *b ); + +.SH DESCRIPTION +Manipulate linked lists in various ways. These functions are heavily used by +the VIPS IO system; use them yourself if you like. VIPS lists store lists of +void * pointers - use casts if you want to store some other type. Note that +if sizeof( your object ) != sizeof( void * ), you will be in trouble! + +All are based on the List type (see above). An empty list is a NULL pointer, a +one element list is a pointer to a List struct, whose this field contains a +pointer to the object in the list and whose next field is NULL. Macros hd(3) +and tl(3) (head and tail) return this and next respectively. + +im_list_len(3) returns the number of elements in list l. im_list_pos(3) searches +list l for stored object t, returning an index. The first list element has +index zero. im_list_pos(3) returns -1 for not present. im_list_index(3) returns +the item at position n in the list, or NULL for index out of range. +im_list_member(3) returns non-zero if the list contains the element. + +im_list_map(3) applies a void * valued function to every element in a list, +running from beginning to end. If the function returns NULL, im_list_map +continues with the next element. If the function returns non-NULL, +im_list_map(3) abandons the map and returns immediately, returning the value +the user function returned. If the list is empty, im_list_map(3) returns NULL. + +The two extra arguments a and b are carried around for you by VIPS and fed +into each call of the function. They are useful for communicating context +information. + +You can use im_list_map to implement many kinds of list search/apply operation. +VIPS supplies the function im_list_eq(3) which tests two void * pointers for +equality, returning the pointer if they match, and returning NULL otherwise. + +Example: search a list for an object + + im_list_map( list, +.br + (im_list_map_fn) im_list_eq, object, NULL ); + +This could also be written as + + List *p; + + for( p = list; p; p = tl( p ) ) +.br + if( object == hd( p ) ) +.br + break; + +I prefer the first. + +im_list_map_rev(3) behaves exactly as im_list_map(3), but applies the function +running from te end to the beginning of the list. It is much slower than +im_list_map(3) and should be used only in emergencies. + +im_list_fold(3) folds up a list with a dyadic function. If a list contains +[1,2], return fn( 2, fn( 1, start, a, b ), a, b ). If the list is empty, +return start. + +The two extra arguments a and b are carried around for you by VIPS and fed +into each call of the function. They are useful for communicating context +information. + +Example: find a pointer to the largest element in a list of ints (assume +sizeof(int) <= sizeof(void *)) + + max_pair( int *new, int *old ) +.br + { +.br + if( !old || *new > *old ) +.br + return( new ); +.br + else +.br + return( old ); +.br + } + + largest = im_list_fold( list, +.br + NULL, (im_list_map_fn) max_pair, NULL, NULL ); + +im_list_add(3) adds a new element to the head of a list. Since the head of the +list will move, you must pass in a *pointer* to your pointer to your old head. + +Example: make a list of the numbers 9-0 (assume sizeof(int) <= sizeof(void *)) + + int i; +.br + List *nlist = NULL; + + for( i = 0; i < 10; i++ ) +.br + im_list_add( &nlist, (void *) i ); + +im_list_insert(3) adds a new element to a list, placing it just before the +indicated old element. If the old element is not found, im_list_insert(3) +returns an error. + +im_list_append(3) appends a new element to the end of a list. This is much +slower than im_list_add(3), and should be avoided if possible. + +im_list_remove(3) removes the specified element from the list. Since the head +of the list may move, you must pass in a *pointer* to your pointer to your +old head. + +im_list_fix(3) finds the fixed-point of a list-altering function. It repeatedly +maps a function over the list until the function returns NULL. Note that, +since the list may be changing, you must pass in a *pointer* to the pointer +you store the list in. + +The two extra arguments a and b are carried around for you by VIPS and fed +into each call of the function. They are useful for communicating context +information. + +Example: remove all elements less than x from a list of numbers (assume +sizeof(int) <= sizeof(void *)) + + int * +.br + test_ele( int *n, List **base, int x ) +.br + { +.br + if( *n < x ) { +.br + im_list_remove( base, n ); +.br + return( base ); +.br + } +.br + else +.br + return( NULL ); +.br + } + + im_list_fix( &nlist, +.br + (im_list_map_fn) test_ele, &nlist, x ); + +im_list_free(3) frees the list, applying a user free function to every element +as it is freed. You may pass NULL instead of a pointer to a function, in which +case im_list_free(3) will just free the memory used by the list nodes. + +The two extra arguments a and b are carried around for you by VIPS and fed +into each call of the function. They are useful for communicating context +information. + +.SH RETURN VALUE +The functions returns a 0 or a pointer on sucess, and non-zero or NULL on +failure. +.SH SEE\ ALSO +im_rect_intersectrect(3), etc. +.SH COPYRIGHT +.br +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/iofuncs/man3/im_list_append.3 b/libsrc/iofuncs/man3/im_list_append.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_append.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_eq.3 b/libsrc/iofuncs/man3/im_list_eq.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_eq.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_fix.3 b/libsrc/iofuncs/man3/im_list_fix.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_fix.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_fold.3 b/libsrc/iofuncs/man3/im_list_fold.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_fold.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_free.3 b/libsrc/iofuncs/man3/im_list_free.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_free.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_index.3 b/libsrc/iofuncs/man3/im_list_index.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_index.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_insert.3 b/libsrc/iofuncs/man3/im_list_insert.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_insert.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_len.3 b/libsrc/iofuncs/man3/im_list_len.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_len.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_map.3 b/libsrc/iofuncs/man3/im_list_map.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_map.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_map_rev.3 b/libsrc/iofuncs/man3/im_list_map_rev.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_map_rev.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_member.3 b/libsrc/iofuncs/man3/im_list_member.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_member.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_pos.3 b/libsrc/iofuncs/man3/im_list_pos.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_pos.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_remove.3 b/libsrc/iofuncs/man3/im_list_remove.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_remove.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_makerw.3 b/libsrc/iofuncs/man3/im_makerw.3 new file mode 100644 index 00000000..e76d462b --- /dev/null +++ b/libsrc/iofuncs/man3/im_makerw.3 @@ -0,0 +1,26 @@ +.TH IM_MAKERW 3 "22 April 1992" +.SH NAME +im_makerw \- make an IMAGE writeable +.SH SYNOPSIS +#include + +int im_makerw( image ) +.br +IMAGE *image; +.SH DESCRIPTION +im_makerw(3) attempts to make image writeable, ready for an in-place operation. +Its actions are just as those for im_incheck(3), except that descriptors which +after im_incheck(3) end up as IM_MMAPIN descriptors, magically become +IM_MMAPINRW descriptors. + +Extreme caution is urged! You can permanently damage valuable data with this +call. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_mmapin(3), im_mmapinrw(3), im_fastlineuser(3). +.SH COPYRIGHT +.br +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/iofuncs/man3/im_malloc.3 b/libsrc/iofuncs/man3/im_malloc.3 new file mode 100644 index 00000000..ed13b2c4 --- /dev/null +++ b/libsrc/iofuncs/man3/im_malloc.3 @@ -0,0 +1,155 @@ +.TH IM_AND 3 "30 October 1992" +.SH NAME +im_add_close_callback, im_add_eval_callback, im_malloc, im_free, +im_add_evalend_callback \- add image callbacks +.SH SYNOPSIS +.B #include + +int im_add_close_callback( im, fn, a, b ) +.br +IMAGE *im; +.br +int (*fn)(); +.br +void *a, *b; + +int im_add_evalend_callback( im, fn, a, b ) +.br +IMAGE *im; +.br +int (*fn)(); +.br +void *a, *b; + +where + +int fn( a, b ) +.br +void *a, *b; + +char *im_malloc( IMAGE *im, int size ); + +int im_free( void *s ); + +.B #include +.br +.B #include + +int im_add_eval_callback( im, fn, a, b ) +.br +IMAGE *im; +.br +int (*fn)(); +.br +void *a, *b; + +where + +int fn( a, b ) +.br +void *a, *b; + +.SH DESCRIPTION +These functions attach callbacks to images. Callbacks are functions, with +optional extra arguments a and b, which are remembered by the IMAGE they are +attached to. These functions are triggered at some later stage in reponse to +some event. You can attach as many callbacks as you like; all will be +remembered, and will be called when the event occurs. The most recently added +callback is called first --- this can be important for close callbacks. + +.B im_add_close_callback(3) +adds a callback which will be triggered when the image +is closed by +.B im_close(3). +The callback is expected to return 0 for success and +non-zero for failure. If the function fails, then the whole +.B im_close(3) +fails. + +This function is used by VIPS to implement +.B im_malloc(3). +This allocates memory +exactly as the standard +.B malloc(3) +function, but memory allocated is local to a +descriptor. When the descriptor is closed, the memory allocated is +automatically freed for you. If you pass NULL for the descriptor, then +.B im_malloc(3) +acts as +.B malloc(3). +On error, +.B im_malloc(3) +returns NULL, setting an +error message. See the man pages for +.B IM_NEW(3) and +.B im_open_local(3) +for further examples. + +Free memory with +.B im_free(3). + +You may use close callbacks to trigger other +.B im_close(3) +operations, and there +may even be circularity in your +.B im_close(3) +lists. + +.B im_add_evalend_callback(3) +adds a callback which will be triggered when VIPS +has finished writing to the descriptor. If you want to output some diagnostics +from your function (an overflow count, for example), this is the callback to +use. + +.B im_add_eval_callback(3) +adds a callback which will be triggered repeatedly as +the image is written to. This works for both PIO and WIO images, +although it is rather more successful with PIO image output. + +When the callback is triggered, the time field of the descriptor will point to +a time_info structure, as defined in + + #include + + struct time_info { + IMAGE *im; /* Image we are part of */ + time_t start; /* Start time, in seconds */ + int run; /* Time we have been running */ + int eta; /* Seconds of computation left */ + int ttiles; /* Tiles we expect to calculate */ + int ntiles; /* Tiles calculated so far */ + int percent; /* Percent complete */ + }; + +These fields are not exact! They should only be used to give approximate +feedback to the user. It is possible to have + + percent > 100 + ntiles > ttiles + eta == 0 + +so be careful. Again, the eval callback should return 0 for success and +non-zero for failure. If the callback fails, evaluation is abandoned. This may +be used to provide a `cancel' feature in your user-interface. + + int + eval_cb( IMAGE *im ) + { + printf( "%d%% complete ...\\n", im->time->percent ); + return( 0 ); + } + + if( im_add_eval_callback( out, eval_cb, out, NULL ) ) + return( -1 ); + + ... now as we write to out, we will get %complete + ... messages on stdout. + +.SH RETURN VALUE +All functions return 0 on success and non-zero on error. +.SH SEE ALSO +IM_NEW(3), im_open_local(3). +.SH COPYRIGHT +National Gallery, 1993 +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_meta.3 b/libsrc/iofuncs/man3/im_meta.3 new file mode 100644 index 00000000..f251cbe3 --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta.3 @@ -0,0 +1,118 @@ +.TH IM_META 3 "7 June 2005" +.SH NAME +im_meta_set_int, im_meta_get_int, im_meta_set_double, im_meta_get_double, +im_meta_set_area, im_meta_get_area, im_meta_set_string, im_meta_get_string, +im_meta_set_blob, im_meta_get_blob, +im_meta_set, im_meta_get +\- read and write extra header fields +.SH SYNOPSIS +#include + +int im_meta_set_int( IMAGE *im, const char *field, int i ); +.br +int im_meta_get_int( IMAGE *im, const char *field, int *i ); +.br +int im_meta_set_double( IMAGE *im, const char *field, double d ); +.br +int im_meta_get_double( IMAGE *im, const char *field, double *d ); +.br +int im_meta_set_area( IMAGE *im, const char *field, + im_callback_fn free_fn, void *data ); +.br +int im_meta_get_area( IMAGE *im, const char *field, void **data ); +.br +int im_meta_set_string( IMAGE *im, const char *field, + const char *str ); +.br +int im_meta_get_string( IMAGE *im, const char *field, char **str ); +.br +int im_meta_set_blob( IMAGE *im, const char *field, + im_callback_fn free_fn, void *blob, int blob_length ); +.br +int im_meta_get_blob( IMAGE *im, const char *field, + void **blob, int *blob_length ); + +int im_meta_set( IMAGE *im, const char *field, GValue *value ); +.br +int im_meta_get( IMAGE *im, const char *field, GValue *value_copy ); +.br +GType im_meta_get_type( IMAGE *im, const char *field ); + +#define IM_TYPE_SAVE_STRING (im_save_string_get_type()) +.br +GType im_save_string_get_type( void ); +.br +const char *im_save_string_get( const GValue *value ); +.br +void im_save_string_set( GValue *value, const char *str ); +.br +void im_save_string_setf( GValue *value, const char *fmt, ... ); + +.SH DESCRIPTION +These functions read and write extra image header fields. Writing to a field +destroys any old value. You must read a field with the correct type of reader: +you can't read an int field as a string. Fields are copied when images are +processed, so you can use them to pass information to subsequent operations. +Unless otherwise noted, image header fields created with these functions are +also saved to disc when an image is saved in VIPS format, and automatically +restored when the image is loaded again. + +.B im_meta_set_int(3) +sets an integer field. Any existing field of any type with this name is +removed. It returns 0 for success, or -1 on error setting +.B im_error(3). + +.B im_meta_get_int(3) +reads an integer field. It returns 0 for success, or -1 on error, setting +.B im_error(3). +It can fail if the field does not exist, or if the field is not an int. + +.B im_meta_set_double(3) +and +.B im_meta_get_double(3) +work exactly as +.B im_meta_set_int(3) +and +.B im_meta_get_int(3). + +.B im_meta_set_area(3) +sets a field which is an area of memory. When the field is copied to a +subsequent IMAGE, VIPS just copies the pointer. VIPS keeps a reference count +and when the last IMAGE using this field is closed, VIPS will call +.B free_fn +to release the memory. You can therefore use this function to attach very +large areas of memory to images efficiently. Areas cannot be saved to VIPS +files on disc, since there is no known length. + +.B im_meta_set_string(3) +is a convenience function over +.B im_meta_set_area(3). +It copies the string, and +then shares that copy between all images derived from this IMAGE. + +.B im_meta_set_blob(3) +sets a field which is a blob (binary object). A blob is just like string, +except that rather than being NULL-terminated, you must pass an explicit +length. + +.B im_meta_set(3), +.B im_meta_get_type(3) +and +.B im_meta_get(3) +operate at a lower level: they let you set image header fields as GValue. Use +one of the convenience functions above if possible. +.B im_meta_get_type(3) +returns 0 if the field is not defined. + +In order for the autoload/save to VIPS files to work, you need to use a GType +with a transform defined to and from +.B IM_TYPE_SAVE_STRING. + +.SH RETURN VALUE +The functions return 0 success and -1 on error. +.SH SEE ALSO +im_header_int(3), im_header_map(3). +.SH COPYRIGHT +The National Gallery, 2005. +.SH AUTHOR +Markus Wollgarten and John Cupitt diff --git a/libsrc/iofuncs/man3/im_meta_get.3 b/libsrc/iofuncs/man3/im_meta_get.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_get_area.3 b/libsrc/iofuncs/man3/im_meta_get_area.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get_area.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_get_blob.3 b/libsrc/iofuncs/man3/im_meta_get_blob.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get_blob.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_get_double.3 b/libsrc/iofuncs/man3/im_meta_get_double.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get_double.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_get_int.3 b/libsrc/iofuncs/man3/im_meta_get_int.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get_int.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_get_string.3 b/libsrc/iofuncs/man3/im_meta_get_string.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get_string.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_get_type.3 b/libsrc/iofuncs/man3/im_meta_get_type.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get_type.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_set.3 b/libsrc/iofuncs/man3/im_meta_set.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_set.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_set_area.3 b/libsrc/iofuncs/man3/im_meta_set_area.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_set_area.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_set_blob.3 b/libsrc/iofuncs/man3/im_meta_set_blob.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_set_blob.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_set_double.3 b/libsrc/iofuncs/man3/im_meta_set_double.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_set_double.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_set_int.3 b/libsrc/iofuncs/man3/im_meta_set_int.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_set_int.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_set_string.3 b/libsrc/iofuncs/man3/im_meta_set_string.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_set_string.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_mmapin.3 b/libsrc/iofuncs/man3/im_mmapin.3 new file mode 100644 index 00000000..271d8603 --- /dev/null +++ b/libsrc/iofuncs/man3/im_mmapin.3 @@ -0,0 +1,22 @@ +.TH IM_MMAPIN 3 "11 April 1990" +.SH NAME +im_mmapin \- memory maps a VASARI image file (read only) and returns +an image descriptor. +.SH SYNOPSIS +.B #include + +.B IMAGE *im_mmapin(file_name) +.br +.B char *file_name; +.SH DESCRIPTION +.B im_mmapin(3) +opens a file for input. It has been replaced by im_open( file_name, "r" ). +.SH RETURN VALUE +The function returns the image descriptor on success and NULL on error. +.SH SEE ALSO +im_open(3), im_close(3), im_mmapinrw(3), im_makerw(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_mmapinrw.3 b/libsrc/iofuncs/man3/im_mmapinrw.3 new file mode 100644 index 00000000..cbcdedbf --- /dev/null +++ b/libsrc/iofuncs/man3/im_mmapinrw.3 @@ -0,0 +1,27 @@ +.TH IM_MMAPINRW 3 "22 April 1991" +.SH NAME +im_mmapinrw \- memory maps a VASARI image file with read and write access +.SH SYNOPSIS +.B #include + +.B IMAGE *im_mmapinrw(file_name) +.br +.B char *file_name; +.SH DESCRIPTION +.B im_mmapinrw(3) +works exactly as im_mmapin but the entered file_name is memory mapped +with read and write permissions. This is useful for some applications, +like writing lines onto an existing file. Since +the user might change accidentally the context of the original file, +great care should taken when handling a file with this routine. + +im_open(3) is generally more convenient. +.SH RETURN VALUE +The function returns the image descriptor on success and NULL on error. +.SH SEE\ ALSO +im_close(3), im_open(3), im_makerw(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_open.3 b/libsrc/iofuncs/man3/im_open.3 new file mode 100644 index 00000000..ddb9379e --- /dev/null +++ b/libsrc/iofuncs/man3/im_open.3 @@ -0,0 +1,135 @@ +.TH IM_OPEN 3 "30 October 1992" +.SH NAME +im_open, im_open_local, im_open_local_array \- open VIPS +image descriptor(s) +.SH SYNOPSIS +#include + +IMAGE *im_open( const char *filename, const char *mode ) + +IMAGE *im_open_local( IMAGE *im, const char *filename, const char *mode ) + +int im_open_local_array( IMAGE *im, + IMAGE **out, int n, const char *filename, const char *mode ) + +.SH DESCRIPTION +.B im_open(3) +examines the mode string, and creates an appropriate VIPS IMAGE descriptor. + +.B "r" +opens the named file for reading. If the file is not in the native VIPS format +for your machine, +.B im_open(3) +automatically converts the file for you in memory. For some large files (eg. +TIFF) this may not be what you want: you should call the appropriate converter +yourself, and arrange for the conversion to take place on disc. See +.B im_tiff2vips(3), +.B im_jpeg2vips(3), +.B im_png2vips(3), +.B im_magick2vips(3), +and +.B im_ppm2vips(3). + +.B im_open(3) +can read files in most formats. + +.B "w" +opens the named file for writing. It looks at the file name suffix to +determine the type to write -- for example: + + im_open( "fred.tif", "w" ) + +will write in TIFF format. + +You can pass parameters to the conversion functions encoded in the filename +string. For example: + + im_open( "fred.tif:deflate", "w" ) + +will write a deflate (ZIP) compressed TIFF file. See the man pages for +.B im_vips2tiff(3), +.B im_vips2jpeg(3), +.B im_vips2png(3) +and +.B im_vips2ppm(3) +for details on all of the options available. + +.B "t" +creates a temporary memory buffer image. + +.B "p" +creates a "glue" descriptor you can use to join two image processing +operations together. + +.B "rw" +opens the named file for reading and writing. This will only work for VIPS +files in a format native to your machine. It is only for paintbox-type +applications. + +.B im_open_local(3) +is a convenience function which opens an image descriptor as +im_open(3), but makes it local to im, that is, when im is closed, the +descriptor created by im_open_local(3) will be closed too. + +.B im_open_local(3) +is handy for saving you from adding many +.B im_close(3) +calls to +escape points. Example: find the total of an array of images. + + #include + #include + + int + total( IMAGE **in, int nin, IMAGE *out ) + { + int i; + IMAGE *t1, *t2; + + if( nin <= 0 ) { + im_errormsg( "total: nin should be > 0" ); + return( -1 ); + } + else if( nin == 1 ) + return( im_copy( *in, out ) ); + else + for( t1 = *in, i = 1; i < nin; i++ ) { + if( i + 1 == nin ) + t2 = out; + else if( !(t2 = im_open_local( out, "t2", "p" )) ) + return( -1 ); + + if( im_add( t1, in[i], t2 ) ) + return( -1 ); + t1 = t2; + } + + return( 0 ); + } + +This function will create many intermediate images, but does not need to close +them. Any which are created will be closed automatically when out is closed by +our caller. + +im_open_local(3) returns NULL on error, or if its first parameter is NULL. + +.B im_open_local_array(3) +will open an array of images, failing if any of the opens fail. It's handy if +you need a number of images for intermediates. Example: + + IMAGE *t[6]; + + if( im_open_local_array( out, t, 6, "mytemps", "p" ) ) + return( -1 ); + +opens 6 temp images (t[0] to t[5]). + +.SH RETURN VALUE +The function returns the image descriptor on success and NULL on error. +.SH SEE ALSO +im_close(3), im_vips2tiff(3), im_vips2jpeg(3), im_vips2ppm(3), +im_tiff2vips(3), im_jpeg2vips(3), im_ppm2vips(3). +.SH COPYRIGHT +K. Martinez, 1992. +.SH AUTHOR +K. Martinez. diff --git a/libsrc/iofuncs/man3/im_open_local.3 b/libsrc/iofuncs/man3/im_open_local.3 new file mode 100644 index 00000000..227e5534 --- /dev/null +++ b/libsrc/iofuncs/man3/im_open_local.3 @@ -0,0 +1 @@ +.so man3/im_open.3 diff --git a/libsrc/iofuncs/man3/im_open_local_array.3 b/libsrc/iofuncs/man3/im_open_local_array.3 new file mode 100644 index 00000000..227e5534 --- /dev/null +++ b/libsrc/iofuncs/man3/im_open_local_array.3 @@ -0,0 +1 @@ +.so man3/im_open.3 diff --git a/libsrc/iofuncs/man3/im_openout.3 b/libsrc/iofuncs/man3/im_openout.3 new file mode 100644 index 00000000..ddbc4bb9 --- /dev/null +++ b/libsrc/iofuncs/man3/im_openout.3 @@ -0,0 +1,22 @@ +.TH IM_OPENOUT 3 "22 April 1991" +.SH NAME +im_openout \- sets an image descriptor corresponding to an output file +.SH SYNOPSIS +.B #include + +.B IMAGE *im_openout(file_name) +.br +.B char *file_name; +.SH DESCRIPTION +.B im_openout(3) +returns a descriptor which when written to will write pels to file_name. This +function has been replaced by im_open(3). +.SH RETURN VALUE +The function returns a valid image descriptor on success and NULL on error. +.SH SEE\ ALSO +im_open(3), im_close(3), im_mmapin(3), im_intro(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 22/04/1991 diff --git a/libsrc/iofuncs/man3/im_outcheck.3 b/libsrc/iofuncs/man3/im_outcheck.3 new file mode 100644 index 00000000..e0092c33 --- /dev/null +++ b/libsrc/iofuncs/man3/im_outcheck.3 @@ -0,0 +1 @@ +.so man3/im_iocheck.3 diff --git a/libsrc/iofuncs/man3/im_partial.3 b/libsrc/iofuncs/man3/im_partial.3 new file mode 100644 index 00000000..82396807 --- /dev/null +++ b/libsrc/iofuncs/man3/im_partial.3 @@ -0,0 +1,24 @@ +.TH IM_OPENOUT 3 "22 April 1991" +.SH NAME +im_partial \- makes a PIO intermediate image +.SH SYNOPSIS +.B #include + +.B IMAGE *im_partial(file_name) +.br +.B char *file_name; +.SH DESCRIPTION +.B im_partial(3) +returns a descriptor which can be used to join PIO image processing +operations together. + +This function is obsolete --- you should call im_open(3) instead. +.SH RETURN VALUE +The function returns a valid image descriptor on success and NULL on error. +.SH SEE ALSO +im_open(3), im_close(3), im_mmapin(3), im_intro(3). +.SH COPYRIGHT +.br +National Gallery, 1993 +.SH AUTHOR +J.Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_pincheck.3 b/libsrc/iofuncs/man3/im_pincheck.3 new file mode 100644 index 00000000..1dc8c392 --- /dev/null +++ b/libsrc/iofuncs/man3/im_pincheck.3 @@ -0,0 +1 @@ +.so man3/im_piocheck.3 diff --git a/libsrc/iofuncs/man3/im_piocheck.3 b/libsrc/iofuncs/man3/im_piocheck.3 new file mode 100644 index 00000000..98280199 --- /dev/null +++ b/libsrc/iofuncs/man3/im_piocheck.3 @@ -0,0 +1,36 @@ +.TH IM_IOCHECK 3 "11 April 1990" +.SH NAME +im_pincheck, im_poutcheck, im_piocheck \- checks image descriptors for +PIO +.SH SYNOPSIS +.B #include + +int im_pincheck( in ) +.br +IMAGE *in; + +int im_poutcheck( out ) +.br +IMAGE *out; + +.B int im_piocheck( in, out) +.br +.B IMAGE *in, *out; +.SH DESCRIPTION +.B im_pincheck(3) +checks that an image descriptor is suitable for PIO input. If the descriptor is +IM_OPENOUT and the descriptor has been written to, it is automatically +`rewound,' that is, it is closed and reopened as an IM_MMAPIN descriptor. + +im_poutcheck(3) checks that a descriptor is suitable for PIO output. + +im_piocheck(3) simply calls in_pincheck(3) for image in and im_poutcheck(3) for +image out. +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_generate(3), im_open(3). +.SH COPYRIGHT +National Gallery +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_poutcheck.3 b/libsrc/iofuncs/man3/im_poutcheck.3 new file mode 100644 index 00000000..1dc8c392 --- /dev/null +++ b/libsrc/iofuncs/man3/im_poutcheck.3 @@ -0,0 +1 @@ +.so man3/im_piocheck.3 diff --git a/libsrc/iofuncs/man3/im_prepare.3 b/libsrc/iofuncs/man3/im_prepare.3 new file mode 100644 index 00000000..49297a1c --- /dev/null +++ b/libsrc/iofuncs/man3/im_prepare.3 @@ -0,0 +1,91 @@ +.TH IM_PREPARE 3 "11 April 1990" +.SH NAME +im_prepare, im_prepare_to \- fill region with data +.SH SYNOPSIS +.B #include +.br +.B #include + +int im_prepare( reg, r ) +.br +REGION *reg; +.br +Rect *r; + +int im_prepare_to( reg, dest, r, x, y ) +.br +REGION *reg, *dest; +.br +Rect *r; +.br +int x, y; + +int im_prepare_many( reg, r ) +.br +REGION **reg; +.br +Rect *r; + +.SH DESCRIPTION +.B im_prepare(3) +fills region reg with pels covering the area inside r. + +r is expected to lie within the image on which reg was defined; if it does not +it will be clipped against the size of the image. Consequence: if + + im_prepare( reg, r ) + +succeeds, VIPS guarantees that pels within reg->valid may be read from reg +with +.B IM_REGION_ADDR(3). +It does not guarantee that you may read all of r! + +The action taken by im_prepare(3) depends upon the image descriptor on which +reg was defined: + +PARTIAL: The area requested is clipped against the edges of the image, local +memory is attached to reg with +.B im_region_buffer(3) +and that area requested +from the image's generate function, see +.B im_generate(3). +If necessary, a new +sequence is started. + +SETBUF: MMAPIN: MMAPINRW: The area defined by r is clipped against the edges +of the image, and that area attached to reg. + +.B im_prepare_to(3) +is very like +.B im_prepare(3), +but rather than writing pixels to local memory on reg (or attaching reg to +some other piece of memory), it instead writes pixels into the region dest at +position x, y. The parameters dest, r, x, y behave in the same way as the +paramaters to +.B im_region_region(3). + +Effectively, it's just like +.B im_prepare(3) +followed by a copy operation. Except that the copy will be skipped when +possible. + +This call is used by (among others) +.B im_generate(3) +to make operations output to +disc buffers, and by +.B im_embed(3) +to get images written inside larger images. + +.B im_prepare_many(3) +prepares the same Rect on a NULL terminated array of REGION pointers, as +returned by +.BR im_start_many(3) . + +.SH RETURN VALUE +The function returns 0 on success and non-zero on error. +.SH SEE ALSO +im_generate(3), im_open(3). +.SH COPYRIGHT +National Gallery +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_prepare_many.3 b/libsrc/iofuncs/man3/im_prepare_many.3 new file mode 100644 index 00000000..381ca378 --- /dev/null +++ b/libsrc/iofuncs/man3/im_prepare_many.3 @@ -0,0 +1 @@ +.so man3/im_prepare.3 diff --git a/libsrc/iofuncs/man3/im_prepare_to.3 b/libsrc/iofuncs/man3/im_prepare_to.3 new file mode 100644 index 00000000..381ca378 --- /dev/null +++ b/libsrc/iofuncs/man3/im_prepare_to.3 @@ -0,0 +1 @@ +.so man3/im_prepare.3 diff --git a/libsrc/iofuncs/man3/im_printdesc.3 b/libsrc/iofuncs/man3/im_printdesc.3 new file mode 100644 index 00000000..9a94b602 --- /dev/null +++ b/libsrc/iofuncs/man3/im_printdesc.3 @@ -0,0 +1,66 @@ +.TH IM_PRINTDESC 3 "11 April 1990" +.SH NAME +im_printdesc, im_Type2char, im_char2Type, im_BandFmt2char, im_char2BandFmt, +im_Coding2char, im_char2Coding, im_Compression2char, im_char2Compression +\- decode image descriptors + +.SH SYNOPSIS +.B #include +.br +.B #include + +.B void im_printdesc(image) +.br +.B IMAGE *image; + +char *im_Type2char( ty ); +.br +int ty; + +char *im_BandFmt2char( bf ); +.br +int bf; + +char *im_Coding2char( cod ); +.br +int cod; + +char *im_Compression2char( comp ); +.br +int comp; + +int im_char2Type( str ); +.br +char *str; + +int im_char2BandFmt( str ); +.br +char *str; + +int im_char2Coding( str ); +.br +char *str; + +int im_char2Compression( str ); +.br +char *str; + +.SH DESCRIPTION +.B im_printdesc(3) +prints the image descriptor pointed by image. + +.B im_printdesc(3) +exports the functions it uses to code and uncode the type fields +in image headers. The functions which return char * return a static string of +the form "" on error, + +The functions which return int return -1 on error, +setting +.B im_error(3). + +.SH SEE ALSO +header(1). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_printlines.3 b/libsrc/iofuncs/man3/im_printlines.3 new file mode 100644 index 00000000..4db97dcb --- /dev/null +++ b/libsrc/iofuncs/man3/im_printlines.3 @@ -0,0 +1 @@ +.so man3/im_debugim.3 diff --git a/libsrc/iofuncs/man3/im_rect_dup.3 b/libsrc/iofuncs/man3/im_rect_dup.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_dup.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_equalsrect.3 b/libsrc/iofuncs/man3/im_rect_equalsrect.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_equalsrect.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_includespoint.3 b/libsrc/iofuncs/man3/im_rect_includespoint.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_includespoint.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_includesrect.3 b/libsrc/iofuncs/man3/im_rect_includesrect.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_includesrect.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_intersectrect.3 b/libsrc/iofuncs/man3/im_rect_intersectrect.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_intersectrect.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_isempty.3 b/libsrc/iofuncs/man3/im_rect_isempty.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_isempty.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_marginadjust.3 b/libsrc/iofuncs/man3/im_rect_marginadjust.3 new file mode 100644 index 00000000..32ea226e --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_marginadjust.3 @@ -0,0 +1,81 @@ +.TH Rect 3 "2 May 1991" +.SH NAME +im_rect_marginadjust, im_rect_includespoint, im_rect_includesrect, +im_rect_intersectrect, im_rect_isempty, im_rect_unionrect, im_rect_normalise, +im_rect_equalsrect, im_rect_dup, +IM_RECT_RIGHT, IM_RECT_BOTTOM, IM_RECT_HCENTRE, IM_RECT_VCENTRE +\- rectangle algebra functions +.SH SYNOPSIS +#include +.br +#include + +typedef struct { +.br + int left, top, width, height; +.br +} Rect; + +#define IM_RECT_RIGHT(R) ((R)->left + (R)->width) +.br +#define IM_RECT_BOTTOM(R) ((R)->top + (R)->height) +.br +#define IM_RECT_HCENTRE(R) ((R)->left + (R)->width / 2) +.br +#define IM_RECT_VCENTRE(R) ((R)->top + (R)->height / 2) + +void im_rect_marginadjust( Rect *r, int n ); +.br +int im_rect_includespoint( Rect *r, int x, int y ); +.br +int im_rect_includesrect( Rect *r1, Rect *r2 ); +.br +void im_rect_intersectrect( Rect *r1, Rect *r2, Rect *r3 ); +.br +int im_rect_isempty( Rect *r ); +.br +void im_rect_unionrect( Rect *r1, Rect *r2, Rect *r3 ); +.br +int im_rect_equalsrect( Rect *r1, Rect *r2 ); +.br +Rect *im_rect_dup( Rect *r ); +.br +void im_rect_normalise( Rect *r ); +.SH DESCRIPTION +These functions perform simple algebra on Rect structs. There should also be a +set of functions to do rectlist algebra. + +im_rect_marginadjust(3) expands a Rect by n units up, down, left and right. +Negative expansions shrink the Rect. + +im_rect_includespoint(3) returns non-zero +if point (x,y) lies within Rect r. + +im_rect_includesrect(3) returns non-zero if +Rect r2 lies completely within Rect r1. + +im_rect_intersectrect(3) fills Rect r3 +with the intersection of Rects r1 and r2. + +im_rect_isempty(3) returns non-zero +if Rect r has either width less than or equal to 0 or height less than or +equal to 0. + +im_rect_unionrect(3) fills Rect r3 with the bounding box of Rect r1 and Rect +r2. A proper union operation requires lists of rectangles, sadly. + +im_rect_equalsrect(3) returns non-zero if r1 and r2 are identical. + +im_rect_dup(3) allocates memory for a new Rect structure and copies the +elements of r into it. It returns a pointer to the new struct, or NULL on +error. + +im_rect_normalise(3) flips r so that the same pixels are enclosed, +but width and height are guaranteed >=0. + +.SH SEE ALSO +im_prepare(3), im_region_create(3) +.SH COPYRIGHT +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/iofuncs/man3/im_rect_normalise.3 b/libsrc/iofuncs/man3/im_rect_normalise.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_normalise.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_unionrect.3 b/libsrc/iofuncs/man3/im_rect_unionrect.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_unionrect.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_region_buffer.3 b/libsrc/iofuncs/man3/im_region_buffer.3 new file mode 100644 index 00000000..97d5afb9 --- /dev/null +++ b/libsrc/iofuncs/man3/im_region_buffer.3 @@ -0,0 +1,119 @@ +.TH REGION-OPERATIONS 3 "11 April 1990" +.SH NAME +im_region_buffer, im_region_image, im_region_region, im_region_position, +im_region_equalsregion \- +attach data to region +.SH SYNOPSIS +.B #include +.br +.B #include + +int im_region_buffer( reg, r ) +.br +REGION *reg; +.br +Rect *r; + +int im_region_image( reg, r ) +.br +REGION *reg; +.br +Rect *r; + +int im_region_region( reg, treg, r, x, y ) +.br +REGION *reg, treg; +.br +Rect *r; +.br +int x, y; + +int im_region_position( reg, x, y ) +.br +REGION *reg; +.br +int x, y; + +int im_region_equalsregion( REGION *reg1, REGION *reg2 ) + +.SH DESCRIPTION +These functions are used to say where exactly input from or output to a +region goes. Briefly, +.B im_region_buffer(3) +gives a region its own piece of local storage, +.B im_region_image(3) +links the region to an image, and +.B im_region_region(3) +links the region to another region. + +.B im_region_region(3) +is the most powerful of the three --- you can use it to +avoid copy operations. See the source for +.B im_extract(3), +.B im_insert(3), +.B im_copy(3), +.B im_embed(3), +and +.B im_lrmerge(3) +for examples of its use, and see also +.B im_prepare_copy(3). + +.B im_region_buffer(3) +clips Rect r against the size of the image upon which reg is +defined, then searches for any calculated pixels which enclose that area. If +there are any suitable pixels already, the region is set to point to them. +Otherwise, a fresh buffer is allocated. + +If region->buffer->done is set, then the region is already calculated. +Otherwise, the pixels need to be calculated. + +.B im_region_buffer(3) +is called by +.B im_prepare(3) +just before it calls the user generate function. + +.B im_region_image(3) +attaches reg to the image on which it was defined. The image +has to be SETBUF, MMAPIN, MMAPINRW, or OPENIN --- ie., the types passed by +.B im_incheck(3). + +.B im_region_region(3) +redirects reg to treg. treg can be defined on another +image. r is clipped against the size of reg's image, and against treg->valid. +treg has to have pel data associated with it (ie. memory local to treg, memory +that is part of the image on which treg is defined, or even memory from a +third region), and the pel data has to be in a form which is compatible with +reg, ie. BandFmt and Bands must be the same. + +r becomes the valid field in reg, (x,y) is the top left-hand corner of the +area in treg to which valid is mapped. + + +.B im_region_position(3) +changes reg->valid, so that reg->valid.left == x and +reg->valid.top == y. width and height are clipped against the size of the +image. If x < 0 or y < 0 or the clipped rect has zero width or height, the +call fails. + +This function affects the way that pels are addressed by the +.B IM_REGION_ADDR(3) +macro. It does not affect the pels themselves! It has the effect of moving +the area of pels a region represents. This call is used by +.B im_extract(3). + +.B im_region_equalsregion(3) +tests for reg1 and reg2 are identical, ie.: + + IM_REGION_ADDR( reg1, x, y ) == + IM_REGION_ADDR( reg2, x, y ) && + *IM_REGION_ADDR( reg1, x, y ) == + *IM_REGION_ADDR( reg2, x, y ) + +It is used internally by VIPS, but may be useful to applications. + +.SH RETURN VALUE +All int-valued functions return zero on success and non-zero on error. +.SH COPYRIGHT +National Gallery, 1993 +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_region_create.3 b/libsrc/iofuncs/man3/im_region_create.3 new file mode 100644 index 00000000..25335c80 --- /dev/null +++ b/libsrc/iofuncs/man3/im_region_create.3 @@ -0,0 +1,40 @@ +.TH PREDICATES 3 "11 April 1990" +.SH NAME +im_region_create, im_region_free \- region creation and destruction +.SH SYNOPSIS +.B #include +.br +.B #include + +REGION *im_region_create( im ) +.br +IMAGE *im; + +int im_region_free( reg ) +.br +REGION *reg; +.SH DESCRIPTION +These functions create and destroy regions on images. Regions are used for +PIO, see accompanying documentation. Regions have type + + typedef struct { + Rect valid /* Area of im represented */ + IMAGE *im; /* im we are defined on */ + + ... more fields, all private and used for + ... housekeeping + } REGION; + +im_region_create(3) returns a pointer to a new region, or NULL on error. +Regions are made blank, with no input or output possible. See im_prepare(3), +im_generate(3), im_start_one(3) and IM_REGION_ADDR(3). + +im_region_free(3) frees a region and any resources associated with that +region. When an image is closed, all regions which have been created on that +image are automatically freed. +.SH RETURN VALUE +All int-valued functions return zero on success and non-zero on error. +.SH COPYRIGHT +National Gallery, 1993 +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_region_free.3 b/libsrc/iofuncs/man3/im_region_free.3 new file mode 100644 index 00000000..d21d2aa2 --- /dev/null +++ b/libsrc/iofuncs/man3/im_region_free.3 @@ -0,0 +1 @@ +.so man3/im_region_create.3 diff --git a/libsrc/iofuncs/man3/im_region_image.3 b/libsrc/iofuncs/man3/im_region_image.3 new file mode 100644 index 00000000..886fa201 --- /dev/null +++ b/libsrc/iofuncs/man3/im_region_image.3 @@ -0,0 +1 @@ +.so man3/im_region_buffer.3 diff --git a/libsrc/iofuncs/man3/im_region_position.3 b/libsrc/iofuncs/man3/im_region_position.3 new file mode 100644 index 00000000..886fa201 --- /dev/null +++ b/libsrc/iofuncs/man3/im_region_position.3 @@ -0,0 +1 @@ +.so man3/im_region_buffer.3 diff --git a/libsrc/iofuncs/man3/im_region_region.3 b/libsrc/iofuncs/man3/im_region_region.3 new file mode 100644 index 00000000..886fa201 --- /dev/null +++ b/libsrc/iofuncs/man3/im_region_region.3 @@ -0,0 +1 @@ +.so man3/im_region_buffer.3 diff --git a/libsrc/iofuncs/man3/im_render.3 b/libsrc/iofuncs/man3/im_render.3 new file mode 100644 index 00000000..bfa7bbb1 --- /dev/null +++ b/libsrc/iofuncs/man3/im_render.3 @@ -0,0 +1,101 @@ +.TH IM_RENDER 3 "5 October 2003" +.SH NAME +im_render, im_render_fade, im_cache \- make image in the background +.SH SYNOPSIS +#include + +int im_render_fade( IMAGE *in, IMAGE *out, IMAGE *mask, +.br + int width, int height, int max, +.br + int fps, int steps, +.br + int priority, +.br + void (*notify)( IMAGE *, Rect *, void * ), void *client ); + +int im_render( IMAGE *in, IMAGE *out, IMAGE *mask, +.br + int width, int height, int max, +.br + void (*notify)( IMAGE *, Rect *, void * ), void *client ); + +int im_cache( IMAGE *in, IMAGE *out, +.br + int width, int height, int max ); + +.SH DESCRIPTION +.B im_render_fade(3) +behaves rather like +.B im_copy(3) +between images +.B in +and +.B out, +except that it keeps a cache of computed pixels. This cache is made of up to +.B max +tiles (a value of -1 for +.B max +means any number of tiles), and each tile is of size +.B width +by +.B height +pixels. Each cache tile is made with a call to +.B im_prepare_thread(3), +so cache tile calculation will be accelerated on multi-CPU machines. If image +.B in +represents a large computation, +.B im_render_fade(3) +can save a lot of time. + +If the +.B notify +parameter points to a function, then tiles are not calculated immediately, but +are added to a job list and calculated as CPU becomes available. When a tile has +been calculated, the notify function is passed the image which was being +cached, the area of the image which is now available, and a client pointer. + +The +.B mask +image is a one band uchar image the same size as out, which has 255 for every +pixels which is in the cache and 0 everywhere else. You should not read +pixels from +.B mask +after +.B out +has been closed. If mask is NULL, then no mask image is written. + +If +.B steps +is greater than zero, then pixels in mask are moved between 0 and 255 as tiles +age in that many steps. The +.B fps +parameter sets how many times per second the notify function is called to +indicate that an updated tile is ready. + +If +.B priority +is zero, then the render will only run when the system is idle, and only a few +renders will run at any time. Higher values will cause the render to happen +more quickly. + +.B im_cache(3) +is a convenience function for +.B im_render_fade(3) +that caches image pixels synchronously. If you ask for an area not in the cache, +execution blocks until the area has been calculated. + +.B im_render(3) +is deprecated: it is the old interface to the render function, before the +.B fps +and +.B steps +parameters were added. + +.SH RETURN VALUE +The function returns 0 on success, and non-zero on error, setting +im_error(). +.SH SEE ALSO +im_prepare(3) +.SH AUTHOR +J Cupitt, 2003 diff --git a/libsrc/iofuncs/man3/im_render_fade.3 b/libsrc/iofuncs/man3/im_render_fade.3 new file mode 100644 index 00000000..b7647daa --- /dev/null +++ b/libsrc/iofuncs/man3/im_render_fade.3 @@ -0,0 +1 @@ +.so man3/im_render.3 diff --git a/libsrc/iofuncs/man3/im_setbuf.3 b/libsrc/iofuncs/man3/im_setbuf.3 new file mode 100644 index 00000000..eb13b64f --- /dev/null +++ b/libsrc/iofuncs/man3/im_setbuf.3 @@ -0,0 +1,21 @@ +.TH IM_SETBUF 3 "11 April 1990" +.SH NAME +im_setbuf \- sets an image descriptor to keep an image in ram +.SH SYNOPSIS +.B #include + +.B IMAGE *im_setbuf(buffer_name) +.br +.B char *buffer_image; +.SH DESCRIPTION +.B im_setbuf(3) +returns an image descriptor which, when written to, will save pels in a memory +buffer. It is generally more convenient to call im_open(3). +.SH RETURN VALUE +The function returns a valid image descriptor on success or NULL on error. +.SH SEE\ ALSO +im_close(3), im_open(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_setupout.3 b/libsrc/iofuncs/man3/im_setupout.3 new file mode 100644 index 00000000..845bd7bf --- /dev/null +++ b/libsrc/iofuncs/man3/im_setupout.3 @@ -0,0 +1,51 @@ +.TH IM_SETUPOUT 3 "11 April 1990" +.SH NAME +im_setupout \- set up an image descriptor for WIO output +.SH SYNOPSIS +.B #include + +.B int im_setupout(image) +.br +.B IMAGE *image; +.SH DESCRIPTION +.B im_setupout(3) +makes a descriptor ready for WIO writing. If the descriptor is a memory +buffer, enough memory is allocated to be able to hold all of the pels of the +image. If the descriptor is an output file, then the header is written to +disc. + +Typically, for WIO, you should have + + int + im_wombat( in, out ) + IMAGE *in, *out; + { + if( im_iocheck( in, out ) ) + return( -1 ); + + ... check parameters, check image descriptors + ... for type-compatibility, etc. etc. + + if( im_cp_desc( out, in ) ) + return( -1 ); + + ... set fields in out for the type of image you + ... wish to write + + if( im_setupout( out ) ) + return( -1 ); + + ... process from input to output, reading from in->data + ... and writing to out with im_writeline(3) + + return( 0 ); + } +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_cp_desc(3), im_iocheck(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990, updated on 22/04/1991 diff --git a/libsrc/iofuncs/man3/im_start_many.3 b/libsrc/iofuncs/man3/im_start_many.3 new file mode 100644 index 00000000..c124af1e --- /dev/null +++ b/libsrc/iofuncs/man3/im_start_many.3 @@ -0,0 +1 @@ +.so man3/im_generate.3 diff --git a/libsrc/iofuncs/man3/im_start_one.3 b/libsrc/iofuncs/man3/im_start_one.3 new file mode 100644 index 00000000..c124af1e --- /dev/null +++ b/libsrc/iofuncs/man3/im_start_one.3 @@ -0,0 +1 @@ +.so man3/im_generate.3 diff --git a/libsrc/iofuncs/man3/im_stop_many.3 b/libsrc/iofuncs/man3/im_stop_many.3 new file mode 100644 index 00000000..c124af1e --- /dev/null +++ b/libsrc/iofuncs/man3/im_stop_many.3 @@ -0,0 +1 @@ +.so man3/im_generate.3 diff --git a/libsrc/iofuncs/man3/im_stop_one.3 b/libsrc/iofuncs/man3/im_stop_one.3 new file mode 100644 index 00000000..c124af1e --- /dev/null +++ b/libsrc/iofuncs/man3/im_stop_one.3 @@ -0,0 +1 @@ +.so man3/im_generate.3 diff --git a/libsrc/iofuncs/man3/im_updatehist.3 b/libsrc/iofuncs/man3/im_updatehist.3 new file mode 100644 index 00000000..d421d4b0 --- /dev/null +++ b/libsrc/iofuncs/man3/im_updatehist.3 @@ -0,0 +1 @@ +.so man3/im_histlin.3 diff --git a/libsrc/iofuncs/man3/im_verror.3 b/libsrc/iofuncs/man3/im_verror.3 new file mode 100644 index 00000000..7db4b63b --- /dev/null +++ b/libsrc/iofuncs/man3/im_verror.3 @@ -0,0 +1 @@ +.so man3/im_error.3 diff --git a/libsrc/iofuncs/man3/im_version.3 b/libsrc/iofuncs/man3/im_version.3 new file mode 100644 index 00000000..ae5522de --- /dev/null +++ b/libsrc/iofuncs/man3/im_version.3 @@ -0,0 +1,46 @@ +.TH IM_VERSION 3 "15 December 1999" +.SH NAME +im_version, im_version_string \- return VIPS version info +.SH SYNOPSIS +.B #include + +.B int im_version( int flag ) + +.B const char *im_version_string( void ) + +.SH DESCRIPTION + +.B im_version(3) +returns the major version number if +.B flag +== 0, the minor version number if +.B flag +== 1, and the micro version number if +.B flag +== 2. + +Major versions are supposed to represent very large changes to the +library, minor versions indicate steady improvements (with odd minor +versions indicating unstable and undocumented development releases), +and micro versions indicating bug-fixes. + +.B im_version_string(3) +returns a static string representing the vips version number, and the date +when the library was built. + +The version number always has three parts, separated by '.' characters. The +first part is the major release, the second the minor release (odd minor +values denote development releases), and the third the micro version number. + +The version number and the date are separated by a '-' character. + +The value of this string is constructed in 'configure.in'. + +A typical value for tthe string might be "7.7.0-Thu Dec 16 18:10:01 GMT 1999". + +.SH RETURN VALUE +The function returns 0 on success and non-zero on error. +.SH COPYRIGHT +National Gallery +.SH AUTHOR +J. Cupitt \- 15/12/99 diff --git a/libsrc/iofuncs/man3/im_version_string.3 b/libsrc/iofuncs/man3/im_version_string.3 new file mode 100644 index 00000000..f4a36ee1 --- /dev/null +++ b/libsrc/iofuncs/man3/im_version_string.3 @@ -0,0 +1 @@ +.so man3/im_version.3 diff --git a/libsrc/iofuncs/man3/im_warn.3 b/libsrc/iofuncs/man3/im_warn.3 new file mode 100644 index 00000000..7db4b63b --- /dev/null +++ b/libsrc/iofuncs/man3/im_warn.3 @@ -0,0 +1 @@ +.so man3/im_error.3 diff --git a/libsrc/iofuncs/man3/im_wrapmany.3 b/libsrc/iofuncs/man3/im_wrapmany.3 new file mode 100644 index 00000000..e2d3a418 --- /dev/null +++ b/libsrc/iofuncs/man3/im_wrapmany.3 @@ -0,0 +1 @@ +.so man3/im_wrapone.3 diff --git a/libsrc/iofuncs/man3/im_wrapone.3 b/libsrc/iofuncs/man3/im_wrapone.3 new file mode 100644 index 00000000..9235bb45 --- /dev/null +++ b/libsrc/iofuncs/man3/im_wrapone.3 @@ -0,0 +1,69 @@ +.TH WRAPPERS 3 "11 April 1990" +.SH NAME +im_wrapone, im_wrapmany \- easy interface to partial image IO system +.SH SYNOPSIS +.B #include +.br +.B #include + +int im_wrapone( IMAGE *in, IMAGE *out, + im_wrapone_fn fn, void *a, void *b ) +.br +int im_wrapmany( IMAGE **in, IMAGE *out, + im_wrapmany_fn fn, void *a, void *b ) + +where + +typedef void (*im_wrapone_fn)( void *in, void *out, int n, + void *a, void *b ) +.br +typedef void (*im_wrapmany_fn)( void **in, void *out, int n, + void *a, void *b ) + +.SH DESCRIPTION +These functions provide a simple way to use the VIPS partial image IO system, +provided that your image processing function involves no coordinate +transformations, that is, that each output PEL depends only upon the +corresponding PEL(s) in the input image(s). + +You should write your operation as a buffer processing function --- for +example: + + static void + invert_buffer( unsigned char *in, unsigned char *out, + int width ) + { + int x; + + for( x = 0; x < width; x++ ) + *out++ = 255 - *in++; + } + +This can now be turned into a full PIO image processing function by: + + int + invert( IMAGE *in, IMAGE *out ) + { + if( in->BandFmt != FMTUCHAR || + in->Coding != NOCODING || + in->Bands != 1 ) { + im_errormsg( "invert: bad input" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_wrapone( in, out, + (im_wrapone_fn) invert_buffer, NULL, NULL ) ) + return( 0 ); + } + +im_wrapmany(3) works as im_wrapone(3), but allows many input images. All the +inputs should be same Xsize and Ysize, but may vary in type. The array of +input images should be NULL-terminated, as for im_start_many(3). + +.SH RETURN VALUE +All int-valued functions return zero on success and non-zero on error. +.SH COPYRIGHT +National Gallery, 1995 +.SH AUTHOR +J. Cupitt \- 9/2/95 diff --git a/libsrc/iofuncs/man3/im_writeline.3 b/libsrc/iofuncs/man3/im_writeline.3 new file mode 100644 index 00000000..96029903 --- /dev/null +++ b/libsrc/iofuncs/man3/im_writeline.3 @@ -0,0 +1,37 @@ +.TH IM_WRITELINE 3 "22 April 1991" +.SH NAME +im_writeline \- writes a line of data in the image descriptor +.SH SYNOPSIS +.B #include + +.B int im_writeline(ypos, image, buffer) +.br +.B int ypos; +.br +.B IMAGE *image; +.br +.B char *buffer; +.SH DESCRIPTION +.B im_writeline(3) +writes the ypos line of an image held in buffer to either the output +buffer image (previously set by im_setbuf(3)) or to an output file +(previously set by im_openput(3)). The function provides uniform treatment of +output irrespectively whether it is a file or a buffer image. + +You should be careful that the buffer does indeed contain enough data for a +complete line of pels --- see lsize(3). + +Any evaluation callbacks which have been added to image are triggered --- see +im_generate(3) and im_add_eval_callback(3). +.SH BUGS +This function changed in VIPS6: +programs written previously must be modified by removing the last argument +of the old im_writeline(3). +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_setbuf(3), im_openout(3), im_add_eval_callback(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris 23/04/1991 diff --git a/libsrc/iofuncs/memory.c b/libsrc/iofuncs/memory.c new file mode 100644 index 00000000..2b9bd068 --- /dev/null +++ b/libsrc/iofuncs/memory.c @@ -0,0 +1,209 @@ +/* @(#) memory.c: mem handling stuff + * + * 2/11/99 JC + * - from im_open.c and callback.c + * - malloc tracking stuff added + * 11/3/01 JC + * - im_strncpy() added + * 20/4/01 JC + * - im_(v)snprintf() added + * 6/7/05 + * - more tracking for DEBUGM + * 20/10/06 + * - return NULL for size <= 0 + * 11/5/06 + * - abort() on malloc() failure with DEBUG + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define for simple malloc tracking ... better to use dmalloc if you can. +#define DEBUGM + */ + +/* abort() on memory errors. +#define DEBUG + */ + +#ifdef DEBUG +# warning DEBUG on in libsrc/iofuncs/memory.c +#endif /*DEBUG*/ + +/* Track total alloc/total free here for debugging. + */ +#ifdef DEBUGM +static size_t int total_mem_alloc = 0; +static unsigned int total_allocs = 0; +static size_t int high_water_mark = 0; +static GMutex *malloc_mutex = NULL; +static GSList *malloc_list = NULL; +static const int trace_freq = 100; /* Msg every this many malloc/free */ +static int next_trace = 0; +#endif /*DEBUGM*/ + +/* VIPS free function. Try to put all vips free() through this. + */ +int +im_free( void *s ) +{ +#ifdef DEBUGM +{ + size_t size; + + s = (void *) ((char*)s - 16); + size = *((size_t*)s); + g_mutex_lock( malloc_mutex ); + + assert( g_slist_find( malloc_list, s ) ); + malloc_list = g_slist_remove( malloc_list, s ); + assert( !g_slist_find( malloc_list, s ) ); + malloc_list = g_slist_remove( malloc_list, s ); + assert( total_allocs > 0 ); + + total_mem_alloc -= size; + total_allocs -= 1; + + next_trace += 1; + if( next_trace > trace_freq ) { + printf( "im_free: %d, %d allocs, total %.3gM, " + "high water %.3gM\n", + size, + total_allocs, + total_mem_alloc / (1024.0 * 1024.0), + high_water_mark / (1024.0 * 1024.0) ); + next_trace = 0; + } + + g_mutex_unlock( malloc_mutex ); +} +#endif /*DEBUGM*/ + +#ifdef DEBUG + if( !s ) + abort(); +#endif /*DEBUG*/ + + free( s ); + + return( 0 ); +} + +/* Malloc local to a descriptor. Try to put all vips malloc through this. Not + * thread-safe if im != NULL. + */ +void * +im_malloc( IMAGE *im, size_t size ) +{ + void *buf; + +#ifdef DEBUGM + /* Assume the first im_malloc() is single-threaded. + */ + if( !malloc_mutex ) + malloc_mutex = g_mutex_new(); +#endif /*DEBUGM*/ + +#ifdef DEBUGM + /* If debugging mallocs, need an extra sizeof(uint) bytes to track + * size of this block. Ask for an extra 16 to make sure we don't break + * alignment rules. + */ + size += 16; +#endif /*DEBUGM*/ + + if( !(buf = malloc( size )) ) { +#ifdef DEBUG + abort(); +#endif /*DEBUG*/ + + im_error( "im_malloc", + _( "out of memory --- size == %dMB" ), + (int) (size / (1024.0*1024.0)) ); + im_warn( "im_malloc", + _( "out of memory --- size == %dMB" ), + (int) (size / (1024.0*1024.0)) ); + return( NULL ); + } + +#ifdef DEBUGM + /* Record number alloced. + */ + g_mutex_lock( malloc_mutex ); + assert( !g_slist_find( malloc_list, buf ) ); + malloc_list = g_slist_prepend( malloc_list, buf ); + *((size_t*)buf) = size; + buf = (void *) ((char*)buf + 16); + total_mem_alloc += size; + if( total_mem_alloc > high_water_mark ) + high_water_mark = total_mem_alloc; + total_allocs += 1; + + next_trace += 1; + if( next_trace > trace_freq ) { + printf( "im_malloc: %d, %d allocs, total %.3gM, " + "high water %.3gM\n", + size, + total_allocs, + total_mem_alloc / (1024.0 * 1024.0), + high_water_mark / (1024.0 * 1024.0) ); + next_trace = 0; + } + + g_mutex_unlock( malloc_mutex ); + + /* Handy to breakpoint on this printf() for catching large mallocs(). + */ + if( size > 1000000 ) + printf( "woah! big!\n" ); +#endif /*DEBUGM*/ + + if( im && im_add_close_callback( im, + (im_callback_fn) im_free, buf, NULL ) ) { + im_free( buf ); + return( NULL ); + } + + return( buf ); +} diff --git a/libsrc/iofuncs/meta.c b/libsrc/iofuncs/meta.c new file mode 100644 index 00000000..819b10fb --- /dev/null +++ b/libsrc/iofuncs/meta.c @@ -0,0 +1,926 @@ +/* Meta information on an IMAGE. Extra fields added by the user/client/etc. + * + * 6/6/05 + * - hacked from code from Markus Wollgarten + * 30/6/05 JC + * - take a copy of field names so we work for sprintf()'d fields + * - separate GType for refstring so we can spot it from im_header_map() + * 13/7/05 + * - added BLOB too (ie. can be saved to xml) + * 26/8/05 + * - get_ funcs set im_error() + * 29/8/05 + * - added im__meta_destroy() + * 1/9/05 + * - oop, hash table insert/replace confusion fixed + */ + +/* + + Copyright (C) 1991-2005 The National Gallery + + 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 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ + +#include + +#include + +#include "base64.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* + + The GValue we store can be a number, mutable string, ref-counted + immutable string (eg. large chunk of XML), etc. Convenience + functions make it less painful. See im_meta_get_int() etc. + + */ + +#ifdef DEBUG +/* Check that this meta is on the hash table. + */ +static void * +meta_sanity_on_hash( Meta *meta, IMAGE *im ) +{ + Meta *found; + + if( meta->im != im ) + printf( "*** field \"%s\" has incorrect im\n", + meta->field ); + + if( !(found = g_hash_table_lookup( im->Meta, meta->field )) ) + printf( "*** field \"%s\" is on traverse but not in hash\n", + meta->field ); + + if( found != meta ) + printf( "*** meta \"%s\" on traverse and hash do not match\n", + meta->field ); + + return( NULL ); +} + +static void +meta_sanity_on_traverse( const char *field, Meta *meta, IMAGE *im ) +{ + if( meta->field != field ) + printf( "*** field \"%s\" has incorrect field\n", + meta->field ); + + if( meta->im != im ) + printf( "*** field \"%s\" has incorrect im\n", + meta->field ); + + if( !g_slist_find( im->Meta_traverse, meta ) ) + printf( "*** field \"%s\" is in hash but not on traverse\n", + meta->field ); +} + +static void +meta_sanity( const IMAGE *im ) +{ + if( im->Meta ) + g_hash_table_foreach( im->Meta, + (GHFunc) meta_sanity_on_traverse, (void *) im ); + im_slist_map2( im->Meta_traverse, + (VSListMap2Fn) meta_sanity_on_hash, (void *) im, NULL ); +} +#endif /*DEBUG*/ + +static void +meta_free( Meta *meta ) +{ +#ifdef DEBUG +{ + char *str_value; + + str_value = g_strdup_value_contents( &meta->value ); + printf( "meta_free: field %s, value = %s\n", + meta->field, str_value ); + g_free( str_value ); +} +#endif /*DEBUG*/ + + if( meta->im ) + meta->im->Meta_traverse = + g_slist_remove( meta->im->Meta_traverse, meta ); + + g_value_unset( &meta->value ); + IM_FREE( meta->field ); + im_free( meta ); +} + +static Meta * +meta_new( IMAGE *im, const char *field, GValue *value ) +{ + Meta *meta; + + if( !(meta = IM_NEW( NULL, Meta )) ) + return( NULL ); + meta->im = im; + meta->field = NULL; + memset( &meta->value, 0, sizeof( GValue ) ); + + if( !(meta->field = im_strdup( NULL, field )) ) { + meta_free( meta ); + return( NULL ); + } + + g_value_init( &meta->value, G_VALUE_TYPE( value ) ); + g_value_copy( value, &meta->value ); + + im->Meta_traverse = g_slist_append( im->Meta_traverse, meta ); + g_hash_table_replace( im->Meta, meta->field, meta ); + +#ifdef DEBUG +{ + char *str_value; + + str_value = g_strdup_value_contents( value ); + printf( "meta_new: field %s, value = %s\n", + field, str_value ); + g_free( str_value ); +} +#endif /*DEBUG*/ + + return( meta ); +} + +/* Destroy all the meta on an image. + */ +void +im__meta_destroy( IMAGE *im ) +{ + IM_FREEF( g_hash_table_destroy, im->Meta ); + assert( !im->Meta_traverse ); +} + +static void +meta_init( IMAGE *im ) +{ + if( !im->Meta ) { + assert( !im->Meta_traverse ); + im->Meta = g_hash_table_new_full( g_str_hash, g_str_equal, + NULL, (GDestroyNotify) meta_free ); + } +} + +static void * +meta_cp_field( Meta *meta, IMAGE *dst ) +{ + Meta *meta_copy; + +#ifdef DEBUG +{ + char *str_value; + + str_value = g_strdup_value_contents( &meta->value ); + printf( "im__meta_cp: copying field %s, value = %s\n", + meta->field, str_value ); + g_free( str_value ); +} +#endif /*DEBUG*/ + + /* No way to return error here, sadly. + */ + meta_copy = meta_new( dst, meta->field, &meta->value ); + +#ifdef DEBUG + meta_sanity( dst ); +#endif /*DEBUG*/ + + return( NULL ); +} + +/* Copy meta on to dst. Called from im_cp_desc(). + */ +int +im__meta_cp( IMAGE *dst, const IMAGE *src ) +{ + if( src->Meta ) { + /* Loop, copying fields. + */ + meta_init( dst ); + im_slist_map2( src->Meta_traverse, + (VSListMap2Fn) meta_cp_field, dst, NULL ); + } + + return( 0 ); +} + +/* Set a meta, overwriting any old meta. + */ +int +im_meta_set( IMAGE *im, const char *field, GValue *value ) +{ + Meta *meta; + + assert( field ); + assert( value ); + + meta_init( im ); + if( !(meta = meta_new( im, field, value )) ) + return( -1 ); + +#ifdef DEBUG + meta_sanity( im ); +#endif /*DEBUG*/ + + return( 0 ); +} + +/* Fill value with a copy of the meta, -1 on error. value_copy must be zeroed + * but uninitialised. + */ +int +im_meta_get( IMAGE *im, const char *field, GValue *value_copy ) +{ + Meta *meta; + + assert( field ); + assert( value_copy ); + + /* Defined? + */ + if( !im->Meta || !(meta = g_hash_table_lookup( im->Meta, field )) ) { + im_error( "im_meta_get", _( "field \"%s\" not found" ), field ); + return( -1 ); + } + + g_value_init( value_copy, G_VALUE_TYPE( &meta->value ) ); + g_value_copy( &meta->value, value_copy ); + + return( 0 ); +} + +GType +im_meta_get_type( IMAGE *im, const char *field ) +{ + Meta *meta; + + assert( field ); + + /* Defined? + */ + if( !im->Meta || !(meta = g_hash_table_lookup( im->Meta, field )) ) + return( 0 ); + + return( G_VALUE_TYPE( &meta->value ) ); +} + +/* Helpers for set/get. Write a value and destroy it. + */ +static int +meta_set_value( IMAGE *im, const char *field, GValue *value ) +{ + if( im_meta_set( im, field, value ) ) { + g_value_unset( value ); + return( -1 ); + } + g_value_unset( value ); + + return( 0 ); +} + +static int +meta_get_value( IMAGE *im, const char *field, GType type, GValue *value_copy ) +{ + if( im_meta_get( im, field, value_copy ) ) + return( -1 ); + if( G_VALUE_TYPE( value_copy ) != type ) { + im_error( "im_meta_get", _( "field \"%s\" " + "is of type %s, not %s" ), + field, + g_type_name( G_VALUE_TYPE( value_copy ) ), + g_type_name( type ) ); + g_value_unset( value_copy ); + return( -1 ); + } + + return( 0 ); +} + +/* Save meta fields to the header. We have a new string type for header fields + * to save to XML and define transform functions to go from our meta types to + * this string type. + */ +GType +im_save_string_get_type( void ) +{ + static GType type = 0; + + if( !type ) { + type = g_boxed_type_register_static( "im_save_string", + (GBoxedCopyFunc) g_strdup, + (GBoxedFreeFunc) g_free ); + } + + return( type ); +} + +const char * +im_save_string_get( const GValue *value ) +{ + return( (char *) g_value_get_boxed( value ) ); +} + +void +im_save_string_set( GValue *value, const char *str ) +{ + assert( G_VALUE_TYPE( value ) == IM_TYPE_SAVE_STRING ); + + g_value_set_boxed( value, str ); +} + +void +im_save_string_setf( GValue *value, const char *fmt, ... ) +{ + va_list ap; + char *str; + + assert( G_VALUE_TYPE( value ) == IM_TYPE_SAVE_STRING ); + + va_start( ap, fmt ); + str = g_strdup_vprintf( fmt, ap ); + va_end( ap ); + + im_save_string_set( value, str ); +} + +/* Read/write int, double metadata. + */ +int +im_meta_set_int( IMAGE *im, const char *field, int i ) +{ + GValue value = { 0 }; + + g_value_init( &value, G_TYPE_INT ); + g_value_set_int( &value, i ); + + return( meta_set_value( im, field, &value ) ); +} + +int +im_meta_get_int( IMAGE *im, const char *field, int *i ) +{ + GValue value_copy = { 0 }; + + if( meta_get_value( im, field, G_TYPE_INT, &value_copy ) ) + return( -1 ); + *i = g_value_get_int( &value_copy ); + g_value_unset( &value_copy ); + + return( 0 ); +} + +int +im_meta_set_double( IMAGE *im, const char *field, double d ) +{ + GValue value = { 0 }; + + g_value_init( &value, G_TYPE_DOUBLE ); + g_value_set_double( &value, d ); + + return( meta_set_value( im, field, &value ) ); +} + +int +im_meta_get_double( IMAGE *im, const char *field, double *d ) +{ + GValue value_copy = { 0 }; + + if( meta_get_value( im, field, G_TYPE_DOUBLE, &value_copy ) ) + return( -1 ); + *d = g_value_get_double( &value_copy ); + g_value_unset( &value_copy ); + + return( 0 ); +} + +/* Transform funcs for builtin types to SAVE_STRING. + */ +static void +transform_int_save_string( const GValue *src_value, GValue *dest_value ) +{ + im_save_string_setf( dest_value, "%d", g_value_get_int( src_value ) ); +} + +static void +transform_save_string_int( const GValue *src_value, GValue *dest_value ) +{ + g_value_set_int( dest_value, atoi( im_save_string_get( src_value ) ) ); +} + +static void +transform_double_save_string( const GValue *src_value, GValue *dest_value ) +{ + char buf[G_ASCII_DTOSTR_BUF_SIZE]; + + /* Need to be locale independent. + */ + g_ascii_dtostr( buf, G_ASCII_DTOSTR_BUF_SIZE, + g_value_get_double( src_value ) ); + im_save_string_set( dest_value, buf ); +} + +static void +transform_save_string_double( const GValue *src_value, GValue *dest_value ) +{ + g_value_set_double( dest_value, + g_ascii_strtod( im_save_string_get( src_value ), NULL ) ); +} + +/* A GType for a ref-counted area of memory. + */ +typedef struct _Area { + int count; + size_t length; /* 0 if not known */ + void *data; + im_callback_fn free_fn; +} Area; + +#ifdef DEBUG +static int area_number = 0; +#endif /*DEBUG*/ + +/* An area of mem with a free func. (eg. \0-terminated string, or a struct). + * Inital count == 1, so _unref() after attaching somewhere. + */ +static Area * +area_new( im_callback_fn free_fn, void *data ) +{ + Area *area; + + if( !(area = IM_NEW( NULL, Area )) ) + return( NULL ); + area->count = 1; + area->length = 0; + area->data = data; + area->free_fn = free_fn; + +#ifdef DEBUG + area_number += 1; + printf( "area_new: %p count = %d (%d in total)\n", + area, area->count, area_number ); +#endif /*DEBUG*/ + + return( area ); +} + +/* An area of mem with a free func and a length (some sort of binary object, + * like an ICC profile). + */ +static Area * +area_new_blob( im_callback_fn free_fn, void *blob, size_t blob_length ) +{ + Area *area; + + if( !(area = area_new( free_fn, blob )) ) + return( NULL ); + area->length = blob_length; + + return( area ); +} + +static Area * +area_copy( Area *area ) +{ + assert( area->count >= 0 ); + + area->count += 1; + +#ifdef DEBUG + printf( "area_copy: %p count = %d\n", area, area->count ); +#endif /*DEBUG*/ + + return( area ); +} + +static void +area_unref( Area *area ) +{ + assert( area->count > 0 ); + + area->count -= 1; + +#ifdef DEBUG + printf( "area_unref: %p count = %d\n", area, area->count ); +#endif /*DEBUG*/ + + if( area->count == 0 && area->free_fn ) { + (void) area->free_fn( area->data, NULL ); + area->free_fn = NULL; + im_free( area ); + +#ifdef DEBUG + area_number -= 1; + printf( "area_unref: free .. total = %d\n", area_number ); +#endif /*DEBUG*/ + } +} + +/* Transform an area to a G_TYPE_STRING. + */ +static void +transform_area_g_string( const GValue *src_value, GValue *dest_value ) +{ + Area *area; + char buf[256]; + + area = g_value_get_boxed( src_value ); + im_snprintf( buf, 256, "IM_TYPE_AREA, count = %d, data = %p", + area->count, area->data ); + g_value_set_string( dest_value, buf ); +} + +GType +im_area_get_type( void ) +{ + static GType type = 0; + + if( !type ) { + type = g_boxed_type_register_static( "im_area", + (GBoxedCopyFunc) area_copy, + (GBoxedFreeFunc) area_unref ); + g_value_register_transform_func( + type, + G_TYPE_STRING, + transform_area_g_string ); + } + + return( type ); +} + +/* Set value to be a ref-counted area of memory with a free function. + */ +static int +value_set_area( im_callback_fn free_fn, void *data, GValue *value ) +{ + Area *area; + + if( !(area = area_new( free_fn, data )) ) + return( -1 ); + + g_value_init( value, IM_TYPE_AREA ); + g_value_set_boxed( value, area ); + area_unref( area ); + + return( 0 ); +} + +/* Don't touch count (area is static). + */ +static void * +value_get_area_data( const GValue *value ) +{ + Area *area; + + area = g_value_get_boxed( value ); + + return( area->data ); +} + +static size_t +value_get_area_length( const GValue *value ) +{ + Area *area; + + area = g_value_get_boxed( value ); + + return( area->length ); +} + +/* Read/write ref-counted mem area. + */ +int +im_meta_set_area( IMAGE *im, const char *field, + im_callback_fn free_fn, void *data ) +{ + GValue value = { 0 }; + + value_set_area( free_fn, data, &value ); + + return( meta_set_value( im, field, &value ) ); +} + +int +im_meta_get_area( IMAGE *im, const char *field, void **data ) +{ + GValue value_copy = { 0 }; + + if( meta_get_value( im, field, IM_TYPE_AREA, &value_copy ) ) + return( -1 ); + *data = value_get_area_data( &value_copy ); + g_value_unset( &value_copy ); + + return( 0 ); +} + +/* Get a char* from a refstring. + */ +const char * +im_ref_string_get( const GValue *value ) +{ + return( value_get_area_data( value ) ); +} + +/* Get cached strlen() from a refstring. + */ +size_t +im_ref_string_get_length( const GValue *value ) +{ + return( value_get_area_length( value ) ); +} + +/* Set value to be a ref-counted string. + */ +int +im_ref_string_set( GValue *value, const char *str ) +{ + Area *area; + char *str_copy; + + assert( G_VALUE_TYPE( value ) == IM_TYPE_REF_STRING ); + + if( !(str_copy = im_strdup( NULL, str )) ) + return( -1 ); + if( !(area = area_new( (im_callback_fn) im_free, str_copy )) ) { + im_free( str_copy ); + return( -1 ); + } + + /* Handy place to cache this. + */ + area->length = strlen( str ); + + g_value_set_boxed( value, area ); + area_unref( area ); + + return( 0 ); +} + +/* Transform a refstring to a G_TYPE_STRING. + */ +static void +transform_ref_string_g_string( const GValue *src_value, GValue *dest_value ) +{ + g_value_set_string( dest_value, im_ref_string_get( src_value ) ); +} + +/* To a save string. + */ +static void +transform_ref_string_save_string( const GValue *src_value, GValue *dest_value ) +{ + im_save_string_setf( dest_value, "%s", im_ref_string_get( src_value ) ); +} + +static void +transform_save_string_ref_string( const GValue *src_value, GValue *dest_value ) +{ + im_ref_string_set( dest_value, im_save_string_get( src_value ) ); +} + +GType +im_ref_string_get_type( void ) +{ + static GType type = 0; + + if( !type ) { + type = g_boxed_type_register_static( "im_ref_string", + (GBoxedCopyFunc) area_copy, + (GBoxedFreeFunc) area_unref ); + g_value_register_transform_func( type, G_TYPE_STRING, + transform_ref_string_g_string ); + g_value_register_transform_func( type, IM_TYPE_SAVE_STRING, + transform_ref_string_save_string ); + g_value_register_transform_func( IM_TYPE_SAVE_STRING, type, + transform_save_string_ref_string ); + } + + return( type ); +} + +/* Read/write C string. + */ +int +im_meta_set_string( IMAGE *im, const char *field, const char *str ) +{ + GValue value = { 0 }; + + g_value_init( &value, IM_TYPE_REF_STRING ); + im_ref_string_set( &value, str ); + + return( meta_set_value( im, field, &value ) ); +} + +int +im_meta_get_string( IMAGE *im, const char *field, char **str ) +{ + GValue value_copy = { 0 }; + Area *area; + + if( meta_get_value( im, field, IM_TYPE_REF_STRING, &value_copy ) ) + return( -1 ); + area = g_value_get_boxed( &value_copy ); + *str = area->data; + g_value_unset( &value_copy ); + + return( 0 ); +} + +/* Get a void * from a BLOB. + */ +void * +im_blob_get( const GValue *value, size_t *blob_length ) +{ + Area *area; + + /* Can't check value type, because we may get called from + * im_blob_get_type(). + */ + + area = g_value_get_boxed( value ); + if( blob_length ) + *blob_length = area->length; + + return( area->data ); +} + +/* Transform a blob to a G_TYPE_STRING. + */ +static void +transform_blob_g_string( const GValue *src_value, GValue *dest_value ) +{ + void *blob; + size_t blob_length; + char buf[256]; + + blob = im_blob_get( src_value, &blob_length ); + im_snprintf( buf, 256, "IM_TYPE_BLOB, data = %p, length = %zd", + blob, blob_length ); + g_value_set_string( dest_value, buf ); +} + +/* Transform a blob to a save string and back. + */ +static void +transform_blob_save_string( const GValue *src_value, GValue *dest_value ) +{ + void *blob; + size_t blob_length; + char *b64; + + blob = im_blob_get( src_value, &blob_length ); + if( (b64 = im__b64_encode( blob, blob_length )) ) { + im_save_string_set( dest_value, b64 ); + im_free( b64 ); + } +} + +static void +transform_save_string_blob( const GValue *src_value, GValue *dest_value ) +{ + const char *b64; + void *blob; + size_t blob_length; + + b64 = im_save_string_get( src_value ); + if( (blob = im__b64_decode( b64, &blob_length )) ) + im_blob_set( dest_value, + (im_callback_fn) im_free, blob, blob_length ); +} + +GType +im_blob_get_type( void ) +{ + static GType type = 0; + + if( !type ) { + type = g_boxed_type_register_static( "im_blob", + (GBoxedCopyFunc) area_copy, + (GBoxedFreeFunc) area_unref ); + g_value_register_transform_func( type, G_TYPE_STRING, + transform_blob_g_string ); + g_value_register_transform_func( type, IM_TYPE_SAVE_STRING, + transform_blob_save_string ); + g_value_register_transform_func( IM_TYPE_SAVE_STRING, type, + transform_save_string_blob ); + } + + return( type ); +} + +/* Set value to be a blob. + */ +int +im_blob_set( GValue *value, + im_callback_fn free_fn, void *blob, size_t blob_length ) +{ + Area *area; + + assert( G_VALUE_TYPE( value ) == IM_TYPE_BLOB ); + + if( !(area = area_new_blob( free_fn, blob, blob_length )) ) + return( -1 ); + + g_value_set_boxed( value, area ); + area_unref( area ); + + return( 0 ); +} + +/* Read/write blob. + */ +int +im_meta_set_blob( IMAGE *im, const char *field, + im_callback_fn free_fn, void *blob, size_t blob_length ) +{ + GValue value = { 0 }; + + g_value_init( &value, IM_TYPE_BLOB ); + im_blob_set( &value, free_fn, blob, blob_length ); + + return( meta_set_value( im, field, &value ) ); +} + +int +im_meta_get_blob( IMAGE *im, const char *field, + void **blob, size_t *blob_length ) +{ + GValue value_copy = { 0 }; + + if( meta_get_value( im, field, IM_TYPE_BLOB, &value_copy ) ) + return( -1 ); + *blob = im_blob_get( &value_copy, blob_length ); + g_value_unset( &value_copy ); + + return( 0 ); +} + +/* Make the types we need for basic functioning. Called from init_world(). + */ +void +im__meta_init_types( void ) +{ + (void) im_save_string_get_type(); + (void) im_area_get_type(); + (void) im_ref_string_get_type(); + (void) im_blob_get_type(); + + /* Register transform functions to go from built-in saveable types to + * a save string. Transform functions for our own types are set + * during type creation. + */ + g_value_register_transform_func( G_TYPE_INT, IM_TYPE_SAVE_STRING, + transform_int_save_string ); + g_value_register_transform_func( IM_TYPE_SAVE_STRING, G_TYPE_INT, + transform_save_string_int ); + g_value_register_transform_func( G_TYPE_DOUBLE, IM_TYPE_SAVE_STRING, + transform_double_save_string ); + g_value_register_transform_func( IM_TYPE_SAVE_STRING, G_TYPE_DOUBLE, + transform_save_string_double ); +} diff --git a/libsrc/iofuncs/package.c b/libsrc/iofuncs/package.c new file mode 100644 index 00000000..7513979b --- /dev/null +++ b/libsrc/iofuncs/package.c @@ -0,0 +1,1029 @@ +/* VIPS package handling. + * + * J. Cupitt, 8/4/93. + * + * 18/2/04 JC + * - now uses g_module_*() instead of dlopen() + * 9/8/04 + * - uses glib dir scanning stuff instead of dirent.h + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Standard VIPS packages. + */ +extern im_package im__arithmetic; +extern im_package im__boolean; +extern im_package im__colour; +extern im_package im__conversion; +extern im_package im__convolution; +extern im_package im__freq_filt; +extern im_package im__histograms_lut; +extern im_package im__inplace; +extern im_package im__matrix; +extern im_package im__morphology; +extern im_package im__mosaicing; +extern im_package im__other; +extern im_package im__relational; +extern im_package im__video; + +/* im_guess_prefix() args. + */ +static im_arg_desc guess_prefix_args[] = { + IM_INPUT_STRING( "argv0" ), + IM_INPUT_STRING( "env_name" ), + IM_OUTPUT_STRING( "PREFIX" ) +}; + +/* Call im_guess_prefix() via arg vector. + */ +static int +guess_prefix_vec( im_object *argv ) +{ + const char *prefix = im_guess_prefix( argv[0], argv[1] ); + + if( !prefix ) { + argv[2] = NULL; + return( -1 ); + } + + argv[2] = im_strdup( NULL, prefix ); + + return( 0 ); +} + +/* Description of im_guess_prefix. + */ +static im_function guess_prefix_desc = { + "im_guess_prefix", /* Name */ + "guess install area", /* Description */ + 0, /* Flags */ + guess_prefix_vec, /* Dispatch function */ + IM_NUMBER( guess_prefix_args ), /* Size of arg list */ + guess_prefix_args /* Arg list */ +}; + +/* im_header_int() args. + */ +static im_arg_desc header_int_args[] = { + IM_INPUT_STRING( "field" ), + IM_INPUT_IMAGE( "image" ), + IM_OUTPUT_INT( "value" ) +}; + +/* Call im_header_int() via arg vector. + */ +static int +header_int_vec( im_object *argv ) +{ + return( im_header_int( (IMAGE *) argv[1], (const char *) argv[0], + (int *) argv[2] ) ); +} + +/* Description of im_header_int(). + */ +static im_function header_int_desc = { + "im_header_int", /* Name */ + "extract int fields from header", /* Description */ + 0, /* Flags */ + header_int_vec, /* Dispatch function */ + IM_NUMBER( header_int_args ), /* Size of arg list */ + header_int_args /* Arg list */ +}; + +/* im_header_get_type() args. + */ +static im_arg_desc header_get_type_args[] = { + IM_INPUT_STRING( "field" ), + IM_INPUT_IMAGE( "image" ), + IM_OUTPUT_INT( "gtype" ) +}; + +/* Call im_header_get_type() via arg vector. + */ +static int +header_get_type_vec( im_object *argv ) +{ + int *out = ((int *) argv[2]); + + *out = im_header_get_type( (IMAGE *) argv[1], (const char *) argv[0] ); + + return( 0 ); +} + +/* Description of im_header_get_type(). + */ +static im_function header_get_type_desc = { + "im_header_get_type", /* Name */ + "return field type", /* Description */ + 0, /* Flags */ + header_get_type_vec, /* Dispatch function */ + IM_NUMBER( header_get_type_args ),/* Size of arg list */ + header_get_type_args /* Arg list */ +}; + +/* im_header_double() args. + */ +static im_arg_desc header_double_args[] = { + IM_INPUT_STRING( "field" ), + IM_INPUT_IMAGE( "image" ), + IM_OUTPUT_DOUBLE( "value" ) +}; + +/* Call im_header_double() via arg vector. + */ +static int +header_double_vec( im_object *argv ) +{ + return( im_header_double( (IMAGE *) argv[1], (const char *) argv[0], + (double *) argv[2] ) ); +} + +/* Description of im_header_double(). + */ +static im_function header_double_desc = { + "im_header_double", /* Name */ + "extract double fields from header", /* Description */ + 0, /* Flags */ + header_double_vec, /* Dispatch function */ + IM_NUMBER( header_double_args ), /* Size of arg list */ + header_double_args /* Arg list */ +}; + +/* im_header_string() args. + */ +static im_arg_desc header_string_args[] = { + IM_INPUT_STRING( "field" ), + IM_INPUT_IMAGE( "image" ), + IM_OUTPUT_STRING( "value" ) +}; + +/* Call im_header_string() via arg vector. + */ +static int +header_string_vec( im_object *argv ) +{ + char *out; + + if( im_header_string( (IMAGE *) argv[1], + (const char *) argv[0], &out ) || + !(argv[2] = im_strdup( NULL, out )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_header_string(). + */ +static im_function header_string_desc = { + "im_header_string", /* Name */ + "extract string fields from header", /* Description */ + 0, /* Flags */ + header_string_vec, /* Dispatch function */ + IM_NUMBER( header_string_args ), /* Size of arg list */ + header_string_args /* Arg list */ +}; + +/* im_version_string() args. + */ +static im_arg_desc version_string_args[] = { + IM_OUTPUT_STRING( "version" ) +}; + +/* Call im_version_string() via arg vector. + */ +static int +version_string_vec( im_object *argv ) +{ + if( !(argv[0] = im_strdup( NULL, im_version_string() )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_version_string. + */ +static im_function version_string_desc = { + "im_version_string", /* Name */ + "VIPS version string", /* Description */ + 0, /* Flags */ + version_string_vec, /* Dispatch function */ + IM_NUMBER( version_string_args ), /* Size of arg list */ + version_string_args /* Arg list */ +}; + +/* im_version() args. + */ +static im_arg_desc version_args[] = { + IM_INPUT_INT( "flag" ), + IM_OUTPUT_INT( "version" ) +}; + +/* Call im_version() via arg vector. + */ +static int +version_vec( im_object *argv ) +{ + int flag = *((int *) argv[0]); + int *out = ((int *) argv[1]); + + int version = im_version( flag ); + + if( version < 0 ) + return( -1 ); + + *out = version; + + return( 0 ); +} + +/* Description of im_version. + */ +static im_function version_desc = { + "im_version", /* Name */ + "VIPS version number", /* Description */ + 0, /* Flags */ + version_vec, /* Dispatch function */ + IM_NUMBER( version_args ), /* Size of arg list */ + version_args /* Arg list */ +}; + +/* im_cache() args. + */ +static im_arg_desc cache_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "tile_width" ), + IM_INPUT_INT( "tile_height" ), + IM_INPUT_INT( "max_tiles" ) +}; + +/* Call im_cache() via arg vector. + */ +static int +cache_vec( im_object *argv ) +{ + int tile_width = *((int *) argv[2]); + int tile_height = *((int *) argv[3]); + int max_tiles = *((int *) argv[4]); + + return( im_cache( argv[0], argv[1], + tile_width, tile_height, max_tiles ) ); +} + +/* Description of im_cache. + */ +static im_function cache_desc = { + "im_cache", /* Name */ + "cache results of an operation",/* Description */ + 0, /* Flags */ + cache_vec, /* Dispatch function */ + IM_NUMBER( cache_args ), /* Size of arg list */ + cache_args /* Arg list */ +}; + +/* im_binfile() args. + */ +static im_arg_desc binfile_args[] = { + IM_INPUT_STRING( "filename" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ), + IM_INPUT_INT( "bands" ), + IM_INPUT_INT( "offset" ) +}; + +/* Call im_binfile() via arg vector. + */ +static int +binfile_vec( im_object *argv ) +{ + int width = *((int *) argv[2]); + int height = *((int *) argv[3]); + int bands = *((int *) argv[4]); + int offset = *((int *) argv[5]); + IMAGE *im; + + if( !(im = im_binfile( argv[0], width, height, bands, offset )) ) + return( -1 ); + + if( im_copy( im, argv[1] ) || + im_add_close_callback( argv[1], + (im_callback_fn) im_close, im, NULL ) ) { + im_close( im ); + return( -1 ); + } + + return( 0 ); +} + +/* Description of im_binfile. + */ +static im_function binfile_desc = { + "im_binfile", /* Name */ + "open a headerless binary file",/* Description */ + 0, /* Flags */ + binfile_vec, /* Dispatch function */ + IM_NUMBER( binfile_args ), /* Size of arg list */ + binfile_args /* Arg list */ +}; + +/* Package up iofuncs functions. + */ +static im_function *iofuncs_list[] = { + &binfile_desc, + &cache_desc, + &guess_prefix_desc, + &header_get_type_desc, + &header_int_desc, + &header_double_desc, + &header_string_desc, + &version_desc, + &version_string_desc +}; + +/* Package of io functions. + */ +static im_package im__iofuncs = { + "iofuncs", + IM_NUMBER( iofuncs_list ), + iofuncs_list +}; + +/* List of built-in VIPS packages. + */ +static im_package *built_in[] = { + &im__arithmetic, + &im__boolean, + &im__colour, + &im__conversion, + &im__convolution, + &im__freq_filt, + &im__histograms_lut, + &im__inplace, + &im__iofuncs, + &im__matrix, + &im__morphology, + &im__mosaicing, + &im__other, + &im__relational, + &im__video +}; + +/* How we represent a loaded plugin. + */ +typedef struct _Plugin { + GModule *module; /* As loaded by g_module_open() */ + char *name; /* Name we loaded */ + im_package *pack; /* Package table */ +} Plugin; + +/* List of loaded plugins. + */ +static GSList *plugin_list = NULL; + +/* Free a plugin. + */ +static int +plugin_free( Plugin *plug ) +{ + char *name = plug->name ? plug->name : ""; + + if( plug->module ) { + if( !g_module_close( plug->module ) ) { + im_error( "plugin", + _( "unable to close plugin \"%s\"" ), name ); + im_error( "plugin", "%s", g_module_error() ); + return( -1 ); + } + + plug->module = NULL; + } + IM_FREE( plug->name ); + plug->pack = NULL; + im_free( plug ); + + plugin_list = g_slist_remove( plugin_list, plug ); + + return( 0 ); +} + +/* Load a plugin. + */ +im_package * +im_load_plugin( const char *name ) +{ + Plugin *plug; + + if( !g_module_supported() ) { + im_error( "plugin", + _( "plugins not supported on this platform" ) ); + return( NULL ); + } + + /* Build a new plugin. + */ + if( !(plug = IM_NEW( NULL, Plugin )) ) + return( NULL ); + plug->module = NULL; + plug->name = NULL; + plug->pack = NULL; + plugin_list = g_slist_prepend( plugin_list, plug ); + + /* Attach name. + */ + if( !(plug->name = im_strdup( NULL, name )) ) { + plugin_free( plug ); + return( NULL ); + } + + /* Open library. + */ + if( !(plug->module = g_module_open( name, 0 )) ) { + im_error( "plugin", _( "unable to open plugin \"%s\"" ), name ); + im_error( "plugin", "%s", g_module_error() ); + plugin_free( plug ); + + return( NULL ); + } + + /* Find package. + */ + /* Bizarre double-cast stops a bogus gcc 4.1 compiler warning. + */ + if( !g_module_symbol( plug->module, + "package_table", (gpointer *) ((void *) &plug->pack) ) ) { + im_error( "plugin", + _( "unable to find symbol \"package_table\" " + "in plugin \"%s\"" ), name ); + im_error( "plugin", "%s", g_module_error() ); + plugin_free( plug ); + + return( NULL ); + } + + /* Sanity check. + */ + if( !plug->pack->name || plug->pack->nfuncs < 0 || + plug->pack->nfuncs > 10000 ) { + im_error( "plugin", + _( "corrupted package table in plugin \"%s\"" ), + name ); + plugin_free( plug ); + + return( NULL ); + } + +#ifdef DEBUG + printf( "added package \"%s\" ...\n", plug->pack->name ); +#endif /*DEBUG*/ + + return( plug->pack ); +} + +/* Load all plugins in a directory ... look for '.plg' suffix. Error if we had + * any probs. + */ +int +im_load_plugins( const char *fmt, ... ) +{ + va_list ap; + char dir_name[PATH_MAX]; + GDir *dir; + const char *name; + int result; + + /* Silently succeed if we can't do modules. + */ + if( !g_module_supported() ) + return( 0 ); + + va_start( ap, fmt ); + (void) im_vsnprintf( dir_name, PATH_MAX - 1, fmt, ap ); + va_end( ap ); + + if( !(dir = g_dir_open( dir_name, 0, NULL )) ) { + im_error( "plugin", + "unable to open directory \"%s\"", dir_name ); + return( -1 ); + } + + result = 0; + while( (name = g_dir_read_name( dir )) ) + if( im_ispostfix( name, ".plg" ) ) { + char path[PATH_MAX]; + + im_snprintf( path, PATH_MAX - 1, + "%s" G_DIR_SEPARATOR_S "%s", dir_name, name ); + if( !im_load_plugin( path ) ) + result = -1; + } + g_dir_close( dir ); + + return( result ); +} + +/* Close all loaded plugins. + */ +int +im_close_plugins( void ) +{ + while( plugin_list ) + if( plugin_free( (Plugin *) plugin_list->data ) ) + return( -1 ); + + return( 0 ); +} + +/* Apply a user-function to a plugin package. + */ +static void * +apply_plugin( Plugin *plug, VSListMap2Fn fn, void *a ) +{ + if( !plug->pack ) + return( NULL ); + else + return( fn( plug->pack, a, NULL ) ); +} + +/* Map a function over all packages. Map over plugins first, to allow + * overriding of VIPS functions. + */ +void * +im_map_packages( VSListMap2Fn fn, void *a ) +{ + void *r = im_slist_map2( plugin_list, + (VSListMap2Fn) apply_plugin, (void *) fn, a ); + int i; + + /* If not there, try main VIPS package list. + */ + if( !r ) + for( i = 0; i < IM_NUMBER( built_in ); i++ ) + if( (r = fn( built_in[i], a, NULL )) ) + return( r ); + + return( r ); +} + +/* Search a package for a function. + */ +static im_function * +search_package( im_package *pack, const char *name ) +{ + int i; + + for( i = 0; i < pack->nfuncs; i++ ) + if( strcmp( pack->table[i]->name, name ) == 0 ) + return( pack->table[i] ); + + return( NULL ); +} + +/* Search all packages for a function. + */ +im_function * +im_find_function( const char *name ) +{ + im_function *fn = im_map_packages( + (VSListMap2Fn) search_package, (void *) name ); + + if( !fn ) { + im_error( "im_find_function", _( "\"%s\" not found" ), name ); + return( NULL ); + } + + return( fn ); +} + +/* Test for package is of name. + */ +static im_package * +package_name( im_package *pack, const char *name ) +{ + if( strcmp( pack->name, name ) == 0 ) + return( pack ); + + return( NULL ); +} + +/* Find a package. + */ +im_package * +im_find_package( const char *name ) +{ + im_package *pack = im_map_packages( + (VSListMap2Fn) package_name, (void *) name ); + + if( !pack ) { + im_error( "im_find_package", _( "\"%s\" not found" ), name ); + return( NULL ); + } + + return( pack ); +} + +/* Test for package contains a function. + */ +static im_package * +package_function( im_package *pack, const char *name ) +{ + if( search_package( pack, name ) ) + return( pack ); + else + return( NULL ); +} + +/* Find a function's package by name. + */ +im_package * +im_package_of_function( const char *name ) +{ + im_package *pack = im_map_packages( + (VSListMap2Fn) package_function, (void *) name ); + + if( !pack ) { + im_error( "im_package_of_function", + _( "\"%s\" not found" ), name ); + return( NULL ); + } + + return( pack ); +} + +/* Free any store we allocated for the argument list. + */ +int +im_free_vargv( im_function *fn, im_object *vargv ) +{ + int i; + int vargc = fn->argc; + + /* Free all elements. + */ + for( i = 0; i < vargc; i++ ) + if( vargv[i] ) { + /* If there is local storage, free it. + */ + if( fn->argv[i].desc->size != 0 ) + im_free( vargv[i] ); + + /* NULL out pointer. + */ + vargv[i] = NULL; + } + + return( 0 ); +} + +/* Allocate any local store the args will need; NULL out all others. + */ +int +im_allocate_vargv( im_function *fn, im_object *vargv ) +{ + int i; + int vargc = fn->argc; + + /* NULL out all pointers. + */ + for( i = 0; i < vargc; i++ ) + vargv[i] = NULL; + + /* Allocate any space we will need. + */ + for( i = 0; i < vargc; i++ ) { + int sz = fn->argv[i].desc->size; + + if( sz != 0 ) + if( !(vargv[i] = im_malloc( NULL, sz )) ) { + /* Free anything we did allocate. + */ + (void) im_free_vargv( fn, vargv ); + return( -1 ); + } + + /* Zero memory. + */ + memset( vargv[i], 0, sz ); + } + + return( 0 ); +} + +/* Destroy the objects in the arg list. + */ +static int +destroy_args( im_function *fn, im_object *vargv ) +{ + int i; + int vargc = fn->argc; + + /* Destoy all elements with destroy functions. + */ + for( i = 0; i < vargc; i++ ) + if( vargv[i] ) + /* If there's a destroy function for this type, + * trigger it. + */ + if( fn->argv[i].desc->dest && + fn->argv[i].desc->dest( vargv[i] ) ) + return( -1 ); + + return( 0 ); +} + +/* Init an im_object array from a set of command-line arguments. + */ +static int +build_args( im_function *fn, im_object *vargv, int argc, char **argv ) +{ + im_arg_desc *arg = fn->argv; + int vargc = fn->argc; + char *str; + int i, j; + + /* Loop, constructing each im_arg. + */ + for( i = 0, j = 0; i < vargc; i++ ) { + /* Find type for this arg. + */ + im_type_desc *type = arg[i].desc; + + /* Do we need to use up a command line argument? + */ + if( type->flags & IM_TYPE_ARG ) { + if( !argv[j] ) { + im_error( "im_run_command", + _( "too few arguments" ) ); + return( -1 ); + } + str = argv[j++]; + + /* Init object. + */ + if( type->init && type->init( &vargv[i], str ) ) + return( -1 ); + } + else { + /* Init object with no arg. + */ + if( type->init && type->init( &vargv[i], "no arg" ) ) + return( -1 ); + } + } + + /* Have we used up all the command-line args? + */ + if( argv[j] ) { + im_error( "im_run_command", _( "too many arguments" ) ); + return( -1 ); + } + + return( 0 ); +} + +/* Free a region, but return 0 so we can be used as a close callback. + */ +static int +region_free( REGION *reg ) +{ + im_region_free( reg ); + + return( 0 ); +} + +/* Make a region on sub, closed by callback on main. + */ +static int +region_local_image( IMAGE *main, IMAGE *sub ) +{ + REGION *reg; + + if( !(reg = im_region_create( sub )) ) + return( -1 ); + if( im_add_close_callback( main, + (im_callback_fn) region_free, reg, NULL ) ) { + im_region_free( reg ); + + return( -1 ); + } + + return( 0 ); +} + +/* i is an output image on a PIO function ... make all input images depend + * on it. + */ +static int +note_dependencies( im_function *fn, im_object *vargv, int i ) +{ + int j; + + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *type = fn->argv[j].desc; + + if( !(type->flags & IM_TYPE_OUTPUT) && + strcmp( type->type, IM_TYPE_IMAGE ) == 0 ) { + if( region_local_image( vargv[i], vargv[j] ) ) + return( -1 ); + } + } + + return( 0 ); +} + +/* Call all defined print functions. + */ +static int +print_args( im_function *fn, im_object *vargv ) +{ + int i; + int vargc = fn->argc; + + /* Print all elements. + */ + for( i = 0; i < vargc; i++ ) + if( fn->argv[i].print && vargv[i] ) + if( fn->argv[i].print( vargv[i] ) ) + return( -1 ); + + return( 0 ); +} + +/* Add to the hist of all output images. + */ +static int +add_hist( im_function *fn, im_object *vargv, int argc, char **argv ) +{ + int i; + int vargc = fn->argc; + + /* Search for output images. + */ + for( i = 0; i < vargc; i++ ) + if( strcmp( fn->argv[i].desc->type, IM_TYPE_IMAGE ) == 0 && + (fn->argv[i].desc->flags & IM_TYPE_OUTPUT) ) + if( im_updatehist( vargv[i], fn->name, argc, argv ) ) + return( -1 ); + + return( 0 ); +} + +/* Call a VIPS function. + */ +static int +dispatch_function( im_function *fn, im_object *vargv, int argc, char **argv ) +{ + int i; + + /* Init memory from command line arguments. + */ + if( build_args( fn, vargv, argc, argv ) ) + return( -1 ); + + /* If this is a PIO function, we need to make sure that we close + * the input images after the output images, since the output image + * may include delayed image conversion filters which will not run + * until the output is closed. + * + * Do this by: + * - for each output image + * - for each input image + * - create a region on the input, closed by a + * close callback on the output image + */ + if( fn->flags & IM_FN_PIO ) + for( i = 0; i < fn->argc; i++ ) { + im_type_desc *type = fn->argv[i].desc; + + if( type->flags & IM_TYPE_OUTPUT && + strcmp( type->type, IM_TYPE_IMAGE ) == 0 ) + if( note_dependencies( fn, vargv, i ) ) + return( -1 ); + } + + /* Call function. + */ + if( fn->disp( vargv ) ) + return( -1 ); + + /* Print output. + */ + if( print_args( fn, vargv ) ) + return( -1 ); + + /* Add to history of all output images. + */ + if( add_hist( fn, vargv, argc, argv ) ) + return( -1 ); + + /* All ok! + */ + return( 0 ); +} + +/* Run a command. + */ +int +im_run_command( char *name, int argc, char **argv ) +{ + static im_object object_array[IM_MAX_ARGS]; + im_object *vargv = object_array; + im_function *fn; + + /* Search packages for a matching function. + */ + if( !(fn = im_find_function( name )) ) + return( -1 ); + + /* Allocate space for arguments. + */ + if( im_allocate_vargv( fn, vargv ) ) + return( -1 ); + + /* Call it. + */ + if( dispatch_function( fn, vargv, argc, argv ) ) { + destroy_args( fn, vargv ); + im_free_vargv( fn, vargv ); + return( -1 ); + } + + /* Clean up and exit. + */ + if( destroy_args( fn, vargv ) ) + return( -1 ); + im_free_vargv( fn, vargv ); + + return( 0 ); +} + +/* Return the version string from configure.in + */ +const char * +im_version_string( void ) +{ + return( IM_VERSION_STRING ); +} + +/* Return major/minor/micro release numbers. + */ +int +im_version( int flag ) +{ + switch( flag ) { + case 0: + return( IM_MAJOR_VERSION ); + + case 1: + return( IM_MINOR_VERSION ); + + case 2: + return( IM_MICRO_VERSION ); + + default: + im_error( "im_version", _( "flag not 0,1,2" ) ); + return( -1 ); + } +} diff --git a/libsrc/iofuncs/predicate.c b/libsrc/iofuncs/predicate.c new file mode 100644 index 00000000..45b273fa --- /dev/null +++ b/libsrc/iofuncs/predicate.c @@ -0,0 +1,528 @@ +/* Test various predicates. + * + * J.Cupitt, 8/4/93. + * 13/10/95 JC + * - ANSIfied + * - im_ispoweroftwo() added + * 14/11/96 Jc + * - im_isjpeg() added + * 25/3/97 JC + * - im_isvips() added + * 14/4/97 JC + * - im_istifftiled() added + * 29/10/98 JC + * - im_isMSBfirst() and im_amiMSBfirst() added + * 16/6/99 JC + * - added im_existsf() + * 22/11/00 JC + * - added im_isppm() + * 23/4/01 JC + * - HAVE_TIFF turns on TIFFness + * 19/10/02 HB + * - PNG added + * 1/5/06 + * - added exr + * 3/8/07 + * - cleanups + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#ifdef HAVE_IO_H +#include +#endif /*HAVE_IO_H*/ +#include +#include + +#include + +#ifdef HAVE_TIFF +#include +#endif /*HAVE_TIFF*/ + +#ifdef HAVE_PNG +#include +#endif /*HAVE_PNG*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Test BandFmt. + */ +int +im_isint( IMAGE *im ) +{ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + return( 1 ); + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + case IM_BANDFMT_COMPLEX: + case IM_BANDFMT_DPCOMPLEX: + return( 0 ); + + default: + error_exit( "im_isint: unknown image BandFmt" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +/* Test endianness of an image. SPARC is MSB first + */ +int +im_isMSBfirst( IMAGE *im ) +{ + if( im->magic == IM_MAGIC_SPARC ) + return( 1 ); + else + return( 0 ); +} + +/* Test this processor for endianness. True for SPARC order. + */ +int +im_amiMSBfirst( void ) +{ + int test; + unsigned char *p = (unsigned char *) &test; + + test = 0; + p[0] = 255; + + if( test == 255 ) + return( 0 ); + else + return( 1 ); +} + +int +im_isuint( IMAGE *im ) +{ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_UINT: + return( 1 ); + + case IM_BANDFMT_INT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + case IM_BANDFMT_COMPLEX: + case IM_BANDFMT_DPCOMPLEX: + return( 0 ); + + default: + error_exit( "im_isuint: unknown image BandFmt" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +int +im_isfloat( IMAGE *im ) +{ + switch( im->BandFmt ) { + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + return( 1 ); + + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + case IM_BANDFMT_COMPLEX: + case IM_BANDFMT_DPCOMPLEX: + return( 0 ); + + default: + error_exit( "im_isfloat: unknown image BandFmt" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +int +im_isscalar( IMAGE *im ) +{ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + return( 1 ); + + case IM_BANDFMT_COMPLEX: + case IM_BANDFMT_DPCOMPLEX: + return( 0 ); + + default: + error_exit( "im_isscalar: unknown image BandFmt" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +int +im_iscomplex( IMAGE *im ) +{ + switch( im->BandFmt ) { + case IM_BANDFMT_COMPLEX: + case IM_BANDFMT_DPCOMPLEX: + return( 1 ); + + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + return( 0 ); + + default: + error_exit( "im_iscomplex: unknown image BandFmt" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +/* Read a few bytes from the start of a file. For sniffing file types. + */ +static int +get_bytes( const char *filename, unsigned char buf[], int len ) +{ + int fd; + + /* File may not even exist (for tmp images for example!) + * so no hasty messages. And the file might be truncated, so no error + * on read either. + */ +#ifdef BINARY_OPEN + if( (fd = open( filename, O_RDONLY | O_BINARY )) == -1 ) +#else /*BINARY_OPEN*/ + if( (fd = open( filename, O_RDONLY )) == -1 ) +#endif /*BINARY_OPEN*/ + return( 0 ); + if( read( fd, buf, len ) != len ) { + close( fd ); + return( 0 ); + } + close( fd ); + + return( 1 ); +} + +int +im_istiff( const char *filename ) +{ + unsigned char buf[2]; + + if( get_bytes( filename, buf, 2 ) ) + if( (buf[0] == 'M' && buf[1] == 'M') || + (buf[0] == 'I' && buf[1] == 'I') ) + return( 1 ); + + return( 0 ); +} + +#ifdef HAVE_PNG +int +im_ispng( const char *filename ) +{ + unsigned char buf[8]; + + return( get_bytes( filename, buf, 8 ) && + !png_sig_cmp( buf, 0, 8 ) ); +} +#else /*HAVE_PNG*/ +int +im_ispng( const char *filename ) +{ + return( 0 ); +} +#endif /*HAVE_PNG*/ + +#ifdef HAVE_MAGICK +int +im_ismagick( const char *filename ) +{ + IMAGE *im; + int result; + + if( !(im = im_open( "dummy", "p" )) ) + return( -1 ); + result = im_magick2vips_header( filename, im ); + im_clear_error_string(); + im_close( im ); + + return( result == 0 ); +} +#else /*HAVE_MAGICK*/ +int +im_ismagick( const char *filename ) +{ + return( 0 ); +} +#endif /*HAVE_MAGICK*/ + +int +im_isppm( const char *filename ) +{ + unsigned char buf[2]; + + if( get_bytes( filename, buf, 2 ) ) + if( buf[0] == 'P' && (buf[1] >= '1' || buf[1] <= '6') ) + return( 1 ); + + return( 0 ); +} + +#ifdef HAVE_TIFF + +/* Handle TIFF errors here. + */ +static void +vhandle( char *module, char *fmt, ... ) +{ + va_list ap; + + im_errormsg( "TIFF error in \"%s\": ", module ); + + va_start( ap, fmt ); + im_verrormsg( fmt, ap ); + va_end( ap ); +} + +int +im_istifftiled( const char *filename ) +{ + TIFF *tif; + int tiled; + + /* Override the default TIFF error handler. + */ + TIFFSetErrorHandler( (TIFFErrorHandler) vhandle ); + +#ifdef BINARY_OPEN + if( !(tif = TIFFOpen( filename, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(tif = TIFFOpen( filename, "r" )) ) { +#endif /*BINARY_OPEN*/ + /* Not a TIFF file ... return False. + */ + im_clear_error_string(); + return( 0 ); + } + tiled = TIFFIsTiled( tif ); + TIFFClose( tif ); + + return( tiled ); +} + +#else /*HAVE_TIFF*/ + +int +im_istifftiled( const char *filename ) +{ + return( 0 ); +} + +#endif /*HAVE_TIFF*/ + +int +im_isjpeg( const char *filename ) +{ + unsigned char buf[2]; + + if( get_bytes( filename, buf, 2 ) ) + if( (int) buf[0] == 0xff && (int) buf[1] == 0xd8 ) + return( 1 ); + + return( 0 ); +} + +int +im_isvips( const char *filename ) +{ + unsigned char buf[4]; + + if( get_bytes( filename, buf, 4 ) ) { + if( buf[0] == 0x08 && buf[1] == 0xf2 && + buf[2] == 0xa6 && buf[3] == 0xb6 ) + /* SPARC-order VIPS image. + */ + return( 1 ); + else if( buf[3] == 0x08 && buf[2] == 0xf2 && + buf[1] == 0xa6 && buf[0] == 0xb6 ) + /* INTEL-order VIPS image. + */ + return( 1 ); + } + + return( 0 ); +} + +int +im_isexr( const char *filename ) +{ + unsigned char buf[4]; + + if( get_bytes( filename, buf, 4 ) ) + if( buf[0] == 0x76 && buf[1] == 0x2f && + buf[2] == 0x31 && buf[3] == 0x01 ) + return( 1 ); + + return( 0 ); +} + +/* Test for file exists. + */ +int +im_existsf( const char *name, ... ) +{ + va_list ap; + char buf1[PATH_MAX]; + + va_start( ap, name ); + (void) im_vsnprintf( buf1, PATH_MAX - 1, name, ap ); + va_end( ap ); + + /* Try that. + */ + if( !access( buf1, R_OK ) ) + return( 1 ); + + return( 0 ); +} + +/* True if this IMAGE is a disc file of some sort. + */ +int +im_isfile( IMAGE *im ) +{ + switch( im->dtype ) { + case IM_MMAPIN: + case IM_MMAPINRW: + case IM_OPENOUT: + case IM_OPENIN: + return( 1 ); + + case IM_PARTIAL: + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_NONE: + return( 0 ); + + default: + error_exit( "im_isfile: corrupt IMAGE descriptor" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +/* True if this IMAGE is a partial of some sort. + */ +int +im_ispartial( IMAGE *im ) +{ + switch( im->dtype ) { + case IM_PARTIAL: + return( 1 ); + + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_MMAPIN: + case IM_MMAPINRW: + case IM_OPENIN: + case IM_OPENOUT: + case IM_NONE: + return( 0 ); + + default: + error_exit( "im_ispartial: corrupt IMAGE descriptor" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +/* True if an int is a power of two ... 1, 2, 4, 8, 16, 32, etc. Do with just + * integer arithmetic for portability. A previous Nicos version using doubles + * and log/log failed on x86 with rounding problems. Return 0 for not + * power of two, otherwise return the position of the set bit (numbering with + * bit 1 as the lsb). + */ +int +im_ispoweroftwo( int p ) +{ + int i, n; + + /* Count set bits. Could use a LUT, I guess. + */ + for( i = 0, n = 0; p; i++, p >>= 1 ) + if( (p & 1) == 1 ) + n++; + + /* Should be just one set bit. + */ + if( n == 1 ) + /* Return position of bit. + */ + return( i ); + else + return( 0 ); +} diff --git a/libsrc/iofuncs/rect.c b/libsrc/iofuncs/rect.c new file mode 100644 index 00000000..7eb59222 --- /dev/null +++ b/libsrc/iofuncs/rect.c @@ -0,0 +1,167 @@ +/* Simple rectangle algebra. Should build rectangle list algebra on top of + * this. + * + * J. Cupitt, 8/4/93. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Move the margins of a rect. +1 means out one pixel. + */ +void +im_rect_marginadjust( Rect *r, int n ) +{ + r->left -= n; + r->top -= n; + r->width += 2 * n; + r->height += 2 * n; +} + +/* Does rect contain a point? + */ +int +im_rect_includespoint( Rect *r, int x, int y ) +{ + return( r->left <= x && + r->top <= y && + r->left + r->width > x && + r->top + r->height > y ); +} + +/* Is r2 a subset of r1? + */ +int +im_rect_includesrect( Rect *r1, Rect *r2 ) +{ + return( r1->left <= r2->left && + r1->top <= r2->top && + r1->left + r1->width >= r2->left + r2->width && + r1->top + r1->height >= r2->top + r2->height ); +} + +/* Fill r3 with the intersection of r1 and r2. r3 can equal r1 or r2. + */ +void +im_rect_intersectrect( Rect *r1, Rect *r2, Rect *r3 ) +{ + int left = IM_MAX( r1->left, r2->left ); + int top = IM_MAX( r1->top, r2->top ); + int right = IM_MIN( IM_RECT_RIGHT( r1 ), IM_RECT_RIGHT( r2 ) ); + int bottom = IM_MIN( IM_RECT_BOTTOM( r1 ), IM_RECT_BOTTOM( r2 ) ); + int width = IM_MAX( 0, right - left ); + int height = IM_MAX( 0, bottom - top ); + + r3->left = left; + r3->top = top; + r3->width = width; + r3->height = height; +} + +/* Is a rect empty? ie. zero width or height. + */ +int +im_rect_isempty( Rect *r ) +{ + return( r->width <= 0 || r->height <= 0 ); +} + +/* Fill r3 with the set union of r1 and r2. Can't do this very well, as can + * only have rectangular areas. Just set to smallest box that encloses both r1 + * and r2. If either is empty, can just return the other. + */ +void +im_rect_unionrect( Rect *r1, Rect *r2, Rect *r3 ) +{ + if( im_rect_isempty( r1 ) ) + *r3 = *r2; + else if( im_rect_isempty( r2 ) ) + *r3 = *r1; + else { + int left = IM_MIN( r1->left, r2->left ); + int top = IM_MIN( r1->top, r2->top ); + int width = IM_MAX( IM_RECT_RIGHT( r1 ), + IM_RECT_RIGHT( r2 ) ) - left; + int height = IM_MAX( IM_RECT_BOTTOM( r1 ), + IM_RECT_BOTTOM( r2 ) )- top; + + r3->left = left; + r3->top = top; + r3->width = width; + r3->height = height; + } +} + +/* Test for equality. + */ +int +im_rect_equalsrect( Rect *r1, Rect *r2 ) +{ + return( r1->left == r2->left && r1->top == r2->top && + r1->width == r2->width && r1->height == r2->height ); +} + +/* DUP a rect. + */ +Rect * +im_rect_dup( Rect *r ) +{ + Rect *out = IM_NEW( NULL, Rect ); + + if( !out ) + return( NULL ); + + *out = *r; + return( out ); +} + +/* Make sure width and height are >0. + */ +void +im_rect_normalise( Rect *r ) +{ + if( r->width < 0 ) { + r->left += r->width; + r->width *= -1; + } + if( r->height < 0 ) { + r->top += r->height; + r->height *= -1; + } +} diff --git a/libsrc/iofuncs/region.c b/libsrc/iofuncs/region.c new file mode 100644 index 00000000..0b523590 --- /dev/null +++ b/libsrc/iofuncs/region.c @@ -0,0 +1,603 @@ +/* Make and destroy partial image regions. + * + * J.Cupitt, 8/4/93. + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 15/8/94 JC + * - start & stop can now be NULL for no-op + * 12/5/94 JC + * - threads v2.0 added + * 22/2/95 JC + * - im_region_region() args changed + * 22/6/95 JC + * - im_region_local() did not always reset the data pointer + * 18/11/98 JC + * - init a, b, c also now, to help rtc avoid spurious checks + * 29/6/01 JC + * - im_region_free() now frees immediately + * 6/8/02 JC + * - new mmap() window regions + * 5/11/02 JC + * - fix for mmap a local region + * 28/2/05 + * - shrink local region memory if required much-greater-than allocated + * 3/6/05 + * - im_region_region() allows Bands and BandFmt to differ, provided + * sizeof( pel ) is the same ... makes im_copy_morph() work + * 30/10/06 + * - switch to im_window_t for mmap window stuff + * 29/11/06 + * - switch to im_buffer_t for local mem buffer stuff + * 19/1/07 + * - im_region_image() only sets r, not whole image + * 1'2'07 + * - gah, im_region_image() could still break (thanks Mikkel) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_MOVE +#define DEBUG_ENVIRONMENT 1 +#define DEBUG_CREATE +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include +#ifdef HAVE_SYS_MMAN_H +#include +#endif +#include + +#include +#include +#include + +#ifdef OS_WIN32 +#include +#endif /*OS_WIN32*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef DEBUG +/* Track all regions here for debugging. + */ +static GSList *im__regions_all = NULL; +#endif /*DEBUG*/ + +/* Call a start function if no sequence is running on this REGION. + */ +int +im__call_start( REGION *reg ) +{ + IMAGE *im = reg->im; + + /* Have we a sequence running on this region? Start one if not. + */ + if( !reg->seq && im->start ) { + g_mutex_lock( im->sslock ); + reg->seq = im->start( im, im->client1, im->client2 ); + g_mutex_unlock( im->sslock ); + + if( !reg->seq ) { + im_error( "im__call_start", + _( "start function failed for image %s" ), + im->filename ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Call a stop function if a sequence is running in this REGION. No error + * return, really. + */ +void +im__call_stop( REGION *reg ) +{ + IMAGE *im = reg->im; + int res; + + /* Stop any running sequence. + */ + if( reg->seq && im->stop ) { + g_mutex_lock( im->sslock ); + res = im->stop( reg->seq, im->client1, im->client2 ); + g_mutex_unlock( im->sslock ); + + if( res ) + error_exit( "panic: user stop callback failed " + "for image %s", im->filename ); + + reg->seq = NULL; + } +} + +/* If a region is being created in one thread (eg. the main thread) and then + * used in another (eg. a worker thread), the new thread needs to tell VIPS + * to stop sanity assert() fails. The previous owner needs to + * im__region_no_ownership() before we can call this. + */ +void +im__region_take_ownership( REGION *reg ) +{ + /* Lock so that there's a memory barrier with the thread doing the + * im__region_no_ownership() before us. + */ + g_mutex_lock( reg->im->sslock ); + + assert( reg->thread == NULL ); + + /* We don't want to move shared buffers: the other region using this + * buffer will still be on the other thread. Not sure if this will + * ever happen: if it does, we'll need to dup the buffer. + */ + assert( !reg->buffer || reg->buffer->ref_count == 1 ); + + reg->thread = g_thread_self(); + + g_mutex_unlock( reg->im->sslock ); +} + +void +im__region_check_ownership( REGION *reg ) +{ + if( reg->thread ) { + assert( reg->thread == g_thread_self() ); + if( reg->buffer && reg->buffer->cache ) + assert( reg->thread == reg->buffer->cache->thread ); + } +} + +/* Call this from the relinquishing thread. Removes the buffer (if any) from + * this thread's buffer cache. + */ +void +im__region_no_ownership( REGION *reg ) +{ + g_mutex_lock( reg->im->sslock ); + + im__region_check_ownership( reg ); + + reg->thread = NULL; + if( reg->buffer ) + im_buffer_undone( reg->buffer ); + + g_mutex_unlock( reg->im->sslock ); +} + +/* Create a region. Set no attachments. Either im_prepare() or im_generate() + * are responsible for getting regions ready for user functions to read + * from/write to. + */ +REGION * +im_region_create( IMAGE *im ) +{ + REGION *reg; + + im_image_sanity( im ); + + if( !(reg = IM_NEW( NULL, REGION )) ) + return( NULL ); + + reg->im = im; + reg->valid.left = 0; + reg->valid.top = 0; + reg->valid.width = 0; + reg->valid.height = 0; + reg->type = IM_REGION_NONE; + reg->data = NULL; + reg->bpl = 0; + reg->seq = NULL; + reg->thread = NULL; + reg->window = NULL; + reg->buffer = NULL; + + im__region_take_ownership( reg ); + + /* We're usually inside the ss lock anyway. But be safe ... + */ + g_mutex_lock( im->sslock ); + im->regions = g_slist_prepend( im->regions, reg ); + g_mutex_unlock( im->sslock ); + +#ifdef DEBUG + g_mutex_lock( im__global_lock ); + im__regions_all = g_slist_prepend( im__regions_all, reg ); + printf( "%d regions in vips\n", g_slist_length( im__regions_all ) ); + g_mutex_unlock( im__global_lock ); +#endif /*DEBUG*/ + + return( reg ); +} + +/* Free any resources we have. + */ +static void +im_region_reset( REGION *reg ) +{ + IM_FREEF( im_window_unref, reg->window ); + IM_FREEF( im_buffer_unref, reg->buffer ); +} + +void +im_region_free( REGION *reg ) +{ + IMAGE *im; + + if( !reg ) + return; + im = reg->im; + + /* Stop this sequence. + */ + im__call_stop( reg ); + + /* Free any attached memory. + */ + im_region_reset( reg ); + + /* Detach from image. + */ + g_mutex_lock( im->sslock ); + im->regions = g_slist_remove( im->regions, reg ); + g_mutex_unlock( im->sslock ); + reg->im = NULL; + + /* Was this the last region on an image with close_pending? If yes, + * close the image too. + */ + if( !im->regions && im->close_pending ) { +#ifdef DEBUG_IO + printf( "im_region_free: closing pending image \"%s\"\n", + im->filename ); +#endif /*DEBUG_IO*/ + /* Time to close the image. + */ + im->close_pending = 0; + im_close( im ); + } + + im_free( reg ); + +#ifdef DEBUG + g_mutex_lock( im__global_lock ); + assert( g_slist_find( im__regions_all, reg ) ); + im__regions_all = g_slist_remove( im__regions_all, reg ); + printf( "%d regions in vips\n", g_slist_length( im__regions_all ) ); + g_mutex_unlock( im__global_lock ); +#endif /*DEBUG*/ +} + +/* Region should be a pixel buffer. On return, check + * reg->buffer->done to see if there are pixels there already. Otherwise, you + * need to calculate. + */ +int +im_region_buffer( REGION *reg, Rect *r ) +{ + IMAGE *im = reg->im; + + Rect image; + Rect clipped; + + im__region_check_ownership( reg ); + + /* Clip against image. + */ + image.top = 0; + image.left = 0; + image.width = im->Xsize; + image.height = im->Ysize; + im_rect_intersectrect( r, &image, &clipped ); + + /* Test for empty. + */ + if( im_rect_isempty( &clipped ) ) { + im_error( "im_region_buffer", _( "valid clipped to nothing" ) ); + return( -1 ); + } + + /* Already have stuff? + */ + if( reg->type == IM_REGION_BUFFER && + im_rect_includesrect( ®->valid, &clipped ) && + reg->buffer && + !reg->buffer->invalid ) + return( 0 ); + + /* Don't call im_region_reset() ... we combine buffer unref and new + * buffer ref in one call to reduce malloc/free cycling. + */ + IM_FREEF( im_window_unref, reg->window ); + if( !(reg->buffer = im_buffer_unref_ref( reg->buffer, im, &clipped )) ) + return( -1 ); + + /* Init new stuff. + */ + reg->valid = reg->buffer->area; + reg->bpl = IM_IMAGE_SIZEOF_PEL( im ) * reg->buffer->area.width; + reg->type = IM_REGION_BUFFER; + reg->data = reg->buffer->buf; + + return( 0 ); +} + +/* Attach a region to a small section of the image on which it is defined. + * The IMAGE we are attached to should be im_mmapin(), im_mmapinrw() or + * im_setbuf(). The Rect is clipped against the image size. + */ +int +im_region_image( REGION *reg, Rect *r ) +{ + Rect image; + Rect clipped; + + /* Sanity check. + */ + im__region_check_ownership( reg ); + + /* Clip against image. + */ + image.top = 0; + image.left = 0; + image.width = reg->im->Xsize; + image.height = reg->im->Ysize; + im_rect_intersectrect( r, &image, &clipped ); + + /* Test for empty. + */ + if( im_rect_isempty( &clipped ) ) { + im_error( "im_region_image", _( "valid clipped to nothing" ) ); + return( -1 ); + } + + if( reg->im->data ) { + /* We have the whole image available ... easy! + */ + im_region_reset( reg ); + + /* We can't just set valid = clipped, since this may be an + * incompletely calculated memory buffer. Just set valid to r. + */ + reg->valid = clipped; + reg->bpl = IM_IMAGE_SIZEOF_LINE( reg->im ); + reg->data = reg->im->data + + (gint64) clipped.top * IM_IMAGE_SIZEOF_LINE( reg->im ) + + clipped.left * IM_IMAGE_SIZEOF_PEL( reg->im ); + reg->type = IM_REGION_OTHER_IMAGE; + } + else if( reg->im->dtype == IM_OPENIN ) { + /* No complete image data ... but we can use a rolling window. + */ + if( reg->type != IM_REGION_WINDOW || !reg->window || + reg->window->top > clipped.top || + reg->window->top + reg->window->height < + clipped.top + clipped.height ) { + im_region_reset( reg ); + + if( !(reg->window = im_window_ref( reg->im, + clipped.top, clipped.height )) ) + return( -1 ); + + reg->type = IM_REGION_WINDOW; + } + + /* Note the area the window actually represents. + */ + reg->valid.left = 0; + reg->valid.top = reg->window->top; + reg->valid.width = reg->im->Xsize; + reg->valid.height = reg->window->height; + reg->bpl = IM_IMAGE_SIZEOF_LINE( reg->im ); + reg->data = reg->window->data; + } + else { + im_error( "im_region_image", _( "bad image type" ) ); + return( -1 ); + } + + return( 0 ); +} + +/* Make IM_REGION_ADDR() stuff to reg go to dest instead. + * + * r is the part of the reg image which you want to be able to write to (this + * effectively becomes the valid field), (x,y) is the top LH corner of the + * corresponding area in dest. + * + * Performs all clippings necessary to ensure that ®->valid is indeed + * valid. + * + * If the region we attach to is modified, we are left with dangling pointers! + * If the region we attach to is on another image, the two images must have + * the same sizeof( pel ). + */ +int +im_region_region( REGION *reg, REGION *dest, Rect *r, int x, int y ) +{ + Rect image; + Rect wanted; + Rect clipped; + Rect clipped2; + Rect final; + + /* Sanity check. + */ + if( !dest->data || + IM_IMAGE_SIZEOF_PEL( dest->im ) != + IM_IMAGE_SIZEOF_PEL( reg->im ) ) { + im_error( "im_region_region", + _( "inappropriate region type" ) ); + return( -1 ); + } + im__region_check_ownership( reg ); + + /* We can't test + + assert( dest->thread == g_thread_self() ); + + * since we can have several threads writing to the same region in + * threadgroup. + */ + + /* Clip r against size of the image. + */ + image.top = 0; + image.left = 0; + image.width = reg->im->Xsize; + image.height = reg->im->Ysize; + im_rect_intersectrect( r, &image, &clipped ); + + /* Translate to dest's coordinate space and clip against the available + * pixels. + */ + wanted.left = x + (clipped.left - r->left); + wanted.top = y + (clipped.top - r->top); + wanted.width = clipped.width; + wanted.height = clipped.height; + + /* Test that dest->valid is large enough. + */ + if( !im_rect_includesrect( &dest->valid, &wanted ) ) { + im_error( "im_region_region", _( "dest too small" ) ); + return( -1 ); + } + + /* Clip against the available pixels. + */ + im_rect_intersectrect( &wanted, &dest->valid, &clipped2 ); + + /* Translate back to reg's coordinate space and set as valid. + */ + final.left = r->left + (clipped2.left - wanted.left); + final.top = r->top + (clipped2.top - wanted.top); + final.width = clipped2.width; + final.height = clipped2.height; + + /* Test for empty. + */ + if( im_rect_isempty( &final ) ) { + im_error( "im_region_region", _( "valid clipped to nothing" ) ); + return( -1 ); + } + + /* Init new stuff. + */ + im_region_reset( reg ); + reg->valid = final; + reg->bpl = dest->bpl; + reg->data = IM_REGION_ADDR( dest, clipped2.left, clipped2.top ); + reg->type = IM_REGION_OTHER_REGION; + + return( 0 ); +} + +/* Do two regions point to the same piece of image? ie. + * IM_REGION_ADDR( reg1, x, y ) == IM_REGION_ADDR( reg2, x, y ) && + * *IM_REGION_ADDR( reg1, x, y ) == + * *IM_REGION_ADDR( reg2, x, y ) for all x, y, reg1, reg2. + */ +int +im_region_equalsregion( REGION *reg1, REGION *reg2 ) +{ + return( reg1->im == reg2->im && + im_rect_equalsrect( ®1->valid, ®2->valid ) && + reg1->data == reg2->data ); +} + +/* Set the position of a region. This only affects reg->valid, ie. the way + * pixels are addressed, not reg->data, the pixels which are addressed. Clip + * against the size of the image. Do not allow negative positions, or + * positions outside the image. + */ +int +im_region_position( REGION *reg, int x, int y ) +{ + Rect req, image, clipped; + + /* Clip! + */ + image.top = 0; + image.left = 0; + image.width = reg->im->Xsize; + image.height = reg->im->Ysize; + req.top = y; + req.left = x; + req.width = reg->valid.width; + req.height = reg->valid.height; + im_rect_intersectrect( &image, &req, &clipped ); + if( x < 0 || y < 0 || im_rect_isempty( &clipped ) ) { + im_error( "im_region_position", _( "bad position" ) ); + return( -1 ); + } + + reg->valid = clipped; + + return( 0 ); +} + +int +im_region_fill( REGION *reg, Rect *r, im_region_fill_fn fn, void *a ) +{ + assert( reg->im->dtype == IM_PARTIAL ); + assert( reg->im->generate ); + + /* Should have local memory. + */ + if( im_region_buffer( reg, r ) ) + return( -1 ); + + /* Evaluate into or, if we've not got calculated pixels. + */ + if( !reg->buffer->done ) { + if( fn( reg, a ) ) + return( -1 ); + + /* Publish our results. + */ + if( reg->buffer ) + im_buffer_done( reg->buffer ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/semaphore.c b/libsrc/iofuncs/semaphore.c new file mode 100644 index 00000000..69219d6e --- /dev/null +++ b/libsrc/iofuncs/semaphore.c @@ -0,0 +1,146 @@ +/* Support for thread stuff. + * + * JC & KM 9/5/94 + * Modified: + * 28/11/94 JC + * - return(0) missing from tidy_thread_info() + * 4/8/99 RP JC + * - reorganised for POSIX + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_IO + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +void +im_semaphore_init( im_semaphore_t *s, int v, char *name ) +{ + s->v = v; + s->name = name; +#ifdef HAVE_THREADS + s->mutex = g_mutex_new(); + s->cond = g_cond_new(); +#endif /*HAVE_THREADS*/ +} + +void +im_semaphore_destroy( im_semaphore_t *s ) +{ +#ifdef HAVE_THREADS + IM_FREEF( g_mutex_free, s->mutex ); + IM_FREEF( g_cond_free, s->cond ); +#endif /*HAVE_THREADS*/ +} + +/* Add n to the semaphore and signal any threads that are blocked waiting + * a change. + */ +int +im_semaphore_upn( im_semaphore_t *s, int n ) +{ + int value_after_op; + +#ifdef HAVE_THREADS + g_mutex_lock( s->mutex ); +#endif /*HAVE_THREADS*/ + s->v += n; + value_after_op = s->v; +#ifdef HAVE_THREADS + g_mutex_unlock( s->mutex ); + g_cond_signal( s->cond ); +#endif /*HAVE_THREADS*/ + +#ifdef DEBUG_IO + printf( "im_semaphore_upn(\"%s\",%d) = %d\n", + s->name, n, value_after_op ); + if( value_after_op > 1 ) + im_errormsg( "up over 1!" ); +#endif /*DEBUG_IO*/ + + return( value_after_op ); +} + +/* Increment the semaphore. + */ +int +im_semaphore_up( im_semaphore_t *s ) +{ + return( im_semaphore_upn( s, 1 ) ); +} + +/* Wait for sem>n, then subtract n. + */ +int +im_semaphore_downn( im_semaphore_t *s, int n ) +{ + int value_after_op; + +#ifdef HAVE_THREADS + g_mutex_lock( s->mutex ); + while( s->v < n ) + g_cond_wait( s->cond, s->mutex ); +#endif /*HAVE_THREADS*/ + s->v -= n; + value_after_op = s->v; +#ifdef HAVE_THREADS + g_mutex_unlock( s->mutex ); +#endif /*HAVE_THREADS*/ + +#ifdef DEBUG_IO + printf( "im_semaphore_downn(\"%s\",%d): %d\n", + s->name, n, value_after_op ); +#endif /*DEBUG_IO*/ + + return( value_after_op ); +} + +/* Wait for sem>0, then decrement. + */ +int +im_semaphore_down( im_semaphore_t *s ) +{ + return( im_semaphore_downn( s, 1 ) ); +} diff --git a/libsrc/iofuncs/threadgroup.c b/libsrc/iofuncs/threadgroup.c new file mode 100644 index 00000000..ca103b00 --- /dev/null +++ b/libsrc/iofuncs/threadgroup.c @@ -0,0 +1,678 @@ +/* Support for thread groups. + * + * 29/9/99 JC + * - from thread.c + * 23/10/03 JC + * - threadgroup now has a kill flag as well + * 9/6/06 + * - use an idle queue instead of flags + * 24/11/06 + * - double-buffer file writes + * 27/11/06 + * - break stuff out to generate / iterate + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define TIME_THREAD +#define DEBUG_CREATE +#define DEBUG_HIGHWATER +#define DEBUG_IO + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef TIME_THREAD +/* Size of time buffers. + */ +#define IM_TBUF_SIZE (20000) +#endif /*TIME_THREAD*/ + +/* Maximum number of concurrent threads we allow. No reason for the limit, + * it's just there to stop mad values for IM_CONCURRENCY killing the system. + */ +#define IM_MAX_THREADS (1024) + +/* Name of environment variable we get concurrency level from. + */ +#define IM_CONCURRENCY "IM_CONCURRENCY" + +/* Default tile geometry ... can be set by init_world. + */ +int im__tile_width = IM__TILE_WIDTH; +int im__tile_height = IM__TILE_HEIGHT; +int im__fatstrip_height = IM__FATSTRIP_HEIGHT; +int im__thinstrip_height = IM__THINSTRIP_HEIGHT; + +/* Default n threads ... 0 means get from environment. + */ +int im__concurrency = 0; + +#ifndef HAVE_THREADS +/* If we're building without gthread, we need stubs for the g_thread_*() and + * g_mutex_*() functions. has #defines which point the g_ + * names here. + */ + +void im__g_thread_init( GThreadFunctions *vtable ) {} +gpointer im__g_thread_join( GThread *dummy ) { return( NULL ); } +gpointer im__g_thread_self( void ) { return( NULL ); } +GThread *im__g_thread_create_full( GThreadFunc d1, + gpointer d2, gulong d3, gboolean d4, gboolean d5, GThreadPriority d6, + GError **d7 ) + { return( NULL ); } + +GMutex *im__g_mutex_new( void ) { return( NULL ); } +void im__g_mutex_free( GMutex *d ) {} +void im__g_mutex_lock( GMutex *d ) {} +void im__g_mutex_unlock( GMutex *d ) {} +#endif /*!HAVE_THREADS*/ + +void +im_concurrency_set( int concurrency ) +{ + im__concurrency = concurrency; +} + +/* Set (p)thr_concurrency() from IM_CONCURRENCY environment variable. Return + * the number of regions we should pass over the image. + */ +int +im_concurrency_get( void ) +{ + const char *str; + int nthr; + + /* Tell the threads system how much concurrency we expect. + */ + if( im__concurrency > 0 ) + nthr = im__concurrency; + else if( (str = g_getenv( IM_CONCURRENCY )) ) + nthr = atoi( str ); + else + /* Stick to minimum. + */ + nthr = 1; + + if( nthr < 1 || nthr > IM_MAX_THREADS ) { + nthr = IM_CLIP( 1, nthr, IM_MAX_THREADS ); + + im_warn( "im_concurrency_get", + _( "threads clipped to %d" ), nthr ); + } + + /* + + FIXME .. hmm + +#ifdef SOLARIS_THREADS + if( thr_setconcurrency( nthr + 1 ) ) { + im_error( "im_concurrency_get", _( "unable to set " + "concurrency level to %d" ), nthr + 1 ); + return( -1 ); + } +#ifdef DEBUG_IO + printf( "im_generate: using thr_setconcurrency(%d)\n", nthr+1 ); +#endif +#endif + +#ifdef HAVE_PTHREAD +#ifdef HAVE_PTHREAD_SETCONCURRENCY + if( pthread_setconcurrency( nthr + 1 ) ) { + im_error( "im_concurrency_get", _( "unable to set " + "concurrency level to %d" ), nthr + 1 ); + return( -1 ); + } +#ifdef DEBUG_IO + printf( "im_generate: using pthread_setconcurrency(%d)\n", nthr+1 ); +#endif +#endif +#endif + */ + + return( nthr ); +} + +#ifdef TIME_THREAD +/* Save time buffers. + */ +static int +save_time_buffers( REGION *reg ) +{ + int i; + static int rn = 1; + FILE *fp; + char name[ 256 ]; + + im_snprintf( name, 256, "time%d", rn++ ); + if( !(fp = fopen( name, "w" )) ) + error_exit( "unable to write to \"%s\"", name ); + for( i = 0; i < reg->tpos; i++ ) + fprintf( fp, "%lld\n%lld\n", reg->btime[i], reg->etime[i] ); + fclose( fp ); + + return( 0 ); +} +#endif /*TIME_THREAD*/ + +/* Handle the idle list. + */ + +/* Make sure thr is not on the idle list ... eg. on thread_free(). + */ +static void +threadgroup_idle_remove( im_thread_t *thr ) +{ + im_threadgroup_t *tg = thr->tg; + + g_mutex_lock( tg->idle_lock ); + + tg->idle = g_slist_remove( tg->idle, thr ); + +#ifdef DEBUG_HIGHWATER + tg->nidle -= 1; + assert( tg->nidle >= 0 && tg->nidle <= tg->nthr ); +#endif /*DEBUG_HIGHWATER*/ + + g_mutex_unlock( tg->idle_lock ); +} + +/* Add to idle. + */ +static void +threadgroup_idle_add( im_thread_t *thr ) +{ + im_threadgroup_t *tg = thr->tg; + + g_mutex_lock( tg->idle_lock ); + + assert( !g_slist_find( tg->idle, thr ) ); + tg->idle = g_slist_prepend( tg->idle, thr ); + +#ifdef DEBUG_HIGHWATER + tg->nidle += 1; + assert( tg->nidle >= 0 && tg->nidle <= tg->nthr ); +#endif /*DEBUG_HIGHWATER*/ + + im_semaphore_up( &tg->idle_sem ); + + g_mutex_unlock( tg->idle_lock ); +} + +/* Take the first thread off idle. + */ +im_thread_t * +im_threadgroup_get( im_threadgroup_t *tg ) +{ + im_thread_t *thr; + + /* Wait for a thread to be added to idle. + */ + im_semaphore_down( &tg->idle_sem ); + + g_mutex_lock( tg->idle_lock ); + + assert( tg->idle ); + assert( tg->idle->data ); + thr = (im_thread_t *) tg->idle->data; + tg->idle = g_slist_remove( tg->idle, thr ); + +#ifdef DEBUG_HIGHWATER + tg->nidle -= 1; + assert( tg->nidle >= 0 && tg->nidle <= tg->nthr ); + if( tg->nidle < tg->min_idle ) + tg->min_idle = tg->nidle; +#endif /*DEBUG_HIGHWATER*/ + + g_mutex_unlock( tg->idle_lock ); + + return( thr ); +} + +/* Wait for all threads to be idle. + */ +void +im_threadgroup_wait( im_threadgroup_t *tg ) +{ + /* Wait for all threads to signal idle. + */ + im_semaphore_downn( &tg->idle_sem, tg->nthr ); + + g_mutex_lock( tg->idle_lock ); + + assert( tg->idle ); + assert( g_slist_length( tg->idle ) == tg->nthr ); + + g_mutex_unlock( tg->idle_lock ); + + /* All threads are now blocked on their 'go' semaphores, and idle is + * zero. Up idle by the number of threads, ready for the next loop. + */ + im_semaphore_upn( &tg->idle_sem, tg->nthr ); +} + +/* Junk a thread. + */ +static void +thread_free( im_thread_t *thr ) +{ + /* Is there a thread running this region? Kill it! + */ + if( thr->thread ) { + thr->kill = -1; + im_semaphore_up( &thr->go ); + + /* Return value is always NULL (see thread_main_loop). + */ + (void) g_thread_join( thr->thread ); +#ifdef DEBUG_CREATE + printf( "thread_free: g_thread_join()\n" ); +#endif /*DEBUG_CREATE*/ + + thr->thread = NULL; + } + im_semaphore_destroy( &thr->go ); + + threadgroup_idle_remove( thr ); + IM_FREEF( im_region_free, thr->reg ); + thr->oreg = NULL; + thr->tg = NULL; + +#ifdef TIME_THREAD + if( thr->btime ) + (void) save_time_buffers( thr ); +#endif /*TIME_THREAD*/ +} + +/* The work we do in one loop ... fill a region, and call a function. Either + * called from the main thread (if no threading), or from worker. + */ +static void +work_fn( im_thread_t *thr ) +{ + /* Doublecheck only one thread per region. + */ + assert( thr->thread == g_thread_self() ); + + /* Prepare this area. + */ + if( thr->tg->inplace ) { + if( im_prepare_to( thr->reg, thr->oreg, + &thr->pos, thr->x, thr->y ) ) + thr->error = -1; + } + else { + /* Attach to this position. + */ + if( im_prepare( thr->reg, &thr->pos ) ) + thr->error = -1; + } + + /* Call our work function. + */ + if( !thr->error && thr->tg->work && + thr->tg->work( thr->reg, thr->a, thr->b, thr->c ) ) + thr->error = -1; + + /* Back on the idle queue again. + */ + threadgroup_idle_add( thr ); +} + +#ifdef HAVE_THREADS +/* What runs as a thread ... loop, waiting to be told to fill our region. + */ +static void * +thread_main_loop( void *a ) +{ + im_thread_t *thr = (im_thread_t *) a; + im_threadgroup_t *tg = thr->tg; + + /* We now control the region (it was created by tg when we were + * built). + */ + im__region_take_ownership( thr->reg ); + + for(;;) { + assert( tg == thr->tg ); + + /* Signal the main thread that we are idle, and block. + */ + im_semaphore_down( &thr->go ); + + /* Asked to exit? + */ + if( thr->kill ) + break; + +#ifdef TIME_THREAD + /* Note start time. + */ + if( thr->btime ) + thr->btime[thr->tpos] = gethrtime(); +#endif /*TIME_THREAD*/ + + /* Loop once. + */ + work_fn( thr ); + +#ifdef TIME_THREAD + /* Note stop time. + */ + if( thr->etime ) { + thr->etime[thr->tpos] = gethrtime(); + thr->tpos++; + } +#endif /*TIME_THREAD*/ + } + + return( NULL ); +} +#endif /*HAVE_THREADS*/ + +/* Attach another thread to a threadgroup. + */ +static im_thread_t * +threadgroup_thread_new( im_threadgroup_t *tg ) +{ + im_thread_t *thr; + + if( !(thr = IM_NEW( tg->im, im_thread_t )) ) + return( NULL ); + thr->tg = tg; + thr->reg = NULL; + thr->thread = NULL; + im_semaphore_init( &thr->go, 0, "go" ); + thr->kill = 0; + thr->error = 0; + thr->oreg = NULL; + thr->a = thr->b = thr->c = NULL; +#ifdef TIME_THREAD + thr->btime = NULL; + thr->etime = NULL; + thr->tpos = 0; +#endif /*TIME_THREAD*/ + + /* Attach stuff. + */ + if( !(thr->reg = im_region_create( tg->im )) ) { + thread_free( thr ); + return( NULL ); + } + + /* Get ready to hand the region over to the thread. + */ + im__region_no_ownership( thr->reg ); + +#ifdef TIME_THREAD + thr->btime = IM_ARRAY( tg->im, IM_TBUF_SIZE, hrtime_t ); + thr->etime = IM_ARRAY( tg->im, IM_TBUF_SIZE, hrtime_t ); + if( !thr->btime || !thr->etime ) { + thread_free( thr ); + return( NULL ); + } +#endif /*TIME_THREAD*/ + +#ifdef HAVE_THREADS + /* Make a worker thread. We have to use g_thread_create_full() because + * we need to insist on a non-tiny stack. Some platforms default to + * very small values (eg. various BSDs). + */ + if( !(thr->thread = g_thread_create_full( thread_main_loop, thr, + IM__DEFAULT_STACK_SIZE, TRUE, FALSE, + G_THREAD_PRIORITY_NORMAL, NULL )) ) { + im_error( "threadgroup_thread_new", + _( "unable to create thread" ) ); + thread_free( thr ); + return( NULL ); + } + +#ifdef DEBUG_CREATE + printf( "threadgroup_thread_new: g_thread_create_full()\n" ); +#endif /*DEBUG_CREATE*/ +#endif /*HAVE_THREADS*/ + + /* It's idle. + */ + threadgroup_idle_add( thr ); + + return( thr ); +} + +/* Kill all threads in a threadgroup, if there are any. + */ +static void +threadgroup_kill_threads( im_threadgroup_t *tg ) +{ + if( tg->thr ) { + int i; + + for( i = 0; i < tg->nthr; i++ ) + thread_free( tg->thr[i] ); + tg->thr = NULL; + + assert( !tg->idle ); + + /* Reset the idle semaphore. + */ + im_semaphore_destroy( &tg->idle_sem ); + im_semaphore_init( &tg->idle_sem, 0, "idle" ); + +#ifdef DEBUG_IO + printf( "threadgroup_kill_threads: killed %d threads\n", + tg->nthr ); +#endif /*DEBUG_IO*/ + } +} + +/* Free a threadgroup. Can be called multiple times. + */ +int +im_threadgroup_free( im_threadgroup_t *tg ) +{ +#ifdef DEBUG_IO + printf( "im_threadgroup_free: \"%s\" (%p)\n", tg->im->filename, tg ); +#endif /*DEBUG_IO*/ + + if( !tg || tg->zombie ) + return( 0 ); + + threadgroup_kill_threads( tg ); + + im_semaphore_destroy( &tg->idle_sem ); + IM_FREEF( g_mutex_free, tg->idle_lock ); + tg->zombie = -1; + +#ifdef DEBUG_HIGHWATER + printf( "im_threadgroup_free %p: max busy workers = %d\n", + tg, tg->nthr - tg->min_idle ); +#endif /*DEBUG_HIGHWATER*/ + + return( 0 ); +} + +/* Attach a threadgroup to an image. + */ +im_threadgroup_t * +im_threadgroup_create( IMAGE *im ) +{ + im_threadgroup_t *tg; + int i; + + /* Allocate and init new thread block. + */ + if( !(tg = IM_NEW( im, im_threadgroup_t )) ) + return( NULL ); + tg->zombie = 0; + tg->im = im; + tg->work = NULL; + tg->inplace = 0; + if( (tg->nthr = im_concurrency_get()) < 0 ) + return( NULL ); + tg->thr = NULL; + tg->kill = 0; + + /* Pick a render geometry. + */ + switch( tg->im->dhint ) { + case IM_SMALLTILE: + tg->pw = im__tile_width; + tg->ph = im__tile_height; + tg->nlines = tg->ph; + break; + + case IM_FATSTRIP: + tg->pw = tg->im->Xsize; + tg->ph = im__fatstrip_height; + tg->nlines = tg->ph * tg->nthr * 2; + break; + + case IM_ANY: + case IM_THINSTRIP: + tg->pw = tg->im->Xsize; + tg->ph = im__thinstrip_height; + tg->nlines = tg->ph * tg->nthr * 2; + break; + + default: + error_exit( "panic: internal error #98i245983425" ); + } + + /* Attach tidy-up callback. + */ + if( im_add_close_callback( im, + (im_callback_fn) im_threadgroup_free, tg, NULL ) ) { + (void) im_threadgroup_free( tg ); + return( NULL ); + } + +#ifdef DEBUG_IO + printf( "im_threadgroup_create: %d by %d patches, " + "groups of %d scanlines\n", tg->pw, tg->ph, tg->nlines ); +#endif /*DEBUG_IO*/ + + /* Init locks. + */ + im_semaphore_init( &tg->idle_sem, 0, "idle" ); + tg->idle = NULL; + tg->idle_lock = g_mutex_new(); + +#ifdef DEBUG_HIGHWATER + tg->nidle = 0; + tg->min_idle = tg->nthr; +#endif /*DEBUG_HIGHWATER*/ + + /* Make thread array. + */ + if( !(tg->thr = IM_ARRAY( im, tg->nthr + 1, im_thread_t * )) ) + return( NULL ); + for( i = 0; i < tg->nthr + 1; i++ ) + tg->thr[i] = NULL; + + /* Attach threads. + */ + for( i = 0; i < tg->nthr; i++ ) + if( !(tg->thr[i] = threadgroup_thread_new( tg )) ) + return( NULL ); + +#ifdef DEBUG_IO + printf( "im_threadgroup_create: \"%s\" (%p), with %d threads\n", + im->filename, tg, tg->nthr ); +#endif /*DEBUG_IO*/ + + return( tg ); +} + +/* Trigger work. If not threading, just call fn directly. + */ +void +im_threadgroup_trigger( im_thread_t *thr ) +{ + im_threadgroup_t *tg = thr->tg; + + /* thr pos needs to be set before coming here ... check. + */ +{ + Rect image; + + image.left = 0; + image.top = 0; + image.width = tg->im->Xsize; + image.height = tg->im->Ysize; + + assert( im_rect_includesrect( &image, &thr->pos ) ); +} + + /* Start worker going. + */ + im_semaphore_up( &thr->go ); + +#ifndef HAVE_THREADS + /* No threading ... just eval directly. + */ + work_fn( thr ); +#endif /*HAVE_THREADS*/ +} + +/* Test all threads for error. + */ +int +im_threadgroup_iserror( im_threadgroup_t *tg ) +{ + int i; + + if( tg->kill ) + return( -1 ); + if( tg->im->kill ) + return( -1 ); + + for( i = 0; i < tg->nthr; i++ ) + if( tg->thr[i]->error ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/time.c b/libsrc/iofuncs/time.c new file mode 100644 index 00000000..3fc88c7b --- /dev/null +++ b/libsrc/iofuncs/time.c @@ -0,0 +1,112 @@ +/* Time execution of pipelines. + * + * 20/7/93 JC + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Allocate a new time struct and fill in start values. + */ +static int +new_time( IMAGE *im ) +{ + struct time_info *tim = IM_NEW( im, struct time_info ); + + if( !tim ) + return( -1 ); + + if( im->time ) { + im_errormsg( "new_time: sanity failure" ); + return( -1 ); + } + + tim->im = im; + tim->start = time( NULL ); + tim->run = 0; + tim->eta = 0; + tim->tpels = (gint64) im->Xsize * im->Ysize; + tim->npels = 0; + tim->percent = 0; + im->time = tim; + + return( 0 ); +} + +/* A new tile has been computed. Update time_info. + */ +static int +update_time( struct time_info *tim, int w, int h ) +{ + float prop; + + tim->run = time( NULL ) - tim->start; + tim->npels += w * h; + prop = (float) tim->npels / (float) tim->tpels; + tim->percent = 100 * prop; + if( prop > 0 ) + tim->eta = (1.0 / prop) * tim->run - tim->run; + + return( 0 ); +} + +/* Handle eval callbacks. w and h are the size of the tile we made this time. + */ +int +im__handle_eval( IMAGE *im, int w, int h ) +{ + if( im->evalfns ) { + if( !im->time ) + if( new_time( im ) ) + return( -1 ); + if( update_time( im->time, w, h ) ) + return( -1 ); + + if( im__trigger_callbacks( im->evalfns ) ) + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/util.c b/libsrc/iofuncs/util.c new file mode 100644 index 00000000..eb0872eb --- /dev/null +++ b/libsrc/iofuncs/util.c @@ -0,0 +1,915 @@ +/* Some basic util functions. + */ + +/* + + Copyright (C) 1991-2003 The National Gallery + + 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 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Temp buffer for snprintf() layer on old systems. + */ +#define MAX_BUF (32768) + +/* Test two lists for eqality. + */ +gboolean +im_slist_equal( GSList *l1, GSList *l2 ) +{ + while( l1 && l2 ) { + if( l1->data != l2->data ) + return( FALSE ); + + l1 = l1->next; + l2 = l2->next; + } + + if( l1 || l2 ) + return( FALSE ); + + return( TRUE ); +} + +/* Map over an slist. _copy() the list in case the callback changes it. + */ +void * +im_slist_map2( GSList *list, VSListMap2Fn fn, void *a, void *b ) +{ + GSList *copy; + GSList *i; + void *result; + + copy = g_slist_copy( list ); + result = NULL; + for( i = copy; i && !(result = fn( i->data, a, b )); i = i->next ) + ; + g_slist_free( copy ); + + return( result ); +} + +void * +im_slist_map4( GSList *list, + VSListMap4Fn fn, void *a, void *b, void *c, void *d ) +{ + GSList *copy; + GSList *i; + void *result; + + copy = g_slist_copy( list ); + result = NULL; + for( i = copy; + i && !(result = fn( i->data, a, b, c, d )); i = i->next ) + ; + g_slist_free( copy ); + + return( result ); +} + +/* Map backwards. We _reverse() rather than recurse and unwind to save stack. + */ +void * +im_slist_map2_rev( GSList *list, VSListMap2Fn fn, void *a, void *b ) +{ + GSList *copy; + GSList *i; + void *result; + + copy = g_slist_copy( list ); + copy = g_slist_reverse( copy ); + result = NULL; + for( i = copy; i && !(result = fn( i->data, a, b )); i = i->next ) + ; + g_slist_free( copy ); + + return( result ); +} + +void * +im_map_equal( void *a, void *b ) +{ + if( a == b ) + return( a ); + + return( NULL ); +} + +void * +im_slist_fold2( GSList *list, void *start, VSListFold2Fn fn, void *a, void *b ) +{ + void *c; + GSList *this, *next; + + for( c = start, this = list; this; this = next ) { + next = this->next; + + if( !(c = fn( this->data, c, a, b )) ) + return( NULL ); + } + + return( c ); +} + +static void +im_slist_free_all_cb( void * thing, void * dummy ) +{ + im_free( thing ); +} + +/* Free a g_slist of things which need im_free()ing. + */ +void +im_slist_free_all( GSList *list ) +{ + g_slist_foreach( list, im_slist_free_all_cb, NULL ); + g_slist_free( list ); +} + +/* Remove all occurences of an item from a list. + */ +GSList * +im_slist_filter( GSList *list, VSListMap2Fn fn, void *a, void *b ) +{ + GSList *tmp; + GSList *prev; + + prev = NULL; + tmp = list; + + while( tmp ) { + if( fn( tmp->data, a, b ) ) { + GSList *next = tmp->next; + + if( prev ) + prev->next = next; + if( list == tmp ) + list = next; + + tmp->next = NULL; + g_slist_free( tmp ); + tmp = next; + } + else { + prev = tmp; + tmp = tmp->next; + } + } + + return( list ); +} + +/* Like strncpy(), but always NULL-terminate, and don't pad with NULLs. + */ +char * +im_strncpy( char *dest, const char *src, int n ) +{ + int i; + + assert( n > 0 ); + + for( i = 0; i < n - 1; i++ ) + if( !(dest[i] = src[i]) ) + break; + dest[i] = '\0'; + + return( dest ); +} + +/* Find the rightmost occurrence of needle in haystack. + */ +char * +im_strrstr( const char *haystack, const char *needle ) +{ + int haystack_len = strlen( haystack ); + int needle_len = strlen( needle ); + int i; + + for( i = haystack_len - needle_len; i >= 0; i-- ) + if( strncmp( needle, haystack + i, needle_len ) == 0 ) + return( (char *) haystack + i ); + + return( NULL ); +} + +/* strdup local to a descriptor. + */ +char * +im_strdup( IMAGE *im, const char *str ) +{ + int l = strlen( str ); + char *buf; + + if( !(buf = (char *) im_malloc( im, l + 1 )) ) + return( NULL ); + strcpy( buf, str ); + + return( buf ); +} + +/* Test for string b ends string a. + */ +gboolean +im_ispostfix( const char *a, const char *b ) +{ + int m = strlen( a ); + int n = strlen( b ); + + if( n > m ) + return( FALSE ); + + return( strcmp( a + m - n, b ) == 0 ); +} + +/* Test for string a starts string b. + */ +gboolean +im_isprefix( const char *a, const char *b ) +{ + int n = strlen( a ); + int m = strlen( b ); + int i; + + if( m < n ) + return( FALSE ); + for( i = 0; i < n; i++ ) + if( a[i] != b[i] ) + return( FALSE ); + + return( TRUE ); +} + +/* Like strtok(). Give a string and a list of break characters. Then: + * - skip initial break characters + * - EOS? return NULL + * - skip a series of non-break characters + * - write a '\0' over the next break character and return a pointer to the + * char after that + * + * The idea is that this can be used in loops as the iterator. Example: + * + * char *p = " 1 2 3 "; // mutable + * char *q; + * int i; + * int v[...]; + * + * for( i = 0; (q = im_break_token( p, " " )); i++, p = q ) + * v[i] = atoi( p ); + * + * will set + * v[0] = 1; + * v[1] = 2; + * v[2] = 3; + * + * or with just one pointer, provided your atoi() is OK with trailing chars + * and you know there is at least one item there + * + * char *p = " 1 2 3 "; // mutable + * int i; + * int v[...]; + * + * for( i = 0; p; p = im_break_token( p, " " ) ) + * v[i] = atoi( p ); + */ +char * +im_break_token( char *str, const char *brk ) +{ + char *p; + + /* Is the string empty? If yes, return NULL immediately. + */ + if( !str || !*str ) + return( NULL ); + + /* Skip initial break characters. + */ + p = str + strspn( str, brk ); + + /* No item? + */ + if( !*p ) + return( NULL ); + + /* We have a token ... search for the first break character after the + * token. + */ + p += strcspn( p, brk ); + + /* Is there string left? + */ + if( *p ) { + /* Write in an end-of-string mark and return the start of the + * next token. + */ + *p++ = '\0'; + p += strspn( p, brk ); + } + + return( p ); +} + +/* Wrapper over (v)snprintf() ... missing on old systems. + */ +int +im_vsnprintf( char *str, size_t size, const char *format, va_list ap ) +{ +#ifdef HAVE_VSNPRINTF + return( vsnprintf( str, size, format, ap ) ); +#else /*HAVE_VSNPRINTF*/ + /* Bleurg! + */ + int n; + static char buf[MAX_BUF]; + + if( size > MAX_BUF ) + error_exit( "panic: buffer overflow " + "(request to write %d bytes to buffer of %d bytes)", + size, MAX_BUF ); + n = vsprintf( buf, format, ap ); + if( n > MAX_BUF ) + error_exit( "panic: buffer overflow " + "(%d bytes written to buffer of %d bytes)", + n, MAX_BUF ); + + im_strncpy( str, buf, size ); + + return( n ); +#endif /*HAVE_VSNPRINTF*/ +} + +int +im_snprintf( char *str, size_t size, const char *format, ... ) +{ + va_list ap; + int n; + + va_start( ap, format ); + n = im_vsnprintf( str, size, format, ap ); + va_end( ap ); + + return( n ); +} + +/* Split filename into name / mode components. name and mode should both be + * FILENAME_MAX chars. + * + * We look for the ':' splitting the name and mode by searching for the + * rightmost occurence of the regexp ".[A-Za-z0-9]+:". Example: consider the + * horror that is + * + * c:\silly:dir:name\fr:ed.tif:jpeg:95,,,,c:\icc\srgb.icc + * + */ +void +im_filename_split( const char *path, char *name, char *mode ) +{ + char *p; + + im_strncpy( name, path, FILENAME_MAX ); + + /* Search back towards start stopping at each ':' char. + */ + for( p = name + strlen( name ) - 1; p > name; p -= 1 ) + if( *p == ':' ) { + char *q; + + for( q = p - 1; isalnum( *q ) && q > name; q -= 1 ) + ; + + if( *q == '.' ) + break; + } + + if( *p == ':' ) { + im_strncpy( mode, p + 1, FILENAME_MAX ); + *p = '\0'; + } + else + strcpy( mode, "" ); +} + +/* Skip any leading path stuff. Horrible: if this is a filename which came + * from win32 and we're a *nix machine, it'll have '\\' not '/' as the + * separator :-( + * + * Try to fudge this ... if the file doesn't contain any of our native + * separators, look for the opposite one as well. If there are none of those + * either, just return the filename. + */ +const char * +im_skip_dir( const char *path ) +{ + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + const char *p; + const char *q; + + const char native_dir_sep = G_DIR_SEPARATOR; + const char non_native_dir_sep = native_dir_sep == '/' ? '\\' : '/'; + + /* Remove any trailing save modifiers: we don't want '/' or '\' in the + * modifier confusing us. + */ + im_filename_split( path, name, mode ); + + /* The '\0' char at the end of the string. + */ + p = name + strlen( name ); + + /* Search back for the first native dir sep, or failing that, the first + * non-native dir sep. + */ + for( q = p; q > name && q[-1] != native_dir_sep; q-- ) + ; + if( q == name ) + for( q = p; q > name && q[-1] != non_native_dir_sep; q-- ) + ; + + return( path + (q - name) ); +} + +/* Extract suffix from filename, ignoring any mode string. Suffix should be + * FILENAME_MAX chars. + */ +void +im_filename_suffix( const char *path, char *suffix ) +{ + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char *p; + + im_filename_split( path, name, mode ); + if( (p = strrchr( name, '.' )) ) + strcpy( suffix, p + 1 ); + else + strcpy( suffix, "" ); +} + +/* Does a filename have one of a set of suffixes. Ignore case. + */ +int +im_filename_suffix_match( const char *path, const char *suffixes[] ) +{ + char suffix[FILENAME_MAX]; + const char **p; + + im_filename_suffix( path, suffix ); + for( p = suffixes; *p; p++ ) + if( g_ascii_strcasecmp( suffix, *p ) == 0 ) + return( 1 ); + + return( 0 ); +} + +/* p points to the start of a buffer ... move it on through the buffer (ready + * for the next call), and return the current option (or NULL for option + * missing). ',' characters inside options can be escaped with a '\'. + */ +char * +im_getnextoption( char **in ) +{ + char *p = *in; + char *q = p; + + if( !p || !*p ) + return( NULL ); + + /* Find the next ',' not prefixed with a '\'. + */ + while( (p = strchr( p, ',' )) && p[-1] == '\\' ) + p += 1; + + if( p ) { + /* Another option follows this one .. set up to pick that out + * next time. + */ + *p = '\0'; + *in = p + 1; + } + else { + /* This is the last one. + */ + *in = NULL; + } + + if( strlen( q ) > 0 ) + return( q ); + else + return( NULL ); +} + +/* Get a suboption string, or NULL. + */ +char * +im_getsuboption( const char *buf ) +{ + char *p, *q, *r; + + if( !(p = strchr( buf, ':' )) ) + /* No suboption. + */ + return( NULL ); + + /* Step over the ':'. + */ + p += 1; + + /* Need to unescape any \, pairs. Shift stuff down one if we find one. + */ + for( q = p; *q; q++ ) + if( q[0] == '\\' && q[1] == ',' ) + for( r = q; *r; r++ ) + r[0] = r[1]; + + return( p ); +} + +/* Make something local to an image descriptor ... pass in a constructor + * and a destructor, plus three args. + */ +void * +im_local( IMAGE *im, + im_construct_fn cons, im_callback_fn dest, void *a, void *b, void *c ) +{ + void *obj; + + if( !im ) { + im_errormsg( "im_local: NULL image descriptor" ); + return( NULL ); + } + + if( !(obj = cons( a, b, c )) ) + return( NULL ); + if( im_add_close_callback( im, (im_callback_fn) dest, obj, a ) ) { + dest( obj, a ); + return( NULL ); + } + + return( obj ); +} + +/* Make an array of things local to a descriptor ... eg. make 6 local temp + * images. + */ +int +im_local_array( IMAGE *im, void **out, int n, + im_construct_fn cons, im_callback_fn dest, void *a, void *b, void *c ) +{ + int i; + + for( i = 0; i < n; i++ ) + if( !(out[i] = im_local( im, cons, dest, a, b, c )) ) + return( -1 ); + + return( 0 ); +} + +/* Get file length ... 64-bitally. -1 for error. + */ +gint64 +im_file_length( int fd ) +{ +#ifdef OS_WIN32 + struct _stati64 st; + + if( _fstati64( fd, &st ) == -1 ) { +#else /*!OS_WIN32*/ + struct stat st; + + if( fstat( fd, &st ) == -1 ) { +#endif /*OS_WIN32*/ + im_error_system( errno, "im_file_length", + _( "unable to get file stats" ) ); + return( -1 ); + } + + return( st.st_size ); +} + +/* Wrap write() up + */ +int +im__write( int fd, const void *buf, size_t count ) +{ + do { + size_t nwritten = write( fd, buf, count ); + + if( nwritten == (size_t) -1 ) { + im_error_system( errno, "im__write", + _( "write failed" ) ); + return( -1 ); + } + + buf = (void *) ((char *) buf + nwritten); + count -= nwritten; + } while( count > 0 ); + + return( 0 ); +} + +/* Load up a file as a string. + */ +char * +im__file_read( FILE *fp, const char *name, unsigned int *length_out ) +{ + long len; + size_t read; + char *str; + + /* Find length. + */ + fseek( fp, 0L, 2 ); + len = ftell( fp ); + if( len > 20 * 1024 * 1024 ) { + /* Seems crazy! + */ + im_error( "im__file_read", _( "\"%s\" too long" ), name ); + return( NULL ); + } + + if( len == -1 ) { + int size; + + /* Can't get length: read in chunks and realloc() to end of + * file. + */ + str = NULL; + len = 0; + size = 0; + do { + size += 1024; + if( !(str = realloc( str, size )) ) { + im_error( "im__file_read", + _( "out of memory" ) ); + return( NULL ); + } + + /* -1 to allow space for an extra NULL we add later. + */ + read = fread( str + len, sizeof( char ), + (size - len - 1) / sizeof( char ), + fp ); + len += read; + } while( !feof( fp ) ); + +#ifdef DEBUG + printf( "read %d bytes from unseekable stream\n", len ); +#endif /*DEBUG*/ + } + else { + /* Allocate memory and fill. + */ + if( !(str = im_malloc( NULL, len + 1 )) ) + return( NULL ); + rewind( fp ); + read = fread( str, sizeof( char ), (size_t) len, fp ); + if( read != (size_t) len ) { + im_free( str ); + im_error( "im__file_read", + _( "error reading from file \"%s\"" ), name ); + return( NULL ); + } + } + + str[len] = '\0'; + + if( length_out ) + *length_out = len; + + return( str ); +} + +/* Load from a filename as a string. + */ +char * +im__file_read_name( const char *name, unsigned int *length_out ) +{ + FILE *fp; + char *buffer; + +#ifdef BINARY_OPEN + if( !(fp = fopen( name, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(fp = fopen( name, "r" )) ) { +#endif /*BINARY_OPEN*/ + im_error( "im__file_read_name", + _( "unable to open file \"%s\"" ), name ); + return( NULL ); + } + if( !(buffer = im__file_read( fp, name, length_out )) ) { + fclose( fp ); + return( NULL ); + } + fclose( fp ); + + return( buffer ); +} + +/* Alloc/free a GValue. + */ +static GValue * +im__gvalue_new( GType type ) +{ + GValue *value; + + value = g_new0( GValue, 1 ); + g_value_init( value, type ); + + return( value ); +} + +static GValue * +im__gvalue_copy( GValue *value ) +{ + GValue *value_copy; + + value_copy = im__gvalue_new( G_VALUE_TYPE( value ) ); + g_value_copy( value, value_copy ); + + return( value_copy ); +} + +static void +im__gvalue_free( GValue *value ) +{ + g_value_unset( value ); + g_free( value ); +} + +GValue * +im__gvalue_ref_string_new( const char *text ) +{ + GValue *value; + + value = im__gvalue_new( IM_TYPE_REF_STRING ); + im_ref_string_set( value, text ); + + return( value ); +} + +/* Free a GSList of GValue. + */ +void +im__gslist_gvalue_free( GSList *list ) +{ + g_slist_foreach( list, (GFunc) im__gvalue_free, NULL ); + g_slist_free( list ); +} + +/* Copy a GSList of GValue. + */ +GSList * +im__gslist_gvalue_copy( const GSList *list ) +{ + GSList *copy; + const GSList *p; + + copy = NULL; + + for( p = list; p; p = p->next ) + copy = g_slist_prepend( copy, + im__gvalue_copy( (GValue *) p->data ) ); + + copy = g_slist_reverse( copy ); + + return( copy ); +} + +/* Merge two GSList of GValue ... append to a all elements in b which are not + * in a. Return the new value of a. Works for any vips refcounted type + * (string, blob, etc.). + */ +GSList * +im__gslist_gvalue_merge( GSList *a, const GSList *b ) +{ + const GSList *i, *j; + GSList *tail; + + tail = NULL; + + for( i = b; i; i = i->next ) { + GValue *value = (GValue *) i->data; + + assert( G_VALUE_TYPE( value ) == IM_TYPE_REF_STRING ); + + for( j = a; j; j = j->next ) { + GValue *value2 = (GValue *) j->data; + + assert( G_VALUE_TYPE( value2 ) == IM_TYPE_REF_STRING ); + + /* Just do a pointer compare ... good enough 99.9% of + * the time. + */ + if( im_ref_string_get( value ) == + im_ref_string_get( value2 ) ) + break; + } + + if( !j ) + tail = g_slist_prepend( tail, + im__gvalue_copy( value ) ); + } + + a = g_slist_concat( a, g_slist_reverse( tail ) ); + + return( a ); +} + +/* Make a char* from GSList of GValue. Each GValue should be a ref_string. + * free the result. Empty list -> "", not NULL. Join strings with '\n'. + */ +char * +im__gslist_gvalue_get( const GSList *list ) +{ + const GSList *p; + size_t length; + char *all; + char *q; + + /* Need to estimate length first. + */ + length = 0; + for( p = list; p; p = p->next ) { + GValue *value = (GValue *) p->data; + + assert( G_VALUE_TYPE( value ) == IM_TYPE_REF_STRING ); + + /* +1 for the newline we will add for each item. + */ + length += im_ref_string_get_length( value ) + 1; + } + + if( length == 0 ) + return( NULL ); + + /* More than 10MB of history? Madness! + */ + assert( length < 10 * 1024 * 1024 ); + + /* +1 for '\0'. + */ + if( !(all = im_malloc( NULL, length + 1 )) ) + return( NULL ); + + q = all; + for( p = list; p; p = p->next ) { + GValue *value = (GValue *) p->data; + + strcpy( q, im_ref_string_get( value ) ); + q += im_ref_string_get_length( value ); + strcpy( q, "\n" ); + q += 1; + } + + assert( q - all == length ); + + return( all ); +} diff --git a/libsrc/iofuncs/vbuf.c b/libsrc/iofuncs/vbuf.c new file mode 100644 index 00000000..5735c26a --- /dev/null +++ b/libsrc/iofuncs/vbuf.c @@ -0,0 +1,332 @@ +/* Some basic util functions. + */ + +/* + + Copyright (C) 1991-2003 The National Gallery + + 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 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Largest string we can append in one operation. + */ +#define MAX_STRSIZE (16000) + +void +im_buf_rewind( VBuf *buf ) +{ + buf->i = 0; + buf->lasti = 0; + buf->full = FALSE; + + if( buf->base ) + buf->base[0] = '\0'; +} + +/* Power on init. + */ +void +im_buf_init( VBuf *buf ) +{ + buf->base = NULL; + buf->mx = 0; + buf->dynamic = FALSE; + im_buf_rewind( buf ); +} + +/* Reset to power on state ... only needed for dynamic bufs. + */ +void +im_buf_destroy( VBuf *buf ) +{ + if( buf->dynamic ) { + if( buf->base ) { + im_free( buf->base ); + buf->base = NULL; + } + } + + im_buf_init( buf ); +} + +/* Set to a static string. + */ +void +im_buf_set_static( VBuf *buf, char *base, int mx ) +{ + assert( mx >= 4 ); + + im_buf_destroy( buf ); + + buf->base = base; + buf->mx = mx; + buf->dynamic = FALSE; + im_buf_rewind( buf ); +} + +void +im_buf_init_static( VBuf *buf, char *base, int mx ) +{ + im_buf_init( buf ); + im_buf_set_static( buf, base, mx ); +} + +/* Set to a dynamic string. + */ +void +im_buf_set_dynamic( VBuf *buf, int mx ) +{ + assert( mx >= 4 ); + + if( buf->mx == mx && buf->dynamic ) + /* No change? + */ + im_buf_rewind( buf ); + else { + im_buf_destroy( buf ); + + if( !(buf->base = IM_ARRAY( NULL, mx, char )) ) + /* No error return, so just block writes. + */ + buf->full = TRUE; + else { + buf->mx = mx; + buf->dynamic = TRUE; + im_buf_rewind( buf ); + } + } +} + +void +im_buf_init_dynamic( VBuf *buf, int mx ) +{ + im_buf_init( buf ); + im_buf_set_dynamic( buf, mx ); +} + +/* Append at most sz chars from string to buf. sz < 0 means unlimited. + * FALSE on overflow. + */ +gboolean +im_buf_appendns( VBuf *buf, const char *str, int sz ) +{ + int len; + int n; + int avail; + int cpy; + + if( buf->full ) + return( FALSE ); + + /* Amount we want to copy. + */ + len = strlen( str ); + if( sz >= 0 ) + n = IM_MIN( sz, len ); + else + n = len; + + /* Space available. + */ + avail = buf->mx - buf->i - 4; + + /* Amount we actually copy. + */ + cpy = IM_MIN( n, avail ); + + strncpy( buf->base + buf->i, str, cpy ); + buf->i += cpy; + + if( buf->i >= buf->mx - 4 ) { + buf->full = TRUE; + strcpy( buf->base + buf->mx - 4, "..." ); + buf->i = buf->mx - 1; + return( FALSE ); + } + + return( TRUE ); +} + +/* Append a string to a buf. Error on overflow. + */ +gboolean +im_buf_appends( VBuf *buf, const char *str ) +{ + return( im_buf_appendns( buf, str, -1 ) ); +} + +/* Append a character to a buf. Error on overflow. + */ +gboolean +im_buf_appendc( VBuf *buf, char ch ) +{ + char tiny[2]; + + tiny[0] = ch; + tiny[1] = '\0'; + + return( im_buf_appendns( buf, tiny, 1 ) ); +} + +/* Append a double, non-localised. Useful for config files etc. + */ +gboolean +im_buf_appendg( VBuf *buf, double g ) +{ + char text[G_ASCII_DTOSTR_BUF_SIZE]; + + g_ascii_dtostr( text, sizeof( text ), g ); + + return( im_buf_appends( buf, text ) ); +} + + +/* Swap the rightmost occurence of old for new. + */ +gboolean +im_buf_change( VBuf *buf, const char *old, const char *new ) +{ + int olen = strlen( old ); + int nlen = strlen( new ); + int i; + + if( buf->full ) + return( FALSE ); + if( buf->i - olen + nlen > buf->mx - 4 ) { + buf->full = TRUE; + return( FALSE ); + } + + /* Find pos of old. + */ + for( i = buf->i - olen; i > 0; i-- ) + if( im_isprefix( old, buf->base + i ) ) + break; + assert( i >= 0 ); + + /* Move tail of buffer to make right-size space for new. + */ + memmove( buf->base + i + nlen, buf->base + i + olen, + buf->i - i - olen ); + + /* Copy new in. + */ + memcpy( buf->base + i, new, nlen ); + buf->i = i + nlen + (buf->i - i - olen); + + return( TRUE ); +} + +/* Remove the last character. + */ +gboolean +im_buf_removec( VBuf *buf, char ch ) +{ + if( buf->full ) + return( FALSE ); + + if( buf->i <= 0 ) + return( FALSE ); + buf->i -= 1; + assert( buf->base[buf->i] == ch ); + + return( TRUE ); +} + +/* Append to a buf, args as printf. FALSE on overflow. + */ +gboolean +im_buf_appendf( VBuf *buf, const char *fmt, ... ) +{ + va_list ap; + char str[MAX_STRSIZE]; + + va_start( ap, fmt ); + (void) im_vsnprintf( str, MAX_STRSIZE, fmt, ap ); + va_end( ap ); + + return( im_buf_appends( buf, str ) ); +} + +/* Append to a buf, args as vprintf. Error on overflow. + */ +gboolean +im_buf_vappendf( VBuf *buf, const char *fmt, va_list ap ) +{ + char str[MAX_STRSIZE]; + + (void) im_vsnprintf( str, MAX_STRSIZE, fmt, ap ); + + return( im_buf_appends( buf, str ) ); +} + +/* Read all text from buffer. + */ +const char * +im_buf_all( VBuf *buf ) +{ + buf->base[buf->i] = '\0'; + + return( buf->base ); +} + +gboolean +im_buf_isempty( VBuf *buf ) +{ + return( buf->i == 0 ); +} + +gboolean +im_buf_isfull( VBuf *buf ) +{ + return( buf->full ); +} + +/* Buffer length ... still need to do im_buf_all(). + */ +int +im_buf_len( VBuf *buf ) +{ + return( buf->i ); +} diff --git a/libsrc/iofuncs/window.c b/libsrc/iofuncs/window.c new file mode 100644 index 00000000..6539ea44 --- /dev/null +++ b/libsrc/iofuncs/window.c @@ -0,0 +1,388 @@ +/* Manage sets of mmap buffers on an image. + * + * 30/10/06 + * - from region.c + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_TOTAL +#define DEBUG_ENVIRONMENT +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include +#ifdef HAVE_SYS_MMAN_H +#include +#endif +#include + +#include +#include +#include + +#ifdef OS_WIN32 +#include +#endif /*OS_WIN32*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Sanity checking ... write to this during read tests to make sure we don't + * get optimised out. + */ +int im__read_test; + +/* Add this many lines above and below the mmap() window. + */ +int im__window_margin = IM__WINDOW_MARGIN; + +/* Track global mmap usage. + */ +#ifdef DEBUG_TOTAL +static int total_mmap_usage = 0; +static int max_mmap_usage = 0; +#endif /*DEBUG_TOTAL*/ + +static int +im_window_unmap( im_window_t *window ) +{ + /* unmap the old window + */ + if( window->baseaddr ) { + if( im__munmap( window->baseaddr, window->length ) ) + return( -1 ); + +#ifdef DEBUG_TOTAL + g_mutex_lock( im__global_lock ); + total_mmap_usage -= window->length; + assert( total_mmap_usage >= 0 ); + g_mutex_unlock( im__global_lock ); +#endif /*DEBUG_TOTAL*/ + + window->data = NULL; + window->baseaddr = NULL; + window->length = 0; + } + + return( 0 ); +} + +static int +im_window_free( im_window_t *window ) +{ + assert( window->ref_count == 0 ); + +#ifdef DEBUG + printf( "** im_window_free: window top = %d, height = %d (%p)\n", + window->top, window->height, window ); +#endif /*DEBUG*/ + + if( im_window_unmap( window ) ) + return( -1 ); + + window->im = NULL; + + im_free( window ); + + return( 0 ); +} + +int +im_window_unref( im_window_t *window ) +{ + IMAGE *im = window->im; + + g_mutex_lock( im->sslock ); + +#ifdef DEBUG + printf( "im_window_unref: window top = %d, height = %d, count = %d\n", + window->top, window->height, window->ref_count ); +#endif /*DEBUG*/ + + assert( window->ref_count > 0 ); + + window->ref_count -= 1; + + if( window->ref_count == 0 ) { + assert( g_slist_find( im->windows, window ) ); + im->windows = g_slist_remove( im->windows, window ); + +#ifdef DEBUG + printf( "im_window_unref: %d windows left\n", + g_slist_length( im->windows ) ); +#endif /*DEBUG*/ + + if( im_window_free( window ) ) { + g_mutex_unlock( im->sslock ); + return( -1 ); + } + } + + g_mutex_unlock( im->sslock ); + + return( 0 ); +} + +#ifdef DEBUG_TOTAL +static void +trace_mmap_usage( void ) +{ + g_mutex_lock( im__global_lock ); + { + static int last_total = 0; + int total = total_mmap_usage / (1024 * 1024); + int max = max_mmap_usage / (1024 * 1024); + + if( total != last_total ) { + printf( "im_window_set: current mmap " + "usage of ~%dMB (high water mark %dMB)\n", + total, max ); + last_total = total; + } + } + g_mutex_unlock( im__global_lock ); +} +#endif /*DEBUG_TOTAL*/ + +static int +im_getpagesize() +{ + static int pagesize = 0; + + if( !pagesize ) { +#ifdef OS_WIN32 + SYSTEM_INFO si; + + GetSystemInfo( &si ); + + pagesize = si.dwAllocationGranularity; +#else /*OS_WIN32*/ + pagesize = getpagesize(); +#endif /*OS_WIN32*/ + +#ifdef DEBUG_TOTAL + printf( "im_getpagesize: 0x%x\n", pagesize ); +#endif /*DEBUG_TOTAL*/ + } + + return( pagesize ); +} + +/* Map a window into a file. + */ +static int +im_window_set( im_window_t *window, int top, int height ) +{ + int pagesize = im_getpagesize(); + + void *baseaddr; + gint64 start, end, pagestart; + size_t length, pagelength; + + /* Calculate start and length for our window. + */ + start = window->im->sizeof_header + + (gint64) IM_IMAGE_SIZEOF_LINE( window->im ) * top; + length = (size_t) IM_IMAGE_SIZEOF_LINE( window->im ) * height; + + pagestart = start - start % pagesize; + end = start + length; + pagelength = end - pagestart; + + if( !(baseaddr = im__mmap( window->im->fd, 0, pagelength, pagestart )) ) + return( -1 ); + + window->baseaddr = baseaddr; + window->length = pagelength; + + window->data = (char *) baseaddr + (start - pagestart); + window->top = top; + window->height = height; + + /* Sanity check ... make sure the data pointer is readable. + */ + im__read_test &= window->data[0]; + +#ifdef DEBUG_TOTAL + g_mutex_lock( im__global_lock ); + total_mmap_usage += window->length; + if( total_mmap_usage > max_mmap_usage ) + max_mmap_usage = total_mmap_usage; + g_mutex_unlock( im__global_lock ); + trace_mmap_usage(); +#endif /*DEBUG_TOTAL*/ + + return( 0 ); +} + +/* Make a new window. + */ +static im_window_t * +im_window_new( IMAGE *im, int top, int height ) +{ + im_window_t *window; + + if( !(window = IM_NEW( NULL, im_window_t )) ) + return( NULL ); + + window->ref_count = 0; + window->im = im; + window->top = 0; + window->height = 0; + window->data = NULL; + window->baseaddr = NULL; + window->length = 0; + + if( im_window_set( window, top, height ) ) { + im_window_free( window ); + return( NULL ); + } + + im->windows = g_slist_prepend( im->windows, window ); + window->ref_count += 1; + +#ifdef DEBUG + printf( "** im_window_new: window top = %d, height = %d (%p)\n", + window->top, window->height, window ); +#endif /*DEBUG*/ + + return( window ); +} + +/* A request for an area of pixels. + */ +typedef struct { + int top; + int height; +} request_t; + +static void * +im_window_fits( im_window_t *window, request_t *req ) +{ + if( window->top <= req->top && + window->top + window->height >= req->top + req->height ) + return( window ); + + return( NULL ); +} + +/* Find an existing window that fits within top/height and return a ref. + */ +static im_window_t * +im_window_find( IMAGE *im, int top, int height ) +{ + request_t req; + im_window_t *window; + + req.top = top; + req.height = height; + window = im_slist_map2( im->windows, + (VSListMap2Fn) im_window_fits, &req, NULL ); + + if( window ) { + window->ref_count += 1; + +#ifdef DEBUG + printf( "im_window_find: ref window top = %d, height = %d, " + "count = %d\n", + top, height, window->ref_count ); +#endif /*DEBUG*/ + } + + return( window ); +} + +/* Return a ref to a window that encloses top/height. + */ +im_window_t * +im_window_ref( IMAGE *im, int top, int height ) +{ + im_window_t *window; +#ifdef DEBUG_ENVIRONMENT + static const char *str = NULL; +#endif /*DEBUG_ENVIRONMENT*/ + +#ifdef DEBUG_ENVIRONMENT + if( !str ) { + if( (str = g_getenv( "IM_WINDOW_MARGIN" )) ) { + printf( "iofuncs/region.c: " + "compiled with DEBUG_ENVIRONMENT enabled\n" ); + im__window_margin = atoi( str ); + printf( "im_region_create: setting window margin to %d " + "from environment\n", im__window_margin ); + } + else + str = "done"; + } +#endif /*DEBUG_ENVIRONMENT*/ + + g_mutex_lock( im->sslock ); + + if( !(window = im_window_find( im, top, height )) ) { + /* No existing window ... make a new one. + */ + top -= im__window_margin; + height += im__window_margin * 2; + top = IM_CLIP( 0, top, im->Ysize - 1 ); + height = IM_CLIP( 0, height, im->Ysize - top ); + + if( !(window = im_window_new( im, top, height )) ) { + g_mutex_unlock( im->sslock ); + return( NULL ); + } + } + + g_mutex_unlock( im->sslock ); + + return( window ); +} + +void +im_window_print( im_window_t *window ) +{ + printf( "im_window_t: %p ref_count = %d, ", window, window->ref_count ); + printf( "im = %p, ", window->im ); + printf( "top = %d, ", window->top ); + printf( "height = %d, ", window->height ); + printf( "data = %p, ", window->data ); + printf( "baseaddr = %p, ", window->baseaddr ); + printf( "length = %zd\n", window->length ); +} diff --git a/libsrc/makedef.pl b/libsrc/makedef.pl new file mode 100755 index 00000000..d84039ef --- /dev/null +++ b/libsrc/makedef.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl + +# update vips.def from "nm" output of the installed library +# +# not very portable :-( eg mac os x and win32 will fail horribly +# +# pass in the install prefix ... or type "make vips.def" + +open DATA, "nm -B @ARGV[0]/lib/libvips.so |"; + +while( ) { + next if ! /^[a-f0-9]+ T (im_[a-zA-Z].*)$/ && + ! /^[a-f0-9]+ T (error_exit)$/; + push @names, $1; +} + +print "EXPORTS\n"; +foreach $i (sort @names) { + print "\t$i\n"; +} diff --git a/libsrc/matrix/Makefile.am b/libsrc/matrix/Makefile.am new file mode 100644 index 00000000..54666042 --- /dev/null +++ b/libsrc/matrix/Makefile.am @@ -0,0 +1,13 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libmatrix.la + +libmatrix_la_SOURCES = \ + im_matcat.c \ + im_matinv.c \ + im_matmul.c \ + im_mattrn.c \ + matalloc.c \ + matrix_dispatch.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/matrix/im_matcat.c b/libsrc/matrix/im_matcat.c new file mode 100644 index 00000000..78e837dd --- /dev/null +++ b/libsrc/matrix/im_matcat.c @@ -0,0 +1,88 @@ +/* @(#) combine two masks. Result mask is made and returned. + * @(#) Pass in the name to set in the creation of the mask. + * @(#) DOUBLEMASK * + * @(#) im_matcat( in1, in2, name ); + * @(#) DOUBLEMASK *in1, *in2; + * @(#) char *name; + * @(#) + * @(#) return NULL for error. + * + * 1994, K. Martinez + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* MATRIX concatenate (join columns ie add mask to bottom of another) + */ +DOUBLEMASK * +im_matcat( DOUBLEMASK *in1, DOUBLEMASK *in2, const char *name ) +{ + int newxsize, newysize; + DOUBLEMASK *mat; + double *out; + + /* matrices must be same width + */ + if( in1->xsize != in2->xsize ) { + im_errormsg( "im_matcat: matrices must be same width" ); + return( NULL ); + } + + newxsize = in1->xsize; + newysize = in1->ysize + in2->ysize; + + /* Allocate output matrix. + */ + if( !(mat = im_create_dmask( name, newxsize, newysize )) ) { + im_errormsg( "im_matcat: unable to allocate output matrix" ); + return( NULL ); + } + + /* copy first matrix then add second on the end + */ + memcpy( mat->coeff, in1->coeff, + in1->xsize * in1->ysize * sizeof( double ) ); + out = mat->coeff + in1->xsize * in1->ysize; + memcpy( out, in2->coeff, + in2->xsize * in2->ysize * sizeof( double ) ); + + return( mat ); +} diff --git a/libsrc/matrix/im_matinv.c b/libsrc/matrix/im_matinv.c new file mode 100644 index 00000000..cded8686 --- /dev/null +++ b/libsrc/matrix/im_matinv.c @@ -0,0 +1,504 @@ +/* @(#) Allocate, and return a pointer to, a DOUBLEMASK representing the LU + * @(#) decomposition of the matrix in DOUBLEMASK *mat. Give it the filename + * @(#) member *name. Returns NULL on error. Scale and offset are ignored. + * @(#) + * @(#) DOUBLEMASK *im_lu_decomp( const DOUBLEMASK *mat, const char *name ); + * @(#) + * @(#) Solve the system of linear equations Ax=b, where matrix A has already + * @(#) been decomposed into LU form in DOUBLEMASK *lu. Input vector b is in + * @(#) vec and is overwritten with vector x. + * @(#) + * @(#) int im_lu_solve( const DOUBLEMASK *lu, double *vec ); + * @(#) + * @(#) Allocate, and return a pointer to, a DOUBLEMASK representing the + * @(#) inverse of the matrix represented in mat. Give it the filename + * @(#) member *name. Returns NULL on error. Scale and offset are ignored. + * @(#) + * @(#) DOUBLEMASK *im_matinv( const DOUBLEMASK *mat, const char *name ); + * @(#) + * @(#) Invert the matrix represented by the DOUBLEMASK *mat, and store + * @(#) it in the place of *mat. Returns -1 on error. Scale and offset + * @(#) are ignored. + * @(#) + * @(#) int im_matinv_inplace( DOUBLEMASK *mat ); + * + * Author: Tom Vajzovic + * Copyright: 2006, Tom Vajzovic + * Written on: 2006-09-08 + * + * undated: + * - page 43-45 of numerical recipes in C 1998 + * + * 2006-09-08 tcv: + * - complete rewrite; algorithm unchanged + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + + +/** CONSTANTS **/ + +#define TOO_SMALL ( 2.0 * DBL_MIN ) +/* DBL_MIN is smallest *normalized* double precision float */ + + +/** MACROS **/ + +#define MATRIX( mask, i, j ) ( (mask)-> coeff[ (j) + (i) * (mask)-> xsize ] ) +/* use DOUBLEMASK or INTMASK as matrix type */ + + +/** LOCAL FUNCTION DECLARATIONS **/ + +static int +mat_inv_lu( + DOUBLEMASK *inv, + const DOUBLEMASK *lu + ); + +static int +mat_inv_direct( + DOUBLEMASK *inv, + const DOUBLEMASK *mat, + const char *function_name + ); + + +/** EXPORTED FUNCTION DEFINITIONS **/ + +DOUBLEMASK * +im_lu_decomp( + const DOUBLEMASK *mat, + const char *name +){ +#define FUNCTION_NAME "im_lu_decomp" +/* This function takes any square NxN DOUBLEMASK treats it as a matrix. + * It allocates a DOUBLEMASK which is (N+1)xN. + * + * It calculates the PLU decomposition, storing the upper and diagonal parts + * of U, together with the lower parts of L, as an NxN matrix in the first + * N rows of the new matrix. The diagonal parts of L are all set to unity + * and are not stored. + * + * The final row of the new DOUBLEMASK has only integer entries, which + * represent the row-wise permutations made by the permuatation matrix P. + * + * The scale and offset members of the input DOUBLEMASK are ignored. + * + * See: + * PRESS, W. et al, 1992. Numerical Recipies in C; The Art of Scientific + * Computing, 2nd ed. Cambridge: Cambridge University Press, pp. 43-50. + */ + int i, j, k; + double *row_scale; + DOUBLEMASK *lu; + + if( mat-> xsize != mat-> ysize ){ + im_error( FUNCTION_NAME, "non-square matrix" ); + return NULL; + } +#define N ( mat -> xsize ) + + lu= im_create_dmask( name, N, N + 1 ); + row_scale= IM_ARRAY( NULL, N, double ); + + if( ! row_scale || ! lu ){ + im_free_dmask( lu ); + im_free( row_scale ); + return NULL; + } + /* copy all coefficients and then perform decomposition in-place */ + + memcpy( lu-> coeff, mat-> coeff, N * N * sizeof( double ) ); + +#define LU( i, j ) MATRIX( lu, (i), (j) ) +#define perm ( lu-> coeff + N * N ) + + for( i= 0; i < N; ++i ){ + + row_scale[ i ]= 0.0; + + for( j= 0; j < N; ++j ){ + double abs_val= fabs( LU( i, j ) ); + + /* find largest in each ROW */ + + if( abs_val > row_scale[ i ] ) + row_scale[ i ]= abs_val; + } + if( ! row_scale[ i ] ){ + im_error( FUNCTION_NAME, "singular matrix" ); + im_free_dmask( lu ); + im_free( row_scale ); + return NULL; + } + /* fill array with scaling factors for each ROW */ + + row_scale[ i ]= 1.0 / row_scale[ i ]; + } + for( j= 0; j < N; ++j ){ /* loop over COLs */ + double max= -1.0; + int i_of_max; + + /* not needed, but stops a compiler warning */ + i_of_max= 0; + + /* loop over ROWS in upper-half, except diagonal */ + + for( i= 0; i < j; ++i ) + for( k= 0; k < i; ++k ) + LU( i, j )-= LU( i, k ) * LU( k, j ); + + /* loop over ROWS in diagonal and lower-half */ + + for( i= j; i < N; ++i ){ + double abs_val; + + for( k= 0; k < j; ++k ) + LU( i, j )-= LU( i, k ) * LU( k, j ); + + /* find largest element in each COLUMN scaled so that */ + /* largest in each ROW is 1.0 */ + + abs_val= row_scale[ i ] * fabs( LU( i, j ) ); + + if( abs_val > max ){ + max= abs_val; + i_of_max= i; + } + } + if( fabs( LU( i_of_max, j ) ) < TOO_SMALL ){ + /* divisor is near zero */ + im_error( FUNCTION_NAME, "singular or near-singular matrix" ); + im_free_dmask( lu ); + im_free( row_scale ); + return NULL; + } + if( i_of_max != j ){ + /* swap ROWS */ + + for( k= 0; k < N; ++k ){ + double temp= LU( j, k ); + LU( j, k )= LU( i_of_max, k ); + LU( i_of_max, k )= temp; + } + row_scale[ i_of_max ]= row_scale[ j ]; + /* no need to copy this scale back up - we won't use it */ + } + /* record permutation */ + perm[ j ]= i_of_max; + + /* divide by best (largest scaled) pivot found */ + for( i= j + 1; i < N; ++i ) + LU( i, j )/= LU( j, j ); + } + im_free( row_scale ); + + return lu; + +#undef N +#undef LU +#undef perm +#undef FUNCTION_NAME +} + +int +im_lu_solve( + const DOUBLEMASK *lu, + double *vec +){ +#define FUNCTION_NAME "im_lu_solve" + int i, j; + + if( lu-> xsize + 1 != lu-> ysize ){ + im_error( FUNCTION_NAME, "not an LU decomposed matrix" ); + return -1; + } +#define N ( lu -> xsize ) +#define LU( i, j ) MATRIX( lu, (i), (j) ) +#define perm ( lu-> coeff + N * N ) + + for( i= 0; i < N; ++i ){ + int i_perm= perm[ i ]; + + if( i_perm != i ){ + double temp= vec[ i ]; + vec[ i ]= vec[ i_perm ]; + vec[ i_perm ]= temp; + } + for( j= 0; j < i; ++j ) + vec[ i ]-= LU( i, j ) * vec [ j ]; + } + + for( i= N - 1; i >= 0; --i ){ + + for( j= i + 1; j < N; ++j ) + vec[ i ]-= LU( i, j ) * vec [ j ]; + + vec[ i ]/= LU( i, i ); + } + return 0; + +#undef LU +#undef perm +#undef N +#undef FUNCTION_NAME +} + +DOUBLEMASK * +im_matinv( + const DOUBLEMASK *mat, + const char *name +){ +#define FUNCTION_NAME "im_matinv" + + DOUBLEMASK *inv; + + if( mat-> xsize != mat-> ysize ){ + im_error( FUNCTION_NAME, "non-square matrix" ); + return NULL; + } +#define N ( mat -> xsize ) + inv= im_create_dmask( name, N, N ); + if( ! inv ) + return NULL; + + if( N < 4 ){ + if( mat_inv_direct( inv, (const DOUBLEMASK *) mat, FUNCTION_NAME ) ){ + im_free_dmask( inv ); + return NULL; + } + return inv; + } + else { + DOUBLEMASK *lu= im_lu_decomp( mat, "temp" ); + + if( ! lu || mat_inv_lu( inv, (const DOUBLEMASK*) lu ) ){ + im_free_dmask( lu ); + im_free_dmask( inv ); + return NULL; + } + im_free_dmask( lu ); + + return inv; + } +#undef N +#undef FUNCTION_NAME +} + +int +im_matinv_inplace( + DOUBLEMASK *mat +){ +#define FUNCTION_NAME "im_matinv_inplace" + int to_return= 0; + + if( mat-> xsize != mat-> ysize ){ + im_error( FUNCTION_NAME, "non-square matrix" ); + return -1; + } +#define N ( mat -> xsize ) + if( N < 4 ){ + DOUBLEMASK *dup= im_dup_dmask( mat, "temp" ); + if( ! dup ) + return -1; + + to_return= mat_inv_direct( mat, (const DOUBLEMASK*) dup, FUNCTION_NAME ); + + im_free_dmask( dup ); + + return to_return; + } + { + DOUBLEMASK *lu; + + lu= im_lu_decomp( mat, "temp" ); + + if( ! lu || mat_inv_lu( mat, (const DOUBLEMASK*) lu ) ) + to_return= -1; + + im_free_dmask( lu ); + + return to_return; + } +#undef N +#undef FUNCTION_NAME +} + +/* Invert a square size x size matrix stored in matrix[][] + * result is returned in the same matrix + */ +int +im_invmat( + double **matrix, + int size + ){ + + DOUBLEMASK *mat= im_create_dmask( "temp", size, size ); + int i; + int to_return= 0; + + for( i= 0; i < size; ++i ) + memcpy( mat-> coeff + i * size, matrix[ i ], size * sizeof( double ) ); + + to_return= im_matinv_inplace( mat ); + + if( ! to_return ) + for( i= 0; i < size; ++i ) + memcpy( matrix[ i ], mat-> coeff + i * size, size * sizeof( double ) ); + + im_free_dmask( mat ); + + return to_return; +} + + +/** LOCAL FUNCTION DEFINITIONS **/ + +static int +mat_inv_lu( + DOUBLEMASK *inv, + const DOUBLEMASK *lu +){ +#define N ( lu-> xsize ) +#define INV( i, j ) MATRIX( inv, (i), (j) ) + + int i, j; + double *vec= IM_ARRAY( NULL, N, double ); + + if( ! vec ) + return -1; + + for( j= 0; j < N; ++j ){ + + for( i= 0; i < N; ++i ) + vec[ i ]= 0.0; + + vec[ j ]= 1.0; + + im_lu_solve( lu, vec ); + + for( i= 0; i < N; ++i ) + INV( i, j )= vec[ i ]; + } + im_free( vec ); + + inv-> scale= 1.0; + inv-> offset= 0.0; + + return 0; + +#undef N +#undef INV +} + +static int +mat_inv_direct( + DOUBLEMASK *inv, + const DOUBLEMASK *mat, + const char *function_name +){ +#define N ( mat -> xsize ) +#define MAT( i, j ) MATRIX( mat, (i), (j) ) +#define INV( i, j ) MATRIX( inv, (i), (j) ) + + inv-> scale= 1.0; + inv-> offset= 0.0; + + switch( N ){ + case 1: { + if( fabs( MAT( 0, 0 ) ) < TOO_SMALL ){ + im_error( function_name, "singular or near-singular matrix" ); + return -1; + } + INV( 0, 0 )= 1.0 / MAT( 0, 0 ); + return 0; + } + case 2: { + double det= MAT( 0, 0 ) * MAT( 1, 1 ) - MAT( 0, 1 ) * MAT( 1, 0 ); + + if( fabs( det ) < TOO_SMALL ){ + im_error( function_name, "singular or near-singular matrix" ); + return -1; + } + INV( 0, 0 )= MAT( 1, 1 ) / det; + INV( 0, 1 )= -MAT( 0, 1 ) / det; + INV( 1, 0 )= -MAT( 1, 0 ) / det; + INV( 1, 1 )= MAT( 0, 0 ) / det; + + return 0; + } + case 3: { + double det= MAT( 0, 0 ) * ( MAT( 1, 1 ) * MAT( 2, 2 ) - MAT( 1, 2 ) * MAT( 2, 1 ) ) + - MAT( 0, 1 ) * ( MAT( 1, 0 ) * MAT( 2, 2 ) - MAT( 1, 2 ) * MAT( 2, 0 ) ) + + MAT( 0, 2 ) * ( MAT( 1, 0 ) * MAT( 2, 1 ) - MAT( 1, 1 ) * MAT( 2, 0 ) ); + + if( fabs( det ) < TOO_SMALL ){ + im_error( function_name, "singular or near-singular matrix" ); + return -1; + } + INV( 0, 0 )= ( MAT( 1, 1 ) * MAT( 2, 2 ) - MAT( 1, 2 ) * MAT( 2, 1 ) ) / det; + INV( 0, 1 )= ( MAT( 0, 2 ) * MAT( 2, 1 ) - MAT( 0, 1 ) * MAT( 2, 2 ) ) / det; + INV( 0, 2 )= ( MAT( 0, 1 ) * MAT( 1, 2 ) - MAT( 0, 2 ) * MAT( 1, 1 ) ) / det; + + INV( 1, 0 )= ( MAT( 1, 2 ) * MAT( 2, 0 ) - MAT( 1, 0 ) * MAT( 2, 2 ) ) / det; + INV( 1, 1 )= ( MAT( 0, 0 ) * MAT( 2, 2 ) - MAT( 0, 2 ) * MAT( 2, 0 ) ) / det; + INV( 1, 2 )= ( MAT( 0, 2 ) * MAT( 1, 0 ) - MAT( 0, 0 ) * MAT( 1, 2 ) ) / det; + + INV( 2, 0 )= ( MAT( 1, 0 ) * MAT( 2, 1 ) - MAT( 1, 1 ) * MAT( 2, 0 ) ) / det; + INV( 2, 1 )= ( MAT( 0, 1 ) * MAT( 2, 0 ) - MAT( 0, 0 ) * MAT( 2, 1 ) ) / det; + INV( 2, 2 )= ( MAT( 0, 0 ) * MAT( 1, 1 ) - MAT( 0, 1 ) * MAT( 1, 0 ) ) / det; + + return 0; + } + default: + return -1; + } + +#undef N +#undef MAT +#undef INV +} + diff --git a/libsrc/matrix/im_matmul.c b/libsrc/matrix/im_matmul.c new file mode 100644 index 00000000..98c9eb59 --- /dev/null +++ b/libsrc/matrix/im_matmul.c @@ -0,0 +1,107 @@ +/* @(#) Multiplies two DOUBLEMASKs. Result matrix is made and returned. + * @(#) Pass the filename to set for the output. + * @(#) + * @(#) DOUBLEMASK * + * @(#) im_matmul( in1, in2, name ) + * @(#) DOUBLEMASK *in1, *in2; + * @(#) char *name; + * @(#) + * @(#) NULL for error. + * + * Copyright: 1990, K. Martinez and J. Cupitt + * + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* MATRIX MULTIPLY? + */ +DOUBLEMASK * +im_matmul( DOUBLEMASK *in1, DOUBLEMASK *in2, const char *name ) +{ + int xc, yc, col; + double sum; + DOUBLEMASK *mat; + double *out, *a, *b; + double *s1, *s2; + + /* Check matrix sizes. + */ + if( in1->xsize != in2->ysize ) { + im_errormsg( "im_matmul: bad sizes" ); + return( NULL ); + } + + /* Allocate output matrix. + */ + if( !(mat = im_create_dmask( name, in2->xsize, in1->ysize )) ) { + im_errormsg( "im_matmul: unable to allocate output mask" ); + return( NULL ); + } + + /* Multiply. + */ + out = mat->coeff; + s1 = in1->coeff; + + for( yc = 0; yc < in1->ysize; yc++ ) { + s2 = in2->coeff; + + for( col = 0; col < in2->xsize; col++ ) { + /* Get ready to sweep a row. + */ + sum = 0.0; + a = s1; + b = s2; + + for( sum = 0.0, xc = 0; xc < in1->xsize; xc++ ) { + sum += *a++ * *b; + b += in2->xsize; + } + + *out++ = sum; + s2++; + } + + s1 += in1->xsize; + } + + return( mat ); +} diff --git a/libsrc/matrix/im_mattrn.c b/libsrc/matrix/im_mattrn.c new file mode 100644 index 00000000..fd6549c2 --- /dev/null +++ b/libsrc/matrix/im_mattrn.c @@ -0,0 +1,87 @@ +/* @(#) Transpose a mask. Result mask is made and returned. Pass in the name + * @(#) to set for the output mask. + * @(#) + * @(#) DOUBLEMASK * + * @(#) im_mattrn( in, name ); + * @(#) DOUBLEMASK *in; + * @(#) char *name; + * @(#) + * @(#) NULL for error. + * + * Copyright: 1990, K. Martinez and J. Cupitt + * + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* MATRIX TRANSPOSE?? + */ +DOUBLEMASK * +im_mattrn( DOUBLEMASK *in, const char *name ) +{ + int xc, yc; + DOUBLEMASK *mat; + double *out, *a, *b; + + /* Allocate output matrix. + */ + if( !(mat = im_create_dmask( name, in->ysize, in->xsize )) ) { + im_errormsg( "im_mattrn: unable to allocate output matrix" ); + return( NULL ); + } + + /* Transpose. + */ + out = mat->coeff; + a = in->coeff; + + for( yc = 0; yc < mat->ysize; yc++ ) { + b = a; + + for( xc = 0; xc < mat->xsize; xc++ ) { + *out++ = *b; + b += in->xsize; + } + + a++; + } + + return( mat ); +} diff --git a/libsrc/matrix/man3/Makefile.am b/libsrc/matrix/man3/Makefile.am new file mode 100644 index 00000000..e2e1c0b2 --- /dev/null +++ b/libsrc/matrix/man3/Makefile.am @@ -0,0 +1,11 @@ +man_MANS = \ +im_lu_decomp.3 \ +im_lu_solve.3 \ +im_matcat.3 \ +im_matinv.3 \ +im_matinv_inplace.3 \ +im_matmul.3 \ +im_mattrn.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/matrix/man3/im_lu_decomp.3 b/libsrc/matrix/man3/im_lu_decomp.3 new file mode 100644 index 00000000..05a51f7b --- /dev/null +++ b/libsrc/matrix/man3/im_lu_decomp.3 @@ -0,0 +1,75 @@ +.TH IM_LU_DECOMP 3 "18 September 2006" +.SH NAME + im_lu_decomp, im_lu_solve \- Solve SLEs by LU decomposition +.SH SYNOPSIS +.nf +.B #include +.sp +.BI "DOUBLEMASK *im_lu_decomp( const DOUBLEMASK " "*mat" ", const char " "*name" " ); +.br + +.BI "int im_lu_solve( const DOUBLEMASK " "*lu" ", double " "*vec" " ); +.fi +.SH DESCRIPTION +.B im_lu_decomp(3) +allocates a DOUBLEMASK representing the LU decomposition of the matrix in +DOUBLEMASK +.IR "*mat" ", +and gives it the filename member +.IR "name" ". +.PP +.B im_lu_solve(3) +solves the system of linear equations (SLE) Ax=b, where matrix A has already +been decomposed into LU form in DOUBLEMASK +.IR "*lu" ". +Input vector b is in +.I *vec +and is overwritten with output vector x. +.PP +DOUBLEMASK +.I *lu +is unaltered by +.BR "im_matinv(3)" ", +and can be used again to solve a different SLE containing matrix A. +.SH NOTES +The scale and offset members of +.I *mat +are ignored. If they are not set to 1.0 and zero respectively, you must first +call +.BR "im_norm_dmask(3)" ". +.PP +To understand the decomposition A=LU, see Press et al. (1992). For the exact +format used to represent the matrices L and U in +.IR "*lu" ", +see the acompanying source code. +.SH ERRORS +If matrix +.I *mat +is singular (non-invertible), or close to singular then +.B im_lu_decomp(3) +will fail, calling +.BR "im_error(3)" ". +.SH RETURN VALUE +.B im_lu_decomp(3) +returns a pointer to the new DOUBLEMASK, or NULL on error. +.PP +.B im_lu_solve(3) +always returns zero, unless +.I lu +was not returned by +.BR "im_lu_decomp(3)" ", +when it returns -1. +.SH SEE ALSO +im_create_dmask(3), im_free_dmask(3), im_norm_dmask(3), im_matinv(3) +.SH REFERENCES +PRESS, W. et al, 1992. Numerical Recipies in C; The Art of Scientific +Computing, 2nd ed. Cambridge: Cambridge University Press, pp. 43-50. +.PP +[Available online: +http://www.library.cornell.edu/nr/bookcpdf.html accessed 2006-09-19] +.SH COPYRIGHT +.br +Copyright 2006, Tom Vajzovic. +.SH AUTHOR +Tom Vajzovic + diff --git a/libsrc/matrix/man3/im_lu_solve.3 b/libsrc/matrix/man3/im_lu_solve.3 new file mode 100644 index 00000000..cfe8df4e --- /dev/null +++ b/libsrc/matrix/man3/im_lu_solve.3 @@ -0,0 +1,2 @@ +.so man3/im_lu_decomp.3 + diff --git a/libsrc/matrix/man3/im_matcat.3 b/libsrc/matrix/man3/im_matcat.3 new file mode 100644 index 00000000..20d77a80 --- /dev/null +++ b/libsrc/matrix/man3/im_matcat.3 @@ -0,0 +1 @@ +.so man3/im_matinv.3 diff --git a/libsrc/matrix/man3/im_matinv.3 b/libsrc/matrix/man3/im_matinv.3 new file mode 100644 index 00000000..147ab6c4 --- /dev/null +++ b/libsrc/matrix/man3/im_matinv.3 @@ -0,0 +1,90 @@ +.TH IM_MATINV 3 "2 May 1991" +.SH NAME +im_matinv, im_matmul, im_mattrn \- matrix operations on DOUBLEMASKs + +.SH SYNOPSIS +.B #include + +.B DOUBLEMASK *im_matinv( const DOUBLEMASK *in, const char *name ); + +.B int im_matinv_inplace( DOUBLEMASK *mat ); + +.B DOUBLEMASK *im_matmul( in1, in2, name ) +.br +.B DOUBLEMASK *in1, *in2; +.br +.B char *name; + +.B DOUBLEMASK *im_matcat( in1, in2, name ) +.br +.B DOUBLEMASK *in1, *in2; +.br +.B char *name; + +.B DOUBLEMASK *im_mattrn( in, name ) +.br +.B DOUBLEMASK *in; +.br +.B char *name; + +.SH DESCRIPTION +These functions treat DOUBLEMASKs as matricies, performing some of the basics +of matrix algebra on them. + +There should be more matrix functions: those implemeneted are just sufficient +for the Gallery's calibration routines. im_matadd, im_matidentity should +really be added. + +None of these functions damage their arguments, except +.BR "im_matinv_inplace(3)" ". + +.B im_matinv(3) +inverts DOUBLEMASK +.IR "in" ", +returning a new DOUBLEMASK, called +.IR "name" ", +which contains the inverse of in. If no inverse exists, NULL is returned and +.B im_error(3) +is called with a diagnostic message. + +.B im_matinv_inplace(3) +is as +.B im_matinv(3) +except that it overwrites its input. + +.B im_matmul() +multiples the matrices held in in1 and in2, returning their product in a +matrix called name. + +.B im_matcat() +returns a new matrix formed by appending matrix in2 to the end of matrix in1. +The two matricies must be the same width. It is useful for combining several +im_measure()s into a single matrix. + +.B im_mattrn() +transposes matrix in, returning the transpose in new matrix called name. +.SH NOTES +.B DO NOT +use matrix inversion to solve systems of linear equations (SLEs). The +routines +.B im_lu_decomp(3) +and +.B im_lu_solve(3) +are more efficient, even for a single SLE. + +.SH RETURN VALUE +The functions returns a new DOUBLEMASK on sucess, and NULL on failure. +.PP +.B im_matinv_inplace(3) +returns zero on success, and -1 on failure. + +.SH SEE\ ALSO +im_create_dmask(3), im_measure(3), etc. im_lu_decomp(3), im_lu_solve(3) + +.SH COPYRIGHT +National Gallery, 1992. Tom Vajzovic, 2006 +.SH AUTHORS +J. Cupitt +.br +Tom Vajzovic + diff --git a/libsrc/matrix/man3/im_matinv_inplace.3 b/libsrc/matrix/man3/im_matinv_inplace.3 new file mode 100644 index 00000000..9f466546 --- /dev/null +++ b/libsrc/matrix/man3/im_matinv_inplace.3 @@ -0,0 +1,2 @@ +.so man3/im_matinv.3 + diff --git a/libsrc/matrix/man3/im_matmul.3 b/libsrc/matrix/man3/im_matmul.3 new file mode 100644 index 00000000..20d77a80 --- /dev/null +++ b/libsrc/matrix/man3/im_matmul.3 @@ -0,0 +1 @@ +.so man3/im_matinv.3 diff --git a/libsrc/matrix/man3/im_mattrn.3 b/libsrc/matrix/man3/im_mattrn.3 new file mode 100644 index 00000000..20d77a80 --- /dev/null +++ b/libsrc/matrix/man3/im_mattrn.3 @@ -0,0 +1 @@ +.so man3/im_matinv.3 diff --git a/libsrc/matrix/matalloc.c b/libsrc/matrix/matalloc.c new file mode 100644 index 00000000..e1e11219 --- /dev/null +++ b/libsrc/matrix/matalloc.c @@ -0,0 +1,241 @@ +/* @(#) Programs for allocating and freeing matrices + * @(#) pages 705- of numerical recipes in C 1998 + * @(#) + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define TINY 1.0e-200 + +/* @(#) Allocates and returns an pointer at the beginning of + * @(#) an integer array array[nl,nh] or + * @(#) float array array[nl,nh] or + * @(#) double array array[nl,nh] + * @(#) + * @(#) Right call + * @(#) int *im_ivector(nl, nh) + * @(#) int nl, nh; + * @(#) returns a pointer to an int array or NULL on error + * @(#) + * @(#) Right call + * @(#) float *im_fvector(nl, nh) + * @(#) int nl, nh; + * @(#) returns a pointer to a float array or NULL on error + * @(#) + * @(#) Right call + * @(#) double *im_dvector(nl, nh) + * @(#) int nl, nh; + * @(#) returns a pointer to a double array or NULL on error + * @(#) + * @(#) The following functions free the array allocated by the functions above + * @(#) + * @(#) void im_free_ivector(v, nl, nh) + * @(#) int *v; + * @(#) int nl, nh; + * @(#) + * @(#) void im_free_fvector(v, nl, nh) + * @(#) float *v; + * @(#) int nl, nh; + * @(#) + * @(#) void im_free_dvector(v, nl, nh) + * @(#) double *v; + * @(#) int nl, nh; + * @(#) + */ + +int * +im_ivector(nl, nh) +int nl, nh; +{ + int *v; + + v = (int *)im_malloc(NULL,(unsigned)(nh - nl + 1) * sizeof(int)); + if (v == NULL) + return(NULL); + else + return(v-nl); +} + +float *im_fvector(nl, nh) +int nl, nh; +{ + float *v; + + v = (float *)im_malloc(NULL,(unsigned)(nh - nl + 1) * sizeof(float)); + if (v == NULL) + return(NULL); + else + return(v-nl); +} + +double *im_dvector(nl, nh) +int nl, nh; +{ + double *v; + + v = (double *)im_malloc(NULL,(unsigned)(nh - nl + 1) * sizeof(double)); + if (v == NULL) + return(NULL); + else + return(v-nl); +} + +void im_free_ivector(v, nl, nh) +int *v; +int nl, nh; +{ + im_free((char*) (v+nl)); +} + +void im_free_fvector(v, nl, nh) +float *v; +int nl, nh; +{ + im_free((char*) (v+nl)); +} + +void im_free_dvector(v, nl, nh) +double *v; +int nl, nh; +{ + im_free((char*) (v+nl)); +} + +/* @(#) Allocates and returns an pointer at the beginning of + * @(#) an int, float or double, two dimensional matrix[nrl,nrh][ncl,nch] + * @(#) + * @(#) Right call + * @(#) int **im_imat_alloc(nrl, nrh, ncl, nch) + * @(#) int nrl, nrh, ncl, nch; + * @(#) returns a pointer to an int matrix or NULL on error + * @(#) + * @(#) float **im_fmat_alloc(nrl, nrh, ncl, nch) + * @(#) int nrl, nrh, ncl, nch; + * @(#) returns a pointer to an int matrix or NULL on error + * @(#) + * @(#) double **im_dmat_alloc(nrl, nrh, ncl, nch) + * @(#) int nrl, nrh, ncl, nch; + * @(#) returns a pointer to a double matrix or NULL on error + * @(#) + * @(#) The following routines free the matrix allocated by the functions above + * @(#) void im_free_imat(m, nrl, nrh, ncl, nch) + * @(#) int **m; + * @(#) int nrl, nrh, ncl, nch; + * @(#) + * @(#) void im_free_fmat(m, nrl, nrh, ncl, nch) + * @(#) float **m; + * @(#) int nrl, nrh, ncl, nch; + * @(#) + * @(#) void im_free_dmat(m, nrl, nrh, ncl, nch) + * @(#) double **m; + * @(#) int nrl, nrh, ncl, nch; + * @(#) + */ +int **im_imat_alloc(nrl, nrh, ncl, nch) +int nrl, nrh, ncl, nch; +{ + int i; + int **m; + + m = (int**)im_malloc(NULL,(unsigned)(nrh-nrl+1) * sizeof(int *)); + if (m == NULL) + return(NULL); + m -= nrl; + + for (i=nrl; i<=nrh; i++) + { + m[i] = (int *)im_malloc(NULL,(unsigned) (nch-ncl+1) * sizeof(int)); + if (m[i] == NULL) + return(NULL); + m[i] -= ncl; + } + return (m); +} + +void im_free_imat(m, nrl, nrh, ncl, nch) +int **m; +int nrl, nrh, ncl, nch; +{ + int i; + + for (i=nrh; i>=nrl; i--) + im_free((char*) (m[i]+ncl)); + im_free((char*) (m+nrl)); +} + +float **im_fmat_alloc(nrl, nrh, ncl, nch) +int nrl, nrh, ncl, nch; +{ + int i; + float **m; + + m = (float**)im_malloc(NULL,(unsigned)(nrh-nrl+1) * sizeof(float *)); + if (m == NULL) + return(NULL); + m -= nrl; + + for (i=nrl; i<=nrh; i++) + { + m[i] = (float *)im_malloc(NULL,(unsigned) (nch-ncl+1) * sizeof(float)); + if (m[i] == NULL) + return(NULL); + m[i] -= ncl; + } + return (m); +} + +void im_free_fmat(m, nrl, nrh, ncl, nch) +float **m; +int nrl, nrh, ncl, nch; +{ + int i; + + for (i=nrh; i>=nrl; i--) + im_free((char*) (m[i]+ncl)); + im_free((char*) (m+nrl)); +} + +double **im_dmat_alloc(nrl, nrh, ncl, nch) +int nrl, nrh, ncl, nch; +{ + int i; + double **m; + + m = (double**)im_malloc(NULL,(unsigned)(nrh-nrl+1) * sizeof(double *)); + if (m == NULL) + return(NULL); + m -= nrl; + + for (i=nrl; i<=nrh; i++) + { + m[i] = (double *)im_malloc(NULL,(unsigned) (nch-ncl+1) * sizeof(double)); + if (m[i] == NULL) + return(NULL); + m[i] -= ncl; + } + return (m); +} + +void im_free_dmat(m, nrl, nrh, ncl, nch) +double **m; +int nrl, nrh, ncl, nch; +{ + int i; + + for (i=nrh; i>=nrl; i--) + im_free((char*) (m[i]+ncl)); + im_free((char*) (m+nrl)); +} diff --git a/libsrc/matrix/matrix_dispatch.c b/libsrc/matrix/matrix_dispatch.c new file mode 100644 index 00000000..e6608fda --- /dev/null +++ b/libsrc/matrix/matrix_dispatch.c @@ -0,0 +1,181 @@ +/* VIPS function dispatch tables for matricies. + * + * J. Cupitt, 14/2/95. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* One matrix in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_DMASK( "in" ), + IM_OUTPUT_DMASK( "out" ) +}; + +/* Two matricies in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_DMASK( "in1" ), + IM_INPUT_DMASK( "in2" ), + IM_OUTPUT_DMASK( "out" ) +}; + +/* Call im_matinv via arg vector. + */ +static int +matinv_vec( im_object *argv ) +{ + im_mask_object *in = argv[0]; + im_mask_object *out = argv[1]; + + if( !(out->mask = + im_matinv( in->mask, out->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_matinv. + */ +static im_function matinv_desc = { + "im_matinv", /* Name */ + "invert matrix", + 0, /* Flags */ + matinv_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_mattrn via arg vector. + */ +static int +mattrn_vec( im_object *argv ) +{ + im_mask_object *in = argv[0]; + im_mask_object *out = argv[1]; + + if( !(out->mask = + im_mattrn( in->mask, out->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_mattrn. + */ +static im_function mattrn_desc = { + "im_mattrn", /* Name */ + "transpose matrix", + 0, /* Flags */ + mattrn_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_matcat via arg vector. + */ +static int +matcat_vec( im_object *argv ) +{ + im_mask_object *in1 = argv[0]; + im_mask_object *in2 = argv[1]; + im_mask_object *out = argv[2]; + + if( !(out->mask = + im_matcat( in1->mask, in2->mask, out->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_matcat. + */ +static im_function matcat_desc = { + "im_matcat", /* Name */ + "append matrix in2 to the end of matrix in1", + 0, /* Flags */ + matcat_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_matmul via arg vector. + */ +static int +matmul_vec( im_object *argv ) +{ + im_mask_object *in1 = argv[0]; + im_mask_object *in2 = argv[1]; + im_mask_object *out = argv[2]; + + if( !(out->mask = + im_matmul( in1->mask, in2->mask, out->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_matmul. + */ +static im_function matmul_desc = { + "im_matmul", /* Name */ + "multiply matrix in1 by matrix in2", + 0, /* Flags */ + matmul_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *matrix_list[] = { + &matcat_desc, + &matinv_desc, + &matmul_desc, + &mattrn_desc +}; + +/* Package of functions. + */ +im_package im__matrix = { + "matrix", + IM_NUMBER( matrix_list ), + matrix_list +}; diff --git a/libsrc/morphology/Makefile.am b/libsrc/morphology/Makefile.am new file mode 100644 index 00000000..1784ef6f --- /dev/null +++ b/libsrc/morphology/Makefile.am @@ -0,0 +1,12 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libmorphology.la + +libmorphology_la_SOURCES = \ + im_cntlines.c \ + im_dilate.c\ + im_erode.c\ + morph_dispatch.c \ + im_profile.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/morphology/im_cntlines.c b/libsrc/morphology/im_cntlines.c new file mode 100644 index 00000000..f02b3355 --- /dev/null +++ b/libsrc/morphology/im_cntlines.c @@ -0,0 +1,131 @@ +/* @(#) Function which calculates the number of transitions + * @(#) between black and white for the horizontal or the vertical + * @(#) direction of an image. black<128 , white>=128 + * @(#) The function calculates the number of transitions for all + * @(#) Xsize or Ysize and returns the mean of the result + * @(#) Input should be binary one channel + * @(#) + * @(#) int im_cntlines(im, nolines, flag) + * @(#) IMAGE *im; + * @(#) float *nolines; + * @(#) int flag; 0 to cnt horizontal and 1 to count vertical lines + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on : + * + * 19/9/95 JC + * - tidied up + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_cntlines( IMAGE *im, double *nolines, int flag ) +{ + int x, y; + PEL *line; + int cnt; + + /* Is im valid? + */ + if( im_incheck( im ) ) + return( -1 ); + if( im->Coding != IM_CODING_NONE || im->BandFmt != IM_BANDFMT_UCHAR || + im->Bands != 1 ) { + im_errormsg( "im_cntlines: 1-band uchar uncoded only" ); + return( -1 ); + } + if( flag != 0 && flag != 1 ) { + im_errormsg( "im_cntlines: flag should be 0 (horizontal) " + "or 1 (vertical)" ); + return( -1 ); + } + + line = (PEL *) im->data; + if( flag == 1 ) { + /* Count vertical lines. + */ + for( cnt = 0, y = 0; y < im->Ysize; y++ ) { + PEL *p = line; + + for( x = 0; x < im->Xsize - 1; x++ ) { + if( p[0] < (PEL)128 && p[1] >= (PEL)128 ) + cnt++; + else if( p[0] >= (PEL)128 && p[1] < (PEL)128 ) + cnt++; + + p++; + } + + line += im->Xsize; + } + + *nolines = (float) cnt / (2.0 * im->Ysize); + } + else { + /* Count horizontal lines. + */ + for( cnt = 0, y = 0; y < im->Ysize - 1; y++ ) { + PEL *p1 = line; + PEL *p2 = line + im->Xsize; + + for( x = 0; x < im->Xsize; x++ ) { + if( *p1 < (PEL)128 && *p2 >= (PEL)128 ) + cnt++; + else if( *p1 >= (PEL)128 && *p2 < (PEL)128 ) + cnt++; + + p1++; + p2++; + } + + line += im->Xsize; + } + + *nolines = (float)cnt / (2.0 * im->Xsize); + } + + return( 0 ); +} diff --git a/libsrc/morphology/im_dilate.c b/libsrc/morphology/im_dilate.c new file mode 100644 index 00000000..c4af9f50 --- /dev/null +++ b/libsrc/morphology/im_dilate.c @@ -0,0 +1,313 @@ +/* @(#) Function which dilates a binary VASARI format picture with a mask. + * @(#) The mask coefficients are either 255 (object) or 0 (bk) or 128 (any). + * @(#) Input image are binary images with either 0 or 255 values, one channel + * @(#) only. The program dilates a white object on a black background. + * @(#) The center of the mask is at location (m->xsize/2, m->ysize/2) + * @(#) integer division. The mask is expected to have an odd width and + * @(#) height. + * @(#) + * @(#) int im_dilate(in, out, m) + * @(#) IMAGE *in, *out; + * @(#) INTMASK *m; + * @(#) + * @(#) Returns either 0 (sucess) or -1 (fail) + * + * 19/9/95 JC + * - rewritten + * 6/7/99 JC + * - small tidies + * 7/4/04 + * - now uses im_embed() with edge stretching on the input, not + * the output + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our sequence value. + */ +typedef struct { + REGION *ir; /* Input region */ + + int *soff; /* Offsets we check for set */ + int ss; /* ... and number we check for set */ + int *coff; /* Offsets we check for clear */ + int cs; /* ... and number we check for clear */ +} SeqInfo; + +/* Stop function. + */ +static int +stop_dilate( SeqInfo *seq, IMAGE *in ) +{ + /* Free attached objects. + */ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Start function. + */ +static void * +start_dilate( IMAGE *out, IMAGE *in, INTMASK *msk ) +{ + SeqInfo *seq = IM_NEW( out, SeqInfo ); + int sz = msk->xsize * msk->ysize; + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->soff = NULL; + seq->ss = 0; + seq->coff = NULL; + seq->cs = 0; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + seq->soff = IM_ARRAY( out, sz, int ); + seq->coff = IM_ARRAY( out, sz, int ); + if( !seq->ir || !seq->soff || !seq->coff ) { + stop_dilate( seq, in ); + return( NULL ); + } + + return( (void *) seq ); +} + +/* Dilate! + */ +static int +gen_dilate( REGION *or, SeqInfo *seq, IMAGE *in, INTMASK *msk ) +{ + REGION *ir = seq->ir; + + int *soff = seq->soff; + int *coff = seq->coff; + + Rect *r = &or->valid; + Rect s; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + + int *t; + + int x, y; + int found, i; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += msk->xsize - 1; + s.height += msk->ysize - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Scan mask, building offsets we check when processing. + */ + seq->ss = 0; + seq->cs = 0; + for( t = msk->coeff, y = 0; y < msk->ysize; y++ ) + for( x = 0; x < msk->xsize; x++, t++ ) + switch( *t ) { + case 255: + soff[seq->ss++] = + IM_REGION_ADDR( ir, x + le, y + to ) - + IM_REGION_ADDR( ir, le, to ); + break; + + case 128: + break; + + case 0: + coff[seq->cs++] = + IM_REGION_ADDR( ir, x + le, y + to ) - + IM_REGION_ADDR( ir, le, to ); + break; + + default: + im_errormsg( "im_dilate: bad mask element " + "(%d should be 0, 128 or 255)", *t ); + return( -1 ); + } + + /* Dilate! + */ + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Loop along line. + */ + for( x = 0; x < sz; x++, q++, p++ ) { + /* Search for a hit on the set list. + */ + found = 0; + for( i = 0; i < seq->ss; i++ ) + if( p[soff[i]] ) { + /* Found a match! Set this output + * pixel and continue. + */ + *q = 255; + found = 1; + break; + } + + /* No set pixels ... search for a hit in the clear + * pixels. + */ + if( !found ) + for( i = 0; i < seq->cs; i++ ) + if( !p[coff[i]] ) { + /* Found a match! Set this + * output pixel and continue. + */ + *q = 255; + found = 1; + break; + } + + if( !found ) + /* All matches failed. Clear this output pixel. + */ + *q = 0; + + } + } + + return( 0 ); +} + +/* Dilate an image. + */ +int +im_dilate_raw( IMAGE *in, IMAGE *out, INTMASK *m ) +{ + INTMASK *msk; + + /* Check mask has odd number of elements in width and height. + */ + if( m->xsize < 1 || !(m->xsize & 0x1) || + m->ysize < 1 || !(m->ysize & 0x1) ) { + im_errormsg( "im_dilate: mask size not odd" ); + return( -1 ); + } + + /* Standard checks. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bbits != 8 || + in->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im_dilate: uchar uncoded only" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= m->xsize - 1; + out->Ysize -= m->ysize - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_dilate: image too small for mask" ); + return( -1 ); + } + + /* Take a copy of m. + */ + if( !(msk = im_dup_imask( m, "conv_mask" )) ) + return( -1 ); + if( im_add_close_callback( out, + (im_callback_fn) im_free_imask, msk, NULL ) ) { + im_free_imask( msk ); + return( -1 ); + } + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, start_dilate, gen_dilate, stop_dilate, in, msk ) ) + return( -1 ); + + out->Xoffset = -m->xsize / 2; + out->Yoffset = -m->ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_dilate( IMAGE *in, IMAGE *out, INTMASK *m ) +{ + IMAGE *t1 = im_open_local( out, "im_dilate:1", "p" ); + + if( !t1 || + im_embed( in, t1, 1, m->xsize / 2, m->ysize / 2, + in->Xsize + m->xsize - 1, + in->Ysize + m->ysize - 1 ) || + im_dilate_raw( t1, out, m ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/morphology/im_erode.c b/libsrc/morphology/im_erode.c new file mode 100644 index 00000000..56683c6e --- /dev/null +++ b/libsrc/morphology/im_erode.c @@ -0,0 +1,311 @@ +/* @(#) Function which erodes a binary VASARI format picture with a mask. + * @(#) The mask coefficients are either 255 (object) or 0 (bk) or 128 (any). + * @(#) Input image are binary images with either 0 or 255 values, one channel + * @(#) only. The program erodes a white object on a black background. + * @(#) The center of the mask is at location (m->xsize/2, m->ysize/2) + * @(#) integer division. The mask is expected to have an odd width and + * @(#) height. + * @(#) + * @(#) int im_erode(in, out, m) + * @(#) IMAGE *in, *out; + * @(#) INTMASK *m; + * @(#) + * @(#) Returns either 0 (sucess) or -1 (fail) + * + * 19/9/95 JC + * - rewrite + * 6/7/99 JC + * - checks and small tidies + * 7/4/04 + * - now uses im_embed() with edge stretching on the input, not + * the output + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our sequence value. + */ +typedef struct { + REGION *ir; /* Input region */ + + int *soff; /* Offsets we check for set */ + int ss; /* ... and number we check for set */ + int *coff; /* Offsets we check for clear */ + int cs; /* ... and number we check for clear */ +} SeqInfo; + +/* Stop function. + */ +static int +stop_erode( SeqInfo *seq, IMAGE *in ) +{ + /* Free attached objects. + */ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Start function. + */ +static void * +start_erode( IMAGE *out, IMAGE *in, INTMASK *msk ) +{ + SeqInfo *seq = IM_NEW( out, SeqInfo ); + int sz = msk->xsize * msk->ysize; + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->soff = NULL; + seq->ss = 0; + seq->coff = NULL; + seq->cs = 0; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + seq->soff = IM_ARRAY( out, sz, int ); + seq->coff = IM_ARRAY( out, sz, int ); + if( !seq->ir || !seq->soff || !seq->coff ) { + stop_erode( seq, in ); + return( NULL ); + } + + return( (void *) seq ); +} + +/* Erode! + */ +static int +gen_erode( REGION *or, SeqInfo *seq, IMAGE *in, INTMASK *msk ) +{ + REGION *ir = seq->ir; + + int *soff = seq->soff; + int *coff = seq->coff; + + Rect *r = &or->valid; + Rect s; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + + int *t; + + int x, y; + int found, i; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += msk->xsize - 1; + s.height += msk->ysize - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Scan mask, building offsets we check when processing. + */ + seq->ss = 0; + seq->cs = 0; + for( t = msk->coeff, y = 0; y < msk->ysize; y++ ) + for( x = 0; x < msk->xsize; x++, t++ ) + switch( *t ) { + case 255: + soff[seq->ss++] = + IM_REGION_ADDR( ir, x + le, y + to ) - + IM_REGION_ADDR( ir, le, to ); + break; + + case 128: + break; + + case 0: + coff[seq->cs++] = + IM_REGION_ADDR( ir, x + le, y + to ) - + IM_REGION_ADDR( ir, le, to ); + break; + + default: + im_errormsg( "im_erode: bad mask element " + "(%d should be 0, 128 or 255)", *t ); + return( -1 ); + } + + /* Erode! + */ + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Loop along line. + */ + for( x = 0; x < sz; x++, q++, p++ ) { + /* Check all set pixels are set. + */ + found = 0; + for( i = 0; i < seq->ss; i++ ) + if( !p[soff[i]] ) { + /* Found a mismatch! Clear this output + * pixel and continue. + */ + *q = 0; + found = 1; + break; + } + + /* Check all clear pixels are clear. + */ + if( !found ) + for( i = 0; i < seq->cs; i++ ) + if( p[coff[i]] ) { + /* Found a mismatch! Clear this + * output pixel and continue. + */ + *q = 0; + found = 1; + break; + } + + if( !found ) + /* No mismatches found - set output pixel. + */ + *q = 255; + } + } + + return( 0 ); +} + +/* Erode an image. + */ +int +im_erode_raw( IMAGE *in, IMAGE *out, INTMASK *m ) +{ + INTMASK *msk; + + /* Check mask has odd number of elements in width and height. + */ + if( m->xsize < 1 || !(m->xsize & 0x1) || + m->ysize < 1 || !(m->ysize & 0x1) ) { + im_errormsg( "im_erode: mask size not odd" ); + return( -1 ); + } + + /* Standard checks. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bbits != 8 || + in->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im_erode: 1-band uchar uncoded only" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= m->xsize - 1; + out->Ysize -= m->ysize - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_erode: image too small for mask" ); + return( -1 ); + } + + /* Take a copy of m. + */ + if( !(msk = im_dup_imask( m, "conv_mask" )) ) + return( -1 ); + if( im_add_close_callback( out, + (im_callback_fn) im_free_imask, msk, NULL ) ) { + im_free_imask( msk ); + return( -1 ); + } + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, start_erode, gen_erode, stop_erode, in, msk ) ) + return( -1 ); + + out->Xoffset = -m->xsize / 2; + out->Yoffset = -m->ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_erode( IMAGE *in, IMAGE *out, INTMASK *m ) +{ + IMAGE *t1 = im_open_local( out, "im_erode:1", "p" ); + + if( !t1 || + im_embed( in, t1, 1, m->xsize / 2, m->ysize / 2, + in->Xsize + m->xsize - 1, + in->Ysize + m->ysize - 1 ) || + im_erode_raw( t1, out, m ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/morphology/im_profile.c b/libsrc/morphology/im_profile.c new file mode 100644 index 00000000..6d7dd700 --- /dev/null +++ b/libsrc/morphology/im_profile.c @@ -0,0 +1,132 @@ +/* @(#) dir = 0: + * @(#) For each vertical line, find the position of the first + * @(#) non-zero pixel from the top. Output is USHORT with + * @(#) width = input width, height = 1. + * @(#) + * @(#) dir = 1: + * @(#) For each horizontal line, find the position of the first + * @(#) non-zero pixel from the left. Output is USHORT with + * @(#) width = 1, height = input height + * @(#) + * @(#) int im_profile( IMAGE *in, IMAGE *out, int dir ) + * @(#) + * @(#) Returns 0 on success and non-zero on error + * + * 11/8/99 JC + * - from im_cntlines() + * 22/4/04 + * - now outputs horizontal/vertical image + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_profile( IMAGE *in, IMAGE *out, int dir ) +{ + int x, y; + unsigned short *buf; + + /* Check im. + */ + if( im_iocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->BandFmt != IM_BANDFMT_UCHAR || + in->Bands != 1 ) { + im_errormsg( "im_profile: 1-band uchar uncoded only" ); + return( -1 ); + } + if( dir != 0 && dir != 1 ) { + im_errormsg( "im_profile: dir not 0 or 1" ); + return( -1 ); + } + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_HISTOGRAM; + if( dir == 0 ) { + out->Xsize = in->Xsize; + out->Ysize = 1; + } + else { + out->Xsize = 1; + out->Ysize = in->Ysize; + } + out->BandFmt = IM_BANDFMT_USHORT; + out->Bbits = IM_BBITS_SHORT; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = IM_ARRAY( out, out->Xsize, unsigned short )) ) + return( -1 ); + + if( dir == 0 ) { + /* Find vertical lines. + */ + for( x = 0; x < in->Xsize; x++ ) { + PEL *p = (PEL *) IM_IMAGE_ADDR( in, x, 0 ); + int lsk = IM_IMAGE_SIZEOF_LINE( in ); + + for( y = 0; y < in->Ysize; y++ ) + if( p[y * lsk] ) + break; + + buf[x] = y; + } + + if( im_writeline( 0, out, (PEL *) buf ) ) + return( -1 ); + } + else { + /* Count horizontal lines. + */ + for( y = 0; y < in->Ysize; y++ ) { + PEL *p = (PEL *) IM_IMAGE_ADDR( in, 0, y ); + + for( x = 0; x < in->Xsize; x++ ) + if( p[x] ) + break; + + buf[0] = x; + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + } + + + return( 0 ); +} diff --git a/libsrc/morphology/man3/Makefile.am b/libsrc/morphology/man3/Makefile.am new file mode 100644 index 00000000..89e85ccd --- /dev/null +++ b/libsrc/morphology/man3/Makefile.am @@ -0,0 +1,9 @@ +man_MANS = \ + im_cntlines.3 \ + im_dilate.3 \ + im_dilate_raw.3 \ + im_erode.3 \ + im_erode_raw.3 \ + im_profile.3 + +EXTRA_DIST = ${man_MANS} diff --git a/libsrc/morphology/man3/im_cntlines.3 b/libsrc/morphology/man3/im_cntlines.3 new file mode 100644 index 00000000..6fe446b7 --- /dev/null +++ b/libsrc/morphology/man3/im_cntlines.3 @@ -0,0 +1,57 @@ +.TH LINES 3 "14 May 1991" +.SH NAME +im_cntlines, im_profile \- calculate transitions between black and white pels horizontally or vertically +.SH SYNOPSIS +.B #include + +.B int im_profile(in, out, dir) +.br +.B IMAGE *in, *out; +.br +.B int dir; + +.B int im_cntlines(in, nolines, dir) +.br +.B IMAGE *in; +.br +.B double *nolines; +.br +.B int dir; + +.SH DESCRIPTION + +.B im_profile(3) +searches inward from the edge of the image and finds the first non-zero pixel. +It outputs an image containing a list of the offsets for each row or column. + +If +.B dir +==0, then +.B im_profile(3) +searches down from the top edge, writing an image as wide as the input +image, but only 1 pixel high, containing the number of pixels down to the +first non-zero pixel for each column of input pixels. + +If +.B dir +==1, then +.B im_profile(3) +searches across from the left edge, writing an image as high as the input +image, but only 1 pixel wide, containing the number of pixels across to the +first non-zero pixel for each row of input pixels. + +.B im_cntlines(3) +calculates the number of transitions between black and white pixels of +an image. If dir is 1 then all transitions across the +vertical direction are calculated for all Xsize lines of the image. If dir +is 0 then all transitions along the horizontal direction for all Ysize +lines are calculated. The function returns the number of transitions +divided by twice the number of the corresponding Xsize of Ysize lines. +The program is primarily used to calculate the number of unbroken horizontal +(dir=0) or vertical lines (dir=1) that exist within an image. +Input image in can have only one channel. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_erode(3), im_dilate(3). diff --git a/libsrc/morphology/man3/im_dilate.3 b/libsrc/morphology/man3/im_dilate.3 new file mode 100644 index 00000000..8a97d741 --- /dev/null +++ b/libsrc/morphology/man3/im_dilate.3 @@ -0,0 +1,72 @@ +.TH IM_DILATE 3 "14 May 1991" +.SH NAME +im_dilate, im_dilate_raw, im_erode, im_erode_raw \- perform morphological operations on a white object against a black background +.SH SYNOPSIS +.B #include + +.B int im_dilate(in, out, m) +.br +.B IMAGE *in, *out; +.br +.B INTMASK *m; + +.B int im_erode(in, out, m) +.br +.B IMAGE *in, *out; +.br +.B INTMASK *m; + +.B int im_dilate_raw(in, out, m) +.br +.B IMAGE *in, *out; +.br +.B INTMASK *m; + +.B int im_erode_raw(in, out, m) +.br +.B IMAGE *in, *out; +.br +.B INTMASK *m; + +.SH DESCRIPTION +The above functions are applications of morphological operations on one +channel binary images ie. images with pixels that are either 0 (black) or 255 +(white). All functions assume that input images contain white objects against +a black background. + +Mask coefficients can be either 0 (for object) or 255 (for background) or 128 +(for do not care). + +The mask should have odd length sides and the origin of the mask is at location +(m-\>xsize/2,m-\>ysize/2) integer division. All algorithms have been based on +the book "Fundamentals of Digital Image Processing" by A. Jain, pp 384-388, +Prentice-Hall, 1989. Essentially, im_dilate(3) sets pixels in the output if +*any* part of the mask matches, whereas im_erode(3) sets pixels only if *all* +of the mask matches. + +im_dilate(3) +dilates the image pointed by in, according to the mask pointed by m and writes +the result in the location pointed by the IMAGE descriptor out. The output +image is the same size as the input, with a black border in the manner of +im_conv(3). + +im_dilate_raw(3) +works as im_dilate(3), but does not add a black border. + +im_erode(3) +erodes the image pointed by in, according to the mask pointed by m and writes +the result in the location pointed by the IMAGE descriptor out. Again, the +output image is forced to have the same size as the input. + +im_erode_raw(3) +works as im_erode(3), but does not add a black border. + +See the boolean operations im_and(3), im_or(3) and im_eor(3) for analogues +of the usual set difference and set union operations. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_read_imask(3), im_conv(3), im_and(3), im_rotate_imask(3). +.SH COPYRIGHT +1991-1995, Birkbeck College and the National Gallery diff --git a/libsrc/morphology/man3/im_dilate_raw.3 b/libsrc/morphology/man3/im_dilate_raw.3 new file mode 100644 index 00000000..1fdf30e4 --- /dev/null +++ b/libsrc/morphology/man3/im_dilate_raw.3 @@ -0,0 +1 @@ +.so man3/im_dilate.3 diff --git a/libsrc/morphology/man3/im_erode.3 b/libsrc/morphology/man3/im_erode.3 new file mode 100644 index 00000000..1fdf30e4 --- /dev/null +++ b/libsrc/morphology/man3/im_erode.3 @@ -0,0 +1 @@ +.so man3/im_dilate.3 diff --git a/libsrc/morphology/man3/im_erode_raw.3 b/libsrc/morphology/man3/im_erode_raw.3 new file mode 100644 index 00000000..1fdf30e4 --- /dev/null +++ b/libsrc/morphology/man3/im_erode_raw.3 @@ -0,0 +1 @@ +.so man3/im_dilate.3 diff --git a/libsrc/morphology/man3/im_profile.3 b/libsrc/morphology/man3/im_profile.3 new file mode 100644 index 00000000..a181029c --- /dev/null +++ b/libsrc/morphology/man3/im_profile.3 @@ -0,0 +1 @@ +.so man3/im_cntlines.3 diff --git a/libsrc/morphology/morph_dispatch.c b/libsrc/morphology/morph_dispatch.c new file mode 100644 index 00000000..86d1e5b4 --- /dev/null +++ b/libsrc/morphology/morph_dispatch.c @@ -0,0 +1,213 @@ +/* VIPS function dispatch tables for morphology. + * + * J. Cupitt, 19/9/95 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Args to im_profile. + */ +static im_arg_desc profile_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "direction" ) +}; + +/* Call im_profile via arg vector. + */ +static int +profile_vec( im_object *argv ) +{ + int dir = *((int *) argv[2]); + + return( im_profile( argv[0], argv[1], dir ) ); +} + +/* Description of im_profile. + */ +static im_function profile_desc = { + "im_profile", /* Name */ + "find first horizontal/vertical edge", /* Descr. */ + IM_FN_TRANSFORM, /* Flags */ + profile_vec, /* Dispatch function */ + IM_NUMBER( profile_args ), /* Size of arg list */ + profile_args /* Arg list */ +}; + +/* Args to im_erode. + */ +static im_arg_desc erode_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_IMASK( "mask" ) +}; + +/* Call im_dilate via arg vector. + */ +static int +dilate_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_dilate( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_dilate. + */ +static im_function dilate_desc = { + "im_dilate", /* Name */ + "dilate image with mask, adding a black border", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + dilate_vec, /* Dispatch function */ + IM_NUMBER( erode_args ), /* Size of arg list */ + erode_args /* Arg list */ +}; + +/* Call im_dilate_raw via arg vector. + */ +static int +dilate_raw_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_dilate_raw( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_dilate_raw. + */ +static im_function dilate_raw_desc = { + "im_dilate_raw", /* Name */ + "dilate image with mask", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + dilate_raw_vec, /* Dispatch function */ + IM_NUMBER( erode_args ), /* Size of arg list */ + erode_args /* Arg list */ +}; + +/* Call im_erode via arg vector. + */ +static int +erode_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_erode( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_erode. + */ +static im_function erode_desc = { + "im_erode", /* Name */ + "erode image with mask, adding a black border", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + erode_vec, /* Dispatch function */ + IM_NUMBER( erode_args ), /* Size of arg list */ + erode_args /* Arg list */ +}; + +/* Call im_erode_raw via arg vector. + */ +static int +erode_raw_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_erode_raw( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_erode_raw. + */ +static im_function erode_raw_desc = { + "im_erode_raw", /* Name */ + "erode image with mask", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + erode_raw_vec, /* Dispatch function */ + IM_NUMBER( erode_args ), /* Size of arg list */ + erode_args /* Arg list */ +}; + +/* Args to im_cntlines. + */ +static im_arg_desc cntlines_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_DOUBLE( "nlines" ), + IM_INPUT_INT( "direction" ) +}; + +/* Call im_cntlines via arg vector. + */ +static int +cntlines_vec( im_object *argv ) +{ + double *out = (double *) argv[1]; + int dir = *((int *) argv[2]); + + return( im_cntlines( argv[0], out, dir ) ); +} + +/* Description of im_cntlines. + */ +static im_function cntlines_desc = { + "im_cntlines", /* Name */ + "count horizontal or vertical lines", + 0, /* Flags */ + cntlines_vec, /* Dispatch function */ + IM_NUMBER( cntlines_args ), /* Size of arg list */ + cntlines_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *morph_list[] = { + &cntlines_desc, + &dilate_desc, + &dilate_raw_desc, + &erode_desc, + &erode_raw_desc, + &profile_desc +}; + +/* Package of functions. + */ +im_package im__morphology = { + "morphology", + IM_NUMBER( morph_list ), + morph_list +}; diff --git a/libsrc/mosaicing/Makefile.am b/libsrc/mosaicing/Makefile.am new file mode 100644 index 00000000..c39d0980 --- /dev/null +++ b/libsrc/mosaicing/Makefile.am @@ -0,0 +1,28 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libmosaicing.la + +libmosaicing_la_SOURCES = \ + im_affine.c \ + match.c \ + mosaic1.c \ + mosaicing_dispatch.c \ + similarity.c \ + global_balance.c \ + im_avgdxdy.c \ + im_chkpair.c \ + im_clinear.c \ + im_improve.c \ + im_initialize.c \ + im_lrcalcon.c \ + im_lrmerge.c \ + im_lrmosaic.c \ + im_tbcalcon.c \ + im_tbmerge.c \ + im_remosaic.c \ + im_tbmosaic.c \ + merge.h \ + global_balance.h \ + mosaic.h + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/mosaicing/global_balance.c b/libsrc/mosaicing/global_balance.c new file mode 100644 index 00000000..ba7db608 --- /dev/null +++ b/libsrc/mosaicing/global_balance.c @@ -0,0 +1,1747 @@ +/* Parse ".desc" files from mosaiced images to generate (x,y) offsets for + * every sub-image. Find all overlap stats and solve balancing with LMS. + * Regenerate mosaic, with balancing fixed. + * + * 1/12/93 JC + * - first version, unfinished! + * 6/9/95 JC + * - LMS fixed, now works, more or less + * 12/9/95 JC + * - now does positions correctly too + * - ignores trivial overlaps + * 19/9/95 JC + * - prints correct number of balance factors! + * 10/11/95 JC + * - now tracks im_copy() calls too, so you can save sub-images + * 12/1/96 JC + * - slightly clearer diagnostics + * - better centre of factors around 1.0 with log() average + * 1/3/96 JC + * - new im_global_balance_float variant lets our caller adjust factor + * range if output has burn-out + * - im_global_balance_search uses the above to produce scaled output ... + * very slow! + * 11/3/96 JC + * - now tries current directory too for input files + * 22/3/96 JC + * - horrible bug in position finding! now fixed + * 1/8/97 JC + * - revised for new mosaic functions and non-square images + * 12/9/97 JC + * - code for im_lrmosaic1() support + * - output type == input type, so works for short images too + * 6/1/99 JC + * - new gamma parameter, do scale in linear space + * - removed _search version, as can now be done with ip + * - renamed _float to f suffix, in line with im_conv()/im_convf() + * 15/2/00 JC + * - balancef() did not scale in linear space + * 2/2/01 JC + * - added tunable max blend width + * 7/11/01 JC + * - global_balance.h broken out for im_remosaic() + * 25/02/02 JC + * - better transform function scheme + * 21/3/01 JC + * - quicker bailout on error + * 8/11/02 JC + * - add <> around file names so you can have spaces :( + * 9/12/02 JC + * - track original params and always reuse them ... makes us proof + * against geo reconstruct errors + * 10/3/03 JC + * - weed out overlaps which contain only transparent pixels + * 4/1/07 + * - switch to new history thing, switch im_errormsg() too + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Strategy: build a tree describing the file + * relationships in the desc file, then walk that passing constraints + * back up to the root. Look up file names in symbol_table. + */ + +/* Define for debug output. +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "merge.h" +#include "global_balance.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define MAX_ITEMS (50) + +/* How pix an overlap has to be (in pixels) before we think it's trivial and + * we ignore it. + */ +#define TRIVIAL (20 * 20) + +/* Break a string into a list of strings. Write '\0's into the string. out + * needs to be MAX_FILES long. -1 for error, otherwise number of args found. + + " " + + out[0] = "fred" + out[1] = "jim poop" + out[2] = "sn aff le" + + */ +static int +break_items( char *line, char **out ) +{ + int i; + char *p; + + for( i = 0; i < MAX_ITEMS; i++ ) { + /* Skip to first '<'. + */ + if( !(p = strchr( line, '<' )) ) + break; + + out[i] = line = p + 1; + + if( !(p = strchr( line, '>' )) ) { + im_error( "break_files", _( "no matching '>'" ) ); + return( -1 ); + } + + *p = '\0'; + line = p + 1; + } + + if( i == MAX_ITEMS ) { + im_error( "break_files", _( "too many items" ) ); + return( -1 ); + } + + return( i ); +} + +/* Try to open a file. If full path fails, try the current directory. + */ +IMAGE * +im__global_open_image( SymbolTable *st, char *name ) +{ + IMAGE *im; + + if( (im = im_open_local( st->im, name, "r" )) ) + return( im ); + if( (im = im_open_local( st->im, im_skip_dir( name ), "r" )) ) + return( im ); + + return( NULL ); +} + +static int +junk_node( JoinNode *node ) +{ + IM_FREEF( g_slist_free, node->overlaps ); + + return( 0 ); +} + +/* Hash from a filename to an index into symbol_table. + */ +static int +hash( char *n ) +{ + int i; + int r = 0; + int l = strlen( n ); + + for( i = 0; i < l; i++ ) + r = ((r + n[i]) * 43) & 0xffffff; + + return( r % SYM_TAB_SIZE ); +} + +/* Make a leaf for a file. + */ +static JoinNode * +build_node( SymbolTable *st, char *name ) +{ + JoinNode *node = IM_NEW( st->im, JoinNode ); + int n = hash( name ); + + /* Fill fields. + */ + if( !node || !(node->name = im_strdup( st->im, name )) ) + return( NULL ); + + node->type = JOIN_LEAF; + node->dirty = 0; + node->mwidth = -2; + node->st = st; + im__transform_init( &node->cumtrn ); + node->trnim = NULL; + node->arg1 = NULL; + node->arg2 = NULL; + node->overlaps = NULL; + node->im = NULL; + node->index = 0; + + if( im_add_close_callback( st->im, + (im_callback_fn) junk_node, node, NULL ) ) + return( NULL ); + + /* Try to open. + */ + if( (node->im = im__global_open_image( st, name )) ) { + /* There is a file there - set width and height. + */ + node->cumtrn.oarea.width = node->im->Xsize; + node->cumtrn.oarea.height = node->im->Ysize; + } + else { + /* Clear the error buffer to lessen confusion. + */ + im_clear_error_string(); + } + + st->table[n] = g_slist_prepend( st->table[n], node ); + + return( node ); +} + +/* Make a new overlap struct. + */ +static OverlapInfo * +build_overlap( JoinNode *node, JoinNode *other, Rect *overlap ) +{ + OverlapInfo *lap = IM_NEW( node->st->im, OverlapInfo ); + + if( !lap ) + return( NULL ); + + lap->node = node; + lap->other = other; + lap->overlap = *overlap; + lap->nstats = NULL; + lap->ostats = NULL; + node->overlaps = g_slist_prepend( node->overlaps, lap ); + node->st->novl++; + + return( lap ); +} + +static void +overlap_destroy( OverlapInfo *lap ) +{ + JoinNode *node = lap->node; + + node->overlaps = g_slist_remove( node->overlaps, lap ); + assert( node->st->novl > 0 ); + node->st->novl--; +} + +static int +junk_table( SymbolTable *st ) +{ + int i; + + for( i = 0; i < st->sz; i++ ) + IM_FREEF( g_slist_free, st->table[i] ); + + return( 0 ); +} + +/* Build a new symbol table. + */ +SymbolTable * +im__build_symtab( IMAGE *out, int sz ) +{ + SymbolTable *st = IM_NEW( out, SymbolTable ); + int i; + + if( !st ) + return( NULL ); + if( !(st->table = IM_ARRAY( out, sz, GSList * )) ) + return( NULL ); + st->sz = sz; + st->im = out; + st->novl = 0; + st->nim = 0; + st->njoin = 0; + st->root = NULL; + st->leaf = NULL; + st->fac = NULL; + + if( im_add_close_callback( out, + (im_callback_fn) junk_table, st, NULL ) ) + return( NULL ); + + for( i = 0; i < sz; i++ ) + st->table[i] = NULL; + + return( st ); +} + +/* Does this node have this file name? + */ +static JoinNode * +test_name( JoinNode *node, char *name ) +{ + if( strcmp( node->name, name ) == 0 ) + return( node ); + else + return( NULL ); +} + +/* Look up a filename in the symbol_table. + */ +static JoinNode * +find_node( SymbolTable *st, char *name ) +{ + return( im_slist_map2( st->table[hash( name )], + (VSListMap2Fn) test_name, name, NULL ) ); +} + +/* Given a name: return either the existing node for that name, or a new node + * we have made. + */ +static JoinNode * +add_node( SymbolTable *st, char *name ) +{ + JoinNode *node; + + if( !(node = find_node( st, name )) && + !(node = build_node( st, name )) ) + return( NULL ); + + return( node ); +} + +/* Map a user function over the whole of the symbol table. + */ +void * +im__map_table( SymbolTable *st, void *(*fn)(), void *a, void *b ) +{ + int i; + void *r; + + for( i = 0; i < st->sz; i++ ) + if( (r = im_slist_map2( st->table[i], + (VSListMap2Fn) fn, a, b )) ) + return( r ); + + return( NULL ); +} + +/* Set the dirty field on a join. + */ +static void * +set_dirty( JoinNode *node, int state ) +{ + node->dirty = state; + + return( NULL ); +} + +/* Clean the whole table. + */ +static void +clean_table( SymbolTable *st ) +{ + (void) im__map_table( st, set_dirty, (void *) 0, NULL ); +} + +/* Do geometry calculations on a node, assuming geo is up to date for any + * children. + */ +static void +calc_geometry( JoinNode *node ) +{ + Rect um; + + switch( node->type ) { + case JOIN_LR: + case JOIN_TB: + case JOIN_LRROTSCALE: + case JOIN_TBROTSCALE: + /* Join two areas. + */ + im_rect_unionrect( &node->arg1->cumtrn.oarea, + &node->arg2->cumtrn.oarea, &um ); + node->cumtrn.iarea.left = 0; + node->cumtrn.iarea.top = 0; + node->cumtrn.iarea.width = um.width; + node->cumtrn.iarea.height = um.height; + im__transform_set_area( &node->cumtrn ); + break; + + case JOIN_CP: + /* Copy from child. + */ + node->cumtrn = node->arg1->cumtrn; + break; + + case JOIN_LEAF: + /* Just use leaf dimensions, if there are any. + */ + if( node->im ) { + node->cumtrn.iarea.left = 0; + node->cumtrn.iarea.top = 0; + node->cumtrn.iarea.width = node->im->Xsize; + node->cumtrn.iarea.height = node->im->Ysize; + im__transform_set_area( &node->cumtrn ); + } + break; + + default: + error_exit( "internal error #98356" ); + /*NOTREACHED*/ + } +} + +/* Propogate a transform down a tree. If dirty is set, we've been here before, + * so there is a doubling up of this node. If this is a leaf, then we have the + * same leaf twice (which, in fact, we can cope with); if this is a node, we + * have circularity. + */ +static int +propogate_transform( JoinNode *node, Transformation *trn ) +{ + if( !node ) + return( 0 ); + + if( node->dirty && node->arg1 && node->arg2 ) { + im_error( "im_global_balance", _( "circularity detected" ) ); + return( -1 ); + } + node->dirty = 1; + + /* Transform our children. + */ + if( propogate_transform( node->arg1, trn ) || + propogate_transform( node->arg2, trn ) ) + return( -1 ); + + /* Transform us, and recalculate our position and size. + */ + im__transform_add( &node->cumtrn, trn, &node->cumtrn ); + calc_geometry( node ); + + return( 0 ); +} + +/* Ah ha! A leaf is actually made up of two smaller files with an lr or a tb + * merge. Turn a leaf node into a join node. Propogate the transform down + * arg2's side of the tree. + */ +static int +make_join( SymbolTable *st, JoinType type, + JoinNode *arg1, JoinNode *arg2, JoinNode *out, + double a, double b, double dx, double dy, int mwidth ) +{ + Transformation trn; + + /* Check output is ok. + */ + if( out->type != JOIN_LEAF ) { + im_error( "im_global_balance", + _( "image \"%s\" used twice as output" ), out->name ); + return( -1 ); + } + + /* Fill fields. + */ + out->type = type; + out->mwidth = mwidth; + out->a = a; + out->b = b; + out->dx = dx; + out->dy = dy; + out->arg1 = arg1; + out->arg2 = arg2; + out->thistrn.a = a; + out->thistrn.b = -b; + out->thistrn.c = b; + out->thistrn.d = a; + out->thistrn.dx = dx; + out->thistrn.dy = dy; + + /* Clean the table and propogate the transform down the RHS of the + * graph. + */ + clean_table( st ); + if( propogate_transform( arg2, &out->thistrn ) ) + return( -1 ); + + /* Find the position and size of our output. + */ + calc_geometry( out ); + + /* Now normalise the result, so that out is at (0,0) again. + */ + trn.a = 1.0; + trn.b = 0.0; + trn.c = 0.0; + trn.d = 1.0; + trn.dx = -out->cumtrn.oarea.left; + trn.dy = -out->cumtrn.oarea.top; + clean_table( st ); + if( propogate_transform( out, &trn ) ) + return( -1 ); + + return( 0 ); +} + +/* Make a copy node. + */ +static int +make_copy( SymbolTable *st, JoinNode *before, JoinNode *after ) +{ + /* Check output is ok. + */ + if( after->type != JOIN_LEAF ) { + im_error( "im_global_balance", + _( "image \"%s\" used twice as output" ), after->name ); + return( -1 ); + } + + /* Fill fields. + */ + after->type = JOIN_CP; + after->arg1 = before; + after->arg2 = NULL; + + /* Copy over the position and size from the before to the after. + */ + calc_geometry( after ); + + return( 0 ); +} + +/* Process a single .desc line. + */ +static int +process_line( SymbolTable *st, const char *text ) +{ + char line[1024]; + +#ifdef DEBUG + printf( "read: %s\n", text ); +#endif /*DEBUG*/ + + /* We destroy line during the parse. + */ + im_strncpy( line, text, 1024 ); + + if( im_isprefix( "#LRJOIN ", line ) || + im_isprefix( "#TBJOIN ", line ) ) { + /* Yes: magic join command. Break into tokens. Format is eg. + + #LRJOIN [] + + */ + char *item[MAX_ITEMS]; + int nitems; + JoinType type; + JoinNode *arg1, *arg2, *join; + int dx, dy, mwidth; + + if( (nitems = break_items( line, item )) < 0 ) + return( -1 ); + if( nitems != 5 && nitems != 6 ) { + im_error( "global_balance", + _( "bad number of args in join line" ) ); + return( -1 ); + } + + if( !(arg1 = add_node( st, item[0] )) || + !(arg2 = add_node( st, item[1] )) || + !(join = add_node( st, item[2] )) ) + return( -1 ); + dx = atoi( item[3] ); + dy = atoi( item[4] ); + if( nitems == 6 ) + mwidth = atoi( item[5] ); + else + mwidth = -1; + if( im_isprefix( "#LRJOIN ", line ) ) + type = JOIN_LR; + else + type = JOIN_TB; + + if( make_join( st, type, arg1, arg2, + join, 1.0, 0.0, dx, dy, mwidth ) ) + return( -1 ); + } + else if( im_isprefix( "#LRROTSCALE ", line ) || + im_isprefix( "#TBROTSCALE ", line ) ) { + /* Rot + scale. Format is eg. + + #LRROTSCALE \ + [] + + */ + char *item[MAX_ITEMS]; + int nitems; + JoinType type; + JoinNode *arg1, *arg2, *join; + double a, b, dx, dy; + int mwidth; + + if( (nitems = break_items( line, item )) < 0 ) + return( -1 ); + if( nitems != 7 && nitems != 8 ) { + im_error( "global_balance", + _( "bad number of args in join1 line" ) ); + return( -1 ); + } + + if( !(arg1 = add_node( st, item[0] )) || + !(arg2 = add_node( st, item[1] )) || + !(join = add_node( st, item[2] )) ) + return( -1 ); + a = g_ascii_strtod( item[3], NULL ); + b = g_ascii_strtod( item[4], NULL ); + dx = g_ascii_strtod( item[5], NULL ); + dy = g_ascii_strtod( item[6], NULL ); + if( nitems == 8 ) + mwidth = atoi( item[7] ); + else + mwidth = -1; + if( im_isprefix( "#LRROTSCALE ", line ) ) + type = JOIN_LRROTSCALE; + else + type = JOIN_TBROTSCALE; + + if( make_join( st, type, arg1, arg2, + join, a, b, dx, dy, mwidth ) ) + return( -1 ); + } + else if( im_isprefix( "copy ", line ) ) { + /* im_copy() call ... make a JOIN_CP node. + */ + char *item[MAX_ITEMS]; + int nitems; + JoinNode *before, *after; + + if( (nitems = break_items( line, item )) < 0 ) + return( -1 ); + if( nitems != 2 ) { + im_error( "global_balance", + _( "bad number of args in copy line" ) ); + return( -1 ); + } + + if( !(before = add_node( st, item[0] )) || + !(after = add_node( st, item[1] )) || + make_copy( st, before, after ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Set the dirty flag on any nodes we reference. + */ +static void * +set_referenced( JoinNode *node ) +{ + if( node->arg1 ) + node->arg1->dirty = 1; + if( node->arg2 ) + node->arg2->dirty = 1; + + return( NULL ); +} + +/* Is this a root node? Should be clean. + */ +static void * +is_root( JoinNode *node ) +{ + if( !node->dirty ) + return( (void *) node ); + else + return( NULL ); +} + +/* Scan the symbol table, looking for a node which no node references. + */ +static JoinNode * +find_root( SymbolTable *st ) +{ + JoinNode *root; + JoinNode *notroot; + + /* Clean the table, then scan it, setting all pointed-to nodes dirty. + */ + clean_table( st ); + im__map_table( st, set_referenced, NULL, NULL ); + + /* Look for the first clean symbol. + */ + root = (JoinNode *) im__map_table( st, is_root, NULL, NULL ); + + /* No root? Hot dang! + */ + if( !root ) { + im_error( "im_global_balance", + _( "mosaic root not found in desc file\n" + "is this really a mosaiced image?" ) ); + return( NULL ); + } + + /* Now dirty that - then if there are any more clean symbols, we have + * more than one root. + */ + root->dirty = 1; + if( (notroot = im__map_table( st, is_root, NULL, NULL )) ) { + im_error( "im_global_balance", _( "more than one root" ) ); + return( NULL ); + } + + return( root ); +} + +/* Walk history_list and parse each line. + */ +int +im__parse_desc( SymbolTable *st, IMAGE *in ) +{ + GSList *p; + + for( p = in->history_list; p; p = p->next ) { + GValue *value = (GValue *) p->data; + + assert( G_VALUE_TYPE( value ) == IM_TYPE_REF_STRING ); + + if( process_line( st, im_ref_string_get( value ) ) ) + return( -1 ); + } + + /* Find root. + */ + if( !(st->root = find_root( st )) ) + return( -1 ); + + return( 0 ); +} + +/* Count and index all leaf images. + */ +static void * +count_leaves( JoinNode *node ) +{ + if( node->type == JOIN_LEAF ) { + node->index = node->st->nim; + node->st->nim++; + } + + return( NULL ); +} + +#ifdef DEBUG +/* Print a JoinNode. + */ +static void +print_node( JoinNode *node ) +{ + printf( "%s, position %dx%d, size %dx%d, index %d\n", + im_skip_dir( node->name ), + node->cumtrn.oarea.left, node->cumtrn.oarea.top, + node->cumtrn.oarea.width, node->cumtrn.oarea.height, + node->index ); +} +#endif /*DEBUG*/ + +#ifdef DEBUG +/* Print a leaf. + */ +static void * +print_leaf( JoinNode *node ) +{ + if( node->type == JOIN_LEAF ) + print_node( node ); + + return( NULL ); +} +#endif /*DEBUG*/ + +/* Count all join nodes. + */ +static void * +count_joins( JoinNode *node ) +{ + if( node->type == JOIN_TB || + node->type == JOIN_LR || + node->type == JOIN_LRROTSCALE || + node->type == JOIN_TBROTSCALE ) + node->st->njoin++; + + return( NULL ); +} + +#ifdef DEBUG +/* Print a few spaces. + */ +static void +spc( int n ) +{ + int i; + + for( i = 0; i < n; i++ ) + printf( " " ); +} +#endif /*DEBUG*/ + +#ifdef DEBUG +static char * +JoinType2char( JoinType type ) +{ + switch( type ) { + case JOIN_LR: return( "JOIN_LR" ); + case JOIN_TB: return( "JOIN_TB" ); + case JOIN_LRROTSCALE: return( "JOIN_LRROTSCALE" ); + case JOIN_TBROTSCALE: return( "JOIN_TBROTSCALE" ); + case JOIN_CP: return( "JOIN_CP" ); + case JOIN_LEAF: return( "JOIN_LEAF" ); + + default: + error_exit( "internal error #9275" ); + /*NOTEACHED*/ + + return( NULL ); + } +} +#endif /*DEBUG*/ + +#ifdef DEBUG +/* Print a join node. + */ +static void * +print_joins( JoinNode *node, int indent ) +{ + switch( node->type ) { + case JOIN_TB: + case JOIN_LR: + case JOIN_TBROTSCALE: + case JOIN_LRROTSCALE: + spc( indent ); + printf( "%s to make %s, size %dx%d, pos. %dx%d, of:\n", + JoinType2char( node->type ), + im_skip_dir( node->name ), + node->cumtrn.oarea.width, node->cumtrn.oarea.height, + node->cumtrn.oarea.left, node->cumtrn.oarea.top ); + spc( indent ); + printf( "reference:\n" ); + print_joins( node->arg1, indent+2 ); + spc( indent ); + printf( "secondary:\n" ); + print_joins( node->arg2, indent+2 ); + break; + + case JOIN_CP: + spc( indent ); + printf( "copy to make %s of:\n", im_skip_dir( node->name ) ); + print_joins( node->arg1, indent+2 ); + break; + + case JOIN_LEAF: + spc( indent ); + printf( "input image %s\n", im_skip_dir( node->name ) ); + break; + } + + return( NULL ); +} +#endif /*DEBUG*/ + +#ifdef DEBUG +/* Print an overlap. + */ +static void * +print_overlap( OverlapInfo *lap ) +{ + printf( "-> %s overlaps with %s; (this, other) = (%.4G, %.4G)\n", + im_skip_dir( lap->node->name ), + im_skip_dir( lap->other->name ), + lap->nstats->coeff[4], + lap->ostats->coeff[4] ); + + return( NULL ); +} +#endif /*DEBUG*/ + +#ifdef DEBUG +/* Print the overlaps on a leaf. + */ +static void * +print_overlaps( JoinNode *node ) +{ + if( node->type == JOIN_LEAF && g_slist_length( node->overlaps ) > 0 ) { + printf( "overlap of %s with:\n", im_skip_dir( node->name ) ); + im_slist_map2( node->overlaps, + (VSListMap2Fn) print_overlap, NULL, NULL ); + } + + return( NULL ); +} +#endif /*DEBUG*/ + +#ifdef DEBUG +/* Print and accumulate the error on an overlap. + */ +static void * +print_overlap_error( OverlapInfo *lap, double *fac, double *total ) +{ + double na = lap->nstats->coeff[4]; + double oa = lap->ostats->coeff[4]; + double err; + + if( fac ) { + na *= fac[lap->node->index]; + oa *= fac[lap->other->index]; + } + + err = na - oa; + + printf( "-> file %s, error = %g\n", + im_skip_dir( lap->other->name ), err ); + *total += err*err; + + return( NULL ); +} +#endif /*DEBUG*/ + +#ifdef DEBUG +/* Print and accumulate the overlap errors on a leaf. + */ +static void * +print_overlap_errors( JoinNode *node, double *fac, double *total ) +{ + if( node->type == JOIN_LEAF && g_slist_length( node->overlaps ) > 0 ) { + printf( "overlap of %s (index %d) with:\n", + im_skip_dir( node->name ), node->index ); + im_slist_map2( node->overlaps, + (VSListMap2Fn) print_overlap_error, fac, total ); + } + + return( NULL ); +} +#endif /*DEBUG*/ + +/* Make a DOUBLEMASK local to an image descriptor. + */ +static DOUBLEMASK * +local_mask( IMAGE *out, DOUBLEMASK *mask ) +{ + if( !mask ) + return( NULL ); + + if( im_add_evalend_callback( out, + (im_callback_fn) im_free_dmask, mask, NULL ) ) { + im_free_dmask( mask ); + return( NULL ); + } + + return( mask ); +} + +/* Extract a rect. + */ +static int +extract_rect( IMAGE *in, IMAGE *out, Rect *r ) +{ + return( im_extract_area( in, out, + r->left, r->top, r->width, r->height ) ); +} + +/* Two images overlap in an area ... make a mask the size of the area, which + * has 255 for every pixel where both images are non-zero. + */ +static int +make_overlap_mask( IMAGE *ref, IMAGE *sec, IMAGE *mask, + Rect *rarea, Rect *sarea ) +{ + IMAGE *t[6]; + + if( im_open_local_array( mask, t, 6, "mytemps", "p" ) || + extract_rect( ref, t[0], rarea ) || + extract_rect( sec, t[1], sarea ) || + im_extract_band( t[0], t[2], 0 ) || + im_extract_band( t[1], t[3], 0 ) || + im_notequalconst( t[2], t[4], 0.0 ) || + im_notequalconst( t[3], t[5], 0.0 ) || + im_andimage( t[4], t[5], mask ) ) + return( -1 ); + + return( 0 ); +} + +/* Find the number of non-zero pixels in a mask image. + */ +static int +count_nonzero( IMAGE *in, gint64 *count ) +{ + double avg; + + if( im_avg( in, &avg ) ) + return( -1 ); + *count = (avg * in->Xsize * in->Ysize ) / 255.0; + + return( 0 ); +} + +/* Find stats on an area of an IMAGE ... consider only pixels for which the + * mask is true. + */ +static DOUBLEMASK * +find_image_stats( IMAGE *in, IMAGE *mask, Rect *area ) +{ + DOUBLEMASK *stats; + IMAGE *t[4]; + gint64 count; + + /* Extract area, build black image, mask out pixels we want. + */ + if( im_open_local_array( in, t, 4, "find_image_stats", "p" ) || + extract_rect( in, t[0], area ) || + im_black( t[1], t[0]->Xsize, t[0]->Ysize, t[0]->Bands ) || + im_clip2fmt( t[1], t[2], t[0]->BandFmt ) || + im_ifthenelse( mask, t[0], t[2], t[3] ) ) + return( NULL ); + + /* Get stats from masked image. + */ + if( !(stats = local_mask( in, im_stats( t[3] ) )) ) + return( NULL ); + + /* Number of non-zero pixels in mask. + */ + if( count_nonzero( mask, &count ) ) + return( NULL ); + + /* And scale masked average to match. + */ + stats->coeff[4] *= (double) count / + ((double) mask->Xsize * mask->Ysize); + + /* Yuk! Zap the deviation column with the pixel count. Used later to + * determine if this is likely to be a significant overlap. + */ + stats->coeff[5] = count; + +#ifdef DEBUG + if( count == 0 ) + im_warn( "global_balance", _( "empty overlap!" ) ); +#endif /*DEBUG*/ + + return( stats ); +} + +/* Find the stats for an overlap struct. + */ +static int +find_overlap_stats( OverlapInfo *lap ) +{ + IMAGE *t1 = im_open_local( lap->node->im, "find_overlap_stats:1", "p" ); + Rect rarea, sarea; + + /* Translate the overlap area into the coordinate scheme for the main + * node. + */ + rarea = lap->overlap; + rarea.left -= lap->node->cumtrn.oarea.left; + rarea.top -= lap->node->cumtrn.oarea.top; + + /* Translate the overlap area into the coordinate scheme for the other + * node. + */ + sarea = lap->overlap; + sarea.left -= lap->other->cumtrn.oarea.left; + sarea.top -= lap->other->cumtrn.oarea.top; + + /* Make a mask for the overlap. + */ + if( make_overlap_mask( lap->node->trnim, lap->other->trnim, t1, + &rarea, &sarea ) ) + return( -1 ); + + /* Find stats for that area. + */ + if( !(lap->nstats = find_image_stats( lap->node->trnim, t1, &rarea )) ) + return( -1 ); + if( !(lap->ostats = find_image_stats( lap->other->trnim, t1, &sarea )) ) + return( -1 ); + + return( 0 ); +} + +/* Sub-fn. of below. + */ +static void * +overlap_eq( OverlapInfo *this, JoinNode *node ) +{ + if( this->other == node ) + return( this ); + else + return( NULL ); +} + +/* Is this an overlapping leaf? If yes, add to overlap list. + */ +static void * +test_overlap( JoinNode *other, JoinNode *node ) +{ + Rect overlap; + OverlapInfo *lap; + + /* Is other a suitable leaf to overlap with node? + */ + if( other->type != JOIN_LEAF || node == other ) + return( NULL ); + + /* Is there an overlap? + */ + im_rect_intersectrect( &node->cumtrn.oarea, &other->cumtrn.oarea, + &overlap ); + if( im_rect_isempty( &overlap ) ) + return( NULL ); + + /* Is this a trivial overlap? Ignore it if it is. + */ + if( overlap.width * overlap.height < TRIVIAL ) + /* Too few pixels. + */ + return( NULL ); + + /* Have we already added this overlap the other way around? ie. is + * node on other's overlap list? + */ + if( im_slist_map2( other->overlaps, + (VSListMap2Fn) overlap_eq, node, NULL ) ) + return( NULL ); + + /* A new overlap - add to overlap list. + */ + if( !(lap = build_overlap( node, other, &overlap )) ) + return( node ); + + /* Calculate overlap statistics. + */ + if( find_overlap_stats( lap ) ) + return( node ); + + /* If the pixel count either masked overlap is trivial, ignore this + * overlap. + */ + if( lap->nstats->coeff[5] < TRIVIAL || + lap->ostats->coeff[5] < TRIVIAL ) { +#ifdef DEBUG + printf( "trivial overlap ... junking\n" ); + printf( "nstats count = %g, ostats count = %g\n", + lap->nstats->coeff[5], lap->ostats->coeff[5] ); + print_overlap( lap ); +#endif /*DEBUG*/ + overlap_destroy( lap ); + } + + return( NULL ); +} + +/* If this is a leaf, look at all other joins for a leaf that overlaps. Aside: + * If this is a leaf, there should be an IMAGE. Flag an error if there is + * not. + */ +static void * +find_overlaps( JoinNode *node, SymbolTable *st ) +{ + if( node->type == JOIN_LEAF ) { + /* Check for image. + */ + if( !node->im ) { + im_error( "im_global_balance", + _( "unable to open \"%s\"" ), node->name ); + return( node ); + } + if( !node->trnim ) + error_exit( "global_balance: sanity failure #9834" ); + + return( im__map_table( st, test_overlap, node, NULL ) ); + } + + return( NULL ); +} + +/* Bundle of variables for matrix creation. + */ +typedef struct { + SymbolTable *st; /* Main table */ + JoinNode *leaf; /* Leaf to be 1.000 */ + DOUBLEMASK *K; /* LHS */ + DOUBLEMASK *M; /* RHS */ + int row; /* Current row */ +} MatrixBundle; + +/* Add a new row for the nominated overlap to the matricies. + */ +static void * +add_nominated( OverlapInfo *ovl, MatrixBundle *bun, double *gamma ) +{ + double *Kp = bun->K->coeff + bun->row; + double *Mp = bun->M->coeff + bun->row*bun->M->xsize; + double ns = pow( ovl->nstats->coeff[4], 1/(*gamma) ); + double os = pow( ovl->ostats->coeff[4], 1/(*gamma) ); + + Kp[0] = ns; + Mp[ovl->other->index - 1] = os; + + bun->row++; + + return( NULL ); +} + +/* Add a new row for an ordinary overlap to the matricies. + */ +static void * +add_other( OverlapInfo *ovl, MatrixBundle *bun, double *gamma ) +{ + double *Mp = bun->M->coeff + bun->row*bun->M->xsize; + double ns = -pow( ovl->nstats->coeff[4], 1/(*gamma) ); + double os = pow( ovl->ostats->coeff[4], 1/(*gamma) ); + + Mp[ovl->node->index - 1] = ns; + Mp[ovl->other->index - 1] = os; + + bun->row++; + + return( NULL ); +} + +/* Add stuff for node to matrix. + */ +static void * +add_row( JoinNode *node, MatrixBundle *bun, double *gamma ) +{ + if( node == bun->leaf ) + im_slist_map2( node->overlaps, + (VSListMap2Fn) add_nominated, bun, gamma ); + else + im_slist_map2( node->overlaps, + (VSListMap2Fn) add_other, bun, gamma ); + + return( NULL ); +} + +/* Fill K and M. leaf is image selected to have factor of 1.000. + */ +static void +fill_matricies( SymbolTable *st, double gamma, DOUBLEMASK *K, DOUBLEMASK *M ) +{ + MatrixBundle bun; + + bun.st = st; + bun.leaf = st->leaf; + bun.K = K; + bun.M = M; + bun.row = 0; + + /* Build matricies. + */ + im__map_table( st, add_row, &bun, &gamma ); +} + +/* Used to select the leaf whose coefficient we set to 1. + */ +static void * +choose_leaf( JoinNode *node ) +{ + if( node->type == JOIN_LEAF ) + return( node ); + + return( NULL ); +} + +/* Make an image from a node. + */ +static IMAGE * +make_mos_image( SymbolTable *st, JoinNode *node, transform_fn tfn, void *a ) +{ + IMAGE *im1, *im2, *out; + + switch( node->type ) { + case JOIN_LR: + case JOIN_TB: + if( !(im1 = make_mos_image( st, node->arg1, tfn, a )) || + !(im2 = make_mos_image( st, node->arg2, tfn, a )) || + !(out = im_open_local( st->im, node->name, "p" )) ) + return( NULL ); + + if( node->type == JOIN_LR ) { + if( im_lrmerge( im1, im2, out, + -node->dx, -node->dy, node->mwidth ) ) + return( NULL ); + } + else { + if( im_tbmerge( im1, im2, out, + -node->dx, -node->dy, node->mwidth ) ) + return( NULL ); + } + + break; + + case JOIN_LRROTSCALE: + case JOIN_TBROTSCALE: + if( !(im1 = make_mos_image( st, node->arg1, tfn, a )) || + !(im2 = make_mos_image( st, node->arg2, tfn, a )) || + !(out = im_open_local( st->im, node->name, "p" )) ) + return( NULL ); + + if( node->type == JOIN_LRROTSCALE ) { + if( im__lrmerge1( im1, im2, out, + node->a, node->b, node->dx, node->dy, + node->mwidth ) ) + return( NULL ); + } + else { + if( im__tbmerge1( im1, im2, out, + node->a, node->b, node->dx, node->dy, + node->mwidth ) ) + return( NULL ); + } + + break; + + case JOIN_LEAF: + /* Trivial case! + */ + if( !(out = tfn( node, a )) ) + return( NULL ); + + break; + + case JOIN_CP: + /* Very trivial case. + */ + out = make_mos_image( st, node->arg1, tfn, a ); + + break; + + default: + error_exit( "internal error #982369824375987" ); + /*NOTEACHED*/ + return( NULL ); + } + + return( out ); +} + +/* Re-build mosaic. + */ +int +im__build_mosaic( SymbolTable *st, IMAGE *out, transform_fn tfn, void *a ) +{ + JoinNode *root = st->root; + IMAGE *im1, *im2; + + switch( root->type ) { + case JOIN_LR: + case JOIN_TB: + if( !(im1 = make_mos_image( st, root->arg1, tfn, a )) || + !(im2 = make_mos_image( st, root->arg2, tfn, a )) ) + return( -1 ); + + if( root->type == JOIN_LR ) { + if( im_lrmerge( im1, im2, out, + -root->dx, -root->dy, root->mwidth ) ) + return( -1 ); + } + else { + if( im_tbmerge( im1, im2, out, + -root->dx, -root->dy, root->mwidth ) ) + return( -1 ); + } + + break; + + case JOIN_LRROTSCALE: + case JOIN_TBROTSCALE: + if( !(im1 = make_mos_image( st, root->arg1, tfn, a )) || + !(im2 = make_mos_image( st, root->arg2, tfn, a )) ) + return( -1 ); + + if( root->type == JOIN_LRROTSCALE ) { + if( im__lrmerge1( im1, im2, out, + root->a, root->b, root->dx, root->dy, + root->mwidth ) ) + return( -1 ); + } + else { + if( im__tbmerge1( im1, im2, out, + root->a, root->b, root->dx, root->dy, + root->mwidth ) ) + return( -1 ); + } + + break; + + case JOIN_LEAF: + /* Trivial case! Just one file in our mosaic. + */ + if( !(im1 = tfn( root, a )) || im_copy( im1, out ) ) + return( -1 ); + + break; + + case JOIN_CP: + /* Very trivial case. + */ + if( !(im1 = make_mos_image( st, root->arg1, tfn, a )) ) + return( -1 ); + if( im_copy( im1, out ) ) + return( -1 ); + + break; + + default: + error_exit( "internal error #982369824375987" ); + /*NOTEACHED*/ + } + + return( 0 ); +} + +/* Find correction factors. + */ +static int +find_factors( SymbolTable *st, double gamma ) +{ + DOUBLEMASK *K; + DOUBLEMASK *M; + DOUBLEMASK *m1, *m2, *m3, *m4, *m5; + double total; + double avg; + int i; + + /* Make output matricies. + */ + if( !(K = local_mask( st->im, im_create_dmask( "K", 1, st->novl ) )) || + !(M = local_mask( st->im, + im_create_dmask( "M", st->nim-1, st->novl ) )) ) + return( -1 ); + fill_matricies( st, gamma, K, M ); +#ifdef DEBUG + im_write_dmask( K ); + im_write_dmask( M ); +#endif /*DEBUG*/ + + /* Calculate LMS. + */ + if( !(m1 = local_mask( st->im, im_mattrn( M, "lms:1" ) )) ) + return( -1 ); + if( !(m2 = local_mask( st->im, im_matmul( m1, M, "lms:2" ) )) ) + return( -1 ); + if( !(m3 = local_mask( st->im, im_matinv( m2, "lms:3" ) )) ) + return( -1 ); + if( !(m4 = local_mask( st->im, im_matmul( m3, m1, "lms:4" ) )) ) + return( -1 ); + if( !(m5 = local_mask( st->im, im_matmul( m4, K, "lms:5" ) )) ) + return( -1 ); + + /* Make array of correction factors. + */ + if( !(st->fac = IM_ARRAY( st->im, st->nim, double )) ) + return( -1 ); + for( i = 0; i < m5->ysize; i++ ) + st->fac[i + 1] = m5->coeff[i]; + st->fac[0] = 1.0; + + /* Find average balance factor, normalise to that average. + */ + total = 0.0; + for( i = 0; i < st->nim; i++ ) + total += st->fac[i]; + avg = total / st->nim; + for( i = 0; i < st->nim; i++ ) + st->fac[i] /= avg; + +#ifdef DEBUG + /* Diagnostics! + */ + printf( "debugging output for im_global_balance():\n" ); + for( i = 0; i < st->nim; i++ ) + printf( "balance factor %d = %g\n", i, st->fac[i] ); + total = 0.0; + printf( "Overlap errors:\n" ); + im__map_table( st, print_overlap_errors, NULL, &total ); + printf( "RMS error = %g\n", sqrt( total / st->novl ) ); + + total = 0.0; + printf( "Overlap errors after adjustment:\n" ); + im__map_table( st, print_overlap_errors, st->fac, &total ); + printf( "RMS error = %g\n", sqrt( total / st->novl ) ); +#endif /*DEBUG*/ + + return( 0 ); +} + +/* Look for all leaves, make sure we have a transformed version of each. + */ +static void * +generate_trn_leaves( JoinNode *node, SymbolTable *st ) +{ + if( node->type == JOIN_LEAF ) { + /* Check for image. + */ + if( !node->im ) { + im_error( "im_global_balance", + _( "unable to open \"%s\"" ), node->name ); + return( node ); + } + if( node->trnim ) + error_exit( "global_balance: sanity failure #765" ); + + /* Special case: if this is an untransformed leaf (there will + * always be at least one), then skip the affine. + */ + if( im__transform_isidentity( &node->cumtrn ) ) + node->trnim = node->im; + else + if( !(node->trnim = + im_open_local( node->im, "trnleaf:1", "p" )) || + im__affine( node->im, node->trnim, + &node->cumtrn ) ) + return( node ); + } + + return( NULL ); +} + +/* Analyse mosaic. + */ +static int +analyse_mosaic( SymbolTable *st, IMAGE *in ) +{ + /* Parse Hist on in. + */ + if( im__parse_desc( st, in ) ) + return( -1 ); + + /* Print parsed data. + */ +#ifdef DEBUG + printf( "Input files:\n" ); + im__map_table( st, print_leaf, NULL, NULL ); + printf( "\nOutput file:\n" ); + print_node( st->root ); + printf( "\nJoin commands:\n" ); + print_joins( st->root, 0 ); +#endif /*DEBUG*/ + + /* Generate transformed leaves. + */ + if( im__map_table( st, generate_trn_leaves, st, NULL ) ) + return( -1 ); + + /* Find overlaps. + */ + if( im__map_table( st, find_overlaps, st, NULL ) ) + return( -1 ); + + /* Scan table, counting and indexing input images and joins. + */ + im__map_table( st, count_leaves, NULL, NULL ); + im__map_table( st, count_joins, NULL, NULL ); + + /* Select leaf to be 1.000. + * This must be index == 0, unless you change stuff above! + */ + st->leaf = im__map_table( st, choose_leaf, NULL, NULL ); + + /* And print overlaps. + */ +#ifdef DEBUG + printf( "\nLeaf to be 1.000:\n" ); + print_node( st->leaf ); + printf( "\nOverlaps:\n" ); + im__map_table( st, print_overlaps, NULL, NULL ); + printf( "\n%d input files, %d unique overlaps, %d joins\n", + st->nim, st->novl, st->njoin ); +#endif /*DEBUG*/ + + return( 0 ); +} + +/* Scale im by fac --- if it's uchar/ushort, use a lut. If we can use a lut, + * transform in linear space. If we can't, don't bother for efficiency. + */ +static IMAGE * +transform( JoinNode *node, double *gamma ) +{ + SymbolTable *st = node->st; + IMAGE *in = node->im; + double fac = st->fac[node->index]; + + IMAGE *out = im_open_local( st->im, node->name, "p" ); + IMAGE *t1 = im_open_local( out, "transform:1", "p" ); + IMAGE *t2 = im_open_local( out, "transform:2", "p" ); + IMAGE *t3 = im_open_local( out, "transform:3", "p" ); + IMAGE *t4 = im_open_local( out, "transform:4", "p" ); + IMAGE *t5 = im_open_local( out, "transform:5", "p" ); + + + if( !out || !t1 || !t2 || !t3 || !t4 || !t5 ) + return( NULL ); + + if( fac == 1.0 ) { + /* Easy! + */ + out = in; + } + else if( in->BandFmt == IM_BANDFMT_UCHAR ) { + if( im_identity( t1, 1 ) || + im_powtra( t1, t2, 1/(*gamma) ) || + im_lintra( fac, t2, 0.0, t3 ) || + im_powtra( t3, t4, *gamma ) || + im_clip( t4, t5 ) || + im_maplut( in, out, t5 ) ) + return( NULL ); + } + else if( in->BandFmt == IM_BANDFMT_USHORT ) { + if( im_identity_ushort( t1, 1, 65535 ) || + im_powtra( t1, t2, 1/(*gamma) ) || + im_lintra( fac, t2, 0.0, t3 ) || + im_powtra( t3, t4, *gamma ) || + im_clip2us( t4, t5 ) || + im_maplut( in, out, t5 ) ) + return( NULL ); + } + else { + /* Just im_lintra it. + */ + if( im_lintra( fac, in, 0.0, t1 ) || + im_clip2fmt( t1, out, in->BandFmt ) ) + return( NULL ); + } + + return( out ); +} + +/* As above, but output as float, not matched to input. + */ +static IMAGE * +transformf( JoinNode *node, double *gamma ) +{ + SymbolTable *st = node->st; + IMAGE *in = node->im; + double fac = node->st->fac[node->index]; + + IMAGE *out = im_open_local( st->im, node->name, "p" ); + IMAGE *t1 = im_open_local( out, "transform:1", "p" ); + IMAGE *t2 = im_open_local( out, "transform:2", "p" ); + IMAGE *t3 = im_open_local( out, "transform:3", "p" ); + IMAGE *t4 = im_open_local( out, "transform:4", "p" ); + + if( !out || !t1 || !t2 || !t3 || !t4 ) + return( NULL ); + + if( fac == 1.0 ) { + /* Easy! + */ + out = in; + } + else if( in->BandFmt == IM_BANDFMT_UCHAR ) { + if( im_identity( t1, 1 ) || + im_powtra( t1, t2, 1/(*gamma) ) || + im_lintra( fac, t2, 0.0, t3 ) || + im_powtra( t3, t4, *gamma ) || + im_maplut( in, out, t4 ) ) + return( NULL ); + } + else if( in->BandFmt == IM_BANDFMT_USHORT ) { + if( im_identity_ushort( t1, 1, 65535 ) || + im_powtra( t1, t2, 1/(*gamma) ) || + im_lintra( fac, t2, 0.0, t3 ) || + im_powtra( t3, t4, *gamma ) || + im_maplut( in, out, t4 ) ) + return( NULL ); + } + else { + /* Just im_lintra it. + */ + if( im_lintra( fac, in, 0.0, out ) ) + return( NULL ); + } + + return( out ); +} + +/* Balance mosaic, outputting in the original format. + */ +int +im_global_balance( IMAGE *in, IMAGE *out, double gamma ) +{ + SymbolTable *st; + + if( !(st = im__build_symtab( out, SYM_TAB_SIZE )) || + analyse_mosaic( st, in ) || + find_factors( st, gamma ) || + im__build_mosaic( st, out, (transform_fn) transform, &gamma ) ) + return( -1 ); + + return( 0 ); +} + +/* Balance mosaic, outputting as float. This is useful if the automatic + * selection of balance range fails - our caller can search the output for the + * min and max, and rescale to prevent burn-out. + */ +int +im_global_balancef( IMAGE *in, IMAGE *out, double gamma ) +{ + SymbolTable *st; + + if( !(st = im__build_symtab( out, SYM_TAB_SIZE )) || + analyse_mosaic( st, in ) || + find_factors( st, gamma ) || + im__build_mosaic( st, out, (transform_fn) transformf, &gamma ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/global_balance.h b/libsrc/mosaicing/global_balance.h new file mode 100644 index 00000000..2d33d809 --- /dev/null +++ b/libsrc/mosaicing/global_balance.h @@ -0,0 +1,126 @@ +/* Header for the .desc file parser in im_global_balance() + * + * 1/11/01 JC + * - cut from global_balance.c + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Number of entries in spine of file name hash table. + */ +#define SYM_TAB_SIZE (113) + +typedef enum _JoinType JoinType; +typedef struct _OverlapInfo OverlapInfo; +typedef struct _JoinNode JoinNode; +typedef struct _SymbolTable SymbolTable; + +/* Type of a transform function. + */ +typedef IMAGE *(*transform_fn)( JoinNode *, void * ); + +/* Join type. + */ +enum _JoinType { + JOIN_LR, /* im_lrmerge join */ + JOIN_TB, /* im_tbmerge join */ + JOIN_LRROTSCALE, /* 1st oder lrmerge */ + JOIN_TBROTSCALE, /* 1st oder tbmerge */ + JOIN_CP, /* im_copy operation */ + JOIN_LEAF /* Base file */ +}; + +/* An overlap struct. Attach a list of these to each leaf, one for each of + * the other leaves we touch. + */ +struct _OverlapInfo { + JoinNode *node; /* The base node - we are on this list */ + JoinNode *other; /* Node we overlap with */ + Rect overlap; /* The overlap area */ + DOUBLEMASK *nstats; /* Node's stats for overlap area */ + DOUBLEMASK *ostats; /* Other's stats for overlap area */ +}; + +/* Struct for a join node. + */ +struct _JoinNode { + char *name; /* This file name */ + JoinType type; /* What kind of join */ + SymbolTable *st; /* Symbol table we are on */ + int dirty; /* Used for circularity detection */ + + /* Params from join line in .desc file. + */ + double a, b; + double dx, dy; + int mwidth; + + /* Cumulative transform for this node. What our parents do to us. + * cumtrn.area is position and size of us, thistrn.area is pos and + * size of arg2. + */ + Transformation cumtrn; + + /* X-tras for LR/TB. thistrn is what we do to arg2. + */ + JoinNode *arg1; /* Left or up thing to join */ + JoinNode *arg2; /* Right or down thing to join */ + Transformation thistrn; /* Transformation for arg2 */ + + /* Special for leaves: all the join_nodes we overlap with, the + * IMAGE for that file, and the index. + */ + GSList *overlaps; + IMAGE *im; + IMAGE *trnim; /* Transformed image .. used in 2nd pass */ + int index; +}; + +/* We need to keep a table of JoinNode, indexed by file name. Hash into one + * of these from the name to get a pointer to the base of a list of JoinNode + * which hash to that offset. + */ +struct _SymbolTable { + GSList **table; /* Ptr to base of hash table */ + int sz; /* Size of hash table */ + IMAGE *im; /* Malloc relative to this */ + + int novl; /* Number of unique overlaps */ + int nim; /* Number of leaf images */ + int njoin; /* Number of join nodes */ + + JoinNode *root; /* Root of join tree */ + JoinNode *leaf; /* Leaf nominated to be 1.000 */ + double *fac; /* Correction factors */ +}; + +IMAGE *im__global_open_image( SymbolTable *st, char *name ); +SymbolTable *im__build_symtab( IMAGE *out, int sz ); +int im__parse_desc( SymbolTable *st, IMAGE *in ); +void *im__map_table( SymbolTable *st, void *(*fn)(), void *a, void *b ); +int im__build_mosaic( SymbolTable *st, + IMAGE *out, transform_fn tfn, void * ); diff --git a/libsrc/mosaicing/im_affine.c b/libsrc/mosaicing/im_affine.c new file mode 100644 index 00000000..73e53374 --- /dev/null +++ b/libsrc/mosaicing/im_affine.c @@ -0,0 +1,761 @@ +/* @(#) im_affine() ... affine transform, bi-linear interpolation. + * @(#) + * @(#) int im_affine(in, out, a, b, c, d, dx, dy, w, h, x, y) + * @(#) + * @(#) IMAGE *in, *out; + * @(#) double a, b, c, d, dx, dy; + * @(#) int w, h, x, y; + * @(#) + * @(#) Forward transform + * @(#) X = a * x + b * y + dx + * @(#) Y = c * x + d * y + dy + * @(#) + * @(#) x and y are the coordinates in input image. + * @(#) X and Y are the coordinates in output image. + * @(#) (0,0) is the upper left corner. + * + * Copyright N. Dessipris + * Written on: 01/11/1991 + * Modified on: 12/3/92 JC + * - rounding error in interpolation routine fixed + * - test for scale=1, angle=0 case fixed + * - clipping of output removed: redundant + * - various little tidies + * - problems remain with scale>20, size<10 + * + * Re-written on: 20/08/92, J.Ph Laurent + * + * 21/02/93, JC + * - speed-ups + * - simplifications + * - im_similarity now calculates a window and calls this routine + * 6/7/93 JC + * - rewritten for partials + * - ANSIfied + * - now rotates any non-complex type + * 3/6/94 JC + * - C revised in bug search + * 9/6/94 JC + * - im_prepare() was preparing too small an area! oops + * 22/5/95 JC + * - added code to detect all-black output area case - helps lazy ip + * 3/7/95 JC + * - IM_CODING_LABQ handling moved to here + * 31/7/97 JC + * - dx/dy sign reversed to be less confusing ... now follows comment at + * top ... ax - by + dx etc. + * - tiny speed up, replaced the *++ on interpolation with [z] + * - im_similarity() moved in here + * - args swapped: was whxy, now xywh + * - didn't agree with dispatch fns before :( + * 3/3/98 JC + * - im_demand_hint() added + * 20/12/99 JC + * - im_affine() made from im_similarity_area() + * - transform stuff cleaned up a bit + * 14/4/01 JC + * - oops, invert_point() had a rounding problem + * 23/2/02 JC + * - pre-calculate interpolation matricies + * - integer interpolation for int8/16 types, double for + * int32/float/double + * - faster transformation + * 15/8/02 JC + * - records Xoffset/Yoffset + * 14/4/04 + * - rounding, clipping and transforming revised, now pixel-perfect (or + * better than gimp, anyway) + * 22/6/05 + * - all revised again, simpler and more reliable now + * 30/3/06 + * - gah, still an occasional clipping problem + * 12/7/06 + * - still more tweaking, gah again + * 7/10/06 + * - set THINSTRIP for no-rotate affines + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG +#define DEBUG_GEOMETRY + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#include "merge.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* "fast" floor() ... on my laptop, anyway. + */ +#define FLOOR( V ) ((V) >= 0 ? (int)(V) : (int)((V) - 1)) + +/* Precalculate a whole bunch of interpolation matricies. int (used for pel + * sizes up to short), and double (for all others). We go to scale + 1, so + * we can round-to-nearest safely. + + FIXME ... should use seperable tables really + + */ +static int im_affine_linear_int + [TRANSFORM_SCALE + 1][TRANSFORM_SCALE + 1][4]; +static double im_affine_linear_double + [TRANSFORM_SCALE + 1][TRANSFORM_SCALE + 1][4]; + +/* Make sure the interpolation tables are built. + */ +static void +affine_interpol_calc( void ) +{ + static int calced = 0; + int x, y; + + if( calced ) + return; + + for( x = 0; x < TRANSFORM_SCALE + 1; x++ ) + for( y = 0; y < TRANSFORM_SCALE + 1; y++ ) { + double X, Y, Xd, Yd; + double c1, c2, c3, c4; + + /* Interpolation errors. + */ + X = (double) x / TRANSFORM_SCALE; + Y = (double) y / TRANSFORM_SCALE; + Xd = 1.0 - X; + Yd = 1.0 - Y; + + /* Weights. + */ + c1 = Xd*Yd; + c2 = X*Yd; + c3 = X*Y; + c4 = Xd*Y; + + im_affine_linear_double[x][y][0] = c1; + im_affine_linear_double[x][y][1] = c2; + im_affine_linear_double[x][y][2] = c3; + im_affine_linear_double[x][y][3] = c4; + + im_affine_linear_int[x][y][0] = c1 * INTERPOL_SCALE; + im_affine_linear_int[x][y][1] = c2 * INTERPOL_SCALE; + im_affine_linear_int[x][y][2] = c3 * INTERPOL_SCALE; + im_affine_linear_int[x][y][3] = c4 * INTERPOL_SCALE; + } + + calced = 1; +} + +/* Calculate the inverse transformation. + */ +int +im__transform_calc_inverse( Transformation *trn ) +{ + DOUBLEMASK *msk, *msk2; + + if( !(msk = im_create_dmaskv( "boink", 2, 2, + trn->a, trn->b, trn->c, trn->d )) ) + return( -1 ); + if( !(msk2 = im_matinv( msk, "boink2" )) ) { + (void) im_free_dmask( msk ); + return( -1 ); + } + trn->ia = msk2->coeff[0]; + trn->ib = msk2->coeff[1]; + trn->ic = msk2->coeff[2]; + trn->id = msk2->coeff[3]; + (void) im_free_dmask( msk ); + (void) im_free_dmask( msk2 ); + + return( 0 ); +} + +/* Init a Transform. + */ +void +im__transform_init( Transformation *trn ) +{ + trn->oarea.left = 0; + trn->oarea.top = 0; + trn->oarea.width = -1; + trn->oarea.height = -1; + trn->iarea.left = 0; + trn->iarea.top = 0; + trn->iarea.width = -1; + trn->iarea.height = -1; + trn->a = 1.0; /* Identity transform */ + trn->b = 0.0; + trn->c = 0.0; + trn->d = 1.0; + trn->dx = 0.0; + trn->dy = 0.0; + + (void) im__transform_calc_inverse( trn ); +} + +/* Test for transform is identity function. + */ +int +im__transform_isidentity( Transformation *trn ) +{ + if( trn->a == 1.0 && trn->b == 0.0 && trn->c == 0.0 && + trn->d == 1.0 && trn->dx == 0.0 && trn->dy == 0.0 ) + return( 1 ); + else + return( 0 ); +} + +/* Map a pixel coordinate through the transform. + */ +void +im__transform_forward( Transformation *trn, + double x, double y, /* In input space */ + double *ox, double *oy ) /* In output space */ +{ + *ox = trn->a * x + trn->b * y + trn->dx; + *oy = trn->c * x + trn->d * y + trn->dy; +} + +/* Map a pixel coordinate through the inverse transform. + */ +void +im__transform_inverse( Transformation *trn, + double x, double y, /* In output space */ + double *ox, double *oy ) /* In input space */ +{ + double mx = x - trn->dx; + double my = y - trn->dy; + + *ox = trn->ia * mx + trn->ib * my; + *oy = trn->ic * mx + trn->id * my; +} + +/* Combine two transformations. out can be one of the ins. + */ +int +im__transform_add( Transformation *in1, Transformation *in2, + Transformation *out ) +{ + out->a = in1->a * in2->a + in1->c * in2->b; + out->b = in1->b * in2->a + in1->d * in2->b; + out->c = in1->a * in2->c + in1->c * in2->d; + out->d = in1->b * in2->c + in1->d * in2->d; + + out->dx = in1->dx * in2->a + in1->dy * in2->b + in2->dx; + out->dy = in1->dx * in2->c + in1->dy * in2->d + in2->dy; + + if( im__transform_calc_inverse( out ) ) + return( -1 ); + + return( 0 ); +} + +void +im__transform_print( Transformation *trn ) +{ + printf( "im__transform_print:\n" ); + printf( " iarea: left=%d, top=%d, width=%d, height=%d\n", + trn->iarea.left, + trn->iarea.top, + trn->iarea.width, + trn->iarea.height ); + printf( " oarea: left=%d, top=%d, width=%d, height=%d\n", + trn->oarea.left, + trn->oarea.top, + trn->oarea.width, + trn->oarea.height ); + printf( " mat: a=%g, b=%g, c=%g, d=%g\n", + trn->a, trn->b, trn->c, trn->d ); + printf( " off: dx=%g, dy=%g\n", + trn->dx, trn->dy ); +} + +/* Map a point through the inverse transform. Used for clipping calculations, + * so it takes account of iarea and oarea. + */ +static void +invert_point( Transformation *trn, + double x, double y, /* In output space */ + double *ox, double *oy ) /* In input space */ +{ + double xin = x - trn->oarea.left - trn->dx; + double yin = y - trn->oarea.top - trn->dy; + + /* Find the inverse transform of current (x, y) + */ + *ox = trn->ia * xin + trn->ib * yin; + *oy = trn->ic * xin + trn->id * yin; +} + +/* Given a bounding box for an area in the output image, set the bounding box + * for the corresponding pixels in the input image. + */ +static void +invert_rect( Transformation *trn, + Rect *in, /* In output space */ + Rect *out ) /* In input space */ +{ + double x1, y1; /* Map corners */ + double x2, y2; + double x3, y3; + double x4, y4; + double left, right, top, bottom; + + /* Map input Rect. + */ + invert_point( trn, in->left, in->top, &x1, &y1 ); + invert_point( trn, in->left, IM_RECT_BOTTOM(in), &x2, &y2 ); + invert_point( trn, IM_RECT_RIGHT(in), in->top, &x3, &y3 ); + invert_point( trn, IM_RECT_RIGHT(in), IM_RECT_BOTTOM(in), &x4, &y4 ); + + /* Find bounding box for these four corners. + */ + left = IM_MIN( x1, IM_MIN( x2, IM_MIN( x3, x4 ) ) ); + right = IM_MAX( x1, IM_MAX( x2, IM_MAX( x3, x4 ) ) ); + top = IM_MIN( y1, IM_MIN( y2, IM_MIN( y3, y4 ) ) ); + bottom = IM_MAX( y1, IM_MAX( y2, IM_MAX( y3, y4 ) ) ); + + /* Set output Rect. + */ + out->left = left; + out->top = top; + out->width = right - left + 1; + out->height = bottom - top + 1; + + /* Add a border for interpolation. You'd think +1 would do it, but + * we need to allow for rounding clipping as well. + + FIXME ... will need adjusting when we add bicubic + + */ + im_rect_marginadjust( out, 2 ); +} + +/* Interpolate a section ... int8/16 types. + */ +#define DO_IPEL(TYPE) { \ + TYPE *tq = (TYPE *) q; \ + \ + int c1 = im_affine_linear_int[xi][yi][0]; \ + int c2 = im_affine_linear_int[xi][yi][1]; \ + int c3 = im_affine_linear_int[xi][yi][2]; \ + int c4 = im_affine_linear_int[xi][yi][3]; \ + \ + /* p1 points to location (x_int, y_int) \ + * p2 " " " (x_int+1, y_int) \ + * p4 " " " (x_int+1, y_int+1) \ + * p3 " " " (x_int, y_int+1) \ + */ \ + PEL *p1 = (PEL *) IM_REGION_ADDR( ir, x_int, y_int ); \ + PEL *p2 = p1 + ofs2; \ + PEL *p3 = p1 + ofs3; \ + PEL *p4 = p1 + ofs4; \ + TYPE *tp1 = (TYPE *) p1; \ + TYPE *tp2 = (TYPE *) p2; \ + TYPE *tp3 = (TYPE *) p3; \ + TYPE *tp4 = (TYPE *) p4; \ + \ + /* Interpolate each band. \ + */ \ + for( z = 0; z < in->Bands; z++ ) \ + tq[z] = (c1*tp1[z] + c2*tp2[z] + \ + c3*tp3[z] + c4*tp4[z]) >> INTERPOL_SHIFT; \ +} + +/* Interpolate a pel ... int32 and float types. + */ +#define DO_FPEL(TYPE) { \ + TYPE *tq = (TYPE *) q; \ + \ + double c1 = im_affine_linear_double[xi][yi][0]; \ + double c2 = im_affine_linear_double[xi][yi][1]; \ + double c3 = im_affine_linear_double[xi][yi][2]; \ + double c4 = im_affine_linear_double[xi][yi][3]; \ + \ + /* p1 points to location (x_int, y_int) \ + * p2 " " " (x_int+1, y_int) \ + * p4 " " " (x_int+1, y_int+1) \ + * p3 " " " (x_int, y_int+1) \ + */ \ + PEL *p1 = (PEL *) IM_REGION_ADDR( ir, x_int, y_int ); \ + PEL *p2 = p1 + ofs2; \ + PEL *p3 = p1 + ofs3; \ + PEL *p4 = p1 + ofs4; \ + TYPE *tp1 = (TYPE *) p1; \ + TYPE *tp2 = (TYPE *) p2; \ + TYPE *tp3 = (TYPE *) p3; \ + TYPE *tp4 = (TYPE *) p4; \ + \ + /* Interpolate each band. \ + */ \ + for( z = 0; z < in->Bands; z++ ) \ + tq[z] = c1*tp1[z] + c2*tp2[z] + \ + c3*tp3[z] + c4*tp4[z]; \ +} + +static int +affine_gen( REGION *or, REGION *ir, IMAGE *in, Transformation *trn ) +{ + /* Output area for this call. + */ + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + Rect *iarea = &trn->iarea; + Rect *oarea = &trn->oarea; + int ps = IM_IMAGE_SIZEOF_PEL( in ); + int x, y, z; + + /* Interpolation variables. + */ + int ofs2, ofs3, ofs4; + + /* Clipping Rects. + */ + Rect image, need, clipped; + + /* Find the area of the input image we need. + */ + image.left = 0; + image.top = 0; + image.width = in->Xsize; + image.height = in->Ysize; + invert_rect( trn, r, &need ); + im_rect_intersectrect( &need, &image, &clipped ); + + /* Outside input image? All black. + */ + if( im_rect_isempty( &clipped ) ) { + im__black_region( or ); + return( 0 ); + } + + /* We do need some pixels from the input image to make our output - + * ask for them. + */ + if( im_prepare( ir, &clipped ) ) + return( -1 ); + +#ifdef DEBUG + printf( "affine: preparing left=%d, top=%d, width=%d, height=%d\n", + clipped.left, + clipped.top, + clipped.width, + clipped.height ); +#endif /*DEBUG*/ + + /* Calculate pel offsets. + */ + ofs2 = IM_IMAGE_SIZEOF_PEL( in ); + ofs3 = ofs2 + IM_REGION_LSKIP( ir ); + ofs4 = IM_REGION_LSKIP( ir ); + + /* Resample! + */ + for( y = to; y < bo; y++ ) { + /* Continuous cods in output space. + */ + double oy = y - oarea->top - trn->dy; + double ox; + + /* Input clipping rectangle. + */ + int ile = iarea->left; + int ito = iarea->top; + int iri = iarea->left + iarea->width; + int ibo = iarea->top + iarea->height; + + /* Derivative of matrix. + */ + double dx = trn->ia; + double dy = trn->ic; + + /* Continuous cods in input space. + */ + double ix, iy; + + PEL *q; + + q = (PEL *) IM_REGION_ADDR( or, le, y ); + ox = le - oarea->left - trn->dx; + + ix = trn->ia * ox + trn->ib * oy; + iy = trn->ic * ox + trn->id * oy; + + /* Offset ix/iy input by iarea.left/top ... so we skip the + * image edges we added for interpolation. + */ + ix += iarea->left; + iy += iarea->top; + + for( x = le; x < ri; x++ ) { + int fx, fy; + + fx = FLOOR( ix ); + fy = FLOOR( iy ); + + /* Clipping! Use >= for right/bottom, since IPOL needs + * to see one pixel more each way. + */ + if( fx < ile || fx >= iri || fy < ito || fy >= ibo ) { + for( z = 0; z < ps; z++ ) + q[z] = 0; + } + else { + double sx, sy; + int x_int, y_int; + int xi, yi; + + /* Subtract 0.5 to centre the bilinear. + + FIXME ... need to adjust for bicubic. + + */ + sx = ix - 0.5; + sy = iy - 0.5; + + /* Now go to scaled int. + */ + sx *= TRANSFORM_SCALE; + sy *= TRANSFORM_SCALE; + x_int = FLOOR( sx ); + y_int = FLOOR( sy ); + + /* Get index into interpolation table and + * unscaled integer position. + */ + xi = x_int & (TRANSFORM_SCALE - 1); + yi = y_int & (TRANSFORM_SCALE - 1); + x_int = x_int >> TRANSFORM_SHIFT; + y_int = y_int >> TRANSFORM_SHIFT; + + /* Interpolate for each input type. + */ + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + DO_IPEL( unsigned char ); + break; + case IM_BANDFMT_CHAR: + DO_IPEL( char ); + break; + case IM_BANDFMT_USHORT: + DO_IPEL( unsigned short ); + break; + case IM_BANDFMT_SHORT: + DO_IPEL( short ); + break; + case IM_BANDFMT_UINT: + DO_FPEL( unsigned int ); + break; + case IM_BANDFMT_INT: + DO_FPEL( int ); + break; + case IM_BANDFMT_FLOAT: + DO_FPEL( float ); + break; + case IM_BANDFMT_DOUBLE: + DO_FPEL( double ); + break; + + default: + error_exit( "im_affine: panic!"); + /*NOTREACHED*/ + } + } + + ix += dx; + iy += dy; + q += ps; + } + } + + return( 0 ); +} + +static int +affine( IMAGE *in, IMAGE *out, Transformation *trn ) +{ + Transformation *trn2; + double edge; + + if( im_iscomplex( in ) ) { + im_errormsg( "im_affine: complex input not supported" ); + return( -1 ); + } + + /* We output at (0,0), so displace output by that amount -ve to get + * output at (ox,oy). Alter our copy of trn. + */ + if( !(trn2 = IM_NEW( out, Transformation )) ) + return( -1 ); + *trn2 = *trn; + trn2->oarea.left = -trn->oarea.left; + trn2->oarea.top = -trn->oarea.top; + + if( im__transform_calc_inverse( trn2 ) ) + return( -1 ); + + /* Make output image. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = trn2->oarea.width; + out->Ysize = trn2->oarea.height; + + /* Normally SMALLTILE ... except if this is a size up/down affine. + */ + if( trn->b == 0.0 && trn->c == 0.0 ) { + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + } + else { + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + } + + /* Check for coordinate overflow ... we want to be able to hold the + * output space inside INT_MAX / TRANSFORM_SCALE. + */ + edge = INT_MAX / TRANSFORM_SCALE; + if( trn2->oarea.left < -edge || trn2->oarea.top < -edge || + IM_RECT_RIGHT( &trn2->oarea ) > edge || + IM_RECT_BOTTOM( &trn2->oarea ) > edge ) { + im_errormsg( "im_affine: output coordinates out of range" ); + return( -1 ); + } + + /* Generate! + */ + if( im_generate( out, + im_start_one, affine_gen, im_stop_one, in, trn2 ) ) + return( -1 ); + + return( 0 ); +} + +/* As above, but do IM_CODING_LABQ too. And embed the input. + */ +int +im__affine( IMAGE *in, IMAGE *out, Transformation *trn ) +{ + IMAGE *t3 = im_open_local( out, "im_affine:3", "p" ); + Transformation trn2; + +#ifdef DEBUG_GEOMETRY + printf( "im__affine: %s\n", in->filename ); + im__transform_print( trn ); +#endif /*DEBUG_GEOMETRY*/ + + /* Add new pixels around the input so we can interpolate at the edges. + * Bilinear needs 0.5 pixels on all edges. + + FIXME ... will need to fiddle with this when we add bicubic + + */ + if( !t3 || + im_embed( in, t3, 1, + 1, 1, in->Xsize + 2, in->Ysize + 2 ) ) + return( -1 ); + + /* Set iarea so we know what part of the input we can take. + */ + trn2 = *trn; + trn2.iarea.left += 1; + trn2.iarea.top += 1; + + affine_interpol_calc(); + + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t1 = im_open_local( out, "im_affine:1", "p" ); + IMAGE *t2 = im_open_local( out, "im_affine:2", "p" ); + + if( !t1 || !t2 || + im_LabQ2LabS( t3, t1 ) || + affine( t1, t2, &trn2 ) || + im_LabS2LabQ( t2, out ) ) + return( -1 ); + } + else if( in->Coding == IM_CODING_NONE ) { + if( affine( t3, out, &trn2 ) ) + return( -1 ); + } + else { + im_errormsg( "im_affine: unknown coding type" ); + return( -1 ); + } + + /* Finally: can now set Xoffset/Yoffset. + */ + out->Xoffset = trn->dx - trn->oarea.left; + out->Yoffset = trn->dy - trn->oarea.top; + + return( 0 ); +} + +int +im_affine( IMAGE *in, IMAGE *out, + double a, double b, double c, double d, double dx, double dy, + int ox, int oy, int ow, int oh ) +{ + Transformation trn; + + trn.oarea.left = ox; + trn.oarea.top = oy; + trn.oarea.width = ow; + trn.oarea.height = oh; + trn.iarea.left = 0; + trn.iarea.top = 0; + trn.iarea.width = in->Xsize; + trn.iarea.height = in->Ysize; + trn.a = a; + trn.b = b; + trn.c = c; + trn.d = d; + trn.dx = dx; + trn.dy = dy; + + return( im__affine( in, out, &trn ) ); +} diff --git a/libsrc/mosaicing/im_avgdxdy.c b/libsrc/mosaicing/im_avgdxdy.c new file mode 100644 index 00000000..e152d553 --- /dev/null +++ b/libsrc/mosaicing/im_avgdxdy.c @@ -0,0 +1,85 @@ +/* @(#) Function which averages the difference x_secondary[] - x_reference[] + * @(#) and y_secondary[] - y_reference[] of the structure points; + * @(#) The rounded integer result is returnd into dx, dy + * @(#) No IMAGES are involved in this function. + * @(#) + * @(#) int im_avgdxdy( points, dx, dy ) + * @(#) TIE_POINTS *points; + * @(#) int *dx, *dy; + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 20/12/1990 + * Modified on : 18/04/1991 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im__avgdxdy( TIE_POINTS *points, int *dx, int *dy ) +{ + int sumdx, sumdy; + int i; + + if( points->nopoints == 0 ) { + im_errormsg( "im_avgdxdy: no points to average" ); + return( -1 ); + } + + /* Lots of points. + */ + sumdx = 0; + sumdy = 0; + for( i = 0; i < points->nopoints; i++ ) { + sumdx += points->x_secondary[i] - points->x_reference[i]; + sumdy += points->y_secondary[i] - points->y_reference[i]; + } + + *dx = IM_RINT( (double) sumdx / (double) points->nopoints ); + *dy = IM_RINT( (double) sumdy / (double) points->nopoints ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_chkpair.c b/libsrc/mosaicing/im_chkpair.c new file mode 100644 index 00000000..13547a87 --- /dev/null +++ b/libsrc/mosaicing/im_chkpair.c @@ -0,0 +1,241 @@ +/* @(#) Functions which improve the selection of two ttie points pairs in two + * @(#) images, by estimating the correlation coefficient given in page 426 + * @(#) 2nd edn of the book Digital Image processing Gonzalez and Wintz + * @(#) The function works as follows: + * @(#) It expects to receive nopoints pairs (coordinates) of points + * @(#) corresponding to ref and sec. + * @(#) The coordinates of the pairs are in arrays x1,y1 and x2,y2 + * @(#) After that the program reads a region of 2*halfcorsize +1 pels centered + * @(#) at point (x1, y1) and looks around + * @(#) an area 2*halfareasize+1 centered at point (x2, y2). + * @(#) For each point in this 2*halfareasize+1, + * @(#) the program reads the corresponding + * @(#) image2 values in a region of 2*halfcorsize+1 pels centered at this point + * @(#) and calculates the corresponding correlation coefficients. + * @(#) The result is stored in a the array + * @(#) corcoef[(2*halfareasize+1)(2*halfareasize+1)]. Within this window, the + * @(#) max correlation coefficient is estimated and its corresponding + * @(#) (x, y) coordinates are returned in (x2, y2). + * @(#) The purpose of this function is to improve the selection of + * @(#) control points entered in (x1, y1) + * @(#) Both input images should are either memory mapped or in a buffer. + * @(#) The variable bandno should be between 1 and ref->Bands + * @(#) The program fills the dx[] and dy[] arrays before returning. + * @(#) + * @(#) int im__chkpair( ref, sec, bandno, points ) + * @(#) IMAGE *ref, *sec; + * @(#) int bandno; + * @(#) TIE_POINTS *points; + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on : 18/04/1991 + * 8/7/93 JC + * - allows IM_CODING_LABQ coding + * - now calls im_incheck() + * 13/7/95 JC + * - rewritten + * - now uses im_spcor() + * 13/8/96 JC + * - order of args changed to help C++ API + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Find position of sec within ref. Search around point xsec, ysec for the + * best match for the area around xref, yref. Search an area of size + * hsearchsize for an of size hwindowsize. + * + * Return a new value for xsec, ysec and the correlation at that point. + * + * Also used by im_match_linear(), im_match_linear_search(), etc. + */ +int +im_correl( IMAGE *ref, IMAGE *sec, + int xref, int yref, int xsec, int ysec, + int hwindowsize, int hsearchsize, + double *correlation, int *x, int *y ) +{ + IMAGE *surface = im_open( "surface", "t" ); + IMAGE *t1, *t2, *t3, *t4; + + Rect refr, secr; + Rect winr, srhr; + Rect wincr, srhcr; + + if( !surface || + !(t1 = im_open_local( surface, "correlate:1", "p" )) || + !(t2 = im_open_local( surface, "correlate:1", "p" )) || + !(t3 = im_open_local( surface, "correlate:1", "p" )) || + !(t4 = im_open_local( surface, "correlate:1", "p" )) ) + return( -1 ); + + /* Find position of window and search area, and clip against image + * size. + */ + refr.left = 0; + refr.top = 0; + refr.width = ref->Xsize; + refr.height = ref->Ysize; + winr.left = xref - hwindowsize; + winr.top = yref - hwindowsize; + winr.width = hwindowsize*2 + 1; + winr.height = hwindowsize*2 + 1; + im_rect_intersectrect( &refr, &winr, &wincr ); + + secr.left = 0; + secr.top = 0; + secr.width = sec->Xsize; + secr.height = sec->Ysize; + srhr.left = xsec - hsearchsize; + srhr.top = ysec - hsearchsize; + srhr.width = hsearchsize*2 + 1; + srhr.height = hsearchsize*2 + 1; + im_rect_intersectrect( &secr, &srhr, &srhcr ); + + /* Extract window and search area. + */ + if( im_extract_area( ref, t1, + wincr.left, wincr.top, wincr.width, wincr.height ) || + im_extract_area( sec, t2, + srhcr.left, srhcr.top, srhcr.width, srhcr.height ) ) { + im_close( surface ); + return( -1 ); + } + + /* Make sure we have just one band. From im_*mosaic() we will, but + * from im_match_linear_search() etc. we may not. + */ + if( t1->Bands != 1 ) { + if( im_extract_band( t1, t3, 0 ) ) { + im_close( surface ); + return( -1 ); + } + t1 = t3; + } + if( t2->Bands != 1 ) { + if( im_extract_band( t2, t4, 0 ) ) { + im_close( surface ); + return( -1 ); + } + t2 = t4; + } + + /* Search! + */ + if( im_spcor( t2, t1, surface ) ) { + im_close( surface ); + return( -1 ); + } + + /* Find maximum of correlation surface. + */ + if( im_maxpos( surface, x, y, correlation ) ) { + im_close( surface ); + return( -1 ); + } + im_close( surface ); + + /* Translate back to position within sec. + */ + *x += srhcr.left; + *y += srhcr.top; + + return( 0 ); +} + +int +im__chkpair( IMAGE *ref, IMAGE *sec, TIE_POINTS *points ) +{ + int i; + int x, y; + double correlation; + + const int hcor = points->halfcorsize; + const int harea = points->halfareasize; + + /* Check images. + */ + if( im_incheck( ref ) || im_incheck( sec ) ) + return( -1 ); + if( ref->Bands != sec->Bands || ref->BandFmt != sec->BandFmt || + ref->Coding != sec->Coding ) { + im_errormsg( "im_chkpair: inputs incompatible"); + return( -1 ); + } + if( ref->Bands != 1 || ref->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im_chkpair: help!" ); + return( -1 ); + } + + for( i = 0; i < points->nopoints; i++ ) { + /* Find correlation point. + */ + if( im_correl( ref, sec, + points->x_reference[i], points->y_reference[i], + points->x_reference[i], points->y_reference[i], + hcor, harea, + &correlation, &x, &y ) ) + return( -1 ); + + /* And note in x_secondary. + */ + points->x_secondary[i] = x; + points->y_secondary[i] = y; + points->correlation[i] = correlation; + + /* Note each dx, dy too. + */ + points->dx[i] = + points->x_secondary[i] - points->x_reference[i]; + points->dy[i] = + points->y_secondary[i] - points->y_reference[i]; + } + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_clinear.c b/libsrc/mosaicing/im_clinear.c new file mode 100644 index 00000000..0f180da7 --- /dev/null +++ b/libsrc/mosaicing/im_clinear.c @@ -0,0 +1,183 @@ +/* @(#) Function which calculates the coefficients between corresponding + * @(#) points from reference and secondary images (probably from the scanner), + * @(#) previously calculated using the functions im_calcon() and im_chpair() + * @(#) It is assummed that a selection of the best(?) possible points has + * @(#) been already carried out and that those nopoints points are in arrays + * @(#) x1, y1 and x2, y2 + * @(#) No IMAGES are involved in this function and the calculated parameters + * @(#) are returned in scale angle deltax and deltay of the TIE_POINTS struct. + * @(#) + * @(#) int im_clinear( points ) + * @(#) TIE_POINTS *points; + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 20/12/1990 + * Modified on : 18/04/1991 + * 24/1/97 JC + * - tiny mem leak fixed + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im__clinear( TIE_POINTS *points ) +{ + double **mat; /* matrix mar[4][4] */ + double *g; /* vector g[1][4] */ + double value; + double sx1=0.0, sx1x1=0.0, sy1=0.0, sy1y1=0.0, sx1y1 = 0.0; + double sx2x1=0.0, sx2y1=0.0, sx2=0.0, sy2=0.0, sy2y1=0.0, sy2x1=0.0; + + int i, j; + int elms; + double scale, angle, xdelta, ydelta; + int *xref, *yref, *xsec, *ysec; + double *dx, *dy, *dev; + double resx, resy; + + xref = &points->x_reference[0]; + yref = &points->y_reference[0]; + xsec = &points->x_secondary[0]; + ysec = &points->y_secondary[0]; + dx = &points->dx[0]; + dy = &points->dy[0]; + dev = &points->deviation[0]; + elms = points->nopoints; + + if( !(mat = im_dmat_alloc( 0, 3, 0, 3 )) ) + return( -1 ); + if( !(g = im_dvector( 0, 3 )) ) { + im_free_dmat( mat, 0, 3, 0, 3 ); + return( -1 ); + } + + resx = 0.0; + resy = 0.0; + for( i = 0; i < points->nopoints; i++ ) { + sx1 += xref[i]; + sx1x1 += xref[i] * xref[i]; + sy1 += yref[i]; + sy1y1 += yref[i] * yref[i]; + sx1y1 += xref[i] * yref[i]; + sx2x1 += xsec[i] * xref[i]; + sx2y1 += xsec[i] * yref[i]; + sy2y1 += ysec[i] * yref[i]; + sy2x1 += ysec[i] * xref[i]; + sx2 += xsec[i]; + sy2 += ysec[i]; + } + + resx = fabs( sx1-sx2 )/points->nopoints; + resy = fabs( sy1-sy2 )/points->nopoints; + + mat[0][0] = sx1x1 + sy1y1; + mat[0][1] = 0; + mat[0][2] = sx1; + mat[0][3] = sy1; + + mat[1][0] = 0; + mat[1][1] = sx1x1 + sy1y1; + mat[1][2] = -sy1; + mat[1][3] = sx1; + + mat[2][0] = sx1; + mat[2][1] = -sy1; + mat[2][2] = (double)elms; + mat[2][3] = 0.0; + + mat[3][0] = sy1; + mat[3][1] = sx1; + mat[3][2] = 0.0; + mat[3][3] = (double)elms; + + g[0] = sx2x1 + sy2y1; + g[1] = -sx2y1 + sy2x1; + g[2] = sx2; + g[3] = sy2; + + if( im_invmat( mat, 4 ) ) { + im_free_dmat( mat, 0, 3, 0, 3 ); + im_free_dvector( g, 0, 3 ); + im_errormsg( "im_clinear: im_invmat failed" ); + return( -1 ); + } + + scale = 0.0; angle = 0.0; + xdelta = 0.0; ydelta = 0.0; + + for( j = 0; j < 4; j++ ) { + scale += mat[0][j] * g[j]; + angle += mat[1][j] * g[j]; + xdelta += mat[2][j] * g[j]; + ydelta += mat[3][j] * g[j]; + } + + /* find the deviation of each point for the estimated variables + * if it greater than 1 then the solution is not good enough + * but this is handled by the main program + */ + for( i = 0; i < points->nopoints; i++ ) { + dx[i] = xsec[i] - + ((scale * xref[i]) - (angle * yref[i]) + xdelta); + + dy[i] = ysec[i] - + ((angle * xref[i]) + (scale * yref[i]) + ydelta); + + value = sqrt( dx[i]*dx[i] + dy[i]*dy[i] ); + dev[i] = value; + } + + points->l_scale = scale; + points->l_angle = angle; + points->l_deltax = xdelta; + points->l_deltay = ydelta; + + im_free_dmat( mat, 0, 3, 0, 3 ); + im_free_dvector( g, 0, 3 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_improve.c b/libsrc/mosaicing/im_improve.c new file mode 100644 index 00000000..3b1ca9a7 --- /dev/null +++ b/libsrc/mosaicing/im_improve.c @@ -0,0 +1,193 @@ +/* @(#) Function which improves the selection of tiepoints carried out by + * @(#) im_clinear() until no points have deviation greater than 1 pixel + * @(#) No reference or secondary images are involved + * @(#) Function im_improve assumes that im_clinear has been applied on points + * @(#) No IMAGES are involved in this function and the result is + * @(#) returned in outpoints which is declared as a pointer in the + * @(#) calling routine. Space for outpoints should be allocated in the calling + * @(#) routine + * @(#) + * @(#) int im_improve( inpoints, outpoints ) + * @(#) TIE_POINTS *inpoints, *outpoints; + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 20/12/1990 + * Modified on : 18/04/1991 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static void +copypoints( TIE_POINTS *pnew, TIE_POINTS *pold ) +{ + int i; + + pnew->reference = pold->reference; + pnew->secondary = pold->secondary; + + pnew->deltax = pold->deltax; + pnew->deltay = pold->deltay; + pnew->nopoints = pold->nopoints; + pnew->halfcorsize = pold->halfcorsize; + pnew->halfareasize = pold->halfareasize; + + for( i = 0; i < pold->nopoints; i++ ) { + pnew->x_reference[i] = pold->x_reference[i]; + pnew->y_reference[i] = pold->y_reference[i]; + pnew->x_secondary[i] = pold->x_secondary[i]; + pnew->y_secondary[i] = pold->y_secondary[i]; + pnew->contrast[i] = pold->contrast[i]; + pnew->correlation[i] = pold->correlation[i]; + pnew->deviation[i] = pold->deviation[i]; + pnew->dx[i] = pold->dx[i]; + pnew->dy[i] = pold->dy[i]; + } + + pnew->l_scale = pold->l_scale; + pnew->l_angle = pold->l_angle; + pnew->l_deltax = pold->l_deltax; + pnew->l_deltay = pold->l_deltay; +} + +/* exclude all points with deviation greater or equal to 1.0 pixel + */ +static int +copydevpoints( TIE_POINTS *pnew, TIE_POINTS *pold ) +{ + int i; + int j; + double thresh_dev,max_dev, min_dev; + double *corr; + + min_dev = 9999.0; + max_dev = 0.0; + corr = &pold->correlation[0]; + + for( i = 0; i < pold->nopoints; i++ ) + if( corr[i] > 0.01 ) { + if( pold->deviation[i]/corr[i] < min_dev ) + min_dev = pold->deviation[i]/corr[i] ; + if( pold->deviation[i]/corr[i] > max_dev ) + max_dev = pold->deviation[i]/corr[i]; + } + + thresh_dev = min_dev + (max_dev - min_dev)*0.3; + if( thresh_dev <= 1.0 ) + thresh_dev = 1.0; + + for( i = 0, j = 0; i < pold->nopoints; i++ ) + if( pold->correlation[i] > 0.01 ) + if( pold->deviation[i]/corr[i] <= thresh_dev ) { + pnew->x_reference[j] = pold->x_reference[i]; + pnew->y_reference[j] = pold->y_reference[i]; + pnew->x_secondary[j] = pold->x_secondary[i]; + pnew->y_secondary[j] = pold->y_secondary[i]; + pnew->contrast[j] = pold->contrast[i]; + pnew->correlation[j] = pold->correlation[i]; + pnew->deviation[j] = pold->deviation[i]; + pnew->dx[j] = pold->dx[i]; + pnew->dy[j] = pold->dy[i]; + j++; + } + pnew->nopoints = j; + + for( i = j; i < IM_MAXPOINTS; i++ ) { + pnew->x_reference[i] = 0; + pnew->y_reference[i] = 0; + pnew->x_secondary[i] = 0; + pnew->y_secondary[i] = 0; + pnew->contrast[i] = 0; + pnew->correlation[i] = 0.0; + pnew->deviation[i] = 0.0; + pnew->dx[i] = 0.0; + pnew->dy[i] = 0.0; + } + + /* Return non-zero if we changed something. + */ + if( j != pold->nopoints ) + return( -1 ); + + return( 0 ); +} + +#define SWAP( A, B ) { void *t = (A); A = B; B = t; } + +int +im__improve( TIE_POINTS *inpoints, TIE_POINTS *outpoints ) +{ + TIE_POINTS points1, points2; + TIE_POINTS *p = &points1; + TIE_POINTS *q = &points2; + + /* p has the current state - make a new state, q, with only those + * points which have a small deviation. + */ + for( copypoints( p, inpoints ); + copypoints( q, p ), copydevpoints( q, p ); ) { + /* If there are only a few left, jump out. + */ + if( q->nopoints < 2 ) + break; + + /* Fit the model to the new set of points. + */ + if( im__clinear( q ) ) + return( -1 ); + + /* And loop. + */ + SWAP( p, q ); + } + + /* q has the output - copy to outpoints. + */ + copypoints( outpoints, q ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_initialize.c b/libsrc/mosaicing/im_initialize.c new file mode 100644 index 00000000..8ab66af0 --- /dev/null +++ b/libsrc/mosaicing/im_initialize.c @@ -0,0 +1,100 @@ +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im__initialize( TIE_POINTS *points ) +{ + if( im__clinear( points ) ) { + /* im_clinear failed! Set some sensible fallback values. + */ + int i, j; + double xdelta, ydelta, max_cor; + double a1, a2; + + int *xref = &points->x_reference[0]; + int *yref = &points->y_reference[0]; + int *xsec = &points->x_secondary[0]; + int *ysec = &points->y_secondary[0]; + + double *corr = &points->correlation[0]; + double *dx = &points->dx[0]; + double *dy = &points->dy[0]; + + int npt = points->nopoints; + + max_cor = 0.0; + for( i = 0; i < npt; i++ ) + if( corr[i] > max_cor ) + max_cor = corr[i]; + + max_cor = max_cor - 0.04; + xdelta = 0.0; + ydelta = 0.0; + j = 0; + for( i = 0; i < npt; i++ ) + if( corr[i] >= max_cor ) { + xdelta += xsec[i] - xref[i]; + ydelta += ysec[i] - yref[i]; + ++j; + } + + xdelta = xdelta/j; + ydelta = ydelta/j; + for(i = 0; i < npt; i++ ) { + dx[i] = (xsec[i] - xref[i]) - xdelta; + dy[i] = (ysec[i] - yref[i]) - ydelta; + } + + for( i = 0; i < npt; i++ ) { + a1 = dx[i]; + a2 = dy[i]; + points->deviation[i] = sqrt( a1*a1 + a2*a2 ); + } + + points->l_scale = 1.0; + points->l_angle = 0.0; + points->l_deltax = xdelta; + points->l_deltay = ydelta; + } + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_lrcalcon.c b/libsrc/mosaicing/im_lrcalcon.c new file mode 100644 index 00000000..368e0aeb --- /dev/null +++ b/libsrc/mosaicing/im_lrcalcon.c @@ -0,0 +1,318 @@ +/* @(#) Functions which takes an initial estimate of deltax, deltay + * @(#) between reference and secondary images (probably from the scanner), + * @(#) and looks in three areas of the overlapping part of the reference image + * @(#) corresponding to reference and secondary. For every other halfreasize + * @(#) point of the three areas of the reference image + * @(#) the contrast is calculated + * @(#) an area 2*halfcorsize+1 centered at this point + * @(#) Results are saved in the structure points + * @(#) The function expects the following valid data in points: + * @(#) deltax, deltay, nopoints, halfcorsize, halfareasize + * @(#) and fills in the memebers: + * @(#) x, y_reference[], contrast and x,y_secondary[], + * @(#) based on deltax and deltay + * @(#) Input image should are either memory mapped or in a buffer. + * @(#) The initial setting checks all points of reference + * @(#) in the overlapping area of the images to be mosaiced + * @(#) To speed up the procedure the ysize of the box can be reduced + * @(#) during the calculation of the ysize + * @(#) An easy way is to change FACTOR to 1 2 or 3. + * @(#) The calculation of the contrast is carried out based on bandno only. + * @(#) The variable bandno should be between 1 and ref->Bands + * @(#) + * @(#) int im_lrcalcon( ref, sec, bandno, points ) + * @(#) IMAGE *ref, *sec; + * @(#) int bandno; + * @(#) TIE_POINTS *points; see mosaic.h + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 20/12/1990 + * Modified on : 18/04/1991 + * 8/7/93 JC + * - now calls im_incheck() + * 12/7/95 JC + * - reworked + * - what a lot of horrible old code there was too + * 24/1/97 JC + * - now ignores black stuff (all bands zero) when selecting possible tie + * points, part of new mosaic policy + * 26/9/97 JC + * - now skips all-black windows, instead of any-black windows + * 11/4/01 JC + * - ooops, < 0 should have been <= 0 + * 10/3/03 JC + * - better error message for overlap too small + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* A position and contrast. + */ +typedef struct { + int x, y; + int cont; +} PosCont; + +/* Search a window for black pelss ... true if window is all black. + * One-band uchar only. + */ +static int +all_black( IMAGE *im, int xpos, int ypos, int winsize ) +{ + const int hwinsize = (winsize - 1)/2; + const int left = xpos - hwinsize; + const int top = ypos - hwinsize; + const int ls = im->Xsize; + + int x, y; + PEL *line; + + /* Loop over image. + */ + line = (PEL *) im->data + top*ls + left; + for( y = 0; y < winsize; y++ ) { + for( x = 0; x < winsize; x++ ) + if( line[x] ) + /* Not all black. + */ + return( 0 ); + + line += ls; + } + + return( -1 ); +} + +/* Calculate a value for 'contrast' within a window + * of (odd) size winsize*winsize centered at location (xpos, ypos). + * One band uchar only, + */ +static int +calculate_contrast( IMAGE *im, int xpos, int ypos, int winsize ) +{ + const int hwinsize = (winsize - 1)/2; + const int left = xpos - hwinsize; + const int top = ypos - hwinsize; + const int ls = im->Xsize; + + int x, y; + PEL *line, *p; + int total; + + line = (PEL *) im->data + top*ls + left; + for( total = 0, y = 0; y < winsize-1; y++ ) { + p = line; + + for( x = 0; x < winsize-1; x++ ) { + const int lrd = (int) p[0] - p[1]; + const int tbd = (int) p[0] - p[ls]; + + total += abs( lrd ) + abs( tbd ); + p += 1; + } + + line += ls; + } + + return( total ); +} + +/* Compare two PosConts for qsort. + */ +static int +pos_compare( const void *vl, const void *vr ) +{ + PosCont *l = (PosCont *) vl; + PosCont *r = (PosCont *) vr; + + return( r->cont - l->cont ); +} + +/* Search an area for the n best contrast areas. + */ +int +im__find_best_contrast( IMAGE *im, + int xpos, int ypos, int xsize, int ysize, + int xarray[], int yarray[], int cont[], + int nbest, int hcorsize ) +{ + /* Geometry: we test squares of size windowsize, overlapping by + * hcorsize. + */ + const int windowsize = 2 * hcorsize + 1; + + /* Number of squares we can fit in area. + */ + const int nacross = (xsize - windowsize + hcorsize) / hcorsize; + const int ndown = (ysize - windowsize + hcorsize) / hcorsize; + + /* Number of squares we search. + */ + int elms; + + /* All points in this area. + */ + PosCont *pc; + + int x, y, i; + + if( nacross <= 0 || ndown <= 0 ) { + im_errormsg( "im__lrcalcon: mosaicing overlap too small for " + "your search size" ); + return( -1 ); + } + + /* Malloc space for 3 int arrays, to keep the int coordinates and + * the contrast. + */ + if( !(pc = IM_ARRAY( NULL, nacross * ndown, PosCont )) ) + return( -1 ); + + /* Find contrast for each area. + */ + for( i = 0, y = 0; y < ndown; y++ ) + for( x = 0; x < nacross; x++ ) { + const int left = xpos + x * hcorsize; + const int top = ypos + y * hcorsize; + + /* Skip this position if it is all black. + */ + if( all_black( im, left, top, windowsize ) ) + continue; + + /* Find contrast and note. + */ + pc[i].x = left; + pc[i].y = top; + pc[i].cont = calculate_contrast( im, + left, top, windowsize ); + i++; + } + + /* Note number found. + */ + elms = i; + + /* Found enough tie-points? + */ + if( elms < nbest ) { + im_errormsg( "im_mosaic: found %d tie-points, need at least %d", + elms, nbest ); + im_free( pc ); + return( -1 ); + } + + /* Sort areas by contrast. + */ + qsort( pc, elms, sizeof( PosCont ), pos_compare ); + + /* Copy the n best into our parent. + */ + for( i = 0; i < nbest; i++ ) { + xarray[i] = pc[i].x; + yarray[i] = pc[i].y; + cont[i] = pc[i].cont; + } + im_free( pc ); + + return( 0 ); +} + +int +im__lrcalcon( IMAGE *ref, TIE_POINTS *points ) +{ + /* Geometry: border we must leave around each area. + */ + const int border = points->halfareasize; + + /* Height of an area. + */ + const int aheight = ref->Ysize / AREAS; + + /* Number of points we find in each area. + */ + const int len = points->nopoints / AREAS; + + int i; + Rect area; + + /* Make sure we can read image. + */ + if( im_incheck( ref ) ) + return( -1 ); + if( ref->Bands != 1 || ref->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im__lrcalcon: not 1-band uchar image" ); + return( -1 ); + } + + /* Define bits to search for high-contrast areas. Need to be able to + * fit at least 1 window in. + */ + area.height = aheight; + area.width = ref->Xsize; + area.left = 0; + area.top = 0; + im_rect_marginadjust( &area, -border ); + area.width--; + area.height--; + + /* Loop over areas, finding points. + */ + for( i = 0; area.top < ref->Ysize; area.top += aheight, i++ ) + if( im__find_best_contrast( ref, + area.left, area.top, area.width, area.height, + points->x_reference + i*len, + points->y_reference + i*len, + points->contrast + i*len, + len, + points->halfcorsize ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_lrmerge.c b/libsrc/mosaicing/im_lrmerge.c new file mode 100644 index 00000000..a9c6901b --- /dev/null +++ b/libsrc/mosaicing/im_lrmerge.c @@ -0,0 +1,1128 @@ +/* Merge two images left-right. dx, dy is the offset needed to get from sec + * (secondary image) to ref (reference image). + * + * Usage: + * + * int + * im_lrmerge( ref, sec, out, dx, dy ) + * IMAGE *ref, *sec, *out; + * int dx, dy; + * + * Returns 0 on success and -1 on error + * + * Copyright: 1990, 1991 N. Dessipris + * Author: N. Dessipris + * Written on: 20/09/1990 + * Updated on: 17/04/1991 + * 1/6/92: JC + * - check for difference bug fixed + * - geometry calculations improved and simplified + * - small speedups + Kirk Martinez for Sys5 29/4/93 + * 7/8/93 JC + * - ANSIfied + * - memory leaks fixed, ready for partial v2 + * - now does IM_CODING_LABQ too + * 8/11/93 JC + * - now propogates both input histories + * - adds magic lines for global mosaic optimisation + * + * + * + May/1994 Ahmed Abbood + * + * - Modified to use partials on all IO + * + June/1995 Ahmed Abbood + * + * - Modified to work with different types of images. + * + * 16/6/95 JC + * - tidied up a little + * - added to VIPS! + * 7/9/95 JC + * - split into two parts: im_lrmerge() and im__lrmerge() + * - latter called by im_lrmosaic() + * - just the same as public im_lrmerge(), but adds no history + * - necessary for im_global_balance() + * - small bugs fixed + * 10/10/95 JC + * - better checks that parameters are sensible + * 11/10/95 JC + * - Kirk spotted what a load of rubbish Ahmed's code is + * - rewritten - many, many bugs fixed + * 24/1/97 JC + * - now outputs bounding area of input images, rather than clipping + * - ignores 0 pixels in blend + * - small tidies + * 7/2/97 JC + * - new blend, caching + * 25/2/97 JC + * - old blend back, much simpler + * - speed this up at some point if you think of an easy way to do it + * 29/7/97 JC + * - IM_CODING_LABQ blend now works, was bug in im_wrapone() + * - small tidies + * 10/1/98 JC + * - merge LUTs now shared between all running mergers + * - frees memory explicitly in im__stop_merge, for much better memory + * use in large mosaics, huge improvement! + * 18/2/98 JC + * - im_demand_hint() call added + * 19/2/98 JC + * - now works for any dx/dy by calling im_insert() for bizarre cases + * 26/9/99 JC + * - ooops, blend lut was wrong! wonder how long that's been broken, + * since feb97 I guess + * 2/2/01 JC + * - added tunable max blend width + * 8/3/01 JC + * - switched to integer arithmetic for integer blends + * 7/11/01 JC + * - more sophisticated transparency handling + * - tiny blend speed up + * 19/3/02 JC + * - move fl cache to main state for better sharing + * 15/8/02 JC + * - records Xoffset/Yoffset + * 20/6/05 + * - now requires all bands == 0 for transparency (used to just check + * band 0) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +/* +#define DEBUG + */ + +#include +#include + +#include "merge.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Blend luts. Shared between all lr and tb blends. + */ +double *im__coef1 = NULL; +double *im__coef2 = NULL; +int *im__icoef1 = NULL; +int *im__icoef2 = NULL; + +/* Create a lut for the merging area. Always BLEND_SIZE entries, we + * scale later when we index it. + */ +int +im__make_blend_luts() +{ + int x; + + /* Already done? + */ + if( im__coef1 && im__coef2 ) + return( 0 ); + + /* Allocate and fill. + */ + im__coef1 = IM_ARRAY( NULL, BLEND_SIZE, double ); + im__coef2 = IM_ARRAY( NULL, BLEND_SIZE, double ); + im__icoef1 = IM_ARRAY( NULL, BLEND_SIZE, int ); + im__icoef2 = IM_ARRAY( NULL, BLEND_SIZE, int ); + if( !im__coef1 || !im__coef2 || !im__icoef1 || !im__icoef2 ) + return( -1 ); + + for( x = 0; x < BLEND_SIZE; x++ ) { + double a = IM_PI * x / (BLEND_SIZE - 1.0); + + im__coef1[x] = (cos( a ) + 1.0) / 2.0; + im__coef2[x] = 1.0 - im__coef1[x]; + im__icoef1[x] = im__coef1[x] * BLEND_SCALE; + im__icoef2[x] = im__coef2[x] * BLEND_SCALE; + } + + return( 0 ); +} + +/* Return the position of the first non-zero pel from the left. + */ +static int +find_first( REGION *ir, int *pos, int x, int y, int w ) +{ + PEL *pr = (PEL *) IM_REGION_ADDR( ir, x, y ); + IMAGE *im = ir->im; + int ne = w * im->Bands; + int i; + + /* Double the number of bands in a complex. + */ + if( im_iscomplex( im ) ) + ne *= 2; + +/* Search for the first non-zero band element from the left edge of the image. + */ +#define lsearch( TYPE ) { \ + TYPE *p = (TYPE *) pr; \ + \ + for( i = 0; i < ne; i++ ) \ + if( p[i] )\ + break;\ +} + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: lsearch( unsigned char ); break; + case IM_BANDFMT_CHAR: lsearch( signed char ); break; + case IM_BANDFMT_USHORT: lsearch( unsigned short ); break; + case IM_BANDFMT_SHORT: lsearch( signed short ); break; + case IM_BANDFMT_UINT: lsearch( unsigned int ); break; + case IM_BANDFMT_INT: lsearch( signed int ); break; + case IM_BANDFMT_FLOAT: lsearch( float ); break; + case IM_BANDFMT_DOUBLE: lsearch( double ); break; + case IM_BANDFMT_COMPLEX:lsearch( float ); break; + case IM_BANDFMT_DPCOMPLEX:lsearch( double ); break; + + default: + im_errormsg( "im_lrmerge: internal error" ); + return( -1 ); + } + + /* i is first non-zero band element, we want first non-zero pixel. + */ + *pos = x + i / im->Bands; + + return( 0 ); +} + +/* Return the position of the first non-zero pel from the right. + */ +static int +find_last( REGION *ir, int *pos, int x, int y, int w ) +{ + PEL *pr = (PEL *) IM_REGION_ADDR( ir, x, y ); + IMAGE *im = ir->im; + int ne = w * im->Bands; + int i; + + /* Double the number of bands in a complex. + */ + if( im_iscomplex( im ) ) + ne *= 2; + +/* Search for the first non-zero band element from the right. + */ +#define rsearch( TYPE ) { \ + TYPE *p = (TYPE *) pr; \ + \ + for( i = ne - 1; i >= 0; i-- )\ + if( p[i] )\ + break;\ +} + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: rsearch( unsigned char ); break; + case IM_BANDFMT_CHAR: rsearch( signed char ); break; + case IM_BANDFMT_USHORT: rsearch( unsigned short ); break; + case IM_BANDFMT_SHORT: rsearch( signed short ); break; + case IM_BANDFMT_UINT: rsearch( unsigned int ); break; + case IM_BANDFMT_INT: rsearch( signed int ); break; + case IM_BANDFMT_FLOAT: rsearch( float ); break; + case IM_BANDFMT_DOUBLE: rsearch( double ); break; + case IM_BANDFMT_COMPLEX:rsearch( float ); break; + case IM_BANDFMT_DPCOMPLEX:rsearch( double ); break; + + default: + im_errormsg( "im_lrmerge: internal error" ); + return( -1 ); + } + + /* i is first non-zero band element, we want first non-zero pixel. + */ + *pos = x + i / im->Bands; + + return( 0 ); +} + +/* Make sure we have first/last for this area. + */ +static int +make_firstlast( MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) +{ + REGION *rir = inf->rir; + REGION *sir = inf->sir; + Rect rr, sr; + int y, yr, ys; + int missing; + + /* We're going to build first/last ... lock it from other generate + * threads. In fact it's harmless if we do get two writers, but we may + * avoid duplicating work. + */ + g_mutex_lock( ovlap->fl_lock ); + + /* Do we already have first/last for this area? Bail out if we do. + */ + missing = 0; + for( y = oreg->top; y < IM_RECT_BOTTOM( oreg ); y++ ) { + const int j = y - ovlap->overlap.top; + const int first = ovlap->first[j]; + + if( first < 0 ) { + missing = 1; + break; + } + } + if( !missing ) { + /* No work to do! + */ + g_mutex_unlock( ovlap->fl_lock ); + return( 0 ); + } + + /* Entire width of overlap in ref for scan-lines we want. + */ + rr.left = ovlap->overlap.left; + rr.top = oreg->top; + rr.width = ovlap->overlap.width; + rr.height = oreg->height; + rr.left -= ovlap->rarea.left; + rr.top -= ovlap->rarea.top; + + /* Entire width of overlap in sec for scan-lines we want. + */ + sr.left = ovlap->overlap.left; + sr.top = oreg->top; + sr.width = ovlap->overlap.width; + sr.height = oreg->height; + sr.left -= ovlap->sarea.left; + sr.top -= ovlap->sarea.top; + +#ifdef DEBUG + printf( "im__lrmerge: making first/last for areas:\n" ); + printf( "ref: left = %d, top = %d, width = %d, height = %d\n", + rr.left, rr.top, rr.width, rr.height ); + printf( "sec: left = %d, top = %d, width = %d, height = %d\n", + sr.left, sr.top, sr.width, sr.height ); +#endif + + /* Make pixels. + */ + if( im_prepare( rir, &rr ) || im_prepare( sir, &sr ) ) { + g_mutex_unlock( ovlap->fl_lock ); + return( -1 ); + } + + /* Make first/last cache. + */ + for( y = oreg->top, yr = rr.top, ys = sr.top; + y < IM_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) { + const int j = y - ovlap->overlap.top; + int *first = &ovlap->first[j]; + int *last = &ovlap->last[j]; + + /* Done this line already? + */ + if( *first < 0 ) { + /* Search for start/end of overlap on this scan-line. + */ + if( find_first( sir, first, + sr.left, ys, sr.width ) || + find_last( rir, last, + rr.left, yr, rr.width ) ) { + g_mutex_unlock( ovlap->fl_lock ); + return( -1 ); + } + + /* Translate to output space. + */ + *first += ovlap->sarea.left; + *last += ovlap->rarea.left; + + /* Clip to maximum blend width, if necessary. + */ + if( ovlap->mwidth >= 0 && + *last - *first > ovlap->mwidth ) { + int shrinkby = (*last - *first) - ovlap->mwidth; + + *first += shrinkby / 2; + *last -= shrinkby / 2; + } + } + } + + g_mutex_unlock( ovlap->fl_lock ); + + return( 0 ); +} + +/* Test pixel == 0. + */ +#define TEST_ZERO( TYPE, T, RESULT ) { \ + TYPE *tt = (T); \ + int ii; \ + \ + for( ii = 0; ii < cb; ii++ ) \ + if( tt[i] ) \ + break; \ + if( ii == cb ) \ + (RESULT) = 1; \ +} + +/* Blend two integer images. + */ +#define iblend( TYPE, B, IN1, IN2, OUT ) { \ + TYPE *tr = (TYPE *) (IN1); \ + TYPE *ts = (TYPE *) (IN2); \ + TYPE *tq = (TYPE *) (OUT); \ + const int cb = (B); \ + const int left = IM_CLIP( 0, first - oreg->left, oreg->width ); \ + const int right = IM_CLIP( left, last - oreg->left, oreg->width ); \ + int ref_zero; \ + int sec_zero; \ + int x, b; \ + int i; \ + \ + /* Left of the blend area. \ + */ \ + for( i = 0, x = 0; x < left; x++ ) { \ + ref_zero = 0; \ + TEST_ZERO( TYPE, tr, ref_zero ); \ + if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + \ + /* In blend area. \ + */ \ + for( x = left; x < right; x++ ) { \ + ref_zero = 0; \ + sec_zero = 0; \ + TEST_ZERO( TYPE, tr, ref_zero ); \ + TEST_ZERO( TYPE, ts, sec_zero ); \ + \ + if( !ref_zero && !sec_zero ) { \ + int inx = ((x + oreg->left - first) << \ + BLEND_SHIFT) / bwidth; \ + int c1 = im__icoef1[inx]; \ + int c2 = im__icoef2[inx]; \ + \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = c1 * tr[i] / BLEND_SCALE + \ + c2 * ts[i] / BLEND_SCALE; \ + } \ + else if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + \ + /* Right of blend. + */ \ + for( x = right; x < oreg->width; x++ ) { \ + sec_zero = 0; \ + TEST_ZERO( TYPE, ts, sec_zero ); \ + if( !sec_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + } \ +} + +/* Blend two float images. + */ +#define fblend( TYPE, B, IN1, IN2, OUT ) { \ + TYPE *tr = (TYPE *) (IN1); \ + TYPE *ts = (TYPE *) (IN2); \ + TYPE *tq = (TYPE *) (OUT); \ + const int cb = (B); \ + const int left = IM_CLIP( 0, first - oreg->left, oreg->width ); \ + const int right = IM_CLIP( left, last - oreg->left, oreg->width ); \ + int ref_zero; \ + int sec_zero; \ + int x, b; \ + int i; \ + \ + /* Left of the blend area. \ + */ \ + for( i = 0, x = 0; x < left; x++ ) { \ + ref_zero = 0; \ + TEST_ZERO( TYPE, tr, ref_zero ); \ + if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + \ + /* In blend area. \ + */ \ + for( x = left; x < right; x++ ) { \ + ref_zero = 0; \ + sec_zero = 0; \ + TEST_ZERO( TYPE, tr, ref_zero ); \ + TEST_ZERO( TYPE, ts, sec_zero ); \ + \ + if( !ref_zero && !sec_zero ) { \ + int inx = ((x + oreg->left - first) << \ + BLEND_SHIFT) / bwidth; \ + double c1 = im__coef1[inx]; \ + double c2 = im__coef2[inx]; \ + \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = c1 * tr[i] + c2 * ts[i]; \ + } \ + else if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + \ + /* Right of blend. + */ \ + for( x = right; x < oreg->width; x++ ) { \ + sec_zero = 0; \ + TEST_ZERO( TYPE, ts, sec_zero ); \ + if( !sec_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + } \ +} + +/* Left-right blend function for non-labpack images. + */ +static int +lr_blend( REGION *or, MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) +{ + REGION *rir = inf->rir; + REGION *sir = inf->sir; + IMAGE *im = or->im; + + Rect prr, psr; + int y, yr, ys; + + /* Make sure we have a complete first/last set for this area. + */ + if( make_firstlast( inf, ovlap, oreg ) ) + return( -1 ); + + /* Part of rr which we will output. + */ + prr = *oreg; + prr.left -= ovlap->rarea.left; + prr.top -= ovlap->rarea.top; + + /* Part of sr which we will output. + */ + psr = *oreg; + psr.left -= ovlap->sarea.left; + psr.top -= ovlap->sarea.top; + + /* Make pixels. + */ + if( im_prepare( rir, &prr ) ) + return( -1 ); + if( im_prepare( sir, &psr ) ) + return( -1 ); + + /* Loop down overlap area. + */ + for( y = oreg->top, yr = prr.top, ys = psr.top; + y < IM_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) { + PEL *pr = (PEL *) IM_REGION_ADDR( rir, prr.left, yr ); + PEL *ps = (PEL *) IM_REGION_ADDR( sir, psr.left, ys ); + PEL *q = (PEL *) IM_REGION_ADDR( or, oreg->left, y ); + + const int j = y - ovlap->overlap.top; + const int first = ovlap->first[j]; + const int last = ovlap->last[j]; + const int bwidth = last - first; + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + iblend( unsigned char, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_CHAR: + iblend( signed char, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_USHORT: + iblend( unsigned short, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_SHORT: + iblend( signed short, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_UINT: + iblend( unsigned int, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_INT: + iblend( signed int, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_FLOAT: + fblend( float, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_DOUBLE: + fblend( double, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_COMPLEX: + fblend( float, im->Bands*2, pr, ps, q ); break; + case IM_BANDFMT_DPCOMPLEX: + fblend( double, im->Bands*2, pr, ps, q ); break; + + default: + im_errormsg( "im_lrmerge: internal error" ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Left-right blend function for IM_CODING_LABQ images. + */ +static int +lr_blend_labpack( REGION *or, MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) +{ + REGION *rir = inf->rir; + REGION *sir = inf->sir; + Rect prr, psr; + int y, yr, ys; + + /* Make sure we have a complete first/last set for this area. This + * will just look at the top 8 bits of L, not all 10, but should be OK. + */ + if( make_firstlast( inf, ovlap, oreg ) ) + return( -1 ); + + /* Part of rr which we will output. + */ + prr = *oreg; + prr.left -= ovlap->rarea.left; + prr.top -= ovlap->rarea.top; + + /* Part of sr which we will output. + */ + psr = *oreg; + psr.left -= ovlap->sarea.left; + psr.top -= ovlap->sarea.top; + + /* Make pixels. + */ + if( im_prepare( rir, &prr ) ) + return( -1 ); + if( im_prepare( sir, &psr ) ) + return( -1 ); + + /* Loop down overlap area. + */ + for( y = oreg->top, yr = prr.top, ys = psr.top; + y < IM_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) { + PEL *pr = (PEL *) IM_REGION_ADDR( rir, prr.left, yr ); + PEL *ps = (PEL *) IM_REGION_ADDR( sir, psr.left, ys ); + PEL *q = (PEL *) IM_REGION_ADDR( or, oreg->left, y ); + + const int j = y - ovlap->overlap.top; + const int first = ovlap->first[j]; + const int last = ovlap->last[j]; + const int bwidth = last - first; + + float *fq = inf->merge; + float *r = inf->from1; + float *s = inf->from2; + + /* Unpack two bits we want. + */ + imb_LabQ2Lab( pr, r, oreg->width ); + imb_LabQ2Lab( ps, s, oreg->width ); + + /* Blend as floats. + */ + fblend( float, 3, r, s, fq ); + + /* Re-pack to output buffer. + */ + imb_Lab2LabQ( inf->merge, q, oreg->width ); + } + + return( 0 ); +} + +static void * +lock_free( GMutex *lock ) +{ + g_mutex_free( lock ); + + return( NULL ); +} + +/* Build basic per-call state and do some geometry calculations. Shared with + * im_tbmerge, so not static. + */ +Overlapping * +im__build_mergestate( IMAGE *ref, IMAGE *sec, IMAGE *out, + int dx, int dy, int mwidth ) +{ + Overlapping *ovlap = IM_NEW( out, Overlapping ); + int x; + + if( !ovlap ) + return( NULL ); + if( mwidth < -1 ) { + im_errormsg( "im_lr/tbmerge: mwidth must be -1 or >= 0" ); + return( NULL ); + } + + ovlap->ref = ref; + ovlap->sec = sec; + ovlap->out = out; + ovlap->dx = dx; + ovlap->dy = dy; + ovlap->mwidth = mwidth; + + /* Area occupied by ref image. Place at (0,0) to start with. + */ + ovlap->rarea.left = 0; + ovlap->rarea.top = 0; + ovlap->rarea.width = ref->Xsize; + ovlap->rarea.height = ref->Ysize; + + /* Area occupied by sec image. + */ + ovlap->sarea.left = -dx; + ovlap->sarea.top = -dy; + ovlap->sarea.width = sec->Xsize; + ovlap->sarea.height = sec->Ysize; + + /* Compute overlap. + */ + im_rect_intersectrect( &ovlap->rarea, &ovlap->sarea, &ovlap->overlap ); + if( im_rect_isempty( &ovlap->overlap ) ) { + im_errormsg( "im_lr/tbmerge: no overlap" ); + return( NULL ); + } + + /* Find position and size of output image. + */ + im_rect_unionrect( &ovlap->rarea, &ovlap->sarea, &ovlap->oarea ); + + /* Now: translate everything, so that the output image, not the left + * image, is at (0,0). + */ + ovlap->rarea.left -= ovlap->oarea.left; + ovlap->rarea.top -= ovlap->oarea.top; + ovlap->sarea.left -= ovlap->oarea.left; + ovlap->sarea.top -= ovlap->oarea.top; + ovlap->overlap.left -= ovlap->oarea.left; + ovlap->overlap.top -= ovlap->oarea.top; + ovlap->oarea.left = 0; + ovlap->oarea.top = 0; + + /* Make sure blend luts are built. + */ + im__make_blend_luts(); + + /* Size of first/last cache. Could be either of these ... just pick + * the larger. + */ + ovlap->flsize = IM_MAX( ovlap->overlap.width, ovlap->overlap.height ); + + /* Build first/last cache. + */ + ovlap->first = IM_ARRAY( out, ovlap->flsize, int ); + ovlap->last = IM_ARRAY( out, ovlap->flsize, int ); + if( !ovlap->first || !ovlap->last ) + return( NULL ); + for( x = 0; x < ovlap->flsize; x++ ) + ovlap->first[x] = -1; + + ovlap->fl_lock = g_mutex_new(); + if( im_add_close_callback( out, + (im_callback_fn) lock_free, ovlap->fl_lock, NULL ) ) { + g_mutex_free( ovlap->fl_lock ); + return( NULL ); + } + + return( ovlap ); +} + +/* Build per-call state. + */ +static Overlapping * +build_lrstate( IMAGE *ref, IMAGE *sec, IMAGE *out, int dx, int dy, int mwidth ) +{ + Overlapping *ovlap; + + if( !(ovlap = im__build_mergestate( ref, sec, out, dx, dy, mwidth )) ) + return( NULL ); + + /* Select blender. + */ + switch( ref->Coding ) { + case IM_CODING_LABQ: + ovlap->blend = lr_blend_labpack; + break; + + case IM_CODING_NONE: + ovlap->blend = lr_blend; + break; + + default: + im_errormsg( "im_lrmerge: unknown coding type" ); + return( NULL ); + } + + /* Find the parts of output which come just from ref and just from sec. + */ + ovlap->rpart = ovlap->rarea; + ovlap->spart = ovlap->sarea; + ovlap->rpart.width -= ovlap->overlap.width; + ovlap->spart.left += ovlap->overlap.width; + ovlap->spart.width -= ovlap->overlap.width; + + /* Is there too much overlap? ie. right edge of ref image is greater + * than right edge of sec image, or left > left. + */ + if( IM_RECT_RIGHT( &ovlap->rarea ) > IM_RECT_RIGHT( &ovlap->sarea ) || + ovlap->rarea.left > ovlap->sarea.left ) { + im_errormsg( "im_lrmerge: too much overlap" ); + return( NULL ); + } + + /* Max number of pixels we may have to blend over. + */ + ovlap->blsize = ovlap->overlap.width; + + return( ovlap ); +} + +/* The area being demanded can be filled using only pels from either the ref + * or the sec images. Attach output to the appropriate part of the input image. + * area is the position that ir->im occupies in the output image. + * + * Shared with im_tbmerge(), so not static. + */ +int +im__attach_input( REGION *or, REGION *ir, Rect *area ) +{ + Rect r = or->valid; + + /* Translate to source coordinate space. + */ + r.left -= area->left; + r.top -= area->top; + + /* Demand input. + */ + if( im_prepare( ir, &r ) ) + return( -1 ); + + /* Attach or to ir. + */ + if( im_region_region( or, ir, &or->valid, r.left, r.top ) ) + return( -1 ); + + return( 0 ); +} + +/* The area being demanded requires pixels from the ref and sec images. As + * above, but just do a sub-area of the output, and make sure we copy rather + * than just pointer-fiddling. reg is the sub-area of or->valid we should do. + * + * Shared with im_tbmerge(), so not static. + */ +int +im__copy_input( REGION *or, REGION *ir, Rect *area, Rect *reg ) +{ + Rect r = *reg; + + /* Translate to source coordinate space. + */ + r.left -= area->left; + r.top -= area->top; + + /* Paint this area of ir into or. + */ + if( im_prepare_to( ir, or, &r, reg->left, reg->top ) ) + return( -1 ); + + return( 0 ); +} + +/* Black out a region. + */ +void +im__black_region( REGION *reg ) +{ + PEL *q = (PEL *) IM_REGION_ADDR( reg, reg->valid.left, reg->valid.top ); + int wd = IM_REGION_SIZEOF_LINE( reg ); + int ls = IM_REGION_LSKIP( reg ); + int y; + + for( y = 0; y < reg->valid.height; y++, q += ls ) + memset( (char *) q, 0, wd ); +} + +/* Generate function for merge. This is shared between im_lrmerge() and + * im_tbmerge(). + */ +int +im__merge_gen( REGION *or, MergeInfo *inf, Overlapping *ovlap ) +{ + Rect *r = &or->valid; + Rect rreg, sreg, oreg; + + /* Find intersection with overlap, ref and sec parts. + */ + im_rect_intersectrect( r, &ovlap->rpart, &rreg ); + im_rect_intersectrect( r, &ovlap->spart, &sreg ); + + /* Do easy cases first: can we satisfy this demand with pixels just + * from ref, or just from sec. + */ + if( im_rect_equalsrect( r, &rreg ) ) { + if( im__attach_input( or, inf->rir, &ovlap->rarea ) ) + return( -1 ); + } + else if( im_rect_equalsrect( r, &sreg ) ) { + if( im__attach_input( or, inf->sir, &ovlap->sarea ) ) + return( -1 ); + } + else { + /* Difficult case - do in three stages: black out whole area, + * copy in parts of ref and sec we touch, write blend area. + * This could be sped up somewhat ... we will usually black + * out far too much, and write to the blend area three times. + * Upgrade in the future! + */ + + /* Need intersections with whole of left & right, and overlap + * too. + */ + im_rect_intersectrect( r, &ovlap->rarea, &rreg ); + im_rect_intersectrect( r, &ovlap->sarea, &sreg ); + im_rect_intersectrect( r, &ovlap->overlap, &oreg ); + + im__black_region( or ); + if( !im_rect_isempty( &rreg ) ) + if( im__copy_input( or, + inf->rir, &ovlap->rarea, &rreg ) ) + return( -1 ); + if( !im_rect_isempty( &sreg ) ) + if( im__copy_input( or, + inf->sir, &ovlap->sarea, &sreg ) ) + return( -1 ); + + /* Nasty: inf->rir and inf->sir now point to the same bit of + * memory (part of or), and we've written twice. We need to + * make sure we get fresh pixels for the blend, so we must + * invalidate them both. Should maybe add a call to the API + * for this. + */ + inf->rir->valid.width = inf->sir->valid.width = 0; + + /* Now blat in the blended area. + */ + if( !im_rect_isempty( &oreg ) ) + if( ovlap->blend( or, inf, ovlap, &oreg ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Stop function. Shared with im_tbmerge(). Free explicitly to reduce mem + * requirements quickly for large mosaics. + */ +int +im__stop_merge( MergeInfo *inf ) +{ + if( inf->rir ) { + im_region_free( inf->rir ); + inf->rir = NULL; + } + if( inf->sir ) { + im_region_free( inf->sir ); + inf->sir = NULL; + } + if( inf->from1 ) { + im_free( inf->from1 ); + inf->from1 = NULL; + } + if( inf->from2 ) { + im_free( inf->from2 ); + inf->from2 = NULL; + } + if( inf->merge ) { + im_free( inf->merge ); + inf->merge = NULL; + } + im_free( inf ); + + return( 0 ); +} + +/* Start function. Shared with im_tbmerge(). + */ +void * +im__start_merge( IMAGE *out, Overlapping *ovlap ) +{ + MergeInfo *inf = IM_NEW( NULL, MergeInfo ); + + if( !inf ) + return( NULL ); + + /* Clear all ptrs. + */ + inf->rir = NULL; + inf->sir = NULL; + inf->from1 = NULL; + inf->from2 = NULL; + inf->merge = NULL; + + /* If this is going to be a IM_CODING_LABQ, we need IM_CODING_LABQ blend buffers. + */ + if( out->Coding == IM_CODING_LABQ ) { + inf->from1 = IM_ARRAY( NULL, ovlap->blsize * 3, float ); + inf->from2 = IM_ARRAY( NULL, ovlap->blsize * 3, float ); + inf->merge = IM_ARRAY( NULL, ovlap->blsize * 3, float ); + if( !inf->from1 || !inf->from2 || !inf->merge ) { + im__stop_merge( inf ); + return( NULL ); + } + } + + /* Make input regions. + */ + inf->rir = im_region_create( ovlap->ref ); + inf->sir = im_region_create( ovlap->sec ); + + if( !inf->rir || !inf->sir ) { + im__stop_merge( inf ); + return( NULL ); + } + + return( inf ); +} + +int +im__lrmerge( IMAGE *ref, IMAGE *sec, IMAGE *out, int dx, int dy, int mwidth ) +{ + Overlapping *ovlap; + +#ifdef DEBUG + printf( "im__lrmerge %s %s %s %d %d %d\n", + ref->filename, sec->filename, out->filename, + dx, dy, mwidth ); + printf( "ref is %d x %d pixels\n", ref->Xsize, ref->Ysize ); + printf( "sec is %d x %d pixels\n", sec->Xsize, sec->Ysize ); +#endif + + /* Check IMAGEs parameters + */ + if( ref->Bands != sec->Bands || ref->Bbits != sec->Bbits || + ref->BandFmt != sec->BandFmt || + ref->Coding != sec->Coding ) { + im_errormsg( "im_lrmerge: input images incompatible" ); + return( -1 ); + } + if( ref->Coding != IM_CODING_NONE && ref->Coding != IM_CODING_LABQ ) { + im_errormsg( "im_lrmerge: inputs not uncoded or IM_CODING_LABQ" ); + return( -1 ); + } + if( dx > 0 || dx < 1 - ref->Xsize ) { +#ifdef DEBUG + printf( "im__lrmerge: no overlap, using insert\n" ); +#endif + + /* No overlap, use insert instead. + */ + if( im_insert( ref, sec, out, -dx, -dy ) ) + return( -1 ); + out->Xoffset = -dx; + out->Yoffset = -dy; + + return( 0 ); + } + if( im_piocheck( ref, out ) || im_piocheck( sec, out ) ) + return( -1 ); + + /* Build state for this join. + */ + if( !(ovlap = build_lrstate( ref, sec, out, dx, dy, mwidth )) ) + return( -1 ); + + /* Prepare the output IMAGE. + */ + if( im_cp_descv( out, ref, sec, NULL ) ) + return( -1 ); + out->Xsize = ovlap->oarea.width; + out->Ysize = ovlap->oarea.height; + out->Xoffset = ovlap->sarea.left; + out->Yoffset = ovlap->sarea.top; + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, ref, sec, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im__start_merge, im__merge_gen, im__stop_merge, ovlap, NULL ) ) + return( -1 ); + + return ( 0 ); +} + +int +im_lrmerge( IMAGE *ref, IMAGE *sec, IMAGE *out, int dx, int dy, int mwidth ) +{ + if( im__lrmerge( ref, sec, out, dx, dy, mwidth ) ) + return( -1 ); + + if( im_histlin( out, "#LRJOIN <%s> <%s> <%s> <%d> <%d> <%d>", + ref->filename, sec->filename, out->filename, + -dx, -dy, mwidth ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_lrmosaic.c b/libsrc/mosaicing/im_lrmosaic.c new file mode 100644 index 00000000..59cdde03 --- /dev/null +++ b/libsrc/mosaicing/im_lrmosaic.c @@ -0,0 +1,475 @@ +/* @(#) Program to calculate the best possible tie points + * @(#) in the overlapping part between the primary and the secondary picture + * @(#) + * @(#) Right call: + * @(#) int im_lrmosaic( reference, secondary, out, bandno, + * @(#) xref, yref, xsec, ysec, halfcorrelation, halfarea ) + * @(#) IMAGE *reference, *secondary, *out; + * @(#) int bandno; + * @(#) int xref, yref, xsec, ysec; + * @(#) int halfcorrelation, halfarea; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 07/11/1989 + * Modified on : 29/11/1989, 18/04/1991 + * + * + * Modified and debugged by Ahmed Abbood . 1995 + * 14/6/95 JC + * - rewritten for new balance ideas + * - more bug-fixes + * 1/11/95 JC + * - frees memory used by analysis phase as soon as possible + * - means large mosaics use significantly less peak memory + * 26/3/96 JC + * - now calls im_lrmerge() rather than im__lrmerge() + * 2/2/01 JC + * - added tunable max blend width + * 24/2/05 + * - im_scale() makes it work for any image type + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef DEBUG +static void +im__print_mdebug( TIE_POINTS *points ) +{ + int i; + double adx = 0.0; + double ady = 0.0; + double acor = 0.0; + + for( i = 0; i < points->nopoints; i++ ) { + adx += points->dx[i]; + ady += points->dy[i]; + acor += points->correlation[i]; + } + adx = adx / (double) points->nopoints; + ady = ady / (double) points->nopoints; + acor = acor / (double) points->nopoints; + + printf( "points: %d\n", points->nopoints ); + printf( "average dx, dy: %g %g\n", adx, ady ); + printf( "average correlation: %g\n", acor ); + printf( "deltax, deltay: %g %g\n", points->l_deltax, points->l_deltay ); +} +#endif /*DEBUG*/ + +int +im__find_lroverlap( IMAGE *ref_in, IMAGE *sec_in, IMAGE *out, + int bandno_in, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int *dx0, int *dy0, + double *scale1, double *angle1, double *dx1, double *dy1 ) +{ + IMAGE *ref, *sec; + TIE_POINTS points, *p_points; + TIE_POINTS newpoints, *p_newpoints; + int dx, dy; + int i; + + Rect left, right, overlap; + + /* Check ref and sec are compatible. + */ + if( ref_in->Bands != sec_in->Bands || + ref_in->BandFmt != sec_in->BandFmt || + ref_in->Coding != sec_in->Coding ) { + im_errormsg( "im_lrmosaic: input images incompatible" ); + return( -1 ); + } + + /* Test cor and area. + */ + if( halfcorrelation < 0 || halfarea < 0 || + halfarea < halfcorrelation ) { + im_errormsg( "im_lrmosaic: bad area parameters" ); + return( -1 ); + } + + /* Set positions of left and right. + */ + left.left = 0; + left.top = 0; + left.width = ref_in->Xsize; + left.height = ref_in->Ysize; + right.left = xref - xsec; + right.top = yref - ysec; + right.width = sec_in->Xsize; + right.height = sec_in->Ysize; + + /* Find overlap. + */ + im_rect_intersectrect( &left, &right, &overlap ); + if( overlap.width < 2*halfarea + 1 || + overlap.height < 2*halfarea + 1 ) { + im_errormsg( "im_lrmosaic: overlap too small for search" ); + return( -1 ); + } + + /* Extract overlaps. + */ + ref = im_open_local( out, "temp_one", "t" ); + sec = im_open_local( out, "temp_two", "t" ); + if( !ref || !sec ) + return( -1 ); + if( ref_in->Coding == IM_CODING_LABQ ) { + IMAGE *t1 = im_open_local( out, "temp:3", "p" ); + IMAGE *t2 = im_open_local( out, "temp:4", "p" ); + IMAGE *t3 = im_open_local( out, "temp:5", "p" ); + IMAGE *t4 = im_open_local( out, "temp:6", "p" ); + IMAGE *t5 = im_open_local( out, "temp:7", "p" ); + IMAGE *t6 = im_open_local( out, "temp:8", "p" ); + + if( !t1 || !t2 || !t3 || !t4 || !t5 || !t6 ) + return( -1 ); + if( im_extract_area( ref_in, t1, + overlap.left, overlap.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_area( sec_in, t2, + overlap.left - right.left, overlap.top - right.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_LabQ2Lab( t1, t3 ) || im_LabQ2Lab( t2, t4 ) || + im_Lab2disp( t3, t5, im_col_displays( 1 ) ) || + im_Lab2disp( t4, t6, im_col_displays( 1 ) ) ) + return( -1 ); + + /* Extract the green. + */ + if( im_extract_band( t5, ref, 1 ) || + im_extract_band( t6, sec, 1 ) ) + return( -1 ); + } + else if( ref_in->Coding == IM_CODING_NONE ) { + IMAGE *t1 = im_open_local( out, "temp:9", "p" ); + IMAGE *t2 = im_open_local( out, "temp:10", "p" ); + IMAGE *t3 = im_open_local( out, "temp:11", "p" ); + IMAGE *t4 = im_open_local( out, "temp:12", "p" ); + + if( !t1 || !t2 || !t3 || !t4 ) + return( -1 ); + if( im_extract_area( ref_in, t1, + overlap.left, overlap.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_area( sec_in, t2, + overlap.left - right.left, overlap.top - right.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_band( t1, t3, bandno_in ) || + im_extract_band( t2, t4, bandno_in ) ) + return( -1 ); + if( im_scale( t3, ref ) || + im_scale( t4, sec ) ) + return( -1 ); + } + else { + im_errormsg( "im_lrmosaic: unknown Coding type" ); + return( -1 ); + } + + /* Initialise and fill TIE_POINTS + */ + p_points = &points; + p_newpoints = &newpoints; + p_points->reference = ref_in->filename; + p_points->secondary = sec_in->filename; + p_points->nopoints = IM_MAXPOINTS; + p_points->deltax = 0; + p_points->deltay = 0; + p_points->halfcorsize = halfcorrelation; + p_points->halfareasize = halfarea; + + /* Initialise the structure + */ + for( i = 0; i < IM_MAXPOINTS; i++ ) { + p_points->x_reference[i] = 0; + p_points->y_reference[i] = 0; + p_points->x_secondary[i] = 0; + p_points->y_secondary[i] = 0; + p_points->contrast[i] = 0; + p_points->correlation[i] = 0.0; + p_points->dx[i] = 0.0; + p_points->dy[i] = 0.0; + p_points->deviation[i] = 0.0; + } + + /* Search ref for possible tie-points. Sets: p_points->contrast, + * p_points->x,y_reference. + */ + if( im__lrcalcon( ref, p_points ) ) + return( -1 ); + + /* For each candidate point, correlate against corresponding part of + * sec. Sets x,y_secondary and fills correlation and dx, dy. + */ + if( im__chkpair( ref, sec, p_points ) ) + return( -1 ); + + /* First call to im_clinear(). + */ + if( im__initialize( p_points ) ) + return( -1 ); + + /* Improve the selection of tiepoints until all abs(deviations) are + * < 1.0 by deleting all wrong points. + */ + if( im__improve( p_points, p_newpoints ) ) + return( -1 ); + + /* Average remaining offsets. + */ + if( im__avgdxdy( p_newpoints, &dx, &dy ) ) + return( -1 ); + + /* Offset with overlap position. + */ + *dx0 = -right.left + dx; + *dy0 = -right.top + dy; + + /* Write 1st order parameters too. + */ + *scale1 = newpoints.l_scale; + *angle1 = newpoints.l_angle; + *dx1 = newpoints.l_deltax; + *dy1 = newpoints.l_deltay; + + return( 0 ); +} + +/* Scale im by fac with a lut. + */ +static IMAGE * +transform( IMAGE *out, IMAGE *im, double fac ) +{ + IMAGE *t1 = im_open_local( out, "transform:1", "p" ); + IMAGE *t2 = im_open_local( out, "transform:2", "p" ); + IMAGE *t3 = im_open_local( out, "transform:3", "p" ); + IMAGE *t4 = im_open_local( out, "transform:4", "p" ); + + if( !t1 || !t2 || !t3 || !t4 ) + return( NULL ); + + if( fac == 1.0 ) + /* Easy! + */ + return( im ); + + if( im_identity( t1, 1 ) || + im_lintra( fac, t1, 0.0, t2 ) || + im_clip( t2, t3 ) || + im_maplut( im, t4, t3 ) ) + return( NULL ); + + return( t4 ); +} + +/* Balance two images. dx, dy parameters as for im_??merge, etc. + */ +int +im__balance( IMAGE *ref, IMAGE *sec, IMAGE *out, + IMAGE **ref_out, IMAGE **sec_out, int dx, int dy, int balancetype ) +{ + double lavg, ravg; + double lfac, rfac; + Rect left, right, overlap; + IMAGE *t1, *t2; + + /* Test balancetype. + */ + if( balancetype < 0 || balancetype > 3 ) { + im_errormsg( "im_mosaic: bad balancetype parameter" ); + return( -1 ); + } + + /* No balance - easy! + */ + if( balancetype == 0 ) { + *ref_out = ref; + *sec_out = sec; + + return( 0 ); + } + + /* Must be uchar uncoded. + */ + if( ref->Coding != IM_CODING_NONE || + ref->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im_mosaic: uncoded uchar only for balancing" ); + return( -1 ); + } + + /* Set positions of left and right. + */ + left.left = 0; + left.top = 0; + left.width = ref->Xsize; + left.height = ref->Ysize; + right.left = -dx; + right.top = -dy; + right.width = sec->Xsize; + right.height = sec->Ysize; + + /* Find overlap. + */ + im_rect_intersectrect( &left, &right, &overlap ); + + /* Extract overlaps. + */ + t1 = im_open_local( out, "temp_one", "p" ); + t2 = im_open_local( out, "temp_two", "p" ); + if( !t1 || !t2 ) + return( -1 ); + + if( im_extract_area( ref, t1, + overlap.left, overlap.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_area( sec, t2, + overlap.left - right.left, overlap.top - right.top, + overlap.width, overlap.height ) ) + return( -1 ); + + /* And find the average. + */ + if( im_avg( t1, &lavg ) || im_avg( t2, &ravg ) ) + return( -1 ); + + /* Compute scale factors. + */ + switch( balancetype ) { + case 1: + /* Ajust left. + */ + rfac = 1.0; + lfac = ravg / lavg; + break; + + case 2: + /* Adjust right. + */ + lfac = 1.0; + rfac = lavg / ravg; + break; + + case 3: + { + /* Adjust both to weighted average. + */ + double ltot = (double) ref->Xsize * ref->Ysize; + double rtot = (double) sec->Xsize * sec->Ysize; + double rat = ltot / (ltot + rtot); + double navg = rat * (lavg - ravg) + ravg; + + lfac = navg / lavg; + rfac = navg / ravg; + } + break; + + default: + error_exit( "internal error #897624395" ); + return( -1 ); + } + + /* Transform the left and right images. + */ + if( !(*ref_out = transform( out, ref, lfac )) ) + return( -1 ); + if( !(*sec_out = transform( out, sec, rfac )) ) + return( -1 ); + + return( 0 ); +} + +int +im_lrmosaic( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ) +{ + int dx0, dy0; + double scale1, angle1, dx1, dy1; + IMAGE *ref2, *sec2; + IMAGE *dummy; + + /* Correct overlap. dummy is just a placeholder used to ensure that + * memory used by the analysis phase is freed as soon as possible. + */ + if( !(dummy = im_open( "placeholder:1", "p" )) ) + return( -1 ); + if( im__find_lroverlap( ref, sec, dummy, + bandno, + xref, yref, xsec, ysec, + halfcorrelation, halfarea, + &dx0, &dy0, + &scale1, &angle1, &dx1, &dy1 ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + /* Balance. + */ + if( im__balance( ref, sec, out, + &ref2, &sec2, + dx0, dy0, balancetype ) ) + return( -1 ); + + /* Merge left right. + */ + if( im_lrmerge( ref2, sec2, out, dx0, dy0, mwidth ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_remosaic.c b/libsrc/mosaicing/im_remosaic.c new file mode 100644 index 00000000..7f225e92 --- /dev/null +++ b/libsrc/mosaicing/im_remosaic.c @@ -0,0 +1,136 @@ +/* Use one mosiaced file to mosaic another set of images. + * + * 1/11/01 JC + * - from global_balance + * 25/02/02 JC + * - detect size change + * 10/4/06 + * - spot file-not-found + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Define for debug output. +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#include "merge.h" +#include "global_balance.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +typedef struct _RemosaicData { + const char *old_str; + const char *new_str; + int new_len; + int old_len; +} RemosaicData; + +static IMAGE * +remosaic( JoinNode *node, RemosaicData *rd ) +{ + SymbolTable *st = node->st; + IMAGE *im = node->im; + + IMAGE *out; + char filename[FILENAME_MAX]; + char *p; + + if( !im ) { + im_error( "im_remosaic", _( "file \"%s\" not found" ), + node->name ); + return( NULL ); + } + + /* Remove substring rd->old_str from in->filename, replace with + * rd->new_str. + */ + im_strncpy( filename, im->filename, FILENAME_MAX ); + if( (p = im_strrstr( filename, rd->old_str )) ) { + int offset = p - &filename[0]; + + im_strncpy( p, rd->new_str, FILENAME_MAX - offset ); + im_strncpy( p + rd->new_len, + im->filename + offset + rd->old_len, + FILENAME_MAX - offset - rd->new_len ); + } + +#ifdef DEBUG + printf( "im_remosaic: filename \"%s\" -> \"%s\"\n", + im->filename, filename ); +#endif /*DEBUG*/ + + if( !(out = im__global_open_image( st, filename )) ) + return( NULL ); + + if( out->Xsize != im->Xsize || out->Ysize != im->Ysize ) { + im_error( "im_remosaic", + _( "substitute image \"%s\" is not " + "the same size as \"%s\"" ), + filename, im->filename ); + return( NULL ); + } + + return( out ); +} + +int +im_remosaic( IMAGE *in, IMAGE *out, const char *old_str, const char *new_str ) +{ + SymbolTable *st; + RemosaicData rd; + + if( !(st = im__build_symtab( out, SYM_TAB_SIZE )) || + im__parse_desc( st, in ) ) + return( -1 ); + + /* Re-make mosaic. + */ + rd.old_str = old_str; + rd.new_str = new_str; + rd.new_len = strlen( new_str ); + rd.old_len = strlen( old_str ); + if( im__build_mosaic( st, out, (transform_fn) remosaic, &rd ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_tbcalcon.c b/libsrc/mosaicing/im_tbcalcon.c new file mode 100644 index 00000000..b444fa43 --- /dev/null +++ b/libsrc/mosaicing/im_tbcalcon.c @@ -0,0 +1,137 @@ +/* @(#) Functions which takes an initial estimate of deltax, deltay + * @(#) between reference and secondary images (probably from the scanner), + * @(#) and looks in three areas of the overlapping part of the reference image + * @(#) corresponding to reference and secondary. For every other halfreasize + * @(#) point of the three areas of the reference image + * @(#) the contrast is calculated + * @(#) an area 2*halfcorsize+1 centered at this point + * @(#) Results are saved in the structure points + * @(#) The function expects the following valid data in points: + * @(#) deltax, deltay, nopoints, halfcorsize, halfareasize + * @(#) and fills in the memebers: + * @(#) x, y_reference[], contrast and x,y_secondary[], + * @(#) based on deltax and deltay + * @(#) Input image should are either memory mapped or in a buffer. + * @(#) To make the calculation faster set FACTOR to 1, 2 or 3 + * @(#) Calculations are based on bandno only. + * @(#) The function uses functions im_calculate_contrast() + * @(#) which is in im_lrcalcon() + * @(#) + * @(#) int im_tbcalcon( ref, sec, bandno, points ) + * @(#) IMAGE *ref, *sec; + * @(#) int bandno; + * @(#) TIE_POINTS *points; see mosaic.h + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 20/12/1990 + * Modified on : 18/04/1991 + * 8/7/93 JC + * - allow IM_CODING_LABQ coding + * - now calls im_incheck() + * 12/7/95 JC + * - reworked + * - what a lot of horrible old code there was too + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im__tbcalcon( IMAGE *ref, TIE_POINTS *points ) +{ + /* Geometry: border we must leave around each area. + */ + const int border = points->halfareasize; + + /* Width of an area. + */ + const int awidth = ref->Xsize / AREAS; + + /* Number of points we find in each area. + */ + const int len = points->nopoints / AREAS; + + int i; + Rect area; + + /* Make sure we can read image. + */ + if( im_incheck( ref ) ) + return( -1 ); + if( ref->Bands != 1 || ref->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im__tbcalcon: help!" ); + return( -1 ); + } + + /* Define bits to search for high-contrast areas. + */ + area.width = awidth; + area.height = ref->Ysize; + area.left = 0; + area.top = 0; + im_rect_marginadjust( &area, -border ); + area.width--; + area.height--; + if( area.width < 0 || area.height < 0 ) { + im_errormsg( "im__tbcalcon: overlap too small" ); + return( -1 ); + } + + /* Loop over areas, finding points. + */ + for( i = 0; area.left < ref->Xsize; area.left += awidth, i++ ) + if( im__find_best_contrast( ref, + area.left, area.top, area.width, area.height, + points->x_reference + i*len, + points->y_reference + i*len, + points->contrast + i*len, + len, + points->halfcorsize ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_tbmerge.c b/libsrc/mosaicing/im_tbmerge.c new file mode 100644 index 00000000..b9432e79 --- /dev/null +++ b/libsrc/mosaicing/im_tbmerge.c @@ -0,0 +1,732 @@ +/* Merge two images top-bottom. dx, dy is the offset needed to get from sec + * (secondary image) to ref (reference image). + * + * Usage: + * + * int + * im_tbmerge( ref, sec, out, dx, dy ) + * IMAGE *ref, *sec, *out; + * int dx, dy; + * + * Returns 0 on success and -1 on error + * + * Copyright: 1990, 1991 N. Dessipris + * Author: N. Dessipris + * Written on: 20/09/1990 + * Updated on: 17/04/1991 + * 1/6/92: J. Cupitt + * - check for difference bug fixed + * - geometry calculations improved and simplified + * - small speedups + * 30/6/93 K.Martinez : coped with IM_CODING_LABQ images + * 7/7/93 JC + * - ANSIfied + * - proper freeing on errors, ready for partial + * 8/11/93 JC + * - now propogates both input histories + * - adds magic lines for global mosaic optimisation + * + * + * 16/May/1994 Ahmed. Abbood + * - Modified to use partials on all IO + * + * June/1995 Ahmed Abbood + * + * - Modified to work with different types of images. + * + * + * 16/6/95 JC + * - added to VIPS! + * 7/9/95 JC + * - split into two parts: im_tbmerge() and im__tbmerge() + * - latter called by im_tbmosaic() + * - just the same as public im_tbmerge(), but adds no history + * - necessary for im_global_balance() + * - small bugs fixed + * 10/10/95 JC + * - better checks that parameters are sensible + * 11/10/95 JC + * - Kirk spotted what a load of rubbish Ahmed's code is + * - rewritten - many, many bugs fixed + * 28/7/97 JC + * - new non-rectangular im_lrmerge adapted to make this + * - small tidies + * 18/2/98 JC + * - im_demand_hint() call added + * 19/2/98 JC + * - now works for any dx/dy by calling im_insert() for bizarre cases + * 2/2/01 JC + * - added tunable max blend width + * 8/3/01 JC + * - switched to integer arithmetic for integer blends + * 23/3/01 JC + * - oops, iblend was broken + * 7/11/01 JC + * - more sophisticated transparency handling + * 15/8/02 JC + * - records Xoffset/Yoffset + * 20/6/05 + * - now requires all bands == 0 for transparency (used to just check + * band 0) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#include "merge.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Return the position of the first non-zero pel from the top. + */ +static int +find_top( REGION *ir, int *pos, int x, int y, int h ) +{ + PEL *pr = (PEL *) IM_REGION_ADDR( ir, x, y ); + IMAGE *im = ir->im; + int ls = IM_REGION_LSKIP( ir ) / IM_IMAGE_SIZEOF_ELEMENT( im ); + int b = im->Bands; + int i, j; + + /* Double the number of bands in a complex. + */ + if( im_iscomplex( im ) ) + b *= 2; + +/* Search for the first non-zero band element from the top edge of the image. + */ +#define tsearch( TYPE ) { \ + TYPE *p = (TYPE *) pr; \ + \ + for( i = 0; i < h; i++ ) { \ + for( j = 0; j < b; j++ ) \ + if( p[j] ) \ + break; \ + if( j < b ) \ + break; \ + \ + p += ls; \ + } \ +} + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: tsearch( unsigned char ); break; + case IM_BANDFMT_CHAR: tsearch( signed char ); break; + case IM_BANDFMT_USHORT: tsearch( unsigned short ); break; + case IM_BANDFMT_SHORT: tsearch( signed short ); break; + case IM_BANDFMT_UINT: tsearch( unsigned int ); break; + case IM_BANDFMT_INT: tsearch( signed int ); break; + case IM_BANDFMT_FLOAT: tsearch( float ); break; + case IM_BANDFMT_DOUBLE: tsearch( double ); break; + case IM_BANDFMT_COMPLEX:tsearch( float ); break; + case IM_BANDFMT_DPCOMPLEX:tsearch( double ); break; + + default: + im_error( "im_tbmerge", _( "internal error" ) ); + return( -1 ); + } + + *pos = y + i; + + return( 0 ); +} + +/* Return the position of the first non-zero pel from the bottom. + */ +static int +find_bot( REGION *ir, int *pos, int x, int y, int h ) +{ + PEL *pr = (PEL *) IM_REGION_ADDR( ir, x, y ); + IMAGE *im = ir->im; + int ls = IM_REGION_LSKIP( ir ) / IM_IMAGE_SIZEOF_ELEMENT( ir->im ); + int b = im->Bands; + int i, j; + + /* Double the number of bands in a complex. + */ + if( im_iscomplex( im ) ) + b *= 2; + +/* Search for the first non-zero band element from the top edge of the image. + */ +#define rsearch( TYPE ) { \ + TYPE *p = (TYPE *) pr + (h - 1) * ls; \ + \ + for( i = h - 1; i >= 0; i-- ) { \ + for( j = 0; j < b; j++ ) \ + if( p[j] ) \ + break; \ + if( j < b ) \ + break; \ + \ + p -= ls; \ + } \ +} + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: rsearch( unsigned char ); break; + case IM_BANDFMT_CHAR: rsearch( signed char ); break; + case IM_BANDFMT_USHORT: rsearch( unsigned short ); break; + case IM_BANDFMT_SHORT: rsearch( signed short ); break; + case IM_BANDFMT_UINT: rsearch( unsigned int ); break; + case IM_BANDFMT_INT: rsearch( signed int ); break; + case IM_BANDFMT_FLOAT: rsearch( float ); break; + case IM_BANDFMT_DOUBLE: rsearch( double ); break; + case IM_BANDFMT_COMPLEX:rsearch( float ); break; + case IM_BANDFMT_DPCOMPLEX:rsearch( double ); break; + + default: + im_error( "im_tbmerge", _( "internal error" ) ); + return( -1 ); + } + + *pos = y + i; + + return( 0 ); +} + +/* Make first/last for oreg. + */ +static int +make_firstlast( MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) +{ + REGION *rir = inf->rir; + REGION *sir = inf->sir; + Rect rr, sr; + int x; + int missing; + + /* We're going to build first/last ... lock it from other generate + * threads. In fact it's harmless if we do get two writers, but we may + * avoid duplicating work. + */ + g_mutex_lock( ovlap->fl_lock ); + + /* Do we already have first/last for this area? Bail out if we do. + */ + missing = 0; + for( x = oreg->left; x < IM_RECT_RIGHT( oreg ); x++ ) { + const int j = x - ovlap->overlap.left; + const int first = ovlap->first[j]; + + if( first < 0 ) { + missing = 1; + break; + } + } + if( !missing ) { + /* No work to do! + */ + g_mutex_unlock( ovlap->fl_lock ); + return( 0 ); + } + + /* Entire height of overlap in ref for oreg ... we know oreg is inside + * overlap. + */ + rr.left = oreg->left; + rr.top = ovlap->overlap.top; + rr.width = oreg->width; + rr.height = ovlap->overlap.height; + rr.left -= ovlap->rarea.left; + rr.top -= ovlap->rarea.top; + + /* Same in sec. + */ + sr.left = oreg->left; + sr.top = ovlap->overlap.top; + sr.width = oreg->width; + sr.height = ovlap->overlap.height; + sr.left -= ovlap->sarea.left; + sr.top -= ovlap->sarea.top; + + /* Make pixels. + */ + if( im_prepare( rir, &rr ) || im_prepare( sir, &sr ) ) { + g_mutex_unlock( ovlap->fl_lock ); + return( -1 ); + } + + /* Make first/last cache. + */ + for( x = 0; x < oreg->width; x++ ) { + const int j = (x + oreg->left) - ovlap->overlap.left; + int *first = &ovlap->first[j]; + int *last = &ovlap->last[j]; + + /* Done this line already? + */ + if( *first < 0 ) { + /* Search for top/bottom of overlap on this scan-line. + */ + if( find_top( sir, first, + x + sr.left, sr.top, sr.height ) || + find_bot( rir, last, + x + rr.left, rr.top, rr.height ) ) { + g_mutex_unlock( ovlap->fl_lock ); + return( -1 ); + } + + /* Translate to output space. + */ + *first += ovlap->sarea.top; + *last += ovlap->rarea.top; + + /* Clip to maximum blend width, if necessary. + */ + if( ovlap->mwidth >= 0 && + *last - *first > ovlap->mwidth ) { + int shrinkby = (*last - *first) - ovlap->mwidth; + + *first += shrinkby / 2; + *last -= shrinkby / 2; + } + } + } + + g_mutex_unlock( ovlap->fl_lock ); + + return( 0 ); +} + +/* Test pixel == 0. + */ +#define TEST_ZERO( TYPE, T, RESULT ) { \ + TYPE *tt = (T); \ + int ii; \ + \ + for( ii = 0; ii < cb; ii++ ) \ + if( tt[i] ) \ + break; \ + if( ii == cb ) \ + (RESULT) = 1; \ +} + +/* Blend two integer images ... one scan-line. + */ +#define iblend( TYPE, B, IN1, IN2, OUT ) { \ + TYPE *tr = (TYPE *) (IN1); \ + TYPE *ts = (TYPE *) (IN2); \ + TYPE *tq = (TYPE *) (OUT); \ + const int cb = (B); \ + int ref_zero; \ + int sec_zero; \ + int x, b; \ + int i; \ + \ + for( i = 0, x = 0; x < oreg->width; x++ ) { \ + ref_zero = 0; \ + sec_zero = 0; \ + TEST_ZERO( TYPE, tr, ref_zero ); \ + TEST_ZERO( TYPE, ts, sec_zero ); \ + \ + /* Above the bottom image? \ + */ \ + if( y < first[x] ) { \ + if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + /* To the right? \ + */ \ + else if( y >= last[x] ) { \ + if( !sec_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + } \ + /* In blend area. \ + */ \ + else { \ + if( !ref_zero && !sec_zero ) { \ + const int bheight = last[x] - first[x]; \ + const int inx = ((y - first[x]) << \ + BLEND_SHIFT) / bheight; \ + int c1 = im__icoef1[inx]; \ + int c2 = im__icoef2[inx]; \ + \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = c1*tr[i] / BLEND_SCALE + \ + c2*ts[i] / BLEND_SCALE; \ + } \ + else if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + } \ +} + +/* Blend two float images. + */ +#define fblend( TYPE, B, IN1, IN2, OUT ) { \ + TYPE *tr = (TYPE *) (IN1); \ + TYPE *ts = (TYPE *) (IN2); \ + TYPE *tq = (TYPE *) (OUT); \ + int ref_zero; \ + int sec_zero; \ + const int cb = (B); \ + int x, b; \ + int i; \ + \ + for( i = 0, x = 0; x < oreg->width; x++ ) { \ + ref_zero = 0; \ + sec_zero = 0; \ + TEST_ZERO( TYPE, tr, ref_zero ); \ + TEST_ZERO( TYPE, ts, sec_zero ); \ + \ + /* Above the bottom image? \ + */ \ + if( y < first[x] ) \ + if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + /* To the right? \ + */ \ + else if( y >= last[x] ) \ + if( !sec_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + /* In blend area. \ + */ \ + else { \ + if( !ref_zero && !sec_zero ) { \ + const int bheight = last[x] - first[x]; \ + const int inx = ((y - first[x]) << \ + BLEND_SHIFT) / bheight; \ + double c1 = im__coef1[inx]; \ + double c2 = im__coef2[inx]; \ + \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = c1 * tr[i] + c2 * ts[i]; \ + } \ + else if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + } \ +} + +/* Top-bottom blend function for non-labpack images. + */ +static int +tb_blend( REGION *or, MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) +{ + REGION *rir = inf->rir; + REGION *sir = inf->sir; + IMAGE *im = or->im; + + Rect prr, psr; + int y, yr, ys; + + /* Make sure we have a complete first/last set for this area. + */ + if( make_firstlast( inf, ovlap, oreg ) ) + return( -1 ); + + /* Part of rr which we will output. + */ + prr = *oreg; + prr.left -= ovlap->rarea.left; + prr.top -= ovlap->rarea.top; + + /* Part of sr which we will output. + */ + psr = *oreg; + psr.left -= ovlap->sarea.left; + psr.top -= ovlap->sarea.top; + + /* Make pixels. + */ + if( im_prepare( rir, &prr ) ) + return( -1 ); + if( im_prepare( sir, &psr ) ) + return( -1 ); + + /* Loop down overlap area. + */ + for( y = oreg->top, yr = prr.top, ys = psr.top; + y < IM_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) { + PEL *pr = (PEL *) IM_REGION_ADDR( rir, prr.left, yr ); + PEL *ps = (PEL *) IM_REGION_ADDR( sir, psr.left, ys ); + PEL *q = (PEL *) IM_REGION_ADDR( or, oreg->left, y ); + + const int j = oreg->left - ovlap->overlap.left; + const int *first = ovlap->first + j; + const int *last = ovlap->last + j; + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + iblend( unsigned char, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_CHAR: + iblend( signed char, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_USHORT: + iblend( unsigned short, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_SHORT: + iblend( signed short, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_UINT: + iblend( unsigned int, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_INT: + iblend( signed int, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_FLOAT: + fblend( float, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_DOUBLE: + fblend( double, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_COMPLEX: + fblend( float, im->Bands*2, pr, ps, q ); break; + case IM_BANDFMT_DPCOMPLEX: + fblend( double, im->Bands*2, pr, ps, q ); break; + + default: + im_error( "im_tbmerge", _( "internal error" ) ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Top-bottom blend function for IM_CODING_LABQ images. + */ +static int +tb_blend_labpack( REGION *or, MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) +{ + REGION *rir = inf->rir; + REGION *sir = inf->sir; + Rect prr, psr; + int y, yr, ys; + + /* Make sure we have a complete first/last set for this area. This + * will just look at the top 8 bits of L, not all 10, but should be OK. + */ + if( make_firstlast( inf, ovlap, oreg ) ) + return( -1 ); + + /* Part of rr which we will output. + */ + prr = *oreg; + prr.left -= ovlap->rarea.left; + prr.top -= ovlap->rarea.top; + + /* Part of sr which we will output. + */ + psr = *oreg; + psr.left -= ovlap->sarea.left; + psr.top -= ovlap->sarea.top; + + /* Make pixels. + */ + if( im_prepare( rir, &prr ) ) + return( -1 ); + if( im_prepare( sir, &psr ) ) + return( -1 ); + + /* Loop down overlap area. + */ + for( y = oreg->top, yr = prr.top, ys = psr.top; + y < IM_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) { + PEL *pr = (PEL *) IM_REGION_ADDR( rir, prr.left, yr ); + PEL *ps = (PEL *) IM_REGION_ADDR( sir, psr.left, ys ); + PEL *q = (PEL *) IM_REGION_ADDR( or, oreg->left, y ); + + const int j = oreg->left - ovlap->overlap.left; + const int *first = ovlap->first + j; + const int *last = ovlap->last + j; + + float *fq = inf->merge; + float *r = inf->from1; + float *s = inf->from2; + + /* Unpack two bits we want. + */ + imb_LabQ2Lab( pr, r, oreg->width ); + imb_LabQ2Lab( ps, s, oreg->width ); + + /* Blend as floats. + */ + fblend( float, 3, r, s, fq ); + + /* Re-pack to output buffer. + */ + imb_Lab2LabQ( inf->merge, q, oreg->width ); + } + + return( 0 ); +} + +/* Build per-call state. + */ +static Overlapping * +build_tbstate( IMAGE *ref, IMAGE *sec, IMAGE *out, int dx, int dy, int mwidth ) +{ + Overlapping *ovlap; + + if( !(ovlap = im__build_mergestate( ref, sec, out, dx, dy, mwidth )) ) + return( NULL ); + + /* Select blender. + */ + switch( ref->Coding ) { + case IM_CODING_LABQ: + ovlap->blend = tb_blend_labpack; + break; + + case IM_CODING_NONE: + ovlap->blend = tb_blend; + break; + + default: + im_error( "im_tbmerge", _( "unknown coding type" ) ); + return( NULL ); + } + + /* Find the parts of output which come just from ref and just from sec. + */ + ovlap->rpart = ovlap->rarea; + ovlap->spart = ovlap->sarea; + ovlap->rpart.height -= ovlap->overlap.height; + ovlap->spart.top += ovlap->overlap.height; + ovlap->spart.height -= ovlap->overlap.height; + + /* Is there too much overlap? ie. bottom edge of ref image is greater + * than bottom edge of sec image, or top edge of ref > top edge of + * sec. + */ + if( IM_RECT_BOTTOM( &ovlap->rarea ) > IM_RECT_BOTTOM( &ovlap->sarea ) || + ovlap->rarea.top > ovlap->sarea.top ) { + im_error( "im_tbmerge", _( "too much overlap" ) ); + return( NULL ); + } + + /* Max number of pixels we may have to blend together. + */ + ovlap->blsize = ovlap->overlap.width; + + return( ovlap ); +} + +int +im__tbmerge( IMAGE *ref, IMAGE *sec, IMAGE *out, int dx, int dy, int mwidth ) +{ + Overlapping *ovlap; + + /* Check IMAGEs parameters + */ + if( ref->Bands != sec->Bands || ref->Bbits != sec->Bbits || + ref->BandFmt != sec->BandFmt || + ref->Coding != sec->Coding ) { + im_error( "im_tbmerge", _( "input images incompatible" ) ); + return( -1 ); + } + if( ref->Coding != IM_CODING_NONE && ref->Coding != IM_CODING_LABQ ) { + im_error( "im_tbmerge", + _( "inputs not uncoded or IM_CODING_LABQ" ) ); + return( -1 ); + } + if( dy > 0 || dy < 1 - ref->Ysize ) { + /* No overlap, use insert instead. + */ + if( im_insert( ref, sec, out, -dx, -dy ) ) + return( -1 ); + out->Xoffset = -dx; + out->Yoffset = -dy; + + return( 0 ); + } + if( im_piocheck( ref, out ) || im_piocheck( sec, out ) ) + return( -1 ); + + /* Build state for this join. + */ + if( !(ovlap = build_tbstate( ref, sec, out, dx, dy, mwidth )) ) + return( -1 ); + + /* Prepare the output IMAGE. + */ + if( im_cp_descv( out, ref, sec, NULL ) ) + return( -1 ); + out->Xsize = ovlap->oarea.width; + out->Ysize = ovlap->oarea.height; + out->Xoffset = ovlap->sarea.left; + out->Yoffset = ovlap->sarea.top; + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, ref, sec, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im__start_merge, im__merge_gen, im__stop_merge, ovlap, NULL ) ) + return( -1 ); + + return ( 0 ); +} + +int +im_tbmerge( IMAGE *ref, IMAGE *sec, IMAGE *out, int dx, int dy, int mwidth ) +{ + if( im__tbmerge( ref, sec, out, dx, dy, mwidth ) ) + return( -1 ); + + if( im_histlin( out, "#TBJOIN <%s> <%s> <%s> <%d> <%d> <%d>", + ref->filename, sec->filename, out->filename, + -dx, -dy, mwidth ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_tbmosaic.c b/libsrc/mosaicing/im_tbmosaic.c new file mode 100644 index 00000000..75a423d6 --- /dev/null +++ b/libsrc/mosaicing/im_tbmosaic.c @@ -0,0 +1,309 @@ +/* @(#) Program to calculate the best possible tie points + * @(#) in the overlapping part between the primary and the secondary picture + * @(#) + * @(#) Right call: + * @(#) int im_tbmosaic( reference, secondary, out, bandno, + * @(#) xref, yref, xsec, ysec, halfcorrelation, halfarea, balancetype ) + * @(#) IMAGE *reference, *secondary, *out; + * @(#) int bandno; + * @(#) int xref, yref, xsec, ysec; + * @(#) int halfcorrelation, halfarea; + * @(#) int balancetype; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 07/11/1989 + * Modified on : 29/11/1989, 18/04/1991 + * Modified and debugged by Ahmed Abbood . 1995 + * 14/6/95 JC + * - adapted for new balance ideas + * - more bug-fixes + * 1/11/95 JC + * - frees memory used by analysis phase as soon as possible + * - means large mosaics use significantly less peak memory + * 26/3/96 JC + * - now calls im_tbmerge() rather than im__tbmerge() + * 30/7/97 JC + * - im__find_tboverlap() returns 1st order params too + * 2/2/01 JC + * - added tunable max blend width + * 24/2/05 + * - im_scale() makes it work for any image type + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im__find_tboverlap( IMAGE *ref_in, IMAGE *sec_in, IMAGE *out, + int bandno_in, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int *dx0, int *dy0, + double *scale1, double *angle1, double *dx1, double *dy1 ) +{ + IMAGE *ref, *sec; + TIE_POINTS points, *p_points; /* defined in mosaic.h */ + TIE_POINTS newpoints, *p_newpoints; + int i; + int dx, dy; + + Rect top, bottom, overlap; + + /* Check ref and sec are compatible. + */ + if( ref_in->Bands != sec_in->Bands || + ref_in->BandFmt != sec_in->BandFmt || + ref_in->Coding != sec_in->Coding ) { + im_errormsg( "im_tbmosaic: input images incompatible" ); + return( -1 ); + } + + /* Test cor and area. + */ + if( halfcorrelation < 0 || halfarea < 0 || + halfarea < halfcorrelation ) { + im_errormsg( "im_tbmosaic: bad area parameters" ); + return( -1 ); + } + + /* Set positions of top and bottom. + */ + top.left = 0; + top.top = 0; + top.width = ref_in->Xsize; + top.height = ref_in->Ysize; + bottom.left = xref - xsec; + bottom.top = yref - ysec; + bottom.width = sec_in->Xsize; + bottom.height = sec_in->Ysize; + + /* Find overlap. + */ + im_rect_intersectrect( &top, &bottom, &overlap ); + if( overlap.width < 2*halfarea + 1 || + overlap.height < 2*halfarea + 1 ) { + im_errormsg( "im_tbmosaic: overlap too small for search" ); + return( -1 ); + } + + /* Extract overlaps. + */ + ref = im_open_local( out, "temp_one", "t" ); + sec = im_open_local( out, "temp_two", "t" ); + if( !ref || !sec ) + return( -1 ); + if( ref_in->Coding == IM_CODING_LABQ ) { + IMAGE *t1 = im_open_local( out, "temp:3", "p" ); + IMAGE *t2 = im_open_local( out, "temp:4", "p" ); + IMAGE *t3 = im_open_local( out, "temp:5", "p" ); + IMAGE *t4 = im_open_local( out, "temp:6", "p" ); + IMAGE *t5 = im_open_local( out, "temp:7", "p" ); + IMAGE *t6 = im_open_local( out, "temp:8", "p" ); + + if( !t1 || !t2 || !t3 || !t4 || !t5 || !t6 ) + return( -1 ); + if( im_extract_area( ref_in, t1, + overlap.left, overlap.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_area( sec_in, t2, + overlap.left - bottom.left, overlap.top - bottom.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_LabQ2Lab( t1, t3 ) || im_LabQ2Lab( t2, t4 ) || + im_Lab2disp( t3, t5, im_col_displays( 1 ) ) || + im_Lab2disp( t4, t6, im_col_displays( 1 ) ) ) + return( -1 ); + + /* Extract the green. + */ + if( im_extract_band( t5, ref, 1 ) || + im_extract_band( t6, sec, 1 ) ) + return( -1 ); + } + else if( ref_in->Coding == IM_CODING_NONE ) { + IMAGE *t1 = im_open_local( out, "temp:9", "p" ); + IMAGE *t2 = im_open_local( out, "temp:10", "p" ); + IMAGE *t3 = im_open_local( out, "temp:11", "p" ); + IMAGE *t4 = im_open_local( out, "temp:12", "p" ); + + if( !t1 || !t2 || !t3 || !t4 ) + return( -1 ); + if( im_extract_area( ref_in, t1, + overlap.left, overlap.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_area( sec_in, t2, + overlap.left - bottom.left, overlap.top - bottom.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_band( t1, t3, bandno_in ) || + im_extract_band( t2, t4, bandno_in ) ) + return( -1 ); + if( im_scale( t3, ref ) || + im_scale( t4, sec ) ) + return( -1 ); + } + else { + im_errormsg( "im_tbmosaic: unknown Coding type" ); + return( -1 ); + } + + /* Initialise and fill TIE_POINTS + */ + p_points = &points; + p_newpoints = &newpoints; + p_points->reference = ref_in->filename; + p_points->secondary = sec_in->filename; + p_points->nopoints = IM_MAXPOINTS; + p_points->deltax = 0; + p_points->deltay = 0; + p_points->halfcorsize = halfcorrelation; + p_points->halfareasize = halfarea; + + /* Initialise the structure + */ + for( i = 0; i < IM_MAXPOINTS; i++ ) { + p_points->x_reference[i] = 0; + p_points->y_reference[i] = 0; + p_points->x_secondary[i] = 0; + p_points->y_secondary[i] = 0; + p_points->contrast[i] = 0; + p_points->correlation[i] = 0.0; + p_points->dx[i] = 0.0; + p_points->dy[i] = 0.0; + p_points->deviation[i] = 0.0; + } + + /* Search ref for possible tie-points. Sets: p_points->contrast, + * p_points->x,y_reference. + */ + if( im__tbcalcon( ref, p_points ) ) + return( -1 ); + + /* For each candidate point, correlate against corresponding part of + * sec. Sets x,y_secondary and fills correlation and dx, dy. + */ + if( im__chkpair( ref, sec, p_points ) ) + return( -1 ); + + /* First call to im_clinear(). + */ + if( im__initialize( p_points ) ) + return( -1 ); + + /* Improve the selection of tiepoints until all abs(deviations) are + * < 1.0 by deleting all wrong points. + */ + if( im__improve( p_points, p_newpoints ) ) + return( -1 ); + + /* Average remaining offsets. + */ + if( im__avgdxdy( p_newpoints, &dx, &dy ) ) + return( -1 ); + + /* Offset with overlap position. + */ + *dx0 = -bottom.left + dx; + *dy0 = -bottom.top + dy; + + /* Write 1st order parameters too. + */ + *scale1 = newpoints.l_scale; + *angle1 = newpoints.l_angle; + *dx1 = newpoints.l_deltax; + *dy1 = newpoints.l_deltay; + + return( 0 ); +} + +int +im_tbmosaic( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ) +{ + int dx0, dy0; + double scale1, angle1, dx1, dy1; + IMAGE *ref2, *sec2; + IMAGE *dummy; + + /* Correct overlap. dummy is just a placeholder used to ensure that + * memory used by the analysis phase is freed as soon as possible. + */ + if( !(dummy = im_open( "placeholder:1", "p" )) ) + return( -1 ); + if( im__find_tboverlap( ref, sec, dummy, + bandno, + xref, yref, xsec, ysec, + halfcorrelation, halfarea, + &dx0, &dy0, + &scale1, &angle1, &dx1, &dy1 ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + /* Balance. + */ + if( im__balance( ref, sec, out, + &ref2, &sec2, + dx0, dy0, balancetype ) ) + return( -1 ); + + /* Merge top-bottom. + */ + if( im_tbmerge( ref2, sec2, out, dx0, dy0, mwidth ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/mosaicing/man3/Makefile.am b/libsrc/mosaicing/man3/Makefile.am new file mode 100644 index 00000000..3b3034f7 --- /dev/null +++ b/libsrc/mosaicing/man3/Makefile.am @@ -0,0 +1,17 @@ +man_MANS = \ + im_affine.3 \ + im_correl.3 \ + im_global_balance.3 \ + im_global_balance_float.3 \ + im_lrmerge.3 \ + im_lrmosaic.3 \ + im_match_linear.3 \ + im_match_linear_search.3 \ + im_similarity.3 \ + im_similarity_area.3 \ + im_tbmerge.3 \ + im_remosaic.3 \ + im_tbmosaic.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/mosaicing/man3/im_affine.3 b/libsrc/mosaicing/man3/im_affine.3 new file mode 100644 index 00000000..a7663852 --- /dev/null +++ b/libsrc/mosaicing/man3/im_affine.3 @@ -0,0 +1,40 @@ +.TH IM_AFFINE 3 "21 December 1999" +.SH NAME +im_affine \- apply an affine transform to an image +.SH SYNOPSIS +.B #include + +int im_affine(in, out, a, b, c, d, dx, dy, x, y, w, h) +.br +.B IMAGE *in, *out; +.br +.B double a, b, c, d, dx, dy; +.br +.B int x, y; +.br +.B int w, h; + +.SH DESCRIPTION +.B im_affine() +applies an affine transformation on the image held by the IMAGE descriptor +in and puts the result at the location pointed by the IMAGE descriptor out. in +many have any number of bands, be any size, and have any non-complex type. + +The transformation is described by a, b, c, d, dx, dy. The point (x,y) in +the input is mapped onto point (X,Y) in the output by + + X = a * x + b * y + dx + Y = c * x + d * y + dy + +The area of the output image given by w, h, x, y is generated. (0,0) is +the position of the transformed top-left-hand corner of the input image. +Function im_affine resamples the transformed image using bilinear +interpolation. + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH BUGS +As with most resamplers, im_affine(3) performs poorly at the edges of +images. +.SH SEE\ ALSO +im_similarity(3) diff --git a/libsrc/mosaicing/man3/im_correl.3 b/libsrc/mosaicing/man3/im_correl.3 new file mode 100644 index 00000000..89730696 --- /dev/null +++ b/libsrc/mosaicing/man3/im_correl.3 @@ -0,0 +1,49 @@ +.TH IM_CORREL 3 "13 May 1991" +.SH NAME +im_correl \- search for image match +.SH SYNOPSIS +#include + +int +.br +im_correl( IMAGE *ref, IMAGE *sec, +.br + int xref, int yref, int xsec, int ysec, +.br + int hwindowsize, int hsearchsize, +.br + double *correlation, int *x, int *y ) + +.SH DESCRIPTION +im_correl() is the base image-searching function used by the mosaicing +functions im_lrmosaic() and im_tbmosaic(), and by the matching funtions +im_match_linear() and im_match_linear_search(). + +It finds the position of the secondary image +.B sec +within +.B ref. +It searches the area around +.B xsec +, +.B ysec +for the best match for the area around +.B xref +, +.B yref. +It searches an area of size +.B hsearchsize +for a match of size +.B hwindowsize. +The position of the best match is returned, together with the correlation at +that point. + +Only the first band of each image is correlated. ref and sec may be very large +--- the function extracts and generated just the parts needed. Correlation is +done with im_spcor(); the position of the maximum is found with im_maxpos(). +.SH SEE ALSO +im_fastcor(3), im_spcor(3), im_maxpos(3), im_match_linear(3), im_lrmosaic(3). +.SH AUTHOR +J.Cupitt \- 22/02/93 +.SH COPYRIGHT +The National Gallery and Birkbeck College, 1989-1996. diff --git a/libsrc/mosaicing/man3/im_global_balance.3 b/libsrc/mosaicing/man3/im_global_balance.3 new file mode 100644 index 00000000..adb5edb4 --- /dev/null +++ b/libsrc/mosaicing/man3/im_global_balance.3 @@ -0,0 +1,53 @@ +.TH IM_GLOBAL_BALANCE 3 "13 May 1991" +.SH NAME +im_global_balance, im_global_balancef \- perform global mosaic balancing on +an image +.SH SYNOPSIS +#include + +int +.br +im_global_balance( IMAGE *in, IMAGE *out, double gamma ) + +int +.br +im_global_balance_float( IMAGE *in, IMAGE *out, double gamma ) + +.SH DESCRIPTION +These functions takes an image assembled with the mosaicing functions +(im_*merge*(), im_*mosaic*()), take it apart, and reassemble it, globally +optimising the image balance. This is useful for assembling image mosaics +from sources where the exposure is uncontrolled and may vary from tile to tile +--- such as video, or photographic sources. + +The function finds a set of factors, one for each of the input images, and +scales each image by its factor before reassembling. The factors are chosen so +as to minimise the average grey-level difference between neighboring images at +their overlaps. Trivial overlaps (where the width and height of the overlap +are both less than 20 pixels) are ignored. + +The gamma parameter is the gamma of the image input system. It is used during +brightness adjustment. Set to 1.0 to disable gamma, to 1.6 for a typical IR +vidicon camera, or 2.3 for a typical video camera. + +It relies on information left by the mosaicing functions in ".desc" files. If +the ".desc" file of the input image has been corrupted, or is strangely +complicated, or if any of the original input images have been moved or +deleted, the function can fail. + +The function will fail for mosaics larger than about 7 by 7 frames, since it +will run out of file descriptors (UNIX sets a limit of 256 per process). To +balance larger mosaics, just assemble them in 7x7 sections, balancing and +saving each part in turn, before loading, assembling and balancing the final +image. The function can also fail if there are significant mosaicing errors. + +im_global_balancef() works as im_global_balance(), but outputs a float +rather than a uchar image. This lets you adjust the range of the image +manually, if the automatically-found scales are causing burn-out. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_lrmerge(3), im_lrmosaic(3). +.SH COPYRIGHT +Birkbeck College and the National Gallery, 1991 - 1995. diff --git a/libsrc/mosaicing/man3/im_global_balance_float.3 b/libsrc/mosaicing/man3/im_global_balance_float.3 new file mode 100644 index 00000000..a0cd72cc --- /dev/null +++ b/libsrc/mosaicing/man3/im_global_balance_float.3 @@ -0,0 +1 @@ +.so man3/im_global_balance.3 diff --git a/libsrc/mosaicing/man3/im_lrmerge.3 b/libsrc/mosaicing/man3/im_lrmerge.3 new file mode 100644 index 00000000..0e295312 --- /dev/null +++ b/libsrc/mosaicing/man3/im_lrmerge.3 @@ -0,0 +1,40 @@ +.TH IM_LRMERGE 3 "13 May 1991" +.SH NAME +im_lrmerge, im_tbmerge, im_lrsmerge, im_tbsmerge, im_lrmergeb, im_tbmergeb \- merges two images with a given dx and dy +.SH SYNOPSIS +#include + +int im_lrmerge( ref, sec, out, dx, dy, mwidth ) +.br +IMAGE *ref, *sec, *out; +.br +int dx, dy; +.br +int mwidth; + +int im_tbmerge( ref, sec, out, dx, dy, mwidth ) +.br +IMAGE *ref, *sec, *out; +.br +int dx, dy; +.br +int mwidth; + +.SH DESCRIPTION +im_lrmerge() and im_tbmerge() merge the images held by the image descriptors +reference and secondary (ref and sec) according to the values dx and dy. dx +and dy give the displacement of sec relative to ref. The result is written on +the image descriptor out. The program carries out a smooth merge using a +raised cosine function. Both work for any image type, including LABPACK. + +The functions treat pixels with the value zero as "transparent", that is, +zero pixels in the overlap area do not contribute to the merge. This makes it +possible to join non-rectangular images. + +The "mwidth" parameter limits the maximum width (or height) of the blend area. +A value of "-1" means "unlimited". All other negative values are errors. + +.SH RETURN VALUE +Both functions return 0 on success and -1 on error. +.SH SEE ALSO +im_lrmosaic(3), im_tbmosaic(3), im_match_linear(3). diff --git a/libsrc/mosaicing/man3/im_lrmosaic.3 b/libsrc/mosaicing/man3/im_lrmosaic.3 new file mode 100644 index 00000000..8192075b --- /dev/null +++ b/libsrc/mosaicing/man3/im_lrmosaic.3 @@ -0,0 +1,104 @@ +.TH IM_LRMOSAIC 3 "13 May 1991" +.SH NAME +im_lrmosaic, im_tbmosaic \- mosaic two images using a zero order procedure +.SH SYNOPSIS +#include + +int +.br +im_lrmosaic( IMAGE *ref, IMAGE *sec, IMAGE *out, +.br + int bandno, +.br + int xref, int yref, int xsec, int ysec, +.br + int halfcorrelation, int halfarea, +.br + int balancetype, +.br + int mwidth ) + +int +.br +im_tbmosaic( IMAGE *ref, IMAGE *sec, IMAGE *out, +.br + int bandno, +.br + int xref, int yref, int xsec, int ysec, +.br + int halfcorrelation, int halfarea, +.br + int balancetype, +.br + int mwidth ) + +.SH DESCRIPTION +im_lrmosaic() and im_tbmosaic() are used to mosaic two images (left-right and +top-bottom respectively). Both input images are held by the IMAGE descriptors +reference and secondary whereas the output is written on the IMAGE descriptor +out. + +In order to carry out mosaicing, the coordinates of one tie point are +required. The tie point is expected to be in the overlapping area and has +coordinates (xref, yref) on the reference image and (xsec, ysec) on the +secondary image. The tie-point is not used as a start point for the search, +but is used to specify the overlap of the two images. + +The functions split the overlap area into three parts (top, middle and bottom; +or left, middle and right) and search the reference image in each part for the +20 best high contrast points. These 60 points are then searched for in the +secondary image, giving a set of 60 possible corrected vectors. + +A straight line is fitted through the 60 vectors, and points discarded which +lie a significant distance from the line. The line is then refitted to the +remaining points, and the process repeated until either all remaining points +lie on a straight line, or too many points have been discarded. + +If a good straight line fit is found, ref and sec are joined. If no fit was +found, the functions fail with an error message. Note that these functions +detect rotation: if the straight line found requires sec to be rotated, they +also fail with an error message. + +Each function requires three more parameters: + +halfcorrelationsize - sets the size of the fragments of ref + for which the function searches sec. The actual window + will be of size 2*halfcorrelationsize+1. We recommend a + value of 5. + +halfareasize - sets the size of the area of sec that is + searched. The actual area searched will be of size + 2*halfareasize+1. We recommend a value of 14. + +balancetype - sets the style of the balancing the functions + perform. Balancing finds the average value of pixels in + the overlap area, and scales the left and right images + (or top and bottom images) so as to make the images + match in average overlap. Possible values are: + + 0 - means do no balancing. + + 1 - means keep the left image unadjusted and adjust + the contrast of the right image to match the left. + + 2 - means keep the right image unadjusted and scale + the left image to match it. + + 3 - means adjust the contrast of both the left and + right images to bring both averages to a middle + value. The middle value chosen is weighted by the + number of pixels in each image: large images will be + adjusted less than small images. + +Balancing is useful for mosaicing frames from photographic or video sources +where exact colour control is impossible and exposure varies from frame to +frame. Balancing is only allowed for uncoded uchar images. + +The final "mwidth" parameter sets the maximum blend width, see im_lrmerge(3). + +See also im_global_balance() for a better way of balancing large mosaics. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_lrmerge(3), im_tbmerge(3), im_global_balance(3). diff --git a/libsrc/mosaicing/man3/im_match_linear.3 b/libsrc/mosaicing/man3/im_match_linear.3 new file mode 100644 index 00000000..d397a7a6 --- /dev/null +++ b/libsrc/mosaicing/man3/im_match_linear.3 @@ -0,0 +1 @@ +.so man3/im_match_linear_search.3 diff --git a/libsrc/mosaicing/man3/im_match_linear_search.3 b/libsrc/mosaicing/man3/im_match_linear_search.3 new file mode 100644 index 00000000..a5b52574 --- /dev/null +++ b/libsrc/mosaicing/man3/im_match_linear_search.3 @@ -0,0 +1,55 @@ +.TH MATCH_LINEAR 3 "13 May 1991" +.SH NAME +im_match_linear_search, im_match_linear \- resample to make a match +.SH SYNOPSIS + +#include + +int +.br +im_match_linear( IMAGE *ref, IMAGE *sec, IMAGE *out, +.br + int xr1, int yr1, int xs1, int ys1, +.br + int xr2, int yr2, int xs2, int ys2 ) + +int +.br +im_match_linear_search( IMAGE *ref, IMAGE *sec, IMAGE *out, +.br + int xr1, int yr1, int xs1, int ys1, +.br + int xr2, int yr2, int xs2, int ys2, +.br + int hwindowsize, int hsearchsize ) + +.SH DESCRIPTION +im_match_linear_search() attempts to transform sec to make it match +ref. The transformation is linear, that is, it only involves scale, +rotate and translate. + +im_match_linear_search() requires a pair of tie points to fix the parameters of +its transformation. You should pick points as far apart as possible to +increase accuracy. im_match_linear_search() will search the area in the image +around each tie point for a good fit, so your selection of points need not +be exact. WARNING! This searching process will fail for rotations of more +than about 10 degrees or for scales of more than about 10 percent. +The best you can hope for is < 1 pixel error, since the command does not +attempt sub-pixel correlation. + +hwindowsize and hsearchsize set the size of the area to be searched: we +recommend values of 5 and 14. + +The output image is positioned and clipped so that you can immediately +subtract it from orig to obtain pixel difference images. + +im_match_linear() works exactly as im_match_linear_search(), but does not +attempt to correlate to correct your tie points. It can thus be used for any +angle and any scale, but you must be far more careful in your selection. + +.SH SEE ALSO +im_lrmosaic(). +.SH AUTHORS +J.Ph.Laurent \- 12/12/92 +.br +J.Cupitt \- 22/02/93 diff --git a/libsrc/mosaicing/man3/im_remosaic.3 b/libsrc/mosaicing/man3/im_remosaic.3 new file mode 100644 index 00000000..406ecbc6 --- /dev/null +++ b/libsrc/mosaicing/man3/im_remosaic.3 @@ -0,0 +1,28 @@ +.TH IM_REMOSAIC 3 "7 Nov 2001" +.SH NAME +im_remosaic \- rebuild a mosaic, substituting filenames +.SH SYNOPSIS +#include + +int im_remosaic( IMAGE *in, IMAGE *out, +.br + const char *old_str, const char *new_str ) + +.SH DESCRIPTION +.B im_remosaic(3) +works rather as im_global_balance(). It takes apart the mosaiced image in and +rebuilds it, substituting images. + +Unlike +.B im_global_balance(3) +images are substituted based on their filenames. The rightmost occurence of +the string old_str is swapped for new_str, that file is opened, and that image +substituted for the old image. + +It's convenient for multispectral images. You can mosaic one band, then use +that mosaic as a template for mosaicing the others automatically. + +.SH RETURN VALUE +Functions return 0 on success and -1 on error. +.SH SEE ALSO +im_global_balance(3) diff --git a/libsrc/mosaicing/man3/im_similarity.3 b/libsrc/mosaicing/man3/im_similarity.3 new file mode 100644 index 00000000..e9a1151d --- /dev/null +++ b/libsrc/mosaicing/man3/im_similarity.3 @@ -0,0 +1 @@ +.so man3/im_similarity_area.3 diff --git a/libsrc/mosaicing/man3/im_similarity_area.3 b/libsrc/mosaicing/man3/im_similarity_area.3 new file mode 100644 index 00000000..330a480e --- /dev/null +++ b/libsrc/mosaicing/man3/im_similarity_area.3 @@ -0,0 +1,61 @@ +.TH IM_SIMILARITY_AREA 3 "13 January 1992" +.SH NAME +im_similarity_area, im_similarity \- apply a similarity transform to an image +.SH SYNOPSIS +.B #include + +int im_similarity_area(in, out, s, a, dx, dy, x, y, w, h) +.br +.B IMAGE *in, *out; +.br +.B double s, a, dx, dy; +.br +.B int x, y; +.br +.B int w, h; + +int im_similarity(in, out, s, a, dx, dy) +.br +.B IMAGE *in, *out; +.br +.B double s, a, dx, dy; + +.SH DESCRIPTION +.B im_similarity_area() +applies a similarity transformation on the image held by the IMAGE descriptor +in and puts the result at the location pointed by the IMAGE descriptor out. in +many have any number of bands, be any size, and have any non-complex type. + +The transformation is described by s, a, dx, dy. The point (x,y) in the input +is mapped onto point (X,Y) in the output by + + X = s * x - a * y + dx + Y = a * x + s * y + dy + +s and a do not correspond to scale and angle of the transformation; the actual +scale and angle are given by the equations: + + scale = sqrt(s*s + a*a) + angle = arctan(s/a). + +The area of the output image given by x, y, w, h is generated. (0,0) is +the position of the transformed top-left-hand corner of the input image. +Function im_similarity_area resamples the transformed image using bilinear +interpolation. + +im_similarity works exactly as im_similarity_area, but calculates x, y, w, h +for you such that the rectangle described just encloses all of the transformed +input pixels. +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH BUGS +As with most resamplers, im_similarity performs poorly at the edges of +images. +.SH SEE\ ALSO +similarity(1), similarity_area(1) +.SH AUTHORS +N. Dessipris -\ 13/01/1992 +.br +J.Ph. Laurent -\ 12/12/92 +.br +J. Cupitt -\ 22/02/93 diff --git a/libsrc/mosaicing/man3/im_tbmerge.3 b/libsrc/mosaicing/man3/im_tbmerge.3 new file mode 100644 index 00000000..ee6cab70 --- /dev/null +++ b/libsrc/mosaicing/man3/im_tbmerge.3 @@ -0,0 +1 @@ +.so man3/im_lrmerge.3 diff --git a/libsrc/mosaicing/man3/im_tbmosaic.3 b/libsrc/mosaicing/man3/im_tbmosaic.3 new file mode 100644 index 00000000..385c7d7d --- /dev/null +++ b/libsrc/mosaicing/man3/im_tbmosaic.3 @@ -0,0 +1 @@ +.so man3/im_lrmosaic.3 diff --git a/libsrc/mosaicing/match.c b/libsrc/mosaicing/match.c new file mode 100644 index 00000000..24b38788 --- /dev/null +++ b/libsrc/mosaicing/match.c @@ -0,0 +1,154 @@ +/* Match images. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Given a pair of points, return scale, angle, dx, dy to resample the 2nd + * image with. + */ +int +im__coeff( int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + double *a, double *b, double *dx, double *dy ) +{ + DOUBLEMASK *in, *out; + + if( !(in = im_create_dmask( "in", 4, 4 )) ) { + im_errormsg( "im__coeff: unable to allocate matrix" ); + return( -1 ); + } + + in->coeff[0] = (double)xs1; + in->coeff[1] = (double)-ys1; + in->coeff[2] = 1.0; + in->coeff[3] = 0.0; + in->coeff[4] = (double)ys1; + in->coeff[5] = (double)xs1; + in->coeff[6] = 0.0; + in->coeff[7] = 1.0; + in->coeff[8] = (double)xs2; + in->coeff[9] = (double)-ys2; + in->coeff[10] = 1.0; + in->coeff[11] = 0.0; + in->coeff[12] = (double)ys2; + in->coeff[13] = (double)xs2; + in->coeff[14] = 0.0; + in->coeff[15] = 1.0; + + if( !(out = im_matinv( in, "out" )) ) { + im_free_dmask( in ); + im_errormsg( "im__coeff: unable to invert matrix" ); + return( -1 ); + } + + *a = out->coeff[0]*xr1 + out->coeff[1]*yr1 + + out->coeff[2]*xr2 + out->coeff[3]*yr2; + *b = out->coeff[4]*xr1 + out->coeff[5]*yr1 + + out->coeff[6]*xr2 + out->coeff[7]*yr2; + *dx= out->coeff[8]*xr1 + out->coeff[9]*yr1 + + out->coeff[10]*xr2 + out->coeff[11]*yr2; + *dy= out->coeff[12]*xr1 + out->coeff[13]*yr1 + + out->coeff[14]*xr2 + out->coeff[15]*yr2; + + im_free_dmask( in ); + im_free_dmask( out ); + + return( 0 ); +} + +int +im_match_linear( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2 ) +{ + double a, b, dx, dy; + int w, h, x, y; + + /* Solve to get scale + rot + disp to obtain match. + */ + if( im__coeff( xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, + &a, &b, &dx, &dy ) ) + return( -1 ); + + /* Output area of ref image. + */ + x = 0; + y = 0; + w = ref->Xsize; + h = ref->Ysize; + + /* Transform image! + */ + if( im_similarity_area( sec, out, a, b, dx, dy, x, y, w, h ) ) + return( -1 ); + + return( 0 ); +} + +int +im_match_linear_search( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int hwindowsize, int hsearchsize ) +{ + int xs3, ys3; + int xs4, ys4; + double cor1, cor2; + + /* Search for new tie-points. + */ + if( im_correl( ref, sec, xr1, yr1, xs1, ys1, + hwindowsize, hsearchsize, &cor1, &xs3, &ys3 ) ) + return( -1 ); + if( im_correl( ref, sec, xr2, yr2, xs2, ys2, + hwindowsize, hsearchsize, &cor2, &xs4, &ys4 ) ) + return( -1 ); + + /* ... and match_linear. + */ + if( im_match_linear( ref, sec, out, + xr1, yr1, xs3, ys3, xr2, yr2, xs4, ys4 ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/merge.h b/libsrc/mosaicing/merge.h new file mode 100644 index 00000000..a65fc93a --- /dev/null +++ b/libsrc/mosaicing/merge.h @@ -0,0 +1,151 @@ +/* Declarations for code shared between im_lrmerge() and im_tbmerge(). + */ + +/* + + Copyright (C) 1991-2003 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Number of entries in blend table. As a power of two as well, for >>ing. + */ +#define BLEND_SHIFT (10) +#define BLEND_SIZE (1< +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "mosaic.h" +#include "merge.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* +#define DEBUG + */ + +/* define this to get old not-really-working joiner. +#define OLD + */ + +/* Like im_similarity(), but return the transform we generated. + */ +static int +apply_similarity( Transformation *trn, IMAGE *in, IMAGE *out, + double a, double b, double dx, double dy ) +{ + trn->iarea.left = 0; + trn->iarea.top = 0; + trn->iarea.width = in->Xsize; + trn->iarea.height = in->Ysize; + trn->a = a; + trn->b = -b; + trn->c = b; + trn->d = a; + trn->dx = dx; + trn->dy = dy; + im__transform_set_area( trn ); + if( im__transform_calc_inverse( trn ) ) + return( -1 ); + + if( im__affine( in, out, trn ) ) + return( -1 ); + + return( 0 ); +} + +/* A join function ... either left-right or top-bottom rotscalemerge. + */ +typedef int (*joinfn)( IMAGE *, IMAGE *, IMAGE *, + double, double, double, double, int ); + +/* similarity+lrmerge. + */ +int +im__lrmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out, + double a, double b, double dx, double dy, int mwidth ) +{ + Transformation trn; + IMAGE *t1 = im_open_local( out, "im_lrmosaic1:1", "p" ); + VBuf buf; + char text[1024]; + + /* Scale, rotate and displace sec. + */ + if( !t1 || apply_similarity( &trn, sec, t1, a, b, dx, dy ) ) + return( -1 ); + + /* And join to ref. + */ + if( im__lrmerge( ref, t1, out, + -trn.oarea.left, -trn.oarea.top, mwidth ) ) + return( -1 ); + + /* Note parameters in history file ... for global balance to pick up + * later. + */ + im_buf_init_static( &buf, text, 1024 ); + im_buf_appendf( &buf, "#LRROTSCALE <%s> <%s> <%s> <", + ref->filename, sec->filename, out->filename ); + im_buf_appendg( &buf, a ); + im_buf_appendf( &buf, "> <" ); + im_buf_appendg( &buf, b ); + im_buf_appendf( &buf, "> <" ); + im_buf_appendg( &buf, dx ); + im_buf_appendf( &buf, "> <" ); + im_buf_appendg( &buf, dy ); + im_buf_appendf( &buf, "> <%d>", mwidth ); + if( im_histlin( out, im_buf_all( &buf ) ) ) + return( -1 ); + + return( 0 ); +} + +/* similarity+tbmerge. + */ +int +im__tbmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out, + double a, double b, double dx, double dy, int mwidth ) +{ + Transformation trn; + IMAGE *t1 = im_open_local( out, "im_lrmosaic1:2", "p" ); + VBuf buf; + char text[1024]; + + /* Scale, rotate and displace sec. + */ + if( !t1 || apply_similarity( &trn, sec, t1, a, b, dx, dy ) ) + return( -1 ); + + /* And join to ref. + */ + if( im__tbmerge( ref, t1, out, + -trn.oarea.left, -trn.oarea.top, mwidth ) ) + return( -1 ); + + /* Note parameters in history file ... for global balance to pick up + * later. + */ + im_buf_init_static( &buf, text, 1024 ); + im_buf_appendf( &buf, "#TBROTSCALE <%s> <%s> <%s> <", + ref->filename, sec->filename, out->filename ); + im_buf_appendg( &buf, a ); + im_buf_appendf( &buf, "> <" ); + im_buf_appendg( &buf, b ); + im_buf_appendf( &buf, "> <" ); + im_buf_appendg( &buf, dx ); + im_buf_appendf( &buf, "> <" ); + im_buf_appendg( &buf, dy ); + im_buf_appendf( &buf, "> <%d>", mwidth ); + if( im_histlin( out, im_buf_all( &buf ) ) ) + return( -1 ); + + return( 0 ); +} + +/* Join two images, using a pair of tie-points as parameters. + */ +static int +rotjoin( IMAGE *ref, IMAGE *sec, IMAGE *out, joinfn jfn, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int mwidth ) +{ + double a, b, dx, dy; + + /* Solve to get scale + rot + disp. + */ + if( im__coeff( xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, + &a, &b, &dx, &dy ) ) + return( -1 ); + + /* Scale, rotate and displace sec. + */ + if( jfn( ref, sec, out, a, b, dx, dy, mwidth ) ) + return( -1 ); + + return( 0 ); +} + +/* 1st order left-right merge. + */ +int +im_lrmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int mwidth ) +{ + return( rotjoin( ref, sec, out, im__lrmerge1, + xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, mwidth ) ); +} + +/* 1st order top-bottom merge. + */ +int +im_tbmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int mwidth ) +{ + return( rotjoin( ref, sec, out, im__tbmerge1, + xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, mwidth ) ); +} + +/* Like rotjoin, but do a search to refine the tie-points. + */ +static int +rotjoin_search( IMAGE *ref, IMAGE *sec, IMAGE *out, joinfn jfn, + int bandno, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ) +{ + Transformation trn; + double cor1, cor2; + double a, b, dx, dy; + double xs3, ys3; + double xs4, ys4; + int xs5, ys5; + int xs6, ys6; + double xs7, ys7; + double xs8, ys8; + + /* Temps. + */ + IMAGE *t[3]; + + if( im_open_local_array( out, t, 3, "rotjoin_search", "p" ) ) + return( -1 ); + + /* Unpack LABQ to LABS for correlation. + */ + if( ref->Coding == IM_CODING_LABQ ) { + if( im_LabQ2LabS( ref, t[0] ) ) + return( -1 ); + } + else + t[0] = ref; + if( sec->Coding == IM_CODING_LABQ ) { + if( im_LabQ2LabS( sec, t[1] ) ) + return( -1 ); + } + else + t[1] = sec; + + /* Solve to get scale + rot + disp. + */ + if( im__coeff( xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, + &a, &b, &dx, &dy ) || + apply_similarity( &trn, t[1], t[2], a, b, dx, dy ) ) + return( -1 ); + + /* Map points on sec to rotated image. + */ + im__transform_forward( &trn, xs1, ys1, &xs3, &ys3 ); + im__transform_forward( &trn, xs2, ys2, &xs4, &ys4 ); + + /* Refine tie-points on rotated image. Remember the clip + * im__transform_set_area() has set, and move the sec tie-points + * accordingly. + */ + if( im_correl( t[0], t[2], xr1, yr1, + xs3 - trn.oarea.left, ys3 - trn.oarea.top, + halfcorrelation, halfarea, &cor1, &xs5, &ys5 ) ) + return( -1 ); + if( im_correl( t[0], t[2], xr2, yr2, + xs4 - trn.oarea.left, ys4 - trn.oarea.top, + halfcorrelation, halfarea, &cor2, &xs6, &ys6 ) ) + return( -1 ); + +#ifdef DEBUG + printf( "rotjoin_search: nudged pair 1 from %d, %d to %d, %d\n", + xs3 - trn.oarea.left, ys3 - trn.oarea.top, + xs5, ys5 ); + printf( "rotjoin_search: nudged pair 2 from %d, %d to %d, %d\n", + xs4 - trn.oarea.left, ys4 - trn.oarea.top, + xs6, ys6 ); +#endif /*DEBUG*/ + + /* Put the sec tie-points back into output space. + */ + xs5 += trn.oarea.left; + ys5 += trn.oarea.top; + xs6 += trn.oarea.left; + ys6 += trn.oarea.top; + + /* ... and now back to input space again. + */ + im__transform_inverse( &trn, xs5, ys5, &xs7, &ys7 ); + im__transform_inverse( &trn, xs6, ys6, &xs8, &ys8 ); + + /* Recalc the transform using the refined points. + */ + if( im__coeff( xr1, yr1, xs7, ys7, xr2, yr2, xs8, ys8, + &a, &b, &dx, &dy ) ) + return( -1 ); + + /* Scale and rotate final. + */ + if( jfn( ref, sec, out, a, b, dx, dy, mwidth ) ) + return( -1 ); + + return( 0 ); +} + +/* 1st order lr mosaic. + */ +int +im_lrmosaic1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ) +{ + return( rotjoin_search( ref, sec, out, im__lrmerge1, + bandno, + xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, + halfcorrelation, halfarea, balancetype, + mwidth ) ); +} + +/* 1st order tb mosaic. + */ +int +im_tbmosaic1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ) +{ + return( rotjoin_search( ref, sec, out, im__tbmerge1, + bandno, + xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, + halfcorrelation, halfarea, balancetype, mwidth ) ); +} + +#ifdef OLD +/* 1st order mosaic using im__find_lroverlap() ... does not work too well :( + * Look at im__find_lroverlap() for problem? + */ +static int +old_lrmosaic1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ) +{ + Transformation trn1, trn2; + int dx0, dy0; + double a, b, dx, dy; + double a1, b1, dx1, dy1; + double af, bf, dxf, dyf; + int xpos, ypos; + int xpos1, ypos1; + + /* Temps. + */ + IMAGE *t1 = im_open_local( out, "im_lrmosaic1:1", "p" ); + IMAGE *t2 = im_open_local( out, "im_lrmosaic1:2", "p" ); + IMAGE *dummy; + + if( !t1 || !t2 ) + return( -1 ); + + /* Solve to get scale + rot + disp. + */ + if( im__coeff( xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, + &a, &b, &dx, &dy ) || + apply_similarity( &trn1, sec, t1, a, b, dx, dy ) ) + return( -1 ); + + /* Correct tie-points. dummy is just a placeholder used to ensure that + * memory used by the analysis phase is freed as soon as possible. + */ + if( !(dummy = im_open( "placeholder:1", "p" )) ) + return( -1 ); + if( im__find_lroverlap( ref, t1, dummy, + bandno, + -trn1.area.left, -trn1.area.top, 0, 0, + halfcorrelation, halfarea, + &dx0, &dy0, + &a1, &b1, &dx1, &dy1 ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + /* Now combine the two transformations to get a corrected transform. + */ + af = a1 * a - b1 * b; + bf = a1 * b + b1 * a; + dxf = a1 * dx - b1 * dy + dx1; + dyf = b1 * dx + a1 * dy + dy1; + + printf( "transform was: a = %g, b = %g, dx = %g, dy = %g\n", + a, b, dx, dy ); + printf( "correction: a = %g, b = %g, dx = %g, dy = %g\n", + a1, b1, dx1, dy1 ); + printf( "final: a = %g, b = %g, dx = %g, dy = %g\n", + af, bf, dxf, dyf ); + + /* Scale and rotate final. + */ + if( apply_similarity( &trn2, sec, t2, af, bf, dxf, dyf ) ) + return( -1 ); + + printf( "disp: trn1 left = %d, top = %d\n", + trn1.area.left, trn1.area.top ); + printf( "disp: trn2 left = %d, top = %d\n", + trn2.area.left, trn2.area.top ); + + /* And join to ref. + */ + if( im_lrmerge( ref, t2, out, + -trn2.area.left, -trn2.area.top, mwidth ) ) + return( -1 ); + + return( 0 ); +} +#endif /*OLD*/ diff --git a/libsrc/mosaicing/mosaicing_dispatch.c b/libsrc/mosaicing/mosaicing_dispatch.c new file mode 100644 index 00000000..c12ea146 --- /dev/null +++ b/libsrc/mosaicing/mosaicing_dispatch.c @@ -0,0 +1,847 @@ +/* Function dispatch tables for mosaicing. + * + * J. Cupitt, 23/2/95 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Merge args. + */ +static im_arg_desc merge_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "dx" ), + IM_INPUT_INT( "dy" ), + IM_INPUT_INT( "mwidth" ) +}; + +/* Merge1 args. + */ +static im_arg_desc merge1_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xr1" ), + IM_INPUT_INT( "yr1" ), + IM_INPUT_INT( "xs1" ), + IM_INPUT_INT( "ys1" ), + IM_INPUT_INT( "xr2" ), + IM_INPUT_INT( "yr2" ), + IM_INPUT_INT( "xs2" ), + IM_INPUT_INT( "ys2" ), + IM_INPUT_INT( "mwidth" ) +}; + +/* Mosaic args. + */ +static im_arg_desc mosaic_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "bandno" ), + IM_INPUT_INT( "xr" ), + IM_INPUT_INT( "yr" ), + IM_INPUT_INT( "xs" ), + IM_INPUT_INT( "ys" ), + IM_INPUT_INT( "halfcorrelation" ), + IM_INPUT_INT( "halfarea" ), + IM_INPUT_INT( "balancetype" ), + IM_INPUT_INT( "mwidth" ) +}; + +/* Mosaic1 args. + */ +static im_arg_desc mosaic1_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "bandno" ), + IM_INPUT_INT( "xr1" ), + IM_INPUT_INT( "yr1" ), + IM_INPUT_INT( "xs1" ), + IM_INPUT_INT( "ys1" ), + IM_INPUT_INT( "xr2" ), + IM_INPUT_INT( "yr2" ), + IM_INPUT_INT( "xs2" ), + IM_INPUT_INT( "ys2" ), + IM_INPUT_INT( "halfcorrelation" ), + IM_INPUT_INT( "halfarea" ), + IM_INPUT_INT( "balancetype" ), + IM_INPUT_INT( "mwidth" ) +}; + +/* Call im_lrmosaic via arg vector. + */ +static int +lrmosaic_vec( im_object *argv ) +{ + int bandno = *((int *) argv[3]); + int xr = *((int *) argv[4]); + int yr = *((int *) argv[5]); + int xs = *((int *) argv[6]); + int ys = *((int *) argv[7]); + int halfcorrelation = *((int *) argv[8]); + int halfarea = *((int *) argv[9]); + int balancetype = *((int *) argv[10]); + int mwidth = *((int *) argv[11]); + + return( im_lrmosaic( argv[0], argv[1], argv[2], + bandno, + xr, yr, xs, ys, + halfcorrelation, halfarea, + balancetype, mwidth ) ); +} + +/* Call im_lrmosaic1 via arg vector. + */ +static int +lrmosaic1_vec( im_object *argv ) +{ + int bandno = *((int *) argv[3]); + int xr1 = *((int *) argv[4]); + int yr1 = *((int *) argv[5]); + int xs1 = *((int *) argv[6]); + int ys1 = *((int *) argv[7]); + int xr2 = *((int *) argv[8]); + int yr2 = *((int *) argv[9]); + int xs2 = *((int *) argv[10]); + int ys2 = *((int *) argv[11]); + int halfcorrelation = *((int *) argv[12]); + int halfarea = *((int *) argv[13]); + int balancetype = *((int *) argv[14]); + int mwidth = *((int *) argv[15]); + + return( im_lrmosaic1( argv[0], argv[1], argv[2], + bandno, + xr1, yr1, xs1, ys1, + xr2, yr2, xs2, ys2, + halfcorrelation, halfarea, + balancetype, mwidth ) ); +} + +/* Description of im_lrmosaic. + */ +static im_function lrmosaic_desc = { + "im_lrmosaic", /* Name */ + "left-right mosaic of ref and sec",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + lrmosaic_vec, /* Dispatch function */ + IM_NUMBER( mosaic_args ), /* Size of arg list */ + mosaic_args /* Arg list */ +}; + +static im_arg_desc find_overlap_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_INPUT_INT( "bandno" ), + IM_INPUT_INT( "xr" ), + IM_INPUT_INT( "yr" ), + IM_INPUT_INT( "xs" ), + IM_INPUT_INT( "ys" ), + IM_INPUT_INT( "halfcorrelation" ), + IM_INPUT_INT( "halfarea" ), + IM_OUTPUT_INT( "dx0" ), + IM_OUTPUT_INT( "dy0" ), + IM_OUTPUT_DOUBLE( "scale1" ), + IM_OUTPUT_DOUBLE( "angle1" ), + IM_OUTPUT_DOUBLE( "dx1" ), + IM_OUTPUT_DOUBLE( "dy1" ) +}; + +/* Call im__find_lroverlap via arg vector. + */ +static int +find_lroverlap_vec( im_object *argv ) +{ + int bandno = *((int *) argv[2]); + int xr = *((int *) argv[3]); + int yr = *((int *) argv[4]); + int xs = *((int *) argv[5]); + int ys = *((int *) argv[6]); + int halfcorrelation = *((int *) argv[7]); + int halfarea = *((int *) argv[8]); + int *dx0 = (int *) argv[9]; + int *dy0 = (int *) argv[10]; + double *scale1 = (double *) argv[11]; + double *angle1 = (double *) argv[12]; + double *dx1 = (double *) argv[13]; + double *dy1 = (double *) argv[14]; + + IMAGE *t; + int result; + + if( !(t = im_open( "find_lroverlap_vec", "p" )) ) + return( -1 ); + result = im__find_lroverlap( argv[0], argv[1], t, + bandno, + xr, yr, xs, ys, + halfcorrelation, halfarea, + dx0, dy0, scale1, angle1, dx1, dy1 ); + im_close( t ); + + return( result ); +} + +/* Description of im__find_lroverlap. + */ +static im_function find_lroverlap_desc = { + "im__find_lroverlap", /* Name */ + "search for left-right overlap of ref and sec",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + find_lroverlap_vec, /* Dispatch function */ + IM_NUMBER( find_overlap_args ), /* Size of arg list */ + find_overlap_args /* Arg list */ +}; + +/* Description of im_lrmosaic1. + */ +static im_function lrmosaic1_desc = { + "im_lrmosaic1", /* Name */ + "first-order left-right mosaic of ref and sec",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + lrmosaic1_vec, /* Dispatch function */ + IM_NUMBER( mosaic1_args ), /* Size of arg list */ + mosaic1_args /* Arg list */ +}; + +/* Call im_tbmosaic via arg vector. + */ +static int +tbmosaic_vec( im_object *argv ) +{ + int bandno = *((int *) argv[3]); + int x1 = *((int *) argv[4]); + int y1 = *((int *) argv[5]); + int x2 = *((int *) argv[6]); + int y2 = *((int *) argv[7]); + int halfcorrelation = *((int *) argv[8]); + int halfarea = *((int *) argv[9]); + int balancetype = *((int *) argv[10]); + int mwidth = *((int *) argv[11]); + + return( im_tbmosaic( argv[0], argv[1], argv[2], + bandno, + x1, y1, x2, y2, + halfcorrelation, halfarea, + balancetype, mwidth ) ); +} + +/* Call im_tbmosaic1 via arg vector. + */ +static int +tbmosaic1_vec( im_object *argv ) +{ + int bandno = *((int *) argv[3]); + int xr1 = *((int *) argv[4]); + int yr1 = *((int *) argv[5]); + int xs1 = *((int *) argv[6]); + int ys1 = *((int *) argv[7]); + int xr2 = *((int *) argv[8]); + int yr2 = *((int *) argv[9]); + int xs2 = *((int *) argv[10]); + int ys2 = *((int *) argv[11]); + int halfcorrelation = *((int *) argv[12]); + int halfarea = *((int *) argv[13]); + int balancetype = *((int *) argv[14]); + int mwidth = *((int *) argv[15]); + + return( im_tbmosaic1( argv[0], argv[1], argv[2], + bandno, + xr1, yr1, xs1, ys1, + xr2, yr2, xs2, ys2, + halfcorrelation, halfarea, + balancetype, mwidth ) ); +} + +/* Call im__find_tboverlap via arg vector. + */ +static int +find_tboverlap_vec( im_object *argv ) +{ + int bandno = *((int *) argv[2]); + int xr = *((int *) argv[3]); + int yr = *((int *) argv[4]); + int xs = *((int *) argv[5]); + int ys = *((int *) argv[6]); + int halfcorrelation = *((int *) argv[7]); + int halfarea = *((int *) argv[8]); + int *dx0 = (int *) argv[9]; + int *dy0 = (int *) argv[10]; + double *scale1 = (double *) argv[11]; + double *angle1 = (double *) argv[12]; + double *dx1 = (double *) argv[13]; + double *dy1 = (double *) argv[14]; + + IMAGE *t; + int result; + + if( !(t = im_open( "find_tboverlap_vec", "p" )) ) + return( -1 ); + result = im__find_tboverlap( argv[0], argv[1], t, + bandno, + xr, yr, xs, ys, + halfcorrelation, halfarea, + dx0, dy0, scale1, angle1, dx1, dy1 ); + im_close( t ); + + return( result ); +} + +/* Description of im__find_tboverlap. + */ +static im_function find_tboverlap_desc = { + "im__find_tboverlap", /* Name */ + "search for top-bottom overlap of ref and sec",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + find_tboverlap_vec, /* Dispatch function */ + IM_NUMBER( find_overlap_args ), /* Size of arg list */ + find_overlap_args /* Arg list */ +}; + +/* Description of im_tbmosaic. + */ +static im_function tbmosaic_desc = { + "im_tbmosaic", /* Name */ + "top-bottom mosaic of in1 and in2",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + tbmosaic_vec, /* Dispatch function */ + IM_NUMBER( mosaic_args ), /* Size of arg list */ + mosaic_args /* Arg list */ +}; + +/* Description of im_tbmosaic1. + */ +static im_function tbmosaic1_desc = { + "im_tbmosaic1", /* Name */ + "first-order top-bottom mosaic of ref and sec",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + tbmosaic1_vec, /* Dispatch function */ + IM_NUMBER( mosaic1_args ), /* Size of arg list */ + mosaic1_args /* Arg list */ +}; + +/* Call im_lrmerge via arg vector. + */ +static int +lrmerge_vec( im_object *argv ) +{ + int dx = *((int *) argv[3]); + int dy = *((int *) argv[4]); + int mwidth = *((int *) argv[5]); + + return( im_lrmerge( argv[0], argv[1], argv[2], dx, dy, mwidth ) ); +} + +/* Call im_lrmerge1 via arg vector. + */ +static int +lrmerge1_vec( im_object *argv ) +{ + int xr1 = *((int *) argv[3]); + int yr1 = *((int *) argv[4]); + int xs1 = *((int *) argv[5]); + int ys1 = *((int *) argv[6]); + int xr2 = *((int *) argv[7]); + int yr2 = *((int *) argv[8]); + int xs2 = *((int *) argv[9]); + int ys2 = *((int *) argv[10]); + int mwidth = *((int *) argv[11]); + + return( im_lrmerge1( argv[0], argv[1], argv[2], + xr1, yr1, xs1, ys1, + xr2, yr2, xs2, ys2, mwidth ) ); +} + +/* Description of im_lrmerge. + */ +static im_function lrmerge_desc = { + "im_lrmerge", /* Name */ + "left-right merge of in1 and in2",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + lrmerge_vec, /* Dispatch function */ + IM_NUMBER( merge_args ), /* Size of arg list */ + merge_args /* Arg list */ +}; + +/* Description of im_lrmerge1. + */ +static im_function lrmerge1_desc = { + "im_lrmerge1", /* Name */ + "first-order left-right merge of ref and sec",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + lrmerge1_vec, /* Dispatch function */ + IM_NUMBER( merge1_args ), /* Size of arg list */ + merge1_args /* Arg list */ +}; + +/* Call im_tbmerge via arg vector. + */ +static int +tbmerge_vec( im_object *argv ) +{ + int dx = *((int *) argv[3]); + int dy = *((int *) argv[4]); + int mwidth = *((int *) argv[5]); + + return( im_tbmerge( argv[0], argv[1], argv[2], dx, dy, mwidth ) ); +} + +/* Call im_tbmerge1 via arg vector. + */ +static int +tbmerge1_vec( im_object *argv ) +{ + int xr1 = *((int *) argv[3]); + int yr1 = *((int *) argv[4]); + int xs1 = *((int *) argv[5]); + int ys1 = *((int *) argv[6]); + int xr2 = *((int *) argv[7]); + int yr2 = *((int *) argv[8]); + int xs2 = *((int *) argv[9]); + int ys2 = *((int *) argv[10]); + int mwidth = *((int *) argv[11]); + + return( im_tbmerge1( argv[0], argv[1], argv[2], + xr1, yr1, xs1, ys1, + xr2, yr2, xs2, ys2, mwidth ) ); +} + +/* Description of im_tbmerge. + */ +static im_function tbmerge_desc = { + "im_tbmerge", /* Name */ + "top-bottom merge of in1 and in2",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + tbmerge_vec, /* Dispatch function */ + IM_NUMBER( merge_args ), /* Size of arg list */ + merge_args /* Arg list */ +}; + +/* Description of im_tbmerge1. + */ +static im_function tbmerge1_desc = { + "im_tbmerge1", /* Name */ + "first-order top-bottom merge of in1 and in2",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + tbmerge1_vec, /* Dispatch function */ + IM_NUMBER( merge1_args ), /* Size of arg list */ + merge1_args /* Arg list */ +}; + +/* affine args + */ +static im_arg_desc affine_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "a" ), + IM_INPUT_DOUBLE( "b" ), + IM_INPUT_DOUBLE( "c" ), + IM_INPUT_DOUBLE( "d" ), + IM_INPUT_DOUBLE( "dx" ), + IM_INPUT_DOUBLE( "dy" ), + IM_INPUT_INT( "x" ), + IM_INPUT_INT( "y" ), + IM_INPUT_INT( "w" ), + IM_INPUT_INT( "h" ) +}; + +/* Call im_affine via arg vector. + */ +static int +affine_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + double b = *((double *) argv[3]); + double c = *((double *) argv[4]); + double d = *((double *) argv[5]); + double dx = *((double *) argv[6]); + double dy = *((double *) argv[7]); + int x = *((int *) argv[8]); + int y = *((int *) argv[9]); + int w = *((int *) argv[10]); + int h = *((int *) argv[11]); + + return( im_affine( argv[0], argv[1], a, b, c, d, dx, dy, x, y, w, h ) ); +} + +/* Description of im_affine. + */ +static im_function affine_desc = { + "im_affine", /* Name */ + "affine transform", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + affine_vec, /* Dispatch function */ + IM_NUMBER( affine_args ), /* Size of arg list */ + affine_args /* Arg list */ +}; + +/* similarity args + */ +static im_arg_desc similarity_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "a" ), + IM_INPUT_DOUBLE( "b" ), + IM_INPUT_DOUBLE( "dx" ), + IM_INPUT_DOUBLE( "dy" ) +}; + +/* Call im_similarity via arg vector. + */ +static int +similarity_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + double b = *((double *) argv[3]); + double dx = *((double *) argv[4]); + double dy = *((double *) argv[5]); + + return( im_similarity( argv[0], argv[1], a, b, dx, dy ) ); +} + +/* Description of im_similarity. + */ +static im_function similarity_desc = { + "im_similarity", /* Name */ + "similarity transformation", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + similarity_vec, /* Dispatch function */ + IM_NUMBER( similarity_args ), /* Size of arg list */ + similarity_args /* Arg list */ +}; + +/* match_linear args + */ +static im_arg_desc match_linear_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xref1" ), + IM_INPUT_INT( "yref1" ), + IM_INPUT_INT( "xsec1" ), + IM_INPUT_INT( "ysec1" ), + IM_INPUT_INT( "xref2" ), + IM_INPUT_INT( "yref2" ), + IM_INPUT_INT( "xsec2" ), + IM_INPUT_INT( "ysec2" ) +}; + +/* Call im_match_linear via arg vector. + */ +static int +match_linear_vec( im_object *argv ) +{ + int xref1 = *((int *) argv[3]); + int yref1 = *((int *) argv[4]); + int xsec1 = *((int *) argv[5]); + int ysec1 = *((int *) argv[6]); + int xref2 = *((int *) argv[7]); + int yref2 = *((int *) argv[8]); + int xsec2 = *((int *) argv[9]); + int ysec2 = *((int *) argv[10]); + + return( im_match_linear( argv[0], argv[1], argv[2], + xref1, yref1, xsec1, ysec1, + xref2, yref2, xsec2, ysec2 ) ); +} + +/* Description of im_match_linear. + */ +static im_function match_linear_desc = { + "im_match_linear", /* Name */ + "resample ref so that tie-points match", + IM_FN_PIO, /* Flags */ + match_linear_vec, /* Dispatch function */ + IM_NUMBER( match_linear_args ), /* Size of arg list */ + match_linear_args /* Arg list */ +}; + +/* match_linear_search args + */ +static im_arg_desc match_linear_search_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xref1" ), + IM_INPUT_INT( "yref1" ), + IM_INPUT_INT( "xsec1" ), + IM_INPUT_INT( "ysec1" ), + IM_INPUT_INT( "xref2" ), + IM_INPUT_INT( "yref2" ), + IM_INPUT_INT( "xsec2" ), + IM_INPUT_INT( "ysec2" ), + IM_INPUT_INT( "hwindowsize" ), + IM_INPUT_INT( "hsearchsize" ) +}; + +/* Call im_match_linear_search via arg vector. + */ +static int +match_linear_search_vec( im_object *argv ) +{ + int xref1 = *((int *) argv[3]); + int yref1 = *((int *) argv[4]); + int xsec1 = *((int *) argv[5]); + int ysec1 = *((int *) argv[6]); + int xref2 = *((int *) argv[7]); + int yref2 = *((int *) argv[8]); + int xsec2 = *((int *) argv[9]); + int ysec2 = *((int *) argv[10]); + int hwin = *((int *) argv[11]); + int hsrch = *((int *) argv[12]); + + return( im_match_linear_search( argv[0], argv[1], argv[2], + xref1, yref1, xsec1, ysec1, + xref2, yref2, xsec2, ysec2, + hwin, hsrch ) ); +} + +/* Description of im_match_linear_search. + */ +static im_function match_linear_search_desc = { + "im_match_linear_search", /* Name */ + "search sec, then resample so that tie-points match", + IM_FN_PIO, /* Flags */ + match_linear_search_vec, /* Dispatch function */ + IM_NUMBER( match_linear_search_args ),/* Size of arg list */ + match_linear_search_args /* Arg list */ +}; + +/* correl args + */ +static im_arg_desc correl_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_INPUT_INT( "xref" ), + IM_INPUT_INT( "yref" ), + IM_INPUT_INT( "xsec" ), + IM_INPUT_INT( "ysec" ), + IM_INPUT_INT( "hwindowsize" ), + IM_INPUT_INT( "hsearchsize" ), + IM_OUTPUT_DOUBLE( "correlation" ), + IM_OUTPUT_INT( "x" ), + IM_OUTPUT_INT( "y" ) +}; + +/* Call im_correl via arg vector. + */ +static int +correl_vec( im_object *argv ) +{ + int xref = *((int *) argv[2]); + int yref = *((int *) argv[3]); + int xsec = *((int *) argv[4]); + int ysec = *((int *) argv[5]); + int cor = *((int *) argv[6]); + int area = *((int *) argv[7]); + int *x = (int *) argv[8]; + int *y = (int *) argv[9]; + double *correlation = (double *) argv[10]; + + return( im_correl( argv[0], argv[1], + xref, yref, xsec, ysec, cor, area, correlation, x, y ) ); +} + +/* Description of im_correl. + */ +static im_function correl_desc = { + "im_correl", /* Name */ + "search area around sec for match for area around ref", + IM_FN_PIO, /* Flags */ + correl_vec, /* Dispatch function */ + IM_NUMBER( correl_args ), /* Size of arg list */ + correl_args /* Arg list */ +}; + +/* similarity_area args + */ +static im_arg_desc similarity_area_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "a" ), + IM_INPUT_DOUBLE( "b" ), + IM_INPUT_DOUBLE( "dx" ), + IM_INPUT_DOUBLE( "dy" ), + IM_INPUT_INT( "x" ), + IM_INPUT_INT( "y" ), + IM_INPUT_INT( "w" ), + IM_INPUT_INT( "h" ) +}; + +/* Call im_similarity_area via arg vector. + */ +static int +similarity_area_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + double b = *((double *) argv[3]); + double dx = *((double *) argv[4]); + double dy = *((double *) argv[5]); + int x = *((int *) argv[6]); + int y = *((int *) argv[7]); + int w = *((int *) argv[8]); + int h = *((int *) argv[9]); + + return( im_similarity_area( argv[0], argv[1], a, b, dx, dy, + x, y, w, h ) ); +} + +/* Description of im_similarity_area. + */ +static im_function similarity_area_desc = { + "im_similarity_area", /* Name */ + "output area xywh of similarity transformation", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + similarity_area_vec, /* Dispatch function */ + IM_NUMBER( similarity_area_args ), /* Size of arg list */ + similarity_area_args /* Arg list */ +}; + +/* global_balance args + */ +static im_arg_desc global_balance_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "gamma" ) +}; + +/* Call im_global_balance via arg vector. + */ +static int +global_balance_vec( im_object *argv ) +{ + double gamma = *((double *) argv[2]); + + return( im_global_balance( argv[0], argv[1], gamma ) ); +} + +/* Description of im_global_balance. + */ +static im_function global_balance_desc = { + "im_global_balance", /* Name */ + "automatically rebuild mosaic with balancing", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + global_balance_vec, /* Dispatch function */ + IM_NUMBER( global_balance_args ), /* Size of arg list */ + global_balance_args /* Arg list */ +}; + +/* Call im_global_balancef via arg vector. + */ +static int +global_balancef_vec( im_object *argv ) +{ + double gamma = *((double *) argv[2]); + + return( im_global_balancef( argv[0], argv[1], gamma ) ); +} + +/* Description of im_global_balancef. + */ +static im_function global_balancef_desc = { + "im_global_balancef", /* Name */ + "automatically rebuild mosaic with balancing, float output", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + global_balancef_vec, /* Dispatch function */ + IM_NUMBER( global_balance_args ), /* Size of arg list */ + global_balance_args /* Arg list */ +}; + +/* remosaic args + */ +static im_arg_desc remosaic_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "old_str" ), + IM_INPUT_STRING( "new_str" ) +}; + +/* Call im_remosaic via arg vector. + */ +static int +remosaic_vec( im_object *argv ) +{ + return( im_remosaic( argv[0], argv[1], argv[2], argv[3] ) ); +} + +/* Description of im_remosaic. + */ +static im_function remosaic_desc = { + "im_remosaic", /* Name */ + "automatically rebuild mosaic with new files", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + remosaic_vec, /* Dispatch function */ + IM_NUMBER( remosaic_args ),/* Size of arg list */ + remosaic_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *mos_list[] = { + &affine_desc, + &correl_desc, + &find_lroverlap_desc, + &find_tboverlap_desc, + &global_balance_desc, + &global_balancef_desc, + &lrmerge_desc, + &lrmerge1_desc, + &lrmosaic_desc, + &lrmosaic1_desc, + &match_linear_desc, + &match_linear_search_desc, + &remosaic_desc, + &similarity_area_desc, + &similarity_desc, + &tbmerge_desc, + &tbmerge1_desc, + &tbmosaic_desc, + &tbmosaic1_desc +}; + +/* Package of functions. + */ +im_package im__mosaicing = { + "mosaicing", + IM_NUMBER( mos_list ), + mos_list +}; diff --git a/libsrc/mosaicing/similarity.c b/libsrc/mosaicing/similarity.c new file mode 100644 index 00000000..5db8883f --- /dev/null +++ b/libsrc/mosaicing/similarity.c @@ -0,0 +1,159 @@ +/* @(#) im_similarity_area() ... similarity transform. Like affine, but + * @(#) rotate/scale only. + * @(#) + * @(#) int im_similarity_area(in, out, a, b, dx, dy, w, h, x, y) + * @(#) IMAGE *in, *out; + * @(#) double a, b, dx, dy; + * @(#) int w, h, x, y; + * @(#) + * @(#) Forward transform + * @(#) X = a * x - b * y + dx + * @(#) Y = b * x + a * y + dy + * @(#) + * @(#) x and y are the coordinates in input image. + * @(#) X and Y are the coordinates in output image. + * @(#) (0,0) is the upper left corner. + * @(#) + * @(#) a and b DO NOT correspond to scale and angle directly + * @(#) + * @(#) scale = sqrt(a*a + b*b) , angle = arctan(a/b) + * @(#) + * @(#) im_similarity_area() returns 0 on success and -1 on error + * @(#) + * + * 3/3/98 JC + * - redone as wrapper for im_affine(), compatibility only + * 8/4/04 + * - transform rounding redone as part of the new im_embed thing + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#include "merge.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Call point from VIPS. + */ +int +im_similarity_area( IMAGE *in, IMAGE *out, + double a, double b, double dx, double dy, + int ox, int oy, int ow, int oh ) +{ + Transformation trn; + + trn.oarea.left = ox; + trn.oarea.top = oy; + trn.oarea.width = ow; + trn.oarea.height = oh; + trn.iarea.left = 0; + trn.iarea.top = 0; + trn.iarea.width = in->Xsize; + trn.iarea.height = in->Ysize; + trn.a = a; + trn.b = -b; + trn.c = b; + trn.d = a; + trn.dx = dx; + trn.dy = dy; + + return( im__affine( in, out, &trn ) ); +} + +/* Set output area of trn so that it just holds all of our input pels. + */ +void +im__transform_set_area( Transformation *trn ) +{ + double xA, xB, xC, xD; + double yA, yB, yC, yD; + int xmin, xmax, ymin, ymax; + + im__transform_forward( trn, + trn->iarea.left, trn->iarea.top, + &xA, &yA ); + im__transform_forward( trn, + IM_RECT_RIGHT( &trn->iarea ) - 1, trn->iarea.top, + &xB, &yB ); + im__transform_forward( trn, + trn->iarea.left, IM_RECT_BOTTOM( &trn->iarea ) - 1, + &xC, &yC ); + im__transform_forward( trn, + IM_RECT_RIGHT( &trn->iarea ) - 1, + IM_RECT_BOTTOM( &trn->iarea ) - 1, + &xD, &yD ); + + xmin = IM_MIN( xA, IM_MIN( xB, IM_MIN( xC, xD ) ) ); + ymin = IM_MIN( yA, IM_MIN( yB, IM_MIN( yC, yD ) ) ); + xmax = IM_MAX( xA, IM_MAX( xB, IM_MAX( xC, xD ) ) ); + ymax = IM_MAX( yA, IM_MAX( yB, IM_MAX( yC, yD ) ) ); + + trn->oarea.left = xmin; + trn->oarea.top = ymin; + trn->oarea.width = xmax - xmin + 1; + trn->oarea.height = ymax - ymin + 1; +} + +/* Output the rect holding all our input PELs. + */ +int +im_similarity( IMAGE *in, IMAGE *out, + double a, double b, double dx, double dy ) +{ + Transformation trn; + + trn.iarea.left = 0; + trn.iarea.top = 0; + trn.iarea.width = in->Xsize; + trn.iarea.height = in->Ysize; + trn.a = a; + trn.b = -b; + trn.c = b; + trn.d = a; + trn.dx = dx; + trn.dy = dy; + im__transform_set_area( &trn ); + + if( im__affine( in, out, &trn ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/other/Makefile.am b/libsrc/other/Makefile.am new file mode 100644 index 00000000..e7759c25 --- /dev/null +++ b/libsrc/other/Makefile.am @@ -0,0 +1,20 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libother.la + +libother_la_SOURCES = \ + cooc_funcs.c \ + glds_funcs.c \ + im_benchmark.c \ + im_dif_std.c \ + im_eye.c \ + im_grey.c \ + im_make_xy.c \ + im_meanstd.c \ + im_simcontr.c \ + im_sines.c \ + im_spatres.c \ + im_zone.c \ + other_dispatch.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/other/cooc_funcs.c b/libsrc/other/cooc_funcs.c new file mode 100644 index 00000000..5f3d9f41 --- /dev/null +++ b/libsrc/other/cooc_funcs.c @@ -0,0 +1,472 @@ +/* @(#) Calculates the cooccurrence matrix of an image and some of its + * @(#) features. The 256x256 cooccurrence matrix of im is held by m + * @(#) There should be enough margin around the box so the (dx,dy) can + * @(#) access neighbouring pixels outside the box + * @(#) + * @(#) Usage: + * @(#) int im_cooc_matrix(im, m, xpos, ypos, xsize, ysize, dx, dy, sym_flag) + * @(#) IMAGE *im, *m; + * @(#) int xpos, ypos, xsize, ysize; location of the box within im + * @(#) int dx, dy; displacements + * @(#) int sym_flag; + * @(#) + * @(#) int im_cooc_asm(m, asmoment) + * @(#) IMAGE *m; + * @(#) double *asmoment; + * @(#) + * @(#) int im_cooc_contrast(m, contrast) + * @(#) IMAGE *m; + * @(#) double *contrast; + * @(#) + * @(#) int im_cooc_correlation(m, correlation) + * @(#) IMAGE *m; + * @(#) double *correlation; + * @(#) + * @(#) int im_cooc_entropy(m, entropy) + * @(#) IMAGE *m; + * @(#) double *entropy; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * + * Copyright: N. Dessipris 1991 + * Written on: 2/12/1991 + * Updated on: 2/12/1991 + * 22/7/93 JC + * - extern decls removed + * - im_incheck() calls added + * 28/5/97 JC + * - protos added :( + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static +int im_cooc_sym(im, m, xpos, ypos, xsize, ysize, dx, dy) +IMAGE *im, *m; +int xpos, ypos, xsize, ysize; /* location of the box within im */ +int dx, dy; /* displacements */ +{ + PEL *input, *cpinput; + int *buf, *pnt, *cpnt; + double *line, *cpline; + int x, y; + int offset; + int bufofst; + int tempA, tempB; + int norm; + + if (im_iocheck(im, m) == -1) + { im_errormsg("im_cooc_sym: im_iocheck failed"); return(-1);} + if ((im->Bands != 1)||(im->Bbits != IM_BBITS_BYTE)||(im->BandFmt != IM_BANDFMT_UCHAR)) + { + im_errormsg("im_cooc_sym: Unable to accept input"); + return(-1); + } + if ( (xpos + xsize + dx > im->Xsize)|| (ypos + ysize + dy > im->Ysize) ) + { im_errormsg("im_cooc_sym: wrong args"); return(-1); } + if (im_cp_desc(m, im) == -1) + { im_errormsg("im_cooc_sym: im_cp_desc failed"); return(-1);} + m->Xsize = 256; + m->Ysize = 256; + m->Bbits = IM_BBITS_DOUBLE; + m->BandFmt = IM_BANDFMT_DOUBLE; + m->Type = IM_TYPE_B_W; + if (im_setupout(m) == -1) + {im_errormsg("im_cooc_sym: im_setupout failed"); return(-1);} +/* malloc space to keep the read values */ + buf = (int *)calloc( (unsigned)m->Xsize*m->Ysize, sizeof(int) ); + line = (double *)calloc( (unsigned)m->Xsize * m->Bands, sizeof(double)); + if ( (buf == NULL) || (line == NULL) ) + { im_errormsg("im_cooc_sym: calloc failed"); return(-1); } + input = (PEL*)im->data; + input += ( ypos * im->Xsize + xpos ); + offset = dy * im->Xsize + dx; + for ( y=0; yXsize; + for ( x=0; xXsize * tempB; + (*(buf + bufofst))++; + bufofst = tempB + m->Xsize * tempA; + (*(buf + bufofst))++; + cpinput++; + } + } + + norm = xsize * ysize * 2; + pnt = buf; + for ( y=0; yYsize; y++ ) + { + cpnt = pnt; + pnt += m->Xsize; + cpline = line; + for (x=0; xXsize; x++) + *cpline++ = (double)(*cpnt++)/(double)norm; + if (im_writeline( y, m, (PEL *) line ) == -1) + { + im_errormsg("im_cooc_sym: unable to im_writeline"); + return(-1); + } + } + free((char*)buf); + free((char*)line); + return(0); +} + +static +int im_cooc_ord(im, m, xpos, ypos, xsize, ysize, dx, dy) +IMAGE *im, *m; +int xpos, ypos, xsize, ysize; /* location of the box within im */ +int dx, dy; /* displacements */ +{ + PEL *input, *cpinput; + int *buf, *pnt, *cpnt; + double *line, *cpline; + int x, y; + int offset; + int bufofst; + int tempA, tempB; + int norm; + + if (im_iocheck(im, m) == -1) + { im_errormsg("im_cooc_ord: im_iocheck failed"); return(-1);} + if ((im->Bands != 1)||(im->Bbits != IM_BBITS_BYTE)||(im->BandFmt != IM_BANDFMT_UCHAR)) + { + im_errormsg("im_cooc_ord: Unable to accept input"); + return(-1); + } + if ( (xpos + xsize + dx > im->Xsize)|| (ypos + ysize + dy > im->Ysize) ) + { im_errormsg("im_cooc_ord: wrong args"); return(-1); } + if (im_cp_desc(m, im) == -1) + { im_errormsg("im_cooc_ord: im_cp_desc failed"); return(-1);} + m->Xsize = 256; + m->Ysize = 256; + m->Bbits = IM_BBITS_DOUBLE; + m->BandFmt = IM_BANDFMT_DOUBLE; + if (im_setupout(m) == -1) + {im_errormsg("im_cooc_ord: im_setupout failed"); return(-1);} +/* malloc space to keep the read values */ + buf = (int *)calloc( (unsigned)m->Xsize*m->Ysize, sizeof(int) ); + line = (double *)calloc( (unsigned)m->Xsize * m->Bands, sizeof(double)); + if ( (buf == NULL) || (line == NULL) ) + { im_errormsg("im_cooc_ord: calloc failed"); return(-1); } + input = (PEL*)im->data; + input += ( ypos * im->Xsize + xpos ); + offset = dy * im->Xsize + dx; + for ( y=0; yXsize; + for ( x=0; xXsize * tempB; + (*(buf + bufofst))++; + cpinput++; + } + } + + norm = xsize * ysize; + pnt = buf; + for ( y=0; yYsize; y++ ) + { + cpnt = pnt; + pnt += m->Xsize; + cpline = line; + for (x=0; xXsize; x++) + *cpline++ = (double)(*cpnt++)/(double)norm; + if (im_writeline( y, m, (PEL *) line ) == -1) + { + im_errormsg("im_cooc_ord: unable to im_writeline"); + return(-1); + } + } + free((char*)buf); + free((char*)line); + return(0); +} + +/* Keep the coocurrence matrix as a 256x256x1 double image */ + +int +im_cooc_matrix( IMAGE *im, IMAGE *m, + int xp, int yp, int xs, int ys, int dx, int dy, int flag ) +{ + if (flag == 0) + return( im_cooc_ord(im, m, xp, yp, xs, ys, dx, dy) ); + else if (flag == 1) /* symmetrical cooc */ + return( im_cooc_sym(im, m, xp, yp, xs, ys, dx, dy) ); + else + { im_errormsg("im_cooc_matrix: wrong flag!"); return(-1); } +} + +/* Calculate contrast, asmoment, entropy and correlation + */ +int +im_cooc_asm( IMAGE *m, double *asmoment ) +{ + double temp, tmpasm, *pnt; + int i; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 256 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { + im_errormsg("im_cooc_asm: unable to accept input"); + return(-1); + } + tmpasm = 0.0; + pnt = (double*)m->data; + for(i=0; iXsize * m->Ysize; i++) + { + temp = *pnt++; + tmpasm += temp * temp; + } + *asmoment = tmpasm; + return(0); +} + +int +im_cooc_contrast( IMAGE *m, double *contrast ) +{ + double dtemp, tmpcon, *pnt, *cpnt; + int x, y; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 256 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { + im_errormsg("im_cooc_contrast: unable to accept input"); + return(-1); + } + tmpcon = 0.0; + pnt = (double*)m->data; + for(y=0; yYsize; y++) + { + cpnt = pnt; + pnt += m->Xsize; + for(x=0; xXsize; x++) + { + dtemp = (double)( (y-x)*(y-x) ); + tmpcon += dtemp * (*cpnt); + cpnt++; + } + } + + *contrast = tmpcon; + return(0); +} + +static void +stats(buffer, size, pmean, pstd) +double *buffer; /* buffer contains the frequency distributions f[i] */ +int size; /* Note that sum(f[i]) = 1.0 and that the */ + /* cooccurence matrix is symmetrical */ +double *pmean, *pstd; +{ + double mean, std; + register int i; + double sumf; /* calculates the sum of f[i] */ + double temp; /* temporary variable */ + double *pbuffer; + double sumf2; /* calculates the sum of f[i]^2 */ + double correction; /* calulates the correction term for the variance */ + double variance; /* = (sumf2 - correction)/n, n=sum(f[i]) = 1 */ + + mean = 0.0; std = 0.0; + sumf = 0.0; sumf2 = 0.0; + pbuffer = buffer; + for (i=0; iXsize != 256 || m->Ysize != 256 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { + im_errormsg("im_cooc_correlation: unable to accept input"); + return(-1); + } + row = (double*)calloc( (unsigned)m->Ysize, sizeof(double)); + col = (double*)calloc( (unsigned)m->Xsize, sizeof(double)); + if ( row == NULL || col == NULL ) + { + im_errormsg("im_cooc_correlation: unable to calloc"); + return(-1); + } + pbuf = (double*)m->data; + for(j=0; jYsize; j++) + { + cpbuf = pbuf; + pbuf += m->Xsize; + sum=0.0; + for(i=0; iXsize; i++) + sum += *cpbuf++; + *(row+j) = sum; + } + + pbuf = (double*)m->data; + for(j=0; jYsize; j++) + { + cpbuf = pbuf; + pbuf++; + sum=0.0; + for(i=0; iXsize; i++) + { + sum += *cpbuf; + cpbuf += m->Xsize; + } + *(col+j) = sum; + } + + stats(row, m->Ysize, &mrow, &stdrow); + + stats(col, m->Ysize ,&mcol, &stdcol); +#ifdef DEBUG + fprintf(stderr, "rows: mean=%f std=%f\ncols: mean=%f std=%f\n", +mrow, stdrow, mcol, stdcol); +#endif + tmpcor = 0.0; + pbuf = (double*)m->data; + for(j=0; jYsize; j++) + { + cpbuf = pbuf; + pbuf += m->Xsize; + for(i=0; iXsize; i++) + { + dtemp = *cpbuf; + tmpcor += ( ((double)i)*((double)j)*dtemp); + cpbuf++; + } + } +#ifdef DEBUG + fprintf(stderr, "tmpcor=%f\n", tmpcor); +#endif + if ( (stdcol==0.0)||(stdrow==0) ) + { + im_errormsg("im_cooc_correlation: zero std"); + return(-1); + } + tmpcor = (tmpcor-(mcol*mrow))/(stdcol*stdrow); + *correlation = tmpcor; + free((char*)row); free((char*)col); + return(0); +} + +int +im_cooc_entropy( IMAGE *m, double *entropy ) +{ + double *pbuf, *pbufstart; + double *cpbuf; + register int i,j; + double tmpent, dtemp; + double val; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 256 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { + im_errormsg("im_cooc_entropy: unable to accept input"); + return(-1); + } + pbufstart = (double*)m->data; + + tmpent = 0.0; + pbuf = pbufstart; + for(j=0; jYsize; j++) + { + cpbuf = pbuf; + pbuf += m->Xsize; + for(i=0; iXsize; i++) + { + if(*cpbuf != 0) + { + dtemp = *cpbuf; + tmpent += (dtemp*log10(dtemp)); + } + cpbuf++; + } + } + val = tmpent*(-1); + +#ifdef DEBUG + fprintf(stderr,"ENT=%f\nwhich is %f bits\n", val, val/log10(2.0) ); +#endif + *entropy = (val/log10(2.0)); + return(0); +} diff --git a/libsrc/other/glds_funcs.c b/libsrc/other/glds_funcs.c new file mode 100644 index 00000000..51b82efd --- /dev/null +++ b/libsrc/other/glds_funcs.c @@ -0,0 +1,248 @@ +/* @(#) Calculates the spatial grey level differnce + * @(#) matrix of an image and some of its + * @(#) features. The 256x1 difference matrix of im is held by m + * @(#) There should be enough margin around the box so the (dx,dy) can + * @(#) access neighbouring pixels outside the box + * @(#) + * @(#) Usage: + * @(#) int im_glds_matrix(im, m, xpos, ypos, xsize, ysize, dx, dy) + * @(#) IMAGE *im, *m; + * @(#) int xpos, ypos, xsize, ysize; location of the box within im + * @(#) int dx, dy; displacements + * @(#) + * @(#) int im_glds_asm(m, asmoment) + * @(#) IMAGE *m; + * @(#) double *asmoment; + * @(#) + * @(#) int im_glds_contrast(m, contrast) + * @(#) IMAGE *m; + * @(#) double *contrast; + * @(#) + * @(#) int im_glds_entropy(m, entropy) + * @(#) IMAGE *m; + * @(#) double *entropy; + * @(#) + * @(#) int im_glds_mean(m, mean) + * @(#) IMAGE *m; + * @(#) double *mean; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * + * Copyright: N. Dessipris, 1991 + * Written on: 2/12/1991 + * Modified on: + * 22/7/93 JC + * - im_incheck() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Keep the greylevel difference matrix as a 256x1 double image */ + +int +im_glds_matrix( IMAGE *im, IMAGE *m, + int xpos, int ypos, int xsize, int ysize, int dx, int dy ) +{ + PEL *in, *cpin; + int *b, *pb; + double *l, *pl; + int x, y; + int ofs; + int tmp; + int norm; + + if (im_iocheck(im, m) == -1) + { im_errormsg("im_glds_matrix: im_iocheck failed"); return(-1);} + + if ((im->Bands != 1)||(im->Bbits != IM_BBITS_BYTE)||(im->BandFmt != IM_BANDFMT_UCHAR)) + { im_errormsg("im_glds_matrix: Wrong input"); return(-1); } + + if ( (xpos + xsize + dx > im->Xsize)|| (ypos + ysize + dy > im->Ysize) ) + { im_errormsg("im_glds_matrix: wrong args"); return(-1); } + + if (im_cp_desc(m, im) == -1) + { im_errormsg("im_glds_matrix: im_cp_desc failed"); return(-1);} + m->Xsize = 256; m->Ysize = 1; + m->Bbits = IM_BBITS_DOUBLE; m->BandFmt = IM_BANDFMT_DOUBLE; + m->Type = IM_TYPE_B_W; + + if (im_setupout(m) == -1) + { im_errormsg("im_glds_matrix: im_setupout failed");return(-1);} + + b = (int *)calloc( (unsigned)m->Xsize, sizeof(int) ); + l = (double *)calloc( (unsigned)m->Xsize, sizeof(double)); + if ( (b == NULL) || (l == NULL) ) + { im_errormsg("im_glds_matrix: calloc failed"); return(-1); } + + in = (PEL*)im->data; + in += ( ypos * im->Xsize + xpos ); + ofs = dy * im->Xsize + dx; + for ( y=0; yXsize; + for ( x=0; xXsize; x++) + *pl++ = ((double)(*pb++))/(double)norm; + if (im_writeline( 0, m, (PEL *) l ) == -1) + {im_errormsg("im_glds_matrix: im_writeline failed");return(-1);} + + free((char*)b); free((char*)l); + return(0); +} + +/* @(#) Calculates the asmoment of the sglds matrix held by m + */ +int +im_glds_asm( IMAGE *m, double *asmoment ) +{ + double temp, tmpasm, *in; + int i; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 1 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + {im_errormsg("im_glds_asm: unable to accept input");return(-1);} + tmpasm = 0.0; + in = (double*)m->data; + for(i=0; iXsize; i++) + { + temp = *in++; + tmpasm += (temp*temp); + } + *asmoment = tmpasm; + return(0); +} + +/* @(#) Calculates the contrast of the coocurence matrix passed in buffer + */ +int +im_glds_contrast( IMAGE *m, double *contrast ) +{ + double tmpcon, *in; + int i; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 1 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { im_errormsg("im_glds_contrast: wrong input"); return(-1); } + tmpcon = 0.0; + in = (double*)m->data; + for(i=0; iXsize; i++) + { + tmpcon += ( ((double)i)*((double)i)*(*in) ); + in++; + } + *contrast = tmpcon; + return(0); +} + +/* @(#) Calculates the entropy of the glds vector passed in buffer + * @(#) Function returns the entropy based on log base 2. + */ +int +im_glds_entropy( IMAGE *m, double *entropy ) +{ + double tmpent, dtemp, *in; + int i; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 1 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { im_errormsg("im_glds_entropy: wrong input"); return(-1); } + tmpent = 0.0; + in = (double*)m->data; + for(i=0; iXsize; i++) + { + if(*in != 0) + { + dtemp = *in; + tmpent += (dtemp*log10(dtemp)); + } + in++; + } + *entropy = ((-1)*tmpent/log10(2.0)); + return(0); +} + +/* @(#) Calculates the mean of the sglds matrix passed in m + */ +int +im_glds_mean( IMAGE *m, double *mean ) +{ + double tmpmean, *in; + int i; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 1 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { im_errormsg("im_glds_mean: wrong input"); return(-1); } + tmpmean = 0.0; + in = (double*)m->data; + for(i=0; iXsize; i++) + { + tmpmean += ( ((double)i)*(*in) ); + in++; + } + tmpmean = tmpmean/((double)m->Xsize); + *mean = tmpmean; + return(0); +} diff --git a/libsrc/other/im_benchmark.c b/libsrc/other/im_benchmark.c new file mode 100644 index 00000000..bd337013 --- /dev/null +++ b/libsrc/other/im_benchmark.c @@ -0,0 +1,287 @@ +/* @(#) Do a complicated compound operation for benchmarking the threading + * @(#) system. Input should be a large LABQ image, output is a large sRGB + * @(#) image. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int im_benchmark( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * @(#) + * + * 6/10/06 + * - hacked in + * 27/11/06 + * - added im_benchmarkn() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* + +VIPS SMP benchmark +------------------ + +This is adapted from the system used to generate images for POD: + + http://cima.ng-london.org.uk/~john/POD + +Images from a 10k by 10k studio digital camera are colour processed, resized, +cropped and sharpened. + +The original POD script was written in nip (see below). This operation is a +reimplementation in vanilla C to make it easier to run (and less fragile!). + +This thing was originally processing images off a remote server over a 100mbit +network. No attempt was made to make it quick (there was no point): you +could make it a lot faster very easily. + +------ benchmark in nip2 ----------- +#!/home/john/vips/bin/nip2 -s + +// get command-line arguments + +image_path = argv?1; +crop_id = parse_pint argv?2; +crop_left = parse_pint argv?3; +crop_top = parse_pint argv?4; +crop_width = parse_pint argv?5; +crop_height = parse_pint argv?6; +width = parse_pint argv?7; +height = parse_pint argv?8; +sharp = parse_pint argv?9; + +// scale down by this much to undo photographic's relativisation +darken = Vector [1.18, 1, 1]; + +// fudge factor in XYZ to get a match under NGC lights on uv-durable paper +white_point_adjust = Vector [1.06, 1, 1.01]; + +// brighten by this in XYZ to get relative colorimetry +brighten = 1.5; + +// blacks down by this much in LAB +blacks_down = Vector [-2, 0, 0]; + +// sharpen params for 400, 300, 200 and 150 dpi +// just change the size of the area we search +sharpen_params_table = [ + [ 11, 2.5, 40, 20, 0.5, 1.5 ], + [ 7, 2.5, 40, 20, 0.5, 1.5 ], + [ 5, 2.5, 40, 20, 0.5, 1.5 ], + [ 3, 2.5, 40, 20, 0.5, 1.5 ] +]; + +// convert D65 XYZ to D50 XYZ +D652D50 = recomb D652D50_direct; + +stage_crop in + = extract_area crop_left crop_top crop_width crop_height in, + crop_id != 0 + = in; + +// fit within a width / height +stage_shrink image + = image, factor > 1; // never upscale + = resize factor factor Interpolate.BILINEAR image +{ + hfactor = width / get_width image; + vfactor = height / get_height image; + factor = min_pair hfactor vfactor; +} + +// unphotoize, go to xyz, convert to D50, adjust white point, back to lab +stage_colour in + = if in?0 > 99 then Vector [100, 0, 0] else in''' +{ + // back to absolute + in' = in / darken; + + xyz = colour_transform_to Image_type.XYZ in'; + + xyz' = D652D50 xyz * white_point_adjust * brighten; + + in'' = colour_transform_to Image_type.LAB xyz'; + + // shadows down + in''' = in'' + blacks_down; +} + +stage_sharp in + = (sharpen params?0 params?1 params?2 params?3 params?4 params?5 @ + colour_transform_to Image_type.LABQ) in +{ + params = sharpen_params_table?sharp; +} + +// This was: +// +// stage_srgb in +// = (icc_export 8 "$VIPSHOME/share/nip2/data/sRGB.icm" 1 @ +// colour_transform_to Image_type.LABQ) in; +// +// but that uses lcms which is single-threaded. So for this benchmark, we use +// VIPS's own ->sRGB converter, which is less accurate but does thread. +stage_srgb in + = colour_transform_to Image_type.sRGB in; + +main = (get_image @ stage_srgb @ + stage_sharp @ stage_colour @ stage_shrink @ stage_crop @ + colour_transform_to Image_type.LAB @ Image_file) image_path; +------ benchmark in nip2 ----------- + + */ + +/* The main part of the benchmark ... transform labq to labq. Chain several of + * these together to get a CPU-bound operation. + */ +static int +benchmark( IMAGE *in, IMAGE *out ) +{ + IMAGE *t[18]; + double one[3] = { 1.0, 1.0, 1.0 }; + double zero[3] = { 0.0, 0.0, 0.0 }; + double darken[3] = { 1.0 / 1.18, 1.0, 1.0 }; + double whitepoint[3] = { 1.06, 1.0, 1.01 }; + double shadow[3] = { -2, 0, 0 }; + double white[3] = { 100, 0, 0 }; + DOUBLEMASK *d652d50 = im_create_dmaskv( "d652d50", 3, 3, + 1.13529, -0.0604663, -0.0606321, + 0.0975399, 0.935024, -0.0256156, + -0.0336428, 0.0414702, 0.994135 ); + + im_add_close_callback( out, + (im_callback_fn) im_free_dmask, d652d50, NULL ); + + return( + /* Set of descriptors for this operation. + */ + im_open_local_array( out, t, 18, "im_benchmark", "p" ) || + + /* Unpack to float. + */ + im_LabQ2Lab( in, t[0] ) || + + /* Crop 100 pixels off all edges. + */ + im_extract_area( t[0], t[1], + 100, 100, t[0]->Xsize - 200, t[0]->Ysize - 200 ) || + + /* Shrink by 10%, bilinear interp. + */ + im_affine( t[1], t[2], + 0.9, 0, 0, 0.9, 0, 0, + 0, 0, t[1]->Xsize * 0.9, t[1]->Ysize * 0.9 ) || + + /* Find L ~= 100 areas (white surround). + */ + im_extract_band( t[2], t[3], 0 ) || + im_moreconst( t[3], t[4], 99 ) || + + /* Adjust white point and shadows. + */ + im_lintra_vec( 3, darken, t[2], zero, t[5] ) || + im_Lab2XYZ( t[5], t[6] ) || + im_recomb( t[6], t[7], d652d50 ) || + im_lintra_vec( 3, whitepoint, t[7], zero, t[8] ) || + im_lintra( 1.5, t[8], 0.0, t[9] ) || + im_XYZ2Lab( t[9], t[10] ) || + im_lintra_vec( 3, one, t[10], shadow, t[11] ) || + + /* Make a solid white image. + */ + im_black( t[12], t[4]->Xsize, t[4]->Ysize, 3 ) || + im_lintra_vec( 3, zero, t[12], white, t[13] ) || + + /* Reattach border. + */ + im_ifthenelse( t[4], t[13], t[11], t[14] ) || + + /* Sharpen. + */ + im_Lab2LabQ( t[14], t[15] ) || + im_sharpen( t[15], out, 11, 2.5, 40, 20, 0.5, 1.5 ) + ); +} + +/* Chain n benchmarks together to get a CPU-bound operation. + */ +int +im_benchmarkn( IMAGE *in, IMAGE *out, int n ) +{ + IMAGE *t[2]; + + if( n == 0 ) + /* To sRGB. + */ + return( im_LabQ2disp( in, out, im_col_displays( 7 ) ) ); + else + return( im_open_local_array( out, t, 2, "benchmarkn", "p" ) || + + benchmark( in, t[0] ) || + + /* Expand back to the original size again ... + * benchmark does a 200 pixel crop plus a 10% shrink, + * so if we chain many of them together the image gets + * too small. + */ + im_affine( t[0], t[1], + (double) in->Xsize / t[0]->Xsize, 0, 0, + (double) in->Ysize / t[0]->Ysize, + 0, 0, + 0, 0, in->Xsize, in->Ysize ) || + + im_benchmarkn( t[1], out, n - 1 ) ); +} + +int +im_benchmark2( IMAGE *in, double *out ) +{ + IMAGE *t; + + return( + !(t = im_open_local( in, "benchmarkn", "p" )) || + im_benchmarkn( in, t, 1 ) || + im_avg( t, out ) + ); +} + diff --git a/libsrc/other/im_dif_std.c b/libsrc/other/im_dif_std.c new file mode 100644 index 00000000..60b1e57c --- /dev/null +++ b/libsrc/other/im_dif_std.c @@ -0,0 +1,101 @@ +/* @(#) Program to calculate the stdev of the differnce image + * @(#) at a given displacement vector + * + * Written : 25/11/1987 + * Author : N. Dessipris + * Updated : 2/12/1991 + * 22/7/93 JC + * - im_incheck() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int im_dif_std(im, xpos, ypos, xsize, ysize, dx, dy, pmean, pstd) +IMAGE *im; +int xpos, ypos, xsize, ysize; /* location of the box within im */ +int dx, dy; /* displacements */ +double *pmean, *pstd; +{ + PEL *input, *cpinput; + double m, s; + int *buf, *pbuf; + int x, y; + int ofst, bufsize; + + + if( im_incheck( im ) ) + return( -1 ); + + if ((im->Bands != 1)||(im->Bbits != IM_BBITS_BYTE)||(im->BandFmt != IM_BANDFMT_UCHAR)) + {im_errormsg("im_dif_std: Unable to accept input"); return(-1);} + if ( (xpos + xsize + dx > im->Xsize)|| (ypos + ysize + dy > im->Ysize) ) + { im_errormsg("im_dif_std: wrong args"); return(-1); } + + bufsize = xsize * ysize; + buf = (int *)calloc( (unsigned)bufsize, sizeof(int) ); + if ( buf == NULL ) + { im_errormsg("im_dif_std: calloc failed"); return(-1); } + input = (PEL*)im->data; + input += ( ypos * im->Xsize + xpos ); + ofst = dy * im->Xsize + dx; + pbuf = buf; + for ( y=0; yXsize; + for ( x=0; x +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_feye( IMAGE *image, const int xsize, const int ysize, const double factor ) +{ + int x, y; + double constant; + double *lut; + float *line; + + /* Check input args + */ + if( im_outcheck( image ) ) + return( -1 ); + if( factor > 1.0 || factor <= 0.0 ) { + im_errormsg( "im_feye: factor should be in [1,0)" ); + return( -1 ); + } + + /* Set image descriptor + */ + im_initdesc( image, xsize, ysize, 1, IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + if( im_setupout( image ) ) + return( -1 ); + + /* Allocate space for line buffer. + */ + if( !(line = IM_ARRAY( image, xsize, float )) ) + return( -1 ); + + /* Make a lut for easy calculations. + */ + if( !(lut = IM_ARRAY( image, image->Xsize, double )) ) + return( -1 ); + constant = factor * IM_PI/(2*(xsize - 1)); + for( x = 0; x < image->Xsize; x++ ) + lut[x] = cos( constant*x*x ) / ((ysize - 1)*(ysize - 1)); + + /* Make image. + */ + for( y = 0; y < image->Ysize; y++ ) { + for( x = 0; x < image->Xsize; x++ ) + line[x] = y*y*lut[x]; + if( im_writeline( y, image, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + +/* As above, but make a IM_BANDFMT_UCHAR image. + */ +int +im_eye( IMAGE *image, const int xsize, const int ysize, const double factor ) +{ + IMAGE *t1 = im_open_local( image, "im_eye:1", "p" ); + IMAGE *t2 = im_open_local( image, "im_eye:2", "p" ); + + if( !t1 ) + return( -1 ); + + /* Change range to [0,255]. + */ + if( im_feye( t1, xsize, ysize, factor ) || + im_lintra( 127.5, t1, 127.5, t2 ) || + im_clip( t2, image ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/other/im_grey.c b/libsrc/other/im_grey.c new file mode 100644 index 00000000..0dfec7f5 --- /dev/null +++ b/libsrc/other/im_grey.c @@ -0,0 +1,146 @@ +/* @(#) Creates a IM_BANDFMT_FLOAT grey level image of a specified size. Range is + * @(#) always [0,1]. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_fgrey( image, xsize, ysize ) + * @(#) IMAGE *image; + * @(#) int xsize, ysize; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/02/1990 + * Modified on: + * 22/7/93 JC + * - im_outcheck() added + * - externs removed + * 8/2/95 JC + * - ANSIfied + * - im_fgrey() made from im_grey() + * 31/8/95 JC + * - now makes [0,1], rather than [0,256) + * - im_grey() now defined in terms of im_fgrey() + * 2/3/98 JC + * - partialed + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Generate function. + */ +static int +fgrey_gen( REGION *or ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int iwm = or->im->Xsize - 1; + + int x, y; + + for( y = 0; y < r->height; y++ ) { + float *q = (float *) IM_REGION_ADDR( or, le, y + to ); + + for( x = 0; x < r->width; x++ ) + q[x] = (float) (x + le) / iwm; + } + + return( 0 ); +} + +/* Make a one band grey ramp image. + */ +int +im_fgrey( IMAGE *out, const int xsize, const int ysize ) +{ + /* Check args. + */ + if( xsize <=0 || ysize <= 0 ) { + im_errormsg( "im_fgrey: bad size" ); + return( -1 ); + } + if( im_poutcheck( out ) ) + return( -1 ); + + /* Set image. + */ + im_initdesc( out, xsize, ysize, 1, IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + + /* Set hints - ANY is ok with us. + */ + if( im_demand_hint( out, IM_ANY, NULL ) ) + return( -1 ); + + /* Generate image. + */ + if( im_generate( out, NULL, fgrey_gen, NULL, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* As above, but make a IM_BANDFMT_UCHAR [0-255] image. + */ +int +im_grey( IMAGE *image, const int xsize, const int ysize ) +{ + IMAGE *t1 = im_open_local( image, "im_grey:1", "p" ); + IMAGE *t2 = im_open_local( image, "im_grey:2", "p" ); + + if( !t1 || !t2 ) + return( -1 ); + + /* Change range to [0,255]. + */ + if( im_fgrey( t1, xsize, ysize ) || + im_lintra( 255.0, t1, 0.0, t2 ) || + im_clip( t2, image ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/other/im_make_xy.c b/libsrc/other/im_make_xy.c new file mode 100644 index 00000000..1e601d14 --- /dev/null +++ b/libsrc/other/im_make_xy.c @@ -0,0 +1,116 @@ +/* @(#) Creates a 2 band IM_BANDFMT_UINT image of a specified size. Each pixel + * @(#) has band 0 == x coordinate, band 1 == y coordinate. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_make_xy( image, xsize, ysize ) + * @(#) IMAGE *image; + * @(#) int xsize, ysize; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 21/4/04 + * - from im_grey + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Generate function. + */ +static int +make_xy_gen( REGION *or ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int ri = IM_RECT_RIGHT( r ); + int bo = IM_RECT_BOTTOM( r ); + + int x, y; + + for( y = to; y < bo; y++ ) { + unsigned int *q = (unsigned int *) IM_REGION_ADDR( or, le, y ); + + for( x = le; x < ri; x++ ) { + q[0] = x; + q[1] = y; + q += 2; + } + } + + return( 0 ); +} + +/* Make a two band image ... band 0 is x coordinates, band 1 is y + * coordinates. + */ +int +im_make_xy( IMAGE *out, const int xsize, const int ysize ) +{ + /* Check args. + */ + if( xsize <=0 || ysize <= 0 ) { + im_errormsg( "im_make_xy: bad size" ); + return( -1 ); + } + if( im_poutcheck( out ) ) + return( -1 ); + + /* Set image. + */ + im_initdesc( out, xsize, ysize, 2, IM_BBITS_INT, IM_BANDFMT_UINT, + IM_CODING_NONE, IM_TYPE_MULTIBAND, 1.0, 1.0, 0, 0 ); + + /* Set hints - ANY is ok with us. + */ + if( im_demand_hint( out, IM_ANY, NULL ) ) + return( -1 ); + + /* Generate image. + */ + if( im_generate( out, NULL, make_xy_gen, NULL, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/other/im_meanstd.c b/libsrc/other/im_meanstd.c new file mode 100644 index 00000000..2edbfeff --- /dev/null +++ b/libsrc/other/im_meanstd.c @@ -0,0 +1,136 @@ +/* @(#) Calculates the mean and the standard deviation (std) of + * @(#) an int or double buffer of size size. + * @(#) + * @(#) Usage: + * @(#) int im__mean_std_double_buffer(buffer, size, pmean, pstd) + * @(#) double *buffer; + * @(#) int size; + * @(#) double *pmean, *pstd; + * @(#) + * @(#) int im__mean_std_int_buffer(buffer, size, pmean, pstd) + * @(#) int *buffer; + * @(#) int size; + * @(#) double *pmean, *pstd; + * @(#) + * @(#) Both functions return 0 on success and -1 on error + * + * Copyright: N. Dessipris 1991 + * Written on: 2/12/1991 + * Updated on: 2/12/1991 + * 22/7/93 JC + * - externs removed + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im__mean_std_double_buffer( double *buffer, int size, + double *pmean, double *pstd ) +{ + double mean, std; + register int i; + double sumf; + double temp; + double *pbuffer; + double sumf2; + double correction; /* calulates the correction term for the variance */ + double variance; /* = (sumf2 - correction)/n */ + + if (size <= 0) { + im_errormsg("im_mean_std_double_buffer: wrong args"); + return(-1); + } + mean = 0.0; std = 0.0; + sumf = 0.0; sumf2 = 0.0; + pbuffer = buffer; + for (i=0; i +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_simcontr( IMAGE *image, int xs, int ys ) +{ + int x, y; + unsigned char *line1, *line2, *cpline; + + +/* Check input args */ + if( im_outcheck( image ) ) + return( -1 ); + +/* Set now image properly */ + im_initdesc(image, xs, ys, 1, IM_BBITS_BYTE, IM_BANDFMT_UCHAR, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + +/* Set up image checking whether the output is a buffer or a file */ + if (im_setupout( image ) == -1 ) + { im_errormsg("im_simcontr: im_setupout failed"); return(-1); } +/* Create data */ + line1 = (unsigned char *)calloc((unsigned)xs, sizeof(char)); + line2 = (unsigned char *)calloc((unsigned)xs, sizeof(char)); + if ( (line1 == NULL) || (line2 == NULL) ) + { im_errormsg("im_simcontr: calloc failed"); return(-1); } + + cpline = line1; + for (x=0; x +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_sines( IMAGE *image, int xsize, int ysize, double horfreq, double verfreq ) +{ + int x, y; + float *line, *cpline; + int size; + double cons, factor; + double theta_rad, costheta, sintheta, ysintheta; + +/* Check input args */ + if( im_outcheck( image ) ) + return( -1 ); + if ( xsize <= 0 || ysize <= 0 ) + { im_errormsg("im_sines: wrong sizes"); return(-1); } + +/* Set now image properly */ + im_initdesc(image, xsize, ysize, 1, IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0); + +/* Set up image checking whether the output is a buffer or a file */ + if (im_setupout( image ) == -1 ) + { im_errormsg("im_sines: im_setupout failed"); return(-1); } +/* Create data */ + size = image->Xsize; + if ( (line=(float *)calloc((unsigned)size, sizeof(float))) == NULL ) + { im_errormsg("im_sines: calloc failed"); return(-1); } + +/* make angle in rad */ + if (horfreq == 0) + theta_rad = IM_PI/2.0; + else + theta_rad = atan(verfreq/horfreq); + costheta = cos(theta_rad); sintheta = sin(theta_rad); + factor = sqrt ((double)(horfreq*horfreq + verfreq*verfreq)); + cons = factor * IM_PI * 2.0/(double)image->Xsize; +/* There is a bug (rounding error ?) for horfreq=0, + *so do this calculation independantly */ + if ( horfreq != 0 ) + { + for (y=0; yYsize; y++) + { + ysintheta = y * sintheta; + cpline = line; + for (x=0; xXsize; x++) + *cpline++ = + (float)(cos(cons*(x*costheta-ysintheta))); + if ( im_writeline( y, image, (PEL *)line ) == -1 ) + { + im_errormsg("im_sines: im_writeline failed"); + free ( (char *)line ); + return( -1 ); + } + } + } + else + { + for (y=0; yYsize; y++) + { + cpline = line; + ysintheta = cos (- cons * y * sintheta); + for (x=0; xXsize; x++) + *cpline++ = (float)ysintheta; + if ( im_writeline( y, image, (PEL *)line ) == -1 ) + { + im_errormsg("im_sines: im_writeline failed"); + free ( (char *)line ); + return( -1 ); + } + } + } + free ( (char *)line ); + return(0); +} diff --git a/libsrc/other/im_spatres.c b/libsrc/other/im_spatres.c new file mode 100644 index 00000000..f8535f15 --- /dev/null +++ b/libsrc/other/im_spatres.c @@ -0,0 +1,146 @@ +/* @(#) Function which changes the spatial resolution of an image according to + * @(#) step + * @(#) + * @(#) int im_spatres(in, out, step) + * @(#) IMAGE *in, *out; + * @(#) int step; + * @(#) Returns either 0 (sucess) or -1 (fail) + * @(#) + * @(#) Picture can have any number of channels (max 64). + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 08/11/1989. + * Modified on: 19/01/1990. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_spatres( IMAGE *in, IMAGE *out, int step ) +{ + int x, y; /* horizontal and vertical direction */ + int z; /* 0 <= z < channel */ + int i, j; + int rounding, step2, sum; + unsigned char *values; + unsigned char *input, *cpinput, *cp2input, *line, *cpline, *pnt, *cpnt; + int os; + +/* Check args */ + if ( step < 1 ) + {im_errormsg("im_spatres: Invalid step %d\n", step);return(-1);} + + if ( (in->Xsize/step == 0)||(in->Ysize/step == 0) ) + {im_errormsg("im_spatres: Invalid step %d\n", step);return(-1);} + + if (im_iocheck(in, out) == -1) + { im_errormsg("im_spatres: im_iocheck failed"); return(-1); } + + if((in->Coding != IM_CODING_NONE)||(in->Bbits != 8)||(in->BandFmt !=IM_BANDFMT_UCHAR)) + { im_errormsg("im_spatres: wrong input"); return(-1); } + +/* Prepare output */ + if (im_cp_desc(out, in) == -1) + { im_errormsg("im_spatres: im_cp_desc failed"); return(-1); } + out->Xsize = in->Xsize - in->Xsize%step; + out->Ysize = in->Ysize - in->Ysize%step; + + if( im_setupout(out) == -1) + { im_errormsg("im_spatres: im_setupout failed"); return(-1); } + + /* Malloc buffer for one 'line' of input data */ + os = in->Xsize * in->Bands; + line = (unsigned char *)calloc((unsigned)os, sizeof(char)); + /* Malloc space for values */ + values = (unsigned char *)calloc((unsigned)out->Bands, sizeof(char)); + if ( line == NULL || values == NULL ) + { im_errormsg("im_spatres: calloc failed"); return(-1); } + + step2 = step * step; + rounding = step2/2; + input = (unsigned char *)in->data; + for ( y = 0; y < out->Ysize; y += step ) + { + cpinput = input; + input += os * step; + /* do the x loop out->Xsize / step times */ + cpline = line; + for (x = 0; x < out->Xsize; x += step) + { + cp2input = cpinput; + cpinput += step * out->Bands; /* ??? */ + for ( z = 0; z < out->Bands; z++ ) + { + pnt = cp2input + z; + sum = 0; + for ( j = 0; j < step; j++ ) + { + cpnt = pnt; + pnt += os; + for ( i = 0; i < step; i++ ) + { + sum += (int)*cpnt; + cpnt += out->Bands; + } + } + *(values + z) = (PEL)((sum + rounding)/step2); + } + /* for this x, write step*bands data */ + for ( j = 0; j < step; j++ ) + for ( z = 0; z < out->Bands; z++ ) + *cpline++ = *(values + z); + } + /* line is now ready. Write now step lines */ + for (j = 0; j < step; j++) + if ( im_writeline ( y+j, out, (PEL *)line ) == -1 ) + { + im_errormsg("im_spatres: im_writeline failed"); + free ( (char *)line ); free ( (char *)values ); + return( -1 ); + } + } /* end of the for (..y..) loop */ + + free ( (char *)line ); free ( (char *)values ); + return(0); +} diff --git a/libsrc/other/im_zone.c b/libsrc/other/im_zone.c new file mode 100644 index 00000000..59477461 --- /dev/null +++ b/libsrc/other/im_zone.c @@ -0,0 +1,127 @@ +/* @(#) square zone plate of size + * @(#) The center of the zone plate is at (xpos/2, ypos/2) + * @(#) + * @(#) Usage: + * @(#) + * @(#) int im_zone( image, size ) + * @(#) IMAGE *image; + * @(#) int size; + * @(#) + * @(#) int im_fzone( image, size ) + * @(#) IMAGE *image; + * @(#) int size; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * N. Dessipris 01/02/1991 + * + * 22/7/93 JC + * - externs removed + * - im_outcheck() added + * 30/8/95 JC + * - modernized + * - memory leaks fixed + * - split into im_zone() and im_fzone() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_fzone( IMAGE *image, int size ) +{ + int x, y; + int i, j; + + float *buf; + const int size2 = size/2; + + /* Check args. + */ + if( im_outcheck( image ) ) + return( -1 ); + if( size <= 0 || (size % 2) != 0 ) { + im_errormsg( "im_zone: size must be even and positive" ); + return( -1 ); + } + + /* Set up output image. + */ + im_initdesc( image, size, size, 1, IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + if( im_setupout( image ) ) + return( -1 ); + + /* Create output buffer. + */ + if( !(buf = IM_ARRAY( image, size, float )) ) + return( -1 ); + + /* Make zone plate. + */ + for( y = 0, j = -size2; j < size2; j++, y++ ) { + for( x = 0, i = -size2; i < size2; i++, x++ ) + buf[x] = cos( (IM_PI/size) * (i*i + j*j) ); + if( im_writeline( y, image, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* As above, but make a IM_BANDFMT_UCHAR image. + */ +int +im_zone( IMAGE *im, int size ) +{ + IMAGE *t1 = im_open_local( im, "im_zone:1", "p" ); + IMAGE *t2 = im_open_local( im, "im_zone:2", "p" ); + + if( !t1 || !t2 ) + return( -1 ); + + if( im_fzone( t1, size ) || + im_lintra( 127.5, t1, 127.5, t2 ) || + im_clip( t2, im ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/other/man3/Makefile.am b/libsrc/other/man3/Makefile.am new file mode 100644 index 00000000..728322f5 --- /dev/null +++ b/libsrc/other/man3/Makefile.am @@ -0,0 +1,30 @@ +man_MANS = \ + im_benchmark.3 \ + im_cooc_asm.3 \ + im_cooc_contrast.3 \ + im_cooc_correlation.3 \ + im_cooc_entropy.3 \ + im_cooc_matrix.3 \ + im_dif_std.3 \ + im_eye.3 \ + im_feye.3 \ + im_fgrey.3 \ + im_fzone.3 \ + im_glds_asm.3 \ + im_glds_contrast.3 \ + im_glds_entropy.3 \ + im_glds_matrix.3 \ + im_glds_mean.3 \ + im_grey.3 \ + im_make_xy.3 \ + im_mean_std_double_buffer.3 \ + im_mean_std_int_buffer.3 \ + im_sines.3 \ + im_quantim.3 \ + im_quantlut.3 \ + im_simcontr.3 \ + im_spatres.3 \ + im_zone.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/other/man3/im_benchmark.3 b/libsrc/other/man3/im_benchmark.3 new file mode 100644 index 00000000..34707c8e --- /dev/null +++ b/libsrc/other/man3/im_benchmark.3 @@ -0,0 +1,16 @@ +.TH IM_BENCHMARK 3 "6 Oct 2006" +.SH NAME +im_benchmark \- do something complicated +.SH SYNOPSIS +.B #include + +int im_benchmark( IMAGE *in, IMAGE *out ) + +.SH DESCRIPTION +.B im_benchmark(3) +performs a complicated operation (based on a real example of VIPS usage) on a +LABPACK image. It is useful for benchmarking the VIPS threading system. It +should speed up (mostly) linearly with more CPUs. + +.SH RETURNED VALUES +The function returns 0 on success and -1 on error. diff --git a/libsrc/other/man3/im_cooc_asm.3 b/libsrc/other/man3/im_cooc_asm.3 new file mode 100644 index 00000000..536836b7 --- /dev/null +++ b/libsrc/other/man3/im_cooc_asm.3 @@ -0,0 +1 @@ +.so man3/im_cooc_matrix.3 diff --git a/libsrc/other/man3/im_cooc_contrast.3 b/libsrc/other/man3/im_cooc_contrast.3 new file mode 100644 index 00000000..536836b7 --- /dev/null +++ b/libsrc/other/man3/im_cooc_contrast.3 @@ -0,0 +1 @@ +.so man3/im_cooc_matrix.3 diff --git a/libsrc/other/man3/im_cooc_correlation.3 b/libsrc/other/man3/im_cooc_correlation.3 new file mode 100644 index 00000000..536836b7 --- /dev/null +++ b/libsrc/other/man3/im_cooc_correlation.3 @@ -0,0 +1 @@ +.so man3/im_cooc_matrix.3 diff --git a/libsrc/other/man3/im_cooc_entropy.3 b/libsrc/other/man3/im_cooc_entropy.3 new file mode 100644 index 00000000..536836b7 --- /dev/null +++ b/libsrc/other/man3/im_cooc_entropy.3 @@ -0,0 +1 @@ +.so man3/im_cooc_matrix.3 diff --git a/libsrc/other/man3/im_cooc_matrix.3 b/libsrc/other/man3/im_cooc_matrix.3 new file mode 100644 index 00000000..12f70df6 --- /dev/null +++ b/libsrc/other/man3/im_cooc_matrix.3 @@ -0,0 +1,88 @@ +.TH IM_COOC_MATRIX 3 "2 Dec 1991" +.SH NAME +im_cooc_matrix, im_cooc_asm, im_cooc_contrast, im_cooc_correlation, +im_cooc_entropy \- calculate the co-occurrence matrix and features on it +.SH SYNOPSIS +.B #include + +int im_cooc_matrix(im, m, xp, yp, xs, ys, dx, dy, sym) +.br +.B IMAGE *im, *m; +.br +.B int xp, yp, xs, ys; +.br +.B int dx, dy; +.br +.B int sym; + +.br +.B int im_cooc_asm(m, asmoment) +.br +.B IMAGE *m; +.br +.B double *asmoment; + +.br +.B int im_cooc_contrast(m, contrast) +.br +.B IMAGE *m; +.br +.B double *contrast; + +.br +.B int im_cooc_correlation(m, correlation) +.br +.B IMAGE *m; +.br +.B double *correlation; + +.br +.B int im_cooc_entropy(m, entropy) +.br +.B IMAGE *m; +.br +.B double *entropy; + +.SH DESCRIPTION +.B im_cooc_matrix() +creates a 256 by 256 one channel co-occurrence matrix of the box determined by +the parameters (xp, yp; xs, ys) within the image pointed by the IMAGE +descriptor im. The matrix is written onto the IMAGE descriptor m. The +displacement vector is determined by (dx, dy). The user must ensure that +there is enough border pixels around the box within im dictated by the +displacement vector (dx,dy) or else the program fails. All entries of the +co-occurrence matrix are double normalised to the number of pairs involved. +This function is a direct implementation of the paper: Haralick R. M., +Shanmugan K. and Dinstein I., 'Textural features for image classification', +IEEE Transactions on Systems, Man, and Cybernetics, Vol. SMC-3, No 6, Nov. +1973, pp 610-621. Input im should be one band unsigned char image. + +If flag sym is 1, the created co-occurrence matrix is symmetric that is +dispacement vectors (dx, dy), (-dx, -dy) create exactly the same matrix. If +sym is 0, the created co-occurrence matrix is not symmetric that is +dispacement vectors (dx, dy), (-dx, -dy) create different matrices. + +.B im_cooc_asm() +calculates the angular second moment of the co-occurrence matrix held by m. +The result is returned into the location pointed by asmoment. + +.B im_cooc_contrast() +calculates the contrast of the co-occurrence matrix held by m. +The result is returned into the location pointed by contrast. + +.B im_cooc_correlation() +calculates the correlation of the co-occurrence matrix held by m. +The result is returned into the location pointed by correlation. + +.B im_cooc_entropy() +calculates the entropy of the co-occurrence matrix held by m. +The result is returned into the location pointed by entropy. +.SH RETURNED VALUES +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_glds_matrix(3) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 2/12/1991 diff --git a/libsrc/other/man3/im_dif_std.3 b/libsrc/other/man3/im_dif_std.3 new file mode 100644 index 00000000..8d505818 --- /dev/null +++ b/libsrc/other/man3/im_dif_std.3 @@ -0,0 +1,40 @@ +.TH IM_DIF_STD 3 "10 May 1991" +.SH NAME +im_dif_std \- calculate the mean and the standard deviation of the difference image for a given displacement vector +.SH SYNOPSIS +.B #include + +int im_dif_std(im, xp, yp, xs, ys, dx, dy, mean, std) +.br +.B IMAGE *im; +.br +.B int xp, yp, xs, ys; +.br +.B int dx, dy; +.br +.B double *mean, *std; + +.SH DESCRIPTION +.B im_dif_std() +calculates the mean and the standard deviation of the difference image +created by the displacement vector (dx,dy) on the box (xp,yp;xs,ys) defined +on the image pointed by im. More specifically the difference image is +of an image given the dispacement vector (dx, dy) is defined as follows: +For each point of the original image, the start of the vector (dx,dy) is +set on that point. The difference image at this point is defined as the +difference of the values pointed by the vector (end value - start value). +The function returns the mean and the standard deviation of the +difference images measured on the area (xp,yp;xs,ys) of im. + +Input im should be one band unsigned char image and it should +have been set by a call to im_mmapin(3) or im_setbuf(3). + +.SH RETURNED VALUES(3) +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_cooc_matrix(3), im_glds_matrix(3) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_eye.3 b/libsrc/other/man3/im_eye.3 new file mode 100644 index 00000000..33216d07 --- /dev/null +++ b/libsrc/other/man3/im_eye.3 @@ -0,0 +1,47 @@ +.TH IM_EYE 3 "10 May 1991" +.SH NAME +im_eye, im_feye \- creates a pattern which shows the spatial response of the human visual system +.SH SYNOPSIS +.B #include + +.B int im_eye(image, xsize, ysize, factor) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; +.br +.B double factor; + +.B int im_feye(image, xsize, ysize, factor) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; +.br +.B double factor; + +.SH DESCRIPTION +.B im_feye() +creates an one band float image with pels in [-1,+1] of a pattern which has +the following properties: +.br +- the spatial frequency increases from left to right. +.br +- the grey level intensity decreases from top to bottom. +.br +The sizes of the produced image are determined by the entered arguments +xsize and ysize. The variable factor which should be between 0 and 1, +determines the number of maximum spatial frequencies present along the +horizontal direction. + +.B im_eye() +behaves exactly as im_feye(), but scales the output to [0,255]. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_grey(3), im_zone(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_feye.3 b/libsrc/other/man3/im_feye.3 new file mode 100644 index 00000000..0f1cc8cb --- /dev/null +++ b/libsrc/other/man3/im_feye.3 @@ -0,0 +1 @@ +.so man3/im_eye.3 diff --git a/libsrc/other/man3/im_fgrey.3 b/libsrc/other/man3/im_fgrey.3 new file mode 100644 index 00000000..af1bec37 --- /dev/null +++ b/libsrc/other/man3/im_fgrey.3 @@ -0,0 +1 @@ +.so man3/im_grey.3 diff --git a/libsrc/other/man3/im_fzone.3 b/libsrc/other/man3/im_fzone.3 new file mode 100644 index 00000000..4d100b95 --- /dev/null +++ b/libsrc/other/man3/im_fzone.3 @@ -0,0 +1 @@ +.so man3/im_zone.3 diff --git a/libsrc/other/man3/im_glds_asm.3 b/libsrc/other/man3/im_glds_asm.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/other/man3/im_glds_asm.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/other/man3/im_glds_contrast.3 b/libsrc/other/man3/im_glds_contrast.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/other/man3/im_glds_contrast.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/other/man3/im_glds_entropy.3 b/libsrc/other/man3/im_glds_entropy.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/other/man3/im_glds_entropy.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/other/man3/im_glds_matrix.3 b/libsrc/other/man3/im_glds_matrix.3 new file mode 100644 index 00000000..2f3aaf4f --- /dev/null +++ b/libsrc/other/man3/im_glds_matrix.3 @@ -0,0 +1,78 @@ +.TH IM_SGLDS_MATRIX 3 "10 May 1991" +.SH NAME +im_glds_matrix, im_glds_asm, im_glds_contrast, im_glds_mean, im_glds_entropy \- calculate the spatial grey level difference matrix and features on it +.SH SYNOPSIS +.B #include + +int im_glds_matrix(im, m, xp, yp, xs, ys, dx, dy) +.br +.B IMAGE *im, *m; +.br +.B int xp, yp, xs, ys; +.br +.B int dx, dy; + +.B int im_glds_asm(m, asmoment) +.br +.B IMAGE *m; +.br +.B double *asmoment; + +.B int im_glds_contrast(m, contrast) +.br +.B IMAGE *m; +.br +.B double *contrast; + +.B int im_glds_entropy(m, entropy) +.br +.B IMAGE *m; +.br +.B double *entropy; + +.B int im_glds_mean(m, mean) +.br +.B IMAGE *m; +.br +.B double *mean; + +.SH DESCRIPTION +.B im_glds_matrix() +creates a 256 by 1 one channel spatial grey level difference matrix (sglds) of +the box determined by the parameters (xp, yp; xs, ys) within the image pointed +by the IMAGE descriptor im. The matrix is written onto the IMAGE descriptor +m. The displacement vector is determined by (dx, dy). The user must ensure +that there is enough border pixels around the box within im dictated by the +displacement vector (dx,dy) or else the program fails. im should be one-band +unsigned char. + +All entries of the sgld matrix are double normalised to the number of pairs +involved. This function is a direct implementation of the paper: Haralick R. +M., Shanmugan K. and Dinstein I., 'Textural features for image +classification', IEEE Transactions on Systems, Man, and Cybernetics, Vol. +SMC-3, No 6, Nov. 1973, pp 610-621. + +.B im_glds_asm() +calculates the angular second moment of the co-occurrence matrix held by m. +The result is returned into the location pointed by asmoment. + +.B im_glds_contrast() +calculates the contrast of the sglds matrix held by m. The result is returned +into the location pointed by contrast. + +.B im_glds_entropy() +calculates the entropy of the sglds matrix held by m. The result is returned +into the location pointed by entropy. + +.B im_glds_mean() +calculates the mean of the sglds matrix held by m. The result is returned +into the location pointed by mean. +.SH RETURNED VALUES +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_cooc_matrix(3) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_glds_mean.3 b/libsrc/other/man3/im_glds_mean.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/other/man3/im_glds_mean.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/other/man3/im_grey.3 b/libsrc/other/man3/im_grey.3 new file mode 100644 index 00000000..1c9af7c5 --- /dev/null +++ b/libsrc/other/man3/im_grey.3 @@ -0,0 +1,46 @@ +.TH IM_GREY 3 "10 May 1991" +.SH NAME +im_grey, im_fgrey, im_make_xy \- creates a grey scale +.SH SYNOPSIS +.B #include + +.B int im_grey( image, xsize, ysize ) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; + +.B int im_fgrey( image, xsize, ysize ) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; + +.B int im_make_xy( image, xsize, ysize ) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; + +.SH DESCRIPTION +.B im_grey(3) +creates a one-band FMTUCHAR grey scale image of sizes xsize by +ysize. The intensity varies from 0 (left) to 255 (right). + +.B im_fgrey(3) +works as +.B im_grey(3), +except that the output image is FMTFLOAT, +allowing pixel values from 0 (left) to 1.0 (right). + +.B im_make_xy(3) +makes a two-band FMTUINT image where each pixel in band 0 has a value equal to +the x coordinate, and each pixel in band 1 has a value equal to the y +coordinate. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_black(3) +.SH COPYRIGHT +Birkbeck College and the National Gallery, 1995 diff --git a/libsrc/other/man3/im_make_xy.3 b/libsrc/other/man3/im_make_xy.3 new file mode 100644 index 00000000..af1bec37 --- /dev/null +++ b/libsrc/other/man3/im_make_xy.3 @@ -0,0 +1 @@ +.so man3/im_grey.3 diff --git a/libsrc/other/man3/im_mean_std_double_buffer.3 b/libsrc/other/man3/im_mean_std_double_buffer.3 new file mode 100644 index 00000000..8467ffdc --- /dev/null +++ b/libsrc/other/man3/im_mean_std_double_buffer.3 @@ -0,0 +1 @@ +.so man3/im_mean_std_int_buffer.3 diff --git a/libsrc/other/man3/im_mean_std_int_buffer.3 b/libsrc/other/man3/im_mean_std_int_buffer.3 new file mode 100644 index 00000000..bf2898ad --- /dev/null +++ b/libsrc/other/man3/im_mean_std_int_buffer.3 @@ -0,0 +1,38 @@ +.TH IM_MEAN_STD 3 "10 May 1991" +.SH NAME +im_mean_std_double_buffer, im_mean_std_int_buffer \- calculates the mean and the std of data held by a by an int or double buffer +.SH SYNOPSIS +.B #include + +int im_mean_std_int_buffer(buf, size, mean, std) +.br +.B int *buf; +.br +.B int size; +.br +.B double *mean, *std; + +int im_mean_std_double_buffer(buf, size, mean, std) +.br +.B double *buf; +.br +.B int size; +.br +.B double *mean, *std; + +.SH DESCRIPTION +im_mean_std_int_buffer() and im_mean_std_double_buffer() +calculate the mean and the standard deviation (std) of data held by the +integer or double buffer buf. The buffer has size elements. The results are +returned to the locations pointed by mean and std. +It is the responsibility of the calling function to ensure that there is +enough data in the buffer. +.SH RETURN VALUE +Both functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_avg(3), im_stats(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_quantim.3 b/libsrc/other/man3/im_quantim.3 new file mode 100644 index 00000000..c25c5138 --- /dev/null +++ b/libsrc/other/man3/im_quantim.3 @@ -0,0 +1,53 @@ +.TH IM_QUANTIM 3 "10 May 1991" +.SH NAME +im_quantim, im_quantlut, im_spatres \- quantise an image or a lut +.SH SYNOPSIS +.B #include + +.B int im_quantim(in, out, no_of_bits) +.br +.B IMAGE *in, *out; +.br +.B int no_of_bits; + +.B int im_quantlut(lut, no_of_bits) +.br +.B IMAGE *lut; +.br +.B int no_of_bits; + +.B int im_spatres(in, out, step) +.br +.B IMAGE *in, *out; +.br +.B int step; +.SH DESCRIPTION +.B im_quantim() +quantises the image file held by in using no_of_bits and writes the result to +out. The no_of_bits should be between 1 and 7 inclusive. Input can have any +number of bands. The function expects as input a valid unsigned char image. + +These functions are here for compatibility only. You should use the boolean +operations im_andconst(3), im_orconst(3), im_shrink(3), im_zoom(3) and +im_lowpass(3). + +.B im_quantlut() +creates an one band unsigned char +lookup table which is used by im_quantim(3). +The no_of_bits should be between 1 and 7 inclusive. + +.B im_spatres() +reduces the spatial resolution of in by averaging step*step pixels +and replicating the result in out. The function can be used in +order to show the effect of reducing the spatial resolution of a given image +and the importance of post-filtering before displaying. +Input can have any number of bands. +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_andconst(3), im_orconst(3), im_shrink(3), im_zoom(3) im_lowpass(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_quantlut.3 b/libsrc/other/man3/im_quantlut.3 new file mode 100644 index 00000000..81b965d9 --- /dev/null +++ b/libsrc/other/man3/im_quantlut.3 @@ -0,0 +1 @@ +.so man3/im_quantim.3 diff --git a/libsrc/other/man3/im_simcontr.3 b/libsrc/other/man3/im_simcontr.3 new file mode 100644 index 00000000..2871fa8f --- /dev/null +++ b/libsrc/other/man3/im_simcontr.3 @@ -0,0 +1,27 @@ +.TH IM_SIMCONTR 3 "10 May 1991" +.SH NAME +im_simcontr \- shows the effect of simultaneous contrast +.SH SYNOPSIS +.B #include + +.B int im_simcontr( image, xsize, ysize ) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; +.SH DESCRIPTION +.B im_simcontr() +creates an unsigned char one band grey scale image of sizes xsize by ysize. +The created pattern consists of two neighbouring squares one dark (left +square) and one light (right square). The left +one containes a smaller light square and the right +one containes a darker square. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_grey(3) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_sines.3 b/libsrc/other/man3/im_sines.3 new file mode 100644 index 00000000..4ee48707 --- /dev/null +++ b/libsrc/other/man3/im_sines.3 @@ -0,0 +1,29 @@ +.TH IM_SINES 3 "10 May 1991" +.SH NAME +im_sines \- creates a spatial sine wave form +.SH SYNOPSIS +.B #include + +.B int im_sines(image, xsize, ysize, horfreq, verfreq) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; +.br +.B double horfreq, verfreq; +.SH DESCRIPTION +.B im_sines() +creates a float one band image of the a sine waveform in two dimensions. +The sizes of the created image are xsize by ysize. The number of +horizontal and vertical spatial frequencies are determined by the variables +horfreq and verfreq respectively. The function is the base for creating +displayable sine waves and square waves in two dimensions. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_grey(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_spatres.3 b/libsrc/other/man3/im_spatres.3 new file mode 100644 index 00000000..81b965d9 --- /dev/null +++ b/libsrc/other/man3/im_spatres.3 @@ -0,0 +1 @@ +.so man3/im_quantim.3 diff --git a/libsrc/other/man3/im_zone.3 b/libsrc/other/man3/im_zone.3 new file mode 100644 index 00000000..14b1ec19 --- /dev/null +++ b/libsrc/other/man3/im_zone.3 @@ -0,0 +1,38 @@ +.TH IM_ZONE 3 "10 May 1991" +.SH NAME +im_zone, im_fzone \- creates a zone plate +.SH SYNOPSIS +.B #include + +.B int im_zone(image, size) +.br +.B IMAGE *image; +.br +.B int size; + +.B int im_fzone(image, size) +.br +.B IMAGE *image; +.br +.B int size; + +.SH DESCRIPTION + +.B im_fzone() +creates a float one band image of a zone plate of size size by size. Pels are +in the range [-1,+1]. The zone plate has spatial frequencies increasing from +0 the center (at size/2, size/2) up to infinity at the edges. size must be +positive and even. + +.B im_zone() +behaves exactly as im_fzone(), but writes a FMTUCHAR image scaled to the range +0-255. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_grey(3), im_fgrey(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/other_dispatch.c b/libsrc/other/other_dispatch.c new file mode 100644 index 00000000..33bf5f5b --- /dev/null +++ b/libsrc/other/other_dispatch.c @@ -0,0 +1,332 @@ +/* Function dispatch tables for other. + * + * J. Cupitt, 8/2/95 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Args for im_eye. + */ +static im_arg_desc eye_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xsize" ), + IM_INPUT_INT( "ysize" ), + IM_INPUT_DOUBLE( "factor" ) +}; + +/* Call im_eye via arg vector. + */ +static int +eye_vec( im_object *argv ) +{ + int xsize = *((int *) argv[1]); + int ysize = *((int *) argv[2]); + double factor = *((double *) argv[3]); + + return( im_eye( argv[0], xsize, ysize, factor ) ); +} + +/* Description of im_eye. + */ +static im_function eye_desc = { + "im_eye", /* Name */ + "generate IM_BANDFMT_UCHAR [0,255] frequency/amplitude image", + 0, /* Flags */ + eye_vec, /* Dispatch function */ + IM_NUMBER( eye_args ), /* Size of arg list */ + eye_args /* Arg list */ +}; + +/* Call im_feye via arg vector. + */ +static int +feye_vec( im_object *argv ) +{ + int xsize = *((int *) argv[1]); + int ysize = *((int *) argv[2]); + double factor = *((double *) argv[3]); + + return( im_feye( argv[0], xsize, ysize, factor ) ); +} + +/* Description of im_feye. + */ +static im_function feye_desc = { + "im_feye", /* Name */ + "generate IM_BANDFMT_FLOAT [-1,1] frequency/amplitude image", + 0, /* Flags */ + feye_vec, /* Dispatch function */ + IM_NUMBER( eye_args ), /* Size of arg list */ + eye_args /* Arg list */ +}; + +/* Args for im_zone. + */ +static im_arg_desc zone_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "size" ) +}; + +/* Call im_zone via arg vector. + */ +static int +zone_vec( im_object *argv ) +{ + int size = *((int *) argv[1]); + + return( im_zone( argv[0], size ) ); +} + +/* Description of im_zone. + */ +static im_function zone_desc = { + "im_zone", /* Name */ + "generate IM_BANDFMT_UCHAR [0,255] zone plate image", /* Description */ + 0, /* Flags */ + zone_vec, /* Dispatch function */ + IM_NUMBER( zone_args ), /* Size of arg list */ + zone_args /* Arg list */ +}; + +/* Call im_fzone via arg vector. + */ +static int +fzone_vec( im_object *argv ) +{ + int size = *((int *) argv[1]); + + return( im_fzone( argv[0], size ) ); +} + +/* Description of im_fzone. + */ +static im_function fzone_desc = { + "im_fzone", /* Name */ + "generate IM_BANDFMT_FLOAT [-1,1] zone plate image", /* Description */ + 0, /* Flags */ + fzone_vec, /* Dispatch function */ + IM_NUMBER( zone_args ), /* Size of arg list */ + zone_args /* Arg list */ +}; + +/* Args for im_benchmark. + */ +static im_arg_desc benchmark_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_benchmark via arg vector. + */ +static int +benchmark_vec( im_object *argv ) +{ + return( im_benchmarkn( argv[0], argv[1], 1 ) ); +} + +/* Description of im_benchmark. + */ +static im_function benchmark_desc = { + "im_benchmark", /* Name */ + "do something complicated for testing", /* Description */ + IM_FN_PIO, /* Flags */ + benchmark_vec, /* Dispatch function */ + IM_NUMBER( benchmark_args ), /* Size of arg list */ + benchmark_args /* Arg list */ +}; + +/* Args for im_benchmark2. + */ +static im_arg_desc benchmark2_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_DOUBLE( "value" ) +}; + +/* Call im_benchmark2 via arg vector. + */ +static int +benchmark2_vec( im_object *argv ) +{ + double f; + + if( im_benchmark2( argv[0], &f ) ) + return( -1 ); + + *((double *) argv[1]) = f; + + return( 0 ); +} + +/* Description of im_benchmark2. + */ +static im_function benchmark2_desc = { + "im_benchmark2", /* Name */ + "do something complicated for testing", /* Description */ + IM_FN_PIO, /* Flags */ + benchmark2_vec, /* Dispatch function */ + IM_NUMBER( benchmark2_args ), /* Size of arg list */ + benchmark2_args /* Arg list */ +}; + +/* Args for im_benchmarkn. + */ +static im_arg_desc benchmarkn_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "n" ) +}; + +/* Call im_benchmarkn via arg vector. + */ +static int +benchmarkn_vec( im_object *argv ) +{ + int n = *((int *) argv[2]); + + return( im_benchmarkn( argv[0], argv[1], n ) ); +} + +/* Description of im_benchmarkn. + */ +static im_function benchmarkn_desc = { + "im_benchmarkn", /* Name */ + "do something complicated for testing", /* Description */ + IM_FN_PIO, /* Flags */ + benchmarkn_vec, /* Dispatch function */ + IM_NUMBER( benchmarkn_args ), /* Size of arg list */ + benchmarkn_args /* Arg list */ +}; + +/* Args for im_grey. + */ +static im_arg_desc grey_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xsize" ), + IM_INPUT_INT( "ysize" ) +}; + +/* Call im_grey via arg vector. + */ +static int +grey_vec( im_object *argv ) +{ + int xsize = *((int *) argv[1]); + int ysize = *((int *) argv[2]); + + return( im_grey( argv[0], xsize, ysize ) ); +} + +/* Description of im_grey. + */ +static im_function grey_desc = { + "im_grey", /* Name */ + "generate IM_BANDFMT_UCHAR [0,255] grey scale image", /* Description */ + 0, /* Flags */ + grey_vec, /* Dispatch function */ + IM_NUMBER( grey_args ), /* Size of arg list */ + grey_args /* Arg list */ +}; + +/* Call im_fgrey via arg vector. + */ +static int +fgrey_vec( im_object *argv ) +{ + int xsize = *((int *) argv[1]); + int ysize = *((int *) argv[2]); + + return( im_fgrey( argv[0], xsize, ysize ) ); +} + +/* Description of im_fgrey. + */ +static im_function fgrey_desc = { + "im_fgrey", /* Name */ + "generate IM_BANDFMT_FLOAT [0,1] grey scale image", /* Description */ + 0, /* Flags */ + fgrey_vec, /* Dispatch function */ + IM_NUMBER( grey_args ), /* Size of arg list */ + grey_args /* Arg list */ +}; + +/* Call im_make_xy via arg vector. + */ +static int +make_xy_vec( im_object *argv ) +{ + int xsize = *((int *) argv[1]); + int ysize = *((int *) argv[2]); + + return( im_make_xy( argv[0], xsize, ysize ) ); +} + +/* Description of im_make_xy. + */ +static im_function make_xy_desc = { + "im_make_xy", /* Name */ + "generate image with pixel value equal to coordinate", /* Description */ + 0, /* Flags */ + make_xy_vec, /* Dispatch function */ + IM_NUMBER( grey_args ), /* Size of arg list */ + grey_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *other_list[] = { + &benchmark_desc, + &benchmark2_desc, + &benchmarkn_desc, + &eye_desc, + &grey_desc, + &feye_desc, + &fgrey_desc, + &fzone_desc, + &make_xy_desc, + &zone_desc +}; + +/* Package of functions. + */ +im_package im__other = { + "other", + IM_NUMBER( other_list ), + other_list +}; diff --git a/libsrc/relational/Makefile.am b/libsrc/relational/Makefile.am new file mode 100644 index 00000000..df0be055 --- /dev/null +++ b/libsrc/relational/Makefile.am @@ -0,0 +1,11 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = librelational.la + +librelational_la_SOURCES = \ + im_ifthenelse.c \ + im_blend.c \ + relational.c \ + relational_dispatch.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/relational/im_blend.c b/libsrc/relational/im_blend.c new file mode 100644 index 00000000..78ddf958 --- /dev/null +++ b/libsrc/relational/im_blend.c @@ -0,0 +1,364 @@ +/* @(#) Two images as input: must match in size and type. Build an output + * @(#) image blending pixels together according to a conditional image. + * @(#) + * @(#) The conditional image can have n bands or 1 band. If n bands, then we + * @(#) choose from the two source images an element at a time. If 1 band, + * @(#) then choose from the source images a pixel at a time. + * @(#) + * @(#) int + * @(#) im_blend( c, a, b, out ) + * @(#) IMAGE *c, *a, *b; + * @(#) IMAGE *out; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail). + * + * Modified: + * 15/4/05 + * - from im_ifthenelse() + * 8/7/05 + * - oops, broken for some combinations of band differences (thanks Joe) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define iblend1( TYPE ) { \ + TYPE *a = (TYPE *) ap; \ + TYPE *b = (TYPE *) bp; \ + TYPE *q = (TYPE *) qp; \ + \ + for( i = 0, x = 0; x < n; i++, x += bands ) { \ + const int v = c[i]; \ + \ + for( z = x; z < x + bands; z++ ) \ + q[z] = (v * a[z] + (255 - v) * b[z] + 128) / 255; \ + } \ +} + +#define iblendn( TYPE ) { \ + TYPE *a = (TYPE *) ap; \ + TYPE *b = (TYPE *) bp; \ + TYPE *q = (TYPE *) qp; \ + \ + for( x = 0; x < n; x += bands ) { \ + for( z = x; z < x + bands; z++ ) { \ + const int v = c[z]; \ + \ + q[z] = (v * a[z] + (255 - v) * b[z] + 128) / 255; \ + } \ + } \ +} + +#define fblend1( TYPE ) { \ + TYPE *a = (TYPE *) ap; \ + TYPE *b = (TYPE *) bp; \ + TYPE *q = (TYPE *) qp; \ + \ + for( i = 0, x = 0; x < n; i++, x += bands ) { \ + const double v = c[i] / 255.0; \ + \ + for( z = x; z < x + bands; z++ ) \ + q[z] = v * a[z] + (1.0 - v) * b[z]; \ + } \ +} + +#define fblendn( TYPE ) { \ + TYPE *a = (TYPE *) ap; \ + TYPE *b = (TYPE *) bp; \ + TYPE *q = (TYPE *) qp; \ + \ + for( x = 0; x < n; x += bands ) { \ + for( z = x; z < x + bands; z++ ) { \ + const double v = c[z] / 255.0; \ + \ + q[z] = v * a[z] + (1.0 - v) * b[z]; \ + } \ + } \ +} + +#define cblend1( TYPE ) { \ + TYPE *a = (TYPE *) ap; \ + TYPE *b = (TYPE *) bp; \ + TYPE *q = (TYPE *) qp; \ + \ + for( i = 0, x = 0; x < n; i++, x += bands ) { \ + const double v = c[i] / 255.0; \ + \ + for( z = x; z < x + 2 * bands; z++ ) \ + q[z] = v * a[z] + (1.0 - v) * b[z]; \ + } \ +} + +#define cblendn( TYPE ) { \ + TYPE *a = (TYPE *) ap; \ + TYPE *b = (TYPE *) bp; \ + TYPE *q = (TYPE *) qp; \ + \ + for( x = 0; x < n; x += bands ) { \ + for( z = x; z < x + bands; z++ ) { \ + const double v = c[z] / 255.0; \ + \ + q[2 * z] = v * a[2 * z] + (1.0 - v) * b[2 * z]; \ + q[2 * z + 1] = v * a[2 * z + 1] + \ + (1.0 - v) * b[2 * z + 1]; \ + } \ + } \ +} + +/* Blend with a 1-band conditional image. + */ +static void +blend1_buffer( PEL *qp, PEL *c, PEL *ap, PEL *bp, int width, IMAGE *im ) +{ + int i, x, z; + const int bands = im->Bands; + const int n = width * bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + iblend1( unsigned char ); break; + case IM_BANDFMT_CHAR: + iblend1( signed char ); break; + case IM_BANDFMT_USHORT: + iblend1( unsigned short ); break; + case IM_BANDFMT_SHORT: + iblend1( signed short ); break; + case IM_BANDFMT_UINT: + iblend1( unsigned int ); break; + case IM_BANDFMT_INT: + iblend1( signed int ); break; + case IM_BANDFMT_FLOAT: + fblend1( float ); break; + case IM_BANDFMT_DOUBLE: + fblend1( double ); break; + case IM_BANDFMT_COMPLEX: + cblend1( float ); break; + case IM_BANDFMT_DPCOMPLEX: + cblend1( double ); break; + + default: + assert( 0 ); + } +} + +/* Blend with a many band conditional image. + */ +static void +blendn_buffer( PEL *qp, PEL *c, PEL *ap, PEL *bp, int width, IMAGE *im ) +{ + int x, z; + const int bands = im->Bands; + const int n = width * bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + iblendn( unsigned char ); break; + case IM_BANDFMT_CHAR: + iblendn( signed char ); break; + case IM_BANDFMT_USHORT: + iblendn( unsigned short ); break; + case IM_BANDFMT_SHORT: + iblendn( signed short ); break; + case IM_BANDFMT_UINT: + iblendn( unsigned int ); break; + case IM_BANDFMT_INT: + iblendn( signed int ); break; + case IM_BANDFMT_FLOAT: + fblendn( float ); break; + case IM_BANDFMT_DOUBLE: + fblendn( double ); break; + case IM_BANDFMT_COMPLEX: + cblendn( float ); break; + case IM_BANDFMT_DPCOMPLEX: + cblendn( double ); break; + + default: + assert( 0 ); + } +} + +static int +blend_gen( REGION *or, REGION **ir ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + IMAGE *c = ir[0]->im; + IMAGE *a = ir[1]->im; + + int c_elements = r->width * c->Bands; + int x, y; + + int all0, all255; + + /* Ask for condition pixels. + */ + if( im_prepare( ir[0], r ) ) + return( -1 ); + + /* Is the conditional all zero or all non-zero? We can avoid asking + * for one of the inputs to be calculated. + */ + all0 = *((PEL *) IM_REGION_ADDR( ir[0], le, to )) == 0; + all255 = *((PEL *) IM_REGION_ADDR( ir[0], le, to )) == 255; + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir[0], le, y ); + + for( x = 0; x < c_elements; x++ ) { + all0 &= p[x] == 0; + all255 &= p[x] == 255; + } + + if( !all0 && !all255 ) + break; + } + + if( all255 ) { + /* All 255. Point or at the then image. + */ + if( im_prepare( ir[1], r ) || + im_region_region( or, ir[1], r, r->left, r->top ) ) + return( -1 ); + } + else if( all0 ) { + /* All zero. Point or at the else image. + */ + if( im_prepare( ir[2], r ) || + im_region_region( or, ir[2], r, r->left, r->top ) ) + return( -1 ); + } + else { + /* Mix of set and clear ... ask for both then and else parts and + * interleave. + */ + if( im_prepare( ir[1], r ) || im_prepare( ir[2], r ) ) + return( -1 ); + + for( y = to; y < bo; y++ ) { + PEL *cp = (PEL *) IM_REGION_ADDR( ir[0], le, y ); + PEL *ap = (PEL *) IM_REGION_ADDR( ir[1], le, y ); + PEL *bp = (PEL *) IM_REGION_ADDR( ir[2], le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + if( c->Bands == 1 ) + blend1_buffer( q, cp, ap, bp, r->width, a ); + else + blendn_buffer( q, cp, ap, bp, r->width, a ); + } + } + + return( 0 ); +} + +int +im_blend( IMAGE *c, IMAGE *a, IMAGE *b, IMAGE *out ) +{ + IMAGE **in; + + /* If a and b are both LABPACK, repack agan after the blend. + */ + if( a->Coding == IM_CODING_LABQ || b->Coding == IM_CODING_LABQ ) { + IMAGE *t[3]; + int repack = a->Coding == IM_CODING_LABQ && + b->Coding == IM_CODING_LABQ; + + if( im_open_local_array( out, t, 3, "relational-1", "p" ) ) + return( -1 ); + + if( a->Coding == IM_CODING_LABQ ) { + if( im_LabQ2Lab( a, t[0] ) ) + return( -1 ); + a = t[0]; + } + + if( b->Coding == IM_CODING_LABQ ) { + if( im_LabQ2Lab( b, t[1] ) ) + return( -1 ); + b = t[1]; + } + + if( repack ) + return( im_blend( c, a, b, t[2] ) || + im_Lab2LabQ( t[2], out ) ); + else + return( im_blend( c, a, b, out ) ); + } + + /* Check args. + */ + if( a->Coding != IM_CODING_NONE || b->Coding != IM_CODING_NONE || + c->Coding != IM_CODING_NONE ) { + im_error( "im_blend", _( "images not uncoded" ) ); + return( -1 ); + } + if( a->BandFmt != b->BandFmt || + a->Bands != b->Bands ) { + im_error( "im_blend", + _( "size and format of then and else must match" ) ); + return( -1 ); + } + if( c->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_blend", + _( "conditional image must be uchar" ) ); + return( -1 ); + } + if( c->Bands != 1 && c->Bands != a->Bands ) { + im_error( "im_blend", + _( "conditional image must be one band or same as " + "then and else images" ) ); + return( -1 ); + } + if( im_piocheck( c, out ) || im_pincheck( a ) || im_pincheck( b ) ) + return( -1 ); + if( im_demand_hint( out, IM_THINSTRIP, a, b, c, NULL ) ) + return( -1 ); + + /* Make output image. + */ + if( im_cp_descv( out, a, b, c, NULL ) || + !(in = im_allocate_input_array( out, c, a, b, NULL )) || + im_generate( out, + im_start_many, blend_gen, im_stop_many, + in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/relational/im_ifthenelse.c b/libsrc/relational/im_ifthenelse.c new file mode 100644 index 00000000..7ffa6d48 --- /dev/null +++ b/libsrc/relational/im_ifthenelse.c @@ -0,0 +1,211 @@ +/* @(#) Two images as input: must match in size and type. Build an output + * @(#) image choosing pixels from the left if a conditional image is + * @(#) non-zero and from the right otherwise. + * @(#) + * @(#) The conditional image can have n bands or 1 band. If n bands, then we + * @(#) choose from the two source images an element at a time. If 1 band, + * @(#) then choose from the source images a pixel at a time. + * @(#) + * @(#) int + * @(#) im_ifthenelse( c, a, b, out ) + * @(#) IMAGE *c, *a, *b; + * @(#) IMAGE *out; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail). + * + * Modified: + * 9/2/95 JC + * - partialed and ANSIfied + * 11/9/95 JC + * - return( 0 ) missing! oops + * 15/4/05 + * - now just evals left/right if all zero/all one + * 7/10/06 + * - set THINSTRIP + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +ifthenelse_gen( REGION *or, REGION **ir ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + IMAGE *c = ir[0]->im; + IMAGE *a = ir[1]->im; + + int size, width; + int i, x, y, z; + + int all0, alln0; + + if( c->Bands == 1 ) { + /* Copying PEL-sized units with a one-band conditional. + */ + size = IM_IMAGE_SIZEOF_PEL( a ); + width = r->width; + } + else { + /* Copying ELEMENT sized-units with an n-band conditional. + */ + size = IM_IMAGE_SIZEOF_ELEMENT( a ); + width = r->width * a->Bands; + } + + if( im_prepare( ir[0], r ) ) + return( -1 ); + + /* Is the conditional all zero or all non-zero? We can avoid asking + * for one of the inputs to be calculated. + */ + all0 = *((PEL *) IM_REGION_ADDR( ir[0], le, to )) == 0; + alln0 = *((PEL *) IM_REGION_ADDR( ir[0], le, to )) != 0; + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir[0], le, y ); + + for( x = 0; x < width; x++ ) { + all0 &= p[x] == 0; + alln0 &= p[x] != 0; + } + + if( !all0 && !alln0 ) + break; + } + + if( alln0 ) { + /* All non-zero. Point or at the then image. + */ + if( im_prepare( ir[1], r ) || + im_region_region( or, ir[1], r, r->left, r->top ) ) + return( -1 ); + } + else if( all0 ) { + /* All zero. Point or at the else image. + */ + if( im_prepare( ir[2], r ) || + im_region_region( or, ir[2], r, r->left, r->top ) ) + return( -1 ); + } + else { + /* Mix of set and clear ... ask for both then and else parts + * and interleave. + */ + if( im_prepare( ir[1], r ) || im_prepare( ir[2], r ) ) + return( -1 ); + + for( y = to; y < bo; y++ ) { + PEL *cp = (PEL *) IM_REGION_ADDR( ir[0], le, y ); + PEL *ap = (PEL *) IM_REGION_ADDR( ir[1], le, y ); + PEL *bp = (PEL *) IM_REGION_ADDR( ir[2], le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + for( x = 0, i = 0; i < width; i++, x += size ) { + if( cp[i] ) + for( z = x; z < x + size; z++ ) + q[z] = ap[z]; + else + for( z = x; z < x + size; z++ ) + q[z] = bp[z]; + } + } + } + + return( 0 ); +} + +/* if-then-else. Use IM_BANDFMT_UCHAR image to choose between two other images. + * Either: all same number of bands, or choice image is one band, others are + * n band. + */ +int +im_ifthenelse( IMAGE *c, IMAGE *a, IMAGE *b, IMAGE *out ) +{ + IMAGE **in; + + /* Check args. + */ + if( a->Coding != IM_CODING_NONE && a->Coding != IM_CODING_LABQ ) { + im_error( "im_ifthenelse", + _( "then image must be uncoded or labpack" ) ); + return( -1 ); + } + if( b->Coding != IM_CODING_NONE && b->Coding != IM_CODING_LABQ ) { + im_error( "im_ifthenelse", + _( "else image must be uncoded or labpack" ) ); + return( -1 ); + } + if( c->Coding != IM_CODING_NONE ) { + im_error( "im_ifthenelse", + _( "condition image must be uncoded" ) ); + return( -1 ); + } + if( a->BandFmt != b->BandFmt || + a->Bands != b->Bands ) { + im_error( "im_ifthenelse", + _( "size and format of then and else must match" ) ); + return( -1 ); + } + if( c->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_ifthenelse", + _( "conditional image must be uchar" ) ); + return( -1 ); + } + if( c->Bands != 1 && c->Bands != a->Bands ) { + im_error( "im_ifthenelse", + _( "conditional image must be one band or same as " + "then and else images" ) ); + return( -1 ); + } + + /* Make output image. + */ + if( im_demand_hint( out, IM_THINSTRIP, c, a, b, NULL ) || + im_cp_descv( out, a, b, c, NULL ) || + !(in = im_allocate_input_array( out, c, a, b, NULL )) || + im_generate( out, + im_start_many, ifthenelse_gen, im_stop_many, + in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/relational/man3/Makefile.am b/libsrc/relational/man3/Makefile.am new file mode 100644 index 00000000..1e6c0562 --- /dev/null +++ b/libsrc/relational/man3/Makefile.am @@ -0,0 +1,24 @@ +man_MANS = \ + im_equal.3 \ + im_equalconst.3 \ + im_ifthenelse.3 \ + im_less.3 \ + im_lessconst.3 \ + im_lesseq.3 \ + im_lesseqconst.3 \ + im_more.3 \ + im_moreconst.3 \ + im_moreeq.3 \ + im_moreeqconst.3 \ + im_blend.3 \ + im_notequal.3 \ + im_notequalconst.3 \ + im_equal_vec.3 \ + im_less_vec.3 \ + im_lesseq_vec.3 \ + im_more_vec.3 \ + im_moreeq_vec.3 \ + im_notequal_vec.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/relational/man3/im_blend.3 b/libsrc/relational/man3/im_blend.3 new file mode 100644 index 00000000..fab8f2d5 --- /dev/null +++ b/libsrc/relational/man3/im_blend.3 @@ -0,0 +1 @@ +.so man3/im_ifthenelse.3 diff --git a/libsrc/relational/man3/im_equal.3 b/libsrc/relational/man3/im_equal.3 new file mode 100644 index 00000000..e953cda7 --- /dev/null +++ b/libsrc/relational/man3/im_equal.3 @@ -0,0 +1,141 @@ +.TH RELATIONAL 3 "30 October 1992" +.SH NAME +im_equal, im_notequal, im_equalconst, im_equal_vec, im_notequalconst, +im_notequal_vec, im_less, im_lessconst, im_less_vec, +im_more, im_moreconst, im_more_vec, im_lesseq, im_lesseqconst, im_lesseq_vec, +im_moreeq, +im_moreeqconst, im_moreeq_vec \- relational tests on images. +.SH SYNOPSIS +.B #include + +.B int im_equal(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_equalconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_equal_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_notequal(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_notequalconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_notequal_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_less(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_lessconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_less_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_more(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_moreconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_more_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_lesseq(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_lesseqconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_lesseq_vec(a, out, n) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_moreeq(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_moreeqconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_moreeq_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.SH DESCRIPTION +These functions perform a range of relational tests between images, or between +an image and a constant. Functions which take two images as arguments require +that the two images be the same size and have the same number of bands. They +can be of mixed type: you may compare an unsigned char image with a double +image. + +All functions return an unsigned char image, with the same number of bands as +the input images, in which band elements have been set to 255 for true and 0 +for false. + +The logical functions (im_andimage(3), im_orimage(3), im_eorimage(3)) may be +used to combine boolean images to make more complex tests. The selection +function im_ifthenelse(3) may be used to take action on the result of a test. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_ifthenelse(3), im_andimage(3). +.SH COPYRIGHT +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/relational/man3/im_equal_vec.3 b/libsrc/relational/man3/im_equal_vec.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_equal_vec.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_equalconst.3 b/libsrc/relational/man3/im_equalconst.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_equalconst.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_ifthenelse.3 b/libsrc/relational/man3/im_ifthenelse.3 new file mode 100644 index 00000000..38c07e11 --- /dev/null +++ b/libsrc/relational/man3/im_ifthenelse.3 @@ -0,0 +1,39 @@ +.TH IM_IFTHENELSE 3 "30 October 1992" +.SH NAME +im_ifthenelse \- use an unsigned char image to join two images together +.SH SYNOPSIS +.B #include + +.B int im_ifthenelse(c, a, b, out) +.br +.B IMAGE *c, *a, *b, *out; + +.B int im_blend(c, a, b, out) +.br +.B IMAGE *c, *a, *b, *out; + +.SH DESCRIPTION +.B im_ifthenelse +builds an output image which uses some pels from a and some from b: if the +conditional image c is non-zero at that point, the pel comes from a; if it +is zero, the pel comes from b. + +The conditional image c can have either 1 band, in which case entire pels +come either from a or b, or n bands, where n is the number of bands in both a +and b, in which case individual band elements are chosen from a and b. + +Images a and b must match in size, type and number of bands. + +.B im_blend(3) +works just as im_ifthenelse(3), except that instead of selecting between a and +b, values in the condition image are used to softly blend between the two. 255 +means a only, 0 means b only, 128 means 50:50. + +.SH RETURN VALUE +0 on success and -1 on error. +.SH SEE ALSO +im_equal(3), im_and(3). +.SH COPYRIGHT +National Gallery +.SH AUTHOR +J. Cupitt diff --git a/libsrc/relational/man3/im_less.3 b/libsrc/relational/man3/im_less.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_less.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_less_vec.3 b/libsrc/relational/man3/im_less_vec.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_less_vec.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_lessconst.3 b/libsrc/relational/man3/im_lessconst.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_lessconst.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_lesseq.3 b/libsrc/relational/man3/im_lesseq.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_lesseq.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_lesseq_vec.3 b/libsrc/relational/man3/im_lesseq_vec.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_lesseq_vec.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_lesseqconst.3 b/libsrc/relational/man3/im_lesseqconst.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_lesseqconst.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_more.3 b/libsrc/relational/man3/im_more.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_more.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_more_vec.3 b/libsrc/relational/man3/im_more_vec.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_more_vec.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_moreconst.3 b/libsrc/relational/man3/im_moreconst.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_moreconst.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_moreeq.3 b/libsrc/relational/man3/im_moreeq.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_moreeq.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_moreeq_vec.3 b/libsrc/relational/man3/im_moreeq_vec.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_moreeq_vec.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_moreeqconst.3 b/libsrc/relational/man3/im_moreeqconst.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_moreeqconst.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_notequal.3 b/libsrc/relational/man3/im_notequal.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_notequal.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_notequal_vec.3 b/libsrc/relational/man3/im_notequal_vec.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_notequal_vec.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_notequalconst.3 b/libsrc/relational/man3/im_notequalconst.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_notequalconst.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/relational.c b/libsrc/relational/relational.c new file mode 100644 index 00000000..ec9c7542 --- /dev/null +++ b/libsrc/relational/relational.c @@ -0,0 +1,738 @@ +/* @(#) Relational operations on VASARI images. All return a unsigned + * @(#) char image with the same number of bands as the input images. 255 + * @(#) for true, 0 for false. All work with mixed images types: eg. + * @(#) comparing float and byte. + * @(#) + * @(#) int im_equal( a, b, out ) int im_notequal( a, b, out ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *b, *out; + * @(#) + * @(#) + * @(#) int im_equalconst( a, out, c ) int im_notequalconst( a, out, c ) + * @(#) IMAGE *a, *out; IMAGE *a, *out; + * @(#) double c; double c; + * @(#) + * @(#) int im_less( a, b, out ) int im_lessconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) double c; + * @(#) + * @(#) int im_more( a, b, out ) int im_moreconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) double c; + * @(#) + * @(#) int im_lesseq( a, b, out ) int im_lesseqconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) double c; + * @(#) + * @(#) int im_moreeq( a, b, out ) int im_moreeqconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) double c; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail). + * + * Modified: + * 26/7/93 JC + * - >,<,>=,<= tests now as (double) to prevent compiler warnings. Should + * split into int/float cases really for speed. + * 25/1/95 JC + * - partialized + * - updated + * 7/2/95 JC + * - oops! bug with doubles fixed + * 3/7/98 JC + * - vector versions added ... im_equal_vec(), im_lesseq_vec() etc + * - small tidies + * - should be a bit faster, lots of *q++ changed to q[x] + * 10/3/03 JC + * - reworked to remove nested #defines: a bit slower, but much smaller + * - all except _vec forms now work on complex + * 31/7/03 JC + * - oops, relational_format was broken for some combinations + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Save a bit of typing. + */ +#define UC IM_BANDFMT_UCHAR +#define C IM_BANDFMT_CHAR +#define US IM_BANDFMT_USHORT +#define S IM_BANDFMT_SHORT +#define UI IM_BANDFMT_UINT +#define I IM_BANDFMT_INT +#define F IM_BANDFMT_FLOAT +#define M IM_BANDFMT_COMPLEX +#define D IM_BANDFMT_DOUBLE +#define DM IM_BANDFMT_DPCOMPLEX + +/* Type conversions for relational operators. For two input types, give the + * smallest common type, that is, the smallest type which can completely + * express the range of each. + */ +static int relational_format[10][10] = { + /* UC C US S UI I F M D DM */ +/* UC */ { UC, S, US, S, UI, I, F, M, D, DM }, +/* C */ { S, C, I, S, D, I, F, M, D, DM }, +/* US */ { US, I, US, I, UI, I, F, M, D, DM }, +/* S */ { S, S, I, S, D, I, F, M, D, DM }, +/* UI */ { UI, D, UI, D, UI, D, F, M, D, DM }, +/* I */ { I, I, I, I, D, I, F, M, D, DM }, +/* F */ { F, F, F, F, F, F, F, M, D, DM }, +/* M */ { M, M, M, M, M, M, M, M, DM, DM }, +/* D */ { D, D, D, D, D, D, D, DM, D, DM }, +/* DM */ { DM, DM, DM, DM, DM, DM, DM, DM, DM, DM } +}; + +/* Check input images, cast both up to the smallest common type, and invoke + * the process function. + */ +static int +relational_process( char *name, IMAGE **in, IMAGE *out, + im_wrapmany_fn fn, void *b ) +{ + int i, n; + + /* Count args. + */ + for( n = 0; in[n]; n++ ) { + if( in[n]->Coding != IM_CODING_NONE ) { + im_errormsg( "%s: uncoded images only", name ); + return( -1 ); + } + } + + /* Check sizes match. We don't need to check xsize/ysize, as wrapmany + * does this for us. + */ + for( i = 1; i < n; i++ ) + if( in[0]->Bands != in[i]->Bands ) { + im_errormsg( "%s: images differ in numbers of bands", + name ); + return( -1 ); + } + + /* Prepare the output image. + */ + if( im_cp_desc_array( out, in ) ) + return( -1 ); + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = IM_BBITS_BYTE; + + /* For binary ops, cast inputs up to a common format. + */ + if( n == 2 ) { + int fmt = relational_format[in[0]->BandFmt][in[1]->BandFmt]; + IMAGE *t[3]; + + if( im_open_local_array( out, t, 2, "relational-1", "p" ) ) + return( -1 ); + t[2] = NULL; + + for( i = 0; i < n; i++ ) + if( im_clip2fmt( in[i], t[i], fmt ) ) + return( -1 ); + + if( im_wrapmany( t, out, fn, t[0], b ) ) + return( -1 ); + } + else + if( im_wrapmany( in, out, fn, in[0], b ) ) + return( -1 ); + + return( 0 ); +} + +/* Switch over bandfmt, calling a complexd and a non-complex processor. + */ +#define SWITCH( T, P_REAL, P_COMPLEX ) \ + switch( T ) {\ + case IM_BANDFMT_UCHAR: \ + P_REAL( unsigned char ); \ + break; \ + case IM_BANDFMT_CHAR: \ + P_REAL( char ); \ + break; \ + case IM_BANDFMT_USHORT: \ + P_REAL( unsigned short ); \ + break; \ + case IM_BANDFMT_SHORT: \ + P_REAL( short ); \ + break; \ + case IM_BANDFMT_UINT: \ + P_REAL( unsigned int ); \ + break; \ + case IM_BANDFMT_INT: \ + P_REAL( int ); \ + break; \ + case IM_BANDFMT_FLOAT: \ + P_REAL( float ); \ + break; \ + case IM_BANDFMT_DOUBLE: \ + P_REAL( double ); \ + break; \ + case IM_BANDFMT_COMPLEX: \ + P_COMPLEX( float ); \ + break; \ + case IM_BANDFMT_DPCOMPLEX: \ + P_COMPLEX( double ); \ + break; \ + default:\ + error_exit( "relational: internal error" );\ + } + +static void +equal_buffer( PEL **p, PEL *q, int n, IMAGE *a ) +{ + int ne = n * a->Bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + int x; + +#define EQUAL_REAL( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + if( i[x] == j[x] ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ +} + +#define EQUAL_COMPLEX( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) { \ + if( i[0] == j[0] && i[1] == j[1] ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ + \ + i += 2; \ + j += 2; \ + } \ +} + + SWITCH( a->BandFmt, EQUAL_REAL, EQUAL_COMPLEX ); +} + +int +im_equal( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( relational_process( "im_equal", invec, out, + (im_wrapmany_fn) equal_buffer, NULL ) ) + return( -1 ); + + return( 0 ); +} + +static void +notequal_buffer( PEL **p, PEL *q, int n, IMAGE *a ) +{ + int ne = n * a->Bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + int x; + +#define NOTEQUAL_REAL( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + if( i[x] != j[x] ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ +} + +#define NOTEQUAL_COMPLEX( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) { \ + if( i[0] != j[0] || i[1] != j[1] ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ + \ + i += 2; \ + j += 2; \ + } \ +} + + SWITCH( a->BandFmt, NOTEQUAL_REAL, NOTEQUAL_COMPLEX ); +} + +int +im_notequal( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( relational_process( "im_equal", invec, out, + (im_wrapmany_fn) notequal_buffer, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* strdup a vector of doubles. + */ +static double * +numdup( IMAGE *out, int n, double *c ) +{ + double *p = IM_ARRAY( out, n, double ); + int i; + + if( !p ) + return( NULL ); + + for( i = 0; i < n; i++ ) + p[i] = c[i]; + + return( p ); +} + +static void +equalvec_buffer( PEL **in, PEL *out, int n, IMAGE *a, double *c ) +{ + int x, b, i; + +#define EQUALVEC_REAL( TYPE ) { \ + TYPE *p = (TYPE *) in[0]; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < a->Bands; b++, i++ ) \ + if( p[i] == c[b] ) \ + out[i] = 255; \ + else \ + out[i] = 0; \ +} + +/* Sanity failure! + */ +#define EQUALVEC_COMPLEX( TYPE ) assert( 0 ); + + SWITCH( a->BandFmt, EQUALVEC_REAL, EQUALVEC_COMPLEX ); +} + +int +im_equal_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + double *p; + + if( n != in->Bands ) { + im_errormsg( "im_equal_vec: vec size does not match bands" ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_errormsg( "im_equal_vec: not implemented for complex" ); + return( -1 ); + } + + invec[0] = in; invec[1] = NULL; + if( !(p = numdup( out, n, c )) || + relational_process( "im_equal_vec", invec, out, + (im_wrapmany_fn) equalvec_buffer, (void *) p ) ) + return( -1 ); + + return( 0 ); +} + +static double * +mkvec( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + int i; + + if( !(v = IM_ARRAY( out, in->Bands, double )) ) + return( NULL ); + for( i = 0; i < in->Bands; i++ ) + v[i] = c; + + return( v ); +} + +int +im_equalconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + + return( !(v = mkvec( in, out, c )) || + im_equal_vec( in, out, in->Bands, v ) ); +} + +static void +notequalvec_buffer( PEL **in, PEL *out, int n, IMAGE *a, double *c ) +{ + int x, b, i; + +#define NOTEQUALVEC_REAL( TYPE ) { \ + TYPE *p = (TYPE *) in[0]; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < a->Bands; b++, i++ ) \ + if( p[i] != c[b] ) \ + out[i] = 255; \ + else \ + out[i] = 0; \ +} + +#define NOTEQUALVEC_COMPLEX( TYPE ) assert( 0 ); + + SWITCH( a->BandFmt, NOTEQUALVEC_REAL, NOTEQUALVEC_COMPLEX ); +} + +int +im_notequal_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + double *p; + + if( n != in->Bands ) { + im_errormsg( "im_notequal_vec: vec size does not match bands" ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_errormsg( "im_notequal_vec: not implemented for complex" ); + return( -1 ); + } + + invec[0] = in; invec[1] = NULL; + if( !(p = numdup( out, n, c )) || + relational_process( "im_notequal_vec", invec, out, + (im_wrapmany_fn) notequalvec_buffer, (void *) p ) ) + return( -1 ); + + return( 0 ); +} + +int +im_notequalconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + + return( !(v = mkvec( in, out, c )) || + im_notequal_vec( in, out, in->Bands, v ) ); +} + +static void +less_buffer( PEL **p, PEL *q, int n, IMAGE *a, IMAGE *b ) +{ + int ne = n * a->Bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + int x; + +#define LESS_REAL( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + if( i[x] < j[x] ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ +} + +/* Take the mod and compare that. + */ +#define LESS_COMPLEX( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) { \ + double m1 = sqrt( i[0] * i[0] + i[1] * i[1] ); \ + double m2 = sqrt( j[0] * j[0] + j[1] * j[1] ); \ + \ + if( m1 < m2 ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ + \ + i += 2; \ + j += 2; \ + } \ +} + + SWITCH( a->BandFmt, LESS_REAL, LESS_COMPLEX ); +} + +int +im_less( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( relational_process( "im_less", invec, out, + (im_wrapmany_fn) less_buffer, NULL ) ) + return( -1 ); + + return( 0 ); +} + +static void +lessvec_buffer( PEL **in, PEL *out, int n, IMAGE *a, double *c ) +{ + int x, b, i; + +#define LESSVEC_REAL( TYPE ) { \ + TYPE *p = (TYPE *) in[0]; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < a->Bands; b++, i++ ) \ + if( p[i] < c[b] ) \ + out[i] = 255; \ + else \ + out[i] = 0; \ +} + +#define LESSVEC_COMPLEX( TYPE ) assert( 0 ); + + SWITCH( a->BandFmt, LESSVEC_REAL, LESSVEC_COMPLEX ); +} + +int +im_less_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + double *p; + + if( n != in->Bands ) { + im_errormsg( "im_less_vec: vec size does not match bands" ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_errormsg( "im_less_vec: not implemented for complex" ); + return( -1 ); + } + + invec[0] = in; invec[1] = NULL; + if( !(p = numdup( out, n, c )) || + relational_process( "im_less_vec", invec, out, + (im_wrapmany_fn) lessvec_buffer, (void *) p ) ) + return( -1 ); + + return( 0 ); +} + +int +im_lessconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + + return( !(v = mkvec( in, out, c )) || + im_less_vec( in, out, in->Bands, v ) ); +} + +static void +lesseq_buffer( PEL **p, PEL *q, int n, IMAGE *a, IMAGE *b ) +{ + int ne = n * a->Bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + int x; + +#define LESSEQ_REAL( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + if( i[x] <= j[x] ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ +} + +/* Take the mod and compare that. + */ +#define LESSEQ_COMPLEX( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) { \ + double m1 = sqrt( i[0] * i[0] + i[1] * i[1] ); \ + double m2 = sqrt( j[0] * j[0] + j[1] * j[1] ); \ + \ + if( m1 <= m2 ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ + \ + i += 2; \ + j += 2; \ + } \ +} + + SWITCH( a->BandFmt, LESSEQ_REAL, LESSEQ_COMPLEX ); +} + +int +im_lesseq( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( relational_process( "im_lesseq", invec, out, + (im_wrapmany_fn) lesseq_buffer, NULL ) ) + return( -1 ); + + return( 0 ); +} + +static void +lesseqvec_buffer( PEL **in, PEL *out, int n, IMAGE *a, double *c ) +{ + int x, b, i; + +#define LESSEQVEC_REAL( TYPE ) { \ + TYPE *p = (TYPE *) in[0]; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < a->Bands; b++, i++ ) \ + if( p[i] <= c[b] ) \ + out[i] = 255; \ + else \ + out[i] = 0; \ +} + +#define LESSEQVEC_COMPLEX( TYPE ) assert( 0 ); + + SWITCH( a->BandFmt, LESSEQVEC_REAL, LESSEQVEC_COMPLEX ); +} + +int +im_lesseq_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + double *p; + + if( n != in->Bands ) { + im_errormsg( "im_lesseq_vec: vec size does not match bands" ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_errormsg( "im_lesseq_vec: not implemented for complex" ); + return( -1 ); + } + + invec[0] = in; invec[1] = NULL; + if( !(p = numdup( out, n, c )) || + relational_process( "im_lesseq_vec", invec, out, + (im_wrapmany_fn) lesseqvec_buffer, (void *) p ) ) + return( -1 ); + + return( 0 ); +} + +int +im_lesseqconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + + return( !(v = mkvec( in, out, c )) || + im_lesseq_vec( in, out, in->Bands, v ) ); +} + +int +im_more( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + return( im_less( in2, in1, out ) ); +} + +int +im_more_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *t; + + /* Same as not (lesseq x). + */ + if( !(t = im_open_local( out, "im_more_vec-1", "p" )) || + im_lesseq_vec( in, t, n, c ) || + im_eorconst( t, out, 255 ) ) + return( -1 ); + + return( 0 ); +} + +int +im_moreconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + + return( !(v = mkvec( in, out, c )) || + im_more_vec( in, out, in->Bands, v ) ); +} + +int +im_moreeq( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + return( im_lesseq( in2, in1, out ) ); +} + +int +im_moreeq_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *t; + + /* Same as not (less x). + */ + if( !(t = im_open_local( out, "im_moreeq_vec-1", "p" )) || + im_less_vec( in, t, n, c ) || + im_eorconst( t, out, 255 ) ) + return( -1 ); + + return( 0 ); +} + +int +im_moreeqconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + + return( !(v = mkvec( in, out, c )) || + im_moreeq_vec( in, out, in->Bands, v ) ); +} diff --git a/libsrc/relational/relational_dispatch.c b/libsrc/relational/relational_dispatch.c new file mode 100644 index 00000000..b57c5b86 --- /dev/null +++ b/libsrc/relational/relational_dispatch.c @@ -0,0 +1,513 @@ +/* VIPS function dispatch tables for relational. + * + * J. Cupitt, 23/2/95 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Two images in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* One image plus one constant in, one image out. + */ +static im_arg_desc const_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "c" ) +}; + +/* One image plus doublevec in, one image out. + */ +static im_arg_desc vec_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLEVEC( "vec" ) +}; + +/* Call im_equal via arg vector. + */ +static int +equal_vec( im_object *argv ) +{ + return( im_equal( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_equal. + */ +static im_function equal_desc = { + "im_equal", /* Name */ + "two images equal in value", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + equal_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_equalconst via arg vector. + */ +static int +equalconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_equalconst( argv[0], argv[1], c ) ); +} + +/* Description of im_equalconst. + */ +static im_function equalconst_desc = { + "im_equalconst", /* Name */ + "image equals const", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + equalconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_equal_vec via arg vector. + */ +static int +equal_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_equal_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_equal_vec. + */ +static im_function equal_vec_desc = { + "im_equal_vec", /* Name */ + "image equals doublevec", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + equal_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_notequal via arg vector. + */ +static int +notequal_vec( im_object *argv ) +{ + return( im_notequal( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_notequal. + */ +static im_function notequal_desc = { + "im_notequal", /* Name */ + "two images not equal in value",/* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + notequal_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_notequalconst via arg vector. + */ +static int +notequalconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_notequalconst( argv[0], argv[1], c ) ); +} + +/* Description of im_notequalconst. + */ +static im_function notequalconst_desc = { + "im_notequalconst", /* Name */ + "image does not equal const", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + notequalconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_notequal_vec via arg vector. + */ +static int +notequal_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_notequal_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_notequal_vec. + */ +static im_function notequal_vec_desc = { + "im_notequal_vec", /* Name */ + "image does not equal doublevec", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + notequal_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_less via arg vector. + */ +static int +less_vec( im_object *argv ) +{ + return( im_less( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_less. + */ +static im_function less_desc = { + "im_less", /* Name */ + "in1 less than in2 in value", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + less_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_lessconst via arg vector. + */ +static int +lessconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_lessconst( argv[0], argv[1], c ) ); +} + +/* Description of im_lessconst. + */ +static im_function lessconst_desc = { + "im_lessconst", /* Name */ + "in less than const", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + lessconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_less_vec via arg vector. + */ +static int +less_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_less_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_less_vec. + */ +static im_function less_vec_desc = { + "im_less_vec", /* Name */ + "in less than doublevec", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + less_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_more via arg vector. + */ +static int +more_vec( im_object *argv ) +{ + return( im_more( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_more. + */ +static im_function more_desc = { + "im_more", /* Name */ + "in1 more than in2 in value", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + more_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_moreconst via arg vector. + */ +static int +moreconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_moreconst( argv[0], argv[1], c ) ); +} + +/* Description of im_moreconst. + */ +static im_function moreconst_desc = { + "im_moreconst", /* Name */ + "in more than const", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + moreconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_more_vec via arg vector. + */ +static int +more_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_more_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_more_vec. + */ +static im_function more_vec_desc = { + "im_more_vec", /* Name */ + "in more than doublevec", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + more_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_moreeq via arg vector. + */ +static int +moreeq_vec( im_object *argv ) +{ + return( im_moreeq( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_moreeq. + */ +static im_function moreeq_desc = { + "im_moreeq", /* Name */ + "in1 more than or equal to in2 in value", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + moreeq_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_moreeqconst via arg vector. + */ +static int +moreeqconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_moreeqconst( argv[0], argv[1], c ) ); +} + +/* Description of im_moreeqconst. + */ +static im_function moreeqconst_desc = { + "im_moreeqconst", /* Name */ + "in more than or equal to const", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + moreeqconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_moreeq_vec via arg vector. + */ +static int +moreeq_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_moreeq_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_moreeq_vec. + */ +static im_function moreeq_vec_desc = { + "im_moreeq_vec", /* Name */ + "in more than or equal to doublevec", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + moreeq_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_lesseq via arg vector. + */ +static int +lesseq_vec( im_object *argv ) +{ + return( im_lesseq( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_lesseq. + */ +static im_function lesseq_desc = { + "im_lesseq", /* Name */ + "in1 less than or equal to in2 in value", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + lesseq_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_lesseqconst via arg vector. + */ +static int +lesseqconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_lesseqconst( argv[0], argv[1], c ) ); +} + +/* Description of im_lesseqconst. + */ +static im_function lesseqconst_desc = { + "im_lesseqconst", /* Name */ + "in less than or equal to const", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + lesseqconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_lesseq_vec via arg vector. + */ +static int +lesseq_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_lesseq_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_lesseq_vec. + */ +static im_function lesseq_vec_desc = { + "im_lesseq_vec", /* Name */ + "in less than or equal to doublevec", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + lesseq_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* If-then-else args. + */ +static im_arg_desc ifthenelse_args[] = { + IM_INPUT_IMAGE( "cond" ), + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_blend via arg vector. + */ +static int +blend_vec( im_object *argv ) +{ + return( im_blend( argv[0], argv[1], argv[2], argv[3] ) ); +} + +/* Description of im_blend. + */ +static im_function blend_desc = { + "im_blend", /* Name */ + "use cond image to blend between images in1 and in2", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + blend_vec, /* Dispatch function */ + IM_NUMBER( ifthenelse_args ), /* Size of arg list */ + ifthenelse_args /* Arg list */ +}; + +/* Call im_ifthenelse via arg vector. + */ +static int +ifthenelse_vec( im_object *argv ) +{ + return( im_ifthenelse( argv[0], argv[1], argv[2], argv[3] ) ); +} + +/* Description of im_ifthenelse. + */ +static im_function ifthenelse_desc = { + "im_ifthenelse", /* Name */ + "use cond image to choose pels from image in1 or in2", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + ifthenelse_vec, /* Dispatch function */ + IM_NUMBER( ifthenelse_args ), /* Size of arg list */ + ifthenelse_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *relational_list[] = { + &blend_desc, + &equal_desc, + &equal_vec_desc, + &equalconst_desc, + &ifthenelse_desc, + &less_desc, + &less_vec_desc, + &lessconst_desc, + &lesseq_desc, + &lesseq_vec_desc, + &lesseqconst_desc, + &more_desc, + &more_vec_desc, + &moreconst_desc, + &moreeq_desc, + &moreeq_vec_desc, + &moreeqconst_desc, + ¬equal_desc, + ¬equal_vec_desc, + ¬equalconst_desc +}; + +/* Package of functions. + */ +im_package im__relational = { + "relational", + IM_NUMBER( relational_list ), + relational_list +}; diff --git a/libsrc/video/Makefile.am b/libsrc/video/Makefile.am new file mode 100644 index 00000000..83f443a1 --- /dev/null +++ b/libsrc/video/Makefile.am @@ -0,0 +1,10 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libvideo.la + +libvideo_la_SOURCES = \ + video_dispatch.c \ + im_video_v4l1.c \ + im_video_test.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/video/im_video_test.c b/libsrc/video/im_video_test.c new file mode 100644 index 00000000..b2144f2a --- /dev/null +++ b/libsrc/video/im_video_test.c @@ -0,0 +1,51 @@ +/* test video grabber ... just generates noise and optional errors + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_video_test( IMAGE *im, int brightness, int error ) +{ + if( error ) { + im_error( "im_video_test", _( "error requested" ) ); + return( -1 ); + } + else + return( im_gaussnoise( im, 720, 576, brightness, 20 ) ); +} + diff --git a/libsrc/video/im_video_v4l1.c b/libsrc/video/im_video_v4l1.c new file mode 100644 index 00000000..4f846fee --- /dev/null +++ b/libsrc/video/im_video_v4l1.c @@ -0,0 +1,677 @@ +/* video grab for linux ... uses the original v4l + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifdef HAVE_VIDEODEV + +/* Lots of debugging output. +#define DEBUG + */ + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Zero freed mem to help catch stray pointers. + */ +#ifdef NDEBUG +#define FREE( S ) { if( S ) { (void) im_free( (char *) (S) ); \ + (char *) (S) = NULL; } } +#else +#define FREE( S ) { if( S ) { memset( (char *)(S), 0, sizeof( *(S) ) ); \ + (void) im_free( (char *) (S) ); (S) = NULL; } } +#endif /*NDEBUG*/ + +#define FREEF( F, S ) { if( S ) { (void) F( S ); (S) = NULL; } } +#define FREEFI( F, S ) { if( S ) { (void) F( S ); (S) = 0; } } +#define SETSTR( S, V ) \ + { const char *sst = (V); FREE( S ); (S) = im_strdup( NULL, sst ); } + +/* Max channels on a device. + */ +#define IM_MAXCHANNELS (10) + +/* Video input sources, e.g. tuner, svideo, composite. + */ +#define TUNER (0) +#define COMPOSITE (1) +#define SVIDEO (2) + +typedef struct lgrab { + /* Mmap here, plus file descriptor. + */ + char *device; + char *capture_buffer; + int capture_size; + int fd; + + /* Current settings. + */ + int c_channel; + int c_width; + int c_height; + int c_ngrabs; + + /* Extract capabilities here. + */ + struct video_capability capability; + struct video_channel channel[IM_MAXCHANNELS]; + struct video_window window; + struct video_picture picture; + struct video_mbuf mbuf; + struct video_mmap mmap; +} LGrab; + +#ifdef DEBUG +/* Decode various things ... capability bits etc. + */ +typedef struct { + int value; + const char *name; + const char *description; +} Decode; + +static const Decode decode_palette[] = { + { VIDEO_PALETTE_GREY, + "VIDEO_PALETTE_GREY", "Linear greyscale" }, + { VIDEO_PALETTE_HI240, + "VIDEO_PALETTE_HI240", "High 240 cube (BT848)" }, + { VIDEO_PALETTE_RGB565, + "VIDEO_PALETTE_RGB565", "565 16 bit RGB" }, + { VIDEO_PALETTE_RGB24, + "VIDEO_PALETTE_RGB24", "24bit RGB" }, + { VIDEO_PALETTE_RGB32, + "VIDEO_PALETTE_RGB32", "32bit RGB" }, + { VIDEO_PALETTE_RGB555, + "VIDEO_PALETTE_RGB555", "555 15bit RGB" }, + { VIDEO_PALETTE_YUV422, + "VIDEO_PALETTE_YUV422", "YUV422 capture" }, + { VIDEO_PALETTE_YUYV, + "VIDEO_PALETTE_YUYV", "" }, + { VIDEO_PALETTE_UYVY, + "VIDEO_PALETTE_UYVY", "" }, + { VIDEO_PALETTE_YUV420, + "VIDEO_PALETTE_YUV420", "" }, + { VIDEO_PALETTE_YUV411, + "VIDEO_PALETTE_YUV411", "YUV411 capture" }, + { VIDEO_PALETTE_RAW, + "VIDEO_PALETTE_RAW", "RAW capture (BT848)" }, + { VIDEO_PALETTE_YUV422P, + "VIDEO_PALETTE_YUV422P", "YUV 4:2:2 Planar" }, + { VIDEO_PALETTE_YUV411P, + "VIDEO_PALETTE_YUV411P", "YUV 4:1:1 Planar" }, + { VIDEO_PALETTE_YUV420P, + "VIDEO_PALETTE_YUV420P", "YUV 4:2:0 Planar" }, + { VIDEO_PALETTE_YUV410P, + "VIDEO_PALETTE_YUV410P", "YUV 4:1:0 Planar" } +}; + +static const Decode decode_type[] = { + { VIDEO_TYPE_TV, + "VIDEO_TYPE_TV", "TV" }, + { VIDEO_TYPE_CAMERA, + "VIDEO_TYPE_CAMERA", "Camera" }, +}; + +static const Decode decode_vtype[] = { + { VID_TYPE_CAPTURE, + "VID_TYPE_CAPTURE", "Can capture to memory" }, + { VID_TYPE_TUNER, + "VID_TYPE_TUNER", "Has Tuner" }, + { VID_TYPE_TELETEXT, + "VID_TYPE_TELETEXT", "Has Teletext" }, + { VID_TYPE_OVERLAY, + "VID_TYPE_OVERLAY", "Chromakeyed overlay" }, + { VID_TYPE_CLIPPING, + "VID_TYPE_CLIPPING", "Overlay clipping" }, + { VID_TYPE_FRAMERAM, + "VID_TYPE_FRAMERAM", "Overlay overwrites frame buffer memory" }, + { VID_TYPE_SCALES, + "VID_TYPE_SCALES", "Hardware supports image scaling" }, + { VID_TYPE_MONOCHROME, + "VID_TYPE_MONOCHROME", "Image capture is grey scale only" }, + { VID_TYPE_SUBCAPTURE, + "VID_TYPE_SUBCAPTURE", "Capture sub-image" }, + { VID_TYPE_MPEG_DECODER, + "VID_TYPE_MPEG_DECODER", "Can decode MPEG streams" }, + { VID_TYPE_MPEG_ENCODER, + "VID_TYPE_MPEG_ENCODER", "Can encode MPEG streams" }, + { VID_TYPE_MJPEG_DECODER, + "VID_TYPE_MJPEG_DECODER", "Can decode MJPEG streams" }, + { VID_TYPE_MJPEG_ENCODER, + "VID_TYPE_MJPEG_ENCODER", "Can encode MJPEG streams" } +}; + +static const Decode decode_ctype[] = { + { VIDEO_VC_TUNER, + "VIDEO_VC_TUNER", "Has tuner" }, + { VIDEO_VC_AUDIO, + "VIDEO_VC_AUDIO", "Has audio" } +}; + +/* Prettyprint a value. + */ +static void +decode_print( const Decode *decode, int ndecode, int value ) +{ + int i; + + for( i = 0; i < ndecode; i++ ) + if( decode[i].value == value ) { + printf( "%s, %s", + decode[i].name, decode[i].description ); + return; + } + + printf( "unknown (%p)", value ); +} + +/* Prettyprint a set of flags. + */ +static void +decode_print_flags( const Decode *decode, int ndecode, int flags ) +{ + int i; + + printf( "0x%x ", (unsigned int) flags ); + + for( i = 0; i < ndecode; i++ ) + if( decode[i].value & flags ) { + printf( "[" ); + decode_print( decode, ndecode, + decode[i].value & flags ); + printf( "] " ); + flags &= -1 ^ decode[i].value; + } + + if( flags ) + printf( "[unknown extra flags 0x%x]", (unsigned int) flags ); +} +#endif /*DEBUG*/ + +static int +lgrab_ioctl( LGrab *lg, int request, void *argp ) +{ + if( !lg->fd ) { + im_error( "lgrab_ioctl", _( "no file descriptor" ) ); + return( -1 ); + } + + if( ioctl( lg->fd, request, argp ) < 0 ) { + im_error( "lgrab_ioctl", _( "ioctl(0x%x) failed: %s" ), + (unsigned int) request, strerror( errno ) ); + return( -1 ); + } + + return( 0 ); +} + +static void +lgrab_destroy( LGrab *lg ) +{ + if( lg->fd != -1 ) { + int zero = 0; + + (void) lgrab_ioctl( lg, VIDIOCCAPTURE, &zero ); + close( lg->fd ); + lg->fd = -1; + } + + if( lg->capture_buffer ) { + munmap( lg->capture_buffer, lg->capture_size ); + lg->capture_buffer = NULL; + } + + FREE( lg->device ); + FREE( lg ); +} + +static LGrab * +lgrab_new( const char *device ) +{ + LGrab *lg = IM_NEW( NULL, LGrab ); + int i; + + if( !lg ) + return( NULL ); + + lg->device = NULL; + lg->capture_buffer = NULL; + lg->capture_size = 0; + lg->fd = -1; + + lg->c_channel = -1; + lg->c_width = -1; + lg->c_height = -1; + lg->c_ngrabs = 1; + + SETSTR( lg->device, device ); + if( !lg->device || (lg->fd = open( lg->device, O_RDWR )) == -1 ) { + im_error( "lgrab_new", _( "cannot open video device \"%s\"" ), + lg->device ); + lgrab_destroy( lg ); + return( NULL ); + } + + if( lgrab_ioctl( lg, VIDIOCGCAP, &lg->capability ) ) { + im_error( "lgrab_new", _( "cannot get video capability" ) ); + lgrab_destroy( lg ); + return( NULL ); + } + + /* Check that it can capture to memory. + */ + if( !(lg->capability.type & VID_TYPE_CAPTURE) ) { + im_error( "lgrab_new", _( "card cannot capture to memory" ) ); + lgrab_destroy( lg ); + return( NULL ); + } + + /* Read channel info. + */ + for( i = 0; i < IM_MIN( lg->capability.channels, IM_MAXCHANNELS ); + i++ ) { + lg->channel[i].channel = i; + if( lgrab_ioctl( lg, VIDIOCGCHAN, &lg->channel[i] ) ) { + lgrab_destroy( lg ); + return( NULL ); + } + } + + /* Get other props. + */ + if( lgrab_ioctl( lg, VIDIOCGWIN, &lg->window) || + lgrab_ioctl( lg, VIDIOCGPICT, &lg->picture) ) { + lgrab_destroy( lg ); + return( NULL ); + } + + /* Set 24 bit mode. + */ + lg->picture.depth = 24; + lg->picture.palette = VIDEO_PALETTE_RGB24; + if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) ) { + lgrab_destroy( lg ); + return( NULL ); + } + + return( lg ); +} + +#ifdef DEBUG +static void +lgrab_dump_capability( struct video_capability *capability ) +{ + printf( "capability->name = \"%s\"\n", capability->name ); + printf( "capability->channels = %d\n", capability->channels ); + printf( "capability->audios = %d\n", capability->audios ); + printf( "capability->maxwidth = %d\n", capability->maxwidth ); + printf( "capability->maxheight = %d\n", capability->maxheight ); + printf( "capability->minwidth = %d\n", capability->maxwidth ); + printf( "capability->minheight = %d\n", capability->maxheight ); + printf( "capability->type = " ); + decode_print_flags( decode_vtype, IM_NUMBER( decode_vtype ), + capability->type ); + printf( "\n" ); +} + +static void +lgrab_dump_channel( struct video_channel *channel ) +{ + printf( "channel->channel = %d\n", channel->channel ); + printf( "channel->name = \"%s\"\n", channel->name ); + printf( "channel->tuners = %d\n", channel->tuners ); + + printf( "channel->flags = " ); + decode_print_flags( decode_ctype, IM_NUMBER( decode_ctype ), + channel->flags ); + printf( "\n" ); + printf( "channel->type = " ); + decode_print( decode_type, IM_NUMBER( decode_type ), + channel->type ); + printf( "\n" ); + printf( "channel->norm = %d\n", channel->norm ); +} + +static void +lgrab_dump_picture( struct video_picture *picture ) +{ + printf( "picture->brightness = %d\n", picture->brightness ); + printf( "picture->hue = %d\n", picture->hue ); + printf( "picture->colour = %d\n", picture->colour ); + printf( "picture->contrast = %d\n", picture->contrast ); + printf( "picture->whiteness = %d\n", picture->whiteness ); + printf( "picture->depth = %d\n", picture->depth ); + printf( "picture->palette = " ); + decode_print( decode_palette, IM_NUMBER( decode_palette ), + picture->palette ); + printf( "\n" ); +} + +static void +lgrab_dump( LGrab *lg ) +{ + int i; + + printf( "lg->device = \"%s\"\n", lg->device ); + printf( "lg->capture_buffer = %p\n", + lg->capture_buffer ); + printf( "lg->capture_size = 0x%x\n", + (unsigned int) lg->capture_size ); + printf( "lg->fd = %d\n", lg->fd ); + + printf( "lg->c_channel = %d\n", lg->c_channel ); + printf( "lg->c_width = %d\n", lg->c_width ); + printf( "lg->c_height = %d\n", lg->c_height ); + printf( "lg->c_ngrabs = %d\n", lg->c_ngrabs ); + + lgrab_dump_capability( &lg->capability ); + for( i = 0; i < lg->capability.channels; i++ ) + lgrab_dump_channel( &lg->channel[i] ); + lgrab_dump_picture( &lg->picture ); + + printf( "mbuf->size = 0x%x\n", (unsigned int) lg->mbuf.size ); + printf( "mbuf->frames = %d\n", lg->mbuf.frames ); + printf( "mbuf->offsets = " ); + for( i = 0; i < lg->mbuf.frames; i++ ) + printf( "0x%x ", (unsigned int) lg->mbuf.offsets[i] ); + printf( "\n" ); +} +#endif /*DEBUG*/ + +static int +lgrab_set_capture_size( LGrab *lg, int width, int height ) +{ + lg->c_width = width; + lg->c_height = height; + + lg->window.clipcount = 0; + lg->window.flags = 0; + lg->window.x = 0; + lg->window.y = 0; + lg->window.width = width; + lg->window.height = height; + if( lgrab_ioctl( lg, VIDIOCSWIN, &lg->window ) ) + return( -1 ); + + /* Make sure the correct amount of memory is mapped. + */ + if( lgrab_ioctl( lg, VIDIOCGMBUF, &lg->mbuf ) ) + return( -1 ); + + if( lg->capture_buffer ) { + munmap( lg->capture_buffer, lg->capture_size ); + lg->capture_buffer = NULL; + } + + lg->capture_size = lg->mbuf.size; + if( !(lg->capture_buffer = mmap( 0, lg->capture_size, + PROT_READ | PROT_WRITE, MAP_SHARED, lg->fd, 0 )) ) { + im_error( "lgrab_set_capture_size", + _( "unable to map memory" ) ); + return( -1 ); + } + + return( 0 ); +} + +static int +lgrab_set_channel( LGrab *lg, int channel ) +{ + if( channel < 0 || channel >= lg->capability.channels ) { + im_error( "lgrab_set_channel", + _( "channel not between 0 and %d" ), + lg->capability.channels - 1 ); + return( -1 ); + } + + if( lgrab_ioctl( lg, VIDIOCSCHAN, &lg->channel[channel] ) ) + return( -1 ); + lg->c_channel = channel; + + return( 0 ); +} + +static int +lgrab_set_brightness( LGrab *lg, int brightness ) +{ + lg->picture.brightness = IM_CLIP( 0, brightness, 65535 ); + if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) ) + return( -1 ); + + return( 0 ); +} + +static int +lgrab_set_colour( LGrab *lg, int colour ) +{ + lg->picture.colour = IM_CLIP( 0, colour, 65535 ); + if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) ) + return( -1 ); + + return( 0 ); +} + +static int +lgrab_set_contrast( LGrab *lg, int contrast ) +{ + lg->picture.contrast = IM_CLIP( 0, contrast, 65535 ); + if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) ) + return( -1 ); + + return( 0 ); +} + +static int +lgrab_set_hue( LGrab *lg, int hue ) +{ + lg->picture.hue = IM_CLIP( 0, hue, 65535 ); + if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) ) + return( -1 ); + + return( 0 ); +} + +static int +lgrab_set_ngrabs( LGrab *lg, int ngrabs ) +{ + lg->c_ngrabs = IM_CLIP( 1, ngrabs, 1000 ); + + return( 0 ); +} + +/* Grab a single frame. + */ +static int +lgrab_capture1( LGrab *lg ) +{ + lg->mmap.format = lg->picture.palette; + lg->mmap.frame = 0; + lg->mmap.width = lg->c_width; + lg->mmap.height = lg->c_height; + if( lgrab_ioctl( lg, VIDIOCMCAPTURE, &lg->mmap ) || + lgrab_ioctl( lg, VIDIOCSYNC, &lg->mmap.frame ) ) + return( -1 ); + + return( 0 ); +} + +/* Grab and average many frames. + */ +static int +lgrab_capturen( LGrab *lg ) +{ + if( lg->c_ngrabs == 1 ) { + if( lgrab_capture1( lg ) ) + return( -1 ); + } + else { + int i, j; + int npx = lg->c_width * lg->c_height * 3; + unsigned int *acc; + + if( !(acc = IM_ARRAY( NULL, npx, unsigned int )) ) + return( -1 ); + memset( acc, 0, npx * sizeof( unsigned int ) ); + + for( i = 0; i < lg->c_ngrabs; i++ ) { + if( lgrab_capture1( lg ) ) { + FREE( acc ); + return( -1 ); + } + + for( j = 0; j < npx; j++ ) + acc[j] += (unsigned char) lg->capture_buffer[j]; + } + + for( j = 0; j < npx; j++ ) { + int avg = (acc[j] + lg->c_ngrabs / 2) / lg->c_ngrabs; + + lg->capture_buffer[j] = IM_CLIP( 0, avg, 255 ); + } + + FREE( acc ); + } + + return( 0 ); +} + +static int +lgrab_capture( LGrab *lg, IMAGE *im ) +{ + int x, y; + unsigned char *line; + + if( lgrab_capturen( lg ) ) + return( -1 ); + + if( im_outcheck( im ) ) + return( -1 ); + im_initdesc( im, lg->c_width, lg->c_height, 3, + IM_BBITS_BYTE, IM_BANDFMT_UCHAR, + IM_CODING_NONE, IM_TYPE_MULTIBAND, 1.0, 1.0, 0, 0 ); + if( im_setupout( im ) ) + return( -1 ); + if( !(line = IM_ARRAY( im, + IM_IMAGE_SIZEOF_LINE( im ), unsigned char )) ) + return( -1 ); + + for( y = 0; y < lg->c_height; y++ ) { + unsigned char *p = (unsigned char *) lg->capture_buffer + + y * IM_IMAGE_SIZEOF_LINE( im ); + unsigned char *q = line; + + for( x = 0; x < lg->c_width; x++ ) { + q[0] = p[2]; + q[1] = p[1]; + q[2] = p[0]; + + p += 3; + q += 3; + } + + if( im_writeline( y, im, line ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_video_v4l1( IMAGE *im, const char *device, + int channel, int brightness, int colour, int contrast, int hue, + int ngrabs ) +{ + LGrab *lg; + + if( !(lg = lgrab_new( device )) ) + return( -1 ); + + if( lgrab_set_capture_size( lg, + lg->capability.maxwidth, lg->capability.maxheight ) || + lgrab_set_channel( lg, channel ) || + lgrab_set_brightness( lg, brightness ) || + lgrab_set_colour( lg, colour ) || + lgrab_set_contrast( lg, contrast ) || + lgrab_set_hue( lg, hue ) || + lgrab_set_ngrabs( lg, ngrabs ) || + lgrab_capture( lg, im ) ) { + lgrab_destroy( lg ); + return( -1 ); + } + +#ifdef DEBUG + printf( "Successful capture with:\n" ); + lgrab_dump( lg ); +#endif /*DEBUG*/ + + lgrab_destroy( lg ); + + return( 0 ); +} + +#else /*!HAVE_VIDEODEV*/ + +#include + +int +im_video_v4l1( IMAGE *im, const char *device, + int channel, int brightness, int colour, int contrast, int hue, + int ngrabs ) +{ + im_error( "im_video_v4l1", + _( "compiled without im_video_v4l1 support" ) ); + return( -1 ); +} + +#endif /*HAVE_VIDEODEV*/ diff --git a/libsrc/video/im_video_v4l1.h b/libsrc/video/im_video_v4l1.h new file mode 100644 index 00000000..57ece650 --- /dev/null +++ b/libsrc/video/im_video_v4l1.h @@ -0,0 +1,76 @@ +/* single frame video capture on linux + */ + +/* + + Copyright (C) 1991-2003 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Video input sources, e.g. tuner, svideo, composite. + */ +#define IM_MAXCHANNELS (10) +#define TUNER (0) +#define COMPOSITE (1) +#define SVIDEO (2) + +typedef struct lgrab { + /* Mmap here, plus file descriptor. + */ + char *device; + char *capture_buffer; + int capture_size; + int fd; + + /* Current settings. + */ + int c_channel; + int c_width; + int c_height; + int c_ngrabs; + + /* Extract capabilities here. + */ + struct video_capability capability; + struct video_channel channel[IM_MAXCHANNELS]; + struct video_window window; + struct video_picture picture; + struct video_mbuf mbuf; + struct video_mmap mmap; +} LGrab; + +void lgrab_destroy( LGrab *lg ); +LGrab *lgrab_new( const char *device ); + +int lgrab_set_capture_size( LGrab *lg, int width, int height ); +int lgrab_set_channel( LGrab *lg, int channel ); +int lgrab_set_brightness( LGrab *lg, int brightness ); +int lgrab_set_colour( LGrab *lg, int colour ); +int lgrab_set_contrast( LGrab *lg, int contrast ); +int lgrab_set_hue( LGrab *lg, int hue ); +int lgrab_set_ngrabs( LGrab *lg, int ngrabs ); +int lgrab_capture( LGrab *lg, IMAGE *im ); +int lgrab_grab( IMAGE *im, const char *device, + int channel, int brightness, int colour, int contrast, int hue, + int ngrabs ); + diff --git a/libsrc/video/man3/Makefile.am b/libsrc/video/man3/Makefile.am new file mode 100644 index 00000000..e9c5172e --- /dev/null +++ b/libsrc/video/man3/Makefile.am @@ -0,0 +1,5 @@ +man_MANS = \ + im_video_v4l1.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/video/man3/im_video_v4l1.3 b/libsrc/video/man3/im_video_v4l1.3 new file mode 100644 index 00000000..c3e71a16 --- /dev/null +++ b/libsrc/video/man3/im_video_v4l1.3 @@ -0,0 +1,39 @@ +.TH VIDEO 3 "3 March 2001" +.SH NAME +im_video_v4l1 \- various image grabbers + +.SH SYNOPSIS +.B #include + +.B int +.B im_video_v4l1( IMAGE *im, const char *device, int channel, +.B int brightness, int colour, int contrast, int hue, +.B int ngrabs ) + +.SH DESCRIPTION +These functions grab single video frames from various devices. Which of these +functions work depends upon how your VIPS has been configured and compiled, +and your platform. As a result, they are far from portable ... you want a +layer on top of these functions. + +.B im_video_v4l1(3) +grabs a frame using Video4Linux. It grabs a 24-bit RGB colour image, at the +maximum resolution your card allows. + +.B device +should typically be "/dev/video". +.B channel +selects the channel to acquire: usually 0 is TV, and 1 is composite video. +.B brightness, +.B colour, +.B contrast +and +.B hue +set grab parameters. Each should be in the range (0 - 32768). 32768 is usually +the value you want. +.B ngrabs +sets the number of frames the card should average. Higher values are slower, +but typically less noisy (and slightly softer). + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. diff --git a/libsrc/video/video_dispatch.c b/libsrc/video/video_dispatch.c new file mode 100644 index 00000000..6a9c959e --- /dev/null +++ b/libsrc/video/video_dispatch.c @@ -0,0 +1,115 @@ +/* function dispatch tables for video + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +video_v4l1_vec( im_object *argv ) +{ + IMAGE *out = argv[0]; + char *device = (char *) argv[1]; + int channel = *((int*)argv[2]); + int brightness = *((int*)argv[3]); + int colour = *((int*)argv[4]); + int contrast = *((int*)argv[5]); + int hue = *((int*)argv[6]); + int ngrabs = *((int*)argv[7]); + + return( im_video_v4l1( out, device, + channel, brightness, colour, contrast, hue, ngrabs ) ); +} + +static im_arg_desc video_v4l1_arg_types[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "device" ), + IM_INPUT_INT( "channel" ), + IM_INPUT_INT( "brightness" ), + IM_INPUT_INT( "colour" ), + IM_INPUT_INT( "contrast" ), + IM_INPUT_INT( "hue" ), + IM_INPUT_INT( "ngrabs" ) +}; + +static im_function video_v4l1_desc = { + "im_video_v4l1", /* Name */ + "grab a video frame with v4l1", /* Description */ + IM_FN_NOCACHE, /* Flags */ + video_v4l1_vec, /* Dispatch function */ + IM_NUMBER( video_v4l1_arg_types ), /* Size of arg list */ + video_v4l1_arg_types /* Arg list */ +}; + +static int +video_test_vec( im_object *argv ) +{ + IMAGE *out = argv[0]; + int brightness = *((int*)argv[1]); + int error = *((int*)argv[2]); + + return( im_video_test( out, brightness, error ) ); +} + +static im_arg_desc video_test_arg_types[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "brightness" ), + IM_INPUT_INT( "error" ) +}; + +static im_function video_test_desc = { + "im_video_test", /* Name */ + "test video grabber", /* Description */ + IM_FN_NOCACHE, /* Flags */ + video_test_vec, /* Dispatch function */ + IM_NUMBER( video_test_arg_types ), /* Size of arg list */ + video_test_arg_types /* Arg list */ +}; + +static im_function *video_list[] = { + &video_test_desc, + &video_v4l1_desc +}; + +im_package im__video = { + "video", /* Package name */ + IM_NUMBER( video_list ), /* Function list */ + video_list +}; + + diff --git a/libsrc/vips.def b/libsrc/vips.def new file mode 100644 index 00000000..f6c9450a --- /dev/null +++ b/libsrc/vips.def @@ -0,0 +1,535 @@ +EXPORTS + error_exit + im_BandFmt2char + im_Coding2char + im_Compression2char + im_LCh2Lab + im_LCh2UCS + im_Lab2LCh + im_Lab2LabQ + im_Lab2LabS + im_Lab2UCS + im_Lab2XYZ + im_Lab2XYZ_temp + im_Lab2disp + im_LabQ2Lab + im_LabQ2LabS + im_LabQ2XYZ + im_LabQ2disp + im_LabQ2disp_build_table + im_LabQ2disp_table + im_LabS2Lab + im_LabS2LabQ + im_Type2char + im_UCS2LCh + im_UCS2Lab + im_UCS2XYZ + im_XYZ2Lab + im_XYZ2Lab_temp + im_XYZ2UCS + im_XYZ2Yxy + im_XYZ2disp + im_XYZ2sRGB + im_Yxy2XYZ + im_abs + im_acostra + im_add + im_add_close_callback + im_add_eval_callback + im_add_evalend_callback + im_addgnoise + im_affine + im_allocate_input_array + im_allocate_vargv + im_amiMSBfirst + im_and_vec + im_andconst + im_andimage + im_append_Hist + im_asintra + im_atantra + im_avg + im_bandjoin + im_bernd + im_binfile + im_black + im_blend + im_c2amph + im_c2imag + im_c2ps + im_c2real + im_c2rect + im_ceil + im_char2BandFmt + im_char2Coding + im_char2Compression + im_char2Type + im_circle + im_clamp + im_clear_error_string + im_clip + im_clip2c + im_clip2cm + im_clip2d + im_clip2dcm + im_clip2f + im_clip2fmt + im_clip2i + im_clip2s + im_clip2ui + im_clip2us + im_close + im_close_plugins + im_cmulnorm + im_cntlines + im_col_C2Cucs + im_col_Ch2ab + im_col_Ch2hucs + im_col_Chucs2h + im_col_Cucs2C + im_col_L2Lucs + im_col_Lab2XYZ + im_col_Lucs2L + im_col_XYZ2Lab + im_col_XYZ2rgb + im_col_ab2Ch + im_col_ab2h + im_col_dE00 + im_col_dECMC + im_col_display_name + im_col_displays + im_col_make_tables_RGB + im_col_make_tables_UCS + im_col_pythagoras + im_col_rgb2XYZ + im_compass + im_conv + im_conv_raw + im_convf + im_convf_raw + im_convsep + im_convsep_raw + im_convsepf + im_convsepf_raw + im_convsub + im_cooc_asm + im_cooc_contrast + im_cooc_correlation + im_cooc_entropy + im_cooc_matrix + im_copy + im_copy_dmask_matrix + im_copy_imask_matrix + im_copy_matrix_dmask + im_copy_matrix_imask + im_copy_set + im_correl + im_costra + im_cp_Hist + im_cp_desc + im_create_dmask + im_create_dmaskv + im_create_fmask + im_create_imask + im_create_imaskv + im_crwrhd + im_dE00_fromLab + im_dECMC_fromLab + im_dECMC_fromdisp + im_dE_fromLab + im_dE_fromXYZ + im_dE_fromdisp + im_debugim + im_demand_hint + im_demand_hint_array + im_desc_hd + im_deviate + im_dhint2char + im_diagnostics + im_dif_std + im_dilate + im_dilate_raw + im_disp2Lab + im_disp2XYZ + im_disp_ps + im_divide + im_dmat_alloc + im_dtype2char + im_dup_dmask + im_dup_imask + im_dvector + im_embed + im_eor_vec + im_eorconst + im_eorimage + im_equal + im_equal_vec + im_equalconst + im_erode + im_erode_raw + im_errormsg + im_errormsg_system + im_errorstring + im_existsf + im_exp10tra + im_expntra + im_expntra_vec + im_exptra + im_extract + im_extract_area + im_extract_band + im_eye + im_falsecolour + im_fastcor + im_fastcor_raw + im_fastline + im_fastlineuser + im_fav4 + im_feye + im_fgrey + im_find_function + im_find_package + im_fliphor + im_flipver + im_flood + im_flood_blob + im_floor + im_flt_image_freq + im_fmat_alloc + im_fractsurf + im_free + im_free_dmask + im_free_dmat + im_free_dvector + im_free_fmat + im_free_fvector + im_free_imask + im_free_imat + im_free_ivector + im_free_vargv + im_freqflt + im_fvector + im_fwfft + im_fzone + im_gadd + im_gaddim + im_gammacorrect + im_gauss_dmask + im_gauss_imask + im_gaussnoise + im_gbandjoin + im_generate + im_gfadd + im_glds_asm + im_glds_contrast + im_glds_entropy + im_glds_matrix + im_glds_mean + im_global_balance + im_global_balancef + im_gradient + im_gradient_old + im_grey + im_guess_prefix + im_header_double + im_header_int + im_header_string + im_heq + im_hist + im_histcum + im_histeq + im_histgr + im_histlin + im_histnD + im_histnorm + im_histplot + im_histspec + im_histspec_old + im_hsp + im_icc_ac2rc + im_icc_export + im_icc_export_depth + im_icc_import + im_icc_present + im_icc_transform + im_identity + im_identity_ushort + im_ifthenelse + im_image + im_image_sanity + im_imat_alloc + im_incheck + im_init + im_initdesc + im_inithd + im_insert + im_insert_noexpand + im_insertplace + im_invert + im_invertlut + im_invfft + im_invfftr + im_invmat + im_iocheck + im_isMSBfirst + im_iscomplex + im_isfile + im_isfloat + im_isint + im_isjpeg + im_ismagick + im_ismonotonic + im_ispartial + im_ispng + im_ispoweroftwo + im_isppm + im_istiff + im_istiffpyramid + im_istifftiled + im_isuint + im_isscalar + im_isvips + im_iterate + im_ivector + im_jpeg2vips + im_jpeg2vips_header + im_lab_morph + im_less + im_less_vec + im_lessconst + im_lesseq + im_lesseq_vec + im_lesseqconst + im_lhisteq + im_lhisteq_raw + im_lindetect + im_lindetect_old + im_line + im_lintra + im_lintra_vec + im_list_add + im_list_append + im_list_eq + im_list_fix + im_list_fold + im_list_free + im_list_index + im_list_insert + im_list_len + im_list_map + im_list_map_rev + im_list_member + im_list_pos + im_list_remove + im_litecor + im_load_plugin + im_load_plugins + im_local + im_local_array + im_lock + im_lock_destroy + im_lock_init + im_log10tra + im_log_dmask + im_log_imask + im_logtra + im_lrjoin + im_lrmerge + im_lrmerge1 + im_lrmosaic + im_lrmosaic1 + im_magick2vips + im_magick2vips_header + im_makerw + im_malloc + im_map_packages + im_mapfile + im_mapfilerw + im_maplut + im_mask2vips + im_matcat + im_match_linear + im_match_linear_search + im_matinv + im_matmul + im_mattrn + im_max + im_maxpos + im_maxvalue + im_measure + im_min + im_minpos + im_more + im_more_vec + im_moreconst + im_moreeq + im_moreeq_vec + im_moreeqconst + im_mpercent + im_multiply + im_notequal + im_notequal_vec + im_notequalconst + im_offsets45 + im_offsets90 + im_open + im_open_header + im_openin + im_openinrw + im_openout + im_or_vec + im_orconst + im_orimage + im_outcheck + im_package_of_function + im_paintrect + im_partial + im_path_is_absolute + im_pincheck + im_piocheck + im_plotmask + im_plotpoint + im_png2vips + im_png2vips_header + im_poutcheck + im_powtra + im_powtra_vec + im_ppm2vips + im_ppm2vips_header + im_prepare + im_prepare_thread + im_prepare_to + im_print + im_print_dmask + im_print_imask + im_printdesc + im_printhd + im_printlines + im_profile + im_rank + im_rank_raw + im_read_dmask + im_read_imask + im_readhist + im_readpoint + im_recomb + im_rect_dup + im_rect_equalsrect + im_rect_includespoint + im_rect_includesrect + im_rect_intersectrect + im_rect_isempty + im_rect_marginadjust + im_rect_normalise + im_rect_unionrect + im_region_create + im_region_equalsregion + im_region_free + im_region_image + im_region_local + im_region_mmap_window + im_region_position + im_region_region + im_remainder + im_remainderconst + im_remainderconst_vec + im_remapfilerw + im_remosaic + im_resize_linear + im_ri2c + im_rot180 + im_rot270 + im_rot90 + im_rotate_dmask45 + im_rotate_dmask90 + im_rotate_imask45 + im_rotate_imask90 + im_rotquad + im_run_command + im_rwcheck + im_sRGB2XYZ + im_scale + im_scale_dmask + im_scaleps + im_semaphore_destroy + im_semaphore_down + im_semaphore_downn + im_semaphore_init + im_semaphore_up + im_semaphore_upn + im_setbox + im_setbuf + im_setupout + im_sharpen + im_shiftleft + im_shiftright + im_shrink + im_sign + im_simcontr + im_similarity + im_similarity_area + im_sines + im_sintra + im_slice + im_smear + im_smudge + im_snprintf + im_spatres + im_spcor + im_spcor_raw + im_start_many + im_start_one + im_stats + im_stdif + im_stdif_raw + im_stop_many + im_stop_one + im_strdup + im_stretch3 + im_strncpy + im_strrstr + im_subsample + im_subtract + im_system + im_tantra + im_tbjoin + im_tbmerge + im_tbmerge1 + im_tbmosaic + im_tbmosaic1 + im_thread_create + im_thread_join + im_threadgroup_create + im_threadgroup_free + im_thresh + im_tiff2vips + im_tiff2vips_header + im_tone_analyse + im_tone_build + im_tone_map + im_unlock + im_unmapfile + im_updatehist + im_verrormsg + im_version + im_version_string + im_video_v4l1 + im_vips2bufjpeg + im_vips2jpeg + im_vips2mask + im_vips2mimejpeg + im_vips2png + im_vips2ppm + im_vips2tiff + im_vsnprintf + im_warning + im_wrapmany + im_wrapone + im_write_dmask + im_write_dmask_name + im_write_imask + im_write_imask_name + im_writeline + im_zerox + im_zone + im_zoom diff --git a/libsrcCC/Makefile.am b/libsrcCC/Makefile.am new file mode 100644 index 00000000..c1e4bc24 --- /dev/null +++ b/libsrcCC/Makefile.am @@ -0,0 +1,20 @@ +INCLUDES = -I$(top_srcdir)/include @VIPS_CFLAGS@ + +lib_LTLIBRARIES = libvipsCC.la + +libvipsCC_la_SOURCES = \ + VImage.cc \ + VError.cc \ + VDisplay.cc \ + VMask.cc + +libvipsCC_la_LDFLAGS = \ + -version-info @LIBRARY_CURRENT@:@LIBRARY_REVISION@:@LIBRARY_AGE@ + +libvipsCC_la_LIBADD = \ + $(top_builddir)/libsrc/libvips.la @VIPS_LIBS@ + +vipsc++.cc: + vips --cppc all > vipsc++.cc + +EXTRA_DIST = vipsc++.cc diff --git a/libsrcCC/VDisplay.cc b/libsrcCC/VDisplay.cc new file mode 100644 index 00000000..d2279d42 --- /dev/null +++ b/libsrcCC/VDisplay.cc @@ -0,0 +1,187 @@ +// Object part of VDisplay class + +/* + + Copyright (C) 1991-2001 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +VIPS_NAMESPACE_START + +/* Refcounting stuff first. + */ + +// Free an im_col_display +static void +free_display( im_col_display *d ) +{ + if( d->d_name ) + im_free( d->d_name ); + im_free( d ); +} + +// Dupe an im_col_display +static im_col_display * +dup_display( im_col_display *in ) throw( VError ) +{ + im_col_display *out; + + if( !(out = IM_NEW( NULL, im_col_display )) ) + verror(); + + *out = *in; + if( in->d_name ) + if( !(out->d_name = strdup( in->d_name )) ) { + free_display( out ); + verror( "out of memory" ); + } + + return( out ); +} + +// Remove lut +void VDisplay::refblock::cleanlut() +{ + if( luts ) { + im_free( luts ); + luts = 0; + } +} + +// Remove attached things +void VDisplay::refblock::cleanref() +{ + if( disp && priv ) { + free_display( disp ); + disp = 0; + priv = 0; + } + cleanlut(); +} + +// Get ready to write to disp +void VDisplay::refblock::wready() throw( VError ) +{ + cleanlut(); + if( !priv ) { + disp = dup_display( disp ); + priv = 1; + } +} + +// Check that luts are up-to-date +void VDisplay::refblock::cluts() throw( VError ) +{ + if( !luts ) + if( !(luts = im_col_make_tables_RGB( NULL, disp )) ) + verror(); +} + +VDisplay::~VDisplay() +{ + ref->nrefs--; + if( !ref->nrefs ) + delete ref; +} + +VDisplay &VDisplay::operator=( const VDisplay &a ) +{ + ref->nrefs--; + + if( ref->nrefs > 0 ) + // Need fresh + ref = new refblock; + else + // Recycle old + ref->cleanref(); + + ref = a.ref; + ref->nrefs++; + + return( *this ); +} + +VDisplay::VDisplay( const char *name ) throw( VError ) +{ + // Search for a matching name in the VIPS colour list + im_col_display *scr = im_col_display_name( name ); + + if( !scr ) { + VError err; + + err.app( "VDisplay error: " ); + err.app( "unknown display type \"" ).app( name ).app( "\"\n" ); + err.app( "display should be one of:" ); + + for( int i = 0; (scr = im_col_displays( i )); i++ ) { + err.app( " \"" ); + err.app( im_col_displays( i )->d_name ); + err.app( "\"" ); + } + + err.app( "\n" ); + + throw( err ); + } + + // Install display + ref = new refblock; + ref->disp = scr; +} + +VDisplay::VDisplay() +{ + // Just use sRGB + ref = new refblock; + ref->disp = im_col_displays( 7 ); +} + +/* + +Setters and getters. We used to have a lot of code of the form: + +float &VDisplay::YCW() + { ref->wready(); return( ((im_col_display*)ref->disp)->d_YCW ); } + +This should be split to separate setters/getters so we can exploit const. Too +annoying to do this on such a useless class (I'm certain no one used these +functions anyway), fix in vips8. + + */ + +VIPS_NAMESPACE_END + diff --git a/libsrcCC/VError.cc b/libsrcCC/VError.cc new file mode 100644 index 00000000..2a0427b4 --- /dev/null +++ b/libsrcCC/VError.cc @@ -0,0 +1,98 @@ +// Code for error type + +/* + + Copyright (C) 1991-2001 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +VIPS_NAMESPACE_START + +void VError::perror() +{ + std::cerr << _what; + exit( 1 ); +} + +void VError::perror( const char *name ) +{ + std::cerr << name << ": " << _what; + exit( 1 ); +} + +// Add a new bit to the end of the error buffer +VError &VError::app( const int i ) +{ + char buf[ 256 ]; + + sprintf( buf, "%d", i ); + _what += buf; + + return( *this ); +} + +VError &VError::app( std::string txt ) +{ + _what += txt; + + return( *this ); +}; + +void VError::ostream_print( std::ostream &file ) const +{ + file << _what; +} + +void verror( std::string str ) throw( VError ) +{ + VError err; + + err.app( "VIPS error: " ); + if( str == "" ) { + err.app( im_error_buffer() ); + im_error_clear(); + } + else + err.app( str ).app( "\n" ); + + throw( err ); +} + +VIPS_NAMESPACE_END diff --git a/libsrcCC/VImage.cc b/libsrcCC/VImage.cc new file mode 100644 index 00000000..5d60829f --- /dev/null +++ b/libsrcCC/VImage.cc @@ -0,0 +1,384 @@ +// Object part of VImage class + +/* + + Copyright (C) 1991-2001 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* +#define DEBUG + */ + +VIPS_NAMESPACE_START + +void VImage::refblock::debug_print() +{ + std::list::iterator i; + + printf( "refblock %p:\n", this ); + printf( " im = %p", im ); + if( im && im->filename ) + printf( " (im->filename = \"%s\")", im->filename ); + printf( "\n" ); + printf( " close_on_delete = %d\n", close_on_delete ); + printf( " nrefs (refs to us) = %d\n", nrefs ); + printf( " orefs (refs we make) = refblocks " ); + for( i = orefs.begin(); i != orefs.end(); i++ ) + printf( "%p ", *i ); + printf( "\n" ); +} + +// dump all refblocks for debugging +void VImage::print_all() +{ +#ifdef DEBUG + std::list::iterator i; + + printf( "*** VImage::refblock::print_all() start\n" ); + for( i = all_refblock.begin(); i != all_refblock.end(); i++ ) + (*i)->debug_print(); + printf( "*** VImage::refblock::print_all() end\n" ); +#endif /*DEBUG*/ +} + +// easy call from C version +void im__ccp_print_all() +{ + VImage::print_all(); +} + +// constructor +VImage::refblock::refblock() +{ + im = 0; + close_on_delete = 1; + nrefs = 1; + +#ifdef DEBUG + all_refblock.push_front( this ); +#endif /*DEBUG*/ +} + +// Add a ref - this (output image) depends upon IMAGE in +void VImage::refblock::addref( refblock *in ) throw( VError ) +{ + if( this == in ) + verror( "sanity failure" ); + + in->nrefs++; + orefs.push_front( in ); +} + +VImage::refblock::~refblock() throw( VError ) +{ +#ifdef DEBUG + printf( "VImage::refblock::removeref(): death!\n" ); + debug_print(); +#endif /*DEBUG*/ + + std::list::iterator i; + + if( close_on_delete && im ) { + if( im_close( im ) ) + verror(); + im = 0; + } + + // remove any refs we have ... may trigger other destructs in turn + for( i = orefs.begin(); i != orefs.end(); i++ ) + (*i)->removeref(); + +#ifdef DEBUG + all_refblock.remove( this ); +#endif /*DEBUG*/ +} + +// Remove a ref +void VImage::refblock::removeref() throw( VError ) +{ + nrefs--; + if( nrefs < 0 ) + verror( "too many closes!" ); + if( nrefs == 0 ) + delete this; +} + +// Init with name ... means open for read. +VImage::VImage( const char *name, const char *mode ) throw( VError ) +{ + _ref = new refblock; + + if( !(_ref->im = im_open( name, mode )) ) + verror(); + _ref->close_on_delete = 1; + +#ifdef DEBUG + printf( "VImage::VImage( \"%s\", \"%s\" )\n", name, mode ); + _ref->debug_print(); +#endif /*DEBUG*/ +} + +// Build a VImage from an IMAGE structure +VImage::VImage( im__IMAGE *in ) +{ + _ref = new refblock; + + _ref->im = in; + _ref->close_on_delete = 0; + +#ifdef DEBUG + printf( "VImage::VImage( IMAGE* %p )\n", in ); + _ref->debug_print(); +#endif /*DEBUG*/ +} + +// Build from memory buffer +VImage::VImage( void *buffer, int width, int height, + int bands, TBandFmt format ) throw( VError ) +{ + _ref = new refblock; + + if( !(_ref->im = im_image( buffer, width, height, + bands, int( format ) )) ) + verror(); + _ref->close_on_delete = 1; + +#ifdef DEBUG + printf( "VImage::VImage( void* %p, %d, %d )\n", + buffer, width, height ); + _ref->debug_print(); +#endif /*DEBUG*/ +} + +// Empty init ... means open intermediate +VImage::VImage() throw( VError ) +{ + static int id = 0; + char filename[256]; + + _ref = new refblock; + + /* This is not 100% safe if VIPS threading is not implemented on this + * platform ... but it doesn't really matter. + */ + g_mutex_lock( im__global_lock ); + im_snprintf( filename, 256, "intermediate image #%d", id++ ); + g_mutex_unlock( im__global_lock ); + + if( !(_ref->im = im_open( filename, "p" )) ) + verror(); + _ref->close_on_delete = 1; + +#ifdef DEBUG + printf( "VImage::VImage()\n" ); + _ref->debug_print(); +#endif /*DEBUG*/ +} + +// Copy constructor +VImage::VImage( const VImage &a ) +{ + _ref = a._ref; + _ref->nrefs++; +} + +// Assignment +VImage &VImage::operator=( const VImage &a ) throw( VError ) +{ + _ref->removeref(); + _ref = a._ref; + _ref->nrefs++; + + return( *this ); +} + +// Extract underlying data pointer +void *VImage::data() const throw( VError ) +{ + if( im_incheck( _ref->im ) ) + verror(); + + return( (void *) _ref->im->data ); +} + +void VImage::debug_print() +{ + im_printdesc( image() ); +} + +// Write this to a VImage +VImage VImage::write( VImage out ) throw( VError ) +{ + if( im_copy( _ref->im, out._ref->im ) ) + verror(); + out._ref->addref( _ref ); + + return( out ); +} + +VImage VImage::write( const char *name ) throw( VError ) +{ + VImage out( name, "w" ); + + if( im_copy( _ref->im, out._ref->im ) ) + verror(); + out._ref->addref( _ref ); + + return( out ); +} + +VImage VImage::write() throw( VError ) +{ + VImage out( "VImage:w1", "t" ); + + if( im_copy( _ref->im, out._ref->im ) ) + verror(); + out._ref->addref( _ref ); + + return( out ); +} + +// Projection functions to get header fields +int VImage::Xsize() { return( _ref->im->Xsize ); } +int VImage::Ysize() { return( _ref->im->Ysize ); } +int VImage::Bands() { return( _ref->im->Bands ); } +VImage::TBandFmt VImage::BandFmt() + { return( (TBandFmt) _ref->im->BandFmt ); } +VImage::TCoding VImage::Coding() + { return( (TCoding) _ref->im->Coding ); } +VImage::TType VImage::Type() { return( (TType) _ref->im->Type ); } +float VImage::Xres() { return( _ref->im->Xres ); } +float VImage::Yres() { return( _ref->im->Yres ); } +int VImage::Length() { return( _ref->im->Length ); } +VImage::TCompression VImage::Compression() + { return( (TCompression) _ref->im->Compression ); } +short VImage::Level() { return( _ref->im->Level ); } +int VImage::Xoffset() { return( _ref->im->Xoffset ); } +int VImage::Yoffset() { return( _ref->im->Yoffset ); } + +// Derived fields +const char *VImage::filename() { return( _ref->im->filename ); } +const char *VImage::Hist() { return( im_history_get( _ref->im ) ); } + +// Set header fields and setbuf() in one go. +void VImage::initdesc( int x, int y, int b, + TBandFmt f, TCoding c, TType t, float xr, float yr, int xo, int yo ) + throw( VError ) +{ + static int fmt[] = { + 0, // NOTSET + IM_BBITS_BYTE, IM_BBITS_BYTE, // uchar/char + IM_BBITS_SHORT, IM_BBITS_SHORT, // ushort/short + IM_BBITS_INT, IM_BBITS_INT, // uint/int + IM_BBITS_FLOAT, // float types ... + IM_BBITS_COMPLEX, + IM_BBITS_DOUBLE, + IM_BBITS_DPCOMPLEX + }; + + im_initdesc( _ref->im, x, y, b, + fmt[(int)f + 1], f, c, t, xr, yr, xo, yo ); + if( im_setupout( _ref->im ) ) + verror(); +} + +// Create a Vargv from a name +Vargv::Vargv( const char *name ) +{ + im_function *f = im_find_function( (char *) name ); + + if( !f ) + verror(); + + fn = (im__function *) f; + base = new im_object[f->argc]; + if( im_allocate_vargv( f, base ) ) { + delete[] base; + verror(); + } +} + +// Destroy a Vargv +Vargv::~Vargv() +{ + im_function *f = (im_function *) fn; + + // free any memory allocated for input vectors + // this is the stuff allocated in each function during _object* build, + // see vipsc++.cc + for( int i = 0; i < f->argc; i++ ) { + im_type_desc *ty = f->argv[i].desc; + + if( !(ty->flags & IM_TYPE_OUTPUT) ) { + if( strcmp( ty->type, IM_TYPE_IMAGEVEC ) == 0 || + strcmp( ty->type, IM_TYPE_DOUBLEVEC ) == 0 || + strcmp( ty->type, IM_TYPE_INTVEC ) == 0 ) { + // will work for doublevec and intvec too + im_imagevec_object *io = + (im_imagevec_object *) base[i]; + + if( io->vec ) { + delete[] io->vec; + io->vec = NULL; + } + } + } + } + + im_free_vargv( f, base ); + delete[] base; +} + +// Call the function +void +Vargv::call() +{ + im_function *f = (im_function *) fn; + + if( f->disp( base ) ) + verror(); +} + +/* Insert automatically generated wrappers for VIPS image processing + * functions. + */ +#include "vipsc++.cc" + +VIPS_NAMESPACE_END diff --git a/libsrcCC/VMask.cc b/libsrcCC/VMask.cc new file mode 100644 index 00000000..479b5952 --- /dev/null +++ b/libsrcCC/VMask.cc @@ -0,0 +1,644 @@ +// Object part of VMask class + +/* + + Copyright (C) 1991-2001 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +VIPS_NAMESPACE_START + +/* Functions for VMask - refcounting layer over VPMask. + */ + +VMask::~VMask() +{ + ref->nrefs--; + if( !ref->nrefs ) + delete ref; +} + +VMask &VMask::operator=( const VMask &a ) +{ + // Loosing ref to LHS + ref->nrefs--; + + if( ref->nrefs > 0 ) + // Need fresh refblock + ref = new refblock; + else + // Recycle old refblock + delete ref->pmask; + + // LHS now points to RHS + ref = a.ref; + ref->nrefs++; + + return( *this ); +} + +// Make sure this is a private copy of pmask --- dup if nrefs != 1 +void VMask::make_private() +{ + if( ref->nrefs > 1 ) { + // Make fresh refblock + refblock *ref2 = new refblock; + + // And copy the mask + ref2->pmask = ref->pmask->dup(); + ref->nrefs--; + ref = ref2; + } +} + +void VMask::ostream_print( std::ostream &file ) const +{ + file << *(ref->pmask); +} + +// Embed INTMASK in VIMask +void VIMask::embed( INTMASK *i ) throw( VError ) +{ + if( ref->pmask ) + verror( "embed: VIMask not empty" ); + ref->pmask = new _private_detail::VPIMask( i ); +} + +// Type conversions: implicit INTMASK to DOUBLEMASK +VIMask::operator VDMask() +{ + VDMask out( xsize(), ysize() ); + + out.mask().dptr->scale = scale(); + out.mask().dptr->offset = offset(); + + for( int i = 0; i < size(); i++ ) + out[i] = (*this)[i]; + + return( out ); +} + + +// Forward ref of VImage class +class VImage; + +// Type conversions: implicit DOUBLEMASK to INTMASK +VDMask::operator VIMask() +{ + VIMask out( xsize(), ysize() ); + + out.mask().iptr->scale = int( scale() ); + out.mask().iptr->offset = int( offset() ); + + for( int i = 0; i < size(); i++ ) + out[i] = (int) rint( (*this)[i] ); + + return( out ); +} + +// Type conversions: implicit DOUBLEMASK to VImage +VDMask::operator VImage() throw( VError ) +{ + VImage out; + + if( im_mask2vips( mask().dptr, out.image() ) ) + verror(); + + return( out ); +} + +// ... and INTMASK to VImage +VIMask::operator VImage() { return( VImage( VDMask( *this ) ) ); } + +// Embed DOUBLEMASK in VDMask +void VDMask::embed( DOUBLEMASK *i ) throw( VError ) +{ + if( ref->pmask ) + verror( "embed: VDMask not empty" ); + ref->pmask = new _private_detail::VPDMask( i ); +} + +/* Functions for P*Mask - layer over im_*_*mask() functions. + */ + +// Create empty imask +_private_detail::VPIMask::VPIMask( int xsize, int ysize ) throw( VError ) +{ + if( !(data.iptr = im_create_imask( "VPIMask::VPIMask", xsize, ysize )) ) + verror(); + type = _private_detail::VPMask::INT; +} + +// Init from data +_private_detail::VPIMask::VPIMask( int xsize, int ysize, + int scale, int offset, va_list ap ) + throw( VError ) +{ + if( !(data.iptr = im_create_imask( "VPIMask::VPIMask", xsize, ysize )) ) + verror(); + type = _private_detail::VPMask::INT; + + data.iptr->scale = scale; + data.iptr->offset = offset; + for( int i = 0; i < xsize * ysize; i++ ) + data.iptr->coeff[i] = va_arg( ap, int ); +} + +// Create from filename +_private_detail::VPIMask::VPIMask( const char *name ) throw( VError ) +{ + if( !(data.iptr = im_read_imask( (char *) name )) ) + verror(); + type = _private_detail::VPMask::INT; +} + +// Create from existing INTMASK +_private_detail::VPIMask::VPIMask( INTMASK *imask ) +{ + data.iptr = imask; + type = _private_detail::VPMask::INT; +} + +// Create empty +_private_detail::VPIMask::VPIMask() +{ + data.iptr = 0; + type = _private_detail::VPMask::UNASSIGNED; +} + +_private_detail::VPIMask::~VPIMask() +{ + if( data.iptr ) { + im_free_imask( data.iptr ); + data.iptr = 0; + type = _private_detail::VPMask::UNASSIGNED; + } +} + +// Duplicate -- we are a VPIMask, return a new VPIMask which is a copy of us. +// Return as a VPMask tho'. +_private_detail::VPMask *_private_detail::VPIMask::dup() const throw( VError ) +{ + _private_detail::VPIMask *out = new _private_detail::VPIMask(); + + INTMASK *msk; + if( !(msk = im_dup_imask( data.iptr, "VPIMask::dup" )) ) { + delete out; + verror(); + } + out->embed( msk ); + + return( out ); +} + +// Insert INTMASK pointer +void _private_detail::VPIMask::embed( INTMASK *msk ) throw( VError ) +{ + if( type != _private_detail::VPMask::UNASSIGNED ) + verror( "VPIMask::embed: VPIMask not empty" ); + + data.iptr = msk; + type = _private_detail::VPMask::INT; +} + +int _private_detail::VPIMask::xsize() const throw( VError ) +{ + if( !data.iptr ) + verror( "xsize: mask not set" ); + + return( data.iptr->xsize ); +} + +int _private_detail::VPIMask::ysize() const throw( VError ) +{ + if( !data.iptr ) + verror( "ysize: mask not set" ); + + return( data.iptr->ysize ); +} + +int _private_detail::VPIMask::scale() const throw( VError ) +{ + if( !data.iptr ) + verror( "scale: mask not set" ); + + return( data.iptr->scale ); +} + +int _private_detail::VPIMask::offset() const throw( VError ) +{ + if( !data.iptr ) + verror( "offset: mask not set" ); + + return( data.iptr->offset ); +} + +const char *_private_detail::VPIMask::filename() const throw( VError ) +{ + if( !data.iptr ) + verror( "filename: mask not set" ); + + return( data.iptr->filename ); +} + +void _private_detail::VPIMask::ostream_print( std::ostream &file ) const + throw( VError ) +{ + if( !data.iptr ) + verror( "internal error #7447234" ); + + int i, j; + int *p = data.iptr->coeff; + + file << this->xsize() << "\t" << this->ysize() << "\t"; + file << this->scale() << "\t" << this->offset() << "\n"; + + for( i = 0; i < this->ysize(); i++ ) { + for( j = 0; j < this->xsize(); j++ ) + file << *p++ << "\t"; + + file << "\n"; + } +} + +// Extract start of int array +int *_private_detail::VPIMask::array() const +{ + return( data.iptr->coeff ); +} + +// Create empty dmask +_private_detail::VPDMask::VPDMask( int xsize, int ysize ) throw( VError ) +{ + if( !(data.dptr = im_create_dmask( "VPDMask::VPDMask", xsize, ysize )) ) + verror(); + type = _private_detail::VPMask::DOUBLE; +} + +// Create from args +_private_detail::VPDMask::VPDMask( int xsize, int ysize, + double scale, double offset, va_list ap ) throw( VError ) +{ + if( !(data.dptr = im_create_dmask( "VPDMask::VPDMask", xsize, ysize )) ) + verror(); + type = _private_detail::VPMask::DOUBLE; + + data.dptr->scale = scale; + data.dptr->offset = offset; + for( int i = 0; i < xsize * ysize; i++ ) + data.dptr->coeff[i] = va_arg( ap, double ); +} + +// Create from filename +_private_detail::VPDMask::VPDMask( const char *name ) throw( VError ) +{ + if( !(data.dptr = im_read_dmask( (char *) name )) ) + verror(); + type = _private_detail::VPMask::DOUBLE; +} + +// Create empty +_private_detail::VPDMask::VPDMask() +{ + data.dptr = 0; + type = _private_detail::VPMask::UNASSIGNED; +} + +// Create from existing DOUBLEMASK +_private_detail::VPDMask::VPDMask( DOUBLEMASK *dmask ) +{ + data.dptr = dmask; + type = _private_detail::VPMask::DOUBLE; +} + +_private_detail::VPDMask::~VPDMask() +{ + if( data.dptr ) { + im_free_dmask( data.dptr ); + data.dptr = 0; + type = _private_detail::VPMask::UNASSIGNED; + } +} + +// Duplicate -- we are a VPIMask, return a new VPIMask which is a copy of us. +// Return as a VPMask tho'. +_private_detail::VPMask *_private_detail::VPDMask::dup() const throw( VError ) +{ + _private_detail::VPDMask *out = new _private_detail::VPDMask(); + + DOUBLEMASK *msk; + if( !(msk = im_dup_dmask( data.dptr, "VPDMask::dup" )) ) { + delete out; + verror(); + } + out->embed( msk ); + + return( out ); +} + +// Insert DOUBLEMASK pointer +void _private_detail::VPDMask::embed( DOUBLEMASK *msk ) throw( VError ) +{ + if( type != _private_detail::VPMask::UNASSIGNED ) + verror( "VPDMask::embed: VPDMask not empty" ); + + data.dptr = msk; + type = _private_detail::VPMask::DOUBLE; +} + +int _private_detail::VPDMask::xsize() const throw( VError ) +{ + if( !data.dptr ) + verror( "xsize: mask not set" ); + + return( data.dptr->xsize ); +} + +int _private_detail::VPDMask::ysize() const throw( VError ) +{ + if( !data.dptr ) + verror( "ysize: mask not set" ); + + return( data.dptr->ysize ); +} + +double _private_detail::VPDMask::scale() const throw( VError ) +{ + if( !data.dptr ) + verror( "scale: mask not set" ); + + return( data.dptr->scale ); +} + +double _private_detail::VPDMask::offset() const throw( VError ) +{ + if( !data.dptr ) + verror( "offset: mask not set" ); + + return( data.dptr->offset ); +} + +const char *_private_detail::VPDMask::filename() const throw( VError ) +{ + if( !data.dptr ) + verror( "filename: mask not set" ); + + return( data.dptr->filename ); +} + +void _private_detail::VPDMask::ostream_print( std::ostream &file ) const + throw( VError ) +{ + if( !data.dptr ) + verror( "internal error #7447234" ); + + int i, j; + double *p = data.dptr->coeff; + + file << this->xsize() << "\t" << this->ysize() << "\t"; + file << this->scale() << "\t" << this->offset() << "\n"; + + for( i = 0; i < this->ysize(); i++ ) { + for( j = 0; j < this->xsize(); j++ ) + file << *p++ << "\t"; + + file << "\n"; + } +} + +// Extract data pointer +double *_private_detail::VPDMask::array() const +{ + return( data.dptr->coeff ); +} + +// Build functions +VIMask VIMask::gauss( double sig, double minamp ) throw( VError ) +{ + VIMask out; + INTMASK *msk; + + if( !(msk = im_gauss_imask( "VIMask::gauss", sig, minamp )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::gauss( double sig, double minamp ) throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_gauss_dmask( "VDMask::gauss", sig, minamp )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VIMask VIMask::log( double sig, double minamp ) throw( VError ) +{ + VIMask out; + INTMASK *msk; + + if( !(msk = im_log_imask( "VIMask::log", sig, minamp )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::log( double sig, double minamp ) throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_log_dmask( "VDMask::log", sig, minamp )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +// Manipulation functions +VIMask VIMask::rotate45() throw( VError ) +{ + VIMask out; + INTMASK *msk; + + if( !(msk = im_rotate_imask45( mask().iptr, "VIMask::rotate45" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VIMask VIMask::rotate90() throw( VError ) +{ + VIMask out; + INTMASK *msk; + + if( !(msk = im_rotate_imask90( mask().iptr, "VIMask::rotate90" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::rotate45() throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_rotate_dmask45( mask().dptr, "VDMask::rotate45" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::rotate90() throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_rotate_dmask90( mask().dptr, "VDMask::rotate90" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::trn() throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_mattrn( mask().dptr, "VDMask::trn" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::inv() throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_matinv( mask().dptr, "VDMask::inv" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::mul( VDMask m ) throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_matmul( mask().dptr, m.mask().dptr, "VDMask::mul" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::cat( VDMask m ) throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_matcat( mask().dptr, m.mask().dptr, "VDMask::cat" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VIMask VDMask::scalei() throw( VError ) +{ + VIMask out; + INTMASK *msk; + + if( !(msk = im_scale_dmask( mask().dptr, "VDMask::scalei" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +// Arithmetic on a VIMask ... just cast and use VDMask +VDMask VIMask::trn() throw( VError ) + { return( ((VDMask)*this).trn() ); } +VDMask VIMask::inv() throw( VError ) + { return( ((VDMask)*this).inv() ); } +VDMask VIMask::cat( VDMask a ) throw( VError ) + { return( ((VDMask)*this).cat( a ) ); } +VDMask VIMask::mul( VDMask a ) throw( VError ) + { return( ((VDMask)*this).mul( a ) ); } + +// Overload [] to get linear array subscript. +// Our caller may write to the result, so make sure we have a private +// copy. +// Involves function call, slow anyway, so do range checking +int &VIMask::operator[]( int x ) throw( VError ) +{ + if( ref->nrefs != 1 ) + make_private(); + + if( x > size() ) + verror( "VIMask::operator[]: subscript out of range" ); + + return( ((_private_detail::VPIMask *)ref->pmask)->array()[x] ); +} + +double &VDMask::operator[]( int x ) throw( VError ) +{ + if( ref->nrefs != 1 ) + make_private(); + + if( x > size() ) + verror( "VDMask::operator[]: subscript out of range" ); + + return( ((_private_detail::VPDMask *)ref->pmask)->array()[x] ); +} + +VIPS_NAMESPACE_END diff --git a/libsrcCC/vipsc++.cc b/libsrcCC/vipsc++.cc new file mode 100644 index 00000000..41439253 --- /dev/null +++ b/libsrcCC/vipsc++.cc @@ -0,0 +1,5367 @@ +// this file automatically generated from +// VIPS library 7.12.2-Tue Jul 17 23:36:09 BST 2007 +// im_abs: absolute value +VImage VImage::abs() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_abs" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_acostra: acos of image (result in degrees) +VImage VImage::acos() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_acostra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_add: add two images +VImage VImage::add( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_add" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_asintra: asin of image (result in degrees) +VImage VImage::asin() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_asintra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_atantra: atan of image (result in degrees) +VImage VImage::atan() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_atantra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_avg: average value of image +double VImage::avg() throw( VError ) +{ + VImage in = *this; + double value; + + Vargv _vec( "im_avg" ); + + _vec.data(0) = in.image(); + _vec.call(); + value = *((double*)_vec.data(1)); + + return( value ); +} + +// im_point_bilinear: interpolate value at single point, linearly +double VImage::point_bilinear( double x, double y, int band ) throw( VError ) +{ + VImage in = *this; + double val; + + Vargv _vec( "im_point_bilinear" ); + + _vec.data(0) = in.image(); + *((double*) _vec.data(1)) = x; + *((double*) _vec.data(2)) = y; + *((int*) _vec.data(3)) = band; + _vec.call(); + val = *((double*)_vec.data(4)); + + return( val ); +} + +// im_bandmean: average image bands +VImage VImage::bandmean() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_bandmean" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_ceil: round to smallest integal value not less than +VImage VImage::ceil() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_ceil" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_cmulnorm: multiply two complex images, normalising output +VImage VImage::cmulnorm( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_cmulnorm" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_costra: cos of image (angles in degrees) +VImage VImage::cos() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_costra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_deviate: standard deviation of image +double VImage::deviate() throw( VError ) +{ + VImage in = *this; + double value; + + Vargv _vec( "im_deviate" ); + + _vec.data(0) = in.image(); + _vec.call(); + value = *((double*)_vec.data(1)); + + return( value ); +} + +// im_divide: divide two images +VImage VImage::divide( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_divide" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_exp10tra: 10^pel of image +VImage VImage::exp10() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_exp10tra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_expntra: x^pel of image +VImage VImage::expn( double x ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_expntra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = x; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_expntra_vec: [x,y,z]^pel of image +VImage VImage::expn( std::vector v ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_expntra_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = v.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[v.size()]; + for( unsigned int i = 0; i < v.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = v[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_exptra: e^pel of image +VImage VImage::exp() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_exptra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_fav4: average of 4 images +VImage VImage::fav4( VImage in2, VImage in3, VImage in4 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_fav4" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = in3.image(); + _vec.data(3) = in4.image(); + _vec.data(4) = out.image(); + _vec.call(); + + return( out ); +} + +// im_floor: round to largest integal value not greater than +VImage VImage::floor() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_floor" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_gadd: calculate a*in1 + b*in2 + c = outfile +VImage VImage::gadd( double a, double b, VImage in2, double c ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_gadd" ); + + *((double*) _vec.data(0)) = a; + _vec.data(1) = in1.image(); + *((double*) _vec.data(2)) = b; + _vec.data(3) = in2.image(); + *((double*) _vec.data(4)) = c; + _vec.data(5) = out.image(); + _vec.call(); + + return( out ); +} + +// im_invert: photographic negative +VImage VImage::invert() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_invert" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lintra: calculate a*in + b = outfile +VImage VImage::lin( double a, double b ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lintra" ); + + *((double*) _vec.data(0)) = a; + _vec.data(1) = in.image(); + *((double*) _vec.data(2)) = b; + _vec.data(3) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_linreg: pixelwise linear regression +VImage VImage::linreg( std::vector ins, std::vector xs ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_linreg" ); + + ((im_imagevec_object*) _vec.data(0))->n = ins.size(); + ((im_imagevec_object*) _vec.data(0))->vec = new IMAGE *[ins.size()]; + for( unsigned int i = 0; i < ins.size(); i++ ) + ((im_imagevec_object*) _vec.data(0))->vec[i] = ins[i].image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = xs.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[xs.size()]; + for( unsigned int i = 0; i < xs.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = xs[i]; + _vec.call(); + for( unsigned int i = 0; i < ins.size(); i++ ) + out._ref->addref( ins[i]._ref ); + + return( out ); +} + +// im_lintra_vec: calculate a*in + b -> out, a and b vectors +VImage VImage::lin( std::vector a, std::vector b ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lintra_vec" ); + + ((im_doublevec_object*) _vec.data(0))->n = a.size(); + ((im_doublevec_object*) _vec.data(0))->vec = new double[a.size()]; + for( unsigned int i = 0; i < a.size(); i++ ) + ((im_doublevec_object*) _vec.data(0))->vec[i] = a[i]; + _vec.data(1) = in.image(); + ((im_doublevec_object*) _vec.data(2))->n = b.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[b.size()]; + for( unsigned int i = 0; i < b.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = b[i]; + _vec.data(3) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_litecor: calculate max(white)*factor*(in/white), if clip == 1 +VImage VImage::litecor( VImage white, int clip, double factor ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_litecor" ); + + _vec.data(0) = in.image(); + _vec.data(1) = white.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = clip; + *((double*) _vec.data(4)) = factor; + _vec.call(); + + return( out ); +} + +// im_log10tra: log10 of image +VImage VImage::log10() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_log10tra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_logtra: ln of image +VImage VImage::log() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_logtra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_max: maximum value of image +double VImage::max() throw( VError ) +{ + VImage in = *this; + double value; + + Vargv _vec( "im_max" ); + + _vec.data(0) = in.image(); + _vec.call(); + value = *((double*)_vec.data(1)); + + return( value ); +} + +// im_maxpos: position of maximum value of image +std::complex VImage::maxpos() throw( VError ) +{ + VImage in = *this; + std::complex position; + + Vargv _vec( "im_maxpos" ); + + _vec.data(0) = in.image(); + _vec.call(); + position = *((std::complex*)_vec.data(1)); + + return( position ); +} + +// im_maxpos_avg: position of maximum value of image, averaging in case of draw +double VImage::maxpos_avg( double& y, double& out ) throw( VError ) +{ + VImage in = *this; + double x; + + Vargv _vec( "im_maxpos_avg" ); + + _vec.data(0) = in.image(); + _vec.call(); + x = *((double*)_vec.data(1)); + y = *((double*)_vec.data(2)); + out = *((double*)_vec.data(3)); + + return( x ); +} + +// im_measure: measure averages of a grid of patches +VDMask VImage::measure( int x, int y, int w, int h, int h_patches, int v_patches ) throw( VError ) +{ + VImage in = *this; + VDMask mask; + + Vargv _vec( "im_measure" ); + + _vec.data(0) = in.image(); + ((im_mask_object*) _vec.data(1))->name = (char*)"noname"; + *((int*) _vec.data(2)) = x; + *((int*) _vec.data(3)) = y; + *((int*) _vec.data(4)) = w; + *((int*) _vec.data(5)) = h; + *((int*) _vec.data(6)) = h_patches; + *((int*) _vec.data(7)) = v_patches; + _vec.call(); + mask.embed( (DOUBLEMASK *)((im_mask_object*)_vec.data(1))->mask ); + + return( mask ); +} + +// im_min: minimum value of image +double VImage::min() throw( VError ) +{ + VImage in = *this; + double value; + + Vargv _vec( "im_min" ); + + _vec.data(0) = in.image(); + _vec.call(); + value = *((double*)_vec.data(1)); + + return( value ); +} + +// im_minpos: position of minimum value of image +std::complex VImage::minpos() throw( VError ) +{ + VImage in = *this; + std::complex position; + + Vargv _vec( "im_minpos" ); + + _vec.data(0) = in.image(); + _vec.call(); + position = *((std::complex*)_vec.data(1)); + + return( position ); +} + +// im_multiply: multiply two images +VImage VImage::multiply( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_multiply" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_powtra: pel^x ofbuildimage +VImage VImage::pow( double x ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_powtra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = x; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_powtra_vec: pel^[x,y,z] of image +VImage VImage::pow( std::vector v ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_powtra_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = v.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[v.size()]; + for( unsigned int i = 0; i < v.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = v[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_remainder: remainder after integer division +VImage VImage::remainder( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_remainder" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_remainderconst: remainder after integer division by a constant +VImage VImage::remainder( double x ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_remainderconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = x; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_remainderconst_vec: remainder after integer division by a vector of constants +VImage VImage::remainder( std::vector x ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_remainderconst_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = x.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[x.size()]; + for( unsigned int i = 0; i < x.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = x[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_rint: round to nearest integal value +VImage VImage::rint() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rint" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_sign: unit vector in direction of value +VImage VImage::sign() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_sign" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_sintra: sin of image (angles in degrees) +VImage VImage::sin() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_sintra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_stats: many image statistics in one pass +VDMask VImage::stats() throw( VError ) +{ + VImage in = *this; + VDMask statistics; + + Vargv _vec( "im_stats" ); + + _vec.data(0) = in.image(); + ((im_mask_object*) _vec.data(1))->name = (char*)"noname"; + _vec.call(); + statistics.embed( (DOUBLEMASK *)((im_mask_object*)_vec.data(1))->mask ); + + return( statistics ); +} + +// im_subtract: subtract two images +VImage VImage::subtract( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_subtract" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_tantra: tan of image (angles in degrees) +VImage VImage::tan() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_tantra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_andimage: bitwise and of two images +VImage VImage::andimage( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_andimage" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_andimageconst: bitwise and of an image with a constant +VImage VImage::andimage( int c ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_andimageconst" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in1._ref ); + + return( out ); +} + +// im_andimage_vec: bitwise and of an image with a vector constant +VImage VImage::andimage( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_andimage_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_orimage: bitwise or of two images +VImage VImage::orimage( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_orimage" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_orimageconst: bitwise or of an image with a constant +VImage VImage::orimage( int c ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_orimageconst" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in1._ref ); + + return( out ); +} + +// im_orimage_vec: bitwise or of an image with a vector constant +VImage VImage::orimage( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_orimage_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_eorimage: bitwise eor of two images +VImage VImage::eorimage( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_eorimage" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_eorimageconst: bitwise eor of an image with a constant +VImage VImage::eorimage( int c ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_eorimageconst" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in1._ref ); + + return( out ); +} + +// im_eorimage_vec: bitwise eor of an image with a vector constant +VImage VImage::eorimage( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_eorimage_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_shiftleft: shift integer image n bits to left +VImage VImage::shiftleft( int c ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_shiftleft" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in1._ref ); + + return( out ); +} + +// im_shiftright: shift integer image n bits to right +VImage VImage::shiftright( int c ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_shiftright" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in1._ref ); + + return( out ); +} + +// im_LCh2Lab: convert LCh to Lab +VImage VImage::LCh2Lab() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LCh2Lab" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LCh2UCS: convert LCh to UCS +VImage VImage::LCh2UCS() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LCh2UCS" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2LCh: convert Lab to LCh +VImage VImage::Lab2LCh() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2LCh" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2LabQ: convert Lab to LabQ +VImage VImage::Lab2LabQ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2LabQ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2LabS: convert Lab to LabS +VImage VImage::Lab2LabS() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2LabS" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2UCS: convert Lab to UCS +VImage VImage::Lab2UCS() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2UCS" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2XYZ: convert D65 Lab to XYZ +VImage VImage::Lab2XYZ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2XYZ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2XYZ_temp: convert Lab to XYZ, with a specified colour temperature +VImage VImage::Lab2XYZ_temp( double X0, double Y0, double Z0 ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2XYZ_temp" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = X0; + *((double*) _vec.data(3)) = Y0; + *((double*) _vec.data(4)) = Z0; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2disp: convert Lab to displayable +VImage VImage::Lab2disp( VDisplay disp ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2disp" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = disp.disp(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LabQ2LabS: convert LabQ to LabS +VImage VImage::LabQ2LabS() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LabQ2LabS" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LabQ2Lab: convert LabQ to Lab +VImage VImage::LabQ2Lab() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LabQ2Lab" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LabQ2XYZ: convert LabQ to XYZ +VImage VImage::LabQ2XYZ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LabQ2XYZ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LabQ2disp: convert LabQ to displayable +VImage VImage::LabQ2disp( VDisplay disp ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LabQ2disp" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = disp.disp(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LabS2LabQ: convert LabS to LabQ +VImage VImage::LabS2LabQ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LabS2LabQ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LabS2Lab: convert LabS to Lab +VImage VImage::LabS2Lab() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LabS2Lab" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_UCS2LCh: convert UCS to LCh +VImage VImage::UCS2LCh() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_UCS2LCh" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_UCS2Lab: convert UCS to Lab +VImage VImage::UCS2Lab() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_UCS2Lab" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_UCS2XYZ: convert UCS to XYZ +VImage VImage::UCS2XYZ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_UCS2XYZ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_XYZ2Lab: convert D65 XYZ to Lab +VImage VImage::XYZ2Lab() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_XYZ2Lab" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_XYZ2Lab_temp: convert XYZ to Lab, with a specified colour temperature +VImage VImage::XYZ2Lab_temp( double X0, double Y0, double Z0 ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_XYZ2Lab_temp" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = X0; + *((double*) _vec.data(3)) = Y0; + *((double*) _vec.data(4)) = Z0; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_XYZ2UCS: convert XYZ to UCS +VImage VImage::XYZ2UCS() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_XYZ2UCS" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_XYZ2Yxy: convert XYZ to Yxy +VImage VImage::XYZ2Yxy() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_XYZ2Yxy" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_XYZ2disp: convert XYZ to displayble +VImage VImage::XYZ2disp( VDisplay disp ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_XYZ2disp" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = disp.disp(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_XYZ2sRGB: convert XYZ to sRGB +VImage VImage::XYZ2sRGB() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_XYZ2sRGB" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Yxy2XYZ: convert Yxy to XYZ +VImage VImage::Yxy2XYZ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Yxy2XYZ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_dE00_fromLab: calculate delta-E CIE2000 for two Lab images +VImage VImage::dE00_fromLab( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_dE00_fromLab" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_dECMC_fromLab: calculate delta-E CMC(1:1) for two Lab images +VImage VImage::dECMC_fromLab( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_dECMC_fromLab" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_dECMC_fromdisp: calculate delta-E CMC(1:1) for two displayable images +VImage VImage::dECMC_fromdisp( VImage in2, VDisplay disp ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_dECMC_fromdisp" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.data(3) = disp.disp(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_dE_fromLab: calculate delta-E for two Lab images +VImage VImage::dE_fromLab( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_dE_fromLab" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_dE_fromXYZ: calculate delta-E for two XYZ images +VImage VImage::dE_fromXYZ( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_dE_fromXYZ" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_dE_fromdisp: calculate delta-E for two displayable images +VImage VImage::dE_fromdisp( VImage in2, VDisplay disp ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_dE_fromdisp" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.data(3) = disp.disp(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_disp2Lab: convert displayable to Lab +VImage VImage::disp2Lab( VDisplay disp ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_disp2Lab" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = disp.disp(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_disp2XYZ: convert displayable to XYZ +VImage VImage::disp2XYZ( VDisplay disp ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_disp2XYZ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = disp.disp(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_icc_ac2rc: convert LAB from AC to RC using an ICC profile +VImage VImage::icc_ac2rc( char* profile ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_icc_ac2rc" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = (im_object) profile; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_icc_export: convert a float LAB to an 8-bit device image with an ICC profile +VImage VImage::icc_export( char* output_profile, int intent ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_icc_export" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = (im_object) output_profile; + *((int*) _vec.data(3)) = intent; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_icc_export_depth: convert a float LAB to device space with an ICC profile +VImage VImage::icc_export_depth( int depth, char* output_profile, int intent ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_icc_export_depth" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = depth; + _vec.data(3) = (im_object) output_profile; + *((int*) _vec.data(4)) = intent; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_icc_import: convert a device image to float LAB with an ICC profile +VImage VImage::icc_import( char* input_profile, int intent ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_icc_import" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = (im_object) input_profile; + *((int*) _vec.data(3)) = intent; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_icc_import_embedded: convert a device image to float LAB using the embedded profile +VImage VImage::icc_import_embedded( int intent ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_icc_import_embedded" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = intent; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_icc_transform: convert between two device images with a pair of ICC profiles +VImage VImage::icc_transform( char* input_profile, char* output_profile, int intent ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_icc_transform" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = (im_object) input_profile; + _vec.data(3) = (im_object) output_profile; + *((int*) _vec.data(4)) = intent; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lab_morph: morph colourspace of a LAB image +VImage VImage::lab_morph( VDMask greyscale, double L_offset, double L_scale, double a_scale, double b_scale ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lab_morph" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = greyscale.mask().dptr; + *((double*) _vec.data(3)) = L_offset; + *((double*) _vec.data(4)) = L_scale; + *((double*) _vec.data(5)) = a_scale; + *((double*) _vec.data(6)) = b_scale; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_sRGB2XYZ: convert sRGB to XYZ +VImage VImage::sRGB2XYZ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_sRGB2XYZ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_bandjoin: bandwise join of two images +VImage VImage::bandjoin( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_bandjoin" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_black: generate black image +VImage VImage::black( int x_size, int y_size, int bands ) throw( VError ) +{ + VImage output; + + Vargv _vec( "im_black" ); + + _vec.data(0) = output.image(); + *((int*) _vec.data(1)) = x_size; + *((int*) _vec.data(2)) = y_size; + *((int*) _vec.data(3)) = bands; + _vec.call(); + + return( output ); +} + +// im_c2amph: convert real and imaginary to phase and amplitude +VImage VImage::c2amph() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_c2amph" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_c2imag: extract imaginary part of complex image +VImage VImage::c2imag() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_c2imag" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_c2ps: find power spectrum of complex image +VImage VImage::c2ps() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_c2ps" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_c2real: extract real part of complex image +VImage VImage::c2real() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_c2real" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_c2rect: convert phase and amplitude to real and imaginary +VImage VImage::c2rect() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_c2rect" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2c: convert to signed 8-bit integer +VImage VImage::clip2c() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2c" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2cm: convert to complex +VImage VImage::clip2cm() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2cm" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2d: convert to double-precision float +VImage VImage::clip2d() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2d" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2dcm: convert to double complex +VImage VImage::clip2dcm() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2dcm" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2f: convert to single-precision float +VImage VImage::clip2f() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2f" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2fmt: convert image format to ofmt +VImage VImage::clip2fmt( int ofmt ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2fmt" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = ofmt; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2i: convert to signed 32-bit integer +VImage VImage::clip2i() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2i" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2s: convert to signed 16-bit integer +VImage VImage::clip2s() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2s" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2ui: convert to unsigned 32-bit integer +VImage VImage::clip2ui() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2ui" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2us: convert to unsigned 16-bit integer +VImage VImage::clip2us() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2us" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip: convert to unsigned 8-bit integer +VImage VImage::clip() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_copy: copy image +VImage VImage::copy() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_copy" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_copy_morph: copy image, setting pixel layout +VImage VImage::copy_morph( int Bands, int BandFmt, int Coding ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_copy_morph" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = Bands; + *((int*) _vec.data(3)) = BandFmt; + *((int*) _vec.data(4)) = Coding; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_copy_swap: copy image, swapping byte order +VImage VImage::copy_swap() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_copy_swap" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_copy_set: copy image, setting informational fields +VImage VImage::copy_set( int Type, double Xres, double Yres, int Xoffset, int Yoffset ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_copy_set" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = Type; + *((double*) _vec.data(3)) = Xres; + *((double*) _vec.data(4)) = Yres; + *((int*) _vec.data(5)) = Xoffset; + *((int*) _vec.data(6)) = Yoffset; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_csv2vips: read a file in csv format +VImage VImage::csv2vips( char* filename ) throw( VError ) +{ + VImage im; + + Vargv _vec( "im_csv2vips" ); + + _vec.data(0) = (im_object) filename; + _vec.data(1) = im.image(); + _vec.call(); + + return( im ); +} + +// im_extract_area: extract area +VImage VImage::extract_area( int left, int top, int width, int height ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_extract_area" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = left; + *((int*) _vec.data(3)) = top; + *((int*) _vec.data(4)) = width; + *((int*) _vec.data(5)) = height; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_extract_areabands: extract area and bands +VImage VImage::extract_areabands( int left, int top, int width, int height, int band, int nbands ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_extract_areabands" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = left; + *((int*) _vec.data(3)) = top; + *((int*) _vec.data(4)) = width; + *((int*) _vec.data(5)) = height; + *((int*) _vec.data(6)) = band; + *((int*) _vec.data(7)) = nbands; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_extract_band: extract band +VImage VImage::extract_band( int band ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_extract_band" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = band; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_extract_bands: extract several bands +VImage VImage::extract_bands( int band, int nbands ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_extract_bands" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = band; + *((int*) _vec.data(3)) = nbands; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_extract: extract area/band +VImage VImage::extract( int left, int top, int width, int height, int band ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_extract" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = left; + *((int*) _vec.data(3)) = top; + *((int*) _vec.data(4)) = width; + *((int*) _vec.data(5)) = height; + *((int*) _vec.data(6)) = band; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_falsecolour: turn luminance changes into chrominance changes +VImage VImage::falsecolour() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_falsecolour" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_fliphor: flip image left-right +VImage VImage::fliphor() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_fliphor" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_flipver: flip image top-bottom +VImage VImage::flipver() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_flipver" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_gbandjoin: bandwise join of many images +VImage VImage::gbandjoin( std::vector in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_gbandjoin" ); + + ((im_imagevec_object*) _vec.data(0))->n = in.size(); + ((im_imagevec_object*) _vec.data(0))->vec = new IMAGE *[in.size()]; + for( unsigned int i = 0; i < in.size(); i++ ) + ((im_imagevec_object*) _vec.data(0))->vec[i] = in[i].image(); + _vec.data(1) = out.image(); + _vec.call(); + for( unsigned int i = 0; i < in.size(); i++ ) + out._ref->addref( in[i]._ref ); + + return( out ); +} + +// im_grid: chop a tall thin image into a grid of images +VImage VImage::grid( int tile_height, int across, int down ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_grid" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = tile_height; + *((int*) _vec.data(3)) = across; + *((int*) _vec.data(4)) = down; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_insert: insert sub-image into main image at position +VImage VImage::insert( VImage sub, int x, int y ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_insert" ); + + _vec.data(0) = in.image(); + _vec.data(1) = sub.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = x; + *((int*) _vec.data(4)) = y; + _vec.call(); + out._ref->addref( in._ref ); + out._ref->addref( sub._ref ); + + return( out ); +} + +// im_insert_noexpand: insert sub-image into main image at position, no expansion +VImage VImage::insert_noexpand( VImage sub, int x, int y ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_insert_noexpand" ); + + _vec.data(0) = in.image(); + _vec.data(1) = sub.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = x; + *((int*) _vec.data(4)) = y; + _vec.call(); + out._ref->addref( in._ref ); + out._ref->addref( sub._ref ); + + return( out ); +} + +// im_jpeg2vips: convert from jpeg +VImage VImage::jpeg2vips( char* in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_jpeg2vips" ); + + _vec.data(0) = (im_object) in; + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_lrjoin: join two images left-right +VImage VImage::lrjoin( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_lrjoin" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_magick2vips: load file with libMagick +VImage VImage::magick2vips( char* in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_magick2vips" ); + + _vec.data(0) = (im_object) in; + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_mask2vips: convert DOUBLEMASK to VIPS image +VImage VImage::mask2vips( VDMask input ) throw( VError ) +{ + VImage output; + + Vargv _vec( "im_mask2vips" ); + + ((im_mask_object*) _vec.data(0))->mask = input.mask().dptr; + _vec.data(1) = output.image(); + _vec.call(); + + return( output ); +} + +// im_msb: convert to uchar by discarding bits +VImage VImage::msb() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_msb" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_msb_band: convert to single band uchar by discarding bits +VImage VImage::msb_band( int band ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_msb_band" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = band; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_png2vips: convert PNG file to VIPS image +VImage VImage::png2vips( char* in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_png2vips" ); + + _vec.data(0) = (im_object) in; + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_exr2vips: convert an OpenEXR file to VIPS +VImage VImage::exr2vips( char* in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_exr2vips" ); + + _vec.data(0) = (im_object) in; + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_ppm2vips: read a file in pbm/pgm/ppm format +VImage VImage::ppm2vips( char* filename ) throw( VError ) +{ + VImage im; + + Vargv _vec( "im_ppm2vips" ); + + _vec.data(0) = (im_object) filename; + _vec.data(1) = im.image(); + _vec.call(); + + return( im ); +} + +// im_analyze2vips: read a file in analyze format +VImage VImage::analyze2vips( char* filename ) throw( VError ) +{ + VImage im; + + Vargv _vec( "im_analyze2vips" ); + + _vec.data(0) = (im_object) filename; + _vec.data(1) = im.image(); + _vec.call(); + + return( im ); +} + +// im_recomb: linear recombination with mask +VImage VImage::recomb( VDMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_recomb" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().dptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_replicate: replicate an image horizontally and vertically +VImage VImage::replicate( int across, int down ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_replicate" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = across; + *((int*) _vec.data(3)) = down; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_ri2c: join two non-complex images to form complex +VImage VImage::ri2c( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_ri2c" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_rot180: rotate image 180 degrees +VImage VImage::rot180() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rot180" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_rot270: rotate image 270 degrees clockwise +VImage VImage::rot270() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rot270" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_rot90: rotate image 90 degrees clockwise +VImage VImage::rot90() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rot90" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_scale: scale image linearly to fit range 0-255 +VImage VImage::scale() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_scale" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_scaleps: logarithmic scale of image to fit range 0-255 +VImage VImage::scaleps() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_scaleps" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_rightshift_size: decrease size by a power-of-two factor +VImage VImage::rightshift_size( int xshift, int yshift, int band_fmt ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rightshift_size" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = xshift; + *((int*) _vec.data(3)) = yshift; + *((int*) _vec.data(4)) = band_fmt; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_slice: slice an image using two thresholds +VImage VImage::slice( double thresh1, double thresh2 ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_slice" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((double*) _vec.data(2)) = thresh1; + *((double*) _vec.data(3)) = thresh2; + _vec.call(); + + return( output ); +} + +// im_subsample: subsample image by integer factors +VImage VImage::subsample( int xshrink, int yshrink ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_subsample" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = xshrink; + *((int*) _vec.data(3)) = yshrink; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_system: run command on image +char* VImage::system( char* command ) throw( VError ) +{ + VImage im = *this; + char* output; + + Vargv _vec( "im_system" ); + + _vec.data(0) = im.image(); + _vec.data(1) = (im_object) command; + _vec.call(); + output = (char*) _vec.data(2); + + return( output ); +} + +// im_tbjoin: join two images top-bottom +VImage VImage::tbjoin( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_tbjoin" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_text: generate text image +VImage VImage::text( char* text, char* font, int width, int alignment, int dpi ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_text" ); + + _vec.data(0) = out.image(); + _vec.data(1) = (im_object) text; + _vec.data(2) = (im_object) font; + *((int*) _vec.data(3)) = width; + *((int*) _vec.data(4)) = alignment; + *((int*) _vec.data(5)) = dpi; + _vec.call(); + + return( out ); +} + +// im_thresh: slice an image at a threshold +VImage VImage::thresh( double threshold ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_thresh" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((double*) _vec.data(2)) = threshold; + _vec.call(); + + return( output ); +} + +// im_tiff2vips: convert TIFF file to VIPS image +VImage VImage::tiff2vips( char* in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_tiff2vips" ); + + _vec.data(0) = (im_object) in; + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_vips2csv: write an image in csv format +void VImage::vips2csv( char* filename ) throw( VError ) +{ + VImage in = *this; + Vargv _vec( "im_vips2csv" ); + + _vec.data(0) = in.image(); + _vec.data(1) = (im_object) filename; + _vec.call(); +} + +// im_vips2jpeg: convert to jpeg +void VImage::vips2jpeg( char* out ) throw( VError ) +{ + VImage in = *this; + Vargv _vec( "im_vips2jpeg" ); + + _vec.data(0) = in.image(); + _vec.data(1) = (im_object) out; + _vec.call(); +} + +// im_vips2mask: convert VIPS image to DOUBLEMASK +VDMask VImage::vips2mask() throw( VError ) +{ + VImage input = *this; + VDMask output; + + Vargv _vec( "im_vips2mask" ); + + _vec.data(0) = input.image(); + ((im_mask_object*) _vec.data(1))->name = (char*)"noname"; + _vec.call(); + output.embed( (DOUBLEMASK *)((im_mask_object*)_vec.data(1))->mask ); + + return( output ); +} + +// im_vips2mimejpeg: convert to jpeg as mime type on stdout +void VImage::vips2mimejpeg( int qfac ) throw( VError ) +{ + VImage in = *this; + Vargv _vec( "im_vips2mimejpeg" ); + + _vec.data(0) = in.image(); + *((int*) _vec.data(1)) = qfac; + _vec.call(); +} + +// im_vips2png: convert VIPS image to PNG file +void VImage::vips2png( char* out ) throw( VError ) +{ + VImage in = *this; + Vargv _vec( "im_vips2png" ); + + _vec.data(0) = in.image(); + _vec.data(1) = (im_object) out; + _vec.call(); +} + +// im_vips2ppm: write a file in pbm/pgm/ppm format +void VImage::vips2ppm( char* filename ) throw( VError ) +{ + VImage im = *this; + Vargv _vec( "im_vips2ppm" ); + + _vec.data(0) = im.image(); + _vec.data(1) = (im_object) filename; + _vec.call(); +} + +// im_vips2tiff: convert VIPS image to TIFF file +void VImage::vips2tiff( char* out ) throw( VError ) +{ + VImage in = *this; + Vargv _vec( "im_vips2tiff" ); + + _vec.data(0) = in.image(); + _vec.data(1) = (im_object) out; + _vec.call(); +} + +// im_zoom: simple zoom of an image by integer factors +VImage VImage::zoom( int xfac, int yfac ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_zoom" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = xfac; + *((int*) _vec.data(3)) = yfac; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_addgnoise: add gaussian noise with mean 0 and std. dev. sigma +VImage VImage::addgnoise( double sigma ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_addgnoise" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = sigma; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_compass: convolve with 8-way rotating integer mask +VImage VImage::compass( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_compass" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_contrast_surface: find high-contrast points in an image +VImage VImage::contrast_surface( int half_win_size, int spacing ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_contrast_surface" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = half_win_size; + *((int*) _vec.data(3)) = spacing; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_contrast_surface_raw: find high-contrast points in an image +VImage VImage::contrast_surface_raw( int half_win_size, int spacing ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_contrast_surface_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = half_win_size; + *((int*) _vec.data(3)) = spacing; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_conv: convolve +VImage VImage::conv( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_conv" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_conv_raw: convolve, no border +VImage VImage::conv_raw( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_conv_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convf: convolve, with DOUBLEMASK +VImage VImage::convf( VDMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convf" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().dptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convf_raw: convolve, with DOUBLEMASK, no border +VImage VImage::convf_raw( VDMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convf_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().dptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convsep: seperable convolution +VImage VImage::convsep( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convsep" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convsep_raw: seperable convolution, no border +VImage VImage::convsep_raw( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convsep_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convsepf: seperable convolution, with DOUBLEMASK +VImage VImage::convsepf( VDMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convsepf" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().dptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convsepf_raw: seperable convolution, with DOUBLEMASK, no border +VImage VImage::convsepf_raw( VDMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convsepf_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().dptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convsub: convolve uchar to uchar, sub-sampling by xskip, yskip +VImage VImage::convsub( VIMask matrix, int xskip, int yskip ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convsub" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + *((int*) _vec.data(3)) = xskip; + *((int*) _vec.data(4)) = yskip; + _vec.call(); + + return( out ); +} + +// im_embed: embed in within a set of borders +VImage VImage::embed( int type, int x, int y, int w, int h ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_embed" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = type; + *((int*) _vec.data(3)) = x; + *((int*) _vec.data(4)) = y; + *((int*) _vec.data(5)) = w; + *((int*) _vec.data(6)) = h; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_fastcor: fast correlate in2 within in1 +VImage VImage::fastcor( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_fastcor" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_fastcor_raw: fast correlate in2 within in1, no border +VImage VImage::fastcor_raw( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_fastcor_raw" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_gaussnoise: generate image of gaussian noise with specified statistics +VImage VImage::gaussnoise( int xsize, int ysize, double mean, double sigma ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_gaussnoise" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = xsize; + *((int*) _vec.data(2)) = ysize; + *((double*) _vec.data(3)) = mean; + *((double*) _vec.data(4)) = sigma; + _vec.call(); + + return( out ); +} + +// im_grad_x: x component of gradient of image +VImage VImage::grad_x() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_grad_x" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_grad_y: y component of gradient of image +VImage VImage::grad_y() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_grad_y" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_gradcor: non-normalised correlation of gradient of in2 within in1 +VImage VImage::gradcor( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_gradcor" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_gradcor_raw: non-normalised correlation of gradient of in2 within in1, no padding +VImage VImage::gradcor_raw( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_gradcor_raw" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_gradient: convolve with 2-way rotating mask +VImage VImage::gradient( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_gradient" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_rank_image: point-wise pixel rank +VImage VImage::rank_image( std::vector in, int index ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_rank_image" ); + + ((im_imagevec_object*) _vec.data(0))->n = in.size(); + ((im_imagevec_object*) _vec.data(0))->vec = new IMAGE *[in.size()]; + for( unsigned int i = 0; i < in.size(); i++ ) + ((im_imagevec_object*) _vec.data(0))->vec[i] = in[i].image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = index; + _vec.call(); + for( unsigned int i = 0; i < in.size(); i++ ) + out._ref->addref( in[i]._ref ); + + return( out ); +} + +// im_lindetect: convolve with 4-way rotating mask +VImage VImage::lindetect( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lindetect" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_maxvalue: point-wise maximum value +VImage VImage::maxvalue( std::vector in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_maxvalue" ); + + ((im_imagevec_object*) _vec.data(0))->n = in.size(); + ((im_imagevec_object*) _vec.data(0))->vec = new IMAGE *[in.size()]; + for( unsigned int i = 0; i < in.size(); i++ ) + ((im_imagevec_object*) _vec.data(0))->vec[i] = in[i].image(); + _vec.data(1) = out.image(); + _vec.call(); + for( unsigned int i = 0; i < in.size(); i++ ) + out._ref->addref( in[i]._ref ); + + return( out ); +} + +// im_mpercent: find threshold above which there are percent values +int VImage::mpercent( double percent ) throw( VError ) +{ + VImage in = *this; + int thresh; + + Vargv _vec( "im_mpercent" ); + + _vec.data(0) = in.image(); + *((double*) _vec.data(1)) = percent; + _vec.call(); + thresh = *((int*)_vec.data(2)); + + return( thresh ); +} + +// im_rank: rank filter nth element of xsize/ysize window +VImage VImage::rank( int xsize, int ysize, int n ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rank" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = xsize; + *((int*) _vec.data(3)) = ysize; + *((int*) _vec.data(4)) = n; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_rank_raw: rank filter nth element of xsize/ysize window, no border +VImage VImage::rank_raw( int xsize, int ysize, int n ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rank_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = xsize; + *((int*) _vec.data(3)) = ysize; + *((int*) _vec.data(4)) = n; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_resize_linear: resize to X by Y pixels with linear interpolation +VImage VImage::resize_linear( int X, int Y ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_resize_linear" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = X; + *((int*) _vec.data(3)) = Y; + _vec.call(); + + return( out ); +} + +// im_sharpen: sharpen high frequencies of L channel of LabQ +VImage VImage::sharpen( int mask_size, double x1, double y2, double y3, double m1, double m2 ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_sharpen" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = mask_size; + *((double*) _vec.data(3)) = x1; + *((double*) _vec.data(4)) = y2; + *((double*) _vec.data(5)) = y3; + *((double*) _vec.data(6)) = m1; + *((double*) _vec.data(7)) = m2; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_shrink: shrink image by xfac, yfac times +VImage VImage::shrink( double xfac, double yfac ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_shrink" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = xfac; + *((double*) _vec.data(3)) = yfac; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_spcor: normalised correlation of in2 within in1 +VImage VImage::spcor( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_spcor" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_spcor_raw: normalised correlation of in2 within in1, no black padding +VImage VImage::spcor_raw( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_spcor_raw" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_spcor2: normalised correlation of in2 within in1 +VImage VImage::spcor2( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_spcor2" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_spcor2_raw: normalised correlation of in2 within in1, no black padding +VImage VImage::spcor2_raw( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_spcor2_raw" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_stretch3: stretch 3%, sub-pixel displace by xdisp/ydisp +VImage VImage::stretch3( double xdisp, double ydisp ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_stretch3" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = xdisp; + *((double*) _vec.data(3)) = ydisp; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_zerox: find +ve or -ve zero crossings in image +VImage VImage::zerox( int flag ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_zerox" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = flag; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_create_fmask: create frequency domain filter mask +VImage VImage::create_fmask( int width, int height, int type, double p1, double p2, double p3, double p4, double p5 ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_create_fmask" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = width; + *((int*) _vec.data(2)) = height; + *((int*) _vec.data(3)) = type; + *((double*) _vec.data(4)) = p1; + *((double*) _vec.data(5)) = p2; + *((double*) _vec.data(6)) = p3; + *((double*) _vec.data(7)) = p4; + *((double*) _vec.data(8)) = p5; + _vec.call(); + + return( out ); +} + +// im_disp_ps: make displayable power spectrum +VImage VImage::disp_ps() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_disp_ps" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_flt_image_freq: frequency domain filter image +VImage VImage::flt_image_freq( int type, double p1, double p2, double p3, double p4, double p5 ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_flt_image_freq" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = type; + *((double*) _vec.data(3)) = p1; + *((double*) _vec.data(4)) = p2; + *((double*) _vec.data(5)) = p3; + *((double*) _vec.data(6)) = p4; + *((double*) _vec.data(7)) = p5; + _vec.call(); + + return( out ); +} + +// im_fractsurf: generate a fractal surface of given dimension +VImage VImage::fractsurf( int size, double dimension ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_fractsurf" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = size; + *((double*) _vec.data(2)) = dimension; + _vec.call(); + + return( out ); +} + +// im_freqflt: frequency-domain filter of in with mask +VImage VImage::freqflt( VImage mask ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_freqflt" ); + + _vec.data(0) = in.image(); + _vec.data(1) = mask.image(); + _vec.data(2) = out.image(); + _vec.call(); + + return( out ); +} + +// im_fwfft: forward fast-fourier transform +VImage VImage::fwfft() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_fwfft" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_rotquad: rotate image quadrants to move origin to centre +VImage VImage::rotquad() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rotquad" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_invfft: inverse fast-fourier transform +VImage VImage::invfft() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_invfft" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_invfftr: real part of inverse fast-fourier transform +VImage VImage::invfftr() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_invfftr" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_gammacorrect: gamma-correct image +VImage VImage::gammacorrect( double exponent ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_gammacorrect" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = exponent; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_heq: histogram-equalise image +VImage VImage::heq( int band_number ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_heq" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = band_number; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_hist: find and graph histogram of image +VImage VImage::hist( int band_number ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_hist" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = band_number; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_histcum: turn histogram to cumulative histogram +VImage VImage::histcum() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histcum" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_histeq: form histogram equalistion LUT +VImage VImage::histeq() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histeq" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_histgr: find histogram of image +VImage VImage::histgr( int band_number ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histgr" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = band_number; + _vec.call(); + + return( out ); +} + +// im_histnD: find 1D, 2D or 3D histogram of image +VImage VImage::histnD( int bins ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histnD" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = bins; + _vec.call(); + + return( out ); +} + +// im_histnorm: form normalised histogram +VImage VImage::histnorm() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histnorm" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_histplot: plot graph of histogram +VImage VImage::histplot() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histplot" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_histspec: find histogram which will make pdf of in match ref +VImage VImage::histspec( VImage ref ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histspec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = ref.image(); + _vec.data(2) = out.image(); + _vec.call(); + + return( out ); +} + +// im_hsp: match stats of in to stats of ref +VImage VImage::hsp( VImage ref ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_hsp" ); + + _vec.data(0) = in.image(); + _vec.data(1) = ref.image(); + _vec.data(2) = out.image(); + _vec.call(); + + return( out ); +} + +// im_identity: generate identity histogram +VImage VImage::identity( int nbands ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_identity" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = nbands; + _vec.call(); + + return( out ); +} + +// im_identity_ushort: generate ushort identity histogram +VImage VImage::identity_ushort( int nbands, int size ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_identity_ushort" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = nbands; + *((int*) _vec.data(2)) = size; + _vec.call(); + + return( out ); +} + +// im_ismonotonic: test LUT for monotonicity +int VImage::ismonotonic() throw( VError ) +{ + VImage lut = *this; + int mono; + + Vargv _vec( "im_ismonotonic" ); + + _vec.data(0) = lut.image(); + _vec.call(); + mono = *((int*)_vec.data(1)); + + return( mono ); +} + +// im_lhisteq: local histogram equalisation +VImage VImage::lhisteq( int width, int height ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lhisteq" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = width; + *((int*) _vec.data(3)) = height; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lhisteq_raw: local histogram equalisation, no border +VImage VImage::lhisteq_raw( int width, int height ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lhisteq_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = width; + *((int*) _vec.data(3)) = height; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_invertlut: generate correction table from set of measures +VImage VImage::invertlut( VDMask measures, int lut_size ) throw( VError ) +{ + VImage lut; + + Vargv _vec( "im_invertlut" ); + + ((im_mask_object*) _vec.data(0))->mask = measures.mask().dptr; + _vec.data(1) = lut.image(); + *((int*) _vec.data(2)) = lut_size; + _vec.call(); + + return( lut ); +} + +// im_buildlut: generate LUT table from set of x/y positions +VImage VImage::buildlut( VDMask xyes ) throw( VError ) +{ + VImage lut; + + Vargv _vec( "im_buildlut" ); + + ((im_mask_object*) _vec.data(0))->mask = xyes.mask().dptr; + _vec.data(1) = lut.image(); + _vec.call(); + + return( lut ); +} + +// im_maplut: map image through LUT +VImage VImage::maplut( VImage lut ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_maplut" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = lut.image(); + _vec.call(); + out._ref->addref( in._ref ); + out._ref->addref( lut._ref ); + + return( out ); +} + +// im_project: find horizontal and vertical projections of an image +VImage VImage::project( VImage& vout ) throw( VError ) +{ + VImage in = *this; + VImage hout; + + Vargv _vec( "im_project" ); + + _vec.data(0) = in.image(); + _vec.data(1) = hout.image(); + _vec.data(2) = vout.image(); + _vec.call(); + + return( hout ); +} + +// im_stdif: statistical differencing +VImage VImage::stdif( double a, double m0, double b, double s0, int xw, int yw ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_stdif" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = a; + *((double*) _vec.data(3)) = m0; + *((double*) _vec.data(4)) = b; + *((double*) _vec.data(5)) = s0; + *((int*) _vec.data(6)) = xw; + *((int*) _vec.data(7)) = yw; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_stdif_raw: statistical differencing, no border +VImage VImage::stdif_raw( double a, double m0, double b, double s0, int xw, int yw ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_stdif_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = a; + *((double*) _vec.data(3)) = m0; + *((double*) _vec.data(4)) = b; + *((double*) _vec.data(5)) = s0; + *((int*) _vec.data(6)) = xw; + *((int*) _vec.data(7)) = yw; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_tone_analyse: analyse in and create LUT for tone adjustment +VImage VImage::tone_analyse( double Ps, double Pm, double Ph, double S, double M, double H ) throw( VError ) +{ + VImage in = *this; + VImage hist; + + Vargv _vec( "im_tone_analyse" ); + + _vec.data(0) = in.image(); + _vec.data(1) = hist.image(); + *((double*) _vec.data(2)) = Ps; + *((double*) _vec.data(3)) = Pm; + *((double*) _vec.data(4)) = Ph; + *((double*) _vec.data(5)) = S; + *((double*) _vec.data(6)) = M; + *((double*) _vec.data(7)) = H; + _vec.call(); + + return( hist ); +} + +// im_tone_build: create LUT for tone adjustment of LabS images +VImage VImage::tone_build( double Lb, double Lw, double Ps, double Pm, double Ph, double S, double M, double H ) throw( VError ) +{ + VImage hist; + + Vargv _vec( "im_tone_build" ); + + _vec.data(0) = hist.image(); + *((double*) _vec.data(1)) = Lb; + *((double*) _vec.data(2)) = Lw; + *((double*) _vec.data(3)) = Ps; + *((double*) _vec.data(4)) = Pm; + *((double*) _vec.data(5)) = Ph; + *((double*) _vec.data(6)) = S; + *((double*) _vec.data(7)) = M; + *((double*) _vec.data(8)) = H; + _vec.call(); + + return( hist ); +} + +// im_tone_build_range: create LUT for tone adjustment +VImage VImage::tone_build_range( int in_max, int out_max, double Lb, double Lw, double Ps, double Pm, double Ph, double S, double M, double H ) throw( VError ) +{ + VImage hist; + + Vargv _vec( "im_tone_build_range" ); + + _vec.data(0) = hist.image(); + *((int*) _vec.data(1)) = in_max; + *((int*) _vec.data(2)) = out_max; + *((double*) _vec.data(3)) = Lb; + *((double*) _vec.data(4)) = Lw; + *((double*) _vec.data(5)) = Ps; + *((double*) _vec.data(6)) = Pm; + *((double*) _vec.data(7)) = Ph; + *((double*) _vec.data(8)) = S; + *((double*) _vec.data(9)) = M; + *((double*) _vec.data(10)) = H; + _vec.call(); + + return( hist ); +} + +// im_tone_map: map L channel of LabS or LabQ image through LUT +VImage VImage::tone_map( VImage lut ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_tone_map" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = lut.image(); + _vec.call(); + out._ref->addref( in._ref ); + out._ref->addref( lut._ref ); + + return( out ); +} + +// im_circle: plot circle on image +void VImage::circle( int cx, int cy, int radius, int intensity ) throw( VError ) +{ + VImage image = *this; + Vargv _vec( "im_circle" ); + + _vec.data(0) = image.image(); + *((int*) _vec.data(1)) = cx; + *((int*) _vec.data(2)) = cy; + *((int*) _vec.data(3)) = radius; + *((int*) _vec.data(4)) = intensity; + _vec.call(); +} + +// im_flood_blob_copy: flood with ink from start_x, start_y while pixel == start pixel +VImage VImage::flood_blob_copy( int start_x, int start_y, std::vector ink ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_flood_blob_copy" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = start_x; + *((int*) _vec.data(3)) = start_y; + ((im_doublevec_object*) _vec.data(4))->n = ink.size(); + ((im_doublevec_object*) _vec.data(4))->vec = new double[ink.size()]; + for( unsigned int i = 0; i < ink.size(); i++ ) + ((im_doublevec_object*) _vec.data(4))->vec[i] = ink[i]; + _vec.call(); + + return( out ); +} + +// im_insertplace: draw image sub inside image main at position (x,y) +void VImage::insertplace( VImage sub, int x, int y ) throw( VError ) +{ + VImage main = *this; + Vargv _vec( "im_insertplace" ); + + _vec.data(0) = main.image(); + _vec.data(1) = sub.image(); + *((int*) _vec.data(2)) = x; + *((int*) _vec.data(3)) = y; + _vec.call(); +} + +// im_line: draw line between points (x1,y1) and (x2,y2) +void VImage::line( int x1, int y1, int x2, int y2, int pelval ) throw( VError ) +{ + VImage im = *this; + Vargv _vec( "im_line" ); + + _vec.data(0) = im.image(); + *((int*) _vec.data(1)) = x1; + *((int*) _vec.data(2)) = y1; + *((int*) _vec.data(3)) = x2; + *((int*) _vec.data(4)) = y2; + *((int*) _vec.data(5)) = pelval; + _vec.call(); +} + +// im_lineset: draw line between points (x1,y1) and (x2,y2) +VImage VImage::lineset( VImage mask, VImage ink, std::vector x1, std::vector y1, std::vector x2, std::vector y2 ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lineset" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = mask.image(); + _vec.data(3) = ink.image(); + ((im_intvec_object*) _vec.data(4))->n = x1.size(); + ((im_intvec_object*) _vec.data(4))->vec = new int[x1.size()]; + for( unsigned int i = 0; i < x1.size(); i++ ) + ((im_intvec_object*) _vec.data(4))->vec[i] = x1[i]; + ((im_intvec_object*) _vec.data(5))->n = y1.size(); + ((im_intvec_object*) _vec.data(5))->vec = new int[y1.size()]; + for( unsigned int i = 0; i < y1.size(); i++ ) + ((im_intvec_object*) _vec.data(5))->vec[i] = y1[i]; + ((im_intvec_object*) _vec.data(6))->n = x2.size(); + ((im_intvec_object*) _vec.data(6))->vec = new int[x2.size()]; + for( unsigned int i = 0; i < x2.size(); i++ ) + ((im_intvec_object*) _vec.data(6))->vec[i] = x2[i]; + ((im_intvec_object*) _vec.data(7))->n = y2.size(); + ((im_intvec_object*) _vec.data(7))->vec = new int[y2.size()]; + for( unsigned int i = 0; i < y2.size(); i++ ) + ((im_intvec_object*) _vec.data(7))->vec[i] = y2[i]; + _vec.call(); + + return( out ); +} + +// im_binfile: open a headerless binary file +VImage VImage::binfile( char* filename, int width, int height, int bands, int offset ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_binfile" ); + + _vec.data(0) = (im_object) filename; + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = width; + *((int*) _vec.data(3)) = height; + *((int*) _vec.data(4)) = bands; + *((int*) _vec.data(5)) = offset; + _vec.call(); + + return( out ); +} + +// im_cache: cache results of an operation +VImage VImage::cache( int tile_width, int tile_height, int max_tiles ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_cache" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = tile_width; + *((int*) _vec.data(3)) = tile_height; + *((int*) _vec.data(4)) = max_tiles; + _vec.call(); + + return( out ); +} + +// im_header_get_type: return field type +int VImage::header_get_type( char* field ) throw( VError ) +{ + VImage image = *this; + int gtype; + + Vargv _vec( "im_header_get_type" ); + + _vec.data(0) = (im_object) field; + _vec.data(1) = image.image(); + _vec.call(); + gtype = *((int*)_vec.data(2)); + + return( gtype ); +} + +// im_header_int: extract int fields from header +int VImage::header_int( char* field ) throw( VError ) +{ + VImage image = *this; + int value; + + Vargv _vec( "im_header_int" ); + + _vec.data(0) = (im_object) field; + _vec.data(1) = image.image(); + _vec.call(); + value = *((int*)_vec.data(2)); + + return( value ); +} + +// im_header_double: extract double fields from header +double VImage::header_double( char* field ) throw( VError ) +{ + VImage image = *this; + double value; + + Vargv _vec( "im_header_double" ); + + _vec.data(0) = (im_object) field; + _vec.data(1) = image.image(); + _vec.call(); + value = *((double*)_vec.data(2)); + + return( value ); +} + +// im_header_string: extract string fields from header +char* VImage::header_string( char* field ) throw( VError ) +{ + VImage image = *this; + char* value; + + Vargv _vec( "im_header_string" ); + + _vec.data(0) = (im_object) field; + _vec.data(1) = image.image(); + _vec.call(); + value = (char*) _vec.data(2); + + return( value ); +} + +// im_cntlines: count horizontal or vertical lines +double VImage::cntlines( int direction ) throw( VError ) +{ + VImage in = *this; + double nlines; + + Vargv _vec( "im_cntlines" ); + + _vec.data(0) = in.image(); + *((int*) _vec.data(2)) = direction; + _vec.call(); + nlines = *((double*)_vec.data(1)); + + return( nlines ); +} + +// im_dilate: dilate image with mask, adding a black border +VImage VImage::dilate( VIMask mask ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_dilate" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = mask.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_dilate_raw: dilate image with mask +VImage VImage::dilate_raw( VIMask mask ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_dilate_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = mask.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_erode: erode image with mask, adding a black border +VImage VImage::erode( VIMask mask ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_erode" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = mask.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_erode_raw: erode image with mask +VImage VImage::erode_raw( VIMask mask ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_erode_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = mask.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_profile: find first horizontal/vertical edge +VImage VImage::profile( int direction ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_profile" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = direction; + _vec.call(); + + return( out ); +} + +// im_affine: affine transform +VImage VImage::affine( double a, double b, double c, double d, double dx, double dy, int x, int y, int w, int h ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_affine" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = a; + *((double*) _vec.data(3)) = b; + *((double*) _vec.data(4)) = c; + *((double*) _vec.data(5)) = d; + *((double*) _vec.data(6)) = dx; + *((double*) _vec.data(7)) = dy; + *((int*) _vec.data(8)) = x; + *((int*) _vec.data(9)) = y; + *((int*) _vec.data(10)) = w; + *((int*) _vec.data(11)) = h; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_correl: search area around sec for match for area around ref +double VImage::correl( VImage sec, int xref, int yref, int xsec, int ysec, int hwindowsize, int hsearchsize, int& x, int& y ) throw( VError ) +{ + VImage ref = *this; + double correlation; + + Vargv _vec( "im_correl" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + *((int*) _vec.data(2)) = xref; + *((int*) _vec.data(3)) = yref; + *((int*) _vec.data(4)) = xsec; + *((int*) _vec.data(5)) = ysec; + *((int*) _vec.data(6)) = hwindowsize; + *((int*) _vec.data(7)) = hsearchsize; + _vec.call(); + correlation = *((double*)_vec.data(8)); + x = *((int*)_vec.data(9)); + y = *((int*)_vec.data(10)); + + return( correlation ); +} + +// im__find_lroverlap: search for left-right overlap of ref and sec +int VImage::_find_lroverlap( VImage sec, int bandno, int xr, int yr, int xs, int ys, int halfcorrelation, int halfarea, int& dy0, double& scale1, double& angle1, double& dx1, double& dy1 ) throw( VError ) +{ + VImage ref = *this; + int dx0; + + Vargv _vec( "im__find_lroverlap" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + *((int*) _vec.data(2)) = bandno; + *((int*) _vec.data(3)) = xr; + *((int*) _vec.data(4)) = yr; + *((int*) _vec.data(5)) = xs; + *((int*) _vec.data(6)) = ys; + *((int*) _vec.data(7)) = halfcorrelation; + *((int*) _vec.data(8)) = halfarea; + _vec.call(); + dx0 = *((int*)_vec.data(9)); + dy0 = *((int*)_vec.data(10)); + scale1 = *((double*)_vec.data(11)); + angle1 = *((double*)_vec.data(12)); + dx1 = *((double*)_vec.data(13)); + dy1 = *((double*)_vec.data(14)); + + return( dx0 ); +} + +// im__find_tboverlap: search for top-bottom overlap of ref and sec +int VImage::_find_tboverlap( VImage sec, int bandno, int xr, int yr, int xs, int ys, int halfcorrelation, int halfarea, int& dy0, double& scale1, double& angle1, double& dx1, double& dy1 ) throw( VError ) +{ + VImage ref = *this; + int dx0; + + Vargv _vec( "im__find_tboverlap" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + *((int*) _vec.data(2)) = bandno; + *((int*) _vec.data(3)) = xr; + *((int*) _vec.data(4)) = yr; + *((int*) _vec.data(5)) = xs; + *((int*) _vec.data(6)) = ys; + *((int*) _vec.data(7)) = halfcorrelation; + *((int*) _vec.data(8)) = halfarea; + _vec.call(); + dx0 = *((int*)_vec.data(9)); + dy0 = *((int*)_vec.data(10)); + scale1 = *((double*)_vec.data(11)); + angle1 = *((double*)_vec.data(12)); + dx1 = *((double*)_vec.data(13)); + dy1 = *((double*)_vec.data(14)); + + return( dx0 ); +} + +// im_global_balance: automatically rebuild mosaic with balancing +VImage VImage::global_balance( double gamma ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_global_balance" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = gamma; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_global_balancef: automatically rebuild mosaic with balancing, float output +VImage VImage::global_balancef( double gamma ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_global_balancef" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = gamma; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lrmerge: left-right merge of in1 and in2 +VImage VImage::lrmerge( VImage sec, int dx, int dy, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_lrmerge" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = dx; + *((int*) _vec.data(4)) = dy; + *((int*) _vec.data(5)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_lrmerge1: first-order left-right merge of ref and sec +VImage VImage::lrmerge1( VImage sec, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_lrmerge1" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = xr1; + *((int*) _vec.data(4)) = yr1; + *((int*) _vec.data(5)) = xs1; + *((int*) _vec.data(6)) = ys1; + *((int*) _vec.data(7)) = xr2; + *((int*) _vec.data(8)) = yr2; + *((int*) _vec.data(9)) = xs2; + *((int*) _vec.data(10)) = ys2; + *((int*) _vec.data(11)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_lrmosaic: left-right mosaic of ref and sec +VImage VImage::lrmosaic( VImage sec, int bandno, int xr, int yr, int xs, int ys, int halfcorrelation, int halfarea, int balancetype, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_lrmosaic" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = bandno; + *((int*) _vec.data(4)) = xr; + *((int*) _vec.data(5)) = yr; + *((int*) _vec.data(6)) = xs; + *((int*) _vec.data(7)) = ys; + *((int*) _vec.data(8)) = halfcorrelation; + *((int*) _vec.data(9)) = halfarea; + *((int*) _vec.data(10)) = balancetype; + *((int*) _vec.data(11)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_lrmosaic1: first-order left-right mosaic of ref and sec +VImage VImage::lrmosaic1( VImage sec, int bandno, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, int halfcorrelation, int halfarea, int balancetype, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_lrmosaic1" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = bandno; + *((int*) _vec.data(4)) = xr1; + *((int*) _vec.data(5)) = yr1; + *((int*) _vec.data(6)) = xs1; + *((int*) _vec.data(7)) = ys1; + *((int*) _vec.data(8)) = xr2; + *((int*) _vec.data(9)) = yr2; + *((int*) _vec.data(10)) = xs2; + *((int*) _vec.data(11)) = ys2; + *((int*) _vec.data(12)) = halfcorrelation; + *((int*) _vec.data(13)) = halfarea; + *((int*) _vec.data(14)) = balancetype; + *((int*) _vec.data(15)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_match_linear: resample ref so that tie-points match +VImage VImage::match_linear( VImage sec, int xref1, int yref1, int xsec1, int ysec1, int xref2, int yref2, int xsec2, int ysec2 ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_match_linear" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = xref1; + *((int*) _vec.data(4)) = yref1; + *((int*) _vec.data(5)) = xsec1; + *((int*) _vec.data(6)) = ysec1; + *((int*) _vec.data(7)) = xref2; + *((int*) _vec.data(8)) = yref2; + *((int*) _vec.data(9)) = xsec2; + *((int*) _vec.data(10)) = ysec2; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_match_linear_search: search sec, then resample so that tie-points match +VImage VImage::match_linear_search( VImage sec, int xref1, int yref1, int xsec1, int ysec1, int xref2, int yref2, int xsec2, int ysec2, int hwindowsize, int hsearchsize ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_match_linear_search" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = xref1; + *((int*) _vec.data(4)) = yref1; + *((int*) _vec.data(5)) = xsec1; + *((int*) _vec.data(6)) = ysec1; + *((int*) _vec.data(7)) = xref2; + *((int*) _vec.data(8)) = yref2; + *((int*) _vec.data(9)) = xsec2; + *((int*) _vec.data(10)) = ysec2; + *((int*) _vec.data(11)) = hwindowsize; + *((int*) _vec.data(12)) = hsearchsize; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_remosaic: automatically rebuild mosaic with new files +VImage VImage::remosaic( char* old_str, char* new_str ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_remosaic" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = (im_object) old_str; + _vec.data(3) = (im_object) new_str; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_similarity_area: output area xywh of similarity transformation +VImage VImage::similarity_area( double a, double b, double dx, double dy, int x, int y, int w, int h ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_similarity_area" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = a; + *((double*) _vec.data(3)) = b; + *((double*) _vec.data(4)) = dx; + *((double*) _vec.data(5)) = dy; + *((int*) _vec.data(6)) = x; + *((int*) _vec.data(7)) = y; + *((int*) _vec.data(8)) = w; + *((int*) _vec.data(9)) = h; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_similarity: similarity transformation +VImage VImage::similarity( double a, double b, double dx, double dy ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_similarity" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = a; + *((double*) _vec.data(3)) = b; + *((double*) _vec.data(4)) = dx; + *((double*) _vec.data(5)) = dy; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_tbmerge: top-bottom merge of in1 and in2 +VImage VImage::tbmerge( VImage sec, int dx, int dy, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_tbmerge" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = dx; + *((int*) _vec.data(4)) = dy; + *((int*) _vec.data(5)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_tbmerge1: first-order top-bottom merge of in1 and in2 +VImage VImage::tbmerge1( VImage sec, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_tbmerge1" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = xr1; + *((int*) _vec.data(4)) = yr1; + *((int*) _vec.data(5)) = xs1; + *((int*) _vec.data(6)) = ys1; + *((int*) _vec.data(7)) = xr2; + *((int*) _vec.data(8)) = yr2; + *((int*) _vec.data(9)) = xs2; + *((int*) _vec.data(10)) = ys2; + *((int*) _vec.data(11)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_tbmosaic: top-bottom mosaic of in1 and in2 +VImage VImage::tbmosaic( VImage sec, int bandno, int xr, int yr, int xs, int ys, int halfcorrelation, int halfarea, int balancetype, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_tbmosaic" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = bandno; + *((int*) _vec.data(4)) = xr; + *((int*) _vec.data(5)) = yr; + *((int*) _vec.data(6)) = xs; + *((int*) _vec.data(7)) = ys; + *((int*) _vec.data(8)) = halfcorrelation; + *((int*) _vec.data(9)) = halfarea; + *((int*) _vec.data(10)) = balancetype; + *((int*) _vec.data(11)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_tbmosaic1: first-order top-bottom mosaic of ref and sec +VImage VImage::tbmosaic1( VImage sec, int bandno, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, int halfcorrelation, int halfarea, int balancetype, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_tbmosaic1" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = bandno; + *((int*) _vec.data(4)) = xr1; + *((int*) _vec.data(5)) = yr1; + *((int*) _vec.data(6)) = xs1; + *((int*) _vec.data(7)) = ys1; + *((int*) _vec.data(8)) = xr2; + *((int*) _vec.data(9)) = yr2; + *((int*) _vec.data(10)) = xs2; + *((int*) _vec.data(11)) = ys2; + *((int*) _vec.data(12)) = halfcorrelation; + *((int*) _vec.data(13)) = halfarea; + *((int*) _vec.data(14)) = balancetype; + *((int*) _vec.data(15)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_benchmark: do something complicated for testing +VImage VImage::benchmark() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_benchmark" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_benchmark2: do something complicated for testing +double VImage::benchmark2() throw( VError ) +{ + VImage in = *this; + double value; + + Vargv _vec( "im_benchmark2" ); + + _vec.data(0) = in.image(); + _vec.call(); + value = *((double*)_vec.data(1)); + + return( value ); +} + +// im_benchmarkn: do something complicated for testing +VImage VImage::benchmarkn( int n ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_benchmarkn" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = n; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_eye: generate IM_BANDFMT_UCHAR [0,255] frequency/amplitude image +VImage VImage::eye( int xsize, int ysize, double factor ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_eye" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = xsize; + *((int*) _vec.data(2)) = ysize; + *((double*) _vec.data(3)) = factor; + _vec.call(); + + return( out ); +} + +// im_grey: generate IM_BANDFMT_UCHAR [0,255] grey scale image +VImage VImage::grey( int xsize, int ysize ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_grey" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = xsize; + *((int*) _vec.data(2)) = ysize; + _vec.call(); + + return( out ); +} + +// im_feye: generate IM_BANDFMT_FLOAT [-1,1] frequency/amplitude image +VImage VImage::feye( int xsize, int ysize, double factor ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_feye" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = xsize; + *((int*) _vec.data(2)) = ysize; + *((double*) _vec.data(3)) = factor; + _vec.call(); + + return( out ); +} + +// im_fgrey: generate IM_BANDFMT_FLOAT [0,1] grey scale image +VImage VImage::fgrey( int xsize, int ysize ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_fgrey" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = xsize; + *((int*) _vec.data(2)) = ysize; + _vec.call(); + + return( out ); +} + +// im_fzone: generate IM_BANDFMT_FLOAT [-1,1] zone plate image +VImage VImage::fzone( int size ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_fzone" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = size; + _vec.call(); + + return( out ); +} + +// im_make_xy: generate image with pixel value equal to coordinate +VImage VImage::make_xy( int xsize, int ysize ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_make_xy" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = xsize; + *((int*) _vec.data(2)) = ysize; + _vec.call(); + + return( out ); +} + +// im_zone: generate IM_BANDFMT_UCHAR [0,255] zone plate image +VImage VImage::zone( int size ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_zone" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = size; + _vec.call(); + + return( out ); +} + +// im_blend: use cond image to blend between images in1 and in2 +VImage VImage::blend( VImage in1, VImage in2 ) throw( VError ) +{ + VImage cond = *this; + VImage out; + + Vargv _vec( "im_blend" ); + + _vec.data(0) = cond.image(); + _vec.data(1) = in1.image(); + _vec.data(2) = in2.image(); + _vec.data(3) = out.image(); + _vec.call(); + out._ref->addref( cond._ref ); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_equal: two images equal in value +VImage VImage::equal( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_equal" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_equal_vec: image equals doublevec +VImage VImage::equal( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_equal_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_equalconst: image equals const +VImage VImage::equal( double c ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_equalconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_ifthenelse: use cond image to choose pels from image in1 or in2 +VImage VImage::ifthenelse( VImage in1, VImage in2 ) throw( VError ) +{ + VImage cond = *this; + VImage out; + + Vargv _vec( "im_ifthenelse" ); + + _vec.data(0) = cond.image(); + _vec.data(1) = in1.image(); + _vec.data(2) = in2.image(); + _vec.data(3) = out.image(); + _vec.call(); + out._ref->addref( cond._ref ); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_less: in1 less than in2 in value +VImage VImage::less( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_less" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_less_vec: in less than doublevec +VImage VImage::less( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_less_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lessconst: in less than const +VImage VImage::less( double c ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lessconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lesseq: in1 less than or equal to in2 in value +VImage VImage::lesseq( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_lesseq" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_lesseq_vec: in less than or equal to doublevec +VImage VImage::lesseq( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lesseq_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lesseqconst: in less than or equal to const +VImage VImage::lesseq( double c ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lesseqconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_more: in1 more than in2 in value +VImage VImage::more( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_more" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_more_vec: in more than doublevec +VImage VImage::more( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_more_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_moreconst: in more than const +VImage VImage::more( double c ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_moreconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_moreeq: in1 more than or equal to in2 in value +VImage VImage::moreeq( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_moreeq" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_moreeq_vec: in more than or equal to doublevec +VImage VImage::moreeq( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_moreeq_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_moreeqconst: in more than or equal to const +VImage VImage::moreeq( double c ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_moreeqconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_notequal: two images not equal in value +VImage VImage::notequal( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_notequal" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_notequal_vec: image does not equal doublevec +VImage VImage::notequal( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_notequal_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_notequalconst: image does not equal const +VImage VImage::notequal( double c ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_notequalconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_video_test: test video grabber +VImage VImage::video_test( int brightness, int error ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_video_test" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = brightness; + *((int*) _vec.data(2)) = error; + _vec.call(); + + return( out ); +} + +// im_video_v4l1: grab a video frame with v4l1 +VImage VImage::video_v4l1( char* device, int channel, int brightness, int colour, int contrast, int hue, int ngrabs ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_video_v4l1" ); + + _vec.data(0) = out.image(); + _vec.data(1) = (im_object) device; + *((int*) _vec.data(2)) = channel; + *((int*) _vec.data(3)) = brightness; + *((int*) _vec.data(4)) = colour; + *((int*) _vec.data(5)) = contrast; + *((int*) _vec.data(6)) = hue; + *((int*) _vec.data(7)) = ngrabs; + _vec.call(); + + return( out ); +} + diff --git a/po/ChangeLog b/po/ChangeLog new file mode 100644 index 00000000..dbf75fcd --- /dev/null +++ b/po/ChangeLog @@ -0,0 +1 @@ +started 17 dec 03 diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 00000000..ad372f35 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,318 @@ +src/mosaicing/find_mosaic.c +src/mosaicing/mergeup.c +src/other/spatres.c +src/other/squares.c +src/other/simcontr.c +src/other/glds_features.c +src/other/glds.c +src/other/sines.c +src/other/cooc.c +src/other/cooc_features.c +src/iofuncs/binfile.c +src/iofuncs/vips.c +src/iofuncs/debugim.c +src/iofuncs/header.c +src/iofuncs/edvips.c +src/iofuncs/printlines.c +contrib/vdump/vdump.c +contrib/vips2dj/vips2ah.c +contrib/vips2dj/vips2dj.c +contrib/mitsub/mitsub.c +libsrc/histograms_lut/im_invertlut.c +libsrc/histograms_lut/hist_dispatch.c +libsrc/histograms_lut/im_project.c +libsrc/histograms_lut/im_hist.c +libsrc/histograms_lut/im_histspec.c +libsrc/histograms_lut/im_stdif.c +libsrc/histograms_lut/im_maplut.c +libsrc/histograms_lut/im_histgr.c +libsrc/histograms_lut/im_identity.c +libsrc/histograms_lut/im_histnD.c +libsrc/histograms_lut/tone.c +libsrc/histograms_lut/im_histplot.c +libsrc/histograms_lut/im_gammacorrect.c +libsrc/histograms_lut/im_heq.c +libsrc/histograms_lut/im_histeq.c +libsrc/histograms_lut/im_lhisteq.c +libsrc/histograms_lut/im_buildlut.c +libsrc/histograms_lut/im_hsp.c +libsrc/mosaicing/im_tbmerge.c +libsrc/mosaicing/im_affine.c +libsrc/mosaicing/im_lrmosaic.c +libsrc/mosaicing/im_chkpair.c +libsrc/mosaicing/global_balance.c +libsrc/mosaicing/im_tbmosaic.c +libsrc/mosaicing/im_improve.c +libsrc/mosaicing/im_avgdxdy.c +libsrc/mosaicing/mosaic1.c +libsrc/mosaicing/im_remosaic.c +libsrc/mosaicing/mosaicing_dispatch.c +libsrc/mosaicing/im_lrcalcon.c +libsrc/mosaicing/im_lrmerge.c +libsrc/mosaicing/im_initialize.c +libsrc/mosaicing/similarity.c +libsrc/mosaicing/im_tbcalcon.c +libsrc/mosaicing/match.c +libsrc/mosaicing/im_clinear.c +libsrc/matrix/im_mattrn.c +libsrc/matrix/im_matcat.c +libsrc/matrix/matalloc.c +libsrc/matrix/matrix_dispatch.c +libsrc/matrix/im_matinv.c +libsrc/matrix/im_matmul.c +libsrc/convolution/im_contrast_surface.c +libsrc/convolution/im_zerox.c +libsrc/convolution/im_rank_image.c +libsrc/convolution/im_compass.c +libsrc/convolution/rw_mask.c +libsrc/convolution/im_resize_linear.c +libsrc/convolution/im_sharpen.c +libsrc/convolution/im_spcor.c +libsrc/convolution/im_shrink.c +libsrc/convolution/im_gaussnoise.c +libsrc/convolution/rotmask.c +libsrc/convolution/im_gradcor.c +libsrc/convolution/im_rank.c +libsrc/convolution/im_mpercent.c +libsrc/convolution/im_convsepf.c +libsrc/convolution/convol_dispatch.c +libsrc/convolution/im_embed.c +libsrc/convolution/im_stretch3.c +libsrc/convolution/im_convsub.c +libsrc/convolution/im_convsep.c +libsrc/convolution/im_convf.c +libsrc/convolution/im_gaussmasks.c +libsrc/convolution/im_fastcor.c +libsrc/convolution/im_conv.c +libsrc/convolution/im_logmasks.c +libsrc/convolution/im_addgnoise.c +libsrc/acquire/im_clamp.c +libsrc/other/im_simcontr.c +libsrc/other/im_zone.c +libsrc/other/im_spatres.c +libsrc/other/cooc_funcs.c +libsrc/other/im_benchmark.c +libsrc/other/other_dispatch.c +libsrc/other/glds_funcs.c +libsrc/other/im_meanstd.c +libsrc/other/im_dif_std.c +libsrc/other/im_make_xy.c +libsrc/other/im_eye.c +libsrc/other/im_sines.c +libsrc/other/im_grey.c +libsrc/iofuncs/im_setbox.c +libsrc/iofuncs/rect.c +libsrc/iofuncs/im_open.c +libsrc/iofuncs/im_writeline.c +libsrc/iofuncs/util.c +libsrc/iofuncs/im_iocheck.c +libsrc/iofuncs/im_mapfile.c +libsrc/iofuncs/package.c +libsrc/iofuncs/vbuf.c +libsrc/iofuncs/window.c +libsrc/iofuncs/im_wrapmany.c +libsrc/iofuncs/im_setbuf.c +libsrc/iofuncs/meta.c +libsrc/iofuncs/im_desc_hd.c +libsrc/iofuncs/im_prepare.c +libsrc/iofuncs/im_openin.c +libsrc/iofuncs/im_init_world.c +libsrc/iofuncs/im_readhist.c +libsrc/iofuncs/debug.c +libsrc/iofuncs/callback.c +libsrc/iofuncs/im_iterate.c +libsrc/iofuncs/im_cp_desc.c +libsrc/iofuncs/im_unmapfile.c +libsrc/iofuncs/im_close.c +libsrc/iofuncs/im_image.c +libsrc/iofuncs/im_init.c +libsrc/iofuncs/memory.c +libsrc/iofuncs/buffer.c +libsrc/iofuncs/im_render.c +libsrc/iofuncs/time.c +libsrc/iofuncs/im_openout.c +libsrc/iofuncs/im_generate.c +libsrc/iofuncs/im_histlin.c +libsrc/iofuncs/im_header.c +libsrc/iofuncs/threadgroup.c +libsrc/iofuncs/im_guess_prefix.c +libsrc/iofuncs/im_demand_hint.c +libsrc/iofuncs/error.c +libsrc/iofuncs/im_makerw.c +libsrc/iofuncs/im_printdesc.c +libsrc/iofuncs/im_piocheck.c +libsrc/iofuncs/base64.c +libsrc/iofuncs/predicate.c +libsrc/iofuncs/semaphore.c +libsrc/iofuncs/im_binfile.c +libsrc/iofuncs/im_debugim.c +libsrc/iofuncs/im_printlines.c +libsrc/iofuncs/im_partial.c +libsrc/iofuncs/im_setupout.c +libsrc/iofuncs/im_updatehist.c +libsrc/iofuncs/im_bits_of_fmt.c +libsrc/iofuncs/im_initdesc.c +libsrc/iofuncs/error_exit.c +libsrc/iofuncs/dispatch_types.c +libsrc/iofuncs/region.c +libsrc/iofuncs/im_wrapone.c +libsrc/morphology/im_profile.c +libsrc/morphology/morph_dispatch.c +libsrc/morphology/im_dilate.c +libsrc/morphology/im_cntlines.c +libsrc/morphology/im_erode.c +libsrc/video/video_dispatch.c +libsrc/video/im_video_v4l1.c +libsrc/video/im_video_test.c +libsrc/dummy.c +libsrc/relational/im_ifthenelse.c +libsrc/relational/relational_dispatch.c +libsrc/relational/relational.c +libsrc/relational/im_blend.c +libsrc/arithmetic/im_cmulnorm.c +libsrc/arithmetic/im_litecor.c +libsrc/arithmetic/im_logtra.c +libsrc/arithmetic/im_gadd.c +libsrc/arithmetic/im_sign.c +libsrc/arithmetic/im_minpos.c +libsrc/arithmetic/im_maxpos_avg.c +libsrc/arithmetic/im_subtract.c +libsrc/arithmetic/im_max.c +libsrc/arithmetic/im_remainder.c +libsrc/arithmetic/im_gfadd.c +libsrc/arithmetic/im_multiply.c +libsrc/arithmetic/im_linreg.c +libsrc/arithmetic/im_gaddim.c +libsrc/arithmetic/im_abs.c +libsrc/arithmetic/im_ceil.c +libsrc/arithmetic/im_avg.c +libsrc/arithmetic/im_bandmean.c +libsrc/arithmetic/im_add.c +libsrc/arithmetic/im_min.c +libsrc/arithmetic/im_deviate.c +libsrc/arithmetic/im_stats.c +libsrc/arithmetic/im_maxpos_vec.c +libsrc/arithmetic/im_lintra.c +libsrc/arithmetic/im_measure.c +libsrc/arithmetic/im_log10tra.c +libsrc/arithmetic/im_powtra.c +libsrc/arithmetic/im_costra.c +libsrc/arithmetic/im_maxpos.c +libsrc/arithmetic/im_invert.c +libsrc/arithmetic/im_rint.c +libsrc/arithmetic/im_sintra.c +libsrc/arithmetic/arith_dispatch.c +libsrc/arithmetic/im_point_bilinear.c +libsrc/arithmetic/im_expntra.c +libsrc/arithmetic/im_tantra.c +libsrc/arithmetic/im_divide.c +libsrc/arithmetic/im_fav4.c +libsrc/arithmetic/im_floor.c +libsrc/boolean/boolean.c +libsrc/boolean/bool_dispatch.c +libsrc/inplace/im_flood.c +libsrc/inplace/inplace_dispatch.c +libsrc/inplace/im_paintrect.c +libsrc/inplace/smudge_area.c +libsrc/inplace/im_insertplace.c +libsrc/inplace/im_plotmask.c +libsrc/inplace/plot_point.c +libsrc/inplace/im_circle.c +libsrc/inplace/line_draw.c +libsrc/inplace/im_line.c +libsrc/colour/im_dE_fromLab.c +libsrc/colour/im_Lab2LCh.c +libsrc/colour/im_dECMC_fromLab.c +libsrc/colour/im_XYZ2Yxy.c +libsrc/colour/derived.c +libsrc/colour/im_disp2XYZ.c +libsrc/colour/im_LabQ2Lab.c +libsrc/colour/im_Lab2LabQ.c +libsrc/colour/im_XYZ2disp.c +libsrc/colour/im_lab_morph.c +libsrc/colour/im_dE00_fromLab.c +libsrc/colour/im_Lab2XYZ.c +libsrc/colour/colour_dispatch.c +libsrc/colour/colour.c +libsrc/colour/im_LabQ2disp.c +libsrc/colour/im_Yxy2XYZ.c +libsrc/colour/im_XYZ2Lab.c +libsrc/colour/im_LCh2Lab.c +libsrc/colour/im_LabS2LabQ.c +libsrc/colour/im_LabQ2LabS.c +libsrc/colour/im_UCS2LCh.c +libsrc/colour/im_Lab2LabS.c +libsrc/colour/im_icc_transform.c +libsrc/colour/im_LCh2UCS.c +libsrc/colour/im_LabS2Lab.c +libsrc/conversion/im_recomb.c +libsrc/conversion/im_tbjoin.c +libsrc/conversion/im_flipver.c +libsrc/conversion/im_magick2vips.c +libsrc/conversion/im_c2rect.c +libsrc/conversion/im_rot90.c +libsrc/conversion/im_vips2csv.c +libsrc/conversion/im_c2ps.c +libsrc/conversion/im_rightshift_size.c +libsrc/conversion/im_csv2vips.c +libsrc/conversion/im_insert.c +libsrc/conversion/im_c2imag.c +libsrc/conversion/im_falsecolour.c +libsrc/conversion/im_text.c +libsrc/conversion/conver_dispatch.c +libsrc/conversion/im_bandjoin.c +libsrc/conversion/im_analyze2vips.c +libsrc/conversion/im_vips2tiff.c +libsrc/conversion/im_slice.c +libsrc/conversion/im_rot180.c +libsrc/conversion/im_scale.c +libsrc/conversion/im_black.c +libsrc/conversion/im_ppm2vips.c +libsrc/conversion/im_c2real.c +libsrc/conversion/im_scaleps.c +libsrc/conversion/im_msb.c +libsrc/conversion/im_ri2c.c +libsrc/conversion/im_print.c +libsrc/conversion/im_system.c +libsrc/conversion/im_c2amph.c +libsrc/conversion/im_bernd.c +libsrc/conversion/im_lrjoin.c +libsrc/conversion/im_tile_cache.c +libsrc/conversion/im_jpeg2vips.c +libsrc/conversion/im_copy.c +libsrc/conversion/im_extract.c +libsrc/conversion/im_thresh.c +libsrc/conversion/im_zoom.c +libsrc/conversion/im_png2vips.c +libsrc/conversion/im_clip.c +libsrc/conversion/im_vips2mask.c +libsrc/conversion/im_vips2ppm.c +libsrc/conversion/im_replicate.c +libsrc/conversion/im_rot270.c +libsrc/conversion/im_grid.c +libsrc/conversion/im_vips2png.c +libsrc/conversion/im_fliphor.c +libsrc/conversion/im_gbandjoin.c +libsrc/conversion/im_vips2jpeg.c +libsrc/conversion/im_subsample.c +libsrc/conversion/im_tiff2vips.c +libsrc/conversion/im_exr2vips.c +libsrc/conversion/im_mask2vips.c +libsrc/conversion/im_raw2vips.c +libsrc/freq_filt/im_freq_mask.c +libsrc/freq_filt/im_invfft.c +libsrc/freq_filt/freq_dispatch.c +libsrc/freq_filt/im_disp_ps.c +libsrc/freq_filt/fft_sp.c +libsrc/freq_filt/fmaskcir.c +libsrc/freq_filt/im_fwfft.c +libsrc/freq_filt/im_fractsurf.c +libsrc/freq_filt/im_freqflt.c +libsrc/freq_filt/im_invfftr.c +libsrc/freq_filt/im_rotquad.c +libsrc/freq_filt/fmask4th.c +libsrcCC/VMask.cc +libsrcCC/vipsc++.cc +libsrcCC/VError.cc +libsrcCC/VImage.cc +libsrcCC/VDisplay.cc diff --git a/po/POTFILES.skip b/po/POTFILES.skip new file mode 100644 index 00000000..e69de29b diff --git a/po/README b/po/README new file mode 100644 index 00000000..b60a7b2a --- /dev/null +++ b/po/README @@ -0,0 +1,51 @@ +translators +----------- + +see this page for a howto: + + http://developer.gnome.org/doc/tutorials/gnome-i18n/translator.html + +Things like + + msgid "/File/_Save Image As ..." + +are menu items. You only need to translate the last part (following the final +"/"). The underscore character marks the accelerator (the underlined character +in the menu item). So you could put: + + msgstr "Sevy i_mago os ..." + +and it would display as "Sevy imago os ...", with the "m" underlined. + +tips +---- + +intltool-update --pot + + make a new vips.pot translation template from the sources + +:%s/msgstr ""/msgstr "Malkovich"/ +:%s/msgstr\[0\] ""/msgstr[0] "Malkovich"/ +:%s/msgstr\[1\] ""/msgstr[1] "Malkovich"/ +:g/#, c-format/d + +add header + +# test translation file +# +msgid "" +msgstr "" +"Project-Id-Version: vips 7.9.3\n" +"POT-Creation-Date: 2003-11-04 12:18+0000\n" +"PO-Revision-Date: 2003-11-04 12:30+0000\n" +"Last-Translator: john \n" +"Language-Team: dk \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + + edits to make vips.pot into test.po + +msgfmt -cv -o /dev/null test.po + + check translation for errors diff --git a/po/en_GB.gmo b/po/en_GB.gmo new file mode 100644 index 00000000..9bf7ae92 Binary files /dev/null and b/po/en_GB.gmo differ diff --git a/po/en_GB.po b/po/en_GB.po new file mode 100644 index 00000000..1dbfd777 --- /dev/null +++ b/po/en_GB.po @@ -0,0 +1,1045 @@ +# en_GB localisation for nip2 +# +msgid "" +msgstr "" +"Project-Id-Version: nip2 7.9.3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2005-06-08 14:51+0100\n" +"PO-Revision-Date: 2003-11-04 18:57+0000\n" +"Last-Translator: john.cupitt@ng-london.org.uk\n" +"Language-Team: en_GB \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: libsrc/convolution/im_addgnoise.c:78 +msgid "too many bands" +msgstr "" + +#: libsrc/convolution/im_sharpen.c:247 +msgid "input not 3-band short" +msgstr "" + +#: libsrc/convolution/im_sharpen.c:258 +msgid "parameters out of range" +msgstr "" + +#: libsrc/colour/im_icc_transform.c:200 libsrc/colour/im_icc_transform.c:210 +#, c-format +msgid "unable to open profile \"%s\"" +msgstr "" + +#: libsrc/matrix/im_invmat.c:69 +msgid "singular matrix in ludcmp" +msgstr "" + +#: libsrc/matrix/im_invmat.c:206 +msgid "singular matrix" +msgstr "" + +#: libsrc/acquire/im_clamp.c:69 +msgid "bad input format" +msgstr "" + +#: libsrc/acquire/im_clamp.c:74 +msgid "bad black format" +msgstr "" + +#: libsrc/freq_filt/im_invfftr.c:89 libsrc/freq_filt/im_invfftr.c:171 +#: libsrc/freq_filt/im_invfft.c:94 libsrc/freq_filt/im_invfft.c:142 +#: libsrc/freq_filt/im_fwfft.c:112 libsrc/freq_filt/im_fwfft.c:217 +#: libsrc/freq_filt/im_fwfft.c:308 libsrc/freq_filt/im_fwfft.c:420 +msgid "one band uncoded only" +msgstr "" + +#: libsrc/freq_filt/im_invfftr.c:126 libsrc/freq_filt/im_invfftr.c:209 +#: libsrc/freq_filt/im_invfft.c:106 libsrc/freq_filt/im_invfft.c:156 +#: libsrc/freq_filt/im_fwfft.c:123 libsrc/freq_filt/im_fwfft.c:228 +#: libsrc/freq_filt/im_fwfft.c:321 libsrc/freq_filt/im_fwfft.c:433 +msgid "unable to create transform plan" +msgstr "" + +#: libsrc/freq_filt/im_invfftr.c:252 libsrc/freq_filt/im_invfft.c:199 +msgid "one band complex uncoded only" +msgstr "" + +#: libsrc/freq_filt/im_invfftr.c:256 libsrc/freq_filt/im_invfft.c:203 +#: libsrc/freq_filt/im_fwfft.c:515 +msgid "sides must be power of 2" +msgstr "" + +#: libsrc/freq_filt/im_invfftr.c:276 libsrc/freq_filt/im_invfft.c:223 +#: libsrc/freq_filt/im_fwfft.c:535 +msgid "fft_sp failed" +msgstr "" + +#: libsrc/freq_filt/im_fwfft.c:511 +msgid "one band non-complex uncoded only" +msgstr "" + +#: libsrc/conversion/im_extract.c:162 +msgid "bad extract area" +msgstr "" + +#: libsrc/conversion/im_extract.c:167 libsrc/conversion/im_extract.c:252 +msgid "band selection out of range" +msgstr "" + +#: libsrc/conversion/im_extract.c:172 +msgid "unknown image coding type" +msgstr "" + +#: libsrc/conversion/im_extract.c:177 +msgid "can't extract band from IM_CODING_LABQ" +msgstr "" + +#: libsrc/conversion/im_extract.c:256 libsrc/arithmetic/im_powtra.c:184 +#: libsrc/arithmetic/im_deviate.c:171 libsrc/arithmetic/im_multiply.c:186 +#: libsrc/arithmetic/im_ceil.c:92 libsrc/arithmetic/im_maxpos.c:94 +#: libsrc/arithmetic/im_costra.c:117 libsrc/arithmetic/im_costra.c:202 +#: libsrc/arithmetic/im_tantra.c:117 libsrc/arithmetic/im_tantra.c:203 +#: libsrc/arithmetic/im_measure.c:189 libsrc/arithmetic/im_sign.c:146 +#: libsrc/arithmetic/im_expntra.c:184 libsrc/arithmetic/im_logtra.c:123 +#: libsrc/arithmetic/im_abs.c:147 libsrc/arithmetic/im_add.c:254 +#: libsrc/arithmetic/im_avg.c:166 libsrc/arithmetic/im_max.c:232 +#: libsrc/arithmetic/im_min.c:231 libsrc/arithmetic/im_lintra.c:281 +#: libsrc/arithmetic/im_invert.c:117 libsrc/arithmetic/im_stats.c:238 +#: libsrc/arithmetic/im_floor.c:92 libsrc/arithmetic/im_remainder.c:103 +#: libsrc/arithmetic/im_remainder.c:212 libsrc/arithmetic/im_subtract.c:171 +#: libsrc/arithmetic/im_sintra.c:117 libsrc/arithmetic/im_sintra.c:202 +#: libsrc/arithmetic/im_log10tra.c:123 libsrc/arithmetic/im_divide.c:150 +msgid "not uncoded" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:54 libsrc/conversion/im_magick2vips.c:61 +msgid "libMagick support disabled" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:190 +#, c-format +msgid "unsupported colorspace %d" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:214 +#, c-format +msgid "unsupported bit depth %d" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:409 +msgid "unable to read pixels" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:431 +#, c-format +msgid "" +"unable to read file \"%s\"\n" +"libMagick error: %s %s" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:456 +#, c-format +msgid "" +"unable to ping file \"%s\"\n" +"libMagick error: %s %s" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:467 +msgid "bad image size" +msgstr "" + +#: libsrc/conversion/im_copy.c:138 libsrc/conversion/im_copy.c:275 +msgid "in must be uncoded" +msgstr "" + +#: libsrc/conversion/im_copy.c:142 +msgid "Coding must be NONE or LABQ" +msgstr "" + +#: libsrc/conversion/im_copy.c:146 +#, c-format +msgid "BandFmt must be in range [0,%d]" +msgstr "" + +#: libsrc/conversion/im_copy.c:169 +msgid "sizeof( pixel ) has changed" +msgstr "" + +#: libsrc/conversion/im_copy.c:312 +msgid "unsupported image type" +msgstr "" + +#: libsrc/conversion/im_ppm2vips.c:250 +msgid "internal error" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:131 libsrc/conversion/im_tiff2vips.c:144 +#: libsrc/conversion/im_vips2tiff.c:127 +msgid "TIFF support disabled" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:284 libsrc/conversion/im_tiff2vips.c:307 +#: libsrc/conversion/im_tiff2vips.c:326 +#, c-format +msgid "required field %d missing" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:288 +#, c-format +msgid "required field %d=%d, not %d" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:632 +msgid "read error" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1029 +#, fuzzy +msgid "bad colormap" +msgstr "False colour" + +#: libsrc/conversion/im_tiff2vips.c:1085 libsrc/conversion/im_tiff2vips.c:1135 +msgid "3 or 4 bands RGB TIFF only" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1284 +msgid "unknown resolution unit" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1360 +#, c-format +msgid "unsupported sample format %d for lab image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1370 +#, c-format +msgid "unsupported depth %d for LAB image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1417 +#, c-format +msgid "unsupported sample format %d for greyscale image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1426 +#, c-format +msgid "unsupported depth %d for greyscale image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1466 +#, c-format +msgid "unsupported sample format %d for rgb image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1475 +#, c-format +msgid "unsupported depth %d for RGB image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1489 +#, c-format +msgid "unknown photometric interpretation %d" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1550 +#, c-format +msgid "bad page number %d" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1577 libsrc/conversion/im_vips2tiff.c:265 +#, c-format +msgid "unable to open \"%s\" for input" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1627 libsrc/conversion/im_tiff2vips.c:1661 +#, c-format +msgid "TIFF file does not contain page %d" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:228 +#, c-format +msgid "TIFF error in \"%s\": " +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:245 +#, c-format +msgid "unable to open \"%s\" for output" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:381 +#, c-format +msgid "file \"%s\" too long" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:393 +#, c-format +msgid "error reading from file \"%s\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:416 +#, c-format +msgid "unable to read profile \"%s\"" +msgstr "" + +#. Out of space! +#. +#: libsrc/conversion/im_vips2tiff.c:688 +msgid "layer buffer exhausted -- try making TIFF output tiles smaller" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:919 +msgid "TIFF write tile failed" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:994 +msgid "internal error #9876345" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1109 +msgid "TIFF write failed" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1238 +msgid "bad JPEG quality parameter" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1244 +#, c-format +msgid "" +"unknown compression mode \"%s\"\n" +"should be one of \"none\", \"packbits\", \"ccittfax4\", \"lzw\", \"deflate\" " +"or \"jpeg\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1258 +msgid "bad tile sizes" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1265 +#, c-format +msgid "bad tile size %dx%d" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1273 +msgid "tile size not a multiple of 16" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1282 +#, c-format +msgid "" +"unknown layout mode \"%s\"\n" +"should be one of \"tile\" or \"strip\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1294 +#, c-format +msgid "" +"unknown multi-res mode \"%s\"\n" +"should be one of \"flat\" or \"pyramid\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1306 +#, c-format +msgid "" +"unknown format \"%s\"\n" +"should be one of \"onebit\" or \"manybit\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1318 +#, c-format +msgid "" +"unknown resolution unit \"%s\"\n" +"should be one of \"res_cm\" or \"res_inch\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1327 +msgid "bad resolution values" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1342 +#, c-format +msgid "unknown extra options \"%s\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1346 +msgid "can't have strip pyramid -- enabling tiling" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1356 +msgid "can only pyramid LABQ and non-complex images" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1372 +msgid "can't have 1-bit JPEG -- disabling JPEG" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1529 +msgid "unknown coding type" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1537 +msgid "unsigned 8-bit int, 16-bit int, and 32-bit float only" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1543 +msgid "1 to 4 bands only" +msgstr "" + +#: libsrc/arithmetic/im_powtra.c:188 libsrc/arithmetic/im_tantra.c:121 +#: libsrc/arithmetic/im_tantra.c:207 libsrc/arithmetic/im_expntra.c:188 +#: libsrc/arithmetic/im_logtra.c:127 libsrc/arithmetic/im_sintra.c:121 +#: libsrc/arithmetic/im_sintra.c:206 libsrc/arithmetic/im_log10tra.c:127 +msgid "not non-complex" +msgstr "" + +#: libsrc/arithmetic/im_powtra.c:193 libsrc/arithmetic/im_expntra.c:193 +#: libsrc/arithmetic/im_lintra.c:290 libsrc/arithmetic/im_remainder.c:217 +#, c-format +msgid "not 1 or %d elements in vector" +msgstr "" + +#: libsrc/arithmetic/im_deviate.c:175 libsrc/arithmetic/im_costra.c:121 +#: libsrc/arithmetic/im_costra.c:206 libsrc/arithmetic/im_measure.c:193 +#: libsrc/arithmetic/im_avg.c:162 libsrc/arithmetic/im_stats.c:234 +msgid "bad input type" +msgstr "" + +#: libsrc/arithmetic/im_multiply.c:177 libsrc/arithmetic/im_remainder.c:94 +msgid "not same size" +msgstr "" + +#: libsrc/arithmetic/im_multiply.c:182 libsrc/arithmetic/im_add.c:250 +#: libsrc/arithmetic/im_remainder.c:99 libsrc/arithmetic/im_subtract.c:167 +#: libsrc/arithmetic/im_divide.c:146 +msgid "not same number of bands" +msgstr "" + +#: libsrc/arithmetic/im_minpos.c:80 +msgid "input must be uncoded" +msgstr "" + +#: libsrc/arithmetic/im_gadd.c:91 libsrc/arithmetic/im_gadd.c:107 +msgid "Unable to accept image1" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:79 +msgid "absolute value" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:98 +msgid "add two images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:123 +msgid "average value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:142 +msgid "multiply two complex images, normalising output" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:167 +msgid "standard deviation of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:186 +msgid "10^pel of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:205 +msgid "e^pel of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:234 +msgid "x^pel of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:263 +msgid "[x,y,z]^pel of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:299 +msgid "average of 4 images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:318 +msgid "divide two images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:352 +msgid "calculate a*in1 + b*in2 + c = outfile" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:371 +msgid "photographic negative" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:402 +msgid "calculate a*in + b = outfile" +msgstr "" + +#: libsrc/arithmetic/arith_dispatch.c:427 +msgid "vectors not same length" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:438 +msgid "calculate a*in + b -> out, a and b vectors" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:470 +msgid "calculate max(white)*factor*(in/white), if clip == 1" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:489 +msgid "log10 of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:508 +msgid "ln of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:527 +msgid "tan of image (angles in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:546 +msgid "atan of image (result in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:565 +msgid "cos of image (angles in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:584 +msgid "acos of image (result in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:603 +msgid "round to smallest integal value not less than" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:622 +msgid "round to largest integal value not greater than" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:641 +msgid "round to nearest integal value" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:660 +msgid "sin of image (angles in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:679 +msgid "unit vector in direction of value" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:698 +msgid "asin of image (result in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:723 +msgid "maximum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:758 +msgid "position of maximum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:818 +msgid "measure averages of a grid of patches" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:843 +msgid "minimum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:871 +msgid "position of minimum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:890 +msgid "remainder after integer division" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:919 +msgid "remainder after integer division by a constant" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:948 +msgid "remainder after integer division by a vector of constants" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:968 +msgid "multiply two images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:989 +msgid "pel^x ofbuildimage" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:1010 +msgid "pel^[x,y,z] of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:1041 +msgid "many image statistics in one pass" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:1060 +msgid "subtract two images" +msgstr "" + +#: libsrc/arithmetic/im_measure.c:106 +#, c-format +msgid "patch %d is out of range" +msgstr "" + +#: libsrc/arithmetic/im_measure.c:148 +#, c-format +msgid "patch %d, band %d: avg = %g, sdev = %g" +msgstr "" + +#: libsrc/arithmetic/im_abs.c:183 +msgid "unknown input type" +msgstr "" + +#: libsrc/arithmetic/im_add.c:190 +#, c-format +msgid "not one band or %d bands" +msgstr "" + +#: libsrc/arithmetic/im_add.c:194 +msgid "bad bands" +msgstr "" + +#: libsrc/arithmetic/im_invert.c:121 +msgid "not UCHAR" +msgstr "" + +#: libsrc/arithmetic/im_remainder.c:239 +msgid "division by zero" +msgstr "" + +#: libsrc/relational/im_blend.c:338 +msgid "images not uncoded" +msgstr "" + +#: libsrc/relational/im_blend.c:344 libsrc/relational/im_ifthenelse.c:183 +msgid "size and format of then and else must match" +msgstr "" + +#: libsrc/relational/im_blend.c:349 libsrc/relational/im_ifthenelse.c:188 +msgid "conditional image must be uchar" +msgstr "" + +#: libsrc/relational/im_blend.c:354 libsrc/relational/im_ifthenelse.c:193 +msgid "conditional image must be one band or same as then and else images" +msgstr "" + +#: libsrc/relational/im_ifthenelse.c:167 +msgid "then image must be uncoded or labpack" +msgstr "" + +#: libsrc/relational/im_ifthenelse.c:172 +msgid "else image must be uncoded or labpack" +msgstr "" + +#: libsrc/relational/im_ifthenelse.c:177 +msgid "condition image must be uncoded" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:112 +#, c-format +msgid "%d overflows detected" +msgstr "" + +#. Switch for input types. Has to be uint type! +#. +#: libsrc/histograms_lut/im_maplut.c:473 +msgid "bad input file" +msgstr "" + +#. Switch for LUT types. One function for non-complex images, a +#. * variant for complex ones. Another pair as well, in case the input is not +#. * uchar. +#. +#: libsrc/histograms_lut/im_maplut.c:504 +msgid "bad lut file" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:554 +msgid "lut is not uncoded" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:558 +msgid "lut seems very large!" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:570 +msgid "input is not uncoded" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:574 +msgid "input is not some unsigned integer type" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:579 +msgid "" +"lut should have 1 band, or same number of bands as input, or any number of " +"bands if input has 1 band" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:586 +msgid "input is uchar and lut does not have 256 elements" +msgstr "" + +#: libsrc/histograms_lut/im_histnD.c:212 +msgid " uncoded images only" +msgstr "" + +#: libsrc/histograms_lut/im_histnD.c:218 +msgid " unsigned 8 or 16 bit images only" +msgstr "" + +#: libsrc/histograms_lut/im_histnD.c:225 +#, c-format +msgid " bins out of range [1,%d]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:233 +msgid "bad in_max, out_max parameters" +msgstr "" + +#: libsrc/histograms_lut/tone.c:237 +msgid "bad Lb, Lw parameters" +msgstr "" + +#: libsrc/histograms_lut/tone.c:241 +msgid "Ps not in range [0.0,1.0]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:245 +msgid "Pm not in range [0.0,1.0]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:249 +msgid "Ph not in range [0.0,1.0]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:253 +msgid "S not in range [-30,+30]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:257 +msgid "M not in range [-30,+30]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:261 +msgid "H not in range [-30,+30]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:342 libsrc/histograms_lut/tone.c:390 +msgid "not 1 by n or n by 1 image" +msgstr "" + +#: libsrc/histograms_lut/tone.c:396 +msgid "not 1024-point IM_BANDFMT_SHORT lut" +msgstr "" + +#: libsrc/histograms_lut/tone.c:413 libsrc/histograms_lut/tone.c:487 +msgid "input not LabS or LabQ" +msgstr "" + +#: libsrc/inplace/im_insertplace.c:81 +msgid "inputs differ in format" +msgstr "" + +#: libsrc/inplace/im_insertplace.c:85 +msgid "input should be uncoded or IM_CODING_LABQ" +msgstr "" + +#: libsrc/inplace/im_insertplace.c:104 +msgid "small not inside big" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:134 libsrc/iofuncs/im_mapfile.c:301 +msgid "unable to CreateFileMapping" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:150 libsrc/iofuncs/im_mapfile.c:313 +msgid "unable to MapViewOfFile" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:177 +msgid "unable to mmap" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:178 +#, c-format +msgid "" +"map failed (%s), running very low on system resources, expect a crash soon" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:195 libsrc/iofuncs/im_mapfile.c:307 +msgid "unable to UnmapViewOfFile" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:201 +msgid "unable to munmap file" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:224 libsrc/iofuncs/im_mapfile.c:265 +msgid "unable to get file status" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:229 +msgid "file is less than 64 bytes" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:233 +msgid "not a regular file" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:270 +msgid "unable to read data" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:333 +#, c-format +msgid "unable to mmap: \"%s\" - %s" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:343 +#, c-format +msgid "unable to mmap \"%s\" to same address" +msgstr "" + +#: libsrc/iofuncs/im_binfile.c:88 libsrc/iofuncs/im_render.c:1079 +msgid "bad parameters" +msgstr "" + +#: libsrc/iofuncs/im_binfile.c:116 +#, c-format +msgid "unable to open %s: file has been truncated" +msgstr "" + +#: libsrc/iofuncs/im_binfile.c:126 +#, c-format +msgid "%s is longer than expected" +msgstr "" + +#: libsrc/iofuncs/error.c:114 +msgid "windows error" +msgstr "" + +#: libsrc/iofuncs/error.c:123 +msgid "unix error" +msgstr "" + +#: libsrc/iofuncs/error.c:149 libsrc/iofuncs/error.c:150 +#: libsrc/iofuncs/error.c:170 libsrc/iofuncs/error.c:171 +#, c-format +msgid "%s: " +msgstr "" + +#: libsrc/iofuncs/error.c:149 +msgid "vips diagnostic" +msgstr "" + +#: libsrc/iofuncs/error.c:170 +msgid "vips warning" +msgstr "" + +#: libsrc/iofuncs/im_readhist.c:163 +msgid "history file too large" +msgstr "" + +#: libsrc/iofuncs/im_readhist.c:174 +msgid "unable to read" +msgstr "" + +#: libsrc/iofuncs/im_header.c:102 +#, c-format +msgid "no such int field \"%s\"" +msgstr "" + +#: libsrc/iofuncs/im_header.c:124 +#, c-format +msgid "no such double field \"%s\"" +msgstr "" + +#: libsrc/iofuncs/im_header.c:146 +#, c-format +msgid "no such string field \"%s\"" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:112 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:118 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:124 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:130 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:136 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:142 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_render.c:511 libsrc/iofuncs/im_render.c:666 +#: libsrc/iofuncs/threadgroup.c:330 +msgid "unable to create thread" +msgstr "" + +#: libsrc/iofuncs/package.c:436 +#, c-format +msgid "unable to close plugin \"%s\"" +msgstr "" + +#: libsrc/iofuncs/package.c:438 libsrc/iofuncs/package.c:488 +#: libsrc/iofuncs/package.c:502 +#, c-format +msgid "plugin error: %s" +msgstr "" + +#: libsrc/iofuncs/package.c:462 +msgid "plugins not supported on this platform" +msgstr "" + +#: libsrc/iofuncs/package.c:486 +#, c-format +msgid "unable to open plugin \"%s\"" +msgstr "" + +#: libsrc/iofuncs/package.c:499 +#, c-format +msgid "unable to find symbol \"package_table\" in plugin \"%s\"" +msgstr "" + +#: libsrc/iofuncs/package.c:513 +#, c-format +msgid "corrupted package table in plugin \"%s\"" +msgstr "" + +#: libsrc/iofuncs/package.c:636 libsrc/iofuncs/package.c:692 +#, c-format +msgid "function \"%s\" not found" +msgstr "" + +#: libsrc/iofuncs/package.c:664 +#, c-format +msgid "package \"%s\" not found" +msgstr "" + +#: libsrc/iofuncs/package.c:802 +msgid "too few arguments" +msgstr "" + +#: libsrc/iofuncs/package.c:824 +msgid "too many arguments" +msgstr "" + +#: libsrc/iofuncs/package.c:1031 +msgid "flag not 0,1,2" +msgstr "" + +#: libsrc/iofuncs/im_wrapmany.c:158 +msgid "too many input images" +msgstr "" + +#: libsrc/iofuncs/im_wrapmany.c:176 +msgid "descriptors differ in size" +msgstr "" + +#~ msgid "Edit color \"%s\"" +#~ msgstr "Edit colour \"%s\"" + +#~ msgid "Set color" +#~ msgstr "Set colour" + +#~ msgid "Double-click to edit this color, or drag-and-drop between colors" +#~ msgstr "Double-click to edit this colour, or drag-and-drop between colours" + +#, fuzzy +#~ msgid "Header for \"%s\"" +#~ msgstr "Edit colour \"%s\"" + +#, fuzzy +#~ msgid "Edit \"%s\"" +#~ msgstr "Edit colour \"%s\"" + +#, fuzzy +#~ msgid "Edit matrix \"%s\"" +#~ msgstr "Edit colour \"%s\"" + +#, fuzzy +#~ msgid "Set option" +#~ msgstr "Set colour" + +#, fuzzy +#~ msgid "Edit column item \"%s\"" +#~ msgstr "Edit colour \"%s\"" + +#, fuzzy +#~ msgid "Edit slider \"%s\"" +#~ msgstr "Edit colour \"%s\"" + +#, fuzzy +#~ msgid "Set Slider" +#~ msgstr "Set colour" + +#, fuzzy +#~ msgid "Set toggle" +#~ msgstr "Set colour" diff --git a/po/malkovich.gmo b/po/malkovich.gmo new file mode 100644 index 00000000..87a13194 Binary files /dev/null and b/po/malkovich.gmo differ diff --git a/po/malkovich.po b/po/malkovich.po new file mode 100644 index 00000000..b3ac4029 --- /dev/null +++ b/po/malkovich.po @@ -0,0 +1,3259 @@ +# test translation file +# +msgid "" +msgstr "" +"Project-Id-Version: nip2 7.9.3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2005-06-08 14:51+0100\n" +"PO-Revision-Date: 2003-11-04 12:30+0000\n" +"Last-Translator: malkovich \n" +"Language-Team: malkovich \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: libsrc/convolution/im_addgnoise.c:78 +#, fuzzy +msgid "too many bands" +msgstr "Malkovich" + +#: libsrc/convolution/im_sharpen.c:247 +msgid "input not 3-band short" +msgstr "" + +#: libsrc/convolution/im_sharpen.c:258 +msgid "parameters out of range" +msgstr "" + +#: libsrc/colour/im_icc_transform.c:200 libsrc/colour/im_icc_transform.c:210 +#, fuzzy, c-format +msgid "unable to open profile \"%s\"" +msgstr "Malkovich" + +#: libsrc/matrix/im_invmat.c:69 +msgid "singular matrix in ludcmp" +msgstr "" + +#: libsrc/matrix/im_invmat.c:206 +#, fuzzy +msgid "singular matrix" +msgstr "Malkovich" + +#: libsrc/acquire/im_clamp.c:69 +msgid "bad input format" +msgstr "" + +#: libsrc/acquire/im_clamp.c:74 +msgid "bad black format" +msgstr "" + +#: libsrc/freq_filt/im_invfftr.c:89 libsrc/freq_filt/im_invfftr.c:171 +#: libsrc/freq_filt/im_invfft.c:94 libsrc/freq_filt/im_invfft.c:142 +#: libsrc/freq_filt/im_fwfft.c:112 libsrc/freq_filt/im_fwfft.c:217 +#: libsrc/freq_filt/im_fwfft.c:308 libsrc/freq_filt/im_fwfft.c:420 +#, fuzzy +msgid "one band uncoded only" +msgstr "Malkovich" + +#: libsrc/freq_filt/im_invfftr.c:126 libsrc/freq_filt/im_invfftr.c:209 +#: libsrc/freq_filt/im_invfft.c:106 libsrc/freq_filt/im_invfft.c:156 +#: libsrc/freq_filt/im_fwfft.c:123 libsrc/freq_filt/im_fwfft.c:228 +#: libsrc/freq_filt/im_fwfft.c:321 libsrc/freq_filt/im_fwfft.c:433 +#, fuzzy +msgid "unable to create transform plan" +msgstr "Malkovich" + +#: libsrc/freq_filt/im_invfftr.c:252 libsrc/freq_filt/im_invfft.c:199 +#, fuzzy +msgid "one band complex uncoded only" +msgstr "Malkovich" + +#: libsrc/freq_filt/im_invfftr.c:256 libsrc/freq_filt/im_invfft.c:203 +#: libsrc/freq_filt/im_fwfft.c:515 +#, fuzzy +msgid "sides must be power of 2" +msgstr "Malkovich" + +#: libsrc/freq_filt/im_invfftr.c:276 libsrc/freq_filt/im_invfft.c:223 +#: libsrc/freq_filt/im_fwfft.c:535 +msgid "fft_sp failed" +msgstr "" + +#: libsrc/freq_filt/im_fwfft.c:511 +msgid "one band non-complex uncoded only" +msgstr "" + +#: libsrc/conversion/im_extract.c:162 +msgid "bad extract area" +msgstr "" + +#: libsrc/conversion/im_extract.c:167 libsrc/conversion/im_extract.c:252 +msgid "band selection out of range" +msgstr "" + +#: libsrc/conversion/im_extract.c:172 +#, fuzzy +msgid "unknown image coding type" +msgstr "Malkovich" + +#: libsrc/conversion/im_extract.c:177 +msgid "can't extract band from IM_CODING_LABQ" +msgstr "" + +#: libsrc/conversion/im_extract.c:256 libsrc/arithmetic/im_powtra.c:184 +#: libsrc/arithmetic/im_deviate.c:171 libsrc/arithmetic/im_multiply.c:186 +#: libsrc/arithmetic/im_ceil.c:92 libsrc/arithmetic/im_maxpos.c:94 +#: libsrc/arithmetic/im_costra.c:117 libsrc/arithmetic/im_costra.c:202 +#: libsrc/arithmetic/im_tantra.c:117 libsrc/arithmetic/im_tantra.c:203 +#: libsrc/arithmetic/im_measure.c:189 libsrc/arithmetic/im_sign.c:146 +#: libsrc/arithmetic/im_expntra.c:184 libsrc/arithmetic/im_logtra.c:123 +#: libsrc/arithmetic/im_abs.c:147 libsrc/arithmetic/im_add.c:254 +#: libsrc/arithmetic/im_avg.c:166 libsrc/arithmetic/im_max.c:232 +#: libsrc/arithmetic/im_min.c:231 libsrc/arithmetic/im_lintra.c:281 +#: libsrc/arithmetic/im_invert.c:117 libsrc/arithmetic/im_stats.c:238 +#: libsrc/arithmetic/im_floor.c:92 libsrc/arithmetic/im_remainder.c:103 +#: libsrc/arithmetic/im_remainder.c:212 libsrc/arithmetic/im_subtract.c:171 +#: libsrc/arithmetic/im_sintra.c:117 libsrc/arithmetic/im_sintra.c:202 +#: libsrc/arithmetic/im_log10tra.c:123 libsrc/arithmetic/im_divide.c:150 +msgid "not uncoded" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:54 libsrc/conversion/im_magick2vips.c:61 +msgid "libMagick support disabled" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:190 +#, fuzzy, c-format +msgid "unsupported colorspace %d" +msgstr "Malkovich" + +#: libsrc/conversion/im_magick2vips.c:214 +#, fuzzy, c-format +msgid "unsupported bit depth %d" +msgstr "Malkovich" + +#: libsrc/conversion/im_magick2vips.c:409 +#, fuzzy +msgid "unable to read pixels" +msgstr "Malkovich" + +#: libsrc/conversion/im_magick2vips.c:431 +#, fuzzy, c-format +msgid "" +"unable to read file \"%s\"\n" +"libMagick error: %s %s" +msgstr "Malkovich" + +#: libsrc/conversion/im_magick2vips.c:456 +#, fuzzy, c-format +msgid "" +"unable to ping file \"%s\"\n" +"libMagick error: %s %s" +msgstr "Malkovich" + +#: libsrc/conversion/im_magick2vips.c:467 +#, fuzzy +msgid "bad image size" +msgstr "Malkovich" + +#: libsrc/conversion/im_copy.c:138 libsrc/conversion/im_copy.c:275 +msgid "in must be uncoded" +msgstr "" + +#: libsrc/conversion/im_copy.c:142 +msgid "Coding must be NONE or LABQ" +msgstr "" + +#: libsrc/conversion/im_copy.c:146 +#, c-format +msgid "BandFmt must be in range [0,%d]" +msgstr "" + +#: libsrc/conversion/im_copy.c:169 +msgid "sizeof( pixel ) has changed" +msgstr "" + +#: libsrc/conversion/im_copy.c:312 +#, fuzzy +msgid "unsupported image type" +msgstr "Malkovich" + +#: libsrc/conversion/im_ppm2vips.c:250 +#, fuzzy +msgid "internal error" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:131 libsrc/conversion/im_tiff2vips.c:144 +#: libsrc/conversion/im_vips2tiff.c:127 +msgid "TIFF support disabled" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:284 libsrc/conversion/im_tiff2vips.c:307 +#: libsrc/conversion/im_tiff2vips.c:326 +#, c-format +msgid "required field %d missing" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:288 +#, c-format +msgid "required field %d=%d, not %d" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:632 +#, fuzzy +msgid "read error" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1029 +#, fuzzy +msgid "bad colormap" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1085 libsrc/conversion/im_tiff2vips.c:1135 +msgid "3 or 4 bands RGB TIFF only" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1284 +#, fuzzy +msgid "unknown resolution unit" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1360 +#, fuzzy, c-format +msgid "unsupported sample format %d for lab image" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1370 +#, fuzzy, c-format +msgid "unsupported depth %d for LAB image" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1417 +#, c-format +msgid "unsupported sample format %d for greyscale image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1426 +#, fuzzy, c-format +msgid "unsupported depth %d for greyscale image" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1466 +#, c-format +msgid "unsupported sample format %d for rgb image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1475 +#, fuzzy, c-format +msgid "unsupported depth %d for RGB image" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1489 +#, c-format +msgid "unknown photometric interpretation %d" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1550 +#, fuzzy, c-format +msgid "bad page number %d" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1577 libsrc/conversion/im_vips2tiff.c:265 +#, fuzzy, c-format +msgid "unable to open \"%s\" for input" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1627 libsrc/conversion/im_tiff2vips.c:1661 +#, c-format +msgid "TIFF file does not contain page %d" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:228 +#, fuzzy, c-format +msgid "TIFF error in \"%s\": " +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:245 +#, fuzzy, c-format +msgid "unable to open \"%s\" for output" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:381 +#, fuzzy, c-format +msgid "file \"%s\" too long" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:393 +#, fuzzy, c-format +msgid "error reading from file \"%s\"" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:416 +#, fuzzy, c-format +msgid "unable to read profile \"%s\"" +msgstr "Malkovich" + +#. Out of space! +#. +#: libsrc/conversion/im_vips2tiff.c:688 +msgid "layer buffer exhausted -- try making TIFF output tiles smaller" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:919 +msgid "TIFF write tile failed" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:994 +#, fuzzy +msgid "internal error #9876345" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:1109 +#, fuzzy +msgid "TIFF write failed" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:1238 +#, fuzzy +msgid "bad JPEG quality parameter" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:1244 +#, c-format +msgid "" +"unknown compression mode \"%s\"\n" +"should be one of \"none\", \"packbits\", \"ccittfax4\", \"lzw\", \"deflate\" " +"or \"jpeg\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1258 +#, fuzzy +msgid "bad tile sizes" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:1265 +#, c-format +msgid "bad tile size %dx%d" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1273 +msgid "tile size not a multiple of 16" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1282 +#, c-format +msgid "" +"unknown layout mode \"%s\"\n" +"should be one of \"tile\" or \"strip\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1294 +#, c-format +msgid "" +"unknown multi-res mode \"%s\"\n" +"should be one of \"flat\" or \"pyramid\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1306 +#, c-format +msgid "" +"unknown format \"%s\"\n" +"should be one of \"onebit\" or \"manybit\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1318 +#, c-format +msgid "" +"unknown resolution unit \"%s\"\n" +"should be one of \"res_cm\" or \"res_inch\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1327 +#, fuzzy +msgid "bad resolution values" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:1342 +#, c-format +msgid "unknown extra options \"%s\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1346 +msgid "can't have strip pyramid -- enabling tiling" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1356 +msgid "can only pyramid LABQ and non-complex images" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1372 +msgid "can't have 1-bit JPEG -- disabling JPEG" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1529 +#, fuzzy +msgid "unknown coding type" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:1537 +msgid "unsigned 8-bit int, 16-bit int, and 32-bit float only" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1543 +msgid "1 to 4 bands only" +msgstr "" + +#: libsrc/arithmetic/im_powtra.c:188 libsrc/arithmetic/im_tantra.c:121 +#: libsrc/arithmetic/im_tantra.c:207 libsrc/arithmetic/im_expntra.c:188 +#: libsrc/arithmetic/im_logtra.c:127 libsrc/arithmetic/im_sintra.c:121 +#: libsrc/arithmetic/im_sintra.c:206 libsrc/arithmetic/im_log10tra.c:127 +#, fuzzy +msgid "not non-complex" +msgstr "Malkovich" + +#: libsrc/arithmetic/im_powtra.c:193 libsrc/arithmetic/im_expntra.c:193 +#: libsrc/arithmetic/im_lintra.c:290 libsrc/arithmetic/im_remainder.c:217 +#, c-format +msgid "not 1 or %d elements in vector" +msgstr "" + +#: libsrc/arithmetic/im_deviate.c:175 libsrc/arithmetic/im_costra.c:121 +#: libsrc/arithmetic/im_costra.c:206 libsrc/arithmetic/im_measure.c:193 +#: libsrc/arithmetic/im_avg.c:162 libsrc/arithmetic/im_stats.c:234 +msgid "bad input type" +msgstr "" + +#: libsrc/arithmetic/im_multiply.c:177 libsrc/arithmetic/im_remainder.c:94 +msgid "not same size" +msgstr "" + +#: libsrc/arithmetic/im_multiply.c:182 libsrc/arithmetic/im_add.c:250 +#: libsrc/arithmetic/im_remainder.c:99 libsrc/arithmetic/im_subtract.c:167 +#: libsrc/arithmetic/im_divide.c:146 +#, fuzzy +msgid "not same number of bands" +msgstr "Malkovich" + +#: libsrc/arithmetic/im_minpos.c:80 +msgid "input must be uncoded" +msgstr "" + +#: libsrc/arithmetic/im_gadd.c:91 libsrc/arithmetic/im_gadd.c:107 +#, fuzzy +msgid "Unable to accept image1" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:79 +#, fuzzy +msgid "absolute value" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:98 +#, fuzzy +msgid "add two images" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:123 +msgid "average value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:142 +msgid "multiply two complex images, normalising output" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:167 +msgid "standard deviation of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:186 +#, fuzzy +msgid "10^pel of image" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:205 +#, fuzzy +msgid "e^pel of image" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:234 +#, fuzzy +msgid "x^pel of image" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:263 +msgid "[x,y,z]^pel of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:299 +msgid "average of 4 images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:318 +msgid "divide two images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:352 +msgid "calculate a*in1 + b*in2 + c = outfile" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:371 +msgid "photographic negative" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:402 +msgid "calculate a*in + b = outfile" +msgstr "" + +#: libsrc/arithmetic/arith_dispatch.c:427 +msgid "vectors not same length" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:438 +msgid "calculate a*in + b -> out, a and b vectors" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:470 +msgid "calculate max(white)*factor*(in/white), if clip == 1" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:489 +#, fuzzy +msgid "log10 of image" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:508 +#, fuzzy +msgid "ln of image" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:527 +msgid "tan of image (angles in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:546 +msgid "atan of image (result in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:565 +msgid "cos of image (angles in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:584 +msgid "acos of image (result in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:603 +msgid "round to smallest integal value not less than" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:622 +msgid "round to largest integal value not greater than" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:641 +msgid "round to nearest integal value" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:660 +msgid "sin of image (angles in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:679 +msgid "unit vector in direction of value" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:698 +msgid "asin of image (result in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:723 +msgid "maximum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:758 +msgid "position of maximum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:818 +msgid "measure averages of a grid of patches" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:843 +msgid "minimum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:871 +msgid "position of minimum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:890 +msgid "remainder after integer division" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:919 +msgid "remainder after integer division by a constant" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:948 +msgid "remainder after integer division by a vector of constants" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:968 +msgid "multiply two images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:989 +msgid "pel^x ofbuildimage" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:1010 +msgid "pel^[x,y,z] of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:1041 +msgid "many image statistics in one pass" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:1060 +msgid "subtract two images" +msgstr "" + +#: libsrc/arithmetic/im_measure.c:106 +#, c-format +msgid "patch %d is out of range" +msgstr "" + +#: libsrc/arithmetic/im_measure.c:148 +#, c-format +msgid "patch %d, band %d: avg = %g, sdev = %g" +msgstr "" + +#: libsrc/arithmetic/im_abs.c:183 +#, fuzzy +msgid "unknown input type" +msgstr "Malkovich" + +#: libsrc/arithmetic/im_add.c:190 +#, fuzzy, c-format +msgid "not one band or %d bands" +msgstr "Malkovich" + +#: libsrc/arithmetic/im_add.c:194 +#, fuzzy +msgid "bad bands" +msgstr "Malkovich" + +#: libsrc/arithmetic/im_invert.c:121 +msgid "not UCHAR" +msgstr "" + +#: libsrc/arithmetic/im_remainder.c:239 +#, fuzzy +msgid "division by zero" +msgstr "Malkovich" + +#: libsrc/relational/im_blend.c:338 +#, fuzzy +msgid "images not uncoded" +msgstr "Malkovich" + +#: libsrc/relational/im_blend.c:344 libsrc/relational/im_ifthenelse.c:183 +msgid "size and format of then and else must match" +msgstr "" + +#: libsrc/relational/im_blend.c:349 libsrc/relational/im_ifthenelse.c:188 +msgid "conditional image must be uchar" +msgstr "" + +#: libsrc/relational/im_blend.c:354 libsrc/relational/im_ifthenelse.c:193 +msgid "conditional image must be one band or same as then and else images" +msgstr "" + +#: libsrc/relational/im_ifthenelse.c:167 +msgid "then image must be uncoded or labpack" +msgstr "" + +#: libsrc/relational/im_ifthenelse.c:172 +msgid "else image must be uncoded or labpack" +msgstr "" + +#: libsrc/relational/im_ifthenelse.c:177 +msgid "condition image must be uncoded" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:112 +#, c-format +msgid "%d overflows detected" +msgstr "" + +#. Switch for input types. Has to be uint type! +#. +#: libsrc/histograms_lut/im_maplut.c:473 +#, fuzzy +msgid "bad input file" +msgstr "Malkovich" + +#. Switch for LUT types. One function for non-complex images, a +#. * variant for complex ones. Another pair as well, in case the input is not +#. * uchar. +#. +#: libsrc/histograms_lut/im_maplut.c:504 +#, fuzzy +msgid "bad lut file" +msgstr "Malkovich" + +#: libsrc/histograms_lut/im_maplut.c:554 +msgid "lut is not uncoded" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:558 +msgid "lut seems very large!" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:570 +msgid "input is not uncoded" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:574 +#, fuzzy +msgid "input is not some unsigned integer type" +msgstr "Malkovich" + +#: libsrc/histograms_lut/im_maplut.c:579 +msgid "" +"lut should have 1 band, or same number of bands as input, or any number of " +"bands if input has 1 band" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:586 +msgid "input is uchar and lut does not have 256 elements" +msgstr "" + +#: libsrc/histograms_lut/im_histnD.c:212 +#, fuzzy +msgid " uncoded images only" +msgstr "Malkovich" + +#: libsrc/histograms_lut/im_histnD.c:218 +msgid " unsigned 8 or 16 bit images only" +msgstr "" + +#: libsrc/histograms_lut/im_histnD.c:225 +#, c-format +msgid " bins out of range [1,%d]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:233 +msgid "bad in_max, out_max parameters" +msgstr "" + +#: libsrc/histograms_lut/tone.c:237 +#, fuzzy +msgid "bad Lb, Lw parameters" +msgstr "Malkovich" + +#: libsrc/histograms_lut/tone.c:241 +msgid "Ps not in range [0.0,1.0]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:245 +msgid "Pm not in range [0.0,1.0]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:249 +msgid "Ph not in range [0.0,1.0]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:253 +msgid "S not in range [-30,+30]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:257 +msgid "M not in range [-30,+30]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:261 +msgid "H not in range [-30,+30]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:342 libsrc/histograms_lut/tone.c:390 +msgid "not 1 by n or n by 1 image" +msgstr "" + +#: libsrc/histograms_lut/tone.c:396 +msgid "not 1024-point IM_BANDFMT_SHORT lut" +msgstr "" + +#: libsrc/histograms_lut/tone.c:413 libsrc/histograms_lut/tone.c:487 +msgid "input not LabS or LabQ" +msgstr "" + +#: libsrc/inplace/im_insertplace.c:81 +msgid "inputs differ in format" +msgstr "" + +#: libsrc/inplace/im_insertplace.c:85 +msgid "input should be uncoded or IM_CODING_LABQ" +msgstr "" + +#: libsrc/inplace/im_insertplace.c:104 +msgid "small not inside big" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:134 libsrc/iofuncs/im_mapfile.c:301 +#, fuzzy +msgid "unable to CreateFileMapping" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:150 libsrc/iofuncs/im_mapfile.c:313 +#, fuzzy +msgid "unable to MapViewOfFile" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:177 +#, fuzzy +msgid "unable to mmap" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:178 +#, c-format +msgid "" +"map failed (%s), running very low on system resources, expect a crash soon" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:195 libsrc/iofuncs/im_mapfile.c:307 +#, fuzzy +msgid "unable to UnmapViewOfFile" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:201 +#, fuzzy +msgid "unable to munmap file" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:224 libsrc/iofuncs/im_mapfile.c:265 +#, fuzzy +msgid "unable to get file status" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:229 +msgid "file is less than 64 bytes" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:233 +msgid "not a regular file" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:270 +#, fuzzy +msgid "unable to read data" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:333 +#, fuzzy, c-format +msgid "unable to mmap: \"%s\" - %s" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:343 +#, fuzzy, c-format +msgid "unable to mmap \"%s\" to same address" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_binfile.c:88 libsrc/iofuncs/im_render.c:1079 +#, fuzzy +msgid "bad parameters" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_binfile.c:116 +#, fuzzy, c-format +msgid "unable to open %s: file has been truncated" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_binfile.c:126 +#, c-format +msgid "%s is longer than expected" +msgstr "" + +#: libsrc/iofuncs/error.c:114 +#, fuzzy +msgid "windows error" +msgstr "Malkovich" + +#: libsrc/iofuncs/error.c:123 +#, fuzzy +msgid "unix error" +msgstr "Malkovich" + +#: libsrc/iofuncs/error.c:149 libsrc/iofuncs/error.c:150 +#: libsrc/iofuncs/error.c:170 libsrc/iofuncs/error.c:171 +#, fuzzy, c-format +msgid "%s: " +msgstr "Malkovich" + +#: libsrc/iofuncs/error.c:149 +msgid "vips diagnostic" +msgstr "" + +#: libsrc/iofuncs/error.c:170 +msgid "vips warning" +msgstr "" + +#: libsrc/iofuncs/im_readhist.c:163 +msgid "history file too large" +msgstr "" + +#: libsrc/iofuncs/im_readhist.c:174 +#, fuzzy +msgid "unable to read" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_header.c:102 +#, c-format +msgid "no such int field \"%s\"" +msgstr "" + +#: libsrc/iofuncs/im_header.c:124 +#, c-format +msgid "no such double field \"%s\"" +msgstr "" + +#: libsrc/iofuncs/im_header.c:146 +#, c-format +msgid "no such string field \"%s\"" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:112 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:118 +#, fuzzy +msgid "" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_printdesc.c:124 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:130 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:136 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:142 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_render.c:511 libsrc/iofuncs/im_render.c:666 +#: libsrc/iofuncs/threadgroup.c:330 +#, fuzzy +msgid "unable to create thread" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:436 +#, fuzzy, c-format +msgid "unable to close plugin \"%s\"" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:438 libsrc/iofuncs/package.c:488 +#: libsrc/iofuncs/package.c:502 +#, fuzzy, c-format +msgid "plugin error: %s" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:462 +msgid "plugins not supported on this platform" +msgstr "" + +#: libsrc/iofuncs/package.c:486 +#, fuzzy, c-format +msgid "unable to open plugin \"%s\"" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:499 +#, c-format +msgid "unable to find symbol \"package_table\" in plugin \"%s\"" +msgstr "" + +#: libsrc/iofuncs/package.c:513 +#, c-format +msgid "corrupted package table in plugin \"%s\"" +msgstr "" + +#: libsrc/iofuncs/package.c:636 libsrc/iofuncs/package.c:692 +#, fuzzy, c-format +msgid "function \"%s\" not found" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:664 +#, fuzzy, c-format +msgid "package \"%s\" not found" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:802 +#, fuzzy +msgid "too few arguments" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:824 +#, fuzzy +msgid "too many arguments" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:1031 +msgid "flag not 0,1,2" +msgstr "" + +#: libsrc/iofuncs/im_wrapmany.c:158 +#, fuzzy +msgid "too many input images" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_wrapmany.c:176 +msgid "descriptors differ in size" +msgstr "" + +#~ msgid "Stack overflow." +#~ msgstr "Malkovich" + +#~ msgid "Spine stack overflow, runaway recursion?" +#~ msgstr "Malkovich" + +#~ msgid "Frame stack overflow, expression too complex." +#~ msgstr "Malkovich" + +#~ msgid "Stack underflow." +#~ msgstr "Malkovich" + +#~ msgid "Frame stack underflow, you've found a bug!" +#~ msgstr "Malkovich" + +#~ msgid "Spine stack underflow, you've found a bug!" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "" +#~ "Error in binary \"%s\".\n" +#~ "left = %s\n" +#~ "right = %s" +#~ msgstr "Malkovich" + +#~ msgid "Member not found." +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "" +#~ "Member \"%s\" not found in object \"%s\".\n" +#~ "object = %s\n" +#~ "tag = %s\n" +#~ "Reference attempted in \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Bad argument." +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "" +#~ "Error in unary \"%s\".\n" +#~ "argument = %s" +#~ msgstr "Malkovich" + +#~ msgid "Symbol on left hand side of '.' is not scope" +#~ msgstr "Malkovich" + +#~ msgid "Right hand side of '.' is not tag." +#~ msgstr "Malkovich" + +#~ msgid "Bad left hand side." +#~ msgstr "Malkovich" + +#~ msgid "Unimplemented." +#~ msgstr "Malkovich" + +#~ msgid "invoking method:" +#~ msgstr "Malkovich" + +#~ msgid "Close _without Saving" +#~ msgstr "Malkovich" + +#~ msgid "translator_credits" +#~ msgstr "Malkovich" + +#~ msgid "About %s." +#~ msgstr "Malkovich" + +#~ msgid "%s: (c) 2003 The National Gallery" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s comes with ABSOLUTELY NO WARRANTY. This is free software\n" +#~ "and you are welcome to redistribute it under certain conditions,\n" +#~ "see http://www.gnu.org." +#~ msgstr "Malkovich" + +#~ msgid "Homepage:" +#~ msgstr "Malkovich" + +#~ msgid "Linked to VIPS library version:" +#~ msgstr "Malkovich" + +#~ msgid "Temp files in:" +#~ msgstr "Malkovich" + +#~ msgid "Help page not found." +#~ msgstr "Malkovich" + +#~ msgid "No indexed help page found for tag \"%s\"" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Search for" +#~ msgstr "Malkovich" + +#~ msgid "Case sensitive" +#~ msgstr "Malkovich" + +#~ msgid "Regular expression" +#~ msgstr "Malkovich" + +#~ msgid "Search from start" +#~ msgstr "Malkovich" + +#~ msgid "Unable to open location." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Unable to open URL \"%s\"\n" +#~ "windows error code = %d" +#~ msgstr "Malkovich" + +#~ msgid "Browser window opened." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Opened window for URL:\n" +#~ "\n" +#~ " %s\n" +#~ "\n" +#~ "This may take a few seconds." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Attempted to launch browser with command:\n" +#~ "\n" +#~ " %s\n" +#~ "\n" +#~ "You can change this command in Preferences." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Opened window for URL:\n" +#~ "\n" +#~ " %s\n" +#~ "\n" +#~ "You may need to switch desktops to see the new window." +#~ msgstr "Malkovich" + +#~ msgid "Cancelling" +#~ msgstr "Malkovich" + +#~ msgid "Computing image." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Calculating all the pixels in an image. Press the\n" +#~ "Cancel button to stop computation early." +#~ msgstr "Malkovich" + +#~ msgid "Calculating" +#~ msgstr "Malkovich" + +#~ msgid "About %d seconds left" +#~ msgstr "Malkovich" + +#~ msgid "Thumbnails" +#~ msgstr "Malkovich" + +#~ msgid "Image files found in: \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Searching ..." +#~ msgstr "Malkovich" + +#~ msgid "Search incomplete!" +#~ msgstr "Malkovich" + +#~ msgid "Not implemented." +#~ msgstr "Malkovich" + +#~ msgid "Complex math ops not implemented." +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Macro error." +#~ msgstr "Malkovich" + +#~ msgid "Builtin \"%s\" takes %d argument." +#~ msgid_plural "Builtin \"%s\" takes %d arguments." +#~ msgstr[0] "Malkovich" +#~ msgstr[1] "Malkovich" + +#~ msgid "" +#~ "Argument %d to builtin \"%s\" should be \"%s\"\n" +#~ "you passed:\n" +#~ " %s" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Object %s\n" +#~ "is not a class." +#~ msgstr "Malkovich" + +#~ msgid "Member \"%s\" not found in class \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Too many arguments to superclass constructor \"%s\".\n" +#~ "No more than %d arguments are possible." +#~ msgstr "Malkovich" + +#~ msgid "Superclass constructor \"%s\" expects %d arguments, not %d." +#~ msgstr "Malkovich" + +#~ msgid "Bad superclass." +#~ msgstr "Malkovich" + +#~ msgid "Superclass constructor \"%s\" should have no secret arguments." +#~ msgstr "Malkovich" + +#~ msgid "First element in superclass of \"%s\" must be class or constructor." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Too many arguments to class constructor \"%s\".\n" +#~ "No more than %d arguments are possible." +#~ msgstr "Malkovich" + +#~ msgid "Class not found." +#~ msgstr "Malkovich" + +#~ msgid "Class \"%s\" not found." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Member \"%s\" of class \"%s\" should be of type \"%s\", instead it's:" +#~ msgstr "Malkovich" + +#~ msgid "_%s() not implemented for class \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Edit color \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Set color" +#~ msgstr "Malkovich" + +#~ msgid "Bad value." +#~ msgstr "Malkovich" + +#~ msgid "Double-click to edit this color, or drag-and-drop between colors" +#~ msgstr "Malkovich" + +#~ msgid "Name clash." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't create column \"%s\".\n" +#~ "A column with that name already exists." +#~ msgstr "Malkovich" + +#~ msgid "Empty column." +#~ msgstr "Malkovich" + +#~ msgid "There are no objects in the current column." +#~ msgstr "Malkovich" + +#~ msgid "Too few items." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "This column only has %d items, but %d items\n" +#~ "are needed by operation \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Save Column \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Name" +#~ msgstr "Malkovich" + +#~ msgid "Toolkit" +#~ msgstr "Malkovich" + +#~ msgid "Filename" +#~ msgstr "Malkovich" + +#~ msgid "Set menu item text here" +#~ msgstr "Malkovich" + +#~ msgid "Add to this toolkit" +#~ msgstr "Malkovich" + +#~ msgid "Store column in this file" +#~ msgstr "Malkovich" + +#~ msgid "New menu item from column \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Menuize" +#~ msgstr "Malkovich" + +#~ msgid "Edit caption, press enter to accept changes, press escape to cancel" +#~ msgstr "Malkovich" + +#~ msgid "Enter expressions here" +#~ msgstr "Malkovich" + +#~ msgid "Close the columnview" +#~ msgstr "Malkovich" + +#~ msgid "Open the columnview" +#~ msgstr "Malkovich" + +#~ msgid "Column menu" +#~ msgstr "Malkovich" + +#~ msgid "Edit caption ..." +#~ msgstr "Malkovich" + +#~ msgid "Select all" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Make column into menu item ..." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Left-drag to move, left-double-click to set title, right-click for menu" +#~ msgstr "Malkovich" + +#~ msgid "Delete the column" +#~ msgstr "Malkovich" + +#~ msgid "Too many shared nodes in graph." +#~ msgstr "Malkovich" + +#~ msgid "Disable optimisation, or raise MAX_RELOC" +#~ msgstr "Malkovich" + +#~ msgid "Member \"%s\" of class \"%s\" should have no arguments." +#~ msgstr "Malkovich" + +#~ msgid "Unable to find image range." +#~ msgstr "Malkovich" + +#~ msgid "Find image range failed." +#~ msgstr "Malkovich" + +#~ msgid "Convert menu" +#~ msgstr "Malkovich" + +#~ msgid "Scale" +#~ msgstr "Malkovich" + +#~ msgid "Interpret" +#~ msgstr "Malkovich" + +#~ msgid "Reset" +#~ msgstr "Malkovich" + +#~ msgid "value" +#~ msgstr "Malkovich" + +#~ msgid "zombie" +#~ msgstr "Malkovich" + +#~ msgid "workspace" +#~ msgstr "Malkovich" + +#~ msgid "workspace group" +#~ msgstr "Malkovich" + +#~ msgid "root symbol" +#~ msgstr "Malkovich" + +#~ msgid "external symbol" +#~ msgstr "Malkovich" + +#~ msgid "built-in symbol" +#~ msgstr "Malkovich" + +#~ msgid "Escape to cancel edit, press Return to accept edit and recalculate" +#~ msgstr "Malkovich" + +#~ msgid "top level" +#~ msgstr "Malkovich" + +#~ msgid "class" +#~ msgstr "Malkovich" + +#~ msgid "instance" +#~ msgstr "Malkovich" + +#~ msgid "definition" +#~ msgstr "Malkovich" + +#~ msgid "parameter \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "member" +#~ msgstr "Malkovich" + +#~ msgid "function" +#~ msgstr "Malkovich" + +#~ msgid "of" +#~ msgstr "Malkovich" + +#~ msgid "XML library error." +#~ msgstr "Malkovich" + +#~ msgid "model_save_filename: xmlNewDoc() failed" +#~ msgstr "Malkovich" + +#~ msgid "model_save_filename: xmlNewDocNode() failed" +#~ msgstr "Malkovich" + +#~ msgid "Save failed." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Save of %s \"%s\" to file \"%s\" failed.\n" +#~ "%s" +#~ msgstr "Malkovich" + +#~ msgid "filemodel_save_all: no save method" +#~ msgstr "Malkovich" + +#~ msgid "Load failed." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't load XML file \"%s\",\n" +#~ "it's not a %s save file" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't load XML file \"%s\",\n" +#~ "Unable to extract version information from namespace." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't load XML file \"%s\",\n" +#~ "The file does not contain a %s." +#~ msgstr "Malkovich" + +#~ msgid "Save %s %s" +#~ msgstr "Malkovich" + +#~ msgid "Save successful." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s \"%s\" successfully saved\n" +#~ "to file \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Object has been modified." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s \"%s\" has been modified since you loaded it\n" +#~ "from file \"%s\".\n" +#~ "Do you want to save your changes?" +#~ msgstr "Malkovich" + +#~ msgid "%s \"%s\" has been modified. Do you want to save your changes?" +#~ msgstr "Malkovich" + +#~ msgid "Select file" +#~ msgstr "Malkovich" + +#~ msgid "Choose" +#~ msgstr "Malkovich" + +#~ msgid "TIFF image files (*.tif, *.tiff)" +#~ msgstr "Malkovich" + +#~ msgid "JPEG image files (*.jpg, *.jpeg, *.jpe)" +#~ msgstr "Malkovich" + +#~ msgid "PNG image files (*.png)" +#~ msgstr "Malkovich" + +#~ msgid "VIPS image files (*.v)" +#~ msgstr "Malkovich" + +#~ msgid "PPM image files (*.ppm, *.pgm, *.pbm)" +#~ msgstr "Malkovich" + +#~ msgid "Workspace files (*.ws)" +#~ msgstr "Malkovich" + +#~ msgid "Recombination matrix files (*.rec)" +#~ msgstr "Malkovich" + +#~ msgid "Morphology matrix files (*.mor)" +#~ msgstr "Malkovich" + +#~ msgid "Convolution matrix files (*.con)" +#~ msgstr "Malkovich" + +#~ msgid "Matrix files (*.mat)" +#~ msgstr "Malkovich" + +#~ msgid "Definition files (*.def)" +#~ msgstr "Malkovich" + +#~ msgid "ICC profiles (*.icc, *.icm)" +#~ msgstr "Malkovich" + +#~ msgid "All files (*)" +#~ msgstr "Malkovich" + +#~ msgid "Unable to determine space free in \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "free in \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Increment filename" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "After Save, add 1 to the last number in the file name" +#~ msgstr "Malkovich" + +#~ msgid "Show thumbnails" +#~ msgstr "Malkovich" + +#~ msgid "Show thumbnails for files in this directory" +#~ msgstr "Malkovich" + +#~ msgid "Overwrite" +#~ msgstr "Malkovich" + +#~ msgid "File \"%s\" exists. OK to overwrite?" +#~ msgstr "Malkovich" + +#~ msgid "circular" +#~ msgstr "Malkovich" + +#~ msgid "circular to label %d" +#~ msgstr "Malkovich" + +#~ msgid "label %d" +#~ msgstr "Malkovich" + +#~ msgid "unevaluated" +#~ msgstr "Malkovich" + +#~ msgid "class (0x%x)" +#~ msgstr "Malkovich" + +#~ msgid "members" +#~ msgstr "Malkovich" + +#~ msgid "secret" +#~ msgstr "Malkovich" + +#~ msgid "from" +#~ msgstr "Malkovich" + +#~ msgid "NULL pointer" +#~ msgstr "Malkovich" + +#~ msgid "symbol" +#~ msgstr "Malkovich" + +#~ msgid "constructor" +#~ msgstr "Malkovich" + +#~ msgid "symref" +#~ msgstr "Malkovich" + +#~ msgid "compileref" +#~ msgstr "Malkovich" + +#~ msgid "image \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "tag \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "unknown element tag %d" +#~ msgstr "Malkovich" + +#~ msgid "Current:" +#~ msgstr "Malkovich" + +#~ msgid "Directories" +#~ msgstr "Malkovich" + +#~ msgid "Files" +#~ msgstr "Malkovich" + +#~ msgid "Selection:" +#~ msgstr "Malkovich" + +#~ msgid "Create Dir" +#~ msgstr "Malkovich" + +#~ msgid "Delete File" +#~ msgstr "Malkovich" + +#~ msgid "Rename File" +#~ msgstr "Malkovich" + +#~ msgid "Error" +#~ msgstr "Malkovich" + +#~ msgid "Close" +#~ msgstr "Malkovich" + +#~ msgid "Create Directory" +#~ msgstr "Malkovich" + +#~ msgid "Directory name:" +#~ msgstr "Malkovich" + +#~ msgid "Create" +#~ msgstr "Malkovich" + +#~ msgid "Cancel" +#~ msgstr "Malkovich" + +#~ msgid "Delete" +#~ msgstr "Malkovich" + +#~ msgid "Rename" +#~ msgstr "Malkovich" + +#~ msgid "Directory unreadable: " +#~ msgstr "Malkovich" + +#~ msgid "Current: " +#~ msgstr "Malkovich" + +#~ msgid "Bad identifier." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Enter an identifier. Identifiers start with\n" +#~ "a letter, and then contain only letters, numbers,\n" +#~ "apostrophy and underscore." +#~ msgstr "Malkovich" + +#~ msgid "Bad floating point number." +#~ msgstr "Malkovich" + +#~ msgid "\"%s\" is not a floating point number." +#~ msgstr "Malkovich" + +#~ msgid "Bad integer." +#~ msgstr "Malkovich" + +#~ msgid "\"%s\" is not an integer." +#~ msgstr "Malkovich" + +#~ msgid "Bad unsigned integer." +#~ msgstr "Malkovich" + +#~ msgid "Bad positive integer." +#~ msgstr "Malkovich" + +#~ msgid "Left-click to change value" +#~ msgstr "Malkovich" + +#~ msgid "Heap full." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "The main calculation heap has filled. Raise\n" +#~ "the heap size limit in the Preferences panel." +#~ msgstr "Malkovich" + +#~ msgid "Typecheck error." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Expected %s, instead saw:\n" +#~ " %s" +#~ msgstr "Malkovich" + +#~ msgid "on" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "at %d" +#~ msgstr "Malkovich" + +#~ msgid "at (%d, %d)" +#~ msgstr "Malkovich" + +#~ msgid "at (%d, %d), offset (%d, %d)" +#~ msgstr "Malkovich" + +#~ msgid "Pin up" +#~ msgstr "Malkovich" + +#~ msgid "Check this to pin the dialog up" +#~ msgstr "Malkovich" + +#~ msgid "Save Image \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Replace Image \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Double-click to open a viewer on this thumbnail" +#~ msgstr "Malkovich" + +#~ msgid "Unable to open image \"%s\" for read." +#~ msgstr "Malkovich" + +#~ msgid "Unable to write to file." +#~ msgstr "Malkovich" + +#~ msgid "File \"%s\" is already open for read." +#~ msgstr "Malkovich" + +#~ msgid "Error writing image to file \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Unable to paint on image." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Unable to get write permission for file \"%s\".\n" +#~ "Check permission settings." +#~ msgstr "Malkovich" + +#~ msgid "Modify" +#~ msgstr "Malkovich" + +#~ msgid "Modify disc file?" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "This image is being shown directly from the disc file:\n" +#~ "\n" +#~ " %s\n" +#~ "\n" +#~ "If you paint on this file, it will be permanently changed.\n" +#~ "If something goes wrong, you may lose work.\n" +#~ "Are you sure you want to modify this file?" +#~ msgstr "Malkovich" + +#~ msgid "No image value" +#~ msgstr "Malkovich" + +#~ msgid "%dx%d %s pixels, %d band, %s" +#~ msgid_plural "%dx%d %s pixels, %d bands, %s" +#~ msgstr[0] "Malkovich" +#~ msgstr[1] "Malkovich" + +#~ msgid "Ruler menu" +#~ msgstr "Malkovich" + +#~ msgid "Header for \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "OK" +#~ msgstr "Malkovich" + +#~ msgid "/_File" +#~ msgstr "Malkovich" + +#~ msgid "/File/_New" +#~ msgstr "Malkovich" + +#~ msgid "/File/New/_Point" +#~ msgstr "Malkovich" + +#~ msgid "/File/New/_HGuide" +#~ msgstr "Malkovich" + +#~ msgid "/File/New/_VGuide" +#~ msgstr "Malkovich" + +#~ msgid "/File/New/_Arrow" +#~ msgstr "Malkovich" + +#~ msgid "/File/New/_Region" +#~ msgstr "Malkovich" + +#~ msgid "/File/sep3" +#~ msgstr "Malkovich" + +#~ msgid "/File/_Replace Image ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Save Image As ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/sep1" +#~ msgstr "Malkovich" + +#~ msgid "/File/Image _Header" +#~ msgstr "Malkovich" + +#~ msgid "/File/Re_calculate Image" +#~ msgstr "Malkovich" + +#~ msgid "/File/sep2" +#~ msgstr "Malkovich" + +#~ msgid "/File/_Close" +#~ msgstr "Malkovich" + +#~ msgid "/_View" +#~ msgstr "Malkovich" + +#~ msgid "/View/_Toolbar" +#~ msgstr "Malkovich" + +#~ msgid "/View/Toolbar/_Status" +#~ msgstr "Malkovich" + +#~ msgid "/View/Toolbar/_Display control" +#~ msgstr "Malkovich" + +#~ msgid "/View/Toolbar/_Paint" +#~ msgstr "Malkovich" + +#~ msgid "/View/Toolbar/_Rulers" +#~ msgstr "Malkovich" + +#~ msgid "/View/M_ode" +#~ msgstr "Malkovich" + +#~ msgid "/View/Mode/_Select" +#~ msgstr "Malkovich" + +#~ msgid "/View/Mode/_Pan" +#~ msgstr "Malkovich" + +#~ msgid "/View/Mode/Zoom _In" +#~ msgstr "Malkovich" + +#~ msgid "/View/Mode/Zoom _Out" +#~ msgstr "Malkovich" + +#~ msgid "/View/Mode/P_aint" +#~ msgstr "Malkovich" + +#~ msgid "/View/sep1" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom _In" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom _Out" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom _100%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom to _Fit" +#~ msgstr "Malkovich" + +#~ msgid "/View/_Zoom" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/6%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/12%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/25%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/50%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/100%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/200%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/400%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/800%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/1600%" +#~ msgstr "Malkovich" + +#~ msgid "/_Help" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Image View" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Paint Bar" +#~ msgstr "Malkovich" + +#~ msgid "Left" +#~ msgstr "Malkovich" + +#~ msgid "Top" +#~ msgstr "Malkovich" + +#~ msgid "Width" +#~ msgstr "Malkovich" + +#~ msgid "Height" +#~ msgstr "Malkovich" + +#~ msgid "Left edge of region" +#~ msgstr "Malkovich" + +#~ msgid "Top edge of region" +#~ msgstr "Malkovich" + +#~ msgid "Width of region" +#~ msgstr "Malkovich" + +#~ msgid "Height of region" +#~ msgstr "Malkovich" + +#~ msgid "Edit \"%s\"" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Set Region" +#~ msgstr "Malkovich" + +#~ msgid "at (%d, %d), size (%d, %d)" +#~ msgstr "Malkovich" + +#~ msgid "%dx%d %s pixels, %d band" +#~ msgid_plural "%dx%d %s pixels, %d bands" +#~ msgstr[0] "Malkovich" +#~ msgstr[1] "Malkovich" + +#~ msgid "scope \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "reference to symbol \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Press Escape to cancel edit, press Return to accept edit and recalculate" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Value display\n" +#~ "Left-click to edit expression" +#~ msgstr "Malkovich" + +#~ msgid "line too long" +#~ msgstr "Malkovich" + +#~ msgid "end of line inside string" +#~ msgstr "Malkovich" + +#~ msgid "no end of string" +#~ msgstr "Malkovich" + +#~ msgid "no end of comment!" +#~ msgstr "Malkovich" + +#~ msgid "bad char constant" +#~ msgstr "Malkovich" + +#~ msgid "illegal character \"%c\"" +#~ msgstr "Malkovich" + +#~ msgid "Circular dependency." +#~ msgstr "Malkovich" + +#~ msgid "Circular dependency detected near symbol \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s: usage:\n" +#~ "%s filename1 filename2 ...\n" +#~ "\tstart in GUI mode, loading the named files" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s -script filename arg1 arg2 ...\n" +#~ "\tread in filename as a set of definitions, set list argv to\n" +#~ "\t[\"filename\", \"arg1\", \"arg2\", ...], set argc to length of list;\n" +#~ "\tprint the value of symbol \"main\" to stdout; exit; useful for\n" +#~ "\trunning %s as an interpreter on unix" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s -main arg1 arg2 ...\n" +#~ "\tas -script, but read the definitions from stdin rather than from a\n" +#~ "\tfile; useful for here-is shell scripts" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s -workspace arg1 arg2 ...\n" +#~ "\tas -main, but read a workspace save file instead; run in GUI mode\n" +#~ "\tprint the value of the final row in the save file on exit" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s -benchmark\n" +#~ "\tload all start objects and quit; useful for bechmarking the compiler" +#~ msgstr "Malkovich" + +#~ msgid "symbol \"main\" not found" +#~ msgstr "Malkovich" + +#~ msgid "symbol \"main\" has no value" +#~ msgstr "Malkovich" + +#~ msgid "Unknown file type." +#~ msgstr "Malkovich" + +#~ msgid "Unable to load \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Unable to load." +#~ msgstr "Malkovich" + +#~ msgid "Error loading plug-in \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Error loading plug-ins in directory \"%s/lib\"" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Ink dropper" +#~ msgstr "Malkovich" + +#~ msgid "Duplicate" +#~ msgstr "Malkovich" + +#~ msgid "Pen" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Line" +#~ msgstr "Malkovich" + +#~ msgid "Text" +#~ msgstr "Malkovich" + +#~ msgid "Smudge" +#~ msgstr "Malkovich" + +#~ msgid "Flood" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Flood Blob" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Fill Rectangle" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Pan" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Select" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "unable to change max file descriptors\n" +#~ "max file descriptors still set to %d" +#~ msgstr "Malkovich" + +#~ msgid "unable to read max file descriptors" +#~ msgstr "Malkovich" + +#~ msgid "-script needs an argument" +#~ msgstr "Malkovich" + +#~ msgid "Startup error." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Startup error log:\n" +#~ "%s" +#~ msgstr "Malkovich" + +#~ msgid "Welcome to %s-%s!" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "A new directory has been created in your home directory to hold startup,\n" +#~ "data and temporary files:\n" +#~ "\n" +#~ " %s\n" +#~ "\n" +#~ "If you've used previous versions of %s, you will probably want\n" +#~ "to move any files over from your old work area and remove any old temps." +#~ msgstr "Malkovich" + +#~ msgid "No temp area" +#~ msgstr "Malkovich" + +#~ msgid "%s free" +#~ msgstr "Malkovich" + +#~ msgid "%d cells free" +#~ msgstr "Malkovich" + +#~ msgid "Selected:" +#~ msgstr "Malkovich" + +#~ msgid "%s in \"%s\", %d cells in heap, %d cells free, %d cells maximum" +#~ msgstr "Malkovich" + +#~ msgid "modified" +#~ msgstr "Malkovich" + +#~ msgid "No objects selected." +#~ msgstr "Malkovich" + +#~ msgid "Find in workspace not implemented yet." +#~ msgstr "Malkovich" + +#~ msgid "Find again in workspace not implemented yet." +#~ msgstr "Malkovich" + +#~ msgid "There are no errors (that I can see) in this workspace." +#~ msgstr "Malkovich" + +#~ msgid "Recalculate" +#~ msgstr "Malkovich" + +#~ msgid "Completely recalculate?" +#~ msgstr "Malkovich" + +#~ msgid "Insert image" +#~ msgstr "Malkovich" + +#~ msgid "Merge workspace" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "No recent workspaces" +#~ msgstr "Malkovich" + +#~ msgid "Open workspace" +#~ msgstr "Malkovich" + +#~ msgid "Caption" +#~ msgstr "Malkovich" + +#~ msgid "Set workspace name here" +#~ msgstr "Malkovich" + +#~ msgid "Set workspace caption here" +#~ msgstr "Malkovich" + +#~ msgid "New Workspace" +#~ msgstr "Malkovich" + +#~ msgid "Create Workspace" +#~ msgstr "Malkovich" + +#~ msgid "Set column name here" +#~ msgstr "Malkovich" + +#~ msgid "Set column caption here" +#~ msgstr "Malkovich" + +#~ msgid "New Column" +#~ msgstr "Malkovich" + +#~ msgid "Create Column" +#~ msgstr "Malkovich" + +#~ msgid "Delete selected objects?" +#~ msgstr "Malkovich" + +#~ msgid "Are you sure you want to delete %s?" +#~ msgstr "Malkovich" + +#~ msgid "/File/_New ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Open ..." +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "/File/Open _Recent" +#~ msgstr "Malkovich" + +#~ msgid "/File/_Save" +#~ msgstr "Malkovich" + +#~ msgid "/File/_Save As ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Program" +#~ msgstr "Malkovich" + +#~ msgid "/File/Re_cover After Crash ..." +#~ msgstr "Malkovich" + +#~ msgid "/_Edit" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/Delete" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/Select All" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/Duplicate" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/sep2" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Find" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/Find _Next" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Jump To Next Error" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/sep4" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Group" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/U_ngroup" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Recalculate" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/sep3" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Preferences" +#~ msgstr "Malkovich" + +#~ msgid "/View/_Statusbar" +#~ msgstr "Malkovich" + +#~ msgid "/View/_Regular" +#~ msgstr "Malkovich" + +#~ msgid "/View/Show _Formula" +#~ msgstr "Malkovich" + +#~ msgid "/View/No _Edits" +#~ msgstr "Malkovich" + +#~ msgid "/_Insert" +#~ msgstr "Malkovich" + +#~ msgid "/Insert/New C_olumn ..." +#~ msgstr "Malkovich" + +#~ msgid "/Insert/sep6" +#~ msgstr "Malkovich" + +#~ msgid "/Insert/Ima_ge From File ..." +#~ msgstr "Malkovich" + +#~ msgid "/Insert/Workspace From File ..." +#~ msgstr "Malkovich" + +#~ msgid "/Insert/Matrix From File ..." +#~ msgstr "Malkovich" + +#~ msgid "/_Toolkits" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_About" +#~ msgstr "Malkovich" + +#~ msgid "/Help/sep7" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_This Window" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Users Guide" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Quick Tour" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Mosaic Tour" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Nerd Tour" +#~ msgstr "Malkovich" + +#~ msgid "/Help/M_enu Reference" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Configuration" +#~ msgstr "Malkovich" + +#~ msgid "/Help/sep6" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Go to VIPS Home Page" +#~ msgstr "Malkovich" + +#~ msgid "Open Workspace" +#~ msgstr "Malkovich" + +#~ msgid "Save Workspace As" +#~ msgstr "Malkovich" + +#~ msgid "Duplicate Workspace" +#~ msgstr "Malkovich" + +#~ msgid "Duplicate Selected Rows" +#~ msgstr "Malkovich" + +#~ msgid "Insert File" +#~ msgstr "Malkovich" + +#~ msgid "Sliders" +#~ msgstr "Malkovich" + +#~ msgid "Toggle buttons" +#~ msgstr "Malkovich" + +#~ msgid "Text, plus scale and offset" +#~ msgstr "Malkovich" + +#~ msgid "Display as" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Edit matrix \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Set matrix" +#~ msgstr "Malkovich" + +#~ msgid "Save Matrix \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Replace Matrix \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Cell (%d, %d):\n" +#~ "%s" +#~ msgstr "Malkovich" + +#~ msgid ": cell (%d, %d): %s" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Unable to load from file \"%s\".\n" +#~ "Error log is:\n" +#~ "%s" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Unable to load from \"%s\".\n" +#~ "Error log is:\n" +#~ "%s" +#~ msgstr "Malkovich" + +#~ msgid "model_save: xmlNewChild() failed" +#~ msgstr "Malkovich" + +#~ msgid "XML load error." +#~ msgstr "Malkovich" + +#~ msgid "Can't load node of type \"%s\" into object of type \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Delete?" +#~ msgstr "Malkovich" + +#~ msgid "Are you sure you want to delete %s \"%s\"?" +#~ msgstr "Malkovich" + +#~ msgid "No options." +#~ msgstr "Malkovich" + +#~ msgid "You need at least one option in your option list" +#~ msgstr "Malkovich" + +#~ msgid "Set option caption here" +#~ msgstr "Malkovich" + +#~ msgid "Options" +#~ msgstr "Malkovich" + +#~ msgid "Value" +#~ msgstr "Malkovich" + +#~ msgid "Set option default value here" +#~ msgstr "Malkovich" + +#~ msgid "Edit option" +#~ msgstr "Malkovich" + +#~ msgid "Set option" +#~ msgstr "Malkovich" + +#~ msgid "Orderlist menu" +#~ msgstr "Malkovich" + +#~ msgid "Delete item" +#~ msgstr "Malkovich" + +#~ msgid "Delete selected items" +#~ msgstr "Malkovich" + +#~ msgid "Delete all items" +#~ msgstr "Malkovich" + +#~ msgid "Current options - right button for menu" +#~ msgstr "Malkovich" + +#~ msgid "Enter new option fields here" +#~ msgstr "Malkovich" + +#~ msgid "Paintbox bar menu" +#~ msgstr "Malkovich" + +#~ msgid "Font not found." +#~ msgstr "Malkovich" + +#~ msgid "Font \"%s\" not found on system." +#~ msgstr "Malkovich" + +#~ msgid "Select font" +#~ msgstr "Malkovich" + +#~ msgid "Set font" +#~ msgstr "Malkovich" + +#~ msgid "Clear undo history?" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Are you sure you want to clear all undo and redo?\n" +#~ "This will free up memory, but you will no longer be\n" +#~ "able to undo or redo any of the painting you have\n" +#~ "done so far." +#~ msgstr "Malkovich" + +#~ msgid "1 round" +#~ msgstr "Malkovich" + +#~ msgid "2 round" +#~ msgstr "Malkovich" + +#~ msgid "3 round" +#~ msgstr "Malkovich" + +#~ msgid "4 round" +#~ msgstr "Malkovich" + +#~ msgid "5 round" +#~ msgstr "Malkovich" + +#~ msgid "6 round" +#~ msgstr "Malkovich" + +#~ msgid "10 round" +#~ msgstr "Malkovich" + +#~ msgid "2 italic" +#~ msgstr "Malkovich" + +#~ msgid "3 italic" +#~ msgstr "Malkovich" + +#~ msgid "4 italic" +#~ msgstr "Malkovich" + +#~ msgid "5 italic" +#~ msgstr "Malkovich" + +#~ msgid "6 italic" +#~ msgstr "Malkovich" + +#~ msgid "10 italic" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Pan window" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Zoom out" +#~ msgstr "Malkovich" + +#~ msgid "Undo last paint action" +#~ msgstr "Malkovich" + +#~ msgid "Redo last paint action" +#~ msgstr "Malkovich" + +#~ msgid "Clear all undo and redo buffers" +#~ msgstr "Malkovich" + +#~ msgid "Nib" +#~ msgstr "Malkovich" + +#~ msgid "Click to select font" +#~ msgstr "Malkovich" + +#~ msgid "Enter text for text tool" +#~ msgstr "Malkovich" + +#~ msgid "Error in %s: %s" +#~ msgstr "Malkovich" + +#~ msgid "definition is too long!" +#~ msgstr "Malkovich" + +#~ msgid "not top level" +#~ msgstr "Malkovich" + +#~ msgid "not strings" +#~ msgstr "Malkovich" + +#~ msgid "Not found." +#~ msgstr "Malkovich" + +#~ msgid "File \"%s\" not found on path" +#~ msgstr "Malkovich" + +#~ msgid "Revert to Defaults" +#~ msgstr "Malkovich" + +#~ msgid "Revert to installation defaults?" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Would you like to reset all preferences to their factory\n" +#~ "settings? This will delete any changes you have ever made\n" +#~ "to your preferences and may take a few seconds." +#~ msgstr "Malkovich" + +#~ msgid "Preferences" +#~ msgstr "Malkovich" + +#~ msgid "Unable to display preferences." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "No preferences workspace was found.\n" +#~ "Preferences probably failed to load when %s started." +#~ msgstr "Malkovich" + +#~ msgid "Program" +#~ msgstr "Malkovich" + +#~ msgid "Edit window" +#~ msgstr "Malkovich" + +#~ msgid "Menu item text" +#~ msgstr "Malkovich" + +#~ msgid "Load column from this file" +#~ msgstr "Malkovich" + +#~ msgid "Edit column item \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Set column item" +#~ msgstr "Malkovich" + +#~ msgid "Unable to save." +#~ msgstr "Malkovich" + +#~ msgid "You can only save toolkits, not tools." +#~ msgstr "Malkovich" + +#~ msgid "You can't save auto-generated toolkits." +#~ msgstr "Malkovich" + +#~ msgid "Toolkit tree menu" +#~ msgstr "Malkovich" + +#~ msgid "Edit ..." +#~ msgstr "Malkovich" + +#~ msgid "Tool changed." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Tool changed by someone else!\n" +#~ "You're going to loose edits unless you're careful." +#~ msgstr "Malkovich" + +#~ msgid "Bad drag." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Sorry, you can only drag tools between toolkits\n" +#~ "You can't reorder toolkits, you can't nest toolkits\n" +#~ "and you can't drag tools to the top level." +#~ msgstr "Malkovich" + +#~ msgid "Sorry, you can't drag to or from from pseudo toolkits." +#~ msgstr "Malkovich" + +#~ msgid "Set toolkit name here" +#~ msgstr "Malkovich" + +#~ msgid "Set toolkit caption here" +#~ msgstr "Malkovich" + +#~ msgid "New toolkit" +#~ msgstr "Malkovich" + +#~ msgid "Nothing selected." +#~ msgstr "Malkovich" + +#~ msgid "No toolkit selected." +#~ msgstr "Malkovich" + +#~ msgid "Display this name" +#~ msgstr "Malkovich" + +#~ msgid "Load definition" +#~ msgstr "Malkovich" + +#~ msgid "Reload" +#~ msgstr "Malkovich" + +#~ msgid "Reload startup objects?" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Would you like to reload all startup menus, workspaces\n" +#~ "and plugins now? This may take a few seconds." +#~ msgstr "Malkovich" + +#~ msgid "No tool selected" +#~ msgstr "Malkovich" + +#~ msgid "Bad regular expression." +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "No match found for \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Find in all toolkits" +#~ msgstr "Malkovich" + +#~ msgid "Enter search string here" +#~ msgstr "Malkovich" + +#~ msgid "No match found." +#~ msgstr "Malkovich" + +#~ msgid "No top-level symbol called \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Symbol \"%s\" has no tool inforation." +#~ msgstr "Malkovich" + +#~ msgid "Go to definition of this symbol" +#~ msgstr "Malkovich" + +#~ msgid "Go to definition" +#~ msgstr "Malkovich" + +#~ msgid "Object information." +#~ msgstr "Malkovich" + +#~ msgid "No unresolved symbols found." +#~ msgstr "Malkovich" + +#~ msgid "Link report." +#~ msgstr "Malkovich" + +#~ msgid "No documentation available." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "On-line documentation is only currently\n" +#~ "available for VIPS functions and nip builtins." +#~ msgstr "Malkovich" + +#~ msgid "/File/New/_Tool" +#~ msgstr "Malkovich" + +#~ msgid "/File/New/Tool_kit ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/New/Separator ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/New/Column Item ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/New/Program Window ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Open Toolkit ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Save Toolkit" +#~ msgstr "Malkovich" + +#~ msgid "/File/Save Toolkit _As ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/Pr_ocess Text ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Reload Start Stuff ..." +#~ msgstr "Malkovich" + +#~ msgid "/Edit/C_ut" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Copy" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Paste" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Select All" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/Delete This _Tool ..." +#~ msgstr "Malkovich" + +#~ msgid "/Edit/Delete This Tool_kit ..." +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Find ..." +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Jump to Definition of ..." +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Info ..." +#~ msgstr "Malkovich" + +#~ msgid "/_Debug" +#~ msgstr "Malkovich" + +#~ msgid "/Debug/_Trace ..." +#~ msgstr "Malkovich" + +#~ msgid "/Debug/_Link ..." +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Program ..." +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Documentation For This Tool ..." +#~ msgstr "Malkovich" + +#~ msgid "Overflow error." +#~ msgstr "Malkovich" + +#~ msgid "%s too long." +#~ msgstr "Malkovich" + +#~ msgid "Not rectangular." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Matrix of real is not rectangular.\n" +#~ "Found row of length %d, should be %d." +#~ msgstr "Malkovich" + +#~ msgid "List only has %d elements, unable to get element %d." +#~ msgstr "Malkovich" + +#~ msgid "No arguments allowed." +#~ msgstr "Malkovich" + +#~ msgid "Object \"%s\" should have no arguments." +#~ msgstr "Malkovich" + +#~ msgid "C stack overflow. Expression too complex." +#~ msgstr "Malkovich" + +#~ msgid "No value." +#~ msgstr "Malkovich" + +#~ msgid "Symbol \"%s\" has no value" +#~ msgstr "Malkovich" + +#~ msgid "Symbol \"%s\" is not defined." +#~ msgstr "Malkovich" + +#~ msgid "Can't duplicate." +#~ msgstr "Malkovich" + +#~ msgid "You can only duplicate top level regions." +#~ msgstr "Malkovich" + +#~ msgid "Can't delete." +#~ msgstr "Malkovich" + +#~ msgid "You can only delete top level regions." +#~ msgstr "Malkovich" + +#~ msgid "Delete Region?" +#~ msgstr "Malkovich" + +#~ msgid "Are you sure you want to delete Region \"%s\"?" +#~ msgstr "Malkovich" + +#~ msgid "Region menu" +#~ msgstr "Malkovich" + +#~ msgid "blocked on" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Left-click to select, shift-left-click to extend select, double-left-" +#~ "click to edit, right-click for menu, left-drag to move" +#~ msgstr "Malkovich" + +#~ msgid "You can only duplicate top level rows." +#~ msgstr "Malkovich" + +#~ msgid "You can only delete top level rows." +#~ msgstr "Malkovich" + +#~ msgid "Drag between columns not yet implemented." +#~ msgstr "Malkovich" + +#~ msgid "Row menu" +#~ msgstr "Malkovich" + +#~ msgid "Ungroup" +#~ msgstr "Malkovich" + +#~ msgid "Replace from file ..." +#~ msgstr "Malkovich" + +#~ msgid "Click to open or close class" +#~ msgstr "Malkovich" + +#~ msgid "Minimum" +#~ msgstr "Malkovich" + +#~ msgid "Maximum" +#~ msgstr "Malkovich" + +#~ msgid "Lower slider value" +#~ msgstr "Malkovich" + +#~ msgid "Upper slider value" +#~ msgstr "Malkovich" + +#~ msgid "Set slider value here" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Edit slider \"%s\"" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Set Slider" +#~ msgstr "Malkovich" + +#~ msgid "Status bar menu" +#~ msgstr "Malkovich" + +#~ msgid "Magnification" +#~ msgstr "Malkovich" + +#~ msgid "Attempt to redefine root symbol \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Name \"%s\" repeated in scope." +#~ msgstr "Malkovich" + +#~ msgid "Can't redefine %s \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Set toggle caption here" +#~ msgstr "Malkovich" + +#~ msgid "State" +#~ msgstr "Malkovich" + +#~ msgid "Set toggle state here" +#~ msgstr "Malkovich" + +#~ msgid "Edit toggle" +#~ msgstr "Malkovich" + +#~ msgid "Set toggle" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't create dialog with name \"%s\"\n" +#~ "an object with that name already exists in kit \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "tool \"%s\", toolkit \"%s\", refers to undefined symbol" +#~ msgstr "Malkovich" + +#~ msgid "Trace buffer stack overflow." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Clear" +#~ msgstr "Malkovich" + +#~ msgid "/View/Operators" +#~ msgstr "Malkovich" + +#~ msgid "/View/Builtin Functions" +#~ msgstr "Malkovich" + +#~ msgid "/View/Class Construction" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Trace ..." +#~ msgstr "Malkovich" + +#~ msgid "Trace" +#~ msgstr "Malkovich" + +#~ msgid "Slider value ... edit!" +#~ msgstr "Malkovich" + +#~ msgid "Left-drag to set number" +#~ msgstr "Malkovich" + +#~ msgid "VIPS library error." +#~ msgstr "Malkovich" + +#~ msgid "Unable to set XML property." +#~ msgstr "Malkovich" + +#~ msgid "Unable to set property \"%s\" to value \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "8-bit signed integer" +#~ msgstr "Malkovich" + +#~ msgid "16-bit unsigned integer" +#~ msgstr "Malkovich" + +#~ msgid "16-bit signed integer" +#~ msgstr "Malkovich" + +#~ msgid "32-bit unsigned integer" +#~ msgstr "Malkovich" + +#~ msgid "32-bit signed integer" +#~ msgstr "Malkovich" + +#~ msgid "32-bit float" +#~ msgstr "Malkovich" + +#~ msgid "64-bit float" +#~ msgstr "Malkovich" + +#~ msgid "128-bit complex" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ msgstr "Malkovich" + +#~ msgid "TIFF image" +#~ msgstr "Malkovich" + +#~ msgid "JPEG image" +#~ msgstr "Malkovich" + +#~ msgid "PNG image" +#~ msgstr "Malkovich" + +#~ msgid "PPM/PGM/PBM image" +#~ msgstr "Malkovich" + +#~ msgid "VIPS image" +#~ msgstr "Malkovich" + +#~ msgid "%s, %s, %s, %dx%d, %d band" +#~ msgid_plural "%s, %s, %s, %dx%d, %d bands" +#~ msgstr[0] "Malkovich" +#~ msgstr[1] "Malkovich" + +#~ msgid "Bad filename." +#~ msgstr "Malkovich" + +#~ msgid "Filenames may not contain ':' characters." +#~ msgstr "Malkovich" + +#~ msgid "Filename is too long." +#~ msgstr "Malkovich" + +#~ msgid "Filename contains only blank characters." +#~ msgstr "Malkovich" + +#~ msgid "Unable to open." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Unable to open file \"%s\" for writing.\n" +#~ "%s." +#~ msgstr "Malkovich" + +#~ msgid "Unable to write." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Unable to write to file \"%s\".\n" +#~ "%s." +#~ msgstr "Malkovich" + +#~ msgid "bytes" +#~ msgstr "Malkovich" + +#~ msgid "KB" +#~ msgstr "Malkovich" + +#~ msgid "MB" +#~ msgstr "Malkovich" + +#~ msgid "GB" +#~ msgstr "Malkovich" + +#~ msgid "TB" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "unable to make temporary file \"%s\"\n" +#~ "%s" +#~ msgstr "Malkovich" + +#~ msgid "Out of memory." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Request for %s of RAM triggered memory allocation\n" +#~ "failure." +#~ msgstr "Malkovich" + +#~ msgid "Unknown type." +#~ msgstr "Malkovich" + +#~ msgid "VIPS type \"%s\" not supported" +#~ msgstr "Malkovich" + +#~ msgid "Error calling library function \"%s\" (%s)." +#~ msgstr "Malkovich" + +#~ msgid "VIPS operator \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "%s, from package \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "\"%s\" takes %d argument:" +#~ msgid_plural "\"%s\" takes %d arguments:" +#~ msgstr[0] "Malkovich" +#~ msgstr[1] "Malkovich" + +#~ msgid "And produces %d result:" +#~ msgid_plural "And produces %d results" +#~ msgstr[0] "Malkovich" +#~ msgstr[1] "Malkovich" + +#~ msgid "flags:" +#~ msgstr "Malkovich" + +#~ msgid "PIO function" +#~ msgstr "Malkovich" + +#~ msgid "WIO function" +#~ msgstr "Malkovich" + +#~ msgid "coordinate transformer" +#~ msgstr "Malkovich" + +#~ msgid "no coordinate transformation" +#~ msgstr "Malkovich" + +#~ msgid "point-to-point operation" +#~ msgstr "Malkovich" + +#~ msgid "area operation" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Argument %d to \"%s\" is the wrong type.\n" +#~ "You passed:\n" +#~ " %s\n" +#~ "Usage:\n" +#~ " %s" +#~ msgstr "Malkovich" + +#~ msgid "doublevec" +#~ msgstr "Malkovich" + +#~ msgid "imagevec" +#~ msgstr "Malkovich" + +#~ msgid "Bad regular expression \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Select exactly one object and try again." +#~ msgstr "Malkovich" + +#~ msgid "More than one object selected." +#~ msgstr "Malkovich" + +#~ msgid "No backup workspaces found." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "You need to enable \"Auto workspace save\" in Preferences\n" +#~ "Before automatic recovery works" +#~ msgstr "Malkovich" + +#~ msgid "No suitable workspace save files found in \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Open workspace backup?" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Found workspace \"%s\",\n" +#~ "dated %sDo you want to recover this workspace?" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't create workspace \"%s\".\n" +#~ "A symbol with that name already exists." +#~ msgstr "Malkovich" + +#~ msgid "Default empty workspace" +#~ msgstr "Malkovich" + +#~ msgid "\"%s\" needs %d arguments, there are %d selected." +#~ msgstr "Malkovich" + +#~ msgid "Too many names selected." +#~ msgstr "Malkovich" + +#~ msgid "You can only remove top level rows." +#~ msgstr "Malkovich" + +#~ msgid "Not all selected objects are top level rows." +#~ msgstr "Malkovich" + +#~ msgid "Unable to ungroup." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "You can only ungroup lists (comma-separated lists of things enclosed in " +#~ "square brackets).\n" +#~ "Use Format=>Decompose to break compound objects into lists." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't create workspacegroup \"%s\".\n" +#~ "A symbol with that name already exists." +#~ msgstr "Malkovich" + +#~ msgid "No text specified." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Enter some text to paint in the entry widget at the top of the window." +#~ msgstr "Malkovich" + +#~ msgid "Edit regions (+CTRL to create)" +#~ msgstr "Malkovich" + +#~ msgid "Pan image (also use middle mouse button)" +#~ msgstr "Malkovich" + +#~ msgid "Zoom in (also 'i' key)" +#~ msgstr "Malkovich" + +#~ msgid "Zoom out (also 'o' key)" +#~ msgstr "Malkovich" + +#~ msgid "Ink:" +#~ msgstr "Malkovich" + +#~ msgid "Find" +#~ msgstr "Malkovich" + +#~ msgid "at %1$d" +#~ msgstr "Malkovich" + +#~ msgid "Tool" +#~ msgstr "Malkovich" + +#~ msgid "Range" +#~ msgstr "Malkovich" diff --git a/po/messages b/po/messages new file mode 100644 index 00000000..3e22b186 Binary files /dev/null and b/po/messages differ diff --git a/po/missing b/po/missing new file mode 100644 index 00000000..e69de29b diff --git a/python/Makefile.am b/python/Makefile.am new file mode 100644 index 00000000..f232c586 --- /dev/null +++ b/python/Makefile.am @@ -0,0 +1,5 @@ +SUBDIRS = \ + vipsCC + +EXTRA_DIST = \ + test diff --git a/python/test/testvipsCC.py b/python/test/testvipsCC.py new file mode 100755 index 00000000..c9ab443d --- /dev/null +++ b/python/test/testvipsCC.py @@ -0,0 +1,37 @@ +#!/usr/bin/python + +import sys + +# just need this for leaktesting +import gc + +from vipsCC import * + +if len (sys.argv) != 3: + print 'usage:', sys.argv[0], 'inputimage outputimage' + print '\tcalculate photographic negative of inputimage' + sys.exit (1) + +try: + a = VImage.VImage (sys.argv[1]) + b = a.invert () + c = b.lin ([1,2,3],[4,5,6]) + c.write (sys.argv[2]) +except VError.VError, e: + e.perror (sys.argv[0]) + +# we can get properties of VImage too +print 'inputimage is', a.Xsize (), 'pixels across' + +print 'starting shutdown ...' +del b +del a +del c +# sometimes have to do several GCs to get them all, not sure why +for i in range(10): + gc.collect () +print 'shutdown!' + +print 'leaked IMAGEs:' +VImage.im__print_all () +print 'done ... hopefully you saw no leaks' diff --git a/python/vipsCC/Makefile.am b/python/vipsCC/Makefile.am new file mode 100644 index 00000000..80e0b39b --- /dev/null +++ b/python/vipsCC/Makefile.am @@ -0,0 +1,46 @@ +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ @PYTHON_INCLUDES@ + +# we install to a directory inside the python area, since we are a module +vipsccdir = $(pyexecdir)/vipsCC + +vipscc_PYTHON = VImage.py VDisplay.py VError.py VMask.py __init__.py + +# need an expanded VImage.h ... SWIG's preprocessor b0rks on includes inside +# class definitions +vimagemodule.cxx: VImage.i + cpp -DSWIG -E $(top_srcdir)/include/vips/VImage.h > VImage.h + swig -python -c++ -interface $(@:.cxx=) -I$(top_srcdir)/include -o $@ $< + +vdisplaymodule.cxx: VDisplay.i + swig -python -c++ -interface $(@:.cxx=) -I$(top_srcdir)/include -o $@ $< + +verrormodule.cxx: VError.i + swig -python -c++ -interface $(@:.cxx=) -I$(top_srcdir)/include -o $@ $< + +vmaskmodule.cxx: VMask.i + swig -python -c++ -interface $(@:.cxx=) -I$(top_srcdir)/include -o $@ $< + +vipscc_LTLIBRARIES = vimagemodule.la vdisplaymodule.la verrormodule.la vmaskmodule.la + +vimagemodule_la_LDFLAGS = -module -avoid-version +vimagemodule_la_LIBADD = ../../libsrcCC/libvipsCC.la ../../libsrc/libvips.la $(VIPS_LIBS) +nodist_vimagemodule_la_SOURCES = vimagemodule.cxx + +vdisplaymodule_la_LDFLAGS = -module -avoid-version +vdisplaymodule_la_LIBADD = ../../libsrcCC/libvipsCC.la $(VIPS_LIBS) +nodist_vdisplaymodule_la_SOURCES = vdisplaymodule.cxx + +verrormodule_la_LDFLAGS = -module -avoid-version +verrormodule_la_LIBADD = ../../libsrcCC/libvipsCC.la $(VIPS_LIBS) +nodist_verrormodule_la_SOURCES = verrormodule.cxx + +vmaskmodule_la_LDFLAGS = -module -avoid-version +vmaskmodule_la_LIBADD = ../../libsrcCC/libvipsCC.la $(VIPS_LIBS) +nodist_vmaskmodule_la_SOURCES = vmaskmodule.cxx + +CLEANFILES = \ + vimagemodule.cxx VImage.h \ + verrormodule.cxx vdisplaymodule.cxx vmaskmodule.cxx \ + VImage.py VDisplay.py VError.py VMask.py + +EXTRA_DIST = VImage.i VDisplay.i VError.i VMask.i __init__.py diff --git a/python/vipsCC/VDisplay.i b/python/vipsCC/VDisplay.i new file mode 100644 index 00000000..7c880761 --- /dev/null +++ b/python/vipsCC/VDisplay.i @@ -0,0 +1,15 @@ +/* SWIG interface file for VDisplay. + */ + +%module VDisplay +%{ +#include +%} + +%import "VError.i" + +/* Need to override assignment to get refcounting working. + */ +%rename(__assign__) *::operator=; + +%include vips/VDisplay.h diff --git a/python/vipsCC/VError.i b/python/vipsCC/VError.i new file mode 100644 index 00000000..084eaa62 --- /dev/null +++ b/python/vipsCC/VError.i @@ -0,0 +1,12 @@ +/* SWIG interface file for VError. + */ + +%module VError +%{ +#include +%} + +%include "std_except.i" +%include "std_string.i" + +%include vips/VError.h diff --git a/python/vipsCC/VImage.i b/python/vipsCC/VImage.i new file mode 100644 index 00000000..6066d0a8 --- /dev/null +++ b/python/vipsCC/VImage.i @@ -0,0 +1,186 @@ +/* SWIG interface file for vipsCC7 + */ + +%module VImage +%{ +#include +%} +/* Need to override assignment to get refcounting working. + */ +%rename(__assign__) vips::VImage::operator=; + +%include "std_list.i" +%include "std_complex.i" +%include "std_vector.i" +%include "std_except.i" + +%import "VError.i" +%import "VMask.i" +%import "VDisplay.i" + +namespace std { + %template(IntVector) vector; + %template(DoubleVector) vector; + %template(ImageVector) vector; +} + +/* VImage defines a lot of other operator overloads ... but SWIGs autowrapping + * doesn't work well for them. Do by hand later. + */ + +/* Need the expanded VImage.h in this directory. SWIG b0rks on #includes + * inside class definitions. + */ +%include VImage.h + +/* Helper code for vips_init(). + */ +%{ +#include + +/* Turn on to print args. +#define DEBUG + */ + +/* Command-line args during parse. + */ +typedef struct _Args { + /* The n strings we alloc when we get from Python. + */ + int n; + char **str; + + /* argc/argv as processed by us. + */ + int argc; + char **argv; +} Args; + +#ifdef DEBUG +static void +args_print (Args *args) +{ + int i; + + printf ("args_print: argc = %d\n", args->argc); + // +1 so we print the trailing NULL too + for (i = 0; i < args->argc + 1; i++) + printf( "\t%2d)\t%s\n", i, args->argv[i]); +} +#endif /*DEBUG*/ + +static void +args_free (Args *args) +{ + int i; + + for (i = 0; i < args->n; i++) + IM_FREE (args->str[i]); + args->n = 0; + args->argc = 0; + IM_FREE (args->str); + IM_FREE (args->argv); + IM_FREE (args); +} + +/* Get argv/argc from python. + */ +static Args * +args_new (void) +{ + Args *args; + PyObject *av; + int i; + int n; + + args = g_new (Args, 1); + args->n = 0; + args->str = NULL; + args->argc = 0; + args->argv = NULL; + + if (!(av = PySys_GetObject ((char *) "argv"))) + return (args); + if (!PyList_Check (av)) { + PyErr_Warn (PyExc_Warning, "ignoring sys.argv: " + "it must be a list of strings"); + return (args); + } + + n = PyList_Size (av); + args->str = g_new (char *, n); + for (i = 0; i < n; i++) + args->str[i] = g_strdup + (PyString_AsString (PyList_GetItem (av, i))); + args->n = n; + + /* +1 for NULL termination. + */ + args->argc = n; + args->argv = g_new (char *, n + 1); + for (i = 0; i < n; i++) + args->argv[i] = args->str[i]; + args->argv[i] = NULL; + + return (args); +} + +static void +vips_fatal (const char *msg) +{ + char buf[256]; + + im_snprintf (buf, 256, "%s\n%s", msg, im_error_buffer()); + im_error_clear(); + Py_FatalError (buf); +} + +%} + +%init %{ +{ + Args *args; + + args = args_new (); + +#ifdef DEBUG + printf ("on startup:\n"); + args_print (args); +#endif /*DEBUG*/ + + if (im_init_world (args->argv[0])) { + args_free (args); + vips_fatal ("can't initialise module vips"); + } + + /* Now parse any GOptions. + */ + GError *error = NULL; + GOptionContext *context; + + context = g_option_context_new ("- vips"); + g_option_context_add_group (context, im_get_option_group()); + + if( !g_option_context_parse (context, + &args->argc, &args->argv, &error)) { + g_option_context_free (context); + args_free (args); + im_error( "vipsmodule", "%s", error->message); + g_error_free (error); + vips_fatal ("can't initialise module vips_core"); + } + g_option_context_free (context); + +#ifdef DEBUG + printf ("after parse:\n"); + args_print (args); +#endif /*DEBUG*/ + + // Write (possibly) modified argc/argv back again. + if (args->argv) + PySys_SetArgv (args->argc, args->argv); + + args_free (args); +} +%} + diff --git a/python/vipsCC/VMask.i b/python/vipsCC/VMask.i new file mode 100644 index 00000000..b274f425 --- /dev/null +++ b/python/vipsCC/VMask.i @@ -0,0 +1,34 @@ +/* SWIG interface file for VMask. + */ + +%module VMask +%{ +#include +%} + +%import "VError.i" +%import "VImage.i" + +/* Need to override assignment to get refcounting working. + */ +%rename(__assign__) *::operator=; + +/* [] is array subscript, as you'd expect. + */ +%rename(__index__) vips::VIMask::operator[]; +%rename(__index__) vips::VDMask::operator[]; + +/* () is 2d array subscript, how odd! + */ +%rename(__call__) vips::VIMask::operator(); +%rename(__call__) vips::VDMask::operator(); + +/* Type conversion operators renamed as functions. + */ +%rename(convert_VImage) vips::VIMask::operator vips::VImage; +%rename(convert_VImage) vips::VDMask::operator vips::VImage; + +%rename(convert_VIMask) vips::VDMask::operator vips::VIMask; +%rename(convert_VDMask) vips::VIMask::operator vips::VDMask; + +%include vips/VMask.h diff --git a/python/vipsCC/__init__.py b/python/vipsCC/__init__.py new file mode 100644 index 00000000..0b05d457 --- /dev/null +++ b/python/vipsCC/__init__.py @@ -0,0 +1 @@ +__all__=["VImage","VMask","VError","VDisplay"] diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..f15088b8 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,6 @@ + +SUBDIRS = \ + iofuncs \ + mosaicing \ + other \ + scripts diff --git a/src/iofuncs/Makefile.am b/src/iofuncs/Makefile.am new file mode 100644 index 00000000..89c8e5b2 --- /dev/null +++ b/src/iofuncs/Makefile.am @@ -0,0 +1,27 @@ +SUBDIRS = man1 + +bin_PROGRAMS = \ + vips \ + binfile \ + debugim \ + edvips \ + header \ + printlines + +vips_SOURCES = vips.c +binfile_SOURCES = binfile.c +debugim_SOURCES = debugim.c +edvips_SOURCES = edvips.c +header_SOURCES = header.c +printlines_SOURCES = printlines.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +AM_LDFLAGS = @LDFLAGS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libsrc/libvips.la @VIPS_LIBS@ + +install-exec-hook: + ${top_srcdir}/src/scripts/post_install ${DESTDIR}${bindir} + +uninstall-hook: + ${RM} ${bindir}/im_* + diff --git a/src/iofuncs/binfile.c b/src/iofuncs/binfile.c new file mode 100644 index 00000000..6238fbfc --- /dev/null +++ b/src/iofuncs/binfile.c @@ -0,0 +1,81 @@ +/* @(#) Command which adds a vasari header to a binary file + * @(#) The user must ensure that the size of the file is correct + * @(#) + * @(#) Usage: binfile infile outfile xs ys bands + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 31/07/1991 + * Modified on: + * 2/2/95 JC + * - ANSIfied + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *bin, *out; + int xs, ys, bands, offset; + + if( argc != 7 ) + error_exit( "usage: %s infile outfile xsize ysize bands offset", + argv[0] ); + + xs = atoi(argv[3]); + ys = atoi(argv[4]); + bands = atoi(argv[5]); + offset = atoi(argv[6]); + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if( !(out = im_open( argv[2], "w" )) ) + error_exit( "unable to open %s for output", argv[2] ); + if( !(bin = im_binfile( argv[1], xs, ys, bands, offset )) ) + error_exit( "unable to im_binfile" ); + if( im_copy( bin, out ) ) + error_exit( "unable to copy to %s", argv[2] ); + + im_close( out ); + im_close( bin ); + + return( 0 ); +} diff --git a/src/iofuncs/debugim.c b/src/iofuncs/debugim.c new file mode 100644 index 00000000..44796f0e --- /dev/null +++ b/src/iofuncs/debugim.c @@ -0,0 +1,69 @@ +/* @(#) Prints the values of a file + * @(#) Result is printed in stderr output + * @(#) + * @(#) Usage: debugim infile + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 03/08/1990 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *in; + + if( argc != 2 ) + error_exit( "usage: %s infile", argv[0] ); + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if( !(in = im_open( argv[1], "r" )) ) + error_exit( "unable to open %s for input", argv[1]); + + if( im_debugim( in ) ) + error_exit( "unable to im_debugim"); + + im_close( in ); + + return( 0 ); +} diff --git a/src/iofuncs/edvips.c b/src/iofuncs/edvips.c new file mode 100644 index 00000000..7aa73200 --- /dev/null +++ b/src/iofuncs/edvips.c @@ -0,0 +1,206 @@ +/* modify vips file header! - useful for setting resolution, coding... +very dangerous! +no way of setting non-used codes in variables like newxres +so need flags to show new parameter has been set.. boring +Copyright K.Martinez 30/6/93 +29/7/93 JC + -format added + - ==0 added to strcmp! +17/11/94 JC + - new header fields added +21/10/04 + - more header updates + +22/8/05 + - less-stupid-ified +20/9/05 + - rewritten with glib option parser, ready for xml options to go in + + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* We have to represent all header fields as char* so we can spot unset args + * safely. + */ +static char *xsize = NULL; +static char *ysize = NULL; +static char *bands = NULL; +static char *format = NULL; +static char *type = NULL; +static char *coding = NULL; +static char *xres = NULL; +static char *yres = NULL; +static char *xoffset = NULL; +static char *yoffset = NULL; +static gboolean setext = FALSE; + +static GOptionEntry entries[] = { + { "xsize", 'x', 0, G_OPTION_ARG_STRING, &xsize, + N_( "set Xsize to N" ), "N" }, + { "ysize", 'y', 0, G_OPTION_ARG_STRING, &ysize, + N_( "set Ysize to N" ), "N" }, + { "bands", 'b', 0, G_OPTION_ARG_STRING, &bands, + N_( "set Bands to N" ), "N" }, + { "format", 'f', 0, G_OPTION_ARG_STRING, &format, + N_( "set BandFmt to F (eg. IM_BANDFMT_UCHAR)" ), "F" }, + { "type", 't', 0, G_OPTION_ARG_STRING, &type, + N_( "set Type to T (eg. IM_TYPE_XYZ)" ), "T" }, + { "coding", 'c', 0, G_OPTION_ARG_STRING, &coding, + N_( "set Coding to C (eg. IM_CODING_LABQ)" ), "C" }, + { "xres", 'X', 0, G_OPTION_ARG_STRING, &xres, + N_( "set Xres to R pixels/mm" ), "R" }, + { "yres", 'Y', 0, G_OPTION_ARG_STRING, &yres, + N_( "set Yres to R pixels/mm" ), "R" }, + { "xoffset", 'u', 0, G_OPTION_ARG_STRING, &xoffset, + N_( "set Xoffset to N" ), "N" }, + { "yoffset", 'v', 0, G_OPTION_ARG_STRING, &yoffset, + N_( "set Yoffset to N" ), "N" }, + { "setext", 'e', 0, G_OPTION_ARG_NONE, &setext, + N_( "replace extension block with stdin" ), NULL }, + { NULL } +}; + +static void +parse_pint( char *arg, int *out ) +{ + /* Might as well set an upper limit. + */ + *out = atoi( arg ); + if( *out <= 0 || *out > 1000000 ) + error_exit( _( "'%s' is not a positive integer" ), arg ); +} + +int +main( int argc, char **argv ) +{ + GOptionContext *context; + GError *error = NULL; + IMAGE *im; + unsigned char header[IM_SIZEOF_HEADER]; + + if( im_init_world( argv[0] ) ) + error_exit( _( "unable to start VIPS" ) ); + + context = g_option_context_new( + _( "vipsfile - edit vipsfile header" ) ); + g_option_context_add_main_entries( context, entries, GETTEXT_PACKAGE ); + g_option_context_add_group( context, im_get_option_group() ); + if( !g_option_context_parse( context, &argc, &argv, &error ) ) { + if( error ) { + fprintf( stderr, "%s\n", error->message ); + g_error_free( error ); + } + + exit( -1 ); + } + if( argc != 2 ) { + fprintf( stderr, _( "usage: %s [OPTION...] vipsfile\n" ), + g_get_prgname() ); + exit( -1 ); + } + + if( !(im = im_init( argv[1] )) || + (im->fd = im__open_image_file( im->filename )) == -1 ) + error_exit( _( "could not open image %s" ), argv[1] ); + if( read( im->fd, header, IM_SIZEOF_HEADER ) != IM_SIZEOF_HEADER || + im__read_header_bytes( im, header ) ) + error_exit( _( "could not read VIPS header for %s" ), + im->filename ); + + if( xsize ) + parse_pint( xsize, &im->Xsize ); + if( ysize ) + parse_pint( ysize, &im->Ysize ); + if( bands ) + parse_pint( bands, &im->Bands ); + if( format ) { + if( (im->BandFmt = im_char2BandFmt( format )) < 0 ) + error_exit( _( "bad format %s" ), format ); + im->Bbits = im_bits_of_fmt( im->BandFmt ); + } + if( type ) { + if( (im->Type = im_char2Type( type )) < 0 ) + error_exit( _( "bad type %s" ), type ); + } + if( coding ) { + if( (im->Coding = im_char2Coding( coding )) < 0 ) + error_exit( _( "bad coding %s" ), coding ); + } + if( xres ) + im->Xres = atof( xres ); + if( yres ) + im->Yres = atof( yres ); + if( xoffset ) + im->Xoffset = atoi( xoffset ); + if( yoffset ) + im->Yoffset = atoi( yoffset ); + + if( lseek( im->fd, 0, SEEK_SET ) == (off_t) -1 ) + error_exit( _( "could not seek on %s" ), im->filename ); + if( im__write_header_bytes( im, header ) || + im__write( im->fd, header, IM_SIZEOF_HEADER ) ) + error_exit( _( "could not write to %s" ), im->filename ); + + if( setext ) { + char *xml; + unsigned int size; + + if( !(xml = im__file_read( stdin, "stdin", &size )) ) + error_exit( _( "could not get ext data" ) ); + + /* Strip trailing whitespace ... we can get stray \n at the + * end, eg. "echo | edvips --setext fred.v". + */ + while( size > 0 && isspace( xml[size - 1] ) ) + size -= 1; + + if( im__write_extension_block( im, xml, size ) ) + error_exit( _( "could not set extension" ) ); + im_free( xml ); + } + + im_close( im ); + + return( 0 ); +} + diff --git a/src/iofuncs/header.c b/src/iofuncs/header.c new file mode 100644 index 00000000..97f0d1ea --- /dev/null +++ b/src/iofuncs/header.c @@ -0,0 +1,174 @@ +/* @(#) Command; reads the header of a Vasari picture file. + * @(#) Usage: header vasari_file + * @(#) + * + * Copyright: Birkbeck College, History of Art Dept, London, VASARI project. + * + * Author: Nicos Dessipris + * Written on: 17/01/1990 + * Modified on : 17/04/1990, 2/6/93 K.Martinez + * 16/6/93 JC + * - now calls im_mmapin instead of bizzare bogosity + * 1/6/95 JC + * - extra field argument for testing particular bits of the header + * 29/10/98 JC + * - now uses im_open() + * 24/5/01 JC + * - uses im_tiff2vips_header() etc., for speed + * 7/5/03 JC + * - uses im_open_header() + * 1/8/05 + * - uses new header API, for great smallness + * 4/8/05 + * - back to plain im_open() now that's lazy enough for us + * 9/9/05 + * - display meta fields in save format, if possible + * 20/9/05 + * - new field name "getext" reads extension block + * 24/8/06 + * - use GOption, loop over args + * 4/1/07 + * - use im_history_get() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +static char *main_option_field = NULL; + +static GOptionEntry main_option[] = { + { "field", 'f', 0, G_OPTION_ARG_STRING, &main_option_field, + N_( "print value of FIELD (\"getext\" reads extension block, " + "\"Hist\" reads image history)" ), + "FIELD" }, + { NULL } +}; + +/* Print header, or parts of header. + */ +static int +print_header( IMAGE *im ) +{ + if( !main_option_field ) + im_printdesc( im ); + else if( strcmp( main_option_field, "getext" ) == 0 ) { + if( im__has_extension_block( im ) ) { + void *buf; + int size; + + if( !(buf = im__read_extension_block( im, &size )) ) + return( -1 ); + printf( "%s", (char *) buf ); + im_free( buf ); + } + } + else if( strcmp( main_option_field, "Hist" ) == 0 ) + printf( "%s", im_history_get( im ) ); + else { + GValue value = { 0 }; + GType type; + + if( im_header_get( im, main_option_field, &value ) ) + return( -1 ); + + /* Display the save form, if there is one. This was we display + * something useful for ICC profiles, xml fields, etc. + */ + type = G_VALUE_TYPE( &value ); + if( g_value_type_transformable( type, IM_TYPE_SAVE_STRING ) ) { + GValue save_value = { 0 }; + + g_value_init( &save_value, IM_TYPE_SAVE_STRING ); + if( !g_value_transform( &value, &save_value ) ) + return( -1 ); + printf( "%s\n", im_save_string_get( &save_value ) ); + g_value_unset( &save_value ); + } + else { + char *str_value; + + str_value = g_strdup_value_contents( &value ); + printf( "%s\n", str_value ); + g_free( str_value ); + } + + g_value_unset( &value ); + } + + return( 0 ); +} + +int +main( int argc, char *argv[] ) +{ + GOptionContext *context; + GError *error = NULL; + int i; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + context = g_option_context_new( _( "- print image header" ) ); + + g_option_context_add_main_entries( context, + main_option, GETTEXT_PACKAGE ); + g_option_context_add_group( context, im_get_option_group() ); + + if( !g_option_context_parse( context, &argc, &argv, &error ) ) { + if( error ) { + fprintf( stderr, "%s\n", error->message ); + g_error_free( error ); + } + + error_exit( "try \"%s --help\"", g_get_prgname() ); + } + + g_option_context_free( context ); + + for( i = 1; i < argc; i++ ) { + IMAGE *im; + + if( !(im = im_open( argv[i], "r" )) ) + error_exit( "unable to open %s", argv[i] ); + if( print_header( im ) ) + error_exit( _( "unable to print header of \"%s\"" ), + argv[i] ); + im_close( im ); + } + + return( 0 ); +} diff --git a/src/iofuncs/man1/Makefile.am b/src/iofuncs/man1/Makefile.am new file mode 100644 index 00000000..49d47d1b --- /dev/null +++ b/src/iofuncs/man1/Makefile.am @@ -0,0 +1,9 @@ +man_MANS = \ + debugim.1 \ + header.1 \ + vips.1 \ + binfile.1 \ + edvips.1 \ + printlines.1 + +EXTRA_DIST = ${man_MANS} diff --git a/src/iofuncs/man1/binfile.1 b/src/iofuncs/man1/binfile.1 new file mode 100644 index 00000000..81f7c62a --- /dev/null +++ b/src/iofuncs/man1/binfile.1 @@ -0,0 +1,19 @@ +.TH BINFILE 1 "11 April 1990" +.SH NAME +im_binfile \- convert raw binary files to VIPS format +.SH SYNOPSIS +.B binfile in out xs ys b +.SH DESCRIPTION +.B binfile +expects as input a raw UNIX file with its filename held by the string in +and writes it to the vasari image file out by filling properly the header +details. It is expected that the file has sizes xs by ys and +has b bands. It is an error if the file in has less than xs*ys*b data. +The program is unable to check whether the supplied xs, ys and b are correct. +.SH SEE\ ALSO +im_binfile(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/src/iofuncs/man1/debugim.1 b/src/iofuncs/man1/debugim.1 new file mode 100644 index 00000000..f1639b5b --- /dev/null +++ b/src/iofuncs/man1/debugim.1 @@ -0,0 +1,21 @@ +.TH DEBUGIM 1 "12 July 1990" +.SH NAME +debugim, printlines \- prints the raw image data of a vasari file format +.SH SYNOPSIS +debugim infile + +printlines infile +.SH DESCRIPTION +debugim prints at the standard error output the raw image data of a vasari +format file. This function is useful for debugging when applied on small +image files + +printlines prints at the standard error output the raw image data of a vasari +format together with the line no and the x location and the value(s) of each +pixel. +.SH SEE ALSO +im_intro(3X), im_debugim(3X), im_printlines(3X), vips2mask(1X). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 12/07/1990 diff --git a/src/iofuncs/man1/edvips.1 b/src/iofuncs/man1/edvips.1 new file mode 100644 index 00000000..73fd861e --- /dev/null +++ b/src/iofuncs/man1/edvips.1 @@ -0,0 +1,49 @@ +.TH EDVIPS 1 "30 June 1993" +.SH NAME +edvips \- edit header of a vips image file +.SH SYNOPSIS +.B edvips [OPTION...] vipsfile +.SH DESCRIPTION +.B edvips +alters a VIPS image file's header. This is useful for setting the resolution, +for example. + +The options are: + + -x, --xsize=N set Xsize to N + -y, --ysize=N set Ysize to N + -b, --bands=N set Bands to N + -f, --format=F set BandFmt to F (eg. IM_BANDFMT_UCHAR) + -t, --type=T set Type to T (eg. IM_TYPE_XYZ) + -c, --coding=C set Coding to C (eg. IM_CODING_LABQ) + -X, --xres=R set Xres to R pixels/mm + -Y, --yres=R set Yres to R pixels/mm + -u, --xoffset=N set Xoffset to N + -v, --yoffset=N set Yoffset to N + -e, --setext replace extension block with stdin + +Be very careful when changing Xsize, Ysize, BandFmt or Bands. edvips does no +checking! + +.SH EXAMPLES +To set the Xsize to 512 and Bands to 6: + + edvips --xsize=512 --bands=6 fred.v + +or + + edvips -x 512 -b 6 fred.v + +Extract the XML metadata from an image with +.B header(1), +edit it, and reattach with +.B edvips(1). + + header -f getext fred.v | sed s/banana/pineapple/ | edvips -e fred.v + +.SH RETURN VALUE +returns 0 on success and non-zero on error. +.SH SEE ALSO +header(1) +.SH COPYRIGHT +K. Martinez 1993 diff --git a/src/iofuncs/man1/header.1 b/src/iofuncs/man1/header.1 new file mode 100644 index 00000000..091da1be --- /dev/null +++ b/src/iofuncs/man1/header.1 @@ -0,0 +1,33 @@ +.TH HEADER 1 "12 July 1990" +.SH NAME +header \- prints information about an image file +.SH SYNOPSIS +header [OPTIONS ...] files ... +.SH DESCRIPTION +.B header(1) +prints image header fields to stdout. + +.SH OPTIONS +.TP +.B -f FIELD, --field=FIELD +Print value of +.B FIELD +from image header. The special field name getext prints +the VIPS extension block: the XML defining the image metadata. You can alter +this, then reattach with +.B edvips(1). + +.SH EXAMPLES + pineapple:~/CVS/vips-7.12/src/iofuncs john$ header -f Xsize ~/pics/*.v + 1024 + 1279 + 22865 + 1 + 256 + +.SH SEE ALSO +im_intro(3), edvips(1), im_printdesc(3), im_header_get(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 12/07/1990 diff --git a/src/iofuncs/man1/printlines.1 b/src/iofuncs/man1/printlines.1 new file mode 100644 index 00000000..a5f43318 --- /dev/null +++ b/src/iofuncs/man1/printlines.1 @@ -0,0 +1 @@ +.so man1/debugim.1 diff --git a/src/iofuncs/man1/vips.1 b/src/iofuncs/man1/vips.1 new file mode 100644 index 00000000..7ddc8ada --- /dev/null +++ b/src/iofuncs/man1/vips.1 @@ -0,0 +1,58 @@ +.TH VIPS 1 "30 June 1993" +.SH NAME +vips \- run vips function from UNIX command line +.SH SYNOPSIS +.B vips [flags] [command] [command-args] +.SH DESCRIPTION +.B vips(1) +is the VIPS universal main program. You can use it to run any VIPS function +from the command line, to query the VIPS function database, and to +maintain parts of the VIPS library. + +To run a VIPS function, the first argument should be the name of the function +and following arguments should be the function parameters. For example: + + example% vips im_invert lena.v lena2.v + +If you make a symbolic link to the vips executable named after an operation, +then you can use that link to call an operation directly. For example: + + example% ln -s vips im_invert + example% im_invert lena.v lena2.v + +.SH OPTIONS +.TP +.B -l PACKAGE, --list=PACKAGE +List operations defined in PACKAGE. + +.TP +.B -u OPERATION, --usage=OPERATION +Show usage message for OPERATION. + +.TP +.B -p PLUGIN, --plugin=PLUGIN +Load PLUGIN. Note that plugins in $VIPSHOME/lib are loaded automatically. + +.TP +.B -k, --links +Print link lines for all operations. + +.TP +.B -h PACKAGE, --cpph=PACKAGE +Print C++ header for PACKAGE. PACKAGE can also be a function name, or "all". + +.TP +.B -c PACKAGE, --cppc=PACKAGE +Print C++ binding for PACKAGE. PACKAGE can also be a function name, or "all". + +.TP +.B -s PACKAGE, --swig=PACKAGE +Print SWIG declaration for PACKAGE. PACKAGE can also be a function name, +or "all". + +.SH RETURN VALUE +returns 0 on success and non-zero on error. +.SH SEE ALSO +header(1) +.SH COPYRIGHT +The National Gallery and Birkbeck College, 1989-1996. diff --git a/src/iofuncs/printlines.c b/src/iofuncs/printlines.c new file mode 100644 index 00000000..8930bd01 --- /dev/null +++ b/src/iofuncs/printlines.c @@ -0,0 +1,70 @@ +/* @(#) Prints the values of a file + * @(#) Result is printed in stderr output + * @(#) + * @(#) Usage: printlines infile + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 03/08/1990 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *in; + + if ( (argc != 2)||(argv[1][0] == '-') ) + error_exit( "Usage:\n%s infile\n\n\ +Image is printed in stderr\n", argv[0]); + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if ((in = im_open(argv[1],"r")) == NULL) + error_exit("Unable to open %s for input", argv[1]); + + if (im_printlines(in) == -1) + error_exit("unable to im_printlines"); + + im_close(in); + + return(0); +} diff --git a/src/iofuncs/vips.c b/src/iofuncs/vips.c new file mode 100644 index 00000000..62bf60fa --- /dev/null +++ b/src/iofuncs/vips.c @@ -0,0 +1,944 @@ +/* VIPS universal main program. + * + * J. Cupitt, 8/4/93. + * 12/5/06 + * - use GOption. g_*_prgname() + * 16/7/06 + * - hmm, was broken for function name as argv1 case + * 11/7/06 + * - add "all" option to -l + * 14/7/06 + * - ignore "--" arguments. + * 2/9/06 + * - do less init ... im_init_world() does more now + * 18/8/06 + * - use IM_EXEEXT + * 16/10/06 + * - add --version + * 17/10/06 + * - add --swig + * - cleanups + * - remove --swig again, sigh + * - add throw() decls to C++ to help SWIG + * 14/1/07 + * - add --list packages + * 26/2/07 + * - add input *VEC arg types to C++ binding + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef OS_WIN32 +#define strcasecmp(a,b) _stricmp(a,b) +#endif + +static char *main_option_list = NULL; +static char *main_option_usage = NULL; +static char *main_option_plugin = NULL; +static gboolean main_option_links; +static char *main_option_cpph = NULL; +static char *main_option_cppc = NULL; +static gboolean *main_option_version; + +static GOptionEntry main_option[] = { + { "list", 'l', 0, G_OPTION_ARG_STRING, &main_option_list, + N_( "list operations in PACKAGE (or \"all\", \"packages\")" ), + "PACKAGE" }, + { "usage", 'u', 0, G_OPTION_ARG_STRING, &main_option_usage, + N_( "show usage message for OPERATION" ), + "OPERATION" }, + { "plugin", 'p', 0, G_OPTION_ARG_FILENAME, &main_option_plugin, + N_( "load PLUGIN" ), + "PLUGIN" }, + { "links", 'k', 0, G_OPTION_ARG_NONE, &main_option_links, + N_( "print link lines for all operations" ), NULL }, + { "cpph", 'h', 0, G_OPTION_ARG_STRING, &main_option_cpph, + N_( "print C++ decls for PACKAGE (or \"all\")" ), + "PACKAGE" }, + { "cppc", 'c', 0, G_OPTION_ARG_STRING, &main_option_cppc, + N_( "print C++ binding for PACKAGE (or \"all\")" ), + "PACKAGE" }, + { "version", 'v', 0, G_OPTION_ARG_NONE, &main_option_version, + N_( "print im_version_string" ), NULL }, + { NULL } +}; + +typedef void *(*map_name_fn)( im_function * ); + +/* Loop over a package. + */ +static void * +map_package( im_package *pack, map_name_fn fn ) +{ + int i; + void *result; + + for( i = 0; i < pack->nfuncs; i++ ) + if( (result = fn( pack->table[i] )) ) + return( result ); + + return( NULL ); +} + +/* Apply a function to a vips operation, or map over a package of operations. + */ +static void * +map_name( const char *name, map_name_fn fn ) +{ + im_package *pack; + im_function *func; + + if( strcmp( name, "all" ) == 0 ) + /* Do all packages. + */ + im_map_packages( (VSListMap2Fn) map_package, fn ); + else if( (pack = im_find_package( name )) ) + /* Do one package. + */ + map_package( pack, fn ); + else if( (func = im_find_function( name )) ) + /* Do a single function. + */ + fn( func ); + else { + im_error( "map_name", + _( "no package or function \"%s\"" ), name ); + return( fn ); + } + + return( NULL ); +} + +static void * +list_package( im_package *pack ) +{ + printf( "%-18s - %d operations\n", pack->name, pack->nfuncs ); + + return( NULL ); +} + +static void * +list_function( im_function *func ) +{ + printf( "%-18s - %s\n", func->name, _( func->desc ) ); + + return( NULL ); +} + +static void +print_list( const char *name ) +{ + if( strcmp( name, "packages" ) == 0 ) + im_map_packages( (VSListMap2Fn) list_package, NULL ); + else { + if( map_name( name, list_function ) ) + error_exit( "unknown package \"%s\"", name ); + } +} + +/* Is s1 a prefix of s2? + */ +static int +isprefix( const char *s1, const char *s2 ) +{ + while( *s1 && *s1 == *s2 ) { + s1++; + s2++; + } + + return( *s1 == '\0' ); +} + +/* Is s1 a postfix of s2? + */ +static int +ispostfix( const char *s1, const char *s2 ) +{ + int l1 = strlen( s1 ); + int l2 = strlen( s2 ); + + if( l2 < l1 ) + return( 0 ); + + return( strcasecmp( s1, s2 + l2 - l1 ) == 0 ); +} + +/* Print "ln -s" lines for this package. + */ +static void * +print_links( im_package *pack ) +{ + int i; + + for( i = 0; i < pack->nfuncs; i++ ) + printf( "rm -f %s" IM_EXEEXT "; " + "ln -s vips" IM_EXEEXT " %s" IM_EXEEXT "\n", + pack->table[i]->name, pack->table[i]->name ); + + return( NULL ); +} + +/* Does a function have any printing output? + */ +static int +has_print( im_function *fn ) +{ + int i; + + for( i = 0; i < fn->argc; i++ ) + if( fn->argv[i].print ) + return( -1 ); + + return( 0 ); +} + +/* Print a usage string from an im_function descriptor. + */ +static void +usage( im_function *fn ) +{ + int i; + im_package *pack = im_package_of_function( fn->name ); + + /* Don't print the prgname if we're being run as a symlink. + */ + fprintf( stderr, "usage: " ); + if( im_isprefix( "vips", g_get_prgname() ) ) + fprintf( stderr, "%s ", g_get_prgname() ); + fprintf( stderr, "%s ", fn->name ); + + /* Print args requiring command-line input. + */ + for( i = 0; i < fn->argc; i++ ) + if( fn->argv[i].desc->flags & IM_TYPE_ARG ) + fprintf( stderr, "%s ", fn->argv[i].name ); + + /* Print types of command line args. + */ + fprintf( stderr, "\nwhere:\n" ); + for( i = 0; i < fn->argc; i++ ) + if( fn->argv[i].desc->flags & IM_TYPE_ARG ) + fprintf( stderr, "\t%s is of type \"%s\"\n", + fn->argv[i].name, fn->argv[i].desc->type ); + + /* Print output print args. + */ + if( has_print( fn ) ) { + fprintf( stderr, "prints:\n" ); + for( i = 0; i < fn->argc; i++ ) + if( fn->argv[i].print ) + fprintf( stderr, "\t%s of type \"%s\"\n", + fn->argv[i].name, + fn->argv[i].desc->type ); + } + + /* Print description of this function, and package it comes from. + */ + fprintf( stderr, "%s", _( fn->desc ) ); + if( pack ) + fprintf( stderr, ", from package \"%s\"", pack->name ); + fprintf( stderr, "\n" ); + + /* Print any flags this function has. + */ + fprintf( stderr, "flags: " ); + if( fn->flags & IM_FN_PIO ) + fprintf( stderr, "(PIO function) " ); + else + fprintf( stderr, "(WIO function) " ); + if( fn->flags & IM_FN_TRANSFORM ) + fprintf( stderr, "(coordinate transformer) " ); + else + fprintf( stderr, "(no coordinate transformation) " ); + if( fn->flags & IM_FN_PTOP ) + fprintf( stderr, "(point-to-point operation) " ); + else + fprintf( stderr, "(area operation) " ); + if( fn->flags & IM_FN_NOCACHE ) + fprintf( stderr, "(nocache operation) " ); + else + fprintf( stderr, "(result can be cached) " ); + + fprintf( stderr, "\n" ); +} + +/* Convert VIPS type name to C++ type name. NULL for type unsupported by C++ + * layer. + */ +static char * +vips2cpp( im_type_desc *ty ) +{ + int k; + + /* VIPS types. + */ + static char *vtypes[] = { + IM_TYPE_DOUBLE, + IM_TYPE_INT, + IM_TYPE_COMPLEX, + IM_TYPE_STRING, + IM_TYPE_IMAGE, + IM_TYPE_IMASK, + IM_TYPE_DMASK, + IM_TYPE_DISPLAY, + IM_TYPE_IMAGEVEC, + IM_TYPE_DOUBLEVEC, + IM_TYPE_INTVEC + }; + + /* Corresponding C++ types. + */ + static char *ctypes[] = { + "double", + "int", + "std::complex", + "char*", + "VImage", + "VIMask", + "VDMask", + "VDisplay", + "std::vector", + "std::vector", + "std::vector" + }; + + for( k = 0; k < IM_NUMBER( vtypes ); k++ ) + if( strcmp( ty->type, vtypes[k] ) == 0 ) + return( ctypes[k] ); + + return( NULL ); +} + +/* Test a function definition for C++ suitability. + */ +static int +is_cppable( im_function *fn ) +{ + int j; + + /* Check we know all the types. + */ + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + if( !vips2cpp( ty ) ) + return( 0 ); + } + + /* We dont wrap output IMAGEVEC/DOUBLEVEC/INTVEC. + */ + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + if( ty->flags & IM_TYPE_OUTPUT ) + if( strcmp( ty->type, IM_TYPE_IMAGEVEC ) == 0 || + strcmp( ty->type, IM_TYPE_DOUBLEVEC ) == 0 || + strcmp( ty->type, IM_TYPE_INTVEC ) == 0 ) + return( 0 ); + } + + /* Must be at least one image argument (input or output) ... since we + * get inserted in the VImage class. Other funcs get wrapped by hand. + */ + for( j = 0; j < fn->argc; j++ ) + if( strcmp( fn->argv[j].desc->type, IM_TYPE_IMAGE ) == 0 ) + break; + if( j == fn->argc ) + return( 0 ); + + return( -1 ); +} + +/* Search for the first output arg, and the first IMAGE input arg. + */ +static void +find_ioargs( im_function *fn, int *ia, int *oa ) +{ + int j; + + /* Look for first output arg - this will be the result of the + * function. + */ + *oa = -1; + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + if( ty->flags & IM_TYPE_OUTPUT ) { + *oa = j; + break; + } + } + + /* Look for first input IMAGE arg. This will become the implicit + * "this" arg. + */ + *ia = -1; + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + if( !(ty->flags & IM_TYPE_OUTPUT) && + strcmp( ty->type, IM_TYPE_IMAGE ) == 0 ) { + *ia = j; + break; + } + } +} + +/* Turn a VIPS name into a C++ name. Eg. im_lintra_vec becomes lin. + */ +static void +c2cpp_name( const char *in, char *out ) +{ + /* chop off "im_" prefix. + */ + if( isprefix( "im_", in ) ) + strcpy( out, in + 3 ); + else + strcpy( out, in ); + + /* Drop "_vec" postfix (eg. so im_lintra_vec becomes lintra). We rely + * on overloading to distinguish conflicts. + */ + if( ispostfix( "_vec", out ) ) + out[strlen( out ) - 4] = '\0'; + + /* Drop "const" postfix (eg. so im_eorimageconst becomes eorimage). + */ + if( ispostfix( "const", out ) ) + out[strlen( out ) - 5] = '\0'; + + /* Drop "tra" postfix (eg. so im_costra becomes cos). + */ + if( ispostfix( "tra", out ) ) + out[strlen( out ) - 3] = '\0'; +} + +/* Print prototype for a function (ie. will be followed by code). + * + * Eg.: + * VImage VImage::lin( double a, double b ) throw( VError ) + */ +static void * +print_cppproto( im_function *fn ) +{ + int j; + char name[4096]; + int oa, ia; + int flg; + + /* If it's not cppable, do nothing. + */ + if( !is_cppable( fn ) ) + return( NULL ); + + /* Make C++ name. + */ + c2cpp_name( fn->name, name ); + + /* Find input and output args. + */ + find_ioargs( fn, &ia, &oa ); + + /* Print output type. + */ + if( oa == -1 ) + printf( "void " ); + else + printf( "%s ", vips2cpp( fn->argv[oa].desc ) ); + + printf( "VImage::%s(", name ); + + /* Print arg list. + */ + flg = 0; + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + /* Skip ia and oa. + */ + if( j == ia || j == oa ) + continue; + + /* Print arg type. + */ + if( flg ) + printf( ", %s", vips2cpp( ty ) ); + else { + printf( " %s", vips2cpp( ty ) ); + flg = 1; + } + + /* If it's an putput arg, print a "&" to make a reference + * argument. + */ + if( ty->flags & IM_TYPE_OUTPUT ) + printf( "&" ); + + /* Print arg name. + */ + printf( " %s", fn->argv[j].name ); + } + + /* End of arg list! + */ + if( flg ) + printf( " " ); + printf( ") throw( VError )\n" ); + + return( NULL ); +} + +/* Print cpp decl for a function. + * + * Eg. + * VImage lin( double, double ) throw( VError ); + */ +static void * +print_cppdecl( im_function *fn ) +{ + int j; + char name[4096]; + int oa, ia; + int flg; + + /* If it's not cppable, do nothing. + */ + if( !is_cppable( fn ) ) + return( NULL ); + + /* Make C++ name. + */ + c2cpp_name( fn->name, name ); + + /* Find input and output args. + */ + find_ioargs( fn, &ia, &oa ); + if( ia == -1 ) + /* No input image, so make it a static in the class + * declaration. + */ + printf( "static " ); + + /* Print output type. + */ + if( oa == -1 ) + printf( "void " ); + else + printf( "%s ", vips2cpp( fn->argv[oa].desc ) ); + + /* Print function name and start arg list. + */ + printf( "%s(", name ); + + /* Print arg list. + */ + flg = 0; + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + /* Skip ia and oa. + */ + if( j == ia || j == oa ) + continue; + + /* Print arg type. + */ + if( flg ) + printf( ", %s", vips2cpp( ty ) ); + else { + printf( " %s", vips2cpp( ty ) ); + flg = 1; + } + + /* If it's an putput arg, print a "&" to make a reference + * argument. + */ + if( ty->flags & IM_TYPE_OUTPUT ) + printf( "&" ); + } + + /* End of arg list! + */ + if( flg ) + printf( " " ); + + printf( ") throw( VError );\n" ); + + return( NULL ); +} + +static void +print_invec( int j, const char *arg, + const char *vips_name, const char *c_name, const char *extract ) +{ + printf( "\t((%s*) _vec.data(%d))->n = %s.size();\n", + vips_name, j, arg ); + printf( "\t((%s*) _vec.data(%d))->vec = new %s[%s.size()];\n", + vips_name, j, c_name, arg ); + printf( "\tfor( unsigned int i = 0; i < %s.size(); i++ )\n", + arg ); + printf( "\t\t((%s*) _vec.data(%d))->vec[i] = %s[i]%s;\n", + vips_name, j, arg, extract ); +} + +/* Print the definition for a function. + */ +static void * +print_cppdef( im_function *fn ) +{ + int j; + int ia, oa; + + /* If it's not cppable, do nothing. + */ + if( !is_cppable( fn ) ) + return( NULL ); + + find_ioargs( fn, &ia, &oa ); + + printf( "// %s: %s\n", fn->name, _( fn->desc ) ); + print_cppproto( fn ); + printf( "{\n" ); + + /* Declare the implicit input image. + */ + if( ia != -1 ) + printf( "\tVImage %s = *this;\n", fn->argv[ia].name ); + + /* Declare return value, if any. + */ + if( oa != -1 ) + printf( "\t%s %s;\n\n", + vips2cpp( fn->argv[oa].desc ), + fn->argv[oa].name ); + + /* Declare the arg vector. + */ + printf( "\tVargv _vec( \"%s\" );\n\n", fn->name ); + + /* Create the input args. + */ + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + /* Images are special - have to init the vector, even + * for output args. Have to translate VImage. + */ + if( strcmp( ty->type, IM_TYPE_IMAGE ) == 0 ) { + printf( "\t_vec.data(%d) = %s.image();\n", + j, fn->argv[j].name ); + continue; + } + + /* For output masks, we have to set an input filename. Not + * freed, so constant string is OK. + */ + if( (ty->flags & IM_TYPE_OUTPUT) && + (strcmp( ty->type, IM_TYPE_IMASK ) == 0 || + strcmp( ty->type, IM_TYPE_DMASK ) == 0) ) { + printf( "\t((im_mask_object*) _vec.data(%d))->name = " + "(char*)\"noname\";\n", j ); + continue; + } + + /* Skip other output args. + */ + if( ty->flags & IM_TYPE_OUTPUT ) + continue; + + if( strcmp( ty->type, IM_TYPE_IMASK ) == 0 ) + /* Mask types are different - have to use + * im_mask_object. + */ + printf( "\t((im_mask_object*) " + "_vec.data(%d))->mask = %s.mask().iptr;\n", + j, fn->argv[j].name ); + else if( strcmp( ty->type, IM_TYPE_DMASK ) == 0 ) + printf( "\t((im_mask_object*) " + "_vec.data(%d))->mask = %s.mask().dptr;\n", + j, fn->argv[j].name ); + else if( strcmp( ty->type, IM_TYPE_DISPLAY ) == 0 ) + /* Display have to use VDisplay. + */ + printf( "\t_vec.data(%d) = %s.disp();\n", + j, fn->argv[j].name ); + else if( strcmp( ty->type, IM_TYPE_STRING ) == 0 ) + /* Zap input strings directly into _vec. + */ + printf( "\t_vec.data(%d) = (im_object) %s;\n", + j, fn->argv[j].name ); + else if( strcmp( ty->type, IM_TYPE_IMAGEVEC ) == 0 ) + print_invec( j, fn->argv[j].name, + "im_imagevec_object", "IMAGE *", ".image()" ); + else if( strcmp( ty->type, IM_TYPE_DOUBLEVEC ) == 0 ) + print_invec( j, fn->argv[j].name, + "im_doublevec_object", "double", "" ); + else if( strcmp( ty->type, IM_TYPE_INTVEC ) == 0 ) + print_invec( j, fn->argv[j].name, + "im_intvec_object", "int", "" ); + else + /* Just use vips2cpp(). + */ + printf( "\t*((%s*) _vec.data(%d)) = %s;\n", + vips2cpp( ty ), j, fn->argv[j].name ); + } + + /* Call function. + */ + printf( "\t_vec.call();\n" ); + + /* Extract output args. + */ + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + /* Skip input args. + */ + if( !(ty->flags & IM_TYPE_OUTPUT) ) + continue; + + /* Skip images (done on input side, really). + */ + if( strcmp( ty->type, IM_TYPE_IMAGE ) == 0 ) + continue; + + if( strcmp( ty->type, IM_TYPE_IMASK ) == 0 || + strcmp( ty->type, IM_TYPE_DMASK ) == 0 ) + /* Mask types are different - have to use + * im_mask_object. + */ + printf( "\t%s.embed( (DOUBLEMASK *)((im_mask_object*)" + "_vec.data(%d))->mask );\n", + fn->argv[j].name, j ); + else if( strcmp( ty->type, IM_TYPE_STRING ) == 0 ) + /* Strings are grabbed out of the vec. + */ + printf( "\t%s = (char*) _vec.data(%d);\n", + fn->argv[j].name, j ); + else + /* Just use vips2cpp(). + */ + printf( "\t%s = *((%s*)_vec.data(%d));\n", + fn->argv[j].name, vips2cpp( ty ), j ); + } + + /* Note dependancies if out is an image and this function uses + * PIO. + */ + if( oa != -1 ) { + im_type_desc *ty = fn->argv[oa].desc; + + if( strcmp( ty->type, IM_TYPE_IMAGE ) == 0 && + (fn->flags & IM_FN_PIO) ) { + /* Loop for all input args again .. + */ + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty2 = fn->argv[j].desc; + + /* Skip output args. + */ + if( ty2->flags & IM_TYPE_OUTPUT ) + continue; + + /* Input image. + */ + if( strcmp( ty2->type, IM_TYPE_IMAGE ) == 0 ) + printf( "\t%s._ref->addref( " + "%s._ref );\n", + fn->argv[oa].name, + fn->argv[j].name ); + else if( strcmp( ty2->type, IM_TYPE_IMAGEVEC ) + == 0 ) { + /* The out depends on every image in + * the input vector. + */ + printf( "\tfor( unsigned int i = 0; " + "i < %s.size(); i++ )\n", + fn->argv[j].name ); + printf( "\t\t%s._ref->addref( " + "%s[i]._ref );\n", + fn->argv[oa].name, + fn->argv[j].name ); + } + } + } + } + + /* Return result. + */ + if( oa != -1 ) + printf( "\n\treturn( %s );\n", fn->argv[oa].name ); + + printf( "}\n\n" ); + + return( NULL ); +} + +/* Print C++ decls for function, package or all. + */ +static void +print_cppdecls( char *name ) +{ + printf( "// this file automatically generated from\n" + "// VIPS library %s\n", im_version_string() ); + + if( map_name( name, print_cppdecl ) ) + error_exit( "unknown package \"%s\"", name ); +} + +/* Print C++ bindings for function, package or all. + */ +static void +print_cppdefs( char *name ) +{ + printf( "// this file automatically generated from\n" + "// VIPS library %s\n", im_version_string() ); + + if( map_name( name, print_cppdef ) ) + error_exit( "unknown package \"%s\"", name ); +} + +/* VIPS universal main program. + */ +int +main( int argc, char **argv ) +{ + GOptionContext *context; + GError *error = NULL; + im_function *fn; + int i, j; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + context = g_option_context_new( _( "- VIPS driver program" ) ); + + g_option_context_add_main_entries( context, + main_option, GETTEXT_PACKAGE ); + g_option_context_add_group( context, im_get_option_group() ); + + if( !g_option_context_parse( context, &argc, &argv, &error ) ) { + if( error ) { + fprintf( stderr, "%s\n", error->message ); + g_error_free( error ); + } + + error_exit( "try \"%s --help\"", g_get_prgname() ); + } + + g_option_context_free( context ); + + if( main_option_plugin ) { + if( !im_load_plugin( main_option_plugin ) ) + error_exit( "unable to load plugin %s", + main_option_plugin ); + } + if( main_option_cpph ) + print_cppdecls( main_option_cpph ); + if( main_option_cppc ) + print_cppdefs( main_option_cppc ); + if( main_option_links ) + im_map_packages( (VSListMap2Fn) print_links, NULL ); + if( main_option_list ) + print_list( main_option_list ); + if( main_option_usage ) { + if( !(fn = im_find_function( main_option_usage )) ) + error_exit( "unknown operation %s", main_option_usage ); + usage( fn ); + } + if( main_option_version ) + printf( "vips-%s\n", im_version_string() ); + + /* Remove any "--" argument. If one of our arguments is a negative + * number, the user will need to have added the "--" flag to stop + * GOption parsing. But "--" is still passed down to us and we need to + * ignore it. + */ + for( i = 1; i < argc - 1; i++ ) + if( strcmp( argv[i], "--" ) == 0 ) { + for( j = i; j < argc; j++ ) + argv[j] = argv[j + 1]; + + argc -= 1; + } + + /* Should we try to run the thing we are named as? + */ + if( !im_isprefix( "vips", g_get_prgname() ) ) { + char name[256]; + + /* Drop any .exe suffix. + */ + im_strncpy( name, g_get_prgname(), 256 ); + if( ispostfix( ".exe", name ) ) + name[strlen( name ) - 4] = '\0'; + + /* If unknown, try with "im_" prepended. + */ + if( !(fn = im_find_function( name )) ) { + im_snprintf( name, 256, "im_%s", g_get_prgname() ); + if( ispostfix( ".exe", name ) ) + name[strlen( name ) - 4] = '\0'; + + if( !(fn = im_find_function( name )) ) + error_exit( "unknown function" ); + } + + /* Execute it! + */ + if( im_run_command( name, argc - 1, argv + 1 ) ) { + usage( fn ); + error_exit( "error calling function" ); + } + } + else if( argc > 1 ) { + /* Nope ... run the first arg instead. + */ + if( im_run_command( argv[1], argc - 2, argv + 2 ) ) { + if( !(fn = im_find_function( argv[1] )) ) + error_exit( "unknown function" ); + usage( fn ); + error_exit( "error calling function" ); + } + } + + im_close_plugins(); + + return( 0 ); +} diff --git a/src/mosaicing/Makefile.am b/src/mosaicing/Makefile.am new file mode 100644 index 00000000..f6e5911e --- /dev/null +++ b/src/mosaicing/Makefile.am @@ -0,0 +1,12 @@ + +bin_PROGRAMS = \ + find_mosaic \ + mergeup + +find_mosaic_SOURCES = find_mosaic.c +mergeup_SOURCES = mergeup.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +AM_LDFLAGS = @LDFLAGS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libsrc/libvips.la @VIPS_LIBS@ + diff --git a/src/mosaicing/find_mosaic.c b/src/mosaicing/find_mosaic.c new file mode 100644 index 00000000..46b62ec4 --- /dev/null +++ b/src/mosaicing/find_mosaic.c @@ -0,0 +1,430 @@ +/* Join together images. + * + * find_mosaic x y file_name .0x0.v .0x1.v ... + * + * Where the image has been take with patches named as + * + * . . + * . . + * .0x1.v .1x1.v .. + * .0x0.v .1x0.v .. + * + * Uses im__find_lroverlap and im__find_tboverlap routines to make .v. + * + * It stores the tie points between patches in a data_file. + * + * It uses partials on all IO by including tbmerge / lrmerge programs. + * + * + * Copyright (C) Feb./1995, Ahmed. Abbood + * National Gallery. London + * + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define NUM_FILES 1000 +#define MAXPOINTS 60 +int xoverlap; +int yoverlap; + +extern int im_lrmerge(); +extern int im_merge_analysis(); +extern int im__find_lroverlap(); +extern int im__find_tboverlap(); +static int file_ptr = 0; +static IMAGE *in[ NUM_FILES ]; + + + +/* Strategy: build a tree describing the sequence of joins we want. Walk the + * tree assigning temporary file names, compile the tree into a linear + * sequence of join commands. + */ + + + +/* Decoded file name info. + */ +static char *file_root = NULL; +static char *output_file = NULL; +static int width = 0; /* Number of frames across */ +static int height = 0; /* Number of frames down */ + +static int file_list[ NUM_FILES ]; + + + + +/* Find the root name of a file name. Return new, shorter, string. + */ +static char * +find_root( name ) +char *name; +{ char *out = strdup( name ); + char *p; + + /* Chop off '.v'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( NULL ); + } + *p = '\0'; + + /* Chop off nxn. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( NULL ); + } + *p = '\0'; + + return( out ); +} + +/* Find the x position of a file name (extract n from .nxm.v). + */ +static int +find_x( name ) +char *name; +{ int n; + char *p; + char *out = strdup( name ); + + /* Chop off '.v'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + *p = '\0'; + + /* Find '.nxm'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + /* Read out x posn. + */ + if( sscanf( p, ".%dx%*d", &n ) != 1 ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + return( n ); +} + +/* Find the y position of a file name (extract m from .nxm.v). + */ +static int +find_y( name ) +char *name; +{ int m; + char *p; + char *out = strdup( name ); + + /* Chop off '.v'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + *p = '\0'; + + /* Find '.nxm'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + /* Read out y posn. + */ + if( sscanf( p, ".%*dx%d", &m ) != 1 ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + free( out ); + return( m ); +} + + + + +static int +mosaic_analysis(int width, int height,IMAGE **inp, IMAGE *out, + int xoff, int yoff, int *vxdisp, int *vydisp,int *hxdisp, int *hydisp) { + + + +int i, j, dx, dy, curr_im, fx, fy; +int halfcorsize, halfareasize; +int mincorsize, minareasize; +int prev_row, curr_row, curr_disp_x, curr_disp_y; +double scale1, angle1, dx1, dy1; + + + curr_im = -1; + curr_disp_x = -1; + curr_disp_y = -1; + dy = -1; + for(i=0; i<=height; i++){ + for(j=0; j<=width; j++){ + ++curr_im; + halfcorsize = 5; + halfareasize = 14; + dx = xoff - inp[curr_im]->Xsize; + dy = yoff - inp[curr_im]->Ysize; + + if( ( j < width ) && ( width > 0 ) ){ + if( dx < 0 ){ + mincorsize = (int)(inp[curr_im]->Xsize + dx - 1)/6; + minareasize = (int)(inp[curr_im]->Xsize + dx + - 3*halfcorsize -1)/2 - mincorsize; + if(mincorsize > halfcorsize) + mincorsize = halfcorsize; + if( minareasize > 0 ){ + if( minareasize < halfareasize ){ + if( minareasize > + (int)(halfcorsize +(int)(halfcorsize/2 + 1))){ + halfareasize = minareasize; + } + else if(mincorsize > 2){ + halfcorsize=mincorsize; + halfareasize=(int)(mincorsize+mincorsize/2 +1); + } + } + } + } + + if( ( inp[curr_im]->Xsize < xoff ) || ( inp[curr_im+1]->Xsize < xoff ) || + ( inp[curr_im]->Ysize < yoff ) || ( inp[curr_im]->Ysize < yoff) ){ + ++curr_disp_x; + hxdisp[curr_disp_x] = 0; + hydisp[curr_disp_x] = 0; + } + else{ + if ( im__find_lroverlap(inp[curr_im], inp[curr_im+1], + out, 0, + (int)(inp[curr_im]->Xsize -xoff/2), + (int)(inp[curr_im]->Ysize /2), + (int)(xoff/2), (int)(inp[curr_im+1]->Ysize /2), + halfcorsize, halfareasize , &fx, &fy, + &scale1, &angle1, &dx1, &dy1 ) == -1 ) + error_exit("Unable to im__find_lroverlap"); + + ++curr_disp_x; + hxdisp[curr_disp_x] = inp[curr_im]->Xsize - xoff + fx; + hydisp[curr_disp_x] = fy; + } + } + } + if( ( i < height ) && ( height > 0 ) ){ + curr_row = curr_im+1+(int)(width/2); + prev_row = curr_im - width+(int)(width/2); + halfcorsize = 5; + halfareasize = 14; + + if( dy < 0){ + mincorsize = (int)(inp[prev_row]->Ysize + dy - 1)/6; + minareasize = (int)(inp[prev_row]->Ysize + dy + - 3*halfcorsize -1)/2 - mincorsize; + if(mincorsize > halfcorsize) + mincorsize = halfcorsize; + if( minareasize > 0 ){ + if( minareasize < halfareasize ){ + if( minareasize > + (int)(halfcorsize +(int)(halfcorsize/2 + 1))){ + halfareasize = minareasize; + } + else if(mincorsize > 2){ + halfcorsize=mincorsize; + halfareasize=(int)(mincorsize+mincorsize/2 +1); + } + } + } + } + if( ( inp[curr_row]->Xsize < xoff ) || ( inp[prev_row]->Xsize < xoff ) || + ( inp[curr_row]->Ysize < yoff ) || ( inp[prev_row]->Ysize < yoff ) ){ + ++curr_disp_y; + vxdisp[curr_disp_y] = 0; + vydisp[curr_disp_y] = 0; + } + else{ + if ( im__find_tboverlap(inp[prev_row], inp[curr_row], + out, 0, + (int)(inp[prev_row]->Xsize/2 ), + (int)(inp[prev_row]->Ysize - yoff/2 ), + (int)(inp[curr_row]->Xsize/2 ), (int)(yoff/2), + halfcorsize, halfareasize, &fx, &fy, + &scale1, &angle1, &dx1, &dy1 ) == -1 ) + error_exit("Unable to im__find_tboverlap"); + + + ++curr_disp_y; + vxdisp[curr_disp_y] = fx; + vydisp[curr_disp_y] = inp[prev_row]->Ysize - yoff + fy; + } + } + } + + + return ( 0 ); +} + + + +int +main( argc, argv ) +int argc; +char **argv; +{ + int i, n, j, k; + char name[ 1000 ]; + FILE *fp; + char *r; + IMAGE *out; + int vxdisp[NUM_FILES + 1] ; + int vydisp[NUM_FILES + 1] ; + int hxdisp[NUM_FILES + 1] ; + int hydisp[NUM_FILES + 1] ; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + /* Too many? + */ + if( argc > NUM_FILES + 1 ) + error_exit( "Too many files to merge" ); + for(i=0; i< NUM_FILES; i++) + file_list[i] = 0; + /* Too few? + */ + if( argc == 1 ) + error_exit( "usage: xoverlap yoverlap file_name " + ".0x0.v .0x1.v ..." ); + xoverlap = atoi(argv[1]); + yoverlap = atoi(argv[2]); + fp = fopen( argv[3] , "w" ); + + for( i = 4; i < argc; i++ ){ + /* Find/check root. + */ + if( !file_root ) { + file_root = find_root( argv[i] ); + if( !file_root ) + error_exit( "error at file_root" ); + } + else { + if( !(r = find_root( argv[i] )) ) + error_exit( "Error in reading parameters" ); + if( strcmp( r, file_root ) != 0 ) + error_exit( "Not all roots identical!" ); + } + + /* Read out position. + */ + if( (n = find_x( argv[i] )) < 0 ) + error_exit( "Error in reading file name" ); + if( n > width - 1 ) + width = n; + if( (n = find_y( argv[i] )) < 0 ) + error_exit( "Error in reading file name" ); + if( n > height - 1 ) + height = n; + + file_list[n] +=1; + } + + /* Make output name. and store them in an array. + */ + if( !(out = im_open( "tmp.v", "t" )) ) + error_exit("unable to open file for output"); + + file_ptr =0; + for(i=height; i>=0; i--) + for(j=0; j.0x0.v .0x1.v ... + * + * Where the image has been take with patches named as + * + * . . + * . . + * .0x1.v .1x1.v .. + * .0x0.v .1x0.v .. + * + * + * Tries to generate optimal join sequence. Does not require any intermidiate + * files for temporary storage. + * It uses partials on all IO by including tbmerge / lrmerge programs. + * + * + * Copyright (C) Feb./1995, Ahmed. Abbood + * National Gallery. London + * + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define NUM_FILES 1000 +#define MAXPOINTS 60 + +static int xoverlap; +static int yoverlap; + +static int file_ptr = 0; +static IMAGE *in[ NUM_FILES ]; + +/* Strategy: build a tree describing the sequence of joins we want. Walk the + * tree assigning temporary file names, compile the tree into a linear + * sequence of join commands. + */ + + + +/* Decoded file name info. + */ +static char *file_root = NULL; +static char *output_file = NULL; +static int width = 0; /* Number of frames across */ +static int height = 0; /* Number of frames down */ + +static int file_list[ NUM_FILES ]; + + +static int +im_phmerge( Rect *larea, Rect *rarea, Rect *outarea ) +{ + + Rect overlap; + + + /* Compute overlap. + */ + im_rect_intersectrect( larea, rarea, &overlap ); + + outarea->width = rarea->left + rarea->width; + outarea->height = overlap.height; + outarea->top = overlap.top; + outarea->left = larea->left; + + return( 0 ); +} + + +static int +im_pvmerge( Rect *tarea, Rect *barea, Rect *outarea ) +{ + + Rect overlap; + + + /* Compute overlap. + */ + im_rect_intersectrect( tarea, barea, &overlap ); + + outarea->width = overlap.width; + outarea->height = barea->top + barea->height ; + outarea->left = overlap.left; + outarea->top = tarea->top; + + return( 0 ); +} + + + + +static int +merge_analysis(int width,int height,IMAGE **in,int xoff, + int yoff,int *vxdisp,int *vydisp,int *hxdisp, + int *hydisp,Rect *hrect,Rect *vrect) +{ +int i,j; +int curr_im,offset; +int curr_x, curr_y; +Rect larea, rarea, barea; + + + + curr_im = -1; + curr_x = -1; + curr_y = -1; + for(i=0; i<=height; i++){ + for(j=0; j<=width; j++){ + ++curr_im; + if( width == 0 ){ + ++curr_x; + hrect[curr_x].width = in[curr_im]->Xsize; + hrect[curr_x].height= in[curr_im]->Ysize; + hrect[curr_x].top = 0; + hrect[curr_x].left = 0; + } + else{ + if( j == 0){ + ++curr_x; + + /* Area occupied by left image. + */ + larea.left = 0; + larea.top = 0; + larea.height = in[curr_im]->Ysize; + larea.width = in[curr_im]->Xsize; + /* Area occupied by right image. + */ + if( in[curr_im]->Xsize < xoff ) + offset = 0; + else + offset =xoff; + rarea.left = in[curr_im]->Xsize - (offset + hxdisp[curr_x]) ; + rarea.top = hydisp[curr_x]; + rarea.width = in[curr_im+1]->Xsize; + rarea.height = in[curr_im+1]->Ysize; + im_phmerge( &larea, &rarea, &hrect[curr_x] ); + } + else if( j < width ){ + ++curr_x; + + /* Area occupied by right image. + */ + if( in[curr_im+1]->Xsize < xoff ) + offset = 0; + else + offset =xoff; + + rarea.left = hrect[curr_x -1].width - (offset + hxdisp[curr_x]) ; + rarea.top = hydisp[curr_x]; + rarea.width = in[curr_im+1]->Xsize; + rarea.height = in[curr_im+1]->Ysize; + im_phmerge( &hrect[curr_x -1], &rarea, &hrect[curr_x] ); + } + } + } + if( i > 0 ){ + ++curr_y; + + /* Area occupied by bottom image in output. + */ + barea.left = vxdisp[curr_y]; + barea.width = hrect[curr_x].width; + barea.height = hrect[curr_x].height; + if( in[curr_x - width]->Ysize < yoff ) + offset = 0; + else + offset = yoff; + if( i == 1){ + barea.top = hrect[curr_x - width].height - offset - vydisp[curr_y] ; + im_pvmerge( &hrect[curr_x - width], &barea, &vrect[curr_y] ); + } + else{ + barea.top = vrect[curr_y - 1].height - yoff - vydisp[curr_y] ; + im_pvmerge( &vrect[curr_y -1], &barea, &vrect[curr_y] ); + } + } + } + + + return( 0 ); +} + +/* Find the root name of a file name. Return new, shorter, string. + */ +static char * +find_root( name ) +char *name; +{ char *out = strdup( name ); + char *p; + + /* Chop off '.v'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( NULL ); + } + *p = '\0'; + + /* Chop off nxn. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( NULL ); + } + *p = '\0'; + + return( out ); +} + +/* Find the x position of a file name (extract n from .nxm.v). + */ +static int +find_x( name ) +char *name; +{ int n; + char *p; + char *out = strdup( name ); + + /* Chop off '.v'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + *p = '\0'; + + /* Find '.nxm'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + /* Read out x posn. + */ + if( sscanf( p, ".%dx%*d", &n ) != 1 ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + return( n ); +} + +/* Find the y position of a file name (extract m from .nxm.v). + */ +static int +find_y( name ) +char *name; +{ int m; + char *p; + char *out = strdup( name ); + + /* Chop off '.v'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + *p = '\0'; + + /* Find '.nxm'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + /* Read out y posn. + */ + if( sscanf( p, ".%*dx%d", &m ) != 1 ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + free( out ); + return( m ); +} + + + + + + +/* Join two frames left-right. Have to open them and find their sizes. + */ +static int +join_leftright(IMAGE *left, IMAGE *right, IMAGE *out, int dx, int dy ) +{ + + if (im_lrmerge(left, right, out, dx, dy, 20) == -1){ + im_errormsg("mergeup: unable to run im_lrmerge"); + return( -1 ); + } +return( 0 ); +} + + +/* Join two frames up-down. Have to open them and find their sizes. +*/ +static int +join_updown( IMAGE *top, IMAGE *bottom, IMAGE *out, int dx, int dy ) +{ + if (im_tbmerge(top, bottom, out, dx, dy, 20) == -1){ + im_errormsg("mergeup: unable to run im_tbmerge"); + return( -1 ); + } + +return( 0 ); +} + + +static int +merge_up( int width, int height, IMAGE **inp, IMAGE *outp, int xoff, int yoff, + int *hxdisp, int *hydisp, Rect *vrect ) +{ + int dx,dy,first_row; + int i, j, partial_no, in_no; + IMAGE **p_img; + char name[29]; + int v_no, h_no; + + + + p_img = (IMAGE **) malloc(1 + 3 * width * height * sizeof(IMAGE *)); + if( p_img == NULL ){ + im_errormsg("mergeup: allocation failure in mergeup"); + return( -1 ); + } + partial_no = 0; + v_no = 0; + h_no = 0; + in_no = 0; + first_row = 0; + + if( (width == 0 ) && (height == 0 ) ){ + im_errormsg("mergeup: Need more than one image"); + return( -1 ); + } + + + for(i=0; i<=height; i++){ + for(j=0; j<=width; j++){ + p_img[partial_no] = inp[in_no]; + ++partial_no; + if( j != 0 ){ + im_snprintf( name, 29, "partial_img.%d.v",partial_no ); + if( !( p_img[partial_no] = im_open( name, "p" )) ){ + im_errormsg("mergeup: unable to open partial image"); + free(p_img); + return( -1 ); + } + ++partial_no; + dy = hydisp[h_no ] ; + dx = -p_img[partial_no-3]->Xsize + hxdisp[h_no] + xoff ; + + if( (height == 0) && ( j == width) ) + join_leftright( p_img[partial_no-3], + p_img[partial_no-2],outp,dx,dy ); + else + join_leftright( p_img[partial_no-3], + p_img[partial_no-2],p_img[partial_no-1],dx,dy ); + ++h_no; + } + ++in_no; + } + + if( first_row == 0) + first_row = partial_no - 1; + + if( ( i > 0 ) || ( height == 0) ){ + if( i < height ){ + im_snprintf( name, 29, "partial_img.%d.v", partial_no ); + if( !( p_img[partial_no] = im_open( name, "p" )) ){ + im_errormsg("mergeup: unable to open partial image"); + free(p_img); + return( -1 ); + } + ++partial_no; + + dy = -( vrect[v_no].height - p_img[partial_no-2]->Ysize ); + dx = vrect[v_no].left ; + + ++v_no; + join_updown( p_img[first_row], + p_img[partial_no-2], p_img[partial_no-1],dx,dy ); + first_row = partial_no-1; + } + else{ + dy = -( vrect[v_no].height - p_img[partial_no-1]->Ysize ); + dx = vrect[v_no].left ; + + join_updown( p_img[first_row], p_img[partial_no-1],outp,dx,dy ); + } + } + } +return( 0 ); +} + + + + + +int +main( argc, argv ) +int argc; +char **argv; +{ + int i, n, j, k; + char name[ 1000 ]; + FILE *fp; + char *r; + IMAGE *out; + int vxdisp[NUM_FILES + 1] ; + int vydisp[NUM_FILES + 1] ; + int hxdisp[NUM_FILES + 1] ; + int hydisp[NUM_FILES + 1] ; + Rect hrect[NUM_FILES]; + Rect vrect[NUM_FILES]; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + /* Too many? + */ + if( argc > NUM_FILES + 1 ) + error_exit( "Too many files to merge" ); + for(i=0; i< NUM_FILES; i++) + file_list[i] = 0; + /* Too few? + */ + if( argc == 1 ) + error_exit( "usage: xoverlap yoverlap file_name output_dir " + ".0x0.v .0x1.v ..." ); + xoverlap = atoi(argv[1]); + yoverlap = atoi(argv[2]); + fp = fopen( argv[3] , "r" ); + + for( i = 5; i < argc; i++ ){ + /* Find/check root. + */ + if( !file_root ) { + file_root = find_root( argv[i] ); + if( !file_root ) + error_exit( "error at file_root" ); + } + else { + if( !(r = find_root( argv[i] )) ) + error_exit( "Error in reading parameters" ); + if( strcmp( r, file_root ) != 0 ) + error_exit( "Not all roots identical!" ); + } + + /* Read out position. + */ + if( (n = find_x( argv[i] )) < 0 ) + error_exit( "Error in reading file name" ); + if( n > width - 1 ) + width = n; + if( (n = find_y( argv[i] )) < 0 ) + error_exit( "Error in reading file name" ); + if( n > height - 1 ) + height = n; + + file_list[n] +=1; + } + + /* Make output name. and store them in an array. + */ + im_snprintf( name, 1000, "%s/paint.hr.v", argv[4] ); + if( !(out = im_open( name, "w" )) ) + error_exit("unable to open file for output"); + + file_ptr =0; + for(i=height; i>=0; i--) + for(j=0; j +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *image, *matrix; + int xpos, ypos, xsize, ysize, dx, dy, flag; + + if (argc != 10) + error_exit("Usage:\n\ +%s image matrix xpos ypos xsize ysize dx dy flag\n\ +WARNING: The program overwrites the output file if the owner has rw access.", +argv[0]); + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + xpos = atoi(argv[3]); + ypos = atoi(argv[4]); + xsize = atoi(argv[5]); + ysize = atoi(argv[6]); + dx = atoi(argv[7]); + dy = atoi(argv[8]); + flag = atoi(argv[9]); + + if ( (image = im_open(argv[1],"r")) == NULL ) + error_exit("Unable to open %s for input", argv[1]); + + if ( (matrix = im_open(argv[2],"w")) == NULL ) + error_exit("Unable to open %s for output", argv[2]); + + if ( im_cooc_matrix(image, matrix, xpos, ypos, xsize, ysize, + dx, dy, flag) == -1 ) + error_exit("Unable to im_cooc_matrix"); + + if ( im_updatehist(matrix, argv[0], argc - 1, argv + 1) == -1) + error_exit("Unable to update history"); + + if ( ( im_close( image ) == -1 )||( im_close( matrix ) == -1 ) ) + error_exit("Unable to close %s or %s",argv[1], argv[2]); + + return(0); +} diff --git a/src/other/cooc_features.c b/src/other/cooc_features.c new file mode 100644 index 00000000..7b8a9ea6 --- /dev/null +++ b/src/other/cooc_features.c @@ -0,0 +1,86 @@ +/* @(#) Prints features of cooc to stdout + * @(#) Usage: cooc_features matrix + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 26/03/1991 + * Modified on: + * 16/6/93 J.Cupitt + * - stupid cooc_features externs removed + * - ANSIfied + * - print to stdout + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char *argv[] ) +{ + IMAGE *matrix; + double fasm, fent, fcor, fcon; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if( argc != 2 ) + error_exit( "usage: %s matrix_image", argv[0] ); + + if( !(matrix = im_open(argv[1],"r")) ) + error_exit( "unable to open %s for input", + argv[1] ); + + if( im_cooc_asm( matrix, &fasm ) ) + error_exit( "unable to im_cooc_asm" ); + + if( im_cooc_contrast( matrix, &fcon ) ) + error_exit( "unable to im_cooc_contrast"); + + if( im_cooc_entropy( matrix, &fent ) ) + error_exit( "unable to im_cooc_entropy"); + + if( im_cooc_correlation( matrix, &fcor ) ) + error_exit( "unable to im_cooc_correlation"); + + if( im_close( matrix ) ) + error_exit( "unable to close %s", argv[1]); + + printf( "cooc: ASM=%f, ENT=%f, COR=%f, CON=%f\n", + fasm, fent, fcor, fcon); + + return( 0 ); +} diff --git a/src/other/glds.c b/src/other/glds.c new file mode 100644 index 00000000..814df4ff --- /dev/null +++ b/src/other/glds.c @@ -0,0 +1,86 @@ +/* @(#) Creates a cooourrence matrix from an image + * @(#) Usage: glds image matrix xpos ypos xsize ysize dx dy + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 26/03/1991 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *image, *matrix; + int xpos, ypos, xsize, ysize, dx, dy; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if (argc != 9) + error_exit("Usage:\n\ +%s image matrix xpos ypos xsize ysize dx dy\n\ +WARNING: The program overwrites the output file if the owner has rw access.", +argv[0]); + + xpos = atoi(argv[3]); + ypos = atoi(argv[4]); + xsize = atoi(argv[5]); + ysize = atoi(argv[6]); + dx = atoi(argv[7]); + dy = atoi(argv[8]); + + if ( (image = im_open(argv[1],"r")) == NULL ) + error_exit("Unable to open %s for input", argv[1]); + + if ( (matrix = im_open(argv[2],"w")) == NULL ) + error_exit("Unable to open %s for output", argv[2]); + + if ( im_glds_matrix(image, matrix, xpos, ypos, xsize, ysize, + dx, dy) == -1 ) + error_exit("Unable to im_glds_matrix"); + + if ( im_updatehist(image, argv[0], argc - 1, argv + 1) == -1) + error_exit("Unable to update history"); + + if ( ( im_close( image ) == -1 )||( im_close( matrix ) == -1 ) ) + error_exit("Unable to close %s or %s", argv[1], argv[2]); + + return(0); +} diff --git a/src/other/glds_features.c b/src/other/glds_features.c new file mode 100644 index 00000000..07aff552 --- /dev/null +++ b/src/other/glds_features.c @@ -0,0 +1,83 @@ +/* @(#) Prints features of glds in stderr + * @(#) Usage: glds_features matrix + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 26/03/1991 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char *argv[] ) +{ + IMAGE *matrix; + double fasm, fent, fmean, fcon; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if( argc != 2 ) + error_exit( "usage: %s matrix_image", argv[0] ); + + + if( !(matrix = im_open(argv[1],"r")) ) + error_exit( "unable to open %s for input", + argv[1] ); + + if( im_glds_asm( matrix, &fasm ) ) + error_exit( "unable to im_glds_asm"); + + if( im_glds_contrast( matrix, &fcon ) ) + error_exit( "unable to im_glds_contrast"); + + if( im_glds_entropy( matrix, &fent ) ) + error_exit( "unable to im_glds_entropy"); + + if( im_glds_mean( matrix, &fmean ) ) + error_exit( "unable to im_glds_mean"); + + if( im_close( matrix ) ) + error_exit( "unable to close %s", argv[1]); + + printf( "glds: ASM=%f, ENT=%f, MEAN=%f, CON=%f\n", + fasm, fent, fmean, fcon); + + return(0); +} diff --git a/src/other/man1/Makefile.am b/src/other/man1/Makefile.am new file mode 100644 index 00000000..01bb7536 --- /dev/null +++ b/src/other/man1/Makefile.am @@ -0,0 +1,10 @@ +man_MANS = \ + cooc.1 \ + cooc_features.1 \ + glds.1 \ + glds_features.1 \ + simcontr.1 \ + sines.1 \ + squares.1 + +EXTRA_DIST = ${man_MANS} diff --git a/src/other/man1/cooc.1 b/src/other/man1/cooc.1 new file mode 100644 index 00000000..049a7117 --- /dev/null +++ b/src/other/man1/cooc.1 @@ -0,0 +1,41 @@ +.TH COOC 1 "27 Jan 1992" +.SH NAME +cooc, cooc_features \- calculate the co-occurrence matrix and features on it +.SH SYNOPSIS +cooc image matrix xpos ypos xsize ysize dx dy flag + +.br +.B cooc_features matrix +.SH DESCRIPTION +.B cooc +creates a 256 by 256 one channel co-occurrence matrix of the box +determined by the parameters (xp, yp; xs, ys) within +the image file. The matrix +is written onto the Vasari image file matrix. The displacement +vector is determined by (dx, dy). The user must ensure that there +is enough border pixels around the box within im dictated by the displacement +vector (dx,dy) or else the program fails. +All entries of the co-occurrence matrix are double normalised to the number +of pairs involved. This function is a direct implementation of the paper: +Haralick R. M., Shanmugan K. and Dinstein I., 'Textural features for +image classification', IEEE Transactions on Systems, Man, and Cybernetics, +Vol. SMC-3, No 6, Nov. 1973, pp 610-621. + +If flag sym is 1, the created co-occurrence matrix is symmetric that is +dispacement vectors (dx, dy), (-dx, -dy) create exactly the same matrix. +If sym is 0, the created co-occurrence matrix is not symmetric that is +dispacement vectors (dx, dy), (-dx, -dy) create different matrices. + +Input image should be one band unsigned char image. + +.B cooc_features +calculates and prints at the standard error output features +of the cooccurrence matrix matrix. +.SH SEE\ ALSO +im_glds_matrix(3X), im_cooc_asm(3X), im_cooc_contrast(3X), +im_cooc_correlation(3X), im_cooc_entropy(3X) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 27/2/1992 diff --git a/src/other/man1/cooc_features.1 b/src/other/man1/cooc_features.1 new file mode 100644 index 00000000..50459e9e --- /dev/null +++ b/src/other/man1/cooc_features.1 @@ -0,0 +1 @@ +.so man1/cooc.1 diff --git a/src/other/man1/glds.1 b/src/other/man1/glds.1 new file mode 100644 index 00000000..e98636d3 --- /dev/null +++ b/src/other/man1/glds.1 @@ -0,0 +1,35 @@ +.TH GLDS 1 "27 January 1992" +.SH NAME +glds, glds_features \- calculate the spatial grey level difference matrix and features on it +.SH SYNOPSIS +glds image matrix xpos ypos xsize ysize dx dy + +glds_features matrix +.SH DESCRIPTION +.B glds +creates a 256 by 1 one channel spatial grey level difference +matrix (sglds) of the box +determined by the parameters (xp, yp; xs, ys) within +the vasari image file image. The matrix +is written onto the vasari image file matrix. The displacement +vector is determined by (dx, dy). The user must ensure that there +is enough border pixels around the box within im dictated by the displacement +vector (dx,dy) or else the program fails. +All entries of the sgld matrix are double normalised to the number +of pairs involved. This function is a direct implementation of the paper: +Haralick R. M., Shanmugan K. and Dinstein I., 'Textural features for +image classification', IEEE Transactions on Systems, Man, and Cybernetics, +Vol. SMC-3, No 6, Nov. 1973, pp 610-621. + +Input im should be one band unsigned char image file. + +.B glds_features +calculates features on the spatial grey level difference vasari image +file matrix. The calculated features are printed in the stderr. +.SH SEE\ ALSO +im_glds_matrix(3X), im_glds_asm(3X), im_glds_contrast(3X), +im_glds_correlation(3X), im_glds_entropy(3X). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 27/02/1992 diff --git a/src/other/man1/glds_features.1 b/src/other/man1/glds_features.1 new file mode 100644 index 00000000..0dd971a6 --- /dev/null +++ b/src/other/man1/glds_features.1 @@ -0,0 +1 @@ +.so man1/glds.1 diff --git a/src/other/man1/simcontr.1 b/src/other/man1/simcontr.1 new file mode 100644 index 00000000..4d92fae7 --- /dev/null +++ b/src/other/man1/simcontr.1 @@ -0,0 +1,18 @@ +.TH SIMCONTR 1 "27 January 1992" +.SH NAME +simcontr \- shows the effect of simultaneous contrast +.SH SYNOPSIS +.B simcontr image xsize ysize +.SH DESCRIPTION +.B simcontr +creates an unsigned char one band grey scale image of sizes xsize by ysize. +The created pattern consists of two neighbouring squares one dark (left +square) and one light (right square). The left +one containes a smaller light square and the right +one containes a darker square. +.SH SEE\ ALSO +grey(1) +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 27/02/1992 diff --git a/src/other/man1/sines.1 b/src/other/man1/sines.1 new file mode 100644 index 00000000..d767e5e7 --- /dev/null +++ b/src/other/man1/sines.1 @@ -0,0 +1,24 @@ +.TH SINES 1 "27 January 1992" +.SH NAME +sines, squares \- creates a spatial sine or square wave form +.SH SYNOPSIS +.B sines image xsize ysize horfreq verfreq + +.B squares image xsize ysize horfreq verfreq +.SH DESCRIPTION +.B sines +creates an one band unsigned char +image of the a sine waveform in two dimensions. +The sizes of the created image are xsize by ysize. The number of +horizontal and vertical spatial frequencies are determined by the variables +horfreq and verfreq respectively. + +.B squares +creates an one band unsigned cahr image by thresholding a sine waveform. +.SH SEE\ ALSO +grey(1), im_sines(3X), squares(1). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 27/02/1992 diff --git a/src/other/man1/squares.1 b/src/other/man1/squares.1 new file mode 100644 index 00000000..baa612a7 --- /dev/null +++ b/src/other/man1/squares.1 @@ -0,0 +1 @@ +.so man1/sines.1 diff --git a/src/other/simcontr.c b/src/other/simcontr.c new file mode 100644 index 00000000..d0f1e53e --- /dev/null +++ b/src/other/simcontr.c @@ -0,0 +1,78 @@ +/* @(#) Creates a pattern showing the simultaneous contrast + * @(#) Usage: simcontr file xsize ysize + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 26/03/1991 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *image; + int xsize, ysize; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if (argc != 4) + error_exit("Usage:\n%s file xsize ysize\n\n\ +WARNING: The program overwrites the output file if the owner has rw access.", +argv[0]); + + xsize = atoi(argv[2]); + ysize = atoi(argv[3]); + + + if ( (image = im_openout(argv[1])) == NULL ) + error_exit("Unable to open %s for output", argv[1]); + + if ( im_simcontr(image, xsize, ysize) == -1 ) + error_exit("Unable to im_simcontr"); + + if ( im_updatehist(image, argv[0], argc - 1, argv + 1) == -1) + error_exit("Unable to update history"); + + if ( im_close( image ) == -1 ) + error_exit("Unable to close %s", argv[1]); + + return(0); +} diff --git a/src/other/sines.c b/src/other/sines.c new file mode 100644 index 00000000..4ba99469 --- /dev/null +++ b/src/other/sines.c @@ -0,0 +1,86 @@ +/* @(#) Creates a scaled uchar sinewave waveform. + * @(#) Usage: sines file xsize ysize horfrew verfreq + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 26/03/1991 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *image, *bufim; + int xsize, ysize; + double horfreq, verfreq; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if (argc != 6) + error_exit("Usage:\n%s file xsize ysize horfreq verfreq\n\n\ +WARNING: The program overwrites the output file if the owner has rw access.", +argv[0]); + + xsize = atoi(argv[2]); + ysize = atoi(argv[3]); + horfreq = atof(argv[4]); + verfreq = atof(argv[5]); + + if ( (bufim = im_setbuf("temp.v")) == NULL ) + error_exit("Unable to set buffer image"); + + if ( im_sines(bufim, xsize, ysize, horfreq, verfreq) == -1 ) + error_exit("Unable to im_sines"); + + if ( (image = im_openout(argv[1])) == NULL ) + error_exit("Unable to open %s for output", argv[1]); + + if ( im_scale(bufim, image) == -1) + error_exit("Unable to im_scale"); + + if ( im_updatehist(image, argv[0], argc - 1, argv + 1) == -1) + error_exit("Unable to update history"); + + if ( ( im_close( image ) == -1 )||( im_close( bufim ) == -1 ) ) + error_exit("Unable to close %s or buffer image",argv[1]); + + return(0); +} diff --git a/src/other/spatres.c b/src/other/spatres.c new file mode 100644 index 00000000..c41b46a5 --- /dev/null +++ b/src/other/spatres.c @@ -0,0 +1,83 @@ +/* @(#) Reduces the spatial resolution of an image by increasing the + * @(#) pixel size + * @(#) + * @(#) Usage: spatres in out step + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 27/03/1991 + * Modified on : + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *in, *out; + int step = 0; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if ( (argc != 4)||(argv[1][0] == '-') ) + error_exit( + "Usage:\n%s in out step\n\n\ +WARNING: The program destroys the opfile if the owner has rw access on it.", + argv[0]); + + step = atoi(argv[3]); + + if ((in= im_open(argv[1],"r")) == NULL) + error_exit("Unable to open %s for input", argv[1]); + + if ( (out=im_open(argv[2],"w")) == NULL ) + error_exit("Unable to open %s", argv[2]); + + if ( im_spatres(in, out, step) == -1) + error_exit("Unable to im_spatres"); + + if ( im_updatehist(out, argv[0], argc - 1, argv + 1) == -1) + error_exit("Unable to update history"); + + if ( (im_close(in) == -1)||(im_close(out) == -1) ) + error_exit("unable to close %s or %s",argv[1],argv[2]); + + return(0); +} diff --git a/src/other/squares.c b/src/other/squares.c new file mode 100644 index 00000000..a91a989f --- /dev/null +++ b/src/other/squares.c @@ -0,0 +1,87 @@ +/* @(#) Creates a byte square waveform. Binary image with 0 and 255 + * @(#) Usage: squares file xsize ysize horfreq verfreq + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 26/03/1991 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *image, *bufim; + int xsize, ysize; + double horfreq, verfreq; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if (argc != 6) + error_exit("Usage:\n%s file xsize ysize horfreq verfreq\n\n\ +WARNING: The program overwrites the output file if the owner has rw access.", +argv[0]); + + xsize = atoi(argv[2]); + ysize = atoi(argv[3]); + horfreq = atof(argv[4]); + verfreq = atof(argv[5]); + + if ( (bufim = im_setbuf("temp.v")) == NULL ) + error_exit("Unable to set buffer image"); + + if ( im_sines(bufim, xsize, ysize, horfreq, verfreq) == -1 ) + error_exit("Unable to im_sines"); + + if ( (image = im_openout(argv[1])) == NULL ) + error_exit("Unable to open %s for output", argv[1]); + + if ( im_thresh(bufim, image, (double)0.0) == -1) + error_exit("Unable to im_thresh"); + + if ( im_updatehist(image, argv[0], argc - 1, argv + 1) == -1) + error_exit("Unable to update history"); + + if ( ( im_close( image ) == -1 )||( im_close( bufim ) == -1 ) ) + error_exit("Unable to close %s or buffer image", + argv[1]); + + return(0); +} diff --git a/src/scripts/Makefile.am b/src/scripts/Makefile.am new file mode 100644 index 00000000..ab9d58aa --- /dev/null +++ b/src/scripts/Makefile.am @@ -0,0 +1,27 @@ +SUBDIRS = man1 + +bin_SCRIPTS = \ + light_correct \ + shrink_width \ + batch_image_convert \ + batch_rubber_sheet \ + batch_crop \ + vips-7.12 + +noinst_SCRIPTS = post_install + +EXTRA_DIST = \ + vips-7.12 \ + ${noinst_SCRIPTS} \ + light_correct.in \ + shrink_width.in \ + batch_image_convert.in \ + batch_rubber_sheet.in \ + batch_crop.in + +install-exec-hook: + chmod ugo+x ${DESTDIR}${bindir}/light_correct + chmod ugo+x ${DESTDIR}${bindir}/shrink_width + chmod ugo+x ${DESTDIR}${bindir}/batch_image_convert + chmod ugo+x ${DESTDIR}${bindir}/batch_rubber_sheet + chmod ugo+x ${DESTDIR}${bindir}/batch_crop diff --git a/src/scripts/batch_crop.in b/src/scripts/batch_crop.in new file mode 100644 index 00000000..982b9e66 --- /dev/null +++ b/src/scripts/batch_crop.in @@ -0,0 +1,45 @@ +#!/bin/sh + +# Crop a set of image files +# +# usage: +# +# example% batch_crop left top width height image1 image2 etc +# +# writes output images crop_image1, crop_image2 + +# default prefix +VIPSHOME=${VIPSHOME-@prefix@} + +name=`basename $0` +left=$1 +top=$2 +width=$3 +height=$4 +shift 4 + +# check args +if [ $# -lt 1 ]; then + echo "usage: $name left top width height image1 image2 ..." + echo + echo "$name writes a new set of images called crop_image1, " + echo "crop_image2, etc., each cropped to the specified size" + + exit 1 +fi + +# convert each argument +for i in $*; do + dir=`dirname $i` + file=`basename $i` + new=$dir/crop_$file + echo "Cropping $file as $new ..." + + if [ -f $new ]; then + echo "$new exists, skipping" + else + $VIPSHOME/bin/vips im_extract_area $i $new $left $top $width $height + fi +done + + diff --git a/src/scripts/batch_image_convert.in b/src/scripts/batch_image_convert.in new file mode 100644 index 00000000..e4fff85a --- /dev/null +++ b/src/scripts/batch_image_convert.in @@ -0,0 +1,43 @@ +#!/bin/sh + +# Convert a set of image files to new file format +# +# usage: +# +# example% batch_image_convert image1 image2 etc +# +# writes output images image1.* and image2.* where * is the new file type. + +# default prefix +VIPSHOME=${VIPSHOME-@prefix@} + +name=`basename $0` +type=$1 +shift + +# check args +if [ $# -lt 1 ]; then + echo "usage: $name image1 image2 ..." + echo + echo "$name uses VIPS to convert a group of image files of" + echo "any image format into a new group of images all of the same" + echo "image format. VIPS can read almost any standard image format" + echo "but it can only write VIPS, JPEG, TIFF, PPM/PBM/PGM or PNG." + + exit 1 +fi + +# convert each argument +for i in $*; do + # drop the suffix on the filename + base=${i%*.*} + echo "Converting $i to $base.$type ..." + + if [ -f $base.$type ]; then + echo "$base.$type already exists skiping $i" + else + $VIPSHOME/bin/vips im_copy $i $base.$type + fi +done + + diff --git a/src/scripts/batch_rubber_sheet.in b/src/scripts/batch_rubber_sheet.in new file mode 100644 index 00000000..d36891f9 --- /dev/null +++ b/src/scripts/batch_rubber_sheet.in @@ -0,0 +1,38 @@ +#!/bin/sh + +# Corrects a set of image files for lens distortion using a preprepared +# recombination matrix + +# usage: +# +# example% batch_rubber_sheet matrix_file image1 image2 .. +# +# writes output images rsc_image1.v, rsc_image2.v .. + +# default prefix +VIPSHOME=${VIPSHOME-@prefix@} + +# get name we were run as +name=`basename $0` +rec=$1 +shift + +# check args +if [ $# -lt 1 ]; then + echo "usage: $name matrix image1 image2 ..." + echo "writes rsc_image1, rsc_image2, ..." + echo + echo "$name uses VIPS to correct a set of images for lens distortion" + echo "using a matrix calculated by the 'resample' function." + + exit 1 +fi + +# transform each argument +for i in $*; do + echo "Transforming $i to rsc_$i ..." + + # bilinear interp., don't wrap edges + $VIPSHOME/bin/vips im_transform $i rsc_$i $rec 1 0 +done + diff --git a/src/scripts/light_correct.in b/src/scripts/light_correct.in new file mode 100644 index 00000000..a96f400e --- /dev/null +++ b/src/scripts/light_correct.in @@ -0,0 +1,57 @@ +#!/bin/sh +# correct a set of files for illumination errors +# usage: +# +# example% light_correct grey.v im1.v im2.v +# +# writes output images ic_im1.v and ic_im2.v + +# default prefix +VIPSHOME=${VIPSHOME-@prefix@} + +# get name we were run as +name=$0 +bname=`basename $name` + +# names of our temp files +t1=light_correct_temp1 +t2=light_correct_temp2 + +# check args +if [ $# -lt 2 ]; then + echo "${bname}: usage: $bname ..." + exit 1 +fi + +echo "Preparing grey ..." +grey=$1 +shift + +# find image size +width=`$VIPSHOME/bin/vips im_header_int Xsize $grey` +height=`$VIPSHOME/bin/vips im_header_int Ysize $grey` + +# smooth the grey out +$VIPSHOME/bin/vips im_shrink $grey $t1.v 20 20 +$VIPSHOME/bin/vips im_resize_linear $t1.v $t2.v $width $height + +# and make the correction image +mean=`$VIPSHOME/bin/vips im_avg $t2.v` +$VIPSHOME/bin/vips im_powtra $t2.v $t1.v -1 +$VIPSHOME/bin/vips im_lintra $mean $t1.v 0 $t2.v + +# grey correct images in order +for i in "$@"; do + echo "Correcting $i as ic_$i ..." + $VIPSHOME/bin/vips im_multiply $t2.v "$i" $t1.v + $VIPSHOME/bin/vips im_clip $t1.v "ic_$i" + + # remove the .desc as well + name=`echo $name | sed -e 's/\.[^\.]*//'` + /bin/rm -f "ic_$name.desc" +done + +# more cleanup +echo "Cleaning up ..." +/bin/rm -f $t1.v $t1.desc +/bin/rm -f $t2.v $t2.desc diff --git a/src/scripts/man1/Makefile.am b/src/scripts/man1/Makefile.am new file mode 100644 index 00000000..0604d009 --- /dev/null +++ b/src/scripts/man1/Makefile.am @@ -0,0 +1,7 @@ +man_MANS = \ + light_correct.1 \ + batch_image_convert.1 \ + batch_crop.1 \ + batch_rubber_sheet.1 + +EXTRA_DIST = ${man_MANS} diff --git a/src/scripts/man1/batch_crop.1 b/src/scripts/man1/batch_crop.1 new file mode 100644 index 00000000..4546f071 --- /dev/null +++ b/src/scripts/man1/batch_crop.1 @@ -0,0 +1,22 @@ +.TH BATCH_CROP 1 "2 Feb 2002" +.SH NAME +batch_crop \- crop a set of images +.SH SYNOPSIS +.B batch_crop left top width height image1 image2 ... +.SH DESCRIPTION +The area defined by the rectangle left, top, width, height is cropped out of +each of the images and saved in a file of the same name, but prefixed by +"crop_". + +For example: + + batch_crop 10 10 100 100 fred.jpg jim.png + +will make two images, crop_fred.jpg and crop_jim.png, each of 100 by 100 +pixels, taken from the corresponding input images. + +.SH RETURN VALUE +returns 0 on success and non-zero on error. +.SH SEE ALSO +header(1), im_vips2tiff(3), im_vips2jpeg(3), im_vips2png(3), im_vips2ppm(3) + diff --git a/src/scripts/man1/batch_image_convert.1 b/src/scripts/man1/batch_image_convert.1 new file mode 100644 index 00000000..1ae6d054 --- /dev/null +++ b/src/scripts/man1/batch_image_convert.1 @@ -0,0 +1,29 @@ +.TH BATCH_IMAGE_CONVERT 1 "2 Feb 2002" +.SH NAME +batch_image_convert \- use VIPS to convert a set of images to a new type +.SH SYNOPSIS +.B batch_image_convert type image1 image2 ... +.SH DESCRIPTION +The first argument is the name of an image type, subsequent arguments are +the names of files to be converted to that type. VIPS can usually read almost +any image type, but it can only write VIPS, PNG, TIFF, PPM/PGM/PBM and JPEG. +You can specify conversion parameters in the type name. + +For example: + + batch_image_convert tiff fred.jpg jim.png + +will convert +.B fred.jpg +and +.B jim.png +to TIFF format. + + batch_image_convert jpeg:95 jim.png + +will write jim.jpeg with a 95% quality factor. +.SH RETURN VALUE +returns 0 on success and non-zero on error. +.SH SEE ALSO +header(1), im_vips2tiff(3), im_vips2jpeg(3), im_vips2png(3), im_vips2ppm(3) + diff --git a/src/scripts/man1/batch_rubber_sheet.1 b/src/scripts/man1/batch_rubber_sheet.1 new file mode 100644 index 00000000..976d80e6 --- /dev/null +++ b/src/scripts/man1/batch_rubber_sheet.1 @@ -0,0 +1,30 @@ +.TH BATCH_RUBBER_SHEET 1 "2 Feb 2002" +.SH NAME +batch_rubber_sheet \- warp a set of images with a rubber-sheet transformation +.SH SYNOPSIS +.B batch_rubber_sheet matrix image1 image2 ... +.SH DESCRIPTION +The first argument specifies a file containing the transformation, subsequent +arguments are image files to be transformed. The transformed image is written +to a new file, named as the old file, but with "rsc_" prepended to the file +name. + +For example: + + batch_rubber_sheet lens.mat fred.jpg jim.png + +will read a transform from the file +.B lens.mat +and apply it to +.B fred.jpg +and +.B jim.png, +writing files +.B rsc_fred.jpg +and +.B rsc_jim.png. +.SH RETURN VALUE +returns 0 on success and non-zero on error. +.SH SEE ALSO +The "Image=>Rubber" menu in nip. + diff --git a/src/scripts/man1/light_correct.1 b/src/scripts/man1/light_correct.1 new file mode 100644 index 00000000..f992499b --- /dev/null +++ b/src/scripts/man1/light_correct.1 @@ -0,0 +1,39 @@ +.TH LIGHT_CORRECT 1 "14 Oct 1996" +.SH NAME +light_correct \- correct illumination errors on set of images +.SH SYNOPSIS +.B light_correct grey image1 image2 image3 ... +.SH DESCRIPTION +The first argument should be an image of a piece of grey card, subsequent +arguments should be images taken with the same lighting set-up which need +correcting. The corrected images are written to files prefixed with "ic_". + +For example, suppose you have a directory with the following files in: + + example% ls + dat1.1.v dat1.2.v dat2.1.v dat2.2.v dat3.1.v dat3.2.v + dat4.1.v dat4.2.v grey.v + +then run light_correct like this: + + example% light_correct grey.v dat*.v + +to generate this: + + example% ls + dat1.1.v dat1.2.v dat2.1.v dat2.2.v dat3.1.v dat3.2.v + dat4.1.v dat4.2.v grey.v + ic_dat1.1.v ic_dat1.2.v ic_dat2.1.v ic_dat2.2.v ic_dat3.1.v + ic_dat3.2.v ic_dat4.1.v ic_dat4.2.v + +light_correct works by smoothing out the grey card image, finding +grey-mean/pixel for each point, and then multiplying the result by all the +following images. It also removes any .desc files it generates, to avoid +problems with im_global_balance(3). + +.SH RETURN VALUE +returns 0 on success and non-zero on error. +.SH SEE ALSO +header(1), vips(1) +.SH COPYRIGHT +The National Gallery and Birkbeck College, 1989-1996. diff --git a/src/scripts/post_install b/src/scripts/post_install new file mode 100755 index 00000000..6bb9d65a --- /dev/null +++ b/src/scripts/post_install @@ -0,0 +1,5 @@ +#!/bin/sh + +bin=$1 + +( cd $bin ; $bin/vips -k | sh ) diff --git a/src/scripts/shrink_width.in b/src/scripts/shrink_width.in new file mode 100644 index 00000000..c9ba5764 --- /dev/null +++ b/src/scripts/shrink_width.in @@ -0,0 +1,18 @@ +#!/bin/sh +# shrink to a target width + +# default prefix +VIPSHOME=${VIPSHOME-@prefix@} + +name=$0 +bname=`basename $0` + +if [ $# != 3 ]; then + echo "${bname}: usage: $bname " + exit 1 +fi + +inwidth=`$VIPSHOME/bin/vips im_header_int Xsize $1` +factor=`(echo scale=10; echo $inwidth / $3) | bc` + +$VIPSHOME/bin/vips im_shrink $1 $2 $factor $factor diff --git a/src/scripts/vips-7.12 b/src/scripts/vips-7.12 new file mode 100755 index 00000000..ee887528 --- /dev/null +++ b/src/scripts/vips-7.12 @@ -0,0 +1,116 @@ +#!/bin/bash +# +# Start script for VIPS + +# need extended regexps, hence we insist on bash above +shopt -s extglob +# set -x + +# name we were invoked as +bname=`basename $0` + +# check args +if [[ $# < 1 ]]; then + echo "usage: $bname [command ...]" + echo "examples:" + echo " $bname nip" + echo " $bname man im_invert" + echo " $bname im_invert /pics/tmp/fred.jpg /pics/tmp/fred2.tif" + exit 1 +fi + +# try to extract the prefix from a path to an executable +# eg. "/home/john/vips/bin/fred" -> "/home/john/vips" +function find_prefix () { + # try to canonicalise the path + ep_canon=$1 + + # relative path? prefix with pwd + if [ ${ep_canon:0:1} != "/" ]; then + ep_canon=`pwd`/$ep_canon + fi + + # replace any "/./" with "/" + ep_canon=${ep_canon//\/.\//\/} + + # any "xxx/../" can go + ep_canon=${ep_canon//+([^\/])\/..\//} + + # trailing "xxx/.." can go + ep_canon=${ep_canon/%+([^\/])\/../} + + # remove trailing "/bin/xxx" to get the prefix + ep_prefix=${ep_canon/%\/bin\/+([^\/])/} + + # was there anything to remove in that final step? if not, the path + # must be wrong + if [ x$ep_prefix == x$ep_canon ]; then + return 1 + fi + + echo $ep_prefix; + + return 0 +} + +# try to guess the install prefix from $0 +function guess_prefix () { + # $0 is a file? must be us + if [ -f $0 ]; then + find_prefix $0 + return + fi + + # nope, extract program name from $0 and try looking along the + # searchpath for it + name=`basename $0` + + fred=$PATH + while [ x$fred != x"" ]; do + path=${fred/:*/}/$name + fred=${fred/*([^:])?(:)/} + + if [ -f $path ]; then + find_prefix $path + return + fi + done + + # not found on path either ... give up! + return 1 +} + +prefix=`guess_prefix`; + +if [ $? != 0 ]; then + echo "unable to find $0 from the file name, or from your PATH" + echo "either run directly, or add the install bin area to " + echo "your PATH" + exit 1 +fi + +export VIPSHOME=$prefix + +# add VIPSHOME to man pages +export MANPATH=$VIPSHOME/man:$MANPATH + +# add the VIPS lib area to the library path +case `uname` in +HPUX) + export SHLIB_PATH=$VIPSHOME/lib:$SHLIB_PATH + ;; + +Darwin) + export DYLD_LIBRARY_PATH=$VIPSHOME/lib:$DYLD_LIBRARY_PATH + ;; + +*) + export LD_LIBRARY_PATH=$VIPSHOME/lib:$LD_LIBRARY_PATH + ;; +esac + +# add VIPS bin area to path +export PATH=$VIPSHOME/bin:$PATH + +# run, passing in args we were passed +exec $* diff --git a/vips-7.12.pc.in b/vips-7.12.pc.in new file mode 100644 index 00000000..ee3aaf2c --- /dev/null +++ b/vips-7.12.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: vips-7.12 +Description: Image processing library +Version: @VERSION@ +Requires: @PACKAGES_USED@ +Libs: -L${libdir} -lvips @VIPS_LIBS@ +Cflags: -I${includedir} diff --git a/vips-7.12.spec.in b/vips-7.12.spec.in new file mode 100644 index 00000000..382281f5 --- /dev/null +++ b/vips-7.12.spec.in @@ -0,0 +1,114 @@ +%define name @PACKAGE@ +%define version @VERSION@ +%define release 1 +%define default_install_prefix /usr/local + +Summary: an image processing library +Name: %{name} +Version: %{version} +Release: %{release} +License: LGPL +Group: Development/Libraries +Source: http://www.vips.ecs.soton.ac.uk/vips-7.12/%{name}-%{version}.tar.gz +URL: http://www.vips.ecs.soton.ac.uk/ +BuildRoot: %{_tmppath}/%{name}-buildroot +Provides: vips +Prefix: %{default_install_prefix} + +%description +VIPS is an image processing library. It is good for very large images (ie. +larger than the amount of RAM in your machine), and for working with colour. +It includes a C++ API, complete man pages, a command-line interface, automatic +threading and an operation database. There are several user interfaces +built on top of VIPS: for example "nip2". + +%package devel +Summary: Headers and links to develop with vips +Requires: vips = %{version} +Group: Development/Libraries + +%description devel +These headers are needed to develop programs which want to use the vips +library. Programs already linked against it only need the vips package. + +%package python +Summary: Python bindings to use vips +Requires: vips = %{version} +Group: Development/Libraries + +%description python +Includes the necessary files to use and develop with vips and python + +%prep +rm -rf $RPM_BUILD_ROOT +%setup + +%build +# grr ... why do we have to set prefix by hand? if you don't do this, you get +# /usr, despite the "Prefix:" tag above +%define _prefix %{default_install_prefix} +%configure + +%install +%makeinstall + +# paste in the non-GPL stuff as a binary plugin +# comment this out if you don't have it :-( +# cp ~john/develop/transform-7.9/src/resample.plg ${RPM_BUILD_ROOT}%{_libdir} + +%clean +rm -rf $RPM_BUILD_ROOT + +%post +ldconfig + +%files +# don't list explicitly ... install to a private prefix and rely on naming the +# dirs to pick up the files +%attr(-, root, root) +%doc AUTHORS THANKS README TODO COPYING ChangeLog +%{_bindir} +%{_libdir}/libvips*.so.* +%doc %{_mandir} +%doc %{_datadir}/doc/vips +%{_datadir}/vips +%{_datadir}/locale + +%files devel +%{_includedir}/vips +%{_libdir}/libvips*.so +%{_libdir}/libvips*.a +%{_libdir}/libvips*.la +%{_libdir}/pkgconfig/* + +%files python +%{_libdir}/python* + +%changelog -n vips +* Fri Apr 27 2007 John Cupitt 7.12 +- updated for 7.12 + +* Fri Dec 29 2006 - plasmahh@projectiwear.org +- Split up into library and devel package + +* Wed Jun 1 2005 John Cupitt 7.11 +- updated for 7.11 + +* Tue Dec 14 2004 John Cupitt 7.10.8 +- updated for 7.10.8 +- now updated from configure +- implicit deps and files + +* Wed Jul 16 2003 John Cupitt 7.8.10 +- updated for 7.8.10 +- updated %files +- copies formatted docs to install area + +* Wed Mar 12 2003 John Cupitt 7.8.8 +- updated for 7.8.8, adding libdrfftw + +* Mon Feb 3 2003 John Cupitt 7.8.7-2 +- hack to change default install prefix to /usr/local + +* Thu Jan 30 2003 John Cupitt 7.8.7-1 +- first stab at an rpm package for vips diff --git a/vipsCC-7.12.pc.in b/vipsCC-7.12.pc.in new file mode 100644 index 00000000..080a8e7b --- /dev/null +++ b/vipsCC-7.12.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: vipsCC-7.12 +Description: C++ API for vips image processing library +Version: @VERSION@ +Requires: vips-7.12 = @VERSION@ +Libs: -L${libdir} -lvipsCC diff --git a/win32/README b/win32/README new file mode 100644 index 00000000..3405ac06 --- /dev/null +++ b/win32/README @@ -0,0 +1,15 @@ +Build files for vips.dll with the MS toolchain + +proj/ + + Build system based on hand-written makefiles for MSVC 6, copy into + vips libsrc area and tweak files before building. No support, sorry. + +tmake/ + + Build system based on trolltech's tmake. + +msvc/ + + A set of project files for visual studio. You'll need to copy them + into the source area and edit them a bit. Version 6 only. diff --git a/win32/msvc/config.h b/win32/msvc/config.h new file mode 100644 index 00000000..462f91bb --- /dev/null +++ b/win32/msvc/config.h @@ -0,0 +1,243 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* define to open non-text files in binary mode */ +#define BINARY_OPEN 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRECT_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have fftw libraries and header files. */ +//#define HAVE_FFTW 1 + +/* Define to 1 if you have the `getcwd' function. */ +#define HAVE_GETCWD 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +/* #undef HAVE_GETTIMEOFDAY */ + +/* Define to 1 if you have the `getwd' function. */ +/* #undef HAVE_GETWD */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_IO_H 1 + +/* Define if you have jpeg libraries and header files. */ +#define HAVE_JPEG 1 + +/* Define if you have lcms libraries and header files. */ +//#define HAVE_LCMS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* define if you have the libMagick libraries and header files. */ +/* #undef HAVE_MAGICK */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `mkstemp' function. */ +/* #undef HAVE_MKSTEMP */ + +/* Define to 1 if you have the `mktemp' function. */ +#define HAVE_MKTEMP 1 + +/* Define to 1 if you have a working `mmap' system call. */ +/* #undef HAVE_MMAP */ + +/* Define to 1 if you have the `munmap' function. */ +/* #undef HAVE_MUNMAP */ + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have png libraries and header files. */ +#define HAVE_PNG 1 + +/* Define if you have POSIX threads libraries and header files. */ +//#define HAVE_PTHREAD 1 + +/* have pthread_setconcurrency() */ +//#define HAVE_PTHREAD_SETCONCURRENCY 1 + +/* Define to 1 if you have the `putenv' function. */ +#define HAVE_PUTENV 1 + +/* Define to 1 if you have the `rand' function. */ +#define HAVE_RAND 1 + +/* Define to 1 if you have the `random' function. */ +/* #undef HAVE_RANDOM */ + +/* Define to 1 if you have the `realpath' function. */ +/* #undef HAVE_REALPATH */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strcspn' function. */ +#define HAVE_STRCSPN 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strrchr' function. */ +#define HAVE_STRRCHR 1 + +/* Define to 1 if you have the `strspn' function. */ +#define HAVE_STRSPN 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IOCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_MMAN_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have tiff libraries and header files. */ +#define HAVE_TIFF 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_UNISTD_H 1 + +/* have video4linux 1 */ +/* #undef HAVE_VIDEODEV */ + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 +#define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a) + + + +/* Define to 1 if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Define if you have libz libraries and header files. */ +#define HAVE_ZIP 1 + +/* Name of package */ +#define PACKAGE "vips" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to the necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "7.8.7" + +/* Define if using the dmalloc debugging malloc package */ +/* #undef WITH_DMALLOC */ + +/* Define to 1 if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING 1 + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if does not define. */ +#define mode_t int + +/* Define to `long' if does not define. */ +/* #undef off_t */ + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ + +#define PATH_MAX 512 +#define R_OK 4 diff --git a/win32/msvc/vips.dsp b/win32/msvc/vips.dsp new file mode 100644 index 00000000..95348017 --- /dev/null +++ b/win32/msvc/vips.dsp @@ -0,0 +1,1327 @@ +# Microsoft Developer Studio Project File - Name="vips" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=vips - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "vips.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "vips.mak" CFG="vips - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "vips - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "vips - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "vips - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VIPS_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "." /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VIPS_EXPORTS" /D "HAVE_CONFIG_H" /YX /FD /I./include /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc0a /d "NDEBUG" +# ADD RSC /l 0xc0a /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ./lib/libtiff.lib ./lib/jpeg.lib ./lib/libpng.lib ./lib/zlib.lib /nologo /dll /machine:I386 /nodefaultlib:"libc.lib" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "vips - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VIPS_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VIPS_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc0a /d "_DEBUG" +# ADD RSC /l 0xc0a /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "vips - Win32 Release" +# Name "vips - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Group "acquire" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\acquire\im_clamp.c +# End Source File +# End Group +# Begin Group "arithmetic" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\arithmetic\arith_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_abs.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_add.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_avg.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_ceil.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_cmulnorm.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_costra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_deviate.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_divide.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_expntra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_fav4.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_floor.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_gadd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_gaddim.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_gfadd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_invert.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_lintra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_litecor.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_log10tra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_logtra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_max.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_maxpos.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_measure.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_min.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_minpos.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_multiply.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_powtra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_remainder.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_sign.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_sintra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_stats.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_subtract.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_tantra.c +# End Source File +# End Group +# Begin Group "boolean" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\boolean\bool_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\boolean\boolean.c +# End Source File +# End Group +# Begin Group "colour" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\colour\colour.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\colour_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\derived.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_dE00_fromLab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_dE_fromLab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_dECMC_fromLab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_disp2XYZ.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_icc_transform.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_Lab2LabQ.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_Lab2LabS.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_Lab2LCh.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_Lab2XYZ.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_lab_morph.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LabQ2disp.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LabQ2Lab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LabQ2LabS.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LabS2Lab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LabS2LabQ.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LCh2Lab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LCh2UCS.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_UCS2LCh.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_XYZ2disp.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_XYZ2Lab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_XYZ2Yxy.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_Yxy2XYZ.c +# End Source File +# End Group +# Begin Group "conversion" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\conversion\conver_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_bandjoin.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_bernd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_black.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_c2amph.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_c2imag.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_c2ps.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_c2real.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_c2rect.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_clip.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_copy.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_extract.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_falsecolour.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_fliphor.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_flipver.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_gbandjoin.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_insert.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_lrjoin.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_magick2vips.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_mask2vips.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_ppm2vips.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_print.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_recomb.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_ri2c.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_rot180.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_rot270.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_rot90.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_scale.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_scaleps.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_slice.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_subsample.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_system.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_tbjoin.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_thresh.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_tiff2vips.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_vips2mask.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_vips2ppm.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_vips2tiff.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_zoom.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\vips_jpeg.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\vips_png.c +# End Source File +# End Group +# Begin Group "convolution" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\convolution\convol_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_addgnoise.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_compass.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_conv.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_convf.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_convsep.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_convsepf.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_convsub.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_embed.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_fastcor.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_gaussmasks.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_gaussnoise.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_gradient.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_lindetect.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_logmasks.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_maxvalue.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_mpercent.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_rank.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_resize_linear.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_sharpen.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_shrink.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_spcor.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_stretch3.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_zerox.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\rotmask.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\rw_mask.c +# End Source File +# End Group +# Begin Group "freq_filt" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\freq_filt\fft_sp.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\fmask4th.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\fmaskcir.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\freq_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_disp_ps.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_fractsurf.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_freq_mask.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_freqflt.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_fwfft.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_invfft.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_invfftr.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_rotquad.c +# End Source File +# End Group +# Begin Group "histograms_lut" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\hist_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_gammacorrect.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_heq.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_hist.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_histeq.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_histgr.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_histnD.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_histplot.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_histspec.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_hsp.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_identity.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_invertlut.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_lhisteq.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_maplut.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_stdif.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\tone.c +# End Source File +# End Group +# Begin Group "inplace" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\inplace\im_circle.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\im_flood.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\im_insertplace.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\im_line.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\im_paintrect.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\im_plotmask.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\inplace_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\line_draw.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\plot_point.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\smudge_area.c +# End Source File +# End Group +# Begin Group "iofuncs" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\iofuncs\callback.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\debug.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\dispatch_types.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\error.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\error_exit.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_append_Hist.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_binfile.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_close.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_cp_desc.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_cp_Hist.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_crwrhd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_debugim.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_demand_hint.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_desc_hd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_generate.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_guess_prefix.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_header.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_histlin.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_image.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_init.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_initdesc.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_inithd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_iocheck.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_iterate.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_makerw.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_mapfile.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_open.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_openin.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_openout.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_partial.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_piocheck.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_prepare.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_printdesc.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_printhd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_printlines.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_readhist.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_setbox.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_setbuf.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_setupout.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_unmapfile.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_updatehist.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_wrapmany.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_wrapone.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_writeline.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\list.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\memory.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\package.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\predicate.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\rect.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\region.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\thread.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\threadgroup.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\time.c +# End Source File +# End Group +# Begin Group "matrix" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\matrix\im_invmat.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\matrix\im_matcat.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\matrix\im_matinv.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\matrix\im_matmul.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\matrix\im_mattrn.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\matrix\matalloc.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\matrix\matrix_dispatch.c +# End Source File +# End Group +# Begin Group "morphology" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\morphology\im_cntlines.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\morphology\im_dilate.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\morphology\im_erode.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\morphology\im_profile.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\morphology\morph_dispatch.c +# End Source File +# End Group +# Begin Group "mosaicing" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\mosaicing\global_balance.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\global_balance.h +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_affine.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_avgdxdy.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_chkpair.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_clinear.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_improve.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_initialize.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_lrcalcon.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_lrmerge.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_lrmosaic.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_remosaic.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_tbcalcon.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_tbmerge.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_tbmosaic.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\match.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\merge.h +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\mosaic.h +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\mosaic1.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\mosaicing_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\similarity.c +# End Source File +# End Group +# Begin Group "other" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\other\cooc_funcs.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\glds_funcs.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_dif_std.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_eye.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_grey.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_meanstd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_simcontr.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_sines.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_spatres.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_zone.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\other_dispatch.c +# End Source File +# End Group +# Begin Group "relational" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\relational\im_blend.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\relational\im_ifthenelse.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\relational\relational.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\relational\relational_dispatch.c +# End Source File +# End Group +# Begin Group "video" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\video\im_video_v4l1.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\video\video_dispatch.c +# End Source File +# End Group +# Begin Source File + +SOURCE=.\libsrc\dummy.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\vips.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Group "vips" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\include\vips\colour.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\debug.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\dispatch.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\fmask.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\history.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\list.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\mosaic.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\proto.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\rect.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\region.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\struct.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\thread.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\threadgroup.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\time.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\util.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\version.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\vips.h +# End Source File +# End Group +# Begin Source File + +SOURCE=.\config.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win32/msvc/vips.dsw b/win32/msvc/vips.dsw new file mode 100644 index 00000000..2df5c036 --- /dev/null +++ b/win32/msvc/vips.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "vips"=.\vips.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/win32/msvc/vips.opt b/win32/msvc/vips.opt new file mode 100644 index 00000000..b0ab0d06 Binary files /dev/null and b/win32/msvc/vips.opt differ diff --git a/win32/proj/README b/win32/proj/README new file mode 100644 index 00000000..32841b77 --- /dev/null +++ b/win32/proj/README @@ -0,0 +1,3 @@ +sample config and makefiles for building with the MSC toolchain + +experts only, no support! diff --git a/win32/proj/config.h.win32 b/win32/proj/config.h.win32 new file mode 100644 index 00000000..462f91bb --- /dev/null +++ b/win32/proj/config.h.win32 @@ -0,0 +1,243 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* define to open non-text files in binary mode */ +#define BINARY_OPEN 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRECT_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have fftw libraries and header files. */ +//#define HAVE_FFTW 1 + +/* Define to 1 if you have the `getcwd' function. */ +#define HAVE_GETCWD 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +/* #undef HAVE_GETTIMEOFDAY */ + +/* Define to 1 if you have the `getwd' function. */ +/* #undef HAVE_GETWD */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_IO_H 1 + +/* Define if you have jpeg libraries and header files. */ +#define HAVE_JPEG 1 + +/* Define if you have lcms libraries and header files. */ +//#define HAVE_LCMS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* define if you have the libMagick libraries and header files. */ +/* #undef HAVE_MAGICK */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `mkstemp' function. */ +/* #undef HAVE_MKSTEMP */ + +/* Define to 1 if you have the `mktemp' function. */ +#define HAVE_MKTEMP 1 + +/* Define to 1 if you have a working `mmap' system call. */ +/* #undef HAVE_MMAP */ + +/* Define to 1 if you have the `munmap' function. */ +/* #undef HAVE_MUNMAP */ + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have png libraries and header files. */ +#define HAVE_PNG 1 + +/* Define if you have POSIX threads libraries and header files. */ +//#define HAVE_PTHREAD 1 + +/* have pthread_setconcurrency() */ +//#define HAVE_PTHREAD_SETCONCURRENCY 1 + +/* Define to 1 if you have the `putenv' function. */ +#define HAVE_PUTENV 1 + +/* Define to 1 if you have the `rand' function. */ +#define HAVE_RAND 1 + +/* Define to 1 if you have the `random' function. */ +/* #undef HAVE_RANDOM */ + +/* Define to 1 if you have the `realpath' function. */ +/* #undef HAVE_REALPATH */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strcspn' function. */ +#define HAVE_STRCSPN 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strrchr' function. */ +#define HAVE_STRRCHR 1 + +/* Define to 1 if you have the `strspn' function. */ +#define HAVE_STRSPN 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IOCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_MMAN_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have tiff libraries and header files. */ +#define HAVE_TIFF 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_UNISTD_H 1 + +/* have video4linux 1 */ +/* #undef HAVE_VIDEODEV */ + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 +#define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a) + + + +/* Define to 1 if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Define if you have libz libraries and header files. */ +#define HAVE_ZIP 1 + +/* Name of package */ +#define PACKAGE "vips" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to the necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "7.8.7" + +/* Define if using the dmalloc debugging malloc package */ +/* #undef WITH_DMALLOC */ + +/* Define to 1 if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING 1 + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if does not define. */ +#define mode_t int + +/* Define to `long' if does not define. */ +/* #undef off_t */ + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ + +#define PATH_MAX 512 +#define R_OK 4 diff --git a/win32/proj/libsrc/acquire/makefile.msc b/win32/proj/libsrc/acquire/makefile.msc new file mode 100644 index 00000000..3d199b2b --- /dev/null +++ b/win32/proj/libsrc/acquire/makefile.msc @@ -0,0 +1,41 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = acquire +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + im_clamp.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/arithmetic/makefile.msc b/win32/proj/libsrc/arithmetic/makefile.msc new file mode 100644 index 00000000..3991a549 --- /dev/null +++ b/win32/proj/libsrc/arithmetic/makefile.msc @@ -0,0 +1,75 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = arithmetic +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + arith_dispatch.obj \ + im_abs.obj \ + im_add.obj \ + im_avg.obj \ + im_cmulnorm.obj \ + im_costra.obj \ + im_deviate.obj \ + im_divide.obj \ + im_ceil.obj \ + im_floor.obj \ + im_expntra.obj \ + im_fav4.obj \ + im_gadd.obj \ + im_gaddim.obj \ + im_gfadd.obj \ + im_invert.obj \ + im_lintra.obj \ + im_litecor.obj \ + im_log10tra.obj \ + im_logtra.obj \ + im_max.obj \ + im_maxpos.obj \ + im_measure.obj \ + im_min.obj \ + im_minpos.obj \ + im_multiply.obj \ + im_powtra.obj \ + im_remainder.obj \ + im_sign.obj \ + im_sintra.obj \ + im_stats.obj \ + im_subtract.obj \ + im_tantra.obj \ + +INCLUDES = \ + -I$(PRJ_TOP) -DHAVE_CONFIG_H \ + -I$(PRJ_TOP)/include + +# @VIPS_CFLAGS@ @VIPS_INCLUDES@ + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/boolean/makefile.msc b/win32/proj/libsrc/boolean/makefile.msc new file mode 100644 index 00000000..bb4b24bf --- /dev/null +++ b/win32/proj/libsrc/boolean/makefile.msc @@ -0,0 +1,42 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = boolean +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +INCLUDES = \ + -I$(PRJ_TOP) -DHAVE_CONFIG_H \ + -I$(PRJ_TOP)/include + +OBJECTS = \ + bool_dispatch.obj \ + boolean.obj \ + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/colour/makefile.msc b/win32/proj/libsrc/colour/makefile.msc new file mode 100644 index 00000000..9ce4485f --- /dev/null +++ b/win32/proj/libsrc/colour/makefile.msc @@ -0,0 +1,65 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = colour +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + colour.obj \ + colour_dispatch.obj \ + derived.obj \ + im_dE00_fromLab.obj \ + im_icc_transform.obj \ + im_LCh2Lab.obj \ + im_LCh2UCS.obj \ + im_Lab2LCh.obj \ + im_Lab2LabQ.obj \ + im_Lab2LabS.obj \ + im_Lab2XYZ.obj \ + im_LabQ2Lab.obj \ + im_LabQ2LabS.obj \ + im_LabQ2disp.obj \ + im_LabS2LabQ.obj \ + im_LabS2Lab.obj \ + im_lab_morph.obj \ + im_UCS2LCh.obj \ + im_XYZ2Lab.obj \ + im_XYZ2Yxy.obj \ + im_Yxy2XYZ.obj \ + im_XYZ2disp.obj \ + im_dECMC_fromLab.obj \ + im_dE_fromLab.obj \ + im_disp2XYZ.obj \ + +INCLUDES = \ + -I$(PRJ_TOP) -DHAVE_CONFIG_H \ + -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/conversion/makefile.msc b/win32/proj/libsrc/conversion/makefile.msc new file mode 100644 index 00000000..495e0a86 --- /dev/null +++ b/win32/proj/libsrc/conversion/makefile.msc @@ -0,0 +1,84 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = conversion +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include \ + $(TIFF_CFLAGS) \ + $(JPEG_CFLAGS) \ + $(PNG_CFLAGS) + +OBJECTS = \ + im_bernd.obj \ + im_vips2tiff.obj \ + im_tiff2vips.obj \ + conver_dispatch.obj \ + im_bandjoin.obj \ + im_black.obj \ + im_c2amph.obj \ + im_c2rect.obj \ + im_c2imag.obj \ + im_c2ps.obj \ + im_c2real.obj \ + im_clip.obj \ + im_copy.obj \ + im_extract.obj \ + im_falsecolour.obj \ + im_fliphor.obj \ + im_flipver.obj \ + im_gbandjoin.obj \ + im_insert.obj \ + im_lrjoin.obj \ + im_magick2vips.obj \ + im_mask2vips.obj \ + im_ppm2vips.obj \ + im_recomb.obj \ + im_ri2c.obj \ + im_rot180.obj \ + im_rot270.obj \ + im_rot90.obj \ + im_scale.obj \ + im_scaleps.obj \ + im_slice.obj \ + im_subsample.obj \ + im_system.obj \ + im_print.obj \ + im_tbjoin.obj \ + im_thresh.obj \ + im_vips2mask.obj \ + im_vips2ppm.obj \ + vips_jpeg.obj \ + im_zoom.obj \ + vips_png.obj \ + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/convolution/makefile.msc b/win32/proj/libsrc/convolution/makefile.msc new file mode 100644 index 00000000..7d4bc748 --- /dev/null +++ b/win32/proj/libsrc/convolution/makefile.msc @@ -0,0 +1,66 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = convolution +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + rotmask.obj \ + rw_mask.obj \ + convol_dispatch.obj \ + im_addgnoise.obj \ + im_compass.obj \ + im_conv.obj \ + im_convf.obj \ + im_convsep.obj \ + im_convsepf.obj \ + im_convsub.obj \ + im_embed.obj \ + im_fastcor.obj \ + im_gaussmasks.obj \ + im_gaussnoise.obj \ + im_gradient.obj \ + im_lindetect.obj \ + im_logmasks.obj \ + im_maxvalue.obj \ + im_mpercent.obj \ + im_rank.obj \ + im_resize_linear.obj \ + im_sharpen.obj \ + im_shrink.obj \ + im_spcor.obj \ + im_stretch3.obj \ + im_zerox.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/freq_filt/makefile.msc b/win32/proj/libsrc/freq_filt/makefile.msc new file mode 100644 index 00000000..3bb32e43 --- /dev/null +++ b/win32/proj/libsrc/freq_filt/makefile.msc @@ -0,0 +1,51 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = freq_filt +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + fft_sp.obj \ + fmask4th.obj \ + fmaskcir.obj \ + freq_dispatch.obj \ + im_disp_ps.obj \ + im_fractsurf.obj \ + im_freq_mask.obj \ + im_freqflt.obj \ + im_fwfft.obj \ + im_invfft.obj \ + im_rotquad.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/histrograms_lut/makefile.msc b/win32/proj/libsrc/histrograms_lut/makefile.msc new file mode 100644 index 00000000..fae4dd43 --- /dev/null +++ b/win32/proj/libsrc/histrograms_lut/makefile.msc @@ -0,0 +1,55 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = histograms_lut +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + hist_dispatch.obj \ + im_gammacorrect.obj \ + im_heq.obj \ + im_hist.obj \ + im_histeq.obj \ + im_histgr.obj \ + im_histplot.obj \ + im_histspec.obj \ + im_hsp.obj \ + im_identity.obj \ + im_invertlut.obj \ + im_lhisteq.obj \ + im_maplut.obj \ + im_stdif.obj \ + tone.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/inplace/makefile.msc b/win32/proj/libsrc/inplace/makefile.msc new file mode 100644 index 00000000..71bc9e7a --- /dev/null +++ b/win32/proj/libsrc/inplace/makefile.msc @@ -0,0 +1,50 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = inplace +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + im_circle.obj \ + im_flood.obj \ + im_insertplace.obj \ + im_line.obj \ + im_paintrect.obj \ + im_plotmask.obj \ + inplace_dispatch.obj \ + line_draw.obj \ + plot_point.obj \ + smudge_area.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/iofuncs/makefile.msc b/win32/proj/libsrc/iofuncs/makefile.msc new file mode 100644 index 00000000..9d9f8062 --- /dev/null +++ b/win32/proj/libsrc/iofuncs/makefile.msc @@ -0,0 +1,97 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = iofuncs +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include \ + -DHAVE_DIRENT_H $(DIRENT_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(TIFF_CFLAGS) \ + $(PNG_CFLAGS) + +OBJECTS = \ + callback.obj \ + debug.obj \ + dispatch_types.obj \ + error.obj \ + error_exit.obj \ + im_append_Hist.obj \ + im_binfile.obj \ + im_close.obj \ + im_cp_Hist.obj \ + im_cp_desc.obj \ + im_crwrhd.obj \ + im_debugim.obj \ + im_demand_hint.obj \ + im_desc_hd.obj \ + im_generate.obj \ + im_header.obj \ + im_histlin.obj \ + im_image.obj \ + im_init.obj \ + im_initdesc.obj \ + im_inithd.obj \ + im_iocheck.obj \ + im_iterate.obj \ + im_makerw.obj \ + im_mapfile.obj \ + im_openin.obj \ + im_open.obj \ + im_openout.obj \ + im_partial.obj \ + im_piocheck.obj \ + im_prepare.obj \ + im_printdesc.obj \ + im_printhd.obj \ + im_printlines.obj \ + im_readhist.obj \ + im_setbox.obj \ + im_setbuf.obj \ + im_setupout.obj \ + im_unmapfile.obj \ + im_updatehist.obj \ + im_guess_prefix.obj \ + im_wrapmany.obj \ + im_wrapone.obj \ + im_writeline.obj \ + list.obj \ + memory.obj \ + package.obj \ + predicate.obj \ + region.obj \ + rect.obj \ + thread.obj \ + threadgroup.obj \ + time.obj \ + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/makefile.msc b/win32/proj/libsrc/makefile.msc new file mode 100644 index 00000000..cdae8594 --- /dev/null +++ b/win32/proj/libsrc/makefile.msc @@ -0,0 +1,78 @@ +# autogenerated from automake.am with automake.py +TOP = ..\.. +PRJ_TOP = .. +PACKAGE = vips +PKG_VER = 7.8.7 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +!IFDEF DEBUG +LDFLAGS = $(LDFLAGS) /NODEFAULTLIB:msvcrt +!ENDIF + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) +SUBDIRS = acquire arithmetic boolean colour conversion convolution freq_filt histograms_lut inplace iofuncs matrix morphology mosaicing other relational video + +sub-all: + for %d in ($(SUBDIRS)) do nmake -nologo -f makefile.msc sub-one THIS=%d + +sub-one: + cd $(THIS) + nmake -nologo -f makefile.msc + cd .. + +PKG_CFLAGS = \ + $(GLIB_CFLAGS) + +PKG_LINK = \ + $(TIFF_LIBS) $(JPEG_LIBS) $(ZLIB_LIBS) $(PNG_LIBS) \ + $(GLIB_LIBS) + +OBJECTS = \ + dummy.obj \ + $(libvips_LIBS) + +libvips_LIBS = \ + acquire/acquire.lib \ + arithmetic/arithmetic.lib \ + boolean/boolean.lib \ + colour/colour.lib \ + conversion/conversion.lib \ + convolution/convolution.lib \ + freq_filt/freq_filt.lib \ + histograms_lut/histograms_lut.lib \ + inplace/inplace.lib \ + iofuncs/iofuncs.lib \ + matrix/matrix.lib \ + morphology/morphology.lib \ + mosaicing/mosaicing.lib \ + other/other.lib \ + relational/relational.lib \ + video/video.lib + +all : \ + $(PRJ_TOP)\config.h \ + sub-all \ + $(PACKAGE)-$(PKG_VER).dll + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib \ + $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/matrix/makefile.msc b/win32/proj/libsrc/matrix/makefile.msc new file mode 100644 index 00000000..9c95e05f --- /dev/null +++ b/win32/proj/libsrc/matrix/makefile.msc @@ -0,0 +1,47 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = matrix +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + im_invmat.obj \ + im_matcat.obj \ + im_matinv.obj \ + im_matmul.obj \ + im_mattrn.obj \ + matalloc.obj \ + matrix_dispatch.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/morphology/makefile.msc b/win32/proj/libsrc/morphology/makefile.msc new file mode 100644 index 00000000..3862175d --- /dev/null +++ b/win32/proj/libsrc/morphology/makefile.msc @@ -0,0 +1,45 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = morphology +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + im_cntlines.obj \ + im_dilate.obj \ + im_erode.obj \ + morph_dispatch.obj \ + im_profile.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/mosaicing/makefile.msc b/win32/proj/libsrc/mosaicing/makefile.msc new file mode 100644 index 00000000..773d39ee --- /dev/null +++ b/win32/proj/libsrc/mosaicing/makefile.msc @@ -0,0 +1,58 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = mosaicing +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + im_affine.obj \ + match.obj \ + mosaic1.obj \ + mosaicing_dispatch.obj \ + similarity.obj \ + global_balance.obj \ + im_avgdxdy.obj \ + im_chkpair.obj \ + im_clinear.obj \ + im_improve.obj \ + im_initialize.obj \ + im_lrcalcon.obj \ + im_lrmerge.obj \ + im_lrmosaic.obj \ + im_tbcalcon.obj \ + im_tbmerge.obj \ + im_remosaic.obj \ + im_tbmosaic.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/other/makefile.msc b/win32/proj/libsrc/other/makefile.msc new file mode 100644 index 00000000..fe16671c --- /dev/null +++ b/win32/proj/libsrc/other/makefile.msc @@ -0,0 +1,51 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = other +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + cooc_funcs.obj \ + glds_funcs.obj \ + im_dif_std.obj \ + im_eye.obj \ + im_grey.obj \ + im_meanstd.obj \ + im_simcontr.obj \ + im_sines.obj \ + im_spatres.obj \ + im_zone.obj \ + other_dispatch.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/relational/makefile.msc b/win32/proj/libsrc/relational/makefile.msc new file mode 100644 index 00000000..8c8face7 --- /dev/null +++ b/win32/proj/libsrc/relational/makefile.msc @@ -0,0 +1,44 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = relational +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + im_ifthenelse.obj \ + im_blend.obj \ + relational.obj \ + relational_dispatch.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/video/makefile.msc b/win32/proj/libsrc/video/makefile.msc new file mode 100644 index 00000000..830e787e --- /dev/null +++ b/win32/proj/libsrc/video/makefile.msc @@ -0,0 +1,42 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = video +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + video_dispatch.obj \ + im_video_v4l1.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/src/iofuncs/makefile.msc b/win32/proj/src/iofuncs/makefile.msc new file mode 100644 index 00000000..71c5eb7b --- /dev/null +++ b/win32/proj/src/iofuncs/makefile.msc @@ -0,0 +1,42 @@ +TOP = ..\..\.. +PRJ_TOP = ..\.. +PKG_VER = 7.8.7 + +!IFNDEF APP + +APPS = header binfile debugim edvips printlines vips + +sub-all: + for %d in ($(APPS)) do nmake -nologo -f makefile.msc sub-one THIS=%d + +sub-one: + nmake -nologo -f makefile.msc $(THIS).exe APP=$(THIS) OBJ_$(THIS)=1 + + +all : \ + sub-all + +!ELSE +!INCLUDE $(TOP)\glib\build\win32\make.msc + +PACKAGE = $(APP) + +!IFNDEF OBJECTS +OBJECTS = $(APP).obj +!ENDIF + +PKG_CFLAGS = -FImsvc_recommended_pragmas.h \ + $(GLIB_CFLAGS) \ + -I $(PRJ_TOP)\include + +PKG_LINK = $(GLIB_LIBS) \ + $(PRJ_TOP)\libsrc\vips-$(PKG_VER).lib + +$(PACKAGE).exe : $(OBJECTS) + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib \ + $(LDFLAGS) + +CFLAGS = -I. -I$(PRJ_TOP) $(PKG_CFLAGS) -DHAVE_CONFIG_H + +!ENDIF \ No newline at end of file diff --git a/win32/tmake/README b/win32/tmake/README new file mode 100644 index 00000000..ac704046 --- /dev/null +++ b/win32/tmake/README @@ -0,0 +1,16 @@ +Build system based on trolltech's tmake. Makes a vips.dll on win32. + +Copy the .pro and Makefiles in this area into the main VIPS libsrc tree, +then either use the makefiles already there, (you'll need to edit them), +or install tmake and regenerate them from the .pro files. + + http://www.trolltech.com/download/tmake.html + + tmake todo.pro -o makefile_todo + nmake -f makefile_todo tmake_all + nmake -f makefile_todo all + tmake vips.pro -o Makefile + nmake -f Makefile all + +You might need to edit the .pro files a little as well. + diff --git a/win32/tmake/config.h b/win32/tmake/config.h new file mode 100644 index 00000000..462f91bb --- /dev/null +++ b/win32/tmake/config.h @@ -0,0 +1,243 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* define to open non-text files in binary mode */ +#define BINARY_OPEN 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRECT_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have fftw libraries and header files. */ +//#define HAVE_FFTW 1 + +/* Define to 1 if you have the `getcwd' function. */ +#define HAVE_GETCWD 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +/* #undef HAVE_GETTIMEOFDAY */ + +/* Define to 1 if you have the `getwd' function. */ +/* #undef HAVE_GETWD */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_IO_H 1 + +/* Define if you have jpeg libraries and header files. */ +#define HAVE_JPEG 1 + +/* Define if you have lcms libraries and header files. */ +//#define HAVE_LCMS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* define if you have the libMagick libraries and header files. */ +/* #undef HAVE_MAGICK */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `mkstemp' function. */ +/* #undef HAVE_MKSTEMP */ + +/* Define to 1 if you have the `mktemp' function. */ +#define HAVE_MKTEMP 1 + +/* Define to 1 if you have a working `mmap' system call. */ +/* #undef HAVE_MMAP */ + +/* Define to 1 if you have the `munmap' function. */ +/* #undef HAVE_MUNMAP */ + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have png libraries and header files. */ +#define HAVE_PNG 1 + +/* Define if you have POSIX threads libraries and header files. */ +//#define HAVE_PTHREAD 1 + +/* have pthread_setconcurrency() */ +//#define HAVE_PTHREAD_SETCONCURRENCY 1 + +/* Define to 1 if you have the `putenv' function. */ +#define HAVE_PUTENV 1 + +/* Define to 1 if you have the `rand' function. */ +#define HAVE_RAND 1 + +/* Define to 1 if you have the `random' function. */ +/* #undef HAVE_RANDOM */ + +/* Define to 1 if you have the `realpath' function. */ +/* #undef HAVE_REALPATH */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strcspn' function. */ +#define HAVE_STRCSPN 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strrchr' function. */ +#define HAVE_STRRCHR 1 + +/* Define to 1 if you have the `strspn' function. */ +#define HAVE_STRSPN 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IOCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_MMAN_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have tiff libraries and header files. */ +#define HAVE_TIFF 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_UNISTD_H 1 + +/* have video4linux 1 */ +/* #undef HAVE_VIDEODEV */ + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 +#define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a) + + + +/* Define to 1 if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Define if you have libz libraries and header files. */ +#define HAVE_ZIP 1 + +/* Name of package */ +#define PACKAGE "vips" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to the necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "7.8.7" + +/* Define if using the dmalloc debugging malloc package */ +/* #undef WITH_DMALLOC */ + +/* Define to 1 if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING 1 + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if does not define. */ +#define mode_t int + +/* Define to `long' if does not define. */ +/* #undef off_t */ + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ + +#define PATH_MAX 512 +#define R_OK 4 diff --git a/win32/tmake/libsrc/Makefile b/win32/tmake/libsrc/Makefile new file mode 100644 index 00000000..8b9b6870 --- /dev/null +++ b/win32/tmake/libsrc/Makefile @@ -0,0 +1,99 @@ +############################################################################# +# Makefile for building vips +# Generated by tmake at 16:03, 2003/06/11 +# Project: vips +# Template: app +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DNO_DEBUG +DESTDIR= ../Release +INCPATH = -I".." -I"..\include" -I"$(QTDIR)\include" +LINK = link +LFLAGS = /NOLOGO /SUBSYSTEM:windows /DLL /DEF:vips.def +LIBS = kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib imm32.lib winmm.lib wsock32.lib ..\lib\libjpeg.lib ..\lib\libtiff.lib ..\lib\libpng.lib ..\lib\libz.lib +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\config.h +SOURCES = dummy.c +OBJECTS = dummy.obj \ + $(DESTDIR)/acquire.lib \ + $(DESTDIR)/arithmetic.lib \ + $(DESTDIR)/boolean.lib \ + $(DESTDIR)/colour.lib \ + $(DESTDIR)/conversion.lib \ + $(DESTDIR)/convolution.lib \ + $(DESTDIR)/freq_filt.lib \ + $(DESTDIR)/histograms_lut.lib \ + $(DESTDIR)/inplace.lib \ + $(DESTDIR)/iofuncs.lib \ + $(DESTDIR)/matrix.lib \ + $(DESTDIR)/morphology.lib \ + $(DESTDIR)/mosaicing.lib \ + $(DESTDIR)/other.lib \ + $(DESTDIR)/relational.lib \ + $(DESTDIR)/video.lib \ + +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\Release\vips.dll +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LINK) $(LFLAGS) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) $(LIBS) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: vips.pro + tmake vips.pro -o Makefile + +dist: + $(ZIP) vips.zip vips.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del dummy.obj + -del $(TARGET) + -del vips.lib + -del vips.exp + +####### Compile + +dummy.obj: dummy.c + diff --git a/win32/tmake/libsrc/acquire/Makefile b/win32/tmake/libsrc/acquire/Makefile new file mode 100644 index 00000000..5dcc7f28 --- /dev/null +++ b/win32/tmake/libsrc/acquire/Makefile @@ -0,0 +1,77 @@ +############################################################################# +# Makefile for building acquire +# Generated by tmake at 16:03, 2003/06/11 +# Project: acquire +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = im_clamp.c +OBJECTS = im_clamp.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\acquire.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: acquire.pro + tmake acquire.pro -o Makefile + +dist: + $(ZIP) acquire.zip acquire.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_clamp.obj + -del $(TARGET) + +####### Compile + +im_clamp.obj: im_clamp.c + diff --git a/win32/tmake/libsrc/acquire/acquire.pro b/win32/tmake/libsrc/acquire/acquire.pro new file mode 100644 index 00000000..76304799 --- /dev/null +++ b/win32/tmake/libsrc/acquire/acquire.pro @@ -0,0 +1,18 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +TARGET = acquire +SOURCES = im_clamp.c diff --git a/win32/tmake/libsrc/arithmetic/Makefile b/win32/tmake/libsrc/arithmetic/Makefile new file mode 100644 index 00000000..2aaaffdb --- /dev/null +++ b/win32/tmake/libsrc/arithmetic/Makefile @@ -0,0 +1,237 @@ +############################################################################# +# Makefile for building arithmetic +# Generated by tmake at 16:03, 2003/06/11 +# Project: arithmetic +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = arith_dispatch.c \ + im_abs.c \ + im_add.c \ + im_avg.c \ + im_cmulnorm.c \ + im_costra.c \ + im_deviate.c \ + im_divide.c \ + im_ceil.c \ + im_floor.c \ + im_expntra.c \ + im_fav4.c \ + im_gadd.c \ + im_gaddim.c \ + im_gfadd.c \ + im_invert.c \ + im_lintra.c \ + im_litecor.c \ + im_log10tra.c \ + im_logtra.c \ + im_max.c \ + im_maxpos.c \ + im_measure.c \ + im_min.c \ + im_minpos.c \ + im_multiply.c \ + im_powtra.c \ + im_remainder.c \ + im_sign.c \ + im_sintra.c \ + im_stats.c \ + im_subtract.c \ + im_tantra.c +OBJECTS = arith_dispatch.obj \ + im_abs.obj \ + im_add.obj \ + im_avg.obj \ + im_cmulnorm.obj \ + im_costra.obj \ + im_deviate.obj \ + im_divide.obj \ + im_ceil.obj \ + im_floor.obj \ + im_expntra.obj \ + im_fav4.obj \ + im_gadd.obj \ + im_gaddim.obj \ + im_gfadd.obj \ + im_invert.obj \ + im_lintra.obj \ + im_litecor.obj \ + im_log10tra.obj \ + im_logtra.obj \ + im_max.obj \ + im_maxpos.obj \ + im_measure.obj \ + im_min.obj \ + im_minpos.obj \ + im_multiply.obj \ + im_powtra.obj \ + im_remainder.obj \ + im_sign.obj \ + im_sintra.obj \ + im_stats.obj \ + im_subtract.obj \ + im_tantra.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\arithmetic.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: arithmetic.pro + tmake arithmetic.pro -o Makefile + +dist: + $(ZIP) arithmetic.zip arithmetic.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del arith_dispatch.obj + -del im_abs.obj + -del im_add.obj + -del im_avg.obj + -del im_cmulnorm.obj + -del im_costra.obj + -del im_deviate.obj + -del im_divide.obj + -del im_ceil.obj + -del im_floor.obj + -del im_expntra.obj + -del im_fav4.obj + -del im_gadd.obj + -del im_gaddim.obj + -del im_gfadd.obj + -del im_invert.obj + -del im_lintra.obj + -del im_litecor.obj + -del im_log10tra.obj + -del im_logtra.obj + -del im_max.obj + -del im_maxpos.obj + -del im_measure.obj + -del im_min.obj + -del im_minpos.obj + -del im_multiply.obj + -del im_powtra.obj + -del im_remainder.obj + -del im_sign.obj + -del im_sintra.obj + -del im_stats.obj + -del im_subtract.obj + -del im_tantra.obj + -del $(TARGET) + +####### Compile + +arith_dispatch.obj: arith_dispatch.c + +im_abs.obj: im_abs.c + +im_add.obj: im_add.c + +im_avg.obj: im_avg.c + +im_cmulnorm.obj: im_cmulnorm.c + +im_costra.obj: im_costra.c + +im_deviate.obj: im_deviate.c + +im_divide.obj: im_divide.c + +im_ceil.obj: im_ceil.c + +im_floor.obj: im_floor.c + +im_expntra.obj: im_expntra.c + +im_fav4.obj: im_fav4.c + +im_gadd.obj: im_gadd.c + +im_gaddim.obj: im_gaddim.c + +im_gfadd.obj: im_gfadd.c + +im_invert.obj: im_invert.c + +im_lintra.obj: im_lintra.c + +im_litecor.obj: im_litecor.c + +im_log10tra.obj: im_log10tra.c + +im_logtra.obj: im_logtra.c + +im_max.obj: im_max.c + +im_maxpos.obj: im_maxpos.c + +im_measure.obj: im_measure.c + +im_min.obj: im_min.c + +im_minpos.obj: im_minpos.c + +im_multiply.obj: im_multiply.c + +im_powtra.obj: im_powtra.c + +im_remainder.obj: im_remainder.c + +im_sign.obj: im_sign.c + +im_sintra.obj: im_sintra.c + +im_stats.obj: im_stats.c + +im_subtract.obj: im_subtract.c + +im_tantra.obj: im_tantra.c + diff --git a/win32/tmake/libsrc/arithmetic/arithmetic.pro b/win32/tmake/libsrc/arithmetic/arithmetic.pro new file mode 100644 index 00000000..c3052089 --- /dev/null +++ b/win32/tmake/libsrc/arithmetic/arithmetic.pro @@ -0,0 +1,51 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + arith_dispatch.c \ + im_abs.c \ + im_add.c \ + im_avg.c \ + im_cmulnorm.c \ + im_costra.c \ + im_deviate.c \ + im_divide.c \ + im_ceil.c \ + im_floor.c \ + im_expntra.c \ + im_fav4.c \ + im_gadd.c \ + im_gaddim.c \ + im_gfadd.c \ + im_invert.c \ + im_lintra.c \ + im_litecor.c \ + im_log10tra.c \ + im_logtra.c \ + im_max.c \ + im_maxpos.c \ + im_measure.c \ + im_min.c \ + im_minpos.c \ + im_multiply.c \ + im_powtra.c \ + im_remainder.c \ + im_sign.c \ + im_sintra.c \ + im_stats.c \ + im_subtract.c \ + im_tantra.c +TARGET = arithmetic diff --git a/win32/tmake/libsrc/boolean/Makefile b/win32/tmake/libsrc/boolean/Makefile new file mode 100644 index 00000000..c7b5f5c2 --- /dev/null +++ b/win32/tmake/libsrc/boolean/Makefile @@ -0,0 +1,82 @@ +############################################################################# +# Makefile for building boolean +# Generated by tmake at 16:03, 2003/06/11 +# Project: boolean +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = bool_dispatch.c \ + boolean.c +OBJECTS = bool_dispatch.obj \ + boolean.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\boolean.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: boolean.pro + tmake boolean.pro -o Makefile + +dist: + $(ZIP) boolean.zip boolean.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del bool_dispatch.obj + -del boolean.obj + -del $(TARGET) + +####### Compile + +bool_dispatch.obj: bool_dispatch.c + +boolean.obj: boolean.c + diff --git a/win32/tmake/libsrc/boolean/boolean.pro b/win32/tmake/libsrc/boolean/boolean.pro new file mode 100644 index 00000000..ea9e4cdc --- /dev/null +++ b/win32/tmake/libsrc/boolean/boolean.pro @@ -0,0 +1,20 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + bool_dispatch.c \ + boolean.c +TARGET = boolean diff --git a/win32/tmake/libsrc/colour/Makefile b/win32/tmake/libsrc/colour/Makefile new file mode 100644 index 00000000..7dba2966 --- /dev/null +++ b/win32/tmake/libsrc/colour/Makefile @@ -0,0 +1,197 @@ +############################################################################# +# Makefile for building colour +# Generated by tmake at 16:03, 2003/06/11 +# Project: colour +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = colour.c \ + colour_dispatch.c \ + derived.c \ + im_icc_transform.c \ + im_LCh2Lab.c \ + im_LCh2UCS.c \ + im_Lab2LCh.c \ + im_Lab2LabQ.c \ + im_Lab2LabS.c \ + im_Lab2XYZ.c \ + im_LabQ2Lab.c \ + im_LabQ2LabS.c \ + im_LabQ2disp.c \ + im_LabS2LabQ.c \ + im_LabS2Lab.c \ + im_lab_morph.c \ + im_UCS2LCh.c \ + im_XYZ2Lab.c \ + im_XYZ2Yxy.c \ + im_Yxy2XYZ.c \ + im_XYZ2disp.c \ + im_dE00_fromLab.c \ + im_dECMC_fromLab.c \ + im_dE_fromLab.c \ + im_disp2XYZ.c +OBJECTS = colour.obj \ + colour_dispatch.obj \ + derived.obj \ + im_icc_transform.obj \ + im_LCh2Lab.obj \ + im_LCh2UCS.obj \ + im_Lab2LCh.obj \ + im_Lab2LabQ.obj \ + im_Lab2LabS.obj \ + im_Lab2XYZ.obj \ + im_LabQ2Lab.obj \ + im_LabQ2LabS.obj \ + im_LabQ2disp.obj \ + im_LabS2LabQ.obj \ + im_LabS2Lab.obj \ + im_lab_morph.obj \ + im_UCS2LCh.obj \ + im_XYZ2Lab.obj \ + im_XYZ2Yxy.obj \ + im_Yxy2XYZ.obj \ + im_XYZ2disp.obj \ + im_dE00_fromLab.obj \ + im_dECMC_fromLab.obj \ + im_dE_fromLab.obj \ + im_disp2XYZ.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\colour.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: colour.pro + tmake colour.pro -o Makefile + +dist: + $(ZIP) colour.zip colour.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del colour.obj + -del colour_dispatch.obj + -del derived.obj + -del im_icc_transform.obj + -del im_LCh2Lab.obj + -del im_LCh2UCS.obj + -del im_Lab2LCh.obj + -del im_Lab2LabQ.obj + -del im_Lab2LabS.obj + -del im_Lab2XYZ.obj + -del im_LabQ2Lab.obj + -del im_LabQ2LabS.obj + -del im_LabQ2disp.obj + -del im_LabS2LabQ.obj + -del im_LabS2Lab.obj + -del im_lab_morph.obj + -del im_UCS2LCh.obj + -del im_XYZ2Lab.obj + -del im_XYZ2Yxy.obj + -del im_Yxy2XYZ.obj + -del im_XYZ2disp.obj + -del im_dE00_fromLab.obj + -del im_dECMC_fromLab.obj + -del im_dE_fromLab.obj + -del im_disp2XYZ.obj + -del $(TARGET) + +####### Compile + +colour.obj: colour.c + +colour_dispatch.obj: colour_dispatch.c + +derived.obj: derived.c + +im_icc_transform.obj: im_icc_transform.c + +im_LCh2Lab.obj: im_LCh2Lab.c + +im_LCh2UCS.obj: im_LCh2UCS.c + +im_Lab2LCh.obj: im_Lab2LCh.c + +im_Lab2LabQ.obj: im_Lab2LabQ.c + +im_Lab2LabS.obj: im_Lab2LabS.c + +im_Lab2XYZ.obj: im_Lab2XYZ.c + +im_LabQ2Lab.obj: im_LabQ2Lab.c + +im_LabQ2LabS.obj: im_LabQ2LabS.c + +im_LabQ2disp.obj: im_LabQ2disp.c + +im_LabS2LabQ.obj: im_LabS2LabQ.c + +im_LabS2Lab.obj: im_LabS2Lab.c + +im_lab_morph.obj: im_lab_morph.c + +im_UCS2LCh.obj: im_UCS2LCh.c + +im_XYZ2Lab.obj: im_XYZ2Lab.c + +im_XYZ2Yxy.obj: im_XYZ2Yxy.c + +im_Yxy2XYZ.obj: im_Yxy2XYZ.c + +im_XYZ2disp.obj: im_XYZ2disp.c + +im_dE00_fromLab.obj: im_dE00_fromLab.c + +im_dECMC_fromLab.obj: im_dECMC_fromLab.c + +im_dE_fromLab.obj: im_dE_fromLab.c + +im_disp2XYZ.obj: im_disp2XYZ.c + diff --git a/win32/tmake/libsrc/colour/colour.pro b/win32/tmake/libsrc/colour/colour.pro new file mode 100644 index 00000000..6f590679 --- /dev/null +++ b/win32/tmake/libsrc/colour/colour.pro @@ -0,0 +1,43 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + colour.c \ + colour_dispatch.c \ + derived.c \ + im_icc_transform.c \ + im_LCh2Lab.c \ + im_LCh2UCS.c \ + im_Lab2LCh.c \ + im_Lab2LabQ.c \ + im_Lab2LabS.c \ + im_Lab2XYZ.c \ + im_LabQ2Lab.c \ + im_LabQ2LabS.c \ + im_LabQ2disp.c \ + im_LabS2LabQ.c \ + im_LabS2Lab.c \ + im_lab_morph.c \ + im_UCS2LCh.c \ + im_XYZ2Lab.c \ + im_XYZ2Yxy.c \ + im_Yxy2XYZ.c \ + im_XYZ2disp.c \ + im_dE00_fromLab.c \ + im_dECMC_fromLab.c \ + im_dE_fromLab.c \ + im_disp2XYZ.c +TARGET = colour diff --git a/win32/tmake/libsrc/conversion/Makefile b/win32/tmake/libsrc/conversion/Makefile new file mode 100644 index 00000000..dc886880 --- /dev/null +++ b/win32/tmake/libsrc/conversion/Makefile @@ -0,0 +1,277 @@ +############################################################################# +# Makefile for building conversion +# Generated by tmake at 16:03, 2003/06/11 +# Project: conversion +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = im_bernd.c \ + im_vips2tiff.c \ + im_tiff2vips.c \ + conver_dispatch.c \ + im_bandjoin.c \ + im_black.c \ + im_c2amph.c \ + im_c2rect.c \ + im_c2imag.c \ + im_c2ps.c \ + im_c2real.c \ + im_clip.c \ + im_copy.c \ + im_extract.c \ + im_falsecolour.c \ + im_fliphor.c \ + im_flipver.c \ + im_gbandjoin.c \ + im_insert.c \ + im_lrjoin.c \ + im_magick2vips.c \ + im_mask2vips.c \ + im_ppm2vips.c \ + im_recomb.c \ + im_ri2c.c \ + im_rot180.c \ + im_rot270.c \ + im_rot90.c \ + im_scale.c \ + im_scaleps.c \ + im_slice.c \ + im_subsample.c \ + im_system.c \ + im_print.c \ + im_tbjoin.c \ + im_thresh.c \ + im_vips2mask.c \ + im_vips2ppm.c \ + vips_jpeg.c \ + vips_png.c \ + im_zoom.c +OBJECTS = im_bernd.obj \ + im_vips2tiff.obj \ + im_tiff2vips.obj \ + conver_dispatch.obj \ + im_bandjoin.obj \ + im_black.obj \ + im_c2amph.obj \ + im_c2rect.obj \ + im_c2imag.obj \ + im_c2ps.obj \ + im_c2real.obj \ + im_clip.obj \ + im_copy.obj \ + im_extract.obj \ + im_falsecolour.obj \ + im_fliphor.obj \ + im_flipver.obj \ + im_gbandjoin.obj \ + im_insert.obj \ + im_lrjoin.obj \ + im_magick2vips.obj \ + im_mask2vips.obj \ + im_ppm2vips.obj \ + im_recomb.obj \ + im_ri2c.obj \ + im_rot180.obj \ + im_rot270.obj \ + im_rot90.obj \ + im_scale.obj \ + im_scaleps.obj \ + im_slice.obj \ + im_subsample.obj \ + im_system.obj \ + im_print.obj \ + im_tbjoin.obj \ + im_thresh.obj \ + im_vips2mask.obj \ + im_vips2ppm.obj \ + vips_jpeg.obj \ + vips_png.obj \ + im_zoom.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\conversion.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: conversion.pro + tmake conversion.pro -o Makefile + +dist: + $(ZIP) conversion.zip conversion.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_bernd.obj + -del im_vips2tiff.obj + -del im_tiff2vips.obj + -del conver_dispatch.obj + -del im_bandjoin.obj + -del im_black.obj + -del im_c2amph.obj + -del im_c2rect.obj + -del im_c2imag.obj + -del im_c2ps.obj + -del im_c2real.obj + -del im_clip.obj + -del im_copy.obj + -del im_extract.obj + -del im_falsecolour.obj + -del im_fliphor.obj + -del im_flipver.obj + -del im_gbandjoin.obj + -del im_insert.obj + -del im_lrjoin.obj + -del im_magick2vips.obj + -del im_mask2vips.obj + -del im_ppm2vips.obj + -del im_recomb.obj + -del im_ri2c.obj + -del im_rot180.obj + -del im_rot270.obj + -del im_rot90.obj + -del im_scale.obj + -del im_scaleps.obj + -del im_slice.obj + -del im_subsample.obj + -del im_system.obj + -del im_print.obj + -del im_tbjoin.obj + -del im_thresh.obj + -del im_vips2mask.obj + -del im_vips2ppm.obj + -del vips_jpeg.obj + -del vips_png.obj + -del im_zoom.obj + -del $(TARGET) + +####### Compile + +im_bernd.obj: im_bernd.c + +im_vips2tiff.obj: im_vips2tiff.c + +im_tiff2vips.obj: im_tiff2vips.c + +conver_dispatch.obj: conver_dispatch.c + +im_bandjoin.obj: im_bandjoin.c + +im_black.obj: im_black.c + +im_c2amph.obj: im_c2amph.c + +im_c2rect.obj: im_c2rect.c + +im_c2imag.obj: im_c2imag.c + +im_c2ps.obj: im_c2ps.c + +im_c2real.obj: im_c2real.c + +im_clip.obj: im_clip.c + +im_copy.obj: im_copy.c + +im_extract.obj: im_extract.c + +im_falsecolour.obj: im_falsecolour.c + +im_fliphor.obj: im_fliphor.c + +im_flipver.obj: im_flipver.c + +im_gbandjoin.obj: im_gbandjoin.c + +im_insert.obj: im_insert.c + +im_lrjoin.obj: im_lrjoin.c + +im_magick2vips.obj: im_magick2vips.c + +im_mask2vips.obj: im_mask2vips.c + +im_ppm2vips.obj: im_ppm2vips.c + +im_recomb.obj: im_recomb.c + +im_ri2c.obj: im_ri2c.c + +im_rot180.obj: im_rot180.c + +im_rot270.obj: im_rot270.c + +im_rot90.obj: im_rot90.c + +im_scale.obj: im_scale.c + +im_scaleps.obj: im_scaleps.c + +im_slice.obj: im_slice.c + +im_subsample.obj: im_subsample.c + +im_system.obj: im_system.c + +im_print.obj: im_print.c + +im_tbjoin.obj: im_tbjoin.c + +im_thresh.obj: im_thresh.c + +im_vips2mask.obj: im_vips2mask.c + +im_vips2ppm.obj: im_vips2ppm.c + +vips_jpeg.obj: vips_jpeg.c + +vips_png.obj: vips_png.c + +im_zoom.obj: im_zoom.c + diff --git a/win32/tmake/libsrc/conversion/conversion.pro b/win32/tmake/libsrc/conversion/conversion.pro new file mode 100644 index 00000000..6bebb88d --- /dev/null +++ b/win32/tmake/libsrc/conversion/conversion.pro @@ -0,0 +1,60 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + im_bernd.c \ + im_vips2tiff.c \ + im_tiff2vips.c \ + conver_dispatch.c \ + im_bandjoin.c \ + im_black.c \ + im_c2amph.c \ + im_c2rect.c \ + im_c2imag.c \ + im_c2ps.c \ + im_c2real.c \ + im_clip.c \ + im_copy.c \ + im_extract.c \ + im_falsecolour.c \ + im_fliphor.c \ + im_flipver.c \ + im_gbandjoin.c \ + im_insert.c \ + im_lrjoin.c \ + im_magick2vips.c \ + im_mask2vips.c \ + im_ppm2vips.c \ + im_recomb.c \ + im_ri2c.c \ + im_rot180.c \ + im_rot270.c \ + im_rot90.c \ + im_scale.c \ + im_scaleps.c \ + im_slice.c \ + im_subsample.c \ + im_system.c \ + im_print.c \ + im_tbjoin.c \ + im_thresh.c \ + im_vips2mask.c \ + im_vips2ppm.c \ + vips_jpeg.c \ + vips_png.c \ + im_zoom.c +TARGET = conversion + diff --git a/win32/tmake/libsrc/convolution/Makefile b/win32/tmake/libsrc/convolution/Makefile new file mode 100644 index 00000000..2c5c76b0 --- /dev/null +++ b/win32/tmake/libsrc/convolution/Makefile @@ -0,0 +1,202 @@ +############################################################################# +# Makefile for building convolution +# Generated by tmake at 16:03, 2003/06/11 +# Project: convolution +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = rotmask.c \ + rw_mask.c \ + convol_dispatch.c \ + im_addgnoise.c \ + im_compass.c \ + im_conv.c \ + im_convf.c \ + im_convsep.c \ + im_convsepf.c \ + im_convsub.c \ + im_embed.c \ + im_fastcor.c \ + im_gaussmasks.c \ + im_gaussnoise.c \ + im_gradient.c \ + im_lindetect.c \ + im_logmasks.c \ + im_maxvalue.c \ + im_mpercent.c \ + im_rank.c \ + im_resize_linear.c \ + im_sharpen.c \ + im_shrink.c \ + im_spcor.c \ + im_stretch3.c \ + im_zerox.c +OBJECTS = rotmask.obj \ + rw_mask.obj \ + convol_dispatch.obj \ + im_addgnoise.obj \ + im_compass.obj \ + im_conv.obj \ + im_convf.obj \ + im_convsep.obj \ + im_convsepf.obj \ + im_convsub.obj \ + im_embed.obj \ + im_fastcor.obj \ + im_gaussmasks.obj \ + im_gaussnoise.obj \ + im_gradient.obj \ + im_lindetect.obj \ + im_logmasks.obj \ + im_maxvalue.obj \ + im_mpercent.obj \ + im_rank.obj \ + im_resize_linear.obj \ + im_sharpen.obj \ + im_shrink.obj \ + im_spcor.obj \ + im_stretch3.obj \ + im_zerox.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\convolution.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: convolution.pro + tmake convolution.pro -o Makefile + +dist: + $(ZIP) convolution.zip convolution.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del rotmask.obj + -del rw_mask.obj + -del convol_dispatch.obj + -del im_addgnoise.obj + -del im_compass.obj + -del im_conv.obj + -del im_convf.obj + -del im_convsep.obj + -del im_convsepf.obj + -del im_convsub.obj + -del im_embed.obj + -del im_fastcor.obj + -del im_gaussmasks.obj + -del im_gaussnoise.obj + -del im_gradient.obj + -del im_lindetect.obj + -del im_logmasks.obj + -del im_maxvalue.obj + -del im_mpercent.obj + -del im_rank.obj + -del im_resize_linear.obj + -del im_sharpen.obj + -del im_shrink.obj + -del im_spcor.obj + -del im_stretch3.obj + -del im_zerox.obj + -del $(TARGET) + +####### Compile + +rotmask.obj: rotmask.c + +rw_mask.obj: rw_mask.c + +convol_dispatch.obj: convol_dispatch.c + +im_addgnoise.obj: im_addgnoise.c + +im_compass.obj: im_compass.c + +im_conv.obj: im_conv.c + +im_convf.obj: im_convf.c + +im_convsep.obj: im_convsep.c + +im_convsepf.obj: im_convsepf.c + +im_convsub.obj: im_convsub.c + +im_embed.obj: im_embed.c + +im_fastcor.obj: im_fastcor.c + +im_gaussmasks.obj: im_gaussmasks.c + +im_gaussnoise.obj: im_gaussnoise.c + +im_gradient.obj: im_gradient.c + +im_lindetect.obj: im_lindetect.c + +im_logmasks.obj: im_logmasks.c + +im_maxvalue.obj: im_maxvalue.c + +im_mpercent.obj: im_mpercent.c + +im_rank.obj: im_rank.c + +im_resize_linear.obj: im_resize_linear.c + +im_sharpen.obj: im_sharpen.c + +im_shrink.obj: im_shrink.c + +im_spcor.obj: im_spcor.c + +im_stretch3.obj: im_stretch3.c + +im_zerox.obj: im_zerox.c + diff --git a/win32/tmake/libsrc/convolution/convolution.pro b/win32/tmake/libsrc/convolution/convolution.pro new file mode 100644 index 00000000..d93463f4 --- /dev/null +++ b/win32/tmake/libsrc/convolution/convolution.pro @@ -0,0 +1,45 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + rotmask.c \ + rw_mask.c \ + convol_dispatch.c \ + im_addgnoise.c \ + im_compass.c \ + im_conv.c \ + im_convf.c \ + im_convsep.c \ + im_convsepf.c \ + im_convsub.c \ + im_embed.c \ + im_fastcor.c \ + im_gaussmasks.c \ + im_gaussnoise.c \ + im_gradient.c \ + im_lindetect.c \ + im_logmasks.c \ + im_maxvalue.c \ + im_mpercent.c \ + im_rank.c \ + im_resize_linear.c \ + im_sharpen.c \ + im_shrink.c \ + im_spcor.c \ + im_stretch3.c \ + im_zerox.c +TARGET = convolution + diff --git a/win32/tmake/libsrc/freq_filt/Makefile b/win32/tmake/libsrc/freq_filt/Makefile new file mode 100644 index 00000000..f519153e --- /dev/null +++ b/win32/tmake/libsrc/freq_filt/Makefile @@ -0,0 +1,132 @@ +############################################################################# +# Makefile for building freq_filt +# Generated by tmake at 16:03, 2003/06/11 +# Project: freq_filt +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = fft_sp.c \ + fmask4th.c \ + fmaskcir.c \ + freq_dispatch.c \ + im_disp_ps.c \ + im_fractsurf.c \ + im_freq_mask.c \ + im_freqflt.c \ + im_fwfft.c \ + im_invfft.c \ + im_invfftr.c \ + im_rotquad.c +OBJECTS = fft_sp.obj \ + fmask4th.obj \ + fmaskcir.obj \ + freq_dispatch.obj \ + im_disp_ps.obj \ + im_fractsurf.obj \ + im_freq_mask.obj \ + im_freqflt.obj \ + im_fwfft.obj \ + im_invfft.obj \ + im_invfftr.obj \ + im_rotquad.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\freq_filt.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: freq_filt.pro + tmake freq_filt.pro -o Makefile + +dist: + $(ZIP) freq_filt.zip freq_filt.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del fft_sp.obj + -del fmask4th.obj + -del fmaskcir.obj + -del freq_dispatch.obj + -del im_disp_ps.obj + -del im_fractsurf.obj + -del im_freq_mask.obj + -del im_freqflt.obj + -del im_fwfft.obj + -del im_invfft.obj + -del im_invfftr.obj + -del im_rotquad.obj + -del $(TARGET) + +####### Compile + +fft_sp.obj: fft_sp.c + +fmask4th.obj: fmask4th.c + +fmaskcir.obj: fmaskcir.c + +freq_dispatch.obj: freq_dispatch.c + +im_disp_ps.obj: im_disp_ps.c + +im_fractsurf.obj: im_fractsurf.c + +im_freq_mask.obj: im_freq_mask.c + +im_freqflt.obj: im_freqflt.c + +im_fwfft.obj: im_fwfft.c + +im_invfft.obj: im_invfft.c + +im_invfftr.obj: im_invfftr.c + +im_rotquad.obj: im_rotquad.c + diff --git a/win32/tmake/libsrc/freq_filt/freq_filt.pro b/win32/tmake/libsrc/freq_filt/freq_filt.pro new file mode 100644 index 00000000..0b6fa30e --- /dev/null +++ b/win32/tmake/libsrc/freq_filt/freq_filt.pro @@ -0,0 +1,31 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + fft_sp.c \ + fmask4th.c \ + fmaskcir.c \ + freq_dispatch.c \ + im_disp_ps.c \ + im_fractsurf.c \ + im_freq_mask.c \ + im_freqflt.c \ + im_fwfft.c \ + im_invfft.c \ + im_invfftr.c \ + im_rotquad.c +TARGET = freq_filt + diff --git a/win32/tmake/libsrc/histograms_lut/Makefile b/win32/tmake/libsrc/histograms_lut/Makefile new file mode 100644 index 00000000..c72093d8 --- /dev/null +++ b/win32/tmake/libsrc/histograms_lut/Makefile @@ -0,0 +1,147 @@ +############################################################################# +# Makefile for building histograms_lut +# Generated by tmake at 16:03, 2003/06/11 +# Project: histograms_lut +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = hist_dispatch.c \ + im_gammacorrect.c \ + im_heq.c \ + im_hist.c \ + im_histeq.c \ + im_histgr.c \ + im_histplot.c \ + im_histspec.c \ + im_hsp.c \ + im_identity.c \ + im_invertlut.c \ + im_lhisteq.c \ + im_maplut.c \ + im_stdif.c \ + tone.c +OBJECTS = hist_dispatch.obj \ + im_gammacorrect.obj \ + im_heq.obj \ + im_hist.obj \ + im_histeq.obj \ + im_histgr.obj \ + im_histplot.obj \ + im_histspec.obj \ + im_hsp.obj \ + im_identity.obj \ + im_invertlut.obj \ + im_lhisteq.obj \ + im_maplut.obj \ + im_stdif.obj \ + tone.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\histograms_lut.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: histograms_lut.pro + tmake histograms_lut.pro -o Makefile + +dist: + $(ZIP) histograms_lut.zip histograms_lut.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del hist_dispatch.obj + -del im_gammacorrect.obj + -del im_heq.obj + -del im_hist.obj + -del im_histeq.obj + -del im_histgr.obj + -del im_histplot.obj + -del im_histspec.obj + -del im_hsp.obj + -del im_identity.obj + -del im_invertlut.obj + -del im_lhisteq.obj + -del im_maplut.obj + -del im_stdif.obj + -del tone.obj + -del $(TARGET) + +####### Compile + +hist_dispatch.obj: hist_dispatch.c + +im_gammacorrect.obj: im_gammacorrect.c + +im_heq.obj: im_heq.c + +im_hist.obj: im_hist.c + +im_histeq.obj: im_histeq.c + +im_histgr.obj: im_histgr.c + +im_histplot.obj: im_histplot.c + +im_histspec.obj: im_histspec.c + +im_hsp.obj: im_hsp.c + +im_identity.obj: im_identity.c + +im_invertlut.obj: im_invertlut.c + +im_lhisteq.obj: im_lhisteq.c + +im_maplut.obj: im_maplut.c + +im_stdif.obj: im_stdif.c + +tone.obj: tone.c + diff --git a/win32/tmake/libsrc/histograms_lut/histograms_lut.pro b/win32/tmake/libsrc/histograms_lut/histograms_lut.pro new file mode 100644 index 00000000..98355189 --- /dev/null +++ b/win32/tmake/libsrc/histograms_lut/histograms_lut.pro @@ -0,0 +1,34 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + hist_dispatch.c \ + im_gammacorrect.c \ + im_heq.c \ + im_hist.c \ + im_histeq.c \ + im_histgr.c \ + im_histnD.c \ + im_histplot.c \ + im_histspec.c \ + im_hsp.c \ + im_identity.c \ + im_invertlut.c \ + im_lhisteq.c \ + im_maplut.c \ + im_stdif.c \ + tone.c +TARGET = histograms_lut diff --git a/win32/tmake/libsrc/inplace/Makefile b/win32/tmake/libsrc/inplace/Makefile new file mode 100644 index 00000000..d89859a2 --- /dev/null +++ b/win32/tmake/libsrc/inplace/Makefile @@ -0,0 +1,122 @@ +############################################################################# +# Makefile for building inplace +# Generated by tmake at 16:03, 2003/06/11 +# Project: inplace +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = im_circle.c \ + im_flood.c \ + im_insertplace.c \ + im_line.c \ + im_paintrect.c \ + im_plotmask.c \ + inplace_dispatch.c \ + line_draw.c \ + plot_point.c \ + smudge_area.c +OBJECTS = im_circle.obj \ + im_flood.obj \ + im_insertplace.obj \ + im_line.obj \ + im_paintrect.obj \ + im_plotmask.obj \ + inplace_dispatch.obj \ + line_draw.obj \ + plot_point.obj \ + smudge_area.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\inplace.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: inplace.pro + tmake inplace.pro -o Makefile + +dist: + $(ZIP) inplace.zip inplace.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_circle.obj + -del im_flood.obj + -del im_insertplace.obj + -del im_line.obj + -del im_paintrect.obj + -del im_plotmask.obj + -del inplace_dispatch.obj + -del line_draw.obj + -del plot_point.obj + -del smudge_area.obj + -del $(TARGET) + +####### Compile + +im_circle.obj: im_circle.c + +im_flood.obj: im_flood.c + +im_insertplace.obj: im_insertplace.c + +im_line.obj: im_line.c + +im_paintrect.obj: im_paintrect.c + +im_plotmask.obj: im_plotmask.c + +inplace_dispatch.obj: inplace_dispatch.c + +line_draw.obj: line_draw.c + +plot_point.obj: plot_point.c + +smudge_area.obj: smudge_area.c + diff --git a/win32/tmake/libsrc/inplace/inplace.pro b/win32/tmake/libsrc/inplace/inplace.pro new file mode 100644 index 00000000..4718cacd --- /dev/null +++ b/win32/tmake/libsrc/inplace/inplace.pro @@ -0,0 +1,28 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + im_circle.c \ + im_flood.c \ + im_insertplace.c \ + im_line.c \ + im_paintrect.c \ + im_plotmask.c \ + inplace_dispatch.c \ + line_draw.c \ + plot_point.c \ + smudge_area.c +TARGET = inplace diff --git a/win32/tmake/libsrc/iofuncs/Makefile b/win32/tmake/libsrc/iofuncs/Makefile new file mode 100644 index 00000000..322e9bd7 --- /dev/null +++ b/win32/tmake/libsrc/iofuncs/Makefile @@ -0,0 +1,337 @@ +############################################################################# +# Makefile for building iofuncs +# Generated by tmake at 16:03, 2003/06/11 +# Project: iofuncs +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = callback.c \ + debug.c \ + dispatch_types.c \ + error.c \ + error_exit.c \ + im_append_Hist.c \ + im_binfile.c \ + im_close.c \ + im_cp_Hist.c \ + im_cp_desc.c \ + im_crwrhd.c \ + im_debugim.c \ + im_demand_hint.c \ + im_desc_hd.c \ + im_generate.c \ + im_header.c \ + im_histlin.c \ + im_image.c \ + im_init.c \ + im_initdesc.c \ + im_inithd.c \ + im_iocheck.c \ + im_iterate.c \ + im_makerw.c \ + im_mapfile.c \ + im_openin.c \ + im_open.c \ + im_openout.c \ + im_partial.c \ + im_piocheck.c \ + im_prepare.c \ + im_printdesc.c \ + im_printhd.c \ + im_printlines.c \ + im_readhist.c \ + im_setbox.c \ + im_setbuf.c \ + im_setupout.c \ + im_unmapfile.c \ + im_updatehist.c \ + im_guess_prefix.c \ + im_wrapmany.c \ + im_wrapone.c \ + im_writeline.c \ + list.c \ + memory.c \ + package.c \ + predicate.c \ + region.c \ + rect.c \ + thread.c \ + threadgroup.c \ + time.c +OBJECTS = callback.obj \ + debug.obj \ + dispatch_types.obj \ + error.obj \ + error_exit.obj \ + im_append_Hist.obj \ + im_binfile.obj \ + im_close.obj \ + im_cp_Hist.obj \ + im_cp_desc.obj \ + im_crwrhd.obj \ + im_debugim.obj \ + im_demand_hint.obj \ + im_desc_hd.obj \ + im_generate.obj \ + im_header.obj \ + im_histlin.obj \ + im_image.obj \ + im_init.obj \ + im_initdesc.obj \ + im_inithd.obj \ + im_iocheck.obj \ + im_iterate.obj \ + im_makerw.obj \ + im_mapfile.obj \ + im_openin.obj \ + im_open.obj \ + im_openout.obj \ + im_partial.obj \ + im_piocheck.obj \ + im_prepare.obj \ + im_printdesc.obj \ + im_printhd.obj \ + im_printlines.obj \ + im_readhist.obj \ + im_setbox.obj \ + im_setbuf.obj \ + im_setupout.obj \ + im_unmapfile.obj \ + im_updatehist.obj \ + im_guess_prefix.obj \ + im_wrapmany.obj \ + im_wrapone.obj \ + im_writeline.obj \ + list.obj \ + memory.obj \ + package.obj \ + predicate.obj \ + region.obj \ + rect.obj \ + thread.obj \ + threadgroup.obj \ + time.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\iofuncs.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: iofuncs.pro + tmake iofuncs.pro -o Makefile + +dist: + $(ZIP) iofuncs.zip iofuncs.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del callback.obj + -del debug.obj + -del dispatch_types.obj + -del error.obj + -del error_exit.obj + -del im_append_Hist.obj + -del im_binfile.obj + -del im_close.obj + -del im_cp_Hist.obj + -del im_cp_desc.obj + -del im_crwrhd.obj + -del im_debugim.obj + -del im_demand_hint.obj + -del im_desc_hd.obj + -del im_generate.obj + -del im_header.obj + -del im_histlin.obj + -del im_image.obj + -del im_init.obj + -del im_initdesc.obj + -del im_inithd.obj + -del im_iocheck.obj + -del im_iterate.obj + -del im_makerw.obj + -del im_mapfile.obj + -del im_openin.obj + -del im_open.obj + -del im_openout.obj + -del im_partial.obj + -del im_piocheck.obj + -del im_prepare.obj + -del im_printdesc.obj + -del im_printhd.obj + -del im_printlines.obj + -del im_readhist.obj + -del im_setbox.obj + -del im_setbuf.obj + -del im_setupout.obj + -del im_unmapfile.obj + -del im_updatehist.obj + -del im_guess_prefix.obj + -del im_wrapmany.obj + -del im_wrapone.obj + -del im_writeline.obj + -del list.obj + -del memory.obj + -del package.obj + -del predicate.obj + -del region.obj + -del rect.obj + -del thread.obj + -del threadgroup.obj + -del time.obj + -del $(TARGET) + +####### Compile + +callback.obj: callback.c + +debug.obj: debug.c + +dispatch_types.obj: dispatch_types.c + +error.obj: error.c + +error_exit.obj: error_exit.c + +im_append_Hist.obj: im_append_Hist.c + +im_binfile.obj: im_binfile.c + +im_close.obj: im_close.c + +im_cp_Hist.obj: im_cp_Hist.c + +im_cp_desc.obj: im_cp_desc.c + +im_crwrhd.obj: im_crwrhd.c + +im_debugim.obj: im_debugim.c + +im_demand_hint.obj: im_demand_hint.c + +im_desc_hd.obj: im_desc_hd.c + +im_generate.obj: im_generate.c + +im_header.obj: im_header.c + +im_histlin.obj: im_histlin.c + +im_image.obj: im_image.c + +im_init.obj: im_init.c + +im_initdesc.obj: im_initdesc.c + +im_inithd.obj: im_inithd.c + +im_iocheck.obj: im_iocheck.c + +im_iterate.obj: im_iterate.c + +im_makerw.obj: im_makerw.c + +im_mapfile.obj: im_mapfile.c + +im_openin.obj: im_openin.c + +im_open.obj: im_open.c + +im_openout.obj: im_openout.c + +im_partial.obj: im_partial.c + +im_piocheck.obj: im_piocheck.c + +im_prepare.obj: im_prepare.c + +im_printdesc.obj: im_printdesc.c + +im_printhd.obj: im_printhd.c + +im_printlines.obj: im_printlines.c + +im_readhist.obj: im_readhist.c + +im_setbox.obj: im_setbox.c + +im_setbuf.obj: im_setbuf.c + +im_setupout.obj: im_setupout.c + +im_unmapfile.obj: im_unmapfile.c + +im_updatehist.obj: im_updatehist.c + +im_guess_prefix.obj: im_guess_prefix.c + +im_wrapmany.obj: im_wrapmany.c + +im_wrapone.obj: im_wrapone.c + +im_writeline.obj: im_writeline.c + +list.obj: list.c + +memory.obj: memory.c + +package.obj: package.c + +predicate.obj: predicate.c + +region.obj: region.c + +rect.obj: rect.c + +thread.obj: thread.c + +threadgroup.obj: threadgroup.c + +time.obj: time.c + diff --git a/win32/tmake/libsrc/iofuncs/iofuncs.pro b/win32/tmake/libsrc/iofuncs/iofuncs.pro new file mode 100644 index 00000000..9d758b8a --- /dev/null +++ b/win32/tmake/libsrc/iofuncs/iofuncs.pro @@ -0,0 +1,72 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + callback.c \ + debug.c \ + dispatch_types.c \ + error.c \ + error_exit.c \ + im_append_Hist.c \ + im_binfile.c \ + im_close.c \ + im_cp_Hist.c \ + im_cp_desc.c \ + im_crwrhd.c \ + im_debugim.c \ + im_demand_hint.c \ + im_desc_hd.c \ + im_generate.c \ + im_header.c \ + im_histlin.c \ + im_image.c \ + im_init.c \ + im_initdesc.c \ + im_inithd.c \ + im_iocheck.c \ + im_iterate.c \ + im_makerw.c \ + im_mapfile.c \ + im_openin.c \ + im_open.c \ + im_openout.c \ + im_partial.c \ + im_piocheck.c \ + im_prepare.c \ + im_printdesc.c \ + im_printhd.c \ + im_printlines.c \ + im_readhist.c \ + im_setbox.c \ + im_setbuf.c \ + im_setupout.c \ + im_unmapfile.c \ + im_updatehist.c \ + im_guess_prefix.c \ + im_wrapmany.c \ + im_wrapone.c \ + im_writeline.c \ + list.c \ + memory.c \ + package.c \ + predicate.c \ + region.c \ + rect.c \ + thread.c \ + threadgroup.c \ + time.c +TARGET = iofuncs + diff --git a/win32/tmake/libsrc/makefile_todo b/win32/tmake/libsrc/makefile_todo new file mode 100644 index 00000000..12fde75b --- /dev/null +++ b/win32/tmake/libsrc/makefile_todo @@ -0,0 +1,222 @@ +############################################################################# +# Makefile for building targets in sub directories. +# Generated by tmake at 13:14, 2003/06/11 +# Project: todo +# Template: subdirs +############################################################################# + +MAKEFILE= Makefile +TMAKE = tmake + +SUBDIRS = acquire \ + arithmetic \ + boolean \ + colour \ + conversion \ + convolution \ + freq_filt \ + histograms_lut \ + inplace \ + iofuncs \ + matrix \ + morphology \ + mosaicing \ + other \ + relational \ + video \ + . + +all: $(SUBDIRS) vips + +acquire: FORCE + cd acquire + $(MAKE) + @cd .. + +arithmetic: FORCE + cd arithmetic + $(MAKE) + @cd .. + +boolean: FORCE + cd boolean + $(MAKE) + @cd .. + +colour: FORCE + cd colour + $(MAKE) + @cd .. + +conversion: FORCE + cd conversion + $(MAKE) + @cd .. + +convolution: FORCE + cd convolution + $(MAKE) + @cd .. + +freq_filt: FORCE + cd freq_filt + $(MAKE) + @cd .. + +histograms_lut: FORCE + cd histograms_lut + $(MAKE) + @cd .. + +inplace: FORCE + cd inplace + $(MAKE) + @cd .. + +iofuncs: FORCE + cd iofuncs + $(MAKE) + @cd .. + +matrix: FORCE + cd matrix + $(MAKE) + @cd .. + +morphology: FORCE + cd morphology + $(MAKE) + @cd .. + +mosaicing: FORCE + cd mosaicing + $(MAKE) + @cd .. + +other: FORCE + cd other + $(MAKE) + @cd .. + +relational: FORCE + cd relational + $(MAKE) + @cd .. + +video: FORCE + cd video + $(MAKE) + @cd .. + +vips: FORCE + $(MAKE) + + +tmake: makefile_todo + +makefile_todo: todo.pro + tmake todo.pro -o makefile_todo + +tmake_all: + cd acquire + $(TMAKE) acquire.pro -o $(MAKEFILE) + @cd .. + cd arithmetic + $(TMAKE) arithmetic.pro -o $(MAKEFILE) + @cd .. + cd boolean + $(TMAKE) boolean.pro -o $(MAKEFILE) + @cd .. + cd colour + $(TMAKE) colour.pro -o $(MAKEFILE) + @cd .. + cd conversion + $(TMAKE) conversion.pro -o $(MAKEFILE) + @cd .. + cd convolution + $(TMAKE) convolution.pro -o $(MAKEFILE) + @cd .. + cd freq_filt + $(TMAKE) freq_filt.pro -o $(MAKEFILE) + @cd .. + cd histograms_lut + $(TMAKE) histograms_lut.pro -o $(MAKEFILE) + @cd .. + cd inplace + $(TMAKE) inplace.pro -o $(MAKEFILE) + @cd .. + cd iofuncs + $(TMAKE) iofuncs.pro -o $(MAKEFILE) + @cd .. + cd matrix + $(TMAKE) matrix.pro -o $(MAKEFILE) + @cd .. + cd morphology + $(TMAKE) morphology.pro -o $(MAKEFILE) + @cd .. + cd mosaicing + $(TMAKE) mosaicing.pro -o $(MAKEFILE) + @cd .. + cd other + $(TMAKE) other.pro -o $(MAKEFILE) + @cd .. + cd relational + $(TMAKE) relational.pro -o $(MAKEFILE) + @cd .. + cd video + $(TMAKE) video.pro -o $(MAKEFILE) + @cd .. + $(TMAKE) vips.pro -o $(MAKEFILE) + +clean: + cd acquire + $(MAKE) clean + @cd .. + cd arithmetic + $(MAKE) clean + @cd .. + cd boolean + $(MAKE) clean + @cd .. + cd colour + $(MAKE) clean + @cd .. + cd conversion + $(MAKE) clean + @cd .. + cd convolution + $(MAKE) clean + @cd .. + cd freq_filt + $(MAKE) clean + @cd .. + cd histograms_lut + $(MAKE) clean + @cd .. + cd inplace + $(MAKE) clean + @cd .. + cd iofuncs + $(MAKE) clean + @cd .. + cd matrix + $(MAKE) clean + @cd .. + cd morphology + $(MAKE) clean + @cd .. + cd mosaicing + $(MAKE) clean + @cd .. + cd other + $(MAKE) clean + @cd .. + cd relational + $(MAKE) clean + @cd .. + cd video + $(MAKE) clean + @cd .. + $(MAKE) clean + +FORCE: diff --git a/win32/tmake/libsrc/matrix/Makefile b/win32/tmake/libsrc/matrix/Makefile new file mode 100644 index 00000000..9c7ab386 --- /dev/null +++ b/win32/tmake/libsrc/matrix/Makefile @@ -0,0 +1,107 @@ +############################################################################# +# Makefile for building matrix +# Generated by tmake at 16:03, 2003/06/11 +# Project: matrix +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = im_invmat.c \ + im_matcat.c \ + im_matinv.c \ + im_matmul.c \ + im_mattrn.c \ + matalloc.c \ + matrix_dispatch.c +OBJECTS = im_invmat.obj \ + im_matcat.obj \ + im_matinv.obj \ + im_matmul.obj \ + im_mattrn.obj \ + matalloc.obj \ + matrix_dispatch.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\matrix.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: matrix.pro + tmake matrix.pro -o Makefile + +dist: + $(ZIP) matrix.zip matrix.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_invmat.obj + -del im_matcat.obj + -del im_matinv.obj + -del im_matmul.obj + -del im_mattrn.obj + -del matalloc.obj + -del matrix_dispatch.obj + -del $(TARGET) + +####### Compile + +im_invmat.obj: im_invmat.c + +im_matcat.obj: im_matcat.c + +im_matinv.obj: im_matinv.c + +im_matmul.obj: im_matmul.c + +im_mattrn.obj: im_mattrn.c + +matalloc.obj: matalloc.c + +matrix_dispatch.obj: matrix_dispatch.c + diff --git a/win32/tmake/libsrc/matrix/matrix.pro b/win32/tmake/libsrc/matrix/matrix.pro new file mode 100644 index 00000000..ba23c8e8 --- /dev/null +++ b/win32/tmake/libsrc/matrix/matrix.pro @@ -0,0 +1,26 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + im_invmat.c \ + im_matcat.c \ + im_matinv.c \ + im_matmul.c \ + im_mattrn.c \ + matalloc.c \ + matrix_dispatch.c +TARGET = matrix + diff --git a/win32/tmake/libsrc/morphology/Makefile b/win32/tmake/libsrc/morphology/Makefile new file mode 100644 index 00000000..4dfb484b --- /dev/null +++ b/win32/tmake/libsrc/morphology/Makefile @@ -0,0 +1,97 @@ +############################################################################# +# Makefile for building morphology +# Generated by tmake at 16:03, 2003/06/11 +# Project: morphology +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = im_cntlines.c \ + im_dilate.c \ + im_erode.c \ + morph_dispatch.c \ + im_profile.c +OBJECTS = im_cntlines.obj \ + im_dilate.obj \ + im_erode.obj \ + morph_dispatch.obj \ + im_profile.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\morphology.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: morphology.pro + tmake morphology.pro -o Makefile + +dist: + $(ZIP) morphology.zip morphology.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_cntlines.obj + -del im_dilate.obj + -del im_erode.obj + -del morph_dispatch.obj + -del im_profile.obj + -del $(TARGET) + +####### Compile + +im_cntlines.obj: im_cntlines.c + +im_dilate.obj: im_dilate.c + +im_erode.obj: im_erode.c + +morph_dispatch.obj: morph_dispatch.c + +im_profile.obj: im_profile.c + diff --git a/win32/tmake/libsrc/morphology/morphology.pro b/win32/tmake/libsrc/morphology/morphology.pro new file mode 100644 index 00000000..1b177773 --- /dev/null +++ b/win32/tmake/libsrc/morphology/morphology.pro @@ -0,0 +1,24 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + im_cntlines.c \ + im_dilate.c \ + im_erode.c \ + morph_dispatch.c \ + im_profile.c +TARGET = morphology + diff --git a/win32/tmake/libsrc/mosaicing/Makefile b/win32/tmake/libsrc/mosaicing/Makefile new file mode 100644 index 00000000..972d16b9 --- /dev/null +++ b/win32/tmake/libsrc/mosaicing/Makefile @@ -0,0 +1,185 @@ +############################################################################# +# Makefile for building mosaicing +# Generated by tmake at 16:03, 2003/06/11 +# Project: mosaicing +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h \ + merge.h \ + global_balance.h \ + mosaic.h +SOURCES = im_affine.c \ + match.c \ + mosaic1.c \ + mosaicing_dispatch.c \ + similarity.c \ + global_balance.c \ + im_avgdxdy.c \ + im_chkpair.c \ + im_clinear.c \ + im_improve.c \ + im_initialize.c \ + im_lrcalcon.c \ + im_lrmerge.c \ + im_lrmosaic.c \ + im_tbcalcon.c \ + im_tbmerge.c \ + im_remosaic.c \ + im_tbmosaic.c +OBJECTS = im_affine.obj \ + match.obj \ + mosaic1.obj \ + mosaicing_dispatch.obj \ + similarity.obj \ + global_balance.obj \ + im_avgdxdy.obj \ + im_chkpair.obj \ + im_clinear.obj \ + im_improve.obj \ + im_initialize.obj \ + im_lrcalcon.obj \ + im_lrmerge.obj \ + im_lrmosaic.obj \ + im_tbcalcon.obj \ + im_tbmerge.obj \ + im_remosaic.obj \ + im_tbmosaic.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\mosaicing.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: mosaicing.pro + tmake mosaicing.pro -o Makefile + +dist: + $(ZIP) mosaicing.zip mosaicing.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_affine.obj + -del match.obj + -del mosaic1.obj + -del mosaicing_dispatch.obj + -del similarity.obj + -del global_balance.obj + -del im_avgdxdy.obj + -del im_chkpair.obj + -del im_clinear.obj + -del im_improve.obj + -del im_initialize.obj + -del im_lrcalcon.obj + -del im_lrmerge.obj + -del im_lrmosaic.obj + -del im_tbcalcon.obj + -del im_tbmerge.obj + -del im_remosaic.obj + -del im_tbmosaic.obj + -del $(TARGET) + +####### Compile + +im_affine.obj: im_affine.c \ + merge.h + +match.obj: match.c \ + mosaic.h + +mosaic1.obj: mosaic1.c \ + mosaic.h \ + merge.h + +mosaicing_dispatch.obj: mosaicing_dispatch.c + +similarity.obj: similarity.c \ + merge.h + +global_balance.obj: global_balance.c \ + merge.h \ + global_balance.h + +im_avgdxdy.obj: im_avgdxdy.c \ + mosaic.h + +im_chkpair.obj: im_chkpair.c \ + mosaic.h + +im_clinear.obj: im_clinear.c \ + mosaic.h + +im_improve.obj: im_improve.c \ + mosaic.h + +im_initialize.obj: im_initialize.c \ + mosaic.h + +im_lrcalcon.obj: im_lrcalcon.c \ + mosaic.h + +im_lrmerge.obj: im_lrmerge.c \ + merge.h + +im_lrmosaic.obj: im_lrmosaic.c \ + mosaic.h + +im_tbcalcon.obj: im_tbcalcon.c \ + mosaic.h + +im_tbmerge.obj: im_tbmerge.c \ + merge.h + +im_remosaic.obj: im_remosaic.c \ + merge.h \ + global_balance.h + +im_tbmosaic.obj: im_tbmosaic.c \ + mosaic.h + diff --git a/win32/tmake/libsrc/mosaicing/mosaicing.pro b/win32/tmake/libsrc/mosaicing/mosaicing.pro new file mode 100644 index 00000000..207151c5 --- /dev/null +++ b/win32/tmake/libsrc/mosaicing/mosaicing.pro @@ -0,0 +1,37 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + im_affine.c \ + match.c \ + mosaic1.c \ + mosaicing_dispatch.c \ + similarity.c \ + global_balance.c \ + im_avgdxdy.c \ + im_chkpair.c \ + im_clinear.c \ + im_improve.c \ + im_initialize.c \ + im_lrcalcon.c \ + im_lrmerge.c \ + im_lrmosaic.c \ + im_tbcalcon.c \ + im_tbmerge.c \ + im_remosaic.c \ + im_tbmosaic.c +TARGET = mosaicing + diff --git a/win32/tmake/libsrc/other/Makefile b/win32/tmake/libsrc/other/Makefile new file mode 100644 index 00000000..bbda705a --- /dev/null +++ b/win32/tmake/libsrc/other/Makefile @@ -0,0 +1,127 @@ +############################################################################# +# Makefile for building other +# Generated by tmake at 16:03, 2003/06/11 +# Project: other +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = cooc_funcs.c \ + glds_funcs.c \ + im_dif_std.c \ + im_eye.c \ + im_grey.c \ + im_meanstd.c \ + im_simcontr.c \ + im_sines.c \ + im_spatres.c \ + im_zone.c \ + other_dispatch.c +OBJECTS = cooc_funcs.obj \ + glds_funcs.obj \ + im_dif_std.obj \ + im_eye.obj \ + im_grey.obj \ + im_meanstd.obj \ + im_simcontr.obj \ + im_sines.obj \ + im_spatres.obj \ + im_zone.obj \ + other_dispatch.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\other.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: other.pro + tmake other.pro -o Makefile + +dist: + $(ZIP) other.zip other.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del cooc_funcs.obj + -del glds_funcs.obj + -del im_dif_std.obj + -del im_eye.obj + -del im_grey.obj + -del im_meanstd.obj + -del im_simcontr.obj + -del im_sines.obj + -del im_spatres.obj + -del im_zone.obj + -del other_dispatch.obj + -del $(TARGET) + +####### Compile + +cooc_funcs.obj: cooc_funcs.c + +glds_funcs.obj: glds_funcs.c + +im_dif_std.obj: im_dif_std.c + +im_eye.obj: im_eye.c + +im_grey.obj: im_grey.c + +im_meanstd.obj: im_meanstd.c + +im_simcontr.obj: im_simcontr.c + +im_sines.obj: im_sines.c + +im_spatres.obj: im_spatres.c + +im_zone.obj: im_zone.c + +other_dispatch.obj: other_dispatch.c + diff --git a/win32/tmake/libsrc/other/other.pro b/win32/tmake/libsrc/other/other.pro new file mode 100644 index 00000000..c65ad353 --- /dev/null +++ b/win32/tmake/libsrc/other/other.pro @@ -0,0 +1,30 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + cooc_funcs.c \ + glds_funcs.c \ + im_dif_std.c \ + im_eye.c \ + im_grey.c \ + im_meanstd.c \ + im_simcontr.c \ + im_sines.c \ + im_spatres.c \ + im_zone.c \ + other_dispatch.c +TARGET = other + diff --git a/win32/tmake/libsrc/relational/Makefile b/win32/tmake/libsrc/relational/Makefile new file mode 100644 index 00000000..73ae58a9 --- /dev/null +++ b/win32/tmake/libsrc/relational/Makefile @@ -0,0 +1,92 @@ +############################################################################# +# Makefile for building relational +# Generated by tmake at 16:03, 2003/06/11 +# Project: relational +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = im_ifthenelse.c \ + im_blend.c \ + relational.c \ + relational_dispatch.c +OBJECTS = im_ifthenelse.obj \ + im_blend.obj \ + relational.obj \ + relational_dispatch.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\relational.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: relational.pro + tmake relational.pro -o Makefile + +dist: + $(ZIP) relational.zip relational.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_ifthenelse.obj + -del im_blend.obj + -del relational.obj + -del relational_dispatch.obj + -del $(TARGET) + +####### Compile + +im_ifthenelse.obj: im_ifthenelse.c + +im_blend.obj: im_blend.c + +relational.obj: relational.c + +relational_dispatch.obj: relational_dispatch.c + diff --git a/win32/tmake/libsrc/relational/relational.pro b/win32/tmake/libsrc/relational/relational.pro new file mode 100644 index 00000000..e1d477e3f --- /dev/null +++ b/win32/tmake/libsrc/relational/relational.pro @@ -0,0 +1,23 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + im_ifthenelse.c \ + im_blend.c \ + relational.c \ + relational_dispatch.c +TARGET = relational + diff --git a/win32/tmake/libsrc/todo.pro b/win32/tmake/libsrc/todo.pro new file mode 100644 index 00000000..83fcd569 --- /dev/null +++ b/win32/tmake/libsrc/todo.pro @@ -0,0 +1,19 @@ +TEMPLATE = subdirs +MAKE = nmake +SUBDIRS = \ + acquire \ + arithmetic \ + boolean \ + colour \ + conversion \ + convolution \ + freq_filt \ + histograms_lut \ + inplace \ + iofuncs \ + matrix \ + morphology \ + mosaicing \ + other \ + relational \ + video diff --git a/win32/tmake/libsrc/video/Makefile b/win32/tmake/libsrc/video/Makefile new file mode 100644 index 00000000..9615ae18 --- /dev/null +++ b/win32/tmake/libsrc/video/Makefile @@ -0,0 +1,82 @@ +############################################################################# +# Makefile for building video +# Generated by tmake at 16:03, 2003/06/11 +# Project: video +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = video_dispatch.c \ + im_video_v4l1.c +OBJECTS = video_dispatch.obj \ + im_video_v4l1.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\video.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: video.pro + tmake video.pro -o Makefile + +dist: + $(ZIP) video.zip video.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del video_dispatch.obj + -del im_video_v4l1.obj + -del $(TARGET) + +####### Compile + +video_dispatch.obj: video_dispatch.c + +im_video_v4l1.obj: im_video_v4l1.c + diff --git a/win32/tmake/libsrc/video/video.pro b/win32/tmake/libsrc/video/video.pro new file mode 100644 index 00000000..cd428d8a --- /dev/null +++ b/win32/tmake/libsrc/video/video.pro @@ -0,0 +1,21 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + video_dispatch.c \ + im_video_v4l1.c +TARGET = video + diff --git a/win32/tmake/libsrc/vips.pro b/win32/tmake/libsrc/vips.pro new file mode 100644 index 00000000..42f9e463 --- /dev/null +++ b/win32/tmake/libsrc/vips.pro @@ -0,0 +1,21 @@ +TEMPLATE = app +DIR_LIB = ../lib +CONFIG = release dll +win32:CONFIG += windows +TMAKEFLAGS = -nologo +INCLUDEPATH = .. ../include +HEADERS = ../config.h +SOURCES = dummy.c +DESTDIR = ../Release +unix:LIBS += \ + $$DESTDIR/*.a +win32:LIBS += \ + $$DESTDIR/*.lib \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DEF_FILE = vips.def +TARGET = vips +VERSION = 7.8.10 +CLEAN = $$DESTDIR/vips.exp $$DESTDIR/vips.lib