diff options
-rw-r--r-- | COPYING | 339 | ||||
-rw-r--r-- | Changes | 180 | ||||
-rw-r--r-- | GNUmakefile | 215 | ||||
-rw-r--r-- | INSTALL | 59 | ||||
-rw-r--r-- | Ida.ad | 655 | ||||
-rw-r--r-- | README | 47 | ||||
-rw-r--r-- | RegEdit.c | 1795 | ||||
-rw-r--r-- | RegEdit.h | 65 | ||||
-rw-r--r-- | RegEditI.h | 368 | ||||
-rw-r--r-- | TODO | 22 | ||||
-rw-r--r-- | VERSION | 1 | ||||
-rw-r--r-- | backup/Ida-de | 173 | ||||
-rw-r--r-- | backup/Ida-default | 178 | ||||
-rw-r--r-- | backup/Ida-fixed | 439 | ||||
-rw-r--r-- | browser.c | 571 | ||||
-rw-r--r-- | browser.h | 3 | ||||
-rw-r--r-- | color.c | 513 | ||||
-rw-r--r-- | color.h | 1 | ||||
-rw-r--r-- | config.h | 0 | ||||
-rw-r--r-- | curl.c | 348 | ||||
-rw-r--r-- | curl.h | 5 | ||||
-rw-r--r-- | desktop.c | 276 | ||||
-rw-r--r-- | desktop.h | 7 | ||||
-rw-r--r-- | desktop/ida.desktop | 9 | ||||
-rw-r--r-- | dither.c | 193 | ||||
-rw-r--r-- | dither.h | 6 | ||||
-rw-r--r-- | exiftran.c | 262 | ||||
-rw-r--r-- | exiftran.man | 106 | ||||
-rwxr-xr-x | fallback.pl | 26 | ||||
-rw-r--r-- | fb-gui.c | 189 | ||||
-rw-r--r-- | fb-gui.h | 11 | ||||
-rwxr-xr-x | fbgs | 69 | ||||
-rw-r--r-- | fbgs.man | 20 | ||||
-rw-r--r-- | fbi.c | 1552 | ||||
-rw-r--r-- | fbi.man | 160 | ||||
-rw-r--r-- | fbtools.c | 523 | ||||
-rw-r--r-- | fbtools.h | 23 | ||||
-rw-r--r-- | filebutton.c | 934 | ||||
-rw-r--r-- | filebutton.h | 71 | ||||
-rw-r--r-- | filelist.c | 619 | ||||
-rw-r--r-- | filelist.h | 3 | ||||
-rw-r--r-- | fileops.c | 100 | ||||
-rw-r--r-- | fileops.h | 1 | ||||
-rw-r--r-- | filter.c | 495 | ||||
-rw-r--r-- | filter.h | 27 | ||||
-rw-r--r-- | fs.c | 502 | ||||
-rw-r--r-- | fs.h | 72 | ||||
-rw-r--r-- | genthumbnail.c | 219 | ||||
-rw-r--r-- | genthumbnail.h | 1 | ||||
-rw-r--r-- | hex.c | 141 | ||||
-rw-r--r-- | hex.h | 1 | ||||
-rw-r--r-- | icons.c | 130 | ||||
-rw-r--r-- | icons.h | 3 | ||||
-rw-r--r-- | ida.c | 1909 | ||||
-rw-r--r-- | ida.h | 26 | ||||
-rw-r--r-- | ida.man | 102 | ||||
-rw-r--r-- | idaconfig.c | 51 | ||||
-rw-r--r-- | idaconfig.h | 24 | ||||
-rw-r--r-- | jpeg/README | 2 | ||||
-rw-r--r-- | jpeg/jinclude.h | 91 | ||||
-rw-r--r-- | jpeg/jpegint.h | 392 | ||||
-rw-r--r-- | jpeg/jpeglib.h | 1096 | ||||
-rw-r--r-- | jpeg/transupp.c | 928 | ||||
-rw-r--r-- | jpeg/transupp.h | 135 | ||||
-rw-r--r-- | jpegtools.c | 621 | ||||
-rw-r--r-- | jpegtools.h | 28 | ||||
-rw-r--r-- | lirc.c | 60 | ||||
-rw-r--r-- | lirc.h | 2 | ||||
-rw-r--r-- | list.h | 168 | ||||
-rw-r--r-- | logo.jpg | bin | 0 -> 16990 bytes | |||
-rw-r--r-- | lut.c | 97 | ||||
-rw-r--r-- | lut.h | 15 | ||||
-rw-r--r-- | man.c | 119 | ||||
-rw-r--r-- | man.h | 4 | ||||
-rw-r--r-- | misc.h | 9 | ||||
-rw-r--r-- | mk/Autoconf.mk | 138 | ||||
-rw-r--r-- | mk/Compile.mk | 88 | ||||
-rw-r--r-- | mk/Maintainer.mk | 12 | ||||
-rw-r--r-- | mk/Variables.mk | 46 | ||||
-rw-r--r-- | mk/utf8.tmp | 25 | ||||
-rw-r--r-- | op.c | 289 | ||||
-rw-r--r-- | op.h | 7 | ||||
-rw-r--r-- | parseconfig.c | 857 | ||||
-rw-r--r-- | parseconfig.h | 68 | ||||
-rw-r--r-- | rd/Makefile | 2 | ||||
-rw-r--r-- | rd/magick.c | 85 | ||||
-rw-r--r-- | rd/read-bmp.c | 216 | ||||
-rw-r--r-- | rd/read-gif.c | 220 | ||||
-rw-r--r-- | rd/read-jpeg.c | 187 | ||||
-rw-r--r-- | rd/read-pcd.c | 78 | ||||
-rw-r--r-- | rd/read-png.c | 164 | ||||
-rw-r--r-- | rd/read-ppm.c | 110 | ||||
-rw-r--r-- | rd/read-tiff.c | 195 | ||||
-rw-r--r-- | rd/read-xpm.c | 287 | ||||
-rw-r--r-- | rd/read-xwd.c | 357 | ||||
-rw-r--r-- | readers.c | 133 | ||||
-rw-r--r-- | readers.h | 104 | ||||
-rw-r--r-- | sane.c | 324 | ||||
-rw-r--r-- | sane.h | 1 | ||||
-rw-r--r-- | selections.c | 623 | ||||
-rw-r--r-- | selections.h | 18 | ||||
-rw-r--r-- | thumbnail.cgi.c | 125 | ||||
-rw-r--r-- | viewer.c | 962 | ||||
-rw-r--r-- | viewer.h | 96 | ||||
-rw-r--r-- | wr/Makefile | 2 | ||||
-rw-r--r-- | wr/write-jpeg.c | 103 | ||||
-rw-r--r-- | wr/write-png.c | 76 | ||||
-rw-r--r-- | wr/write-ppm.c | 34 | ||||
-rw-r--r-- | wr/write-ps.c | 475 | ||||
-rw-r--r-- | wr/write-tiff.c | 60 | ||||
-rw-r--r-- | writers.c | 15 | ||||
-rw-r--r-- | writers.h | 14 | ||||
-rw-r--r-- | x11.c | 456 | ||||
-rw-r--r-- | x11.h | 42 | ||||
-rw-r--r-- | xdnd.c | 321 | ||||
-rw-r--r-- | xdnd.h | 5 | ||||
-rw-r--r-- | xpm/ccw.xpm | 22 | ||||
-rw-r--r-- | xpm/cw.xpm | 22 | ||||
-rw-r--r-- | xpm/dir.xpm | 41 | ||||
-rw-r--r-- | xpm/exit.xpm | 22 | ||||
-rw-r--r-- | xpm/file.xpm | 39 | ||||
-rw-r--r-- | xpm/fliph.xpm | 22 | ||||
-rw-r--r-- | xpm/flipv.xpm | 22 | ||||
-rw-r--r-- | xpm/next.xpm | 22 | ||||
-rw-r--r-- | xpm/prev.xpm | 22 | ||||
-rw-r--r-- | xpm/question.xpm | 38 | ||||
-rw-r--r-- | xpm/unknown.xpm | 39 | ||||
-rw-r--r-- | xpm/zoomin.xpm | 22 | ||||
-rw-r--r-- | xpm/zoomout.xpm | 22 | ||||
-rw-r--r-- | xwd.h | 1 |
130 files changed, 26383 insertions, 0 deletions
@@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies 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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. @@ -0,0 +1,180 @@ + +0.21 +==== + + * add support to edit jpeg comments. + * various minor fixes/tweaks. + + +0.20 +==== + + * file browser code is largely rewritten. + * added support to maintain file lists. + * lots of minor tweaks. + + +0.19 +==== + + * url support for most image formats. + + +0.16 => 0.17 +============ + + * fixed some signed/unsigned bugs. + * fixed tiff loader bug. + * utf-8 locale crash workaround. + + +0.15 => 0.16 +============ + + * ditched autotools crap into the waste basket. + * fixed plenty of gcc 3.3 warnings. + * file browser improvements. + * tweaked font sets. + * multiplage support (Andrey Kiselev). + + +0.14 => 0.15 +============ + + * more tiff loader fixes (based on patches by + Andrey Kiselev <dron@ak4719.spb.edu>). + * png loader fixes. + + +0.13 => 0.14 +============ + + * tiff loader fixes (for b/w images). + * Made pixmap selection transfer less strict. + + +0.12 => 0.13 +============ + + * fixed selections / clipboard / cut+paste handling. + * fixed some warnings. + * dropped extra "make depend" pass from build proccess. + + +0.11 => 0.12 +============ + + * added tooltips (needs OpenMotif 2.2 to work). + * made the scale image dialog take care about image resolution. + * some fixes in sane code. + * man page updates. + * unbundled libpcd. + + +0.10 => 0.11 +============ + + * renamed "iv" to "ida". + * hex viewer fixes. + * [ fixme: weißpunkt ] + * sane support. + * keep track of the image resolution + * The image size for printing / PostScript export defaults to the + original size (calculated from image resolution if available). + + +0.9 => 0.10 +=========== + + * do scaling with floats to avoid rounding errors with large + scale factors. + * fixed segfault with huge zoom factors / huge images. + * need a new name for the utility, "iv" has way to much name + clashes. ideas are welcome ... + + +0.8 => 0.9 +========== + + * fixed memory leak in file browser. + + +0.7 => 0.8 +========== + + * added loader for (uncompressed) windows bitmap files. + * some minor changes for image save code - can write tmp files + cleanly now. DnD for uses jpeg for tmp files, so you can drop them + into netscape. + * added loader for xbm files. + * Some minor DnD fixes. + * better "busy cursor" handling. + * added wildcards filter to the file browser + * file browser DnD fixes. + * made PhotoCD resolution switchable at runtime. + + +0.6 => 0.7 +========== + + * added some handlers for selection data transfer, for + clipboard + drag'n'drop support. + + +0.5 => 0.6 +========== + + * added simple file browser. + * fixed bugs in png image loader. + * fixed a bug in the gif loader. + * figured out that libungif 4.1 is broken (if iv segfaults when loading + gif images, try downgrading to libungif 3.0 + rebuild) + * added autocrop. + * added print dialog. + * added blur,sharpe and emboss filters. + + +0.4 => 0.5 +========== + + * add options for image saving (jpeg, PostScript). + * added PNG write support. + + +0.3 => 0.4 +========== + + * Added resize. + * Added free rotation. + * Added edge detect filter. + * Some fine tuning here and there. + * Added i18n + german translation. + + +0.2 => 0.3 +========== + + * added PhotoCD support. + * fixed some minor nitpicks. + * added toolbar. + * added support for writing tiff images. + * most image operations look at the selected area now. + * added support for writing Postscript. Needs more work, not + configurable yet (does: A4, portrait, image centered and scaled to + max size with same aspect ratio). + + +0.1 => 0.2 +========== + + * implemented saving (ppm, jpeg). + * redirect stderr to a message box. + * color editor works. + * fixed a bug in the gif loader. + * fixed bugs in the tiff loader. + * added -debug + -help command line switches. + * added x window dump loader. + * added support for reading images from stdin (you can now do + screenshots using "xwd | iv -" for example). + * added one-level undo. + * added crop diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..f7f86d9 --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,215 @@ +# config +srcdir = . +VPATH = $(srcdir) +-include Make.config +include $(srcdir)/mk/Variables.mk + +resdir = $(DESTDIR)$(RESDIR) + +# fixup flags +CFLAGS += -DVERSION='"$(VERSION)"' -I$(srcdir) + +# default target +all: build + +# what to build +TARGETS := exiftran thumbnail.cgi +ifeq ($(HAVE_LINUX_FB_H),yes) + TARGETS += fbi +endif +ifeq ($(HAVE_MOTIF),yes) + TARGETS += ida +endif + + +################################################################# +# poor man's autoconf ;-) + +include $(srcdir)/mk/Autoconf.mk + +define make-config +LIB := $(LIB) +RESDIR := $(call ac_resdir) +HAVE_ENDIAN_H := $(call ac_header,endian.h) +HAVE_LINUX_FB_H := $(call ac_header,linux/fb.h) +HAVE_GLIBC := $(call ac_func,fopencookie) +HAVE_STRCASESTR := $(call ac_func,strcasestr) +HAVE_LIBPCD := $(call ac_lib,pcd_open,pcd) +HAVE_LIBUNGIF := $(call ac_lib,DGifOpenFileName,ungif) +HAVE_LIBPNG := $(call ac_lib,png_read_info,png,-lz) +HAVE_LIBTIFF := $(call ac_lib,TIFFOpen,tiff) +#HAVE_LIBMAGICK := $(call ac_binary,Magick-config) +HAVE_LIBSANE := $(call ac_lib,sane_init,sane) +HAVE_LIBCURL := $(call ac_lib,curl_easy_init,curl) +HAVE_LIBLIRC := $(call ac_lib,lirc_init,lirc_client) +HAVE_MOTIF := $(call ac_lib,XmStringGenerate,Xm,-L/usr/X11R6/$(LIB) -lXpm -lXt -lXext -lX11) +endef + +# transparent http/ftp access using curl depends on fopencookie (glibc) +ifneq ($(HAVE_GLIBC),yes) + HAVE_LIBCURL := no +endif + +# catch fopen calls for transparent ftp/http access +ifeq ($(HAVE_LIBCURL),yes) + ida fbi : CFLAGS += -D_GNU_SOURCE + ida fbi : LDFLAGS += -Wl,--wrap=fopen +endif + + +######################################################################## +# conditional stuff + +includes = ENDIAN_H STRCASESTR +libraries = PCD UNGIF PNG TIFF CURL SANE LIRC +ida_libs = PCD UNGIF PNG TIFF CURL SANE +fbi_libs = PCD UNGIF PNG TIFF CURL LIRC + +#MAGICK_CFLAGS = $(shell Magick-config --cflags) +#MAGICK_LDFLAGS = $(shell Magick-config --ldflags) +#MAGICK_LDLIBS = $(shell Magick-config --libs) +#MAGICK_OBJS := rd/magick.o + +PNG_LDLIBS := -lpng -lz +TIFF_LDLIBS := -ltiff +PCD_LDLIBS := -lpcd +UNGIF_LDLIBS := -lungif +SANE_LDLIBS := -lsane +CURL_LDLIBS := -lcurl +LIRC_LDLIBS := -llirc_client + +PNG_OBJS := rd/read-png.o wr/write-png.o +TIFF_OBJS := rd/read-tiff.o wr/write-tiff.o +PCD_OBJS := rd/read-pcd.o +UNGIF_OBJS := rd/read-gif.o +SANE_OBJS := sane.o +CURL_OBJS := curl.o +LIRC_OBJS := lirc.o + +# common objs +OBJS_READER := readers.o rd/read-ppm.o rd/read-bmp.o rd/read-jpeg.o +OBJS_WRITER := writers.o wr/write-ppm.o wr/write-ps.o wr/write-jpeg.o + +# update various flags depending on HAVE_* +CFLAGS += $(call ac_inc_cflags,$(includes)) +CFLAGS += $(call ac_lib_cflags,$(libraries)) +CFLAGS += $(call ac_lib_mkvar,$(libraries),CFLAGS) +LDFLAGS += $(call ac_lib_mkvar,$(libraries),LDFLAGS) + +# link which conditional libs +ida : LDLIBS += $(call ac_lib_mkvar,$(ida_libs),LDLIBS) +fbi : LDLIBS += $(call ac_lib_mkvar,$(fbi_libs),LDLIBS) + + +######################################################################## +# rules for the small tools + +# jpeg/exif libs +exiftran : LDLIBS += -ljpeg -lexif -lm +genthumbnail : LDLIBS += -ljpeg -lexif -lm +thumbnail.cgi : LDLIBS += -lexif -lm + +exiftran: exiftran.o genthumbnail.o jpegtools.o jpeg/transupp.o \ + filter.o op.o readers.o rd/read-jpeg.o +thumbnail.cgi: thumbnail.cgi.o + + +######################################################################## +# rules for ida + +# object files +OBJS_IDA := \ + ida.o man.o hex.o x11.o viewer.o dither.o icons.o \ + parseconfig.o idaconfig.o fileops.o desktop.o \ + RegEdit.o selections.o xdnd.o jpeg/transupp.o \ + filebutton.o filelist.o browser.o jpegtools.o \ + op.o filter.o lut.o color.o \ + rd/read-xwd.o rd/read-xpm.o + +OBJS_IDA += $(call ac_lib_mkvar,$(ida_libs),OBJS) + +# for X11 + Motif +ida : CFLAGS += -I/usr/X11R6/include +ida : LDFLAGS += -L/usr/X11R6/$(LIB) +ida : LDLIBS += -lXm -lXpm -lXt -lXext -lX11 + +# jpeg/exif libs +ida : LDLIBS += -ljpeg -lexif -lm + +# RegEdit.c is good old K&R ... +RegEdit.o : CFLAGS += -Wno-missing-prototypes -Wno-strict-prototypes + +ida: $(OBJS_IDA) $(OBJS_READER) $(OBJS_WRITER) + +Ida.ad.h: Ida.ad $(srcdir)/fallback.pl + perl $(srcdir)/fallback.pl < $< > $@ + +logo.h: logo.jpg + hexdump -v -e '1/1 "0x%02x,"' < $< > $@ + echo >> $@ # make gcc 3.x happy + +ida.o: Ida.ad.h logo.h + + +######################################################################## +# rules for fbi + +# object files +OBJS_FBI := \ + fbi.o fbtools.o fs.o fb-gui.o desktop.o \ + jpegtools.o jpeg/transupp.o \ + dither.o filter.o op.o + +OBJS_FBI += $(filter-out wr/%,$(call ac_lib_mkvar,$(fbi_libs),OBJS)) + +# jpeg/exif libs +fbi : LDLIBS += -ljpeg -lexif -lm +fs.o fb-gui.o : CFLAGS += -DX_DISPLAY_MISSING=1 + +fbi: $(OBJS_FBI) $(OBJS_READER) + + +######################################################################## +# general rules + +.PHONY: build install clean distclean realclean +build: $(TARGETS) + +install: build + $(INSTALL_DIR) $(bindir) + $(INSTALL_DIR) $(mandir)/man1 + $(INSTALL_BINARY) exiftran $(bindir) + $(INSTALL_DATA) $(srcdir)/exiftran.man $(mandir)/man1/exiftran.1 +ifeq ($(HAVE_LINUX_FB_H),yes) + $(INSTALL_BINARY) fbi fbgs $(bindir) + $(INSTALL_DATA) $(srcdir)/fbi.man $(mandir)/man1/fbi.1 + $(INSTALL_DATA) $(srcdir)/fbgs.man $(mandir)/man1/fbgs.1 +endif +ifeq ($(HAVE_MOTIF),yes) + $(INSTALL_BINARY) ida $(bindir) + $(INSTALL_DATA) $(srcdir)/ida.man $(mandir)/man1/ida.1 + $(INSTALL_DIR) $(resdir)/app-defaults + $(INSTALL_DATA) $(srcdir)/Ida.ad $(resdir)/app-defaults/Ida +endif + +clean: + -rm -f *.o jpeg/*.o rd/*.o wr/*.o $(depfiles) core core.* + +realclean distclean: clean + -rm -f Make.config + -rm -f $(TARGETS) *~ rd/*~ wr/*~ xpm/*~ Ida.ad.h logo.h + + +include $(srcdir)/mk/Compile.mk +-include $(depfiles) + + +######################################################################## +# maintainer stuff + +include $(srcdir)/mk/Maintainer.mk + +sync:: + cp $(srcdir)/../xawtv/common/parseconfig.[ch] $(srcdir) + cp $(srcdir)/../xawtv/console/fbtools.[ch] $(srcdir) + cp $(srcdir)/../xawtv/console/fs.[ch] $(srcdir) @@ -0,0 +1,59 @@ + +howto compile and install this package +====================================== + + +really short install instructions +--------------------------------- + + $ make + $ su -c "make install" + + + +the more detailed version +------------------------- + +Make sure you use GNU make. The file name "GNUmakefile" isn't a joke, +this package really requires GNU make. + +As first step make will do some config checks on your system and write +the results to Make.config. If you want to have a look at Make.config +before the actual build starts you can run this step separately using +"make config". + +The Makefiles use the usual GNU-ish Makefile conventions for variable +names and default values, i.e. prefix=/usr/local, ... + +The values for some frequently adapted variables are initialized from +the enviroment. Thus you can change the defaults simply by setting +environment variables: + + $ prefix="/usr" + $ CFLAGS="-O3 -mcpu=i686" + $ export prefix CFLAGS + +Almost any variable can be overridden on the make command line. It is +often used this way to install into some buildroot for packaging ... + + $ su -c "make DESTDIR=/tmp/buildroot install" + +... but it works for most other variables equally well. There are +some exceptions through, it usually does _not_ work for CFLAGS for +example. + +Try "make verbose=yes" if you want to see the complete command lines +executed by make instead of the short messages (for trouble shooting, +because you like this way, for whatever reason ...). This also makes +the config checks performed by "make config" more verbose. + +If you don't trust my Makefiles you can run "make -n install" to see +what "make install" would do on your system. It will produce +human-readable output (unlike automake ...). + +Have fun, + + Gerd + +-- +Gerd Knorr <kraxel@bytesex.org> @@ -0,0 +1,655 @@ +! ---------------------------------------------------------------------------- +! fonts + +*renderTable: small +*renderTable.fontType: FONT_IS_FONTSET +*renderTable.fontName: \ + -*-bitstream vera sans-medium-r-normal-*-*-140-*-*-p-*-iso8859-1, \ + -*-bitstream vera sans-medium-r-normal-*-*-140-*-*-p-*-iso8859-15, \ + -microsoft-tahoma-medium-r-normal-*-*-140-*-*-p-*-iso8859-*, \ + -adobe-helvetica-medium-r-normal-*-*-140-*-*-p-*-iso8859-*, \ + -cronyx-helvetica-medium-r-normal-*-*-140-*-*-p-*-koi8-r, \ + -*-lucida-medium-r-normal-*-*-140-*-*-p-*-iso8859-*, \ + -gnu-unifont-medium-r-normal-*-*-160-*-*-*-*-*-*, \ + -*-*-medium-r-normal-*-*-140-*-*-p-*-*-*, \ + -*-*-medium-r-normal-*-*-160-*-*-p-*-*-*, \ + -*-*-*-*-*-*-*-140-*-*-*-*-*-*, \ + -*-*-*-*-*-*-*-160-*-*-*-*-*-*, * +*renderTable.small.fontType: FONT_IS_FONTSET +*renderTable.small.fontName: \ + -*-bitstream vera sans-medium-r-normal-*-*-100-*-*-p-*-iso8859-1, \ + -*-bitstream vera sans-medium-r-normal-*-*-100-*-*-p-*-iso8859-15, \ + -microsoft-tahoma-medium-r-normal-*-*-100-*-*-p-*-iso8859-*, \ + -adobe-helvetica-medium-r-normal-*-*-100-*-*-p-*-iso8859-*, \ + -cronyx-helvetica-medium-r-normal-*-*-100-*-*-p-*-koi8-r, \ + -*-lucida-medium-r-normal-*-*-100-*-*-p-*-iso8859-*, \ + -*-*-medium-r-normal-*-*-100-*-*-p-*-*-*, \ + -*-*-medium-r-normal-*-*-120-*-*-p-*-*-*, \ + -*-*-*-*-*-*-*-100-*-*-*-*-*-*, \ + -*-*-*-*-*-*-*-120-*-*-*-*-*-*, * + +*XmTextField.renderTable: +*XmTextField.renderTable.fontType: FONT_IS_FONTSET +*XmTextField.renderTable.fontName: \ + -*-bitstream vera sans mono-medium-r-normal-*-*-140-*-*-*-*-iso8859-1, \ + -*-bitstream vera sans mono-medium-r-normal-*-*-140-*-*-*-*-iso8859-15, \ + -monotype-andale mono-medium-r-normal-*-*-140-*-*-*-*-iso8859-*, \ + -adobe-courier-medium-r-normal-*-*-140-*-*-m-*-iso8859-*, \ + -cronyx-courier-medium-r-normal-*-*-140-*-*-m-*-koi8-r, \ + -*-lucidatypewriter-medium-r-normal-*-*-140-*-*-m-*-iso8859-*, \ + -*-*-medium-r-normal-*-*-140-*-*-m-*-*-*, \ + -*-*-medium-r-normal-*-*-160-*-*-m-*-*-*, \ + -*-*-*-*-*-*-*-140-*-*-*-*-*-*, \ + -*-*-*-*-*-*-*-160-*-*-*-*-*-*, * + + +*background: gray77 +!*shadowThickness: 2 +!*highlightThickness: 1 + + +! ---------------------------------------------------------------------------- +! image window + +Ida.geometry: 75x50 +!Ida.winGravity: static +Ida.view*translations: #override \ + <Key>space: Next() \n\ + <Key>osfDelete: Prev() \n\ + <Key>osfBackSpace: Prev() \n\ + <Key>Page_Down: NextPage() \n\ + <Key>Page_Up: PrevPage() \n\ + <Key>N: NextPage() \n\ + ~Ctrl<Key>P: PrevPage() \n\ + <Key>KP_Add: Zoom(inc) \n\ + <Key>KP_Subtract: Zoom(dec) \n\ + <Btn2Down>: Ipc(drag) \n\ + <Btn3Up>: Popup(control) \n\ + \ + <Key>G: Gamma() \n\ + <Key>F: Browser() \n\ + <Key>L: Filelist() \n\ + Ctrl<Key>V: Ipc(paste) \n\ + Alt<Key>V: Ipc(paste) \n\ + Ctrl<Key>C: Ipc(copy) \n\ + Alt<Key>C: Ipc(copy) \n\ + \ + Ctrl<Key>P: Print() \n\ + Ctrl<Key>L: Load() \n\ + Ctrl<Key>S: Save() \n\ + Alt<Key>S: Sharpe() \n\ + ~Alt ~Ctrl<Key>S: Resize() \n\ + <Key>plus: Zoom(inc) \n\ + <Key>minus: Zoom(dec) \n\ + <Key>U: Undo() \n\ + ~Alt ~Ctrl<Key>C: Filter(crop) \n\ + ~Alt ~Ctrl<Key>V: Filter(flip-vert) \n\ + <Key>H: Filter(flip-horz) \n\ + ~Alt ~Shift<Key>T: Filter(rotate-cw) \n\ + Shift<Key>T: Filter(rotate-ccw) \n\ + Alt<Key>T: Rotate() \n\ + <Key>I: Filter(invert) \n\ + ~Alt<Key>E: Color() \n\ + Alt<Key>E: F3x3(-1,-1,-1,-1,8,-1,-1,-1,-1) \n\ + Alt<Key>B: F3x3(1,1,1,1,1,1,1,1,1, 1,9) \n\ + Alt<Key>M: F3x3(1,0,0,0,0,0,0,0,-1, 0,0,128) \n\ + <Key>osfHelp: Man(ida) \n\ + <Key>Q: Exit() + +Ida.view.VertScrollBar.accelerators: #override \ + <Btn4Up>: IncrementUpOrLeft(Up) \n\ + <Btn5Up>: IncrementDownOrRight(Down)\n\ + ~Ctrl<Key>osfUp: IncrementUpOrLeft(Up) \n\ + ~Ctrl<Key>osfDown: IncrementDownOrRight(Down)\n\ + Ctrl<Key>osfUp: PageUpOrLeft(Up) \n\ + Ctrl<Key>osfDown: PageDownOrRight(Down) +Ida.view.HorScrollBar.accelerators: #override \ + ~Ctrl<Key>osfLeft: IncrementUpOrLeft(Left) \n\ + ~Ctrl<Key>osfRight: IncrementDownOrRight(Right)\n\ + Ctrl<Key>osfLeft: PageUpOrLeft(Left) \n\ + Ctrl<Key>osfRight: PageDownOrRight(Right) + + +Ida.view.shadowThickness: 1 +Ida.view.scrollingPolicy: AUTOMATIC +Ida.view.scrollBarPlacement: BOTTOM_RIGHT +Ida.view.scrolledWindowChildType: SCROLL_VERT + +Ida.view*image.backgroundPixmap: none +Ida.view*image.borderWidth: 0 +Ida.view*image.highlightThickness: 1 +Ida.view*image.highlightColor: red + +Ida.aboutbox_popup.deleteResponse: DESTROY +Ida.aboutbox_popup.title: About ida +Ida*aboutbox_popup*messageString: \ + ida - image viewer & editor \n\ + \n\ + (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> + +Ida.sorrybox_popup.deleteResponse: DESTROY + +Ida.noundobox_popup.deleteResponse: DESTROY +Ida.noundobox_popup.title: No undo +Ida*noundobox_popup*messageString: \ + No undo info available, sorry. \n\ + You can undo the last step only. + + +! ---------------------------------------------------------------------------- +! dialog boxes + +Ida.XmDialogShell.deleteResponse: DESTROY +Ida.XmDialogShell*scale.orientation: HORIZONTAL +Ida.XmDialogShell*scale.showValue: True + +Ida.errbox_popup.deleteResponse: UNMAP +Ida.errbox_popup.title: Errors + +Ida.load_popup.deleteResponse: UNMAP +Ida.load_popup.title: Load File + +Ida.save_popup*deleteResponse: UNMAP +Ida.save_popup*dialogStyle: DIALOG_PRIMARY_APPLICATION_MODAL +Ida.save_popup.title: Save File +Ida.save_popup*format.labelString: Image format: + +Ida.print_popup*deleteResponse: UNMAP +Ida.print_popup*dialogStyle: DIALOG_PRIMARY_APPLICATION_MODAL +Ida.print_popup.title: Print File +Ida.print_popup*selectionLabelString: Print command +Ida.print_popup*textString: lpr + +Ida*ps_popup*rc1.orientation: HORIZONTAL +Ida*ps_popup*draw.borderWidth: 1 +Ida*ps_popup*draw.background: white +Ida*ps_popup*draw.resizePolicy: RESIZE_NONE +Ida*ps_popup*scale.titleString: Scaling +Ida*ps_popup*scale.minimum: 10 +Ida*ps_popup*scale.maximum: 1000 +Ida*ps_popup*scale.decimalPoints: 1 +Ida*ps_popup.title: PostScript Options +Ida*ps_popup*paper.labelString: Paper size: +Ida*ps_popup*ori.labelString: Orientation: + +Ida*jpeg_popup.title: JPEG Options +Ida*jpeg_popup*selectionLabelString: Image quality (0 ... 100) + +Ida.gamma_popup*scale.minimum: 20 +Ida.gamma_popup*scale.maximum: 500 +Ida.gamma_popup*scale.decimalPoints: 2 +Ida.gamma_popup.title: Gamma correction +Ida.gamma_popup*selectionLabelString: Gamma value + +Ida.bright_popup*scale.minimum: -256 +Ida.bright_popup*scale.maximum: 256 +Ida.bright_popup.title: Adjust bright +Ida.bright_popup*selectionLabelString: Bright + +Ida.contrast_popup*scale.minimum: -128 +Ida.contrast_popup*scale.maximum: 512 +Ida.contrast_popup.title: Adjust contrast +Ida.contrast_popup*selectionLabelString: Contrast + +Ida.rotate_popup*scale.minimum: -180 +Ida.rotate_popup*scale.maximum: 180 +Ida.rotate_popup.title: Rotate image +Ida.rotate_popup*selectionLabelString: angle + +Ida.sharpe_popup*scale.minimum: 0 +Ida.sharpe_popup*scale.maximum: 100 +Ida.sharpe_popup.title: Sharpe image +Ida.sharpe_popup*selectionLabelString: value + +Ida.resize_popup.deleteResponse: DESTROY +Ida.resize_popup*rc.adjustMargin: false +Ida.resize_popup*rc.rc.orientation: HORIZONTAL +Ida.resize_popup*rc.rc.?.indicatorType: ONE_OF_MANY +Ida.resize_popup.title: Scale image +Ida.resize_popup*lx.labelString: Width (pixels) +Ida.resize_popup*ly.labelString: Height (pixels) +Ida.resize_popup*lr.labelString: Resolution (dpi) +Ida.resize_popup*lock.labelString: Keep aspect ratio +Ida.resize_popup*size.labelString: Change size +Ida.resize_popup*res.labelString: Change resolution +Ida.resize_popup*phys.labelString: Image size + + +! ---------------------------------------------------------------------------- +! edit colors dialog + +Ida.color_popup.deleteResponse: DESTROY + +Ida.color_popup.title: Edit colors +Ida.color_popup*hist.labelString: Histograms +Ida.color_popup*map.labelString: Maps +Ida.color_popup*lock.labelString: Same values for all channels +Ida.color_popup*vals.labelString: Show values for channel: +Ida.color_popup*valsM.red.labelString: red +Ida.color_popup*valsM.green.labelString: green +Ida.color_popup*valsM.blue.labelString: blue +Ida.color_popup*in.label.labelString: Input range: +Ida.color_popup*out.label.labelString: Output range: +Ida.color_popup*gamma.label.labelString: Gamma: +!Ida.color_popup*white.labelString: FIXME + +Ida.color_popup*XmForm*leftOffset: 10 +Ida.color_popup*XmForm*rightOffset: 10 +Ida.color_popup*XmForm*topOffset: 10 +Ida.color_popup*XmForm*bottomOffset: 10 +Ida.color_popup*XmForm*leftAttachment: ATTACH_WIDGET +Ida.color_popup*XmForm*topAttachment: ATTACH_WIDGET +Ida.color_popup*XmForm.sep.rightAttachment: ATTACH_FORM +Ida.color_popup*XmForm.XmRowColumn.rightAttachment: ATTACH_FORM +Ida.color_popup*XmForm.XmRowColumn.orientation: HORIZONTAL +Ida.color_popup*XmText.columns: 5 + +Ida.color_popup*XmDrawingArea.background: white +Ida.color_popup*XmDrawingArea.borderWidth: 1 +Ida.color_popup*XmDrawingArea.borderColor: black + +Ida.color_popup*hred.topWidget: hist +Ida.color_popup*hgreen.topWidget: hred +Ida.color_popup*hblue.topWidget: hgreen + +Ida.color_popup*map.leftWidget: hred +Ida.color_popup*mred.topWidget: hist +Ida.color_popup*mred.leftWidget: hred +Ida.color_popup*mred.rightAttachment: ATTACH_FORM +Ida.color_popup*mgreen.topWidget: mred +Ida.color_popup*mgreen.leftWidget: hgreen +Ida.color_popup*mgreen.rightAttachment: ATTACH_FORM +Ida.color_popup*mblue.topWidget: mgreen +Ida.color_popup*mblue.leftWidget: hblue +Ida.color_popup*mblue.rightAttachment: ATTACH_FORM + +Ida.color_popup*lock.topWidget: hblue +Ida.color_popup*vals.topWidget: lock +Ida.color_popup*in.topWidget: vals +Ida.color_popup*out.topWidget: in +Ida.color_popup*gamma.topWidget: out +Ida.color_popup*pick.topWidget: gamma + + +! ---------------------------------------------------------------------------- +! control + +ctrl.title: ida controls +ctrl.form.status.labelString: fixme +ctrl*XmMenuShell.XmRowColumn.tearOffModel: TEAR_OFF_ENABLED + +ctrl*tool.orientation: HORIZONTAL +ctrl*tool.XmPushButton.shadowThickness: 1 + +ctrl.toolTipEnable: 1 +ctrl.toolTipPostDelay: 2000 +ctrl.toolTipPostDuration: 5000 +ctrl*TipLabel.foreground: black +ctrl*TipLabel.background: lightyellow +ctrl*TipShell.borderWidth: 1 +ctrl*TipShell.borderColor: black +ctrl*tool.XmSeparator.orientation: VERTICAL +ctrl*tool.XmSeparator.width: 12 +ctrl*tool.XmSeparator.margin: 3 +ctrl*tool.XmPushButton.labelType: PIXMAP +ctrl*tool.prev.toolTipString: previous file +ctrl*tool.prev.labelPixmap: prev +ctrl*tool.next.toolTipString: next file +ctrl*tool.next.labelPixmap: next +ctrl*tool.zoomin.toolTipString: zoom in +ctrl*tool.zoomin.labelPixmap: zoomin +ctrl*tool.zoomout.toolTipString: zoom out +ctrl*tool.zoomout.labelPixmap: zoomout +ctrl*tool.flipv.toolTipString: flip vertical +ctrl*tool.flipv.labelPixmap: flipv +ctrl*tool.fliph.toolTipString: flip horizontal +ctrl*tool.fliph.labelPixmap: fliph +ctrl*tool.rotccw.toolTipString: turn counter clockwise +ctrl*tool.rotccw.labelPixmap: rotccw +ctrl*tool.rotcw.toolTipString: turn clockwise +ctrl*tool.rotcw.labelPixmap: rotcw +ctrl*tool.exit.toolTipString: quit +ctrl*tool.exit.labelPixmap: exit + +ctrl.form*list.visibleItemCount: 12 +ctrl.form*list.translations: #override \ + <Key>space: Next() \n\ + <Key>osfDelete: Prev() \n\ + <Key>osfBackSpace: Prev() \n\ + <Key>KP_Add: Zoom(inc) \n\ + <Key>KP_Subtract: Zoom(dec) + +! file menu +ctrl*bar.file.labelString: File +ctrl*bar.file.mnemonic: F +ctrl*bar*load.labelString: Load image ... +ctrl*bar*load.mnemonic: L +ctrl*bar*load.acceleratorText: Ctrl+L +ctrl*bar*load.accelerator: Ctrl<Key>L +ctrl*bar*save.labelString: Save image ... +ctrl*bar*save.mnemonic: S +ctrl*bar*save.acceleratorText: Ctrl+S +ctrl*bar*save.accelerator: Ctrl<Key>S +ctrl*bar*browse.labelString: File browser ... +ctrl*bar*browse.acceleratorText: F +ctrl*bar*browse.accelerator: <Key>F +ctrl*bar*filelist.labelString: File list ... +ctrl*bar*filelist.acceleratorText: L +ctrl*bar*filelist.accelerator: <Key>L +ctrl*bar*scan.labelString: Scan +ctrl*bar*print.labelString: Print ... +ctrl*bar*print.mnemonic: P +ctrl*bar*print.acceleratorText: Ctrl+P +ctrl*bar*print.accelerator: Ctrl<Key>P +ctrl*bar*quit.labelString: Quit +ctrl*bar*quit.mnemonic: Q +ctrl*bar*quit.acceleratorText: Q +ctrl*bar*quit.accelerator: <Key>Q + +! edit menu +ctrl*bar.edit.labelString: Edit +ctrl*bar.edit.mnemonic: E +ctrl*bar*undo.labelString: Undo last operation +ctrl*bar*undo.mnemonic: U +ctrl*bar*undo.acceleratorText: U +ctrl*bar*undo.accelerator: <Key>U +ctrl*bar*copy.labelString: Copy +ctrl*bar*copy.acceleratorText: Ctrl+C +ctrl*bar*copy.accelerator: Ctrl<Key>C +ctrl*bar*paste.labelString: Paste +ctrl*bar*paste.acceleratorText: Ctrl+V +ctrl*bar*paste.accelerator: Ctrl<Key>V +ctrl*bar*flipv.labelString: Flip vertical +ctrl*bar*flipv.mnemonic: v +ctrl*bar*flipv.acceleratorText: V +ctrl*bar*flipv.accelerator: <Key>V +ctrl*bar*fliph.labelString: Flip horizontal +ctrl*bar*fliph.mnemonic: h +ctrl*bar*fliph.acceleratorText: H +ctrl*bar*fliph.accelerator: <Key>H +ctrl*bar*rotcw.labelString: Turn clockwise +ctrl*bar*rotcw.mnemonic: T +ctrl*bar*rotcw.acceleratorText: T +ctrl*bar*rotcw.accelerator: ~Meta ~Shift<Key>T +ctrl*bar*rotccw.labelString: Turn counter clockwise +ctrl*bar*rotccw.acceleratorText: Shift+T +ctrl*bar*rotccw.accelerator: Shift<Key>T +ctrl*bar*invert.labelString: Invert +ctrl*bar*invert.mnemonic: I +ctrl*bar*invert.acceleratorText: I +ctrl*bar*invert.accelerator: <Key>I +ctrl*bar*crop.labelString: Crop +ctrl*bar*crop.mnemonic: C +ctrl*bar*crop.acceleratorText: C +ctrl*bar*crop.accelerator: <Key>C +ctrl*bar*acrop.labelString: Autocrop +ctrl*bar*acrop.mnemonic: A +ctrl*bar*scale.labelString: Scale ... +ctrl*bar*scale.mnemonic: S +ctrl*bar*scale.acceleratorText: S +ctrl*bar*scale.accelerator: ~Ctrl<Key>S +ctrl*bar*rotany.labelString: Rotate ... +ctrl*bar*rotany.acceleratorText: Alt+T +ctrl*bar*rotany.accelerator: Alt<Key>T + +! filter menu +ctrl*bar.op.labelString: Filters +ctrl*bar.op.mnemonic: F +ctrl*bar*gamma.labelString: Gamma ... +ctrl*bar*gamma.mnemonic: G +ctrl*bar*gamma.acceleratorText: G +ctrl*bar*gamma.accelerator: <Key>G +ctrl*bar*bright.labelString: Bright ... +ctrl*bar*bright.mnemonic: B +ctrl*bar*contr.labelString: Contrast ... +ctrl*bar*contr.mnemonic: C +ctrl*bar*color.labelString: Edit colors ... +ctrl*bar*color.mnemonic: E +ctrl*bar*color.acceleratorText: E +ctrl*bar*color.accelerator: ~Alt<Key>E +ctrl*bar*gray.labelString: Grayscale +ctrl*bar*blur.labelString: Blur +ctrl*bar*blur.acceleratorText: Alt+B +ctrl*bar*blur.accelerator: Alt<Key>B +ctrl*bar*sharpe.labelString: Sharpe ... +ctrl*bar*sharpe.acceleratorText: Alt+S +ctrl*bar*sharpe.accelerator: Alt<Key>S +ctrl*bar*edge.labelString: Edge detect +ctrl*bar*edge.acceleratorText: Alt+E +ctrl*bar*edge.accelerator: Alt<Key>E +ctrl*bar*emboss.labelString: Emboss +ctrl*bar*emboss.acceleratorText: Alt+M +ctrl*bar*emboss.accelerator: Alt<Key>m + +! view menu +ctrl*bar.view.labelString: View +ctrl*bar.view.mnemonic: V +ctrl*bar*prev.labelString: Previous file +ctrl*bar*prev.acceleratorText: backspace +!ctrl*bar*prev.accelerator: <Key>Backspace +ctrl*bar*next.labelString: Next file +ctrl*bar*next.acceleratorText: space +!ctrl*bar*next.accelerator: <Key>space +ctrl*bar*prevpage.labelString: Previous page +ctrl*bar*prevpage.acceleratorText: PageUp +!ctrl*bar*prevpage.accelerator: <Key>osfPageUp +ctrl*bar*nextpage.labelString: Next page +ctrl*bar*nextpage.acceleratorText: PageDown +!ctrl*bar*nextpage.accelerator: <Key>osfPageDown +ctrl*bar*zoomin.labelString: Zoom in +ctrl*bar*zoomin.acceleratorText: plus +ctrl*bar*zoomin.accelerator: <Key>plus +ctrl*bar*zoomout.labelString: Zoom out +ctrl*bar*zoomout.acceleratorText: minus +ctrl*bar*zoomout.accelerator: <Key>minus + +! options menu +ctrl*bar.opt.labelString: Options +ctrl*bar.opt.mnemonic: O +ctrl*bar*pcd.labelString: PhotoCD resolution +ctrl*bar*autozoom.labelString: Autozoom +ctrl*bar*cfgsave.labelString: Save Options + +! options/photocd menu +ctrl*bar*pcdM.1.labelString: 192 x 128 +ctrl*bar*pcdM.2.labelString: 384 x 256 +ctrl*bar*pcdM.3.labelString: 768 x 512 +ctrl*bar*pcdM.4.labelString: 1536 x 1024 +ctrl*bar*pcdM.5.labelString: 3072 x 2048 + +! help menu +ctrl*bar.help.labelString: Help +ctrl*bar.help.mnemonic: H +ctrl*bar*man.labelString: Manual page ... +ctrl*bar*man.mnemonic: M +ctrl*bar*man.acceleratorText: F1 +ctrl*bar*man.accelerator: <Key>F1 +ctrl*bar*about.labelString: About ... +ctrl*bar*about.mnemonic: A + +ctrl.form.*.leftAttachment: ATTACH_FORM +ctrl.form.*.rightAttachment: ATTACH_FORM +ctrl.form.tool.topAttachment: ATTACH_WIDGET +ctrl.form.tool.topWidget: bar +ctrl.form.listSW.topAttachment: ATTACH_WIDGET +ctrl.form.listSW.topWidget: tool +ctrl.form.listSW.bottomAttachment: ATTACH_WIDGET +ctrl.form.listSW.bottomWidget: status +ctrl.form.listSW.width: 320 +ctrl.form.listSW.height: 240 +ctrl.form.status.bottomAttachment: ATTACH_FORM +ctrl.form.status.alignment: ALIGNMENT_BEGINNING + + +! ---------------------------------------------------------------------------- +! man page renderer + +Ida.man_popup.deleteResponse: DESTROY +Ida.man_popup*view.width: 500 +Ida.man_popup*view.height: 600 +Ida.man_popup*view.scrollingPolicy: AUTOMATIC +Ida.man_popup*view.scrollBarPlacement: BOTTOM_RIGHT + +Ida.man_popup.title: Manual page +Ida.man_popup*okLabelString: close window +Ida.man_popup*label.labelString: please wait ... + +Ida.man_popup*label.alignment: ALIGNMENT_BEGINNING +Ida.man_popup*label.marginWidth: 5 +Ida.man_popup*label.marginHeight: 5 +Ida.man_popup*label.renderTable: bold,underline +Ida.man_popup*label.renderTable.fontType: FONT_IS_FONTSET +Ida.man_popup*label.renderTable.fontName: \ + -*-fixed-medium-r-normal--13-*-*-*-*-*-iso8859-*, \ + -*-fixed-medium-r-normal-ja-13-*-*-*-*-*-iso10646-1, \ + -gnu-unifont-medium-r-normal--16-*-*-*-*-*-*-*,* + +Ida.man_popup*label.renderTable.bold.fontType: FONT_IS_FONTSET +Ida.man_popup*label.renderTable.bold.fontName: \ + -*-fixed-bold-r-normal--13-*-*-*-*-*-iso8859-*, \ + -*-fixed-medium-r-normal-ja-13-*-*-*-*-*-iso10646-1, \ + -gnu-unifont-bold-r-normal--16-*-*-*-*-*-*-*,* + +Ida.man_popup*label.renderTable.underline.underlineType: SINGLE_LINE + + +! ---------------------------------------------------------------------------- +! hex viewer + +Ida.hex_popup.deleteResponse: DESTROY +Ida.hex_popup*view.width: 600 +Ida.hex_popup*view.height: 600 +Ida.hex_popup*view.scrollingPolicy: AUTOMATIC +Ida.hex_popup*view.scrollBarPlacement: BOTTOM_RIGHT + +Ida.hex_popup*label.alignment: ALIGNMENT_BEGINNING +Ida.hex_popup*label.marginWidth: 5 +Ida.hex_popup*label.marginHeight: 5 +Ida.hex_popup*label.renderTable: bold,underline +Ida.hex_popup*label.renderTable.fontType: FONT_IS_FONTSET +Ida.hex_popup*label.renderTable.fontName: \ + -*-fixed-medium-r-normal--13-*-*-*-*-*-iso8859-*, \ + -*-fixed-medium-r-normal-ja-13-*-*-*-*-*-iso10646-1, \ + -gnu-unifont-medium-r-normal--16-*-*-*-*-*-*-*,* + +Ida.hex_popup*label.renderTable.bold.fontType: FONT_IS_FONTSET +Ida.hex_popup*label.renderTable.bold.fontName: \ + -*-fixed-bold-r-normal--13-*-*-*-*-*-iso8859-*, \ + -*-fixed-medium-r-normal-ja-13-*-*-*-*-*-iso10646-1, \ + -gnu-unifont-bold-r-normal--16-*-*-*-*-*-*-*,* + +Ida.hex_popup*label.renderTable.underline.underlineType: SINGLE_LINE + + +! ---------------------------------------------------------------------------- +! file browser + +browser.geometry: 600x450 +browser.form.?.leftAttachment: ATTACH_FORM +browser.form.?.rightAttachment: ATTACH_FORM +browser.form.scroll.topAttachment: ATTACH_WIDGET +browser.form.scroll.topWidget: cbar +browser.form.scroll.bottomAttachment: ATTACH_WIDGET +browser.form.scroll.bottomWidget: status +browser.form.status.bottomAttachment: ATTACH_FORM +browser.form.status.alignment: ALIGNMENT_BEGINNING + +browser.form.scroll.scrollingPolicy: AUTOMATIC +browser.form.scroll.scrollBarPlacement: BOTTOM_RIGHT +browser.form.scroll.XmScrollBar.highlightThickness: 1 + +browser.filter_popup.title: Filter +browser.filter_popup*selectionLabelString: pattern? + +browser*comment_popup.title: JPEG Comment +browser*comment_popup*selectionLabelString: Please enter/edit the comment here. + + +! ---------------------------------------------------------------------------- +! file lists + +filelist.geometry: 300x400 +filelist.form.?.leftAttachment: ATTACH_FORM +filelist.form.?.rightAttachment: ATTACH_FORM +filelist.form.scroll.topAttachment: ATTACH_WIDGET +filelist.form.scroll.topWidget: cbar +filelist.form.scroll.bottomAttachment: ATTACH_WIDGET +filelist.form.scroll.bottomWidget: status +filelist.form.status.bottomAttachment: ATTACH_FORM +filelist.form.status.alignment: ALIGNMENT_BEGINNING + +filelist.form.scroll.scrollingPolicy: AUTOMATIC +filelist.form.scroll.scrollBarPlacement: BOTTOM_RIGHT +filelist.form.scroll.XmScrollBar.highlightThickness: 1 + + +! ---------------------------------------------------------------------------- +! browser + file list common stuff + +*container.outlineButtonPolicy: OUTLINE_BUTTON_ABSENT +*container.spatialStyle: CELLS +*container.spatialResizeModel: GROW_MINOR +*container.spatialSnapModel: CENTER +*container.detailTabList: 3cm +!*container.spatialIncludeModel: APPEND +!*container.layoutDirection: LEFT_TO_RIGHT_TOP_TO_BOTTOM +!*container.background: gray85 +*container.primaryOwnership: XmOWN_NEVER +*container.XmIconGadget.highlightColor: darkred +*container.XmIconGadget.shadowThickness: 1 + +! file menu +*cbar.file.labelString: File +*cbar.file.mnemonic: F +*cbar*new.labelString: New list +*cbar*new.mnemonic: N +*cbar*load.labelString: Load list ... +*cbar*load.mnemonic: L +*cbar*save.labelString: Save list +*cbar*save.mnemonic: S +*cbar*saveas.labelString: Save list as ... +*cbar*saveas.mnemonic: a +*cbar*close.labelString: Close window +*cbar*close.acceleratorText: Q +*cbar*close.accelerator: <Key>Q + +! edit menu +*cbar.edit.labelString: Edit +*cbar.edit.mnemonic: E +*cbar*copy.labelString: Copy +*cbar*copy.acceleratorText: Ctrl+C +*cbar*copy.accelerator: Ctrl<Key>C +*cbar*paste.labelString: Paste +*cbar*paste.acceleratorText: Ctrl+V +*cbar*paste.accelerator: Ctrl<Key>V +*cbar*del.labelString: Delete + +! view menu +*cbar.view.labelString: View +*cbar.view.mnemonic: V +*cbar*spatial.labelString: Large Icons +*cbar*details.labelString: Details +*cbar*filter.labelString: Filter ... +*cbar*filter.acceleratorText: F +*cbar*filter.accelerator: <Key>F +*cbar*freset.labelString: Reset filter + +! jpeg ops menu +*cbar.ops.labelString: JPEG +*cbar.ops.mnemonic: J +*cbar*rotexif.labelString: Auto Rotate (by EXIT Tag) +*cbar*rotexif.mnemonic: A +*cbar*rotcw.labelString: Rotate Clockwise +*cbar*rotccw.labelString: Rotate Counterclockwise +*cbar*comment.labelString: Edit Comment ... +*cbar*comment.mnemonic: E +*cbar*comment.acceleratorText: E +*cbar*comment.accelerator: <Key>E + +! bookmarks +*cbar.dirs.labelString: Bookmarks +*cbar.dirs.mnemonic: B + +! lists +*cbar.lists.labelString: Lists +*cbar.lists.mnemonic: L @@ -0,0 +1,47 @@ + +what is it? +=========== + +Ida is a small and fast image viewer, motif-based. For people who +don't want the KDE/GNOME overhead. Some basic editing functions are +available too. + + +build +===== + +Check the INSTALL file for detailed build instructions. + +ida uses Motif 2.x features (utm, render tables). This means you need +openmotif, lesstif does *not* cut it. + +It also uses the usual graphics libraries (libtiff, libpng, libjpeg, +...), you should have them installed to get support for these image +formats. + + +usage +===== + +There is a manual page, check it out. + + +what "ida" stands for? +====================== + +It is just a name. The utility used to be named "iv" for "Image +Viewer", but that gave lots of name clashes. Its very likely that +people name such a tool "iv", it is also used as shortcut for +InterViews (remember Sun's Open Windows?). So i decided to rename it. + +I looked for a short name starting with 'i' in a list for children +first names. I like "ida", so I picked this one. It is a old, german +name. + + +Have fun, + + Gerd + +-- +Gerd Knorr <kraxel@bytesex.org> diff --git a/RegEdit.c b/RegEdit.c new file mode 100644 index 0000000..fa01b88 --- /dev/null +++ b/RegEdit.c @@ -0,0 +1,1795 @@ +/* $XConsortium: RegEdit.c /main/5 1995/07/15 20:44:04 drk $ */ +/* + * @OPENGROUP_COPYRIGHT@ + * COPYRIGHT NOTICE + * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. + * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group + * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for + * the full copyright text. + * + * This software is subject to an open license. It may only be + * used on, with or for operating systems which are themselves open + * source systems. You must contact The Open Group for a license + * allowing distribution and sublicensing of this software on, with, + * or for operating systems which are not Open Source programs. + * + * See http://www.opengroup.org/openmotif/license for full + * details of the license agreement. Any use, reproduction, or + * distribution of the program constitutes recipient's acceptance of + * this agreement. + * + * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS + * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY + * OR FITNESS FOR A PARTICULAR PURPOSE + * + * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT + * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE + * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + */ +/* + * HISTORY + */ +#include <stdio.h> +#include <Xm/XmP.h> +#include <X11/ShellP.h> +#include "RegEditI.h" + + +/* static forward. move from global in the original Editres code */ +static void _XEditResCheckMessages(); +static void _XEditResPutString8(); +static void _XEditResPut8(); +static void _XEditResPut16(); +static void _XEditResPut32(); +static void _XEditResPutWidgetInfo(); +static void _XEditResResetStream(); +static Boolean _XEditResGet8(); +static Boolean _XEditResGet16(); +static Boolean _XEditResGetSigned16(); +static Boolean _XEditResGet32(); +static Boolean _XEditResGetString8(); +static Boolean _XEditResGetWidgetInfo(); + +/* the only entry point here */ +void +XmdRegisterEditres(Widget toplevel) +{ + XtAddEventHandler(toplevel, (EventMask) 0, TRUE, + _XEditResCheckMessages, NULL); +} + + +/************************************************************ + * + * Dump the content of the R5 lib/Xmu/EditresCom.c module. + * just move global as static. + * + ************************************************************/ + +#define _XEditResPutBool _XEditResPut8 +#define _XEditResPutResourceType _XEditResPut8 + +/************************************************************ + * + * Local structure definitions. + * + ************************************************************/ + +typedef enum { BlockNone, BlockSetValues, BlockAll } EditresBlock; + +typedef struct _SetValuesEvent { + EditresCommand type; /* first field must be type. */ + WidgetInfo * widgets; + unsigned short num_entries; /* number of set values requests. */ + char * name; + char * res_type; + XtPointer value; + unsigned short value_len; +} SetValuesEvent; + +typedef struct _SVErrorInfo { + SetValuesEvent * event; + ProtocolStream * stream; + unsigned short * count; + WidgetInfo * entry; +} SVErrorInfo; + +typedef struct _FindChildEvent { + EditresCommand type; /* first field must be type. */ + WidgetInfo * widgets; + short x, y; +} FindChildEvent; + +typedef struct _GenericGetEvent { + EditresCommand type; /* first field must be type. */ + WidgetInfo * widgets; + unsigned short num_entries; /* number of set values requests. */ +} GenericGetEvent, GetResEvent, GetGeomEvent; + +/* + * Things that are common to all events. + */ + +typedef struct _AnyEvent { + EditresCommand type; /* first field must be type. */ + WidgetInfo * widgets; +} AnyEvent; + +/* + * The event union. + */ + +typedef union _EditresEvent { + AnyEvent any_event; + SetValuesEvent set_values_event; + GetResEvent get_resources_event; + GetGeomEvent get_geometry_event; + FindChildEvent find_child_event; +} EditresEvent; + +typedef struct _Globals { + EditresBlock block; + SVErrorInfo error_info; + ProtocolStream stream; + ProtocolStream * command_stream; /* command stream. */ +} Globals; + +#define CURRENT_PROTOCOL_VERSION 4L + +#define streq(a,b) (strcmp( (a), (b) ) == 0) + +static Atom res_editor_command, res_editor_protocol, client_value; + +static Globals globals; + +static void SendFailure(), SendCommand(), InsertWidget(), ExecuteCommand(); +static void FreeEvent(), ExecuteSetValues(), ExecuteGetGeometry(); +static void ExecuteGetResources(); + +static void GetCommand(); +static void LoadResources(); +static Boolean IsChild(); +static void DumpChildren(); +static char *DumpWidgets(), *DoSetValues(), *DoFindChild(); +static char *DoGetGeometry(), *DoGetResources(); + +/************************************************************ + * + * Resource Editor Communication Code + * + ************************************************************/ + +/* Function Name: _XEditResCheckMessages + * Description: This callback routine is set on all shell widgets, + * and checks to see if a client message event + * has come from the resource editor. + * Arguments: w - the shell widget. + * data - *** UNUSED *** + * event - The X Event that triggered this handler. + * cont - *** UNUSED ***. + * Returns: none. + */ + +/* ARGSUSED */ +static void +_XEditResCheckMessages(w, data, event, cont) +Widget w; +XtPointer data; +XEvent *event; +Boolean *cont; +{ + Time time; + ResIdent ident; + static Boolean first_time = FALSE; + static Atom res_editor, res_comm; + Display * dpy; + + if (event->type == ClientMessage) { + XClientMessageEvent * c_event = (XClientMessageEvent *) event; + dpy = XtDisplay(w); + + if (!first_time) { + first_time = TRUE; + res_editor = XInternAtom(dpy, EDITRES_NAME, False); + res_editor_command = XInternAtom(dpy, EDITRES_COMMAND_ATOM, False); + res_editor_protocol = XInternAtom(dpy, EDITRES_PROTOCOL_ATOM, + False); + + /* Used in later procedures. */ + client_value = XInternAtom(dpy, EDITRES_CLIENT_VALUE, False); + LoadResources(w); + } + + if ((c_event->message_type != res_editor) || + (c_event->format != EDITRES_SEND_EVENT_FORMAT)) + return; + + time = c_event->data.l[0]; + res_comm = c_event->data.l[1]; + ident = (ResIdent) c_event->data.l[2]; + if (c_event->data.l[3] != CURRENT_PROTOCOL_VERSION) { + _XEditResResetStream(&globals.stream); + _XEditResPut8(&globals.stream, CURRENT_PROTOCOL_VERSION); + SendCommand(w, res_comm, ident, ProtocolMismatch, &globals.stream); + return; + } + + XtGetSelectionValue(w, res_comm, res_editor_command, + GetCommand, (XtPointer) (long) ident, time); + } +} + +/* Function Name: BuildEvent + * Description: Takes the info out the protocol stream an constructs + * the proper event structure. + * Arguments: w - widget to own selection, in case of error. + * sel - selection to send error message beck in. + * data - the data for the request. + * ident - the id number we are looking for. + * length - length of request. + * Returns: the event, or NULL. + */ + +#define ERROR_MESSAGE ("Client: Improperly formatted protocol request") + +static EditresEvent * +BuildEvent(w, sel, data, ident, length) +Widget w; +Atom sel; +XtPointer data; +ResIdent ident; +unsigned long length; +{ + EditresEvent * event; + ProtocolStream alloc_stream, *stream; + unsigned char temp; + register unsigned int i; + + stream = &alloc_stream; /* easier to think of it this way... */ + + stream->current = stream->top = (unsigned char *) data; + stream->size = HEADER_SIZE; /* size of header. */ + + /* + * Retrieve the Header. + */ + + if (length < HEADER_SIZE) { + SendFailure(w, sel, ident, Failure, ERROR_MESSAGE); + return(NULL); + } + + (void) _XEditResGet8(stream, &temp); + if (temp != ident) /* Id's don't match, ignore request. */ + return(NULL); + + event = (EditresEvent *) XtCalloc(sizeof(EditresEvent), 1); + + (void) _XEditResGet8(stream, &temp); + event->any_event.type = (EditresCommand) temp; + (void) _XEditResGet32(stream, &(stream->size)); + stream->top = stream->current; /* reset stream to top of value.*/ + + /* + * Now retrieve the data segment. + */ + + switch(event->any_event.type) { + case SendWidgetTree: + break; /* no additional info */ + case SetValues: + { + SetValuesEvent * sv_event = (SetValuesEvent *) event; + + if ( !(_XEditResGetString8(stream, &(sv_event->name)) && + _XEditResGetString8(stream, &(sv_event->res_type)))) + { + goto done; + } + + /* + * Since we need the value length, we have to pull the + * value out by hand. + */ + + if (!_XEditResGet16(stream, &(sv_event->value_len))) + goto done; + + sv_event->value = XtMalloc(sizeof(char) * + (sv_event->value_len + 1)); + + for (i = 0; i < sv_event->value_len; i++) { + if (!_XEditResGet8(stream, + (unsigned char *) sv_event->value + i)) + { + goto done; + } + } + ((char*)sv_event->value)[i] = '\0'; /* NULL terminate that sucker. */ + + if (!_XEditResGet16(stream, &(sv_event->num_entries))) + goto done; + + sv_event->widgets = (WidgetInfo *) + XtCalloc(sizeof(WidgetInfo), sv_event->num_entries); + + for (i = 0; i < sv_event->num_entries; i++) { + if (!_XEditResGetWidgetInfo(stream, sv_event->widgets + i)) + goto done; + } + } + break; + case FindChild: + { + FindChildEvent * find_event = (FindChildEvent *) event; + + find_event->widgets = (WidgetInfo *) + XtCalloc(sizeof(WidgetInfo), 1); + + if (!(_XEditResGetWidgetInfo(stream, find_event->widgets) && + _XEditResGetSigned16(stream, &(find_event->x)) && + _XEditResGetSigned16(stream, &(find_event->y)))) + { + goto done; + } + + } + break; + case GetGeometry: + case GetResources: + { + GenericGetEvent * get_event = (GenericGetEvent *) event; + + if (!_XEditResGet16(stream, &(get_event->num_entries))) + goto done; + + get_event->widgets = (WidgetInfo *) + XtCalloc(sizeof(WidgetInfo), get_event->num_entries); + for (i = 0; i < get_event->num_entries; i++) { + if (!_XEditResGetWidgetInfo(stream, get_event->widgets + i)) + goto done; + } + } + break; + default: + { + char buf[BUFSIZ]; + + sprintf(buf, "Unknown Protocol request %d.",event->any_event.type); + SendFailure(w, sel, ident, buf); + return(NULL); + } + } + return(event); + + done: + + SendFailure(w, sel, ident, ERROR_MESSAGE); + FreeEvent(event); + return(NULL); +} + +/* Function Name: FreeEvent + * Description: Frees the event structure and any other pieces + * in it that need freeing. + * Arguments: event - the event to free. + * Returns: none. + */ + +static void +FreeEvent(event) +EditresEvent * event; +{ + if (event->any_event.widgets != NULL) { + XtFree((char *)event->any_event.widgets->ids); + XtFree((char *)event->any_event.widgets); + } + + if (event->any_event.type == SetValues) { + XtFree(event->set_values_event.name); /* XtFree does not free if */ + XtFree(event->set_values_event.res_type); /* value is NULL. */ + } + + XtFree((char *)event); +} + +/* Function Name: GetCommand + * Description: Gets the Command out of the selection asserted by the + * resource manager. + * Arguments: (See Xt XtConvertSelectionProc) + * data - contains the ident number for the command. + * Returns: none. + */ + +/* ARGSUSED */ +static void +GetCommand(w, data, selection, type, value, length, format) +Widget w; +XtPointer data, value; +Atom *selection, *type; +unsigned long *length; +int * format; +{ + ResIdent ident = (ResIdent) (long) data; + EditresEvent * event; + + if ( (*type != res_editor_protocol) || (*format != EDITRES_FORMAT) ) + return; + + if ((event = BuildEvent(w, *selection, value, ident, *length)) != NULL) { + ExecuteCommand(w, *selection, ident, event); + FreeEvent(event); + } +} + +/* Function Name: ExecuteCommand + * Description: Executes a command string received from the + * resource editor. + * Arguments: w - a widget. + * command - the command to execute. + * value - the associated with the command. + * Returns: none. + * + * NOTES: munges str + */ + +/* ARGSUSED */ +static void +ExecuteCommand(w, sel, ident, event) +Widget w; +Atom sel; +ResIdent ident; +EditresEvent * event; +{ + char * (*func)(); + char * str; + + if (globals.block == BlockAll) { + SendFailure(w, sel, ident, + "This client has blocked all Editres commands."); + return; + } + else if ((globals.block == BlockSetValues) && + (event->any_event.type == SetValues)) { + SendFailure(w, sel, ident, + "This client has blocked all SetValues requests."); + return; + } + + switch(event->any_event.type) { + case SendWidgetTree: + func = DumpWidgets; + break; + case SetValues: + func = DoSetValues; + break; + case FindChild: + func = DoFindChild; + break; + case GetGeometry: + func = DoGetGeometry; + break; + case GetResources: + func = DoGetResources; + break; + default: + { + char buf[BUFSIZ]; + sprintf(buf,"Unknown Protocol request %d.",event->any_event.type); + SendFailure(w, sel, ident, buf); + return; + } + } + + _XEditResResetStream(&globals.stream); + if ((str = (*func)(w, event, &globals.stream)) == NULL) + SendCommand(w, sel, ident, PartialSuccess, &globals.stream); + else { + SendFailure(w, sel, ident, str); + XtFree(str); + } +} + +/* Function Name: ConvertReturnCommand + * Description: Converts a selection. + * Arguments: w - the widget that owns the selection. + * selection - selection to convert. + * target - target type for this selection. + * type_ret - type of the selection. + * value_ret - selection value; + * length_ret - lenght of this selection. + * format_ret - the format the selection is in. + * Returns: True if conversion was sucessful. + */ + +/* ARGSUSED */ +static Boolean +ConvertReturnCommand(w, selection, target, + type_ret, value_ret, length_ret, format_ret) +Widget w; +Atom * selection, * target, * type_ret; +XtPointer *value_ret; +unsigned long * length_ret; +int * format_ret; +{ + /* + * I assume the intrinsics give me the correct selection back. + */ + + if ((*target != client_value)) + return(FALSE); + + *type_ret = res_editor_protocol; + *value_ret = (XtPointer) globals.command_stream->real_top; + *length_ret = globals.command_stream->size + HEADER_SIZE; + *format_ret = EDITRES_FORMAT; + + return(TRUE); +} + +/* Function Name: CommandDone + * Description: done with the selection. + * Arguments: *** UNUSED *** + * Returns: none. + */ + +/* ARGSUSED */ +static void +CommandDone(widget, selection, target) +Widget widget; +Atom *selection; +Atom *target; +{ + /* Keep the toolkit from automaticaly freeing the selection value */ +} + +/* Function Name: SendFailure + * Description: Sends a failure message. + * Arguments: w - the widget to own the selection. + * sel - the selection to assert. + * ident - the identifier. + * str - the error message. + * Returns: none. + */ + +static void +SendFailure(w, sel, ident, str) +Widget w; +Atom sel; +ResIdent ident; +char * str; +{ + _XEditResResetStream(&globals.stream); + _XEditResPutString8(&globals.stream, str); + SendCommand(w, sel, ident, Failure, &globals.stream); +} + +/* Function Name: BuildReturnPacket + * Description: Builds a return packet, given the data to send. + * Arguments: ident - the identifier. + * command - the command code. + * stream - the protocol stream. + * Returns: packet - the packet to send. + */ + +static XtPointer +BuildReturnPacket(ident, command, stream) +ResIdent ident; +EditresCommand command; +ProtocolStream * stream; +{ + long old_alloc, old_size; + unsigned char * old_current; + + /* + * We have cleverly keep enough space at the top of the header + * for the return protocol stream, so all we have to do is + * fill in the space. + */ + + /* + * Fool the insert routines into putting the header in the right + * place while being damn sure not to realloc (that would be very bad. + */ + + old_current = stream->current; + old_alloc = stream->alloc; + old_size = stream->size; + + stream->current = stream->real_top; + stream->alloc = stream->size + (2 * HEADER_SIZE); + + _XEditResPut8(stream, ident); + _XEditResPut8(stream, (unsigned char) command); + _XEditResPut32(stream, old_size); + + stream->alloc = old_alloc; + stream->current = old_current; + stream->size = old_size; + + return((XtPointer) stream->real_top); +} + +/* Function Name: SendCommand + * Description: Builds a return command line. + * Arguments: w - the widget to own the selection. + * sel - the selection to assert. + * ident - the identifier. + * command - the command code. + * stream - the protocol stream. + * Returns: none. + */ + +static void +SendCommand(w, sel, ident, command, stream) +Widget w; +Atom sel; +ResIdent ident; +EditresCommand command; +ProtocolStream * stream; +{ + BuildReturnPacket(ident, command, stream); + globals.command_stream = stream; + +/* + * I REALLY want to own the selection. Since this was not triggered + * by a user action, and I am the only one using this atom it is safe to + * use CurrentTime. + */ + + XtOwnSelection(w, sel, CurrentTime, + ConvertReturnCommand, NULL, CommandDone); +} + +/************************************************************ + * + * Generic Utility Functions. + * + ************************************************************/ + +/* Function Name: FindChildren + * Description: Retuns all children (popup, normal and otherwise) + * of this widget + * Arguments: parent - the parent widget. + * children - the list of children. + * normal - return normal children. + * popup - return popup children. + * Returns: the number of children. + */ + +static int +FindChildren(parent, children, normal, popup) +Widget parent, **children; +Boolean normal, popup; +{ + CompositeWidget cw = (CompositeWidget) parent; + int i, num_children, current = 0; + + num_children = 0; + + if (XtIsWidget(parent) && popup) + num_children += parent->core.num_popups; + + if (XtIsComposite(parent) && normal) + num_children += cw->composite.num_children; + + if (num_children == 0) { + *children = NULL; + return(0); + } + + *children =(Widget*) XtMalloc((Cardinal) sizeof(Widget) * num_children); + + if (XtIsComposite(parent) && normal) + for (i = 0; i < cw->composite.num_children; i++,current++) + (*children)[current] = cw->composite.children[i]; + + if (XtIsWidget(parent) && popup) + for ( i = 0; i < parent->core.num_popups; i++, current++) + (*children)[current] = parent->core.popup_list[i]; + + return(num_children); +} + +/* Function Name: IsChild + * Description: check to see of child is a child of parent. + * Arguments: top - the top of the tree. + * parent - the parent widget. + * child - the child. + * Returns: none. + */ + +static Boolean +IsChild(top, parent, child) +Widget top, parent, child; +{ + int i, num_children; + Widget * children; + + if (parent == NULL) + return(top == child); + + num_children = FindChildren(parent, &children, TRUE, TRUE); + + for (i = 0; i < num_children; i++) { + if (children[i] == child) { + XtFree((char *)children); + return(TRUE); + } + } + + XtFree((char *)children); + return(FALSE); +} + +/* Function Name: VerifyWidget + * Description: Makes sure all the widgets still exist. + * Arguments: w - any widget in the tree. + * info - the info about the widget to verify. + * Returns: an error message or NULL. + */ + +static char * +VerifyWidget(w, info) +Widget w; +WidgetInfo *info; +{ + Widget top; + + register int count; + register Widget parent; + register unsigned long * child; + + for (top = w; XtParent(top) != NULL; top = XtParent(top)) {} + + parent = NULL; + child = info->ids; + count = 0; + + while (TRUE) { + if (!IsChild(top, parent, (Widget) *child)) + return(XtNewString("This widget no longer exists in the client.")); + + if (++count == info->num_widgets) + break; + + parent = (Widget) *child++; + } + + info->real_widget = (Widget) *child; + return(NULL); +} + +/************************************************************ + * + * Code to Perform SetValues operations. + * + ************************************************************/ + + +/* Function Name: DoSetValues + * Description: performs the setvalues requested. + * Arguments: w - a widget in the tree. + * event - the event that caused this action. + * stream - the protocol stream to add. + * Returns: NULL. + */ + +static char * +DoSetValues(w, event, stream) +Widget w; +EditresEvent * event; +ProtocolStream * stream; +{ + char * str; + register unsigned i; + unsigned short count = 0; + SetValuesEvent * sv_event = (SetValuesEvent *) event; + + _XEditResPut16(stream, count); /* insert 0, will be overwritten later. */ + + for (i = 0 ; i < sv_event->num_entries; i++) { + if ((str = VerifyWidget(w, &(sv_event->widgets[i]))) != NULL) { + _XEditResPutWidgetInfo(stream, &(sv_event->widgets[i])); + _XEditResPutString8(stream, str); + XtFree(str); + count++; + } + else + ExecuteSetValues(sv_event->widgets[i].real_widget, + sv_event, sv_event->widgets + i, stream, &count); + } + + /* + * Overwrite the first 2 bytes with the real count. + */ + + *(stream->top) = count >> XER_NBBY; + *(stream->top + 1) = count; + return(NULL); +} + +/* Function Name: HandleToolkitErrors + * Description: Handles X Toolkit Errors. + * Arguments: name - name of the error. + * type - type of the error. + * class - class of the error. + * msg - the default message. + * params, num_params - the extra parameters for this message. + * Returns: none. + */ + +/* ARGSUSED */ +static void +HandleToolkitErrors(name, type, class, msg, params, num_params) +String name, type, class, msg, *params; +Cardinal * num_params; +{ + SVErrorInfo * info = &globals.error_info; + char buf[BUFSIZ]; + + if ( streq(name, "unknownType") ) + sprintf(buf, "The `%s' resource is not used by this widget.", + info->event->name); + else if ( streq(name, "noColormap") ) + sprintf(buf, msg, params[0]); + else if (streq(name, "conversionFailed") || streq(name, "conversionError")) + { + if (streq(info->event->value, XtRString)) + sprintf(buf, + "Could not convert the string '%s' for the `%s' resource.", + (char*)info->event->value, info->event->name); + else + sprintf(buf, "Could not convert the `%s' resource.", + info->event->name); + } + else + sprintf(buf, "Name: %s, Type: %s, Class: %s, Msg: %s", + name, type, class, msg); + + /* + * Insert this info into the protocol stream, and update the count. + */ + + (*(info->count))++; + _XEditResPutWidgetInfo(info->stream, info->entry); + _XEditResPutString8(info->stream, buf); +} + +/* Function Name: ExecuteSetValues + * Description: Performs a setvalues for a given command. + * Arguments: w - the widget to perform the set_values on. + * sv_event - the set values event. + * sv_info - the set_value info. + * Returns: none. + */ + +static void +ExecuteSetValues(w, sv_event, entry, stream, count) +Widget w; +SetValuesEvent * sv_event; +WidgetInfo * entry; +ProtocolStream * stream; +unsigned short * count; +{ + XtErrorMsgHandler old; + + SVErrorInfo * info = &globals.error_info; + info->event = sv_event; /* No data can be passed to */ + info->stream = stream; /* an error handler, so we */ + info->count = count; /* have to use a global, YUCK... */ + info->entry = entry; + + old = XtAppSetWarningMsgHandler(XtWidgetToApplicationContext(w), + HandleToolkitErrors); + + XtVaSetValues(w, XtVaTypedArg, + sv_event->name, sv_event->res_type, + sv_event->value, sv_event->value_len, + NULL); + + (void)XtAppSetWarningMsgHandler(XtWidgetToApplicationContext(w), old); +} + + +/************************************************************ + * + * Code for Creating and dumping widget tree. + * + ************************************************************/ + +/* Function Name: DumpWidgets + * Description: Given a widget it builds a protocol packet + * containing the entire widget heirarchy. + * Arguments: w - a widget in the tree. + * event - the event that caused this action. + * stream - the protocol stream to add. + * Returns: NULL + */ + +/* ARGSUSED */ +static char * +DumpWidgets(w, event, stream) +Widget w; +EditresEvent * event; /* UNUSED */ +ProtocolStream * stream; +{ + unsigned short count = 0; + + /* Find Tree's root. */ + for ( ; XtParent(w) != NULL; w = XtParent(w)) {} + + /* + * hold space for count, overwritten later. + */ + + _XEditResPut16(stream, (unsigned int) 0); + + DumpChildren(w, stream, &count); + + /* + * Overwrite the first 2 bytes with the real count. + */ + + *(stream->top) = count >> XER_NBBY; + *(stream->top + 1) = count; + return(NULL); +} + +/* Function Name: DumpChildren + * Description: Adds a child's name to the list. + * Arguments: w - the widget to dump. + * stream - the stream to dump to. + * count - number of dumps we have performed. + * Returns: none. + */ + +/* This is a trick/kludge. To make shared libraries happier (linking + * against Xmu but not linking against Xt, and apparently even work + * as we desire on SVR4, we need to avoid an explicit data reference + * to applicationShellWidgetClass. XtIsTopLevelShell is known + * (implementation dependent assumption!) to use a bit flag. So we + * go that far. Then, we test whether it is an applicationShellWidget + * class by looking for an explicit class name. Seems pretty safe. + */ +static Bool isApplicationShell(w) + Widget w; +{ + register WidgetClass c; + + if (!XtIsTopLevelShell(w)) + return False; + for (c = XtClass(w); c; c = c->core_class.superclass) { + if (!strcmp(c->core_class.class_name, "ApplicationShell")) + return True; + } + return False; +} + +static void +DumpChildren(w, stream, count) +Widget w; +ProtocolStream * stream; +unsigned short *count; +{ + int i, num_children; + Widget *children; + unsigned long window; + char * class; + + (*count)++; + + InsertWidget(stream, w); /* Insert the widget into the stream. */ + + _XEditResPutString8(stream, XtName(w)); /* Insert name */ + + if (isApplicationShell(w)) + class = ((ApplicationShellWidget) w)->application.class; + else + class = XtClass(w)->core_class.class_name; + + _XEditResPutString8(stream, class); /* Insert class */ + + if (XtIsWidget(w)) + if (XtIsRealized(w)) + window = XtWindow(w); + else + window = EDITRES_IS_UNREALIZED; + else + window = EDITRES_IS_OBJECT; + + _XEditResPut32(stream, window); /* Insert window id. */ + + /* + * Find children and recurse. + */ + + num_children = FindChildren(w, &children, TRUE, TRUE); + for (i = 0; i < num_children; i++) + DumpChildren(children[i], stream, count); + + XtFree((char *)children); +} + +/************************************************************ + * + * Code for getting the geometry of widgets. + * + ************************************************************/ + +/* Function Name: DoGetGeometry + * Description: retrieves the Geometry of each specified widget. + * Arguments: w - a widget in the tree. + * event - the event that caused this action. + * stream - the protocol stream to add. + * Returns: NULL + */ + +static char * +DoGetGeometry(w, event, stream) +Widget w; +EditresEvent * event; +ProtocolStream * stream; +{ + unsigned i; + char * str; + GetGeomEvent * geom_event = (GetGeomEvent *) event; + + _XEditResPut16(stream, geom_event->num_entries); + + for (i = 0 ; i < geom_event->num_entries; i++) { + + /* + * Send out the widget id. + */ + + _XEditResPutWidgetInfo(stream, &(geom_event->widgets[i])); + if ((str = VerifyWidget(w, &(geom_event->widgets[i]))) != NULL) { + _XEditResPutBool(stream, True); /* an error occured. */ + _XEditResPutString8(stream, str); /* set message. */ + XtFree(str); + } + else + ExecuteGetGeometry(geom_event->widgets[i].real_widget, stream); + } + return(NULL); +} + +/* Function Name: ExecuteGetGeometry + * Description: Gets the geometry for each widget specified. + * Arguments: w - the widget to get geom on. + * stream - stream to append to. + * Returns: True if no error occured. + */ + +static void +ExecuteGetGeometry(w, stream) +Widget w; +ProtocolStream * stream; +{ + int i; + Boolean mapped_when_man; + Dimension width, height, border_width; + Arg args[8]; + Cardinal num_args = 0; + Position x, y; + + if ( !XtIsRectObj(w) || (XtIsWidget(w) && !XtIsRealized(w)) ) { + _XEditResPutBool(stream, False); /* no error. */ + _XEditResPutBool(stream, False); /* not visable. */ + for (i = 0; i < 5; i++) /* fill in extra space with 0's. */ + _XEditResPut16(stream, 0); + return; + } + + XtSetArg(args[num_args], XtNwidth, &width); num_args++; + XtSetArg(args[num_args], XtNheight, &height); num_args++; + XtSetArg(args[num_args], XtNborderWidth, &border_width); num_args++; + XtSetArg(args[num_args], XtNmappedWhenManaged, &mapped_when_man); + num_args++; + XtGetValues(w, args, num_args); + + if (!(XtIsManaged(w) && mapped_when_man) && XtIsWidget(w)) { + XWindowAttributes attrs; + + /* + * The toolkit does not maintain mapping state, we have + * to go to the server. + */ + + if (XGetWindowAttributes(XtDisplay(w), XtWindow(w), &attrs) != 0) { + if (attrs.map_state != IsViewable) { + _XEditResPutBool(stream, False); /* no error. */ + _XEditResPutBool(stream, False); /* not visable. */ + for (i = 0; i < 5; i++) /* fill in extra space with 0's. */ + _XEditResPut16(stream, 0); + return; + } + } + else { + _XEditResPut8(stream, True); /* Error occured. */ + _XEditResPutString8(stream, "XGetWindowAttributes failed."); + return; + } + } + + XtTranslateCoords(w, -((int) border_width), -((int) border_width), &x, &y); + + _XEditResPutBool(stream, False); /* no error. */ + _XEditResPutBool(stream, True); /* Visable. */ + _XEditResPut16(stream, x); + _XEditResPut16(stream, y); + _XEditResPut16(stream, width); + _XEditResPut16(stream, height); + _XEditResPut16(stream, border_width); +} + +/************************************************************ + * + * Code for executing FindChild. + * + ************************************************************/ + +/* Function Name: PositionInChild + * Description: returns true if this location is in the child. + * Arguments: child - the child widget to check. + * x, y - location of point to check in the parent's + * coord space. + * Returns: TRUE if the position is in this child. + */ + +static Boolean +PositionInChild(child, x, y) +Widget child; +int x, y; +{ + Arg args[6]; + Cardinal num; + Dimension width, height, border_width; + Position child_x, child_y; + Boolean mapped_when_managed; + + if (!XtIsRectObj(child)) /* we must at least be a rect obj. */ + return(FALSE); + + num = 0; + XtSetArg(args[num], XtNmappedWhenManaged, &mapped_when_managed); num++; + XtSetArg(args[num], XtNwidth, &width); num++; + XtSetArg(args[num], XtNheight, &height); num++; + XtSetArg(args[num], XtNx, &child_x); num++; + XtSetArg(args[num], XtNy, &child_y); num++; + XtSetArg(args[num], XtNborderWidth, &border_width); num++; + XtGetValues(child, args, num); + + /* + * The only way we will know of the widget is mapped is to see if + * mapped when managed is True and this is a managed child. Otherwise + * we will have to ask the server if this window is mapped. + */ + + if (XtIsWidget(child) && !(mapped_when_managed && XtIsManaged(child)) ) { + XWindowAttributes attrs; + + if (XGetWindowAttributes(XtDisplay(child), + XtWindow(child), &attrs) != 0) { + /* oops */ + } + else if (attrs.map_state != IsViewable) + return(FALSE); + } + + return (x >= child_x) && + (x <= (child_x + (Position)width + 2 * (Position)border_width)) && + (y >= child_y) && + (y <= (child_y + (Position)height + 2 * (Position)border_width)); +} + +/* Function Name: _FindChild + * Description: Finds the child that actually contatians the point shown. + * Arguments: parent - a widget that is known to contain the point + * specified. + * x, y - The point in coordinates relative to the + * widget specified. + * Returns: none. + */ + +static Widget +_FindChild(parent, x, y) +Widget parent; +int x, y; +{ + Widget * children; + int i = FindChildren(parent, &children, TRUE, FALSE); + + while (i > 0) { + i--; + + if (PositionInChild(children[i], x, y)) { + Widget child = children[i]; + + XtFree((char *)children); + return(_FindChild(child, x - child->core.x, y - child->core.y)); + } + } + + XtFree((char *)children); + return(parent); +} + +/* Function Name: DoFindChild + * Description: finds the child that contains the location specified. + * Arguments: w - a widget in the tree. + * event - the event that caused this action. + * stream - the protocol stream to add. + * Returns: an allocated error message if something went horribly + * wrong and no set values were performed, else NULL. + */ + +static char * +DoFindChild(w, event, stream) +Widget w; +EditresEvent * event; +ProtocolStream * stream; +{ + char * str; + Widget parent, child; + Position parent_x, parent_y; + FindChildEvent * find_event = (FindChildEvent *) event; + + if ((str = VerifyWidget(w, find_event->widgets)) != NULL) + return(str); + + parent = find_event->widgets->real_widget; + + XtTranslateCoords(parent, (Position) 0, (Position) 0, + &parent_x, &parent_y); + + child = _FindChild(parent, find_event->x - (int) parent_x, + find_event->y - (int) parent_y); + + InsertWidget(stream, child); + return(NULL); +} + +/************************************************************ + * + * Procedures for performing GetResources. + * + ************************************************************/ + +/* Function Name: DoGetResources + * Description: Gets the Resources associated with the widgets passed. + * Arguments: w - a widget in the tree. + * event - the event that caused this action. + * stream - the protocol stream to add. + * Returns: NULL + */ + +static char * +DoGetResources(w, event, stream) +Widget w; +EditresEvent * event; +ProtocolStream * stream; +{ + unsigned int i; + char * str; + GetResEvent * res_event = (GetResEvent *) event; + + _XEditResPut16(stream, res_event->num_entries); /* number of replys */ + + for (i = 0 ; i < res_event->num_entries; i++) { + /* + * Send out the widget id. + */ + _XEditResPutWidgetInfo(stream, &(res_event->widgets[i])); + if ((str = VerifyWidget(w, &(res_event->widgets[i]))) != NULL) { + _XEditResPutBool(stream, True); /* an error occured. */ + _XEditResPutString8(stream, str); /* set message. */ + XtFree(str); + } + else { + _XEditResPutBool(stream, False); /* no error occured. */ + ExecuteGetResources(res_event->widgets[i].real_widget, + stream); + } + } + return(NULL); +} + +/* Function Name: ExecuteGetResources. + * Description: Gets the resources for any individual widget. + * Arguments: w - the widget to get resources on. + * stream - the protocol stream. + * Returns: none. + */ + +static void +ExecuteGetResources(w, stream) +Widget w; +ProtocolStream * stream; +{ + XtResourceList norm_list, cons_list; + Cardinal num_norm, num_cons; + register int i; + + /* + * Get Normal Resources. + */ + + XtGetResourceList(XtClass(w), &norm_list, &num_norm); + + if (XtParent(w) != NULL) + XtGetConstraintResourceList(XtClass(XtParent(w)),&cons_list,&num_cons); + else + num_cons = 0; + + _XEditResPut16(stream, num_norm + num_cons); /* how many resources. */ + + /* + * Insert all the normal resources. + */ + + for ( i = 0; i < (int) num_norm; i++) { + _XEditResPutResourceType(stream, NormalResource); + _XEditResPutString8(stream, norm_list[i].resource_name); + _XEditResPutString8(stream, norm_list[i].resource_class); + _XEditResPutString8(stream, norm_list[i].resource_type); + } + XtFree((char *) norm_list); + + /* + * Insert all the constraint resources. + */ + + if (num_cons > 0) { + for ( i = 0; i < (int) num_cons; i++) { + _XEditResPutResourceType(stream, ConstraintResource); + _XEditResPutString8(stream, cons_list[i].resource_name); + _XEditResPutString8(stream, cons_list[i].resource_class); + _XEditResPutString8(stream, cons_list[i].resource_type); + } + XtFree((char *) cons_list); + } +} + +/************************************************************ + * + * Code for inserting values into the protocol stream. + * + ************************************************************/ + +/* Function Name: InsertWidget + * Description: Inserts the full parent heirarchy of this + * widget into the protocol stream as a widget list. + * Arguments: stream - the protocol stream. + * w - the widget to insert. + * Returns: none + */ + +static void +InsertWidget(stream, w) +ProtocolStream * stream; +Widget w; +{ + Widget temp; + unsigned long * widget_list; + register int i, num_widgets; + + for (temp = w, i = 0; temp != 0; temp = XtParent(temp), i++) {} + + num_widgets = i; + widget_list = (unsigned long *) + XtMalloc(sizeof(unsigned long) * num_widgets); + + /* + * Put the widgets into the list. + * make sure that they are inserted in the list from parent -> child. + */ + + for (i--, temp = w; temp != NULL; temp = XtParent(temp), i--) + widget_list[i] = (unsigned long) temp; + + _XEditResPut16(stream, num_widgets); /* insert number of widgets. */ + for (i = 0; i < num_widgets; i++) /* insert Widgets themselves. */ + _XEditResPut32(stream, widget_list[i]); + + XtFree((char *)widget_list); +} + +/************************************************************ + * + * All of the following routines are public. + * + ************************************************************/ + +/* Function Name: _XEditResPutString8 + * Description: Inserts a string into the protocol stream. + * Arguments: stream - stream to insert string into. + * str - string to insert. + * Returns: none. + */ + +static void +_XEditResPutString8(stream, str) +ProtocolStream * stream; +char * str; +{ + int i, len = strlen(str); + + _XEditResPut16(stream, len); + for (i = 0 ; i < len ; i++, str++) + _XEditResPut8(stream, *str); +} + +/* Function Name: _XEditResPut8 + * Description: Inserts an 8 bit integer into the protocol stream. + * Arguments: stream - stream to insert string into. + * value - value to insert. + * Returns: none + */ + +static void +_XEditResPut8(stream, value) +ProtocolStream * stream; +unsigned int value; +{ + unsigned char temp; + + if (stream->size >= stream->alloc) { + stream->alloc += 100; + stream->real_top = (unsigned char *) XtRealloc( + (char *)stream->real_top, + stream->alloc + HEADER_SIZE); + stream->top = stream->real_top + HEADER_SIZE; + stream->current = stream->top + stream->size; + } + + temp = (unsigned char) (value & BYTE_MASK); + *((stream->current)++) = temp; + (stream->size)++; +} + +/* Function Name: _XEditResPut16 + * Description: Inserts a 16 bit integer into the protocol stream. + * Arguments: stream - stream to insert string into. + * value - value to insert. + * Returns: void + */ + +static void +_XEditResPut16(stream, value) +ProtocolStream * stream; +unsigned int value; +{ + _XEditResPut8(stream, (value >> XER_NBBY) & BYTE_MASK); + _XEditResPut8(stream, value & BYTE_MASK); +} + +/* Function Name: _XEditResPut32 + * Description: Inserts a 32 bit integer into the protocol stream. + * Arguments: stream - stream to insert string into. + * value - value to insert. + * Returns: void + */ + +static void +_XEditResPut32(stream, value) +ProtocolStream * stream; +unsigned long value; +{ + int i; + + for (i = 3; i >= 0; i--) + _XEditResPut8(stream, (value >> (XER_NBBY*i)) & BYTE_MASK); +} + +/* Function Name: _XEditResPutWidgetInfo + * Description: Inserts the widget info into the protocol stream. + * Arguments: stream - stream to insert widget info into. + * info - info to insert. + * Returns: none + */ + +static void +_XEditResPutWidgetInfo(stream, info) +ProtocolStream * stream; +WidgetInfo * info; +{ + unsigned int i; + + _XEditResPut16(stream, info->num_widgets); + for (i = 0; i < info->num_widgets; i++) + _XEditResPut32(stream, info->ids[i]); +} + +/************************************************************ + * + * Code for retrieving values from the protocol stream. + * + ************************************************************/ + +/* Function Name: _XEditResResetStream + * Description: resets the protocol stream + * Arguments: stream - the stream to reset. + * Returns: none. + */ + +static void +_XEditResResetStream(stream) +ProtocolStream * stream; +{ + stream->current = stream->top; + stream->size = 0; + if (stream->real_top == NULL) { + stream->real_top = (unsigned char *) XtRealloc( + (char *)stream->real_top, + stream->alloc + HEADER_SIZE); + stream->top = stream->real_top + HEADER_SIZE; + stream->current = stream->top + stream->size; + } +} + +/* + * NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE + * + * The only modified field if the "current" field. + * + * The only fields that must be set correctly are the "current", "top" + * and "size" fields. + */ + +/* Function Name: _XEditResGetg8 + * Description: Retrieves an unsigned 8 bit value + * from the protocol stream. + * Arguments: stream. + * val - a pointer to value to return. + * Returns: TRUE if sucessful. + */ + +static Boolean +_XEditResGet8(stream, val) +ProtocolStream * stream; +unsigned char * val; +{ + if (stream->size < (stream->current - stream->top)) + return(FALSE); + + *val = *((stream->current)++); + return(TRUE); +} + +/* Function Name: _XEditResGet16 + * Description: Retrieves an unsigned 16 bit value + * from the protocol stream. + * Arguments: stream. + * val - a pointer to value to return. + * Returns: TRUE if sucessful. + */ + +static Boolean +_XEditResGet16(stream, val) +ProtocolStream * stream; +unsigned short * val; +{ + unsigned char temp1, temp2; + + if ( !(_XEditResGet8(stream, &temp1) && _XEditResGet8(stream, &temp2)) ) + return(FALSE); + + *val = (((unsigned short) temp1 << XER_NBBY) + ((unsigned short) temp2)); + return(TRUE); +} + +/* Function Name: _XEditResGetSigned16 + * Description: Retrieves an signed 16 bit value from the protocol stream. + * Arguments: stream. + * val - a pointer to value to return. + * Returns: TRUE if sucessful. + */ + +static Boolean +_XEditResGetSigned16(stream, val) +ProtocolStream * stream; +short * val; +{ + unsigned char temp1, temp2; + + if ( !(_XEditResGet8(stream, &temp1) && _XEditResGet8(stream, &temp2)) ) + return(FALSE); + + if (temp1 & (1 << (XER_NBBY - 1))) { /* If the sign bit is active. */ + *val = -1; /* store all 1's */ + *val &= (temp1 << XER_NBBY); /* Now and in the MSB */ + *val &= temp2; /* and LSB */ + } + else + *val = (((unsigned short) temp1 << XER_NBBY) + ((unsigned short) temp2)); + + return(TRUE); +} + +/* Function Name: _XEditResGet32 + * Description: Retrieves an unsigned 32 bit value + * from the protocol stream. + * Arguments: stream. + * val - a pointer to value to return. + * Returns: TRUE if sucessful. + */ + +static Boolean +_XEditResGet32(stream, val) +ProtocolStream * stream; +unsigned long * val; +{ + unsigned short temp1, temp2; + + if ( !(_XEditResGet16(stream, &temp1) && _XEditResGet16(stream, &temp2)) ) + return(FALSE); + + *val = (((unsigned short) temp1 << (XER_NBBY * 2)) + + ((unsigned short) temp2)); + return(TRUE); +} + +/* Function Name: _XEditResGetString8 + * Description: Retrieves an 8 bit string value from the protocol stream. + * Arguments: stream - the protocol stream + * str - the string to retrieve. + * Returns: True if retrieval was successful. + */ + +static Boolean +_XEditResGetString8(stream, str) +ProtocolStream * stream; +char ** str; +{ + unsigned short len; + register unsigned i; + + if (!_XEditResGet16(stream, &len)) { + return(FALSE); + } + + *str = XtMalloc(sizeof(char) * (len + 1)); + + for (i = 0; i < len; i++) { + if (!_XEditResGet8(stream, (unsigned char *) *str + i)) { + XtFree(*str); + *str = NULL; + return(FALSE); + } + } + (*str)[i] = '\0'; /* NULL terminate that sucker. */ + return(TRUE); +} + +/* Function Name: _XEditResGetWidgetInfo + * Description: Retrieves the list of widgets that follow and stores + * them in the widget info structure provided. + * Arguments: stream - the protocol stream + * info - the widget info struct to store into. + * Returns: True if retrieval was successful. + */ + +static Boolean +_XEditResGetWidgetInfo(stream, info) +ProtocolStream * stream; +WidgetInfo * info; +{ + unsigned int i; + + if (!_XEditResGet16(stream, &(info->num_widgets))) + return(FALSE); + + info->ids = (unsigned long *) XtMalloc(sizeof(long) * (info->num_widgets)); + + for (i = 0; i < info->num_widgets; i++) { + if (!_XEditResGet32(stream, info->ids + i)) { + XtFree((char *)info->ids); + info->ids = NULL; + return(FALSE); + } + } + return(TRUE); +} + +/************************************************************ + * + * Code for Loading the EditresBlock resource. + * + ************************************************************/ + +/* Function Name: CvStringToBlock + * Description: Converts a string to an editres block value. + * Arguments: dpy - the display. + * args, num_args - **UNUSED ** + * from_val, to_val - value to convert, and where to put result + * converter_data - ** UNUSED ** + * Returns: TRUE if conversion was sucessful. + */ + +/* ARGSUSED */ +static Boolean +CvtStringToBlock(dpy, args, num_args, from_val, to_val, converter_data) +Display * dpy; +XrmValue * args; +Cardinal * num_args; +XrmValue * from_val, * to_val; +XtPointer * converter_data; +{ + char ptr[BUFSIZ]; + static EditresBlock block; + +/* XmuCopyISOLatin1Lowered(ptr, from_val->addr);*/ + + + if (streq(ptr, "none")) + block = BlockNone; + else if (streq(ptr, "setvalues")) + block = BlockSetValues; + else if (streq(ptr, "all")) + block = BlockAll; + else { + Cardinal num_params = 1; + String params[1]; + + params[0] = from_val->addr; + XtAppWarningMsg(XtDisplayToApplicationContext(dpy), + "CvtStringToBlock", "unknownValue", "EditresError", + "Could not convert string \"%s\" to EditresBlock.", + params, &num_params); + return(FALSE); + } + + if (to_val->addr != NULL) { + if (to_val->size < sizeof(EditresBlock)) { + to_val->size = sizeof(EditresBlock); + return(FALSE); + } + *(EditresBlock *)(to_val->addr) = block; + } + else + to_val->addr = (XtPointer) block; + + to_val->size = sizeof(EditresBlock); + return(TRUE); +} + +#define XtREditresBlock ("EditresBlock") + +/* Function Name: LoadResources + * Description: Loads a global resource the determines of this + * application should allow Editres requests. + * Arguments: w - any widget in the tree. + * Returns: none. + */ + +static void +LoadResources(w) +Widget w; +{ + static XtResource resources[] = { + {"editresBlock", "EditresBlock", XtREditresBlock, sizeof(EditresBlock), + XtOffsetOf(Globals, block), XtRImmediate, (XtPointer) BlockNone} + }; + + for (; XtParent(w) != NULL; w = XtParent(w)) {} + + XtAppSetTypeConverter(XtWidgetToApplicationContext(w), + XtRString, XtREditresBlock, CvtStringToBlock, + NULL, (Cardinal) 0, XtCacheAll, NULL); + + XtGetApplicationResources( w, (caddr_t) &globals, resources, + XtNumber(resources), NULL, (Cardinal) 0); +} + + diff --git a/RegEdit.h b/RegEdit.h new file mode 100644 index 0000000..5274151 --- /dev/null +++ b/RegEdit.h @@ -0,0 +1,65 @@ +/* $XConsortium: RegEdit.h /main/5 1995/07/15 20:44:09 drk $ */ +/* + * @OPENGROUP_COPYRIGHT@ + * COPYRIGHT NOTICE + * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. + * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group + * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for + * the full copyright text. + * + * This software is subject to an open license. It may only be + * used on, with or for operating systems which are themselves open + * source systems. You must contact The Open Group for a license + * allowing distribution and sublicensing of this software on, with, + * or for operating systems which are not Open Source programs. + * + * See http://www.opengroup.org/openmotif/license for full + * details of the license agreement. Any use, reproduction, or + * distribution of the program constitutes recipient's acceptance of + * this agreement. + * + * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS + * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY + * OR FITNESS FOR A PARTICULAR PURPOSE + * + * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT + * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE + * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + */ +/* + * HISTORY + */ + + /* Ensure that the file be included only once. */ +#ifndef _XmdRegEdit_h +#define _XmdRegEdit_h + +#include <Xm/Xm.h> + +/* Allow for C++ compilation. */ +#ifdef __cplusplus +extern "C" { +#endif + + +extern void XmdRegisterEditres(Widget toplevel); + +/* Allow for C++ compilation. */ +#ifdef __cplusplus +} /* Close scope of 'extern "C"' declaration which encloses file. */ +#endif + + +/* Ensure that the file be included only once. */ +#endif /* _XmdRegEdit_h */ +/* DON'T ADD ANYTHING AFTER THIS #endif */ + diff --git a/RegEditI.h b/RegEditI.h new file mode 100644 index 0000000..4608593 --- /dev/null +++ b/RegEditI.h @@ -0,0 +1,368 @@ +/* + * @OPENGROUP_COPYRIGHT@ + * COPYRIGHT NOTICE + * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. + * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group + * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for + * the full copyright text. + * + * This software is subject to an open license. It may only be + * used on, with or for operating systems which are themselves open + * source systems. You must contact The Open Group for a license + * allowing distribution and sublicensing of this software on, with, + * or for operating systems which are not Open Source programs. + * + * See http://www.opengroup.org/openmotif/license for full + * details of the license agreement. Any use, reproduction, or + * distribution of the program constitutes recipient's acceptance of + * this agreement. + * + * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS + * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY + * OR FITNESS FOR A PARTICULAR PURPOSE + * + * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT + * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE + * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + */ +/* + * HISTORY + */ + + +/* $XConsortium: RegEditI.h /main/4 1995/07/14 10:05:01 drk $ */ + +/* + +Copyright (c) 1989 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + +*/ + +/* + * Author: Chris D. Peterson, MIT X Consortium + */ + +/************************************************************ + + The Editres Protocol + + + The Client message sent to the application is: + + ATOM = "ResEditor" --- RES_EDITOR_NAME + + FORMAT = 32 --- RES_EDIT_SEND_EVENT_FORMAT + + l[0] = timestamp + l[1] = command atom name + l[2] = ident of command. + l[3] = protocol version number to use. + + + + The binary protocol has the following format: + + Card8: 8-bit unsingned integer + Card16: 16-bit unsingned integer + Card32: 32-bit unsingned integer + Int16: 16-bit signed integer + Window: 32-bit value + Widget: 32-bit value + String8: ListOfCard8 + + [a][b][c] represent an exclusive list of choices. + + All widgets are passed as a list of widgets, containing the + full instance heirarch of this widget. The hierarchy is ordered + from parent to child. Thus the first element of each list is + the root of the widget tree (this makes verifying that the widget + still exists, MUCH faster). + + ListOfFoo comprises a list of things in the following format: + + number: Card16 + <number> things: ???? + + This is a synchronous protocol, every request MUST be followed by a + reply. + + Request: + + Serial Number: Card8 + Op Code: Card8 - { SendWidgetTree = 0, + SetValues = 1, + GetResources = 2, + GetGeometry = 3, + FindChild = 4, + GetValues = 5 } + Length: Card32 + Data: + + Reply: + + Serial Number: Card8 + Type: Card8 - { Formatted = 0, + Unformatted = 1, + ProtocolMismatch = 2 + } + Length: Card32 + + + Byte Order: + + All Fields are MSB -> LSB + + Data: + + Formatted: + + The data contains the reply information for the request as + specified below if the reply type is "Formatted". The return + values for the other reply types are shown below. + + Unformatted: + + Message: String8 + + ProtocolMismatch: + + RequestedVersion: Card8 + +------------------------------------------------------------ + + SendWidgetTree: + + ---> + + Number of Entries: Card16 + Entry: + widget: ListOfWidgets + name: String8 + class: String8 + window: Card32 + toolkit: String8 + + Send Widget Tree returns the toolkit type, and a fuly specified list + of widgets for each widget in the tree. This is enough information + to completely reconstruct the entire widget heirarchy. + + The window return value contains the Xid of the window currently + used by this widget. If the widget is unrealized then 0 is returned, + and if widget is a non-windowed object a value of 2 is returned. + + SetValues: + + name: String8 + type: String8 + value: String8 + Number of Entries: Card16 + Entry: + widget: ListOfWidgets + + ---> + + Number of Entries: Card16 + Entry: + widget: ListOfWidgets + message: String8 + + SetValues will allow the same resource to be set on a number of + widgets. This function will return an error message if the SetValues + request caused an Xt error. + + GetValues: + + names: ListOfString8 + widget: Widget + + ---> + novalues: ListOfCard16 + values: ListOfString8 + + GetValues will allow a number of resource values to be read + on a particular widget. The request specifies the names of + the resources wanted and the widget id these resources are + from. The reply returns a list of indices from the requests + name list of resources for which a value can not be returned. + It also returns a list of returned values, in the order of the + requests names list, skipping those indices present in novalues. + + GetResources: + + Number of Entries: Card16 + Entry + widget: ListOfWidgets: + + ----> + + Number of Entries: Card16 + Entry + Widget: ListOfWidgets: + Error: Bool + + [ Message: String 8 ] + [ Number of Resources: Card16 + Resource: + Kind: {normal, constraint} + Name: String8 + Class: String8 + Type: String8 ] + + GetResource retrieves the kind, name, class and type for every + widget passed to it. If an error occured with the resource fetch + Error will be set to True for the given widget and a message + is returned rather than the resource info. + + GetGeometry: + + Number of Entries: Card16 + Entry + Widget: ListOfWidgets: + + ----> + + Number of Entries: Card16 + Entry + Widget: ListOfWidgets: + Error: Bool + + [ message: String 8 ] + [ mapped: Boolean + X: Int16 + Y: Int16 + Width: Card16 + Height: Card16 + BorderWidth: Card16 ] + + GetGeometry retreives the mapping state, x, y, width, height + and border width for each widget specified. If an error occured + with the geometry fetch "Error" will be set to True for the given + widget and a message is returned rather than the geometry info. + X an Y corrospond to the root coordinates of the upper left corner + of the widget (outside the window border). + + FindChild: + + Widget: ListOfWidgets + X: Int16 + Y: Int16 + + ---> + + Widget: ListOfWidgets + + Find Child returns a descendent of the widget specified that + is at the root coordinates specified. + + NOTE: + + The returned widget is undefined if the point is contained in + two or more mapped widgets, or in two overlapping Rect objs. + + GetValues: + + names: ListOfString8 + widget: Widget + + ---> + + values: ListOfString8 + + GetValues will allow a number of resource values to be read + on a particular widget. Currently only InterViews 3.0.1 Styles + and their attributes are supported. In addition, the current + user interface only supports the return of 1 resource. The ability + to specify and return multiple resources is defined for future editres + interfaces where some or all of a widgets resource values are returned + and displayed at once. + + +************************************************************/ + +#include <X11/Intrinsic.h> +#include <X11/Xfuncproto.h> + +#define XER_NBBY 8 /* number of bits in a byte */ +#define BYTE_MASK 255 + +#define HEADER_SIZE 6 + +#define EDITRES_IS_OBJECT 2 +#define EDITRES_IS_UNREALIZED 0 + +/* + * Format for atoms. + */ + +#define EDITRES_FORMAT 8 +#define EDITRES_SEND_EVENT_FORMAT 32 + +/* + * Atoms + */ + +#define EDITRES_NAME "Editres" +#define EDITRES_COMMAND_ATOM "EditresCommand" +#define EDITRES_COMM_ATOM "EditresComm" +#define EDITRES_CLIENT_VALUE "EditresClientVal" +#define EDITRES_PROTOCOL_ATOM "EditresProtocol" + +typedef enum { SendWidgetTree = 0, + SetValues = 1, + GetResources = 2, + GetGeometry = 3, + FindChild = 4, + GetValues = 5 + } EditresCommand; + +typedef enum {NormalResource = 0, ConstraintResource = 1} ResourceType; + +/* + * The type of a resource identifier. + */ + +typedef unsigned char ResIdent; + +typedef enum {PartialSuccess= 0, Failure= 1, ProtocolMismatch= 2} EditResError; + +typedef struct _WidgetInfo { + unsigned short num_widgets; + unsigned long * ids; + Widget real_widget; +} WidgetInfo; + +typedef struct _ProtocolStream { + unsigned long size, alloc; + unsigned char *real_top, *top, *current; +} ProtocolStream; + @@ -0,0 +1,22 @@ + +- some effect filters +- improve cut+paste [does complete images only right now ... ] +- make the file browser save the thumbnails somewhere (optionally), + so it can be much faster the second time ... +- screenshots +- put images on root window / some batch processing. + + +List is ordered according to my personal priorities. More ideas +welcome, but it doesn't automatically mean they will be added to the +list. iv is a interactive, small + fast[1] utility and will only +support commonly needed features (focus on photos), compareable to xv. + +If you need a full featured image processing tool with all bells and +whistles, better use the gimp. To to stuff batched in scripts, use +ImageMagick (convert utility / perl interface). + + +[1] It has finished loading the image while the gimp still shows the + splash screen :-) + @@ -0,0 +1 @@ +2.0 diff --git a/backup/Ida-de b/backup/Ida-de new file mode 100644 index 0000000..31d5b40 --- /dev/null +++ b/backup/Ida-de @@ -0,0 +1,173 @@ + +! ---------------------------------------------------------------------------- +! some standard motif stuff [i18] + +*.cancelLabelString: Abbrechen +*.XmFileSelectionBox.dirListLabelString: Verzeichnisse +*.XmFileSelectionBox.fileListLabelString: Dateien +*.XmFileSelectionBox.selectionLabelString: Auswahl + + +! ---------------------------------------------------------------------------- +! strings [i18] + +Ida.aboutbox_popup.title: Über ida +Ida*aboutbox_popup*messageString: \ + ida - image viewer & editor \n\ + \n\ + (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> + +Ida.noundobox_popup.title: Nicht verfügbar +Ida*noundobox_popup*messageString: \ + Es kann nur der letzte Arbeitsschritt\n\ + rückgänig gemacht werden. + +Ida.errbox_popup.title: Fehler +Ida.load_popup.title: Datei öffnen +Ida.save_popup.title: Datei speichern +Ida.save_popup*format.labelString: Dateiformat: +Ida.print_popup.title: Datei drucken +Ida.print_popup*selectionLabelString: Kommando +Ida.print_popup*textString: lpr + +Ida*jpeg_popup.title: JPEG Einstellungen +Ida*jpeg_popup*selectionLabelString: Bildqualität (0 ... 100) +Ida*ps_popup.title: PostScript Einstellungen +Ida*ps_popup*paper.labelString: Papier: +Ida*ps_popup*ori.labelString: Format: +Ida*ps_popup*oriM.portrait.labelString: Hochformat +Ida*ps_popup*oriM.landscape.labelString: Querformat + +Ida.gamma_popup.title: Gammakorrektur +Ida.gamma_popup*selectionLabelString: Gamma Wert +Ida.bright_popup.title: Helligkeit ändern +Ida.bright_popup*selectionLabelString: Helligkeit +Ida.contrast_popup.title: Kontrast ändern +Ida.contrast_popup*selectionLabelString: Kontrast +Ida.rotate_popup.title: Bild drehen +Ida.rotate_popup*selectionLabelString: Drehwinkel +Ida.sharpe_popup.title: Bild schärfen +Ida.sharpe_popup*selectionLabelString: Wert + +Ida.resize_popup.title: Bild skalieren +Ida.resize_popup*lx.labelString: Breite (Pixel) +Ida.resize_popup*ly.labelString: Höhe (Pixel) +Ida.resize_popup*lr.labelString: Auflösung (dpi) +Ida.resize_popup*lock.labelString: Seitenverhältnis beibehalten +Ida.resize_popup*size.labelString: Größe ändern +Ida.resize_popup*res.labelString: Auflösung ändern +Ida.resize_popup*phys.labelString: Bildgröße + +Ida.color_popup.title: Edit colors +Ida.color_popup*hist.labelString: Histograms +Ida.color_popup*map.labelString: Maps +Ida.color_popup*lock.labelString: Same values for all channels +Ida.color_popup*vals.labelString: Show values for channel: +Ida.color_popup*valsM.red.labelString: red +Ida.color_popup*valsM.green.labelString: green +Ida.color_popup*valsM.blue.labelString: blue +Ida.color_popup*in.label.labelString: Input range: +Ida.color_popup*out.label.labelString: Output range: +Ida.color_popup*gamma.label.labelString: Gamma: +Ida.color_popup*white.labelString: Weißpunkt setzen + +ctrl.title: ida controls +ctrl.form.status.labelString: fixme + +ctrl*bar.file.labelString: Datei +ctrl*bar.file.mnemonic: D +ctrl*bar*load.labelString: Öffnen ... +ctrl*bar*load.mnemonic: Odiaeresis +ctrl*bar*save.labelString: Speichern ... +ctrl*bar*save.mnemonic: S +ctrl*bar*browse.labelString: Datei Browser ... +ctrl*bar*scan.labelString: Scannen +ctrl*bar*print.labelString: Drucken ... +ctrl*bar*quit.labelString: Beenden +ctrl*bar*quit.mnemonic: B + +ctrl*bar.edit.labelString: Bearbeiten +ctrl*bar.edit.mnemonic: B +ctrl*bar*undo.labelString: Rückgänig +ctrl*bar*undo.mnemonic: udiaeresis +ctrl*bar*copy.labelString: Kopieren +ctrl*bar*paste.labelString: Einfügen +ctrl*bar*flipv.labelString: Vertikal spiegeln +ctrl*bar*flipv.mnemonic: V +ctrl*bar*fliph.labelString: Horizontal spiegeln +ctrl*bar*fliph.mnemonic: H +ctrl*bar*rotcw.labelString: Drehen im Uhrzeigersinn +ctrl*bar*rotccw.labelString: Drehen gegen den Uhrzeigersinn +ctrl*bar*invert.labelString: Invertieren +ctrl*bar*invert.mnemonic: I +ctrl*bar*crop.labelString: Crop +ctrl*bar*crop.mnemonic: C +ctrl*bar*acrop.labelString: Autocrop +ctrl*bar*acrop.mnemonic: A +ctrl*bar*scale.labelString: Skalieren ... +ctrl*bar*scale.mnemonic: S +ctrl*bar*rotany.labelString: Drehen ... + +ctrl*bar.op.labelString: Filter +ctrl*bar.op.mnemonic: F +ctrl*bar*gamma.labelString: Gamma ... +ctrl*bar*gamma.mnemonic: G +ctrl*bar*bright.labelString: Helligheit ... +ctrl*bar*contr.labelString: Kontrast ... +ctrl*bar*color.labelString: Farben bearbeiten +ctrl*bar*color.mnemonic: e +ctrl*bar*gray.labelString: In Graustufen wandeln +ctrl*bar*blur.labelString: Weich zeichnen +ctrl*bar*sharpe.labelString: Schärfen ... +ctrl*bar*edge.labelString: Kanten finden +ctrl*bar*emboss.labelString: Schattenriß + +ctrl*bar.view.labelString: Ansicht +ctrl*bar.view.mnemonic: A +ctrl*bar*prev.labelString: Vorherige Datei +ctrl*bar*next.labelString: Nächste Datei +ctrl*bar*prevpage.labelString: Vorherige Seite +ctrl*bar*nextpage.labelString: Nächste Seite +ctrl*bar*zoomin.labelString: Vergrößern +ctrl*bar*zoomout.labelString: Verkleinern + +ctrl*bar.opt.labelString: Einstellungen +ctrl*bar.view.mnemonic: E +ctrl*bar*pcd.labelString: PhotoCD Auflösung + +ctrl*bar.help.labelString: Hilfe +ctrl*bar.help.mnemonic: H +ctrl*bar*man.labelString: Manual page ... +ctrl*bar*man.mnemonic: M +ctrl*bar*about.labelString: Über ... +ctrl*bar*about.mnemonic: Udiaeresis + +ctrl*tool.prev.toolTipString: vorherige Datei +ctrl*tool.next.toolTipString: nächste Datei +ctrl*tool.zoomin.toolTipString: Vergrößern +ctrl*tool.zoomout.toolTipString: Verkleinern +ctrl*tool.flipv.toolTipString: Vertikal spiegeln +ctrl*tool.fliph.toolTipString: Horizontal spiegeln +ctrl*tool.rotccw.toolTipString: Drehen gegen Uhrzeigersinn +ctrl*tool.rotcw.toolTipString: Drehen im Uhrzeigersinn +ctrl*tool.exit.toolTipString: Beenden + +browser*bar.file.labelString: Datei +browser*bar.file.mnemonic: D +browser*bar*close.labelString: Fenster schließen + +browser*bar.view.labelString: Ansicht +browser*bar.view.mnemonic: A +browser*bar*filter.labelString: Filtern ... +browser*bar*freset.labelString: Filter aufheben + +browser.filter_popup.title: Filter +browser.filter_popup*selectionLabelString: pattern? + +browser*menu.copy.labelString: Kopieren (ins Clipboard) +browser*menu.rotcw.labelString: Drehen im Uhrzeigersinn +browser*menu.rotccw.labelString: Drehen gegen den Uhrzeigersinn + +Ida.man_popup.title: Manual page +Ida.man_popup*okLabelString: Fenster schließen +Ida.man_popup*label.labelString: Einen Moment bitte ... diff --git a/backup/Ida-default b/backup/Ida-default new file mode 100644 index 0000000..3d50d99 --- /dev/null +++ b/backup/Ida-default @@ -0,0 +1,178 @@ +! ---------------------------------------------------------------------------- +! strings [i18] + +Ida.aboutbox_popup.title: About ida +Ida*aboutbox_popup*messageString: \ + ida - image viewer & editor \n\ + \n\ + (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> + +Ida.noundobox_popup.title: No undo +Ida*noundobox_popup*messageString: \ + No undo info available, sorry. \n\ + You can undo the last step only. + +Ida.errbox_popup.title: Errors +Ida.load_popup.title: Load File +Ida.save_popup.title: Save File +Ida.save_popup*format.labelString: Image format: +Ida.print_popup.title: Print File +Ida.print_popup*selectionLabelString: Print command +Ida.print_popup*textString: lpr + +Ida*jpeg_popup.title: JPEG Options +Ida*jpeg_popup*selectionLabelString: Image quality (0 ... 100) +Ida*ps_popup.title: PostScript Options +Ida*ps_popup*paper.labelString: Paper size: +Ida*ps_popup*ori.labelString: Orientation: + +Ida.gamma_popup.title: Gamma correction +Ida.gamma_popup*selectionLabelString: Gamma value +Ida.bright_popup.title: Adjust bright +Ida.bright_popup*selectionLabelString: Bright +Ida.contrast_popup.title: Adjust contrast +Ida.contrast_popup*selectionLabelString: Contrast +Ida.rotate_popup.title: Rotate image +Ida.rotate_popup*selectionLabelString: angle +Ida.sharpe_popup.title: Sharpe image +Ida.sharpe_popup*selectionLabelString: value + +Ida.resize_popup.title: Scale image +Ida.resize_popup*lx.labelString: Width (pixels) +Ida.resize_popup*ly.labelString: Height (pixels) +Ida.resize_popup*lr.labelString: Resolution (dpi) +Ida.resize_popup*lock.labelString: Keep aspect ratio +Ida.resize_popup*size.labelString: Change size +Ida.resize_popup*res.labelString: Change resolution +Ida.resize_popup*phys.labelString: Image size + +Ida.color_popup.title: Edit colors +Ida.color_popup*hist.labelString: Histograms +Ida.color_popup*map.labelString: Maps +Ida.color_popup*lock.labelString: Same values for all channels +Ida.color_popup*vals.labelString: Show values for channel: +Ida.color_popup*valsM.red.labelString: red +Ida.color_popup*valsM.green.labelString: green +Ida.color_popup*valsM.blue.labelString: blue +Ida.color_popup*in.label.labelString: Input range: +Ida.color_popup*out.label.labelString: Output range: +Ida.color_popup*gamma.label.labelString: Gamma: +!Ida.color_popup*white.labelString: FIXME + +ctrl.title: ida controls +ctrl.form.status.labelString: fixme + +ctrl*bar.file.labelString: File +ctrl*bar.file.mnemonic: F +ctrl*bar*load.labelString: Load image ... +ctrl*bar*load.mnemonic: L +ctrl*bar*save.labelString: Save image ... +ctrl*bar*save.mnemonic: S +ctrl*bar*browse.labelString: File browser ... +ctrl*bar*filelist.labelString: File list ... +ctrl*bar*scan.labelString: Scan +ctrl*bar*print.labelString: Print ... +ctrl*bar*print.mnemonic: P +ctrl*bar*quit.labelString: Quit +ctrl*bar*quit.mnemonic: Q + +ctrl*bar.edit.labelString: Edit +ctrl*bar.edit.mnemonic: E +ctrl*bar*undo.labelString: Undo last operation +ctrl*bar*undo.mnemonic: U +ctrl*bar*copy.labelString: Copy +ctrl*bar*paste.labelString: Paste +ctrl*bar*flipv.labelString: Flip vertical +ctrl*bar*flipv.mnemonic: v +ctrl*bar*fliph.labelString: Flip horizontal +ctrl*bar*fliph.mnemonic: h +ctrl*bar*rotcw.labelString: Turn clockwise +ctrl*bar*rotcw.mnemonic: T +ctrl*bar*rotccw.labelString: Turn counter clockwise +ctrl*bar*invert.labelString: Invert +ctrl*bar*invert.mnemonic: I +ctrl*bar*crop.labelString: Crop +ctrl*bar*crop.mnemonic: C +ctrl*bar*acrop.labelString: Autocrop +ctrl*bar*acrop.mnemonic: A +ctrl*bar*scale.labelString: Scale ... +ctrl*bar*scale.mnemonic: S +ctrl*bar*rotany.labelString: Rotate ... + +ctrl*bar.op.labelString: Filters +ctrl*bar.op.mnemonic: F +ctrl*bar*gamma.labelString: Gamma ... +ctrl*bar*gamma.mnemonic: G +ctrl*bar*bright.labelString: Bright ... +ctrl*bar*bright.mnemonic: B +ctrl*bar*contr.labelString: Contrast ... +ctrl*bar*contr.mnemonic: C +ctrl*bar*color.labelString: Edit colors ... +ctrl*bar*color.mnemonic: E +ctrl*bar*gray.labelString: Grayscale +ctrl*bar*blur.labelString: Blur +ctrl*bar*sharpe.labelString: Sharpe ... +ctrl*bar*edge.labelString: Edge detect +ctrl*bar*emboss.labelString: Emboss + +ctrl*bar.view.labelString: View +ctrl*bar.view.mnemonic: V +ctrl*bar*prev.labelString: previous file +ctrl*bar*next.labelString: next file +ctrl*bar*prevpage.labelString: previous page +ctrl*bar*nextpage.labelString: next page +ctrl*bar*zoomin.labelString: Zoom in +ctrl*bar*zoomout.labelString: Zoom out + +ctrl*bar.opt.labelString: Options +ctrl*bar.view.mnemonic: O +ctrl*bar*pcd.labelString: PhotoCD resolution + +ctrl*bar.help.labelString: Help +ctrl*bar.help.mnemonic: H +ctrl*bar*man.labelString: Manual page ... +ctrl*bar*man.mnemonic: M +ctrl*bar*about.labelString: About ... +ctrl*bar*about.mnemonic: A + +ctrl*tool.prev.toolTipString: previous file +ctrl*tool.next.toolTipString: next file +ctrl*tool.zoomin.toolTipString: zoom in +ctrl*tool.zoomout.toolTipString: zoom out +ctrl*tool.flipv.toolTipString: flip vertical +ctrl*tool.fliph.toolTipString: flip horizontal +ctrl*tool.rotccw.toolTipString: turn counter clockwise +ctrl*tool.rotcw.toolTipString: turn clockwise +ctrl*tool.exit.toolTipString: quit + +browser.filter_popup.title: Filter +browser.filter_popup*selectionLabelString: pattern? + +Ida.man_popup.title: Manual page +Ida.man_popup*okLabelString: close window +Ida.man_popup*label.labelString: please wait ... + +*cbar.file.labelString: File +*cbar.file.mnemonic: F +*cbar*new.labelString: New list +*cbar*new.mnemonic: N +*cbar*load.labelString: Load list ... +*cbar*load.mnemonic: L +*cbar*save.labelString: Save list +*cbar*save.mnemonic: S +*cbar*saveas.labelString: Save list as ... +*cbar*saveas.mnemonic: a +*cbar*close.labelString: Close window + +*cbar.edit.labelString: Edit +*cbar.edit.mnemonic: E +*cbar*copy.labelString: Copy +*cbar*paste.labelString: Paste +*cbar*del.labelString: Delete + +*cbar.view.labelString: View +*cbar.view.mnemonic: V +*cbar*spatial.labelString: Large Icons +*cbar*details.labelString: Details +*cbar*filter.labelString: Filter ... +*cbar*freset.labelString: Reset filter diff --git a/backup/Ida-fixed b/backup/Ida-fixed new file mode 100644 index 0000000..2c6baf6 --- /dev/null +++ b/backup/Ida-fixed @@ -0,0 +1,439 @@ + +! ---------------------------------------------------------------------------- +! fonts + +*renderTable: small +*renderTable.fontType: FONT_IS_FONTSET +*renderTable.fontName: \ + -*-bitstream vera sans-medium-r-normal-*-*-140-*-*-p-*-iso8859-1, \ + -*-bitstream vera sans-medium-r-normal-*-*-140-*-*-p-*-iso8859-15, \ + -microsoft-tahoma-medium-r-normal-*-*-140-*-*-p-*-iso8859-*, \ + -adobe-helvetica-medium-r-normal-*-*-140-*-*-p-*-iso8859-*, \ + -cronyx-helvetica-medium-r-normal-*-*-140-*-*-p-*-koi8-r, \ + -*-lucida-medium-r-normal-*-*-140-*-*-p-*-iso8859-*, \ + -gnu-unifont-medium-r-normal-*-*-160-*-*-*-*-*-*, \ + -*-*-medium-r-normal-*-*-140-*-*-p-*-*-*, \ + -*-*-medium-r-normal-*-*-160-*-*-p-*-*-*, \ + -*-*-*-*-*-*-*-140-*-*-*-*-*-*, \ + -*-*-*-*-*-*-*-160-*-*-*-*-*-*, * +*renderTable.small.fontType: FONT_IS_FONTSET +*renderTable.small.fontName: \ + -*-bitstream vera sans-medium-r-normal-*-*-100-*-*-p-*-iso8859-1, \ + -*-bitstream vera sans-medium-r-normal-*-*-100-*-*-p-*-iso8859-15, \ + -microsoft-tahoma-medium-r-normal-*-*-100-*-*-p-*-iso8859-*, \ + -adobe-helvetica-medium-r-normal-*-*-100-*-*-p-*-iso8859-*, \ + -cronyx-helvetica-medium-r-normal-*-*-100-*-*-p-*-koi8-r, \ + -*-lucida-medium-r-normal-*-*-100-*-*-p-*-iso8859-*, \ + -*-*-medium-r-normal-*-*-100-*-*-p-*-*-*, \ + -*-*-medium-r-normal-*-*-120-*-*-p-*-*-*, \ + -*-*-*-*-*-*-*-100-*-*-*-*-*-*, \ + -*-*-*-*-*-*-*-120-*-*-*-*-*-*, * + +*XmTextField.renderTable: +*XmTextField.renderTable.fontType: FONT_IS_FONTSET +*XmTextField.renderTable.fontName: \ + -*-bitstream vera sans mono-medium-r-normal-*-*-140-*-*-*-*-iso8859-1, \ + -*-bitstream vera sans mono-medium-r-normal-*-*-140-*-*-*-*-iso8859-15, \ + -monotype-andale mono-medium-r-normal-*-*-140-*-*-*-*-iso8859-*, \ + -adobe-courier-medium-r-normal-*-*-140-*-*-m-*-iso8859-*, \ + -cronyx-courier-medium-r-normal-*-*-140-*-*-m-*-koi8-r, \ + -*-lucidatypewriter-medium-r-normal-*-*-140-*-*-m-*-iso8859-*, \ + -*-*-medium-r-normal-*-*-140-*-*-m-*-*-*, \ + -*-*-medium-r-normal-*-*-160-*-*-m-*-*-*, \ + -*-*-*-*-*-*-*-140-*-*-*-*-*-*, \ + -*-*-*-*-*-*-*-160-*-*-*-*-*-*, * + +!*.shadowThickness: 2 +!*.highlightThickness: 1 + + +! ---------------------------------------------------------------------------- +! image window + +Ida.geometry: 75x50 +!Ida.winGravity: static +Ida.view*translations: #override \ + <Key>space: Next() \n\ + <Key>osfDelete: Prev() \n\ + <Key>osfBackSpace: Prev() \n\ + <Key>Page_Down: NextPage() \n\ + <Key>Page_Up: PrevPage() \n\ + <Key>KP_Add: Zoom(inc) \n\ + <Key>KP_Subtract: Zoom(dec) \n\ + <Btn2Down>: Ipc(drag) \n\ + <Btn3Up>: Popup(control) \n\ + \ + <Key>G: Gamma() \n\ + <Key>F: Browser() \n\ + <Key>L: Filelist() \n\ + Ctrl<Key>V: Ipc(paste) \n\ + Alt<Key>V: Ipc(paste) \n\ + Ctrl<Key>C: Ipc(copy) \n\ + Alt<Key>C: Ipc(copy) \n\ + \ + Ctrl<Key>P: Print() \n\ + Ctrl<Key>L: Load() \n\ + Ctrl<Key>S: Save() \n\ + Alt<Key>S: Sharpe() \n\ + ~Alt ~Ctrl<Key>S: Resize() \n\ + <Key>plus: Zoom(inc) \n\ + <Key>minus: Zoom(dec) \n\ + <Key>U: Undo() \n\ + ~Alt ~Ctrl<Key>C: Filter(crop) \n\ + ~Alt ~Ctrl<Key>V: Filter(flip-vert) \n\ + <Key>H: Filter(flip-horz) \n\ + ~Alt ~Shift<Key>T: Filter(rotate-cw) \n\ + Shift<Key>T: Filter(rotate-ccw) \n\ + Alt<Key>T: Rotate() \n\ + <Key>I: Filter(invert) \n\ + ~Alt<Key>E: Color() \n\ + Alt<Key>E: F3x3(-1,-1,-1,-1,8,-1,-1,-1,-1) \n\ + Alt<Key>B: F3x3(1,1,1,1,1,1,1,1,1, 1,9) \n\ + Alt<Key>M: F3x3(1,0,0,0,0,0,0,0,-1, 0,0,128) \n\ + <Key>osfHelp: Man(ida) \n\ + <Key>Q: Exit() + +Ida.view.VertScrollBar.accelerators: #override \ + <Btn4Up>: IncrementUpOrLeft(Up) \n\ + <Btn5Up>: IncrementDownOrRight(Down)\n\ + ~Ctrl<Key>osfUp: IncrementUpOrLeft(Up) \n\ + ~Ctrl<Key>osfDown: IncrementDownOrRight(Down)\n\ + Ctrl<Key>osfUp: PageUpOrLeft(Up) \n\ + Ctrl<Key>osfDown: PageDownOrRight(Down) +Ida.view.HorScrollBar.accelerators: #override \ + ~Ctrl<Key>osfLeft: IncrementUpOrLeft(Left) \n\ + ~Ctrl<Key>osfRight: IncrementDownOrRight(Right)\n\ + Ctrl<Key>osfLeft: PageUpOrLeft(Left) \n\ + Ctrl<Key>osfRight: PageDownOrRight(Right) + + +Ida.view.shadowThickness: 1 +Ida.view.scrollingPolicy: AUTOMATIC +Ida.view.scrollBarPlacement: BOTTOM_RIGHT +Ida.view.scrolledWindowChildType: SCROLL_VERT + +Ida.view*image.backgroundPixmap: none +Ida.view*image.borderWidth: 0 +Ida.view*image.highlightThickness: 1 +Ida.view*image.highlightColor: red + +Ida.aboutbox_popup.deleteResponse: DESTROY +Ida.sorrybox_popup.deleteResponse: DESTROY +Ida.noundobox_popup.deleteResponse: DESTROY + + +! ---------------------------------------------------------------------------- +! dialog boxes + +Ida.XmDialogShell.deleteResponse: DESTROY +Ida.XmDialogShell*scale.orientation: HORIZONTAL +Ida.XmDialogShell*scale.showValue: True + +Ida.errbox_popup.deleteResponse: UNMAP +Ida.load_popup.deleteResponse: UNMAP +Ida.save_popup*deleteResponse: UNMAP +Ida.save_popup*dialogStyle: DIALOG_PRIMARY_APPLICATION_MODAL +Ida.print_popup*deleteResponse: UNMAP +Ida.print_popup*dialogStyle: DIALOG_PRIMARY_APPLICATION_MODAL + +Ida*ps_popup*rc1.orientation: HORIZONTAL +Ida*ps_popup*draw.borderWidth: 1 +Ida*ps_popup*draw.background: white +Ida*ps_popup*draw.resizePolicy: RESIZE_NONE +Ida*ps_popup*scale.titleString: Scaling +Ida*ps_popup*scale.minimum: 10 +Ida*ps_popup*scale.maximum: 1000 +Ida*ps_popup*scale.decimalPoints: 1 + +Ida.gamma_popup*scale.minimum: 20 +Ida.gamma_popup*scale.maximum: 500 +Ida.gamma_popup*scale.decimalPoints: 2 +Ida.bright_popup*scale.minimum: -256 +Ida.bright_popup*scale.maximum: 256 +Ida.contrast_popup*scale.minimum: -128 +Ida.contrast_popup*scale.maximum: 512 +Ida.rotate_popup*scale.minimum: -180 +Ida.rotate_popup*scale.maximum: 180 +Ida.sharpe_popup*scale.minimum: 0 +Ida.sharpe_popup*scale.maximum: 100 + +Ida.resize_popup.deleteResponse: DESTROY +Ida.resize_popup*rc.adjustMargin: false +Ida.resize_popup*rc.rc.orientation: HORIZONTAL +Ida.resize_popup*rc.rc.?.indicatorType: ONE_OF_MANY + + +! ---------------------------------------------------------------------------- +! edit colors dialog + +Ida.color_popup.deleteResponse: DESTROY + +Ida.color_popup*XmForm*leftOffset: 10 +Ida.color_popup*XmForm*rightOffset: 10 +Ida.color_popup*XmForm*topOffset: 10 +Ida.color_popup*XmForm*bottomOffset: 10 +Ida.color_popup*XmForm*leftAttachment: ATTACH_WIDGET +Ida.color_popup*XmForm*topAttachment: ATTACH_WIDGET +Ida.color_popup*XmForm.sep.rightAttachment: ATTACH_FORM +Ida.color_popup*XmForm.XmRowColumn.rightAttachment: ATTACH_FORM +Ida.color_popup*XmForm.XmRowColumn.orientation: HORIZONTAL +Ida.color_popup*XmText.columns: 5 + +Ida.color_popup*XmDrawingArea.background: white +Ida.color_popup*XmDrawingArea.borderWidth: 1 +Ida.color_popup*XmDrawingArea.borderColor: black + +Ida.color_popup*hred.topWidget: hist +Ida.color_popup*hgreen.topWidget: hred +Ida.color_popup*hblue.topWidget: hgreen + +Ida.color_popup*map.leftWidget: hred +Ida.color_popup*mred.topWidget: hist +Ida.color_popup*mred.leftWidget: hred +Ida.color_popup*mred.rightAttachment: ATTACH_FORM +Ida.color_popup*mgreen.topWidget: mred +Ida.color_popup*mgreen.leftWidget: hgreen +Ida.color_popup*mgreen.rightAttachment: ATTACH_FORM +Ida.color_popup*mblue.topWidget: mgreen +Ida.color_popup*mblue.leftWidget: hblue +Ida.color_popup*mblue.rightAttachment: ATTACH_FORM + +Ida.color_popup*lock.topWidget: hblue +Ida.color_popup*vals.topWidget: lock +Ida.color_popup*in.topWidget: vals +Ida.color_popup*out.topWidget: in +Ida.color_popup*gamma.topWidget: out +Ida.color_popup*pick.topWidget: gamma + + +! ---------------------------------------------------------------------------- +! control + +ctrl*XmMenuShell.XmRowColumn.tearOffModel: TEAR_OFF_ENABLED + +ctrl*tool.orientation: HORIZONTAL +ctrl*tool.XmPushButton.shadowThickness: 1 + +ctrl.toolTipEnable: 1 +ctrl.toolTipPostDelay: 2000 +ctrl.toolTipPostDuration: 5000 +ctrl*TipLabel.foreground: black +ctrl*TipLabel.background: lightyellow +ctrl*TipShell.borderWidth: 1 +ctrl*TipShell.borderColor: black +ctrl*tool.XmSeparator.orientation: VERTICAL +ctrl*tool.XmSeparator.width: 12 +ctrl*tool.XmSeparator.margin: 3 +ctrl*tool.XmPushButton.labelType: PIXMAP +ctrl*tool.prev.labelPixmap: prev +ctrl*tool.next.labelPixmap: next +ctrl*tool.zoomin.labelPixmap: zoomin +ctrl*tool.zoomout.labelPixmap: zoomout +ctrl*tool.flipv.labelPixmap: flipv +ctrl*tool.fliph.labelPixmap: fliph +ctrl*tool.rotccw.labelPixmap: rotccw +ctrl*tool.rotcw.labelPixmap: rotcw +ctrl*tool.exit.labelPixmap: exit + +ctrl.form*list.visibleItemCount: 12 +ctrl.form*list.translations: #override \ + <Key>space: Next() \n\ + <Key>osfDelete: Prev() \n\ + <Key>osfBackSpace: Prev() \n\ + <Key>KP_Add: Zoom(inc) \n\ + <Key>KP_Subtract: Zoom(dec) + +ctrl*bar*load.acceleratorText: Ctrl+L +ctrl*bar*load.accelerator: Ctrl<Key>L +ctrl*bar*save.acceleratorText: Ctrl+S +ctrl*bar*save.accelerator: Ctrl<Key>S +ctrl*bar*browse.acceleratorText: F +ctrl*bar*browse.accelerator: <Key>F +ctrl*bar*filelist.acceleratorText: L +ctrl*bar*filelist.accelerator: <Key>L +ctrl*bar*print.acceleratorText: Ctrl+P +ctrl*bar*print.accelerator: Ctrl<Key>P +ctrl*bar*quit.acceleratorText: Q +ctrl*bar*quit.accelerator: <Key>Q + +ctrl*bar*undo.acceleratorText: U +ctrl*bar*undo.accelerator: <Key>U +ctrl*bar*copy.acceleratorText: Ctrl+C +ctrl*bar*copy.accelerator: Ctrl<Key>C +ctrl*bar*paste.acceleratorText: Ctrl+V +ctrl*bar*paste.accelerator: Ctrl<Key>V +ctrl*bar*flipv.acceleratorText: V +ctrl*bar*flipv.accelerator: <Key>V +ctrl*bar*fliph.acceleratorText: H +ctrl*bar*fliph.accelerator: <Key>H +ctrl*bar*rotcw.acceleratorText: T +ctrl*bar*rotcw.accelerator: ~Meta ~Shift<Key>T +ctrl*bar*rotccw.acceleratorText: Shift+T +ctrl*bar*rotccw.accelerator: Shift<Key>T +ctrl*bar*invert.acceleratorText: I +ctrl*bar*invert.accelerator: <Key>I +ctrl*bar*crop.acceleratorText: C +ctrl*bar*crop.accelerator: <Key>C +ctrl*bar*scale.acceleratorText: S +ctrl*bar*scale.accelerator: ~Ctrl<Key>S +ctrl*bar*rotany.acceleratorText: Alt+T +ctrl*bar*rotany.accelerator: Alt<Key>T + +ctrl*bar*gamma.acceleratorText: G +ctrl*bar*gamma.accelerator: <Key>G +ctrl*bar*color.acceleratorText: E +ctrl*bar*color.accelerator: ~Alt<Key>E +ctrl*bar*blur.acceleratorText: Alt+B +ctrl*bar*blur.accelerator: Alt<Key>B +ctrl*bar*sharpe.acceleratorText: Alt+S +ctrl*bar*sharpe.accelerator: Alt<Key>S +ctrl*bar*edge.acceleratorText: Alt+E +ctrl*bar*edge.accelerator: Alt<Key>E +ctrl*bar*emboss.acceleratorText: Alt+M +ctrl*bar*emboss.accelerator: Alt<Key>m + +ctrl*bar*prev.acceleratorText: backspace +!ctrl*bar*prev.accelerator: <Key>Backspace +ctrl*bar*next.acceleratorText: space +!ctrl*bar*next.accelerator: <Key>space +ctrl*bar*prevpage.acceleratorText: PageUp +!ctrl*bar*prevpage.accelerator: <Key>osfPageUp +ctrl*bar*nextpage.acceleratorText: PageDown +!ctrl*bar*nextpage.accelerator: <Key>osfPageDown +ctrl*bar*zoomin.acceleratorText: plus +ctrl*bar*zoomin.accelerator: <Key>plus +ctrl*bar*zoomout.acceleratorText: minus +ctrl*bar*zoomout.accelerator: <Key>minus + +ctrl*bar*pcdM.1.labelString: 192 x 128 +ctrl*bar*pcdM.2.labelString: 384 x 256 +ctrl*bar*pcdM.3.labelString: 768 x 512 +ctrl*bar*pcdM.4.labelString: 1536 x 1024 +ctrl*bar*pcdM.5.labelString: 3072 x 2048 + +ctrl*bar*man.acceleratorText: F1 +ctrl*bar*man.accelerator: <Key>F1 + +ctrl.form.*.leftAttachment: ATTACH_FORM +ctrl.form.*.rightAttachment: ATTACH_FORM +ctrl.form.tool.topAttachment: ATTACH_WIDGET +ctrl.form.tool.topWidget: bar +ctrl.form.listSW.topAttachment: ATTACH_WIDGET +ctrl.form.listSW.topWidget: tool +ctrl.form.listSW.bottomAttachment: ATTACH_WIDGET +ctrl.form.listSW.bottomWidget: status +ctrl.form.listSW.width: 320 +ctrl.form.listSW.height: 240 +ctrl.form.status.bottomAttachment: ATTACH_FORM +ctrl.form.status.alignment: ALIGNMENT_BEGINNING + + +! ---------------------------------------------------------------------------- +! man page renderer + +Ida.man_popup.deleteResponse: DESTROY +Ida.man_popup*view.width: 500 +Ida.man_popup*view.height: 600 +Ida.man_popup*view.scrollingPolicy: AUTOMATIC +Ida.man_popup*view.scrollBarPlacement: BOTTOM_RIGHT + +Ida.man_popup*label.alignment: ALIGNMENT_BEGINNING +Ida.man_popup*label.marginWidth: 5 +Ida.man_popup*label.marginHeight: 5 +Ida.man_popup*label.renderTable: bold,underline +Ida.man_popup*label.renderTable.fontType: FONT_IS_FONTSET +Ida.man_popup*label.renderTable.fontName: \ + -*-fixed-medium-r-normal--13-*-*-*-*-*-iso8859-*, \ + -*-fixed-medium-r-normal-ja-13-*-*-*-*-*-iso10646-1, \ + -gnu-unifont-medium-r-normal--16-*-*-*-*-*-*-*,* + +Ida.man_popup*label.renderTable.bold.fontType: FONT_IS_FONTSET +Ida.man_popup*label.renderTable.bold.fontName: \ + -*-fixed-bold-r-normal--13-*-*-*-*-*-iso8859-*, \ + -*-fixed-medium-r-normal-ja-13-*-*-*-*-*-iso10646-1, \ + -gnu-unifont-bold-r-normal--16-*-*-*-*-*-*-*,* + +Ida.man_popup*label.renderTable.underline.underlineType: SINGLE_LINE + + +! ---------------------------------------------------------------------------- +! hex viewer + +Ida.hex_popup.deleteResponse: DESTROY +Ida.hex_popup*view.width: 600 +Ida.hex_popup*view.height: 600 +Ida.hex_popup*view.scrollingPolicy: AUTOMATIC +Ida.hex_popup*view.scrollBarPlacement: BOTTOM_RIGHT + +Ida.hex_popup*label.alignment: ALIGNMENT_BEGINNING +Ida.hex_popup*label.marginWidth: 5 +Ida.hex_popup*label.marginHeight: 5 +Ida.hex_popup*label.renderTable: bold,underline +Ida.hex_popup*label.renderTable.fontType: FONT_IS_FONTSET +Ida.hex_popup*label.renderTable.fontName: \ + -*-fixed-medium-r-normal--13-*-*-*-*-*-iso8859-*, \ + -*-fixed-medium-r-normal-ja-13-*-*-*-*-*-iso10646-1, \ + -gnu-unifont-medium-r-normal--16-*-*-*-*-*-*-*,* + +Ida.hex_popup*label.renderTable.bold.fontType: FONT_IS_FONTSET +Ida.hex_popup*label.renderTable.bold.fontName: \ + -*-fixed-bold-r-normal--13-*-*-*-*-*-iso8859-*, \ + -*-fixed-medium-r-normal-ja-13-*-*-*-*-*-iso10646-1, \ + -gnu-unifont-bold-r-normal--16-*-*-*-*-*-*-*,* + +Ida.hex_popup*label.renderTable.underline.underlineType: SINGLE_LINE + + +! ---------------------------------------------------------------------------- +! file browser + +browser.geometry: 600x450 +browser.form.?.leftAttachment: ATTACH_FORM +browser.form.?.rightAttachment: ATTACH_FORM +browser.form.scroll.topAttachment: ATTACH_WIDGET +browser.form.scroll.topWidget: cbar +browser.form.scroll.bottomAttachment: ATTACH_WIDGET +browser.form.scroll.bottomWidget: status +browser.form.status.bottomAttachment: ATTACH_FORM +browser.form.status.alignment: ALIGNMENT_BEGINNING + +browser.form.scroll.scrollingPolicy: AUTOMATIC +browser.form.scroll.scrollBarPlacement: BOTTOM_RIGHT +browser.form.scroll.XmScrollBar.highlightThickness: 1 + +*container.outlineButtonPolicy: OUTLINE_BUTTON_ABSENT +*container.spatialStyle: CELLS +*container.spatialResizeModel: GROW_MINOR +*container.spatialSnapModel: CENTER +!*container.spatialIncludeModel: APPEND +!*container.layoutDirection: LEFT_TO_RIGHT_TOP_TO_BOTTOM +*container.background: gray85 +*container.XmIconGadget.highlightColor: darkred +*container.XmIconGadget.shadowThickness: 1 + +*cbar*close.acceleratorText: Q +*cbar*close.accelerator: <Key>Q +*cbar*copy.acceleratorText: Ctrl+C +*cbar*copy.accelerator: Ctrl<Key>C +*cbar*paste.acceleratorText: Ctrl+V +*cbar*paste.accelerator: Ctrl<Key>V +*cbar*filter.acceleratorText: F +*cbar*filter.accelerator: <Key>F + + +! ---------------------------------------------------------------------------- +! file lists + +filelist.geometry: 300x400 +filelist.form.?.leftAttachment: ATTACH_FORM +filelist.form.?.rightAttachment: ATTACH_FORM +filelist.form.scroll.topAttachment: ATTACH_WIDGET +filelist.form.scroll.topWidget: cbar +filelist.form.scroll.bottomAttachment: ATTACH_WIDGET +filelist.form.scroll.bottomWidget: status +filelist.form.status.bottomAttachment: ATTACH_FORM +filelist.form.status.alignment: ALIGNMENT_BEGINNING + +filelist.form.scroll.scrollingPolicy: AUTOMATIC +filelist.form.scroll.scrollBarPlacement: BOTTOM_RIGHT +filelist.form.scroll.XmScrollBar.highlightThickness: 1 diff --git a/browser.c b/browser.c new file mode 100644 index 0000000..3313d0c --- /dev/null +++ b/browser.c @@ -0,0 +1,571 @@ +/* + * simple file browser + * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> +#include <fnmatch.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> +#include <X11/extensions/XShm.h> +#include <Xm/Xm.h> +#include <Xm/Form.h> +#include <Xm/Label.h> +#include <Xm/RowColumn.h> +#include <Xm/PushB.h> +#include <Xm/CascadeB.h> +#include <Xm/ScrolledW.h> +#include <Xm/SelectioB.h> +#include <Xm/Transfer.h> +#include <Xm/TransferP.h> +#include <Xm/Container.h> +#include <Xm/Separator.h> + +#include "RegEdit.h" +#include "ida.h" +#include "readers.h" +#include "viewer.h" +#include "browser.h" +#include "filter.h" +#include "x11.h" +#include "dither.h" +#include "selections.h" +#include "filebutton.h" +#include "misc.h" +#include "idaconfig.h" +#include "desktop.h" + +/*----------------------------------------------------------------------*/ + +struct browser_handle; + +struct browser_handle { + char *dirname; + char *lastdir; + char *filter; + Widget shell; + Widget scroll; + Widget container; + Widget status; + XmString details[DETAIL_COUNT+1]; + + struct list_head files; + struct list_head *item; + unsigned int dirs,sfiles,afiles; + + XtWorkProcId wproc; +}; + +/*----------------------------------------------------------------------*/ + +static void dir_info(struct file_button *file) +{ + char path[256]; + char comment[256]; + int len; + + snprintf(path,sizeof(path),"%s/.directory",file->filename); + len = desktop_read_entry(path, "Comment=", comment, sizeof(comment)); + + if (len) { + XmStringFree(file->details[DETAIL_COMMENT]); + file->details[DETAIL_COMMENT] = + XmStringGenerate(comment, NULL, XmMULTIBYTE_TEXT,NULL); + XtVaSetValues(file->widget, + XmNdetail, file->details, + XmNdetailCount, DETAIL_COUNT, + NULL); + } +} + +static Boolean +browser_statfiles(XtPointer clientdata) +{ + struct browser_handle *h = clientdata; + struct file_button *file; + struct list_head *item; + char line[80]; + XmString str; + Pixmap pix; + char *type; + + if (h->item == &h->files) { + /* done => read thumbnails now */ + h->wproc = 0; + list_for_each(item,&h->files) { + file = list_entry(item, struct file_button, window); + if ((file->st.st_mode & S_IFMT) != S_IFREG) + continue; + fileinfo_queue(file); + } + + list_for_each(item,&h->files) { + file = list_entry(item, struct file_button, window); + if ((file->st.st_mode & S_IFMT) != S_IFDIR) + continue; + dir_info(file); + } + + /* update status line */ + if (h->filter) { + snprintf(line, sizeof(line), "%d dirs, %d/%d files [%s]", + h->dirs,h->sfiles,h->afiles,h->filter); + } else { + snprintf(line, sizeof(line), "%d dirs, %d files", + h->dirs,h->afiles); + } + str = XmStringGenerate(line, NULL, XmMULTIBYTE_TEXT, NULL); + XtVaSetValues(h->status,XmNlabelString,str,NULL); + XmStringFree(str); + h->item = NULL; + return TRUE; + } + + /* handle file */ + file = list_entry(h->item, struct file_button, window); + switch (file->st.st_mode & S_IFMT) { + case S_IFDIR: + type = "dir"; + break; + case S_IFREG: + type = "file"; + break; + default: + type = NULL; + } + if (type) { + pix = XmGetPixmap(XtScreen(h->container),type,0,0); + file_set_icon(file,pix,pix); + } + + h->item = h->item->next; + return FALSE; +} + +static void list_add_sorted(struct browser_handle *h, struct file_button *add) +{ + struct file_button *file; + struct list_head *item; + + list_for_each(item,&h->files) { + file = list_entry(item, struct file_button, window); + if (file_cmp_alpha(add,file) <= 0) { + list_add_tail(&add->window,&file->window); + return; + } + } + list_add_tail(&add->window,&h->files); +} + +static Boolean browser_readdir(XtPointer clientdata) +{ + struct browser_handle *h = clientdata; + struct file_button *file; + struct list_head *item; + struct dirent *dirent; + WidgetList children; + struct file_button *lastdir = NULL; + Cardinal nchildren; + XmString str,elem; + DIR *dir; + unsigned int len; + + /* status line */ + str = XmStringGenerate("scanning ", NULL, XmMULTIBYTE_TEXT, NULL); + elem = XmStringGenerate(h->dirname, NULL, XmMULTIBYTE_TEXT, NULL); + str = XmStringConcatAndFree(str,elem); + elem = XmStringGenerate(" ...", NULL, XmMULTIBYTE_TEXT, NULL); + str = XmStringConcatAndFree(str,elem); + XtVaSetValues(h->status,XmNlabelString,str,NULL); + XmStringFree(str); + ptr_busy(); + + /* read + sort dir */ + dir = opendir(h->dirname); + if (NULL == dir) { + fprintf(stderr,"opendir %s: %s\n",h->dirname,strerror(errno)); + return -1; + } + h->dirs = 0; + h->sfiles = 0; + h->afiles = 0; + while (NULL != (dirent = readdir(dir))) { + /* skip dotfiles */ + if (dirent->d_name[0] == '.' && 0 != strcmp(dirent->d_name,"..")) + continue; + + /* get memory */ + file = malloc(sizeof(*file)); + memset(file,0,sizeof(*file)); + + /* get file type */ + file->basename = strdup(dirent->d_name); + file->d_type = dirent->d_type; + if (0 == strcmp(dirent->d_name, "..")) { + char *slash; + file->filename = strdup(h->dirname); + slash = strrchr(file->filename,'/'); + if (slash == file->filename) + slash++; + *slash = 0; + } else { + len = strlen(h->dirname)+strlen(file->basename)+4; + file->filename = malloc(len); + if (0 == strcmp(h->dirname,"/")) { + sprintf(file->filename,"/%s",file->basename); + } else { + sprintf(file->filename,"%s/%s",h->dirname, + file->basename); + } + } + if (file->d_type != DT_UNKNOWN) { + file->st.st_mode = DTTOIF(file->d_type); + } else { + if (-1 == stat(file->filename, &file->st)) { + fprintf(stderr,"stat %s: %s\n", + file->filename,strerror(errno)); + } + } + + /* user-specified filter */ + if (S_ISDIR(file->st.st_mode)) { + h->dirs++; + if (h->lastdir && 0 == strcmp(h->lastdir,file->filename)) { + lastdir = file; + } + } else { + h->afiles++; + if (h->filter && 0 != fnmatch(h->filter,dirent->d_name,0)) { + free(file); + continue; + } else + h->sfiles++; + } + + list_add_sorted(h,file); + } + closedir(dir); + + /* create & manage widgets */ + list_for_each(item,&h->files) { + file = list_entry(item, struct file_button, window); + file_createwidgets(h->container, file); + } + nchildren = XmContainerGetItemChildren(h->container,NULL,&children); + XtManageChildren(children,nchildren); + if (nchildren) + XtFree((XtPointer)children); + container_relayout(h->container); + if (h->lastdir) { + if (lastdir) { + if (debug) + fprintf(stderr,"lastdir: %s\n",h->lastdir); + XtVaSetValues(h->container, + XmNinitialFocus, lastdir->widget, + NULL); +// XmScrollVisible(h->scroll, lastdir->widget, 25, 25); +// XtSetKeyboardFocus(h->shell,h->container); + } + free(h->lastdir); + h->lastdir = NULL; + } + XtVaSetValues(h->shell,XtNtitle,h->dirname,NULL); + ptr_idle(); + + /* start bg processing */ + h->item = h->files.next; + h->wproc = XtAppAddWorkProc(app_context,browser_statfiles,h); + return TRUE; +} + +static void browser_bgcancel(struct browser_handle *h) +{ + if (h->wproc) + XtRemoveWorkProc(h->wproc); + h->wproc = 0; +} + +/*----------------------------------------------------------------------*/ + +static void +browser_cd(struct browser_handle *h, char *dir) +{ + /* build new dir path */ + if (h->lastdir) + free(h->lastdir); + h->lastdir = h->dirname; + h->dirname = strdup(dir); + + /* cleanup old stuff + read dir */ + browser_bgcancel(h); + container_delwidgets(h->container); + h->wproc = XtAppAddWorkProc(app_context,browser_readdir,h); +} + +static void +browser_filter_done(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct browser_handle *h = clientdata; + XmSelectionBoxCallbackStruct *cb = call_data; + char *filter; + + if (cb->reason == XmCR_OK) { + filter = XmStringUnparse(cb->value,NULL, + XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT, + NULL,0,0); + if (h->filter) + free(h->filter); + h->filter = NULL; + if (strlen(filter) > 0) + h->filter = strdup(filter); + XtFree(filter); + + if (debug) + fprintf(stderr,"filter: %s\n", h->filter ? h->filter : "[none]"); + browser_bgcancel(h); + container_delwidgets(h->container); + h->wproc = XtAppAddWorkProc(app_context,browser_readdir,h); + } + XtDestroyWidget(widget); +} + +static void +browser_filter(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct browser_handle *h = clientdata; + Widget shell; + + shell = XmCreatePromptDialog(h->shell,"filter",NULL,0); + XtUnmanageChild(XmSelectionBoxGetChild(shell,XmDIALOG_HELP_BUTTON)); + XtAddCallback(shell,XmNokCallback,browser_filter_done,h); + XtAddCallback(shell,XmNcancelCallback,browser_filter_done,h); + XtManageChild(shell); +} + +static void +browser_nofilter(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct browser_handle *h = clientdata; + + if (!h->filter) + return; + if (debug) + fprintf(stderr,"filter: reset\n"); + free(h->filter); + h->filter = NULL; + + browser_bgcancel(h); + container_delwidgets(h->container); + h->wproc = XtAppAddWorkProc(app_context,browser_readdir,h); +} + +/*----------------------------------------------------------------------*/ + +static void +browser_action_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XmContainerSelectCallbackStruct *cd = call_data; + struct browser_handle *h = clientdata; + struct stat st; + char *file; + + if (XmCR_DEFAULT_ACTION == cd->reason && 1 == cd->selected_item_count) { + file = XtName(cd->selected_items[0]); + if (debug) + fprintf(stderr,"browser: action %s\n", file); + if (-1 == stat(file,&st)) { + fprintf(stderr,"stat %s: %s\n",file,strerror(errno)); + return; + } + if (S_ISDIR(st.st_mode)) + browser_cd(h,file); + if (S_ISREG(st.st_mode)) + new_file(file,1); + } +} + +static void +browser_bookmark_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct browser_handle *h = clientdata; + browser_cd(h, cfg_get_str(O_BOOKMARKS, XtName(widget))); +} + +/*----------------------------------------------------------------------*/ + +static void +browser_destroy(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct browser_handle *h = clientdata; + + if (debug) + fprintf(stderr,"browser: destroy\n"); + browser_bgcancel(h); + ptr_unregister(h->shell); + free(h); +} + +static char* +browser_fixpath(char *dir) +{ + char path[1024]; + char *s,*d; + + memset(path,0,sizeof(path)); + if (dir[0] == '/') { + /* absolute */ + strncpy(path,dir,sizeof(path)-1); + } else { + /* relative */ + getcwd(path,sizeof(path)-1); + if (strlen(path)+strlen(dir)+4 < sizeof(path)) { + strcat(path,"/"); + strcat(path,dir); + } + } + + for (s = d = path; *s != 0;) { + if (0 == strncmp(s,"//",2)) { + s++; + continue; + } + if (0 == strncmp(s,"/./",3)) { + s+=2; + continue; + } + if (0 == strcmp(s,"/")) + s++; + if (0 == strcmp(s,"/.")) + s+=2; + *d = *s; + s++, d++; + } + return strdup(path); +} + +void +browser_window(char *dirname) +{ + Widget form,menubar,menu,push,clip; + struct browser_handle *h; + Arg args[8]; + char *list; + int n = 0; + + h = malloc(sizeof(*h)); + if (NULL == h) { + fprintf(stderr,"out of memory"); + return; + } + memset(h,0,sizeof(*h)); + INIT_LIST_HEAD(&h->files); + h->dirname = browser_fixpath(dirname); + + h->shell = XtVaAppCreateShell("browser","Ida", + topLevelShellWidgetClass, + dpy, + XtNclientLeader,app_shell, + XmNdeleteResponse,XmDESTROY, + NULL); + XmdRegisterEditres(h->shell); + XtAddCallback(h->shell,XtNdestroyCallback,browser_destroy,h); + + /* widgets */ + form = XtVaCreateManagedWidget("form", xmFormWidgetClass, h->shell, + NULL); + menubar = XmCreateMenuBar(form,"cbar",NULL,0); + XtManageChild(menubar); + h->status = XtVaCreateManagedWidget("status",xmLabelWidgetClass, form, + NULL); + + /* scrolled container */ + h->details[0] = XmStringGenerate("Image", NULL, XmMULTIBYTE_TEXT,NULL); + h->details[DETAIL_SIZE+1] = + XmStringGenerate("Size", NULL, XmMULTIBYTE_TEXT,NULL); + h->details[DETAIL_COMMENT+1] = + XmStringGenerate("Comment", NULL, XmMULTIBYTE_TEXT,NULL); + XtSetArg(args[n], XmNdetailColumnHeading, h->details); n++; + XtSetArg(args[n], XmNdetailColumnHeadingCount, DETAIL_COUNT+1); n++; + + h->scroll = XmCreateScrolledWindow(form, "scroll", NULL, 0); + XtManageChild(h->scroll); + h->container = XmCreateContainer(h->scroll,"container", + args,n); + XtManageChild(h->container); + XtAddCallback(h->container,XmNdefaultActionCallback, + browser_action_cb,h); + + XtAddCallback(h->scroll, XmNtraverseObscuredCallback, + container_traverse_cb, NULL); + XtAddCallback(h->container,XmNconvertCallback, + container_convert_cb,h); + + XtVaGetValues(h->scroll,XmNclipWindow,&clip,NULL); + XtAddEventHandler(clip,StructureNotifyMask,True,container_resize_eh,NULL); + + /* menu - file */ + menu = XmCreatePulldownMenu(menubar,"fileM",NULL,0); + XtVaCreateManagedWidget("file",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + push = XtVaCreateManagedWidget("close",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,destroy_cb,h->shell); + + /* menu - edit */ + menu = XmCreatePulldownMenu(menubar,"editM",NULL,0); + XtVaCreateManagedWidget("edit",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + container_menu_edit(menu,h->container, 0,1,0,0); + + /* menu - view */ + menu = XmCreatePulldownMenu(menubar,"viewM",NULL,0); + XtVaCreateManagedWidget("view",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + container_menu_view(menu,h->container); + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); + push = XtVaCreateManagedWidget("filter",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,browser_filter,h); + push = XtVaCreateManagedWidget("freset",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,browser_nofilter,h); + + /* menu - ops */ + menu = XmCreatePulldownMenu(menubar,"opsM",NULL,0); + XtVaCreateManagedWidget("ops",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + container_menu_ops(menu,h->container); + + /* menu - dirs (bookmarks) */ + menu = XmCreatePulldownMenu(menubar,"dirsM",NULL,0); + XtVaCreateManagedWidget("dirs",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + for (list = cfg_entries_first(O_BOOKMARKS); + list != NULL; + list = cfg_entries_next(O_BOOKMARKS,list)) { + push = XtVaCreateManagedWidget(list,xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,browser_bookmark_cb,h); + } + + /* read dir and show window */ + container_spatial_cb(NULL,h->container,NULL); + browser_readdir(h); + XtPopup(h->shell,XtGrabNone); + ptr_register(h->shell); +} + +void +browser_ac(Widget widget, XEvent *event, + String *params, Cardinal *num_params) +{ + if (*num_params > 0) + browser_window(params[0]); + else + browser_window("."); +} diff --git a/browser.h b/browser.h new file mode 100644 index 0000000..62979cf --- /dev/null +++ b/browser.h @@ -0,0 +1,3 @@ +void browser_window(char *dirname); +void browser_ac(Widget widget, XEvent *event, + String *params, Cardinal *num_params); @@ -0,0 +1,513 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <math.h> + +#include <X11/X.h> +#include <X11/Intrinsic.h> +#include <Xm/Xm.h> +#include <Xm/Form.h> +#include <Xm/Label.h> +#include <Xm/DrawingA.h> +#include <Xm/RowColumn.h> +#include <Xm/PushB.h> +#include <Xm/ToggleB.h> +#include <Xm/Scale.h> +#include <Xm/Separator.h> +#include <Xm/Text.h> +#include <Xm/SelectioB.h> + +#include "RegEdit.h" +#include "ida.h" +#include "x11.h" +#include "readers.h" +#include "viewer.h" +#include "color.h" +#include "lut.h" + +/* ---------------------------------------------------------------------- */ + +#define HIST_SIZE 60 + +struct ida_coledit; + +struct ida_hist { + /* x11 */ + GC gc; + unsigned long color; + + /* histogram */ + Widget hist; + unsigned int max; + unsigned int data[256]; + + /* mapping */ + Widget map; + struct op_map_parm_ch parm; + + struct ida_coledit *up; +}; + +struct ida_coledit { + /* misc */ + Widget dlg,form,vals,toggle; + Widget l,r,t,b,g; + int lock,apply; + + /* histogram data */ + struct ida_hist red; + struct ida_hist green; + struct ida_hist blue; + struct ida_hist *cur; +}; + +/* ---------------------------------------------------------------------- */ + +static void +color_calchist(struct ida_image *img, struct ida_coledit *me) +{ + unsigned char *pix; + unsigned int i,x,y,max; + + pix = img->data; + for (y = 0; y < img->i.height; y++) { + for (x = 0; x < img->i.width; x++) { + me->red.data[pix[0]]++; + me->green.data[pix[1]]++; + me->blue.data[pix[2]]++; + pix += 3; + } + } + max = 0; + for (i = 0; i < 256; i++) { + if (max < me->red.data[i]) + max = me->red.data[i]; + if (max < me->green.data[i]) + max = me->green.data[i]; + if (max < me->blue.data[i]) + max = me->blue.data[i]; + } + me->red.max = max; + me->green.max = max; + me->blue.max = max; +} + +static void +color_update(struct ida_coledit *me, struct ida_hist *h, int text) +{ + struct op_map_parm param; + char tmp[32]; + + if (me->lock) { + if (&me->red != h) + me->red.parm = h->parm; + if (&me->green != h) + me->green.parm = h->parm; + if (&me->blue != h) + me->blue.parm = h->parm; + XClearArea(XtDisplay(me->red.hist), XtWindow(me->red.hist), + 0,0,0,0, True); + XClearArea(XtDisplay(me->red.map), XtWindow(me->red.map), + 0,0,0,0, True); + XClearArea(XtDisplay(me->green.hist), XtWindow(me->green.hist), + 0,0,0,0, True); + XClearArea(XtDisplay(me->green.map), XtWindow(me->green.map), + 0,0,0,0, True); + XClearArea(XtDisplay(me->blue.hist), XtWindow(me->blue.hist), + 0,0,0,0, True); + XClearArea(XtDisplay(me->blue.map), XtWindow(me->blue.map), + 0,0,0,0, True); + } else { + XClearArea(XtDisplay(h->hist), XtWindow(h->hist), + 0,0,0,0, True); + XClearArea(XtDisplay(h->map), XtWindow(h->map), + 0,0,0,0, True); + } + if ((me->lock || h == me->cur) && text >= 1) { + /* mouse-click updateable values */ + sprintf(tmp,"%d",h->parm.left); + XmTextSetString(me->l,tmp); + sprintf(tmp,"%d",h->parm.right); + XmTextSetString(me->r,tmp); + } + if ((me->lock || h == me->cur) && text >= 2) { + /* others */ + sprintf(tmp,"%d",h->parm.bottom); + XmTextSetString(me->b,tmp); + sprintf(tmp,"%d",h->parm.top); + XmTextSetString(me->t,tmp); + sprintf(tmp,"%.2f",h->parm.gamma); + XmTextSetString(me->g,tmp); + } + + param.red = me->red.parm; + param.green = me->green.parm; + param.blue = me->blue.parm; + viewer_start_preview(ida,&desc_map,¶m); +} + +static void +color_drawmap(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_hist *me = client_data; + XmDrawingAreaCallbackStruct *cb = calldata; + XGCValues values; + int left,right,top,bottom,i,val,x1,y1,x2,y2; + float p; + + if (cb->reason == XmCR_EXPOSE) { + /* window needs redraw */ + XExposeEvent *e = (XExposeEvent*)cb->event; + if (e->count) + return; + values.foreground = x11_gray; + XChangeGC(dpy,me->gc,GCForeground,&values); + left = me->parm.left * HIST_SIZE / 255; + right = me->parm.right * HIST_SIZE / 255; + bottom = me->parm.bottom * HIST_SIZE / 255; + top = me->parm.top * HIST_SIZE / 255; + if (me->parm.left > 0) + XFillRectangle(dpy,XtWindow(me->map),me->gc, + 0,0,left,HIST_SIZE); + if (me->parm.right < 255) + XFillRectangle(dpy,XtWindow(me->map),me->gc, + right,0,HIST_SIZE-right,HIST_SIZE); + values.foreground = me->color; + XChangeGC(dpy,me->gc,GCForeground,&values); + if (me->parm.left > 0) + XDrawLine(dpy,XtWindow(me->map),me->gc, + 0,HIST_SIZE-bottom,left,HIST_SIZE-bottom); + if (me->parm.right < 255) + XDrawLine(dpy,XtWindow(me->map),me->gc, + right,HIST_SIZE-top,HIST_SIZE,HIST_SIZE-top); + p = 1/me->parm.gamma; + x2 = y2 = 0; + for (i = left; i <= right; i++) { + val = pow((float)(i-left)/(right-left),p) * (top-bottom) + 0.5; + val += bottom; + if (val < 0) val = 0; + if (val > HIST_SIZE) val = HIST_SIZE; + x1 = x2; + y1 = y2; + x2 = i; + y2 = HIST_SIZE-val; + if (i > left) + XDrawLine(dpy,XtWindow(me->map),me->gc, + x1,y1,x2,y2); + } + } +} + +static void +color_drawhist(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_hist *me = client_data; + XmDrawingAreaCallbackStruct *cb = calldata; + XGCValues values; + int i,val; + + if (cb->reason == XmCR_EXPOSE) { + /* window needs redraw */ + XExposeEvent *e = (XExposeEvent*)cb->event; + if (e->count) + return; + values.foreground = x11_gray; + XChangeGC(dpy,me->gc,GCForeground,&values); + if (me->parm.left > 0) + XFillRectangle(dpy,XtWindow(me->hist),me->gc, + 0,0,me->parm.left,HIST_SIZE); + if (me->parm.right < 255) + XFillRectangle(dpy,XtWindow(me->hist),me->gc, + me->parm.right,0,256-me->parm.right,HIST_SIZE); + values.foreground = me->color; + XChangeGC(dpy,me->gc,GCForeground,&values); + for (i = 0; i < 256; i++) { + val = log(me->data[i])*HIST_SIZE/log(me->max); + XDrawLine(dpy,XtWindow(me->hist),me->gc, + i,HIST_SIZE,i,HIST_SIZE-val); + } + } +} + +static void +color_mouse(Widget widget, XtPointer client_data, + XEvent *ev, Boolean *cont) +{ + struct ida_hist *me = client_data; + int x,y; + + switch (ev->type) { + case ButtonPress: + case ButtonRelease: + { + XButtonEvent *e = (XButtonEvent*)ev; + + x = e->x; + y = e->y; + break; + } + case MotionNotify: + { + XMotionEvent *e = (XMotionEvent*)ev; + + x = e->x; + y = e->y; + break; + default: + return; + } + } + if (x > (me->parm.right + me->parm.left)/2) { + me->parm.right = x; + if (me->parm.right > 255) + me->parm.right = 255; + if (me->parm.right < me->parm.left) + me->parm.right = me->parm.left; + } else { + me->parm.left = x; + if (me->parm.left < 0) + me->parm.left = 0; + if (me->parm.left > me->parm.right) + me->parm.left = me->parm.right; + } + color_update(me->up,me,1); +} + +static void +color_lock(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_coledit *me = client_data; + XmToggleButtonCallbackStruct *cb = calldata; + Widget label,button; + + label = XmOptionLabelGadget(me->vals); + button = XmOptionButtonGadget(me->vals); + me->lock = cb->set; + XtVaSetValues(label,XtNsensitive,!me->lock,NULL); + XtVaSetValues(button,XtNsensitive,!me->lock,NULL); +} + +static void +color_vals(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_hist *cur = client_data; + struct ida_coledit *me = cur->up; + + me->cur = cur; + color_update(me,cur,2); +} + +static void +color_text(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_coledit *me = client_data; + int left,right,bottom,top; + float gamma; + + if (widget == me->l && + 1 == sscanf(XmTextGetString(me->l),"%d",&left) && + left >= 0 && left <= me->cur->parm.right) { + me->cur->parm.left = left; + } + if (widget == me->r && + 1 == sscanf(XmTextGetString(me->r),"%d",&right) && + me->cur->parm.left <= right && right <= 255) { + me->cur->parm.right = right; + } + if (widget == me->b && + 1 == sscanf(XmTextGetString(me->b),"%d",&bottom) && + bottom <= me->cur->parm.top) { + me->cur->parm.bottom = bottom; + } + if (widget == me->t && + 1 == sscanf(XmTextGetString(me->t),"%d",&top) && + me->cur->parm.bottom <= top) { + me->cur->parm.top = top; + } + if (widget == me->g && + 1 == sscanf(XmTextGetString(me->g),"%f",&gamma)) { + me->cur->parm.gamma = gamma; + } + color_update(me,me->cur,0); +} + +static void +color_pick_ok(int x, int y, unsigned char *pix, XtPointer data) +{ + struct ida_coledit *me = data; + int max; + + if (debug) + fprintf(stderr,"color_pick_ok: +%d+%d %d/%d/%d\n", + x,y, pix[0],pix[1],pix[2]); + + max = 0; + if (max < pix[0]) + max = pix[0]; + if (max < pix[1]) + max = pix[1]; + if (max < pix[2]) + max = pix[2]; + + XmToggleButtonSetState(me->toggle,False,True); + me->red.parm.right = (int)255 * pix[0] / max; + color_update(me,&me->red,1); + me->green.parm.right = (int)255 * pix[1] / max; + color_update(me,&me->green,1); + me->blue.parm.right = (int)255 * pix[2] / max; + color_update(me,&me->blue,1); + + if (debug) + fprintf(stderr,"color_pick_ok: %d/%d/%d max=%d\n", + me->red.parm.right, + me->green.parm.right, + me->blue.parm.right, + max); +} + +static void +color_pick(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_coledit *me = client_data; + viewer_pick(ida,color_pick_ok,me); +} + +static void +color_createhist(Widget parent, char *name, unsigned long color, + struct ida_hist *me) +{ + char tmp[32]; + + sprintf(tmp,"h%s",name); + me->hist = XtVaCreateManagedWidget(tmp,xmDrawingAreaWidgetClass,parent, + XtNwidth,256, + XtNheight,HIST_SIZE, + NULL); + sprintf(tmp,"m%s",name); + me->map = XtVaCreateManagedWidget(tmp,xmDrawingAreaWidgetClass,parent, + XtNwidth,HIST_SIZE, + XtNheight,HIST_SIZE, + NULL); + XtAddEventHandler(me->hist, + ButtonPressMask | + ButtonReleaseMask | + ButtonMotionMask, + False,color_mouse,me); + XtAddCallback(me->hist,XmNexposeCallback,color_drawhist,me); + XtAddCallback(me->map,XmNexposeCallback,color_drawmap,me); + me->gc = XCreateGC(dpy,XtWindow(app_shell),0,NULL); + me->color = color; + me->parm = op_map_nothing; +} + +static void +color_button_cb(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_coledit *me = client_data; + XmSelectionBoxCallbackStruct *cb = calldata; + + if (cb->reason == XmCR_OK) + me->apply = 1; + XtDestroyWidget(XtParent(me->form)); +} + +static void +color_destroy(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_coledit *me = client_data; + struct op_map_parm param; + + if (me->apply) { + param.red = me->red.parm; + param.green = me->green.parm; + param.blue = me->blue.parm; + viewer_start_op(ida,&desc_map,¶m); + } else + viewer_cancel_preview(ida); + viewer_unpick(ida); + free(me); +} + +void +color_init(struct ida_image *img) +{ + Widget menu,push,rc; + struct ida_coledit *me; + Arg args[2]; + + me = malloc(sizeof(*me)); + memset(me,0,sizeof(*me)); + color_calchist(img,me); + + /* dialog shell */ + me->dlg = XmCreatePromptDialog(app_shell,"color",NULL,0); + XmdRegisterEditres(XtParent(me->dlg)); + XtUnmanageChild(XmSelectionBoxGetChild(me->dlg,XmDIALOG_SELECTION_LABEL)); + XtUnmanageChild(XmSelectionBoxGetChild(me->dlg,XmDIALOG_HELP_BUTTON)); + XtUnmanageChild(XmSelectionBoxGetChild(me->dlg,XmDIALOG_TEXT)); + me->form = XtVaCreateManagedWidget("form",xmFormWidgetClass, + me->dlg,NULL); + XtAddCallback(XtParent(me->dlg),XmNdestroyCallback,color_destroy,me); + XtAddCallback(me->dlg,XmNokCallback,color_button_cb,me); + XtAddCallback(me->dlg,XmNcancelCallback,color_button_cb,me); + + /* histograms */ + XtVaCreateManagedWidget("hist",xmLabelWidgetClass, + me->form,NULL); + color_createhist(me->form,"red", x11_red, &me->red); + color_createhist(me->form,"green",x11_green,&me->green); + color_createhist(me->form,"blue", x11_blue, &me->blue); + me->red.up = me; + me->green.up = me; + me->blue.up = me; + XtVaCreateManagedWidget("map",xmLabelWidgetClass, + me->form,NULL); + + /* control */ + me->toggle = XtVaCreateManagedWidget("lock",xmToggleButtonWidgetClass, + me->form,NULL); + XtAddCallback(me->toggle,XmNvalueChangedCallback,color_lock,me); + menu = XmCreatePulldownMenu(me->form,"valsM",NULL,0); + XtSetArg(args[0],XmNsubMenuId,menu); + me->vals = XmCreateOptionMenu(me->form,"vals",args,1); + XtManageChild(me->vals); + push = XtVaCreateManagedWidget("red",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,color_vals,&me->red); + push = XtVaCreateManagedWidget("green",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,color_vals,&me->green); + push = XtVaCreateManagedWidget("blue",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,color_vals,&me->blue); + + /* in range */ + rc = XtVaCreateManagedWidget("in",xmRowColumnWidgetClass,me->form,NULL); + XtVaCreateManagedWidget("label",xmLabelWidgetClass,rc,NULL); + me->l = XtVaCreateManagedWidget("left",xmTextWidgetClass,rc,NULL); + XtAddCallback(me->l,XmNvalueChangedCallback,color_text,me); + me->r = XtVaCreateManagedWidget("right",xmTextWidgetClass,rc,NULL); + XtAddCallback(me->r,XmNvalueChangedCallback,color_text,me); + + /* out range */ + rc = XtVaCreateManagedWidget("out",xmRowColumnWidgetClass,me->form,NULL); + XtVaCreateManagedWidget("label",xmLabelWidgetClass,rc,NULL); + me->b = XtVaCreateManagedWidget("bottom",xmTextWidgetClass,rc,NULL); + XtAddCallback(me->b,XmNvalueChangedCallback,color_text,me); + me->t = XtVaCreateManagedWidget("top",xmTextWidgetClass,rc,NULL); + XtAddCallback(me->t,XmNvalueChangedCallback,color_text,me); + + /* gamma */ + rc = XtVaCreateManagedWidget("gamma",xmRowColumnWidgetClass,me->form,NULL); + XtVaCreateManagedWidget("label",xmLabelWidgetClass,rc,NULL); + me->g = XtVaCreateManagedWidget("gamma",xmTextWidgetClass,rc,NULL); + XtAddCallback(me->g,XmNvalueChangedCallback,color_text,me); + + /* testing stuff */ + rc = XtVaCreateManagedWidget("pick",xmRowColumnWidgetClass,me->form,NULL); + push = XtVaCreateManagedWidget("white",xmPushButtonWidgetClass,rc,NULL); + XtAddCallback(push,XmNactivateCallback,color_pick,me); + + XtManageChild(me->dlg); + + me->cur = &me->red; + color_update(me,me->cur,2); + XmToggleButtonSetState(me->toggle,True,True); +} @@ -0,0 +1 @@ +void color_init(struct ida_image *img); diff --git a/config.h b/config.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/config.h @@ -0,0 +1,348 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <errno.h> +#include <sys/select.h> + +#include <curl/curl.h> +#include <curl/easy.h> + +#include "curl.h" + +/* curl globals */ +static CURLM *curlm; +static fd_set rd, wr, ex; + +/* my globals */ +static int url_debug = 0; +static int url_timeout = 30; + +/* my structs */ +struct iobuf { + off_t start; + size_t size; + char *data; +}; + +struct url_state { + char *path; + CURL *curl; + char errmsg[CURL_ERROR_SIZE]; + off_t curl_pos; + off_t buf_pos; + struct iobuf buf; + int eof; +}; + +/* ---------------------------------------------------------------------- */ +/* curl stuff */ + +static void __attribute__ ((constructor)) curl_init(void) +{ + curl_global_init(CURL_GLOBAL_ALL); + curlm = curl_multi_init(); +} + +static void __attribute__ ((destructor)) curl_fini(void) +{ + curl_multi_cleanup(curlm); + curl_global_cleanup(); +} + +static void curl_free_buffer(struct iobuf *buf) +{ + if (buf->data) { + free(buf->data); + memset(buf,0,sizeof(*buf)); + } +} + +/* CURLOPT_WRITEFUNCTION */ +static int curl_write(void *data, size_t size, size_t nmemb, void *handle) +{ + struct url_state *h = handle; + + curl_free_buffer(&h->buf); + h->buf.start = h->curl_pos; + h->buf.size = size * nmemb; + h->buf.data = malloc(h->buf.size); + memcpy(h->buf.data, data, h->buf.size); + if (url_debug) + fprintf(stderr," put %5d @ %5d\n", + (int)h->buf.size, (int)h->buf.start); + + h->curl_pos += h->buf.size; + return h->buf.size; +} + +/* do transfers */ +static int curl_xfer(struct url_state *h) +{ + CURLMcode rc; + struct timeval tv; + int count, maxfd; + + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + maxfd = -1; + rc = curl_multi_fdset(curlm, &rd, &wr, &ex, &maxfd); + if (CURLM_OK != rc) { + fprintf(stderr,"curl_multi_fdset: %d %s\n",rc,h->errmsg); + return -1; + } + if (-1 == maxfd) { + /* wait 0.1 sec */ + if (url_debug) + fprintf(stderr,"wait 0.01 sec\n"); + tv.tv_sec = 0; + tv.tv_usec = 100000; + } else { + /* wait for data */ + if (url_debug) + fprintf(stderr,"select for data [maxfd=%d]\n",maxfd); + tv.tv_sec = url_timeout; + tv.tv_usec = 0; + } + switch (select(maxfd+1, &rd, &wr, &ex, &tv)) { + case -1: + /* Huh? */ + perror("select"); + exit(1); + case 0: + /* timeout */ + return -1; + } + for (;;) { + rc = curl_multi_perform(curlm,&count); + if (CURLM_CALL_MULTI_PERFORM == rc) + continue; + if (CURLM_OK != rc) { + fprintf(stderr,"curl_multi_perform: %d %s\n",rc,h->errmsg); + return -1; + } + if (0 == count) + h->eof = 1; + break; + } + return 0; +} + +/* curl setup */ +static int curl_setup(struct url_state *h) +{ + if (h->curl) { + curl_multi_remove_handle(curlm,h->curl); + curl_easy_cleanup(h->curl); + } + + h->curl = curl_easy_init(); + curl_easy_setopt(h->curl, CURLOPT_URL, h->path); + curl_easy_setopt(h->curl, CURLOPT_ERRORBUFFER, h->errmsg); + curl_easy_setopt(h->curl, CURLOPT_WRITEFUNCTION, curl_write); + curl_easy_setopt(h->curl, CURLOPT_WRITEDATA, h); + curl_multi_add_handle(curlm, h->curl); + + h->buf_pos = 0; + h->curl_pos = 0; + h->eof = 0; + return 0; +} + +/* ---------------------------------------------------------------------- */ +/* GNU glibc custom stream interface */ + +static ssize_t url_read(void *handle, char *buf, size_t size) +{ + struct url_state *h = handle; + size_t bytes, total; + off_t off; + int count; + + if (url_debug) + fprintf(stderr,"url_read(size=%d)\n",(int)size); + for (total = 0; size > 0;) { + if (h->buf.start <= h->buf_pos && + h->buf.start + h->buf.size > h->buf_pos) { + /* can satisfy from current buffer */ + bytes = h->buf.start + h->buf.size - h->buf_pos; + off = h->buf_pos - h->buf.start; + if (bytes > size) + bytes = size; + memcpy(buf+total, h->buf.data + off, bytes); + if (url_debug) + fprintf(stderr," get %5d @ %5d [%5d]\n", + (int)bytes, (int)h->buf_pos, (int)off); + size -= bytes; + total += bytes; + h->buf_pos += bytes; + continue; + } + if (h->buf_pos < h->buf.start) { + /* seeking backwards -- restart transfer */ + if (url_debug) + fprintf(stderr," rewind\n"); + curl_free_buffer(&h->buf); + curl_setup(h); + while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curlm,&count)) + /* nothing */; + } + if (h->eof) + /* stop on eof */ + break; + /* fetch more data */ + if (-1 == curl_xfer(h)) { + if (0 == total) + return -1; + break; + } + } + return total; +} + +#if 0 +static ssize_t url_write(void *handle, const char *buf, size_t size) +{ + //struct url_state *h = handle; + + if (url_debug) + fprintf(stderr,"url_write(size=%d)\n",(int)size); + return -1; +} +#endif + +static int url_seek(void *handle, off64_t *pos, int whence) +{ + struct url_state *h = handle; + int rc = 0; + + if (url_debug) + fprintf(stderr,"url_seek(pos=%d,whence=%d)\n", (int)(*pos), whence); + switch (whence) { + case SEEK_SET: + h->buf_pos = *pos; + break; + case SEEK_CUR: + h->buf_pos += *pos; + break; + case SEEK_END: + rc = -1; + } + *pos = h->buf_pos; + return rc; +} + +static int url_close(void *handle) +{ + struct url_state *h = handle; + + if (url_debug) + fprintf(stderr,"url_close()\n"); + curl_multi_remove_handle(curlm,h->curl); + curl_easy_cleanup(h->curl); + if (h->buf.data) + free(h->buf.data); + free(h->path); + free(h); + return 0; +} + +static cookie_io_functions_t url_hooks = { + .read = url_read, +#if 0 + .write = url_write, +#endif + .seek = url_seek, + .close = url_close, +}; + +static FILE *url_open(const char *path, const char *mode) +{ + FILE *fp; + struct url_state *h; + int count; + + if (url_debug) + fprintf(stderr,"url_open(%s,%s)\n",path,mode); + + h = malloc(sizeof(*h)); + if (NULL == h) + goto err; + memset(h,0,sizeof(*h)); + + h->path = strdup(path); + if (NULL == h->path) + goto err; + + /* setup */ + curl_setup(h); + fp = fopencookie(h, mode, url_hooks); + if (NULL == fp) + goto err; + + /* connect + start fetching */ + while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curlm,&count)) + /* nothing */; + + /* check for errors */ + if (0 == count && NULL == h->buf.data) { + errno = ENOENT; + goto fetch_err; + } + + /* all done */ + return fp; + + + fetch_err: + curl_multi_remove_handle(curlm,h->curl); + err: + if (h->curl) + curl_easy_cleanup(h->curl); + if (h->path) + free(h->path); + if (h) + free(h); + return NULL; +} + +/* ---------------------------------------------------------------------- */ +/* hook into fopen using GNU ld's --wrap */ + +int curl_is_url(const char *url) +{ + static char *protocols[] = { + "ftp://", + "http://", + NULL, + }; + int i; + + for (i = 0; protocols[i] != NULL; i++) + if (0 == strncasecmp(url, protocols[i], strlen(protocols[i]))) + return 1; + return 0; +} + +FILE *__wrap_fopen(const char *path, const char *mode); +FILE *__real_fopen(const char *path, const char *mode); + +FILE *__wrap_fopen(const char *path, const char *mode) +{ + if (url_debug) + fprintf(stderr,"fopen(%s,%s)\n",path,mode); + + /* catch URLs */ + if (curl_is_url(path)) { + if (strchr(mode,'w')) { + fprintf(stderr,"write access over ftp/http is not supported, sorry\n"); + return NULL; + } + return url_open(path,mode); + } + + /* files passed to the real fopen */ + return __real_fopen(path,mode); +} @@ -0,0 +1,5 @@ +#ifdef HAVE_LIBCURL +extern int curl_is_url(const char *url); +#else +static inline int curl_is_url(const char *url) { return 0; } +#endif diff --git a/desktop.c b/desktop.c new file mode 100644 index 0000000..d30d3c7 --- /dev/null +++ b/desktop.c @@ -0,0 +1,276 @@ +/* + * some code to handle desktop files + * http://www.freedesktop.org/Standards/desktop-entry-spec + * + * This is really very simple and basic: next to no locale handling, + * no caching, no other clever tricks ... + * ida + fbi only use the Comment= entry of .directory files. + * + * (c) 2004 Gerd Knorr <kraxel@bytesex.org> + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <iconv.h> +#include <langinfo.h> + +#include "list.h" +#include "desktop.h" + +extern int debug; + +/* ------------------------------------------------------------------------- */ +/* desktop files are in utf-8 */ + +static int iconv_string(char *to, char *from, + char *src, char *dst, size_t max) +{ + size_t ilen = strlen(src); + size_t olen = max-1; + iconv_t ic; + + ic = iconv_open(to,from); + if (NULL == ic) + return 0; + + while (ilen > 0) { + if (-1 == iconv(ic,&src,&ilen,&dst,&olen)) { + /* skip + quote broken byte unless we are out of space */ + if (E2BIG == errno) + break; + if (olen < 4) + break; + sprintf(dst,"\\x%02x",(int)(unsigned char)src[0]); + src += 1; + dst += 4; + ilen -= 1; + olen -= 4; + } + } + dst[0] = 0; + iconv_close(ic); + return max-1 - olen; +} + +int utf8_to_locale(char *src, char *dst, size_t max) +{ + char *codeset = nl_langinfo(CODESET); + return iconv_string(codeset, "UTF-8", src, dst, max); +} + +int locale_to_utf8(char *src, char *dst, size_t max) +{ + char *codeset = nl_langinfo(CODESET); + return iconv_string("UTF-8", codeset, src, dst, max); +} + +/* ------------------------------------------------------------------------- */ +/* read/write desktop files */ + +struct desktop_line { + struct list_head next; + char line[1024]; +}; + +static int read_file(char *filename, struct list_head *file) +{ + struct desktop_line *l; + int len,count = 0; + FILE *fp; + + INIT_LIST_HEAD(file); + fp = fopen(filename,"r"); + if (NULL == fp) { + if (debug) + fprintf(stderr,"open %s: %s\n",filename,strerror(errno)); + return 0; + } + for (;;) { + l = malloc(sizeof(*l)); + memset(l,0,sizeof(*l)); + if (NULL == fgets(l->line,sizeof(l->line),fp)) { + free(l); + break; + } + len = strlen(l->line); + if (l->line[len-1] == '\n') + l->line[len-1] = 0; + list_add_tail(&l->next,file); + count++; + } + fclose(fp); + return count; +} + +static int write_file(char *filename, struct list_head *file) +{ + struct desktop_line *l; + struct list_head *item; + FILE *fp; + + fp = fopen(filename,"w"); + if (NULL == fp) { + fprintf(stderr,"open %s: %s\n",filename,strerror(errno)); + return 0; + } + list_for_each(item,file) { + l = list_entry(item, struct desktop_line, next); + fprintf(fp,"%s\n",l->line); + } + fclose(fp); + return 0; +} + +static int dump_file(struct list_head *file) +{ + struct desktop_line *l; + struct list_head *item; + + fprintf(stderr,"\n"); + fprintf(stderr,"+--------------------\n"); + list_for_each(item,file) { + l = list_entry(item, struct desktop_line, next); + fprintf(stderr,"| %s\n",l->line); + } + return 0; +} + +static int free_file(struct list_head *file) +{ + struct desktop_line *l; + + while (!list_empty(file)) { + l = list_entry(file->next, struct desktop_line, next); + list_del(&l->next); + free(l); + } + return 0; +} + +/* ------------------------------------------------------------------------- */ + +static char* get_entry(struct list_head *file, char *entry) +{ + struct desktop_line *l; + struct list_head *item; + int in_desktop_entry = 0; + int len = strlen(entry); + + list_for_each(item,file) { + l = list_entry(item, struct desktop_line, next); + if (0 == strcmp(l->line,"[Desktop Entry]")) { + in_desktop_entry = 1; + continue; + } + if (0 == strncmp(l->line,"[",1)) { + in_desktop_entry = 0; + continue; + } + if (!in_desktop_entry) + continue; + if (0 == strncmp(l->line,entry,len)) + return l->line+len; + } + return NULL; +} + +/* ------------------------------------------------------------------------- */ + +static int add_line(struct list_head *file, char *line) +{ + struct desktop_line *add; + + add = malloc(sizeof(*add)); + memset(add,0,sizeof(*add)); + snprintf(add->line,sizeof(add->line),"%s",line); + list_add_tail(&add->next,file); + return 0; +} + +static int add_entry(struct list_head *file, char *entry, char *value) +{ + struct desktop_line *l,*add; + struct list_head *item; + + list_for_each(item,file) { + l = list_entry(item, struct desktop_line, next); + if (0 != strcmp(l->line,"[Desktop Entry]")) + continue; + add = malloc(sizeof(*add)); + memset(add,0,sizeof(*add)); + snprintf(add->line,sizeof(add->line),"%s%s",entry,value); + list_add(&add->next,item); + return 0; + } + return -1; +} + +static int set_entry(struct list_head *file, char *type, char *entry, char *value) +{ + struct desktop_line *l; + struct list_head *item; + int in_desktop_entry = 0; + int len = strlen(entry); + + list_for_each(item,file) { + l = list_entry(item, struct desktop_line, next); + if (0 == strcmp(l->line,"[Desktop Entry]")) { + in_desktop_entry = 1; + continue; + } + if (0 == strncmp(l->line,"[",1)) { + in_desktop_entry = 0; + continue; + } + if (!in_desktop_entry) + continue; + if (0 == strncmp(l->line,entry,len)) { + snprintf(l->line,sizeof(l->line),"%s%s",entry,value); + return 0; + } + } + if (0 != add_entry(file,entry,value)) { + add_line(file,"[Desktop Entry]"); + add_entry(file,"Type=",type); + add_entry(file,entry,value); + } + return 0; +} + +/* ------------------------------------------------------------------------- */ +/* public interface */ + +int desktop_read_entry(char *filename, char *entry, char *dest, size_t max) +{ + struct list_head file; + char *value; + int rc = 0; + + read_file(filename,&file); + if (debug) + dump_file(&file); + value = get_entry(&file,entry); + if (NULL != value) { + rc = utf8_to_locale(value,dest,max); + if (rc && debug) + fprintf(stderr,"# %s\n",dest); + }; + free_file(&file); + return rc; +} + +int desktop_write_entry(char *filename, char *type, char *entry, char *value) +{ + struct list_head file; + char utf8[1024]; + + read_file(filename,&file); + locale_to_utf8(value,utf8,sizeof(utf8)); + set_entry(&file,"Directory",entry,utf8); + write_file(filename,&file); + free_file(&file); + return 0; +} diff --git a/desktop.h b/desktop.h new file mode 100644 index 0000000..b0cc2c4 --- /dev/null +++ b/desktop.h @@ -0,0 +1,7 @@ +/* these are "for free", the desktop file stuff needs this anyway ... */ +int utf8_to_locale(char *src, char *dst, size_t max); +int locale_to_utf8(char *src, char *dst, size_t max); + +/* handle desktop files */ +int desktop_read_entry(char *filename, char *entry, char *dest, size_t max); +int desktop_write_entry(char *filename, char *type, char *entry, char *value); diff --git a/desktop/ida.desktop b/desktop/ida.desktop new file mode 100644 index 0000000..9cdd34c --- /dev/null +++ b/desktop/ida.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Encoding=UTF-8 +Name=ida +GenericName=Image Viewer +Exec=ida %F +Terminal=no +Categories=Motif;Graphics;Viewer +MimeType=image/jpeg;image/tiff;image/png diff --git a/dither.c b/dither.c new file mode 100644 index 0000000..2eaeb31 --- /dev/null +++ b/dither.c @@ -0,0 +1,193 @@ +/* + * ordered dither rotines + * + * stolen from The GIMP and trimmed for speed + * + */ + +#include <stdlib.h> +#include "dither.h" + +#define DITHER_LEVEL 8 + +void (*dither_line)(unsigned char *, unsigned char *, int, int); + +static long red_mult, green_mult; +static long red_dither[256]; +static long green_dither[256]; +static long blue_dither[256]; +static long gray_dither[256]; + +typedef unsigned long vector[DITHER_LEVEL]; +typedef vector matrix[DITHER_LEVEL]; + +#if DITHER_LEVEL == 8 +#define DITHER_MASK 7 +static matrix DM = +{ + {0, 32, 8, 40, 2, 34, 10, 42}, + {48, 16, 56, 24, 50, 18, 58, 26}, + {12, 44, 4, 36, 14, 46, 6, 38}, + {60, 28, 52, 20, 62, 30, 54, 22}, + {3, 35, 11, 43, 1, 33, 9, 41}, + {51, 19, 59, 27, 49, 17, 57, 25}, + {15, 47, 7, 39, 13, 45, 5, 37}, + {63, 31, 55, 23, 61, 29, 53, 21} +}; + +#endif + +#if DITHER_LEVEL == 4 +#define DITHER_MASK 3 +static matrix DM = +{ + {0, 8, 2, 10}, + {12, 4, 14, 6}, + {3, 11, 1, 9}, + {15, 7, 13, 5} +}; + +#endif + +void +init_dither(int shades_r, int shades_g, int shades_b, int shades_gray) +{ + int i, j; + unsigned char low_shade, high_shade; + unsigned short index; + float red_colors_per_shade; + float green_colors_per_shade; + float blue_colors_per_shade; + float gray_colors_per_shade; + + red_mult = shades_g * shades_b; + green_mult = shades_b; + + red_colors_per_shade = 256.0 / (shades_r - 1); + green_colors_per_shade = 256.0 / (shades_g - 1); + blue_colors_per_shade = 256.0 / (shades_b - 1); + gray_colors_per_shade = 256.0 / (shades_gray - 1); + + /* this avoids a shift when checking these values */ + for (i = 0; i < DITHER_LEVEL; i++) + for (j = 0; j < DITHER_LEVEL; j++) + DM[i][j] *= 0x10000; + + /* setup arrays containing three bytes of information for red, green, & blue */ + /* the arrays contain : + * 1st byte: low end shade value + * 2nd byte: high end shade value + * 3rd & 4th bytes: ordered dither matrix index + */ + + for (i = 0; i < 256; i++) { + + /* setup the red information */ + { + low_shade = (unsigned char) (i / red_colors_per_shade); + high_shade = low_shade + 1; + + index = (unsigned short) + (((i - low_shade * red_colors_per_shade) / red_colors_per_shade) * + (DITHER_LEVEL * DITHER_LEVEL + 1)); + + low_shade *= red_mult; + high_shade *= red_mult; + + red_dither[i] = (index << 16) + (high_shade << 8) + (low_shade); + } + + /* setup the green information */ + { + low_shade = (unsigned char) (i / green_colors_per_shade); + high_shade = low_shade + 1; + + index = (unsigned short) + (((i - low_shade * green_colors_per_shade) / green_colors_per_shade) * + (DITHER_LEVEL * DITHER_LEVEL + 1)); + + low_shade *= green_mult; + high_shade *= green_mult; + + green_dither[i] = (index << 16) + (high_shade << 8) + (low_shade); + } + + /* setup the blue information */ + { + low_shade = (unsigned char) (i / blue_colors_per_shade); + high_shade = low_shade + 1; + + index = (unsigned short) + (((i - low_shade * blue_colors_per_shade) / blue_colors_per_shade) * + (DITHER_LEVEL * DITHER_LEVEL + 1)); + + blue_dither[i] = (index << 16) + (high_shade << 8) + (low_shade); + } + + /* setup the gray information */ + { + low_shade = (unsigned char) (i / gray_colors_per_shade); + high_shade = low_shade + 1; + + index = (unsigned short) + (((i - low_shade * gray_colors_per_shade) / gray_colors_per_shade) * + (DITHER_LEVEL * DITHER_LEVEL + 1)); + + gray_dither[i] = (index << 16) + (high_shade << 8) + (low_shade); + } + } +} + +void +dither_line_color(unsigned char *src, unsigned char *dest, int y, int width) +{ + register long a, b; + long *ymod, xmod; + + ymod = DM[y & DITHER_MASK]; + + while (width--) { + xmod = width & DITHER_MASK; + + b = red_dither[*(src++)]; + if (ymod[xmod] < b) + b >>= 8; + + a = green_dither[*(src++)]; + if (ymod[xmod] < a) + a >>= 8; + b += a; + + a = blue_dither[*(src++)]; + if (ymod[xmod] < a) + a >>= 8; + b += a; + + *(dest++) = b & 0xff; + } +} + +void +dither_line_gray(unsigned char *src, unsigned char *dest, int y, int width) +{ + long *ymod, xmod; + register long a,g; + + ymod = DM[y & DITHER_MASK]; + + while (width--) { + xmod = width & DITHER_MASK; + +#if 1 + g = (src[0]*3 + src[1]*6 + src[2]) / 10; + a = gray_dither[g]; + src += 3; +#else + a = gray_dither[*(src++)]; +#endif + if (ymod[xmod] < a) + a >>= 8; + + *(dest++) = a & 0xff; + } +} diff --git a/dither.h b/dither.h new file mode 100644 index 0000000..b209de6 --- /dev/null +++ b/dither.h @@ -0,0 +1,6 @@ + +extern void (*dither_line)(unsigned char *, unsigned char *, int, int); + +void init_dither(int, int, int, int); +void dither_line_color(unsigned char *, unsigned char *, int, int); +void dither_line_gray(unsigned char *, unsigned char *, int, int); diff --git a/exiftran.c b/exiftran.c new file mode 100644 index 0000000..6db3fd2 --- /dev/null +++ b/exiftran.c @@ -0,0 +1,262 @@ +/* + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#include <libexif/exif-data.h> + +#include <jpeglib.h> +#include "jpeg/transupp.h" /* Support routines for jpegtran */ +#include "jpegtools.h" +#include "genthumbnail.h" + +/* ---------------------------------------------------------------------- */ + +static void dump_exif(FILE *out, ExifData *ed) +{ + const char *title, *value; + ExifEntry *ee; + int tag,i; + + for (i = 0; i < EXIF_IFD_COUNT; i++) { + fprintf(out," ifd %s\n", exif_ifd_get_name (i)); + for (tag = 0; tag < 0xffff; tag++) { + title = exif_tag_get_title(tag); + if (!title) + continue; + ee = exif_content_get_entry (ed->ifd[i], tag); + if (NULL == ee) + continue; + value = exif_entry_get_value(ee); + fprintf(out," 0x%04x %-30s %s\n", tag, title, value); + } + } + if (ed->data && ed->size) + fprintf(out," thumbnail\n %d bytes data\n", ed->size); +} + +static int dump_file(FILE *out, char *filename) +{ + ExifData *ed; + + ed = exif_data_new_from_file (filename); + if (NULL == ed) { + fprintf(stderr,"%s: no EXIF data\n",filename); + return -1; + } + + fprintf(out,"%s\n",filename); + dump_exif(out,ed); + fprintf(out,"--\n"); + + exif_data_unref (ed); + return 0; +} + +/* ---------------------------------------------------------------------- */ + +#define THUMB_MAX 65536 + +static void +usage(FILE *fp, char *name) +{ + char *h; + + if (NULL != (h = strrchr(name, '/'))) + name = h+1; + fprintf(fp, + "usage: %s [ options ] file\n" + "\n" + "transform options:\n" + " -a automatic (using exif orientation tag)\n" + " -9 rotate by 90 degrees\n" + " -1 rotate by 180 degrees\n" + " -2 rotate by 270 degrees\n" + " -f flip vertical\n" + " -F flip horizontal\n" + " -t transpose\n" + " -T transverse\n" + "\n" + " -nt don't rotate exif thumbnail\n" + " -ni don't rotate jpeg image\n" + " -no don't update the orientation tag\n" + "\n" + "other options:\n" + " -h print this text\n" + " -d dump exif data\n" + " -c <text> create/update comment\n" + " -g (re)generate thumbnail\n" + " -o <file> output file\n" + " -i change files inplace\n" + " -b create a backup file (with -i)\n" + " -p preserve timestamps (with -i)\n" + "\n" + "-- \n" + "Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]\n", + name); +} + +int main(int argc, char *argv[]) +{ + JXFORM_CODE transform = JXFORM_NONE; + unsigned char *comment = NULL; + unsigned char *outfile = NULL; + unsigned char *thumbnail = NULL; + int tsize = 0; + int inplace = 0; + unsigned int flags = + JFLAG_TRANSFORM_IMAGE | + JFLAG_TRANSFORM_THUMBNAIL | + JFLAG_UPDATE_ORIENTATION; + int dump = 0; + int i, c, rc; + + for (;;) { + c = getopt(argc, argv, "hbpid912fFtTagc:o:n:"); + if (c == -1) + break; + switch (c) { + case '9': + transform = JXFORM_ROT_90; + break; + case '1': + transform = JXFORM_ROT_180; + break; + case '2': + transform = JXFORM_ROT_270; + break; + case 'f': + transform = JXFORM_FLIP_V; + break; + case 'F': + transform = JXFORM_FLIP_H; + break; + case 't': + transform = JXFORM_TRANSPOSE; + break; + case 'T': + transform = JXFORM_TRANSVERSE; + break; + case 'a': + transform = -1; /* automagic */ + break; + + case 'n': + /* don't ... */ + switch (optarg[0]) { + case 't': + flags &= ~JFLAG_TRANSFORM_THUMBNAIL; + break; + case 'i': + flags &= ~JFLAG_TRANSFORM_IMAGE; + break; + case 'o': + flags &= ~JFLAG_UPDATE_ORIENTATION; + break; + default: + fprintf(stderr,"unknown option -n%c\n",optarg[0]); + exit(1); + } + break; + + case 'c': + flags |= JFLAG_UPDATE_COMMENT; + comment = optarg; + break; + case 'g': + flags |= JFLAG_UPDATE_THUMBNAIL; + break; + case 'o': + outfile = optarg; + break; + case 'd': + dump = 1; + break; + + case 'b': + flags |= JFLAG_FILE_BACKUP; + break; + case 'p': + flags |= JFLAG_FILE_KEEP_TIME; + break; + case 'i': + inplace = 1; + break; + + case 'h': + usage(stdout,argv[0]); + exit(0); + default: + usage(stderr,argv[0]); + exit(1); + } + } + + /* sanity checks on the arguments */ + if (optind == argc) { + fprintf(stderr, + "no image file specified (try -h for more info)\n"); + exit(1); + } + + /* read-only stuff */ + if (dump) { + rc = 0; + for (i = optind; i < argc; i++) { + if (0 != dump_file(stdout,argv[i])) + rc = 1; + } + return rc; + } + + /* r/w sanity checks */ + if (NULL != outfile && optind+1 > argc) { + fprintf(stderr, + "when specifying a output file you can process\n" + "one file at a time only (try -h for more info).\n"); + exit(1); + } + if (NULL == outfile && 0 == inplace) { + fprintf(stderr, + "you have to either specify a output file (-o <file>)\n" + "or enable inplace editing (-i). Try -h for more info.\n"); + exit(1); + } + if (JXFORM_NONE == transform && + !(flags & JFLAG_UPDATE_COMMENT) && + !(flags & JFLAG_UPDATE_THUMBNAIL)) { + fprintf(stderr, + "What do you want to do today? Neither a new comment nor a\n" + "tranformation operation was specified (try -h for more info).\n"); + exit(1); + } + + /* do actual update work */ + if (outfile) { + if (flags & JFLAG_UPDATE_THUMBNAIL) { + thumbnail = malloc(THUMB_MAX); + tsize = create_thumbnail(argv[optind],thumbnail,THUMB_MAX); + } + return jpeg_transform_files(argv[optind], outfile, transform, + comment, thumbnail, tsize, flags); + } else { + rc = 0; + for (i = optind; i < argc; i++) { + fprintf(stderr,"processing %s\n",argv[i]); + if (flags & JFLAG_UPDATE_THUMBNAIL) { + thumbnail = malloc(THUMB_MAX); + tsize = create_thumbnail(argv[i],thumbnail,THUMB_MAX); + } + if (0 != jpeg_transform_inplace(argv[i], transform, comment, + thumbnail, tsize, flags)) + rc = 1; + } + return rc; + } +} diff --git a/exiftran.man b/exiftran.man new file mode 100644 index 0000000..4f3efde --- /dev/null +++ b/exiftran.man @@ -0,0 +1,106 @@ +.TH exiftran 1 "(c) 2003,04 Gerd Knorr" +.SH NAME +exiftran - transform digital camera jpeg images +.SH SYNOPSIS +.B exiftran [ options ] file(s) +.SH DESCRIPTION +.B exiftran +is a command line utility to transform digital image jpeg images. It +can do lossless rotations like jpegtran, but unlike jpegtran it cares +about the EXIF data: It can rotate images automatically by checking +the exif orientation tag, it updates the exif informaton if needed +(image dimension, orientation), it also rotates the exif thumbnail. +It can process multiple images at once. +.SH TRANSFORM OPTIONS +.TP +.B -a +automatic (using exif orientation tag) +.TP +.B -9 +rotate by 90 degrees +.TP +.B -1 +rotate by 180 degrees +.TP +.B -2 +rotate by 270 degrees +.TP +.B -f +flip vertical +.TP +.B -F +flip horizontal +.TP +.B -t +transpose +.TP +.B -T +transverse +.TP +.B -nt +Don't rotate exif thumbnail. +.TP +.B -ni +Don't rotate jpeg image. You might need this or or the -nt option to +fixup things in case you rotated the image with some utility which +ignores the exif thumbnail. Just generating a new thumbnail with -g is +another way to fix it. +.TP +.B -no +Don't update the orientation tag. By default exiftran sets the +orientation to "1" (no transformation needed) to avoid other +exif-aware applications try to rotate the already-rotated image +again. +.SH OTHER OPTIONS +.TP +.B -h +print a short help text +.TP +.B -d +Dump exif data for the file(s). +.TP +.B -c <text> +Set jpeg comment tag to <text>. +.TP +.B -g +(re)generate EXIF thumbnail. +.TP +.B -o <file> +Specify output file. Only one input file is allowed in this mode. +.TP +.B -i +Enable inplace editing of the images. Exiftran allows multiple input +files then. You must specify either this option or a output file with +-o for all operations which modify the image (i.e. everything but -d +right now). +.TP +.B -b +Create a backup file when doing inplace editing. +.TP +.B -p +Preserve timestamps (atime + mtime) when doing inplace editing. +.SH EXAMPLES +Autorotate all jpeg files in the current directory: +.nf + exiftran -ai *.jpeg +.fi +.SH SEE ALSO +jpegtran(1), exif(1) +.SH AUTHOR +Gerd Knorr <kraxel@bytesex.org> +.SH COPYRIGHT +Copyright (C) 2002 Gerd Knorr <kraxel@bytesex.org> +.P +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. +.P +This program is distributed in the hope that 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. +.P +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., 675 Mass Ave, Cambridge, MA 02139, USA. diff --git a/fallback.pl b/fallback.pl new file mode 100755 index 0000000..03706c4 --- /dev/null +++ b/fallback.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl -w +# +# build header file from +# +use strict; + +while (my $line = <>) { + chomp $line; + + # ignore comments + next if $line =~ /^!/; +# next if $line =~ /^\s*$/; + + # quote stuff + $line =~ s/\\/\\\\/g; + $line =~ s/\"/\\\"/g; + + # continued line? + if ($line =~ s/\\\\$//) { + printf "\"%s\"\n",$line; + next; + } + + # write out + printf "\"%s\",\n",$line; +} diff --git a/fb-gui.c b/fb-gui.c new file mode 100644 index 0000000..3c6328c --- /dev/null +++ b/fb-gui.c @@ -0,0 +1,189 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <linux/fb.h> + +#include "fbtools.h" +#include "fs.h" +#include "fb-gui.h" + +/* public */ +int visible = 1; + +/* private */ +static struct fs_font *f; +static char *x11_font = "10x20"; + +static int ys = 3; +static int xs = 10; + +/* ---------------------------------------------------------------------- */ +/* clear screen (areas) */ + +void fb_clear_mem(void) +{ + if (visible) + fb_memset(fb_mem,0,fb_fix.smem_len); +} + +void fb_clear_screen(void) +{ + if (visible) + fb_memset(fb_mem,0,fb_fix.line_length * fb_var.yres); +} + +static void fb_clear_rect(int x1, int x2, int y1,int y2) +{ + unsigned char *ptr; + int y,h; + + if (!visible) + return; + + if (x2 < x1) + h = x2, x2 = x1, x1 = h; + if (y2 < y1) + h = y2, y2 = y1, y1 = h; + ptr = fb_mem; + ptr += y1 * fb_fix.line_length; + ptr += x1 * fs_bpp; + + for (y = y1; y <= y2; y++) { + fb_memset(ptr, 0, (x2 - x1 + 1) * fs_bpp); + ptr += fb_fix.line_length; + } +} + +/* ---------------------------------------------------------------------- */ +/* draw lines */ + +static void fb_setpixel(int x, int y, unsigned int color) +{ + unsigned char *ptr; + + ptr = fb_mem; + ptr += y * fb_fix.line_length; + ptr += x * fs_bpp; + fs_setpixel(ptr, color); +} + +static void fb_line(int x1, int x2, int y1,int y2) +{ + int x,y,h; + float inc; + + if (x2 < x1) + h = x2, x2 = x1, x1 = h; + if (y2 < y1) + h = y2, y2 = y1, y1 = h; + + if (x2 - x1 < y2 - y1) { + inc = (float)(x2-x1)/(float)(y2-y1); + for (y = y1; y <= y2; y++) { + x = x1 + inc * (y - y1); + fb_setpixel(x,y,fs_white); + } + } else { + inc = (float)(y2-y1)/(float)(x2-x1); + for (x = x1; x <= x2; x++) { + y = y1 + inc * (x - x1); + fb_setpixel(x,y,fs_white); + } + } +} + +static void fb_rect(int x1, int x2, int y1,int y2) +{ + fb_line(x1, x2, y1, y1); + fb_line(x1, x2, y2, y2); + fb_line(x1, x1, y1, y2); + fb_line(x2, x2, y1, y2); +} + +/* ---------------------------------------------------------------------- */ +/* text stuff */ + +void fb_text_init1(char *font) +{ + char *fonts[2] = { font, NULL }; + + if (NULL == f) + f = fs_consolefont(font ? fonts : NULL); +#ifndef X_DISPLAY_MISSING + if (NULL == f && 0 == fs_connect(NULL)) + f = fs_open(font ? font : x11_font); +#endif + if (NULL == f) { + fprintf(stderr,"no font available\n"); + exit(1); + } +} + +void fb_text_init2(void) +{ + fs_init_fb(255); +} + +int fb_font_width(void) +{ + return f->width; +} + +void fb_status_line(unsigned char *msg) +{ + int y; + + if (!visible) + return; + y = fb_var.yres - f->height - ys; + fb_memset(fb_mem + fb_fix.line_length * y, 0, + fb_fix.line_length * (f->height+ys)); + fb_line(0, fb_var.xres, y, y); + fs_puts(f, 0, y+ys, msg); +} + +void fb_edit_line(unsigned char *str, int pos) +{ + int x,y; + + if (!visible) + return; + y = fb_var.yres - f->height - ys; + x = pos * f->width; + fb_memset(fb_mem + fb_fix.line_length * y, 0, + fb_fix.line_length * (f->height+ys)); + fb_line(0, fb_var.xres, y, y); + fs_puts(f, 0, y+ys, str); + fb_line(x, x + f->width, fb_var.yres-1, fb_var.yres-1); + fb_line(x, x + f->width, fb_var.yres-2, fb_var.yres-2); +} + +void fb_text_box(int x, int y, char *lines[], unsigned int count) +{ + unsigned int i,len,max, x1, x2, y1, y2; + + if (!visible) + return; + + max = 0; + for (i = 0; i < count; i++) { + len = strlen(lines[i]); + if (max < len) + max = len; + } + x1 = x; + x2 = x + max * f->width; + y1 = y; + y2 = y + count * f->height; + + x += xs; x2 += 2*xs; + y += ys; y2 += 2*ys; + + fb_clear_rect(x1, x2, y1, y2); + fb_rect(x1, x2, y1, y2); + for (i = 0; i < count; i++) { + fs_puts(f,x,y,lines[i]); + y += f->height; + } +} + diff --git a/fb-gui.h b/fb-gui.h new file mode 100644 index 0000000..1e7d7b8 --- /dev/null +++ b/fb-gui.h @@ -0,0 +1,11 @@ +extern int visible; + +void fb_clear_mem(void); +void fb_clear_screen(void); + +void fb_text_init1(char *font); +void fb_text_init2(void); +int fb_font_width(void); +void fb_status_line(unsigned char *msg); +void fb_edit_line(unsigned char *str, int pos); +void fb_text_box(int x, int y, char *lines[], unsigned int count); @@ -0,0 +1,69 @@ +#!/bin/bash + +# tmp dir +DIR="${TMPDIR-/var/tmp}/fbps-$$" +mkdir -p $DIR || exit 1 +trap "rm -rf $DIR" EXIT + +# parse options +fbiopts="" +gsopts="" +passwd="" +opt=1 +while test "$opt" = "1"; do + case "$1" in + -l) gsopts="$gsopts -r100x100" + shift + ;; + -xl) gsopts="$gsopts -r120x120" + shift + ;; + -xxl) gsopts="$gsopts -r150x150" + shift + ;; + -q | -a) + fbiopts="$fbiopts $1" + shift + ;; + -d | -m | -t | -g | -f) + fbiopts="$fbiopts $1 $2" + shift; shift + ;; + -p) password="$2" + shift; shift + ;; + -h) echo fixme: help text + exit 1 + ;; + -*) echo "unknown option: $1" + exit 1 + ;; + *) opt=0 + ;; + esac +done + +# run ghostscript +echo +echo "### rendering pages, please wait ... ###" +echo +gs -dSAVER -dNOPAUSE -dBATCH \ + -sPDFPassword="$password" \ + -sDEVICE=tiffpack -sOutputFile=$DIR/ps%03d.tiff \ + $gsopts \ + "$1" + +# tell the user we are done :-) +echo -ne "\\007" + +# sanity check +pages=`ls $DIR/ps*.tiff 2>/dev/null | wc -l` +if test "$pages" -eq "0"; then + echo + echo "Oops: ghostscript wrote no pages?" + echo + exit 1 +fi + +# show pages +fbi $fbiopts -P $DIR/ps*.tiff diff --git a/fbgs.man b/fbgs.man new file mode 100644 index 0000000..b53020a --- /dev/null +++ b/fbgs.man @@ -0,0 +1,20 @@ +.TH fbps 1 "(c) 1999-2003 Gerd Knorr" +.SH NAME +fbgs - poor man's PostScript/pdf viewer for the linux +framebuffer console +.SH SYNOPSIS +.B fbgs [ options ] file +.SH DESCRIPTION +.B fbgs +is a simple wrapper script which takes a PostScript or pdf +file as input, renders the pages using ghostscript into a +temporarely directory and finally calls fbi to display them. +.SH OPTIONS +fbps understands all fbi options (they are passed throuth). +Additionally you can specify -l, -xl or -xxl to get the pages +rendered with 100, 120 or 150 dpi (default is 75). You can +use option -p <password> if your PDF file requires password. +.SH SEE ALSO +fbi(1), gs(1) +.SH AUTHOR +Gerd Knorr <kraxel@bytesex.org> @@ -0,0 +1,1552 @@ +/* + * image viewer, for framebuffer devices + * + * (c) 1998-2002 Gerd Knorr <kraxel@bytesex.org> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <fcntl.h> +#include <errno.h> +#include <termios.h> +#include <getopt.h> +#include <math.h> +#include <setjmp.h> +#include <signal.h> +#include <ctype.h> +#include <locale.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <linux/fb.h> + +#include <jpeglib.h> + +#include <libexif/exif-data.h> + +#ifdef HAVE_LIBLIRC +# include "lirc/lirc_client.h" +# include "lirc.h" +#endif + +#include "readers.h" +#include "dither.h" +#include "fbtools.h" +#include "fb-gui.h" +#include "filter.h" +#include "desktop.h" +#include "list.h" + +#include "jpeg/transupp.h" /* Support routines for jpegtran */ +#include "jpegtools.h" + +#define TRUE 1 +#define FALSE 0 +#define MAX(x,y) ((x)>(y)?(x):(y)) +#define MIN(x,y) ((x)<(y)?(x):(y)) +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +#define KEY_EOF -1 /* ^D */ +#define KEY_ESC -2 +#define KEY_SPACE -3 +#define KEY_Q -4 +#define KEY_PGUP -5 +#define KEY_PGDN -6 +#define KEY_TIMEOUT -7 +#define KEY_TAGFILE -8 +#define KEY_PLUS -9 +#define KEY_MINUS -10 +#define KEY_VERBOSE -11 +#define KEY_ASCALE -12 +#define KEY_DESC -13 + +/* with arg */ +#define KEY_GOTO -100 +#define KEY_SCALE -101 + +/* edit */ +#define KEY_DELETE -200 +#define KEY_ROT_CW -201 +#define KEY_ROT_CCW -202 + +#define DEFAULT_DEVICE "/dev/fb0" + +/* ---------------------------------------------------------------------- */ + +/* lirc fd */ +int lirc = -1; + +/* variables for read_image */ +int32_t lut_red[256], lut_green[256], lut_blue[256]; +int dither = FALSE, pcd_res = 3, steps = 50; +int textreading = 0, redraw = 0, statusline = 1; +int new_image; +int left, top; + +/* file list */ +struct flist { + int nr; + int tag; + char *name; + struct list_head list; +}; +static LIST_HEAD(flist); +static int fcount; +static struct flist *fcurrent; + +/* framebuffer */ +char *fbdev = NULL; +char *fbmode = NULL; +int fd, switch_last, debug; + +unsigned short red[256], green[256], blue[256]; +struct fb_cmap cmap = { 0, 256, red, green, blue }; + +static float fbgamma = 1; + +/* Command line options. */ + +int autodown = 0; +int autoup = 0; +int comments = 0; + +struct option fbi_options[] = { + {"version", no_argument, NULL, 'V'}, /* version */ + {"help", no_argument, NULL, 'h'}, /* help */ + {"device", required_argument, NULL, 'd'}, /* device */ + {"mode", required_argument, NULL, 'm'}, /* video mode */ + {"gamma", required_argument, NULL, 'g'}, /* set gamma */ + {"quiet", no_argument, NULL, 'q'}, /* quiet */ + {"verbose", no_argument, NULL, 'v'}, /* verbose */ + {"scroll", required_argument, NULL, 's'}, /* set scrool */ + {"timeout", required_argument, NULL, 't'}, /* timeout value */ + {"once", no_argument, NULL, '1'}, /* loop only once */ + {"resolution", required_argument, NULL, 'r'}, /* select resolution */ + {"random", no_argument, NULL, 'u'}, /* randomize images */ + {"font", required_argument, NULL, 'f'}, /* font */ + {"autozoom", no_argument, NULL, 'a'}, + {"edit", no_argument, NULL, 'e'}, /* enable editing */ + {"list", required_argument, NULL, 'l'}, + {"vt", required_argument, NULL, 'T'}, + {"backup", no_argument, NULL, 'b'}, + {"preserve", no_argument, NULL, 'p'}, + + /* long-only options */ + {"autoup", no_argument, &autoup, 1 }, + {"autodown", no_argument, &autodown, 1 }, + {"comments", no_argument, &comments, 1 }, + {0,0,0,0} +}; + +/* font handling */ +static char *fontname = NULL; + +/* ---------------------------------------------------------------------- */ + +static void +version(void) +{ + fprintf(stderr, "fbi version " VERSION + " (c) 1999-2003 Gerd Knorr; compiled on %s.\n", __DATE__ ); +} + +static void +usage(char *name) +{ + char *h; + + if (NULL != (h = strrchr(name, '/'))) + name = h+1; + fprintf(stderr, + "\n" + "This program displays images using the Linux framebuffer device.\n" + "Supported formats: PhotoCD, jpeg, ppm, gif, tiff, xwd, bmp, png.\n" + "It tries to use ImageMagick's convert for unknown file formats.\n" + "\n" + " Usage: %s [ options ] file1 file2 ... fileN\n" + "\n" + " --help [-h] Print this text\n" + " --version [-V] Show the fbi version number\n" + " --device [-d] dev Framebuffer device [%s]\n" + " --mode [-m] mode Video mode (must be listed in /etc/fb.modes)\n" + " - Default is current mode.\n" + " --gamma [-g] f Set gamma\n" + " --scroll [-s] n Set scroll steps in pixels (default: 50)\n" + " --quiet [-q] don't print anything at all\n" + " --verbose [-v] show print filenames all the time\n" + " --timeout [-t] n Load next image after N sec without any keypress\n" + " --once [-1] Don't loop (for use with -t).\n" + " --resolution [-r] n Select resolution [1..5] (PhotoCD)\n" + " --random [-u] Show file1 .. fileN in a random order\n" + " --font [-f] fn Use font fn (either console psf file or\n" + " X11 font spec if a font server is available\n" + " --autozoom [-a] Automagically pick useful zoom factor.\n" + " --autoup Like the above, but upscale only.\n" + " --autodown Like the above, but downscale only.\n" + " --edit [-e] enable editing commands (see man page).\n" + " --backup [-b] create backup files when editing.\n" + " --preserve [-p] preserve timestamps when editing.\n" + " --list [-l] file read list of images from file\n" + " --comments display image comments\n" + " --vt [-T] vt start on console #vt\n" + "\n" + "Large images can be scrolled using the cursor keys. Zoom in/out\n" + "works with '+' and '-'. Use ESC or 'q' to quit. Space and PgDn\n" + "show the next, PgUp shows the previous image. Jumping to a image\n" + "works with <number>g. Return acts like Space but additionally\n" + "prints the filename of the currently displayed image to stdout.\n" + "\n", + name, fbdev ? fbdev : "/dev/fb0"); +} + +/* ---------------------------------------------------------------------- */ + +static int flist_add(char *filename) +{ + struct flist *f; + + f = malloc(sizeof(*f)); + memset(f,0,sizeof(*f)); + f->name = strdup(filename); + list_add_tail(&f->list,&flist); + return 0; +} + +static int flist_add_list(char *listfile) +{ + char filename[256]; + FILE *list; + + list = fopen(listfile,"r"); + if (NULL == list) { + fprintf(stderr,"open %s: %s\n",listfile,strerror(errno)); + return -1; + } + while (NULL != fgets(filename,sizeof(filename)-1,list)) { + size_t off = strcspn(filename,"\r\n"); + if (off) + filename[off] = 0; + flist_add(filename); + } + fclose(list); + return 0; +} + +static int flist_del(struct flist *f) +{ + list_del(&f->list); + free(f->name); + free(f); + return 0; +} + +static void flist_renumber(void) +{ + struct list_head *item; + struct flist *f; + int i = 0; + + list_for_each(item,&flist) { + f = list_entry(item, struct flist, list); + f->nr = ++i; + } + fcount = i; +} + +static int flist_islast(struct flist *f) +{ + return (&flist == f->list.next) ? 1 : 0; +} + +static int flist_isfirst(struct flist *f) +{ + return (&flist == f->list.prev) ? 1 : 0; +} + +static struct flist* flist_first(void) +{ + return list_entry(flist.next, struct flist, list); +} + +static struct flist* flist_next(struct flist *f, int eof, int loop) +{ + if (flist_islast(f)) { + if (eof) + return NULL; + if (loop) + return flist_first(); + return f; + } + return list_entry(f->list.next, struct flist, list); +} + +static struct flist* flist_prev(struct flist *f) +{ + if (flist_isfirst(f)) + return f; + return list_entry(f->list.prev, struct flist, list); +} + +static struct flist* flist_goto(int dest) +{ + struct list_head *item; + struct flist *f; + + list_for_each(item,&flist) { + f = list_entry(item, struct flist, list); + if (f->nr == dest) + return f; + } + return NULL; +} + +static void flist_randomize(void) +{ + struct flist *f; + int count; + + srand((unsigned)time(NULL)); + flist_renumber(); + for (count = fcount; count > 0; count--) { + f = flist_goto((rand() % count)+1); + list_del(&f->list); + list_add_tail(&f->list,&flist); + flist_renumber(); + } +} + +static void flist_print_tagged(FILE *fp) +{ + struct list_head *item; + struct flist *f; + + list_for_each(item,&flist) { + f = list_entry(item, struct flist, list); + if (f->tag) + fprintf(fp,"%s\n",f->name); + } +} + +/* ---------------------------------------------------------------------- */ + +static void status(unsigned char *desc, char *info) +{ + int chars, ilen; + char *str; + + if (!statusline) + return; + chars = fb_var.xres / fb_font_width(); + str = malloc(chars+1); + if (info) { + ilen = strlen(info); + sprintf(str, "%-*.*s [ %s ] H - Help", + chars-14-ilen, chars-14-ilen, desc, info); + } else { + sprintf(str, "%-*.*s | H - Help", chars-11, chars-11, desc); + } + fb_status_line(str); + free(str); +} + +static void show_error(unsigned char *msg) +{ + fb_status_line(msg); + sleep(2); +} + +static void show_exif(struct flist *f) +{ + static unsigned int tags[] = { + 0x010f, // Manufacturer + 0x0110, // Model + + 0x0112, // Orientation + 0x0132, // Date and Time + + 0x01e3, // White Point + 0x829a, // Exposure Time + 0x829d, // FNumber + 0x9206, // Subject Distance + 0xa40c, // Subject Distance Range + 0xa405, // Focal Length In 35mm Film + 0x9209, // Flash + }; + ExifData *ed; + ExifEntry *ee; + unsigned int tag,l1,l2,len,count,i; + const char *title[ARRAY_SIZE(tags)]; + char *value[ARRAY_SIZE(tags)]; + char *linebuffer[ARRAY_SIZE(tags)]; + + if (!visible) + return; + + ed = exif_data_new_from_file(f->name); + if (NULL == ed) { + status("image has no EXIF data", NULL); + return; + } + + /* pass one -- get data + calc size */ + l1 = 0; + l2 = 0; + for (tag = 0; tag < ARRAY_SIZE(tags); tag++) { + ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], tags[tag]); + if (NULL == ee) + ee = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], tags[tag]); + if (NULL == ee) { + title[tag] = NULL; + value[tag] = NULL; + continue; + } + title[tag] = exif_tag_get_title(tags[tag]); + value[tag] = strdup(exif_entry_get_value(ee)); + len = strlen(title[tag]); + if (l1 < len) + l1 = len; + len = strlen(value[tag]); + if (l2 < len) + l2 = len; + } + + /* pass two -- print stuff */ + count = 0; + for (tag = 0; tag < ARRAY_SIZE(tags); tag++) { + if (NULL == title[tag]) + continue; + linebuffer[count] = malloc(l1+l2+8); + sprintf(linebuffer[count],"%-*.*s : %-*.*s", + l1, l1, title[tag], + l2, l2, value[tag]); + count++; + } + fb_text_box(24,16,linebuffer,count); + + /* pass three -- free data */ + for (tag = 0; tag < ARRAY_SIZE(tags); tag++) + if (NULL != value[tag]) + free(value[tag]); + exif_data_unref (ed); + for (i = 0; i < count; i++) + free(linebuffer[i]); +} + +static void show_help(void) +{ + static char *help[] = { + "keyboard commands", + "~~~~~~~~~~~~~~~~~", + " ESC, Q - quit", + " pgdn, space - next image", + " pgup - previous image", + " +/- - zoom in/out", + " A - autozoom image", + " cursor keys - scroll image", + "", + " H - show this help text", + " I - show EXIF info", + " P - pause slideshow", + " V - toggle statusline", + "", + "available if started with --edit switch,", + "rotation works for jpeg images only:", + " shift+D - delete image", + " R - rotate clockwise", + " L - rotate counter-clockwise", + }; + + fb_text_box(24,16,help,ARRAY_SIZE(help)); +} + +/* ---------------------------------------------------------------------- */ + +struct termios saved_attributes; +int saved_fl; + +static void +tty_raw(void) +{ + struct termios tattr; + + fcntl(0,F_GETFL,&saved_fl); + tcgetattr (0, &saved_attributes); + + fcntl(0,F_SETFL,O_NONBLOCK); + memcpy(&tattr,&saved_attributes,sizeof(struct termios)); + tattr.c_lflag &= ~(ICANON|ECHO); + tattr.c_cc[VMIN] = 1; + tattr.c_cc[VTIME] = 0; + tcsetattr (0, TCSAFLUSH, &tattr); +} + +static void +tty_restore(void) +{ + fcntl(0,F_SETFL,saved_fl); + tcsetattr (0, TCSANOW, &saved_attributes); +} + +/* testing: find key codes */ +static void debug_key(char *key) +{ + char linebuffer[128]; + int i,len; + + len = sprintf(linebuffer,"key: "); + for (i = 0; key[i] != '\0'; i++) + len += sprintf(linebuffer+len, "%s%c", + key[i] < 0x20 ? "^" : "", + key[i] < 0x20 ? key[i] + 0x40 : key[i]); + status(linebuffer, NULL); +} + +static void +console_switch(int is_busy) +{ + switch (fb_switch_state) { + case FB_REL_REQ: + fb_switch_release(); + case FB_INACTIVE: + visible = 0; + break; + case FB_ACQ_REQ: + fb_switch_acquire(); + case FB_ACTIVE: + visible = 1; + redraw = 1; + ioctl(fd,FBIOPAN_DISPLAY,&fb_var); + fb_clear_screen(); + if (is_busy) + status("busy, please wait ...", NULL); + break; + default: + break; + } + switch_last = fb_switch_state; + return; +} + +/* ---------------------------------------------------------------------- */ + +static void free_image(struct ida_image *img) +{ + if (img) { + if (img->data) + free(img->data); + free(img); + } +} + +static struct ida_image* +read_image(char *filename) +{ + char command[1024]; + struct ida_loader *loader = NULL; + struct ida_image *img; + struct list_head *item; + char blk[512]; + FILE *fp; + unsigned int y; + void *data; + + new_image = 1; + + /* open file */ + if (NULL == (fp = fopen(filename, "r"))) { + fprintf(stderr,"open %s: %s\n",filename,strerror(errno)); + return NULL; + } + memset(blk,0,sizeof(blk)); + fread(blk,1,sizeof(blk),fp); + rewind(fp); + + /* pick loader */ + list_for_each(item,&loaders) { + loader = list_entry(item, struct ida_loader, list); + if (NULL == loader->magic) + break; + if (0 == memcmp(blk+loader->moff,loader->magic,loader->mlen)) + break; + loader = NULL; + } + if (NULL == loader) { + /* no loader found, try to use ImageMagick's convert */ + sprintf(command,"convert -depth 8 \"%s\" ppm:-",filename); + if (NULL == (fp = popen(command,"r"))) + return NULL; + loader = &ppm_loader; + } + + /* load image */ + img = malloc(sizeof(*img)); + memset(img,0,sizeof(*img)); + data = loader->init(fp,filename,0,&img->i,0); + if (NULL == data) { + fprintf(stderr,"loading %s [%s] FAILED\n",filename,loader->name); + free_image(img); + return NULL; + } + img->data = malloc(img->i.width * img->i.height * 3); + for (y = 0; y < img->i.height; y++) { + if (switch_last != fb_switch_state) + console_switch(1); + loader->read(img->data + img->i.width * 3 * y, y, data); + } + loader->done(data); + return img; +} + +static struct ida_image* +scale_image(struct ida_image *src, float scale) +{ + struct op_resize_parm p; + struct ida_rect rect; + struct ida_image *dest; + void *data; + unsigned int y; + + dest = malloc(sizeof(*dest)); + memset(dest,0,sizeof(*dest)); + memset(&rect,0,sizeof(rect)); + memset(&p,0,sizeof(p)); + + p.width = src->i.width * scale; + p.height = src->i.height * scale; + p.dpi = src->i.dpi; + if (0 == p.width) + p.width = 1; + if (0 == p.height) + p.height = 1; + + data = desc_resize.init(src,&rect,&dest->i,&p); + dest->data = malloc(dest->i.width * dest->i.height * 3); + for (y = 0; y < dest->i.height; y++) { + if (switch_last != fb_switch_state) + console_switch(1); + desc_resize.work(src,&rect, + dest->data + 3 * dest->i.width * y, + y, data); + } + desc_resize.done(data); + return dest; +} + +static float auto_scale(struct ida_image *img) +{ + float xs,ys,scale; + + xs = (float)fb_var.xres / img->i.width; + ys = (float)fb_var.yres / img->i.height; + scale = (xs < ys) ? xs : ys; + return scale; +} + +/* ---------------------------------------------------------------------- */ + +static unsigned char * +convert_line(int bpp, int line, int owidth, + char unsigned *dest, char unsigned *buffer) +{ + unsigned char *ptr = (void*)dest; + unsigned short *ptr2 = (void*)dest; + unsigned long *ptr4 = (void*)dest; + int x; + + switch (fb_var.bits_per_pixel) { + case 8: + dither_line(buffer, ptr, line, owidth); + ptr += owidth; + return ptr; + case 15: + case 16: + for (x = 0; x < owidth; x++) { + ptr2[x] = lut_red[buffer[x*3]] | + lut_green[buffer[x*3+1]] | + lut_blue[buffer[x*3+2]]; + } + ptr2 += owidth; + return (char*)ptr2; + case 24: + for (x = 0; x < owidth; x++) { + ptr[3*x+2] = buffer[3*x+0]; + ptr[3*x+1] = buffer[3*x+1]; + ptr[3*x+0] = buffer[3*x+2]; + } + ptr += owidth * 3; + return ptr; + case 32: + for (x = 0; x < owidth; x++) { + ptr4[x] = lut_red[buffer[x*3]] | + lut_green[buffer[x*3+1]] | + lut_blue[buffer[x*3+2]]; + } + ptr4 += owidth; + return (char*)ptr4; + default: + /* keep compiler happy */ + return NULL; + } +} + +/* ---------------------------------------------------------------------- */ + +static void init_one(int32_t *lut, int bits, int shift) +{ + int i; + + if (bits > 8) + for (i = 0; i < 256; i++) + lut[i] = (i << (bits + shift - 8)); + else + for (i = 0; i < 256; i++) + lut[i] = (i >> (8 - bits)) << shift; +} + +static void +lut_init(int depth) +{ + if (fb_var.red.length && + fb_var.green.length && + fb_var.blue.length) { + /* fb_var.{red|green|blue} looks sane, use it */ + init_one(lut_red, fb_var.red.length, fb_var.red.offset); + init_one(lut_green, fb_var.green.length, fb_var.green.offset); + init_one(lut_blue, fb_var.blue.length, fb_var.blue.offset); + } else { + /* fallback */ + int i; + switch (depth) { + case 15: + for (i = 0; i < 256; i++) { + lut_red[i] = (i & 0xf8) << 7; /* bits -rrrrr-- -------- */ + lut_green[i] = (i & 0xf8) << 2; /* bits ------gg ggg----- */ + lut_blue[i] = (i & 0xf8) >> 3; /* bits -------- ---bbbbb */ + } + break; + case 16: + for (i = 0; i < 256; i++) { + lut_red[i] = (i & 0xf8) << 8; /* bits rrrrr--- -------- */ + lut_green[i] = (i & 0xfc) << 3; /* bits -----ggg ggg----- */ + lut_blue[i] = (i & 0xf8) >> 3; /* bits -------- ---bbbbb */ + } + break; + case 24: + for (i = 0; i < 256; i++) { + lut_red[i] = i << 16; /* byte -r-- */ + lut_green[i] = i << 8; /* byte --g- */ + lut_blue[i] = i; /* byte ---b */ + } + break; + } + } +} + +static unsigned short calc_gamma(int n, int max) +{ + int ret =65535.0 * pow((float)n/(max), 1 / fbgamma); + if (ret > 65535) ret = 65535; + if (ret < 0) ret = 0; + return ret; +} + +static void +linear_palette(int bit) +{ + int i, size = 256 >> (8 - bit); + + for (i = 0; i < size; i++) + red[i] = green[i] = blue[i] = calc_gamma(i,size); +} + +static void +svga_dither_palette(int r, int g, int b) +{ + int rs, gs, bs, i; + + rs = 256 / (r - 1); + gs = 256 / (g - 1); + bs = 256 / (b - 1); + for (i = 0; i < 256; i++) { + red[i] = calc_gamma(rs * ((i / (g * b)) % r), 255); + green[i] = calc_gamma(gs * ((i / b) % g), 255); + blue[i] = calc_gamma(bs * ((i) % b), 255); + } +} + +static void +svga_display_image(struct ida_image *img, int xoff, int yoff) +{ + unsigned int dwidth = MIN(img->i.width, fb_var.xres); + unsigned int dheight = MIN(img->i.height, fb_var.yres); + unsigned int data, video, bank, offset, bytes, y; + + if (!visible) + return; + bytes = (fb_var.bits_per_pixel+7)/8; + + /* offset for image data (image > screen, select visible area) */ + offset = (yoff * img->i.width + xoff) * 3; + + /* offset for video memory (image < screen, center image) */ + video = 0, bank = 0; + if (img->i.width < fb_var.xres) + video += bytes * ((fb_var.xres - img->i.width) / 2); + if (img->i.height < fb_var.yres) + video += fb_fix.line_length * ((fb_var.yres - img->i.height) / 2); + + /* go ! */ + for (data = 0, y = 0; + data < img->i.width * img->i.height * 3 + && data / img->i.width / 3 < dheight; + data += img->i.width * 3, video += fb_fix.line_length) { + convert_line(fb_var.bits_per_pixel, y++, dwidth, + fb_mem+video, img->data + data + offset); + } +} + +static int +svga_show(struct ida_image *img, int timeout, char *desc, char *info, int *nr) +{ + static int paused = 0; + int exif = 0, help = 0; + int rc; + char key[11]; + fd_set set; + struct timeval limit; + char linebuffer[80]; + int fdmax; + + *nr = 0; + if (NULL == img) + return KEY_SPACE; /* skip */ + + if (new_image) { + /* start with centered image, if larger than screen */ + if (img->i.width > fb_var.xres) + left = (img->i.width - fb_var.xres) / 2; + if (img->i.height > fb_var.yres && !textreading) + top = (img->i.height - fb_var.yres) / 2; + new_image = 0; + } + + redraw = 1; + for (;;) { + if (redraw) { + redraw = 0; + if (img->i.height <= fb_var.yres) { + top = 0; + } else { + if (top < 0) + top = 0; + if (top + fb_var.yres > img->i.height) + top = img->i.height - fb_var.yres; + } + if (img->i.width <= fb_var.xres) { + left = 0; + } else { + if (left < 0) + left = 0; + if (left + fb_var.xres > img->i.width) + left = img->i.width - fb_var.xres; + } + svga_display_image(img, left, top); + status(desc, info); + } + if (switch_last != fb_switch_state) { + console_switch(0); + continue; + } + FD_SET(0, &set); + fdmax = 1; +#ifdef HAVE_LIBLIRC + if (-1 != lirc) { + FD_SET(lirc,&set); + fdmax = lirc+1; + } +#endif + limit.tv_sec = timeout; + limit.tv_usec = 0; + rc = select(fdmax, &set, NULL, NULL, + (-1 != timeout && !paused) ? &limit : NULL); + if (switch_last != fb_switch_state) { + console_switch(0); + continue; + } + if (0 == rc) + return KEY_TIMEOUT; + + if (FD_ISSET(0,&set)) { + /* stdin, i.e. keyboard */ + rc = read(0, key, sizeof(key)-1); + if (rc < 1) { + /* EOF */ + return KEY_EOF; + } + key[rc] = 0; + } +#ifdef HAVE_LIBLIRC + if (lirc != -1 && FD_ISSET(lirc,&set)) { + /* lirc input */ + if (-1 == lirc_fbi_havedata(&rc,key)) { + fprintf(stderr,"lirc: connection lost\n"); + close(lirc); + lirc = -1; + } + key[rc] = 0; + } +#endif + + if (rc == 1 && (*key == 'q' || *key == 'Q' || + *key == 'e' || *key == 'E' || + *key == '\x1b' || *key == '\n')) { + if (*key == '\n') + return KEY_TAGFILE; + if (*key == '\x1b') + return KEY_ESC; + return KEY_Q; + + } else if (0 == strcmp(key, " ")) { + if (textreading && top < (int)(img->i.height - fb_var.yres)) { + redraw = 1; + top += (fb_var.yres-100); + } else { + return KEY_SPACE; + } + + } else if (0 == strcmp(key, "\x1b[A") && img->i.height > fb_var.yres) { + redraw = 1; + top -= steps; + } else if (0 == strcmp(key, "\x1b[B") && img->i.height > fb_var.yres) { + redraw = 1; + top += steps; + } else if (0 == strcmp(key, "\x1b[1~") && img->i.height > fb_var.yres) { + redraw = 1; + top = 0; + } else if (0 == strcmp(key, "\x1b[4~")) { + redraw = 1; + top = img->i.height - fb_var.yres; + } else if (0 == strcmp(key, "\x1b[D") && img->i.width > fb_var.xres) { + redraw = 1; + left -= steps; + } else if (0 == strcmp(key, "\x1b[C") && img->i.width > fb_var.xres) { + redraw = 1; + left += steps; + + } else if (0 == strcmp(key, "\x1b[5~")) { + return KEY_PGUP; + } else if (0 == strcmp(key, "\x1b[6~") || + 0 == strcmp(key, "n") || + 0 == strcmp(key, "N")) { + return KEY_PGDN; + + } else if (0 == strcmp(key, "+")) { + return KEY_PLUS; + } else if (0 == strcmp(key, "-")) { + return KEY_MINUS; + } else if (0 == strcmp(key, "a") || + 0 == strcmp(key, "A")) { + return KEY_ASCALE; + + } else if (0 == strcmp(key, "p") || + 0 == strcmp(key, "P")) { + if (-1 != timeout) { + paused = !paused; + status(paused ? "pause on " : "pause off", NULL); + } + + } else if (0 == strcmp(key, "D")) { + return KEY_DELETE; + } else if (0 == strcmp(key, "r") || + 0 == strcmp(key, "R")) { + return KEY_ROT_CW; + } else if (0 == strcmp(key, "l") || + 0 == strcmp(key, "L")) { + return KEY_ROT_CCW; + + } else if (0 == strcmp(key, "h") || + 0 == strcmp(key, "H")) { + if (!help) { + show_help(); + help = 1; + } else { + redraw = 1; + help = 0; + } + exif = 0; + + } else if (0 == strcmp(key, "i") || + 0 == strcmp(key, "I")) { + if (!exif) { + show_exif(fcurrent); + exif = 1; + } else { + redraw = 1; + exif = 0; + } + help = 0; + + } else if (0 == strcmp(key, "v") || + 0 == strcmp(key, "V")) { + return KEY_VERBOSE; + + } else if (0 == strcmp(key, "t") || + 0 == strcmp(key, "T")) { + return KEY_DESC; + + } else if (rc == 1 && (*key == 'g' || *key == 'G')) { + return KEY_GOTO; + } else if (rc == 1 && (*key == 's' || *key == 'S')) { + return KEY_SCALE; + } else if (rc == 1 && *key >= '0' && *key <= '9') { + *nr = *nr * 10 + (*key - '0'); + sprintf(linebuffer, "> %d",*nr); + status(linebuffer, NULL); + } else { + + *nr = 0; +#if 0 + debug_key(key); +#endif + } + } +} + +static void scale_fix_top_left(float old, float new, struct ida_image *img) +{ + unsigned int width, height; + float cx,cy; + + cx = (float)(left + fb_var.xres/2) / (img->i.width * old); + cy = (float)(top + fb_var.yres/2) / (img->i.height * old); + + width = img->i.width * new; + height = img->i.height * new; + left = cx * width - fb_var.xres/2; + top = cy * height - fb_var.yres/2; +} + +/* ---------------------------------------------------------------------- */ + +static char *my_basename(char *filename) +{ + char *h; + + h = strrchr(filename,'/'); + if (h) + return h+1; + return filename; +} + +static char *file_desktop(char *filename) +{ + static char desc[128]; + char *h; + + strncpy(desc,filename,sizeof(desc)-1); + if (NULL != (h = strrchr(filename,'/'))) { + snprintf(desc,sizeof(desc),"%.*s/%s", + (int)(h - filename), filename, + ".directory"); + } else { + strcpy(desc,".directory"); + } + return desc; +} + +static char *make_desc(struct ida_image_info *img, char *filename) +{ + static char linebuffer[128]; + struct ida_extra *extra; + char *desc; + int len; + + memset(linebuffer,0,sizeof(linebuffer)); + strncpy(linebuffer,filename,sizeof(linebuffer)-1); + + if (comments) { + extra = load_find_extra(img, EXTRA_COMMENT); + if (extra) + snprintf(linebuffer,sizeof(linebuffer),"%.*s", + extra->size,extra->data); + } else { + desc = file_desktop(filename); + len = desktop_read_entry(desc, "Comment=", linebuffer, sizeof(linebuffer)); + if (0 != len) + snprintf(linebuffer+len,sizeof(linebuffer)-len, + " (%s)", my_basename(filename)); + } + + return linebuffer; +} + +static char *make_info(struct ida_image *img, float scale) +{ + static char linebuffer[128]; + + snprintf(linebuffer, sizeof(linebuffer), + "%s%.0f%% %dx%d %d/%d", + fcurrent->tag ? "* " : "", + scale*100, + img->i.width, img->i.height, + fcurrent->nr, fcount); + return linebuffer; +} + +static char edit_line(char *line, int max) +{ + int len = strlen(line); + int pos = len; + int rc; + char key[11]; + fd_set set; + + do { + fb_edit_line(line,pos); + + FD_SET(0, &set); + rc = select(1, &set, NULL, NULL, NULL); + if (switch_last != fb_switch_state) { + console_switch(0); + continue; + } + rc = read(0, key, sizeof(key)-1); + if (rc < 1) { + /* EOF */ + return KEY_EOF; + } + key[rc] = 0; + + if (0 == strcmp(key,"\x0a")) { + /* Enter */ + return 0; + + } else if (0 == strcmp(key,"\x1b")) { + /* ESC */ + return KEY_ESC; + + } else if (0 == strcmp(key,"\x1b[C")) { + /* cursor right */ + if (pos < len) + pos++; + + } else if (0 == strcmp(key,"\x1b[D")) { + /* cursor left */ + if (pos > 0) + pos--; + + } else if (0 == strcmp(key,"\x1b[1~")) { + /* home */ + pos = 0; + + } else if (0 == strcmp(key,"\x1b[4~")) { + /* end */ + pos = len; + + } else if (0 == strcmp(key,"\x7f")) { + /* backspace */ + if (pos > 0) { + memmove(line+pos-1,line+pos,len-pos+1); + pos--; + len--; + } + + } else if (0 == strcmp(key,"\x1b[3~")) { + /* delete */ + if (pos < len) { + memmove(line+pos,line+pos+1,len-pos); + len--; + } + + } else if (1 == rc && isprint(key[0]) && len < max) { + /* new key */ + if (pos < len) + memmove(line+pos+1,line+pos,len-pos+1); + line[pos] = key[0]; + pos++; + len++; + line[len] = 0; + + } else if (0 /* debug */) { + debug_key(key); + sleep(1); + } + } while (1); +} + +static void edit_desc(char *filename) +{ + static char linebuffer[128]; + char *desc; + int len, rc; + + desc = file_desktop(filename); + len = desktop_read_entry(desc, "Comment=", linebuffer, sizeof(linebuffer)); + if (0 == len) { + linebuffer[0] = 0; + len = 0; + } + rc = edit_line(linebuffer, sizeof(linebuffer)-1); + if (0 != rc) + return; + desktop_write_entry(desc, "Directory", "Comment=", linebuffer); +} + +/* ---------------------------------------------------------------------- */ + +static void cleanup_and_exit(int code) +{ + fb_clear_mem(); + tty_restore(); + fb_cleanup(); + flist_print_tagged(stdout); + exit(code); +} + +int +main(int argc, char *argv[]) +{ + int timeout = -1; + int randomize = -1; + int opt_index = 0; + int vt = 0; + int backup = 0; + int preserve = 0; + + struct ida_image *fimg = NULL; + struct ida_image *simg = NULL; + struct ida_image *img = NULL; + float scale = 1; + float newscale = 1; + + int c, editable = 0, once = 0; + int need_read, need_refresh; + int i, arg, key; + + char *line, *info, *desc; + char linebuffer[128]; + + if (NULL != (line = getenv("FRAMEBUFFER"))) + fbdev = line; + if (NULL != (line = getenv("FBGAMMA"))) + fbgamma = atof(line); + if (NULL != (line = getenv("FBFONT"))) + fontname = line; + +#ifdef HAVE_LIBLIRC + lirc = lirc_fbi_init(); +#endif + + setlocale(LC_ALL,""); + for (;;) { + c = getopt_long(argc, argv, "u1evahPqVbpr:t:m:d:g:s:f:l:T:", + fbi_options, &opt_index); + if (c == -1) + break; + switch (c) { + case 0: + /* long option, nothing to do */ + break; + case '1': + once = 1; + break; + case 'a': + autoup = 1; + autodown = 1; + break; + case 'q': + statusline = 0; + break; + case 'v': + statusline = 1; + break; + case 'P': + textreading = 1; + break; + case 'g': + fbgamma = atof(optarg); + break; + case 'r': + pcd_res = atoi(optarg); + break; + case 's': + steps = atoi(optarg); + break; + case 't': + timeout = atoi(optarg); + break; + case 'u': + randomize = 1; + break; + case 'd': + fbdev = optarg; + break; + case 'm': + fbmode = optarg; + break; + case 'f': + fontname = optarg; + break; + case 'e': + editable = 1; + break; + case 'b': + backup = 1; + break; + case 'p': + preserve = 1; + break; + case 'l': + flist_add_list(optarg); + break; + case 'T': + vt = atoi(optarg); + break; + case 'V': + version(); + exit(0); + break; + default: + case 'h': + usage(argv[0]); + exit(1); + } + } + + for (i = optind; i < argc; i++) { + flist_add(argv[i]); + } + flist_renumber(); + + if (0 == fcount) { + usage(argv[0]); + exit(1); + } + + if (randomize != -1) + flist_randomize(); + fcurrent = flist_first(); + + need_read = 1; + need_refresh = 1; + + fb_text_init1(fontname); + fd = fb_init(fbdev, fbmode, vt); + fb_catch_exit_signals(); + fb_switch_init(); + signal(SIGTSTP,SIG_IGN); + fb_text_init2(); + + switch (fb_var.bits_per_pixel) { + case 8: + svga_dither_palette(8, 8, 4); + dither = TRUE; + init_dither(8, 8, 4, 2); + break; + case 15: + case 16: + if (fb_fix.visual == FB_VISUAL_DIRECTCOLOR) + linear_palette(5); + if (fb_var.green.length == 5) { + lut_init(15); + } else { + lut_init(16); + } + break; + case 24: + if (fb_fix.visual == FB_VISUAL_DIRECTCOLOR) + linear_palette(8); + break; + case 32: + if (fb_fix.visual == FB_VISUAL_DIRECTCOLOR) + linear_palette(8); + lut_init(24); + break; + default: + fprintf(stderr, "Oops: %i bit/pixel ???\n", + fb_var.bits_per_pixel); + exit(1); + } + if (fb_fix.visual == FB_VISUAL_DIRECTCOLOR || + fb_var.bits_per_pixel == 8) { + if (-1 == ioctl(fd,FBIOPUTCMAP,&cmap)) { + perror("ioctl FBIOPUTCMAP"); + exit(1); + } + } + + /* svga main loop */ + tty_raw(); + desc = NULL; + info = NULL; + for (;;) { + if (need_read) { + need_read = 0; + need_refresh = 1; + sprintf(linebuffer,"loading %s ...",fcurrent->name); + status(linebuffer, NULL); + free_image(fimg); + free_image(simg); + fimg = read_image(fcurrent->name); + simg = NULL; + img = NULL; + scale = 1; + if (fimg) { + if (autoup || autodown) { + scale = auto_scale(fimg); + if (scale < 1 && !autodown) + scale = 1; + if (scale > 1 && !autoup) + scale = 1; + } + if (scale != 1) { + sprintf(linebuffer,"scaling (%.0f%%) %s ...", + scale*100, fcurrent->name); + status(linebuffer, NULL); + simg = scale_image(fimg,scale); + img = simg; + } else { + img = fimg; + } + desc = make_desc(&fimg->i,fcurrent->name); + } + if (!img) { + sprintf(linebuffer,"%s: FAILED",fcurrent->name); + show_error(linebuffer); + } + } + if (img) { + if (need_refresh) { + need_refresh = 0; + if (img->i.width < fb_var.xres || img->i.height < fb_var.yres) + fb_clear_screen(); + } + info = make_info(fimg,scale); + } + switch (key = svga_show(img, timeout, desc, info, &arg)) { + case KEY_DELETE: + if (editable) { + struct flist *fdel = fcurrent; + if (flist_islast(fcurrent)) + fcurrent = flist_prev(fcurrent); + else + fcurrent = flist_next(fcurrent,0,0); + unlink(fdel->name); + flist_del(fdel); + flist_renumber(); + need_read = 1; + if (list_empty(&flist)) { + /* deleted last one */ + fb_clear_mem(); + tty_restore(); + fb_cleanup(); + exit(0); + } + } else { + show_error("readonly mode, sorry [start with --edit?]"); + } + break; + case KEY_ROT_CW: + case KEY_ROT_CCW: + { + if (editable) { + sprintf(linebuffer,"rotating %s ...",fcurrent->name); + status(linebuffer, NULL); + jpeg_transform_inplace + (fcurrent->name, + (key == KEY_ROT_CW) ? JXFORM_ROT_90 : JXFORM_ROT_270, + NULL, + NULL,0, + (backup ? JFLAG_FILE_BACKUP : 0) | + (preserve ? JFLAG_FILE_KEEP_TIME : 0) | + JFLAG_TRANSFORM_IMAGE | + JFLAG_TRANSFORM_THUMBNAIL | + JFLAG_UPDATE_ORIENTATION); + need_read = 1; + } else { + show_error("readonly mode, sorry [start with --edit?]"); + } + break; + } + case KEY_TAGFILE: + fcurrent->tag = !fcurrent->tag; + /* fall throuth */ + case KEY_SPACE: + need_read = 1; + fcurrent = flist_next(fcurrent,1,0); + if (NULL != fcurrent) + break; + /* else fall */ + case KEY_ESC: + case KEY_Q: + case KEY_EOF: + cleanup_and_exit(0); + break; + case KEY_PGDN: + need_read = 1; + fcurrent = flist_next(fcurrent,0,0); + break; + case KEY_PGUP: + need_read = 1; + fcurrent = flist_prev(fcurrent); + break; + case KEY_TIMEOUT: + need_read = 1; + fcurrent = flist_next(fcurrent,once,1); + if (NULL == fcurrent) { + fb_clear_mem(); + tty_restore(); + fb_cleanup(); + } + /* FIXME: wrap around */ + break; + case KEY_PLUS: + case KEY_MINUS: + case KEY_ASCALE: + case KEY_SCALE: + if (key == KEY_PLUS) { + newscale = scale * 1.6; + } else if (key == KEY_MINUS) { + newscale = scale / 1.6; + } else if (key == KEY_ASCALE) { + newscale = auto_scale(fimg); + } else { + newscale = arg / 100; + } + if (newscale < 0.1) + newscale = 0.1; + if (newscale > 10) + newscale = 10; + scale_fix_top_left(scale, newscale, img); + scale = newscale; + sprintf(linebuffer,"scaling (%.0f%%) %s ...", + scale*100, fcurrent->name); + status(linebuffer, NULL); + free_image(simg); + simg = scale_image(fimg,scale); + img = simg; + need_refresh = 1; + break; + case KEY_GOTO: + if (arg > 0 && arg <= fcount) { + need_read = 1; + fcurrent = flist_goto(arg); + } + break; + case KEY_VERBOSE: + statusline = !statusline; + need_refresh = 1; + break; + case KEY_DESC: + if (!comments) { + edit_desc(fcurrent->name); + desc = make_desc(&fimg->i,fcurrent->name); + } + break; + } + } +} @@ -0,0 +1,160 @@ +.TH fbi 1 "(c) 1999-2002 Gerd Knorr" +.SH NAME +fbi - linux \fBf\fPrame\fBb\fPuffer \fBi\fPmageviewer +.SH SYNOPSIS +.B fbi [ options ] file ... +.SH DESCRIPTION +.B fbi +displays the specified file(s) on the linux console using the +framebuffer device. PhotoCD, jpeg, ppm, gif, tiff, xwd, bmp and png +are supported directly. For other formats fbi tries to use +ImageMagick's convert. +.SH OPTIONS +.TP +.B -h +print usage info +.TP +.B -d device +framebuffer device to use. Default is the one your vc is +mapped to. +.TP +.B -m mode +name of the video mode to use video mode (must be listed in +/etc/fb.modes). Default is not to change the video mode. +.TP +.B -q +be quiet: don't print anything. +.TP +.B -v +be verbose: allways print filenames. +.TP +.B -P +Enable textreading mode. This has the effect that fbi will display +large images without vertical offset (default is to center the +images). Space will first try to scroll down and go to the next image +only if it is already on the bottom of the page. Useful if the images +you are watching text pages, all you have to do to get the next piece +of text is to press space... +.TP +.B -t sec +timeout: load next image after >sec< seconds without any +keypress +.TP +.B -g gamma +gamma correction. Can also be put into the FBGAMMA environment +variable. Default is 1.0. Requires Pseudocolor or Directcolor +visual, doesn't work for Truecolor. +.TP +.B -r n +select resolution. PhotoCD only, n = 1..5. +.TP +.B -s n +set scroll steps in pixels (default is 50). +.TP +.B -f font +Set font. This can be either a pcf console font file or a X11 font +spec. Using X11 fonts requires a font server (The one specified in +the environment variable FONTSERVER or on localhost). The FBFONT +environment variable is used as default. If unset, fbi will +fallback to 10x20 (X11) / lat1u-16.psf (console). +.TP +.B -a +Enable autozoom. fbi will automagically pick a reasonable zoom factor +when loading a new image. +.TP +.B --autoup +Like autozoom, but scale up only. +.TP +.B --autodown +Like autozoom, but scale down only. +.TP +.B -u +Randomize the order of the filenames. +.TP +.B -e +Enable editing commands. +.TP +.B -b +create backup files (when editing images). +.TP +.B -p +preserve timestamps (when editing images). +.TP +.B --comments +Display comment tags (if present) instead of the filename. Probaby +only useful if you added reasonable comments yourself (using wrjpgcom +for example), otherwise you likely just find texts pointing to the +software which created the image. +.SH KEYS +.nf +cursor keys scroll large images ++, - zoom in/out +ESQ, Q quit +PgUp previous image +PgDn, Space next image +Return next image, write the filename of the current + image to stdout. +P pause the slideshow (if started with -t, toggle) +<number>g jump to image #<number> +.fi +.P +The Return vs. Space key thing can be used to create a file list while +reviewing the images and use the list for batch processing later on. +.SH EDIT IMAGE +fbi also provides some very basic image editing facilities. You have +to start fbi with the -e switch to use them. +.P +.nf +Shift+D delete image +R rotate 90° clockwise +L rotate 90° counter-clock wise +.fi +.P +The delete function actually wants a capital letter 'D', thus you have +to type Shift+D. This is done to avoid deleting images by mistake +because there are no safety bells: If you ask fbi to delete the image, +it will be deleted without questions asked. +.P +The rotate function actually works for JPEG images only because it +calls the jpegtran command to perform a lossless rotation if the image. +It is especially useful if you review the images of your digital +camera. +.SH COMMON PROBLEMS +.B fbi +needs rw access to the framebuffer devices (/dev/fbN), i.e you (our +your admin) have to make sure fbi can open the devices in rw mode. +The IMHO most elegant way is to use pam_console (see +/etc/security/console.perms) to chown the devices to the user logged +in on the console. Another way is to create some group, chown the +special files to that group and put the users which are allowed to use +the framebuffer device into the group. You can also make the special +files world writable, but be aware of the security implications this +has. On a private box it might be fine to handle it this way +througth. +.P +.B fbi +also needs access to the linux console (i.e. /dev/ttyN) for sane +console switch handling. That is obviously no problem for console +logins, but any kind of a pseudo tty (xterm, ssh, screen, ...) will +.B not +work. +.SH SEE ALSO +fbset(1), convert(1), jpegtran(1) +.SH AUTHOR +Gerd Knorr <kraxel@bytesex.org> +.SH COPYRIGHT +Copyright (C) 1999-2000 Gerd Knorr <kraxel@bytesex.org> +.P +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. +.P +This program is distributed in the hope that 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. +.P +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., 675 Mass Ave, Cambridge, MA 02139, USA. diff --git a/fbtools.c b/fbtools.c new file mode 100644 index 0000000..a03f4d3 --- /dev/null +++ b/fbtools.c @@ -0,0 +1,523 @@ +/* + * some generic framebuffer device stuff + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <termios.h> +#include <signal.h> +#include <errno.h> +#include <setjmp.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/wait.h> +#include <sys/stat.h> + +#include <linux/kd.h> +#include <linux/vt.h> +#include <linux/fb.h> + +#include <asm/page.h> + +#include "fbtools.h" + +/* -------------------------------------------------------------------- */ +/* exported stuff */ + +struct fb_fix_screeninfo fb_fix; +struct fb_var_screeninfo fb_var; +unsigned char *fb_mem; +int fb_mem_offset = 0; +int fb_switch_state = FB_ACTIVE; + +/* -------------------------------------------------------------------- */ +/* internal variables */ + +static int fb,tty; +#if 0 +static int bpp,black,white; +#endif + +static int orig_vt_no = 0; +static struct vt_mode vt_mode; + +static int kd_mode; +static struct vt_mode vt_omode; +static struct termios term; +static struct fb_var_screeninfo fb_ovar; +static unsigned short ored[256], ogreen[256], oblue[256]; +static struct fb_cmap ocmap = { 0, 256, ored, ogreen, oblue }; + +/* -------------------------------------------------------------------- */ +/* devices */ + +struct DEVS { + char *fb0; + char *fbnr; + char *ttynr; +}; + +struct DEVS devs_default = { + fb0: "/dev/fb0", + fbnr: "/dev/fb%d", + ttynr: "/dev/tty%d", +}; +struct DEVS devs_devfs = { + fb0: "/dev/fb/0", + fbnr: "/dev/fb/%d", + ttynr: "/dev/vc/%d", +}; +struct DEVS *devices; + +static void dev_init(void) +{ + struct stat dummy; + + if (NULL != devices) + return; + if (0 == stat("/dev/.devfsd",&dummy)) + devices = &devs_devfs; + else + devices = &devs_default; +} + +/* -------------------------------------------------------------------- */ +/* console switching */ + +extern int debug; + +static void +fb_switch_signal(int signal) +{ + if (signal == SIGUSR1) { + /* release */ + fb_switch_state = FB_REL_REQ; + if (debug) + write(2,"vt: SIGUSR1\n",12); + } + if (signal == SIGUSR2) { + /* acquisition */ + fb_switch_state = FB_ACQ_REQ; + if (debug) + write(2,"vt: SIGUSR2\n",12); + } +} + +void +fb_switch_release() +{ + ioctl(tty, VT_RELDISP, 1); + fb_switch_state = FB_INACTIVE; + if (debug) + write(2,"vt: release\n",12); +} + +void +fb_switch_acquire() +{ + ioctl(tty, VT_RELDISP, VT_ACKACQ); + fb_switch_state = FB_ACTIVE; + if (debug) + write(2,"vt: acquire\n",12); +} + +int +fb_switch_init() +{ + struct sigaction act,old; + + memset(&act,0,sizeof(act)); + act.sa_handler = fb_switch_signal; + sigemptyset(&act.sa_mask); + sigaction(SIGUSR1,&act,&old); + sigaction(SIGUSR2,&act,&old); + + if (-1 == ioctl(tty,VT_GETMODE, &vt_mode)) { + perror("ioctl VT_GETMODE"); + exit(1); + } + vt_mode.mode = VT_PROCESS; + vt_mode.waitv = 0; + vt_mode.relsig = SIGUSR1; + vt_mode.acqsig = SIGUSR2; + + if (-1 == ioctl(tty,VT_SETMODE, &vt_mode)) { + perror("ioctl VT_SETMODE"); + exit(1); + } + return 0; +} + +/* -------------------------------------------------------------------- */ +/* initialisation & cleanup */ + +void +fb_memset (void *addr, int c, size_t len) +{ +#if 1 /* defined(__powerpc__) */ + unsigned int i, *p; + + i = (c & 0xff) << 8; + i |= i << 16; + len >>= 2; + for (p = addr; len--; p++) + *p = i; +#else + memset(addr, c, len); +#endif +} + +static int +fb_setmode(char *name) +{ + FILE *fp; + char line[80],label[32],value[16]; + int geometry=0, timings=0; + + /* load current values */ + if (-1 == ioctl(fb,FBIOGET_VSCREENINFO,&fb_var)) { + perror("ioctl FBIOGET_VSCREENINFO"); + exit(1); + } + + if (NULL == name) + return -1; + if (NULL == (fp = fopen("/etc/fb.modes","r"))) + return -1; + while (NULL != fgets(line,79,fp)) { + if (1 == sscanf(line, "mode \"%31[^\"]\"",label) && + 0 == strcmp(label,name)) { + /* fill in new values */ + fb_var.sync = 0; + fb_var.vmode = 0; + while (NULL != fgets(line,79,fp) && + NULL == strstr(line,"endmode")) { + if (5 == sscanf(line," geometry %d %d %d %d %d", + &fb_var.xres,&fb_var.yres, + &fb_var.xres_virtual,&fb_var.yres_virtual, + &fb_var.bits_per_pixel)) + geometry = 1; + if (7 == sscanf(line," timings %d %d %d %d %d %d %d", + &fb_var.pixclock, + &fb_var.left_margin, &fb_var.right_margin, + &fb_var.upper_margin, &fb_var.lower_margin, + &fb_var.hsync_len, &fb_var.vsync_len)) + timings = 1; + if (1 == sscanf(line, " hsync %15s",value) && + 0 == strcasecmp(value,"high")) + fb_var.sync |= FB_SYNC_HOR_HIGH_ACT; + if (1 == sscanf(line, " vsync %15s",value) && + 0 == strcasecmp(value,"high")) + fb_var.sync |= FB_SYNC_VERT_HIGH_ACT; + if (1 == sscanf(line, " csync %15s",value) && + 0 == strcasecmp(value,"high")) + fb_var.sync |= FB_SYNC_COMP_HIGH_ACT; + if (1 == sscanf(line, " extsync %15s",value) && + 0 == strcasecmp(value,"true")) + fb_var.sync |= FB_SYNC_EXT; + if (1 == sscanf(line, " laced %15s",value) && + 0 == strcasecmp(value,"true")) + fb_var.vmode |= FB_VMODE_INTERLACED; + if (1 == sscanf(line, " double %15s",value) && + 0 == strcasecmp(value,"true")) + fb_var.vmode |= FB_VMODE_DOUBLE; + } + /* ok ? */ + if (!geometry || !timings) + return -1; + /* set */ + fb_var.xoffset = 0; + fb_var.yoffset = 0; + if (-1 == ioctl(fb,FBIOPUT_VSCREENINFO,&fb_var)) + perror("ioctl FBIOPUT_VSCREENINFO"); + /* look what we have now ... */ + if (-1 == ioctl(fb,FBIOGET_VSCREENINFO,&fb_var)) { + perror("ioctl FBIOGET_VSCREENINFO"); + exit(1); + } + return 0; + } + } + return -1; +} + +static void +fb_setvt(int vtno) +{ + struct vt_stat vts; + char vtname[12]; + + if (vtno < 0) { + if (-1 == ioctl(tty,VT_OPENQRY, &vtno) || vtno == -1) { + perror("ioctl VT_OPENQRY"); + exit(1); + } + } + + vtno &= 0xff; + sprintf(vtname, devices->ttynr, vtno); + chown(vtname, getuid(), getgid()); + if (-1 == access(vtname, R_OK | W_OK)) { + fprintf(stderr,"access %s: %s\n",vtname,strerror(errno)); + exit(1); + } + switch (fork()) { + case 0: + break; + case -1: + perror("fork"); + exit(1); + default: + exit(0); + } + close(tty); + close(0); + close(1); + close(2); + setsid(); + open(vtname,O_RDWR); + dup(0); + dup(0); + + if (-1 == ioctl(tty,VT_GETSTATE, &vts)) { + perror("ioctl VT_GETSTATE"); + exit(1); + } + orig_vt_no = vts.v_active; + if (-1 == ioctl(tty,VT_ACTIVATE, vtno)) { + perror("ioctl VT_ACTIVATE"); + exit(1); + } + if (-1 == ioctl(tty,VT_WAITACTIVE, vtno)) { + perror("ioctl VT_WAITACTIVE"); + exit(1); + } +} + +/* Hmm. radeonfb needs this. matroxfb doesn't. */ +static int fb_activate_current(int tty) +{ + struct vt_stat vts; + + if (-1 == ioctl(tty,VT_GETSTATE, &vts)) { + perror("ioctl VT_GETSTATE"); + return -1; + } + if (-1 == ioctl(tty,VT_ACTIVATE, vts.v_active)) { + perror("ioctl VT_ACTIVATE"); + return -1; + } + if (-1 == ioctl(tty,VT_WAITACTIVE, vts.v_active)) { + perror("ioctl VT_WAITACTIVE"); + return -1; + } + return 0; +} + +int +fb_init(char *device, char *mode, int vt) +{ + char fbdev[16]; + struct vt_stat vts; + + dev_init(); + tty = 0; + if (vt != 0) + fb_setvt(vt); + + if (-1 == ioctl(tty,VT_GETSTATE, &vts)) { + fprintf(stderr,"ioctl VT_GETSTATE: %s (not a linux console?)\n", + strerror(errno)); + exit(1); + } + + if (NULL == device) { + device = getenv("FRAMEBUFFER"); + if (NULL == device) { + struct fb_con2fbmap c2m; + if (-1 == (fb = open(devices->fb0,O_RDWR /* O_WRONLY */,0))) { + fprintf(stderr,"open %s: %s\n",devices->fb0,strerror(errno)); + exit(1); + } + c2m.console = vts.v_active; + if (-1 == ioctl(fb, FBIOGET_CON2FBMAP, &c2m)) { + perror("ioctl FBIOGET_CON2FBMAP"); + exit(1); + } + close(fb); + fprintf(stderr,"map: vt%02d => fb%d\n", + c2m.console,c2m.framebuffer); + sprintf(fbdev,devices->fbnr,c2m.framebuffer); + device = fbdev; + } + } + + /* get current settings (which we have to restore) */ + if (-1 == (fb = open(device,O_RDWR /* O_WRONLY */))) { + fprintf(stderr,"open %s: %s\n",device,strerror(errno)); + exit(1); + } + if (-1 == ioctl(fb,FBIOGET_VSCREENINFO,&fb_ovar)) { + perror("ioctl FBIOGET_VSCREENINFO"); + exit(1); + } + if (-1 == ioctl(fb,FBIOGET_FSCREENINFO,&fb_fix)) { + perror("ioctl FBIOGET_FSCREENINFO"); + exit(1); + } + if (fb_ovar.bits_per_pixel == 8 || + fb_fix.visual == FB_VISUAL_DIRECTCOLOR) { + if (-1 == ioctl(fb,FBIOGETCMAP,&ocmap)) { + perror("ioctl FBIOGETCMAP"); + exit(1); + } + } + if (-1 == ioctl(tty,KDGETMODE, &kd_mode)) { + perror("ioctl KDGETMODE"); + exit(1); + } + if (-1 == ioctl(tty,VT_GETMODE, &vt_omode)) { + perror("ioctl VT_GETMODE"); + exit(1); + } + tcgetattr(tty, &term); + + /* switch mode */ + fb_setmode(mode); + + /* checks & initialisation */ + if (-1 == ioctl(fb,FBIOGET_FSCREENINFO,&fb_fix)) { + perror("ioctl FBIOGET_FSCREENINFO"); + exit(1); + } + if (fb_fix.type != FB_TYPE_PACKED_PIXELS) { + fprintf(stderr,"can handle only packed pixel frame buffers\n"); + goto err; + } +#if 0 + switch (fb_var.bits_per_pixel) { + case 8: + white = 255; black = 0; bpp = 1; + break; + case 15: + case 16: + if (fb_var.green.length == 6) + white = 0xffff; + else + white = 0x7fff; + black = 0; bpp = 2; + break; + case 24: + white = 0xffffff; black = 0; bpp = fb_var.bits_per_pixel/8; + break; + case 32: + white = 0xffffff; black = 0; bpp = fb_var.bits_per_pixel/8; + fb_setpixels = fb_setpixels4; + break; + default: + fprintf(stderr, "Oops: %i bit/pixel ???\n", + fb_var.bits_per_pixel); + goto err; + } +#endif + fb_mem_offset = (unsigned long)(fb_fix.smem_start) & (~PAGE_MASK); + fb_mem = mmap(NULL,fb_fix.smem_len+fb_mem_offset, + PROT_READ|PROT_WRITE,MAP_SHARED,fb,0); + if (-1L == (long)fb_mem) { + perror("mmap"); + goto err; + } + /* move viewport to upper left corner */ + if (fb_var.xoffset != 0 || fb_var.yoffset != 0) { + fb_var.xoffset = 0; + fb_var.yoffset = 0; + if (-1 == ioctl(fb,FBIOPAN_DISPLAY,&fb_var)) { + perror("ioctl FBIOPAN_DISPLAY"); + goto err; + } + } + if (-1 == ioctl(tty,KDSETMODE, KD_GRAPHICS)) { + perror("ioctl KDSETMODE"); + goto err; + } + fb_activate_current(tty); + + /* cls */ + fb_memset(fb_mem+fb_mem_offset,0,fb_fix.smem_len); + return fb; + + err: + fb_cleanup(); + exit(1); +} + +void +fb_cleanup(void) +{ + /* restore console */ + if (-1 == ioctl(fb,FBIOPUT_VSCREENINFO,&fb_ovar)) + perror("ioctl FBIOPUT_VSCREENINFO"); + if (-1 == ioctl(fb,FBIOGET_FSCREENINFO,&fb_fix)) + perror("ioctl FBIOGET_FSCREENINFO"); + if (fb_ovar.bits_per_pixel == 8 || + fb_fix.visual == FB_VISUAL_DIRECTCOLOR) { + if (-1 == ioctl(fb,FBIOPUTCMAP,&ocmap)) + perror("ioctl FBIOPUTCMAP"); + } + close(fb); + + if (-1 == ioctl(tty,KDSETMODE, kd_mode)) + perror("ioctl KDSETMODE"); + if (-1 == ioctl(tty,VT_SETMODE, &vt_omode)) + perror("ioctl VT_SETMODE"); + if (orig_vt_no && -1 == ioctl(tty, VT_ACTIVATE, orig_vt_no)) + perror("ioctl VT_ACTIVATE"); + if (orig_vt_no && -1 == ioctl(tty, VT_WAITACTIVE, orig_vt_no)) + perror("ioctl VT_WAITACTIVE"); + tcsetattr(tty, TCSANOW, &term); + close(tty); +} + +/* -------------------------------------------------------------------- */ +/* handle fatal errors */ + +static jmp_buf fb_fatal_cleanup; + +static void +fb_catch_exit_signal(int signal) +{ + siglongjmp(fb_fatal_cleanup,signal); +} + +void +fb_catch_exit_signals(void) +{ + struct sigaction act,old; + int termsig; + + memset(&act,0,sizeof(act)); + act.sa_handler = fb_catch_exit_signal; + sigemptyset(&act.sa_mask); + sigaction(SIGINT, &act,&old); + sigaction(SIGQUIT,&act,&old); + sigaction(SIGTERM,&act,&old); + + sigaction(SIGABRT,&act,&old); + sigaction(SIGTSTP,&act,&old); + + sigaction(SIGBUS, &act,&old); + sigaction(SIGILL, &act,&old); + sigaction(SIGSEGV,&act,&old); + + if (0 == (termsig = sigsetjmp(fb_fatal_cleanup,0))) + return; + + /* cleanup */ + fb_cleanup(); + fprintf(stderr,"Oops: %s\n",sys_siglist[termsig]); + exit(42); +} diff --git a/fbtools.h b/fbtools.h new file mode 100644 index 0000000..469675c --- /dev/null +++ b/fbtools.h @@ -0,0 +1,23 @@ +#define FB_ACTIVE 0 +#define FB_REL_REQ 1 +#define FB_INACTIVE 2 +#define FB_ACQ_REQ 3 + +/* info about videomode - yes I know, quick & dirty... */ +extern struct fb_fix_screeninfo fb_fix; +extern struct fb_var_screeninfo fb_var; +extern unsigned char *fb_mem; +extern int fb_mem_offset; +extern int fb_switch_state; + +/* init + cleanup */ +int fb_probe(void); +int fb_init(char *device, char *mode, int vt); +void fb_cleanup(void); +void fb_catch_exit_signals(void); +void fb_memset(void *addr, int c, size_t len); + +/* console switching */ +int fb_switch_init(void); +void fb_switch_release(void); +void fb_switch_acquire(void); diff --git a/filebutton.c b/filebutton.c new file mode 100644 index 0000000..3f922e2 --- /dev/null +++ b/filebutton.c @@ -0,0 +1,934 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> +#include <Xm/Xm.h> +#include <Xm/Label.h> +#include <Xm/RowColumn.h> +#include <Xm/PushB.h> +#include <Xm/Transfer.h> +#include <Xm/TransferP.h> +#include <Xm/Container.h> +#include <Xm/IconG.h> +#include <Xm/ScrolledW.h> +#include <Xm/SelectioB.h> +#include <Xm/Text.h> + +#include "RegEdit.h" +#include "list.h" +#include "ida.h" +#include "x11.h" +#include "icons.h" +#include "readers.h" +#include "filter.h" +#include "viewer.h" +#include "selections.h" +#include "filebutton.h" +#include "fileops.h" +#include "idaconfig.h" + +/*----------------------------------------------------------------------*/ + +struct fileinfo { + struct list_head list; + char *path; + struct ida_image_info img; + Pixmap small; + Pixmap large; +}; + +static LIST_HEAD(pcache); +static LIST_HEAD(pqueue); +static LIST_HEAD(files); +static XtWorkProcId pproc; + +/*----------------------------------------------------------------------*/ + +static struct fileinfo* +fileinfo_cache_add(char *path, struct ida_image_info *img, + Pixmap small, Pixmap large) +{ + struct fileinfo *item; + + item = malloc(sizeof(*item)); + memset(item,0,sizeof(*item)); + item->path = strdup(path); + item->img = *img; + item->small = small; + item->large = large; + list_add_tail(&item->list,&pcache); + return item; +} + +static void fileinfo_cache_del(char *path) +{ + struct list_head *item; + struct fileinfo *b; + + list_for_each(item,&pcache) { + b = list_entry(item,struct fileinfo,list); + if (0 == strcmp(path,b->path)) { + list_del(&b->list); + free(b); + return; + } + } +} + +static struct fileinfo* fileinfo_cache_get(char *path) +{ + struct list_head *item; + struct fileinfo *b; + + list_for_each(item,&pcache) { + b = list_entry(item,struct fileinfo,list); + if (0 == strcmp(path,b->path)) + return b; + } + return 0; +} + +/*----------------------------------------------------------------------*/ + +static void +fileinfo_cleanup(struct file_button *file) +{ + switch (file->state) { + case 1: + file->loader->done(file->wdata); + break; + case 2: + desc_resize.done(file->wdata); + break; + } + file->state = 0; + + if (file->wimg.data) { + free(file->wimg.data); + file->wimg.data = NULL; + } + if (file->simg.data) { + free(file->simg.data); + file->simg.data = NULL; + } + if (!list_empty(&file->queue)) { + list_del_init(&file->queue); + } +} + +static void fileinfo_details(struct file_button *file) +{ + struct ida_image_info *img; + struct ida_extra *extra; + char buf[80]; + + img = &file->info->img; + snprintf(buf, sizeof(buf), "%dx%d", + img->thumbnail ? img->real_width : img->width, + img->thumbnail ? img->real_height : img->height); + XmStringFree(file->details[DETAIL_SIZE]); + file->details[DETAIL_SIZE] = XmStringGenerate(buf, NULL, XmMULTIBYTE_TEXT,NULL); + + extra = load_find_extra(img, EXTRA_COMMENT); + if (extra) { + XmStringFree(file->details[DETAIL_COMMENT]); + file->details[DETAIL_COMMENT] = + XmStringGenerate(extra->data, NULL, XmMULTIBYTE_TEXT,NULL); + } + + XtVaSetValues(file->widget, + XmNdetail, file->details, + XmNdetailCount, DETAIL_COUNT, + NULL); +} + +static Boolean +fileinfo_loader(XtPointer clientdata) +{ + struct op_resize_parm resize; + struct ida_rect rect; + struct list_head *item; + struct file_button *file; + struct fileinfo *info; + Pixmap pix; + char blk[512]; + FILE *fp; + float xs,ys,scale; + struct ida_image timg; + void *data; + + if (list_empty(&pqueue)) { + /* nothing to do */ + pproc = 0; + return TRUE; + } + file = list_entry(pqueue.next, struct file_button, queue); + + switch (file->state) { + case 0: + /* ------------------- new file -------------------- */ + info = fileinfo_cache_get(file->filename); + if (info) { + file_set_info(file,info); + goto next; + } + + /* open file */ + if (NULL == (fp = fopen(file->filename, "r"))) { + if (debug) + fprintf(stderr,"open %s: %s\n",file->filename, + strerror(errno)); + goto unknown; + } + if (debug) + fprintf(stderr,"OPENED: %s\n",file->filename); + fstat(fileno(fp),&file->st); + + /* pick loader */ + memset(blk,0,sizeof(blk)); + fread(blk,1,sizeof(blk),fp); + rewind(fp); + list_for_each(item,&loaders) { + file->loader = list_entry(item, struct ida_loader, list); + if (NULL == file->loader->magic) + continue; + if (0 == memcmp(blk+file->loader->moff,file->loader->magic, + file->loader->mlen)) + break; + file->loader = NULL; + } + if (NULL == file->loader) { + if (debug) + fprintf(stderr,"%s: unknown format\n",file->filename); + fclose(fp); + goto unknown; + } + + /* load image */ + file->wdata = file->loader->init(fp, file->filename, + 0, &file->wimg.i, 1); + if (NULL == file->wdata) { + if (debug) + fprintf(stderr,"loading %s [%s] FAILED\n", + file->filename, file->loader->name); + goto unknown; + } + + file->wimg.data = malloc(file->wimg.i.width * file->wimg.i.height * 3); + file->state = 1; + file->y = 0; + return FALSE; + + case 1: + /* ------------------- loading file -------------------- */ + if (file->y < file->wimg.i.height) { + file->loader->read(file->wimg.data + + 3 * file->y * file->wimg.i.width, + file->y, file->wdata); + file->y++; + return FALSE; + } + file->loader->done(file->wdata); + if (debug) + fprintf(stderr,"LOADED: %s [%dx%d]\n", + file->filename, file->wimg.i.width, file->wimg.i.height); + + /* resize image */ + xs = (float)GET_ICON_LARGE() / file->wimg.i.width; + ys = (float)GET_ICON_LARGE() / file->wimg.i.height; + scale = (xs < ys) ? xs : ys; + resize.width = file->wimg.i.width * scale; + resize.height = file->wimg.i.height * scale; + if (0 == resize.width) + resize.width = 1; + if (0 == resize.height) + resize.height = 1; + + rect.x1 = 0; + rect.x2 = file->wimg.i.width; + rect.y1 = 0; + rect.y2 = file->wimg.i.height; + file->wdata = desc_resize.init(&file->wimg,&rect,&file->simg.i,&resize); + file->simg.data = malloc(file->simg.i.width * file->simg.i.height * 3); + + file->state = 2; + file->y = 0; + return FALSE; + + case 2: + /* ------------------- scaling file -------------------- */ + if (file->y < file->simg.i.height) { + desc_resize.work(&file->wimg,&rect, file->simg.data + + 3 * file->simg.i.width * file->y, + file->y, file->wdata); + file->y++; + return FALSE; + } + desc_resize.done(file->wdata); + if (debug) + fprintf(stderr,"SCALED: %s [%dx%d]\n", + file->filename,file->simg.i.width,file->simg.i.height); + + /* scale once more (small icon) */ + xs = (float)GET_ICON_SMALL() / file->simg.i.width; + ys = (float)GET_ICON_SMALL() / file->simg.i.height; + scale = (xs < ys) ? xs : ys; + resize.width = file->simg.i.width * scale; + resize.height = file->simg.i.height * scale; + if (0 == resize.width) + resize.width = 1; + if (0 == resize.height) + resize.height = 1; + + rect.x1 = 0; + rect.x2 = file->simg.i.width; + rect.y1 = 0; + rect.y2 = file->simg.i.height; + data = desc_resize.init(&file->simg,&rect,&timg.i,&resize); + timg.data = malloc(timg.i.width * timg.i.height * 3); + + for (file->y = 0; file->y < timg.i.height; file->y++) + desc_resize.work(&file->simg,&rect, + timg.data + 3 * timg.i.width * file->y, + file->y, data); + desc_resize.done(data); + + /* build, cache + install pixmap */ + info = fileinfo_cache_add(file->filename,&file->wimg.i, + image_to_pixmap(&timg), + image_to_pixmap(&file->simg)); + file_set_info(file,info); + free(timg.data); + file->state = 0; + goto next; + + default: + /* shouldn't happen */ + fprintf(stderr,"Oops: %s:%d\n",__FILE__,__LINE__); + exit(1); + } + + unknown: + /* generic file icon */ + pix = XmGetPixmap(file->screen,"unknown",0,0); + file_set_icon(file,pix,pix); + + next: + fileinfo_cleanup(file); + return FALSE; +} + +/*----------------------------------------------------------------------*/ + +void fileinfo_queue(struct file_button *file) +{ + if (NULL == file->queue.next) + INIT_LIST_HEAD(&file->queue); + + if (!list_empty(&file->queue)) { + /* already queued */ + if (0 == file->state) + return; + fileinfo_cleanup(file); + } + + file->state = 0; + memset(&file->wimg,0,sizeof(file->wimg)); + memset(&file->simg,0,sizeof(file->simg)); + list_add_tail(&file->queue,&pqueue); + if (0 == pproc) + pproc = XtAppAddWorkProc(app_context,fileinfo_loader,NULL); +} + +void fileinfo_invalidate(char *filename) +{ + struct file_button *file; + struct list_head *item; + Pixmap pix; + + if (debug) + fprintf(stderr,"fileinfo invalidate: %s\n",filename); + fileinfo_cache_del(filename); + + list_for_each(item,&files) { + file = list_entry(item, struct file_button, global); + if (0 != strcmp(file->filename,filename)) + continue; + if (debug) + fprintf(stderr," %p %s\n",file,filename); + file->info = NULL; + pix = XmGetPixmap(file->screen,"file",0,0); + file_set_icon(file,pix,pix); + fileinfo_queue(file); + } +} + +/*----------------------------------------------------------------------*/ + +static void +container_ops_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + Widget container = clientdata; + WidgetList children; + Cardinal nchildren,i; + + XtVaGetValues(container, + XmNselectedObjects,&children, + XmNselectedObjectCount,&nchildren, + NULL); + for (i = 0; i < nchildren; i++) { + struct stat st; + if (-1 == stat(XtName(children[i]),&st)) + continue; + if (!S_ISREG(st.st_mode)) + continue; + job_submit(XtName(widget),XtName(children[i]), NULL); + } +} + +static void +comment_box_cb(Widget widget, XtPointer clientdata, XtPointer calldata) +{ + Widget container = clientdata; + XmSelectionBoxCallbackStruct *cd = calldata; + WidgetList children; + Cardinal nchildren,i; + Widget text; + char *comment; + + if (XmCR_OK == cd->reason) { + /* TODO */ + text = XmSelectionBoxGetChild(widget,XmDIALOG_TEXT); + comment = XmTextGetString(text); + XtVaGetValues(container, + XmNselectedObjects,&children, + XmNselectedObjectCount,&nchildren, + NULL); + for (i = 0; i < nchildren; i++) { + struct stat st; + if (-1 == stat(XtName(children[i]),&st)) + continue; + if (!S_ISREG(st.st_mode)) + continue; + job_submit("comment",XtName(children[i]), comment); + } + } + XtDestroyWidget(XtParent(widget)); +} + +static void +container_comment_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + Widget container = clientdata; + Widget box,text; + WidgetList children; + Cardinal nchildren; + static struct fileinfo *info; + struct ida_extra *extra; + char *comment = ""; + + XtVaGetValues(container, + XmNselectedObjects,&children, + XmNselectedObjectCount,&nchildren, + NULL); + switch (nchildren) { + case 0: + /* nothing to do */ + return; + case 1: + /* get old comment */ + info = fileinfo_cache_get(XtName(children[0])); + if (!info) + /* not a image */ + return; + extra = load_find_extra(&info->img, EXTRA_COMMENT); + if (extra) + comment = extra->data; + break; + default: + /* start with a empty comment */ + break; + } + + /* dialog box */ + box = XmCreatePromptDialog(container,"comment",NULL,0); + XtUnmanageChild(XmSelectionBoxGetChild(box,XmDIALOG_HELP_BUTTON)); + XmdRegisterEditres(XtParent(box)); + XtAddCallback(box,XmNokCallback,comment_box_cb,clientdata); + XtAddCallback(box,XmNcancelCallback,comment_box_cb,clientdata); + XtAddCallback(XtParent(box),XmNdestroyCallback,destroy_cb,XtParent(box)); + + text = XmSelectionBoxGetChild(box,XmDIALOG_TEXT); + XmTextSetString(text,comment); + XmTextSetInsertionPosition(text,strlen(comment)); + XtManageChild(box); +} + +void container_menu_ops(Widget menu, Widget container) +{ + Widget push; + + push = XtVaCreateManagedWidget("rotexif",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,container_ops_cb,container); + push = XtVaCreateManagedWidget("rotcw",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,container_ops_cb,container); + push = XtVaCreateManagedWidget("rotccw",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,container_ops_cb,container); + push = XtVaCreateManagedWidget("comment",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,container_comment_cb,container); +} + +/*----------------------------------------------------------------------*/ + +void +container_resize_eh(Widget widget, XtPointer clientdata, XEvent *event, Boolean *d) +{ + Widget clip,scroll,container; + Dimension width, height, ch; + + clip = widget; + scroll = XtParent(widget); + XtVaGetValues(scroll,XmNworkWindow,&container,NULL); + + XtVaGetValues(clip, + XtNwidth, &width, + XtNheight, &height, + NULL); + XtVaGetValues(container, + XtNheight, &ch, + NULL); + if (ch < height-5) + ch = height-5; + XtVaSetValues(container, + XtNwidth, width-5, + XtNheight,ch, + NULL); + container_relayout(container); +} + +void +container_spatial_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + Widget container = clientdata; + WidgetList children; + Cardinal nchildren; + + XtVaSetValues(container, + XmNlayoutType, XmSPATIAL, + XmNspatialStyle, XmNONE, + NULL); + nchildren = XmContainerGetItemChildren(container,NULL,&children); + if (nchildren) { + XtFree((XtPointer)children); + /* FIXME: Hmm, why ??? */ + XtVaSetValues(container, + XmNentryViewType, XmLARGE_ICON, + NULL); + } + container_relayout(container); +} + +void container_detail_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + Widget container = clientdata; + + XtVaSetValues(container, + XmNlayoutType, XmDETAIL, + XmNentryViewType, XmSMALL_ICON, + NULL); + container_relayout(container); +} + +void +container_traverse_cb(Widget scroll, XtPointer clientdata, XtPointer call_data) +{ + XmTraverseObscuredCallbackStruct *cd = call_data; + + if (cd->reason == XmCR_OBSCURED_TRAVERSAL) + XmScrollVisible(scroll, cd->traversal_destination, 25, 25); +} + +void +container_convert_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XmConvertCallbackStruct *ccs = call_data; + char *file = NULL; + Atom *targs; + int i,n,len; + WidgetList children; + Cardinal nchildren; + + if (ccs->location_data) { + Widget item = ccs->location_data; + children = &item; + nchildren = 1; + } else { + XtVaGetValues(widget, + XmNselectedObjects,&children, + XmNselectedObjectCount,&nchildren, + NULL); + } + + if (debug) { + char *t = !ccs->target ? NULL : XGetAtomName(dpy,ccs->target); + char *s = !ccs->selection ? NULL : XGetAtomName(dpy,ccs->selection); + fprintf(stderr,"drag: target=%s selection=%s [%d files,%p]\n", + t, s, nchildren, ccs->location_data); + if (t) XFree(t); + if (s) XFree(s); + } + + if ((ccs->target == XA_TARGETS) || + (ccs->target == _MOTIF_CLIPBOARD_TARGETS) || + (ccs->target == _MOTIF_EXPORT_TARGETS)) { + targs = (Atom*)XtMalloc(sizeof(Atom)*8); + n = 0; + if (nchildren >= 1) { + targs[n++] = MIME_TEXT_URI_LIST; + } + if (1 == nchildren) { + targs[n++] = XA_FILE_NAME; + targs[n++] = XA_FILE; + targs[n++] = _NETSCAPE_URL; + targs[n++] = XA_STRING; + } + ccs->value = targs; + ccs->length = n; + ccs->type = XA_ATOM; + ccs->format = 32; + ccs->status = XmCONVERT_MERGE; + return; + } + + if (ccs->target == _MOTIF_DEFERRED_CLIPBOARD_TARGETS) { + targs = (Atom*)XtMalloc(sizeof(Atom)*8); + n = 0; + ccs->value = targs; + ccs->length = n; + ccs->type = XA_ATOM; + ccs->format = 32; + ccs->status = XmCONVERT_DONE; + } + + if ((ccs->target == _MOTIF_LOSE_SELECTION) || + (ccs->target == XA_DONE)) { + /* free stuff */ + ccs->value = NULL; + ccs->length = 0; + ccs->type = XA_INTEGER; + ccs->format = 32; + ccs->status = XmCONVERT_DONE; + return; + } + + if (ccs->target == XA_FILE_NAME || + ccs->target == XA_FILE || + ccs->target == XA_STRING) { + file = XtMalloc(strlen(XtName(children[0])+1)); + strcpy(file,XtName(children[0])); + ccs->value = file; + ccs->length = strlen(file); + ccs->type = XA_STRING; + ccs->format = 8; + ccs->status = XmCONVERT_DONE; + return; + } + + if (ccs->target == _NETSCAPE_URL || + ccs->target == MIME_TEXT_URI_LIST) { + for (i = 0, len = 0; i < nchildren; i++) + len += strlen(XtName(children[i])); + file = XtMalloc(len + 8 * nchildren); + for (i = 0, len = 0; i < nchildren; i++) + len += sprintf(file+len,"file:%s\n",XtName(children[i])); + ccs->value = file; + ccs->length = len; + ccs->type = XA_STRING; + ccs->format = 8; + ccs->status = XmCONVERT_DONE; + return; + } + + ccs->status = XmCONVERT_DEFAULT; +} + +static void +container_copy_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + Widget container = clientdata; + XmeClipboardSource(container,XmCOPY,XtLastTimestampProcessed(dpy)); +} + +static void +container_paste_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + Widget container = clientdata; + XmeClipboardSink(container,XmCOPY,NULL); +} + +static void +container_del_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + Widget container = clientdata; + WidgetList children,list; + Cardinal nchildren,i; + + XtVaGetValues(container, + XmNselectedObjects,&children, + XmNselectedObjectCount,&nchildren, + NULL); + list = malloc(sizeof(Widget*)*nchildren); + memcpy(list,children,sizeof(Widget*)*nchildren); + + XtVaSetValues(container, + XmNselectedObjectCount,0, + NULL); + XtUnmanageChildren(list,nchildren); + for (i = 0; i < nchildren; i++) + XtDestroyWidget(list[i]); + free(list); +} + +void container_menu_edit(Widget menu, Widget container, + int cut, int copy, int paste, int del) +{ + Widget push; + +#if 0 + if (cut) { + push = XtVaCreateManagedWidget("cut",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback, + container_cut_cb,container); + } +#endif + if (copy) { + push = XtVaCreateManagedWidget("copy",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback, + container_copy_cb,container); + } + if (paste) { + push = XtVaCreateManagedWidget("paste",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback, + container_paste_cb,container); + } + if (del) { + push = XtVaCreateManagedWidget("del",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback, + container_del_cb,container); + } +} + +void container_menu_view(Widget menu, Widget container) +{ + Widget push; + + push = XtVaCreateManagedWidget("details",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback, + container_detail_cb,container); + push = XtVaCreateManagedWidget("spatial",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback, + container_spatial_cb,container); +} + +void +container_relayout(Widget container) +{ + Widget clip = XtParent(container); + WidgetList children; + Cardinal nchildren; + Dimension wwidth,wheight; + Dimension iwidth,iheight; + Position x,y; + unsigned char layout,style; + int i,margin = 10; + + XtVaGetValues(container, + XmNlayoutType, &layout, + XmNspatialStyle, &style, + NULL); + if (XmSPATIAL != layout || XmNONE != style) { + XmContainerRelayout(container); + return; + } + + nchildren = XmContainerGetItemChildren(container,NULL,&children); + XtVaGetValues(clip, + XtNwidth, &wwidth, + XtNheight, &wheight, + NULL); + + wwidth -= 5; + x = margin; y = margin; + for (i = 0; i < nchildren; i++) { + if (!XtIsManaged(children[i])) + continue; + XtVaGetValues(children[i], + XtNwidth,&iwidth, + XtNheight,&iheight, + NULL); + if (x > 0 && x + iwidth + margin > wwidth) { + /* new row */ + x = margin; y += iheight + margin; + } + XtVaSetValues(children[i], + XtNx,x, XtNy,y, + XmNpositionIndex,i, + NULL); + x += iwidth + margin; + } + + if (wheight < y + iheight + margin) + wheight = y + iheight + margin; + XtVaSetValues(container, + XtNwidth, wwidth, + XtNheight, wheight, + NULL); + if (nchildren) + XtFree((XtPointer)children); +} + +void +container_delwidgets(Widget container) +{ + WidgetList children; + Cardinal nchildren; + unsigned int i; + + /* delete widgets */ + XtVaSetValues(container, + XmNselectedObjectCount,0, + NULL); + nchildren = XmContainerGetItemChildren(container,NULL,&children); + XtUnmanageChildren(children,nchildren); + for (i = 0; i < nchildren; i++) + XtDestroyWidget(children[i]); + if (nchildren) + XtFree((XtPointer)children); +} + +/*----------------------------------------------------------------------*/ + +void file_set_icon(struct file_button *file, Pixmap s, Pixmap l) +{ + Pixmap large, small; + Pixel background; + + if (file->info) + return; + + XtVaGetValues(file->widget, XmNbackground,&background, NULL); + small = x11_icon_fit(DisplayOfScreen(file->screen), s, background, + GET_ICON_SMALL(), GET_ICON_SMALL()); + large = x11_icon_fit(DisplayOfScreen(file->screen), l, background, + GET_ICON_LARGE(), GET_ICON_LARGE()); + XtVaSetValues(file->widget, + XmNsmallIconPixmap, small, + XmNlargeIconPixmap, large, + NULL); + if (file->small) + XFreePixmap(DisplayOfScreen(file->screen),file->small); + if (file->large) + XFreePixmap(DisplayOfScreen(file->screen),file->large); + file->small = small; + file->large = large; +} + +void file_set_info(struct file_button *file, struct fileinfo *info) +{ + file->info = NULL; + file_set_icon(file,info->small,info->large); + file->info = info; + fileinfo_details(file); +} + +#if 0 +static void +file_copy_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct file_button *file = clientdata; + + XmeClipboardSource(file->widget,XmCOPY,XtLastTimestampProcessed(dpy)); +} +#endif + +/*----------------------------------------------------------------------*/ + +int file_cmp_alpha(const struct file_button *aa, + const struct file_button *bb) +{ + if (S_ISDIR(aa->st.st_mode) != S_ISDIR(bb->st.st_mode)) + return S_ISDIR(aa->st.st_mode) ? -1 : 1; + return strcmp(aa->basename,bb->basename); +} + +static void +file_destroy_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct file_button *file = clientdata; + int i; + + if (debug) + fprintf(stderr,"file: del %p [%s]\n",file,file->filename); + + if (NULL != file->queue.next) + fileinfo_cleanup(file); + + if (file->basename) + free(file->basename); + if (file->filename) + free(file->filename); + + XtVaSetValues(file->widget, + XmNsmallIconPixmap, XmUNSPECIFIED_PIXMAP, + XmNlargeIconPixmap, XmUNSPECIFIED_PIXMAP, + NULL); + if (file->small) + XFreePixmap(DisplayOfScreen(file->screen),file->small); + if (file->large) + XFreePixmap(DisplayOfScreen(file->screen),file->large); + + if (file->label) + XmStringFree(file->label); + for (i = 0; i < DETAIL_COUNT; i++) + if (file->details[i]) + XmStringFree(file->details[i]); + + list_del(&file->global); + list_del(&file->window); + free(file); +} + +int file_createwidgets(Widget parent, struct file_button *file) +{ + struct fileinfo *info; + Pixmap pix; + Arg args[8]; + int i, n = 0; + + if (debug) + fprintf(stderr,"file: new %p [%s]\n",file,file->filename); + + file->screen = XtScreen(parent); + file->label = XmStringGenerate(file->basename, NULL, XmMULTIBYTE_TEXT,NULL); + for (i = 0; i < DETAIL_COUNT; i++) + file->details[i] = XmStringGenerate("-", NULL, XmMULTIBYTE_TEXT,NULL); + + XtSetArg(args[n], XmNlabelString, file->label); n++; + XtSetArg(args[n], XmNdetail, file->details); n++; + XtSetArg(args[n], XmNdetailCount, DETAIL_COUNT); n++; + file->widget = XmCreateIconGadget(parent,file->filename,args,n); + XtAddCallback(file->widget,XtNdestroyCallback,file_destroy_cb,file); + list_add_tail(&file->global,&files); + + info = fileinfo_cache_get(file->filename); + if (info) { + file_set_info(file,info); + } else { + pix = XmGetPixmap(file->screen,"question",0,0); + file_set_icon(file,pix,pix); + } + return 0; +} diff --git a/filebutton.h b/filebutton.h new file mode 100644 index 0000000..4e5ebf1 --- /dev/null +++ b/filebutton.h @@ -0,0 +1,71 @@ +#include <sys/stat.h> +#include <Xm/Xm.h> +#include "list.h" + +#define DETAIL_SIZE 0 +#define DETAIL_COMMENT 1 +#define DETAIL_COUNT 2 + +struct fileinfo_cache; + +struct file_button { + /* file info */ + char *filename; + char *basename; + unsigned char d_type; + struct stat st; + struct fileinfo *info; + + /* Widget + other X11 stuff */ + Screen *screen; + Widget widget; + XmString label; + XmString details[DETAIL_COUNT]; + Pixmap small,large; + + /* lists */ + struct list_head global; + struct list_head window; + + /* private for file info + icon loader */ + struct list_head queue; + int state,y; + struct ida_loader *loader; + void *wdata; + struct ida_image wimg; + struct ida_image simg; +}; + +void fileinfo_queue(struct file_button *file); +void fileinfo_invalidate(char *filename); +void file_set_icon(struct file_button *file, Pixmap s, Pixmap l); +void file_set_info(struct file_button *file, struct fileinfo *info); + +/*----------------------------------------------------------------------*/ + +void container_detail_cb(Widget widget, XtPointer clientdata, + XtPointer call_data); +void container_spatial_cb(Widget widget, XtPointer clientdata, + XtPointer call_data); + +void container_resize_eh(Widget widget, XtPointer clientdata, + XEvent *event, Boolean *d); +void container_convert_cb(Widget widget, XtPointer clientdata, + XtPointer call_data); +void container_traverse_cb(Widget scroll, XtPointer clientdata, + XtPointer call_data); + +void container_menu_edit(Widget menu, Widget container, + int cut, int copy, int paste, int del); +void container_menu_view(Widget menu, Widget container); +void container_menu_ops(Widget menu, Widget container); + +void container_relayout(Widget container); +void container_setsize(Widget container); +void container_delwidgets(Widget container); + +/*----------------------------------------------------------------------*/ + +int file_cmp_alpha(const struct file_button *aa, + const struct file_button *bb); +int file_createwidgets(Widget parent, struct file_button *file); diff --git a/filelist.c b/filelist.c new file mode 100644 index 0000000..317e3b1 --- /dev/null +++ b/filelist.c @@ -0,0 +1,619 @@ +/* + * file list management ("virtual photo album"). + * (c) 2003 Gerd Knorr <kraxel@bytesex.org> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> +#include <fnmatch.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> +#include <X11/extensions/XShm.h> +#include <Xm/Xm.h> +#include <Xm/Form.h> +#include <Xm/Label.h> +#include <Xm/RowColumn.h> +#include <Xm/PushB.h> +#include <Xm/CascadeB.h> +#include <Xm/ScrolledW.h> +#include <Xm/SelectioB.h> +#include <Xm/Transfer.h> +#include <Xm/TransferP.h> +#include <Xm/Container.h> +#include <Xm/FileSB.h> +#include <Xm/Separator.h> + +#include "RegEdit.h" +#include "ida.h" +#include "readers.h" +#include "viewer.h" +#include "browser.h" +#include "filter.h" +#include "x11.h" +#include "dither.h" +#include "selections.h" +#include "filebutton.h" +#include "filelist.h" +#include "xdnd.h" +#include "idaconfig.h" + +/*----------------------------------------------------------------------*/ + +struct list_handle; + +struct list_handle { + char *filename; + struct list_head files; + + Widget shell; + Widget scroll; + Widget container; + Widget status; + XmString details[DETAIL_COUNT+1]; + + Widget loadbox; + Widget savebox; + + XtWorkProcId wproc; +}; + +/* ---------------------------------------------------------------------- */ + +static void filelist_add(struct list_handle *h, char *filename) +{ + struct file_button *file; + char *tmp; + + /* fixup filename */ + if (0 == strncmp(filename,"file:",5)) + filename += 5; + if (NULL != (tmp = strchr(filename,'\n'))) + *tmp = 0; + if (NULL != (tmp = strchr(filename,'\r'))) + *tmp = 0; + if (0 == strlen(filename)) + return; + + /* add file */ + file = malloc(sizeof(*file)); + memset(file,0,sizeof(*file)); + + tmp = strrchr(filename,'/'); + if (!tmp) + goto oops; + file->basename = strdup(tmp+1); + file->filename = strdup(filename); + + if (-1 == stat(file->filename,&file->st)) { + fprintf(stderr,"stat %s: %s\n",file->filename,strerror(errno)); + goto oops; + } + if (!S_ISREG(file->st.st_mode)) { + fprintf(stderr,"%s: not a regular file\n",file->filename); + goto oops; + } + + list_add_tail(&file->window,&h->files); + file_createwidgets(h->container, file); + XtManageChild(file->widget); + fileinfo_queue(file); + container_relayout(h->container); + return; + + oops: + if (file->filename) + free(file->filename); + if (file->basename) + free(file->basename); + free(file); +} + +static void filelist_file(struct list_handle *h, char *filename) +{ + if (h->filename == filename) + return; + if (h->filename) + free(h->filename); + h->filename = strdup(filename); + XtVaSetValues(h->shell,XtNtitle,h->filename,NULL); +} + +static void filelist_read(struct list_handle *h, char *filename) +{ + FILE *fp; + char line[128]; + + fp = fopen(filename,"r"); + if (NULL == fp) { + fprintf(stderr,"open %s: %s\n",filename,strerror(errno)); + return; + } + while (NULL != fgets(line, sizeof(line), fp)) { + filelist_add(h, line); + } + fclose(fp); + filelist_file(h,filename); + container_relayout(h->container); +} + +static void filelist_write(struct list_handle *h, char *filename) +{ + struct file_button *file; + struct list_head *item; + FILE *fp; + + fp = fopen(filename,"w"); + if (NULL == fp) { + fprintf(stderr,"open %s: %s\n",filename,strerror(errno)); + return; + } + list_for_each(item, &h->files) { + file = list_entry(item, struct file_button, window); + fprintf(fp,"%s\n",file->filename); + } + fclose(fp); + filelist_file(h,filename); +} + +static void filelist_delall(struct list_handle *h) +{ + struct file_button *file; + struct list_head *item; + + list_for_each(item, &h->files) { + file = list_entry(item, struct file_button, window); + XtUnmanageChild(file->widget); + XtDestroyWidget(file->widget); + } +} + +/* ---------------------------------------------------------------------- */ +/* receive data (drops, paste) */ + +static Atom targets[16]; +static Cardinal ntargets; + +static void +filelist_xfer(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct list_handle *h = clientdata; + XmSelectionCallbackStruct *scs = call_data; + unsigned char *cdata = scs->value; + unsigned long *ldata = scs->value; + Atom target = 0; + unsigned int i,j,pending; + char *file; + + if (debug) { + char *y = !scs->type ? NULL : XGetAtomName(dpy,scs->type); + char *t = !scs->target ? NULL : XGetAtomName(dpy,scs->target); + char *s = !scs->selection ? NULL : XGetAtomName(dpy,scs->selection); + fprintf(stderr,"list: id=%p target=%s type=%s selection=%s\n", + scs->transfer_id,t,y,s); + if (y) XFree(y); + if (t) XFree(t); + if (s) XFree(s); + } + + pending = scs->remaining; + if (scs->target == XA_TARGETS) { + /* look if we find a target we can deal with ... */ + for (i = 0; !target && i < scs->length; i++) { + for (j = 0; j < ntargets; j++) { + if (ldata[i] == targets[j]) { + target = ldata[i]; + break; + } + } + } + if (target) { + XmTransferValue(scs->transfer_id, target, filelist_xfer, + clientdata, XtLastTimestampProcessed(dpy)); + pending++; + } + if (debug) { + fprintf(stderr,"list: available targets: "); + for (i = 0; i < scs->length; i++) { + char *name = !ldata[i] ? NULL : XGetAtomName(dpy,ldata[i]); + fprintf(stderr,"%s%s", i != 0 ? ", " : "", name); + XFree(name); + } + fprintf(stderr,"\n"); + if (0 == scs->length) + fprintf(stderr,"list: Huh? no TARGETS available?\n"); + } + } + + if (scs->target == XA_FILE_NAME || + scs->target == XA_FILE) { + /* load file */ + if (debug) + fprintf(stderr,"list: => \"%s\"\n",cdata); + filelist_add(h,cdata); + } + + if (scs->target == _NETSCAPE_URL) { + /* load file */ + if (debug) + fprintf(stderr,"list: => \"%s\"\n",cdata); + filelist_add(h,cdata); + } + + if (scs->target == MIME_TEXT_URI_LIST) { + /* load file(s) */ + for (file = strtok(cdata,"\r\n"); + NULL != file; + file = strtok(NULL,"\r\n")) { + if (debug) + fprintf(stderr,"list: => \"%s\"\n",file); + filelist_add(h,file); + } + } + + XFree(scs->value); + if (1 == pending) { + /* all done -- clean up */ + if (debug) + fprintf(stderr,"list: all done\n"); + XmTransferDone(scs->transfer_id, XmTRANSFER_DONE_SUCCEED); + XdndDropFinished(widget,scs); + } +} + +static void +filelist_dest_cb(Widget w, XtPointer clientdata, XtPointer call_data) +{ + XmDestinationCallbackStruct *dcs = call_data; + + if (debug) + fprintf(stderr,"list: xfer id=%p\n",dcs->transfer_id); + XmTransferValue(dcs->transfer_id, XA_TARGETS, filelist_xfer, + clientdata, XtLastTimestampProcessed(dpy)); +} + +/*----------------------------------------------------------------------*/ + +static void +filelist_new_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct list_handle *h = clientdata; + + filelist_delall(h); + if (h->filename) { + free(h->filename); + h->filename = NULL; + } + XtVaSetValues(h->shell,XtNtitle,"new list",NULL); +} + +static void +init_file_box(Widget box, char *filename) +{ + char *dir,*file; + XmString s1; + + if (NULL == filename) { + dir = strdup(ida_lists); + } else { + dir = strdup(filename); + file = strrchr(dir,'/'); + if (NULL == file) + return; + *file = 0; + file++; + } + + s1 = XmStringGenerate(dir, NULL, XmMULTIBYTE_TEXT, NULL); + XtVaSetValues(box, + XmNdirectory, s1, + XmNpattern, NULL, + NULL); + XmFileSelectionDoSearch(box,NULL); + XmStringFree(s1); + free(dir); +} + +static void +load_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XmFileSelectionBoxCallbackStruct *cb = call_data; + struct list_handle *h = clientdata; + char *filename; + + if (cb->reason == XmCR_OK) { + filename = XmStringUnparse(cb->value,NULL, + XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT, + NULL,0,0); + if (debug) + fprintf(stderr,"read list from %s\n",filename); + filelist_read(h, filename); + } + XtUnmanageChild(widget); +} + +static void +filelist_load_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct list_handle *h = clientdata; + Widget help; + + if (NULL == h->loadbox) { + h->loadbox = XmCreateFileSelectionDialog(h->shell,"load",NULL,0); + help = XmFileSelectionBoxGetChild(h->loadbox,XmDIALOG_HELP_BUTTON); + XtUnmanageChild(help); + XtAddCallback(h->loadbox,XmNokCallback,load_done_cb,h); + XtAddCallback(h->loadbox,XmNcancelCallback,load_done_cb,h); + } + init_file_box(h->loadbox,h->filename); + XtManageChild(h->loadbox); +} + +static void +save_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XmFileSelectionBoxCallbackStruct *cb = call_data; + struct list_handle *h = clientdata; + char *filename; + + if (cb->reason == XmCR_OK) { + filename = XmStringUnparse(cb->value,NULL, + XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT, + NULL,0,0); + if (debug) + fprintf(stderr,"write list to %s\n",filename); + filelist_write(h, filename); + } + XtUnmanageChild(widget); +} + +static void +filelist_save_as_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct list_handle *h = clientdata; + Widget help; + + if (NULL == h->savebox) { + h->savebox = XmCreateFileSelectionDialog(h->shell,"save",NULL,0); + help = XmFileSelectionBoxGetChild(h->savebox,XmDIALOG_HELP_BUTTON); + XtUnmanageChild(help); + + XtAddCallback(h->savebox,XmNokCallback,save_done_cb,h); + XtAddCallback(h->savebox,XmNcancelCallback,save_done_cb,h); + } + init_file_box(h->savebox,h->filename); + XtManageChild(h->savebox); +} + +static void +filelist_save_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct list_handle *h = clientdata; + + if (h->filename) { + filelist_write(h, h->filename); + } else { + filelist_save_as_cb(widget, h, call_data); + } +} + +static void +filelist_destroy(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct list_handle *h = clientdata; + + if (h->filename) + free(h->filename); + ptr_unregister(h->shell); + free(h); +} + +static void +filelist_list_load(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct list_handle *h = clientdata; + + filelist_delall(h); + filelist_read(h, XtName(widget)); +} + +static void filelist_builddir(Widget menu, char *path, XtPointer clientdata) +{ + Widget push,submenu; + XmString str; + char filename[1024]; + struct dirent *ent; + struct stat st; + DIR *dir; + + dir = opendir(path); + while (NULL != (ent = readdir(dir))) { + if (ent->d_name[0] == '.') + continue; + snprintf(filename,sizeof(filename),"%s/%s", + path,ent->d_name); + if (-1 == lstat(filename,&st)) + continue; + + str = XmStringGenerate(ent->d_name,NULL, XmMULTIBYTE_TEXT,NULL); + if (S_ISREG(st.st_mode)) { + push = XtVaCreateManagedWidget(filename, + xmPushButtonWidgetClass,menu, + XmNlabelString,str, + NULL); + XtAddCallback(push,XmNactivateCallback,filelist_list_load,clientdata); + } + if (S_ISDIR(st.st_mode)) { + submenu = XmCreatePulldownMenu(menu,"subdirM",NULL,0); + XtVaCreateManagedWidget("subdir",xmCascadeButtonWidgetClass,menu, + XmNlabelString,str, + XmNsubMenuId,submenu, + NULL); + filelist_builddir(submenu,filename,clientdata); + } + XmStringFree(str); + } + closedir(dir); +} + +static void +filelist_lists(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + WidgetList children,list; + Cardinal nchildren; + int i; + + XtVaGetValues(widget, + XtNchildren,&children, + XtNnumChildren,&nchildren, + NULL); + list = malloc(sizeof(Widget*)*nchildren); + memcpy(list,children,sizeof(Widget*)*nchildren); + for (i = 0; i < nchildren; i++) + XtDestroyWidget(list[i]); + free(list); + + filelist_builddir(widget,ida_lists,clientdata); +} + +static void +filelist_action_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XmContainerSelectCallbackStruct *cd = call_data; + char *file; + + if (XmCR_DEFAULT_ACTION == cd->reason && 1 == cd->selected_item_count) { + file = XtName(cd->selected_items[0]); + if (debug) + fprintf(stderr,"browser: action %s\n", file); + new_file(file,1); + } +} + +/*----------------------------------------------------------------------*/ + +void +filelist_window(void) +{ + Widget form,clip,menubar,menu,push; + struct list_handle *h; + Arg args[8]; + int n = 0; + + if (0 == ntargets) { + /* first time init */ + targets[ntargets++] = MIME_TEXT_URI_LIST; + targets[ntargets++] = XA_FILE_NAME; + targets[ntargets++] = XA_FILE; + targets[ntargets++] = _NETSCAPE_URL; + } + + h = malloc(sizeof(*h)); + if (NULL == h) { + fprintf(stderr,"out of memory"); + return; + } + memset(h,0,sizeof(*h)); + INIT_LIST_HEAD(&h->files); + + h->shell = XtVaAppCreateShell("filelist","Ida", + topLevelShellWidgetClass, + dpy, + XtNclientLeader,app_shell, + XmNdeleteResponse,XmDESTROY, + XtNtitle,"new list", + NULL); + XmdRegisterEditres(h->shell); + XtAddCallback(h->shell,XtNdestroyCallback,filelist_destroy,h); + + /* widgets */ + form = XtVaCreateManagedWidget("form", xmFormWidgetClass, h->shell, + NULL); + menubar = XmCreateMenuBar(form,"cbar",NULL,0); + XtManageChild(menubar); + h->status = XtVaCreateManagedWidget("status",xmLabelWidgetClass, form, + NULL); + + /* scrolled container */ + h->details[0] = XmStringGenerate("Image", NULL, XmMULTIBYTE_TEXT,NULL); + h->details[DETAIL_SIZE+1] = + XmStringGenerate("Size", NULL, XmMULTIBYTE_TEXT,NULL); + h->details[DETAIL_COMMENT+1] = + XmStringGenerate("Comment", NULL, XmMULTIBYTE_TEXT,NULL); + XtSetArg(args[n], XmNdetailColumnHeading, h->details); n++; + XtSetArg(args[n], XmNdetailColumnHeadingCount, DETAIL_COUNT+1); n++; + + h->scroll = XmCreateScrolledWindow(form, "scroll", NULL, 0); + XtManageChild(h->scroll); + h->container = XmCreateContainer(h->scroll,"container", + args,n); + XtManageChild(h->container); + XdndDropSink(h->container); + + XtAddCallback(h->scroll, XmNtraverseObscuredCallback, + container_traverse_cb, NULL); + XtAddCallback(h->container,XmNdefaultActionCallback, + filelist_action_cb,h); + XtAddCallback(h->container,XmNconvertCallback, + container_convert_cb,h); + XtAddCallback(h->container,XmNdestinationCallback, + filelist_dest_cb,h); + + XtVaGetValues(h->scroll,XmNclipWindow,&clip,NULL); + XtAddEventHandler(clip,StructureNotifyMask,True,container_resize_eh,NULL); + + /* menu - file */ + menu = XmCreatePulldownMenu(menubar,"fileM",NULL,0); + XtVaCreateManagedWidget("file",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + push = XtVaCreateManagedWidget("new",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,filelist_new_cb,h); + push = XtVaCreateManagedWidget("load",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,filelist_load_cb,h); + push = XtVaCreateManagedWidget("save",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,filelist_save_cb,h); + push = XtVaCreateManagedWidget("saveas",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,filelist_save_as_cb,h); + + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); + push = XtVaCreateManagedWidget("close",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,destroy_cb,h->shell); + + /* menu - edit */ + menu = XmCreatePulldownMenu(menubar,"editM",NULL,0); + XtVaCreateManagedWidget("edit",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + container_menu_edit(menu,h->container, 0,1,1,1); + + /* menu - view */ + menu = XmCreatePulldownMenu(menubar,"viewM",NULL,0); + XtVaCreateManagedWidget("view",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + container_menu_view(menu,h->container); + + /* menu - lists */ + menu = XmCreatePulldownMenu(menubar,"listsM",NULL,0); + XtVaCreateManagedWidget("lists",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + XtAddCallback(menu, XmNmapCallback, filelist_lists, h); + + /* read dir and show window */ + container_detail_cb(NULL,h->container,NULL); + XtPopup(h->shell,XtGrabNone); + ptr_register(h->shell); +} + +void +filelist_ac(Widget widget, XEvent *event, + String *params, Cardinal *num_params) +{ + filelist_window(); +} diff --git a/filelist.h b/filelist.h new file mode 100644 index 0000000..f7fc2ab --- /dev/null +++ b/filelist.h @@ -0,0 +1,3 @@ +void filelist_window(void); +void filelist_ac(Widget widget, XEvent *event, + String *params, Cardinal *num_params); diff --git a/fileops.c b/fileops.c new file mode 100644 index 0000000..fb24930 --- /dev/null +++ b/fileops.c @@ -0,0 +1,100 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> + +#include "list.h" +#include "ida.h" +#include "readers.h" +#include "filebutton.h" +#include "fileops.h" + +#include <jpeglib.h> +#include "jpeg/transupp.h" /* Support routines for jpegtran */ +#include "jpegtools.h" + +/*----------------------------------------------------------------------*/ + +struct jobqueue { + struct list_head list; + char *op; + char *filename; + char *args; +}; +static LIST_HEAD(jobs); +static XtWorkProcId jobproc; + +/*----------------------------------------------------------------------*/ + +static Boolean job_worker(XtPointer clientdata) +{ + struct jobqueue *job; + unsigned int flags = + JFLAG_TRANSFORM_IMAGE | + JFLAG_TRANSFORM_THUMBNAIL | + JFLAG_UPDATE_ORIENTATION; + + if (list_empty(&jobs)) { + /* nothing to do */ + jobproc = 0; + return TRUE; + } + job = list_entry(jobs.next, struct jobqueue, list); + + /* process job */ + if (debug) + fprintf(stderr,"job worker: %s %s\n",job->op,job->filename); + ptr_busy(); + if (0 == strcmp(job->op,"rotexif")) { + jpeg_transform_inplace(job->filename, -1/*auto*/, + NULL, NULL, 0, flags); + + } else if (0 == strcmp(job->op,"rotcw")) { + jpeg_transform_inplace(job->filename, JXFORM_ROT_90, + NULL, NULL, 0, flags); + + } else if (0 == strcmp(job->op,"rotccw")) { + jpeg_transform_inplace(job->filename, JXFORM_ROT_270, + NULL, NULL, 0, flags); + + } else if (0 == strcmp(job->op,"comment")) { + jpeg_transform_inplace(job->filename, JXFORM_NONE, job->args, + NULL, 0, + JFLAG_UPDATE_COMMENT); + + } else { + fprintf(stderr,"job: \"%s\" is *unknown*\n",job->op); + } + ptr_idle(); + fileinfo_invalidate(job->filename); + + /* cleanup */ + list_del(&job->list); + free(job->filename); + free(job->op); + if (job->args) + free(job->args); + free(job); + return FALSE; +} + +/*----------------------------------------------------------------------*/ + +void job_submit(char *op, char *filename, char *args) +{ + struct jobqueue *job; + + job = malloc(sizeof(*job)); + memset(job,0,sizeof(*job)); + job->op = strdup(op); + job->filename = strdup(filename); + if (args) + job->args = strdup(args); + list_add_tail(&job->list,&jobs); + if (debug) + fprintf(stderr,"job submit: %s %s\n",job->op,job->filename); + if (0 == jobproc) + jobproc = XtAppAddWorkProc(app_context,job_worker,NULL); +} diff --git a/fileops.h b/fileops.h new file mode 100644 index 0000000..d5ea2db --- /dev/null +++ b/fileops.h @@ -0,0 +1 @@ +void job_submit(char *op, char *filename, char *args); diff --git a/filter.c b/filter.c new file mode 100644 index 0000000..283a1fa --- /dev/null +++ b/filter.c @@ -0,0 +1,495 @@ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> + +#include "readers.h" +#include "filter.h" + +int debug = 0; + +/* ----------------------------------------------------------------------- */ + +static void +op_grayscale(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int line, void *data) +{ + unsigned char *scanline; + int i,g; + + scanline = src->data + line * src->i.width * 3; + memcpy(dst,scanline,src->i.width * 3); + if (line < rect->y1 || line >= rect->y2) + return; + dst += 3*rect->x1; + scanline += 3*rect->x1; + for (i = rect->x1; i < rect->x2; i++) { + g = (scanline[0]*30 + scanline[1]*59+scanline[2]*11)/100; + dst[0] = g; + dst[1] = g; + dst[2] = g; + scanline += 3; + dst += 3; + } +} + +/* ----------------------------------------------------------------------- */ + +struct op_3x3_handle { + struct op_3x3_parm filter; + int *linebuf; +}; + +static void* +op_3x3_init(struct ida_image *src, struct ida_rect *rect, + struct ida_image_info *i, void *parm) +{ + struct op_3x3_parm *args = parm; + struct op_3x3_handle *h; + + h = malloc(sizeof(*h)); + memcpy(&h->filter,args,sizeof(*args)); + h->linebuf = malloc(sizeof(int)*3*(src->i.width)); + + *i = src->i; + return h; +} + +static int inline +op_3x3_calc_pixel(struct op_3x3_parm *p, unsigned char *s1, + unsigned char *s2, unsigned char *s3) +{ + int val = 0; + + val += p->f1[0] * s1[0]; + val += p->f1[1] * s1[3]; + val += p->f1[2] * s1[6]; + val += p->f2[0] * s2[0]; + val += p->f2[1] * s2[3]; + val += p->f2[2] * s2[6]; + val += p->f3[0] * s3[0]; + val += p->f3[1] * s3[3]; + val += p->f3[2] * s3[6]; + if (p->mul && p->div) + val = val * p->mul / p->div; + val += p->add; + return val; +} + +static void +op_3x3_calc_line(struct ida_image *src, struct ida_rect *rect, + int *dst, unsigned int line, struct op_3x3_parm *p) +{ + unsigned char b1[9],b2[9],b3[9]; + unsigned char *s1,*s2,*s3; + unsigned int i,left,right; + + s1 = src->data + (line-1) * src->i.width * 3; + s2 = src->data + line * src->i.width * 3; + s3 = src->data + (line+1) * src->i.width * 3; + if (0 == line) + s1 = src->data + line * src->i.width * 3; + if (src->i.height-1 == line) + s3 = src->data + line * src->i.width * 3; + + left = rect->x1; + right = rect->x2; + if (0 == left) { + /* left border special case: dup first col */ + memcpy(b1,s1,3); + memcpy(b2,s2,3); + memcpy(b3,s3,3); + memcpy(b1+3,s1,6); + memcpy(b2+3,s2,6); + memcpy(b3+3,s3,6); + dst[0] = op_3x3_calc_pixel(p,b1,b2,b3); + dst[1] = op_3x3_calc_pixel(p,b1+1,b2+1,b3+1); + dst[2] = op_3x3_calc_pixel(p,b1+2,b2+2,b3+2); + left++; + } + if (src->i.width == right) { + /* right border */ + memcpy(b1,s1+src->i.width*3-6,6); + memcpy(b2,s2+src->i.width*3-6,6); + memcpy(b3,s3+src->i.width*3-6,6); + memcpy(b1+3,s1+src->i.width*3-3,3); + memcpy(b2+3,s2+src->i.width*3-3,3); + memcpy(b3+3,s3+src->i.width*3-3,3); + dst[src->i.width*3-3] = op_3x3_calc_pixel(p,b1,b2,b3); + dst[src->i.width*3-2] = op_3x3_calc_pixel(p,b1+1,b2+1,b3+1); + dst[src->i.width*3-1] = op_3x3_calc_pixel(p,b1+2,b2+2,b3+2); + right--; + } + + dst += 3*left; + s1 += 3*(left-1); + s2 += 3*(left-1); + s3 += 3*(left-1); + for (i = left; i < right; i++) { + dst[0] = op_3x3_calc_pixel(p,s1++,s2++,s3++); + dst[1] = op_3x3_calc_pixel(p,s1++,s2++,s3++); + dst[2] = op_3x3_calc_pixel(p,s1++,s2++,s3++); + dst += 3; + } +} + +static void +op_3x3_clip_line(unsigned char *dst, int *src, int left, int right) +{ + int i,val; + + src += left*3; + dst += left*3; + for (i = left*3; i < right*3; i++) { + val = *(src++); + if (val < 0) + val = 0; + if (val > 255) + val = 255; + *(dst++) = val; + } +} + +static void +op_3x3_work(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int line, void *data) +{ + struct op_3x3_handle *h = data; + unsigned char *scanline; + + scanline = src->data + line * src->i.width * 3; + memcpy(dst,scanline,src->i.width * 3); + if (line < rect->y1 || line >= rect->y2) + return; + + op_3x3_calc_line(src,rect,h->linebuf,line,&h->filter); + op_3x3_clip_line(dst,h->linebuf,rect->x1,rect->x2); +} + +static void +op_3x3_free(void *data) +{ + struct op_3x3_handle *h = data; + + free(h->linebuf); + free(h); +} + +/* ----------------------------------------------------------------------- */ + +struct op_sharpe_handle { + int factor; + int *linebuf; +}; + +static void* +op_sharpe_init(struct ida_image *src, struct ida_rect *rect, + struct ida_image_info *i, void *parm) +{ + struct op_sharpe_parm *args = parm; + struct op_sharpe_handle *h; + + h = malloc(sizeof(*h)); + h->factor = args->factor; + h->linebuf = malloc(sizeof(int)*3*(src->i.width)); + + *i = src->i; + return h; +} + +static void +op_sharpe_work(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int line, void *data) +{ + static struct op_3x3_parm laplace = { + f1: { 1, 1, 1 }, + f2: { 1, -8, 1 }, + f3: { 1, 1, 1 }, + }; + struct op_sharpe_handle *h = data; + unsigned char *scanline; + int i; + + scanline = src->data + line * src->i.width * 3; + memcpy(dst,scanline,src->i.width * 3); + if (line < rect->y1 || line >= rect->y2) + return; + + op_3x3_calc_line(src,rect,h->linebuf,line,&laplace); + for (i = rect->x1*3; i < rect->x2*3; i++) + h->linebuf[i] = scanline[i] - h->linebuf[i] * h->factor / 256; + op_3x3_clip_line(dst,h->linebuf,rect->x1,rect->x2); +} + +static void +op_sharpe_free(void *data) +{ + struct op_sharpe_handle *h = data; + + free(h->linebuf); + free(h); +} + +/* ----------------------------------------------------------------------- */ + +struct op_resize_state { + float xscale,yscale,inleft; + float *rowbuf; + unsigned int width,height,srcrow; +}; + +static void* +op_resize_init(struct ida_image *src, struct ida_rect *rect, + struct ida_image_info *i, void *parm) +{ + struct op_resize_parm *args = parm; + struct op_resize_state *h; + + h = malloc(sizeof(*h)); + h->width = args->width; + h->height = args->height; + h->xscale = (float)args->width/src->i.width; + h->yscale = (float)args->height/src->i.height; + h->rowbuf = malloc(src->i.width * 3 * sizeof(float)); + h->srcrow = 0; + h->inleft = 1; + + *i = src->i; + i->width = args->width; + i->height = args->height; + i->dpi = args->dpi; + return h; +} + +static void +op_resize_work(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int line, void *data) +{ + struct op_resize_state *h = data; + float outleft,left,weight,d0,d1,d2; + unsigned char *csrcline; + float *fsrcline; + unsigned int i,sx,dx; + + /* scale y */ + memset(h->rowbuf, 0, src->i.width * 3 * sizeof(float)); + outleft = 1/h->yscale; + while (outleft > 0 && h->srcrow < src->i.height) { + if (outleft < h->inleft) { + weight = outleft * h->yscale; + h->inleft -= outleft; + outleft = 0; + } else { + weight = h->inleft * h->yscale; + outleft -= h->inleft; + h->inleft = 0; + } +#if 0 + if (debug) + fprintf(stderr,"y: %6.2f%%: %d/%d => %d/%d\n", + weight*100,h->srcrow,src->height,line,h->height); +#endif + csrcline = src->data + h->srcrow * src->i.width * 3; + for (i = 0; i < src->i.width * 3; i++) + h->rowbuf[i] += (float)csrcline[i] * weight; + if (0 == h->inleft) { + h->inleft = 1; + h->srcrow++; + } + } + + /* scale x */ + left = 1; + fsrcline = h->rowbuf; + for (sx = 0, dx = 0; dx < h->width; dx++) { + d0 = d1 = d2 = 0; + outleft = 1/h->xscale; + while (outleft > 0 && dx < h->width && sx < src->i.width) { + if (outleft < left) { + weight = outleft * h->xscale; + left -= outleft; + outleft = 0; + } else { + weight = left * h->xscale; + outleft -= left; + left = 0; + } +#if 0 + if (debug) + fprintf(stderr," x: %6.2f%%: %d/%d => %d/%d\n", + weight*100,sx,src->width,dx,h->width); +#endif + d0 += fsrcline[3*sx+0] * weight; + d1 += fsrcline[3*sx+1] * weight; + d2 += fsrcline[3*sx+2] * weight; + if (0 == left) { + left = 1; + sx++; + } + } + dst[0] = d0; + dst[1] = d1; + dst[2] = d2; + dst += 3; + } +} + +static void +op_resize_done(void *data) +{ + struct op_resize_state *h = data; + + free(h->rowbuf); + free(h); +} + +/* ----------------------------------------------------------------------- */ + +struct op_rotate_state { + float angle,sina,cosa; + struct ida_rect calc; + int cx,cy; +}; + +static void* +op_rotate_init(struct ida_image *src, struct ida_rect *rect, + struct ida_image_info *i, void *parm) +{ + struct op_rotate_parm *args = parm; + struct op_rotate_state *h; + float diag; + + h = malloc(sizeof(*h)); + h->angle = args->angle * 2 * M_PI / 360; + h->sina = sin(h->angle); + h->cosa = cos(h->angle); + h->cx = (rect->x2 - rect->x1) / 2 + rect->x1; + h->cy = (rect->y2 - rect->y1) / 2 + rect->y1; + + /* the area we have to process (worst case: 45°) */ + diag = sqrt((rect->x2 - rect->x1)*(rect->x2 - rect->x1) + + (rect->y2 - rect->y1)*(rect->y2 - rect->y1))/2; + h->calc.x1 = h->cx - diag; + h->calc.x2 = h->cx + diag; + h->calc.y1 = h->cy - diag; + h->calc.y2 = h->cy + diag; + if (h->calc.x1 < 0) + h->calc.x1 = 0; + if (h->calc.x2 > src->i.width) + h->calc.x2 = src->i.width; + if (h->calc.y1 < 0) + h->calc.y1 = 0; + if (h->calc.y2 > src->i.height) + h->calc.y2 = src->i.height; + + *i = src->i; + return h; +} + +static inline +unsigned char* op_rotate_getpixel(struct ida_image *src, struct ida_rect *rect, + int sx, int sy, int dx, int dy) +{ + static unsigned char black[] = { 0, 0, 0}; + + if (sx < rect->x1 || sx >= rect->x2 || + sy < rect->y1 || sy >= rect->y2) { + if (dx < rect->x1 || dx >= rect->x2 || + dy < rect->y1 || dy >= rect->y2) + return src->data + dy * src->i.width * 3 + dx * 3; + return black; + } + return src->data + sy * src->i.width * 3 + sx * 3; +} + +static void +op_rotate_work(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int y, void *data) +{ + struct op_rotate_state *h = data; + unsigned char *pix; + float fx,fy,w; + int x,sx,sy; + + pix = src->data + y * src->i.width * 3; + memcpy(dst,pix,src->i.width * 3); + if (y < h->calc.y1 || y >= h->calc.y2) + return; + + dst += 3*h->calc.x1; + memset(dst, 0, (h->calc.x2-h->calc.x1) * 3); + for (x = h->calc.x1; x < h->calc.x2; x++, dst+=3) { + fx = h->cosa * (x - h->cx) - h->sina * (y - h->cy) + h->cx; + fy = h->sina * (x - h->cx) + h->cosa * (y - h->cy) + h->cy; + sx = (int)fx; + sy = (int)fy; + if (fx < 0) + sx--; + if (fy < 0) + sy--; + fx -= sx; + fy -= sy; + + pix = op_rotate_getpixel(src,rect,sx,sy,x,y); + w = (1-fx) * (1-fy); + dst[0] += pix[0] * w; + dst[1] += pix[1] * w; + dst[2] += pix[2] * w; + pix = op_rotate_getpixel(src,rect,sx+1,sy,x,y); + w = fx * (1-fy); + dst[0] += pix[0] * w; + dst[1] += pix[1] * w; + dst[2] += pix[2] * w; + pix = op_rotate_getpixel(src,rect,sx,sy+1,x,y); + w = (1-fx) * fy; + dst[0] += pix[0] * w; + dst[1] += pix[1] * w; + dst[2] += pix[2] * w; + pix = op_rotate_getpixel(src,rect,sx+1,sy+1,x,y); + w = fx * fy; + dst[0] += pix[0] * w; + dst[1] += pix[1] * w; + dst[2] += pix[2] * w; + } +} + +static void +op_rotate_done(void *data) +{ + struct op_rotate_state *h = data; + + free(h); +} + +/* ----------------------------------------------------------------------- */ + +struct ida_op desc_grayscale = { + name: "grayscale", + init: op_none_init, + work: op_grayscale, + done: op_none_done, +}; +struct ida_op desc_3x3 = { + name: "3x3", + init: op_3x3_init, + work: op_3x3_work, + done: op_3x3_free, +}; +struct ida_op desc_sharpe = { + name: "sharpe", + init: op_sharpe_init, + work: op_sharpe_work, + done: op_sharpe_free, +}; +struct ida_op desc_resize = { + name: "resize", + init: op_resize_init, + work: op_resize_work, + done: op_resize_done, +}; +struct ida_op desc_rotate = { + name: "rotate", + init: op_rotate_init, + work: op_rotate_work, + done: op_rotate_done, +}; diff --git a/filter.h b/filter.h new file mode 100644 index 0000000..37d67ee --- /dev/null +++ b/filter.h @@ -0,0 +1,27 @@ + +struct op_3x3_parm { + int f1[3]; + int f2[3]; + int f3[3]; + int mul,div,add; +}; + +struct op_sharpe_parm { + int factor; +}; + +struct op_resize_parm { + int width; + int height; + int dpi; +}; + +struct op_rotate_parm { + int angle; +}; + +extern struct ida_op desc_grayscale; +extern struct ida_op desc_3x3; +extern struct ida_op desc_sharpe; +extern struct ida_op desc_resize; +extern struct ida_op desc_rotate; @@ -0,0 +1,502 @@ +/* + * text rendering for the framebuffer console + * pick fonts from X11 font server or + * use linux consolefont psf files. + * (c) 2001 Gerd Knorr <kraxel@bytesex.org> + */ +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <linux/fb.h> + +#include "fbtools.h" +#include "fs.h" + +/* ------------------------------------------------------------------ */ + +#define BIT_ORDER BitmapFormatBitOrderMSB +#ifdef BYTE_ORDER +#undef BYTE_ORDER +#endif +#define BYTE_ORDER BitmapFormatByteOrderMSB +#define SCANLINE_UNIT BitmapFormatScanlineUnit8 +#define SCANLINE_PAD BitmapFormatScanlinePad8 +#define EXTENTS BitmapFormatImageRectMin + +#define SCANLINE_PAD_BYTES 1 +#define GLWIDTHBYTESPADDED(bits, nBytes) \ + ((nBytes) == 1 ? (((bits) + 7) >> 3) /* pad to 1 byte */\ + :(nBytes) == 2 ? ((((bits) + 15) >> 3) & ~1) /* pad to 2 bytes */\ + :(nBytes) == 4 ? ((((bits) + 31) >> 3) & ~3) /* pad to 4 bytes */\ + :(nBytes) == 8 ? ((((bits) + 63) >> 3) & ~7) /* pad to 8 bytes */\ + : 0) + +static const unsigned fs_masktab[] = { + (1 << 7), (1 << 6), (1 << 5), (1 << 4), + (1 << 3), (1 << 2), (1 << 1), (1 << 0), +}; + +/* ------------------------------------------------------------------ */ + +#ifndef X_DISPLAY_MISSING +static FSServer *svr; +#endif +unsigned int fs_bpp, fs_black, fs_white; + +void (*fs_setpixel)(void *ptr, unsigned int color); + +static void setpixel1(void *ptr, unsigned int color) +{ + unsigned char *p = ptr; + *p = color; +} +static void setpixel2(void *ptr, unsigned int color) +{ + unsigned short *p = ptr; + *p = color; +} +static void setpixel3(void *ptr, unsigned int color) +{ + unsigned char *p = ptr; + *(p++) = (color >> 16) & 0xff; + *(p++) = (color >> 8) & 0xff; + *(p++) = color & 0xff; +} +static void setpixel4(void *ptr, unsigned int color) +{ + unsigned long *p = ptr; + *p = color; +} + +int fs_init_fb(int white8) +{ + switch (fb_var.bits_per_pixel) { + case 8: + fs_white = white8; fs_black = 0; fs_bpp = 1; + fs_setpixel = setpixel1; + break; + case 15: + case 16: + if (fb_var.green.length == 6) + fs_white = 0xffff; + else + fs_white = 0x7fff; + fs_black = 0; fs_bpp = 2; + fs_setpixel = setpixel2; + break; + case 24: + fs_white = 0xffffff; fs_black = 0; fs_bpp = fb_var.bits_per_pixel/8; + fs_setpixel = setpixel3; + break; + case 32: + fs_white = 0xffffff; fs_black = 0; fs_bpp = fb_var.bits_per_pixel/8; + fs_setpixel = setpixel4; + break; + default: + fprintf(stderr, "Oops: %i bit/pixel ???\n", + fb_var.bits_per_pixel); + return -1; + } + return 0; +} + +void fs_render_fb(unsigned char *ptr, int pitch, + FSXCharInfo *charInfo, unsigned char *data) +{ + int row,bit,bpr,x; + + bpr = GLWIDTHBYTESPADDED((charInfo->right - charInfo->left), + SCANLINE_PAD_BYTES); + for (row = 0; row < (charInfo->ascent + charInfo->descent); row++) { + for (x = 0, bit = 0; bit < (charInfo->right - charInfo->left); bit++) { + if (data[bit>>3] & fs_masktab[bit&7]) + fs_setpixel(ptr+x,fs_white); + x += fs_bpp; + } + data += bpr; + ptr += pitch; + } +} + +int fs_puts(struct fs_font *f, unsigned int x, unsigned int y, + unsigned char *str) +{ + unsigned char *pos,*start; + int i,c,j,w; + + pos = fb_mem+fb_mem_offset; + pos += fb_fix.line_length * y; + for (i = 0; str[i] != '\0'; i++) { + c = str[i]; + if (NULL == f->eindex[c]) + continue; + /* clear with bg color */ + start = pos + x*fs_bpp + f->fontHeader.max_bounds.descent * fb_fix.line_length; + w = (f->eindex[c]->width+1)*fs_bpp; + for (j = 0; j < f->height; j++) { + memset(start,0,w); + start += fb_fix.line_length; + } + /* draw char */ + start = pos + x*fs_bpp + fb_fix.line_length * (f->height-f->eindex[c]->ascent); + fs_render_fb(start,fb_fix.line_length,f->eindex[c],f->gindex[c]); + x += f->eindex[c]->width; + if (x > fb_var.xres - f->width) + return -1; + } + return x; +} + +int fs_textwidth(struct fs_font *f, unsigned char *str) +{ + int width = 0; + int i,c; + + for (i = 0; str[i] != '\0'; i++) { + c = str[i]; + if (NULL == f->eindex[c]) + continue; + width += f->eindex[c]->width; + } + return width; +} + +void fs_render_tty(FSXCharInfo *charInfo, unsigned char *data) +{ + int bpr,row,bit,on; + + bpr = GLWIDTHBYTESPADDED((charInfo->right - charInfo->left), + SCANLINE_PAD_BYTES); + for (row = 0; row < (charInfo->ascent + charInfo->descent); row++) { + fprintf(stdout,"|"); + for (bit = 0; bit < (charInfo->right - charInfo->left); bit++) { + on = data[bit>>3] & fs_masktab[bit&7]; + fprintf(stdout,"%s",on ? "##" : " "); + } + fprintf(stdout,"|\n"); + data += bpr; + } + fprintf(stdout,"--\n"); +} + +/* ------------------------------------------------------------------ */ + +#ifndef X_DISPLAY_MISSING +/* connect to font server */ +int fs_connect(char *servername) +{ + if (NULL == servername) + servername = getenv("FONTSERVER"); + if (NULL == servername) + servername = "unix/:7100"; + svr = FSOpenServer(servername); + if (NULL == svr) { + if (NULL == FSServerName(servername)) { + fprintf(stderr, "no font server defined\n"); + } else { + fprintf(stderr, "unable to open server \"%s\"\n", + FSServerName(servername)); + } + return -1; + } + return 0; +} + +/* load font from font server */ +struct fs_font* fs_open(char *pattern) +{ + int nnames = 1; + int available,high,low,encoding,bpr; + char **fonts; + unsigned char *glyph; + Font dummy; + FSBitmapFormat format; + FSXCharInfo *charInfo; + struct fs_font *f = NULL; + + if (NULL == svr) { + fprintf(stderr,"fs: not connected\n"); + return NULL; + } + + fonts = FSListFonts(svr, pattern, nnames, &available); + if (0 == available) { + fprintf(stderr,"fs: font not available [%s]\n",pattern); + goto out; + } + fprintf(stderr,"using x11 font \"%s\"\n",fonts[0]); + + f = malloc(sizeof(*f)); + memset(f,0,sizeof(*f)); + f->font = FSOpenBitmapFont(svr, 0, 0, fonts[0], &dummy); + FSFreeFontNames(fonts); + if (0 == f->font) + goto out; + + FSQueryXInfo(svr,f->font,&f->fontHeader, &f->propInfo, + &f->propOffsets, &f->propData); + format = BYTE_ORDER | BIT_ORDER | SCANLINE_UNIT | SCANLINE_PAD | EXTENTS; + FSQueryXExtents16(svr, f->font, True, (FSChar2b *) 0, 0, &f->extents); + FSQueryXBitmaps16(svr, f->font, format, True, (FSChar2b *) 0, 0, + &f->offsets, &f->glyphs); + + f->maxenc = (f->fontHeader.char_range.max_char.high+1) << 8; + f->width = f->fontHeader.max_bounds.right - f->fontHeader.min_bounds.left; + f->height = f->fontHeader.max_bounds.ascent + f->fontHeader.max_bounds.descent; + f->eindex = malloc(f->maxenc * sizeof(FSXCharInfo*)); + f->gindex = malloc(f->maxenc * sizeof(unsigned char*)); + memset(f->eindex,0,f->maxenc * sizeof(FSXCharInfo*)); + memset(f->gindex,0,f->maxenc * sizeof(unsigned char*)); + + glyph = f->glyphs; + charInfo = f->extents; + for (high = f->fontHeader.char_range.min_char.high; + high <= f->fontHeader.char_range.max_char.high; + high++) { + for (low = f->fontHeader.char_range.min_char.low; + low <= f->fontHeader.char_range.max_char.low; + low++) { + bpr = GLWIDTHBYTESPADDED((charInfo->right - charInfo->left), + SCANLINE_PAD_BYTES); + encoding = (high<<8) + low; +#ifdef TTY + fprintf(stdout,"e=0x%x | w=%d l=%d r=%d | a=%d d=%d\n", + encoding,charInfo->width,charInfo->left, + charInfo->right,charInfo->ascent,charInfo->descent); +#endif + if ((charInfo->width != 0) || (charInfo->right != charInfo->left)) { + f->gindex[encoding] = glyph; + f->eindex[encoding] = charInfo; +#ifdef TTY + fs_render_tty(f->eindex[encoding], + f->gindex[encoding]); +#endif + } + glyph += (charInfo->descent + charInfo->ascent) * bpr; + charInfo++; + } + } + return f; + + out: + if (f) + fs_free(f); + return NULL; +} +#endif + +void fs_free(struct fs_font *f) +{ + if (f->gindex) + free(f->gindex); +#if 0 + if (f->extents) + FSFree((char *) f->extents); + if (f->offsets) + FSFree((char *) f->offsets); + if (f->propOffsets) + FSFree((char *) (f->propOffsets)); + if (f->propData) + FSFree((char *) (f->propData)); +#endif +#if 0 /* FIXME */ + if (f->glyphs) + FSFree((char *) f->glyphs); +#endif + free(f); +} + +/* ------------------------------------------------------------------ */ +/* load console font file */ + +static char *default_font[] = { + /* why the heck every f*cking distribution picks another + location for these fonts ??? */ + "/usr/share/consolefonts/lat1-16.psf", + "/usr/share/consolefonts/lat1-16.psf.gz", + "/usr/share/consolefonts/lat1-16.psfu.gz", + "/usr/share/kbd/consolefonts/lat1-16.psf", + "/usr/share/kbd/consolefonts/lat1-16.psf.gz", + "/usr/share/kbd/consolefonts/lat1-16.psfu.gz", + "/usr/lib/kbd/consolefonts/lat1-16.psf", + "/usr/lib/kbd/consolefonts/lat1-16.psf.gz", + "/usr/lib/kbd/consolefonts/lat1-16.psfu.gz", + "/lib/kbd/consolefonts/lat1-16.psf", + "/lib/kbd/consolefonts/lat1-16.psf.gz", + "/lib/kbd/consolefonts/lat1-16.psfu.gz", + NULL +}; + +struct fs_font* fs_consolefont(char **filename) +{ + int i; + char *h,command[256]; + struct fs_font *f = NULL; + FILE *fp; + + if (NULL == filename) + filename = default_font; + + for(i = 0; filename[i] != NULL; i++) { + if (-1 == access(filename[i],R_OK)) + continue; + break; + } + if (NULL == filename[i]) { + fprintf(stderr,"can't find console font file\n"); + return NULL; + } + + h = filename[i]+strlen(filename[i])-3; + if (0 == strcmp(h,".gz")) { + sprintf(command,"zcat %s",filename[i]); + fp = popen(command,"r"); + } else { + fp = fopen(filename[i], "r"); + } + if (NULL == fp) { + fprintf(stderr,"can't open %s: %s\n",filename[i],strerror(errno)); + return NULL; + } + + if (fgetc(fp) != 0x36 || + fgetc(fp) != 0x04) { + fprintf(stderr,"can't use font %s\n",filename[i]); + return NULL; + } + fprintf(stderr,"using linux console font \"%s\"\n",filename[i]); + + f = malloc(sizeof(*f)); + memset(f,0,sizeof(*f)); + + fgetc(fp); + f->maxenc = 256; + f->width = 8; + f->height = fgetc(fp); + f->fontHeader.min_bounds.left = 0; + f->fontHeader.max_bounds.right = f->width; + f->fontHeader.max_bounds.descent = 0; + f->fontHeader.max_bounds.ascent = f->height; + + f->glyphs = malloc(f->height * 256); + f->extents = malloc(sizeof(FSXCharInfo)*256); + fread(f->glyphs, 256, f->height, fp); + fclose(fp); + + f->eindex = malloc(sizeof(FSXCharInfo*) * 256); + f->gindex = malloc(sizeof(unsigned char*) * 256); + for (i = 0; i < 256; i++) { + f->eindex[i] = f->extents +i; + f->gindex[i] = f->glyphs +i * f->height; + f->eindex[i]->left = 0; + f->eindex[i]->right = 7; + f->eindex[i]->width = 8; + f->eindex[i]->descent = 0; + f->eindex[i]->ascent = f->height; + } + return f; +} + + +#ifdef TESTING +/* ------------------------------------------------------------------ */ +/* for testing */ + +int debug; + +/* list fonts */ +int fs_ls(char *pattern) +{ + int nnames = 16; + int available,i; + char **fonts; + + if (NULL == svr) { + fprintf(stderr,"fs: not connected\n"); + return -1; + } + + fonts = FSListFonts(svr, pattern, nnames, &available); + while (nnames <= available) { + nnames *= 2; + FSFreeFontNames(fonts); + fonts = FSListFonts(svr, pattern, nnames, &available); + } + for (i = 0; i < available; i++) { + fprintf(stderr,"%s\n",fonts[i]); + } + FSFreeFontNames(fonts); + return 0; +} + +void dump_charset(struct fs_font *f) +{ + unsigned char *pos; + int c,x,y; + + x = 0, y = 0; + for (c = 0; c < f->maxenc; c++) { + if (NULL == f->eindex[c]) + continue; + pos = fb_mem+fb_mem_offset; + pos += fb_fix.line_length * (y+f->height-f->eindex[c]->ascent); + pos += x*bpp; + fs_render_fb(pos,fb_fix.line_length,f->eindex[c],f->gindex[c]); + x += f->eindex[c]->right-f->eindex[c]->left+1; + if (x > fb_var.xres - f->width) { + x = 0; + y += f->height+1; + } + if (y > fb_var.yres - f->height) + break; + } +} + +int main(int argc, char *argv[]) +{ + struct fs_font *f = NULL; + unsigned char dummy[42]; + int fd; + + if (argc < 2) { + fprintf(stderr,"missing arg\n"); + exit(1); + } + + /* try font server */ + if (-1 != fs_connect(NULL)) { + fs_ls(argv[1]); + f = fs_open(argv[1]); + if (NULL == f) + fprintf(stderr,"no such font\n"); + } + + /* try console font */ + if (NULL == f) + f = fs_consolefont(NULL); + if (NULL == f) + exit(1); + +#ifdef TTY + exit(1); +#endif + + fd = fb_init(NULL, NULL, 0); + fb_cleanup_fork(); + fb_switch_init(); + fs_init_fb(); + + if (argc < 3) { + dump_charset(f); + } else { + fs_puts(f,0,0,argv[2]); + } + fgets(dummy,42,stdin); + + return 0; +} +#endif @@ -0,0 +1,72 @@ +#ifndef X_DISPLAY_MISSING +# include <FSlib.h> + +struct fs_font { + Font font; + FSXFontInfoHeader fontHeader; + FSPropInfo propInfo; + FSPropOffset *propOffsets; + unsigned char *propData; + + FSXCharInfo *extents; + FSOffset *offsets; + unsigned char *glyphs; + + int maxenc,width,height; + FSXCharInfo **eindex; + unsigned char **gindex; +}; + +#else + +typedef struct _FSXCharInfo { + short left; + short right; + short width; + short ascent; + short descent; + //unsigned short attributes; +} FSXCharInfo; + +typedef struct _FSXFontInfoHeader { + //int flags; + //FSRange char_range; + //unsigned draw_direction; + //FSChar2b default_char; + FSXCharInfo min_bounds; + FSXCharInfo max_bounds; + short font_ascent; + short font_descent; +} FSXFontInfoHeader; + +struct fs_font { + FSXFontInfoHeader fontHeader; + //unsigned char *propData; + FSXCharInfo *extents; + unsigned char *glyphs; + int maxenc,width,height; + FSXCharInfo **eindex; + unsigned char **gindex; +}; + +#endif + +/* ------------------------------------------------------------------ */ + +extern unsigned int fs_bpp, fs_black, fs_white; +void (*fs_setpixel)(void *ptr, unsigned int color); + +int fs_init_fb(int white8); +void fs_render_fb(unsigned char *ptr, int pitch, + FSXCharInfo *charInfo, unsigned char *data); +int fs_puts(struct fs_font *f, unsigned int x, unsigned int y, + unsigned char *str); +int fs_textwidth(struct fs_font *f, unsigned char *str); +void fs_render_tty(FSXCharInfo *charInfo, unsigned char *data); + +#ifndef X_DISPLAY_MISSING +int fs_connect(char *servername); +struct fs_font* fs_open(char *pattern); +#endif +struct fs_font* fs_consolefont(char **filename); +void fs_free(struct fs_font *f); diff --git a/genthumbnail.c b/genthumbnail.c new file mode 100644 index 0000000..e3c0505 --- /dev/null +++ b/genthumbnail.c @@ -0,0 +1,219 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#include <jpeglib.h> +#include "jpeg/transupp.h" /* Support routines for jpegtran */ +#include "jpegtools.h" + +#include "misc.h" + +#include "readers.h" +#include "filter.h" +#include "genthumbnail.h" + +/* ---------------------------------------------------------------------- */ + +static struct ida_image* +read_jpeg(char *filename) +{ + struct ida_image *img; + FILE *fp; + unsigned int y; + void *data; + + /* open file */ + if (NULL == (fp = fopen(filename, "r"))) { + fprintf(stderr,"open %s: %s\n",filename,strerror(errno)); + return NULL; + } + + /* load image */ + img = malloc(sizeof(*img)); + memset(img,0,sizeof(*img)); + data = jpeg_loader.init(fp,filename,0,&img->i,0); + if (NULL == data) { + fprintf(stderr,"loading %s [%s] FAILED\n",filename,jpeg_loader.name); + free(img); + return NULL; + } + img->data = malloc(img->i.width * img->i.height * 3); + for (y = 0; y < img->i.height; y++) + jpeg_loader.read(img->data + img->i.width * 3 * y, y, data); + jpeg_loader.done(data); + return img; +} + +/* ---------------------------------------------------------------------- */ + +static struct ida_image* +scale_thumbnail(struct ida_image *src, int max) +{ + struct op_resize_parm p; + struct ida_rect rect; + struct ida_image *dest; + void *data; + unsigned int y; + float xs,ys,scale; + + xs = (float)max / src->i.width; + ys = (float)max / src->i.height; + scale = (xs < ys) ? xs : ys; + + dest = malloc(sizeof(*dest)); + memset(dest,0,sizeof(*dest)); + memset(&rect,0,sizeof(rect)); + memset(&p,0,sizeof(p)); + + p.width = src->i.width * scale; + p.height = src->i.height * scale; + p.dpi = src->i.dpi; + if (0 == p.width) + p.width = 1; + if (0 == p.height) + p.height = 1; + + data = desc_resize.init(src,&rect,&dest->i,&p); + dest->data = malloc(dest->i.width * dest->i.height * 3); + for (y = 0; y < dest->i.height; y++) + desc_resize.work(src,&rect, + dest->data + 3 * dest->i.width * y, + y, data); + desc_resize.done(data); + return dest; +} + +/* ---------------------------------------------------------------------- */ + +struct thc { + struct jpeg_compress_struct dst; + struct jpeg_error_mgr err; + unsigned char *out; + int osize; +}; + +static void thc_dest_init(struct jpeg_compress_struct *cinfo) +{ + struct thc *h = container_of(cinfo, struct thc, dst); + cinfo->dest->next_output_byte = h->out; + cinfo->dest->free_in_buffer = h->osize; +} + +static boolean thc_dest_flush(struct jpeg_compress_struct *cinfo) +{ + fprintf(stderr,"jpeg: panic: output buffer full\n"); + exit(1); +} + +static void thc_dest_term(struct jpeg_compress_struct *cinfo) +{ + struct thc *h = container_of(cinfo, struct thc, dst); + h->osize -= cinfo->dest->free_in_buffer; +} + +static struct jpeg_destination_mgr thumbnail_dst = { + .init_destination = thc_dest_init, + .empty_output_buffer = thc_dest_flush, + .term_destination = thc_dest_term, +}; + +static int +compress_thumbnail(struct ida_image *img, char *dest, int max) +{ + struct thc thc; + unsigned char *line; + unsigned int i; + + memset(&thc,0,sizeof(thc)); + thc.dst.err = jpeg_std_error(&thc.err); + jpeg_create_compress(&thc.dst); + thc.dst.dest = &thumbnail_dst; + thc.out = dest; + thc.osize = max; + + thc.dst.image_width = img->i.width; + thc.dst.image_height = img->i.height; + thc.dst.input_components = 3; + thc.dst.in_color_space = JCS_RGB; + jpeg_set_defaults(&thc.dst); + jpeg_start_compress(&thc.dst, TRUE); + + for (i = 0, line = img->data; i < img->i.height; i++, line += img->i.width*3) + jpeg_write_scanlines(&thc.dst, &line, 1); + + jpeg_finish_compress(&(thc.dst)); + jpeg_destroy_compress(&(thc.dst)); + + return thc.osize; +} + +/* ---------------------------------------------------------------------- */ + +int create_thumbnail(char *filename, char *dest, int max) +{ + struct ida_image *img,*thumb; + int size; + + //fprintf(stderr,"%s: read ",filename); + img = read_jpeg(filename); + if (!img) { + fprintf(stderr,"FAILED\n"); + return -1; + } + + //fprintf(stderr,"scale "); + thumb = scale_thumbnail(img,160); + if (!thumb) { + free(img->data); + free(img); + fprintf(stderr,"FAILED\n"); + return -1; + } + + //fprintf(stderr,"compress "); + size = compress_thumbnail(thumb,dest,max); + + /* cleanup */ + free(img->data); + free(img); + free(thumb->data); + free(thumb); + return size; +} + +/* ---------------------------------------------------------------------- */ + +#if 0 + +#define THUMB_MAX 65536 + +static int handle_image(char *filename) +{ + char *dest; + int size; + + dest = malloc(THUMB_MAX); + size = create_thumbnail(filename,dest,THUMB_MAX); + + fprintf(stderr,"transform "); + jpeg_transform_inplace(filename, JXFORM_NONE, NULL, + dest, size, JFLAG_UPDATE_THUMBNAIL); + + fprintf(stderr,"done\n"); + return 0; +} + +int main(int argc, char *argv[]) +{ + int i; + + for (i = 1; i < argc; i++) + handle_image(argv[i]); + return 0; +} + +#endif + diff --git a/genthumbnail.h b/genthumbnail.h new file mode 100644 index 0000000..add3ff6 --- /dev/null +++ b/genthumbnail.h @@ -0,0 +1 @@ +int create_thumbnail(char *filename, char *dest, int max); @@ -0,0 +1,141 @@ +/* + * file viewer (hex dump) + * quick & dirty for now, handling xxl files doesn't work very well ... + * + * (c) 2001 Gerd Knorr <kraxel@bytesex.org> + * + */ + +#include <stdio.h> +#include <ctype.h> + +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> +#include <Xm/Xm.h> +#include <Xm/Form.h> +#include <Xm/Label.h> +#include <Xm/RowColumn.h> +#include <Xm/PushB.h> +#include <Xm/ScrolledW.h> +#include <Xm/SelectioB.h> + +#include "RegEdit.h" +#include "hex.h" + +extern Display *dpy; +extern Widget app_shell; + +/*----------------------------------------------------------------------*/ + +static Widget hex_dlg, hex_label; + +static void +hex_destroy(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XtDestroyWidget(hex_dlg); + hex_dlg = NULL; +} + +static XmString +hexify(int addr,unsigned char *data, int bytes) +{ + XmString xmpage,xmchunk; + char line[120]; + int len,y,i; + + xmpage = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT, NULL); + for (y = 0; y < bytes; y += 16) { + /* hexify line */ + len = sprintf(line,"%04x ",addr+y); + for (i = 0; i < 16; i++) { + if (y+i < bytes) + len += sprintf(line+len,"%02x ",data[y+i]); + else + len += sprintf(line+len," "); + if (3 == i%4) + len += sprintf(line+len," "); + } + for (i = 0; i < 16; i++) { + if (y+i < bytes) + if (isprint(data[y+i])) + len += sprintf(line+len,"%c",data[y+i]); + else + len += sprintf(line+len,"."); + else + len += sprintf(line+len," "); + } + len += sprintf(line+len,"\n"); + /* add last chunk for line */ + xmchunk = XmStringGenerate(line, NULL, XmMULTIBYTE_TEXT, NULL); + xmpage = XmStringConcatAndFree(xmpage, xmchunk); + } + return xmpage; +} + +/* guess whenever we have ascii or binary data */ +static int +is_binary(unsigned char *data, int bytes) +{ + int i, bin; + + bin = 0; + for (i = 0; i < 64 && i < bytes; i++) { + if (!isprint(data[i]) && + data[i] != '\t' && + data[i] != '\n') + bin = 1; + } + return bin; +} + +void +hex_display(char *filename) +{ + Widget view; + XmString xmpage,chunk; + unsigned char data[32768+1]; + int chars; + FILE *fp; + + /* build dialog */ + if (!hex_dlg) { + hex_dlg = XmCreatePromptDialog(app_shell,"hex",NULL,0); + XmdRegisterEditres(XtParent(hex_dlg)); + XtUnmanageChild(XmSelectionBoxGetChild(hex_dlg, + XmDIALOG_SELECTION_LABEL)); + XtUnmanageChild(XmSelectionBoxGetChild(hex_dlg,XmDIALOG_HELP_BUTTON)); + XtUnmanageChild(XmSelectionBoxGetChild(hex_dlg, + XmDIALOG_CANCEL_BUTTON)); + XtUnmanageChild(XmSelectionBoxGetChild(hex_dlg,XmDIALOG_TEXT)); + XtAddCallback(hex_dlg,XmNokCallback,hex_destroy,NULL); + view = XmCreateScrolledWindow(hex_dlg,"view",NULL,0); + XtManageChild(view); + hex_label = XtVaCreateManagedWidget("label", xmLabelWidgetClass, + view, NULL); + } + XtManageChild(hex_dlg); + + /* read the file (32k max) */ + fp = fopen(filename,"r"); + memset(data,0,sizeof(data)); + chars = fread(data, 1, sizeof(data)-1, fp); + if (is_binary(data,chars)) { + xmpage = hexify(0, data, chars); + if (sizeof(data)-1 == chars) { + chunk = XmStringGenerate("[ ... ]\n",NULL, + XmMULTIBYTE_TEXT, NULL); + xmpage = XmStringConcatAndFree(xmpage,chunk); + } + } else { + xmpage = XmStringGenerate(data, NULL, XmMULTIBYTE_TEXT, NULL); + if (sizeof(data)-1 == chars) { + chunk = XmStringGenerate("\n[ ... ]\n",NULL, + XmMULTIBYTE_TEXT, NULL); + xmpage = XmStringConcatAndFree(xmpage,chunk); + } + } + XtVaSetValues(hex_label,XmNlabelString,xmpage,NULL); + XmStringFree(xmpage); + XtVaSetValues(XtParent(hex_dlg),XmNtitle,filename,NULL); + fclose(fp); +} @@ -0,0 +1 @@ +void hex_display(char *filename); @@ -0,0 +1,130 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> +#include <X11/xpm.h> +#include <Xm/Xm.h> + +#include "icons.h" +#include "misc.h" + +#include "xpm/cw.xpm" +#include "xpm/ccw.xpm" +#include "xpm/fliph.xpm" +#include "xpm/flipv.xpm" +#include "xpm/prev.xpm" +#include "xpm/next.xpm" +#include "xpm/zoomin.xpm" +#include "xpm/zoomout.xpm" +#include "xpm/exit.xpm" + +#include "xpm/question.xpm" +#include "xpm/dir.xpm" +#include "xpm/file.xpm" +#include "xpm/unknown.xpm" + +static void patch_bg(XImage *image, XImage *shape, + int width, int height, Pixel bg) +{ + unsigned int x,y; + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + if (!XGetPixel(shape, x, y)) + XPutPixel(image, x, y, bg); +} + +static void +add_pixmap(Display *dpy, Pixel bg, char *name, char **data) +{ + XImage *image,*shape; + XpmAttributes attr; + char sname[32]; + + memset(&attr,0,sizeof(attr)); + XpmCreateImageFromData(dpy,data,&image,&shape,&attr); + + if (shape) { + patch_bg(image,shape,attr.width,attr.height,bg); + snprintf(sname,sizeof(sname),"%s_shape",name); + XmInstallImage(shape,sname); + } + XmInstallImage(image,name); +} + +Pixmap x11_icon_fit(Display *dpy, Pixmap icon, unsigned long bg, + int width, int height) +{ + Pixmap pix; + GC gc; + XGCValues values; + Window root; + unsigned int w,h,b,d; + int x,y; + + XGetGeometry(dpy,icon,&root,&x,&y,&w,&h,&b,&d); + pix = XCreatePixmap(dpy,icon,width,height,d); + + /* fill background */ + values.foreground = bg; + values.background = bg; + values.fill_style = FillSolid; + gc = XCreateGC(dpy, icon, GCForeground | GCBackground | GCFillStyle, + &values); + XFillRectangle(dpy,pix,gc,0,0,width,height); + + /* blit */ + if (w <= width && h <= height) { + /* just center ... */ + x = (width - w) / 2; + y = (height - h) / 2; + XCopyArea(dpy,icon,pix,gc,0,0,w,h,x,y); + } else { + /* must scale down */ +#if 0 + float xs,ys,scale; + + xs = (float)width / w; + ys = (float)height / h; + scale = (xs < ys) ? xs : ys; + w = w * scale; + h = h * scale; + if (0 == w) w = 1; + if (0 == h) h = 1; + + x = (width - w) / 2; + y = (height - h) / 2; + XCopyArea(dpy,icon,pix,gc,0,0,w,h,x,y); +#endif + x = (width - w) / 2; + y = (height - h) / 2; + if (x < 0) + x = 0; + if (y < 0) + y = 0; + XCopyArea(dpy,icon,pix,gc,0,0,w,h,0,0); + } + + XFreeGC(dpy, gc); + return pix; +} + +void +x11_icons_init(Display *dpy, unsigned long bg) +{ + add_pixmap(dpy, bg, "rotcw", cw_xpm); + add_pixmap(dpy, bg, "rotccw", ccw_xpm); + add_pixmap(dpy, bg, "fliph", fliph_xpm); + add_pixmap(dpy, bg, "flipv", flipv_xpm); + add_pixmap(dpy, bg, "prev", prev_xpm); + add_pixmap(dpy, bg, "next", next_xpm); + add_pixmap(dpy, bg, "zoomin", zoomin_xpm); + add_pixmap(dpy, bg, "zoomout", zoomout_xpm); + add_pixmap(dpy, bg, "exit", exit_xpm); + + add_pixmap(dpy, bg, "question", question_xpm); + add_pixmap(dpy, bg, "dir", dir_xpm); + add_pixmap(dpy, bg, "file", file_xpm); + add_pixmap(dpy, bg, "unknown", unknown_xpm); +} @@ -0,0 +1,3 @@ +Pixmap x11_icon_fit(Display *dpy, Pixmap icon, unsigned long bg, + int width, int height); +void x11_icons_init(Display *dpy, unsigned long bg); @@ -0,0 +1,1909 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <locale.h> +#include <langinfo.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <Xm/Xm.h> +#include <Xm/Primitive.h> +#include <Xm/Label.h> +#include <Xm/CascadeB.h> +#include <Xm/PushB.h> +#include <Xm/Separator.h> +#include <Xm/RowColumn.h> +#include <Xm/ScrolledW.h> +#include <Xm/Protocols.h> +#include <Xm/List.h> +#include <Xm/Form.h> +#include <Xm/MessageB.h> +#include <Xm/SelectioB.h> +#include <Xm/Scale.h> +#include <Xm/Text.h> +#include <Xm/FileSB.h> +#include <Xm/ToggleB.h> +#include <Xm/DrawingA.h> +#include <Xm/Transfer.h> +#include <Xm/TransferP.h> + +#include "RegEdit.h" +#include "ida.h" +#include "x11.h" +#include "man.h" +#include "readers.h" +#include "writers.h" +#include "viewer.h" +#include "op.h" +#include "lut.h" +#include "filter.h" +#include "color.h" +#include "icons.h" +#include "browser.h" +#include "filelist.h" +#include "xdnd.h" +#include "selections.h" +#include "sane.h" +#include "curl.h" +#include "idaconfig.h" + +/* ---------------------------------------------------------------------- */ + +static void popup_ac(Widget, XEvent*, String*, Cardinal*); +static void exit_ac(Widget, XEvent*, String*, Cardinal*); +static void next_ac(Widget, XEvent*, String*, Cardinal*); +static void prev_ac(Widget, XEvent*, String*, Cardinal*); +static void next_page_ac(Widget, XEvent*, String*, Cardinal*); +static void prev_page_ac(Widget, XEvent*, String*, Cardinal*); +static void zoom_ac(Widget, XEvent*, String*, Cardinal*); +static void scroll_ac(Widget, XEvent*, String*, Cardinal*); +static void debug_ac(Widget, XEvent*, String*, Cardinal*); +static void load_ac(Widget, XEvent*, String*, Cardinal*); +static void save_ac(Widget, XEvent*, String*, Cardinal*); +static void scan_ac(Widget, XEvent*, String*, Cardinal*); +static void print_ac(Widget, XEvent*, String*, Cardinal*); + +static void undo_ac(Widget, XEvent*, String*, Cardinal*); +static void filter_ac(Widget, XEvent*, String*, Cardinal*); +static void gamma_ac(Widget, XEvent*, String*, Cardinal*); +static void bright_ac(Widget, XEvent*, String*, Cardinal*); +static void contrast_ac(Widget, XEvent*, String*, Cardinal*); +static void color_ac(Widget, XEvent*, String*, Cardinal*); +static void f3x3_ac(Widget, XEvent*, String*, Cardinal*); +static void resize_ac(Widget, XEvent*, String*, Cardinal*); +static void rotate_ac(Widget, XEvent*, String*, Cardinal*); +static void sharpe_ac(Widget, XEvent*, String*, Cardinal*); + +static XtActionsRec actionTable[] = { + { "Exit", exit_ac }, + { "Next", next_ac }, + { "Prev", prev_ac }, + { "NextPage", next_page_ac }, + { "PrevPage", prev_page_ac }, + { "Zoom", zoom_ac }, + { "Scroll", scroll_ac }, + { "Debug", debug_ac }, + { "Popup", popup_ac }, + { "Man", man_action }, + { "Load", load_ac }, + { "Save", save_ac }, + { "Scan", scan_ac }, + { "Print", print_ac }, + { "Browser", browser_ac }, + { "Filelist", filelist_ac }, + + { "Undo", undo_ac }, + { "Filter", filter_ac }, + { "Gamma", gamma_ac }, + { "Bright", bright_ac }, + { "Contrast", contrast_ac }, + { "Color", color_ac }, + { "F3x3", f3x3_ac }, + { "Resize", resize_ac }, + { "Rotate", rotate_ac }, + { "Sharpe", sharpe_ac }, + + { "Ipc", ipc_ac }, + { "Xdnd", XdndAction }, +}; + +/* ---------------------------------------------------------------------- */ + +XtAppContext app_context; +Display *dpy; +Widget app_shell; +int gray=0; +char *binary; +struct ida_viewer *ida; + +/* ---------------------------------------------------------------------- */ + +struct ARGS args; +unsigned int pcd_res; +unsigned int sane_res; + +XtResource args_desc[] = { + { + "debug", + XtCBoolean, XtRBoolean, sizeof(Boolean), + XtOffset(struct ARGS*,debug), + XtRString, "false" + },{ + "help", + XtCBoolean, XtRBoolean, sizeof(Boolean), + XtOffset(struct ARGS*,help), + XtRString, "false" + },{ + "testload", + XtCBoolean, XtRBoolean, sizeof(Boolean), + XtOffset(struct ARGS*,testload), + XtRString, "false" + } +}; +const int args_count = XtNumber(args_desc); + +XrmOptionDescRec opt_desc[] = { + { "-d", "debug", XrmoptionNoArg, "true" }, + { "-debug", "debug", XrmoptionNoArg, "true" }, + { "-testload", "testload", XrmoptionNoArg, "true" }, + { "-h", "help", XrmoptionNoArg, "true" }, + { "-help", "help", XrmoptionNoArg, "true" }, + { "--help", "help", XrmoptionNoArg, "true" }, +}; +const int opt_count = (sizeof(opt_desc)/sizeof(XrmOptionDescRec)); + +static String fallback_ressources[] = { +#include "Ida.ad.h" + NULL +}; + +/* ---------------------------------------------------------------------- */ + +static struct ida_writer *cwriter; +static char *save_filename; +static char *print_command; + +static Widget control_shell,status; +static Atom wm_delete_window; + +static Widget view,loadbox,savebox,printbox; + +/* file list */ +static Widget wlist; +static char **files = NULL; +static int cfile = -1; +static int nfiles = 0; +static int cpage = 0; +static int npages = 1; + +/* filter controls */ +static int gamma_val = 100; +static int bright_val = 0; +static int contrast_val = 0; +static int rotate_val = 0; +static int sharpe_val = 10; + +static struct MY_TOPLEVELS { + char *name; + Widget *shell; + int mapped; +} my_toplevels [] = { + { "control", &control_shell }, +}; +#define TOPLEVELS (sizeof(my_toplevels)/sizeof(struct MY_TOPLEVELS)) + +/* ---------------------------------------------------------------------- */ + +static void +popup_ac(Widget widget, XEvent *event, + String *params, Cardinal *num_params) +{ + unsigned int i; + + /* which window we are talking about ? */ + if (*num_params > 0) { + for (i = 0; i < TOPLEVELS; i++) { + if (0 == strcasecmp(my_toplevels[i].name,params[0])) + break; + } + if (i == TOPLEVELS) { + fprintf(stderr,"PopupAction: oops: shell not found (name=%s)\n", + params[0]); + return; + } + } else { + for (i = 0; i < TOPLEVELS; i++) { + if (*(my_toplevels[i].shell) == widget) + break; + } + if (i == TOPLEVELS) { + fprintf(stderr,"PopupAction: oops: shell not found (%p:%s)\n", + widget,XtName(widget)); + return; + } + } + + /* popup/down window */ + if (!my_toplevels[i].mapped) { + XtPopup(*(my_toplevels[i].shell), XtGrabNone); + my_toplevels[i].mapped = 1; + } else { + XtPopdown(*(my_toplevels[i].shell)); + my_toplevels[i].mapped = 0; + } +} + +static void +popupdown_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + int i = 0; + popup_ac(clientdata, NULL, NULL, &i); +} + +/* ---------------------------------------------------------------------- */ + +void +destroy_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XtDestroyWidget(clientdata); +} + +void +action_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + char *calls, *action, *argv[16]; /* max: F3x3(9 args) */ + int argc; + + calls = strdup(clientdata); + action = strtok(calls,"(),"); + for (argc = 0; NULL != (argv[argc] = strtok(NULL,"(),")); argc++) + /* nothing */; + XtCallActionProc(widget,action,NULL,argv,argc); + free(calls); +} + +static void +about_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + Widget msgbox; + + msgbox = XmCreateInformationDialog(app_shell,"aboutbox",NULL,0); + XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_HELP_BUTTON)); + XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_CANCEL_BUTTON)); + XtAddCallback(msgbox,XmNokCallback,destroy_cb,msgbox); + XtManageChild(msgbox); +} + +#if 0 +static void +sorry_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + Widget msgbox; + + msgbox = XmCreateErrorDialog(app_shell,"sorrybox",NULL,0); + XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_HELP_BUTTON)); + XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_CANCEL_BUTTON)); + XtAddCallback(msgbox,XmNokCallback,destroy_cb,msgbox); + XtManageChild(msgbox); +} +#endif + +void +debug_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + unsigned int i; + + fprintf(stderr,"Debug:"); + for (i = 0; i < *num; i++) + fprintf(stderr," %s",params[i]); + fprintf(stderr,"\n"); +} + +static void +display_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XmDisplayCallbackStruct *arg = call_data; + + switch (arg->reason) { + case XmCR_NO_RENDITION: + fprintf(stderr,"display_cb: no rendition: \"%s\"\n",arg->tag); + break; + case XmCR_NO_FONT: + fprintf(stderr,"display_cb: no font: \"%s\"\n",arg->font_name); + break; + default: + /* should not happen */ + fprintf(stderr,"display_cb: unknown reason [%d]\n",arg->reason); + break; + } +} + +/* ---------------------------------------------------------------------- */ + +struct ptr_list { + struct ptr_list *next; + Widget widget; +}; +struct ptr_list *ptr_head; + +void ptr_register(Widget widget) +{ + struct ptr_list *item; + + if (XtWindow(widget)) + XDefineCursor(XtDisplay(widget), XtWindow(widget), + ptrs[POINTER_NORMAL]); + item = malloc(sizeof(*item)); + memset(item,0,sizeof(*item)); + item->widget = widget; + item->next = ptr_head; + ptr_head = item; +} + +void ptr_unregister(Widget widget) +{ + struct ptr_list *item,*fitem; + + if (ptr_head->widget == widget) { + fitem = ptr_head; + ptr_head = ptr_head->next; + free(fitem); + return; + } + for (item = ptr_head; NULL != item->next; item = item->next) { + if (item->next->widget == widget) { + fitem = item->next; + item->next = fitem->next; + free(fitem); + return; + } + } + /* shouldn't happen */ + fprintf(stderr,"Oops: widget not found in list\n"); +} + +void ptr_busy(void) +{ + struct ptr_list *item; + + for (item = ptr_head; NULL != item; item = item->next) { + if (!XtWindow(item->widget)) + continue; + XDefineCursor(XtDisplay(item->widget), XtWindow(item->widget), + ptrs[POINTER_BUSY]); + } + XSync(dpy,False); +} + +void ptr_idle(void) +{ + struct ptr_list *item; + + for (item = ptr_head; NULL != item; item = item->next) { + if (!XtWindow(item->widget)) + continue; + XDefineCursor(XtDisplay(item->widget), XtWindow(item->widget), + ptrs[POINTER_NORMAL]); + } +} + +/* ---------------------------------------------------------------------- */ + +static Boolean +exit_wp(XtPointer client_data) +{ + exit(0); +} + +static void +exit_cb(Widget widget, XtPointer client_data, XtPointer calldata) +{ + XtAppAddWorkProc(app_context,exit_wp, NULL); + XtDestroyWidget(app_shell); +} + +void +exit_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + exit_cb(widget,NULL,NULL); +} + +/* ---------------------------------------------------------------------- */ + +static void +list_update(void) +{ + XmStringTable tab; + int i; + + tab = malloc(nfiles * sizeof(XmString)); + for (i = 0; i < nfiles; i++) + tab[i] = XmStringGenerate(files[i], NULL, XmMULTIBYTE_TEXT, NULL); + XtVaSetValues(wlist, + XmNitems, tab, + XmNitemCount, nfiles, + NULL); + for (i = 0; i < nfiles; i++) + XmStringFree(tab[i]); + free(tab); +} + +static int +list_append(char *filename) +{ + int i; + + for (i = 0; i < nfiles; i++) + if (0 == strcmp(files[i],filename)) + return i; + files = realloc(files,sizeof(char*)*(nfiles+1)); + files[nfiles] = strdup(filename); + nfiles++; + return nfiles-1; +} + +static void +list_cb(Widget widget, XtPointer client_data, XtPointer calldata) +{ + XmListCallbackStruct *list = calldata; + + if (0 == list->selected_item_count) + return; + cfile = list->selected_item_positions[0]-1; + cpage = 0; + npages = viewer_loadimage(ida,files[cfile],cpage); + if (-1 == npages) + return; + resize_shell(); +} + +static void +pcd_set(Widget widget) +{ + WidgetList items; + Cardinal nitems; + unsigned int i; + int value; + + value = GET_PHOTOCD_RES(); + XtVaGetValues(widget,XtNchildren,&items, + XtNnumChildren,&nitems,NULL); + for (i = 0; i < nitems; i++) + XmToggleButtonSetState(items[i],value == i+1,False); + pcd_res = value; +} + +static void +pcd_cb(Widget widget, XtPointer client_data, XtPointer calldata) +{ + cfg_set_int(O_PHOTOCD_RES,(intptr_t)client_data); + pcd_set(XtParent(widget)); +} + +static void +cfg_bool_cb(Widget widget, XtPointer client_data, XtPointer calldata) +{ + char *option = XtName(widget); + Boolean value = XmToggleButtonGetState(widget); + cfg_set_bool(O_OPTIONS, option, value); +} + +static void +cfg_save_cb(Widget widget, XtPointer client_data, XtPointer calldata) +{ + ida_write_config(); +} + +static void +create_control(void) +{ + Widget form,menubar,tool,menu,smenu,push; + + control_shell = XtVaAppCreateShell("ctrl","Iv", + topLevelShellWidgetClass, + dpy, + XtNclientLeader,app_shell, + XmNdeleteResponse,XmDO_NOTHING, + NULL); + XmdRegisterEditres(control_shell); + XmAddWMProtocolCallback(control_shell,wm_delete_window, + popupdown_cb,control_shell); + + /* widgets */ + form = XtVaCreateManagedWidget("form", xmFormWidgetClass, control_shell, + NULL); + menubar = XmCreateMenuBar(form,"bar",NULL,0); + XtManageChild(menubar); + tool = XtVaCreateManagedWidget("tool",xmRowColumnWidgetClass, form, + NULL); + status = XtVaCreateManagedWidget("status", xmLabelWidgetClass, form, + NULL); + wlist = XmCreateScrolledList(form,"list",NULL,0); + XtManageChild(wlist); + XtAddCallback(wlist,XmNdefaultActionCallback,list_cb,NULL); + XtAddCallback(wlist,XmNdestinationCallback,selection_dest,NULL); + dnd_add(wlist); + + /* menu - file */ + menu = XmCreatePulldownMenu(menubar,"fileM",NULL,0); + XtVaCreateManagedWidget("file",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + push = XtVaCreateManagedWidget("load",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Load()"); + push = XtVaCreateManagedWidget("save",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Save()"); + push = XtVaCreateManagedWidget("browse",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Browser()"); + push = XtVaCreateManagedWidget("filelist",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Filelist()"); + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); +#ifdef HAVE_LIBSANE + sane_menu(menu); +#endif + push = XtVaCreateManagedWidget("print",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Print()"); + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); + push = XtVaCreateManagedWidget("quit",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,exit_cb,NULL); + + /* menu - edit */ + menu = XmCreatePulldownMenu(menubar,"editM",NULL,0); + XtVaCreateManagedWidget("edit",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + push = XtVaCreateManagedWidget("undo",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Undo()"); + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); + push = XtVaCreateManagedWidget("copy",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Ipc(copy)"); + push = XtVaCreateManagedWidget("paste",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Ipc(paste)"); + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); + push = XtVaCreateManagedWidget("flipv",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(flip-vert)"); + push = XtVaCreateManagedWidget("fliph",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(flip-horz)"); + push = XtVaCreateManagedWidget("rotcw",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(rotate-cw)"); + push = XtVaCreateManagedWidget("rotccw",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(rotate-ccw)"); + push = XtVaCreateManagedWidget("invert",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(invert)"); + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); + push = XtVaCreateManagedWidget("crop",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(crop)"); + push = XtVaCreateManagedWidget("acrop",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(autocrop)"); + push = XtVaCreateManagedWidget("scale",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Resize()"); + push = XtVaCreateManagedWidget("rotany",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Rotate()"); + + /* menu - filters / operations */ + menu = XmCreatePulldownMenu(menubar,"opM",NULL,0); + XtVaCreateManagedWidget("op",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + push = XtVaCreateManagedWidget("gamma",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Gamma()"); + push = XtVaCreateManagedWidget("bright",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Bright()"); + push = XtVaCreateManagedWidget("contr",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Contrast()"); + push = XtVaCreateManagedWidget("color",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Color()"); + push = XtVaCreateManagedWidget("gray",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(grayscale)"); + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); + push = XtVaCreateManagedWidget("blur",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb, + "F3x3(1,1,1, 1,1,1, 1,1,1, 1,9,0)"); + push = XtVaCreateManagedWidget("sharpe",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Sharpe()"); + push = XtVaCreateManagedWidget("edge",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb, + "F3x3(-1,-1,-1, -1,8,-1, -1,-1,-1)"); + push = XtVaCreateManagedWidget("emboss",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb, + "F3x3(1,0,0, 0,0,0, 0,0,-1, 0,0,128)"); + + /* menu - view */ + menu = XmCreatePulldownMenu(menubar,"viewM",NULL,0); + XtVaCreateManagedWidget("view",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + push = XtVaCreateManagedWidget("prev",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Prev()"); + push = XtVaCreateManagedWidget("next",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Next()"); + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); + push = XtVaCreateManagedWidget("prevpage",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"PrevPage()"); + push = XtVaCreateManagedWidget("nextpage",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"NextPage()"); + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); + push = XtVaCreateManagedWidget("zoomin",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Zoom(inc)"); + push = XtVaCreateManagedWidget("zoomout",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Zoom(dec)"); + + /* menu - options */ + menu = XmCreatePulldownMenu(menubar,"optM",NULL,0); + push = XtVaCreateManagedWidget("opt",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + smenu = XmCreatePulldownMenu(menu,"pcdM",NULL,0); + XtVaCreateManagedWidget("pcd",xmCascadeButtonWidgetClass,menu, + XmNsubMenuId,smenu,NULL); + push = XtVaCreateManagedWidget("1",xmToggleButtonWidgetClass,smenu,NULL); + XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)1); + push = XtVaCreateManagedWidget("2",xmToggleButtonWidgetClass,smenu,NULL); + XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)2); + push = XtVaCreateManagedWidget("3",xmToggleButtonWidgetClass,smenu,NULL); + XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)3); + push = XtVaCreateManagedWidget("4",xmToggleButtonWidgetClass,smenu,NULL); + XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)4); + push = XtVaCreateManagedWidget("5",xmToggleButtonWidgetClass,smenu,NULL); + XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)5); + pcd_set(smenu); + + push = XtVaCreateManagedWidget("autozoom",xmToggleButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNvalueChangedCallback,cfg_bool_cb,NULL); + XmToggleButtonSetState(push,GET_AUTOZOOM(),False); + + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); + push = XtVaCreateManagedWidget("cfgsave",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,cfg_save_cb,NULL); + + /* menu - help */ + menu = XmCreatePulldownMenu(menubar,"helpM",NULL,0); + push = XtVaCreateManagedWidget("help",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + XtVaSetValues(menubar,XmNmenuHelpWidget,push,NULL); + push = XtVaCreateManagedWidget("man",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Man(ida)"); + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); + push = XtVaCreateManagedWidget("about",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,about_cb,NULL); + + /* toolbar */ + push = XtVaCreateManagedWidget("prev",xmPushButtonWidgetClass,tool,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Prev()"); + push = XtVaCreateManagedWidget("next",xmPushButtonWidgetClass,tool,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Next()"); + push = XtVaCreateManagedWidget("zoomin",xmPushButtonWidgetClass,tool,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Zoom(inc)"); + push = XtVaCreateManagedWidget("zoomout",xmPushButtonWidgetClass,tool,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Zoom(dec)"); + + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,tool,NULL); + push = XtVaCreateManagedWidget("flipv",xmPushButtonWidgetClass,tool,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(flip-vert)"); + push = XtVaCreateManagedWidget("fliph",xmPushButtonWidgetClass,tool,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(flip-horz)"); + push = XtVaCreateManagedWidget("rotccw",xmPushButtonWidgetClass,tool,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(rotate-ccw)"); + push = XtVaCreateManagedWidget("rotcw",xmPushButtonWidgetClass,tool,NULL); + XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(rotate-cw)"); + + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,tool,NULL); + push = XtVaCreateManagedWidget("exit",xmPushButtonWidgetClass,tool,NULL); + XtAddCallback(push,XmNactivateCallback,exit_cb,NULL); +} + +/* ---------------------------------------------------------------------- */ + +void +resize_shell(void) +{ + char *title,*base; + Dimension x,y,w,h,sw,sh; + XmString str; + int len; + + XtVaGetValues(app_shell, XtNx,&x, XtNy,&y, NULL); + + /* resize shell + move shell + size: image size + 2*shadowThickness */ + w = ida->scrwidth+2; + h = ida->scrheight+2; + sw = XtScreen(ida->widget)->width; + sh = XtScreen(ida->widget)->height; + if (w > sw) + w = sw; + if (h > sh) + h = sh; + if (x+w > sw) + x = sw-w; + if (y+h > sh) + y = sh-h; + + base = strrchr(ida->file,'/'); + if (base) + base++; + else + base = ida->file; + title = malloc(strlen(base)+128); + len = sprintf(title,"%s (%dx%d", base, + ida->img.i.width, ida->img.i.height); + if (ida->img.i.dpi) + len += sprintf(title+len," | %d dpi", + ida->img.i.dpi); + if (ida->img.i.npages > 1) + len += sprintf(title+len," | page %d/%d", + cpage+1, ida->img.i.npages); + len += sprintf(title+len," | %d%%)", viewer_i2s(ida->zoom,100)); + XtVaSetValues(app_shell, XtNtitle,title, + /* XtNx,x, XtNy,y, */ XtNwidth,w, XtNheight,h, + NULL); + str = XmStringGenerate(title,NULL,XmMULTIBYTE_TEXT,NULL); + XtVaSetValues(status,XmNlabelString,str,NULL); + XmStringFree(str); + free(title); +} + +static int +load_file(int nr, int np) +{ + if(nr < 0 || nr >= nfiles) + return -1; + npages = viewer_loadimage(ida,files[nr],np); + if (-1 == npages) + return -1; + resize_shell(); +#if 0 + XmListSelectPos(wlist,nr+1,False); + cfile = nr; +#endif + return npages; +} + +char* +load_tmpfile(char *base) +{ + char *tmpdir; + char *filename; + + tmpdir = getenv("TMPDIR"); + if (NULL == tmpdir) + tmpdir="/tmp"; + filename = malloc(strlen(tmpdir)+strlen(base)+16); + sprintf(filename,"%s/%s-XXXXXX",tmpdir,base); + return filename; +} + +static void +load_logo(void) +{ + static unsigned char logo[] = { +#include "logo.h" + }; + char *filename = load_tmpfile("ida-logo"); + int fd; + fd = mkstemp(filename); + write(fd,logo,sizeof(logo)); + close(fd); + cpage = 0; + npages = 1; + if (0 < viewer_loadimage(ida,filename,cpage)) { + ida->file = "ida " VERSION; + resize_shell(); + } + unlink(filename); + free(filename); +} + +static void +load_stdin(void) +{ + char *filename = load_tmpfile("ida-stdin"); + char buf[4096]; + int rc,fd; + fd = mkstemp(filename); + for (;;) { + rc = read(0,buf,sizeof(buf)); + if (rc <= 0) + break; + write(fd,buf,rc); + } + close(fd); + cpage = 0; + npages = 1; + if (0 < viewer_loadimage(ida,filename,cpage)) { + ida->file = "stdin"; + resize_shell(); + } + unlink(filename); + free(filename); +} + +void +next_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + for (;;) { + if (cfile >= nfiles-1) + return; + cfile++; + cpage = 0; + if (0 <= load_file(cfile,cpage)) + break; + } +} + +void +prev_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + for (;;) { + if (cfile < 1) + return; + cfile--; + cpage = 0; + if (0 <= load_file(cfile,cpage)) + break; + } +} + +void +next_page_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + for (;;) { + if (cpage >= npages-1) + return; + cpage++; + if (0 <= load_file(cfile,cpage)) + break; + } +} + +void +prev_page_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + for (;;) { + if (cpage <= 0) + return; + cpage--; + if (0 <= load_file(cfile,cpage)) + break; + } +} + +void +zoom_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + int zoom; + + if (0 == *num) + return; + + if (0 == strcasecmp(params[0],"auto")) { + viewer_autozoom(ida); + return; + } + + if (0 == strcasecmp(params[0],"inc")) { + zoom = ida->zoom+1; + } else if (0 == strcasecmp(params[0],"dec")) { + zoom = ida->zoom-1; + } else { + zoom = atoi(params[0]); + } + viewer_setzoom(ida,zoom); + resize_shell(); +} + +void +scroll_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + fprintf(stderr,"Scroll(): %s\n",XtName(widget)); +} + +/* ---------------------------------------------------------------------- */ + +void new_file(char *name, int complain) +{ + struct stat st; + int n; + + if (curl_is_url(name)) + goto load; + + if (0 == strncasecmp(name,"file:",5)) + name += 5; + if (-1 == stat(name,&st)) { + if (complain) + fprintf(stderr,"stat %s: %s\n",name,strerror(errno)); + return; + } + switch (st.st_mode & S_IFMT) { + case S_IFDIR: + browser_window(name); + break; + case S_IFREG: + goto load; + break; + } + return; + + load: + n = list_append(name); + list_update(); + cpage = 0; + load_file(n,cpage); +} + +static void +load_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XmFileSelectionBoxCallbackStruct *cb = call_data; + char *line; + + if (cb->reason == XmCR_OK) { + line = XmStringUnparse(cb->value,NULL, + XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT, + NULL,0,0); + new_file(line,1); + } + XtUnmanageChild(widget); +} + +void +load_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + Widget help; + + if (NULL == loadbox) { + loadbox = XmCreateFileSelectionDialog(app_shell,"load",NULL,0); + help = XmFileSelectionBoxGetChild(loadbox,XmDIALOG_HELP_BUTTON); + XtUnmanageChild(help); + XtAddCallback(loadbox,XmNokCallback,load_done_cb,NULL); + XtAddCallback(loadbox,XmNcancelCallback,load_done_cb,NULL); + } else { + XmFileSelectionDoSearch(loadbox,NULL); + } + XtManageChild(loadbox); +} + +void +scan_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ +#ifdef HAVE_LIBSANE + cpage = 0; + if (*num) + npages = viewer_loader_start(ida, &sane_loader, NULL, params[0], 0); + else + npages = viewer_loader_start(ida, &sane_loader, NULL, "", 0); + if (-1 == npages) + return; + ida->file = "scanned image"; + resize_shell(); +#endif +} + +/* ---------------------------------------------------------------------- */ + +void +do_save_print(void) +{ + FILE *fp; + + if (save_filename) { + XtUnmanageChild(savebox); + ptr_busy(); + if (NULL == (fp = fopen(save_filename,"wb"))) { + fprintf(stderr,"save: can't open %s: %s\n", + save_filename,strerror(errno)); + } else if (-1 == cwriter->write(fp,&ida->img)) { + fclose(fp); + fprintf(stderr,"saving %s FAILED",save_filename); + } else { + fclose(fp); + list_append(save_filename); + list_update(); + } + ptr_idle(); + } + if (print_command) { + XtUnmanageChild(printbox); + ptr_busy(); + if (NULL == (fp = popen(print_command,"w"))) { + fprintf(stderr,"print: can't exec %s: %s\n", + print_command,strerror(errno)); + } else { + if (-1 == cwriter->write(fp,&ida->img)) + fprintf(stderr,"printing FAILED"); + fclose(fp); + } + ptr_idle(); + } +} + +static void +save_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XmFileSelectionBoxCallbackStruct *cb = call_data; + + if (cb->reason == XmCR_OK) { + print_command = NULL; + save_filename = XmStringUnparse(cb->value,NULL, + XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT, + NULL,0,0); + if (cwriter->conf) { + cwriter->conf(widget,&ida->img); + } else { + do_save_print(); + } + } else { + XtUnmanageChild(widget); + } +} + +static void +save_fmt_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + cwriter = clientdata; +} + +static void +save_ext_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + Widget option = clientdata; + Widget menu; + WidgetList children; + Cardinal nchildren; + struct ida_writer *wr = NULL; + struct list_head *item; + char *name,*ext; + int i,j,pick; + + name = XmTextGetString(widget); + ext = strrchr(name,'.'); + if (NULL == ext) + return; + if (strchr(ext,'/')) + return; + ext++; + + i = 0; pick = -1; + list_for_each(item,&writers) { + wr = list_entry(item, struct ida_writer, list); + for (j = 0; NULL != wr->ext[j]; j++) + if (0 == strcasecmp(ext,wr->ext[j])) + pick = i; + if (-1 != pick) + break; + i++; + } + if (-1 == pick) + return; + + XtVaGetValues(option,XmNsubMenuId,&menu,NULL); + XtVaGetValues(menu,XtNchildren,&children, + XtNnumChildren,&nchildren,NULL); + XtVaSetValues(option,XmNmenuHistory,children[pick],NULL); + cwriter = wr; +} + +static void +save_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + Widget help,menu,option,push,text; + Arg args[2]; + struct ida_writer *wr = NULL; + struct list_head *item; + + if (NULL == savebox) { + savebox = XmCreateFileSelectionDialog(app_shell,"save",NULL,0); + help = XmFileSelectionBoxGetChild(savebox,XmDIALOG_HELP_BUTTON); + text = XmFileSelectionBoxGetChild(savebox,XmDIALOG_TEXT); + XtUnmanageChild(help); + + menu = XmCreatePulldownMenu(savebox,"formatM",NULL,0); + XtSetArg(args[0],XmNsubMenuId,menu); + option = XmCreateOptionMenu(savebox,"format",args,1); + XtManageChild(option); + list_for_each(item,&writers) { + wr = list_entry(item, struct ida_writer, list); + push = XtVaCreateManagedWidget(wr->label, + xmPushButtonWidgetClass,menu, + NULL); + XtAddCallback(push,XmNactivateCallback,save_fmt_cb,wr); + } + cwriter = list_entry(writers.next, struct ida_writer, list); + + XtAddCallback(text,XmNvalueChangedCallback,save_ext_cb,option); + XtAddCallback(savebox,XmNokCallback,save_done_cb,NULL); + XtAddCallback(savebox,XmNcancelCallback,save_done_cb,NULL); + } else { + XmFileSelectionDoSearch(savebox,NULL); + } + XtManageChild(savebox); +} + +/* ---------------------------------------------------------------------- */ + +static void +print_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XmSelectionBoxCallbackStruct *cb = call_data; + + if (cb->reason == XmCR_OK) { + save_filename = NULL; + print_command = XmStringUnparse(cb->value,NULL, + XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT, + NULL,0,0); + cwriter = &ps_writer; + cwriter->conf(widget,&ida->img); + } else { + XtUnmanageChild(widget); + } +} + +static void +print_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + if (NULL == printbox) { + printbox = XmCreatePromptDialog(app_shell,"print",NULL,0); + XtUnmanageChild(XmSelectionBoxGetChild(printbox,XmDIALOG_HELP_BUTTON)); + XtAddCallback(printbox,XmNokCallback,print_done_cb,NULL); + XtAddCallback(printbox,XmNcancelCallback,print_done_cb,NULL); + } + XtManageChild(printbox); +} + +/* ---------------------------------------------------------------------- */ + +static struct ida_op *ops[] = { + &desc_flip_vert, + &desc_flip_horz, + &desc_rotate_cw, + &desc_rotate_ccw, + &desc_invert, + &desc_crop, + &desc_autocrop, + &desc_grayscale, + NULL +}; + +void +filter_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + struct ida_op *op = NULL; + int i; + + if (*num < 1) + return; + for (i = 0; NULL != ops[i]; i++) { + op = ops[i]; + if (0 == strcasecmp(op->name,params[0])) + break; + } + if (NULL == ops[i]) { + fprintf(stderr,"Oops: unknown filter: %s\n",params[0]); + return; + } + + viewer_start_op(ida,op,NULL); + if (ida->op_src.i.width != ida->img.i.width || + ida->op_src.i.height != ida->img.i.height) + resize_shell(); +} + +void +f3x3_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + struct op_3x3_parm p; + + if (*num < 9) { + fprintf(stderr,"F3x3: wrong number of args (%d, need 9)\n",*num); + return; + } + memset(&p,0,sizeof(p)); + p.f1[0] = atoi(params[0]); + p.f1[1] = atoi(params[1]); + p.f1[2] = atoi(params[2]); + p.f2[0] = atoi(params[3]); + p.f2[1] = atoi(params[4]); + p.f2[2] = atoi(params[5]); + p.f3[0] = atoi(params[6]); + p.f3[1] = atoi(params[7]); + p.f3[2] = atoi(params[8]); + if (*num > 9) p.mul = atoi(params[ 9]); + if (*num > 10) p.div = atoi(params[10]); + if (*num > 11) p.add = atoi(params[11]); + if (debug) { + fprintf(stderr,"f3x3: -----------\n"); + fprintf(stderr,"f3x3: %3d %3d %3d\n",p.f1[0],p.f1[1],p.f1[2]); + fprintf(stderr,"f3x3: %3d %3d %3d\n",p.f2[0],p.f2[1],p.f2[2]); + fprintf(stderr,"f3x3: %3d %3d %3d\n",p.f3[0],p.f3[1],p.f3[2]); + fprintf(stderr,"f3x3: *%d/%d+%d\n",p.mul,p.div,p.add); + } + viewer_start_op(ida,&desc_3x3,&p); +} + +void +undo_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + Widget msgbox; + int resize; + + resize = (ida->undo.i.width != ida->img.i.width || + ida->undo.i.height != ida->img.i.height); + if (-1 == viewer_undo(ida)) { + msgbox = XmCreateInformationDialog(app_shell,"noundobox",NULL,0); + XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_HELP_BUTTON)); + XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_CANCEL_BUTTON)); + XtAddCallback(msgbox,XmNokCallback,destroy_cb,msgbox); + XtManageChild(msgbox); + } else { + if (resize) + resize_shell(); + } +} + +/* ---------------------------------------------------------------------- */ + +struct ida_prompt { + Widget shell; + Widget box; + Widget scale; + Widget text; + int apply; + int value; + int decimal; + int factor; /* 10^decimal */ + void (*notify)(int value, int preview); +}; + +static void +prompt_setvalue(struct ida_prompt *me, int value, int scale, int text) +{ + char str[32]; + int min,max; + + if (me->value == value) + return; + XtVaGetValues(me->scale,XmNminimum,&min,XmNmaximum,&max,NULL); + if (value < min || value > max) + return; + + me->value = value; + if (scale) + XmScaleSetValue(me->scale,value); + if (text) { + if (me->decimal) { + sprintf(str,"%*.*f",me->decimal+2,me->decimal, + (float)value/me->factor); + } else { + sprintf(str,"%d",value); + } + XmTextSetString(me->text,str); + } + if (me->notify) + me->notify(value,1); +} + +static void +prompt_scale_cb(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_prompt *me = client_data; + XmScaleCallbackStruct *cd = calldata; + + prompt_setvalue(me,cd->value,0,1); +} + +static void +prompt_text_cb(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_prompt *me = client_data; + float fvalue; + int value; + + if (me->decimal) { + fvalue = atof(XmTextGetString(me->text)); + fvalue += 0.5/me->factor; + value = (int)(fvalue * me->factor); + } else { + value = atoi(XmTextGetString(me->text)); + } + prompt_setvalue(me,value,1,0); +} + +static void +prompt_box_cb(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_prompt *me = client_data; + XmSelectionBoxCallbackStruct *cd = calldata; + + if (XmCR_OK == cd->reason) + me->apply = 1; + XtDestroyWidget(me->shell); +} + +static void +prompt_shell_cb(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_prompt *me = client_data; + + if (me->apply) + me->notify(me->value,0); + else + viewer_cancel_preview(ida); + free(me); +} + +static void +prompt_init(char *name, int decimal, int value, + void (*notify)(int value, int preview)) +{ + struct ida_prompt *me; + + me = malloc(sizeof(*me)); + memset(me,0,sizeof(*me)); + if (decimal) { + int i; + me->decimal = decimal; + me->factor = 1; + for (i = 0; i < decimal; i++) + me->factor *= 10; + } + me->notify = notify; + + me->box = XmCreatePromptDialog(app_shell,name,NULL,0); + me->shell = XtParent(me->box); + me->text = XmSelectionBoxGetChild(me->box,XmDIALOG_TEXT); + XmdRegisterEditres(XtParent(me->box)); + XtUnmanageChild(XmSelectionBoxGetChild(me->box,XmDIALOG_HELP_BUTTON)); + me->scale = XtVaCreateManagedWidget("scale",xmScaleWidgetClass, + me->box,NULL); + + XtAddCallback(me->scale,XmNdragCallback,prompt_scale_cb,me); + XtAddCallback(me->scale,XmNvalueChangedCallback,prompt_scale_cb,me); + XtAddCallback(me->text,XmNvalueChangedCallback,prompt_text_cb,me); + XtAddCallback(me->box,XmNokCallback,prompt_box_cb,me); + XtAddCallback(me->box,XmNcancelCallback,prompt_box_cb,me); + XtAddCallback(me->shell,XmNdestroyCallback,prompt_shell_cb,me); + + XtManageChild(me->box); + prompt_setvalue(me,value,1,1); +} + +/* ---------------------------------------------------------------------- */ + +static void +gamma_notify(int value, int preview) +{ + struct op_map_parm param; + float gamma = (float)value/100; + + param.red = op_map_nothing; + param.red.gamma = gamma; + param.green = param.red; + param.blue = param.red; + if (preview) { + viewer_start_preview(ida,&desc_map,¶m); + } else { + gamma_val = value; + viewer_start_op(ida,&desc_map,¶m); + } +} + +void +gamma_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + prompt_init("gamma",2,gamma_val,gamma_notify); +} + +static void +bright_notify(int value, int preview) +{ + struct op_map_parm param; + + param.red = op_map_nothing; + param.red.bottom += value; + param.red.top += value; + param.green = param.red; + param.blue = param.red; + if (preview) { + viewer_start_preview(ida,&desc_map,¶m); + } else { + bright_val = value; + viewer_start_op(ida,&desc_map,¶m); + } +} + +void +bright_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + prompt_init("bright",0,bright_val,bright_notify); +} + +static void +contrast_notify(int value, int preview) +{ + struct op_map_parm param; + + param.red = op_map_nothing; + param.red.bottom -= value; + param.red.top += value; + param.green = param.red; + param.blue = param.red; + if (preview) { + viewer_start_preview(ida,&desc_map,¶m); + } else { + contrast_val = value; + viewer_start_op(ida,&desc_map,¶m); + } +} + +void +contrast_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + prompt_init("contrast",0,contrast_val,contrast_notify); +} + +static void +rotate_notify(int value, int preview) +{ + struct op_rotate_parm parm; + + parm.angle = value; + if (preview) { + viewer_start_preview(ida,&desc_rotate,&parm); + } else { + rotate_val = value; + viewer_start_op(ida,&desc_rotate,&parm); + } +} + +void +rotate_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + prompt_init("rotate",0,rotate_val,rotate_notify); +} + +static void +sharpe_notify(int value, int preview) +{ + struct op_sharpe_parm parm; + + parm.factor = value; + if (preview) { + viewer_start_preview(ida,&desc_sharpe,&parm); + } else { + sharpe_val = value; + viewer_start_op(ida,&desc_sharpe,&parm); + } +} + +void +sharpe_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + prompt_init("sharpe",0,sharpe_val,sharpe_notify); +} + +void +color_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + color_init(&ida->img); +} + +/* ---------------------------------------------------------------------- */ + +struct ida_resize { + Widget dlg,tx,ty,tr,lock,size,res,label; + int yupdate,xupdate,rupdate; + int apply; +}; + +static void +resize_phys_size(struct ida_resize *h) +{ + char buf[128]; + XmString str; + int dpi; + float x,y; + + dpi = atoi(XmTextGetString(h->tr)); + if (dpi) { + x = (float)atoi(XmTextGetString(h->tx)) / dpi; + y = (float)atoi(XmTextGetString(h->ty)) / dpi; + sprintf(buf,"%.2f x %.2f inch\n%.2f x %.2f cm", + x,y, x*2.54, y*2.54); + } else { + strcpy(buf,"unknown"); + } + str = XmStringGenerate(buf, NULL, XmMULTIBYTE_TEXT,NULL); + XtVaSetValues(h->label,XmNlabelString,str,NULL); +} + +static void +resize_sync_cb(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_resize *h = client_data; + char buf[32]; + int i,lock,res; + + lock = XmToggleButtonGetState(h->lock); + res = XmToggleButtonGetState(h->res); + + /* update text fields */ + if (h->tx == widget) { + if (h->xupdate) { + h->xupdate--; + return; + } + i = atoi(XmTextGetString(h->tx)); + if (lock) { + sprintf(buf,"%d",i * ida->img.i.height / ida->img.i.width); + h->yupdate++; + XmTextSetString(h->ty,buf); + if (res) { + sprintf(buf,"%d", ida->img.i.dpi * i / ida->img.i.width); + h->rupdate++; + XmTextSetString(h->tr,buf); + } + } else { + if (res) { + h->rupdate++; + XmTextSetString(h->tr,"0"); + } + } + resize_phys_size(h); + } + if (h->ty == widget) { + if (h->yupdate) { + h->yupdate--; + return; + } + i = atoi(XmTextGetString(h->ty)); + if (lock) { + sprintf(buf,"%d",i * ida->img.i.width / ida->img.i.height); + h->xupdate++; + XmTextSetString(h->tx,buf); + if (res) { + sprintf(buf,"%d", ida->img.i.dpi * i / ida->img.i.height); + h->rupdate++; + XmTextSetString(h->tr,buf); + } + } else { + if (res) { + h->rupdate++; + XmTextSetString(h->tr,"0"); + } + } + resize_phys_size(h); + } + if (h->tr == widget) { + if (h->rupdate) { + h->rupdate--; + return; + } + i = atoi(XmTextGetString(h->tr)); + sprintf(buf,"%d", ida->img.i.width * i / ida->img.i.dpi); + h->xupdate++; + XmTextSetString(h->tx,buf); + sprintf(buf,"%d", ida->img.i.height * i / ida->img.i.dpi); + h->yupdate++; + XmTextSetString(h->ty,buf); + resize_phys_size(h); + } + + /* radio buttons pressed */ + if (h->size == widget && XmToggleButtonGetState(h->size)) { + XmToggleButtonSetState(h->res,0,False); + sprintf(buf,"%d", ida->img.i.dpi); + h->rupdate++; + XmTextSetString(h->tr,buf); + XtVaSetValues(h->tr,XmNsensitive,False,NULL); + resize_phys_size(h); + } + if (h->res == widget && XmToggleButtonGetState(h->res)) { + XmToggleButtonSetState(h->size,0,False); + XtVaSetValues(h->tr,XmNsensitive,True,NULL); + } +} + +static void +resize_button_cb(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_resize *h = client_data; + XmSelectionBoxCallbackStruct *cb = calldata; + + if (cb->reason == XmCR_OK) + h->apply = 1; + XtDestroyWidget(XtParent(h->dlg)); +} + +static void +resize_destroy(Widget widget, XtPointer client_data, XtPointer calldata) +{ + struct ida_resize *h = client_data; + struct op_resize_parm param; + + if (!h->apply) + return; + param.width = atoi(XmTextGetString(h->tx)); + param.height = atoi(XmTextGetString(h->ty)); + param.dpi = atoi(XmTextGetString(h->tr)); + if (0 == param.width || + 0 == param.height) { + fprintf(stderr,"resize: invalid argument\n"); + return; + } + + viewer_start_op(ida,&desc_resize,¶m); + resize_shell(); + free(h); +} + +static void +resize_ac(Widget widget, XEvent *event, String *params, Cardinal *num) +{ + Widget rc,rc2; + char buf[32]; + struct ida_resize *h; + + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + + h->dlg = XmCreatePromptDialog(app_shell,"resize",NULL,0); + XmdRegisterEditres(XtParent(h->dlg)); + XtUnmanageChild(XmSelectionBoxGetChild(h->dlg,XmDIALOG_SELECTION_LABEL)); + XtUnmanageChild(XmSelectionBoxGetChild(h->dlg,XmDIALOG_HELP_BUTTON)); + XtUnmanageChild(XmSelectionBoxGetChild(h->dlg,XmDIALOG_TEXT)); + rc = XtVaCreateManagedWidget("rc", xmRowColumnWidgetClass,h->dlg, NULL); + XtVaCreateManagedWidget("lx", xmLabelWidgetClass,rc, NULL); + h->tx = XtVaCreateManagedWidget("tx", xmTextWidgetClass,rc, NULL); + XtVaCreateManagedWidget("ly", xmLabelWidgetClass,rc, NULL); + h->ty = XtVaCreateManagedWidget("ty", xmTextWidgetClass,rc, NULL); + XtVaCreateManagedWidget("lr", xmLabelWidgetClass,rc, NULL); + h->tr = XtVaCreateManagedWidget("tr", xmTextWidgetClass,rc, NULL); + h->lock = XtVaCreateManagedWidget("lock", xmToggleButtonWidgetClass, + rc, NULL); + rc2 = XtVaCreateManagedWidget("rc", xmRowColumnWidgetClass,rc, NULL); + h->size = XtVaCreateManagedWidget("size", xmToggleButtonWidgetClass, + rc2, NULL); + h->res = XtVaCreateManagedWidget("res", xmToggleButtonWidgetClass, + rc2, NULL); + XtVaCreateManagedWidget("phys", xmLabelWidgetClass,rc,NULL); + h->label = XtVaCreateManagedWidget("label", xmLabelWidgetClass, + rc, NULL); + + sprintf(buf,"%d",ida->img.i.width); + XmTextSetString(h->tx,buf); + sprintf(buf,"%d",ida->img.i.height); + XmTextSetString(h->ty,buf); + sprintf(buf,"%d",ida->img.i.dpi); + XmTextSetString(h->tr,buf); + XtVaSetValues(h->tr,XmNsensitive,False,NULL); + XmToggleButtonSetState(h->lock,1,False); + XmToggleButtonSetState(h->size,1,False); + XmToggleButtonSetState(h->res,0,False); + if (!ida->img.i.dpi) { + XtVaSetValues(h->size,XmNsensitive,False,NULL); + XtVaSetValues(h->res, XmNsensitive,False,NULL); + } + resize_phys_size(h); + + XtAddCallback(XtParent(h->dlg),XmNdestroyCallback,resize_destroy,h); + XtAddCallback(h->dlg, XmNokCallback, resize_button_cb, h); + XtAddCallback(h->dlg, XmNcancelCallback, resize_button_cb, h); + XtAddCallback(h->tx, XmNvalueChangedCallback, resize_sync_cb, h); + XtAddCallback(h->ty, XmNvalueChangedCallback, resize_sync_cb, h); + XtAddCallback(h->tr, XmNvalueChangedCallback, resize_sync_cb, h); + XtAddCallback(h->size,XmNvalueChangedCallback, resize_sync_cb, h); + XtAddCallback(h->res, XmNvalueChangedCallback, resize_sync_cb, h); + XtManageChild(h->dlg); +} + +/* ---------------------------------------------------------------------- */ + +struct stderr_handler { + Widget box; + XmString str; + int pipe,err; + XtInputId id; +}; + +static void +stderr_input(XtPointer clientdata, int *src, XtInputId *id) +{ + struct stderr_handler *h = clientdata; + XmString item; + Widget label; + char buf[1024]; + int rc; + + rc = read(h->pipe,buf,sizeof(buf)-1); + if (rc <= 0) { + /* Oops */ + XtRemoveInput(h->id); + close(h->pipe); + XtDestroyWidget(h->box); + free(h); + } + buf[rc] = 0; + write(h->err,buf,rc); + item = XmStringGenerate(buf, NULL, XmMULTIBYTE_TEXT,NULL); + h->str = XmStringConcatAndFree(h->str,item); + label = XmMessageBoxGetChild(h->box,XmDIALOG_MESSAGE_LABEL); + XtVaSetValues(label,XmNlabelString,h->str,NULL); + XtManageChild(h->box); +} + +static void +stderr_ok_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct stderr_handler *h = clientdata; + + XmStringFree(h->str); + h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL); + XtUnmanageChild(h->box); +} + +static void +stderr_close_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct stderr_handler *h = clientdata; + + XmStringFree(h->str); + h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL); +} + +static void +stderr_init(void) +{ + struct stderr_handler *h; + int p[2]; + + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL); + h->box = XmCreateErrorDialog(app_shell,"errbox",NULL,0); + XtUnmanageChild(XmMessageBoxGetChild(h->box,XmDIALOG_HELP_BUTTON)); + XtUnmanageChild(XmMessageBoxGetChild(h->box,XmDIALOG_CANCEL_BUTTON)); + XtAddCallback(h->box,XmNokCallback,stderr_ok_cb,h); + XtAddCallback(XtParent(h->box),XmNpopdownCallback,stderr_close_cb,h); + XSync(XtDisplay(app_shell),False); + if (!debug) { + pipe(p); + h->err = dup(2); + dup2(p[1],2); + close(p[1]); + h->pipe = p[0]; + h->id = XtAppAddInput(app_context,h->pipe,(XtPointer)XtInputReadMask, + stderr_input,h); + } +} + +/* ---------------------------------------------------------------------- */ + +static void +create_mainwindow(void) +{ + Widget img; + + XmdRegisterEditres(app_shell); + view = XmCreateScrolledWindow(app_shell,"view",NULL,0); + XtManageChild(view); + img = XtVaCreateManagedWidget("image", xmDrawingAreaWidgetClass,view,NULL); + XtAddCallback(img,XmNdestinationCallback,selection_dest,NULL); + XtAddCallback(img,XmNconvertCallback,selection_convert,NULL); + dnd_add(img); + ida = viewer_init(img); + XtInstallAllAccelerators(img,app_shell); +} + +static void +usage(void) +{ + fprintf(stderr, + "ida " VERSION " - image viewer & editor\n" + "usage: ida [ options ] [ files ]\n" + "options:\n" + " -h, -help this text\n" + " -pcd n pick PhotoCD size (n = 1 .. 5, default 3)\n" + " -d, -debug enable debug messages\n"); + exit(0); +} + +int +main(int argc, char *argv[]) +{ + int i, files, zero = 0; + struct stat st; + Pixel background; + + setlocale(LC_ALL,""); + if (0 == strcasecmp("utf-8", nl_langinfo(CODESET))) { + /* ### FIXME ### + * for not-yet known reasons ida crashes somewhere deep in + * the Motif libraries when running in utf-8 locale ... */ + setenv("LC_ALL", "POSIX", 1); + setlocale(LC_ALL,""); + } + + binary = argv[0]; + ida_init_config(); + ida_read_config(); + + XtSetLanguageProc(NULL,NULL,NULL); + app_shell = XtAppInitialize(&app_context, "Ida", + opt_desc, opt_count, + &argc, argv, + fallback_ressources, + NULL, 0); + dpy = XtDisplay(app_shell); + XtGetApplicationResources(app_shell,&args, + args_desc,args_count, + NULL,0); + pcd_res = GET_PHOTOCD_RES(); + sane_res = GET_SANE_RES(); + if (args.help) + usage(); + if (args.debug) { + debug=1; + xdnd_debug = 1; + XSynchronize(dpy,1); + } + + XtAppAddActions(app_context, actionTable, + sizeof(actionTable) / sizeof(XtActionsRec)); + if (0) { + XtAddCallback(XmGetXmDisplay(dpy),XmNnoFontCallback, + display_cb,NULL); + XtAddCallback(XmGetXmDisplay(dpy),XmNnoRenditionCallback, + display_cb,NULL); + } + XtVaGetValues(app_shell, XtNbackground,&background, NULL); + x11_color_init(app_shell,&gray); + x11_icons_init(dpy, background /* x11_gray */); + stderr_init(); + ipc_init(); + + wm_delete_window = XInternAtom(dpy,"WM_DELETE_WINDOW",False); + create_mainwindow(); + create_control(); + XtRealizeWidget(app_shell); + ptr_register(ida->widget); + ptr_register(control_shell); + + /* handle cmd line args */ + if (2 == argc && 0 == strcmp(argv[1],"-")) { + load_stdin(); + } else if (argc > 1) { + for (files = 0, i = 1; i < argc; i++) { + if (curl_is_url(argv[i])) { + list_append(argv[i]); + files++; + continue; + } + if (-1 == stat(argv[i],&st)) { + if (debug) + fprintf(stderr,"stat %s: %s\n",argv[i],strerror(errno)); + continue; + } + switch (st.st_mode & S_IFMT) { + case S_IFDIR: + browser_window(argv[i]); + break; + case S_IFREG: + list_append(argv[i]); + files++; + break; + } + } + if (files) { + list_update(); + next_ac(ida->widget,NULL,NULL,&zero); + } + } + + if (NULL == ida->file) + load_logo(); + + XtAppMainLoop(app_context); + return 0; /* keep compiler happy */ +} @@ -0,0 +1,26 @@ +struct ARGS { + Boolean debug; + Boolean help; + Boolean testload; +}; + +extern struct ARGS args; +extern unsigned int pcd_res; + +extern Widget app_shell; +extern XtAppContext app_context; +extern Display *dpy; +extern struct ida_viewer *ida; + +void action_cb(Widget widget, XtPointer clientdata, XtPointer call_data); +void destroy_cb(Widget widget, XtPointer clientdata, XtPointer call_data); + +void ptr_register(Widget widget); +void ptr_unregister(Widget widget); +void ptr_busy(void); +void ptr_idle(void); + +void do_save_print(void); +void resize_shell(void); +char* load_tmpfile(char *base); +void new_file(char *name, int complain); @@ -0,0 +1,102 @@ +.TH ida 1 "(c) 2001,02 Gerd Knorr" +.SH NAME +ida - image viewing and editing program +.SH SYNOPSIS +.B ida [ options ] files +.SH DESCRIPTION +.B ida +is a small and fast application for viewing images. Some basic +editing functions are available too. +.P +You can specify any number of image files as arguments on the command +line. Or you can read a single image from stdin by specifying "-" as +only argument ("xwd | ida -" works nice for screenshots). +.SH OPTIONS +.B ida +understands the usual toolkit options (-geometry + friends). +Additional options are: +.TP +.B -help +print a short help text +.TP +.B -pcd n +Pick size for PhotoCD images (1 .. 5, default 3). +.TP +.B -debug +enable debug messages. Also has the side effect that error messages +are displayed on stderr only and \fInot\fP as message box. +.SH GETTING STARTED +.SS Mouse functions +With the left mouse button you can creates and edit a selection +rectangle. The middle mouse button is used to start drag'n'drop +operations. The right mouse button brings up the control window with +menus, toolbar and file list. +.br +.SS Keyboard Shortcuts +Many keyboard shortcuts used by xv are available in ida too. If you +are familiar with xv if should be easy for you to get started with +ida. +.P +All available keyboard shortcuts are also listed in the menus of the +control window. The most important ones are listed below: +.P +.nf +space next file +backspace previous file +cursor keys scrolling (hold ctrl key for big steps). +plus / minus zoom in/out +Q quit +.fi +.SS Supported image formats +.B read: +PPM, xwd, PhotoCD, xpm, xbm, bmp (uncompressed), JPEG, TIFF, PNG, GIF. +The last four are supported using the usual libraries, i.e. you need +to have them installed at compile time. +.br +.B write: +PPM, PostScript, JPEG, TIFF, PNG. +.SS Using drag'n'drop +.B ida +is a motif application and thus supports the motif drag'n'drop +protocol in both directions. The xdnd protocol is supported too, but +only in one direction (receive drops). +.P +.B ida +uses the middle mouse button to start a drag'n'drop operation (as the +motif style guide suggests). This works for the main window and the +file buttons within the file browser. +.P +.B motif applications +should have absolutely no problems to deal with ida's drag'n'drop +support. You can drop images into some netscape 4.x window -- it +simply works. Mozilla accepts motif drops too. +.P +Interoperation with +.B gnome / gtk +is good. I can drag files from ida to eeyes and visa versa without +problems. File drops from gmc into ida work just fine too. +.P +Interoperation with +.B KDE +is bad. cut+paste works most of the time, drag'n'drop often doesn't. +The X11 selection handling of the Qt toolkit has a few design bugs and +sucks. Basically the troll guys didn't understand what the TARGETS +target is good for and violate the ICCCM specs by ignoring it. +.SH AUTHOR +Gerd Knorr <kraxel@bytesex.org> +.SH COPYRIGHT +Copyright (C) 2002 Gerd Knorr <kraxel@bytesex.org> +.P +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. +.P +This program is distributed in the hope that 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. +.P +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., 675 Mass Ave, Cambridge, MA 02139, USA. diff --git a/idaconfig.c b/idaconfig.c new file mode 100644 index 0000000..56b97d8 --- /dev/null +++ b/idaconfig.c @@ -0,0 +1,51 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/stat.h> + +#include "idaconfig.h" + +char *ida_lists; +static char *ida_config; + +void ida_init_config(void) +{ + char *home; + char *conf; + struct stat st; + + home = getenv("HOME"); + if (NULL == home) + return; + + conf = malloc(strlen(home) + 16); + ida_lists = malloc(strlen(home) + 16); + ida_config = malloc(strlen(home) + 16); + sprintf(conf, "%s/.ida", home); + sprintf(ida_lists, "%s/.ida/lists", home); + sprintf(ida_config,"%s/.ida/config", home); + + if (-1 == stat(ida_lists,&st)) { + if (-1 == stat(conf,&st)) + mkdir(conf,0777); + mkdir(ida_lists,0777); + } + free(conf); +} + +void ida_read_config(void) +{ + int rc; + + rc = cfg_parse_file("config", ida_config); + if (-1 == rc) { + /* set some defaults */ + cfg_set_str(O_BOOKMARKS, "Home", getenv("HOME")); + } +} + +void ida_write_config(void) +{ + cfg_write_file("config", ida_config); +} diff --git a/idaconfig.h b/idaconfig.h new file mode 100644 index 0000000..8d616ef --- /dev/null +++ b/idaconfig.h @@ -0,0 +1,24 @@ +#include "parseconfig.h" + +#define O_OPTIONS "config", "options" +#define O_BOOKMARKS "config", "bookmarks" + +#define O_AUTOZOOM O_OPTIONS, "autozoom" +#define O_PHOTOCD_RES O_OPTIONS, "photocd_res" +#define O_SANE_RES O_OPTIONS, "sane_res" +#define O_ICON_SMALL O_OPTIONS, "icon_small" +#define O_ICON_LARGE O_OPTIONS, "icon_large" + +#define GET_AUTOZOOM() cfg_get_bool(O_AUTOZOOM, 1) +#define GET_PHOTOCD_RES() cfg_get_int(O_PHOTOCD_RES, 3) +#define GET_SANE_RES() cfg_get_int(O_SANE_RES, 300) +#define GET_ICON_SMALL() cfg_get_int(O_ICON_SMALL, 32) +#define GET_ICON_LARGE() cfg_get_int(O_ICON_LARGE, 96) + +/* -------------------------------------------------------------------------- */ + +char *ida_lists; + +void ida_init_config(void); +void ida_read_config(void); +void ida_write_config(void); diff --git a/jpeg/README b/jpeg/README new file mode 100644 index 0000000..76e7464 --- /dev/null +++ b/jpeg/README @@ -0,0 +1,2 @@ +some source files copyed over from +The Independent JPEG Group's JPEG software diff --git a/jpeg/jinclude.h b/jpeg/jinclude.h new file mode 100644 index 0000000..0a4f151 --- /dev/null +++ b/jpeg/jinclude.h @@ -0,0 +1,91 @@ +/* + * jinclude.h + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file exists to provide a single place to fix any problems with + * including the wrong system include files. (Common problems are taken + * care of by the standard jconfig symbols, but on really weird systems + * you may have to edit this file.) + * + * NOTE: this file is NOT intended to be included by applications using the + * JPEG library. Most applications need only include jpeglib.h. + */ + + +/* Include auto-config file to find out which system include files we need. */ + +#include "jconfig.h" /* auto configuration options */ +#define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */ + +/* + * We need the NULL macro and size_t typedef. + * On an ANSI-conforming system it is sufficient to include <stddef.h>. + * Otherwise, we get them from <stdlib.h> or <stdio.h>; we may have to + * pull in <sys/types.h> as well. + * Note that the core JPEG library does not require <stdio.h>; + * only the default error handler and data source/destination modules do. + * But we must pull it in because of the references to FILE in jpeglib.h. + * You can remove those references if you want to compile without <stdio.h>. + */ + +#ifdef HAVE_STDDEF_H +#include <stddef.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef NEED_SYS_TYPES_H +#include <sys/types.h> +#endif + +#include <stdio.h> + +/* + * We need memory copying and zeroing functions, plus strncpy(). + * ANSI and System V implementations declare these in <string.h>. + * BSD doesn't have the mem() functions, but it does have bcopy()/bzero(). + * Some systems may declare memset and memcpy in <memory.h>. + * + * NOTE: we assume the size parameters to these functions are of type size_t. + * Change the casts in these macros if not! + */ + +#ifdef NEED_BSD_STRINGS + +#include <strings.h> +#define MEMZERO(target,size) bzero((void *)(target), (size_t)(size)) +#define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (size_t)(size)) + +#else /* not BSD, assume ANSI/SysV string lib */ + +#include <string.h> +#define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size)) +#define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size)) + +#endif + +/* + * In ANSI C, and indeed any rational implementation, size_t is also the + * type returned by sizeof(). However, it seems there are some irrational + * implementations out there, in which sizeof() returns an int even though + * size_t is defined as long or unsigned long. To ensure consistent results + * we always use this SIZEOF() macro in place of using sizeof() directly. + */ + +#define SIZEOF(object) ((size_t) sizeof(object)) + +/* + * The modules that use fread() and fwrite() always invoke them through + * these macros. On some systems you may need to twiddle the argument casts. + * CAUTION: argument order is different from underlying functions! + */ + +#define JFREAD(file,buf,sizeofbuf) \ + ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) +#define JFWRITE(file,buf,sizeofbuf) \ + ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) diff --git a/jpeg/jpegint.h b/jpeg/jpegint.h new file mode 100644 index 0000000..95b00d4 --- /dev/null +++ b/jpeg/jpegint.h @@ -0,0 +1,392 @@ +/* + * jpegint.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides common declarations for the various JPEG modules. + * These declarations are considered internal to the JPEG library; most + * applications using the library shouldn't need to include this file. + */ + + +/* Declarations for both compression & decompression */ + +typedef enum { /* Operating modes for buffer controllers */ + JBUF_PASS_THRU, /* Plain stripwise operation */ + /* Remaining modes require a full-image buffer to have been created */ + JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ + JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ + JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ +} J_BUF_MODE; + +/* Values of global_state field (jdapi.c has some dependencies on ordering!) */ +#define CSTATE_START 100 /* after create_compress */ +#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */ +#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */ +#define CSTATE_WRCOEFS 103 /* jpeg_write_coefficients done */ +#define DSTATE_START 200 /* after create_decompress */ +#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */ +#define DSTATE_READY 202 /* found SOS, ready for start_decompress */ +#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/ +#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */ +#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */ +#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */ +#define DSTATE_BUFIMAGE 207 /* expecting jpeg_start_output */ +#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in jpeg_finish_output */ +#define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */ +#define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ + + +/* Declarations for compression modules */ + +/* Master control module */ +struct jpeg_comp_master { + JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo)); + JMETHOD(void, pass_startup, (j_compress_ptr cinfo)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean call_pass_startup; /* True if pass_startup must be called */ + boolean is_last_pass; /* True during last pass */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_c_main_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail)); +}; + +/* Compression preprocessing (downsampling input buffer control) */ +struct jpeg_c_prep_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, pre_process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, + JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_c_coef_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf)); +}; + +/* Colorspace conversion */ +struct jpeg_color_converter { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, color_convert, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +}; + +/* Downsampling */ +struct jpeg_downsampler { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, downsample, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, + JDIMENSION out_row_group_index)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Forward DCT (also controls coefficient quantization) */ +struct jpeg_forward_dct { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + /* perhaps this should be an array??? */ + JMETHOD(void, forward_DCT, (j_compress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks)); +}; + +/* Entropy encoding */ +struct jpeg_entropy_encoder { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); +}; + +/* Marker writing */ +struct jpeg_marker_writer { + JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_frame_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_scan_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo)); + JMETHOD(void, write_tables_only, (j_compress_ptr cinfo)); + /* These routines are exported to allow insertion of extra markers */ + /* Probably only COM and APPn markers should be written this way */ + JMETHOD(void, write_marker_header, (j_compress_ptr cinfo, int marker, + unsigned int datalen)); + JMETHOD(void, write_marker_byte, (j_compress_ptr cinfo, int val)); +}; + + +/* Declarations for decompression modules */ + +/* Master control module */ +struct jpeg_decomp_master { + JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ +}; + +/* Input control module */ +struct jpeg_input_controller { + JMETHOD(int, consume_input, (j_decompress_ptr cinfo)); + JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo)); + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean has_multiple_scans; /* True if file has multiple scans */ + boolean eoi_reached; /* True when EOI has been consumed */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_d_main_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_d_coef_controller { + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); + JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, + JSAMPIMAGE output_buf)); + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + jvirt_barray_ptr *coef_arrays; +}; + +/* Decompression postprocessing (color quantization buffer control) */ +struct jpeg_d_post_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, post_process_data, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Marker reading & parsing */ +struct jpeg_marker_reader { + JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo)); + /* Read markers until SOS or EOI. + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + JMETHOD(int, read_markers, (j_decompress_ptr cinfo)); + /* Read a restart marker --- exported for use by entropy decoder only */ + jpeg_marker_parser_method read_restart_marker; + + /* State of marker reader --- nominally internal, but applications + * supplying COM or APPn handlers might like to know the state. + */ + boolean saw_SOI; /* found SOI? */ + boolean saw_SOF; /* found SOF? */ + int next_restart_num; /* next restart number expected (0-7) */ + unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ +}; + +/* Entropy decoding */ +struct jpeg_entropy_decoder { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); + + /* This is here to share code between baseline and progressive decoders; */ + /* other modules probably should not use it */ + boolean insufficient_data; /* set TRUE after emitting warning */ +}; + +/* Inverse DCT (also performs dequantization) */ +typedef JMETHOD(void, inverse_DCT_method_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col)); + +struct jpeg_inverse_dct { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + /* It is useful to allow each component to have a separate IDCT method. */ + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; +}; + +/* Upsampling (note that upsampler must also call color converter) */ +struct jpeg_upsampler { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, upsample, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Colorspace conversion */ +struct jpeg_color_deconverter { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, color_convert, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +}; + +/* Color quantization or color precision reduction */ +struct jpeg_color_quantizer { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan)); + JMETHOD(void, color_quantize, (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, + int num_rows)); + JMETHOD(void, finish_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, new_color_map, (j_decompress_ptr cinfo)); +}; + + +/* Miscellaneous useful macros */ + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + + +/* We assume that right shift corresponds to signed division by 2 with + * rounding towards minus infinity. This is correct for typical "arithmetic + * shift" instructions that shift in copies of the sign bit. But some + * C compilers implement >> with an unsigned shift. For these machines you + * must define RIGHT_SHIFT_IS_UNSIGNED. + * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + * It is only applied with constant shift counts. SHIFT_TEMPS must be + * included in the variables of any routine using RIGHT_SHIFT. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define SHIFT_TEMPS INT32 shift_temp; +#define RIGHT_SHIFT(x,shft) \ + ((shift_temp = (x)) < 0 ? \ + (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + (shift_temp >> (shft))) +#else +#define SHIFT_TEMPS +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jinit_compress_master jICompress +#define jinit_c_master_control jICMaster +#define jinit_c_main_controller jICMainC +#define jinit_c_prep_controller jICPrepC +#define jinit_c_coef_controller jICCoefC +#define jinit_color_converter jICColor +#define jinit_downsampler jIDownsampler +#define jinit_forward_dct jIFDCT +#define jinit_huff_encoder jIHEncoder +#define jinit_phuff_encoder jIPHEncoder +#define jinit_marker_writer jIMWriter +#define jinit_master_decompress jIDMaster +#define jinit_d_main_controller jIDMainC +#define jinit_d_coef_controller jIDCoefC +#define jinit_d_post_controller jIDPostC +#define jinit_input_controller jIInCtlr +#define jinit_marker_reader jIMReader +#define jinit_huff_decoder jIHDecoder +#define jinit_phuff_decoder jIPHDecoder +#define jinit_inverse_dct jIIDCT +#define jinit_upsampler jIUpsampler +#define jinit_color_deconverter jIDColor +#define jinit_1pass_quantizer jI1Quant +#define jinit_2pass_quantizer jI2Quant +#define jinit_merged_upsampler jIMUpsampler +#define jinit_memory_mgr jIMemMgr +#define jdiv_round_up jDivRound +#define jround_up jRound +#define jcopy_sample_rows jCopySamples +#define jcopy_block_row jCopyBlocks +#define jzero_far jZeroFar +#define jpeg_zigzag_order jZIGTable +#define jpeg_natural_order jZAGTable +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Compression module initialization routines */ +EXTERN(void) jinit_compress_master JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_c_master_control JPP((j_compress_ptr cinfo, + boolean transcode_only)); +EXTERN(void) jinit_c_main_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_prep_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_color_converter JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_downsampler JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_huff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_phuff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_marker_writer JPP((j_compress_ptr cinfo)); +/* Decompression module initialization routines */ +EXTERN(void) jinit_master_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_d_main_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_post_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_input_controller JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_marker_reader JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_huff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_upsampler JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_color_deconverter JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); +/* Memory manager initialization */ +EXTERN(void) jinit_memory_mgr JPP((j_common_ptr cinfo)); + +/* Utility routines in jutils.c */ +EXTERN(long) jdiv_round_up JPP((long a, long b)); +EXTERN(long) jround_up JPP((long a, long b)); +EXTERN(void) jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols)); +EXTERN(void) jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks)); +EXTERN(void) jzero_far JPP((void FAR * target, size_t bytestozero)); +/* Constant tables in jutils.c */ +#if 0 /* This table is not actually needed in v6a */ +extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ +#endif +extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ + +/* Suppress undefined-structure complaints if necessary. */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef AM_MEMORY_MANAGER /* only jmemmgr.c defines these */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +#endif +#endif /* INCOMPLETE_TYPES_BROKEN */ diff --git a/jpeg/jpeglib.h b/jpeg/jpeglib.h new file mode 100644 index 0000000..d1be8dd --- /dev/null +++ b/jpeg/jpeglib.h @@ -0,0 +1,1096 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". + */ + +#define JPEG_LIB_VERSION 62 /* Version 6b */ + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples. Always DCTSIZE for compression. + * For decompression this is the size of the output from one DCT block, + * reflecting any scaling we choose to apply during the IDCT step. + * Values of 1,2,4,8 are likely to be supported. Note that different + * components may receive different IDCT scalings. + */ + int DCT_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface), thus + * downsampled_width = ceil(image_width * Hi/Hmax) + * and similarly for height. For decompression, IDCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.doc concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#endif /* JPEGLIB_H */ diff --git a/jpeg/transupp.c b/jpeg/transupp.c new file mode 100644 index 0000000..e5ec564 --- /dev/null +++ b/jpeg/transupp.c @@ -0,0 +1,928 @@ +/* + * transupp.c + * + * Copyright (C) 1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains image transformation routines and other utility code + * used by the jpegtran sample application. These are NOT part of the core + * JPEG library. But we keep these routines separate from jpegtran.c to + * ease the task of maintaining jpegtran-like programs that have other user + * interfaces. + */ + +/* Although this file really shouldn't have access to the library internals, + * it's helpful to let it call jround_up() and jcopy_block_row(). + */ +#define JPEG_INTERNALS + +#include "jinclude.h" +#include "jpeglib.h" +#include "transupp.h" /* My own external interface */ + + +#if TRANSFORMS_SUPPORTED + +/* + * Lossless image transformation routines. These routines work on DCT + * coefficient arrays and thus do not require any lossy decompression + * or recompression of the image. + * Thanks to Guido Vollbeding for the initial design and code of this feature. + * + * Horizontal flipping is done in-place, using a single top-to-bottom + * pass through the virtual source array. It will thus be much the + * fastest option for images larger than main memory. + * + * The other routines require a set of destination virtual arrays, so they + * need twice as much memory as jpegtran normally does. The destination + * arrays are always written in normal scan order (top to bottom) because + * the virtual array manager expects this. The source arrays will be scanned + * in the corresponding order, which means multiple passes through the source + * arrays for most of the transforms. That could result in much thrashing + * if the image is larger than main memory. + * + * Some notes about the operating environment of the individual transform + * routines: + * 1. Both the source and destination virtual arrays are allocated from the + * source JPEG object, and therefore should be manipulated by calling the + * source's memory manager. + * 2. The destination's component count should be used. It may be smaller + * than the source's when forcing to grayscale. + * 3. Likewise the destination's sampling factors should be used. When + * forcing to grayscale the destination's sampling factors will be all 1, + * and we may as well take that as the effective iMCU size. + * 4. When "trim" is in effect, the destination's dimensions will be the + * trimmed values but the source's will be untrimmed. + * 5. All the routines assume that the source and destination buffers are + * padded out to a full iMCU boundary. This is true, although for the + * source buffer it is an undocumented property of jdcoefct.c. + * Notes 2,3,4 boil down to this: generally we should use the destination's + * dimensions and ignore the source's. + */ + + +LOCAL(void) +do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays) +/* Horizontal flip; done in-place, so no separate dest array is required */ +{ + JDIMENSION MCU_cols, comp_width, blk_x, blk_y; + int ci, k, offset_y; + JBLOCKARRAY buffer; + JCOEFPTR ptr1, ptr2; + JCOEF temp1, temp2; + jpeg_component_info *compptr; + + /* Horizontal mirroring of DCT blocks is accomplished by swapping + * pairs of blocks in-place. Within a DCT block, we perform horizontal + * mirroring by changing the signs of odd-numbered columns. + * Partial iMCUs at the right edge are left untouched. + */ + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + for (blk_y = 0; blk_y < compptr->height_in_blocks; + blk_y += compptr->v_samp_factor) { + buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (blk_x = 0; blk_x * 2 < comp_width; blk_x++) { + ptr1 = buffer[offset_y][blk_x]; + ptr2 = buffer[offset_y][comp_width - blk_x - 1]; + /* this unrolled loop doesn't need to know which row it's on... */ + for (k = 0; k < DCTSIZE2; k += 2) { + temp1 = *ptr1; /* swap even column */ + temp2 = *ptr2; + *ptr1++ = temp2; + *ptr2++ = temp1; + temp1 = *ptr1; /* swap odd column with sign change */ + temp2 = *ptr2; + *ptr1++ = -temp2; + *ptr2++ = -temp1; + } + } + } + } + } +} + + +LOCAL(void) +do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Vertical flip */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* We output into a separate array because we can't touch different + * rows of the source virtual array simultaneously. Otherwise, this + * is a pretty straightforward analog of horizontal flip. + * Within a DCT block, vertical mirroring is done by changing the signs + * of odd-numbered rows. + * Partial iMCUs at the bottom edge are copied verbatim. + */ + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } else { + /* Bottom-edge blocks will be copied verbatim. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + /* copy even row */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + /* copy odd row with sign change */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Just copy row verbatim. */ + jcopy_block_row(src_buffer[offset_y], dst_buffer[offset_y], + compptr->width_in_blocks); + } + } + } + } +} + + +LOCAL(void) +do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transpose source into destination */ +{ + JDIMENSION dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Transposing pixels within a block just requires transposing the + * DCT coefficients. + * Partial iMCUs at the edges require no special treatment; we simply + * process all the available DCT blocks for every component. + */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } +} + + +LOCAL(void) +do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 90 degree rotation is equivalent to + * 1. Transposing the image; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) right edge properly. They just get transposed and + * not mirrored. + */ + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + if (dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* Edge blocks are transposed but not mirrored. */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 270 degree rotation is equivalent to + * 1. Horizontal mirroring; + * 2. Transposing the image. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) bottom edge properly. They just get transposed and + * not mirrored. + */ + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + if (dst_blk_y < comp_height) { + /* Block is within the mirrorable area. */ + src_ptr = src_buffer[offset_x] + [comp_height - dst_blk_y - offset_y - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Edge blocks are transposed but not mirrored. */ + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 180 degree rotation is equivalent to + * 1. Vertical mirroring; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (dst_blk_y < comp_height) { + /* Row is within the vertically mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } else { + /* Bottom-edge rows are only mirrored horizontally. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + /* Process the blocks that can be mirrored both ways. */ + for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE; i += 2) { + /* For even row, negate every odd column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + /* For odd row, negate every even column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = - *src_ptr++; + *dst_ptr++ = *src_ptr++; + } + } + } + /* Any remaining right-edge blocks are only mirrored vertically. */ + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Remaining rows are just mirrored horizontally. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[offset_y]; + /* Process the blocks that can be mirrored. */ + for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE2; i += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + } + /* Any remaining right-edge blocks are only copied. */ + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE2; i++) + *dst_ptr++ = *src_ptr++; + } + } + } + } + } +} + + +LOCAL(void) +do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transverse transpose is equivalent to + * 1. 180 degree rotation; + * 2. Transposition; + * or + * 1. Horizontal mirroring; + * 2. Transposition; + * 3. Horizontal mirroring. + * These steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + if (dst_blk_y < comp_height) { + src_ptr = src_buffer[offset_x] + [comp_height - dst_blk_y - offset_y - 1]; + if (dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + i++; + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Right-edge blocks are mirrored in y only */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } + } else { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + if (dst_blk_x < comp_width) { + /* Bottom-edge blocks are mirrored in x only */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* At lower right corner, just transpose, no mirroring */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } + } +} + + +/* Request any required workspace. + * + * We allocate the workspace virtual arrays from the source decompression + * object, so that all the arrays (both the original data and the workspace) + * will be taken into account while making memory management decisions. + * Hence, this routine must be called after jpeg_read_header (which reads + * the image dimensions) and before jpeg_read_coefficients (which realizes + * the source's virtual arrays). + */ + +GLOBAL(void) +jtransform_request_workspace (j_decompress_ptr srcinfo, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *coef_arrays = NULL; + jpeg_component_info *compptr; + int ci; + + if (info->force_grayscale && + srcinfo->jpeg_color_space == JCS_YCbCr && + srcinfo->num_components == 3) { + /* We'll only process the first component */ + info->num_components = 1; + } else { + /* Process all the components */ + info->num_components = srcinfo->num_components; + } + + switch (info->transform) { + case JXFORM_NONE: + case JXFORM_FLIP_H: + /* Don't need a workspace array */ + break; + case JXFORM_FLIP_V: + case JXFORM_ROT_180: + /* Need workspace arrays having same dimensions as source image. + * Note that we allocate arrays padded out to the next iMCU boundary, + * so that transform routines need not worry about missing edge blocks. + */ + coef_arrays = (jvirt_barray_ptr *) + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + for (ci = 0; ci < info->num_components; ci++) { + compptr = srcinfo->comp_info + ci; + coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) + ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) compptr->v_samp_factor); + } + break; + case JXFORM_TRANSPOSE: + case JXFORM_TRANSVERSE: + case JXFORM_ROT_90: + case JXFORM_ROT_270: + /* Need workspace arrays having transposed dimensions. + * Note that we allocate arrays padded out to the next iMCU boundary, + * so that transform routines need not worry about missing edge blocks. + */ + coef_arrays = (jvirt_barray_ptr *) + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + for (ci = 0; ci < info->num_components; ci++) { + compptr = srcinfo->comp_info + ci; + coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) + ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) compptr->h_samp_factor); + } + break; + } + info->workspace_coef_arrays = coef_arrays; +} + + +/* Transpose destination image parameters */ + +LOCAL(void) +transpose_critical_parameters (j_compress_ptr dstinfo) +{ + int tblno, i, j, ci, itemp; + jpeg_component_info *compptr; + JQUANT_TBL *qtblptr; + JDIMENSION dtemp; + UINT16 qtemp; + + /* Transpose basic image dimensions */ + dtemp = dstinfo->image_width; + dstinfo->image_width = dstinfo->image_height; + dstinfo->image_height = dtemp; + + /* Transpose sampling factors */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + itemp = compptr->h_samp_factor; + compptr->h_samp_factor = compptr->v_samp_factor; + compptr->v_samp_factor = itemp; + } + + /* Transpose quantization tables */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + qtblptr = dstinfo->quant_tbl_ptrs[tblno]; + if (qtblptr != NULL) { + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < i; j++) { + qtemp = qtblptr->quantval[i*DCTSIZE+j]; + qtblptr->quantval[i*DCTSIZE+j] = qtblptr->quantval[j*DCTSIZE+i]; + qtblptr->quantval[j*DCTSIZE+i] = qtemp; + } + } + } + } +} + + +/* Trim off any partial iMCUs on the indicated destination edge */ + +LOCAL(void) +trim_right_edge (j_compress_ptr dstinfo) +{ + int ci, max_h_samp_factor; + JDIMENSION MCU_cols; + + /* We have to compute max_h_samp_factor ourselves, + * because it hasn't been set yet in the destination + * (and we don't want to use the source's value). + */ + max_h_samp_factor = 1; + for (ci = 0; ci < dstinfo->num_components; ci++) { + int h_samp_factor = dstinfo->comp_info[ci].h_samp_factor; + max_h_samp_factor = MAX(max_h_samp_factor, h_samp_factor); + } + MCU_cols = dstinfo->image_width / (max_h_samp_factor * DCTSIZE); + if (MCU_cols > 0) /* can't trim to 0 pixels */ + dstinfo->image_width = MCU_cols * (max_h_samp_factor * DCTSIZE); +} + +LOCAL(void) +trim_bottom_edge (j_compress_ptr dstinfo) +{ + int ci, max_v_samp_factor; + JDIMENSION MCU_rows; + + /* We have to compute max_v_samp_factor ourselves, + * because it hasn't been set yet in the destination + * (and we don't want to use the source's value). + */ + max_v_samp_factor = 1; + for (ci = 0; ci < dstinfo->num_components; ci++) { + int v_samp_factor = dstinfo->comp_info[ci].v_samp_factor; + max_v_samp_factor = MAX(max_v_samp_factor, v_samp_factor); + } + MCU_rows = dstinfo->image_height / (max_v_samp_factor * DCTSIZE); + if (MCU_rows > 0) /* can't trim to 0 pixels */ + dstinfo->image_height = MCU_rows * (max_v_samp_factor * DCTSIZE); +} + + +/* Adjust output image parameters as needed. + * + * This must be called after jpeg_copy_critical_parameters() + * and before jpeg_write_coefficients(). + * + * The return value is the set of virtual coefficient arrays to be written + * (either the ones allocated by jtransform_request_workspace, or the + * original source data arrays). The caller will need to pass this value + * to jpeg_write_coefficients(). + */ + +GLOBAL(jvirt_barray_ptr *) +jtransform_adjust_parameters (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + /* If force-to-grayscale is requested, adjust destination parameters */ + if (info->force_grayscale) { + /* We use jpeg_set_colorspace to make sure subsidiary settings get fixed + * properly. Among other things, the target h_samp_factor & v_samp_factor + * will get set to 1, which typically won't match the source. + * In fact we do this even if the source is already grayscale; that + * provides an easy way of coercing a grayscale JPEG with funny sampling + * factors to the customary 1,1. (Some decoders fail on other factors.) + */ + if ((dstinfo->jpeg_color_space == JCS_YCbCr && + dstinfo->num_components == 3) || + (dstinfo->jpeg_color_space == JCS_GRAYSCALE && + dstinfo->num_components == 1)) { + /* We have to preserve the source's quantization table number. */ + int sv_quant_tbl_no = dstinfo->comp_info[0].quant_tbl_no; + jpeg_set_colorspace(dstinfo, JCS_GRAYSCALE); + dstinfo->comp_info[0].quant_tbl_no = sv_quant_tbl_no; + } else { + /* Sorry, can't do it */ + ERREXIT(dstinfo, JERR_CONVERSION_NOTIMPL); + } + } + + /* Correct the destination's image dimensions etc if necessary */ + switch (info->transform) { + case JXFORM_NONE: + /* Nothing to do */ + break; + case JXFORM_FLIP_H: + if (info->trim) + trim_right_edge(dstinfo); + break; + case JXFORM_FLIP_V: + if (info->trim) + trim_bottom_edge(dstinfo); + break; + case JXFORM_TRANSPOSE: + transpose_critical_parameters(dstinfo); + /* transpose does NOT have to trim anything */ + break; + case JXFORM_TRANSVERSE: + transpose_critical_parameters(dstinfo); + if (info->trim) { + trim_right_edge(dstinfo); + trim_bottom_edge(dstinfo); + } + break; + case JXFORM_ROT_90: + transpose_critical_parameters(dstinfo); + if (info->trim) + trim_right_edge(dstinfo); + break; + case JXFORM_ROT_180: + if (info->trim) { + trim_right_edge(dstinfo); + trim_bottom_edge(dstinfo); + } + break; + case JXFORM_ROT_270: + transpose_critical_parameters(dstinfo); + if (info->trim) + trim_bottom_edge(dstinfo); + break; + } + + /* Return the appropriate output data set */ + if (info->workspace_coef_arrays != NULL) + return info->workspace_coef_arrays; + return src_coef_arrays; +} + + +/* Execute the actual transformation, if any. + * + * This must be called *after* jpeg_write_coefficients, because it depends + * on jpeg_write_coefficients to have computed subsidiary values such as + * the per-component width and height fields in the destination object. + * + * Note that some transformations will modify the source data arrays! + */ + +GLOBAL(void) +jtransform_execute_transformation (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *dst_coef_arrays = info->workspace_coef_arrays; + + switch (info->transform) { + case JXFORM_NONE: + break; + case JXFORM_FLIP_H: + do_flip_h(srcinfo, dstinfo, src_coef_arrays); + break; + case JXFORM_FLIP_V: + do_flip_v(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSPOSE: + do_transpose(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSVERSE: + do_transverse(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_90: + do_rot_90(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_180: + do_rot_180(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_270: + do_rot_270(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + } +} + +#endif /* TRANSFORMS_SUPPORTED */ + + +/* Setup decompression object to save desired markers in memory. + * This must be called before jpeg_read_header() to have the desired effect. + */ + +GLOBAL(void) +jcopy_markers_setup (j_decompress_ptr srcinfo, JCOPY_OPTION option) +{ +#ifdef SAVE_MARKERS_SUPPORTED + int m; + + /* Save comments except under NONE option */ + if (option != JCOPYOPT_NONE) { + jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF); + } + /* Save all types of APPn markers iff ALL option */ + if (option == JCOPYOPT_ALL) { + for (m = 0; m < 16; m++) + jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF); + } +#endif /* SAVE_MARKERS_SUPPORTED */ +} + +/* Copy markers saved in the given source object to the destination object. + * This should be called just after jpeg_start_compress() or + * jpeg_write_coefficients(). + * Note that those routines will have written the SOI, and also the + * JFIF APP0 or Adobe APP14 markers if selected. + */ + +GLOBAL(void) +jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option) +{ + jpeg_saved_marker_ptr marker; + + /* In the current implementation, we don't actually need to examine the + * option flag here; we just copy everything that got saved. + * But to avoid confusion, we do not output JFIF and Adobe APP14 markers + * if the encoder library already wrote one. + */ + for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) { + if (dstinfo->write_JFIF_header && + marker->marker == JPEG_APP0 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x4A && + GETJOCTET(marker->data[1]) == 0x46 && + GETJOCTET(marker->data[2]) == 0x49 && + GETJOCTET(marker->data[3]) == 0x46 && + GETJOCTET(marker->data[4]) == 0) + continue; /* reject duplicate JFIF */ + if (dstinfo->write_Adobe_marker && + marker->marker == JPEG_APP0+14 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x41 && + GETJOCTET(marker->data[1]) == 0x64 && + GETJOCTET(marker->data[2]) == 0x6F && + GETJOCTET(marker->data[3]) == 0x62 && + GETJOCTET(marker->data[4]) == 0x65) + continue; /* reject duplicate Adobe */ +#ifdef NEED_FAR_POINTERS + /* We could use jpeg_write_marker if the data weren't FAR... */ + { + unsigned int i; + jpeg_write_m_header(dstinfo, marker->marker, marker->data_length); + for (i = 0; i < marker->data_length; i++) + jpeg_write_m_byte(dstinfo, marker->data[i]); + } +#else + jpeg_write_marker(dstinfo, marker->marker, + marker->data, marker->data_length); +#endif + } +} diff --git a/jpeg/transupp.h b/jpeg/transupp.h new file mode 100644 index 0000000..5c2d32a --- /dev/null +++ b/jpeg/transupp.h @@ -0,0 +1,135 @@ +/* + * transupp.h + * + * Copyright (C) 1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for image transformation routines and + * other utility code used by the jpegtran sample application. These are + * NOT part of the core JPEG library. But we keep these routines separate + * from jpegtran.c to ease the task of maintaining jpegtran-like programs + * that have other user interfaces. + * + * NOTE: all the routines declared here have very specific requirements + * about when they are to be executed during the reading and writing of the + * source and destination files. See the comments in transupp.c, or see + * jpegtran.c for an example of correct usage. + */ + +/* If you happen not to want the image transform support, disable it here */ +#ifndef TRANSFORMS_SUPPORTED +#define TRANSFORMS_SUPPORTED 1 /* 0 disables transform code */ +#endif + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jtransform_request_workspace jTrRequest +#define jtransform_adjust_parameters jTrAdjust +#define jtransform_execute_transformation jTrExec +#define jcopy_markers_setup jCMrkSetup +#define jcopy_markers_execute jCMrkExec +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* + * Codes for supported types of image transformations. + */ + +typedef enum { + JXFORM_NONE, /* no transformation */ + JXFORM_FLIP_H, /* horizontal flip */ + JXFORM_FLIP_V, /* vertical flip */ + JXFORM_TRANSPOSE, /* transpose across UL-to-LR axis */ + JXFORM_TRANSVERSE, /* transpose across UR-to-LL axis */ + JXFORM_ROT_90, /* 90-degree clockwise rotation */ + JXFORM_ROT_180, /* 180-degree rotation */ + JXFORM_ROT_270 /* 270-degree clockwise (or 90 ccw) */ +} JXFORM_CODE; + +/* + * Although rotating and flipping data expressed as DCT coefficients is not + * hard, there is an asymmetry in the JPEG format specification for images + * whose dimensions aren't multiples of the iMCU size. The right and bottom + * image edges are padded out to the next iMCU boundary with junk data; but + * no padding is possible at the top and left edges. If we were to flip + * the whole image including the pad data, then pad garbage would become + * visible at the top and/or left, and real pixels would disappear into the + * pad margins --- perhaps permanently, since encoders & decoders may not + * bother to preserve DCT blocks that appear to be completely outside the + * nominal image area. So, we have to exclude any partial iMCUs from the + * basic transformation. + * + * Transpose is the only transformation that can handle partial iMCUs at the + * right and bottom edges completely cleanly. flip_h can flip partial iMCUs + * at the bottom, but leaves any partial iMCUs at the right edge untouched. + * Similarly flip_v leaves any partial iMCUs at the bottom edge untouched. + * The other transforms are defined as combinations of these basic transforms + * and process edge blocks in a way that preserves the equivalence. + * + * The "trim" option causes untransformable partial iMCUs to be dropped; + * this is not strictly lossless, but it usually gives the best-looking + * result for odd-size images. Note that when this option is active, + * the expected mathematical equivalences between the transforms may not hold. + * (For example, -rot 270 -trim trims only the bottom edge, but -rot 90 -trim + * followed by -rot 180 -trim trims both edges.) + * + * We also offer a "force to grayscale" option, which simply discards the + * chrominance channels of a YCbCr image. This is lossless in the sense that + * the luminance channel is preserved exactly. It's not the same kind of + * thing as the rotate/flip transformations, but it's convenient to handle it + * as part of this package, mainly because the transformation routines have to + * be aware of the option to know how many components to work on. + */ + +typedef struct { + /* Options: set by caller */ + JXFORM_CODE transform; /* image transform operator */ + boolean trim; /* if TRUE, trim partial MCUs as needed */ + boolean force_grayscale; /* if TRUE, convert color image to grayscale */ + + /* Internal workspace: caller should not touch these */ + int num_components; /* # of components in workspace */ + jvirt_barray_ptr * workspace_coef_arrays; /* workspace for transformations */ +} jpeg_transform_info; + + +#if TRANSFORMS_SUPPORTED + +/* Request any required workspace */ +EXTERN(void) jtransform_request_workspace + JPP((j_decompress_ptr srcinfo, jpeg_transform_info *info)); +/* Adjust output image parameters */ +EXTERN(jvirt_barray_ptr *) jtransform_adjust_parameters + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); +/* Execute the actual transformation, if any */ +EXTERN(void) jtransform_execute_transformation + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); + +#endif /* TRANSFORMS_SUPPORTED */ + + +/* + * Support for copying optional markers from source to destination file. + */ + +typedef enum { + JCOPYOPT_NONE, /* copy no optional markers */ + JCOPYOPT_COMMENTS, /* copy only comment (COM) markers */ + JCOPYOPT_ALL /* copy all optional markers */ +} JCOPY_OPTION; + +#define JCOPYOPT_DEFAULT JCOPYOPT_COMMENTS /* recommended default */ + +/* Setup decompression object to save desired markers in memory */ +EXTERN(void) jcopy_markers_setup + JPP((j_decompress_ptr srcinfo, JCOPY_OPTION option)); +/* Copy markers saved in the given source object to the destination object */ +EXTERN(void) jcopy_markers_execute + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option)); diff --git a/jpegtools.c b/jpegtools.c new file mode 100644 index 0000000..90ac7dd --- /dev/null +++ b/jpegtools.c @@ -0,0 +1,621 @@ +/* + * jpegtran.c + * + * Copyright (C) 1995-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * plenty of changes by Gerd Knorr <kraxel@bytesex.org>, with focus on + * digital image processing and sane exif handling: + * + * - does transformations only (flip/rotate/transpose/transverse). + * - also transforms the exif thumbnail if present. + * - can automatically figure transformation from the + * exif orientation tag. + * - updates the exif orientation tag. + * - updates the exif pixel dimension tags. + * + * This file contains a command-line user interface for JPEG transcoding. + * It is very similar to cjpeg.c, but provides lossless transcoding between + * different JPEG file formats. It also provides some lossless and sort-of- + * lossless transformations of JPEG data. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <utime.h> +#include <setjmp.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <jpeglib.h> +#include "jpeg/transupp.h" /* Support routines for jpegtran */ +#include "jpegtools.h" + +#include "misc.h" + +#include <libexif/exif-data.h> +#include <libexif/exif-utils.h> +#include <libexif/exif-ifd.h> +#include <libexif/exif-tag.h> + +static int do_transform(struct jpeg_decompress_struct *src, + struct jpeg_compress_struct *dst, + JXFORM_CODE transform, + unsigned char *comment, + char *thumbnail, int tsize, + unsigned int flags); + +static JXFORM_CODE transmagic[] = { + [ 1 ] = JXFORM_NONE, + [ 2 ] = JXFORM_FLIP_H, + [ 3 ] = JXFORM_ROT_180, + [ 4 ] = JXFORM_FLIP_V, + [ 5 ] = JXFORM_TRANSPOSE, + [ 6 ] = JXFORM_ROT_90, + [ 7 ] = JXFORM_TRANSVERSE, + [ 8 ] = JXFORM_ROT_270, +}; + +#if 0 +static char *transname[] = { + [ JXFORM_NONE ] = "none", + [ JXFORM_FLIP_H ] = "flip h", + [ JXFORM_FLIP_V ] = "flip v", + [ JXFORM_TRANSPOSE ] = "transpose", + [ JXFORM_TRANSVERSE ] = "transverse", + [ JXFORM_ROT_90 ] = "rot 90", + [ JXFORM_ROT_180 ] = "rot 190", + [ JXFORM_ROT_270 ] = "rot 270", +}; +#endif + +/* ---------------------------------------------------------------------- */ + +/* libjpeg error handler -- exit via longjump */ +struct longjmp_error_mgr { + struct jpeg_error_mgr jpeg; + jmp_buf setjmp_buffer; +}; + +static void longjmp_error_exit(j_common_ptr cinfo) +{ + struct longjmp_error_mgr *h = (struct longjmp_error_mgr*)cinfo->err; + (*cinfo->err->output_message)(cinfo); + longjmp(h->setjmp_buffer, 1); +} + +/* ---------------------------------------------------------------------- */ + +static long get_int(ExifData *ed, ExifEntry *ee) +{ + ExifByteOrder o = exif_data_get_byte_order(ed); + long value; + + switch (ee->format) { + case EXIF_FORMAT_SHORT: + value = exif_get_short (ee->data, o); + break; + case EXIF_FORMAT_LONG: + value = exif_get_long (ee->data, o); + break; + case EXIF_FORMAT_SLONG: + value = exif_get_slong (ee->data, o); + break; + default: + fprintf(stderr,"get_int oops\n"); + exit(1); + } + return value; +} + +static void set_int(ExifData *ed, ExifEntry *ee, long value) +{ + ExifByteOrder o = exif_data_get_byte_order(ed); + + switch (ee->format) { + case EXIF_FORMAT_SHORT: + exif_set_short (ee->data, o, value); + break; + case EXIF_FORMAT_LONG: + exif_set_long (ee->data, o, value); + break; + case EXIF_FORMAT_SLONG: + exif_set_slong (ee->data, o, value); + break; + default: + fprintf(stderr,"set_int oops\n"); + exit(1); + } +} + +static void update_orientation(ExifData *ed, int ifd, int orientation) +{ + ExifEntry *ee; + + ee = exif_content_get_entry(ed->ifd[ifd], 0x0112); + if (NULL == ee) + return; + set_int(ed,ee,orientation); +} + +static void update_dimension(ExifData *ed, JXFORM_CODE transform, + int src_x, int src_y) +{ + static struct { + int idf; + int tag; + int x; + } fields[] = { + { + .idf = EXIF_IFD_EXIF, + .tag = EXIF_TAG_PIXEL_X_DIMENSION, + .x = 1, + },{ + .idf = EXIF_IFD_EXIF, + .tag = EXIF_TAG_PIXEL_Y_DIMENSION, + .x = 0, + },{ + .idf = EXIF_IFD_INTEROPERABILITY, + .tag = EXIF_TAG_RELATED_IMAGE_WIDTH, + .x = 1, + },{ + .idf = EXIF_IFD_INTEROPERABILITY, + .tag = EXIF_TAG_RELATED_IMAGE_LENGTH, + .x = 0, + } + }; + ExifEntry *ee; + int i; + + for (i = 0; i < sizeof(fields)/sizeof(fields[0]); i++) { + ee = exif_content_get_entry(ed->ifd[fields[i].idf], fields[i].tag); + if (!ee) + continue; + switch (transform) { + case JXFORM_ROT_90: + case JXFORM_ROT_270: + case JXFORM_TRANSPOSE: + case JXFORM_TRANSVERSE: + /* x/y reversed */ + set_int(ed, ee, fields[i].x ? src_y : src_x); + break; + default: + /* normal */ + set_int(ed, ee, fields[i].x ? src_x : src_y); + break; + } + } +} + +static int get_orientation(ExifData *ed) +{ + ExifEntry *ee; + + ee = exif_content_get_entry(ed->ifd[EXIF_IFD_0], 0x0112); + if (NULL == ee) + return 1; /* top - left */ + return get_int(ed,ee); +} + +/* ---------------------------------------------------------------------- */ + +struct th { + struct jpeg_decompress_struct src; + struct jpeg_compress_struct dst; + struct jpeg_error_mgr jsrcerr, jdsterr; + unsigned char *in; + unsigned char *out; + int isize, osize; +}; + +static void thumbnail_src_init(struct jpeg_decompress_struct *cinfo) +{ + struct th *h = container_of(cinfo, struct th, src); + cinfo->src->next_input_byte = h->in; + cinfo->src->bytes_in_buffer = h->isize; +} + +static int thumbnail_src_fill(struct jpeg_decompress_struct *cinfo) +{ + fprintf(stderr,"jpeg: panic: no more thumbnail input data\n"); + exit(1); +} + +static void thumbnail_src_skip(struct jpeg_decompress_struct *cinfo, + long num_bytes) +{ + cinfo->src->next_input_byte += num_bytes; +} + +static void thumbnail_src_term(struct jpeg_decompress_struct *cinfo) +{ + /* nothing */ +} + +static void thumbnail_dest_init(struct jpeg_compress_struct *cinfo) +{ + struct th *h = container_of(cinfo, struct th, dst); + h->osize = h->isize * 2; + h->out = malloc(h->osize); + cinfo->dest->next_output_byte = h->out; + cinfo->dest->free_in_buffer = h->osize; +} + +static boolean thumbnail_dest_flush(struct jpeg_compress_struct *cinfo) +{ + fprintf(stderr,"jpeg: panic: output buffer full\n"); + exit(1); +} + +static void thumbnail_dest_term(struct jpeg_compress_struct *cinfo) +{ + struct th *h = container_of(cinfo, struct th, dst); + h->osize -= cinfo->dest->free_in_buffer; +} + +static struct jpeg_source_mgr thumbnail_src = { + .init_source = thumbnail_src_init, + .fill_input_buffer = thumbnail_src_fill, + .skip_input_data = thumbnail_src_skip, + .resync_to_restart = jpeg_resync_to_restart, + .term_source = thumbnail_src_term, +}; + +static struct jpeg_destination_mgr thumbnail_dst = { + .init_destination = thumbnail_dest_init, + .empty_output_buffer = thumbnail_dest_flush, + .term_destination = thumbnail_dest_term, +}; + +static void do_thumbnail(ExifData *ed, JXFORM_CODE transform) +{ + struct th th; + + if (JXFORM_NONE == transform) + return; + + memset(&th,0,sizeof(th)); + th.in = ed->data; + th.isize = ed->size; + + /* setup src */ + th.src.err = jpeg_std_error(&th.jsrcerr); + jpeg_create_decompress(&th.src); + th.src.src = &thumbnail_src; + + /* setup dst */ + th.dst.err = jpeg_std_error(&th.jdsterr); + jpeg_create_compress(&th.dst); + th.dst.dest = &thumbnail_dst; + + /* transform image */ + do_transform(&th.src,&th.dst,transform,NULL,NULL,0,JFLAG_TRANSFORM_IMAGE); + + /* cleanup */ + jpeg_destroy_decompress(&th.src); + jpeg_destroy_compress(&th.dst); + + /* replace thumbnail */ + free(ed->data); + ed->data = th.out; + ed->size = th.osize; +} + +static void do_exif(struct jpeg_decompress_struct *src, + JXFORM_CODE *transform, + char *thumbnail, int tsize, + unsigned int flags) +{ + jpeg_saved_marker_ptr mark; + ExifData *ed = NULL; + unsigned char *data; + unsigned int size; + + for (mark = src->marker_list; NULL != mark; mark = mark->next) { + if (mark->marker != JPEG_APP0 +1) + continue; + ed = exif_data_new_from_data(mark->data,mark->data_length); + break; + } + if (flags & JFLAG_UPDATE_THUMBNAIL) { + if (NULL == ed) + ed = exif_data_new(); + if (NULL == mark) { + mark = src->mem->alloc_large((j_common_ptr)src,JPOOL_IMAGE,sizeof(*mark)); + memset(mark,0,sizeof(*mark)); + mark->marker = JPEG_APP0 +1; + mark->next = src->marker_list->next; + src->marker_list->next = mark; + } + if (ed->data) + free(ed->data); + ed->data = thumbnail; + ed->size = tsize; + } + if (NULL == ed) + return; + + if (-1 == *transform) { + /* automagic image transformation */ + int orientation = get_orientation(ed); + *transform = JXFORM_NONE; + if (orientation >= 1 && orientation <= 8) + *transform = transmagic[orientation]; +#if 0 + if (debug) + fprintf(stderr,"autotrans: %s\n",transname[*transform]); +#endif + } + + /* update exif data */ + if (flags & JFLAG_UPDATE_ORIENTATION) { + update_orientation(ed,EXIF_IFD_0,1); + update_orientation(ed,EXIF_IFD_1,1); + } + if (ed->data && ed->data[0] == 0xff && ed->data[1] == 0xd8 && + (flags & JFLAG_TRANSFORM_THUMBNAIL)) + do_thumbnail(ed,*transform); + update_dimension(ed, (flags & JFLAG_TRANSFORM_IMAGE) ? *transform : JXFORM_NONE, + src->image_width, src->image_height); + + /* build new exif data block */ + exif_data_save_data(ed,&data,&size); + exif_data_unref(ed); + + /* update jpeg APP1 (EXIF) marker */ + mark->data = src->mem->alloc_large((j_common_ptr)src,JPOOL_IMAGE,size); + mark->original_length = size; + mark->data_length = size; + memcpy(mark->data,data,size); + free(data); +} + +/* ---------------------------------------------------------------------- */ + +static void do_comment(struct jpeg_decompress_struct *src, + unsigned char *comment) +{ + jpeg_saved_marker_ptr mark; + int size; + + /* find or create comment marker */ + for (mark = src->marker_list;; mark = mark->next) { + if (mark->marker == JPEG_COM) + break; + if (NULL == mark->next) { + mark->next = src->mem->alloc_large((j_common_ptr)src,JPOOL_IMAGE, + sizeof(*mark)); + mark = mark->next; + memset(mark,0,sizeof(*mark)); + mark->marker = JPEG_COM; + break; + } + } + + /* update comment marker */ + size = strlen(comment) +1; + mark->data = src->mem->alloc_large((j_common_ptr)src,JPOOL_IMAGE,size); + mark->original_length = size; + mark->data_length = size; + memcpy(mark->data,comment,size); +} + +static int do_transform(struct jpeg_decompress_struct *src, + struct jpeg_compress_struct *dst, + JXFORM_CODE transform, + unsigned char *comment, + char *thumbnail, int tsize, + unsigned int flags) +{ + jvirt_barray_ptr * src_coef_arrays; + jvirt_barray_ptr * dst_coef_arrays; + jpeg_transform_info transformoption; + + jcopy_markers_setup(src, JCOPYOPT_ALL); + if (JPEG_HEADER_OK != jpeg_read_header(src, TRUE)) + return -1; + + do_exif(src,&transform,thumbnail,tsize,flags); + if (-1 == transform) + transform = JXFORM_NONE; + if (!(flags & JFLAG_TRANSFORM_IMAGE)) + transform = JXFORM_NONE; + if ((flags & JFLAG_UPDATE_COMMENT) && NULL != comment) + do_comment(src,comment); + + memset(&transformoption,0,sizeof(transformoption)); + transformoption.transform = transform; + transformoption.trim = FALSE; + transformoption.force_grayscale = FALSE; + + /* Any space needed by a transform option must be requested before + * jpeg_read_coefficients so that memory allocation will be done right. + */ + jtransform_request_workspace(src, &transformoption); + src_coef_arrays = jpeg_read_coefficients(src); + jpeg_copy_critical_parameters(src, dst); + dst_coef_arrays = jtransform_adjust_parameters + (src, dst, src_coef_arrays, &transformoption); + + /* Start compressor (note no image data is actually written here) */ + jpeg_write_coefficients(dst, dst_coef_arrays); + + /* Copy to the output file any extra markers that we want to preserve */ + jcopy_markers_execute(src, dst, JCOPYOPT_ALL); + + /* Execute image transformation, if any */ + jtransform_execute_transformation(src, dst, + src_coef_arrays, + &transformoption); + + /* Finish compression and release memory */ + jpeg_finish_compress(dst); + jpeg_finish_decompress(src); + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +int jpeg_transform_fp(FILE *in, FILE *out, + JXFORM_CODE transform, + unsigned char *comment, + char *thumbnail, int tsize, + unsigned int flags) +{ + struct jpeg_decompress_struct src; + struct jpeg_compress_struct dst; + struct jpeg_error_mgr jdsterr; + struct longjmp_error_mgr jsrcerr; + + /* setup src */ + src.err = jpeg_std_error(&jsrcerr.jpeg); + jsrcerr.jpeg.error_exit = longjmp_error_exit; + if (setjmp(jsrcerr.setjmp_buffer)) + /* something went wrong within the jpeg library ... */ + goto oops; + jpeg_create_decompress(&src); + jpeg_stdio_src(&src, in); + + /* setup dst */ + dst.err = jpeg_std_error(&jdsterr); + jpeg_create_compress(&dst); + jpeg_stdio_dest(&dst, out); + + /* transform image */ + do_transform(&src,&dst,transform,comment,thumbnail,tsize,flags); + + /* cleanup */ + jpeg_destroy_decompress(&src); + jpeg_destroy_compress(&dst); + return 0; + + oops: + jpeg_destroy_decompress(&src); + jpeg_destroy_compress(&dst); + return -1; +} + +int jpeg_transform_files(char *infile, char *outfile, + JXFORM_CODE transform, + unsigned char *comment, + char *thumbnail, int tsize, + unsigned int flags) +{ + int rc; + FILE *in; + FILE *out; + + /* open infile */ + in = fopen(infile,"r"); + if (NULL == in) { + fprintf(stderr,"open %s: %s\n",infile,strerror(errno)); + return -1; + } + + /* open outfile */ + out = fopen(outfile,"w"); + if (NULL == out) { + fprintf(stderr,"open %s: %s\n",outfile,strerror(errno)); + fclose(in); + return -1; + } + + /* go! */ + rc = jpeg_transform_fp(in,out,transform,comment,thumbnail,tsize,flags); + fclose(in); + fclose(out); + + return rc; +} + +int jpeg_transform_inplace(char *file, + JXFORM_CODE transform, + unsigned char *comment, + char *thumbnail, int tsize, + unsigned int flags) +{ + char *tmpfile; + char *bakfile; + struct stat st; + int fd; + FILE *in = NULL; + FILE *out = NULL; + + /* are we allowed to write to the file? */ + if (0 != access(file,W_OK)) { + fprintf(stderr,"access %s: %s\n",file,strerror(errno)); + return -1; + } + + /* open infile */ + in = fopen(file,"r"); + if (NULL == in) { + fprintf(stderr,"open %s: %s\n",file,strerror(errno)); + return -1; + } + + /* open tmpfile */ + tmpfile = malloc(strlen(file)+10); + sprintf(tmpfile,"%s.XXXXXX",file); + fd = mkstemp(tmpfile); + if (-1 == fd) { + fprintf(stderr,"mkstemp(%s): %s\n",tmpfile,strerror(errno)); + goto oops; + } + out = fdopen(fd,"w"); + + /* copy owner and permissions */ + if (-1 == fstat(fileno(in),&st)) { + fprintf(stderr,"fstat(%s): %s\n",file,strerror(errno)); + goto oops; + } + if (-1 == fchown(fileno(out),st.st_uid,st.st_gid)) { + fprintf(stderr,"fchown(%s): %s\n",tmpfile,strerror(errno)); + goto oops; + } + if (-1 == fchmod(fileno(out),st.st_mode)) { + fprintf(stderr,"fchmod(%s): %s\n",tmpfile,strerror(errno)); + goto oops; + } + + /* transform */ + if (0 != jpeg_transform_fp(in,out,transform,comment,thumbnail,tsize,flags)) + goto oops; + + /* worked ok -- commit */ + fclose(in); + fclose(out); + if (flags & JFLAG_FILE_BACKUP) { + bakfile = malloc(strlen(file)+2); + sprintf(bakfile,"%s~",file); + rename(file,bakfile); + free(bakfile); + } + rename(tmpfile,file); + if (flags & JFLAG_FILE_KEEP_TIME) { + struct utimbuf u; + u.actime = st.st_atime; + u.modtime = st.st_mtime; + utime(file,&u); + } + + /* cleanup & return */ + free(tmpfile); + return 0; + + oops: + /* something went wrong -- rollback */ + if (in) + fclose(in); + if (out) { + fclose(out); + unlink(tmpfile); + } + return -1; +} diff --git a/jpegtools.h b/jpegtools.h new file mode 100644 index 0000000..78035b0 --- /dev/null +++ b/jpegtools.h @@ -0,0 +1,28 @@ + +/* various flags */ +#define JFLAG_TRANSFORM_IMAGE 0x0001 +#define JFLAG_TRANSFORM_THUMBNAIL 0x0002 + +#define JFLAG_UPDATE_COMMENT 0x0010 +#define JFLAG_UPDATE_ORIENTATION 0x0020 +#define JFLAG_UPDATE_THUMBNAIL 0x0040 + +#define JFLAG_FILE_BACKUP 0x0100 +#define JFLAG_FILE_KEEP_TIME 0x0200 + +/* functions */ +int jpeg_transform_fp(FILE *in, FILE *out, + JXFORM_CODE transform, + unsigned char *comment, + char *thumbnail, int tsize, + unsigned int flags); +int jpeg_transform_files(char *infile, char *outfile, + JXFORM_CODE transform, + unsigned char *comment, + char *thumbnail, int tsize, + unsigned int flags); +int jpeg_transform_inplace(char *file, + JXFORM_CODE transform, + unsigned char *comment, + char *thumbnail, int tsize, + unsigned int flags); @@ -0,0 +1,60 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> + +#include <lirc/lirc_client.h> +#include "lirc.h" + +/*-----------------------------------------------------------------------*/ + +static int debug = 0; +static struct lirc_config *config = NULL; + +int lirc_fbi_init() +{ + int fd; + if (-1 == (fd = lirc_init("fbi",debug))) { + if (debug) + fprintf(stderr,"lirc: no infrared remote support available\n"); + return -1; + } + if (0 != lirc_readconfig(NULL,&config,NULL)) { + config = NULL; + } + if (debug) + fprintf(stderr, "lirc: ~/.lircrc file %sfound\n", + config ? "" : "not "); + + fcntl(fd,F_SETFL,O_NONBLOCK); + fcntl(fd,F_SETFD,FD_CLOEXEC); + if (debug) + fprintf(stderr,"lirc: init ok\n"); + + return fd; +} + +int lirc_fbi_havedata(int* rc, char key[11]) +{ + char *code,*cmd; + int ret=-1; + + while (lirc_nextcode(&code) == 0 && code != NULL) { + ret = 0; + if (config) { + /* use ~/.lircrc */ + while (lirc_code2char(config,code,&cmd)==0 && cmd != NULL) { + memset(key,0,11); + strncpy(key,cmd,10); + *rc = strlen(cmd); + if (debug) + fprintf(stderr,"lirc: cmd \"%s\"\n", cmd); + } + } + free(code); + } + return ret; +} @@ -0,0 +1,2 @@ +int lirc_fbi_init(void); +int lirc_fbi_havedata(int* rc, char key[11]); @@ -0,0 +1,168 @@ +#ifndef _LIST_H_ +#define _LIST_H_ +/* + * Simple doubly linked list implementation. + * -- shameless stolen from the linux kernel sources + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_add(struct list_head * new, + struct list_head * prev, + struct list_head * next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static __inline__ void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is in an undefined state. + */ +static __inline__ void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static __inline__ void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static __inline__ int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static __inline__ void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev - iterate over a list in reverse order + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +#endif /* _LIST_H_ */ diff --git a/logo.jpg b/logo.jpg Binary files differnew file mode 100644 index 0000000..f9814b0 --- /dev/null +++ b/logo.jpg @@ -0,0 +1,97 @@ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> + +#include "readers.h" +#include "viewer.h" +#include "lut.h" + +/* ----------------------------------------------------------------------- */ + +struct op_map_parm_ch op_map_nothing = { + gamma: 1, + bottom: 0, + top: 255, + left: 0, + right: 255 +}; + +struct op_map_lut { + unsigned char red[256]; + unsigned char green[256]; + unsigned char blue[256]; +}; + +/* ----------------------------------------------------------------------- */ +/* functions */ + +static void build_lut(struct op_map_parm_ch *arg, unsigned char *lut) +{ + int i,val; + int inrange,outrange; + float p; + + inrange = arg->right - arg->left +1; + outrange = arg->top - arg->bottom +1; + p = 1/arg->gamma; + + for (i = 0; i < arg->left; i++) + lut[i] = 0; + for (; i <= arg->right; i++) { + val = pow((float)(i-arg->left)/inrange,p) * outrange + 0.5; + val += arg->bottom; + if (val < 0) val = 0; + if (val > 255) val = 255; + lut[i] = val; + } + for (; i < 256; i++) + lut[i] = 255; +} + +static void* +op_map_init(struct ida_image *src, struct ida_rect *rect, + struct ida_image_info *i, void *parm) +{ + struct op_map_parm *args = parm; + struct op_map_lut *lut; + + lut = malloc(sizeof(*lut)); + build_lut(&args->red,lut->red); + build_lut(&args->green,lut->green); + build_lut(&args->blue,lut->blue); + + *i = src->i; + return lut; +} + +static void +op_map_work(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int line, void *data) +{ + struct op_map_lut *lut = data; + unsigned char *scanline; + int i; + + scanline = src->data + line * src->i.width * 3; + memcpy(dst,scanline,src->i.width * 3); + if (line < rect->y1 || line >= rect->y2) + return; + dst += 3*rect->x1; + scanline += 3*rect->x1; + for (i = rect->x1; i < rect->x2; i++) { + dst[0] = lut->red[scanline[0]]; + dst[1] = lut->green[scanline[1]]; + dst[2] = lut->blue[scanline[2]]; + scanline += 3; + dst += 3; + } +} + +/* ----------------------------------------------------------------------- */ + +struct ida_op desc_map = { + name: "map", + init: op_map_init, + work: op_map_work, + done: op_free_done, +}; @@ -0,0 +1,15 @@ +struct op_map_parm_ch { + float gamma; + int bottom; + int top; + int left; + int right; +}; +struct op_map_parm { + struct op_map_parm_ch red; + struct op_map_parm_ch green; + struct op_map_parm_ch blue; +}; + +extern struct op_map_parm_ch op_map_nothing; +extern struct ida_op desc_map; @@ -0,0 +1,119 @@ +/* + * motif-based man page renderer + * (c) 2001 Gerd Knorr <kraxel@bytesex.org> + * + */ + +#include <stdio.h> + +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> +#include <Xm/Xm.h> +#include <Xm/Form.h> +#include <Xm/Label.h> +#include <Xm/RowColumn.h> +#include <Xm/PushB.h> +#include <Xm/ScrolledW.h> +#include <Xm/SelectioB.h> + +#include "RegEdit.h" +#include "man.h" + +extern Display *dpy; +extern Widget app_shell; + +/*----------------------------------------------------------------------*/ + +#define MAN_UNDEF 0 +#define MAN_NORMAL 1 +#define MAN_BOLD 2 +#define MAN_UNDERLINE 3 + +static XmStringTag man_tags[] = { + NULL, NULL, "bold", "underline" +}; + +static void +man_destroy(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XtDestroyWidget(clientdata); +} + +void +man(char *page) +{ + Widget dlg,view,label; + XmString xmpage,xmchunk; + char line[1024],chunk[256]; + int s,d,cur,last; + FILE *fp; + + /* build dialog */ + dlg = XmCreatePromptDialog(app_shell,"man",NULL,0); + XmdRegisterEditres(XtParent(dlg)); + XtUnmanageChild(XmSelectionBoxGetChild(dlg,XmDIALOG_SELECTION_LABEL)); + XtUnmanageChild(XmSelectionBoxGetChild(dlg,XmDIALOG_HELP_BUTTON)); + XtUnmanageChild(XmSelectionBoxGetChild(dlg,XmDIALOG_CANCEL_BUTTON)); + XtUnmanageChild(XmSelectionBoxGetChild(dlg,XmDIALOG_TEXT)); + XtAddCallback(dlg,XmNokCallback,man_destroy,dlg); + view = XmCreateScrolledWindow(dlg,"view",NULL,0); + XtManageChild(view); + label = XtVaCreateManagedWidget("label", xmLabelWidgetClass,view, NULL); + XtManageChild(dlg); + + /* fetch page and render into XmString */ + sprintf(line,"man %s 2>/dev/null",page); + fp = popen(line,"r"); + xmpage = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT, NULL); + while (NULL != fgets(line,sizeof(line)-1,fp)) { + last = MAN_UNDEF; + for (s = 0, d = 0; line[s] != '\0';) { + /* check current char */ + cur = MAN_NORMAL; + if (line[s+1] == '\010' && line[s] == line[s+2]) + cur = MAN_BOLD; + if (line[s] == '_' && line[s+1] == '\010') + cur = MAN_UNDERLINE; + /* add chunk if completed */ + if (MAN_UNDEF != last && cur != last) { + xmchunk = XmStringGenerate(chunk,NULL,XmMULTIBYTE_TEXT, + man_tags[last]); + xmpage = XmStringConcatAndFree(xmpage,xmchunk); + d = 0; + } + /* add char to chunk */ + switch (cur) { + case MAN_BOLD: + case MAN_UNDERLINE: + s += 2; + case MAN_NORMAL: + chunk[d++] = line[s++]; + break; + } + chunk[d] = '\0'; + last = cur; + } + /* add last chunk for line */ + xmchunk = XmStringGenerate(chunk, NULL, XmMULTIBYTE_TEXT, + man_tags[last]); + xmpage = XmStringConcatAndFree(xmpage,xmchunk); + } + XtVaSetValues(label,XmNlabelString,xmpage,NULL); + XmStringFree(xmpage); + fclose(fp); +} + +void +man_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + char *page = clientdata; + man(page); +} + +void +man_action(Widget widget, XEvent *event, + String *params, Cardinal *num_params) +{ + if (*num_params > 0) + man(params[0]); +} @@ -0,0 +1,4 @@ +void man(char *page); +void man_cb(Widget widget, XtPointer clientdata, XtPointer call_data); +void man_action(Widget widget, XEvent *event, + String *params, Cardinal *num_params); @@ -0,0 +1,9 @@ +/* + * misc useful #defines ... + */ + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define array_size(x) (sizeof(x)/sizeof(x[0])) diff --git a/mk/Autoconf.mk b/mk/Autoconf.mk new file mode 100644 index 0000000..4d25d21 --- /dev/null +++ b/mk/Autoconf.mk @@ -0,0 +1,138 @@ +# +# simple autoconf system for GNU make +# +# (c) 2002-2004 Gerd Knorr <kraxel@bytesex.org> +# +# credits for creating this one go to the autotools people because +# they managed it to annoy lots of developers and users (including +# me) with version incompatibilities. +# +# This file is public domain. No warranty. If it breaks you keep +# both pieces. +# +######################################################################## + +# verbose yes/no +verbose ?= no + +# some stuff used by the tests +ifneq ($(verbose),no) + # verbose (for debug) + ac_init = echo "checking $(1) ... " >&2; rc=no + ac_b_cmd = echo "run: $(1)" >&2; $(1) >/dev/null && rc=yes + ac_s_cmd = echo "run: $(1)" >&2; rc=`$(1)` + ac_fini = echo "... result is $${rc}" >&2; echo >&2; echo "$${rc}" +else + # normal + ac_init = echo -n "checking $(1) ... " >&2; rc=no + ac_b_cmd = $(1) >/dev/null 2>&1 && rc=yes + ac_s_cmd = rc=`$(1) 2>/dev/null` + ac_fini = echo "$${rc}" >&2; echo "$${rc}" +endif + +# some helpers to build cflags and related variables +ac_def_cflags_1 = $(if $(filter yes,$($(1))),-D$(1)) +ac_lib_cflags = $(foreach lib,$(1),$(call ac_def_cflags_1,HAVE_LIB$(lib))) +ac_inc_cflags = $(foreach inc,$(1),$(call ac_def_cflags_1,HAVE_$(inc))) +ac_lib_mkvar_1 = $(if $(filter yes,$(HAVE_LIB$(1))),$($(1)_$(2))) +ac_lib_mkvar = $(foreach lib,$(1),$(call ac_lib_mkvar_1,$(lib),$(2))) + + +######################################################################## +# the tests ... + +# get uname +ac_uname = $(shell \ + $(call ac_init,for system);\ + $(call ac_s_cmd,uname -s | tr 'A-Z' 'a-z');\ + $(call ac_fini)) + +# check for some header file +# args: header file +ac_header = $(shell \ + $(call ac_init,for $(1));\ + $(call ac_b_cmd,echo '\#include <$(1)>' |\ + $(CC) $(CFLAGS) -E -);\ + $(call ac_fini)) + +# check for some function +# args: function [, additional libs ] +ac_func = $(shell \ + $(call ac_init,for $(1));\ + echo 'void $(1)(void); int main(void) {$(1)();return 0;}' \ + > __actest.c;\ + $(call ac_b_cmd,$(CC) $(CFLAGS) $(LDFLAGS) -o \ + __actest __actest.c $(2));\ + rm -f __actest __actest.c;\ + $(call ac_fini)) + +# check for some library +# args: function, library [, additional libs ] +ac_lib = $(shell \ + $(call ac_init,for $(1) in $(2));\ + echo 'void $(1)(void); int main(void) {$(1)();return 0;}' \ + > __actest.c;\ + $(call ac_b_cmd,$(CC) $(CFLAGS) $(LDFLAGS) -o \ + __actest __actest.c -l$(2) $(3));\ + rm -f __actest __actest.c;\ + $(call ac_fini)) + +# check if some compiler flag works +# args: compiler flag +ac_cflag = $(shell \ + $(call ac_init,if $(CC) supports $(1));\ + echo 'int main() {return 0;}' > __actest.c;\ + $(call ac_b_cmd,$(CC) $(CFLAGS) $(1) $(LDFLAGS) -o \ + __actest __actest.c);\ + rm -f __actest __actest.c;\ + $(call ac_fini)) + +# check for some binary +# args: binary name +ac_binary = $(shell \ + $(call ac_init,for $(1));\ + $(call ac_s_cmd,which $(1));\ + bin="$$rc";rc="no";\ + $(call ac_b_cmd,test -x "$$$$bin");\ + $(call ac_fini)) + +# check if lib64 is used +ac_lib64 = $(shell \ + $(call ac_init,for libdir name);\ + $(call ac_s_cmd,$(CC) -print-search-dirs | grep -q lib64 &&\ + echo "lib64" || echo "lib");\ + $(call ac_fini)) + +# check for x11 ressource dir prefix +ac_resdir = $(shell \ + $(call ac_init,for X11 app-defaults prefix);\ + $(call ac_s_cmd, test -d /etc/X11/app-defaults &&\ + echo "/etc/X11" || echo "/usr/X11R6/lib/X11");\ + $(call ac_fini)) + + +######################################################################## +# build Make.config + +define newline + + +endef +make-config-q = $(subst $(newline),\n,$(make-config)) + +ifeq ($(filter config,$(MAKECMDGOALS)),config) +.PHONY: Make.config + LIB := $(call ac_lib64) +else + LIB ?= $(call ac_lib64) + LIB := $(LIB) +endif +.PHONY: config +config: Make.config + @true + +Make.config: $(srcdir)/GNUmakefile + @echo -e "$(make-config-q)" > $@ + @echo + @echo "Make.config written, edit if needed" + @echo diff --git a/mk/Compile.mk b/mk/Compile.mk new file mode 100644 index 0000000..49dddbf --- /dev/null +++ b/mk/Compile.mk @@ -0,0 +1,88 @@ +# +# some rules to compile stuff ... +# +# (c) 2002-2004 Gerd Knorr <kraxel@bytesex.org> +# +# main features: +# * autodependencies via "cpp -MD" +# * fancy, non-verbose output +# +# This file is public domain. No warranty. If it breaks you keep +# both pieces. +# +######################################################################## + +# verbose yes/no +verbose ?= no + +# dependency files +tmpdep = mk/$(subst /,_,$*).tmp +depfile = mk/$(subst /,_,$*).dep +depfiles = mk/*.dep + +compile_c = $(CC) $(CFLAGS) -Wp,-MD,$(tmpdep) -c -o $@ $< +compile_cc = $(CXX) $(CXXFLAGS) -Wp,-MD,$(tmpdep) -c -o $@ $< +fixup_deps = sed -e "s|.*\.o:|$@:|" < $(tmpdep) > $(depfile) && rm -f $(tmpdep) +cc_makedirs = mkdir -p $(dir $@) $(dir $(depfile)) + +link_app = $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) +link_so = $(CC) $(LDFLAGS) -shared -Wl,-soname,$(@F) -o $@ $^ $(LDLIBS) +ar_lib = rm -f $@ && ar -r $@ $^ && ranlib $@ + +moc_h = $(MOC) $< -o $@ +msgfmt_po = msgfmt -o $@ $< + +# non-verbose output +ifeq ($(verbose),no) + echo_compile_c = echo " CC " $@ + echo_compile_cc = echo " CXX " $@ + echo_link_app = echo " LD " $@ + echo_link_so = echo " LD " $@ + echo_ar_lib = echo " AR " $@ + echo_moc_h = echo " MOC " $@ + echo_msgfmt_po = echo " MSGFMT " $@ +else + echo_compile_c = echo $(compile_c) + echo_compile_cc = echo $(compile_cc) + echo_link_app = echo $(link_app) + echo_link_so = echo $(link_so) + echo_ar_lib = echo $(ar_lib) + echo_moc_h = echo $(moc_h) + echo_msgfmt_po = echo $(msgfmt_po) +endif + +%.o: %.c + @$(cc_makedirs) + @$(echo_compile_c) + @$(compile_c) + @$(fixup_deps) + +%.o: %.cc + @$(cc_makedirs) + @$(echo_compile_cc) + @$(compile_cc) + @$(fixup_deps) + +%.o: %.cpp + @$(cc_makedirs) + @$(echo_compile_cc) + @$(compile_cc) + @$(fixup_deps) + + +%.so: %.o + @$(echo_link_so) + @$(link_so) + +%: %.o + @$(echo_link_app) + @$(link_app) + +%.moc : %.h + @$(echo_moc_h) + @$(moc_h) + +%.mo : %.po + @$(echo_msgfmt_po) + @$(msgfmt_po) + diff --git a/mk/Maintainer.mk b/mk/Maintainer.mk new file mode 100644 index 0000000..5bf9480 --- /dev/null +++ b/mk/Maintainer.mk @@ -0,0 +1,12 @@ +# just some maintainer stuff for me ... +######################################################################## + +make-sync-dir = $(HOME)/src/gnu-make + +.PHONY: sync +sync:: distclean + test -d $(make-sync-dir) + rm -f $(srcdir)/INSTALL $(srcdir)/mk/*.mk + cp -v $(make-sync-dir)/INSTALL $(srcdir)/. + cp -v $(make-sync-dir)/*.mk $(srcdir)/mk + chmod 444 $(srcdir)/INSTALL $(srcdir)/mk/*.mk diff --git a/mk/Variables.mk b/mk/Variables.mk new file mode 100644 index 0000000..930f824 --- /dev/null +++ b/mk/Variables.mk @@ -0,0 +1,46 @@ +# common variables ... +######################################################################## + +# directories +DESTDIR = +srcdir ?= . +prefix ?= /usr/local +bindir = $(DESTDIR)$(prefix)/bin +mandir = $(DESTDIR)$(prefix)/share/man +locdir = $(DESTDIR)$(prefix)/share/locale + +# package + version +empty := +space := $(empty) $(empty) +ifneq ($(wildcard $(srcdir)/VERSION),) + VERSION := $(shell cat $(srcdir)/VERSION) +else + VERSION := 42 +endif + +# programs +CC ?= gcc +CXX ?= g++ +MOC ?= $(if $(QTDIR),$(QTDIR)/bin/moc,moc) +INSTALL ?= install +INSTALL_BINARY := $(INSTALL) -s +INSTALL_SCRIPT := $(INSTALL) +INSTALL_DATA := $(INSTALL) -m 644 +INSTALL_DIR := $(INSTALL) -d + +# cflags +CFLAGS ?= -g -O2 +CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes \ + -Wpointer-arith -Wunused + +# add /usr/local to the search path if something is in there ... +ifneq ($(wildcard /usr/local/include/*.h),) + CFLAGS += -I/usr/local/include + LDFLAGS += -L/usr/local/$(LIB) +endif + +# fixup include path for $(srcdir) != "." +ifneq ($(srcdir),.) + CFLAGS += -I. -I$(srcdir) +endif + diff --git a/mk/utf8.tmp b/mk/utf8.tmp new file mode 100644 index 0000000..ccd8e44 --- /dev/null +++ b/mk/utf8.tmp @@ -0,0 +1,25 @@ +utf8.o: utf8.c /usr/include/stdio.h /usr/include/features.h \ + /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \ + /usr/lib64/gcc-lib/x86_64-suse-linux/3.3.3/include/stddef.h \ + /usr/include/bits/types.h /usr/include/bits/wordsize.h \ + /usr/include/bits/typesizes.h /usr/include/libio.h \ + /usr/include/_G_config.h /usr/include/wchar.h /usr/include/bits/wchar.h \ + /usr/include/gconv.h \ + /usr/lib64/gcc-lib/x86_64-suse-linux/3.3.3/include/stdarg.h \ + /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \ + /usr/include/bits/stdio.h /usr/include/stdlib.h \ + /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \ + /usr/include/endian.h /usr/include/bits/endian.h /usr/include/xlocale.h \ + /usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \ + /usr/include/bits/select.h /usr/include/bits/sigset.h \ + /usr/include/bits/time.h /usr/include/sys/sysmacros.h \ + /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h \ + /usr/include/alloca.h /usr/include/unistd.h \ + /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \ + /usr/include/bits/confname.h /usr/include/getopt.h \ + /usr/include/string.h /usr/include/bits/string.h \ + /usr/include/bits/string2.h /usr/include/errno.h \ + /usr/include/bits/errno.h /usr/include/linux/errno.h \ + /usr/include/asm/errno.h /usr/include/asm-x86_64/errno.h \ + /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \ + /usr/include/iconv.h list.h @@ -0,0 +1,289 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "readers.h" +#include "op.h" +#include "filter.h" + +/* ----------------------------------------------------------------------- */ +/* functions */ + +static char op_none_data; + +static void +op_flip_vert(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int line, void *data) +{ + char *scanline; + + scanline = src->data + (src->i.height - line - 1) * src->i.width * 3; + memcpy(dst,scanline,src->i.width*3); +} + +static void +op_flip_horz(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int line, void *data) +{ + char *scanline; + unsigned int i; + + scanline = src->data + (line+1) * src->i.width * 3; + for (i = 0; i < src->i.width; i++) { + scanline -= 3; + dst[0] = scanline[0]; + dst[1] = scanline[1]; + dst[2] = scanline[2]; + dst += 3; + } +} + +static void* +op_rotate_init(struct ida_image *src, struct ida_rect *rect, + struct ida_image_info *i, void *parm) +{ + *i = src->i; + i->height = src->i.width; + i->width = src->i.height; + i->dpi = src->i.dpi; + return &op_none_data; +} + +static void +op_rotate_cw(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int line, void *data) +{ + char *pix; + unsigned int i; + + pix = src->data + src->i.width * src->i.height * 3 + line * 3; + for (i = 0; i < src->i.height; i++) { + pix -= src->i.width * 3; + dst[0] = pix[0]; + dst[1] = pix[1]; + dst[2] = pix[2]; + dst += 3; + } +} + +static void +op_rotate_ccw(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int line, void *data) +{ + char *pix; + unsigned int i; + + pix = src->data + (src->i.width-line-1) * 3; + for (i = 0; i < src->i.height; i++) { + dst[0] = pix[0]; + dst[1] = pix[1]; + dst[2] = pix[2]; + pix += src->i.width * 3; + dst += 3; + } +} + +static void +op_invert(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int line, void *data) +{ + unsigned char *scanline; + int i; + + scanline = src->data + line * src->i.width * 3; + memcpy(dst,scanline,src->i.width * 3); + if (line < rect->y1 || line >= rect->y2) + return; + dst += 3*rect->x1; + scanline += 3*rect->x1; + for (i = rect->x1; i < rect->x2; i++) { + dst[0] = 255-scanline[0]; + dst[1] = 255-scanline[1]; + dst[2] = 255-scanline[2]; + scanline += 3; + dst += 3; + } +} + +static void* +op_crop_init(struct ida_image *src, struct ida_rect *rect, + struct ida_image_info *i, void *parm) +{ + if (rect->x2 - rect->x1 == src->i.width && + rect->y2 - rect->y1 == src->i.height) + return NULL; + *i = src->i; + i->width = rect->x2 - rect->x1; + i->height = rect->y2 - rect->y1; + return &op_none_data; +} + +static void +op_crop_work(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int line, void *data) +{ + unsigned char *scanline; + int i; + + scanline = src->data + (line+rect->y1) * src->i.width * 3 + rect->x1 * 3; + for (i = rect->x1; i < rect->x2; i++) { + dst[0] = scanline[0]; + dst[1] = scanline[1]; + dst[2] = scanline[2]; + scanline += 3; + dst += 3; + } +} + +static void* +op_autocrop_init(struct ida_image *src, struct ida_rect *unused, + struct ida_image_info *i, void *parm) +{ + static struct op_3x3_parm filter = { + f1: { -1, -1, -1 }, + f2: { -1, 8, -1 }, + f3: { -1, -1, -1 }, + }; + struct ida_rect rect; + struct ida_image img; + int x,y,limit; + unsigned char *line; + void *data; + + /* detect edges */ + rect.x1 = 0; + rect.x2 = src->i.width; + rect.y1 = 0; + rect.y2 = src->i.height; + data = desc_3x3.init(src, &rect, &img.i, &filter); + + img.data = malloc(img.i.width * img.i.height * 3); + for (y = 0; y < (int)img.i.height; y++) + desc_3x3.work(src, &rect, img.data+3*img.i.width*y, y, data); + desc_3x3.done(data); + limit = 64; + + /* y border */ + for (y = 0; y < (int)img.i.height; y++) { + line = img.data + img.i.width*y*3; + for (x = 0; x < (int)img.i.width; x++) + if (line[3*x+0] > limit || + line[3*x+1] > limit || + line[3*x+2] > limit) + break; + if (x != (int)img.i.width) + break; + } + rect.y1 = y; + for (y = (int)img.i.height-1; y > rect.y1; y--) { + line = img.data + img.i.width*y*3; + for (x = 0; x < (int)img.i.width; x++) + if (line[3*x+0] > limit || + line[3*x+1] > limit || + line[3*x+2] > limit) + break; + if (x != (int)img.i.width) + break; + } + rect.y2 = y+1; + + /* x border */ + for (x = 0; x < (int)img.i.width; x++) { + for (y = 0; y < (int)img.i.height; y++) { + line = img.data + (img.i.width*y+x) * 3; + if (line[0] > limit || + line[1] > limit || + line[2] > limit) + break; + } + if (y != (int)img.i.height) + break; + } + rect.x1 = x; + for (x = (int)img.i.width-1; x > rect.x1; x--) { + for (y = 0; y < (int)img.i.height; y++) { + line = img.data + (img.i.width*y+x) * 3; + if (line[0] > limit || + line[1] > limit || + line[2] > limit) + break; + } + if (y != (int)img.i.height) + break; + } + rect.x2 = x+1; + + free(img.data); + if (debug) + fprintf(stderr,"y: %d-%d/%d -- x: %d-%d/%d\n", + rect.y1, rect.y2, img.i.height, + rect.x1, rect.x2, img.i.width); + + if (0 == rect.x2 - rect.x1 || 0 == rect.y2 - rect.y1) + return NULL; + + *unused = rect; + *i = src->i; + i->width = rect.x2 - rect.x1; + i->height = rect.y2 - rect.y1; + return &op_none_data; +} + +/* ----------------------------------------------------------------------- */ + +static char op_none_data; + +void* op_none_init(struct ida_image *src, struct ida_rect *sel, + struct ida_image_info *i, void *parm) +{ + *i = src->i; + return &op_none_data; +} + +void op_none_done(void *data) {} +void op_free_done(void *data) { free(data); } + +/* ----------------------------------------------------------------------- */ + +struct ida_op desc_flip_vert = { + name: "flip-vert", + init: op_none_init, + work: op_flip_vert, + done: op_none_done, +}; +struct ida_op desc_flip_horz = { + name: "flip-horz", + init: op_none_init, + work: op_flip_horz, + done: op_none_done, +}; +struct ida_op desc_rotate_cw = { + name: "rotate-cw", + init: op_rotate_init, + work: op_rotate_cw, + done: op_none_done, +}; +struct ida_op desc_rotate_ccw = { + name: "rotate-ccw", + init: op_rotate_init, + work: op_rotate_ccw, + done: op_none_done, +}; +struct ida_op desc_invert = { + name: "invert", + init: op_none_init, + work: op_invert, + done: op_none_done, +}; +struct ida_op desc_crop = { + name: "crop", + init: op_crop_init, + work: op_crop_work, + done: op_none_done, +}; +struct ida_op desc_autocrop = { + name: "autocrop", + init: op_autocrop_init, + work: op_crop_work, + done: op_none_done, +}; @@ -0,0 +1,7 @@ +extern struct ida_op desc_flip_vert; +extern struct ida_op desc_flip_horz; +extern struct ida_op desc_rotate_cw; +extern struct ida_op desc_rotate_ccw; +extern struct ida_op desc_invert; +extern struct ida_op desc_crop; +extern struct ida_op desc_autocrop; diff --git a/parseconfig.c b/parseconfig.c new file mode 100644 index 0000000..8c20169 --- /dev/null +++ b/parseconfig.c @@ -0,0 +1,857 @@ +/* + * config file parser + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include <sys/stat.h> +#include <sys/types.h> + +#include "list.h" +#include "parseconfig.h" + +struct cfg_entry { + struct list_head next; + char *name; + unsigned int flags; + char *value; +}; + +struct cfg_section { + struct list_head next; + char *name; + unsigned int flags; + struct list_head entries; +}; + +struct cfg_domain { + struct list_head next; + char *name; + struct list_head sections; +}; + +LIST_HEAD(domains); + +/* ------------------------------------------------------------------------ */ +/* prehistoric libc ;) */ + +#ifndef HAVE_STRCASESTR +char* strcasestr(char *haystack, char *needle) +{ + int hlen = strlen(haystack); + int nlen = strlen(needle); + int offset; + + for (offset = 0; offset <= hlen - nlen; offset++) + if (0 == strncasecmp(haystack+offset,needle,nlen)) + return haystack+offset; + return NULL; +} +#endif + +/* ------------------------------------------------------------------------ */ +/* internal stuff */ + +static struct cfg_domain *d_last; +static struct cfg_section *s_last; +static struct cfg_entry *e_last; + +static struct cfg_domain* +cfg_find_domain(char *dname) +{ + struct list_head *item; + struct cfg_domain *domain; + + if (d_last && 0 == strcmp(d_last->name,dname)) + return d_last; + d_last = NULL; + s_last = NULL; + e_last = NULL; + + list_for_each(item,&domains) { + domain = list_entry(item, struct cfg_domain, next); + if (0 == strcasecmp(domain->name,dname)) { + d_last = domain; + return domain; + } + } + return NULL; +} + +static struct cfg_domain* +cfg_get_domain(char *dname) +{ + struct cfg_domain *domain; + + domain = cfg_find_domain(dname); + if (NULL == domain) { + domain = malloc(sizeof(*domain)); + memset(domain,0,sizeof(*domain)); + domain->name = strdup(dname); + INIT_LIST_HEAD(&domain->sections); + list_add_tail(&domain->next,&domains); + } + d_last = domain; + return domain; +} + +static struct cfg_section* +cfg_find_section(struct cfg_domain *domain, char *sname) +{ + struct list_head *item; + struct cfg_section *section; + + if (s_last && 0 == strcmp(s_last->name,sname)) + return s_last; + s_last = NULL; + e_last = NULL; + + list_for_each(item,&domain->sections) { + section = list_entry(item, struct cfg_section, next); + if (0 == strcasecmp(section->name,sname)) { + s_last = section; + return section; + } + } + return NULL; +} + +static struct cfg_section* +cfg_get_section(struct cfg_domain *domain, char *sname) +{ + struct cfg_section *section; + + section = cfg_find_section(domain,sname); + if (NULL == section) { + section = malloc(sizeof(*section)); + memset(section,0,sizeof(*section)); + section->name = strdup(sname); + INIT_LIST_HEAD(§ion->entries); + list_add_tail(§ion->next,&domain->sections); + } + s_last = section; + return section; +} + +static struct cfg_entry* +cfg_find_entry(struct cfg_section *section, char *ename) +{ + struct list_head *item; + struct cfg_entry *entry; + + if (e_last && 0 == strcmp(e_last->name,ename)) + return e_last; + e_last = NULL; + + list_for_each(item,§ion->entries) { + entry = list_entry(item, struct cfg_entry, next); + if (0 == strcasecmp(entry->name,ename)) { + e_last = entry; + return entry; + } + } + return NULL; +} + +static struct cfg_entry* +cfg_get_entry(struct cfg_section *section, char *ename) +{ + struct cfg_entry *entry; + + entry = cfg_find_entry(section,ename); + if (NULL == entry) { + entry = malloc(sizeof(*entry)); + memset(entry,0,sizeof(*entry)); + entry->name = strdup(ename); + list_add_tail(&entry->next,§ion->entries); + } + e_last = entry; + return entry; +} + +static void +cfg_set_entry(struct cfg_section *section, char *name, const char *value) +{ + struct cfg_entry *entry; + + entry = cfg_get_entry(section,name); + if (entry->value) + free(entry->value); + entry->value = strdup(value); +} + +static struct cfg_section* +cfg_get_sec(char *dname, char *sname) +{ + struct cfg_domain *domain; + + domain = cfg_find_domain(dname); + if (NULL == domain) + return NULL; + return cfg_find_section(domain,sname); +} + +static struct cfg_entry* +cfg_get_ent(char *dname, char *sname, char *ename) +{ + struct cfg_section *section; + + section = cfg_get_sec(dname,sname); + if (NULL == section) + return NULL; + return cfg_find_entry(section,ename); +} + +/* ------------------------------------------------------------------------ */ +/* import / add / del config data */ + +int +cfg_parse_file(char *dname, char *filename) +{ + struct cfg_domain *domain = NULL; + struct cfg_section *section = NULL; + char line[256],tag[64],value[192]; + FILE *fp; + int nr; + + if (NULL == (fp = fopen(filename,"r"))) + return -1; + + nr = 0; + domain = cfg_get_domain(dname); + while (NULL != fgets(line,255,fp)) { + nr++; + if (1 == sscanf(line,"# include \"%[^\"]\"",value)) { + /* includes */ + char *h,*inc; + inc = malloc(strlen(filename)+strlen(value)); + strcpy(inc,filename); + h = strrchr(inc,'/'); + if (h) + h++; + else + h = inc; + strcpy(h,value); + cfg_parse_file(dname,inc); + free(inc); + continue; + } + if (line[0] == '\n' || line[0] == '#' || line[0] == '%') + continue; + if (1 == sscanf(line,"[%99[^]]]",value)) { + /* [section] */ + section = cfg_get_section(domain,value); + } else if (2 == sscanf(line," %63[^= ] = %191[^\n]",tag,value)) { + /* foo = bar */ + if (NULL == section) { + fprintf(stderr,"%s:%d: error: no section\n",filename,nr); + } else { + char *c = value + strlen(value)-1; + while (c > value && (*c == ' ' || *c == '\t')) + *(c--) = 0; + cfg_set_entry(section,tag,value); + } + } else { + /* Huh ? */ + fprintf(stderr,"%s:%d: syntax error\n",filename,nr); + } + } + fclose(fp); + return 0; +} + +void +cfg_set_str(char *dname, char *sname, char *ename, const char *value) +{ + struct cfg_domain *domain = NULL; + struct cfg_section *section = NULL; + + domain = cfg_get_domain(dname); + section = cfg_get_section(domain,sname); + cfg_set_entry(section,ename,value); +} + +void +cfg_set_int(char *dname, char *sname, char *ename, int value) +{ + char str[32]; + + snprintf(str,sizeof(str),"%d",value); + cfg_set_str(dname,sname,ename,str); +} + +void +cfg_set_bool(char *dname, char *sname, char *ename, int value) +{ + cfg_set_str(dname,sname,ename, value ? "true" : "false"); +} + +void +cfg_del_section(char *dname, char *sname) +{ + struct cfg_section *section; + struct cfg_entry *entry; + + section= cfg_get_sec(dname,sname); + if (!section) + return; + list_del(§ion->next); + while (!list_empty(§ion->entries)) { + entry = list_entry(section->entries.next, struct cfg_entry, next); + list_del(&entry->next); + free(entry->name); + free(entry->value); + free(entry); + } + s_last = NULL; + e_last = NULL; + free(section->name); + free(section); +} + +void +cfg_del_entry(char *dname, char *sname, char *ename) +{ + struct cfg_entry *entry; + + entry = cfg_get_ent(dname,sname,ename); + if (!entry) + return; + e_last = NULL; + list_del(&entry->next); + free(entry->name); + free(entry->value); + free(entry); +} + +void +cfg_parse_cmdline(int *argc, char **argv, struct cfg_cmdline *opt) +{ + int i,j,o,shift,len; + + for (i = 1; i < *argc;) { + if (argv[i][0] != '-') { + i++; + continue; + } + + for (shift = 0, o = 0; + 0 == shift && opt[o].cmdline != NULL; + o++) { + len = strlen(opt[o].cmdline); + if (opt[o].yesno && 0 == strcmp(argv[i]+1,opt[o].cmdline)) { + /* yesno: -foo */ + cfg_set_bool(opt[o].option.domain, + opt[o].option.section, + opt[o].option.entry, + 1); + shift = 1; + + } else if (opt[o].yesno && + 0 == strncmp(argv[i]+1,"no",2) && + 0 == strcmp(argv[i]+3,opt[o].cmdline)) { + /* yesno: -nofoo */ + cfg_set_bool(opt[o].option.domain, + opt[o].option.section, + opt[o].option.entry, + 0); + shift = 1; + + } else if (opt[o].needsarg && + 0 == strcmp(argv[i]+1,opt[o].cmdline) && + i+1 < *argc) { + /* arg: -foo bar */ + cfg_set_str(opt[o].option.domain, + opt[o].option.section, + opt[o].option.entry, + argv[i+1]); + shift = 2; + + } else if (opt[o].needsarg && + 0 == strncmp(argv[i]+1,opt[o].cmdline,len) && + 0 == strncmp(argv[i]+1+len,"=",1)) { + /* arg: -foo=bar */ + cfg_set_str(opt[o].option.domain, + opt[o].option.section, + opt[o].option.entry, + argv[i]+2+len); + shift = 1; + + } else if (opt[o].value && + 0 == strcmp(argv[i]+1,opt[o].cmdline)) { + /* -foo sets some fixed value */ + cfg_set_str(opt[o].option.domain, + opt[o].option.section, + opt[o].option.entry, + opt[o].value); + shift = 1; + } + } + + if (shift) { + /* remove processed args */ + for (j = i; j < *argc+1-shift; j++) + argv[j] = argv[j+shift]; + (*argc) -= shift; + } else + i++; + } +} + +void +cfg_help_cmdline(struct cfg_cmdline *opt, int w1, int w2, int w3) +{ + char *val; + int o,len; + + for (o = 0; opt[o].cmdline != NULL; o++) { + fprintf(stderr,"%*s",w1,""); + if (opt[o].yesno) { + len = fprintf(stderr,"-(no)%s ",opt[o].cmdline); + } else if (opt[o].needsarg) { + len = fprintf(stderr,"-%s <arg> ",opt[o].cmdline); + } else { + len = fprintf(stderr,"-%s ",opt[o].cmdline); + } + if (len < w2) + fprintf(stderr,"%*s",w2-len,""); + + len = fprintf(stderr,"%s ",opt[o].desc); + if (len < w3) + fprintf(stderr,"%*s",w3-len,""); + + val = cfg_get_str(opt[o].option.domain, + opt[o].option.section, + opt[o].option.entry); + if (val) + fprintf(stderr,"[%s]",val); + fprintf(stderr,"\n"); + } +} + +/* ------------------------------------------------------------------------ */ +/* export config data */ + +static int cfg_mkdir(char *filename) +{ + char *h; + int rc; + + h = strrchr(filename,'/'); + if (!h || h == filename) + return -1; + *h = '\0'; + rc = mkdir(filename,0777); + if (-1 == rc && ENOENT == errno) { + cfg_mkdir(filename); + rc = mkdir(filename,0777); + } + if (-1 == rc) + fprintf(stderr,"mkdir(%s): %s\n",filename,strerror(errno)); + *h = '/'; + return rc; +} + +int +cfg_write_file(char *dname, char *filename) +{ + struct list_head *item1,*item2; + struct cfg_domain *domain; + struct cfg_section *section; + struct cfg_entry *entry; + char *bfile, *tfile; + int len; + FILE *fp; + + len = strlen(filename)+10; + bfile = malloc(len); + tfile = malloc(len); + sprintf(bfile,"%s~",filename); + sprintf(tfile,"%s.$$$",filename); + + fp = fopen(tfile,"w"); + if (NULL == fp && ENOENT == errno) { + cfg_mkdir(tfile); + fp = fopen(tfile,"w"); + } + if (NULL == fp) { + fprintf(stderr,"open(%s): %s\n",tfile,strerror(errno)); + return -1; + } + + domain = cfg_find_domain(dname); + if (NULL != domain) { + list_for_each(item1,&domain->sections) { + section = list_entry(item1, struct cfg_section, next); + fprintf(fp,"[%s]\n",section->name); + list_for_each(item2,§ion->entries) { + entry = list_entry(item2, struct cfg_entry, next); + fprintf(fp,"%s = %s\n",entry->name,entry->value); + } + fprintf(fp,"\n"); + } + } + fclose(fp); + + if (-1 == unlink(bfile) && ENOENT != errno) { + fprintf(stderr,"unlink(%s): %s\n",bfile,strerror(errno)); + return -1; + } + if (-1 == rename(filename,bfile) && ENOENT != errno) { + fprintf(stderr,"rename(%s,%s): %s\n",filename,bfile,strerror(errno)); + return -1; + } + if (-1 == rename(tfile,filename)) { + fprintf(stderr,"rename(%s,%s): %s\n",tfile,filename,strerror(errno)); + return -1; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ +/* list / search config data */ + +char* +cfg_sections_first(char *dname) +{ + struct list_head *item; + struct cfg_domain *domain; + struct cfg_section *section; + + domain = cfg_find_domain(dname); + if (NULL == domain) + return NULL; + + item = &domain->sections; + if (item->next == &domain->sections) + return NULL; + section = list_entry(item->next, struct cfg_section, next); + s_last = section; + e_last = NULL; + return section->name; +} + +char* +cfg_sections_next(char *dname, char *current) +{ + struct list_head *item; + struct cfg_domain *domain; + struct cfg_section *section; + + domain = cfg_find_domain(dname); + if (NULL == domain) + return NULL; + section = cfg_find_section(domain,current); + if (NULL == section) + return NULL; + item = §ion->next; + + if (item->next == &domain->sections) + return NULL; + section = list_entry(item->next, struct cfg_section, next); + s_last = section; + e_last = NULL; + return section->name; +} + +char* +cfg_sections_prev(char *dname, char *current) +{ + struct list_head *item; + struct cfg_domain *domain; + struct cfg_section *section; + + domain = cfg_find_domain(dname); + if (NULL == domain) + return NULL; + section = cfg_find_section(domain,current); + if (NULL == section) + return NULL; + item = §ion->next; + + if (item->prev == &domain->sections) + return NULL; + section = list_entry(item->prev, struct cfg_section, next); + s_last = section; + e_last = NULL; + return section->name; +} + +int cfg_sections_count(char *dname) +{ + struct list_head *item; + struct cfg_domain *domain; + int count = 0; + + domain = cfg_find_domain(dname); + if (NULL != domain) + list_for_each(item,&domain->sections) + count++; + return count; +} + +char* cfg_sections_index(char *dname, int i) +{ + struct list_head *item; + struct cfg_domain *domain; + struct cfg_section *section; + int count = 0; + + domain = cfg_find_domain(dname); + if (NULL == domain) + return NULL; + + list_for_each(item,&domain->sections) { + if (i == count) { + section = list_entry(item, struct cfg_section, next); + s_last = section; + e_last = NULL; + return section->name; + } + count++; + } + return NULL; +} + +char* +cfg_entries_first(char *dname, char *sname) +{ + struct list_head *item; + struct cfg_section *section; + struct cfg_entry *entry; + + section = cfg_get_sec(dname,sname); + if (NULL == section) + return NULL; + + item = §ion->entries; + if (item->next == §ion->entries) + return NULL; + entry = list_entry(item->next, struct cfg_entry, next); + e_last = entry; + return entry->name; +} + +char* +cfg_entries_next(char *dname, char *sname, char *current) +{ + struct list_head *item; + struct cfg_section *section; + struct cfg_entry *entry; + + section = cfg_get_sec(dname,sname); + if (NULL == section) + return NULL; + entry = cfg_find_entry(section,current); + if (NULL == entry) + return NULL; + item = &entry->next; + + if (item->next == §ion->entries) + return NULL; + entry = list_entry(item->next, struct cfg_entry, next); + e_last = entry; + return entry->name; +} + +char* +cfg_entries_prev(char *dname, char *sname, char *current) +{ + struct list_head *item; + struct cfg_section *section; + struct cfg_entry *entry; + + section = cfg_get_sec(dname,sname); + if (NULL == section) + return NULL; + entry = cfg_find_entry(section,current); + if (NULL == entry) + return NULL; + item = &entry->next; + + if (item->prev == §ion->entries) + return NULL; + entry = list_entry(item->prev, struct cfg_entry, next); + e_last = entry; + return entry->name; +} + +int cfg_entries_count(char *dname, char *sname) +{ + struct list_head *item; + struct cfg_section *section; + int count = 0; + + section = cfg_get_sec(dname,sname); + if (NULL != section) + list_for_each(item,§ion->entries) + count++; + return count; +} + +char* cfg_entries_index(char *dname, char *sname, int i) +{ + struct list_head *item; + struct cfg_section *section; + struct cfg_entry *entry; + int count = 0; + + section = cfg_get_sec(dname,sname); + if (NULL == section) + return NULL; + + list_for_each(item,§ion->entries) { + if (i == count) { + entry = list_entry(item, struct cfg_entry, next); + e_last = entry; + return entry->name; + } + count++; + } + return NULL; +} + +char* cfg_search(char *dname, char *sname, char *ename, char *value) +{ + struct list_head *item1,*item2; + struct cfg_domain *domain; + struct cfg_section *section; + struct cfg_entry *entry; + + domain = cfg_find_domain(dname); + if (NULL == domain) + return NULL; + list_for_each(item1,&domain->sections) { + section = list_entry(item1, struct cfg_section, next); + if (sname && 0 != strcasecmp(section->name,sname)) + continue; + if (!ename) + return section->name; + list_for_each(item2,§ion->entries) { + entry = list_entry(item2, struct cfg_entry, next); + if (0 != strcasecmp(entry->name,ename)) + continue; + if (0 == strcasecmp(entry->value,value)) + return section->name; + } + } + return NULL; +} + +/* ------------------------------------------------------------------------ */ +/* get config data */ + +char* +cfg_get_str(char *dname, char *sname, char *ename) +{ + struct cfg_entry *entry; + + entry = cfg_get_ent(dname, sname, ename); + if (NULL == entry) + return NULL; + return entry->value; +} + +int +cfg_get_int(char *dname, char *sname, char *ename, int def) +{ + char *val; + + val = cfg_get_str(dname,sname,ename); + if (NULL == val) + return def; + return atoi(val); +} + +int +cfg_get_signed_int(char *dname, char *sname, char *ename, unsigned int def) +{ + char *val; + + val = cfg_get_str(dname,sname,ename); + if (NULL == val) + return def; + return atoi(val); +} + +float +cfg_get_float(char *dname, char *sname, char *ename) +{ + char *val; + + val = cfg_get_str(dname,sname,ename); + if (NULL == val) + return -1; + return atof(val); +} + +int +cfg_get_bool(char *dname, char *sname, char *ename, int def) +{ + static char *yes[] = { "true", "yes", "on", "1" }; + char *val; + int i; + int retval = 0; + + val = cfg_get_str(dname,sname,ename); + if (NULL == val) + return def; + for (i = 0; i < sizeof(yes)/sizeof(yes[0]); i++) + if (0 == strcasecmp(val,yes[i])) + retval = 1; + return retval; +} + +/* ------------------------------------------------------------------------ */ +/* get/set flags */ + +int cfg_get_sflags(char *dname, char *sname) +{ + struct cfg_section *section; + + section = cfg_get_sec(dname, sname); + if (NULL == section) + return 0; + return section->flags; +} + +int cfg_get_eflags(char *dname, char *sname, char *ename) +{ + struct cfg_entry *entry; + + entry = cfg_get_ent(dname, sname, ename); + if (NULL == entry) + return 0; + return entry->flags; +} + +int cfg_set_sflags(char *dname, char *sname, + unsigned int mask, unsigned int bits) +{ + struct cfg_section *section; + + section = cfg_get_sec(dname, sname); + if (NULL == section) + return 0; + section->flags &= ~mask; + section->flags |= bits; + return section->flags; +} + +int cfg_set_eflags(char *dname, char *sname, char *ename, + unsigned int mask, unsigned int bits) +{ + struct cfg_entry *entry; + + entry = cfg_get_ent(dname, sname, ename); + if (NULL == entry) + return 0; + entry->flags &= ~mask; + entry->flags |= bits; + return entry->flags; +} diff --git a/parseconfig.h b/parseconfig.h new file mode 100644 index 0000000..90abbfe --- /dev/null +++ b/parseconfig.h @@ -0,0 +1,68 @@ +#ifndef HAVE_STRCASESTR +extern char* strcasestr(char *haystack, char *needle); +#endif + +/* config options */ +struct cfg_option { + char *domain; + char *section; + char *entry; +}; +struct cfg_cmdline { + char *cmdline; + struct cfg_option option; + char *value; + char *desc; + int needsarg:1; + int yesno:1; +}; +void cfg_parse_cmdline(int *argc, char **argv, struct cfg_cmdline *opt); +void cfg_help_cmdline(struct cfg_cmdline *opt, int w1, int w2, int w3); + +/* file I/O */ +int cfg_parse_file(char *dname, char *filename); +int cfg_write_file(char *dname, char *filename); + +/* update */ +void cfg_set_str(char *dname, char *sname, char *ename, const char *value); +void cfg_set_int(char *dname, char *sname, char *ename, int value); +void cfg_set_bool(char *dname, char *sname, char *ename, int value); + +void cfg_del_section(char *dname, char *sname); +void cfg_del_entry(char *dname, char *sname, char *ename); + +/* search */ +char* cfg_sections_first(char *dname); +char* cfg_sections_next(char *dname, char *current); +char* cfg_sections_prev(char *dname, char *current); +int cfg_sections_count(char *dname); +char* cfg_sections_index(char *dname, int i); + +char* cfg_entries_first(char *dname, char *sname); +char* cfg_entries_next(char *dname, char *sname, char *current); +char* cfg_entries_prev(char *dname, char *sname, char *current); +int cfg_entries_count(char *dname, char *sname); +char* cfg_entries_index(char *dname, char *sname, int i); + +#define cfg_sections_for_each(dname, item) \ + for (item = cfg_sections_first(dname); NULL != item; \ + item = cfg_sections_next(dname,item)) + +char* cfg_search(char *dname, char *sname, char *ename, char *value); + +/* read */ +char* cfg_get_str(char *dname, char *sname, char *ename); +int cfg_get_int(char *dname, char *sname, char *ename, + int def); +int cfg_get_signed_int(char *dname, char *sname, char *ename, + unsigned int def); +float cfg_get_float(char *dname, char *sname, char *ename); +int cfg_get_bool(char *dname, char *sname, char *ename, + int def); + +int cfg_get_sflags(char *dname, char *sname); +int cfg_get_eflags(char *dname, char *sname, char *ename); +int cfg_set_sflags(char *dname, char *sname, + unsigned int mask, unsigned int bits); +int cfg_set_eflags(char *dname, char *sname, char *ename, + unsigned int mask, unsigned int bits); diff --git a/rd/Makefile b/rd/Makefile new file mode 100644 index 0000000..4e33e30 --- /dev/null +++ b/rd/Makefile @@ -0,0 +1,2 @@ +default: + cd ..; $(MAKE) diff --git a/rd/magick.c b/rd/magick.c new file mode 100644 index 0000000..f17e5e3 --- /dev/null +++ b/rd/magick.c @@ -0,0 +1,85 @@ +#include <stdio.h> +#include <stdlib.h> +#include <magick/api.h> + +#include "loader.h" +#include "viewer.h" + +extern char *binary; + +static int first = 1; +static ExceptionInfo exception; + +struct magick_state { + ImageInfo *image_info; + Image *image; +}; + +static void* +magick_init(FILE *fp, char *filename, int *width, int *height) +{ + struct magick_state *h; + + /* libmagick wants a filename */ + fclose(fp); + + if (first) { + /* init library first time */ + MagickIncarnate(binary); + GetExceptionInfo(&exception); + first = 0; + } + + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + + h->image_info=CloneImageInfo(NULL); + strcpy(h->image_info->filename,filename); + h->image = ReadImage(h->image_info,&exception); + if (NULL == h->image) { + MagickError(exception.severity,exception.reason,exception.description); + goto oops; + } + + *width = h->image->rows; + *height = h->image->columns; + return h; + + oops: + if (h->image) + DestroyImage(h->image); + if (h->image_info) + DestroyImageInfo(h->image_info); + free(h); + return NULL; +} + +static void +magick_read(unsigned char *dst, int line, void *data) +{ + struct magick_state *h = data; + DispatchImage (h->image,0,line,h->image->columns, 1, + "RGB", 0, data); +} + +static void +magick_done(void *data) +{ + struct magick_state *h = data; + + DestroyImageInfo(h->image_info); + DestroyImage(h->image); + free(h); +} + +static struct ida_loader magick_loader = { + name: "libmagick", + init: magick_init, + read: magick_read, + done: magick_done, +}; + +static void __init init_rd(void) +{ + load_register(&magick_loader); +} diff --git a/rd/read-bmp.c b/rd/read-bmp.c new file mode 100644 index 0000000..30b5574 --- /dev/null +++ b/rd/read-bmp.c @@ -0,0 +1,216 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#ifdef HAVE_ENDIAN_H +# include <endian.h> +#endif + +#include "readers.h" + +/* ---------------------------------------------------------------------- */ + +typedef unsigned int uint32; +typedef unsigned short uint16; + +/* bitmap files are little endian */ +#if BYTE_ORDER == LITTLE_ENDIAN +# define le16_to_cpu(x) (x) +# define le32_to_cpu(x) (x) +#elif BYTE_ORDER == BIG_ENDIAN +# define le16_to_cpu(x) (((x>>8) & 0x00ff) |\ + ((x<<8) & 0xff00)) +# define le32_to_cpu(x) (((x>>24) & 0x000000ff) |\ + ((x>>8) & 0x0000ff00) |\ + ((x<<8) & 0x00ff0000) |\ + ((x<<24) & 0xff000000)) +#else +# error "Oops: unknown byte order" +#endif + +/* ---------------------------------------------------------------------- */ +/* load */ + +struct bmp_hdr { + uint32 foobar; + + uint32 size; /* == BitMapInfoHeader */ + uint32 width; + uint32 height; + uint16 planes; + uint16 bit_cnt; + char compression[4]; + uint32 image_size; + uint32 xpels_meter; + uint32 ypels_meter; + uint32 num_colors; /* used colors */ + uint32 imp_colors; /* important colors */ + /* may be more for some codecs */ +}; + +struct bmp_cmap { + unsigned char blue; + unsigned char green; + unsigned char red; + unsigned char unused; +}; + +struct bmp_state { + struct bmp_hdr hdr; + struct bmp_cmap cmap[256]; + FILE *fp; +}; + +static void* +bmp_init(FILE *fp, char *filename, unsigned int page, + struct ida_image_info *i, int thumbnail) +{ + struct bmp_state *h; + + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + h->fp = fp; + + fseek(fp,10,SEEK_SET); + fread(&h->hdr,sizeof(struct bmp_hdr),1,fp); + +#if BYTE_ORDER == BIG_ENDIAN + h->hdr.foobar = le32_to_cpu(h->hdr.foobar); + h->hdr.size = le32_to_cpu(h->hdr.size); + h->hdr.width = le32_to_cpu(h->hdr.width); + h->hdr.height = le32_to_cpu(h->hdr.height); + + h->hdr.planes = le16_to_cpu(h->hdr.planes); + h->hdr.bit_cnt = le16_to_cpu(h->hdr.bit_cnt); + + h->hdr.image_size = le32_to_cpu(h->hdr.image_size); + h->hdr.xpels_meter = le32_to_cpu(h->hdr.xpels_meter); + h->hdr.ypels_meter = le32_to_cpu(h->hdr.ypels_meter); + h->hdr.num_colors = le32_to_cpu(h->hdr.num_colors); + h->hdr.imp_colors = le32_to_cpu(h->hdr.imp_colors); +#endif + + if (debug) + fprintf(stderr,"bmp: hdr=%d size=%dx%d planes=%d" + " bits=%d size=%d res=%dx%d colors=%d/%d | %d\n", + h->hdr.size,h->hdr.width,h->hdr.height, + h->hdr.planes,h->hdr.bit_cnt,h->hdr.image_size, + h->hdr.xpels_meter,h->hdr.ypels_meter, + h->hdr.num_colors,h->hdr.imp_colors,h->hdr.foobar); + if (h->hdr.bit_cnt != 1 && + h->hdr.bit_cnt != 4 && + h->hdr.bit_cnt != 8 && + h->hdr.bit_cnt != 24) { + fprintf(stderr,"bmp: can't handle depth [%d]\n",h->hdr.bit_cnt); + goto oops; + } + if (h->hdr.compression[0] || h->hdr.compression[1] || + h->hdr.compression[2] || h->hdr.compression[3]) { + fprintf(stderr,"bmp: can't handle compressed bitmaps [%c%c%c%c]\n", + h->hdr.compression[0], + h->hdr.compression[1], + h->hdr.compression[2], + h->hdr.compression[3]); + goto oops; + } + + if (0 == h->hdr.num_colors && h->hdr.bit_cnt <= 8) + h->hdr.num_colors = (1 << h->hdr.bit_cnt); + if (h->hdr.num_colors > 256) + h->hdr.num_colors = 256; + if (h->hdr.num_colors) { + fseek(fp,14+h->hdr.size,SEEK_SET); + fread(&h->cmap,sizeof(struct bmp_cmap),h->hdr.num_colors,fp); + } + + i->width = h->hdr.width; + i->height = h->hdr.height; + if (h->hdr.xpels_meter) + i->dpi = res_m_to_inch(h->hdr.xpels_meter); + i->npages = 1; + return h; + + oops: + free(h); + return NULL; +} + +static void +bmp_read(unsigned char *dst, unsigned int line, void *data) +{ + struct bmp_state *h = data; + unsigned int ll,y,x,pixel,byte = 0; + + ll = (((h->hdr.width * h->hdr.bit_cnt + 31) & ~0x1f) >> 3); + y = h->hdr.height - line - 1; + fseek(h->fp,h->hdr.foobar + y * ll,SEEK_SET); + + switch (h->hdr.bit_cnt) { + case 1: + for (x = 0; x < h->hdr.width; x++) { + if (0 == (x & 0x07)) + byte = fgetc(h->fp); + pixel = byte & (0x80 >> (x & 0x07)) ? 1 : 0; + *(dst++) = h->cmap[pixel].red; + *(dst++) = h->cmap[pixel].green; + *(dst++) = h->cmap[pixel].blue; + } + break; + case 4: + for (x = 0; x < h->hdr.width; x++) { + if (x & 1) { + pixel = byte & 0xf; + } else { + byte = fgetc(h->fp); + pixel = byte >> 4; + } + *(dst++) = h->cmap[pixel].red; + *(dst++) = h->cmap[pixel].green; + *(dst++) = h->cmap[pixel].blue; + } + break; + case 8: + for (x = 0; x < h->hdr.width; x++) { + pixel = fgetc(h->fp); + *(dst++) = h->cmap[pixel].red; + *(dst++) = h->cmap[pixel].green; + *(dst++) = h->cmap[pixel].blue; + } + break; + case 24: + for (x = 0; x < h->hdr.width; x++) { + dst[2] = fgetc(h->fp); + dst[1] = fgetc(h->fp); + dst[0] = fgetc(h->fp); + dst += 3; + } + break; + default: + memset(dst,128,h->hdr.width*3); + break; + } +} + +static void +bmp_done(void *data) +{ + struct bmp_state *h = data; + + fclose(h->fp); + free(h); +} + +static struct ida_loader bmp_loader = { + magic: "BM", + moff: 0, + mlen: 2, + name: "bmp", + init: bmp_init, + read: bmp_read, + done: bmp_done, +}; + +static void __init init_rd(void) +{ + load_register(&bmp_loader); +} diff --git a/rd/read-gif.c b/rd/read-gif.c new file mode 100644 index 0000000..b841dc7 --- /dev/null +++ b/rd/read-gif.c @@ -0,0 +1,220 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <gif_lib.h> + +#include "readers.h" + +struct gif_state { + FILE *infile; + GifFileType *gif; + GifPixelType *row; + GifPixelType *il; + int w,h; +}; + +static GifRecordType +gif_fileread(struct gif_state *h) +{ + GifRecordType RecordType; + GifByteType *Extension; + int ExtCode, rc; + char *type; + + for (;;) { + if (GIF_ERROR == DGifGetRecordType(h->gif,&RecordType)) { + if (debug) + fprintf(stderr,"gif: DGifGetRecordType failed\n"); + PrintGifError(); + return -1; + } + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (debug) + fprintf(stderr,"gif: IMAGE_DESC_RECORD_TYPE found\n"); + return RecordType; + case EXTENSION_RECORD_TYPE: + if (debug) + fprintf(stderr,"gif: EXTENSION_RECORD_TYPE found\n"); + for (rc = DGifGetExtension(h->gif,&ExtCode,&Extension); + NULL != Extension; + rc = DGifGetExtensionNext(h->gif,&Extension)) { + if (rc == GIF_ERROR) { + if (debug) + fprintf(stderr,"gif: DGifGetExtension failed\n"); + PrintGifError(); + return -1; + } + if (debug) { + switch (ExtCode) { + case COMMENT_EXT_FUNC_CODE: type="comment"; break; + case GRAPHICS_EXT_FUNC_CODE: type="graphics"; break; + case PLAINTEXT_EXT_FUNC_CODE: type="plaintext"; break; + case APPLICATION_EXT_FUNC_CODE: type="appl"; break; + default: type="???"; break; + } + fprintf(stderr,"gif: extcode=0x%x [%s]\n",ExtCode,type); + } + } + break; + case TERMINATE_RECORD_TYPE: + if (debug) + fprintf(stderr,"gif: TERMINATE_RECORD_TYPE found\n"); + return RecordType; + default: + if (debug) + fprintf(stderr,"gif: unknown record type [%d]\n",RecordType); + return -1; + } + } +} + +#if 0 +static void +gif_skipimage(struct gif_state *h) +{ + unsigned char *line; + int i; + + if (debug) + fprintf(stderr,"gif: skipping image record ...\n"); + DGifGetImageDesc(h->gif); + line = malloc(h->gif->SWidth); + for (i = 0; i < h->gif->SHeight; i++) + DGifGetLine(h->gif, line, h->gif->SWidth); + free(line); +} +#endif + +static void* +gif_init(FILE *fp, char *filename, unsigned int page, + struct ida_image_info *info, int thumbnail) +{ + struct gif_state *h; + GifRecordType RecordType; + int i, image = 0; + + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + + h->infile = fp; + h->gif = DGifOpenFileHandle(fileno(fp)); + h->row = malloc(h->gif->SWidth * sizeof(GifPixelType)); + + while (0 == image) { + RecordType = gif_fileread(h); + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (GIF_ERROR == DGifGetImageDesc(h->gif)) { + if (debug) + fprintf(stderr,"gif: DGifGetImageDesc failed\n"); + PrintGifError(); + } + if (NULL == h->gif->SColorMap && + NULL == h->gif->Image.ColorMap) { + if (debug) + fprintf(stderr,"gif: oops: no colormap found\n"); + goto oops; + } +#if 0 + info->width = h->w = h->gif->SWidth; + info->height = h->h = h->gif->SHeight; +#else + info->width = h->w = h->gif->Image.Width; + info->height = h->h = h->gif->Image.Height; +#endif + info->npages = 1; + image = 1; + if (debug) + fprintf(stderr,"gif: reading image record ...\n"); + if (h->gif->Image.Interlace) { + if (debug) + fprintf(stderr,"gif: interlaced\n"); + h->il = malloc(h->w * h->h * sizeof(GifPixelType)); + for (i = 0; i < h->h; i += 8) + DGifGetLine(h->gif, h->il + h->w*i,h->w); + for (i = 4; i < h->gif->SHeight; i += 8) + DGifGetLine(h->gif, h->il + h->w*i,h->w); + for (i = 2; i < h->gif->SHeight; i += 4) + DGifGetLine(h->gif, h->il + h->w*i,h->w); + } + break; + case TERMINATE_RECORD_TYPE: + default: + goto oops; + } + } + if (0 == info->width || 0 == info->height) + goto oops; + + if (debug) + fprintf(stderr,"gif: s=%dx%d i=%dx%d\n", + h->gif->SWidth,h->gif->SHeight, + h->gif->Image.Width,h->gif->Image.Height); + return h; + + oops: + if (debug) + fprintf(stderr,"gif: fatal error, aborting\n"); + DGifCloseFile(h->gif); + fclose(h->infile); + free(h->row); + free(h); + return NULL; +} + +static void +gif_read(unsigned char *dst, unsigned int line, void *data) +{ + struct gif_state *h = data; + GifColorType *cmap; + int x; + + if (h->gif->Image.Interlace) { + if (line % 2) { + DGifGetLine(h->gif, h->row, h->w); + } else { + memcpy(h->row, h->il + h->w * line, h->w); + } + } else { + DGifGetLine(h->gif, h->row, h->w); + } + cmap = h->gif->Image.ColorMap ? + h->gif->Image.ColorMap->Colors : h->gif->SColorMap->Colors; + for (x = 0; x < h->w; x++) { + dst[0] = cmap[h->row[x]].Red; + dst[1] = cmap[h->row[x]].Green; + dst[2] = cmap[h->row[x]].Blue; + dst += 3; + } +} + +static void +gif_done(void *data) +{ + struct gif_state *h = data; + + if (debug) + fprintf(stderr,"gif: done, cleaning up\n"); + DGifCloseFile(h->gif); + fclose(h->infile); + if (h->il) + free(h->il); + free(h->row); + free(h); +} + +static struct ida_loader gif_loader = { + magic: "GIF", + moff: 0, + mlen: 3, + name: "libungif", + init: gif_init, + read: gif_read, + done: gif_done, +}; + +static void __init init_rd(void) +{ + load_register(&gif_loader); +} diff --git a/rd/read-jpeg.c b/rd/read-jpeg.c new file mode 100644 index 0000000..d12fb40 --- /dev/null +++ b/rd/read-jpeg.c @@ -0,0 +1,187 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <errno.h> +#include <jpeglib.h> + +#include <libexif/exif-data.h> + +#include "readers.h" +#include "misc.h" + +/* ---------------------------------------------------------------------- */ +/* load */ + +struct jpeg_state { + FILE * infile; /* source file */ + + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPARRAY buffer; /* Output row buffer */ + int row_stride,linelength; /* physical row width in output buffer */ + unsigned char *image,*ptr; + + /* thumbnail */ + unsigned char *thumbnail; + unsigned int tpos, tsize; +}; + +/* ---------------------------------------------------------------------- */ +/* data source manager for thumbnail images */ + +static void thumbnail_src_init(struct jpeg_decompress_struct *cinfo) +{ + struct jpeg_state *h = container_of(cinfo, struct jpeg_state, cinfo); + cinfo->src->next_input_byte = h->thumbnail; + cinfo->src->bytes_in_buffer = h->tsize; +} + +static int thumbnail_src_fill(struct jpeg_decompress_struct *cinfo) +{ + fprintf(stderr,"jpeg: panic: no more thumbnail input data\n"); + exit(1); +} + +static void thumbnail_src_skip(struct jpeg_decompress_struct *cinfo, + long num_bytes) +{ + cinfo->src->next_input_byte += num_bytes; +} + +static void thumbnail_src_term(struct jpeg_decompress_struct *cinfo) +{ + /* nothing */ +} + +static struct jpeg_source_mgr thumbnail_mgr = { + .init_source = thumbnail_src_init, + .fill_input_buffer = thumbnail_src_fill, + .skip_input_data = thumbnail_src_skip, + .resync_to_restart = jpeg_resync_to_restart, + .term_source = thumbnail_src_term, +}; + +/* ---------------------------------------------------------------------- */ +/* jpeg loader */ + +static void* +jpeg_init(FILE *fp, char *filename, unsigned int page, + struct ida_image_info *i, int thumbnail) +{ + struct jpeg_state *h; + jpeg_saved_marker_ptr mark; + + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + h->infile = fp; + + h->cinfo.err = jpeg_std_error(&h->jerr); + jpeg_create_decompress(&h->cinfo); + jpeg_save_markers(&h->cinfo, JPEG_COM, 0xffff); /* comment */ + jpeg_save_markers(&h->cinfo, JPEG_APP0+1, 0xffff); /* EXIF */ + jpeg_stdio_src(&h->cinfo, h->infile); + jpeg_read_header(&h->cinfo, TRUE); + + for (mark = h->cinfo.marker_list; NULL != mark; mark = mark->next) { + switch (mark->marker) { + case JPEG_COM: + if (debug) + fprintf(stderr,"jpeg: comment found (COM marker) [%.*s]\n", + (int)mark->data_length, mark->data); + load_add_extra(i,EXTRA_COMMENT,mark->data,mark->data_length); + break; + case JPEG_APP0 +1: + if (debug) + fprintf(stderr,"jpeg: exif data found (APP1 marker)\n"); + load_add_extra(i,EXTRA_COMMENT,mark->data,mark->data_length); + + if (thumbnail) { + ExifData *ed; + + ed = exif_data_new_from_data(mark->data,mark->data_length); + if (ed->data && + ed->data[0] == 0xff && + ed->data[1] == 0xd8) { + if (debug) + fprintf(stderr,"jpeg: exif thumbnail found\n"); + + /* save away thumbnail data */ + h->thumbnail = malloc(ed->size); + h->tsize = ed->size; + memcpy(h->thumbnail,ed->data,ed->size); + } + exif_data_unref(ed); + } + break; + } + } + + if (h->thumbnail) { + /* save image size */ + i->thumbnail = 1; + i->real_width = h->cinfo.image_width; + i->real_height = h->cinfo.image_height; + + /* re-setup jpeg */ + jpeg_destroy_decompress(&h->cinfo); + fclose(h->infile); + h->infile = NULL; + jpeg_create_decompress(&h->cinfo); + h->cinfo.src = &thumbnail_mgr; + jpeg_read_header(&h->cinfo, TRUE); + } + + h->cinfo.out_color_space = JCS_RGB; + jpeg_start_decompress(&h->cinfo); + i->width = h->cinfo.image_width; + i->height = h->cinfo.image_height; + i->npages = 1; + switch (h->cinfo.density_unit) { + case 0: /* unknown */ + break; + case 1: /* dot per inch */ + i->dpi = h->cinfo.X_density; + break; + case 2: /* dot per cm */ + i->dpi = res_cm_to_inch(h->cinfo.X_density); + break; + } + + return h; +} + +static void +jpeg_read(unsigned char *dst, unsigned int line, void *data) +{ + struct jpeg_state *h = data; + JSAMPROW row = dst; + jpeg_read_scanlines(&h->cinfo, &row, 1); +} + +static void +jpeg_done(void *data) +{ + struct jpeg_state *h = data; + jpeg_destroy_decompress(&h->cinfo); + if (h->infile) + fclose(h->infile); + if (h->thumbnail) + free(h->thumbnail); + free(h); +} + +struct ida_loader jpeg_loader = { + magic: "\xff\xd8", + moff: 0, + mlen: 2, + name: "libjpeg", + init: jpeg_init, + read: jpeg_read, + done: jpeg_done, +}; + +static void __init init_rd(void) +{ + load_register(&jpeg_loader); +} diff --git a/rd/read-pcd.c b/rd/read-pcd.c new file mode 100644 index 0000000..40c43d5 --- /dev/null +++ b/rd/read-pcd.c @@ -0,0 +1,78 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "pcd.h" + +#include "readers.h" + +extern int pcd_res; + +/* ---------------------------------------------------------------------- */ +/* load */ + +struct pcd_state { + struct PCD_IMAGE img; + int left,top,width,height; +}; + +static void* +pcd_init(FILE *fp, char *filename, unsigned int page, + struct ida_image_info *i, int thumbnail) +{ + struct pcd_state *h; + + fclose(fp); + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + + if (0 != pcd_open(&h->img, filename)) + goto oops; + if (-1 == pcd_select(&h->img, thumbnail ? 1 : pcd_res, + 0,0,0, pcd_get_rot(&h->img, 0), + &h->left, &h->top, &h->width, &h->height)) + goto oops; + if (-1 == pcd_decode(&h->img)) + goto oops; + + i->width = h->width; + i->height = h->height; + i->npages = 1; + return h; + + oops: + free(h); + return NULL; +} + +static void +pcd_read(unsigned char *dst, unsigned int line, void *data) +{ + struct pcd_state *h = data; + + pcd_get_image_line(&h->img, line, dst, PCD_TYPE_RGB, 0); +} + +static void +pcd_done(void *data) +{ + struct pcd_state *h = data; + + pcd_close(&h->img); + free(h); +} + +static struct ida_loader pcd_loader = { + magic: "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", + moff: 0, + mlen: 16, + name: "libpcd", + init: pcd_init, + read: pcd_read, + done: pcd_done, +}; + +static void __init init_rd(void) +{ + load_register(&pcd_loader); +} diff --git a/rd/read-png.c b/rd/read-png.c new file mode 100644 index 0000000..c4f82d2 --- /dev/null +++ b/rd/read-png.c @@ -0,0 +1,164 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <png.h> + +#include "readers.h" + +static const char *ct[] = { + "gray", "X1", "rgb", "palette", + "graya", "X5", "rgba", "X7", +}; + +struct png_state { + FILE *infile; + png_structp png; + png_infop info; + png_bytep image; + png_uint_32 w,h; + int color_type; +}; + +static void* +png_init(FILE *fp, char *filename, unsigned int page, + struct ida_image_info *i, int thumbnail) +{ + struct png_state *h; + int bit_depth, interlace_type; + int pass, number_passes; + unsigned int y; + png_uint_32 resx, resy; + png_color_16 *file_bg, my_bg = { + .red = 192, + .green = 192, + .blue = 192, + .gray = 192, + }; + int unit; + + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + + h->infile = fp; + + h->png = png_create_read_struct(PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + if (NULL == h->png) + goto oops; + h->info = png_create_info_struct(h->png); + if (NULL == h->info) + goto oops; + + png_init_io(h->png, h->infile); + png_read_info(h->png, h->info); + png_get_IHDR(h->png, h->info, &h->w, &h->h, + &bit_depth,&h->color_type,&interlace_type, NULL,NULL); + png_get_pHYs(h->png, h->info, &resx, &resy, &unit); + i->width = h->w; + i->height = h->h; + if (PNG_RESOLUTION_METER == unit) + i->dpi = res_m_to_inch(resx); + if (debug) + fprintf(stderr,"png: color_type=%s #1\n",ct[h->color_type]); + i->npages = 1; + + png_set_packing(h->png); + if (bit_depth == 16) + png_set_strip_16(h->png); + if (h->color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(h->png); + if (h->color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_gray_1_2_4_to_8(h->png); + + if (png_get_bKGD(h->png, h->info, &file_bg)) { + png_set_background(h->png,file_bg,PNG_BACKGROUND_GAMMA_FILE,1,1.0); + } else { + png_set_background(h->png,&my_bg,PNG_BACKGROUND_GAMMA_SCREEN,0,1.0); + } + + number_passes = png_set_interlace_handling(h->png); + png_read_update_info(h->png, h->info); + + h->color_type = png_get_color_type(h->png, h->info); + if (debug) + fprintf(stderr,"png: color_type=%s #2\n",ct[h->color_type]); + + h->image = malloc(i->width * i->height * 4); + + for (pass = 0; pass < number_passes-1; pass++) { + if (debug) + fprintf(stderr,"png: pass #%d\n",pass); + for (y = 0; y < i->height; y++) { + png_bytep row = h->image + y * i->width * 4; + png_read_rows(h->png, &row, NULL, 1); + } + } + + return h; + + oops: + if (h->image) + free(h->image); + if (h->png) + png_destroy_read_struct(&h->png, NULL, NULL); + fclose(h->infile); + free(h); + return NULL; +} + +static void +png_read(unsigned char *dst, unsigned int line, void *data) +{ + struct png_state *h = data; + + png_bytep row = h->image + line * h->w * 4; + switch (h->color_type) { + case PNG_COLOR_TYPE_GRAY: + png_read_rows(h->png, &row, NULL, 1); + load_gray(dst,row,h->w); + break; + case PNG_COLOR_TYPE_RGB: + png_read_rows(h->png, &row, NULL, 1); + memcpy(dst,row,3*h->w); + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + png_read_rows(h->png, &row, NULL, 1); + load_rgba(dst,row,h->w); + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + png_read_rows(h->png, &row, NULL, 1); + load_graya(dst,row,h->w); + break; + default: + /* shouldn't happen */ + fprintf(stderr,"Oops: %s:%d\n",__FILE__,__LINE__); + exit(1); + } +} + +static void +png_done(void *data) +{ + struct png_state *h = data; + + free(h->image); + png_destroy_read_struct(&h->png, &h->info, NULL); + fclose(h->infile); + free(h); +} + +static struct ida_loader png_loader = { + magic: "\x89PNG", + moff: 0, + mlen: 4, + name: "libpng", + init: png_init, + read: png_read, + done: png_done, +}; + +static void __init init_rd(void) +{ + load_register(&png_loader); +} diff --git a/rd/read-ppm.c b/rd/read-ppm.c new file mode 100644 index 0000000..b7eb326 --- /dev/null +++ b/rd/read-ppm.c @@ -0,0 +1,110 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "readers.h" + +/* ---------------------------------------------------------------------- */ +/* load */ + +struct ppm_state { + FILE *infile; + int width,height; + unsigned char *row; +}; + +static void* +pnm_init(FILE *fp, char *filename, unsigned int page, + struct ida_image_info *i, int thumbnail) +{ + struct ppm_state *h; + char line[1024]; + + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + + h->infile = fp; + fgets(line,sizeof(line),fp); /* Px */ + fgets(line,sizeof(line),fp); /* width height */ + while ('#' == line[0]) + fgets(line,sizeof(line),fp); /* skip comments */ + sscanf(line,"%d %d",&h->width,&h->height); + fgets(line,sizeof(line),fp); /* ??? */ + if (0 == h->width || 0 == h->height) + goto oops; + i->width = h->width; + i->height = h->height; + i->npages = 1; + h->row = malloc(h->width*3); + + return h; + + oops: + fclose(fp); + free(h); + return NULL; +} + +static void +ppm_read(unsigned char *dst, unsigned int line, void *data) +{ + struct ppm_state *h = data; + + fread(dst,h->width,3,h->infile); +} + +static void +pgm_read(unsigned char *dst, unsigned int line, void *data) +{ + struct ppm_state *h = data; + unsigned char *src; + int x; + + fread(h->row,h->width,1,h->infile); + src = h->row; + for (x = 0; x < h->width; x++) { + dst[0] = src[0]; + dst[1] = src[0]; + dst[2] = src[0]; + dst += 3; + src += 1; + } +} + +static void +pnm_done(void *data) +{ + struct ppm_state *h = data; + + fclose(h->infile); + free(h->row); + free(h); +} + +struct ida_loader ppm_loader = { + magic: "P6", + moff: 0, + mlen: 2, + name: "ppm parser", + init: pnm_init, + read: ppm_read, + done: pnm_done, +}; + +static struct ida_loader pgm_loader = { + magic: "P5", + moff: 0, + mlen: 2, + name: "pgm parser", + init: pnm_init, + read: pgm_read, + done: pnm_done, +}; + +static void __init init_rd(void) +{ + load_register(&ppm_loader); + load_register(&pgm_loader); +} + diff --git a/rd/read-tiff.c b/rd/read-tiff.c new file mode 100644 index 0000000..3f75b83 --- /dev/null +++ b/rd/read-tiff.c @@ -0,0 +1,195 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <tiffio.h> + +#include "readers.h" + +struct tiff_state { + TIFF* tif; + char emsg[1024]; + tdir_t ndirs; /* Number of directories */ + /* (could be interpreted as number of pages) */ + uint32 width,height; + uint16 config,nsamples,depth,fillorder,photometric; + uint32* row; + uint32* image; + uint16 resunit; + float xres,yres; +}; + +static void* +tiff_init(FILE *fp, char *filename, unsigned int page, + struct ida_image_info *i, int thumbnail) +{ + struct tiff_state *h; + + fclose(fp); + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + + TIFFSetWarningHandler(NULL); + h->tif = TIFFOpen(filename,"r"); + if (NULL == h->tif) + goto oops; + /* Determine number of directories */ + h->ndirs = 1; + while (TIFFReadDirectory(h->tif)) + h->ndirs++; + i->npages = h->ndirs; + /* Select requested directory (page) */ + if (!TIFFSetDirectory(h->tif, (tdir_t)page)) + goto oops; + + TIFFGetField(h->tif, TIFFTAG_IMAGEWIDTH, &h->width); + TIFFGetField(h->tif, TIFFTAG_IMAGELENGTH, &h->height); + TIFFGetField(h->tif, TIFFTAG_PLANARCONFIG, &h->config); + TIFFGetField(h->tif, TIFFTAG_SAMPLESPERPIXEL, &h->nsamples); + TIFFGetField(h->tif, TIFFTAG_BITSPERSAMPLE, &h->depth); + TIFFGetField(h->tif, TIFFTAG_FILLORDER, &h->fillorder); + TIFFGetField(h->tif, TIFFTAG_PHOTOMETRIC, &h->photometric); + h->row = malloc(TIFFScanlineSize(h->tif)); + if (debug) + fprintf(stderr,"tiff: %" PRId32 "x%" PRId32 ", planar=%d, " + "nsamples=%d, depth=%d fo=%d pm=%d scanline=%" PRId32 "\n", + h->width,h->height,h->config,h->nsamples,h->depth, + h->fillorder,h->photometric, + TIFFScanlineSize(h->tif)); + + if (PHOTOMETRIC_PALETTE == h->photometric || + PHOTOMETRIC_YCBCR == h->photometric || + PHOTOMETRIC_SEPARATED == h->photometric || + TIFFIsTiled(h->tif) || + (1 != h->depth && 8 != h->depth)) { + /* for the more difficuilt cases we let libtiff + * do all the hard work. Drawback is that we lose + * progressive loading and decode everything here */ + if (debug) + fprintf(stderr,"tiff: reading whole image [TIFFReadRGBAImage]\n"); + h->image=malloc(4*h->width*h->height); + TIFFReadRGBAImage(h->tif, h->width, h->height, h->image, 0); + } else { + if (debug) + fprintf(stderr,"tiff: reading scanline by scanline\n"); + h->row = malloc(TIFFScanlineSize(h->tif)); + } + + i->width = h->width; + i->height = h->height; + + if (TIFFGetField(h->tif, TIFFTAG_RESOLUTIONUNIT, &h->resunit) && + TIFFGetField(h->tif, TIFFTAG_XRESOLUTION, &h->xres) && + TIFFGetField(h->tif, TIFFTAG_YRESOLUTION, &h->yres)) { + switch (h->resunit) { + case RESUNIT_NONE: + break; + case RESUNIT_INCH: + i->dpi = h->xres; + break; + case RESUNIT_CENTIMETER: + i->dpi = res_cm_to_inch(h->xres); + break; + } + } + + return h; + + oops: + if (h->tif) + TIFFClose(h->tif); + free(h); + return NULL; +} + +static void +tiff_read(unsigned char *dst, unsigned int line, void *data) +{ + struct tiff_state *h = data; + int s,on,off; + + if (h->image) { + /* loaded whole image using TIFFReadRGBAImage() */ + uint32 *row = h->image + h->width * (h->height - line -1); + load_rgba(dst,(unsigned char*)row,h->width); + return; + } + + if (h->config == PLANARCONFIG_CONTIG) { + TIFFReadScanline(h->tif, h->row, line, 0); + } else if (h->config == PLANARCONFIG_SEPARATE) { + for (s = 0; s < h->nsamples; s++) + TIFFReadScanline(h->tif, h->row, line, s); + } + + switch (h->nsamples) { + case 1: + if (1 == h->depth) { + /* black/white */ + on = 0, off = 0; + if (PHOTOMETRIC_MINISWHITE == h->photometric) + on = 0, off = 255; + if (PHOTOMETRIC_MINISBLACK == h->photometric) + on = 255, off = 0; +#if 0 + /* Huh? Does TIFFReadScanline handle this already ??? */ + if (FILLORDER_MSB2LSB == h->fillorder) + load_bits_msb(dst,(unsigned char*)(h->row),h->width,on,off); + else + load_bits_lsb(dst,(unsigned char*)(h->row),h->width,on,off); +#else + load_bits_msb(dst,(unsigned char*)(h->row),h->width,on,off); +#endif + } else { + /* grayscaled */ + load_gray(dst,(unsigned char*)(h->row),h->width); + } + break; + case 3: + /* rgb */ + memcpy(dst,h->row,3*h->width); + break; + case 4: + /* rgb+alpha */ + load_rgba(dst,(unsigned char*)(h->row),h->width); + break; + } +} + +static void +tiff_done(void *data) +{ + struct tiff_state *h = data; + + TIFFClose(h->tif); + if (h->row) + free(h->row); + if (h->image) + free(h->image); + free(h); +} + +static struct ida_loader tiff1_loader = { + magic: "MM\x00\x2a", + moff: 0, + mlen: 4, + name: "libtiff", + init: tiff_init, + read: tiff_read, + done: tiff_done, +}; +static struct ida_loader tiff2_loader = { + magic: "II\x2a\x00", + moff: 0, + mlen: 4, + name: "libtiff", + init: tiff_init, + read: tiff_read, + done: tiff_done, +}; + +static void __init init_rd(void) +{ + load_register(&tiff1_loader); + load_register(&tiff2_loader); +} diff --git a/rd/read-xpm.c b/rd/read-xpm.c new file mode 100644 index 0000000..affdce6 --- /dev/null +++ b/rd/read-xpm.c @@ -0,0 +1,287 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> + +#include "ida.h" + +#include "readers.h" +#include "viewer.h" + +/* ---------------------------------------------------------------------- */ +/* load */ + +struct xpm_color { + char name[8]; + XColor color; +}; + +struct xpm_state { + FILE *infile; + int width,height,colors,chars; + struct xpm_color *cmap; + char *charline; + char *rgbrow; +}; + +static void* +xpm_init(FILE *fp, char *filename, unsigned int page, + struct ida_image_info *info, int thumbnail) +{ + struct xpm_state *h; + char line[1024],cname[32],*tmp; + XColor dummy; + int i; + Colormap cmap = DefaultColormapOfScreen(XtScreen(app_shell)); + + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + h->infile = fp; + + fgets(line,sizeof(line)-1,fp); /* XPM */ + fgets(line,sizeof(line)-1,fp); /* static char ... */ + while (0 == strncmp(line,"/*",2)) { + while (NULL == strstr(line,"*/")) + fgets(line,sizeof(line)-1,fp); + fgets(line,sizeof(line)-1,fp); + } + + /* size, colors */ + fgets(line,sizeof(line)-1,fp); + if (0 == strncmp(line,"/*",2)) { + while (NULL == strstr(line,"*/")) + fgets(line,sizeof(line)-1,fp); + fgets(line,sizeof(line)-1,fp); + } + if (4 != sscanf(line,"\"%d %d %d %d", + &h->width,&h->height,&h->colors,&h->chars)) + goto oops; + if (h->chars > 7) + goto oops; + + /* read color table */ + h->cmap = malloc(h->colors * sizeof(struct xpm_color)); + memset(h->cmap,0,h->colors * sizeof(struct xpm_color)); + for (i = 0; i < h->colors; i++) { + fgets(line,sizeof(line)-1,fp); + while (0 == strncmp(line,"/*",2)) { + while (NULL == strstr(line,"*/")) + fgets(line,sizeof(line)-1,fp); + fgets(line,sizeof(line)-1,fp); + } + memcpy(h->cmap[i].name,line+1,h->chars); + + if (NULL != (tmp = strstr(line+1+h->chars,"c "))) { + /* color */ + sscanf(tmp+2,"%32[^\" ]",cname); + } else if (NULL != (tmp = strstr(line+h->chars,"m "))) { + /* mono */ + sscanf(tmp+2,"%32[^\" ]",cname); + } else if (NULL != (tmp = strstr(line+h->chars,"g "))) { + /* gray? */ + sscanf(tmp+2,"%32[^\" ]",cname); + } else + goto oops; + if (0 == strcasecmp(cname,"none")) + /* transparent */ + strcpy(cname,"lightgray"); + if (debug) + fprintf(stderr,"xpm: cmap: \"%*.*s\" => %s\n", + h->chars,h->chars,h->cmap[i].name,cname); +#if 0 + if (1 != sscanf(line+1+h->chars," c %32[^\"]",cname)) + goto oops; +#endif + XLookupColor(dpy,cmap,cname,&h->cmap[i].color,&dummy); + } + h->charline = malloc(h->width * h->chars + 8); + h->rgbrow = malloc(h->width * 3); + + info->width = h->width; + info->height = h->height; + info->npages = 1; + return h; + + oops: + fclose(fp); + free(h); + return NULL; +} + +static void +xpm_read(unsigned char *dst, unsigned int line, void *data) +{ + struct xpm_state *h = data; + char *src; + int i,c; + + fgets(h->charline,h->width * h->chars + 8,h->infile); + while (0 == strncmp(h->charline,"/*",2)) { + while (NULL == strstr(h->charline,"*/")) + fgets(h->charline,h->width * h->chars + 8,h->infile); + fgets(h->charline,h->width * h->chars + 8,h->infile); + } + src = h->charline+1; + for (i = 0; i < h->width; i++) { + for (c = 0; c < h->colors; c++) { + char *name = h->cmap[c].name; + if (src[0] != name[0]) + continue; + if (1 == h->chars) + break; + if (src[1] != name[1]) + continue; + if (2 == h->chars) + break; + if (0 == strncmp(src+2,name+2,h->chars-2)) + break; + } + if (c == h->colors) + continue; + dst[0] = h->cmap[c].color.red >> 8; + dst[1] = h->cmap[c].color.green >> 8; + dst[2] = h->cmap[c].color.blue >> 8; + src += h->chars; + dst += 3; + } +} + +static void +xpm_done(void *data) +{ + struct xpm_state *h = data; + + fclose(h->infile); + free(h->charline); + free(h->rgbrow); + free(h->cmap); + free(h); +} + +/* ---------------------------------------------------------------------- */ + +struct xbm_state { + FILE *infile; + int width,height; +}; + +static void* +xbm_init(FILE *fp, char *filename, unsigned int page, + struct ida_image_info *info, int thumbnail) +{ + struct xbm_state *h; + char line[256],dummy[128]; + int i; + + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + h->infile = fp; + + for (i = 0; i < 128; i++) { + fgets(line,sizeof(line)-1,fp); + if (0 == strncmp(line,"#define",7)) + break; + } + if (128 == i) + goto oops; + + if (2 != sscanf(line,"#define %127s %d",dummy,&h->width)) + goto oops; + fgets(line,sizeof(line)-1,fp); + if (2 != sscanf(line,"#define %127s %d",dummy,&h->height)) + goto oops; + if (debug) + fprintf(stderr,"xbm: %dx%d\n",h->width,h->height); + + for (i = 0; i < 4; i++) { + fgets(line,sizeof(line)-1,fp); + if (strstr(line,"[] = {")) + break; + } + if (4 == i) + goto oops; + + info->width = h->width; + info->height = h->height; + info->npages = 1; + return h; + + oops: + if (debug) + fprintf(stderr,"xbm: %s",line); + fclose(fp); + free(h); + return NULL; +} + +static void +xbm_read(unsigned char *dst, unsigned int line, void *data) +{ + struct xbm_state *h = data; + int x,val; + + for (x = 0; x < h->width; x++) { + if (0 == (x % 8)) + fscanf(h->infile," 0x%x,",&val); + if (val & (1 << (x % 8))) { + *(dst++) = 0; + *(dst++) = 0; + *(dst++) = 0; + } else { + *(dst++) = 255; + *(dst++) = 255; + *(dst++) = 255; + } + } +} + +static void +xbm_done(void *data) +{ + struct xpm_state *h = data; + + fclose(h->infile); + free(h); +} + +/* ---------------------------------------------------------------------- */ + +static struct ida_loader xpm_loader = { + magic: "/* XPM */", + moff: 0, + mlen: 9, + name: "xpm parser", + init: xpm_init, + read: xpm_read, + done: xpm_done, +}; + +static struct ida_loader xbm1_loader = { + magic: "#define", + moff: 0, + mlen: 7, + name: "xbm parser", + init: xbm_init, + read: xbm_read, + done: xbm_done, +}; + +static struct ida_loader xbm2_loader = { + magic: "/*", + moff: 0, + mlen: 2, + name: "xbm parser", + init: xbm_init, + read: xbm_read, + done: xbm_done, +}; + +static void __init init_rd(void) +{ + load_register(&xpm_loader); + load_register(&xbm1_loader); + load_register(&xbm2_loader); +} diff --git a/rd/read-xwd.c b/rd/read-xwd.c new file mode 100644 index 0000000..8d15cf3 --- /dev/null +++ b/rd/read-xwd.c @@ -0,0 +1,357 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/XWDFile.h> +#ifdef HAVE_ENDIAN_H +# include <endian.h> +#endif + +#include "readers.h" +#include "viewer.h" +#include "xwd.h" +#include "x11.h" +#include "ida.h" + +/* xwd files are big endian */ +#if BYTE_ORDER == BIG_ENDIAN +# define be16_to_cpu(x) (x) +# define be32_to_cpu(x) (x) +#elif BYTE_ORDER == LITTLE_ENDIAN +# define be16_to_cpu(x) (((x>>8) & 0x00ff) |\ + ((x<<8) & 0xff00)) +# define be32_to_cpu(x) (((x>>24) & 0x000000ff) |\ + ((x>>8) & 0x0000ff00) |\ + ((x<<8) & 0x00ff0000) |\ + ((x<<24) & 0xff000000)) +#else +# error "Oops: unknown byte order" +#endif + +static char *vclass[] = { + "StaticGray", + "GrayScale", + "StaticColor", + "PseudoColor", + "TrueColor", + "DirectColor", +}; +static char *order[] = { + "LSBFirst", + "MSBFirst", +}; +static char *fmt[] = { + "XYBitmap", + "XYPixmap", + "ZPixmap", +}; + +/* ----------------------------------------------------------------------- */ + +struct xwd_state { + FILE *infile; + XWDFileHeader header; + XWDColor cmap[256]; + int width,bpp; + unsigned char *row; + unsigned long *pix; + unsigned long r_mask,g_mask,b_mask; + int r_shift,g_shift,b_shift; + int r_bits,g_bits,b_bits; +}; + +static void +xwd_map(struct xwd_state *h) +{ + int i; + unsigned long mask; + + h->r_mask = be32_to_cpu(h->header.red_mask); + h->g_mask = be32_to_cpu(h->header.green_mask); + h->b_mask = be32_to_cpu(h->header.blue_mask); + for (i = 0; i < 32; i++) { + mask = (1 << i); + if (h->r_mask & mask) + h->r_bits++; + else if (!h->r_bits) + h->r_shift++; + if (h->g_mask & mask) + h->g_bits++; + else if (!h->g_bits) + h->g_shift++; + if (h->b_mask & mask) + h->b_bits++; + else if (!h->b_bits) + h->b_shift++; + } + h->r_shift -= (8 - h->r_bits); + h->g_shift -= (8 - h->g_bits); + h->b_shift -= (8 - h->b_bits); +#if 0 + fprintf(stderr,"xwd: color: bits shift\n"); + fprintf(stderr,"xwd: red : %04i %05i\n", h->r_bits, h->r_shift); + fprintf(stderr,"xwd: green: %04i %05i\n", h->g_bits, h->g_shift); + fprintf(stderr,"xwd: blue : %04i %05i\n", h->b_bits, h->b_shift); +#endif +} + + +static void* +xwd_init(FILE *fp, char *filename, unsigned int page, + struct ida_image_info *i, int thumbnail) +{ + struct xwd_state *h; + char *buf; + int size; + + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + h->infile = fp; + + fread(&h->header,sizeof(h->header),1,fp); + + if ((be32_to_cpu(h->header.pixmap_format) >sizeof(fmt)/sizeof(char*)) || + (be32_to_cpu(h->header.byte_order) >sizeof(order)/sizeof(char*)) || + (be32_to_cpu(h->header.visual_class) >sizeof(vclass)/sizeof(char*))) { + fprintf(stderr,"xwd: invalid file\n"); + goto oops; + } + + if (debug) + fprintf(stderr, + "xwd: fmt=%s depth=%" PRId32 " size=%" PRId32 "x%" PRId32 + " bpp=%" PRId32 " bpl=%" PRId32 "\n" + "xwd: order=%s vclass=%s masks=%" PRIx32 "/%" PRIx32 + "/%" PRIx32 " cmap=%" PRId32 "\n", + fmt[be32_to_cpu(h->header.pixmap_format)], + be32_to_cpu(h->header.pixmap_depth), + be32_to_cpu(h->header.pixmap_width), + be32_to_cpu(h->header.pixmap_height), + be32_to_cpu(h->header.bits_per_pixel), + be32_to_cpu(h->header.bytes_per_line), + order[be32_to_cpu(h->header.byte_order)], + vclass[be32_to_cpu(h->header.visual_class)], + be32_to_cpu(h->header.red_mask), + be32_to_cpu(h->header.green_mask), + be32_to_cpu(h->header.blue_mask), + be32_to_cpu(h->header.colormap_entries)); + + size = be32_to_cpu(h->header.header_size)-sizeof(h->header); + buf = malloc(size); + fread(buf,size,1,fp); + if (debug) + fprintf(stderr,"xwd: name=%s\n",buf); + free(buf); + + /* check format */ + if (8 != be32_to_cpu(h->header.bits_per_pixel) && + 16 != be32_to_cpu(h->header.bits_per_pixel) && + 24 != be32_to_cpu(h->header.bits_per_pixel) && + 32 != be32_to_cpu(h->header.bits_per_pixel)) { + fprintf(stderr,"xwd: Oops: bpp != 8/16/24/32\n"); + goto oops; + } + if (be32_to_cpu(h->header.pixmap_format) != ZPixmap) { + fprintf(stderr,"xwd: Oops: can read only ZPixmap format\n"); + goto oops; + } + + /* color map */ + if (be32_to_cpu(h->header.colormap_entries) > 256) { + fprintf(stderr,"xwd: colormap too big (%" PRId32 " > 256)\n", + be32_to_cpu(h->header.colormap_entries)); + goto oops; + } + fread(&h->cmap,sizeof(XWDColor),be32_to_cpu(h->header.colormap_entries),fp); +#if 0 + for (i = 0; i < be32_to_cpu(h->header.colormap_entries); i++) + fprintf(stderr, "xwd cmap: %d: " + "pix=%ld rgb=%d/%d/%d flags=%d pad=%d\n",i, + be32_to_cpu(h->cmap[i].pixel), + be16_to_cpu(h->cmap[i].red), + be16_to_cpu(h->cmap[i].green), + be16_to_cpu(h->cmap[i].blue), + h->cmap[i].flags, + h->cmap[i].pad); +#endif + + switch (be32_to_cpu(h->header.visual_class)) { + case StaticGray: + case PseudoColor: + /* nothing */ + break; + case TrueColor: + case DirectColor: + xwd_map(h); + break; + default: + fprintf(stderr,"xwd: Oops: visual not implemented [%s]\n", + vclass[be32_to_cpu(h->header.visual_class)]); + goto oops; + } + + h->bpp = be32_to_cpu(h->header.bits_per_pixel); + h->width = be32_to_cpu(h->header.pixmap_width); + h->pix = malloc(h->width*sizeof(unsigned long)); + h->row = malloc(be32_to_cpu(h->header.bytes_per_line)); + i->width = be32_to_cpu(h->header.pixmap_width); + i->height = be32_to_cpu(h->header.pixmap_height); + i->npages = 1; + return h; + + oops: + fclose(h->infile); + free(h); + return NULL; +} + +static void +xwd_parse(unsigned char *dst, unsigned int line, void *data) +{ + struct xwd_state *h = data; + unsigned long r,g,b; + int x,i,bits; + + /* data to 32bit values */ + memset(h->pix,0,h->width * sizeof(unsigned long)); + if (be32_to_cpu(h->header.byte_order) == LSBFirst) { + for (i = 0, x = 0; x < h->width; x++) + for (bits = 0; bits < h->bpp; bits += 8) + h->pix[x] |= h->row[i++] << bits; + } else { + for (i = 0, x = 0; x < h->width; x++) + for (bits = 0; bits < h->bpp; bits += 8) + h->pix[x] <<= 8, h->pix[x] |= h->row[i++]; + } + + /* transform to rgb */ + switch (be32_to_cpu(h->header.visual_class)) { + case StaticGray: + for (x = 0; x < h->width; x++) { + dst[0] = h->pix[x]; + dst[1] = h->pix[x]; + dst[2] = h->pix[x]; + dst += 3; + } + break; + case PseudoColor: + for (x = 0; x < h->width; x++) { + dst[0] = be16_to_cpu(h->cmap[h->pix[x]].red) >> 8; + dst[1] = be16_to_cpu(h->cmap[h->pix[x]].green) >> 8; + dst[2] = be16_to_cpu(h->cmap[h->pix[x]].blue) >> 8; + dst += 3; + } + break; + case TrueColor: + case DirectColor: + for (x = 0; x < h->width; x++) { + r = h->pix[x] & h->r_mask; + if (h->r_shift > 0) + r >>= h->r_shift; + if (h->r_shift < 0) + r <<= -h->r_shift; + g = h->pix[x] & h->g_mask; + if (h->g_shift > 0) + g >>= h->g_shift; + if (h->g_shift < 0) + g <<= -h->g_shift; + b = h->pix[x] & h->b_mask; + if (h->b_shift > 0) + b >>= h->b_shift; + if (h->b_shift < 0) + b <<= -h->b_shift; + dst[0] = r; + dst[1] = g; + dst[2] = b; + dst += 3; + } + break; + } +} + +static void +xwd_read(unsigned char *dst, unsigned int line, void *data) +{ + struct xwd_state *h = data; + + fread(h->row,be32_to_cpu(h->header.bytes_per_line),1,h->infile); + xwd_parse(dst, line, data); +} + +static void +xwd_done(void *data) +{ + struct xwd_state *h = data; + + fclose(h->infile); + free(h->pix); + free(h->row); + free(h); +} + +static struct ida_loader xwd_loader = { + magic: "\0\0\0\7", + moff: 4, + mlen: 4, + name: "xwd", + init: xwd_init, + read: xwd_read, + done: xwd_done, +}; + +static void __init init_rd(void) +{ + load_register(&xwd_loader); +} + +/* ----------------------------------------------------------------------- */ + +void +parse_ximage(struct ida_image *dest, XImage *src) +{ + struct xwd_state h; + Colormap cmap; + XColor col; + int y,i; + + memset(&h,0,sizeof(h)); + h.width = src->width; + h.bpp = src->bits_per_pixel; + h.header.red_mask = be32_to_cpu(info->red_mask); + h.header.green_mask = be32_to_cpu(info->green_mask); + h.header.blue_mask = be32_to_cpu(info->blue_mask); + h.header.visual_class = be32_to_cpu(info->class); + h.header.byte_order = be32_to_cpu(ImageByteOrder(dpy)); + h.pix = malloc(src->width * sizeof(unsigned long)); + + switch (be32_to_cpu(h.header.visual_class)) { + case PseudoColor: + cmap = DefaultColormapOfScreen(XtScreen(app_shell)); + for (i = 0; i < 256; i++) { + col.pixel = i; + XQueryColor(dpy,cmap,&col); + h.cmap[i].red = be16_to_cpu(col.red); + h.cmap[i].green = be16_to_cpu(col.green); + h.cmap[i].blue = be16_to_cpu(col.blue); + } + break; + case TrueColor: + case DirectColor: + xwd_map(&h); + break; + } + + memset(dest,0,sizeof(*dest)); + dest->i.width = src->width; + dest->i.height = src->height; + dest->data = malloc(dest->i.width * dest->i.height * 3); + memset(dest->data,0,dest->i.width * dest->i.height * 3); + + for (y = 0; y < src->height; y++) { + h.row = src->data + y*src->bytes_per_line; + xwd_parse(dest->data + 3*y*dest->i.width, y, &h); + } + free(h.pix); +} diff --git a/readers.c b/readers.c new file mode 100644 index 0000000..065be06 --- /dev/null +++ b/readers.c @@ -0,0 +1,133 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "readers.h" + +/* ----------------------------------------------------------------------- */ + +void load_bits_lsb(unsigned char *dst, unsigned char *src, int width, + int on, int off) +{ + int i,mask,bit; + + for (i = 0; i < width; i++) { + mask = 1 << (i & 0x07); + bit = src[i>>3] & mask; + dst[0] = bit ? on : off; + dst[1] = bit ? on : off; + dst[2] = bit ? on : off; + dst += 3; + } +} + +void load_bits_msb(unsigned char *dst, unsigned char *src, int width, + int on, int off) +{ + int i,mask,bit; + + for (i = 0; i < width; i++) { + mask = 1 << (7 - (i & 0x07)); + bit = src[i>>3] & mask; + dst[0] = bit ? on : off; + dst[1] = bit ? on : off; + dst[2] = bit ? on : off; + dst += 3; + } +} + +void load_gray(unsigned char *dst, unsigned char *src, int width) +{ + int i; + + for (i = 0; i < width; i++) { + dst[0] = src[0]; + dst[1] = src[0]; + dst[2] = src[0]; + dst += 3; + src += 1; + } +} + +void load_graya(unsigned char *dst, unsigned char *src, int width) +{ + int i; + + for (i = 0; i < width; i++) { + dst[0] = src[0]; + dst[1] = src[0]; + dst[2] = src[0]; + dst += 3; + src += 2; + } +} + +void load_rgba(unsigned char *dst, unsigned char *src, int width) +{ + int i; + + for (i = 0; i < width; i++) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst += 3; + src += 4; + } +} + +/* ----------------------------------------------------------------------- */ + +int load_add_extra(struct ida_image_info *info, enum ida_extype type, + unsigned char *data, unsigned int size) +{ + struct ida_extra *extra; + + extra = malloc(sizeof(*extra)); + if (NULL == extra) + return -1; + memset(extra,0,sizeof(*extra)); + extra->data = malloc(size); + if (NULL == extra->data) { + free(extra); + return -1; + } + extra->type = type; + extra->size = size; + memcpy(extra->data,data,size); + extra->next = info->extra; + info->extra = extra; + return 0; +}; + +struct ida_extra* load_find_extra(struct ida_image_info *info, + enum ida_extype type) +{ + struct ida_extra *extra; + + for (extra = info->extra; NULL != extra; extra = extra->next) + if (type == extra->type) + return extra; + return NULL; +} + +int load_free_extras(struct ida_image_info *info) +{ + struct ida_extra *next; + + while (NULL != info->extra) { + next = info->extra->next; + free(info->extra->data); + free(info->extra); + info->extra = next; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ + +LIST_HEAD(loaders); + +void load_register(struct ida_loader *loader) +{ + list_add_tail(&loader->list, &loaders); +} diff --git a/readers.h b/readers.h new file mode 100644 index 0000000..b54e5b6 --- /dev/null +++ b/readers.h @@ -0,0 +1,104 @@ +#include "list.h" + +enum ida_extype { + EXTRA_COMMENT = 1, + EXTRA_EXIF = 2, +}; + +struct ida_extra { + enum ida_extype type; + unsigned char *data; + unsigned int size; + struct ida_extra *next; +}; + +/* image data and metadata */ +struct ida_image_info { + unsigned int width; + unsigned int height; + unsigned int dpi; + unsigned int npages; + struct ida_extra *extra; + + int thumbnail; + unsigned int real_width; + unsigned int real_height; +}; + +struct ida_image { + struct ida_image_info i; + unsigned char *data; +}; +struct ida_rect { + int x1,y1,x2,y2; +}; + +/* load image files */ +struct ida_loader { + char *magic; + int moff; + int mlen; + char *name; + void* (*init)(FILE *fp, char *filename, unsigned int page, + struct ida_image_info *i, int thumbnail); + void (*read)(unsigned char *dst, unsigned int line, void *data); + void (*done)(void *data); + struct list_head list; +}; + +/* filter + operations */ +struct ida_op { + char *name; + void* (*init)(struct ida_image *src, struct ida_rect *rect, + struct ida_image_info *i, void *parm); + void (*work)(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int line, + void *data); + void (*done)(void *data); +}; + +void* op_none_init(struct ida_image *src, struct ida_rect *rect, + struct ida_image_info *i, void *parm); +void op_none_done(void *data); +void op_free_done(void *data); + +/* ----------------------------------------------------------------------- */ +/* resolution */ + +#define res_cm_to_inch(x) ((x * 2540 + 5) / 1000) +#define res_m_to_inch(x) ((x * 2540 + 5) / 100000) +#define res_inch_to_m(x) ((x * 100000 + 5) / 2540) + +/* ----------------------------------------------------------------------- */ + +/* helpers */ +void load_bits_lsb(unsigned char *dst, unsigned char *src, int width, + int on, int off); +void load_bits_msb(unsigned char *dst, unsigned char *src, int width, + int on, int off); +void load_gray(unsigned char *dst, unsigned char *src, int width); +void load_graya(unsigned char *dst, unsigned char *src, int width); +void load_rgba(unsigned char *dst, unsigned char *src, int width); + +int load_add_extra(struct ida_image_info *info, enum ida_extype type, + unsigned char *data, unsigned int size); +struct ida_extra* load_find_extra(struct ida_image_info *info, + enum ida_extype type); +int load_free_extras(struct ida_image_info *info); + +/* ----------------------------------------------------------------------- */ + +/* other */ +extern int debug; +extern struct ida_loader ppm_loader; +extern struct ida_loader jpeg_loader; +extern struct ida_loader sane_loader; +extern struct ida_writer ps_writer; +extern struct ida_writer jpeg_writer; + +/* lists */ +#define __init __attribute__ ((constructor)) +#define __fini __attribute__ ((destructor)) + +extern struct list_head loaders; +void load_register(struct ida_loader *loader); @@ -0,0 +1,324 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sane/sane.h> +#include <sane/saneopts.h> + +#include "readers.h" +#include "viewer.h" +#include "sane.h" +#include "ida.h" + +#include <X11/Intrinsic.h> +#include <Xm/Xm.h> +#include <Xm/PushB.h> +#include <Xm/CascadeB.h> +#include <Xm/RowColumn.h> + +extern int sane_res; + +/* ---------------------------------------------------------------------- */ + +static void +build_menu(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + WidgetList children,wlist; + Cardinal nchildren; + const SANE_Device **list; + Widget push; + XmString str; + char action[256]; + int rc,i; + + /* del old */ + XtVaGetValues(widget, + XtNchildren,&children, + XtNnumChildren,&nchildren, + NULL); + wlist = malloc(sizeof(Widget*)*nchildren); + memcpy(list,children,sizeof(Widget*)*nchildren); + for (i = 0; i < nchildren; i++) + XtDestroyWidget(wlist[i]); + free(wlist); + + /* create new */ + if (SANE_STATUS_GOOD != (rc = sane_init(NULL,NULL))) { + fprintf(stderr,"sane_init: %s\n",sane_strstatus(rc)); + goto done; + } + sane_get_devices(&list,0); + if (NULL == list[0]) + goto done; + + for (i = 0; list[i] != NULL; i++) { + if (debug) + fprintf(stderr,"sane dev: %s | %s | %s | %s\n", + list[i]->name, list[i]->vendor, + list[i]->model, list[i]->type); + str = XmStringGenerate((char*)list[i]->model, + NULL, XmMULTIBYTE_TEXT, NULL); + push = XtVaCreateManagedWidget(list[i]->name, + xmPushButtonWidgetClass,widget, + XmNlabelString,str, + NULL); + XmStringFree(str); + sprintf(action,"Scan(%s)",list[i]->name); + XtAddCallback(push,XmNactivateCallback,action_cb,strdup(action)); + } + + done: + sane_exit(); +} + +void +sane_menu(Widget menu) +{ + Widget submenu; + int rc; + + if (SANE_STATUS_GOOD != (rc = sane_init(NULL,NULL))) { + fprintf(stderr,"sane_init: %s\n",sane_strstatus(rc)); + goto done; + } + submenu = XmCreatePulldownMenu(menu,"scanM",NULL,0); + XtVaCreateManagedWidget("scan",xmCascadeButtonWidgetClass,menu, + XmNsubMenuId,submenu,NULL); + XtAddCallback(submenu, XmNmapCallback, build_menu, NULL); + + done: + sane_exit(); +} + +/* ---------------------------------------------------------------------- */ +/* load */ + +#define BUF_LINES 16 /* read bigger chunks to reduce overhead */ + +struct sane_state { + SANE_Handle sane; + SANE_Parameters parm; + SANE_Byte *buf; + int started; +}; + +static void dump_desc(SANE_Handle h, int nr, const SANE_Option_Descriptor *opt) +{ + SANE_Bool b; + SANE_Int i,flags; + + fprintf(stderr,"sane opt: name=%s title=%s type=%d unit=%d cap=%d\n", + opt->name, opt->title, opt->type, opt->unit, opt->cap); + switch (opt->type) { + case SANE_TYPE_BOOL: + sane_control_option(h, nr, SANE_ACTION_GET_VALUE, + &b, &flags); + fprintf(stderr," value=%s [bool]\n",b ? "true" : "false"); + break; + case SANE_TYPE_INT: + sane_control_option(h, nr, SANE_ACTION_GET_VALUE, + &i, &flags); + fprintf(stderr," value=%d [int]\n",i); + break; + case SANE_TYPE_FIXED: + case SANE_TYPE_STRING: + case SANE_TYPE_BUTTON: + case SANE_TYPE_GROUP: + break; + } + switch (opt->constraint_type) { + case SANE_CONSTRAINT_NONE: + break; + case SANE_CONSTRAINT_RANGE: + fprintf(stderr," range=%d-%d\n", + opt->constraint.range->min, + opt->constraint.range->max); + break; + case SANE_CONSTRAINT_WORD_LIST: + fprintf(stderr," constraint word_list:"); + for (i = 1; i <= opt->constraint.word_list[0]; i++) + fprintf(stderr," %d",opt->constraint.word_list[i]); + fprintf(stderr,"\n"); + break; + case SANE_CONSTRAINT_STRING_LIST: + fprintf(stderr," constraint string_list:"); + for (i = 0; opt->constraint.string_list[i] != NULL; i++) + fprintf(stderr," %s",opt->constraint.string_list[i]); + fprintf(stderr,"\n"); + break; + }; +} + +static void* +sane_idainit(FILE *fp, char *filename, unsigned int page, struct ida_image_info *info, + int thumbnail) +{ + const SANE_Option_Descriptor *opt; + SANE_Int flags, count; + struct sane_state *h; + int rc,i,value,dpi = 0; + + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + + if (SANE_STATUS_GOOD != (rc = sane_init(NULL,NULL))) { + fprintf(stderr,"sane_init: %s\n",sane_strstatus(rc)); + goto oops; + } + if (SANE_STATUS_GOOD != (rc = sane_open(filename,&h->sane))) { + fprintf(stderr,"sane_open: %s\n",sane_strstatus(rc)); + goto oops; + } + + /* set options */ + opt = sane_get_option_descriptor(h->sane,0); + rc = sane_control_option(h->sane, 0, SANE_ACTION_GET_VALUE, + &count, &flags); + for (i = 1; i < count; i++) { + opt = sane_get_option_descriptor(h->sane,i); + if (opt->name && 0 == strcmp(opt->name,SANE_NAME_SCAN_TL_X)) { + value = opt->constraint.range->min; + sane_control_option(h->sane, i, SANE_ACTION_SET_VALUE, + &value, &flags); + } else if (opt->name && 0 == strcmp(opt->name,SANE_NAME_SCAN_TL_Y)) { + value = opt->constraint.range->min; + sane_control_option(h->sane, i, SANE_ACTION_SET_VALUE, + &value, &flags); + } else if (opt->name && 0 == strcmp(opt->name,SANE_NAME_SCAN_BR_X)) { + value = opt->constraint.range->max; + sane_control_option(h->sane, i, SANE_ACTION_SET_VALUE, + &value, &flags); + } else if (opt->name && 0 == strcmp(opt->name,SANE_NAME_SCAN_BR_Y)) { + value = opt->constraint.range->max; + sane_control_option(h->sane, i, SANE_ACTION_SET_VALUE, + &value, &flags); + } else if (opt->name && 0 == strcmp(opt->name,SANE_NAME_PREVIEW)) { + value = SANE_FALSE; + sane_control_option(h->sane, i, SANE_ACTION_SET_VALUE, + &value, &flags); + } else if (opt->cap & SANE_CAP_AUTOMATIC) + sane_control_option(h->sane, i, SANE_ACTION_SET_AUTO, + NULL, &flags); + if (opt->name && 0 == strcmp(opt->name,SANE_NAME_SCAN_RESOLUTION)) { + if (sane_res) { + dpi = sane_res; + sane_control_option(h->sane, i, SANE_ACTION_SET_VALUE, + &dpi, &flags); + } + sane_control_option(h->sane, i, SANE_ACTION_GET_VALUE, + &dpi, &flags); + } + if (debug) + dump_desc(h->sane,i,opt); + } + + if (SANE_STATUS_GOOD != (rc = sane_start(h->sane))) { + fprintf(stderr,"sane_start: %s\n",sane_strstatus(rc)); + goto oops; + } + h->started = 1; + + if (SANE_STATUS_GOOD != (rc = sane_get_parameters(h->sane,&h->parm))) { + fprintf(stderr,"sane_get_parameters: %s\n",sane_strstatus(rc)); + goto oops; + } + + if (h->parm.format != SANE_FRAME_GRAY && + h->parm.format != SANE_FRAME_RGB) { + fprintf(stderr,"sane: unsupported frame format (%d)\n",h->parm.format); + goto oops; + } + if (h->parm.depth != 8) { + fprintf(stderr,"sane: unsupported color depth (%d)\n",h->parm.depth); + goto oops; + } + if (-1 == h->parm.lines) { + fprintf(stderr,"sane: can't handle unknown image size\n"); + goto oops; + } + + info->width = h->parm.pixels_per_line; + info->height = h->parm.lines; + if (dpi) + info->dpi = dpi; + h->buf = malloc(h->parm.bytes_per_line * BUF_LINES); + if (debug) + fprintf(stderr,"sane: scanning %dx%d %s\n",info->width,info->height, + (h->parm.format == SANE_FRAME_GRAY) ? "gray" : "color"); + + return h; + + oops: + if (h->buf) + free(h->buf); + if (h->started) + sane_cancel(h->sane); + if (h->sane) + sane_close(h->sane); + sane_exit(); + free(h); + return NULL; +} + +static void +sane_idaread(unsigned char *dst, unsigned int line, void *data) +{ + struct sane_state *h = data; + unsigned int lines, total, offset, len; + int rc, i; + SANE_Byte *row; + + if (0 == (line % BUF_LINES)) { + lines = BUF_LINES; + if (lines > h->parm.lines - line) + lines = h->parm.lines - line; + total = h->parm.bytes_per_line * lines; + offset = 0; + while (offset < total) { + rc = sane_read(h->sane, h->buf + offset, total - offset, &len); + if (rc != SANE_STATUS_GOOD) + return; + offset += len; + } + } + row = h->buf + (line % BUF_LINES) * h->parm.bytes_per_line; + switch (h->parm.format) { + case SANE_FRAME_GRAY: + for (i = 0; i < h->parm.pixels_per_line; i++) { + dst[3*i+0] = row[i]; + dst[3*i+1] = row[i]; + dst[3*i+2] = row[i]; + } + break; + case SANE_FRAME_RGB: + memcpy(dst,row,h->parm.pixels_per_line*3); + break; + default: + fprintf(stderr,"sane: read: internal error\n"); + exit(1); + } +} + +static void +sane_idadone(void *data) +{ + struct sane_state *h = data; + + sane_cancel(h->sane); + sane_close(h->sane); + sane_exit(); + free(h->buf); + free(h); +} + +struct ida_loader sane_loader = { + name: "sane interface", + init: sane_idainit, + read: sane_idaread, + done: sane_idadone, +}; + +static void __init init_rd(void) +{ + load_register(&sane_loader); +} @@ -0,0 +1 @@ +void sane_menu(Widget menu); diff --git a/selections.c b/selections.c new file mode 100644 index 0000000..262caa5 --- /dev/null +++ b/selections.c @@ -0,0 +1,623 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <Xm/Xm.h> +#include <Xm/Transfer.h> +#include <Xm/TransferP.h> +#include <Xm/DragIcon.h> + +#include "ida.h" +#include "readers.h" +#include "writers.h" +#include "viewer.h" +#include "xwd.h" +#include "xdnd.h" +#include "selections.h" +#include "list.h" + +Atom XA_TARGETS, XA_DONE; +Atom XA_FILE_NAME, XA_FILE; +Atom XA_BACKGROUND, XA_FOREGROUND, XA_PIXEL; +Atom _MOTIF_EXPORT_TARGETS; +Atom _MOTIF_CLIPBOARD_TARGETS; +Atom _MOTIF_DEFERRED_CLIPBOARD_TARGETS; +Atom _MOTIF_SNAPSHOT; +Atom _MOTIF_DROP; +Atom _MOTIF_LOSE_SELECTION; +Atom _NETSCAPE_URL; +Atom MIME_TEXT_URI_LIST; +Atom MIME_IMAGE_PPM; +Atom MIME_IMAGE_PGM; +Atom MIME_IMAGE_XPM; +Atom MIME_IMAGE_BMP; +Atom MIME_IMAGE_JPEG; +Atom MIME_IMAGE_GIF; +Atom MIME_IMAGE_PNG; +Atom MIME_IMAGE_TIFF; + +/* ---------------------------------------------------------------------- */ +/* send data (drags, copy) */ + +struct sel_data { + struct list_head list; + Atom atom; + struct ida_image img; + Pixmap pixmap; + char *filename; + Pixmap icon_pixmap; + Widget icon_widget; +}; +static struct list_head selections; + +static void +iconify(Widget widget, struct sel_data *data) +{ + struct ida_image small; + unsigned int scale,x,y,depth; + char *src,*dst; + Arg args[4]; + Cardinal n=0; + + /* calc size */ + memset(&small,0,sizeof(small)); + for (scale = 1;; scale++) { + small.i.width = data->img.i.width / scale; + small.i.height = data->img.i.height / scale; + if (small.i.width < 128 && small.i.height < 128) + break; + } + + /* scale down & create pixmap */ + dst = small.data = malloc(small.i.width * small.i.height * 3); + for (y = 0; y < small.i.height; y++) { + src = data->img.data + 3 * y * scale * data->img.i.width; + for (x = 0; x < small.i.width; x++) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst += 3; + src += 3*scale; + } + } + data->icon_pixmap = image_to_pixmap(&small); + + /* build DnD icon */ + n = 0; + depth = DefaultDepthOfScreen(XtScreen(widget)); + XtSetArg(args[n], XmNpixmap, data->icon_pixmap); n++; + XtSetArg(args[n], XmNwidth, small.i.width); n++; + XtSetArg(args[n], XmNheight, small.i.height); n++; + XtSetArg(args[n], XmNdepth, depth); n++; + data->icon_widget = XmCreateDragIcon(widget,"dragicon",args,n); + + free(small.data); +} + +static struct sel_data* +sel_find(Atom selection) +{ + struct list_head *item; + struct sel_data *sel; + + list_for_each(item,&selections) { + sel = list_entry(item, struct sel_data, list); + if (sel->atom == selection) + return sel; + } + return NULL; +} + +static void +sel_free(Atom selection) +{ + struct sel_data *sel; + + sel = sel_find(selection); + if (NULL == sel) + return; + if (sel->filename) { + unlink(sel->filename); + free(sel->filename); + } + if (sel->icon_widget) + XtDestroyWidget(sel->icon_widget); + if (sel->icon_pixmap) + XFreePixmap(dpy,sel->icon_pixmap); + if (sel->pixmap) + XFreePixmap(dpy,sel->pixmap); + if (sel->img.data) + free(sel->img.data); + + list_del(&sel->list); + free(sel); +} + +static struct sel_data* +sel_init(Atom selection) +{ + struct sel_data *sel; + + sel_free(selection); + sel = malloc(sizeof(*sel)); + memset(sel,0,sizeof(*sel)); + + sel->atom = selection; + sel->img = ida->img; + sel->img.data = malloc(ida->img.i.width * ida->img.i.height * 3); + memcpy(sel->img.data, ida->img.data, + ida->img.i.width * ida->img.i.height * 3); + + list_add_tail(&sel->list,&selections); + return sel; +} + +static void +sel_tmpfile(struct sel_data *sel, struct ida_writer *wr) +{ + static char *base = "ida"; + char *tmpdir; + FILE *fp; + int fd; + + tmpdir = getenv("TMPDIR"); + if (NULL == tmpdir) + tmpdir="/tmp"; + sel->filename = malloc(strlen(tmpdir)+strlen(base)+16); + sprintf(sel->filename,"%s/%s-XXXXXX",tmpdir,base); + fd = mkstemp(sel->filename); + fp = fdopen(fd,"w"); + wr->write(fp,&sel->img); + fclose(fp); +} + +Atom sel_unique_atom(Widget widget) +{ + char id_name[32]; + Atom id; + int i; + + for (i = 0;; i++) { + sprintf(id_name,"_IDA_DATA_%lX_%d",XtWindow(widget),i); + id = XInternAtom(XtDisplay(widget),id_name,False); + if (NULL == sel_find(id)) + break; + } + return id; +} + +void +selection_convert(Widget widget, XtPointer ignore, XtPointer call_data) +{ + XmConvertCallbackStruct *ccs = call_data; + unsigned long *ldata; + unsigned char *cdata; + struct sel_data *sel; + char *filename; + int n; + + if (debug) { + char *t = !ccs->target ? NULL : XGetAtomName(dpy,ccs->target); + char *s = !ccs->selection ? NULL : XGetAtomName(dpy,ccs->selection); + fprintf(stderr,"conv: target=%s selection=%s\n",t,s); + if (t) XFree(t); + if (s) XFree(s); + } + + /* tell which formats we can handle */ + if ((ccs->target == XA_TARGETS) || + (ccs->target == _MOTIF_CLIPBOARD_TARGETS) || + (ccs->target == _MOTIF_DEFERRED_CLIPBOARD_TARGETS) || + (ccs->target == _MOTIF_EXPORT_TARGETS)) { + n = 0; + ldata = (Atom*)XtMalloc(sizeof(Atom)*12); + if (ccs->target != _MOTIF_CLIPBOARD_TARGETS) { + ldata[n++] = XA_TARGETS; + ldata[n++] = MIME_IMAGE_PPM; + ldata[n++] = XA_PIXMAP; + ldata[n++] = XA_FOREGROUND; + ldata[n++] = XA_BACKGROUND; + ldata[n++] = XA_COLORMAP; + ldata[n++] = XA_FILE_NAME; + ldata[n++] = XA_FILE; + ldata[n++] = MIME_TEXT_URI_LIST; + ldata[n++] = _NETSCAPE_URL; + } + ccs->value = ldata; + ccs->length = n; + ccs->type = XA_ATOM; + ccs->format = 32; + ccs->status = XmCONVERT_DONE; + return; + + } else if (ccs->target == _MOTIF_SNAPSHOT) { + /* save away clipboard data */ + n = 0; + ldata = (Atom*)XtMalloc(sizeof(Atom)); + ldata[n++] = sel_unique_atom(widget); + sel_init(ldata[0]); + ccs->value = ldata; + ccs->length = n; + ccs->type = XA_ATOM; + ccs->format = 32; + ccs->status = XmCONVERT_DONE; + return; + } + + sel = sel_find(ccs->selection); + if (NULL == sel) { + /* should not happen */ + fprintf(stderr,"Oops: selection not found\n"); + ccs->status = XmCONVERT_REFUSE; + return; + } + + if ((ccs->target == _MOTIF_LOSE_SELECTION) || + (ccs->target == XA_DONE)) { + /* free stuff */ + sel_free(ccs->selection); + ccs->value = NULL; + ccs->length = 0; + ccs->type = XA_INTEGER; + ccs->format = 32; + ccs->status = XmCONVERT_DONE; + return; + } + + /* convert data */ + if (ccs->target == XA_BACKGROUND || + ccs->target == XA_FOREGROUND || + ccs->target == XA_COLORMAP) { + n = 0; + ldata = (Atom*)XtMalloc(sizeof(Atom)*8); + if (ccs->target == XA_BACKGROUND) { + ldata[n++] = WhitePixelOfScreen(XtScreen(widget)); + ccs->type = XA_PIXEL; + } + if (ccs->target == XA_FOREGROUND) { + ldata[n++] = BlackPixelOfScreen(XtScreen(widget)); + ccs->type = XA_PIXEL; + } + if (ccs->target == XA_COLORMAP) { + ldata[n++] = DefaultColormapOfScreen(XtScreen(widget)); + ccs->type = XA_COLORMAP; + } + ccs->value = ldata; + ccs->length = n; + ccs->format = 32; + ccs->status = XmCONVERT_DONE; + + } else if (ccs->target == XA_PIXMAP) { + /* xfer pixmap id */ + if (!sel->pixmap) + sel->pixmap = image_to_pixmap(&sel->img); + ldata = (Pixmap*)XtMalloc(sizeof(Pixmap)); + ldata[0] = sel->pixmap; + if (debug) + fprintf(stderr,"conv: pixmap id is 0x%lx\n",ldata[0]); + ccs->value = ldata; + ccs->length = 1; + ccs->type = XA_DRAWABLE; + ccs->format = 32; + ccs->status = XmCONVERT_DONE; + + } else if (ccs->target == MIME_IMAGE_PPM) { + /* xfer image data directly */ + cdata = XtMalloc(sel->img.i.width * sel->img.i.height * 3 + 32); + n = sprintf(cdata,"P6\n%d %d\n255\n", + sel->img.i.width, sel->img.i.height); + memcpy(cdata+n, sel->img.data, sel->img.i.width*sel->img.i.height*3); + ccs->value = cdata; + ccs->length = n + sel->img.i.width * sel->img.i.height * 3; + ccs->type = MIME_IMAGE_PPM; + ccs->format = 8; + ccs->status = XmCONVERT_DONE; + + } else if (ccs->target == XA_FILE_NAME || + ccs->target == XA_FILE || + ccs->target == XA_STRING || + ccs->target == MIME_TEXT_URI_LIST || + ccs->target == _NETSCAPE_URL) { + /* xfer image via tmp file */ + if (NULL == sel->filename) + sel_tmpfile(sel,&jpeg_writer); + if (ccs->target == MIME_TEXT_URI_LIST || + ccs->target == _NETSCAPE_URL) { + /* filename => url */ + filename = XtMalloc(strlen(sel->filename)+8); + sprintf(filename,"file:%s\r\n",sel->filename); + ccs->type = ccs->target; + if (debug) + fprintf(stderr,"conv: tmp url is %s\n",filename); + } else { + filename = XtMalloc(strlen(sel->filename)); + strcpy(filename,sel->filename); + ccs->type = XA_STRING; + if (debug) + fprintf(stderr,"conv: tmp file is %s\n",filename); + } + ccs->value = filename; + ccs->length = strlen(filename); + ccs->format = 8; + ccs->status = XmCONVERT_DONE; + + } else { + /* shouldn't happen */ + fprintf(stderr,"Huh? unknown target\n"); + ccs->status = XmCONVERT_REFUSE; + } +} + +static void +dnd_done(Widget widget, XtPointer ignore, XtPointer call_data) +{ + if (debug) + fprintf(stderr,"conv: transfer finished\n"); + sel_free(_MOTIF_DROP); +} + +/* ---------------------------------------------------------------------- */ +/* receive data (drops, paste) */ + +static Atom targets[16]; +static Cardinal ntargets; + +static void +selection_xfer(Widget widget, XtPointer ignore, XtPointer call_data) +{ + XmSelectionCallbackStruct *scs = call_data; + unsigned char *cdata = scs->value; + unsigned long *ldata = scs->value; + Atom target = 0; + unsigned int i,j,pending; + char *file,*tmp; + + if (debug) { + char *y = !scs->type ? NULL : XGetAtomName(dpy,scs->type); + char *t = !scs->target ? NULL : XGetAtomName(dpy,scs->target); + char *s = !scs->selection ? NULL : XGetAtomName(dpy,scs->selection); + fprintf(stderr,"xfer: id=%p target=%s type=%s selection=%s\n", + scs->transfer_id,t,y,s); + if (y) XFree(y); + if (t) XFree(t); + if (s) XFree(s); + } + + pending = scs->remaining; + if (scs->target == XA_TARGETS) { + /* look if we find a target we can deal with ... */ + for (i = 0; !target && i < scs->length; i++) { + for (j = 0; j < ntargets; j++) { + if (ldata[i] == targets[j]) { + target = ldata[i]; + break; + } + } + } + if (target) { + XmTransferValue(scs->transfer_id, target, selection_xfer, + NULL, XtLastTimestampProcessed(dpy)); + pending++; + } + if (debug) { + fprintf(stderr,"xfer: available targets: "); + for (i = 0; i < scs->length; i++) { + char *name = !ldata[i] ? NULL : XGetAtomName(dpy,ldata[i]); + fprintf(stderr,"%s%s", i != 0 ? ", " : "", name); + XFree(name); + } + fprintf(stderr,"\n"); + if (0 == scs->length) + fprintf(stderr,"xfer: Huh? no TARGETS available?\n"); + } + } + + if (scs->target == XA_FILE_NAME || + scs->target == XA_FILE) { + /* load file */ + if (debug) + fprintf(stderr,"xfer: => \"%s\"\n",cdata); + new_file(cdata,1); + } + + if (scs->target == _NETSCAPE_URL) { + /* load file */ + if (NULL != (tmp = strchr(cdata,'\n'))) + *tmp = 0; + if (NULL != (tmp = strchr(cdata,'\r'))) + *tmp = 0; + if (debug) + fprintf(stderr,"xfer: => \"%s\"\n",cdata); + new_file(cdata,1); + } + + if (scs->target == MIME_TEXT_URI_LIST) { + /* load file(s) */ + for (file = strtok(cdata,"\r\n"); + NULL != file; + file = strtok(NULL,"\r\n")) { + if (debug) + fprintf(stderr,"xfer: => \"%s\"\n",file); + new_file(file,1); + } + } + + if (scs->target == XA_STRING) { + /* might be a file name too, but don't complain if not */ + if (debug) + fprintf(stderr,"xfer: => \"%s\"\n",cdata); + new_file(cdata,0); + } + + if (scs->target == MIME_IMAGE_PPM || + scs->target == MIME_IMAGE_PGM || + scs->target == MIME_IMAGE_JPEG || + scs->target == MIME_IMAGE_GIF || + scs->target == MIME_IMAGE_PNG || + scs->target == MIME_IMAGE_TIFF || + scs->target == MIME_IMAGE_XPM || + scs->target == MIME_IMAGE_BMP) { + /* xfer image data directly */ + char *filename = load_tmpfile("ida"); + int fd; + fd = mkstemp(filename); + write(fd,scs->value,scs->length); + close(fd); + if (0 == viewer_loadimage(ida,filename,0)) { + ida->file = "selection"; + resize_shell(); + } + unlink(filename); + free(filename); + } + + if (scs->target == XA_PIXMAP) { + /* beaming pixmaps between apps */ + Screen *scr; + Window root; + Pixmap pix; + int x,y,w,h,bw,depth; + XImage *ximage; + struct ida_image img; + + pix = ldata[0]; + if (debug) + fprintf(stderr,"xfer: => id=0x%lx\n",pix); + scr = XtScreen(widget); + XGetGeometry(dpy,pix,&root,&x,&y,&w,&h,&bw,&depth); + ximage = XGetImage(dpy,pix,0,0,w,h,-1,ZPixmap); + parse_ximage(&img, ximage); + XDestroyImage(ximage); + viewer_setimage(ida,&img,"selection"); + resize_shell(); + } + + XFree(scs->value); + if (1 == pending) { + /* all done -- clean up */ + if (debug) + fprintf(stderr,"xfer: all done\n"); + XmTransferDone(scs->transfer_id, XmTRANSFER_DONE_SUCCEED); + XdndDropFinished(widget,scs); + } +} + +void +selection_dest(Widget w, XtPointer ignore, XtPointer call_data) +{ + XmDestinationCallbackStruct *dcs = call_data; + + if (NULL != sel_find(_MOTIF_DROP)) { + if (debug) + fprintf(stderr,"dest: ignore self drop\n"); + XmTransferDone(dcs->transfer_id, XmTRANSFER_DONE_FAIL); + return; + } + if (debug) + fprintf(stderr,"dest: xfer id=%p\n",dcs->transfer_id); + XmTransferValue(dcs->transfer_id, XA_TARGETS, selection_xfer, + NULL, XtLastTimestampProcessed(dpy)); +} + +/* ---------------------------------------------------------------------- */ + +void ipc_ac(Widget widget, XEvent *event, String *argv, Cardinal *argc) +{ + struct sel_data *sel; + Widget drag; + Arg args[4]; + Cardinal n=0; + + if (0 == *argc) + return; + + if (debug) + fprintf(stderr,"ipc: %s\n",argv[0]); + + if (0 == strcmp(argv[0],"paste")) { + XmeClipboardSink(ida->widget,XmCOPY,NULL); + + } else if (0 == strcmp(argv[0],"copy")) { + XmeClipboardSource(ida->widget,XmCOPY,XtLastTimestampProcessed(dpy)); + + } else if (0 == strcmp(argv[0],"drag")) { + sel = sel_init(_MOTIF_DROP); + iconify(widget,sel); + XtSetArg(args[n], XmNdragOperations, XmDROP_COPY); n++; + XtSetArg(args[n], XmNsourcePixmapIcon, sel->icon_widget); n++; + drag = XmeDragSource(ida->widget, NULL, event, args, n); + XtAddCallback(drag, XmNdragDropFinishCallback, dnd_done, NULL); + } +} + +void dnd_add(Widget widget) +{ + Arg args[4]; + Cardinal n=0; + + XtSetArg(args[n], XmNimportTargets, targets); n++; + XtSetArg(args[n], XmNnumImportTargets, ntargets); n++; + XmeDropSink(widget,args,n); + XdndDropSink(widget); +} + +void ipc_init() +{ + _MOTIF_EXPORT_TARGETS = + XInternAtom(dpy, "_MOTIF_EXPORT_TARGETS", False); + _MOTIF_CLIPBOARD_TARGETS = + XInternAtom(dpy, "_MOTIF_CLIPBOARD_TARGETS", False); + _MOTIF_DEFERRED_CLIPBOARD_TARGETS = + XInternAtom(dpy, "_MOTIF_DEFERRED_CLIPBOARD_TARGETS", False); + _MOTIF_SNAPSHOT = + XInternAtom(dpy, "_MOTIF_SNAPSHOT", False); + _MOTIF_DROP = + XInternAtom(dpy, "_MOTIF_DROP", False); + _MOTIF_LOSE_SELECTION = + XInternAtom(dpy, "_MOTIF_LOSE_SELECTION", False); + + XA_TARGETS = XInternAtom(dpy, "TARGETS", False); + XA_DONE = XInternAtom(dpy, "DONE", False); + XA_FILE_NAME = XInternAtom(dpy, "FILE_NAME", False); + XA_FILE = XInternAtom(dpy, "FILE", False); + XA_FOREGROUND = XInternAtom(dpy, "FOREGROUND", False); + XA_BACKGROUND = XInternAtom(dpy, "BACKGROUND", False); + XA_PIXEL = XInternAtom(dpy, "PIXEL", False); + _NETSCAPE_URL = XInternAtom(dpy, "_NETSCAPE_URL", False); + + MIME_TEXT_URI_LIST = XInternAtom(dpy, "text/uri-list", False); + MIME_IMAGE_PPM = XInternAtom(dpy, "image/ppm", False); + MIME_IMAGE_PGM = XInternAtom(dpy, "image/pgm", False); + MIME_IMAGE_XPM = XInternAtom(dpy, "image/xpm", False); + MIME_IMAGE_BMP = XInternAtom(dpy, "image/bmp", False); + MIME_IMAGE_JPEG = XInternAtom(dpy, "image/jpeg", False); + MIME_IMAGE_GIF = XInternAtom(dpy, "image/gif", False); + MIME_IMAGE_PNG = XInternAtom(dpy, "image/png", False); + MIME_IMAGE_TIFF = XInternAtom(dpy, "image/tiff", False); + + targets[ntargets++] = XA_FILE_NAME; + targets[ntargets++] = XA_FILE; + targets[ntargets++] = _NETSCAPE_URL; + targets[ntargets++] = MIME_TEXT_URI_LIST; + targets[ntargets++] = MIME_IMAGE_PPM; + targets[ntargets++] = MIME_IMAGE_PGM; + targets[ntargets++] = MIME_IMAGE_XPM; + targets[ntargets++] = MIME_IMAGE_BMP; + targets[ntargets++] = MIME_IMAGE_JPEG; +#ifdef HAVE_LIBUNGIF + targets[ntargets++] = MIME_IMAGE_GIF; +#endif +#ifdef HAVE_LIBPNG + targets[ntargets++] = MIME_IMAGE_PNG; +#endif +#ifdef HAVE_LIBTIFF + targets[ntargets++] = MIME_IMAGE_TIFF; +#endif + targets[ntargets++] = XA_PIXMAP; + targets[ntargets++] = XA_STRING; + + INIT_LIST_HEAD(&selections); +} diff --git a/selections.h b/selections.h new file mode 100644 index 0000000..784b9b7 --- /dev/null +++ b/selections.h @@ -0,0 +1,18 @@ +extern Atom XA_TARGETS, XA_DONE; +extern Atom _MOTIF_EXPORT_TARGETS; +extern Atom _MOTIF_CLIPBOARD_TARGETS; +extern Atom _MOTIF_DEFERRED_CLIPBOARD_TARGETS; +extern Atom _MOTIF_SNAPSHOT; +extern Atom _MOTIF_LOSE_SELECTION; +extern Atom XA_FILE_NAME, XA_FILE; +extern Atom _NETSCAPE_URL; +extern Atom MIME_TEXT_URI_LIST; + +Atom sel_unique_atom(Widget widget); + +void selection_dest(Widget w, XtPointer ignore, XtPointer call_data); +void selection_convert(Widget widget, XtPointer ignore, XtPointer call_data); +void ipc_ac(Widget widget, XEvent *event, String *argv, Cardinal *argc); +void dnd_add(Widget widget); +void ipc_init(void); + diff --git a/thumbnail.cgi.c b/thumbnail.cgi.c new file mode 100644 index 0000000..a4ba3c8 --- /dev/null +++ b/thumbnail.cgi.c @@ -0,0 +1,125 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <sys/stat.h> +#include <libexif/exif-data.h> + +/* -------------------------------------------------------------------------- */ + +static const char *shellhelp = + "\n" + "This is a cgi script. It doesn't do any useful (beside printing this text)\n" + "when started from the shell prompt, it is supposed to be started by your\n" + "web server\n"; + +static const char *description = + "\n" + "The script deliveres the EXIF thumbnail of JPEG images to the web browser.\n" + "It will lookup the path passed via path info below your document root, i.e.\n" + "a request like this ...\n" + "\n" + " http://your.server/cgi-bin/thumbnail.cgi/path/file.jpg\n" + "\n" + "... will make the script send the thumbnail of the file ...\n" + "\n" + " %s/path/file.jpg\n" + "\n" + "to the client\n" + "\n" + "Security note: The script refuses paths containing \"..\" to avoid breaking\n" + "out of the document root. There are no other checks through, so it will\n" + "deliver thumbnails for any JPEG image below below your document root which\n" + "it is allowed to open by unix file permissions.\n" + "\n" + "(c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]\n" + "\n"; + +/* -------------------------------------------------------------------------- */ + +static void panic(int code, char *message) +{ + printf("Status: %d %s\n" + "Content-Type: text/plain\n" + "\n" + "ERROR: %s\n", + code, message, message); + fflush(stdout); + exit(1); +} + +static void dump_thumbnail(char *filename) +{ + char *cached; + char mtime[64]; + struct stat st; + struct tm *tm; + ExifData *ed = NULL; + + if (-1 == stat(filename,&st)) + panic(404,"can't stat file"); + tm = gmtime(&st.st_mtime); + strftime(mtime,sizeof(mtime),"%a, %d %b %Y %H:%M:%S GMT",tm); + cached = getenv("HTTP_IF_MODIFIED_SINCE"); + if (NULL != cached && 0 == strcmp(cached,mtime)) { + /* shortcut -- browser has a up-to-date copy */ + printf("Status: 304 Image not modified\n" + "\n"); + fflush(stdout); + return; + } + + ed = exif_data_new_from_file(filename); + if (!ed) + panic(500,"file has no exif data\n"); + if (!ed->data) + panic(500,"no exif thumbnail present"); + if (ed->data[0] != 0xff || ed->data[1] != 0xd8) + panic(500,"exif thumbnail has no jpeg magic"); + + + printf("Status: 200 Thumbnail follows\n" + "Content-Type: image/jpeg\n" + "Content-Length: %d\n" + "Last-modified: %s\n" + "\n", + ed->size,mtime); + fwrite(ed->data,ed->size,1,stdout); + fflush(stdout); +} + +/* -------------------------------------------------------------------------- */ + +int main(int argc, char *argv[]) +{ + char filename[1024]; + char *document_root; + char *path_info; + + if (NULL == getenv("GATEWAY_INTERFACE")) { + fprintf(stderr,shellhelp); + fprintf(stderr,description,"$DOCUMENT_ROOT"); + exit(1); + } + + document_root = getenv("DOCUMENT_ROOT"); + if (NULL == document_root) + panic(500,"DOCUMENT_ROOT unset"); + + path_info = getenv("PATH_INFO"); + if (NULL == path_info || 0 == strlen(path_info)) { + printf("Content-type: text/plain\n" + "\n"); + printf(description,document_root); + fflush(stdout); + return 0; + } + + if (NULL != strstr(path_info,"..")) + panic(403,"\"..\" not allowed in path"); + snprintf(filename,sizeof(filename),"%s/%s",document_root,path_info); + dump_thumbnail(filename); + return 0; +} diff --git a/viewer.c b/viewer.c new file mode 100644 index 0000000..0bc81f5 --- /dev/null +++ b/viewer.c @@ -0,0 +1,962 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <signal.h> + +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/cursorfont.h> +#include <X11/extensions/XShm.h> + +#include "ida.h" +#include "x11.h" +#include "dither.h" +#include "readers.h" +#include "viewer.h" +#include "hex.h" +#include "idaconfig.h" + +/* ----------------------------------------------------------------------- */ + +#define POINTER_NORMAL 0 +#define POINTER_BUSY 1 +#define POINTER_PICK 2 +#define RUBBER_NEW 3 +#define RUBBER_MOVE 4 +#define RUBBER_X1 5 +#define RUBBER_Y1 6 +#define RUBBER_X2 7 +#define RUBBER_Y2 8 + +#define RUBBER_RANGE 6 +#define RUBBER_INTERVAL 100 + +#define PROCESS_LINES 16 + +int debug; +Cursor ptrs[POINTER_COUNT]; + +/* ----------------------------------------------------------------------- */ + +Pixmap image_to_pixmap(struct ida_image *img) +{ + unsigned char line[256],*src; + XImage *ximage; + void *shm; + Pixmap pix; + GC gc; + unsigned int x,y; + + ximage = x11_create_ximage(app_shell, img->i.width, img->i.height, &shm); + for (y = 0; y < img->i.height; y++) { + src = img->data + 3*y*img->i.width; + if (display_type == PSEUDOCOLOR) { + dither_line(src, line, y, img->i.width); + for (x = 0; x < img->i.width; x++) + XPutPixel(ximage, x, y, x11_map[line[x]]); + } else { + for (x = 0; x < img->i.width; x++, src += 3) { + pix = x11_lut_red[src[0]] | + x11_lut_green[src[1]] | + x11_lut_blue[src[2]]; + XPutPixel(ximage, x, y, pix); + } + } + } + pix = XCreatePixmap(dpy,XtWindow(app_shell),img->i.width, img->i.height, + DefaultDepthOfScreen(XtScreen(app_shell))); + gc = XCreateGC(dpy, pix, 0, NULL); + XPUTIMAGE(dpy, pix, gc, ximage, 0, 0, 0, 0, img->i.width, img->i.height); + XFreeGC(dpy, gc); + x11_destroy_ximage(app_shell, ximage, shm); + return pix; +} + +/* ----------------------------------------------------------------------- */ + +int viewer_i2s(int zoom, int val) +{ + if (0 > zoom) + return val/(-zoom+1); + if (0 < zoom) + return val*(zoom+1); + return val; +} + +/* ----------------------------------------------------------------------- */ + +static void +viewer_renderline(struct ida_viewer *ida, char *scanline) +{ + unsigned char *src,*dst,*rgb; + unsigned long pix; + unsigned int x,s,scrline; + + src = scanline; + + if (0 == ida->zoom) { + /* as-is */ + if (display_type == PSEUDOCOLOR) { + dst = ida->dither_line; + dither_line(src, dst, ida->line, ida->scrwidth); + for (x = 0; x < ida->scrwidth; x++, dst++) + XPutPixel(ida->ximage, x, ida->line, x11_map[*dst]); + } else { + for (x = 0; x < ida->scrwidth; x++, src += 3) { + pix = x11_lut_red[src[0]] | + x11_lut_green[src[1]] | + x11_lut_blue[src[2]]; + XPutPixel(ida->ximage, x, ida->line, pix); + } + } + + } else if (ida->zoom < 0) { + /* zoom out */ + s = -ida->zoom+1; + if (s-1 != (ida->line % s)) + return; + scrline = ida->line/s; + if (display_type == PSEUDOCOLOR) { + rgb = ida->rgb_line; + for (x = 0; x < ida->scrwidth; x++, rgb += 3, src += 3*s) { + rgb[0] = src[0]; + rgb[1] = src[1]; + rgb[2] = src[2]; + } + rgb = ida->rgb_line; + dst = ida->dither_line; + dither_line(rgb, dst, scrline, ida->scrwidth); + for (x = 0; x < ida->scrwidth; x++, dst++) + XPutPixel(ida->ximage, x, scrline, x11_map[*dst]); + } else { +#if 0 + /* just drop pixels */ + for (x = 0; x < ida->scrwidth; x++, src += 3*s) { + pix = x11_lut_red[src[0]] | + x11_lut_green[src[1]] | + x11_lut_blue[src[2]]; + XPutPixel(ida->ximage, x, scrline, pix); + } +#else + /* horizontal interpolation (vertical is much harder ...) */ + for (x = 0; x < ida->scrwidth; x++, src += 3*s) { + int red,green,blue,count,ix; + red = 0; + green = 0; + blue = 0; + count = 0; + for (ix = 0; ix < 3*s; ix += 3) { + red += src[ix+0]; + green += src[ix+1]; + blue += src[ix+2]; + count += 1; + } + pix = x11_lut_red[red/count] | + x11_lut_green[green/count] | + x11_lut_blue[blue/count]; + XPutPixel(ida->ximage, x, scrline, pix); + } +#endif + } + + } else { + /* zoom in */ + s = ida->zoom+1; + if (display_type == PSEUDOCOLOR) { + rgb = ida->rgb_line; + for (x = 0; x < ida->scrwidth; rgb += 3) { + rgb[0] = src[0]; + rgb[1] = src[1]; + rgb[2] = src[2]; + x++; + if (0 == (x%s)) + src += 3; + } + for (scrline = ida->line*s; scrline < ida->line*s+s; scrline++) { + rgb = ida->rgb_line; + dst = ida->dither_line; + dither_line(rgb, dst, scrline, ida->scrwidth); + for (x = 0; x < ida->scrwidth; x++, dst++) + XPutPixel(ida->ximage, x, scrline, x11_map[*dst]); + } + } else { + for (scrline = ida->line*s; scrline < ida->line*s+s; scrline++) { + src = scanline; + for (x = 0; x < ida->scrwidth; src += 3) { + unsigned int i; + pix = x11_lut_red[src[0]] | + x11_lut_green[src[1]] | + x11_lut_blue[src[2]]; + for (i = 0; i < s; i++, x++) + XPutPixel(ida->ximage, x, scrline, pix); + } + } + } + } +} + +/* ----------------------------------------------------------------------- */ + +static void +viewer_cleanup(struct ida_viewer *ida) +{ + if (ida->load_read) { + ida->load_done(ida->load_data); + ida->load_line = 0; + ida->load_read = NULL; + ida->load_done = NULL; + ida->load_data = NULL; + } + if (ida->op_work) { + ida->op_done(ida->op_data); + ida->op_line = 0; + ida->op_work = NULL; + ida->op_done = NULL; + ida->op_data = NULL; + if (ida->op_src.data) { + if (ida->undo.data) { + fprintf(stderr,"have undo buffer /* shouldn't happen */"); + free(ida->undo.data); + } + ida->undo = ida->op_src; + memset(&ida->op_src,0,sizeof(ida->op_src)); + } + } +} + +static Boolean +viewer_workproc(XtPointer client_data) +{ + struct ida_viewer *ida = client_data; + unsigned int start,end; + char *scanline; + + start = ida->line; + end = ida->line + ida->steps; + if (end > ida->img.i.height) + end = ida->img.i.height; + + /* image loading */ + if (ida->load_read) { + for (ida->line = start; ida->line < end; ida->line++) { + if (ida->load_line > ida->line) + continue; + scanline = ida->img.data + ida->img.i.width * ida->load_line * 3; + ida->load_read(scanline,ida->load_line,ida->load_data); + ida->load_line++; + } + } + + /* image processing */ + if (ida->op_work && 0 == ida->op_preview) { + for (ida->line = start; ida->line < end; ida->line++) { + if (ida->op_line > ida->line) + continue; + scanline = ida->img.data + ida->img.i.width * ida->op_line * 3; + ida->op_work(&ida->op_src,&ida->op_rect, + scanline,ida->op_line,ida->op_data); + ida->op_line++; + } + } + + /* image rendering */ + if (ida->op_work && ida->op_preview) { + for (ida->line = start; ida->line < end; ida->line++) { + ida->op_line = ida->line; + ida->op_work(&ida->img,&ida->op_rect, + ida->preview_line,ida->line,ida->op_data); + viewer_renderline(ida,ida->preview_line); + } + } else { + for (ida->line = start; ida->line < end; ida->line++) { + scanline = ida->img.data + ida->img.i.width * ida->line * 3; + viewer_renderline(ida,scanline); + } + } + + /* trigger redraw */ + XClearArea(XtDisplay(ida->widget), XtWindow(ida->widget), + 0, viewer_i2s(ida->zoom,start), + ida->scrwidth, viewer_i2s(ida->zoom,ida->steps), True); + + /* all done ? */ + if (ida->line == ida->img.i.height) { + viewer_cleanup(ida); + ida->wproc = 0; +#if 1 + if (args.testload) + XtCallActionProc(ida->widget,"Next",NULL,NULL,0); +#endif + return TRUE; + } + return FALSE; +} + +static void viewer_workstart(struct ida_viewer *ida) +{ + /* (re-) start */ + ida->line = 0; + if (!ida->wproc) + ida->wproc = XtAppAddWorkProc(app_context,viewer_workproc,ida); +} + +static void viewer_workstop(struct ida_viewer *ida) +{ + if (!ida->wproc) + return; + + viewer_cleanup(ida); + XtRemoveWorkProc(ida->wproc); + ida->wproc = 0; +} + +static void viewer_workfinish(struct ida_viewer *ida) +{ + char *scanline; + + if (ida->load_read) { + for (ida->line = ida->load_line; ida->line < ida->img.i.height;) { + scanline = ida->img.data + ida->img.i.width * ida->line * 3; + ida->load_read(scanline,ida->load_line,ida->load_data); + ida->line++; + ida->load_line++; + } + } + if (ida->op_work && 0 == ida->op_preview) { + for (ida->line = ida->op_line; ida->line < ida->img.i.height;) { + scanline = ida->img.data + ida->img.i.width * ida->line * 3; + ida->op_work(&ida->op_src,&ida->op_rect, + scanline,ida->op_line,ida->op_data); + ida->line++; + ida->op_line++; + } + } + viewer_workstop(ida); +} + +/* ----------------------------------------------------------------------- */ + +static void +viewer_new_view(struct ida_viewer *ida) +{ + if (NULL != ida->ximage) + x11_destroy_ximage(ida->widget,ida->ximage,ida->ximage_shm); + if (NULL != ida->rgb_line) + free(ida->rgb_line); + if (NULL != ida->dither_line) + free(ida->dither_line); + if (NULL != ida->preview_line) + free(ida->preview_line); + + ida->scrwidth = viewer_i2s(ida->zoom,ida->img.i.width); + ida->scrheight = viewer_i2s(ida->zoom,ida->img.i.height); + ida->steps = PROCESS_LINES; + if (ida->zoom < 0) + while ((ida->steps % (-ida->zoom+1)) != 0) + ida->steps++; + + ida->rgb_line = malloc(ida->scrwidth*3); + ida->dither_line = malloc(ida->scrwidth); + ida->preview_line = malloc(ida->img.i.width*3); + ida->ximage = x11_create_ximage(ida->widget, ida->scrwidth, ida->scrheight, + &ida->ximage_shm); + if (NULL == ida->ximage) { + ida->zoom--; + return viewer_new_view(ida); + } + XtVaSetValues(ida->widget, + XtNwidth, ida->scrwidth, + XtNheight, ida->scrheight, + NULL); + viewer_workstart(ida); +} + +static void +viewer_timeout(XtPointer client_data, XtIntervalId *id); + +static int +viewer_rubber_draw(struct ida_viewer *ida) +{ + XGCValues values; + struct ida_rect r = ida->current; + int x,y,w,h; + + values.function = GXxor; + values.foreground = ida->mask; + XChangeGC(dpy,ida->wgc,GCFunction|GCForeground,&values); + if (r.x1 < r.x2) { + x = viewer_i2s(ida->zoom,r.x1); + w = viewer_i2s(ida->zoom,r.x2 - r.x1); + } else { + x = viewer_i2s(ida->zoom,r.x2); + w = viewer_i2s(ida->zoom,r.x1 - r.x2); + } + if (r.y1 < r.y2) { + y = viewer_i2s(ida->zoom,r.y1); + h = viewer_i2s(ida->zoom,r.y2 - r.y1); + } else { + y = viewer_i2s(ida->zoom,r.y2); + h = viewer_i2s(ida->zoom,r.y1 - r.y2); + } + if (0 == h && 0 == w) + return 0; + if (w) + w--; + if (h) + h--; + XDrawRectangle(dpy,XtWindow(ida->widget),ida->wgc,x,y,w,h); + return 1; +} + +static void +viewer_rubber_off(struct ida_viewer *ida) +{ + if (ida->marked) + viewer_rubber_draw(ida); + ida->marked = 0; + if (ida->timer) + XtRemoveTimeOut(ida->timer); + ida->timer = 0; +} + +static void +viewer_rubber_on(struct ida_viewer *ida) +{ + ida->marked = viewer_rubber_draw(ida); + if (ida->marked) + ida->timer = XtAppAddTimeOut(app_context,RUBBER_INTERVAL, + viewer_timeout,ida); +} + +static void +viewer_timeout(XtPointer client_data, XtIntervalId *id) +{ + struct ida_viewer *ida = client_data; + + ida->timer = 0; + viewer_rubber_off(ida); + ida->mask <<= 1; + if ((ida->mask & 0x10) == 0x10) + ida->mask |= 0x01; + viewer_rubber_on(ida); +} + +static void +viewer_redraw(Widget widget, XtPointer client_data, + XEvent *ev, Boolean *cont) +{ + struct ida_viewer *ida = client_data; + XExposeEvent *event; + XGCValues values; + + if (ev->type != Expose) + return; + event = (XExposeEvent*)ev; + + if (NULL == ida->ximage) + return; + if (event->x + event->width > (int)ida->scrwidth) + return; + if (event->y + event->height > (int)ida->scrheight) + return; + if (NULL == ida->wgc) + ida->wgc = XCreateGC(XtDisplay(widget), XtWindow(widget), 0, NULL); + + viewer_rubber_off(ida); + values.function = GXcopy; + XChangeGC(dpy,ida->wgc,GCFunction,&values); + XPUTIMAGE(XtDisplay(ida->widget), XtWindow(widget), + ida->wgc, ida->ximage, + event->x, event->y, event->x, event->y, + event->width, event->height); + viewer_rubber_on(ida); +} + +static int +viewer_pos2state(struct ida_viewer *ida, int x, int y) +{ + int x1,x2,y1,y2; + + if (POINTER_PICK == ida->state) + return ida->state; + + x1 = viewer_i2s(ida->zoom,ida->current.x1); + x2 = viewer_i2s(ida->zoom,ida->current.x2); + y1 = viewer_i2s(ida->zoom,ida->current.y1); + y2 = viewer_i2s(ida->zoom,ida->current.y2); + if ((x1 < x && x < x2) || (x2 < x && x < x1)) { + if (y1-RUBBER_RANGE < y && y < y1+RUBBER_RANGE) + return RUBBER_Y1; + if (y2-RUBBER_RANGE < y && y < y2+RUBBER_RANGE) + return RUBBER_Y2; + } + if ((y1 < y && y < y2) || (y2 < y && y < y1)) { + if (x1-RUBBER_RANGE < x && x < x1+RUBBER_RANGE) + return RUBBER_X1; + if (x2-RUBBER_RANGE < x && x < x2+RUBBER_RANGE) + return RUBBER_X2; + } + if (((x1 < x && x < x2) || (x2 < x && x < x1)) && + ((y1 < y && y < y2) || (y2 < y && y < y1))) + return RUBBER_MOVE; + return RUBBER_NEW; +} + +static void +viewer_mouse(Widget widget, XtPointer client_data, + XEvent *ev, Boolean *cont) +{ + struct ida_viewer *ida = client_data; + int state = POINTER_NORMAL; + unsigned char *pix; + int x,y; + + viewer_rubber_off(ida); + + switch (ev->type) { + case ButtonPress: + { + XButtonEvent *eb = (XButtonEvent*)ev; + + if (eb->button != Button1) + goto out; + ida->state = viewer_pos2state(ida,eb->x,eb->y); + switch (ida->state) { + case POINTER_PICK: + x = viewer_i2s(-ida->zoom,eb->x); + y = viewer_i2s(-ida->zoom,eb->y); + pix = ida->img.data + ida->img.i.width*y*3 + x*3; + ida->pick_cb(x,y,pix,ida->pick_data); + ida->pick_cb = NULL; + ida->pick_data = NULL; + ida->state = POINTER_NORMAL; + state = POINTER_NORMAL; + break; + case RUBBER_NEW: + ida->mask = 0x33333333; + ida->current.x1 = ida->current.x2 = viewer_i2s(-ida->zoom,eb->x); + ida->current.y1 = ida->current.y2 = viewer_i2s(-ida->zoom,eb->y); + break; + case RUBBER_MOVE: + ida->last_x = viewer_i2s(-ida->zoom,eb->x); + ida->last_y = viewer_i2s(-ida->zoom,eb->y); + break; + case RUBBER_X1: + ida->current.x1 = viewer_i2s(-ida->zoom,eb->x); + break; + case RUBBER_Y1: + ida->current.y1 = viewer_i2s(-ida->zoom,eb->y); + break; + case RUBBER_X2: + ida->current.x2 = viewer_i2s(-ida->zoom,eb->x); + break; + case RUBBER_Y2: + ida->current.y2 = viewer_i2s(-ida->zoom,eb->y); + break; + } + state = ida->state; + break; + } + case MotionNotify: + { + XMotionEvent *em = (XMotionEvent*)ev; + + if (!(em->state & Button1Mask)) { + state = viewer_pos2state(ida,em->x,em->y); + goto out; + } + switch (ida->state) { + case RUBBER_NEW: + ida->current.x2 = viewer_i2s(-ida->zoom,em->x); + ida->current.y2 = viewer_i2s(-ida->zoom,em->y); + if (em->state & ShiftMask) { + /* square selection */ + int xlen,ylen; + xlen = abs(ida->current.x1 - ida->current.x2); + ylen = abs(ida->current.y1 - ida->current.y2); + if (ylen > xlen) { + if (ida->current.x1 < ida->current.x2) + ida->current.x2 -= (xlen - ylen); + else + ida->current.x2 += (xlen - ylen); + } else { + if (ida->current.y1 < ida->current.y2) + ida->current.y2 -= (ylen - xlen); + else + ida->current.y2 += (ylen - xlen); + } + } + break; + case RUBBER_MOVE: + x = viewer_i2s(-ida->zoom,em->x); + y = viewer_i2s(-ida->zoom,em->y); + ida->current.x1 += (x - ida->last_x); + ida->current.x2 += (x - ida->last_x); + ida->current.y1 += (y - ida->last_y); + ida->current.y2 += (y - ida->last_y); + ida->last_x = x; + ida->last_y = y; + break; + case RUBBER_X1: + ida->current.x1 = viewer_i2s(-ida->zoom,em->x); + break; + case RUBBER_Y1: + ida->current.y1 = viewer_i2s(-ida->zoom,em->y); + break; + case RUBBER_X2: + ida->current.x2 = viewer_i2s(-ida->zoom,em->x); + break; + case RUBBER_Y2: + ida->current.y2 = viewer_i2s(-ida->zoom,em->y); + break; + } + state = ida->state; + break; + } + case ButtonRelease: + { + XButtonEvent *eb = (XButtonEvent*)ev; + + if (eb->button != Button1) + goto out; + ida->state = POINTER_NORMAL; + state = ida->state; + break; + } + } + + if (ida->current.x1 < 0) + ida->current.x1 = 0; + if (ida->current.x1 > ida->img.i.width) + ida->current.x1 = ida->img.i.width; + if (ida->current.x2 < 0) + ida->current.x2 = 0; + if (ida->current.x2 > ida->img.i.width) + ida->current.x2 = ida->img.i.width; + if (ida->current.y1 < 0) + ida->current.y1 = 0; + if (ida->current.y1 > ida->img.i.height) + ida->current.y1 = ida->img.i.height; + if (ida->current.y2 < 0) + ida->current.y2 = 0; + if (ida->current.y2 > ida->img.i.height) + ida->current.y2 = ida->img.i.height; + + out: + XDefineCursor(dpy, XtWindow(widget), ptrs[state]); + viewer_rubber_on(ida); +} + +/* ----------------------------------------------------------------------- */ +/* public stuff */ + +void viewer_pick(struct ida_viewer *ida, viewer_pick_cb cb, XtPointer data) +{ + if (POINTER_NORMAL != ida->state) + return; + if (debug) + fprintf(stderr,"viewer_pick\n"); + ida->state = POINTER_PICK; + ida->pick_cb = cb; + ida->pick_data = data; +} + +void viewer_unpick(struct ida_viewer *ida) +{ + if (POINTER_PICK != ida->state) + return; + if (debug) + fprintf(stderr,"viewer_unpick\n"); + ida->state = POINTER_NORMAL; + ida->pick_cb = NULL; + ida->pick_data = NULL; +} + +void +viewer_autozoom(struct ida_viewer *ida) +{ + if (GET_AUTOZOOM()) { + ida->zoom = 0; + while (XtScreen(ida->widget)->width < viewer_i2s(ida->zoom,ida->img.i.width) || + XtScreen(ida->widget)->height < viewer_i2s(ida->zoom,ida->img.i.height)) + ida->zoom--; + } + viewer_new_view(ida); +} + +void +viewer_setzoom(struct ida_viewer *ida, int zoom) +{ + ida->zoom = zoom; + viewer_new_view(ida); +} + +static void +viewer_op_rect(struct ida_viewer *ida) +{ + if (ida->current.x1 == ida->current.x2 && + ida->current.y1 == ida->current.y2) { + /* full image */ + ida->op_rect.x1 = 0; + ida->op_rect.x2 = ida->img.i.width; + ida->op_rect.y1 = 0; + ida->op_rect.y2 = ida->img.i.height; + return; + } else { + /* have selection */ + if (ida->current.x1 < ida->current.x2) { + ida->op_rect.x1 = ida->current.x1; + ida->op_rect.x2 = ida->current.x2; + } else { + ida->op_rect.x1 = ida->current.x2; + ida->op_rect.x2 = ida->current.x1; + } + if (ida->current.y1 < ida->current.y2) { + ida->op_rect.y1 = ida->current.y1; + ida->op_rect.y2 = ida->current.y2; + } else { + ida->op_rect.y1 = ida->current.y2; + ida->op_rect.y2 = ida->current.y1; + } + } +} + +int +viewer_start_op(struct ida_viewer *ida, struct ida_op *op, void *parm) +{ + struct ida_image dst; + + ptr_busy(); + viewer_workfinish(ida); + viewer_rubber_off(ida); + + /* try init */ + viewer_op_rect(ida); + if (debug) + fprintf(stderr,"viewer_start_op: init %s(%p)\n",op->name,parm); + ida->op_data = op->init(&ida->img,&ida->op_rect,&dst.i,parm); + ptr_idle(); + if (NULL == ida->op_data) + return -1; + dst.data = malloc(dst.i.width * dst.i.height * 3); + + /* prepare background processing */ + if (ida->undo.data) { + free(ida->undo.data); + memset(&ida->undo,0,sizeof(ida->undo)); + } + if (ida->op_src.data) { + fprintf(stderr,"have op_src buffer /* shouldn't happen */"); + free(ida->op_src.data); + } + ida->op_src = ida->img; + ida->img = dst; + ida->op_line = 0; + ida->op_work = op->work; + ida->op_done = op->done; + ida->op_preview = 0; + + if (ida->op_src.i.width != ida->img.i.width || + ida->op_src.i.height != ida->img.i.height) { + memset(&ida->current,0,sizeof(ida->current)); + viewer_autozoom(ida); + } else + viewer_new_view(ida); + return 0; +} + +int +viewer_undo(struct ida_viewer *ida) +{ + int resize; + + viewer_workfinish(ida); + if (NULL == ida->undo.data) + return -1; + viewer_rubber_off(ida); + memset(&ida->current,0,sizeof(ida->current)); + + resize = (ida->undo.i.width != ida->img.i.width || + ida->undo.i.height != ida->img.i.height); + free(ida->img.data); + ida->img = ida->undo; + memset(&ida->undo,0,sizeof(ida->undo)); + + if (resize) + viewer_autozoom(ida); + else + viewer_new_view(ida); + return 0; +} + +int +viewer_start_preview(struct ida_viewer *ida, struct ida_op *op, void *parm) +{ + struct ida_image dst; + + viewer_workfinish(ida); + + /* try init */ + viewer_op_rect(ida); + ida->op_data = op->init(&ida->img,&ida->op_rect,&dst.i,parm); + if (NULL == ida->op_data) + return -1; + + /* prepare background preview */ + ida->op_line = 0; + ida->op_work = op->work; + ida->op_done = op->done; + ida->op_preview = 1; + + viewer_workstart(ida); + return 0; +} + +int +viewer_cancel_preview(struct ida_viewer *ida) +{ + viewer_workstop(ida); + viewer_workstart(ida); + return 0; +} + +int +viewer_loader_start(struct ida_viewer *ida, struct ida_loader *loader, + FILE *fp, char *filename, unsigned int page) +{ + struct ida_image_info info; + void *data; + + /* init loader */ + ptr_busy(); + memset(&info,0,sizeof(info)); + data = loader->init(fp,filename,page,&info,0); + ptr_idle(); + if (NULL == data) { + fprintf(stderr,"loading %s [%s] FAILED\n",filename,loader->name); + if (fp) + hex_display(filename); + return -1; + } + + /* ok, going to load new image */ + viewer_workstop(ida); + viewer_rubber_off(ida); + memset(&ida->current,0,sizeof(ida->current)); + if (ida->undo.data) { + free(ida->undo.data); + memset(&ida->undo,0,sizeof(ida->undo)); + } + if (NULL != ida->img.data) + free(ida->img.data); + ida->file = filename; + ida->img.i = info; + ida->img.data = malloc(ida->img.i.width * ida->img.i.height * 3); + + /* prepare background loading */ + ida->load_line = 0; + ida->load_read = loader->read; + ida->load_done = loader->done; + ida->load_data = data; + + viewer_autozoom(ida); + return info.npages; +} + +int +viewer_loadimage(struct ida_viewer *ida, char *filename, unsigned int page) +{ + struct list_head *item; + struct ida_loader *loader; + char blk[512]; + FILE *fp; + + if (NULL == (fp = fopen(filename, "r"))) { + fprintf(stderr,"fopen %s: %s\n",filename,strerror(errno)); + return -1; + } + if (debug) + fprintf(stderr,"load: %s\n",filename); + memset(blk,0,sizeof(blk)); + fread(blk,1,sizeof(blk),fp); + rewind(fp); + + /* pick loader */ + list_for_each(item,&loaders) { + loader = list_entry(item, struct ida_loader, list); +#if 0 + if (NULL == loader->magic) + break; +#else + if (NULL == loader->magic) + continue; +#endif + if (0 == memcmp(blk+loader->moff,loader->magic,loader->mlen)) + return viewer_loader_start(ida,loader,fp,filename,page); + } + fprintf(stderr,"%s: unknown format\n",filename); + hex_display(filename); + fclose(fp); + return -1; +} + +int +viewer_setimage(struct ida_viewer *ida, struct ida_image *img, char *name) +{ + /* ok, going to load new image */ + viewer_workstop(ida); + viewer_rubber_off(ida); + memset(&ida->current,0,sizeof(ida->current)); + if (ida->undo.data) { + free(ida->undo.data); + memset(&ida->undo,0,sizeof(ida->undo)); + } + + if (NULL != ida->img.data) + free(ida->img.data); + ida->file = name; + ida->img = *img; + + viewer_autozoom(ida); + return 0; +} + +struct ida_viewer* +viewer_init(Widget widget) +{ + Colormap cmap = DefaultColormapOfScreen(XtScreen(widget)); + struct ida_viewer *ida; + XColor white,red,dummy; + unsigned int i; + + ida = malloc(sizeof(*ida)); + memset(ida,0,sizeof(*ida)); + ida->widget = widget; + XtAddEventHandler(widget,ExposureMask,False,viewer_redraw,ida); + XtAddEventHandler(widget, + ButtonPressMask | + ButtonReleaseMask | + PointerMotionMask, + False,viewer_mouse,ida); + + ptrs[POINTER_NORMAL] = XCreateFontCursor(dpy,XC_left_ptr); + ptrs[POINTER_BUSY] = XCreateFontCursor(dpy,XC_watch); + ptrs[POINTER_PICK] = XCreateFontCursor(dpy,XC_tcross); + ptrs[RUBBER_NEW] = XCreateFontCursor(dpy,XC_left_ptr); + ptrs[RUBBER_MOVE] = XCreateFontCursor(dpy,XC_fleur); + ptrs[RUBBER_X1] = XCreateFontCursor(dpy,XC_sb_h_double_arrow); + ptrs[RUBBER_X2] = XCreateFontCursor(dpy,XC_sb_h_double_arrow); + ptrs[RUBBER_Y1] = XCreateFontCursor(dpy,XC_sb_v_double_arrow); + ptrs[RUBBER_Y2] = XCreateFontCursor(dpy,XC_sb_v_double_arrow); + if (XAllocNamedColor(dpy,cmap,"white",&white,&dummy) && + XAllocNamedColor(dpy,cmap,"red",&red,&dummy)) + for (i = 0; i < sizeof(ptrs)/sizeof(Cursor); i++) + XRecolorCursor(dpy,ptrs[i],&red,&white); + + return ida; +} diff --git a/viewer.h b/viewer.h new file mode 100644 index 0000000..0f81c5b --- /dev/null +++ b/viewer.h @@ -0,0 +1,96 @@ +#include <X11/Intrinsic.h> + +#define POINTER_NORMAL 0 +#define POINTER_BUSY 1 +#define POINTER_PICK 2 +#define RUBBER_NEW 3 +#define RUBBER_MOVE 4 +#define RUBBER_X1 5 +#define RUBBER_Y1 6 +#define RUBBER_X2 7 +#define RUBBER_Y2 8 +#define POINTER_COUNT 9 + +extern Cursor ptrs[]; + +/* ----------------------------------------------------------------------- */ + +typedef void (*viewer_pick_cb)(int x, int y, unsigned char *pix, + XtPointer data); + +struct ida_viewer { + /* x11 stuff */ + Widget widget; + GC wgc; + XtIntervalId timer; + + /* image data */ + struct ida_image img; + struct ida_image undo; + char *file; + + /* view data */ + int zoom; + unsigned int scrwidth, scrheight; + XImage *ximage; + void *ximage_shm; + unsigned char *rgb_line; + unsigned char *dither_line; + unsigned char *preview_line; + + /* marked rectangle */ + struct ida_rect current; + int marked,state; + int last_x,last_y; + unsigned long mask; + + /* pixel picker */ + viewer_pick_cb pick_cb; + XtPointer pick_data; + + /* workproc state */ + XtWorkProcId wproc; + unsigned int line; + unsigned int steps; + + /* image loader */ + unsigned int load_line; + void (*load_read)(unsigned char *dst, unsigned int line, + void *data); + void (*load_done)(void *data); + void *load_data; + + /* image operation */ + struct ida_image op_src; + struct ida_rect op_rect; + unsigned int op_line; + unsigned int op_preview; + void (*op_work)(struct ida_image *src, struct ida_rect *rect, + unsigned char *dst, int line, + void *data); + void (*op_done)(void *data); + void *op_data; +}; + +/* ----------------------------------------------------------------------- */ + +Pixmap image_to_pixmap(struct ida_image *img); + +/* ----------------------------------------------------------------------- */ + +struct ida_viewer* viewer_init(Widget widget); +int viewer_loader_start(struct ida_viewer *ida, struct ida_loader *loader, + FILE *fp, char *filename, unsigned int page); +int viewer_loadimage(struct ida_viewer *ida, char *filename, unsigned int page); +int viewer_setimage(struct ida_viewer *ida, struct ida_image *img, char *name); +void viewer_autozoom(struct ida_viewer *ida); +void viewer_setzoom(struct ida_viewer *ida, int zoom); +int viewer_i2s(int zoom, int val); +int viewer_undo(struct ida_viewer *ida); +int viewer_start_op(struct ida_viewer *ida, struct ida_op *op, void *parm); +int viewer_start_preview(struct ida_viewer *ida, struct ida_op *op, + void *parm); +int viewer_cancel_preview(struct ida_viewer *ida); + +void viewer_pick(struct ida_viewer *ida, viewer_pick_cb cb, XtPointer data); +void viewer_unpick(struct ida_viewer *ida); diff --git a/wr/Makefile b/wr/Makefile new file mode 100644 index 0000000..4e33e30 --- /dev/null +++ b/wr/Makefile @@ -0,0 +1,2 @@ +default: + cd ..; $(MAKE) diff --git a/wr/write-jpeg.c b/wr/write-jpeg.c new file mode 100644 index 0000000..2e39c68 --- /dev/null +++ b/wr/write-jpeg.c @@ -0,0 +1,103 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <errno.h> +#include <jpeglib.h> + +#include "readers.h" +#include "writers.h" +#include "misc.h" + +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> +#include <Xm/Xm.h> +#include <Xm/Text.h> +#include <Xm/SelectioB.h> +#include "RegEdit.h" +#include "ida.h" +#include "viewer.h" + +/* ---------------------------------------------------------------------- */ +/* jpeg writer */ + +static Widget jpeg_shell; +static Widget jpeg_text; +static int jpeg_quality = 75; + +static void +jpeg_button_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XmSelectionBoxCallbackStruct *cb = call_data; + + if (XmCR_OK == cb->reason) { + jpeg_quality = atoi(XmTextGetString(jpeg_text)); + do_save_print(); + } + XtUnmanageChild(jpeg_shell); +} + +static int +jpeg_conf(Widget parent, struct ida_image *img) +{ + char tmp[32]; + + if (!jpeg_shell) { + /* build dialog */ + jpeg_shell = XmCreatePromptDialog(parent,"jpeg",NULL,0); + XmdRegisterEditres(XtParent(jpeg_shell)); + XtUnmanageChild(XmSelectionBoxGetChild(jpeg_shell,XmDIALOG_HELP_BUTTON)); + jpeg_text = XmSelectionBoxGetChild(jpeg_shell,XmDIALOG_TEXT); + XtAddCallback(jpeg_shell,XmNokCallback,jpeg_button_cb,NULL); + XtAddCallback(jpeg_shell,XmNcancelCallback,jpeg_button_cb,NULL); + } + sprintf(tmp,"%d",jpeg_quality); + XmTextSetString(jpeg_text,tmp); + XtManageChild(jpeg_shell); + return 0; +} + +static int +jpeg_write(FILE *fp, struct ida_image *img) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + unsigned char *line; + unsigned int i; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + jpeg_stdio_dest(&cinfo, fp); + cinfo.image_width = img->i.width; + cinfo.image_height = img->i.height; + if (img->i.dpi) { + cinfo.density_unit = 1; + cinfo.X_density = img->i.dpi; + cinfo.Y_density = img->i.dpi; + } + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, jpeg_quality, TRUE); + jpeg_start_compress(&cinfo, TRUE); + + for (i = 0, line = img->data; i < img->i.height; i++, line += img->i.width*3) + jpeg_write_scanlines(&cinfo, &line, 1); + + jpeg_finish_compress(&(cinfo)); + jpeg_destroy_compress(&(cinfo)); + return 0; +} + +struct ida_writer jpeg_writer = { + label: "JPEG", + ext: { "jpg", "jpeg", NULL}, + write: jpeg_write, + conf: jpeg_conf, +}; + +static void __init init_wr(void) +{ + write_register(&jpeg_writer); +} + diff --git a/wr/write-png.c b/wr/write-png.c new file mode 100644 index 0000000..ee4fc09 --- /dev/null +++ b/wr/write-png.c @@ -0,0 +1,76 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <png.h> + +#include "readers.h" +#include "writers.h" +#include "viewer.h" + +/* ---------------------------------------------------------------------- */ +/* save */ + +static int +png_write(FILE *fp, struct ida_image *img) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_bytep row; + unsigned int y; + + /* Create and initialize the png_struct with the desired error handler + * functions. If you want to use the default stderr and longjump method, + * you can supply NULL for the last three parameters. We also check that + * the library version is compatible with the one used at compile time, + * in case we are using dynamically linked libraries. REQUIRED. + */ + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); + if (png_ptr == NULL) + goto oops; + + /* Allocate/initialize the image information data. REQUIRED */ + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + goto oops; + if (setjmp(png_jmpbuf(png_ptr))) + goto oops; + + png_init_io(png_ptr, fp); + png_set_IHDR(png_ptr, info_ptr, img->i.width, img->i.height, 8, + PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + if (img->i.dpi) { + png_set_pHYs(png_ptr, info_ptr, + res_inch_to_m(img->i.dpi), + res_inch_to_m(img->i.dpi), + PNG_RESOLUTION_METER); + } + png_write_info(png_ptr, info_ptr); + png_set_packing(png_ptr); + + for (y = 0; y < img->i.height; y++) { + row = img->data + y * 3 * img->i.width; + png_write_rows(png_ptr, &row, 1); + } + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + return 0; + + oops: + fprintf(stderr,"can't save image: libpng error\n"); + if (png_ptr) + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + return -1; +} + +static struct ida_writer png_writer = { + label: "PNG", + ext: { "png", NULL}, + write: png_write, +}; + +static void __init init_wr(void) +{ + write_register(&png_writer); +} diff --git a/wr/write-ppm.c b/wr/write-ppm.c new file mode 100644 index 0000000..d16035b --- /dev/null +++ b/wr/write-ppm.c @@ -0,0 +1,34 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "readers.h" +#include "writers.h" +#include "viewer.h" + +/* ---------------------------------------------------------------------- */ +/* save */ + +static int +ppm_write(FILE *fp, struct ida_image *img) +{ + fprintf(fp,"P6\n" + "# written by ida " VERSION "\n" + "# http://bytesex.org/ida/\n" + "%d %d\n255\n", + img->i.width,img->i.height); + fwrite(img->data, img->i.height, 3*img->i.width, fp); + return 0; +} + +static struct ida_writer ppm_writer = { + label: "PPM", + ext: { "ppm", NULL}, + write: ppm_write, +}; + +static void __init init_wr(void) +{ + write_register(&ppm_writer); +} diff --git a/wr/write-ps.c b/wr/write-ps.c new file mode 100644 index 0000000..914a896 --- /dev/null +++ b/wr/write-ps.c @@ -0,0 +1,475 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <inttypes.h> + +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> +#include <Xm/Xm.h> +#include <Xm/Text.h> +#include <Xm/SelectioB.h> +#include <Xm/DrawingA.h> +#include <Xm/RowColumn.h> +#include <Xm/PushB.h> +#include <Xm/Scale.h> +#include <Xm/Label.h> +#include "RegEdit.h" + +#include "ida.h" +#include "readers.h" +#include "writers.h" +#include "viewer.h" + +struct PAPER { + char *name; + int width,height; +}; + +static struct PAPER formats[] = { + { + name: "A4", + width: 595, + height: 842, + },{ + name: "Letter", + width: 612, + height: 792, + },{ + /* EOF */ + } +}; + +static const char *header = +"%%!PS-Adobe-2.0 EPSF-2.0\n" +"%%%%Creator: ida " VERSION " (http://bytesex.org/ida/)\n" +"%%%%Pages: 1\n" +"%%%%BoundingBox: %d %d %d %d\n" +"%%%%DocumentFonts: \n" +"%%%%EndComments\n" +"%%%%EndProlog\n" +"\n" +"%%%%Page: 1 1" +"\n" +"/origstate save def\n" +"20 dict begin\n"; + +static const char *footer = +"\n" +"showpage\n" +"end\n" +"origstate restore\n" +"%%%%Trailer\n"; + +/* taken from xwd2ps, ftp://ftp.x.org/R5contrib/xwd2ps.tar.Z */ +static const char *ColorImage = +"% define 'colorimage' if it isn't defined\n" +"% ('colortogray' and 'mergeprocs' come from xwd2ps\n" +"% via xgrab)\n" +"/colorimage where % do we know about 'colorimage'?\n" +" { pop } % yes: pop off the 'dict' returned\n" +" { % no: define one\n" +" /colortogray { % define an RGB->I function\n" +" /rgbdata exch store % call input 'rgbdata'\n" +" rgbdata length 3 idiv\n" +" /npixls exch store\n" +" /rgbindx 0 store\n" +" 0 1 npixls 1 sub {\n" +" grays exch\n" +" rgbdata rgbindx get 20 mul % Red\n" +" rgbdata rgbindx 1 add get 32 mul % Green\n" +" rgbdata rgbindx 2 add get 12 mul % Blue\n" +" add add 64 idiv % I = .5G + .31R + .18B\n" +" put\n" +" /rgbindx rgbindx 3 add store\n" +" } for\n" +" grays 0 npixls getinterval\n" +" } bind def\n" +"\n" +" % Utility procedure for colorimage operator.\n" +" % This procedure takes two procedures off the\n" +" % stack and merges them into a single procedure.\n" +"\n" +" /mergeprocs { % def\n" +" dup length\n" +" 3 -1 roll\n" +" dup\n" +" length\n" +" dup\n" +" 5 1 roll\n" +" 3 -1 roll\n" +" add\n" +" array cvx\n" +" dup\n" +" 3 -1 roll\n" +" 0 exch\n" +" putinterval\n" +" dup\n" +" 4 2 roll\n" +" putinterval\n" +" } bind def\n" +"\n" +" /colorimage { % def\n" +" pop pop % remove 'false 3' operands\n" +" {colortogray} mergeprocs\n" +" image\n" +" } bind def\n" +" } ifelse % end of 'false' case\n" +"\n"; + + +/* ---------------------------------------------------------------------- */ +/* save */ + +#define PORTRAIT 0 +#define LANDSCAPE 1 + +#define DRAW_SIZE 200 +#define DRAW_SCALE 6 +#define DSCALED(x) (((x)+DRAW_SCALE/2)/DRAW_SCALE) + +static struct ps_options { + Widget shell,draw,scale,geo; + int xscale,yscale; + GC gc; + int lastx,lasty; + + int format; + int ori; + int scaling; + + int iwidth,iheight,ires; + int pwidth,pheight; + int mwidth,mheight; + int width,height,xcenter,ycenter; +} ps; + +static void +ps_draw(Widget widget, XtPointer client_data, XtPointer calldata) +{ + XmDrawingAreaCallbackStruct *cb = calldata; + XExposeEvent *e; + XmString str; + char buf[128]; + int x,y,w,h; + + if (!(cb->reason == XmCR_EXPOSE)) + return; + e = (XExposeEvent*)cb->event; + if (e->count) + return; + + if (!ps.gc) + ps.gc = XCreateGC(dpy,XtWindow(app_shell),0,NULL); + + w = DSCALED(ps.pwidth); + h = DSCALED(ps.pheight); + x = (DRAW_SIZE-w) / 2; + y = (DRAW_SIZE-h) / 2; + XDrawRectangle(dpy,XtWindow(widget),ps.gc, x,y,w,h); + + w = DSCALED(ps.width); + h = DSCALED(ps.height); + x += DSCALED(ps.xcenter - ps.width/2); + y += DSCALED(ps.ycenter - ps.height/2); + XFillRectangle(dpy,XtWindow(widget),ps.gc, x,y,w,h); + + sprintf(buf,"%d dpi",ps.iwidth * 72 / ps.width); + str = XmStringGenerate(buf, NULL, XmMULTIBYTE_TEXT, NULL); + XtVaSetValues(ps.geo,XmNlabelString,str,NULL); + XmStringFree(str); +} + +static void +ps_defaults(void) +{ + /* max size, keep aspect ratio */ + if (ps.ori == PORTRAIT) { + ps.pwidth = formats[ps.format].width; + ps.pheight = formats[ps.format].height; + } else { + ps.pheight = formats[ps.format].width; + ps.pwidth = formats[ps.format].height; + } + + if (ps.iwidth * ps.pheight > ps.iheight * ps.pwidth) { + ps.mwidth = ps.pwidth; + ps.mheight = ps.iheight * ps.mwidth / ps.iwidth; + } else { + ps.mheight = ps.pheight; + ps.mwidth = ps.iwidth * ps.mheight / ps.iheight; + } + ps.scaling = 0; + if (ps.ires) { + /* Use image resolution to calculate default scaling factor. + * The image will be printed in original size if it fits into + * one page */ + ps.scaling = ps.iwidth * 72 * 1000 / ps.mwidth / ps.ires; + } + if (ps.scaling > 1000 || ps.scaling < 1) { + /* default: maxpect with some border */ + ps.scaling = 1000; + while (ps.mwidth * ps.scaling / 1000 + 50 > ps.mwidth || + ps.mheight * ps.scaling / 1000 + 50 > ps.mheight) + ps.scaling--; + } + XmScaleSetValue(ps.scale,ps.scaling); + ps.width = ps.mwidth * ps.scaling / 1000; + ps.height = ps.mheight * ps.scaling / 1000; + ps.xcenter = ps.pwidth/2; + ps.ycenter = ps.pheight/2; + + if (XtWindow(ps.draw)) + XClearArea(XtDisplay(ps.draw), XtWindow(ps.draw), + 0,0,0,0, True); +} + +static void +ps_ranges(void) +{ + if (ps.width == 0) + ps.width = 1; + if (ps.height == 0) + ps.height = 1; + if (ps.xcenter - ps.width/2 < 0) + ps.xcenter = ps.width/2; + if (ps.xcenter + ps.width/2 > ps.pwidth) + ps.xcenter = ps.pwidth - ps.width/2; + if (ps.ycenter - ps.height/2 < 0) + ps.ycenter = ps.height/2; + if (ps.ycenter + ps.height/2 > ps.pheight) + ps.ycenter = ps.pheight - ps.height/2; +} + +static void +ps_mouse(Widget widget, XtPointer client_data, + XEvent *ev, Boolean *cont) +{ + switch (ev->type) { + case ButtonPress: + { + XButtonEvent *e = (XButtonEvent*)ev; + + ps.lastx = e->x; + ps.lasty = e->y; + break; + } + case MotionNotify: + { + XMotionEvent *e = (XMotionEvent*)ev; + + if (e->state & Button1Mask) { + ps.xcenter += (e->x - ps.lastx) * DRAW_SCALE; + ps.ycenter += (e->y - ps.lasty) * DRAW_SCALE; + ps.lastx = e->x; + ps.lasty = e->y; + } + break; + default: + return; + } + } + ps_ranges(); + if (XtWindow(ps.draw)) + XClearArea(XtDisplay(ps.draw), XtWindow(ps.draw), + 0,0,0,0, True); +} + +static void +ps_paper_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + ps.format = (intptr_t)clientdata; + ps_defaults(); +} + +static void +ps_ori_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + ps.ori = (intptr_t)clientdata; + ps_defaults(); +} + +static void +ps_scaling_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XmScaleCallbackStruct *cd = call_data; + + ps.scaling = cd->value; + ps.width = ps.mwidth * ps.scaling / 1000; + ps.height = ps.mheight * ps.scaling / 1000; + ps_ranges(); + if (XtWindow(ps.draw)) + XClearArea(XtDisplay(ps.draw), XtWindow(ps.draw), + 0,0,0,0, True); +} + +static void +ps_button_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XmSelectionBoxCallbackStruct *cb = call_data; + + if (XmCR_OK == cb->reason) { + do_save_print(); + } + XtUnmanageChild(ps.shell); +} + +static int +ps_conf(Widget parent, struct ida_image *img) +{ + Widget rc,menu,push,opt; + Arg args[2]; + intptr_t i; + + if (!ps.shell) { + /* build dialog */ + ps.shell = XmCreatePromptDialog(parent,"ps",NULL,0); + XmdRegisterEditres(XtParent(ps.shell)); + XtUnmanageChild(XmSelectionBoxGetChild(ps.shell,XmDIALOG_HELP_BUTTON)); + XtUnmanageChild(XmSelectionBoxGetChild(ps.shell,XmDIALOG_SELECTION_LABEL)); + XtUnmanageChild(XmSelectionBoxGetChild(ps.shell,XmDIALOG_TEXT)); + XtAddCallback(ps.shell,XmNokCallback,ps_button_cb,NULL); + XtAddCallback(ps.shell,XmNcancelCallback,ps_button_cb,NULL); + + rc = XtVaCreateManagedWidget("rc1",xmRowColumnWidgetClass, + ps.shell,NULL); + ps.draw = XtVaCreateManagedWidget("draw",xmDrawingAreaWidgetClass,rc, + XtNwidth,DRAW_SIZE, + XtNheight,DRAW_SIZE, + NULL); + XtAddCallback(ps.draw,XmNexposeCallback,ps_draw,NULL); + XtAddEventHandler(ps.draw, + ButtonPressMask | + ButtonReleaseMask | + ButtonMotionMask, + False,ps_mouse,NULL); + rc = XtVaCreateManagedWidget("rc2",xmRowColumnWidgetClass, + rc,NULL); + + /* paper */ + menu = XmCreatePulldownMenu(rc,"paperM",NULL,0); + XtSetArg(args[0],XmNsubMenuId,menu); + opt = XmCreateOptionMenu(rc,"paper",args,1); + XtManageChild(opt); + for (i = 0; formats[i].name != NULL; i++) { + push = XtVaCreateManagedWidget(formats[i].name,xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,ps_paper_cb,(XtPointer)i); + } + + /* orientation */ + menu = XmCreatePulldownMenu(rc,"oriM",NULL,0); + XtSetArg(args[0],XmNsubMenuId,menu); + opt = XmCreateOptionMenu(rc,"ori",args,1); + XtManageChild(opt); + push = XtVaCreateManagedWidget("portrait",xmPushButtonWidgetClass, + menu,NULL); + XtAddCallback(push,XmNactivateCallback,ps_ori_cb,(XtPointer)PORTRAIT); + push = XtVaCreateManagedWidget("landscape",xmPushButtonWidgetClass, + menu,NULL); + XtAddCallback(push,XmNactivateCallback,ps_ori_cb,(XtPointer)LANDSCAPE); + + ps.scale = XtVaCreateManagedWidget("scale",xmScaleWidgetClass,rc,NULL); + XtAddCallback(ps.scale,XmNdragCallback,ps_scaling_cb,NULL); + XtAddCallback(ps.scale,XmNvalueChangedCallback,ps_scaling_cb,NULL); + + /* output */ + ps.geo = XtVaCreateManagedWidget("geo",xmLabelWidgetClass,rc,NULL); + } + + ps.iwidth = img->i.width; + ps.iheight = img->i.height; + ps.ires = 0; + if (ida->img.i.dpi) + ps.ires = ida->img.i.dpi; + ps_defaults(); + + XtManageChild(ps.shell); + return 0; +} + +static int +ps_write(FILE *fp, struct ida_image *img) +{ + unsigned int width,height,xoff,yoff; + unsigned int iwidth,iheight; + unsigned int x,y; + unsigned char *p; + + if (ps.ori == PORTRAIT) { + iwidth = img->i.width; + iheight = img->i.height; + width = ps.width; + height = ps.height; + xoff = ps.xcenter - ps.width/2; + yoff = (ps.pheight - ps.ycenter) - ps.height/2; + } else{ + iwidth = img->i.height; + iheight = img->i.width; + width = ps.height; + height = ps.width; + xoff = ps.ycenter - ps.height/2; + yoff = ps.xcenter - ps.width/2; + } + + /* PS header */ + fprintf(fp,header, /* includes bbox */ + xoff,yoff,xoff+width,yoff+height); + fprintf(fp,"\n" + "/pix %d string def\n" + "/grays %d string def\n" + "/npixls 0 def\n" + "/rgbindx 0 def\n" + "\n", + img->i.width*3,img->i.width); + fwrite(ColorImage,strlen(ColorImage),1,fp); + + fprintf(fp,"%d %d translate\n",xoff,yoff); + fprintf(fp,"%d %d scale\n",width,height); + + fprintf(fp,"\n" + "%d %d 8\n" + "[%d 0 0 -%d 0 %d]\n" + "{currentfile pix readhexstring pop}\n" + "false 3 colorimage\n", + iwidth,iheight,iwidth,iheight,iheight); + + /* image data + ps footer */ + if (ps.ori == PORTRAIT) { + p = img->data; + for (y = 0; y < img->i.height; y++) { + for (x = 0; x < img->i.width; x++) { + if (0 == (x % 10)) + fprintf(fp,"\n"); + fprintf(fp,"%02x%02x%02x ",p[0],p[1],p[2]); + p += 3; + } + fprintf(fp,"\n"); + } + } else { + for (x = img->i.width-1; x != -1; x--) { + p = img->data + 3*x; + for (y = 0; y < img->i.height; y++) { + if (0 == (y % 10)) + fprintf(fp,"\n"); + fprintf(fp,"%02x%02x%02x ",p[0],p[1],p[2]); + p += img->i.width*3; + } + fprintf(fp,"\n"); + } + } + fprintf(fp,footer); + return 0; +} + +struct ida_writer ps_writer = { + label: "PostScript", + ext: { "ps", "eps", NULL}, + write: ps_write, + conf: ps_conf, +}; + +static void __init init_wr(void) +{ + write_register(&ps_writer); +} + diff --git a/wr/write-tiff.c b/wr/write-tiff.c new file mode 100644 index 0000000..9d4910e --- /dev/null +++ b/wr/write-tiff.c @@ -0,0 +1,60 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <tiffio.h> + +#include "readers.h" +#include "writers.h" +#include "viewer.h" + +/* ---------------------------------------------------------------------- */ +/* save */ + +static int +tiff_write(FILE *fp, struct ida_image *img) +{ + TIFF *TiffHndl; + tdata_t buf; + unsigned int y; + + TiffHndl = TIFFFdOpen(fileno(fp),"42.tiff","w"); + if (TiffHndl == NULL) + return -1; + TIFFSetField(TiffHndl, TIFFTAG_IMAGEWIDTH, img->i.width); + TIFFSetField(TiffHndl, TIFFTAG_IMAGELENGTH, img->i.height); + TIFFSetField(TiffHndl, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(TiffHndl, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(TiffHndl, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(TiffHndl, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField(TiffHndl, TIFFTAG_ROWSPERSTRIP, 2); + TIFFSetField(TiffHndl, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); +#if 0 /* fixme: make this configureable */ + TIFFSetField(TiffHndl, TIFFTAG_COMPRESSION, COMPRESSION_LZW); + TIFFSetField(TiffHndl, TIFFTAG_PREDICTOR, 2); +#endif + if (img->i.dpi) { + float dpi = img->i.dpi; + TIFFSetField(TiffHndl, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); + TIFFSetField(TiffHndl, TIFFTAG_XRESOLUTION, dpi); + TIFFSetField(TiffHndl, TIFFTAG_YRESOLUTION, dpi); + } + + for (y = 0; y < img->i.height; y++) { + buf = img->data + 3*img->i.width*y; + TIFFWriteScanline(TiffHndl, buf, y, 0); + } + TIFFClose(TiffHndl); + return 0; +} + +static struct ida_writer tiff_writer = { + label: "TIFF", + ext: { "tif", "tiff", NULL}, + write: tiff_write, +}; + +static void __init init_wr(void) +{ + write_register(&tiff_writer); +} diff --git a/writers.c b/writers.c new file mode 100644 index 0000000..18a1991 --- /dev/null +++ b/writers.c @@ -0,0 +1,15 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "readers.h" +#include "writers.h" + +/* ----------------------------------------------------------------------- */ + +LIST_HEAD(writers); + +void write_register(struct ida_writer *writer) +{ + list_add_tail(&writer->list, &writers); +} diff --git a/writers.h b/writers.h new file mode 100644 index 0000000..bd4a2b3 --- /dev/null +++ b/writers.h @@ -0,0 +1,14 @@ +#include "list.h" +#include <X11/Intrinsic.h> + +/* save image files */ +struct ida_writer { + char *label; + char *ext[8]; + int (*write)(FILE *fp, struct ida_image *img); + int (*conf)(Widget widget, struct ida_image *img); + struct list_head list; +}; + +extern struct list_head writers; +void write_register(struct ida_writer *writer); @@ -0,0 +1,456 @@ +/* + * some X11 ximage / pixmaps rotines + * + * (c) 1996 Gerd Knorr <kraxel@goldbach.in-berlin.de> + * + * basic usage: + * 1) call x11_color_init() + * this does all the visual checking/colormap handling stuff and returns + * TRUECOLOR or PSEUDOCOLOR + * 2) create/load the image + * 3) call x11_create_pixmaps() + * For TRUECOLOR: It expects the data in one long (4 byte) per pixel. + * To create the long, run the rgb-values throuth the + * x11_lut_[red|green|blue] tables and or the results + * For PSEUDOCOLOR: The data is expected to be one byte per pixel, + * containing the results from dither_line (see dither.c) + * Not required to call init_dither, this is done by + * x11_color_init + * returns a pixmap. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ipc.h> +#include <sys/shm.h> + +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> +#include <X11/extensions/XShm.h> + +#include "x11.h" +#include "dither.h" + +extern Display *dpy; + +#define PERROR(str) fprintf(stderr,"%s:%d: %s: %s\n",__FILE__,__LINE__,str,strerror(errno)) + +/* ------------------------------------------------------------------------ */ + +int display_type = 0; +int display_depth = 0; +XVisualInfo *info; + +/* PseudoColor: ditherresult => colormap-entry */ +int x11_colors; +int x11_grays; +unsigned long *x11_map; +unsigned long x11_map_color[256]; +unsigned long x11_map_gray[64]; + +unsigned long x11_red; +unsigned long x11_green; +unsigned long x11_blue; + +int have_shmem = 0; + +/* + * - xv uses 4:8:4 for truecolor images. + * - The GIMP 0.99.9 uses 6:6:4, but the 6 intervals for red+green are + * choosen somehow wired :-( + * - ImageMagick tries to optimize the palette for each image individual + */ +static int try_red[] = {4, 6, 6, 5, 4}; +static int try_green[] = {8, 6, 6, 5, 4}; +static int try_blue[] = {4, 6, 4, 5, 4}; + +/* TrueColor: r,g,b => X11-color */ +unsigned long x11_lut_red[256]; +unsigned long x11_lut_green[256]; +unsigned long x11_lut_blue[256]; +unsigned long x11_lut_gray[256]; + +static int +x11_alloc_grays(Display * dpy, Colormap cmap, unsigned long *colors, int gray) +{ + XColor akt_color; + int i; + + for (i = 0; i < gray; i++) { + akt_color.red = i * 65535 / (gray - 1); + akt_color.green = i * 65535 / (gray - 1); + akt_color.blue = i * 65535 / (gray - 1); + + if (!XAllocColor(dpy, cmap, &akt_color)) { + /* failed, free them */ + XFreeColors(dpy, cmap, colors, i, 0); + return 1; + } + colors[i] = akt_color.pixel; +#if 0 + fprintf(stderr, "%2lx: %04x %04x %04x\n", + akt_color.pixel,akt_color.red,akt_color.green,akt_color.red); +#endif + } + return 0; +} + +static int +x11_alloc_colorcube(Display * dpy, Colormap cmap, unsigned long *colors, + int red, int green, int blue) +{ + XColor akt_color; + int i; + + for (i = 0; i < red * green * blue; i++) { + akt_color.red = ((i / (green * blue)) % red) * 65535 / (red - 1); + akt_color.green = ((i / blue) % green) * 65535 / (green - 1); + akt_color.blue = (i % blue) * 65535 / (blue - 1); +#if 0 + fprintf(stderr, "%04x %04x %04x\n", + akt_color.red, akt_color.green, akt_color.red); +#endif + + if (!XAllocColor(dpy, cmap, &akt_color)) { + /* failed, free them */ + XFreeColors(dpy, cmap, colors, i, 0); + return 1; + } + colors[i] = akt_color.pixel; + } + return 0; +} + +static unsigned long +x11_alloc_color(Display * dpy, Colormap cmap, int red, int green, int blue) +{ + XColor akt_color; + + akt_color.red = red; + akt_color.green = green; + akt_color.blue = blue; + + XAllocColor(dpy, cmap, &akt_color); + return akt_color.pixel; +} + +static void +x11_create_lut(unsigned long red_mask, + unsigned long green_mask, + unsigned long blue_mask) +{ + int rgb_red_bits = 0; + int rgb_red_shift = 0; + int rgb_green_bits = 0; + int rgb_green_shift = 0; + int rgb_blue_bits = 0; + int rgb_blue_shift = 0; + int i; + unsigned long mask; + + for (i = 0; i < 24; i++) { + mask = (1 << i); + if (red_mask & mask) + rgb_red_bits++; + else if (!rgb_red_bits) + rgb_red_shift++; + if (green_mask & mask) + rgb_green_bits++; + else if (!rgb_green_bits) + rgb_green_shift++; + if (blue_mask & mask) + rgb_blue_bits++; + else if (!rgb_blue_bits) + rgb_blue_shift++; + } +#if 0 + printf("color: bits shift\n"); + printf("red : %04i %05i\n", rgb_red_bits, rgb_red_shift); + printf("green: %04i %05i\n", rgb_green_bits, rgb_green_shift); + printf("blue : %04i %05i\n", rgb_blue_bits, rgb_blue_shift); +#endif + + for (i = 0; i < 256; i++) { + x11_lut_red[i] = (i >> (8 - rgb_red_bits)) << rgb_red_shift; + x11_lut_green[i] = (i >> (8 - rgb_green_bits)) << rgb_green_shift; + x11_lut_blue[i] = (i >> (8 - rgb_blue_bits)) << rgb_blue_shift; + x11_lut_gray[i] = + x11_lut_red[i] | x11_lut_green[i] | x11_lut_blue[i]; + } +} + +int +x11_color_init(Widget shell, int *gray) +{ + Screen *scr; + Colormap cmap; + XVisualInfo template; + unsigned int found, i; + + scr = XtScreen(shell); + cmap = DefaultColormapOfScreen(scr); + if (0 == x11_grays) + x11_grays = 8; + + /* Ask for visual type */ + template.screen = XDefaultScreen(dpy); + template.visualid = + XVisualIDFromVisual(DefaultVisualOfScreen(scr)); + info = XGetVisualInfo(dpy, VisualIDMask | VisualScreenMask, &template, + &found); + if (XShmQueryExtension(dpy)) { + have_shmem = 1; + } + + /* display_depth = (info->depth+7)/8; */ + if (info->class == TrueColor) { + /* TrueColor */ + *gray = 0; /* XXX testing... */ + display_depth = 4; + display_type = TRUECOLOR; + x11_create_lut(info->red_mask, info->green_mask, info->blue_mask); + x11_black = x11_alloc_color(dpy, cmap, 0, 0, 0); + x11_gray = x11_alloc_color(dpy, cmap, 0xc400, 0xc400, 0xc400); + x11_lightgray = x11_alloc_color(dpy, cmap, 0xe000, 0xe000, 0xe000); + x11_white = x11_alloc_color(dpy, cmap, 0xffff, 0xffff, 0xffff); + } else if (info->class == PseudoColor && info->depth == 8) { + /* 8bit PseudoColor */ + display_depth = 1; + display_type = PSEUDOCOLOR; + if (0 != x11_alloc_grays(dpy, cmap, x11_map_gray, x11_grays)) { + fprintf(stderr, "sorry, can't allocate %d grays\n", x11_grays); + exit(1); + } + if (!*gray) { + for (i = 0; i < sizeof(try_red) / sizeof(int); i++) { + if (0 == x11_alloc_colorcube + (dpy, cmap, x11_map_color, + try_red[i], try_green[i], try_blue[i])) { + x11_colors = try_red[i] * try_green[i] * try_blue[i]; + init_dither(try_red[i], try_green[i], try_blue[i], x11_grays); + break; + } + } + if (i == sizeof(try_red) / sizeof(int)) { + *gray = 1; + fprintf(stderr, "failed to allocate enouth colors, " + "using grayscaled\n"); + } + } + if (*gray) + init_dither(2, 2, 2, x11_grays); + } else if (info->class == StaticGray || info->class == GrayScale) { + /* Grayscale */ + display_depth = 1; + display_type = PSEUDOCOLOR; + x11_grays = 64; + *gray = 1; + init_dither(2, 2, 2, x11_grays); + if (0 != x11_alloc_grays(dpy, cmap, x11_map_gray, x11_grays)) { + fprintf(stderr, "sorry, can't allocate %d grays\n", x11_grays); + exit(1); + } + } else { + fprintf(stderr, "sorry, can't handle visual\n"); + exit(1); + } + + /* some common colors */ + x11_red = x11_alloc_color(dpy, cmap, 65535, 0, 0); + x11_green = x11_alloc_color(dpy, cmap, 0, 65535, 0); + x11_blue = x11_alloc_color(dpy, cmap, 0, 0, 65535); + + if (*gray) { + x11_map = x11_map_gray; + dither_line = dither_line_gray; + } else { + x11_map = x11_map_color; + dither_line = dither_line_color; + } + + return display_type; +} + +/* ------------------------------------------------------------------------ */ + +static int mitshm_bang = 0; + +static int +x11_error_dev_null(Display * dpy, XErrorEvent * event) +{ + mitshm_bang = 1; + return 0; +} + +XImage* +x11_create_ximage(Widget shell, int width, int height, void **shm) +{ + XImage *ximage = NULL; + unsigned char *ximage_data; + XShmSegmentInfo *shminfo = NULL; + XtPointer old_handler; + Screen *scr = XtScreen(shell); + + if (have_shmem) { + old_handler = XSetErrorHandler(x11_error_dev_null); + (*shm) = shminfo = malloc(sizeof(XShmSegmentInfo)); + memset(shminfo, 0, sizeof(XShmSegmentInfo)); + ximage = XShmCreateImage(dpy, + DefaultVisualOfScreen(scr), + DefaultDepthOfScreen(scr), + ZPixmap, NULL, + shminfo, width, height); + if (ximage) { + shminfo->shmid = shmget(IPC_PRIVATE, + ximage->bytes_per_line * ximage->height, + IPC_CREAT | 0777); + if (-1 == shminfo->shmid) { + fprintf(stderr,"shmget(%dMB): %s\n", + ximage->bytes_per_line * ximage->height / 1024 / 1024, + strerror(errno)); + goto oom; + } + shminfo->shmaddr = (char *) shmat(shminfo->shmid, 0, 0); + if ((void *) -1 == shminfo->shmaddr) { + perror("shmat"); + goto oom; + } + ximage->data = shminfo->shmaddr; + shminfo->readOnly = False; + + XShmAttach(dpy, shminfo); + XSync(dpy, False); + shmctl(shminfo->shmid, IPC_RMID, 0); + if (mitshm_bang) { + have_shmem = 0; + shmdt(shminfo->shmaddr); + free(shminfo); + shminfo = *shm = NULL; + XDestroyImage(ximage); + ximage = NULL; + } + } else { + have_shmem = 0; + free(shminfo); + shminfo = *shm = NULL; + } + XSetErrorHandler(old_handler); + } + + if (ximage == NULL) { + (*shm) = NULL; + if (NULL == (ximage_data = malloc(width * height * display_depth))) { + fprintf(stderr,"Oops: out of memory\n"); + goto oom; + } + ximage = XCreateImage(dpy, + DefaultVisualOfScreen(scr), + DefaultDepthOfScreen(scr), + ZPixmap, 0, ximage_data, + width, height, + 8, 0); + } + memset(ximage->data, 0, ximage->bytes_per_line * ximage->height); + + return ximage; + + oom: + if (shminfo) { + if (shminfo->shmid && shminfo->shmid != -1) + shmctl(shminfo->shmid, IPC_RMID, 0); + free(shminfo); + } + if (ximage) + XDestroyImage(ximage); + return NULL; +} + +void +x11_destroy_ximage(Widget shell, XImage * ximage, void *shm) +{ + XShmSegmentInfo *shminfo = shm; + + if (shminfo) { + XShmDetach(dpy, shminfo); + XDestroyImage(ximage); + shmdt(shminfo->shmaddr); + free(shminfo); + } else + XDestroyImage(ximage); +} + +Pixmap +x11_create_pixmap(Widget shell, unsigned char *byte_data, + int width, int height, int gray) +{ + Pixmap pixmap; + XImage *ximage; + XGCValues values; + GC gc; + unsigned long *long_data = (unsigned long *) byte_data; + int x, y; + void *shm; + unsigned long *map = gray ? x11_map_gray : x11_map; + + Screen *scr = XtScreen(shell); + + pixmap = XCreatePixmap(dpy, + RootWindowOfScreen(scr), + width, height, + DefaultDepthOfScreen(scr)); + gc = XCreateGC(dpy, pixmap, 0, &values); + + if (NULL == (ximage = x11_create_ximage(shell, width, height, &shm))) { + XFreePixmap(dpy, pixmap); + XFreeGC(dpy, gc); + return 0; + } + for (y = 0; y < height; y++) + if (display_type == TRUECOLOR) + for (x = 0; x < width; x++) + XPutPixel(ximage, x, y, *(long_data++)); + else + for (x = 0; x < width; x++) + XPutPixel(ximage, x, y, map[(int) (*(byte_data++))]); + + XPUTIMAGE(dpy, pixmap, gc, ximage, 0, 0, 0, 0, width, height); + + x11_destroy_ximage(shell, ximage, shm); + XFreeGC(dpy, gc); + return pixmap; +} + +void +x11_data_to_ximage(unsigned char *data, unsigned char *ximage, + int x, int y, int sy, int gray) +{ + unsigned long *d; + int i, n; + + if (display_type == PSEUDOCOLOR) { + if (gray) { + for (i = 0; i < y; i++) + dither_line_gray(data + x * i, ximage + x * i, i + sy, x); + } else { + for (i = 0; i < y; i++) + dither_line(data + 3 * x * i, ximage + x * i, i + sy, x); + } + } else { + d = (unsigned long *) ximage; + if (gray) { + n = x * y; + for (i = 0; i < n; i++) + *(d++) = x11_lut_gray[data[i]]; + } else { + n = 3 * x * y; + for (i = 0; i < n; i += 3) + *(d++) = x11_lut_red[data[i]] | + x11_lut_green[data[i + 1]] | + x11_lut_blue[data[i + 2]]; + } + } +} @@ -0,0 +1,42 @@ +#define PSEUDOCOLOR 1 +#define TRUECOLOR 2 + +extern int display_type; +extern int display_depth; +extern XVisualInfo *info; + +extern int x11_grays; +extern unsigned long *x11_map; + +extern unsigned long x11_lut_red[256]; +extern unsigned long x11_lut_green[256]; +extern unsigned long x11_lut_blue[256]; +extern unsigned long x11_lut_gray[256]; +extern unsigned long x11_map_color[256]; +extern unsigned long x11_map_gray[64]; + +#define x11_black x11_map_gray[0] +#define x11_gray x11_map_gray[47*x11_grays/64] +#define x11_lightgray x11_map_gray[55*x11_grays/64] +#define x11_white x11_map_gray[63*x11_grays/64] + +extern unsigned long x11_red; +extern unsigned long x11_green; +extern unsigned long x11_blue; + +extern int have_shmem; + +int x11_color_init(Widget shell, int *gray); + +void x11_data_to_ximage(unsigned char *rgb, unsigned char *ximage, + int x, int y, int sy, int gray); +XImage *x11_create_ximage(Widget shell, int width, int height, void **shm); +void x11_destroy_ximage(Widget shell, XImage * ximage, void *shm); +Pixmap x11_create_pixmap(Widget shell, unsigned char *data, + int width, int height, int gray); + +#define XPUTIMAGE(dpy,dr,gc,xi,a,b,c,d,w,h) \ + if (have_shmem) \ + XShmPutImage(dpy,dr,gc,xi,a,b,c,d,w,h,True); \ + else \ + XPutImage(dpy,dr,gc,xi,a,b,c,d,w,h) @@ -0,0 +1,321 @@ +/* + * basic Xdnd support for Motif 2.x + * + * Receive drops only. Works fine in parallel with Motif DnD. + * + * Initiate drags is probably hard do do without breaking Motif DnD or + * heavily mucking with the Motif internals. Highlighting seems to be + * non-trivial too. + * + * Usage: + * (1) register XdndAction as "Xdnd" + * (2) register Widgets using XdndDropSink (acts like XmeDropSink + * for Motif DnD) + * (3) the transfer callback functions have to call + * XdndDropFinished() when they are done (i.e. after calling + * XmTransferDone) + * + * Data transfer is done using the usual Motif 2.x way, using UTM. + * Read: XmNdestinationCallback will be called, with + * XmDestinationCallbackStruct->selection set to XdndSelection. + * + * It is up to the application to handle the Xdnd MIME targets + * correctly, i.e. accept both "TEXT" and "text/plain" targets for + * example. Otherwise Xdnd support shouldn't need much work if Motif + * DnD support is present already. + * + * Known problems: + * - Not working with KDE 2.x as they don't provide a TARGETS + * target (which IMO is illegal, see ICCCM specs). + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <Xm/Xm.h> +#include <Xm/Transfer.h> +#include <Xm/TransferP.h> + +#include "xdnd.h" + +/* ---------------------------------------------------------------------- */ + +int xdnd_debug = 0; + +static Atom XdndAware; +static Atom XdndTypeList; +static Atom XdndSelection; + +static Atom XdndEnter; +static Atom XdndPosition; +static Atom XdndStatus; +static Atom XdndLeave; +static Atom XdndDrop; +static Atom XdndFinished; + +static Atom XdndActionCopy; +static Atom XdndActionMove; +static Atom XdndActionLink; +static Atom XdndActionAsk; +static Atom XdndActionPrivate; + +/* ---------------------------------------------------------------------- */ + +static void XdndInit(Display *dpy) +{ + if (XdndAware) + return; + + XdndAware = XInternAtom(dpy, "XdndAware", False); + XdndTypeList = XInternAtom(dpy, "XdndTypeList", False); + XdndSelection = XInternAtom(dpy, "XdndSelection", False); + + /* client messages */ + XdndEnter = XInternAtom(dpy, "XdndEnter", False); + XdndPosition = XInternAtom(dpy, "XdndPosition", False); + XdndStatus = XInternAtom(dpy, "XdndStatus", False); + XdndLeave = XInternAtom(dpy, "XdndLeave", False); + XdndDrop = XInternAtom(dpy, "XdndDrop", False); + XdndFinished = XInternAtom(dpy, "XdndFinished", False); + + /* actions */ + XdndActionCopy = XInternAtom(dpy, "XdndActionCopy", False); + XdndActionMove = XInternAtom(dpy, "XdndActionMove", False); + XdndActionLink = XInternAtom(dpy, "XdndActionLink", False); + XdndActionAsk = XInternAtom(dpy, "XdndActionAsk", False); + XdndActionPrivate = XInternAtom(dpy, "XdndActionPrivate", False); +} + +static void +init_window(Widget widget) +{ + int version = 4; + + /* window */ + XChangeProperty(XtDisplay(widget),XtWindow(widget), + XdndAware, XA_ATOM, 32, PropModeReplace, + (XtPointer)&version, 1); + + /* shell */ + while (!XtIsShell(widget)) + widget = XtParent(widget); + + XChangeProperty(XtDisplay(widget),XtWindow(widget), + XdndAware, XA_ATOM, 32, PropModeReplace, + (XtPointer)&version, 1); + XtOverrideTranslations(widget, XtParseTranslationTable + ("<Message>XdndEnter: Xdnd()\n" + "<Message>XdndPosition: Xdnd()\n" + "<Message>XdndLeave: Xdnd()\n" + "<Message>XdndDrop: Xdnd()")); +} + +static Widget +find_window(Widget widget, int wx, int wy, int rx, int ry) +{ + WidgetList children; + Cardinal nchildren; + Dimension x,y,w,h; + Widget found = NULL; + int i; + + nchildren = 0; + XtVaGetValues(widget,XtNchildren,&children, + XtNnumChildren,&nchildren,NULL); + fprintf(stderr,"findwindow %s\n",XtName(widget)); + for (i = nchildren-1; i >= 0; i--) { + XtVaGetValues(children[i],XtNx,&x,XtNy,&y, + XtNwidth,&w,XtNheight,&h,NULL); + if (!XtIsManaged(children[i])) + continue; + if (XtIsSubclass(children[i],xmGadgetClass)) + continue; + if (rx < wx+x || rx > wx+x+w) + continue; + if (ry < wy+y || ry > wy+y+h) + continue; + found = children[i]; + break; + } + if (found) { + fprintf(stderr," more: %s\n",XtName(found)); + return find_window(found,wx+x,wy+y,rx,ry); + } + fprintf(stderr," done: %s\n",XtName(widget)); + return widget; +} + +static int +check_window(Widget widget) +{ + Atom type; + int format,rc; + unsigned long nitems,rest; + unsigned long *ldata; + + rc = XGetWindowProperty(XtDisplay(widget),XtWindow(widget), + XdndAware,0,64,False,AnyPropertyType, + &type,&format,&nitems,&rest, + (XtPointer)&ldata); + XFree(ldata); + return rc == Success && nitems > 0; +} + +static XtEnum get_operation(Atom action) +{ + if (XdndActionCopy == action) + return XmCOPY; + if (XdndActionLink == action) + return XmLINK; + if (XdndActionMove == action) + return XmMOVE; + return 0; +} + +static Atom get_action(XtEnum operation) +{ + if (XmCOPY == operation) + return XdndActionCopy; + if (XmLINK == operation) + return XdndActionLink; + if (XmMOVE == operation) + return XdndActionMove; + return None; +} + +static void +XdndEvent(Widget widget, XtPointer clientdata, XEvent *event, Boolean *cont) +{ + switch(event->type) { + case MapNotify: + init_window(widget); + break; + } +} + +/* ---------------------------------------------------------------------- */ + +/* + * not very nice this way, but as you can hardly have two drags at the + * same time with one pointer only it should be fine ... + */ +static Widget target; +static int target_ok,drop_ok; +static Window source; +static XtEnum operation; + +void +XdndAction(Widget widget, XEvent *event, + String *params, Cardinal *num_params) +{ + char *name; + XEvent reply; + + if (NULL == event) + return; + if (ClientMessage != event->type) + return; + + if (XdndEnter == event->xclient.message_type) { + if (xdnd_debug) + fprintf(stderr,"Xdnd: Enter: win=0x%lx ver=%ld more=%s\n", + event->xclient.data.l[0], + event->xclient.data.l[1] >> 24, + (event->xclient.data.l[1] & 1) ? "yes" : "no"); + } + + if (XdndPosition == event->xclient.message_type) { + source = event->xclient.data.l[0]; + target = find_window(widget,0,0, + event->xclient.data.l[2] >> 16, + event->xclient.data.l[2] & 0xffff); + target_ok = check_window(target); + if (target_ok) { + operation = get_operation(event->xclient.data.l[4]); + operation = XmCOPY; /* FIXME */ + drop_ok = 1; + } else { + operation = 0; + drop_ok = 0; + } + if (xdnd_debug) { + name = NULL; + if (event->xclient.data.l[4]) + name=XGetAtomName(XtDisplay(widget),event->xclient.data.l[4]); + fprintf(stderr,"Xdnd: Position: win=0x%lx pos=+%ld+%ld ts=%ld " + "ac=%s op=%d widget=%s drop=%s\n", + event->xclient.data.l[0], + event->xclient.data.l[2] >> 16, + event->xclient.data.l[2] & 0xffff, + event->xclient.data.l[3], + name,operation, + XtName(target),target_ok ? "yes" : "no"); + if (name) + XFree(name); + } + memset(&reply,0,sizeof(reply)); + reply.xany.type = ClientMessage; + reply.xany.display = XtDisplay(widget); + reply.xclient.window = event->xclient.data.l[0]; + reply.xclient.message_type = XdndStatus; + reply.xclient.format = 32; + reply.xclient.data.l[0] = XtWindow(widget); + reply.xclient.data.l[1] = drop_ok ? 1 : 0; + reply.xclient.data.l[4] = get_action(operation); + XSendEvent(XtDisplay(widget),reply.xclient.window,0,0,&reply); + } + + if (XdndDrop == event->xclient.message_type) { + source = event->xclient.data.l[0]; + if (xdnd_debug) + fprintf(stderr,"Xdnd: Drop: win=0x%lx ts=%ld\n", + event->xclient.data.l[0], + event->xclient.data.l[2]); + XmeNamedSink(target,XdndSelection,XmCOPY,NULL, + XtLastTimestampProcessed(XtDisplay(widget))); + } + + if (XdndLeave == event->xclient.message_type) { + source = 0; + if (xdnd_debug) + fprintf(stderr,"Xdnd: Leave: win=0x%lx\n", + event->xclient.data.l[0]); + } +} + +void XdndDropFinished(Widget widget, XmSelectionCallbackStruct *scs) +{ + XEvent reply; + + if (XdndSelection != scs->selection) + return; + if (0 == source) + return; + + if (xdnd_debug) + fprintf(stderr,"Xdnd: sending Finished (0x%lx)\n",source); + memset(&reply,0,sizeof(reply)); + reply.xany.type = ClientMessage; + reply.xany.display = XtDisplay(widget); + reply.xclient.window = source; + reply.xclient.message_type = XdndFinished; + reply.xclient.format = 32; + while (!XtIsShell(widget)) + widget = XtParent(widget); + reply.xclient.data.l[0] = XtWindow(widget); + XSendEvent(XtDisplay(widget),reply.xclient.window,0,0,&reply); + source = 0; +} + +void XdndDropSink(Widget widget) +{ + XdndInit(XtDisplay(widget)); + + if (XtWindow(widget)) + init_window(widget); + XtAddEventHandler(widget,StructureNotifyMask,True,XdndEvent,NULL); +} @@ -0,0 +1,5 @@ +extern int xdnd_debug; +void XdndDropSink(Widget widget); +void XdndAction(Widget widget, XEvent *event, + String *params, Cardinal *num_params); +void XdndDropFinished(Widget widget, XmSelectionCallbackStruct *scs); diff --git a/xpm/ccw.xpm b/xpm/ccw.xpm new file mode 100644 index 0000000..c10a68f --- /dev/null +++ b/xpm/ccw.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * ccw_xpm[] = { +"16 16 3 1", +" c none", +". c #000000000000", +"X c #868682828686", +" ", +" ", +" . ...... ", +" .......... ", +" ...XXXXXX.. ", +" .... .. ", +" ..... ..X ", +" XXXXX ..X ", +" ..X ", +" ..X ", +" . ...X ", +" .. ...XX ", +" ........XX ", +" ......XX ", +" XXXXXX ", +" "}; diff --git a/xpm/cw.xpm b/xpm/cw.xpm new file mode 100644 index 0000000..3942890 --- /dev/null +++ b/xpm/cw.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * cw_xpm[] = { +"16 16 3 1", +" c none", +". c #000000000000", +"X c #868682828686", +" ", +" ", +" ...... . ", +" ..........X ", +" ..XXXXXX...X ", +" ..XX ....X ", +" ..X .....X ", +" ..X XXXXX ", +" ..X ", +" ..X ", +" ... . ", +" ... ..X ", +" ........XX ", +" ......XX ", +" XXXXXX ", +" "}; diff --git a/xpm/dir.xpm b/xpm/dir.xpm new file mode 100644 index 0000000..9295ca3 --- /dev/null +++ b/xpm/dir.xpm @@ -0,0 +1,41 @@ +/* XPM */ +static char * dir_xpm[] = { +"32 32 6 1", +" c none", +". c #000000000000", +"X c #FFFFFFFFC71B", +"o c #FFFFFFFFFFFF", +"O c #FFFFFFFF0000", +"+ c #861782078617", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ...... ", +" .XXXXXX. ", +" .XXXXXXXX. ", +" ................... ", +" .ooooooooooooooooooo. ", +" .oXXXXXXXXXXXXXXXXXXXO. ", +" .oXXXXXXXXXXXXXXXXXXXO.+ ", +" .oXXXXXXXXXXXXXXXXXXXO.++ ", +" .oXXXXXXXXXXXXXXXXXXXO.++ ", +" .oXXXXXXXXXXXXXXXXXXXO.++ ", +" .oXXXXXXXXXXXXXXXXXXXO.++ ", +" .oXXXXXXXXXXXXXXXXXXXO.++ ", +" .oXXXXXXXXXXXXXXXXXXXO.++ ", +" .oXXXXXXXXXXXXXXXXXXXO.++ ", +" .oXXXXXXXXXXXXXXXXXXXO.++ ", +" .oXXXXXXXXXXXXXXXXXXXO.++ ", +" .oXXXXXXXXXXXXXXXXXXXO.++ ", +" .OOOOOOOOOOOOOOOOOOO..++ ", +" ....................+++ ", +" +++++++++++++++++++++ ", +" +++++++++++++++++++ ", +" ", +" ", +" "}; diff --git a/xpm/exit.xpm b/xpm/exit.xpm new file mode 100644 index 0000000..53eee1a --- /dev/null +++ b/xpm/exit.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * exit_xpm[] = { +"16 16 3 1", +" c none", +". c #000000000000", +"X c #861782078617", +" ", +" ", +" .. ", +" ..X ", +" . ..X . ", +" ..X ..X .. ", +" ..X ..X .. ", +" ..X ..X ..X ", +" ..X ..X ..X ", +" ..X XX ..X ", +" ..X ..X ", +" .. ..XX ", +" ........XX ", +" ......XX ", +" XXXXXX ", +" "}; diff --git a/xpm/file.xpm b/xpm/file.xpm new file mode 100644 index 0000000..deb89ef --- /dev/null +++ b/xpm/file.xpm @@ -0,0 +1,39 @@ +/* XPM */ +static char * file_xpm[] = { +"32 32 4 1", +" c none", +". c #000000000000", +"X c #861782078617", +"o c #FFFFFFFFFFFF", +" ", +" ", +" ", +" ......... ", +" ..........X ", +" ...Xooooo..XX ", +" ..o.Xooooo..XX ", +" ..oo.Xooooo..XX ", +" ..ooo.Xooooo..XX ", +" ..oooo.Xooooo..XX ", +" ..ooooo.Xooooo..XX ", +" .........Xooooo..XX ", +" ..XXXXXXXXooooo..XX ", +" ..ooooooooooooo..XX ", +" ..ooooooooooooo..XX ", +" ..ooooooooooooo..XX ", +" ..ooooooooooooo..XX ", +" ..ooooooooooooo..XX ", +" ..ooooooooooooo..XX ", +" ..ooooooooooooo..XX ", +" ..ooooooooooooo..XX ", +" ..ooooooooooooo..XX ", +" ..ooooooooooooo..XX ", +" ..ooooooooooooo..XX ", +" ..ooooooooooooo..XX ", +" .................XX ", +" ................XXX ", +" XXXXXXXXXXXXXXXXXX ", +" XXXXXXXXXXXXXXXXX ", +" ", +" ", +" "}; diff --git a/xpm/fliph.xpm b/xpm/fliph.xpm new file mode 100644 index 0000000..0ef2dc0 --- /dev/null +++ b/xpm/fliph.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * fliph_xpm[] = { +"16 16 3 1", +" c none", +". c #000000000000", +"X c #868682828686", +" . . ", +" ..X .X ", +" ...X .X ", +"............. ", +".............X ", +" ...XXX.XXXXXX ", +" ..X .X ", +" .X .X . ", +" X .X .. ", +" .X ... ", +" ............. ", +" .............X", +" .XXX...XX", +" .X ..XX ", +" .X .XX ", +" X X "}; diff --git a/xpm/flipv.xpm b/xpm/flipv.xpm new file mode 100644 index 0000000..e296b28 --- /dev/null +++ b/xpm/flipv.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * flipv_xpm[] = { +"16 16 3 1", +" c none", +". c #000000000000", +"X c #861782078617", +" .. ", +" .... ", +" ...... .. ", +"........ ..X ", +" XX..XXXX ..X ", +" ..X ..X ", +" ..X ..X ", +"............... ", +" XX..XXXXX..XXXX", +" ..X ..X ", +" ..X ..X ", +" ..X ........ ", +" ..X ......XX", +" XX ....XX ", +" ..XX ", +" XX "}; diff --git a/xpm/next.xpm b/xpm/next.xpm new file mode 100644 index 0000000..b5729e6 --- /dev/null +++ b/xpm/next.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * next_xpm[] = { +"16 16 3 1", +" c none", +". c #000000000000", +"X c #868682828686", +" ", +" ", +" . ", +" .. ", +" ... ", +" .... ", +" ........... ", +" ............ ", +" ...........XX ", +" XXXXX....XX ", +" ...XX ", +" ..XX ", +" .XX ", +" X ", +" ", +" "}; diff --git a/xpm/prev.xpm b/xpm/prev.xpm new file mode 100644 index 0000000..b4ae0af --- /dev/null +++ b/xpm/prev.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * prev_xpm[] = { +"16 16 3 1", +" c none", +". c #000000000000", +"X c #868682828686", +" ", +" ", +" . ", +" ..X ", +" ...X ", +" ....X ", +" ........... ", +" ............X ", +" ...........X ", +" ....XXXXXXX ", +" ...X ", +" ..X ", +" .X ", +" X ", +" ", +" "}; diff --git a/xpm/question.xpm b/xpm/question.xpm new file mode 100644 index 0000000..662e74e --- /dev/null +++ b/xpm/question.xpm @@ -0,0 +1,38 @@ +/* XPM */ +static char * question_xpm[] = { +"32 32 3 1", +" c none", +". c #000000000000", +"X c #861782078617", +" ", +" ", +" ", +" ", +" ", +" ", +" ...... ", +" ......... ", +" ....XX.... ", +" ....XX X... ", +" ...XX ...X ", +" ...X ....X ", +" XXX ...XX ", +" ....X ", +" ....XX ", +" ...XX ", +" ...XX ", +" ...X ", +" ...X ", +" ...X ", +" XXX ", +" ", +" ... ", +" ...X ", +" ...X ", +" XXX ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/xpm/unknown.xpm b/xpm/unknown.xpm new file mode 100644 index 0000000..89a6e4e --- /dev/null +++ b/xpm/unknown.xpm @@ -0,0 +1,39 @@ +/* XPM */ +static char * unknown_xpm[] = { +"32 32 4 1", +" c none", +". c #000000000000", +"X c #861782078617", +"o c #FFFFFFFFFFFF", +" ", +" ", +" ", +" ......... ", +" ..........X ", +" ...Xooooo..XX ", +" ..o.Xooooo..XX ", +" ..oo.Xooooo..XX ", +" ..ooo.Xooooo..XX ", +" ..oooo.Xooooo..XX ", +" ..ooooo.Xooooo..XX ", +" .........Xooooo..XX ", +" ..XXXXXXXXooooo..XX ", +" ..ooooooooooooo..XX ", +" ..ooooo...ooooo..XX ", +" ..oooo.XXX.oooo..XX ", +" ..oooo.Xoo.Xooo..XX ", +" ..oooooXoo.Xooo..XX ", +" ..ooooooo.XXooo..XX ", +" ..oooooo.XXoooo..XX ", +" ..oooooo.Xooooo..XX ", +" ..oooooooXooooo..XX ", +" ..oooooo.oooooo..XX ", +" ..oooooooXooooo..XX ", +" ..ooooooooooooo..XX ", +" .................XX ", +" ................XXX ", +" XXXXXXXXXXXXXXXXXX ", +" XXXXXXXXXXXXXXXXX ", +" ", +" ", +" "}; diff --git a/xpm/zoomin.xpm b/xpm/zoomin.xpm new file mode 100644 index 0000000..99c82a0 --- /dev/null +++ b/xpm/zoomin.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * zoomin_xpm[] = { +"16 16 3 1", +" c none", +". c #000000000000", +"X c #868682828686", +" ", +" ..... ", +" .XXXXX. ", +" .XX . ", +" .XX . . ", +" .X .X .X ", +" .X ..... .X ", +" .X X.XXX .X ", +" .X .X .X ", +" . X .XX ", +" . ... ", +" .....X... ", +" XXXXX ... ", +" ..X ", +" XX ", +" "}; diff --git a/xpm/zoomout.xpm b/xpm/zoomout.xpm new file mode 100644 index 0000000..cce23e7 --- /dev/null +++ b/xpm/zoomout.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char * zoomout_xpm[] = { +"16 16 3 1", +" c none", +". c #000000000000", +"X c #868682828686", +" ", +" ..... ", +" .XXXXX. ", +" .XX . ", +" .XX . ", +" .X .X ", +" .X ..... .X ", +" .X XXXXX .X ", +" .X .X ", +" . .XX ", +" . ... ", +" .....X... ", +" XXXXX ... ", +" ..X ", +" XX ", +" "}; @@ -0,0 +1 @@ +void parse_ximage(struct ida_image *dest, XImage *src); |