diff options
Diffstat (limited to 'plug-ins/file-dds')
-rw-r--r-- | plug-ins/file-dds/COPYING | 339 | ||||
-rw-r--r-- | plug-ins/file-dds/Makefile.am | 68 | ||||
-rw-r--r-- | plug-ins/file-dds/Makefile.in | 1046 | ||||
-rw-r--r-- | plug-ins/file-dds/README | 29 | ||||
-rw-r--r-- | plug-ins/file-dds/TODO | 7 | ||||
-rw-r--r-- | plug-ins/file-dds/color.c | 58 | ||||
-rw-r--r-- | plug-ins/file-dds/color.h | 96 | ||||
-rw-r--r-- | plug-ins/file-dds/dds.c | 482 | ||||
-rw-r--r-- | plug-ins/file-dds/dds.h | 326 | ||||
-rw-r--r-- | plug-ins/file-dds/ddsplugin.h | 79 | ||||
-rw-r--r-- | plug-ins/file-dds/ddsread.c | 1439 | ||||
-rw-r--r-- | plug-ins/file-dds/ddswrite.c | 2278 | ||||
-rw-r--r-- | plug-ins/file-dds/dxt.c | 1526 | ||||
-rw-r--r-- | plug-ins/file-dds/dxt.h | 49 | ||||
-rw-r--r-- | plug-ins/file-dds/dxt_tables.h | 216 | ||||
-rw-r--r-- | plug-ins/file-dds/endian_rw.h | 69 | ||||
-rw-r--r-- | plug-ins/file-dds/imath.h | 77 | ||||
-rw-r--r-- | plug-ins/file-dds/mipmap.c | 1132 | ||||
-rw-r--r-- | plug-ins/file-dds/mipmap.h | 75 | ||||
-rw-r--r-- | plug-ins/file-dds/misc.c | 261 | ||||
-rw-r--r-- | plug-ins/file-dds/misc.h | 31 | ||||
-rw-r--r-- | plug-ins/file-dds/mktables.c | 130 | ||||
-rw-r--r-- | plug-ins/file-dds/vec.h | 245 |
23 files changed, 10058 insertions, 0 deletions
diff --git a/plug-ins/file-dds/COPYING b/plug-ins/file-dds/COPYING new file mode 100644 index 0000000..6f17fd7 --- /dev/null +++ b/plug-ins/file-dds/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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., 59 Temple Place, Suite 330, Boston, MA 02111 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. diff --git a/plug-ins/file-dds/Makefile.am b/plug-ins/file-dds/Makefile.am new file mode 100644 index 0000000..72fc5a7 --- /dev/null +++ b/plug-ins/file-dds/Makefile.am @@ -0,0 +1,68 @@ +## Process this file with automake to produce Makefile.in + +libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la +libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la +libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la +libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la +libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la +libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la +libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la + +if OS_WIN32 +mwindows = -mwindows +else +libm = -lm +endif + +if HAVE_WINDRES +include $(top_srcdir)/build/windows/gimprc-plug-ins.rule +file_dds_RC = file-dds.rc.o +endif + +AM_LDFLAGS = $(mwindows) +AM_CFLAGS = -fno-strict-aliasing + +libexecdir = $(gimpplugindir)/plug-ins/file-dds + +libexec_PROGRAMS = file-dds + +file_dds_SOURCES = \ + dds.c \ + dds.h \ + color.c \ + color.h \ + ddsplugin.h \ + ddsread.c \ + ddswrite.c \ + dxt.c \ + dxt.h \ + dxt_tables.h \ + endian_rw.h \ + imath.h \ + mipmap.c \ + mipmap.h \ + misc.c \ + misc.h \ + mktables.c \ + vec.h + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(GTK_CFLAGS) \ + $(GEGL_CFLAGS) \ + -I$(includedir) + +LDADD = \ + $(libgimpui) \ + $(libgimpwidgets) \ + $(libgimpconfig) \ + $(libgimp) \ + $(libgimpcolor) \ + $(libgimpmath) \ + $(libgimpbase) \ + $(libm) \ + $(GTK_LIBS) \ + $(GEGL_LIBS) \ + $(RT_LIBS) \ + $(INTLLIBS) \ + $(file_dds_RC) diff --git a/plug-ins/file-dds/Makefile.in b/plug-ins/file-dds/Makefile.in new file mode 100644 index 0000000..8cb3eb5 --- /dev/null +++ b/plug-ins/file-dds/Makefile.in @@ -0,0 +1,1046 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Version resources for Microsoft Windows + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +libexec_PROGRAMS = file-dds$(EXEEXT) +subdir = plug-ins/file-dds +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/m4macros/alsa.m4 \ + $(top_srcdir)/m4macros/ax_compare_version.m4 \ + $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \ + $(top_srcdir)/m4macros/detectcflags.m4 \ + $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(libexecdir)" +PROGRAMS = $(libexec_PROGRAMS) +am_file_dds_OBJECTS = dds.$(OBJEXT) color.$(OBJEXT) ddsread.$(OBJEXT) \ + ddswrite.$(OBJEXT) dxt.$(OBJEXT) mipmap.$(OBJEXT) \ + misc.$(OBJEXT) mktables.$(OBJEXT) +file_dds_OBJECTS = $(am_file_dds_OBJECTS) +file_dds_LDADD = $(LDADD) +am__DEPENDENCIES_1 = +file_dds_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \ + $(libgimpconfig) $(libgimp) $(libgimpcolor) $(libgimpmath) \ + $(libgimpbase) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(file_dds_RC) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/color.Po ./$(DEPDIR)/dds.Po \ + ./$(DEPDIR)/ddsread.Po ./$(DEPDIR)/ddswrite.Po \ + ./$(DEPDIR)/dxt.Po ./$(DEPDIR)/mipmap.Po ./$(DEPDIR)/misc.Po \ + ./$(DEPDIR)/mktables.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(file_dds_SOURCES) +DIST_SOURCES = $(file_dds_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build/windows/gimprc-plug-ins.rule \ + $(top_srcdir)/depcomp COPYING README TODO +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +AA_LIBS = @AA_LIBS@ +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +ALL_LINGUAS = @ALL_LINGUAS@ +ALSA_CFLAGS = @ALSA_CFLAGS@ +ALSA_LIBS = @ALSA_LIBS@ +ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPSTREAM_UTIL = @APPSTREAM_UTIL@ +AR = @AR@ +AS = @AS@ +ATK_CFLAGS = @ATK_CFLAGS@ +ATK_LIBS = @ATK_LIBS@ +ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BABL_CFLAGS = @BABL_CFLAGS@ +BABL_LIBS = @BABL_LIBS@ +BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ +BUG_REPORT_URL = @BUG_REPORT_URL@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +BZIP2_LIBS = @BZIP2_LIBS@ +CAIRO_CFLAGS = @CAIRO_CFLAGS@ +CAIRO_LIBS = @CAIRO_LIBS@ +CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@ +CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@ +CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@ +CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CC_VERSION = @CC_VERSION@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DESKTOP_DATADIR = @DESKTOP_DATADIR@ +DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@ +DLLTOOL = @DLLTOOL@ +DOC_SHOOTER = @DOC_SHOOTER@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILE_AA = @FILE_AA@ +FILE_EXR = @FILE_EXR@ +FILE_HEIF = @FILE_HEIF@ +FILE_JP2_LOAD = @FILE_JP2_LOAD@ +FILE_JPEGXL = @FILE_JPEGXL@ +FILE_MNG = @FILE_MNG@ +FILE_PDF_SAVE = @FILE_PDF_SAVE@ +FILE_PS = @FILE_PS@ +FILE_WMF = @FILE_WMF@ +FILE_XMC = @FILE_XMC@ +FILE_XPM = @FILE_XPM@ +FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@ +FONTCONFIG_LIBS = @FONTCONFIG_LIBS@ +FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@ +FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@ +FREETYPE_CFLAGS = @FREETYPE_CFLAGS@ +FREETYPE_LIBS = @FREETYPE_LIBS@ +GDBUS_CODEGEN = @GDBUS_CODEGEN@ +GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@ +GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@ +GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@ +GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@ +GEGL = @GEGL@ +GEGL_CFLAGS = @GEGL_CFLAGS@ +GEGL_LIBS = @GEGL_LIBS@ +GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@ +GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GEXIV2_CFLAGS = @GEXIV2_CFLAGS@ +GEXIV2_LIBS = @GEXIV2_LIBS@ +GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@ +GIMP_API_VERSION = @GIMP_API_VERSION@ +GIMP_APP_VERSION = @GIMP_APP_VERSION@ +GIMP_BINARY_AGE = @GIMP_BINARY_AGE@ +GIMP_COMMAND = @GIMP_COMMAND@ +GIMP_DATA_VERSION = @GIMP_DATA_VERSION@ +GIMP_FULL_NAME = @GIMP_FULL_NAME@ +GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@ +GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@ +GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@ +GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@ +GIMP_MKENUMS = @GIMP_MKENUMS@ +GIMP_MODULES = @GIMP_MODULES@ +GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@ +GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@ +GIMP_PLUGINS = @GIMP_PLUGINS@ +GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@ +GIMP_REAL_VERSION = @GIMP_REAL_VERSION@ +GIMP_RELEASE = @GIMP_RELEASE@ +GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@ +GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@ +GIMP_UNSTABLE = @GIMP_UNSTABLE@ +GIMP_USER_VERSION = @GIMP_USER_VERSION@ +GIMP_VERSION = @GIMP_VERSION@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@ +GIO_UNIX_LIBS = @GIO_UNIX_LIBS@ +GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@ +GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@ +GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@ +GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GOBJECT_QUERY = @GOBJECT_QUERY@ +GREP = @GREP@ +GS_LIBS = @GS_LIBS@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GTK_CFLAGS = @GTK_CFLAGS@ +GTK_LIBS = @GTK_LIBS@ +GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@ +GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@ +GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@ +GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ +HARFBUZZ_LIBS = @HARFBUZZ_LIBS@ +HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@ +HAVE_CXX14 = @HAVE_CXX14@ +HAVE_FINITE = @HAVE_FINITE@ +HAVE_ISFINITE = @HAVE_ISFINITE@ +HAVE_VFORK = @HAVE_VFORK@ +HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@ +INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@ +INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@ +INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@ +ISO_CODES_LOCATION = @ISO_CODES_LOCATION@ +JPEG_LIBS = @JPEG_LIBS@ +JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@ +JSON_GLIB_LIBS = @JSON_GLIB_LIBS@ +JXL_CFLAGS = @JXL_CFLAGS@ +JXL_LIBS = @JXL_LIBS@ +JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@ +JXL_THREADS_LIBS = @JXL_THREADS_LIBS@ +LCMS_CFLAGS = @LCMS_CFLAGS@ +LCMS_LIBS = @LCMS_LIBS@ +LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@ +LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@ +LIBHEIF_LIBS = @LIBHEIF_LIBS@ +LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@ +LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@ +LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@ +LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@ +LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@ +LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@ +LIBOBJS = @LIBOBJS@ +LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@ +LIBUNWIND_LIBS = @LIBUNWIND_LIBS@ +LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LT_VERSION_INFO = @LT_VERSION_INFO@ +LZMA_CFLAGS = @LZMA_CFLAGS@ +LZMA_LIBS = @LZMA_LIBS@ +MAIL = @MAIL@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@ +MIME_INFO_LIBS = @MIME_INFO_LIBS@ +MIME_TYPES = @MIME_TYPES@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@ +MNG_CFLAGS = @MNG_CFLAGS@ +MNG_LIBS = @MNG_LIBS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@ +MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@ +NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@ +NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENEXR_CFLAGS = @OPENEXR_CFLAGS@ +OPENEXR_LIBS = @OPENEXR_LIBS@ +OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@ +OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@ +OPENJPEG_LIBS = @OPENJPEG_LIBS@ +OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@ +PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@ +PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@ +PATHSEP = @PATHSEP@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@ +PERL_VERSION = @PERL_VERSION@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PNG_CFLAGS = @PNG_CFLAGS@ +PNG_LIBS = @PNG_LIBS@ +POFILES = @POFILES@ +POPPLER_CFLAGS = @POPPLER_CFLAGS@ +POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@ +POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@ +POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@ +POPPLER_LIBS = @POPPLER_LIBS@ +POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PYBIN_PATH = @PYBIN_PATH@ +PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@ +PYCAIRO_LIBS = @PYCAIRO_LIBS@ +PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@ +PYGTK_CFLAGS = @PYGTK_CFLAGS@ +PYGTK_CODEGEN = @PYGTK_CODEGEN@ +PYGTK_DEFSDIR = @PYGTK_DEFSDIR@ +PYGTK_LIBS = @PYGTK_LIBS@ +PYLINK_LIBS = @PYLINK_LIBS@ +PYTHON = @PYTHON@ +PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_INCLUDES = @PYTHON_INCLUDES@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ +RT_LIBS = @RT_LIBS@ +SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ +SED = @SED@ +SENDMAIL = @SENDMAIL@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOCKET_LIBS = @SOCKET_LIBS@ +SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@ +SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@ +SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@ +STRIP = @STRIP@ +SVG_CFLAGS = @SVG_CFLAGS@ +SVG_LIBS = @SVG_LIBS@ +SYMPREFIX = @SYMPREFIX@ +TIFF_LIBS = @TIFF_LIBS@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WEBKIT_CFLAGS = @WEBKIT_CFLAGS@ +WEBKIT_LIBS = @WEBKIT_LIBS@ +WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@ +WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@ +WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@ +WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@ +WEBPMUX_LIBS = @WEBPMUX_LIBS@ +WEBP_CFLAGS = @WEBP_CFLAGS@ +WEBP_LIBS = @WEBP_LIBS@ +WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@ +WEB_PAGE = @WEB_PAGE@ +WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@ +WINDRES = @WINDRES@ +WMF_CFLAGS = @WMF_CFLAGS@ +WMF_CONFIG = @WMF_CONFIG@ +WMF_LIBS = @WMF_LIBS@ +WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@ +XDG_EMAIL = @XDG_EMAIL@ +XFIXES_CFLAGS = @XFIXES_CFLAGS@ +XFIXES_LIBS = @XFIXES_LIBS@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@ +XMC_CFLAGS = @XMC_CFLAGS@ +XMC_LIBS = @XMC_LIBS@ +XMKMF = @XMKMF@ +XMLLINT = @XMLLINT@ +XMU_LIBS = @XMU_LIBS@ +XPM_LIBS = @XPM_LIBS@ +XSLTPROC = @XSLTPROC@ +XVFB_RUN = @XVFB_RUN@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +Z_LIBS = @Z_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +gimpdatadir = @gimpdatadir@ +gimpdir = @gimpdir@ +gimplocaledir = @gimplocaledir@ +gimpplugindir = @gimpplugindir@ +gimpsysconfdir = @gimpsysconfdir@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +intltool__v_merge_options_ = @intltool__v_merge_options_@ +intltool__v_merge_options_0 = @intltool__v_merge_options_0@ +libdir = @libdir@ +libexecdir = $(gimpplugindir)/plug-ins/file-dds +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +manpage_gimpdir = @manpage_gimpdir@ +mkdir_p = @mkdir_p@ +ms_librarian = @ms_librarian@ +mypaint_brushes_dir = @mypaint_brushes_dir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la +libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la +libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la +libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la +libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la +libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la +libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la +@OS_WIN32_TRUE@mwindows = -mwindows +@OS_WIN32_FALSE@libm = -lm +@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc +@HAVE_WINDRES_TRUE@file_dds_RC = file-dds.rc.o +AM_LDFLAGS = $(mwindows) +AM_CFLAGS = -fno-strict-aliasing +file_dds_SOURCES = \ + dds.c \ + dds.h \ + color.c \ + color.h \ + ddsplugin.h \ + ddsread.c \ + ddswrite.c \ + dxt.c \ + dxt.h \ + dxt_tables.h \ + endian_rw.h \ + imath.h \ + mipmap.c \ + mipmap.h \ + misc.c \ + misc.h \ + mktables.c \ + vec.h + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(GTK_CFLAGS) \ + $(GEGL_CFLAGS) \ + -I$(includedir) + +LDADD = \ + $(libgimpui) \ + $(libgimpwidgets) \ + $(libgimpconfig) \ + $(libgimp) \ + $(libgimpcolor) \ + $(libgimpmath) \ + $(libgimpbase) \ + $(libm) \ + $(GTK_LIBS) \ + $(GEGL_LIBS) \ + $(RT_LIBS) \ + $(INTLLIBS) \ + $(file_dds_RC) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-dds/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu plug-ins/file-dds/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-libexecPROGRAMS: $(libexec_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-libexecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(libexecdir)" && rm -f $$files + +clean-libexecPROGRAMS: + @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +file-dds$(EXEEXT): $(file_dds_OBJECTS) $(file_dds_DEPENDENCIES) $(EXTRA_file_dds_DEPENDENCIES) + @rm -f file-dds$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(file_dds_OBJECTS) $(file_dds_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dds.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ddsread.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ddswrite.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dxt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mipmap.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mktables.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(libexecdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/color.Po + -rm -f ./$(DEPDIR)/dds.Po + -rm -f ./$(DEPDIR)/ddsread.Po + -rm -f ./$(DEPDIR)/ddswrite.Po + -rm -f ./$(DEPDIR)/dxt.Po + -rm -f ./$(DEPDIR)/mipmap.Po + -rm -f ./$(DEPDIR)/misc.Po + -rm -f ./$(DEPDIR)/mktables.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libexecPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/color.Po + -rm -f ./$(DEPDIR)/dds.Po + -rm -f ./$(DEPDIR)/ddsread.Po + -rm -f ./$(DEPDIR)/ddswrite.Po + -rm -f ./$(DEPDIR)/dxt.Po + -rm -f ./$(DEPDIR)/mipmap.Po + -rm -f ./$(DEPDIR)/misc.Po + -rm -f ./$(DEPDIR)/mktables.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libexecPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libexecPROGRAMS clean-libtool \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-libexecPROGRAMS \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-libexecPROGRAMS + +.PRECIOUS: Makefile + + +# `windres` seems a very stupid tool and it breaks with double shlashes +# in parameter paths. Strengthen the rule a little. +@HAVE_WINDRES_TRUE@%.rc.o: +@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \ +@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \ +@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \ +@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \ +@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\ +@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\ +@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/plug-ins/file-dds/README b/plug-ins/file-dds/README new file mode 100644 index 0000000..4090343 --- /dev/null +++ b/plug-ins/file-dds/README @@ -0,0 +1,29 @@ +DDS plugin for The GIMP +(C) 2004-2012 Shawn Kirst <skirst@gmail.com>, +with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. +========================================== + +This is a plugin for GIMP version 2.4.x. It allows you to load and save +images in Direct Draw Surface (DDS) format. + +Features +========================================== +* Load/Save DDS files using DXT texture compression +* Automatic mipmap generation on save +* Load mipmaps into separate layers +* Load cube map faces and volume map slices into separate layers +* Cube and volume map saving +* Pixel conversion selection for custom formats (RGBA4, R5G6B5, RGB10A2, etc.) +* Load/save DDS files, optionally using DirectX texture compression (DXT) +* Optional automatic mipmap generation when saving +* Load mipmaps into separate layers +* Load cube map faces and volume map slices into separate layers +* Save cube maps and volume maps with automatic mipmap generation support +* Save image with a custom pixel format +* Non-power-of-two image loading and saving support with automatic mipmap generation support +* Compliant with DirectX 10 compressed formats + + +Installation +========================================== +See the file INSTALL for installation instructions diff --git a/plug-ins/file-dds/TODO b/plug-ins/file-dds/TODO new file mode 100644 index 0000000..94b0411 --- /dev/null +++ b/plug-ins/file-dds/TODO @@ -0,0 +1,7 @@ +TODO list for future releases of gimp-dds: + +* Add support for DX10 DDS extensions +* BC6H and BC7 compression support +* Volume map compression support (VTC) +* Add support for GIMP 2.6.x GEGL for reading and writing higher precision +pixel formats diff --git a/plug-ins/file-dds/color.c b/plug-ins/file-dds/color.c new file mode 100644 index 0000000..0c1be40 --- /dev/null +++ b/plug-ins/file-dds/color.c @@ -0,0 +1,58 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. + */ + +#include <math.h> +#include "color.h" + +int +linear_to_sRGB(int c) +{ + float v = (float)c / 255.0f; + + if(v < 0) + v = 0; + else if(v > 1) + v = 1; + else if(v <= 0.0031308f) + v = 12.92f * v; + else + v = 1.055f * powf(v, 0.41666f) - 0.055f; + + return (int)floorf(255.0f * v + 0.5f); +} + +int +sRGB_to_linear(int c) +{ + float v = (float)c / 255.0f; + + if(v < 0) + v = 0; + else if(v > 1) + v = 1; + else if(v <= 0.04045f) + v /= 12.92f; + else + v = powf((v + 0.055f) / 1.055f, 2.4f); + + return (int)floorf(255.0f * v + 0.5f); +} diff --git a/plug-ins/file-dds/color.h b/plug-ins/file-dds/color.h new file mode 100644 index 0000000..70bdb3a --- /dev/null +++ b/plug-ins/file-dds/color.h @@ -0,0 +1,96 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef __COLOR_H__ +#define __COLOR_H__ + +#include "imath.h" + +/* sRGB encoding/decoding */ +int linear_to_sRGB(int c); +int sRGB_to_linear(int c); + +/* YCoCg encoding */ +static inline void +RGB_to_YCoCg (unsigned char *dst, int r, int g, int b) +{ + int y = ((r + (g << 1) + b) + 2) >> 2; + int co = ((((r << 1) - (b << 1)) + 2) >> 2) + 128; + int cg = (((-r + (g << 1) - b) + 2) >> 2) + 128; + + dst[0] = 255; + dst[1] = (cg > 255 ? 255 : (cg < 0 ? 0 : cg)); + dst[2] = (co > 255 ? 255 : (co < 0 ? 0 : co)); + dst[3] = (y > 255 ? 255 : (y < 0 ? 0 : y)); +} + +/* other color conversions */ + +static inline int +rgb_to_luminance (int r, int g, int b) +{ + /* ITU-R BT.709 luma coefficients, scaled by 256 */ + return ((r * 54 + g * 182 + b * 20) + 128) >> 8; +} + +static inline unsigned short +pack_r5g6b5 (int r, int g, int b) +{ + return (mul8bit(r, 31) << 11) | + (mul8bit(g, 63) << 5) | + (mul8bit(b, 31) ); +} + +static inline unsigned short +pack_rgba4 (int r, int g, int b, int a) +{ + return (mul8bit(a, 15) << 12) | + (mul8bit(r, 15) << 8) | + (mul8bit(g, 15) << 4) | + (mul8bit(b, 15) ); +} + +static inline unsigned short +pack_rgb5a1 (int r, int g, int b, int a) +{ + return (((a >> 7) & 0x01) << 15) | + (mul8bit(r, 31) << 10) | + (mul8bit(g, 31) << 5) | + (mul8bit(b, 31) ); +} + +static inline unsigned char +pack_r3g3b2(int r, int g, int b) +{ + return (mul8bit(r, 7) << 5) | + (mul8bit(g, 7) << 2) | + (mul8bit(b, 3) ); +} + +static inline unsigned int +pack_rgb10a2 (int r, int g, int b, int a) +{ + return ((unsigned int)((a >> 6) & 0x003) << 30) | + ((unsigned int)((b << 2) & 0x3ff) << 20) | + ((unsigned int)((g << 2) & 0x3ff) << 10) | + ((unsigned int)((r << 2) & 0x3ff) ); +} + +#endif /* __COLOR_H__ */ diff --git a/plug-ins/file-dds/dds.c b/plug-ins/file-dds/dds.c new file mode 100644 index 0000000..421f8bd --- /dev/null +++ b/plug-ins/file-dds/dds.c @@ -0,0 +1,482 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <gtk/gtk.h> + +#include <libgimp/gimp.h> +#include <libgimp/gimpui.h> + +#include <libgimp/stdplugins-intl.h> + +#include "ddsplugin.h" +#include "dds.h" +#include "misc.h" + + +static void query (void); +static void run (const gchar *name, + gint nparams, + const GimpParam *param, + gint *nreturn_vals, + GimpParam **return_vals); + + +GimpPlugInInfo PLUG_IN_INFO = +{ + 0, + 0, + query, + run +}; + +DDSWriteVals dds_write_vals = +{ + DDS_COMPRESS_NONE, + DDS_MIPMAP_NONE, + DDS_SAVE_SELECTED_LAYER, + DDS_FORMAT_DEFAULT, + -1, + DDS_MIPMAP_FILTER_DEFAULT, + DDS_MIPMAP_WRAP_DEFAULT, + 0, + 0, + 0.0, + 0, + 0, + 0, + 0.5 +}; + +DDSReadVals dds_read_vals = +{ + 1, + 1 +}; + +static GimpParamDef load_args[] = +{ + { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"}, + { GIMP_PDB_STRING, "filename", "The name of the file to load"}, + { GIMP_PDB_STRING, "raw_filename", "The name entered"}, + { GIMP_PDB_INT32, "load_mipmaps", "Load mipmaps if present"}, + { GIMP_PDB_INT32, "decode_images", "Decode YCoCg/AExp images when detected"} +}; + +static GimpParamDef load_return_vals[] = +{ + { GIMP_PDB_IMAGE, "image", "Output image"} +}; + +static GimpParamDef save_args[] = +{ + { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"}, + { GIMP_PDB_IMAGE, "image", "Input image"}, + { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save"}, + { GIMP_PDB_STRING, "filename", "The name of the file to save the image as"}, + { GIMP_PDB_STRING, "raw_filename", "The name entered"}, + { GIMP_PDB_INT32, "compression_format", "Compression format (0 = None, 1 = BC1/DXT1, 2 = BC2/DXT3, 3 = BC3/DXT5, 4 = BC3n/DXT5nm, 5 = BC4/ATI1N, 6 = BC5/ATI2N, 7 = RXGB (DXT5), 8 = Alpha Exponent (DXT5), 9 = YCoCg (DXT5), 10 = YCoCg scaled (DXT5))"}, + { GIMP_PDB_INT32, "mipmaps", "How to handle mipmaps (0 = No mipmaps, 1 = Generate mipmaps, 2 = Use existing mipmaps (layers)"}, + { GIMP_PDB_INT32, "savetype", "How to save the image (0 = selected layer, 1 = cube map, 2 = volume map, 3 = texture array, 4 = all visible layers"}, + { GIMP_PDB_INT32, "format", "Custom pixel format (0 = default, 1 = R5G6B5, 2 = RGBA4, 3 = RGB5A1, 4 = RGB10A2)"}, + { GIMP_PDB_INT32, "transparent_index", "Index of transparent color or -1 to disable (for indexed images only)."}, + { GIMP_PDB_INT32, "mipmap_filter", "Filtering to use when generating mipmaps (0 = default, 1 = nearest, 2 = box, 3 = triangle, 4 = quadratic, 5 = bspline, 6 = mitchell, 7 = lanczos, 8 = kaiser)"}, + { GIMP_PDB_INT32, "mipmap_wrap", "Wrap mode to use when generating mipmaps (0 = default, 1 = mirror, 2 = repeat, 3 = clamp)"}, + { GIMP_PDB_INT32, "gamma_correct", "Use gamma correct mipmap filtering"}, + { GIMP_PDB_INT32, "srgb", "Use sRGB colorspace for gamma correction"}, + { GIMP_PDB_FLOAT, "gamma", "Gamma value to use for gamma correction (i.e. 2.2)"}, + { GIMP_PDB_INT32, "perceptual_metric", "Use a perceptual error metric during compression"}, + { GIMP_PDB_INT32, "preserve_alpha_coverage", "Preserve alpha test converage for alpha channel maps"}, + { GIMP_PDB_FLOAT, "alpha_test_threshold", "Alpha test threshold value for which alpha test converage should be preserved"} +}; + +static GimpParamDef save_args2[] = +{ + { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"}, + { GIMP_PDB_IMAGE, "image", "Input image"}, + { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save"}, + { GIMP_PDB_STRING, "filename", "The name of the file to save the image as"}, + { GIMP_PDB_STRING, "raw_filename", "The name entered"}, + { GIMP_PDB_INT32, "compression_format", "Compression format (0 = None, 1 = BC1/DXT1, 2 = BC2/DXT3, 3 = BC3/DXT5, 4 = BC3n/DXT5nm, 5 = BC4/ATI1N, 6 = BC5/ATI2N, 7 = RXGB (DXT5), 8 = Alpha Exponent (DXT5), 9 = YCoCg (DXT5), 10 = YCoCg scaled (DXT5))"}, + { GIMP_PDB_INT32, "mipmaps", "How to handle mipmaps (0 = No mipmaps, 1 = Generate mipmaps, 2 = Use existing mipmaps (layers)"}, + { GIMP_PDB_INT32, "savetype", "How to save the image (0 = selected layer, 1 = cube map, 2 = volume map, 3 = texture array, 4 = all visible layers"}, + { GIMP_PDB_INT32, "format", "Custom pixel format (0 = default, 1 = R5G6B5, 2 = RGBA4, 3 = RGB5A1, 4 = RGB10A2)"}, + { GIMP_PDB_INT32, "transparent_index", "Index of transparent color or -1 to disable (for indexed images only)."}, + { GIMP_PDB_INT32, "mipmap_filter", "Filtering to use when generating mipmaps (0 = default, 1 = nearest, 2 = box, 3 = triangle, 4 = quadratic, 5 = bspline, 6 = mitchell, 7 = lanczos, 8 = kaiser)"}, + { GIMP_PDB_INT32, "mipmap_wrap", "Wrap mode to use when generating mipmaps (0 = default, 1 = mirror, 2 = repeat, 3 = clamp)"}, + { GIMP_PDB_INT32, "gamma_correct", "Use gamma correct mipmap filtering"}, + { GIMP_PDB_INT32, "srgb", "Use sRGB colorspace for gamma correction"}, + { GIMP_PDB_FLOAT, "gamma", "Gamma value to use for gamma correction (i.e. 2.2)"}, + { GIMP_PDB_INT32, "perceptual_metric", "Use a perceptual error metric during compression"}, + { GIMP_PDB_INT32, "preserve_alpha_coverage", "Preserve alpha test converage for alpha channel maps"}, + { GIMP_PDB_FLOAT, "alpha_test_threshold", "Alpha test threshold value for which alpha test converage should be preserved"}, + { GIMP_PDB_INT32, "flip_image", "Flip image vertically on export"} +}; + +#if 0 +static GimpParamDef decode_args[] = +{ + { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"}, + { GIMP_PDB_IMAGE, "image", "Input image"}, + { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save"} +}; +#endif + + +MAIN () + + +static void +query (void) +{ + gimp_install_procedure (LOAD_PROC, + "Loads files in DDS image format", + "Loads files in DDS image format", + "Shawn Kirst", + "Shawn Kirst", + "2008", + N_("DDS image"), + 0, + GIMP_PLUGIN, + G_N_ELEMENTS (load_args), + G_N_ELEMENTS (load_return_vals), + load_args, load_return_vals); + + gimp_register_file_handler_mime (LOAD_PROC, "image/dds"); + gimp_register_magic_load_handler (LOAD_PROC, + "dds", + "", + "0,string,DDS"); + + gimp_install_procedure (SAVE_PROC, + "Saves files in DDS image format", + "Saves files in DDS image format", + "Shawn Kirst", + "Shawn Kirst", + "2008", + N_("DDS image"), + "INDEXED, GRAY, RGB", + GIMP_PLUGIN, + G_N_ELEMENTS (save_args), 0, + save_args, 0); + + gimp_register_file_handler_mime (SAVE_PROC, "image/dds"); + gimp_register_save_handler (SAVE_PROC, + "dds", + ""); + + gimp_install_procedure (SAVE_PROC2, + "Saves files in DDS image format " + "with additional export options", + "Saves files in DDS image format " + "with additional export options", + "Shawn Kirst", + "Shawn Kirst", + "2008", + N_("DDS image"), + "INDEXED, GRAY, RGB", + GIMP_PLUGIN, + G_N_ELEMENTS (save_args2), 0, + save_args2, 0); + + gimp_register_file_handler_mime (SAVE_PROC2, "image/dds"); + gimp_register_save_handler (SAVE_PROC2, + "dds", + ""); +#if 0 + gimp_install_procedure (DECODE_YCOCG_PROC, + "Converts YCoCg encoded pixels to RGB", + "Converts YCoCg encoded pixels to RGB", + "Shawn Kirst", + "Shawn Kirst", + "2008", + N_("Decode YCoCg"), + "RGBA", + GIMP_PLUGIN, + G_N_ELEMENTS (decode_args), 0, + decode_args, 0); + /*gimp_plugin_menu_register (DECODE_YCOCG_PROC, "<Image>/Filters/Colors");*/ + + gimp_install_procedure (DECODE_YCOCG_SCALED_PROC, + "Converts YCoCg (scaled) encoded pixels to RGB", + "Converts YCoCg (scaled) encoded pixels to RGB", + "Shawn Kirst", + "Shawn Kirst", + "2008", + N_("Decode YCoCg (scaled)"), + "RGBA", + GIMP_PLUGIN, + G_N_ELEMENTS (decode_args), 0, + decode_args, 0); + /*gimp_plugin_menu_register (DECODE_YCOCG_SCALED_PROC, "<Image>/Filters/Colors");*/ + + gimp_install_procedure (DECODE_ALPHA_EXP_PROC, + "Converts alpha exponent encoded pixels to RGB", + "Converts alpha exponent encoded pixels to RGB", + "Shawn Kirst", + "Shawn Kirst", + "2008", + N_("Decode Alpha exponent"), + "RGBA", + GIMP_PLUGIN, + G_N_ELEMENTS (decode_args), 0, + decode_args, 0); + /*gimp_plugin_menu_register (DECODE_ALPHA_EXP_PROC, "<Image>/Filters/Colors");*/ +#endif +} + +static void +run (const gchar *name, + gint nparams, + const GimpParam *param, + gint *nreturn_vals, + GimpParam **return_vals) +{ + static GimpParam values[2]; + GimpRunMode run_mode; + GimpPDBStatusType status = GIMP_PDB_SUCCESS; + gint32 imageID; + gint32 drawableID; + GimpExportReturn export = GIMP_EXPORT_CANCEL; + + INIT_I18N (); + gegl_init (NULL, NULL); + + run_mode = param[0].data.d_int32; + + *nreturn_vals = 1; + *return_vals = values; + + values[0].type = GIMP_PDB_STATUS; + values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR; + + if (! strcmp (name, LOAD_PROC)) + { + switch (run_mode) + { + case GIMP_RUN_INTERACTIVE: + gimp_ui_init ("dds", 0); + gimp_get_data (LOAD_PROC, &dds_read_vals); + break; + + case GIMP_RUN_NONINTERACTIVE: + dds_read_vals.mipmaps = param[3].data.d_int32; + dds_read_vals.decode_images = param[4].data.d_int32; + if (nparams != G_N_ELEMENTS (load_args)) + status = GIMP_PDB_CALLING_ERROR; + break; + + default: + break; + } + + if (status == GIMP_PDB_SUCCESS) + { + status = read_dds (param[1].data.d_string, &imageID, + run_mode == GIMP_RUN_INTERACTIVE); + if (status == GIMP_PDB_SUCCESS && imageID != -1) + { + *nreturn_vals = 2; + values[1].type = GIMP_PDB_IMAGE; + values[1].data.d_image = imageID; + if (run_mode == GIMP_RUN_INTERACTIVE) + gimp_set_data (LOAD_PROC, &dds_read_vals, sizeof (dds_read_vals)); + } + else if (status != GIMP_PDB_CANCEL) + { + status = GIMP_PDB_EXECUTION_ERROR; + } + } + } + else if (! strcmp (name, SAVE_PROC) || + ! strcmp (name, SAVE_PROC2)) + { + imageID = param[1].data.d_int32; + drawableID = param[2].data.d_int32; + + switch (run_mode) + { + case GIMP_RUN_INTERACTIVE: + case GIMP_RUN_WITH_LAST_VALS: + gimp_ui_init ("dds", 0); + export = gimp_export_image (&imageID, &drawableID, "DDS", + (GIMP_EXPORT_CAN_HANDLE_RGB | + GIMP_EXPORT_CAN_HANDLE_GRAY | + GIMP_EXPORT_CAN_HANDLE_INDEXED | + GIMP_EXPORT_CAN_HANDLE_ALPHA | + GIMP_EXPORT_CAN_HANDLE_LAYERS)); + if (export == GIMP_EXPORT_CANCEL) + { + values[0].data.d_status = GIMP_PDB_CANCEL; + return; + } + + default: + break; + } + + switch (run_mode) + { + case GIMP_RUN_INTERACTIVE: + gimp_get_data (SAVE_PROC, &dds_write_vals); + break; + + case GIMP_RUN_NONINTERACTIVE: + if (nparams != G_N_ELEMENTS (save_args)) + { + status = GIMP_PDB_CALLING_ERROR; + } + else + { + dds_write_vals.compression = param[5].data.d_int32; + dds_write_vals.mipmaps = param[6].data.d_int32; + dds_write_vals.savetype = param[7].data.d_int32; + dds_write_vals.format = param[8].data.d_int32; + dds_write_vals.transindex = param[9].data.d_int32; + dds_write_vals.mipmap_filter = param[10].data.d_int32; + dds_write_vals.mipmap_wrap = param[11].data.d_int32; + dds_write_vals.gamma_correct = param[12].data.d_int32; + dds_write_vals.srgb = param[13].data.d_int32; + dds_write_vals.gamma = param[14].data.d_float; + dds_write_vals.perceptual_metric = param[15].data.d_int32; + dds_write_vals.preserve_alpha_coverage = param[16].data.d_int32; + dds_write_vals.alpha_test_threshold = param[17].data.d_float; + if (nparams > 18) + dds_write_vals.flip_image = param[18].data.d_int32; + else + dds_write_vals.flip_image = FALSE; + + if ((dds_write_vals.compression < DDS_COMPRESS_NONE) || + (dds_write_vals.compression >= DDS_COMPRESS_MAX)) + { + status = GIMP_PDB_CALLING_ERROR; + } + + if ((dds_write_vals.mipmaps < DDS_MIPMAP_NONE) || + (dds_write_vals.mipmaps >= DDS_MIPMAP_MAX)) + { + status = GIMP_PDB_CALLING_ERROR; + } + + if ((dds_write_vals.savetype < DDS_SAVE_SELECTED_LAYER) || + (dds_write_vals.savetype >= DDS_SAVE_MAX)) + { + status = GIMP_PDB_CALLING_ERROR; + } + + if ((dds_write_vals.format < DDS_FORMAT_DEFAULT) || + (dds_write_vals.format >= DDS_FORMAT_MAX)) + { + status = GIMP_PDB_CALLING_ERROR; + } + + if ((dds_write_vals.mipmap_filter < DDS_MIPMAP_FILTER_DEFAULT) || + (dds_write_vals.mipmap_filter >= DDS_MIPMAP_FILTER_MAX)) + { + status = GIMP_PDB_CALLING_ERROR; + } + + if ((dds_write_vals.mipmap_wrap < DDS_MIPMAP_WRAP_DEFAULT) || + (dds_write_vals.mipmap_wrap >= DDS_MIPMAP_WRAP_MAX)) + { + status = GIMP_PDB_CALLING_ERROR; + } + } + break; + + case GIMP_RUN_WITH_LAST_VALS: + gimp_get_data (SAVE_PROC, &dds_write_vals); + break; + + default: + break; + } + + if (dds_write_vals.gamma < 1e-04f) + /* gimp_gamma () got removed and was always returning 2.2 anyway. + * XXX Review this piece of code if we expect gamma value could + * be parameterized. + */ + dds_write_vals.gamma = 2.2; + + if (status == GIMP_PDB_SUCCESS) + { + status = write_dds (param[3].data.d_string, imageID, drawableID, + run_mode == GIMP_RUN_INTERACTIVE, + export == GIMP_EXPORT_EXPORT); + if (status == GIMP_PDB_SUCCESS) + gimp_set_data (SAVE_PROC, &dds_write_vals, sizeof (dds_write_vals)); + } + + if (export == GIMP_EXPORT_EXPORT) + gimp_image_delete (imageID); + } +#if 0 + else if (! strcmp (name, DECODE_YCOCG_PROC)) + { + imageID = param[1].data.d_int32; + drawableID = param[2].data.d_int32; + + decode_ycocg_image (drawableID, TRUE); + + status = GIMP_PDB_SUCCESS; + + if (run_mode != GIMP_RUN_NONINTERACTIVE) + gimp_displays_flush (); + } + else if (! strcmp (name, DECODE_YCOCG_SCALED_PROC)) + { + imageID = param[1].data.d_int32; + drawableID = param[2].data.d_int32; + + decode_ycocg_scaled_image (drawableID, TRUE); + + status = GIMP_PDB_SUCCESS; + + if (run_mode != GIMP_RUN_NONINTERACTIVE) + gimp_displays_flush (); + } + else if (! strcmp (name, DECODE_ALPHA_EXP_PROC)) + { + imageID = param[1].data.d_int32; + drawableID = param[2].data.d_int32; + + decode_alpha_exp_image (drawableID, TRUE); + + status = GIMP_PDB_SUCCESS; + + if (run_mode != GIMP_RUN_NONINTERACTIVE) + gimp_displays_flush (); + } +#endif + else + { + status = GIMP_PDB_CALLING_ERROR; + } + + values[0].data.d_status = status; +} diff --git a/plug-ins/file-dds/dds.h b/plug-ins/file-dds/dds.h new file mode 100644 index 0000000..971b00a --- /dev/null +++ b/plug-ins/file-dds/dds.h @@ -0,0 +1,326 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef __DDS_H__ +#define __DDS_H__ + +#define FOURCC(a, b, c, d) \ + ((unsigned int)((unsigned int)(a) ) | \ + ((unsigned int)(b) << 8) | \ + ((unsigned int)(c) << 16) | \ + ((unsigned int)(d) << 24)) + +typedef enum +{ + DDS_COMPRESS_NONE = 0, + DDS_COMPRESS_BC1, /* DXT1 */ + DDS_COMPRESS_BC2, /* DXT3 */ + DDS_COMPRESS_BC3, /* DXT5 */ + DDS_COMPRESS_BC3N, /* DXT5n */ + DDS_COMPRESS_BC4, /* ATI1 */ + DDS_COMPRESS_BC5, /* ATI2 */ + DDS_COMPRESS_RXGB, /* DXT5 */ + DDS_COMPRESS_AEXP, /* DXT5 */ + DDS_COMPRESS_YCOCG, /* DXT5 */ + DDS_COMPRESS_YCOCGS, /* DXT5 */ + DDS_COMPRESS_MAX +} DDS_COMPRESSION_TYPE; + +typedef enum +{ + DDS_SAVE_SELECTED_LAYER = 0, + DDS_SAVE_CUBEMAP, + DDS_SAVE_VOLUMEMAP, + DDS_SAVE_ARRAY, + DDS_SAVE_VISIBLE_LAYERS, + DDS_SAVE_MAX +} DDS_SAVE_TYPE; + +typedef enum +{ + DDS_FORMAT_DEFAULT = 0, + DDS_FORMAT_RGB8, + DDS_FORMAT_RGBA8, + DDS_FORMAT_BGR8, + DDS_FORMAT_ABGR8, + DDS_FORMAT_R5G6B5, + DDS_FORMAT_RGBA4, + DDS_FORMAT_RGB5A1, + DDS_FORMAT_RGB10A2, + DDS_FORMAT_R3G3B2, + DDS_FORMAT_A8, + DDS_FORMAT_L8, + DDS_FORMAT_L8A8, + DDS_FORMAT_AEXP, + DDS_FORMAT_YCOCG, + DDS_FORMAT_MAX +} DDS_FORMAT_TYPE; + +typedef enum +{ + DDS_MIPMAP_NONE = 0, + DDS_MIPMAP_GENERATE, + DDS_MIPMAP_EXISTING, + DDS_MIPMAP_MAX +} DDS_MIPMAP; + +typedef enum +{ + DDS_MIPMAP_FILTER_DEFAULT = 0, + DDS_MIPMAP_FILTER_NEAREST, + DDS_MIPMAP_FILTER_BOX, + DDS_MIPMAP_FILTER_TRIANGLE, + DDS_MIPMAP_FILTER_QUADRATIC, + DDS_MIPMAP_FILTER_BSPLINE, + DDS_MIPMAP_FILTER_MITCHELL, + DDS_MIPMAP_FILTER_LANCZOS, + DDS_MIPMAP_FILTER_KAISER, + DDS_MIPMAP_FILTER_MAX +} DDS_MIPMAP_FILTER; + +typedef enum +{ + DDS_MIPMAP_WRAP_DEFAULT = 0, + DDS_MIPMAP_WRAP_MIRROR, + DDS_MIPMAP_WRAP_REPEAT, + DDS_MIPMAP_WRAP_CLAMP, + DDS_MIPMAP_WRAP_MAX +} DDS_MIPMAP_WRAP; + +#define DDS_HEADERSIZE 128 +#define DDS_HEADERSIZE_DX10 20 + +#define DDSD_CAPS 0x00000001 +#define DDSD_HEIGHT 0x00000002 +#define DDSD_WIDTH 0x00000004 +#define DDSD_PITCH 0x00000008 +#define DDSD_PIXELFORMAT 0x00001000 +#define DDSD_MIPMAPCOUNT 0x00020000 +#define DDSD_LINEARSIZE 0x00080000 +#define DDSD_DEPTH 0x00800000 + +#define DDPF_ALPHAPIXELS 0x00000001 +#define DDPF_ALPHA 0x00000002 +#define DDPF_FOURCC 0x00000004 +#define DDPF_PALETTEINDEXED8 0x00000020 +#define DDPF_RGB 0x00000040 +#define DDPF_LUMINANCE 0x00020000 +#define DDPF_NORMAL 0x80000000 // nvidia specific + +#define DDSCAPS_COMPLEX 0x00000008 +#define DDSCAPS_TEXTURE 0x00001000 +#define DDSCAPS_MIPMAP 0x00400000 + +#define DDSCAPS2_CUBEMAP 0x00000200 +#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400 +#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800 +#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000 +#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000 +#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000 +#define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000 +#define DDSCAPS2_CUBEMAP_ALL_FACES \ + (DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX | \ + DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP_NEGATIVEY | \ + DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ) + +#define DDSCAPS2_VOLUME 0x00200000 + +#define D3D10_RESOURCE_MISC_TEXTURECUBE 0x04 +#define D3D10_RESOURCE_DIMENSION_BUFFER 1 +#define D3D10_RESOURCE_DIMENSION_TEXTURE1D 2 +#define D3D10_RESOURCE_DIMENSION_TEXTURE2D 3 +#define D3D10_RESOURCE_DIMENSION_TEXTURE3D 4 + +typedef struct +{ + unsigned int size; + unsigned int flags; + char fourcc[4]; + unsigned int bpp; + unsigned int rmask; + unsigned int gmask; + unsigned int bmask; + unsigned int amask; +} dds_pixel_format_t; + +typedef struct +{ + unsigned int caps1; + unsigned int caps2; + unsigned int reserved[2]; +} dds_caps_t; + +typedef struct +{ + unsigned int magic; + unsigned int size; + unsigned int flags; + unsigned int height; + unsigned int width; + unsigned int pitch_or_linsize; + unsigned int depth; + unsigned int num_mipmaps; + union + { + struct + { + unsigned int magic1; // FOURCC "GIMP" + unsigned int magic2; // FOURCC "-DDS" + unsigned int version; + unsigned int extra_fourcc; + } gimp_dds_special; + unsigned char pad[4 * 11]; + } reserved; + dds_pixel_format_t pixelfmt; + dds_caps_t caps; + unsigned int reserved2; +} dds_header_t; + +typedef enum +{ + DXGI_FORMAT_UNKNOWN = 0, + DXGI_FORMAT_R32G32B32A32_TYPELESS = 1, + DXGI_FORMAT_R32G32B32A32_FLOAT = 2, + DXGI_FORMAT_R32G32B32A32_UINT = 3, + DXGI_FORMAT_R32G32B32A32_SINT = 4, + DXGI_FORMAT_R32G32B32_TYPELESS = 5, + DXGI_FORMAT_R32G32B32_FLOAT = 6, + DXGI_FORMAT_R32G32B32_UINT = 7, + DXGI_FORMAT_R32G32B32_SINT = 8, + DXGI_FORMAT_R16G16B16A16_TYPELESS = 9, + DXGI_FORMAT_R16G16B16A16_FLOAT = 10, + DXGI_FORMAT_R16G16B16A16_UNORM = 11, + DXGI_FORMAT_R16G16B16A16_UINT = 12, + DXGI_FORMAT_R16G16B16A16_SNORM = 13, + DXGI_FORMAT_R16G16B16A16_SINT = 14, + DXGI_FORMAT_R32G32_TYPELESS = 15, + DXGI_FORMAT_R32G32_FLOAT = 16, + DXGI_FORMAT_R32G32_UINT = 17, + DXGI_FORMAT_R32G32_SINT = 18, + DXGI_FORMAT_R32G8X24_TYPELESS = 19, + DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20, + DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21, + DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22, + DXGI_FORMAT_R10G10B10A2_TYPELESS = 23, + DXGI_FORMAT_R10G10B10A2_UNORM = 24, + DXGI_FORMAT_R10G10B10A2_UINT = 25, + DXGI_FORMAT_R11G11B10_FLOAT = 26, + DXGI_FORMAT_R8G8B8A8_TYPELESS = 27, + DXGI_FORMAT_R8G8B8A8_UNORM = 28, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29, + DXGI_FORMAT_R8G8B8A8_UINT = 30, + DXGI_FORMAT_R8G8B8A8_SNORM = 31, + DXGI_FORMAT_R8G8B8A8_SINT = 32, + DXGI_FORMAT_R16G16_TYPELESS = 33, + DXGI_FORMAT_R16G16_FLOAT = 34, + DXGI_FORMAT_R16G16_UNORM = 35, + DXGI_FORMAT_R16G16_UINT = 36, + DXGI_FORMAT_R16G16_SNORM = 37, + DXGI_FORMAT_R16G16_SINT = 38, + DXGI_FORMAT_R32_TYPELESS = 39, + DXGI_FORMAT_D32_FLOAT = 40, + DXGI_FORMAT_R32_FLOAT = 41, + DXGI_FORMAT_R32_UINT = 42, + DXGI_FORMAT_R32_SINT = 43, + DXGI_FORMAT_R24G8_TYPELESS = 44, + DXGI_FORMAT_D24_UNORM_S8_UINT = 45, + DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46, + DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47, + DXGI_FORMAT_R8G8_TYPELESS = 48, + DXGI_FORMAT_R8G8_UNORM = 49, + DXGI_FORMAT_R8G8_UINT = 50, + DXGI_FORMAT_R8G8_SNORM = 51, + DXGI_FORMAT_R8G8_SINT = 52, + DXGI_FORMAT_R16_TYPELESS = 53, + DXGI_FORMAT_R16_FLOAT = 54, + DXGI_FORMAT_D16_UNORM = 55, + DXGI_FORMAT_R16_UNORM = 56, + DXGI_FORMAT_R16_UINT = 57, + DXGI_FORMAT_R16_SNORM = 58, + DXGI_FORMAT_R16_SINT = 59, + DXGI_FORMAT_R8_TYPELESS = 60, + DXGI_FORMAT_R8_UNORM = 61, + DXGI_FORMAT_R8_UINT = 62, + DXGI_FORMAT_R8_SNORM = 63, + DXGI_FORMAT_R8_SINT = 64, + DXGI_FORMAT_A8_UNORM = 65, + DXGI_FORMAT_R1_UNORM = 66, + DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67, + DXGI_FORMAT_R8G8_B8G8_UNORM = 68, + DXGI_FORMAT_G8R8_G8B8_UNORM = 69, + DXGI_FORMAT_BC1_TYPELESS = 70, + DXGI_FORMAT_BC1_UNORM = 71, + DXGI_FORMAT_BC1_UNORM_SRGB = 72, + DXGI_FORMAT_BC2_TYPELESS = 73, + DXGI_FORMAT_BC2_UNORM = 74, + DXGI_FORMAT_BC2_UNORM_SRGB = 75, + DXGI_FORMAT_BC3_TYPELESS = 76, + DXGI_FORMAT_BC3_UNORM = 77, + DXGI_FORMAT_BC3_UNORM_SRGB = 78, + DXGI_FORMAT_BC4_TYPELESS = 79, + DXGI_FORMAT_BC4_UNORM = 80, + DXGI_FORMAT_BC4_SNORM = 81, + DXGI_FORMAT_BC5_TYPELESS = 82, + DXGI_FORMAT_BC5_UNORM = 83, + DXGI_FORMAT_BC5_SNORM = 84, + DXGI_FORMAT_B5G6R5_UNORM = 85, + DXGI_FORMAT_B5G5R5A1_UNORM = 86, + DXGI_FORMAT_B8G8R8A8_UNORM = 87, + DXGI_FORMAT_B8G8R8X8_UNORM = 88, + DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89, + DXGI_FORMAT_B8G8R8A8_TYPELESS = 90, + DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91, + DXGI_FORMAT_B8G8R8X8_TYPELESS = 92, + DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93, + DXGI_FORMAT_BC6H_TYPELESS = 94, + DXGI_FORMAT_BC6H_UF16 = 95, + DXGI_FORMAT_BC6H_SF16 = 96, + DXGI_FORMAT_BC7_TYPELESS = 97, + DXGI_FORMAT_BC7_UNORM = 98, + DXGI_FORMAT_BC7_UNORM_SRGB = 99, + DXGI_FORMAT_AYUV = 100, + DXGI_FORMAT_Y410 = 101, + DXGI_FORMAT_Y416 = 102, + DXGI_FORMAT_NV12 = 103, + DXGI_FORMAT_P010 = 104, + DXGI_FORMAT_P016 = 105, + DXGI_FORMAT_420_OPAQUE = 106, + DXGI_FORMAT_YUY2 = 107, + DXGI_FORMAT_Y210 = 108, + DXGI_FORMAT_Y216 = 109, + DXGI_FORMAT_NV11 = 110, + DXGI_FORMAT_AI44 = 111, + DXGI_FORMAT_IA44 = 112, + DXGI_FORMAT_P8 = 113, + DXGI_FORMAT_A8P8 = 114, + DXGI_FORMAT_B4G4R4A4_UNORM = 115, + DXGI_FORMAT_FORCE_UINT = 0xffffffffUL +} DXGI_FORMAT; + +typedef struct +{ + DXGI_FORMAT dxgiFormat; + unsigned int resourceDimension; + unsigned int miscFlag; + unsigned int arraySize; + unsigned int reserved; +} dds_header_dx10_t; + +#endif /* __DDS_H__ */ diff --git a/plug-ins/file-dds/ddsplugin.h b/plug-ins/file-dds/ddsplugin.h new file mode 100644 index 0000000..96257c9 --- /dev/null +++ b/plug-ins/file-dds/ddsplugin.h @@ -0,0 +1,79 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef __DDSPLUGIN_H__ +#define __DDSPLUGIN_H__ + +#define DDS_PLUGIN_VERSION_MAJOR 3 +#define DDS_PLUGIN_VERSION_MINOR 9 +#define DDS_PLUGIN_VERSION_REVISION 92 + +#define DDS_PLUGIN_VERSION \ + ((unsigned int)(DDS_PLUGIN_VERSION_MAJOR << 16) | \ + (unsigned int)(DDS_PLUGIN_VERSION_MINOR << 8) | \ + (unsigned int)(DDS_PLUGIN_VERSION_REVISION)) + +typedef struct +{ + int compression; + int mipmaps; + int savetype; + int format; + int transindex; + int mipmap_filter; + int mipmap_wrap; + int gamma_correct; + int srgb; + float gamma; + int perceptual_metric; + int show_adv_opt; + int preserve_alpha_coverage; + float alpha_test_threshold; + gboolean flip_image; +} DDSWriteVals; + +typedef struct +{ + int mipmaps; + int decode_images; +} DDSReadVals; + +extern DDSWriteVals dds_write_vals; +extern DDSReadVals dds_read_vals; + +extern GimpPDBStatusType read_dds (gchar *filename, + gint32 *imageID, + gboolean interactive_dds); +extern GimpPDBStatusType write_dds (gchar *filename, + gint32 image_id, + gint32 drawable_id, + gboolean interactive_dds, + gboolean is_duplicate_image); + + +#define LOAD_PROC "file-dds-load" +#define SAVE_PROC "file-dds-save" +#define SAVE_PROC2 "file-dds-save2" + +#define DECODE_YCOCG_PROC "color-decode-ycocg" +#define DECODE_YCOCG_SCALED_PROC "color-decode-ycocg-scaled" +#define DECODE_ALPHA_EXP_PROC "color-decode-alpha-exp" + +#endif /* __DDSPLUGIN_H__ */ diff --git a/plug-ins/file-dds/ddsread.c b/plug-ins/file-dds/ddsread.c new file mode 100644 index 0000000..dcb4449 --- /dev/null +++ b/plug-ins/file-dds/ddsread.c @@ -0,0 +1,1439 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. + */ + +/* + ** !!! COPYRIGHT NOTICE !!! + ** + ** The following is based on code (C) 2003 Arne Reuter <homepage@arnereuter.de> + ** URL: http://www.dr-reuter.de/arne/dds.html + ** + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <gtk/gtk.h> +#include <glib/gstdio.h> + +#include <libgimp/gimp.h> +#include <libgimp/gimpui.h> + +#include <libgimp/stdplugins-intl.h> + +#include "ddsplugin.h" +#include "dds.h" +#include "dxt.h" +#include "endian_rw.h" +#include "misc.h" +#include "imath.h" + +typedef struct +{ + unsigned char rshift, gshift, bshift, ashift; + unsigned char rbits, gbits, bbits, abits; + unsigned int rmask, gmask, bmask, amask; + unsigned int bpp, gimp_bpp; + unsigned int gimp_bps; /* bytes per sample */ + int tile_height; + unsigned char *palette; +} dds_load_info_t; + +static int read_header (dds_header_t *hdr, + FILE *fp); +static int read_header_dx10 (dds_header_dx10_t *hdr, + FILE *fp); +static int validate_header (dds_header_t *hdr); +static int setup_dxgi_format (dds_header_t *hdr, + dds_header_dx10_t *dx10hdr); +static int load_layer (FILE *fp, + dds_header_t *hdr, + dds_load_info_t *d, + gint32 image, + unsigned int level, + char *prefix, + unsigned int *l, + guchar *pixels, + unsigned char *buf); +static int load_mipmaps (FILE *fp, + dds_header_t *hdr, + dds_load_info_t *d, + gint32 image, + char *prefix, + unsigned int *l, + guchar *pixels, + unsigned char *buf); +static int load_face (FILE *fp, + dds_header_t *hdr, + dds_load_info_t *d, + gint32 image, + char *prefix, + unsigned int *l, + guchar *pixels, + unsigned char *buf); +static unsigned char color_bits (unsigned int mask); +static unsigned char color_shift (unsigned int mask); +static int load_dialog (void); + +static gboolean runme = FALSE; + +GimpPDBStatusType +read_dds (gchar *filename, + gint32 *imageID, + gboolean interactive_dds) +{ + gint32 image = 0; + unsigned char *buf; + unsigned int l = 0; + guchar *pixels; + gchar *tmp; + FILE *fp; + gsize file_size; + dds_header_t hdr; + dds_header_dx10_t dx10hdr; + dds_load_info_t d; + gint *layers, layer_count; + GimpImageBaseType type; + GimpPrecision precision; + int i, j; + + if (interactive_dds) + { + if (!load_dialog ()) + return GIMP_PDB_CANCEL; + } + + fp = g_fopen (filename, "rb"); + if (fp == 0) + { + g_message ("Error opening file.\n"); + return GIMP_PDB_EXECUTION_ERROR; + } + + fseek (fp, 0L, SEEK_END); + file_size = ftell (fp); + fseek (fp, 0, SEEK_SET); + + if (strrchr (filename, '/')) + tmp = g_strdup_printf ("Loading %s:", strrchr (filename, '/') + 1); + else + tmp = g_strdup_printf ("Loading %s:", filename); + gimp_progress_init (tmp); + g_free (tmp); + + /* read header */ + read_header (&hdr, fp); + + memset (&dx10hdr, 0, sizeof (dds_header_dx10_t)); + + /* read DX10 header if necessary */ + if (GETL32(hdr.pixelfmt.fourcc) == FOURCC ('D','X','1','0')) + { + read_header_dx10(&dx10hdr, fp); + + if (!setup_dxgi_format (&hdr, &dx10hdr)) + { + fclose (fp); + return GIMP_PDB_EXECUTION_ERROR; + } + } + + if (!validate_header (&hdr)) + { + fclose (fp); + g_message ("Invalid DDS header!\n"); + return GIMP_PDB_EXECUTION_ERROR; + } + + /* a lot of DDS images out there don't have this for some reason -_- */ + if (hdr.pitch_or_linsize == 0) + { + if (hdr.pixelfmt.flags & DDPF_FOURCC) /* assume linear size */ + { + hdr.pitch_or_linsize = ((hdr.width + 3) >> 2) * ((hdr.height + 3) >> 2); + switch (GETL32(hdr.pixelfmt.fourcc)) + { + case FOURCC ('D','X','T','1'): + case FOURCC ('A','T','I','1'): + case FOURCC ('B','C','4','U'): + case FOURCC ('B','C','4','S'): + hdr.pitch_or_linsize *= 8; + break; + default: + hdr.pitch_or_linsize *= 16; + break; + } + } + else /* assume pitch */ + { + hdr.pitch_or_linsize = hdr.height * hdr.width * (hdr.pixelfmt.bpp >> 3); + } + } + + if (hdr.pixelfmt.flags & DDPF_FOURCC) + { + /* fourcc is dXt* or rXgb */ + if (hdr.pixelfmt.fourcc[1] == 'X') + hdr.pixelfmt.flags |= DDPF_ALPHAPIXELS; + } + + d.gimp_bps = 1; /* Most formats will be converted to 1 byte per sample */ + if (hdr.pixelfmt.flags & DDPF_FOURCC) + { + switch (GETL32(hdr.pixelfmt.fourcc)) + { + case FOURCC ('A','T','I','1'): + case FOURCC ('B','C','4','U'): + case FOURCC ('B','C','4','S'): + d.bpp = d.gimp_bpp = 1; + type = GIMP_GRAY; + break; + case FOURCC ('A','T','I','2'): + case FOURCC ('B','C','5','U'): + case FOURCC ('B','C','5','S'): + d.bpp = d.gimp_bpp = 3; + type = GIMP_RGB; + break; + default: + d.bpp = d.gimp_bpp = 4; + type = GIMP_RGB; + break; + } + } + else + { + d.bpp = hdr.pixelfmt.bpp >> 3; + + if (d.bpp == 2) + { + if (hdr.pixelfmt.amask == 0xf000) // RGBA4 + { + d.gimp_bpp = 4; + type = GIMP_RGB; + } + else if (hdr.pixelfmt.amask == 0xff00) //L8A8 + { + d.gimp_bpp = 2; + type = GIMP_GRAY; + } + else if (hdr.pixelfmt.bmask == 0x1f) //R5G6B5 or RGB5A1 + { + if (hdr.pixelfmt.amask == 0x8000) // RGB5A1 + d.gimp_bpp = 4; + else + d.gimp_bpp = 3; + + type = GIMP_RGB; + } + else if (hdr.pixelfmt.rmask == 0xffff) /* L16 */ + { + d.gimp_bpp = 2; + d.gimp_bps = 2; + type = GIMP_GRAY; + } + else + { + g_message ("Unsupported uncompressed dds format: " + "bpp: %d, Rmask: %x, Gmask: %x, Bmask: %x, Amask: %x", + hdr.pixelfmt.bpp, + hdr.pixelfmt.rmask, hdr.pixelfmt.gmask, + hdr.pixelfmt.bmask, hdr.pixelfmt.amask); + return GIMP_PDB_EXECUTION_ERROR; + } + } + else + { + if (hdr.pixelfmt.flags & DDPF_PALETTEINDEXED8) + { + type = GIMP_INDEXED; + d.gimp_bpp = 1; + } + else if (hdr.pixelfmt.rmask == 0xe0) // R3G3B2 + { + type = GIMP_RGB; + d.gimp_bpp = 3; + } + else + { + /* test alpha only image */ + if (d.bpp == 1 && (hdr.pixelfmt.flags & DDPF_ALPHA)) + { + d.gimp_bpp = 2; + type = GIMP_GRAY; + } + else + { + d.gimp_bpp = d.bpp; + type = (d.bpp == 1) ? GIMP_GRAY : GIMP_RGB; + } + } + } + } + + if (d.gimp_bps == 2) + { + precision = GIMP_PRECISION_U16_GAMMA; + } + else + { + precision = GIMP_PRECISION_U8_GAMMA; + } + + /* verify header information is accurate */ + if (d.bpp < 1 || + (hdr.pitch_or_linsize > (file_size - sizeof (hdr)))) + { + fclose (fp); + g_message ("Invalid or corrupted DDS header\n"); + return GIMP_PDB_EXECUTION_ERROR; + } + + image = gimp_image_new_with_precision (hdr.width, hdr.height, type, precision); + + if (image == -1) + { + g_message ("Can't allocate new image.\n"); + fclose (fp); + return GIMP_PDB_EXECUTION_ERROR; + } + + gimp_image_set_filename (image, filename); + + if (hdr.pixelfmt.flags & DDPF_PALETTEINDEXED8) + { + d.palette = g_malloc (256 * 4); + if (fread (d.palette, 1, 1024, fp) != 1024) + { + g_message ("Error reading palette.\n"); + fclose (fp); + gimp_image_delete (image); + return GIMP_PDB_EXECUTION_ERROR; + } + for (i = j = 0; i < 768; i += 3, j += 4) + { + d.palette[i + 0] = d.palette[j + 0]; + d.palette[i + 1] = d.palette[j + 1]; + d.palette[i + 2] = d.palette[j + 2]; + } + gimp_image_set_colormap (image, d.palette, 256); + } + + d.tile_height = gimp_tile_height (); + + pixels = g_new (guchar, d.tile_height * hdr.width * d.gimp_bpp); + buf = g_malloc (hdr.pitch_or_linsize); + + d.rshift = color_shift (hdr.pixelfmt.rmask); + d.gshift = color_shift (hdr.pixelfmt.gmask); + d.bshift = color_shift (hdr.pixelfmt.bmask); + d.ashift = color_shift (hdr.pixelfmt.amask); + d.rbits = color_bits (hdr.pixelfmt.rmask); + d.gbits = color_bits (hdr.pixelfmt.gmask); + d.bbits = color_bits (hdr.pixelfmt.bmask); + d.abits = color_bits (hdr.pixelfmt.amask); + d.rmask = (hdr.pixelfmt.rmask >> d.rshift) << (8 - d.rbits); + d.gmask = (hdr.pixelfmt.gmask >> d.gshift) << (8 - d.gbits); + d.bmask = (hdr.pixelfmt.bmask >> d.bshift) << (8 - d.bbits); + d.amask = (hdr.pixelfmt.amask >> d.ashift) << (8 - d.abits); + + if (!(hdr.caps.caps2 & DDSCAPS2_CUBEMAP) && + !(hdr.caps.caps2 & DDSCAPS2_VOLUME) && + dx10hdr.arraySize == 0) + { + if (!load_layer (fp, &hdr, &d, image, 0, "", &l, pixels, buf)) + { + fclose (fp); + gimp_image_delete (image); + return GIMP_PDB_EXECUTION_ERROR; + } + if (!load_mipmaps (fp, &hdr, &d, image, "", &l, pixels, buf)) + { + fclose (fp); + gimp_image_delete (image); + return GIMP_PDB_EXECUTION_ERROR; + } + } + else if (hdr.caps.caps2 & DDSCAPS2_CUBEMAP) + { + if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEX) && + !load_face (fp, &hdr, &d, image, "(positive x)", &l, pixels, buf)) + { + fclose (fp); + gimp_image_delete (image); + return GIMP_PDB_EXECUTION_ERROR; + } + if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX) && + !load_face (fp, &hdr, &d, image, "(negative x)", &l, pixels, buf)) + { + fclose (fp); + gimp_image_delete (image); + return GIMP_PDB_EXECUTION_ERROR; + } + if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEY) && + !load_face (fp, &hdr, &d, image, "(positive y)", &l, pixels, buf)) + { + fclose (fp); + gimp_image_delete (image); + return GIMP_PDB_EXECUTION_ERROR; + } + if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY) && + !load_face (fp, &hdr, &d, image, "(negative y)", &l, pixels, buf)) + { + fclose (fp); + gimp_image_delete (image); + return GIMP_PDB_EXECUTION_ERROR; + } + if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ) && + !load_face (fp, &hdr, &d, image, "(positive z)", &l, pixels, buf)) + { + fclose (fp); + gimp_image_delete (image); + return GIMP_PDB_EXECUTION_ERROR; + } + if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ) && + !load_face (fp, &hdr, &d, image, "(negative z)", &l, pixels, buf)) + { + fclose (fp); + gimp_image_delete (image); + return GIMP_PDB_EXECUTION_ERROR; + } + } + else if ((hdr.caps.caps2 & DDSCAPS2_VOLUME) && + (hdr.flags & DDSD_DEPTH)) + { + unsigned int i, level; + char *plane; + for (i = 0; i < hdr.depth; ++i) + { + plane = g_strdup_printf ("(z = %d)", i); + if (!load_layer (fp, &hdr, &d, image, 0, plane, &l, pixels, buf)) + { + g_free (plane); + fclose (fp); + gimp_image_delete (image); + return GIMP_PDB_EXECUTION_ERROR; + } + g_free (plane); + } + + if ((hdr.flags & DDSD_MIPMAPCOUNT) && + (hdr.caps.caps1 & DDSCAPS_MIPMAP) && + (dds_read_vals.mipmaps != 0)) + { + for (level = 1; level < hdr.num_mipmaps; ++level) + { + int n = hdr.depth >> level; + if (n < 1) n = 1; + for (i = 0; i < n; ++i) + { + plane = g_strdup_printf ("(z = %d)", i); + if (!load_layer (fp, &hdr, &d, image, level, plane, &l, pixels, buf)) + { + g_free (plane); + fclose (fp); + gimp_image_delete (image); + return GIMP_PDB_EXECUTION_ERROR; + } + g_free (plane); + } + } + } + } + else if (dx10hdr.arraySize > 0) + { + unsigned int i; + char *elem; + + for (i = 0; i < dx10hdr.arraySize; ++i) + { + elem = g_strdup_printf ("(array element %d)", i); + if (!load_layer (fp, &hdr, &d, image, 0, elem, &l, pixels, buf)) + { + fclose (fp); + gimp_image_delete (image); + return GIMP_PDB_EXECUTION_ERROR; + } + if (!load_mipmaps (fp, &hdr, &d, image, elem, &l, pixels, buf)) + { + fclose (fp); + gimp_image_delete (image); + return GIMP_PDB_EXECUTION_ERROR; + } + g_free (elem); + } + } + + gimp_progress_update (1.0); + + if (hdr.pixelfmt.flags & DDPF_PALETTEINDEXED8) + g_free (d.palette); + + g_free (buf); + g_free (pixels); + fclose (fp); + + layers = gimp_image_get_layers (image, &layer_count); + + if (layers == NULL || layer_count == 0) + { + g_message ("Oops! NULL image read! Please report this!"); + return GIMP_PDB_EXECUTION_ERROR; + } + + gimp_image_set_active_layer (image, layers[0]); + g_free (layers); + + *imageID = image; + + return GIMP_PDB_SUCCESS; +} + +static int +read_header (dds_header_t *hdr, + FILE *fp) +{ + unsigned char buf[DDS_HEADERSIZE]; + + memset (hdr, 0, sizeof (dds_header_t)); + + if (fread (buf, 1, DDS_HEADERSIZE, fp) != DDS_HEADERSIZE) + return 0; + + hdr->magic = GETL32(buf); + + hdr->size = GETL32(buf + 4); + hdr->flags = GETL32(buf + 8); + hdr->height = GETL32(buf + 12); + hdr->width = GETL32(buf + 16); + hdr->pitch_or_linsize = GETL32(buf + 20); + hdr->depth = GETL32(buf + 24); + hdr->num_mipmaps = GETL32(buf + 28); + + hdr->pixelfmt.size = GETL32(buf + 76); + hdr->pixelfmt.flags = GETL32(buf + 80); + hdr->pixelfmt.fourcc[0] = buf[84]; + hdr->pixelfmt.fourcc[1] = buf[85]; + hdr->pixelfmt.fourcc[2] = buf[86]; + hdr->pixelfmt.fourcc[3] = buf[87]; + hdr->pixelfmt.bpp = GETL32(buf + 88); + hdr->pixelfmt.rmask = GETL32(buf + 92); + hdr->pixelfmt.gmask = GETL32(buf + 96); + hdr->pixelfmt.bmask = GETL32(buf + 100); + hdr->pixelfmt.amask = GETL32(buf + 104); + + hdr->caps.caps1 = GETL32(buf + 108); + hdr->caps.caps2 = GETL32(buf + 112); + + /* GIMP-DDS special info */ + if (GETL32(buf + 32) == FOURCC ('G','I','M','P') && + GETL32(buf + 36) == FOURCC ('-','D','D','S')) + { + hdr->reserved.gimp_dds_special.magic1 = GETL32(buf + 32); + hdr->reserved.gimp_dds_special.magic2 = GETL32(buf + 36); + hdr->reserved.gimp_dds_special.version = GETL32(buf + 40); + hdr->reserved.gimp_dds_special.extra_fourcc = GETL32(buf + 44); + } + + return 1; +} + +static int +read_header_dx10 (dds_header_dx10_t *hdr, + FILE *fp) +{ + char buf[DDS_HEADERSIZE_DX10]; + + memset (hdr, 0, sizeof (dds_header_dx10_t)); + + if (fread (buf, 1, DDS_HEADERSIZE_DX10, fp) != DDS_HEADERSIZE_DX10) + return 0; + + hdr->dxgiFormat = GETL32(buf); + hdr->resourceDimension = GETL32(buf + 4); + hdr->miscFlag = GETL32(buf + 8); + hdr->arraySize = GETL32(buf + 12); + hdr->reserved = GETL32(buf + 16); + + return 1; +} + +static int +validate_header (dds_header_t *hdr) +{ + unsigned int fourcc; + + if (hdr->magic != FOURCC ('D','D','S',' ')) + { + g_message ("Invalid DDS file.\n"); + return 0; + } + + if (hdr->pixelfmt.flags & DDPF_FOURCC) + { + if (hdr->flags & DDSD_PITCH) + { + g_message ("Warning: DDSD_PITCH is incorrectly set for DDPF_FOURCC!"); + hdr->flags &= DDSD_PITCH; + } + if (! (hdr->flags & DDSD_LINEARSIZE)) + { + g_message ("Warning: DDSD_LINEARSIZE is incorrectly not set for DDPF_FOURCC!"); + hdr->flags |= DDSD_LINEARSIZE; + } + } + else + { + if (! (hdr->flags & DDSD_PITCH)) + { + g_printerr ("Warning: DDSD_PITCH is incorrectly not set for an uncompressed texture! (recovered)\n"); + hdr->flags |= DDSD_PITCH; + } + if ((hdr->flags & DDSD_LINEARSIZE)) + { + g_printerr ("Warning: DDSD_LINEARSIZE is incorrectly set for an uncompressed texture! (recovered)\n"); + hdr->flags &= DDSD_LINEARSIZE; + } + } + + /* + if ((hdr->pixelfmt.flags & DDPF_FOURCC) == + (hdr->pixelfmt.flags & DDPF_RGB)) + { + g_message ("Invalid pixel format.\n"); + return 0; + } + */ + fourcc = GETL32(hdr->pixelfmt.fourcc); + + if ((hdr->pixelfmt.flags & DDPF_FOURCC) && + fourcc != FOURCC ('D','X','T','1') && + fourcc != FOURCC ('D','X','T','2') && + fourcc != FOURCC ('D','X','T','3') && + fourcc != FOURCC ('D','X','T','4') && + fourcc != FOURCC ('D','X','T','5') && + fourcc != FOURCC ('R','X','G','B') && + fourcc != FOURCC ('A','T','I','1') && + fourcc != FOURCC ('B','C','4','U') && + fourcc != FOURCC ('B','C','4','S') && + fourcc != FOURCC ('A','T','I','2') && + fourcc != FOURCC ('B','C','5','U') && + fourcc != FOURCC ('B','C','5','S') && + fourcc != FOURCC ('D','X','1','0')) + { + g_message ("Unsupported format (FOURCC: %c%c%c%c, hex: %08x).\n", + hdr->pixelfmt.fourcc[0], + hdr->pixelfmt.fourcc[1], + hdr->pixelfmt.fourcc[2], + hdr->pixelfmt.fourcc[3], + GETL32(hdr->pixelfmt.fourcc)); + return 0; + } + + if (hdr->pixelfmt.flags & DDPF_RGB) + { + if ((hdr->pixelfmt.bpp != 8) && + (hdr->pixelfmt.bpp != 16) && + (hdr->pixelfmt.bpp != 24) && + (hdr->pixelfmt.bpp != 32)) + { + g_message ("Invalid BPP.\n"); + return 0; + } + } + else if (hdr->pixelfmt.flags & DDPF_LUMINANCE) + { + if ((hdr->pixelfmt.bpp != 8) && + (hdr->pixelfmt.bpp != 16)) + { + g_message ("Invalid BPP.\n"); + return 0; + } + + hdr->pixelfmt.flags |= DDPF_RGB; + } + else if (hdr->pixelfmt.flags & DDPF_PALETTEINDEXED8) + { + hdr->pixelfmt.flags |= DDPF_RGB; + } + + if (!(hdr->pixelfmt.flags & DDPF_RGB) && + !(hdr->pixelfmt.flags & DDPF_ALPHA) && + !(hdr->pixelfmt.flags & DDPF_FOURCC) && + !(hdr->pixelfmt.flags & DDPF_LUMINANCE)) + { + g_message ("Unknown pixel format! Taking a guess, expect trouble!"); + switch (fourcc) + { + case FOURCC ('D','X','T','1'): + case FOURCC ('D','X','T','2'): + case FOURCC ('D','X','T','3'): + case FOURCC ('D','X','T','4'): + case FOURCC ('D','X','T','5'): + case FOURCC ('R','X','G','B'): + case FOURCC ('A','T','I','1'): + case FOURCC ('B','C','4','U'): + case FOURCC ('B','C','4','S'): + case FOURCC ('A','T','I','2'): + case FOURCC ('B','C','5','U'): + case FOURCC ('B','C','5','S'): + hdr->pixelfmt.flags |= DDPF_FOURCC; + break; + default: + switch (hdr->pixelfmt.bpp) + { + case 8: + if (hdr->pixelfmt.flags & DDPF_ALPHAPIXELS) + hdr->pixelfmt.flags |= DDPF_ALPHA; + else + hdr->pixelfmt.flags |= DDPF_LUMINANCE; + break; + case 16: + case 24: + case 32: + hdr->pixelfmt.flags |= DDPF_RGB; + break; + default: + g_message ("Invalid pixel format."); + return 0; + } + break; + } + } + + return 1; +} + +/* + * This function will set the necessary flags and attributes in the standard + * dds header using the information found in the DX10 header. + */ +static int +setup_dxgi_format (dds_header_t *hdr, + dds_header_dx10_t *dx10hdr) +{ + if ((dx10hdr->resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE2D) && + (dx10hdr->miscFlag & D3D10_RESOURCE_MISC_TEXTURECUBE)) + { + hdr->caps.caps2 |= DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALL_FACES; + } + else if (dx10hdr->resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE3D) + { + hdr->flags |= DDSD_DEPTH; + hdr->caps.caps2 |= DDSCAPS2_VOLUME; + } + + if ((dx10hdr->resourceDimension != D3D10_RESOURCE_DIMENSION_TEXTURE1D) && + (dx10hdr->resourceDimension != D3D10_RESOURCE_DIMENSION_TEXTURE2D) && + (dx10hdr->resourceDimension != D3D10_RESOURCE_DIMENSION_TEXTURE3D)) + return 0; + + // check for a compressed DXGI format + if ((dx10hdr->dxgiFormat >= DXGI_FORMAT_BC1_TYPELESS) && + (dx10hdr->dxgiFormat <= DXGI_FORMAT_BC5_SNORM)) + { + // set flag and replace FOURCC + hdr->pixelfmt.flags |= DDPF_FOURCC; + + switch (dx10hdr->dxgiFormat) + { + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + PUTL32(hdr->pixelfmt.fourcc, FOURCC ('D','X','T','1')); + break; + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + PUTL32(hdr->pixelfmt.fourcc, FOURCC ('D','X','T','3')); + break; + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + PUTL32(hdr->pixelfmt.fourcc, FOURCC ('D','X','T','5')); + break; + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + PUTL32(hdr->pixelfmt.fourcc, FOURCC ('A','T','I','1')); + break; + case DXGI_FORMAT_BC4_SNORM: + PUTL32(hdr->pixelfmt.fourcc, FOURCC ('B','C','4','S')); + break; + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + PUTL32(hdr->pixelfmt.fourcc, FOURCC ('A','T','I','2')); + break; + case DXGI_FORMAT_BC5_SNORM: + PUTL32(hdr->pixelfmt.fourcc, FOURCC ('B','C','5','S')); + break; + default: + break; + } + } + else + { + /* unset the FOURCC flag */ + hdr->pixelfmt.flags &= ~DDPF_FOURCC; + + switch (dx10hdr->dxgiFormat) + { + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + hdr->pixelfmt.bpp = 32; + hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS; + hdr->pixelfmt.rmask = 0x00ff0000; + hdr->pixelfmt.gmask = 0x0000ff00; + hdr->pixelfmt.bmask = 0x000000ff; + hdr->pixelfmt.amask = 0xff000000; + break; + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + hdr->pixelfmt.bpp = 32; + hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS; + hdr->pixelfmt.rmask = 0x00ff0000; + hdr->pixelfmt.gmask = 0x0000ff00; + hdr->pixelfmt.bmask = 0x000000ff; + hdr->pixelfmt.amask = 0x00000000; + break; + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + hdr->pixelfmt.bpp = 32; + hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS; + hdr->pixelfmt.rmask = 0x000000ff; + hdr->pixelfmt.gmask = 0x0000ff00; + hdr->pixelfmt.bmask = 0x00ff0000; + hdr->pixelfmt.amask = 0xff000000; + break; + case DXGI_FORMAT_B5G6R5_UNORM: + hdr->pixelfmt.bpp = 16; + hdr->pixelfmt.rmask = 0x0000f800; + hdr->pixelfmt.gmask = 0x000007e0; + hdr->pixelfmt.bmask = 0x0000001f; + hdr->pixelfmt.amask = 0x00000000; + break; + case DXGI_FORMAT_B5G5R5A1_UNORM: + hdr->pixelfmt.bpp = 16; + hdr->pixelfmt.rmask = 0x00007c00; + hdr->pixelfmt.gmask = 0x000003e0; + hdr->pixelfmt.bmask = 0x0000001f; + hdr->pixelfmt.amask = 0x00008000; + break; + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + hdr->pixelfmt.bpp = 32; + hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS; + hdr->pixelfmt.rmask = 0x000003ff; + hdr->pixelfmt.gmask = 0x000ffc00; + hdr->pixelfmt.bmask = 0x3ff00000; + hdr->pixelfmt.amask = 0xc0000000; + break; + case DXGI_FORMAT_A8_UNORM: + hdr->pixelfmt.bpp = 8; + hdr->pixelfmt.flags |= DDPF_ALPHA | DDPF_ALPHAPIXELS; + hdr->pixelfmt.rmask = hdr->pixelfmt.gmask = hdr->pixelfmt.bmask = 0; + hdr->pixelfmt.amask = 0x000000ff; + break; + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_R8_SINT: + hdr->pixelfmt.bpp = 8; + hdr->pixelfmt.rmask = 0x000000ff; + hdr->pixelfmt.gmask = hdr->pixelfmt.bmask = hdr->pixelfmt.amask = 0; + break; + case DXGI_FORMAT_B4G4R4A4_UNORM: + hdr->pixelfmt.bpp = 16; + hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS; + hdr->pixelfmt.rmask = 0x00000f00; + hdr->pixelfmt.gmask = 0x000000f0; + hdr->pixelfmt.bmask = 0x0000000f; + hdr->pixelfmt.amask = 0x0000f000; + break; + case DXGI_FORMAT_UNKNOWN: + g_message ("Unknown DXGI format. Expect problems..."); + break; + default: /* unsupported DXGI format */ + g_message ("Unsupported DXGI format (%d)", dx10hdr->dxgiFormat); + return 0; + } + } + + return 1; +} + + +static const Babl* +premultiplied_variant (const Babl* format) +{ + if (format == babl_format ("R'G'B'A u8")) + return babl_format ("R'aG'aB'aA u8"); + else + g_printerr ("Add format %s to premultiplied_variant () %s: %d\n", babl_get_name (format), __FILE__, __LINE__); + return format; +} + +static int +load_layer (FILE *fp, + dds_header_t *hdr, + dds_load_info_t *d, + gint32 image, + unsigned int level, + char *prefix, + unsigned int *l, + guchar *pixels, + unsigned char *buf) +{ + GeglBuffer *buffer; + const Babl *bablfmt = NULL; + GimpImageType type = GIMP_RGBA_IMAGE; + gchar *layer_name; + gint x, y, z, n; + gint32 layer; + unsigned int width = hdr->width >> level; + unsigned int height = hdr->height >> level; + unsigned int size = hdr->pitch_or_linsize >> (2 * level); + unsigned int layerw; + int format = DDS_COMPRESS_NONE; + gsize file_size; + gsize current_position; + + current_position = ftell (fp); + fseek (fp, 0L, SEEK_END); + file_size = ftell (fp); + fseek (fp, 0, SEEK_SET); + fseek (fp, current_position, SEEK_SET); + + if (width < 1) width = 1; + if (height < 1) height = 1; + + switch (d->bpp) + { + case 1: + if (hdr->pixelfmt.flags & DDPF_PALETTEINDEXED8) + { + type = GIMP_INDEXED_IMAGE; + } + else if (hdr->pixelfmt.rmask == 0xe0) + { + type = GIMP_RGB_IMAGE; + bablfmt = babl_format ("R'G'B' u8"); + } + else if (hdr->pixelfmt.flags & DDPF_ALPHA) + { + type = GIMP_GRAYA_IMAGE; + bablfmt = babl_format ("Y'A u8"); + } + else + { + type = GIMP_GRAY_IMAGE; + bablfmt = babl_format ("Y' u8"); + } + break; + case 2: + if ((hdr->pixelfmt.flags & (DDPF_PALETTEINDEXED8 + DDPF_ALPHA)) == + DDPF_PALETTEINDEXED8 + DDPF_ALPHA) + { + type = GIMP_INDEXEDA_IMAGE; + } + else if (hdr->pixelfmt.amask == 0xf000) /* RGBA4 */ + { + type = GIMP_RGBA_IMAGE; + bablfmt = babl_format ("R'G'B'A u8"); + } + else if (hdr->pixelfmt.amask == 0xff00) /* L8A8 */ + { + type = GIMP_GRAYA_IMAGE; + bablfmt = babl_format ("Y'A u8"); + } + else if (hdr->pixelfmt.bmask == 0x1f) /* R5G6B5 or RGB5A1 */ + { + type = (hdr->pixelfmt.amask == 0x8000) ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE; + bablfmt = (hdr->pixelfmt.amask == 0x8000) ? babl_format ("R'G'B'A u8") : babl_format ("R'G'B' u8"); + } + else if (hdr->pixelfmt.rmask == 0xffff) /* L16 */ + { + type = GIMP_GRAY_IMAGE; + bablfmt = babl_format ("Y' u16"); + } + break; + case 3: type = GIMP_RGB_IMAGE; bablfmt = babl_format ("R'G'B' u8"); break; + case 4: type = GIMP_RGBA_IMAGE; bablfmt = babl_format ("R'G'B'A u8"); break; + } + + layer_name = (level) ? g_strdup_printf ("mipmap %d %s", level, prefix) : + g_strdup_printf ("main surface %s", prefix); + + layer = gimp_layer_new (image, layer_name, width, height, type, 100, + GIMP_LAYER_MODE_NORMAL); + g_free (layer_name); + + gimp_image_insert_layer (image, layer, 0, *l); + + if (type == GIMP_INDEXED_IMAGE || type == GIMP_INDEXEDA_IMAGE) + bablfmt = gimp_drawable_get_format (layer); + + if ((*l)++) gimp_item_set_visible (layer, FALSE); + + buffer = gimp_drawable_get_buffer (layer); + + layerw = gegl_buffer_get_width (buffer); + + if (hdr->pixelfmt.flags & DDPF_FOURCC) + { + unsigned int w = (width + 3) >> 2; + unsigned int h = (height + 3) >> 2; + + switch (GETL32(hdr->pixelfmt.fourcc)) + { + case FOURCC ('D','X','T','1'): format = DDS_COMPRESS_BC1; break; + case FOURCC ('D','X','T','2'): bablfmt = premultiplied_variant (bablfmt); + case FOURCC ('D','X','T','3'): format = DDS_COMPRESS_BC2; break; + case FOURCC ('D','X','T','4'): bablfmt = premultiplied_variant (bablfmt); + case FOURCC ('D','X','T','5'): format = DDS_COMPRESS_BC3; break; + case FOURCC ('R','X','G','B'): format = DDS_COMPRESS_BC3; break; + case FOURCC ('A','T','I','1'): + case FOURCC ('B','C','4','U'): + case FOURCC ('B','C','4','S'): format = DDS_COMPRESS_BC4; break; + case FOURCC ('A','T','I','2'): + case FOURCC ('B','C','5','U'): + case FOURCC ('B','C','5','S'): format = DDS_COMPRESS_BC5; break; + } + + size = w * h; + if ((format == DDS_COMPRESS_BC1) || (format == DDS_COMPRESS_BC4)) + size *= 8; + else + size *= 16; + } + + if (size > (file_size - current_position) || + size > hdr->pitch_or_linsize) + { + g_message ("Requested data exceeds size of file.\n"); + return 0; + } + + if ((hdr->flags & DDSD_LINEARSIZE) && + !fread (buf, size, 1, fp)) + { + g_message ("Unexpected EOF.\n"); + return 0; + } + + if ((hdr->pixelfmt.flags & DDPF_RGB) || + (hdr->pixelfmt.flags & DDPF_ALPHA)) + { + guint ired = 0; + guint iblue = 2; + + if (hdr->reserved.gimp_dds_special.magic1 == FOURCC ('G','I','M','P') && + hdr->reserved.gimp_dds_special.version <= 199003 && + hdr->reserved.gimp_dds_special.version > 0 && + d->bpp >= 3 && hdr->pixelfmt.amask == 0xc0000000) + { + /* GIMP dds plug-in versions before or equal to 199003 (3.9.91) wrote + * the red and green channels reversed for RGB10A2. We will fix that here. + */ + g_printerr ("Switching incorrect red and green channels in RGB10A2 dds " + "written by an older version of GIMP's dds plug-in.\n"); + ired = 2; + iblue = 0; + } + + z = 0; + for (y = 0, n = 0; y < height; ++y, ++n) + { + if (n >= d->tile_height) + { + gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0, + bablfmt, pixels, GEGL_AUTO_ROWSTRIDE); + n = 0; + gimp_progress_update ((double)y / (double)hdr->height); + } + + current_position = ftell (fp); + if ((hdr->flags & DDSD_PITCH) && + ((width * d->bpp) > (file_size - current_position) || + (width * d->bpp) > hdr->pitch_or_linsize)) + { + g_message ("Requested data exceeds size of file.\n"); + return 0; + } + + if ((hdr->flags & DDSD_PITCH) && + !fread (buf, width * d->bpp, 1, fp)) + { + g_message ("Unexpected EOF.\n"); + return 0; + } + + if (!(hdr->flags & DDSD_LINEARSIZE)) z = 0; + + for (x = 0; x < layerw; ++x) + { + unsigned int pixel = buf[z]; + unsigned int pos = (n * layerw + x) * d->gimp_bpp; + + if (d->bpp > 1) pixel += ((unsigned int)buf[z + 1] << 8); + if (d->bpp > 2) pixel += ((unsigned int)buf[z + 2] << 16); + if (d->bpp > 3) pixel += ((unsigned int)buf[z + 3] << 24); + + if (d->bpp >= 3) + { + if (hdr->pixelfmt.amask == 0xc0000000) // handle RGB10A2 + { + pixels[pos + ired] = (pixel >> d->rshift) >> 2; + pixels[pos + 1] = (pixel >> d->gshift) >> 2; + pixels[pos + iblue] = (pixel >> d->bshift) >> 2; + if (hdr->pixelfmt.flags & DDPF_ALPHAPIXELS) + pixels[pos + 3] = (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask; + } + else + { + pixels[pos] = + (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask; + pixels[pos + 1] = + (pixel >> d->gshift << (8 - d->gbits) & d->gmask) * 255 / d->gmask; + pixels[pos + 2] = + (pixel >> d->bshift << (8 - d->bbits) & d->bmask) * 255 / d->bmask; + if (hdr->pixelfmt.flags & DDPF_ALPHAPIXELS) + { + pixels[pos + 3] = + (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask; + } + } + } + else if (d->bpp == 2) + { + if (hdr->pixelfmt.amask == 0xf000) //RGBA4 + { + pixels[pos] = + (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask; + pixels[pos + 1] = + (pixel >> d->gshift << (8 - d->gbits) & d->gmask) * 255 / d->gmask; + pixels[pos + 2] = + (pixel >> d->bshift << (8 - d->bbits) & d->bmask) * 255 / d->bmask; + pixels[pos + 3] = + (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask; + } + else if (hdr->pixelfmt.amask == 0xff00) //L8A8 + { + pixels[pos] = + (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask; + pixels[pos + 1] = + (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask; + } + else if (hdr->pixelfmt.bmask == 0x1f) //R5G6B5 or RGB5A1 + { + pixels[pos] = + (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask; + pixels[pos + 1] = + (pixel >> d->gshift << (8 - d->gbits) & d->gmask) * 255 / d->gmask; + pixels[pos + 2] = + (pixel >> d->bshift << (8 - d->bbits) & d->bmask) * 255 / d->bmask; + if (hdr->pixelfmt.amask == 0x8000) + { + pixels[pos + 3] = + (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask; + } + } + else if (hdr->pixelfmt.rmask == 0xffff) /* L16 */ + { + guint16 *pixels16 = (guint16 *) &pixels[pos]; + + *pixels16 = (guint16) (pixel & 0xffff); + } + } + else + { + if (hdr->pixelfmt.flags & DDPF_PALETTEINDEXED8) + { + pixels[pos] = pixel & 0xff; + } + else if (hdr->pixelfmt.rmask == 0xe0) // R3G3B2 + { + pixels[pos] = + (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask; + pixels[pos + 1] = + (pixel >> d->gshift << (8 - d->gbits) & d->gmask) * 255 / d->gmask; + pixels[pos + 2] = + (pixel >> d->bshift << (8 - d->bbits) & d->bmask) * 255 / d->bmask; + } + else if (hdr->pixelfmt.flags & DDPF_ALPHA) + { + pixels[pos + 0] = 255; + pixels[pos + 1] = pixel & 0xff; + } + else // LUMINANCE + { + pixels[pos] = pixel & 0xff; + } + } + + z += d->bpp; + } + } + + gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0, + bablfmt, pixels, GEGL_AUTO_ROWSTRIDE); + } + else if (hdr->pixelfmt.flags & DDPF_FOURCC) + { + unsigned char *dst; + + dst = g_malloc (width * height * d->gimp_bpp); + memset (dst, 0, width * height * d->gimp_bpp); + + if (d->gimp_bpp == 4) + { + for (y = 0; y < height; ++y) + for (x = 0; x < width; ++x) + dst[y * (width * 4) + (x * 4) + 3] = 255; + } + + dxt_decompress (dst, buf, format, size, width, height, d->gimp_bpp, + hdr->pixelfmt.flags & DDPF_NORMAL); + + if (format == DDS_COMPRESS_BC5 && + hdr->reserved.gimp_dds_special.magic1 == FOURCC ('G','I','M','P') && + hdr->reserved.gimp_dds_special.version > 0 && + hdr->reserved.gimp_dds_special.version <= 199002) + { + /* GIMP dds plug-in versions before 199002 == 3.9.90 wrote + * the red and green channels reversed. We will fix that here. + */ + g_printerr ("Switching incorrect red and green channels in BC5 dds " + "written by an older version of GIMP's dds plug-in.\n"); + + for (y = 0; y < height; ++y) + for (x = 0; x < width; ++x) + { + guchar tmpG; + guint pix_width = width * d->gimp_bpp; + guint x_width = x * d->gimp_bpp; + + tmpG = dst[y * pix_width + x_width]; + dst[y * pix_width + x_width] = dst[y * pix_width + x_width + 1]; + dst[y * pix_width + x_width + 1] = tmpG; + } + } + + z = 0; + for (y = 0, n = 0; y < height; ++y, ++n) + { + if (n >= d->tile_height) + { + gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0, + bablfmt, pixels, GEGL_AUTO_ROWSTRIDE); + n = 0; + gimp_progress_update ((double)y / (double)hdr->height); + } + + memcpy (pixels + n * layerw * d->gimp_bpp, + dst + y * layerw * d->gimp_bpp, + width * d->gimp_bpp); + } + + gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0, + bablfmt, pixels, GEGL_AUTO_ROWSTRIDE); + + g_free (dst); + } + + gegl_buffer_flush (buffer); + + g_object_unref (buffer); + + /* gimp dds specific. decode encoded images */ + if (dds_read_vals.decode_images && + hdr->reserved.gimp_dds_special.magic1 == FOURCC ('G','I','M','P') && + hdr->reserved.gimp_dds_special.magic2 == FOURCC ('-','D','D','S')) + { + switch (hdr->reserved.gimp_dds_special.extra_fourcc) + { + case FOURCC ('A','E','X','P'): + decode_alpha_exp_image (layer, FALSE); + break; + case FOURCC ('Y','C','G','1'): + decode_ycocg_image (layer, FALSE); + break; + case FOURCC ('Y','C','G','2'): + decode_ycocg_scaled_image (layer, FALSE); + break; + default: + break; + } + } + + return 1; +} + +static int +load_mipmaps (FILE *fp, + dds_header_t *hdr, + dds_load_info_t *d, + gint32 image, + char *prefix, + unsigned int *l, + guchar *pixels, + unsigned char *buf) +{ + unsigned int level; + + if ((hdr->flags & DDSD_MIPMAPCOUNT) && + (hdr->caps.caps1 & DDSCAPS_MIPMAP) && + (dds_read_vals.mipmaps != 0)) + { + for (level = 1; level < hdr->num_mipmaps; ++level) + { + if (!load_layer (fp, hdr, d, image, level, prefix, l, pixels, buf)) + return 0; + } + } + + return 1; +} + +static int +load_face (FILE *fp, + dds_header_t *hdr, + dds_load_info_t *d, + gint32 image, + char *prefix, + unsigned int *l, + guchar *pixels, + unsigned char *buf) +{ + if (!load_layer (fp, hdr, d, image, 0, prefix, l, pixels, buf)) + return 0; + + return load_mipmaps (fp, hdr, d, image, prefix, l, pixels, buf); +} + +static unsigned char +color_bits (unsigned int mask) +{ + unsigned char i = 0; + + while (mask) + { + if (mask & 1) ++i; + mask >>= 1; + } + + return i; +} + +static unsigned char +color_shift (unsigned int mask) +{ + guchar i = 0; + + if (! mask) + return 0; + + while (!((mask >> i) & 1)) + ++i; + + return i; +} + +static void +load_dialog_response (GtkWidget *widget, + gint response_id, + gpointer data) +{ + switch (response_id) + { + case GTK_RESPONSE_OK: + runme = TRUE; + default: + gtk_widget_destroy (widget); + break; + } +} + +static void +toggle_clicked (GtkWidget *widget, + gpointer data) +{ + int *flag = (int*) data; + (*flag) = !(*flag); +} + +static int +load_dialog (void) +{ + GtkWidget *dlg; + GtkWidget *vbox; + GtkWidget *check; + + dlg = gimp_dialog_new (_("Load DDS"), "dds", NULL, GTK_WIN_POS_MOUSE, + gimp_standard_help_func, LOAD_PROC, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_OK"), GTK_RESPONSE_OK, + NULL); + + g_signal_connect (dlg, "response", + G_CALLBACK (load_dialog_response), + 0); + g_signal_connect (dlg, "destroy", + G_CALLBACK (gtk_main_quit), + 0); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 8); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), + vbox, 1, 1, 0); + gtk_widget_show (vbox); + + check = gtk_check_button_new_with_mnemonic (_("_Load mipmaps")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), dds_read_vals.mipmaps); + g_signal_connect (check, "clicked", + G_CALLBACK (toggle_clicked), &dds_read_vals.mipmaps); + gtk_box_pack_start (GTK_BOX (vbox), check, 1, 1, 0); + gtk_widget_show (check); + + check = gtk_check_button_new_with_mnemonic (_("_Automatically decode YCoCg/AExp images when detected")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), dds_read_vals.decode_images); + g_signal_connect (check, "clicked", + G_CALLBACK (toggle_clicked), &dds_read_vals.decode_images); + gtk_box_pack_start (GTK_BOX (vbox), check, 1, 1, 0); + gtk_widget_show (check); + + gtk_widget_show (dlg); + + runme = FALSE; + + gtk_main (); + + return runme; +} diff --git a/plug-ins/file-dds/ddswrite.c b/plug-ins/file-dds/ddswrite.c new file mode 100644 index 0000000..5fb658a --- /dev/null +++ b/plug-ins/file-dds/ddswrite.c @@ -0,0 +1,2278 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include <gtk/gtk.h> +#include <glib/gstdio.h> + +#include <libgimp/gimp.h> +#include <libgimp/gimpui.h> + +#include <libgimp/stdplugins-intl.h> + +#include "ddsplugin.h" +#include "dds.h" +#include "dxt.h" +#include "mipmap.h" +#include "endian_rw.h" +#include "imath.h" +#include "color.h" + + +enum +{ + COMBO_VALUE, + COMBO_STRING, + COMBO_SENSITIVE +}; + + +static gint save_dialog (gint32 image_id, + gint32 drawable); +static void save_dialog_response (GtkWidget *widget, + gint response_id, + gpointer data); +static gboolean write_image (FILE *fp, + gint32 image_id, + gint32 drawable_id); + + +static gboolean runme = FALSE; + +static const char *cubemap_face_names[4][6] = +{ + { + "positive x", "negative x", + "positive y", "negative y", + "positive z", "negative z" + }, + { + "pos x", "neg x", + "pos y", "neg y", + "pos z", "neg z", + }, + { + "+x", "-x", + "+y", "-y", + "+z", "-z" + }, + { + "right", "left", + "top", "bottom", + "back", "front" + } +}; + +static gint cubemap_faces[6]; +static gboolean is_cubemap = FALSE; +static gboolean is_volume = FALSE; +static gboolean is_array = FALSE; +static gboolean is_mipmap_chain_valid = FALSE; + +static GtkWidget *compress_opt; +static GtkWidget *format_opt; +static GtkWidget *mipmap_opt; +static GtkWidget *mipmap_filter_opt; +static GtkWidget *mipmap_wrap_opt; +static GtkWidget *srgb_chk; +static GtkWidget *gamma_chk; +static GtkWidget *gamma_spin; +static GtkWidget *pm_chk; +static GtkWidget *alpha_coverage_chk; +static GtkWidget *alpha_test_threshold_spin; + +typedef struct string_value_s +{ + gint value; + gchar *string; +} string_value_t; + +static string_value_t compression_strings[] = +{ + { DDS_COMPRESS_NONE, "None" }, + { DDS_COMPRESS_BC1, "BC1 / DXT1" }, + { DDS_COMPRESS_BC2, "BC2 / DXT3" }, + { DDS_COMPRESS_BC3, "BC3 / DXT5" }, + { DDS_COMPRESS_BC3N, "BC3nm / DXT5nm" }, + { DDS_COMPRESS_BC4, "BC4 / ATI1 (3Dc+)" }, + { DDS_COMPRESS_BC5, "BC5 / ATI2 (3Dc)" }, + { DDS_COMPRESS_RXGB, "RXGB (DXT5)" }, + { DDS_COMPRESS_AEXP, "Alpha Exponent (DXT5)" }, + { DDS_COMPRESS_YCOCG, "YCoCg (DXT5)" }, + { DDS_COMPRESS_YCOCGS, "YCoCg scaled (DXT5)" }, + { -1, 0} +}; + +static string_value_t format_strings[] = +{ + { DDS_FORMAT_DEFAULT, "Default" }, + { DDS_FORMAT_RGB8, "RGB8" }, + { DDS_FORMAT_RGBA8, "RGBA8" }, + { DDS_FORMAT_BGR8, "BGR8" }, + { DDS_FORMAT_ABGR8, "ABGR8" }, + { DDS_FORMAT_R5G6B5, "R5G6B5" }, + { DDS_FORMAT_RGBA4, "RGBA4" }, + { DDS_FORMAT_RGB5A1, "RGB5A1" }, + { DDS_FORMAT_RGB10A2, "RGB10A2" }, + { DDS_FORMAT_R3G3B2, "R3G3B2" }, + { DDS_FORMAT_A8, "A8" }, + { DDS_FORMAT_L8, "L8" }, + { DDS_FORMAT_L8A8, "L8A8" }, + { DDS_FORMAT_AEXP, "AExp" }, + { DDS_FORMAT_YCOCG, "YCoCg" }, + { -1, 0} +}; + +static string_value_t mipmap_strings[] = +{ + { DDS_MIPMAP_NONE, "No mipmaps" }, + { DDS_MIPMAP_GENERATE, "Generate mipmaps" }, + { DDS_MIPMAP_EXISTING, "Use existing mipmaps" }, + { -1, 0} +}; + +static string_value_t mipmap_filter_strings[] = +{ + { DDS_MIPMAP_FILTER_DEFAULT, "Default" }, + { DDS_MIPMAP_FILTER_NEAREST, "Nearest" }, + { DDS_MIPMAP_FILTER_BOX, "Box" }, + { DDS_MIPMAP_FILTER_TRIANGLE, "Triangle" }, + { DDS_MIPMAP_FILTER_QUADRATIC, "Quadratic" }, + { DDS_MIPMAP_FILTER_BSPLINE, "B-Spline" }, + { DDS_MIPMAP_FILTER_MITCHELL, "Mitchell" }, + { DDS_MIPMAP_FILTER_LANCZOS, "Lanczos" }, + { DDS_MIPMAP_FILTER_KAISER, "Kaiser" }, + { -1, 0} +}; + +static string_value_t mipmap_wrap_strings[] = +{ + { DDS_MIPMAP_WRAP_DEFAULT, "Default" }, + { DDS_MIPMAP_WRAP_MIRROR, "Mirror" }, + { DDS_MIPMAP_WRAP_REPEAT, "Repeat" }, + { DDS_MIPMAP_WRAP_CLAMP, "Clamp" }, + { -1, 0} +}; + +static string_value_t save_type_strings[] = +{ + { DDS_SAVE_SELECTED_LAYER, "Selected layer" }, + { DDS_SAVE_CUBEMAP, "As cube map" }, + { DDS_SAVE_VOLUMEMAP, "As volume map" }, + { DDS_SAVE_ARRAY, "As texture array" }, + { DDS_SAVE_VISIBLE_LAYERS, "All visible layers" }, + { -1, 0} +}; + +static struct +{ + int format; + DXGI_FORMAT dxgi_format; + int bpp; + int alpha; + unsigned int rmask; + unsigned int gmask; + unsigned int bmask; + unsigned int amask; +} format_info[] = +{ + { DDS_FORMAT_RGB8, DXGI_FORMAT_UNKNOWN, 3, 0, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000}, + { DDS_FORMAT_RGBA8, DXGI_FORMAT_B8G8R8A8_UNORM, 4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000}, + { DDS_FORMAT_BGR8, DXGI_FORMAT_UNKNOWN, 3, 0, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000}, + { DDS_FORMAT_ABGR8, DXGI_FORMAT_R8G8B8A8_UNORM, 4, 1, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000}, + { DDS_FORMAT_R5G6B5, DXGI_FORMAT_B5G6R5_UNORM, 2, 0, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000}, + { DDS_FORMAT_RGBA4, DXGI_FORMAT_B4G4R4A4_UNORM, 2, 1, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000}, + { DDS_FORMAT_RGB5A1, DXGI_FORMAT_B5G5R5A1_UNORM, 2, 1, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000}, + { DDS_FORMAT_RGB10A2, DXGI_FORMAT_R10G10B10A2_UNORM, 4, 1, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000}, + { DDS_FORMAT_R3G3B2, DXGI_FORMAT_UNKNOWN, 1, 0, 0x000000e0, 0x0000001c, 0x00000003, 0x00000000}, + { DDS_FORMAT_A8, DXGI_FORMAT_A8_UNORM, 1, 0, 0x00000000, 0x00000000, 0x00000000, 0x000000ff}, + { DDS_FORMAT_L8, DXGI_FORMAT_R8_UNORM, 1, 0, 0x000000ff, 0x000000ff, 0x000000ff, 0x00000000}, + { DDS_FORMAT_L8A8, DXGI_FORMAT_UNKNOWN, 2, 1, 0x000000ff, 0x000000ff, 0x000000ff, 0x0000ff00}, + { DDS_FORMAT_AEXP, DXGI_FORMAT_B8G8R8A8_UNORM, 4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000}, + { DDS_FORMAT_YCOCG, DXGI_FORMAT_B8G8R8A8_UNORM, 4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000} +}; + + +static gboolean +check_mipmaps (gint32 image_id, + gint savetype) +{ + gint *layers; + gint num_layers; + gint i, j, w, h, mipw, miph; + gint num_mipmaps; + gint num_surfaces = 0; + gint min_surfaces = 1; + gint max_surfaces = 1; + gboolean valid = TRUE; + GimpImageType type; + + /* not handling volume maps for the moment... */ + if (savetype == DDS_SAVE_VOLUMEMAP) + return 0; + + if (savetype == DDS_SAVE_CUBEMAP) + { + min_surfaces = 6; + max_surfaces = 6; + } + else if (savetype == DDS_SAVE_ARRAY) + { + min_surfaces = 2; + max_surfaces = INT_MAX; + } + + layers = gimp_image_get_layers (image_id, &num_layers); + + w = gimp_image_width (image_id); + h = gimp_image_height (image_id); + + num_mipmaps = get_num_mipmaps (w, h); + + type = gimp_drawable_type (layers[0]); + + for (i = 0; i < num_layers; ++i) + { + if (type != gimp_drawable_type (layers[i])) + return 0; + + if ((gimp_drawable_width (layers[i]) == w) && + (gimp_drawable_height (layers[i]) == h)) + ++num_surfaces; + } + + if ((num_surfaces < min_surfaces) || + (num_surfaces > max_surfaces) || + (num_layers != (num_surfaces * num_mipmaps))) + return 0; + + for (i = 0; valid && i < num_layers; i += num_mipmaps) + { + if ((gimp_drawable_width (layers[i]) != w) || + (gimp_drawable_height (layers[i]) != h)) + { + valid = FALSE; + break; + } + + for (j = 1; j < num_mipmaps; ++j) + { + mipw = w >> j; + miph = h >> j; + if (mipw < 1) mipw = 1; + if (miph < 1) miph = 1; + if ((gimp_drawable_width (layers[i + j]) != mipw) || + (gimp_drawable_height (layers[i + j]) != miph)) + { + valid = FALSE; + break; + } + } + } + + return valid; +} + +static gboolean +check_cubemap (gint32 image_id) +{ + gint *layers; + gint num_layers; + gboolean cubemap = TRUE; + gint i, j, k, w, h; + gchar *layer_name; + GimpImageType type; + + layers = gimp_image_get_layers (image_id, &num_layers); + + if (num_layers < 6) + return FALSE; + + /* check for a valid cubemap with mipmap layers */ + if (num_layers > 6) + { + /* check that mipmap layers are in order for a cubemap */ + if (! check_mipmaps (image_id, DDS_SAVE_CUBEMAP)) + return FALSE; + + /* invalidate cubemap faces */ + for (i = 0; i < 6; ++i) + cubemap_faces[i] = -1; + + /* find the mipmap level 0 layers */ + w = gimp_image_width (image_id); + h = gimp_image_height (image_id); + + for (i = 0; i < num_layers; ++i) + { + if ((gimp_drawable_width (layers[i]) != w) || + (gimp_drawable_height (layers[i]) != h)) + continue; + + layer_name = (char*)gimp_item_get_name (layers[i]); + for (j = 0; j < 6; ++j) + { + for (k = 0; k < 4; ++k) + { + if (strstr (layer_name, cubemap_face_names[k][j])) + { + if (cubemap_faces[j] == -1) + { + cubemap_faces[j] = layers[i]; + break; + } + } + } + } + } + + /* check for 6 valid faces */ + for (i = 0; i < 6; ++i) + { + if (cubemap_faces[i] == -1) + { + cubemap = FALSE; + break; + } + } + + /* make sure they are all the same type */ + if (cubemap) + { + type = gimp_drawable_type (cubemap_faces[0]); + for (i = 1; i < 6 && cubemap; ++i) + { + if (gimp_drawable_type (cubemap_faces[i]) != type) + cubemap = FALSE; + } + } + } + + if (num_layers == 6) + { + /* invalidate cubemap faces */ + for (i = 0; i < 6; ++i) + cubemap_faces[i] = -1; + + for (i = 0; i < 6; ++i) + { + layer_name = (char*)gimp_item_get_name (layers[i]); + for (j = 0; j < 6; ++j) + { + for (k = 0; k < 4; ++k) + { + if (strstr (layer_name, cubemap_face_names[k][j])) + { + if (cubemap_faces[j] == -1) + { + cubemap_faces[j] = layers[i]; + break; + } + } + } + } + } + + /* check for 6 valid faces */ + for (i = 0; i < 6; ++i) + { + if (cubemap_faces[i] == -1) + { + cubemap = FALSE; + break; + } + } + + /* make sure they are all the same size */ + if (cubemap) + { + w = gimp_drawable_width (cubemap_faces[0]); + h = gimp_drawable_height (cubemap_faces[0]); + + for (i = 1; i < 6 && cubemap; ++i) + { + if ((gimp_drawable_width (cubemap_faces[i]) != w) || + (gimp_drawable_height (cubemap_faces[i]) != h)) + cubemap = FALSE; + } + } + + /* make sure they are all the same type */ + if (cubemap) + { + type = gimp_drawable_type (cubemap_faces[0]); + for (i = 1; i < 6 && cubemap; ++i) + { + if (gimp_drawable_type (cubemap_faces[i]) != type) + cubemap = FALSE; + } + } + } + + return cubemap; +} + +static gboolean +check_volume (gint32 image_id) +{ + gint *layers; + gint num_layers; + gboolean volume = FALSE; + gint i, w, h; + GimpImageType type; + + layers = gimp_image_get_layers (image_id, &num_layers); + + if (num_layers > 1) + { + volume = TRUE; + + /* make sure all layers are the same size */ + w = gimp_drawable_width (layers[0]); + h = gimp_drawable_height (layers[0]); + + for (i = 1; i < num_layers && volume; ++i) + { + if ((gimp_drawable_width (layers[i]) != w) || + (gimp_drawable_height (layers[i]) != h)) + volume = FALSE; + } + + if (volume) + { + /* make sure all layers are the same type */ + type = gimp_drawable_type (layers[0]); + for (i = 1; i < num_layers && volume; ++i) + { + if (gimp_drawable_type (layers[i]) != type) + volume = FALSE; + } + } + } + + return volume; +} + +static gboolean +check_array (gint32 image_id) +{ + gint *layers; + gint num_layers; + gboolean array = FALSE; + gint i, w, h; + GimpImageType type; + + if (check_mipmaps (image_id, DDS_SAVE_ARRAY)) + return 1; + + layers = gimp_image_get_layers (image_id, &num_layers); + + if (num_layers > 1) + { + array = TRUE; + + /* make sure all layers are the same size */ + w = gimp_drawable_width (layers[0]); + h = gimp_drawable_height (layers[0]); + + for (i = 1; i < num_layers && array; ++i) + { + if ((gimp_drawable_width (layers[i]) != w) || + (gimp_drawable_height (layers[i]) != h)) + array = FALSE; + } + + if (array) + { + /* make sure all layers are the same type */ + type = gimp_drawable_type (layers[0]); + for (i = 1; i < num_layers; ++i) + { + if (gimp_drawable_type (layers[i]) != type) + { + array = FALSE; + break; + } + } + } + } + + return array; +} + +static int +get_array_size (gint32 image_id) +{ + gint *layers; + gint num_layers; + gint i, w, h; + gint elements = 0; + + layers = gimp_image_get_layers (image_id, &num_layers); + + w = gimp_image_width (image_id); + h = gimp_image_height (image_id); + + for (i = 0; i < num_layers; ++i) + { + if ((gimp_drawable_width (layers[i]) == w) && + (gimp_drawable_height (layers[i]) == h)) + { + elements++; + } + } + + return elements; +} + +GimpPDBStatusType +write_dds (gchar *filename, + gint32 image_id, + gint32 drawable_id, + gboolean interactive_dds, + gboolean is_duplicate_image) +{ + FILE *fp; + gchar *tmp; + int rc = 0; + + is_mipmap_chain_valid = check_mipmaps (image_id, dds_write_vals.savetype); + + is_cubemap = check_cubemap (image_id); + is_volume = check_volume (image_id); + is_array = check_array (image_id); + + if (interactive_dds) + { + if (! is_mipmap_chain_valid && + dds_write_vals.mipmaps == DDS_MIPMAP_EXISTING) + dds_write_vals.mipmaps = DDS_MIPMAP_NONE; + + if (! save_dialog (image_id, drawable_id)) + return GIMP_PDB_CANCEL; + } + else + { + if (dds_write_vals.savetype == DDS_SAVE_CUBEMAP && ! is_cubemap) + { + g_message ("DDS: Cannot save image as cube map"); + return GIMP_PDB_EXECUTION_ERROR; + } + + if (dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP && ! is_volume) + { + g_message ("DDS: Cannot save image as volume map"); + return GIMP_PDB_EXECUTION_ERROR; + } + + if (dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP && + dds_write_vals.compression != DDS_COMPRESS_NONE) + { + g_message ("DDS: Cannot save volume map with compression"); + return GIMP_PDB_EXECUTION_ERROR; + } + + if (dds_write_vals.mipmaps == DDS_MIPMAP_EXISTING && + ! is_mipmap_chain_valid) + { + g_message ("DDS: Cannot save with existing mipmaps as the mipmap chain is incomplete"); + return GIMP_PDB_EXECUTION_ERROR; + } + } + + fp = g_fopen (filename, "wb"); + if (fp == 0) + { + g_message ("Error opening %s", filename); + return GIMP_PDB_EXECUTION_ERROR; + } + + if (strrchr (filename, '/')) + tmp = g_strdup_printf ("Saving %s:", strrchr (filename, '/') + 1); + else + tmp = g_strdup_printf ("Saving %s:", filename); + gimp_progress_init (tmp); + g_free (tmp); + + /* If destructive changes are going to happen to the image, + * make sure we send a duplicate of it to write_image + */ + if (! is_duplicate_image) + { + gint32 duplicate_image = gimp_image_duplicate (image_id); + rc = write_image (fp, duplicate_image, drawable_id); + gimp_image_delete (duplicate_image); + } + else + { + rc = write_image (fp, image_id, drawable_id); + } + + + fclose (fp); + + return rc ? GIMP_PDB_SUCCESS : GIMP_PDB_EXECUTION_ERROR; +} + +static void +swap_rb (unsigned char *pixels, + unsigned int n, + int bpp) +{ + unsigned int i; + unsigned char t; + + for (i = 0; i < n; ++i) + { + t = pixels[bpp * i + 0]; + pixels[bpp * i + 0] = pixels[bpp * i + 2]; + pixels[bpp * i + 2] = t; + } +} + +static void +alpha_exp (unsigned char *dst, + int r, + int g, + int b, + int a) +{ + float ar, ag, ab, aa; + + ar = (float)r / 255.0f; + ag = (float)g / 255.0f; + ab = (float)b / 255.0f; + + aa = MAX (ar, MAX (ag, ab)); + + if (aa < 1e-04f) + { + dst[0] = b; + dst[1] = g; + dst[2] = r; + dst[3] = 255; + return; + } + + ar /= aa; + ag /= aa; + ab /= aa; + + r = (int)floorf (255.0f * ar + 0.5f); + g = (int)floorf (255.0f * ag + 0.5f); + b = (int)floorf (255.0f * ab + 0.5f); + a = (int)floorf (255.0f * aa + 0.5f); + + dst[0] = MAX (0, MIN (255, b)); + dst[1] = MAX (0, MIN (255, g)); + dst[2] = MAX (0, MIN (255, r)); + dst[3] = MAX (0, MIN (255, a)); +} + +static void +convert_pixels (unsigned char *dst, + unsigned char *src, + int format, + int w, + int h, + int d, + int bpp, + unsigned char *palette, + int mipmaps) +{ + unsigned int i, num_pixels; + unsigned char r, g, b, a; + + if (d > 0) + num_pixels = get_volume_mipmapped_size (w, h, d, 1, 0, mipmaps, DDS_COMPRESS_NONE); + else + num_pixels = get_mipmapped_size (w, h, 1, 0, mipmaps, DDS_COMPRESS_NONE); + + for (i = 0; i < num_pixels; ++i) + { + if (bpp == 1) + { + if (palette) + { + r = palette[3 * src[i] + 0]; + g = palette[3 * src[i] + 1]; + b = palette[3 * src[i] + 2]; + } + else + r = g = b = src[i]; + + if (format == DDS_FORMAT_A8) + a = src[i]; + else + a = 255; + } + else if (bpp == 2) + { + r = g = b = src[2 * i]; + a = src[2 * i + 1]; + } + else if (bpp == 3) + { + b = src[3 * i + 0]; + g = src[3 * i + 1]; + r = src[3 * i + 2]; + a = 255; + } + else + { + b = src[4 * i + 0]; + g = src[4 * i + 1]; + r = src[4 * i + 2]; + a = src[4 * i + 3]; + } + + switch (format) + { + case DDS_FORMAT_RGB8: + dst[3 * i + 0] = b; + dst[3 * i + 1] = g; + dst[3 * i + 2] = r; + break; + case DDS_FORMAT_RGBA8: + dst[4 * i + 0] = b; + dst[4 * i + 1] = g; + dst[4 * i + 2] = r; + dst[4 * i + 3] = a; + break; + case DDS_FORMAT_BGR8: + dst[3 * i + 0] = r; + dst[3 * i + 1] = g; + dst[3 * i + 2] = b; + break; + case DDS_FORMAT_ABGR8: + dst[4 * i + 0] = r; + dst[4 * i + 1] = g; + dst[4 * i + 2] = b; + dst[4 * i + 3] = a; + break; + case DDS_FORMAT_R5G6B5: + PUTL16(&dst[2 * i], pack_r5g6b5(r, g, b)); + break; + case DDS_FORMAT_RGBA4: + PUTL16(&dst[2 * i], pack_rgba4(r, g, b, a)); + break; + case DDS_FORMAT_RGB5A1: + PUTL16(&dst[2 * i], pack_rgb5a1(r, g, b, a)); + break; + case DDS_FORMAT_RGB10A2: + PUTL32(&dst[4 * i], pack_rgb10a2(r, g, b, a)); + break; + case DDS_FORMAT_R3G3B2: + dst[i] = pack_r3g3b2(r, g, b); + break; + case DDS_FORMAT_A8: + dst[i] = a; + break; + case DDS_FORMAT_L8: + dst[i] = rgb_to_luminance (r, g, b); + break; + case DDS_FORMAT_L8A8: + dst[2 * i + 0] = rgb_to_luminance (r, g, b); + dst[2 * i + 1] = a; + break; + case DDS_FORMAT_YCOCG: + dst[4 * i] = a; + RGB_to_YCoCg (&dst[4 * i], r, g, b); + break; + case DDS_FORMAT_AEXP: + alpha_exp (&dst[4 * i], r, g, b, a); + break; + default: + break; + } + } +} + +static void +get_mipmap_chain (unsigned char *dst, + int w, + int h, + int bpp, + gint32 image_id, + gint drawable_id) +{ + gint *layers, num_layers; + GeglBuffer *buffer; + const Babl *format = 0; + int i, idx = 0, offset, mipw, miph; + + if (bpp == 1) + format = babl_format ("Y' u8"); + else if (bpp == 2) + format = babl_format ("Y'A u8"); + else if (bpp == 3) + format = babl_format ("R'G'B' u8"); + else + format = babl_format ("R'G'B'A u8"); + + layers = gimp_image_get_layers (image_id, &num_layers); + + for (i = 0; i < num_layers; ++i) + { + if (layers[i] == drawable_id) + { + idx = i; + break; + } + } + + if (i == num_layers) return; + + offset = 0; + + while (get_next_mipmap_dimensions (&mipw, &miph, w, h)) + { + buffer = gimp_drawable_get_buffer (layers[++idx]); + + if ((gegl_buffer_get_width (buffer) != mipw) || + (gegl_buffer_get_height (buffer) != miph)) + { + g_object_unref (buffer); + return; + } + + gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, mipw, miph), 1.0, format, + dst + offset, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + g_object_unref (buffer); + + /* we need BGRX or BGRA */ + if (bpp >= 3) + swap_rb (dst + offset, mipw * miph, bpp); + + offset += (mipw * miph * bpp); + w = mipw; + h = miph; + } +} + +static void +write_layer (FILE *fp, + gint32 image_id, + gint32 drawable_id, + int w, + int h, + int bpp, + int fmtbpp, + int mipmaps) +{ + GeglBuffer *buffer; + const Babl *format = 0; + GimpImageBaseType basetype; + GimpImageType type; + unsigned char *src, *dst, *fmtdst, *tmp; + unsigned char *palette = NULL; + int i, c, x, y, size, fmtsize, offset, colors; + int compression = dds_write_vals.compression; + int flags = 0; + + basetype = gimp_image_base_type (image_id); + type = gimp_drawable_type (drawable_id); + + buffer = gimp_drawable_get_buffer (drawable_id); + + src = g_malloc (w * h * bpp); + + if (basetype == GIMP_INDEXED) + format = gimp_drawable_get_format (drawable_id); + else if (bpp == 1) + format = babl_format ("Y' u8"); + else if (bpp == 2) + format = babl_format ("Y'A u8"); + else if (bpp == 3) + format = babl_format ("R'G'B' u8"); + else + format = babl_format ("R'G'B'A u8"); + + gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format, src, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + if (basetype == GIMP_INDEXED) + { + palette = gimp_image_get_colormap (image_id, &colors); + + if (type == GIMP_INDEXEDA_IMAGE) + { + tmp = g_malloc (w * h); + for (i = 0; i < w * h; ++i) + tmp[i] = src[2 * i]; + g_free (src); + src = tmp; + bpp = 1; + } + } + + /* we want and assume BGRA ordered pixels for bpp >= 3 from here and + onwards */ + if (bpp >= 3) + swap_rb (src, w * h, bpp); + + if (compression == DDS_COMPRESS_BC3N) + { + if (bpp != 4) + { + fmtsize = w * h * 4; + fmtdst = g_malloc (fmtsize); + convert_pixels (fmtdst, src, DDS_FORMAT_RGBA8, w, h, 0, bpp, + palette, 1); + g_free (src); + src = fmtdst; + bpp = 4; + } + + for (y = 0; y < h; ++y) + { + for (x = 0; x < w; ++x) + { + /* set alpha to red (x) */ + src[y * (w * 4) + (x * 4) + 3] = + src[y * (w * 4) + (x * 4) + 2]; + /* set red to 1 */ + src[y * (w * 4) + (x * 4) + 2] = 255; + } + } + } + + /* RXGB (Doom3) */ + if (compression == DDS_COMPRESS_RXGB) + { + if (bpp != 4) + { + fmtsize = w * h * 4; + fmtdst = g_malloc (fmtsize); + convert_pixels (fmtdst, src, DDS_FORMAT_RGBA8, w, h, 0, bpp, + palette, 1); + g_free (src); + src = fmtdst; + bpp = 4; + } + + for (y = 0; y < h; ++y) + { + for (x = 0; x < w; ++x) + { + /* swap red and alpha */ + c = src[y * (w * 4) + (x * 4) + 3]; + src[y * (w * 4) + (x * 4) + 3] = + src[y * (w * 4) + (x * 4) + 2]; + src[y * (w * 4) + (x * 4) + 2] = c; + } + } + } + + if (compression == DDS_COMPRESS_YCOCG || + compression == DDS_COMPRESS_YCOCGS) /* convert to YCoCG */ + { + fmtsize = w * h * 4; + fmtdst = g_malloc (fmtsize); + convert_pixels (fmtdst, src, DDS_FORMAT_YCOCG, w, h, 0, bpp, + palette, 1); + g_free (src); + src = fmtdst; + bpp = 4; + } + + if (compression == DDS_COMPRESS_AEXP) + { + fmtsize = w * h * 4; + fmtdst = g_malloc (fmtsize); + convert_pixels (fmtdst, src, DDS_FORMAT_AEXP, w, h, 0, bpp, + palette, 1); + g_free (src); + src = fmtdst; + bpp = 4; + } + + if (compression == DDS_COMPRESS_NONE) + { + if (mipmaps > 1) + { + /* pre-convert indexed images to RGB for better quality mipmaps + if a pixel format conversion is requested */ + if (dds_write_vals.format > DDS_FORMAT_DEFAULT && basetype == GIMP_INDEXED) + { + fmtsize = get_mipmapped_size (w, h, 3, 0, mipmaps, DDS_COMPRESS_NONE); + fmtdst = g_malloc (fmtsize); + convert_pixels (fmtdst, src, DDS_FORMAT_RGB8, w, h, 0, bpp, + palette, 1); + g_free (src); + src = fmtdst; + bpp = 3; + palette = NULL; + } + + size = get_mipmapped_size (w, h, bpp, 0, mipmaps, DDS_COMPRESS_NONE); + dst = g_malloc (size); + if (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) + { + generate_mipmaps (dst, src, w, h, bpp, palette != NULL, + mipmaps, + dds_write_vals.mipmap_filter, + dds_write_vals.mipmap_wrap, + dds_write_vals.gamma_correct + dds_write_vals.srgb, + dds_write_vals.gamma, + dds_write_vals.preserve_alpha_coverage, + dds_write_vals.alpha_test_threshold); + } + else + { + memcpy (dst, src, w * h * bpp); + get_mipmap_chain (dst + (w * h * bpp), w, h, bpp, image_id, drawable_id); + } + + if (dds_write_vals.format > DDS_FORMAT_DEFAULT) + { + fmtsize = get_mipmapped_size (w, h, fmtbpp, 0, mipmaps, + DDS_COMPRESS_NONE); + fmtdst = g_malloc (fmtsize); + + convert_pixels (fmtdst, dst, dds_write_vals.format, w, h, 0, bpp, + palette, mipmaps); + + g_free (dst); + dst = fmtdst; + bpp = fmtbpp; + } + + offset = 0; + + for (i = 0; i < mipmaps; ++i) + { + size = get_mipmapped_size (w, h, bpp, i, 1, DDS_COMPRESS_NONE); + fwrite (dst + offset, 1, size, fp); + offset += size; + } + + g_free (dst); + } + else + { + if (dds_write_vals.format > DDS_FORMAT_DEFAULT) + { + fmtdst = g_malloc (h * w * fmtbpp); + convert_pixels (fmtdst, src, dds_write_vals.format, w, h, 0, bpp, + palette, 1); + g_free (src); + src = fmtdst; + bpp = fmtbpp; + } + + fwrite (src, 1, h * w * bpp, fp); + } + } + else + { + size = get_mipmapped_size (w, h, bpp, 0, mipmaps, compression); + + dst = g_malloc (size); + + if (basetype == GIMP_INDEXED) + { + fmtsize = get_mipmapped_size (w, h, 3, 0, mipmaps, + DDS_COMPRESS_NONE); + fmtdst = g_malloc (fmtsize); + convert_pixels (fmtdst, src, DDS_FORMAT_RGB8, w, h, 0, bpp, + palette, mipmaps); + g_free (src); + src = fmtdst; + bpp = 3; + } + + if (mipmaps > 1) + { + fmtsize = get_mipmapped_size (w, h, bpp, 0, mipmaps, + DDS_COMPRESS_NONE); + fmtdst = g_malloc (fmtsize); + if (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) + { + generate_mipmaps (fmtdst, src, w, h, bpp, 0, mipmaps, + dds_write_vals.mipmap_filter, + dds_write_vals.mipmap_wrap, + dds_write_vals.gamma_correct + dds_write_vals.srgb, + dds_write_vals.gamma, + dds_write_vals.preserve_alpha_coverage, + dds_write_vals.alpha_test_threshold); + } + else + { + memcpy (fmtdst, src, w * h * bpp); + get_mipmap_chain (fmtdst + (w * h * bpp), w, h, bpp, image_id, drawable_id); + } + + g_free (src); + src = fmtdst; + } + + flags = 0; + if (dds_write_vals.perceptual_metric) flags |= DXT_PERCEPTUAL; + + dxt_compress (dst, src, compression, w, h, bpp, mipmaps, flags); + + fwrite (dst, 1, size, fp); + + g_free (dst); + } + + g_free (src); + + g_object_unref (buffer); +} + +static void +write_volume_mipmaps (FILE *fp, + gint32 image_id, + gint32 *layers, + int w, + int h, + int d, + int bpp, + int fmtbpp, + int mipmaps) +{ + int i, size, offset, colors; + unsigned char *src, *dst, *tmp, *fmtdst; + unsigned char *palette = 0; + GeglBuffer *buffer; + const Babl *format; + GimpImageBaseType type; + + type = gimp_image_base_type (image_id); + + if (dds_write_vals.compression != DDS_COMPRESS_NONE) return; + + src = g_malloc (w * h * bpp * d); + + if (bpp == 1) + format = babl_format ("Y' u8"); + else if (bpp == 2) + format = babl_format ("Y'A u8"); + else if (bpp == 3) + format = babl_format ("R'G'B' u8"); + else + format = babl_format ("R'G'B'A u8"); + + if (gimp_image_base_type (image_id) == GIMP_INDEXED) + palette = gimp_image_get_colormap (image_id, &colors); + + offset = 0; + for (i = 0; i < d; ++i) + { + buffer = gimp_drawable_get_buffer (layers[i]); + gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format, + src + offset, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + offset += (w * h * bpp); + g_object_unref (buffer); + } + + if (gimp_drawable_type (layers[0]) == GIMP_INDEXEDA_IMAGE) + { + tmp = g_malloc (w * h * d); + for (i = 0; i < w * h * d; ++i) + tmp[i] = src[2 * i]; + g_free (src); + src = tmp; + bpp = 1; + } + + /* we want and assume BGRA ordered pixels for bpp >= 3 from here and + onwards */ + if (bpp >= 3) + swap_rb (src, w * h * d, bpp); + + /* pre-convert indexed images to RGB for better mipmaps if a + pixel format conversion is requested */ + if (dds_write_vals.format > DDS_FORMAT_DEFAULT && type == GIMP_INDEXED) + { + size = get_volume_mipmapped_size (w, h, d, 3, 0, mipmaps, + DDS_COMPRESS_NONE); + dst = g_malloc (size); + convert_pixels (dst, src, DDS_FORMAT_RGB8, w, h, d, bpp, palette, 1); + g_free (src); + src = dst; + bpp = 3; + palette = NULL; + } + + size = get_volume_mipmapped_size (w, h, d, bpp, 0, mipmaps, + dds_write_vals.compression); + + dst = g_malloc (size); + + offset = get_volume_mipmapped_size (w, h, d, bpp, 0, 1, + dds_write_vals.compression); + + generate_volume_mipmaps (dst, src, w, h, d, bpp, + palette != NULL, mipmaps, + dds_write_vals.mipmap_filter, + dds_write_vals.mipmap_wrap, + dds_write_vals.gamma_correct + dds_write_vals.srgb, + dds_write_vals.gamma); + + if (dds_write_vals.format > DDS_FORMAT_DEFAULT) + { + size = get_volume_mipmapped_size (w, h, d, fmtbpp, 0, mipmaps, + dds_write_vals.compression); + offset = get_volume_mipmapped_size (w, h, d, fmtbpp, 0, 1, + dds_write_vals.compression); + fmtdst = g_malloc (size); + + convert_pixels (fmtdst, dst, dds_write_vals.format, w, h, d, bpp, + palette, mipmaps); + g_free (dst); + dst = fmtdst; + } + + fwrite (dst + offset, 1, size, fp); + + g_free (src); + g_free (dst); +} + +static gboolean +write_image (FILE *fp, + gint32 image_id, + gint32 drawable_id) +{ + GimpImageType drawable_type; + GimpImageBaseType basetype; + gint i, w, h; + gint bpp = 0; + gint fmtbpp = 0; + gint has_alpha = 0; + gint num_mipmaps; + guchar hdr[DDS_HEADERSIZE]; + guchar hdr10[DDS_HEADERSIZE_DX10]; + guint flags = 0, pflags = 0, caps = 0, caps2 = 0, size = 0; + guint rmask = 0, gmask = 0, bmask = 0, amask = 0; + guint fourcc = 0; + DXGI_FORMAT dxgi_format = DXGI_FORMAT_UNKNOWN; + gint32 num_layers; + gint32 *layers; + guchar *cmap; + gint colors; + guchar zero[4] = {0, 0, 0, 0}; + gint is_dx10 = 0; + gint array_size = 1; + + if (dds_write_vals.flip_image) + { + gimp_image_flip (image_id, GIMP_ORIENTATION_VERTICAL); + drawable_id = gimp_image_get_active_drawable (image_id); + } + + layers = gimp_image_get_layers (image_id, &num_layers); + + if (dds_write_vals.mipmaps == DDS_MIPMAP_EXISTING) + drawable_id = layers[0]; + + if (dds_write_vals.savetype == DDS_SAVE_SELECTED_LAYER) + { + w = gimp_drawable_width (drawable_id); + h = gimp_drawable_height (drawable_id); + } + else + { + w = gimp_image_width (image_id); + h = gimp_image_height (image_id); + } + + basetype = gimp_image_base_type (image_id); + drawable_type = gimp_drawable_type (drawable_id); + + switch (drawable_type) + { + case GIMP_RGB_IMAGE: bpp = 3; break; + case GIMP_RGBA_IMAGE: bpp = 4; break; + case GIMP_GRAY_IMAGE: bpp = 1; break; + case GIMP_GRAYA_IMAGE: bpp = 2; break; + case GIMP_INDEXED_IMAGE: bpp = 1; break; + case GIMP_INDEXEDA_IMAGE: bpp = 2; break; + default: + break; + } + + if (dds_write_vals.format > DDS_FORMAT_DEFAULT) + { + for (i = 0; ; ++i) + { + if (format_info[i].format == dds_write_vals.format) + { + fmtbpp = format_info[i].bpp; + has_alpha = format_info[i].alpha; + rmask = format_info[i].rmask; + gmask = format_info[i].gmask; + bmask = format_info[i].bmask; + amask = format_info[i].amask; + dxgi_format = format_info[i].dxgi_format; + break; + } + } + } + else if (bpp == 1) + { + if (basetype == GIMP_INDEXED) + { + fmtbpp = 1; + has_alpha = 0; + rmask = bmask = gmask = amask = 0; + } + else + { + fmtbpp = 1; + has_alpha = 0; + rmask = 0x000000ff; + gmask = bmask = amask = 0; + dxgi_format = DXGI_FORMAT_R8_UNORM; + } + } + else if (bpp == 2) + { + if (basetype == GIMP_INDEXED) + { + fmtbpp = 1; + has_alpha = 0; + rmask = gmask = bmask = amask = 0; + } + else + { + fmtbpp = 2; + has_alpha = 1; + rmask = 0x000000ff; + gmask = 0x000000ff; + bmask = 0x000000ff; + amask = 0x0000ff00; + } + } + else if (bpp == 3) + { + fmtbpp = 3; + rmask = 0x00ff0000; + gmask = 0x0000ff00; + bmask = 0x000000ff; + amask = 0x00000000; + } + else + { + fmtbpp = 4; + has_alpha = 1; + rmask = 0x00ff0000; + gmask = 0x0000ff00; + bmask = 0x000000ff; + amask = 0xff000000; + dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM; + } + + memset (hdr, 0, DDS_HEADERSIZE); + + PUTL32(hdr, FOURCC ('D','D','S',' ')); + PUTL32(hdr + 4, 124); + PUTL32(hdr + 12, h); + PUTL32(hdr + 16, w); + PUTL32(hdr + 76, 32); + + if (dds_write_vals.compression == DDS_COMPRESS_NONE) + { + PUTL32(hdr + 88, fmtbpp << 3); + PUTL32(hdr + 92, rmask); + PUTL32(hdr + 96, gmask); + PUTL32(hdr + 100, bmask); + PUTL32(hdr + 104, amask); + } + + /* + put some information in the reserved area to identify the origin + of the image + */ + PUTL32(hdr + 32, FOURCC ('G','I','M','P')); + PUTL32(hdr + 36, FOURCC ('-','D','D','S')); + PUTL32(hdr + 40, DDS_PLUGIN_VERSION); + + flags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT; + + caps = DDSCAPS_TEXTURE; + if (dds_write_vals.mipmaps) + { + flags |= DDSD_MIPMAPCOUNT; + caps |= (DDSCAPS_COMPLEX | DDSCAPS_MIPMAP); + num_mipmaps = get_num_mipmaps (w, h); + } + else + { + num_mipmaps = 1; + } + + if ((dds_write_vals.savetype == DDS_SAVE_CUBEMAP) && is_cubemap) + { + caps |= DDSCAPS_COMPLEX; + caps2 |= (DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALL_FACES); + } + else if ((dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP) && is_volume) + { + PUTL32(hdr + 24, num_layers); /* depth */ + flags |= DDSD_DEPTH; + caps |= DDSCAPS_COMPLEX; + caps2 |= DDSCAPS2_VOLUME; + } + + PUTL32(hdr + 28, num_mipmaps); + PUTL32(hdr + 108, caps); + PUTL32(hdr + 112, caps2); + + if (dds_write_vals.compression == DDS_COMPRESS_NONE) + { + flags |= DDSD_PITCH; + + if (dds_write_vals.format > DDS_FORMAT_DEFAULT) + { + if (dds_write_vals.format == DDS_FORMAT_A8) + pflags |= DDPF_ALPHA; + else + { + if (((fmtbpp == 1) || (dds_write_vals.format == DDS_FORMAT_L8A8)) && + (dds_write_vals.format != DDS_FORMAT_R3G3B2)) + pflags |= DDPF_LUMINANCE; + else + pflags |= DDPF_RGB; + } + } + else + { + if (bpp == 1) + { + if (basetype == GIMP_INDEXED) + pflags |= DDPF_PALETTEINDEXED8; + else + pflags |= DDPF_LUMINANCE; + } + else if ((bpp == 2) && (basetype == GIMP_INDEXED)) + { + pflags |= DDPF_PALETTEINDEXED8; + } + else + { + pflags |= DDPF_RGB; + } + } + + if (has_alpha) + pflags |= DDPF_ALPHAPIXELS; + + PUTL32 (hdr + 8, flags); + PUTL32 (hdr + 20, w * fmtbpp); /* pitch */ + PUTL32 (hdr + 80, pflags); + + /* + * write extra fourcc info - this is special to GIMP DDS. When the image + * is read by the plugin, we can detect the added information to decode + * the pixels + */ + if (dds_write_vals.format == DDS_FORMAT_AEXP) + { + PUTL32 (hdr + 44, FOURCC ('A','E','X','P')); + } + else if (dds_write_vals.format == DDS_FORMAT_YCOCG) + { + PUTL32 (hdr + 44, FOURCC ('Y','C','G','1')); + } + } + else + { + flags |= DDSD_LINEARSIZE; + pflags = DDPF_FOURCC; + + switch (dds_write_vals.compression) + { + case DDS_COMPRESS_BC1: + fourcc = FOURCC ('D','X','T','1'); + dxgi_format = DXGI_FORMAT_BC1_UNORM; + break; + + case DDS_COMPRESS_BC2: + fourcc = FOURCC ('D','X','T','3'); + dxgi_format = DXGI_FORMAT_BC2_UNORM; + break; + + case DDS_COMPRESS_BC3: + case DDS_COMPRESS_BC3N: + case DDS_COMPRESS_YCOCG: + case DDS_COMPRESS_YCOCGS: + case DDS_COMPRESS_AEXP: + fourcc = FOURCC ('D','X','T','5'); + dxgi_format = DXGI_FORMAT_BC3_UNORM; + break; + + case DDS_COMPRESS_RXGB: + fourcc = FOURCC ('R','X','G','B'); + dxgi_format = DXGI_FORMAT_BC3_UNORM; + break; + + case DDS_COMPRESS_BC4: + fourcc = FOURCC ('A','T','I','1'); + dxgi_format = DXGI_FORMAT_BC4_UNORM; + //is_dx10 = 1; + break; + + case DDS_COMPRESS_BC5: + fourcc = FOURCC ('A','T','I','2'); + dxgi_format = DXGI_FORMAT_BC5_UNORM; + //is_dx10 = 1; + break; + } + + if ((dds_write_vals.compression == DDS_COMPRESS_BC3N) || + (dds_write_vals.compression == DDS_COMPRESS_RXGB)) + { + pflags |= DDPF_NORMAL; + } + + PUTL32 (hdr + 8, flags); + PUTL32 (hdr + 80, pflags); + PUTL32 (hdr + 84, fourcc); + + size = ((w + 3) >> 2) * ((h + 3) >> 2); + if ((dds_write_vals.compression == DDS_COMPRESS_BC1) || + (dds_write_vals.compression == DDS_COMPRESS_BC4)) + size *= 8; + else + size *= 16; + + PUTL32 (hdr + 20, size); /* linear size */ + + /* + * write extra fourcc info - this is special to GIMP DDS. When the image + * is read by the plugin, we can detect the added information to decode + * the pixels + */ + if (dds_write_vals.compression == DDS_COMPRESS_AEXP) + { + PUTL32 (hdr + 44, FOURCC ('A','E','X','P')); + } + else if (dds_write_vals.compression == DDS_COMPRESS_YCOCG) + { + PUTL32 (hdr + 44, FOURCC ('Y','C','G','1')); + } + else if (dds_write_vals.compression == DDS_COMPRESS_YCOCGS) + { + PUTL32 (hdr + 44, FOURCC ('Y','C','G','2')); + } + } + + /* texture arrays require a DX10 header */ + if (dds_write_vals.savetype == DDS_SAVE_ARRAY) + is_dx10 = 1; + + if (is_dx10) + { + array_size = (dds_write_vals.savetype == DDS_SAVE_SELECTED_LAYER || + dds_write_vals.savetype == DDS_SAVE_VISIBLE_LAYERS) ? 1 : get_array_size (image_id); + + PUTL32 (hdr10 + 0, dxgi_format); + PUTL32 (hdr10 + 4, D3D10_RESOURCE_DIMENSION_TEXTURE2D); + PUTL32 (hdr10 + 8, 0); + PUTL32 (hdr10 + 12, array_size); + PUTL32 (hdr10 + 16, 0); + + /* update main header accordingly */ + PUTL32 (hdr + 80, pflags | DDPF_FOURCC); + PUTL32 (hdr + 84, FOURCC ('D','X','1','0')); + } + + fwrite (hdr, DDS_HEADERSIZE, 1, fp); + + if (is_dx10) + fwrite (hdr10, DDS_HEADERSIZE_DX10, 1, fp); + + /* write palette for indexed images */ + if ((basetype == GIMP_INDEXED) && + (dds_write_vals.format == DDS_FORMAT_DEFAULT) && + (dds_write_vals.compression == DDS_COMPRESS_NONE)) + { + cmap = gimp_image_get_colormap (image_id, &colors); + + for (i = 0; i < colors; ++i) + { + fwrite (&cmap[3 * i], 1, 3, fp); + if (i == dds_write_vals.transindex) + fputc (0, fp); + else + fputc (255, fp); + } + + for (; i < 256; ++i) + fwrite (zero, 1, 4, fp); + } + + if (dds_write_vals.savetype == DDS_SAVE_CUBEMAP) + { + for (i = 0; i < 6; ++i) + { + write_layer (fp, image_id, cubemap_faces[i], w, h, bpp, fmtbpp, + num_mipmaps); + gimp_progress_update ((float)(i + 1) / 6.0); + } + } + else if (dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP) + { + for (i = 0; i < num_layers; ++i) + { + write_layer (fp, image_id, layers[i], w, h, bpp, fmtbpp, 1); + gimp_progress_update ((float)i / (float)num_layers); + } + + if (num_mipmaps > 1) + write_volume_mipmaps (fp, image_id, layers, w, h, num_layers, + bpp, fmtbpp, num_mipmaps); + } + else if (dds_write_vals.savetype == DDS_SAVE_ARRAY) + { + for (i = 0; i < num_layers; ++i) + { + if ((gimp_drawable_width (layers[i]) == w) && + (gimp_drawable_height (layers[i]) == h)) + { + write_layer (fp, image_id, layers[i], + w, h, bpp, fmtbpp, num_mipmaps); + } + + gimp_progress_update ((float)i / (float)num_layers); + } + } + else + { + if (dds_write_vals.savetype == DDS_SAVE_VISIBLE_LAYERS) + drawable_id = gimp_image_merge_visible_layers (image_id, 1); + write_layer (fp, image_id, drawable_id, w, h, bpp, fmtbpp, num_mipmaps); + } + + gimp_progress_update (1.0); + + return TRUE; +} + +static GtkWidget * +string_value_combo_new (string_value_t *strings, + int active_value) +{ + GtkWidget *opt; + GtkCellRenderer *renderer; + GtkListStore *store; + GtkTreeIter iter; + gint i; + gint active = 0; + + store = gtk_list_store_new (3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_BOOLEAN); + for (i = 0; strings[i].string; ++i) + { + if (strings[i].value == active_value) active = i; + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, strings[i].value, + 1, strings[i].string, + 2, 1, + -1); + } + + renderer = gtk_cell_renderer_text_new (); + + opt = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (opt), renderer, 1); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (opt), renderer, + "text", COMBO_STRING, + "sensitive", COMBO_SENSITIVE, + NULL); + + gtk_combo_box_set_active (GTK_COMBO_BOX (opt), active); + + g_object_unref (store); + + return opt; +} + +static void +string_value_combo_selected (GtkWidget *widget, + gpointer data) +{ + gint value; + GtkTreeIter iter; + GtkTreeModel *model; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget)); + gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter); + gtk_tree_model_get (model, &iter, COMBO_VALUE, &value, -1); + + *((int *)data) = value; +} + +static void +string_value_combo_set_item_sensitive (GtkWidget *widget, + gint value, + gboolean sensitive) +{ + GtkTreeIter iter; + GtkTreeModel *model; + gint val; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget)); + gtk_tree_model_get_iter_first (model, &iter); + do + { + gtk_tree_model_get (model, &iter, COMBO_VALUE, &val, -1); + if (val == value) + { + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + COMBO_SENSITIVE, sensitive, -1); + break; + } + } while (gtk_tree_model_iter_next (model, &iter)); +} + +static void +string_value_combo_set_active (GtkWidget *widget, + gint value) +{ + GtkTreeIter iter; + GtkTreeModel *model; + int val; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget)); + gtk_tree_model_get_iter_first (model, &iter); + do + { + gtk_tree_model_get (model, &iter, COMBO_VALUE, &val, -1); + if (val == value) + { + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), &iter); + break; + } + } while (gtk_tree_model_iter_next (model, &iter)); +} + +static void +save_dialog_response (GtkWidget *widget, + gint response_id, + gpointer data) +{ + switch (response_id) + { + case GTK_RESPONSE_OK: + runme = TRUE; + + default: + gtk_widget_destroy (widget); + break; + } +} + +static void +compression_selected (GtkWidget *widget, + gpointer data) +{ + GtkTreeIter iter; + GtkTreeModel *model; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget)); + gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter); + gtk_tree_model_get (model, &iter, COMBO_VALUE, + &dds_write_vals.compression, + -1); + + gtk_widget_set_sensitive (format_opt, + dds_write_vals.compression == DDS_COMPRESS_NONE); + gtk_widget_set_sensitive (pm_chk, + dds_write_vals.compression != DDS_COMPRESS_NONE); +} + +static void +savetype_selected (GtkWidget *widget, + gpointer data) +{ + gint32 image_id = *((gint32 *)data); + + dds_write_vals.savetype = gtk_combo_box_get_active (GTK_COMBO_BOX (widget)); + + switch (dds_write_vals.savetype) + { + case DDS_SAVE_SELECTED_LAYER: + case DDS_SAVE_VISIBLE_LAYERS: + case DDS_SAVE_CUBEMAP: + case DDS_SAVE_ARRAY: + gtk_widget_set_sensitive (compress_opt, TRUE); + break; + + case DDS_SAVE_VOLUMEMAP: + dds_write_vals.compression = DDS_COMPRESS_NONE; + gtk_combo_box_set_active (GTK_COMBO_BOX (compress_opt), + DDS_COMPRESS_NONE); + gtk_widget_set_sensitive (compress_opt, FALSE); + break; + } + + string_value_combo_set_item_sensitive (mipmap_opt, DDS_MIPMAP_EXISTING, + check_mipmaps (image_id, dds_write_vals.savetype)); +} + +static void +mipmaps_selected (GtkWidget *widget, + gpointer data) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget)); + gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter); + gtk_tree_model_get (model, &iter, COMBO_VALUE, + &dds_write_vals.mipmaps, -1); + + gtk_widget_set_sensitive (mipmap_filter_opt, + dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE); + gtk_widget_set_sensitive (mipmap_wrap_opt, + dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE); + gtk_widget_set_sensitive (gamma_chk, + dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE); + gtk_widget_set_sensitive (srgb_chk, + (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) && + dds_write_vals.gamma_correct); + gtk_widget_set_sensitive (gamma_spin, + (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) && + dds_write_vals.gamma_correct && + !dds_write_vals.srgb); + gtk_widget_set_sensitive (alpha_coverage_chk, + dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE); + gtk_widget_set_sensitive (alpha_test_threshold_spin, + (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) && + dds_write_vals.preserve_alpha_coverage); +} + +static void +toggle_clicked (GtkWidget *widget, + gpointer data) +{ + gint *flag = (int *)data; + + (*flag) = !(*flag); +} + +static void +transindex_clicked (GtkWidget *widget, + gpointer data) +{ + GtkWidget *spin = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "spin")); + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) + { + dds_write_vals.transindex = 0; + gtk_widget_set_sensitive (spin, TRUE); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 0); + } + else + { + gtk_widget_set_sensitive (spin, FALSE); + dds_write_vals.transindex = -1; + } +} + +static void +transindex_changed (GtkWidget *widget, + gpointer data) +{ + dds_write_vals.transindex = + gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget)); +} + +static void +gamma_correct_clicked (GtkWidget *widget, + gpointer data) +{ + dds_write_vals.gamma_correct = + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + + gtk_widget_set_sensitive (srgb_chk, + dds_write_vals.gamma_correct); + gtk_widget_set_sensitive (gamma_spin, + dds_write_vals.gamma_correct && + ! dds_write_vals.srgb); +} + +static void +srgb_clicked (GtkWidget *widget, + gpointer data) +{ + dds_write_vals.srgb = + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + + gtk_widget_set_sensitive (gamma_spin, !dds_write_vals.srgb); +} + +static void +gamma_changed (GtkWidget *widget, + gpointer data) +{ + dds_write_vals.gamma = + gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget)); +} + +static void +alpha_coverage_clicked (GtkWidget *widget, + gpointer data) +{ + dds_write_vals.preserve_alpha_coverage = + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + + gtk_widget_set_sensitive (alpha_test_threshold_spin, + dds_write_vals.preserve_alpha_coverage); +} + +static void +alpha_test_threshold_changed (GtkWidget *widget, + gpointer data) +{ + dds_write_vals.alpha_test_threshold = + gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget)); +} + +static gint +save_dialog (gint32 image_id, + gint32 drawable_id) +{ + GtkWidget *dlg; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *opt; + GtkWidget *check; + GtkWidget *spin; + GtkWidget *frame; + GimpImageBaseType basetype; + + if (is_cubemap || is_volume || is_array) + dds_write_vals.savetype = DDS_SAVE_SELECTED_LAYER; + + basetype = gimp_image_base_type (image_id); + + dlg = gimp_dialog_new (_("Export as DDS"), "dds", NULL, GTK_WIN_POS_MOUSE, + gimp_standard_help_func, SAVE_PROC, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Export"), GTK_RESPONSE_OK, + NULL); + + g_signal_connect (dlg, "response", + G_CALLBACK (save_dialog_response), + NULL); + g_signal_connect (dlg, "destroy", + G_CALLBACK (gtk_main_quit), + NULL); + + gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), + vbox, TRUE, TRUE, 0); + gtk_widget_show (vbox); + + table = gtk_table_new (6, 2, 0); + gtk_widget_show (table); + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_table_set_col_spacings (GTK_TABLE (table), 8); + + opt = string_value_combo_new (compression_strings, + dds_write_vals.compression); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + _("_Compression:"), + 0.0, 0.5, + opt, 1, FALSE); + + g_signal_connect (opt, "changed", + G_CALLBACK (compression_selected), + NULL); + + compress_opt = opt; + + check = gtk_check_button_new_with_mnemonic (_("Use _perceptual error metric")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), + dds_write_vals.perceptual_metric); + gtk_table_attach (GTK_TABLE (table), check, 1, 2, 1, 2, + GTK_FILL, 0, 0, 0); + gtk_widget_show (check); + + g_signal_connect (check, "clicked", + G_CALLBACK (toggle_clicked), + &dds_write_vals.perceptual_metric); + + pm_chk = check; + + opt = string_value_combo_new (format_strings, dds_write_vals.format); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 2, + _("_Format:"), + 0.0, 0.5, + opt, 1, FALSE); + + g_signal_connect (opt, "changed", + G_CALLBACK (string_value_combo_selected), + &dds_write_vals.format); + + gtk_widget_set_sensitive (opt, dds_write_vals.compression == DDS_COMPRESS_NONE); + + format_opt = opt; + + opt = string_value_combo_new (save_type_strings, dds_write_vals.savetype); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 3, + _("_Save:"), + 0.0, 0.5, + opt, 1, FALSE); + + g_signal_connect (opt, "changed", + G_CALLBACK (savetype_selected), + &image_id); + + string_value_combo_set_item_sensitive (opt, DDS_SAVE_CUBEMAP, is_cubemap); + string_value_combo_set_item_sensitive (opt, DDS_SAVE_VOLUMEMAP, is_volume); + string_value_combo_set_item_sensitive (opt, DDS_SAVE_ARRAY, is_array); + + check = gtk_check_button_new_with_mnemonic (_("Flip the image _vertically on export")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), + dds_write_vals.flip_image); + gtk_table_attach (GTK_TABLE (table), check, 1, 2, 4, 5, + GTK_FILL, 0, 0, 0); + gtk_widget_show (check); + + g_signal_connect (check, "clicked", + G_CALLBACK (toggle_clicked), + &dds_write_vals.flip_image); + + opt = string_value_combo_new (mipmap_strings, dds_write_vals.mipmaps); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 5, + _("_Mipmaps:"), + 0.0, 0.5, + opt, 1, FALSE); + + g_signal_connect (opt, "changed", + G_CALLBACK (mipmaps_selected), + &image_id); + + string_value_combo_set_item_sensitive (opt, DDS_MIPMAP_EXISTING, + check_mipmaps (image_id, + dds_write_vals.savetype)); + + mipmap_opt = opt; + + string_value_combo_set_item_sensitive (opt, DDS_MIPMAP_EXISTING, + ! (is_volume || is_cubemap) && + is_mipmap_chain_valid); + + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + check = gtk_check_button_new_with_label (_("Transparent index:")); + gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0); + gtk_widget_show (check); + + g_signal_connect (check, "clicked", + G_CALLBACK (transindex_clicked), + NULL); + + spin = gimp_spin_button_new + (GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 255, 1, 1, 0)), 1, 0); + gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0); + gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin), + GTK_UPDATE_IF_VALID); + gtk_widget_show (spin); + + g_signal_connect (spin, "value-changed", + G_CALLBACK (transindex_changed), + NULL); + + g_object_set_data (G_OBJECT (check), "spin", spin); + + if (basetype != GIMP_INDEXED) + { + gtk_widget_set_sensitive (check, FALSE); + gtk_widget_set_sensitive (spin, FALSE); + } + else if (dds_write_vals.transindex < 0) + { + gtk_widget_set_sensitive (spin, FALSE); + } + else if (dds_write_vals.transindex >= 0) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), + dds_write_vals.transindex); + } + + if (is_volume && dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP) + { + dds_write_vals.compression = DDS_COMPRESS_NONE; + string_value_combo_set_active (compress_opt, DDS_COMPRESS_NONE); + gtk_widget_set_sensitive (compress_opt, FALSE); + } + + frame = gimp_frame_new (_("Mipmap Options")); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + table = gtk_table_new (7, 2, 0); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_table_set_col_spacings (GTK_TABLE (table), 8); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + opt = string_value_combo_new (mipmap_filter_strings, + dds_write_vals.mipmap_filter); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + _("F_ilter:"), + 0.0, 0.5, + opt, 1, FALSE); + + g_signal_connect (opt, "changed", + G_CALLBACK (string_value_combo_selected), + &dds_write_vals.mipmap_filter); + + mipmap_filter_opt = opt; + + opt = string_value_combo_new (mipmap_wrap_strings, + dds_write_vals.mipmap_wrap); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, + _("_Wrap mode:"), + 0.0, 0.5, + opt, 1, FALSE); + + g_signal_connect (opt, "changed", + G_CALLBACK (string_value_combo_selected), + &dds_write_vals.mipmap_wrap); + + mipmap_wrap_opt = opt; + + check = gtk_check_button_new_with_mnemonic (_("Appl_y gamma correction")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), + dds_write_vals.gamma_correct && + dds_write_vals.mipmaps); + gtk_table_attach (GTK_TABLE (table), check, 1, 2, 2, 3, + GTK_FILL, 0, 0, 0); + gtk_widget_show (check); + + g_signal_connect (check, "clicked", + G_CALLBACK (gamma_correct_clicked), + NULL); + + gamma_chk = check; + + check = gtk_check_button_new_with_mnemonic (_("Use s_RGB colorspace")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), + dds_write_vals.gamma_correct && + dds_write_vals.srgb); + gtk_table_attach (GTK_TABLE (table), check, 1, 2, 3, 4, + GTK_FILL, 0, 0, 0); + gtk_widget_show (check); + + g_signal_connect (check, "clicked", + G_CALLBACK (srgb_clicked), + NULL); + + srgb_chk = check; + + spin = gimp_spin_button_new + (GTK_ADJUSTMENT (gtk_adjustment_new (dds_write_vals.gamma, + 1e-05, 100, 0.1, 0.5, 0)), 1, 1); + gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin), GTK_UPDATE_IF_VALID); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 4, + _("_Gamma:"), + 0.0, 0.5, + spin, 1, FALSE); + + g_signal_connect (spin, "value_changed", + G_CALLBACK (gamma_changed), + NULL); + + gamma_spin = spin; + + check = gtk_check_button_new_with_mnemonic (_("Preserve alpha _test coverage")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), + dds_write_vals.preserve_alpha_coverage && + dds_write_vals.mipmaps); + gtk_table_attach (GTK_TABLE (table), check, 1, 2, 5, 6, + GTK_FILL, 0, 0, 0); + gtk_widget_show (check); + + g_signal_connect (check, "clicked", + G_CALLBACK (alpha_coverage_clicked), + NULL); + + alpha_coverage_chk = check; + + spin = gimp_spin_button_new + (GTK_ADJUSTMENT (gtk_adjustment_new (dds_write_vals.alpha_test_threshold, + 0, 1, 0.01, 0.1, 0)), 1, 2); + gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin), GTK_UPDATE_IF_VALID); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 6, + _("_Alpha test threshold:"), + 0.0, 0.5, + spin, 1, FALSE); + + g_signal_connect (spin, "value_changed", + G_CALLBACK (alpha_test_threshold_changed), + NULL); + + alpha_test_threshold_spin = spin; + + gtk_widget_set_sensitive (mipmap_filter_opt, + dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE); + gtk_widget_set_sensitive (mipmap_wrap_opt, + dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE); + gtk_widget_set_sensitive (gamma_chk + , dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE); + gtk_widget_set_sensitive (srgb_chk, + (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) && + dds_write_vals.gamma_correct); + gtk_widget_set_sensitive (gamma_spin, + (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) && + dds_write_vals.gamma_correct && + !dds_write_vals.srgb); + gtk_widget_set_sensitive (pm_chk, + dds_write_vals.compression != DDS_COMPRESS_NONE); + gtk_widget_set_sensitive (alpha_coverage_chk, + dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE); + gtk_widget_set_sensitive (alpha_test_threshold_spin, + (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) && + dds_write_vals.preserve_alpha_coverage); + + gtk_widget_show (dlg); + + runme = FALSE; + + gtk_main (); + + return runme; +} diff --git a/plug-ins/file-dds/dxt.c b/plug-ins/file-dds/dxt.c new file mode 100644 index 0000000..28d407a --- /dev/null +++ b/plug-ins/file-dds/dxt.c @@ -0,0 +1,1526 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. + */ + +/* + * Parts of this code have been generously released in the public domain + * by Fabian 'ryg' Giesen. The original code can be found (at the time + * of writing) here: http://mollyrocket.com/forums/viewtopic.php?t=392 + * + * For more information about this code, see the README.dxt file that + * came with the source. + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <glib.h> + +#include "dds.h" +#include "dxt.h" +#include "endian_rw.h" +#include "mipmap.h" +#include "imath.h" +#include "vec.h" + +#include "dxt_tables.h" + +#define SWAP(a, b) do { typeof(a) t; t = a; a = b; b = t; } while(0) + +/* SIMD constants */ +static const vec4_t V4ZERO = VEC4_CONST1(0.0f); +static const vec4_t V4ONE = VEC4_CONST1(1.0f); +static const vec4_t V4HALF = VEC4_CONST1(0.5f); +static const vec4_t V4ONETHIRD = VEC4_CONST3(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f); +static const vec4_t V4TWOTHIRDS = VEC4_CONST3(2.0f / 3.0f, 2.0f / 3.0f, 2.0f / 3.0f); +static const vec4_t V4GRID = VEC4_CONST3(31.0f, 63.0f, 31.0f); +static const vec4_t V4GRIDRCP = VEC4_CONST3(1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f); +static const vec4_t V4EPSILON = VEC4_CONST1(1e-04f); + +typedef struct +{ + unsigned int single; + unsigned int alphamask; + vec4_t points[16]; + vec4_t palette[4]; + vec4_t max; + vec4_t min; + vec4_t metric; +} dxtblock_t; + +/* extract 4x4 BGRA block */ +static void +extract_block (const unsigned char *src, + int x, + int y, + int w, + int h, + unsigned char *block) +{ + int i, j; + int bw = MIN(w - x, 4); + int bh = MIN(h - y, 4); + int bx, by; + const int rem[] = + { + 0, 0, 0, 0, + 0, 1, 0, 1, + 0, 1, 2, 0, + 0, 1, 2, 3 + }; + + for (i = 0; i < 4; ++i) + { + by = rem[(bh - 1) * 4 + i] + y; + for (j = 0; j < 4; ++j) + { + bx = rem[(bw - 1) * 4 + j] + x; + block[(i * 4 * 4) + (j * 4) + 0] = + src[(by * (w * 4)) + (bx * 4) + 0]; + block[(i * 4 * 4) + (j * 4) + 1] = + src[(by * (w * 4)) + (bx * 4) + 1]; + block[(i * 4 * 4) + (j * 4) + 2] = + src[(by * (w * 4)) + (bx * 4) + 2]; + block[(i * 4 * 4) + (j * 4) + 3] = + src[(by * (w * 4)) + (bx * 4) + 3]; + } + } +} + +/* pack BGR8 to RGB565 */ +static inline unsigned short +pack_rgb565 (const unsigned char *c) +{ + return (mul8bit(c[2], 31) << 11) | + (mul8bit(c[1], 63) << 5) | + (mul8bit(c[0], 31) ); +} + +/* unpack RGB565 to BGR */ +static void +unpack_rgb565 (unsigned char *dst, + unsigned short v) +{ + int r = (v >> 11) & 0x1f; + int g = (v >> 5) & 0x3f; + int b = (v ) & 0x1f; + + dst[0] = (b << 3) | (b >> 2); + dst[1] = (g << 2) | (g >> 4); + dst[2] = (r << 3) | (r >> 2); +} + +/* linear interpolation at 1/3 point between a and b */ +static void +lerp_rgb13 (unsigned char *dst, + unsigned char *a, + unsigned char *b) +{ +#if 0 + dst[0] = blerp(a[0], b[0], 0x55); + dst[1] = blerp(a[1], b[1], 0x55); + dst[2] = blerp(a[2], b[2], 0x55); +#else + /* + * according to the S3TC/DX10 specs, this is the correct way to do the + * interpolation (with no rounding bias) + * + * dst = (2 * a + b) / 3; + */ + dst[0] = (2 * a[0] + b[0]) / 3; + dst[1] = (2 * a[1] + b[1]) / 3; + dst[2] = (2 * a[2] + b[2]) / 3; +#endif +} + +static void +vec4_endpoints_to_565 (int *start, + int *end, + const vec4_t a, + const vec4_t b) +{ + int c[8] __attribute__((aligned(16))); + vec4_t ta = a * V4GRID + V4HALF; + vec4_t tb = b * V4GRID + V4HALF; + +#ifdef USE_SSE +# ifdef __SSE2__ + const __m128i C565 = _mm_setr_epi16(31, 63, 31, 0, 31, 63, 31, 0); + __m128i ia = _mm_cvttps_epi32(ta); + __m128i ib = _mm_cvttps_epi32(tb); + __m128i zero = _mm_setzero_si128(); + __m128i words = _mm_packs_epi32(ia, ib); + words = _mm_min_epi16(C565, _mm_max_epi16(zero, words)); + *((__m128i *)&c[0]) = _mm_unpacklo_epi16(words, zero); + *((__m128i *)&c[4]) = _mm_unpackhi_epi16(words, zero); +# else + const __m64 C565 = _mm_setr_pi16(31, 63, 31, 0); + __m64 lo, hi, c0, c1; + __m64 zero = _mm_setzero_si64(); + lo = _mm_cvttps_pi32(ta); + hi = _mm_cvttps_pi32(_mm_movehl_ps(ta, ta)); + c0 = _mm_packs_pi32(lo, hi); + lo = _mm_cvttps_pi32(tb); + hi = _mm_cvttps_pi32(_mm_movehl_ps(tb, tb)); + c1 = _mm_packs_pi32(lo, hi); + c0 = _mm_min_pi16(C565, _mm_max_pi16(zero, c0)); + c1 = _mm_min_pi16(C565, _mm_max_pi16(zero, c1)); + *((__m64 *)&c[0]) = _mm_unpacklo_pi16(c0, zero); + *((__m64 *)&c[2]) = _mm_unpackhi_pi16(c0, zero); + *((__m64 *)&c[4]) = _mm_unpacklo_pi16(c1, zero); + *((__m64 *)&c[6]) = _mm_unpackhi_pi16(c1, zero); + _mm_empty(); +# endif +#else + c[0] = (int)ta[0]; c[4] = (int)tb[0]; + c[1] = (int)ta[1]; c[5] = (int)tb[1]; + c[2] = (int)ta[2]; c[6] = (int)tb[2]; + c[0] = MIN(31, MAX(0, c[0])); + c[1] = MIN(63, MAX(0, c[1])); + c[2] = MIN(31, MAX(0, c[2])); + c[4] = MIN(31, MAX(0, c[4])); + c[5] = MIN(63, MAX(0, c[5])); + c[6] = MIN(31, MAX(0, c[6])); +#endif + + *start = ((c[2] << 11) | (c[1] << 5) | c[0]); + *end = ((c[6] << 11) | (c[5] << 5) | c[4]); +} + +static void +dxtblock_init (dxtblock_t *dxtb, + const unsigned char *block, + int flags) +{ + int i, c0, c; + int bc1 = (flags & DXT_BC1); + float x, y, z; + vec4_t min, max, center, t, cov, inset; + + dxtb->single = 1; + dxtb->alphamask = 0; + + if(flags & DXT_PERCEPTUAL) + /* ITU-R BT.709 luma coefficients */ + dxtb->metric = vec4_set(0.2126f, 0.7152f, 0.0722f, 0.0f); + else + dxtb->metric = vec4_set(1.0f, 1.0f, 1.0f, 0.0f); + + c0 = GETL24(block); + + for (i = 0; i < 16; ++i) + { + if (bc1 && (block[4 * i + 3] < 128)) + dxtb->alphamask |= (3 << (2 * i)); + + x = (float)block[4 * i + 0] / 255.0f; + y = (float)block[4 * i + 1] / 255.0f; + z = (float)block[4 * i + 2] / 255.0f; + + dxtb->points[i] = vec4_set(x, y, z, 0); + + c = GETL24(&block[4 * i]); + dxtb->single = dxtb->single && (c == c0); + } + + // no need to continue if this is a single color block + if (dxtb->single) + return; + + min = vec4_set1(1.0f); + max = vec4_zero(); + + // get bounding box extents + for (i = 0; i < 16; ++i) + { + min = vec4_min(min, dxtb->points[i]); + max = vec4_max(max, dxtb->points[i]); + } + + // select diagonal + center = (max + min) * V4HALF; + cov = vec4_zero(); + for (i = 0; i < 16; ++i) + { + t = dxtb->points[i] - center; + cov += t * vec4_splatz(t); + } + +#ifdef USE_SSE + { + __m128 mask, tmp; + // get mask + mask = _mm_cmplt_ps(cov, _mm_setzero_ps()); + // clear high bits (z, w) + mask = _mm_movelh_ps(mask, _mm_setzero_ps()); + // mask and combine + tmp = _mm_or_ps(_mm_and_ps(mask, min), _mm_andnot_ps(mask, max)); + min = _mm_or_ps(_mm_and_ps(mask, max), _mm_andnot_ps(mask, min)); + max = tmp; + } +#else + { + float x0, x1, y0, y1; + x0 = max[0]; + y0 = max[1]; + x1 = min[0]; + y1 = min[1]; + + if (cov[0] < 0) SWAP(x0, x1); + if (cov[1] < 0) SWAP(y0, y1); + + max[0] = x0; + max[1] = y0; + min[0] = x1; + min[1] = y1; + } +#endif + + // inset bounding box and clamp to [0,1] + inset = (max - min) * vec4_set1(1.0f / 16.0f) - vec4_set1((8.0f / 255.0f) / 16.0f); + max = vec4_min(V4ONE, vec4_max(V4ZERO, max - inset)); + min = vec4_min(V4ONE, vec4_max(V4ZERO, min + inset)); + + // clamp to color space and save + dxtb->max = vec4_trunc(V4GRID * max + V4HALF) * V4GRIDRCP; + dxtb->min = vec4_trunc(V4GRID * min + V4HALF) * V4GRIDRCP; +} + +static void +construct_palette3 (dxtblock_t *dxtb) +{ + dxtb->palette[0] = dxtb->max; + dxtb->palette[1] = dxtb->min; + dxtb->palette[2] = (dxtb->max * V4HALF) + (dxtb->min * V4HALF); + dxtb->palette[3] = vec4_zero(); +} + +static void +construct_palette4 (dxtblock_t *dxtb) +{ + dxtb->palette[0] = dxtb->max; + dxtb->palette[1] = dxtb->min; + dxtb->palette[2] = (dxtb->max * V4TWOTHIRDS) + (dxtb->min * V4ONETHIRD ); + dxtb->palette[3] = (dxtb->max * V4ONETHIRD ) + (dxtb->min * V4TWOTHIRDS); +} + +/* + * from nvidia-texture-tools; see LICENSE.nvtt for copyright information + */ +static void +optimize_endpoints3 (dxtblock_t *dxtb, + unsigned int indices, + vec4_t *max, + vec4_t *min) +{ + float alpha, beta; + vec4_t alpha2_sum, alphax_sum; + vec4_t beta2_sum, betax_sum; + vec4_t alphabeta_sum, a, b, factor; + int i, bits; + + alpha2_sum = beta2_sum = alphabeta_sum = vec4_zero(); + alphax_sum = vec4_zero(); + betax_sum = vec4_zero(); + + for (i = 0; i < 16; ++i) + { + bits = indices >> (2 * i); + + // skip alpha pixels + if ((bits & 3) == 3) + continue; + + beta = (float)(bits & 1); + if (bits & 2) + beta = 0.5f; + alpha = 1.0f - beta; + + a = vec4_set1(alpha); + b = vec4_set1(beta); + alpha2_sum += a * a; + beta2_sum += b * b; + alphabeta_sum += a * b; + alphax_sum += dxtb->points[i] * a; + betax_sum += dxtb->points[i] * b; + } + + factor = alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum; + if (vec4_cmplt(factor, V4EPSILON)) + return; + factor = vec4_rcp(factor); + + a = (alphax_sum * beta2_sum - betax_sum * alphabeta_sum) * factor; + b = (betax_sum * alpha2_sum - alphax_sum * alphabeta_sum) * factor; + + // clamp to the color space + a = vec4_min(V4ONE, vec4_max(V4ZERO, a)); + b = vec4_min(V4ONE, vec4_max(V4ZERO, b)); + a = vec4_trunc(V4GRID * a + V4HALF) * V4GRIDRCP; + b = vec4_trunc(V4GRID * b + V4HALF) * V4GRIDRCP; + + *max = a; + *min = b; +} + +/* + * from nvidia-texture-tools; see LICENSE.nvtt for copyright information + */ +static void +optimize_endpoints4 (dxtblock_t *dxtb, + unsigned int indices, + vec4_t *max, + vec4_t *min) +{ + float alpha, beta; + vec4_t alpha2_sum, alphax_sum; + vec4_t beta2_sum, betax_sum; + vec4_t alphabeta_sum, a, b, factor; + int i, bits; + + alpha2_sum = beta2_sum = alphabeta_sum = vec4_zero(); + alphax_sum = vec4_zero(); + betax_sum = vec4_zero(); + + for (i = 0; i < 16; ++i) + { + bits = indices >> (2 * i); + + beta = (float)(bits & 1); + if (bits & 2) + beta = (1.0f + beta) / 3.0f; + alpha = 1.0f - beta; + + a = vec4_set1(alpha); + b = vec4_set1(beta); + alpha2_sum += a * a; + beta2_sum += b * b; + alphabeta_sum += a * b; + alphax_sum += dxtb->points[i] * a; + betax_sum += dxtb->points[i] * b; + } + + factor = alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum; + if (vec4_cmplt(factor, V4EPSILON)) + return; + factor = vec4_rcp(factor); + + a = (alphax_sum * beta2_sum - betax_sum * alphabeta_sum) * factor; + b = (betax_sum * alpha2_sum - alphax_sum * alphabeta_sum) * factor; + + // clamp to the color space + a = vec4_min(V4ONE, vec4_max(V4ZERO, a)); + b = vec4_min(V4ONE, vec4_max(V4ZERO, b)); + a = vec4_trunc(V4GRID * a + V4HALF) * V4GRIDRCP; + b = vec4_trunc(V4GRID * b + V4HALF) * V4GRIDRCP; + + *max = a; + *min = b; +} + +static unsigned int +match_colors3 (dxtblock_t *dxtb) +{ + int i, idx; + unsigned int indices = 0; + vec4_t t0, t1, t2; +#ifdef USE_SSE + vec4_t d, bits, zero = _mm_setzero_ps(); + int mask; +#else + float d0, d1, d2; +#endif + + // match each point to the closest color + for (i = 0; i < 16; ++i) + { + // skip alpha pixels + if (((dxtb->alphamask >> (2 * i)) & 3) == 3) + { + indices |= (3 << (2 * i)); + continue; + } + + t0 = (dxtb->points[i] - dxtb->palette[0]) * dxtb->metric; + t1 = (dxtb->points[i] - dxtb->palette[1]) * dxtb->metric; + t2 = (dxtb->points[i] - dxtb->palette[2]) * dxtb->metric; + +#ifdef USE_SSE + _MM_TRANSPOSE4_PS(t0, t1, t2, zero); + d = t0 * t0 + t1 * t1 + t2 * t2; + bits = _mm_cmplt_ps(_mm_shuffle_ps(d, d, _MM_SHUFFLE(3, 1, 0, 0)), + _mm_shuffle_ps(d, d, _MM_SHUFFLE(3, 2, 2, 1))); + mask = _mm_movemask_ps(bits); + if((mask & 3) == 3) idx = 0; + else if(mask & 4) idx = 1; + else idx = 2; +#else + d0 = vec4_dot(t0, t0); + d1 = vec4_dot(t1, t1); + d2 = vec4_dot(t2, t2); + + if ((d0 < d1) && (d0 < d2)) + idx = 0; + else if (d1 < d2) + idx = 1; + else + idx = 2; +#endif + + indices |= (idx << (2 * i)); + } + + return indices; +} + +static unsigned int +match_colors4 (dxtblock_t *dxtb) +{ + int i; + unsigned int idx, indices = 0; + unsigned int b0, b1, b2, b3, b4; + unsigned int x0, x1, x2; + vec4_t t0, t1, t2, t3; +#ifdef USE_SSE + vec4_t d; +#else + float d[4]; +#endif + + // match each point to the closest color + for (i = 0; i < 16; ++i) + { + t0 = (dxtb->points[i] - dxtb->palette[0]) * dxtb->metric; + t1 = (dxtb->points[i] - dxtb->palette[1]) * dxtb->metric; + t2 = (dxtb->points[i] - dxtb->palette[2]) * dxtb->metric; + t3 = (dxtb->points[i] - dxtb->palette[3]) * dxtb->metric; + +#ifdef USE_SSE + _MM_TRANSPOSE4_PS(t0, t1, t2, t3); + d = t0 * t0 + t1 * t1 + t2 * t2; +#else + d[0] = vec4_dot(t0, t0); + d[1] = vec4_dot(t1, t1); + d[2] = vec4_dot(t2, t2); + d[3] = vec4_dot(t3, t3); +#endif + + b0 = d[0] > d[3]; + b1 = d[1] > d[2]; + b2 = d[0] > d[2]; + b3 = d[1] > d[3]; + b4 = d[2] > d[3]; + + x0 = b1 & b2; + x1 = b0 & b3; + x2 = b0 & b4; + + idx = x2 | ((x0 | x1) << 1); + + indices |= (idx << (2 * i)); + } + + return indices; +} + +static float +compute_error3 (dxtblock_t *dxtb, + unsigned int indices) +{ + int i, idx; + float error = 0; + vec4_t t; + + // compute error + for (i = 0; i < 16; ++i) + { + idx = (indices >> (2 * i)) & 3; + // skip alpha pixels + if(idx == 3) + continue; + t = (dxtb->points[i] - dxtb->palette[idx]) * dxtb->metric; + error += vec4_dot(t, t); + } + + return error; +} + +static float +compute_error4 (dxtblock_t *dxtb, + unsigned int indices) +{ + int i, idx; + float error = 0; + +#ifdef USE_SSE + vec4_t a0, a1, a2, a3; + vec4_t b0, b1, b2, b3; + vec4_t d; + + for (i = 0; i < 4; ++i) + { + idx = indices >> (8 * i); + a0 = dxtb->points[4 * i + 0]; + a1 = dxtb->points[4 * i + 1]; + a2 = dxtb->points[4 * i + 2]; + a3 = dxtb->points[4 * i + 3]; + b0 = dxtb->palette[(idx ) & 3]; + b1 = dxtb->palette[(idx >> 2) & 3]; + b2 = dxtb->palette[(idx >> 4) & 3]; + b3 = dxtb->palette[(idx >> 6) & 3]; + a0 = (a0 - b0) * dxtb->metric; + a1 = (a1 - b1) * dxtb->metric; + a2 = (a2 - b2) * dxtb->metric; + a3 = (a3 - b3) * dxtb->metric; + _MM_TRANSPOSE4_PS(a0, a1, a2, a3); + d = a0 * a0 + a1 * a1 + a2 * a2; + error += vec4_accum(d); + } +#else + vec4_t t; + + // compute error + for (i = 0; i < 16; ++i) + { + idx = (indices >> (2 * i)) & 3; + t = (dxtb->points[i] - dxtb->palette[idx]) * dxtb->metric; + error += vec4_dot(t, t); + } +#endif + + return error; +} + +static unsigned int +compress3 (dxtblock_t *dxtb) +{ + const int MAX_ITERATIONS = 8; + int i; + unsigned int indices, bestindices; + float error, besterror = FLT_MAX; + vec4_t oldmax, oldmin; + + construct_palette3(dxtb); + + indices = match_colors3(dxtb); + bestindices = indices; + + for (i = 0; i < MAX_ITERATIONS; ++i) + { + oldmax = dxtb->max; + oldmin = dxtb->min; + + optimize_endpoints3(dxtb, indices, &dxtb->max, &dxtb->min); + construct_palette3(dxtb); + indices = match_colors3(dxtb); + error = compute_error3(dxtb, indices); + + if (error < besterror) + { + besterror = error; + bestindices = indices; + } + else + { + dxtb->max = oldmax; + dxtb->min = oldmin; + break; + } + } + + return bestindices; +} + +static unsigned int +compress4 (dxtblock_t *dxtb) +{ + const int MAX_ITERATIONS = 8; + int i; + unsigned int indices, bestindices; + float error, besterror = FLT_MAX; + vec4_t oldmax, oldmin; + + construct_palette4(dxtb); + + indices = match_colors4(dxtb); + bestindices = indices; + + for (i = 0; i < MAX_ITERATIONS; ++i) + { + oldmax = dxtb->max; + oldmin = dxtb->min; + + optimize_endpoints4(dxtb, indices, &dxtb->max, &dxtb->min); + construct_palette4(dxtb); + indices = match_colors4(dxtb); + error = compute_error4(dxtb, indices); + + if (error < besterror) + { + besterror = error; + bestindices = indices; + } + else + { + dxtb->max = oldmax; + dxtb->min = oldmin; + break; + } + } + + return bestindices; +} + +static void +encode_color_block (unsigned char *dst, + unsigned char *block, + int flags) +{ + dxtblock_t dxtb; + int max16, min16; + unsigned int indices, mask; + + dxtblock_init(&dxtb, block, flags); + + if (dxtb.single) // single color block + { + max16 = (omatch5[block[2]][0] << 11) | + (omatch6[block[1]][0] << 5) | + (omatch5[block[0]][0] ); + min16 = (omatch5[block[2]][1] << 11) | + (omatch6[block[1]][1] << 5) | + (omatch5[block[0]][1] ); + + indices = 0xaaaaaaaa; // 101010... + + if ((flags & DXT_BC1) && dxtb.alphamask) + { + // DXT1 compression, non-opaque block. Add alpha indices. + indices |= dxtb.alphamask; + if (max16 > min16) + SWAP(max16, min16); + } + else if (max16 < min16) + { + SWAP(max16, min16); + indices ^= 0x55555555; // 010101... + } + } + else if ((flags & DXT_BC1) && dxtb.alphamask) // DXT1 compression, non-opaque block + { + indices = compress3(&dxtb); + + vec4_endpoints_to_565(&max16, &min16, dxtb.max, dxtb.min); + + if (max16 > min16) + { + SWAP(max16, min16); + // remap indices 0 -> 1, 1 -> 0 + mask = indices & 0xaaaaaaaa; + mask = mask | (mask >> 1); + indices = (indices & mask) | ((indices ^ 0x55555555) & ~mask); + } + } + else + { + indices = compress4(&dxtb); + + vec4_endpoints_to_565(&max16, &min16, dxtb.max, dxtb.min); + + if (max16 < min16) + { + SWAP(max16, min16); + indices ^= 0x55555555; // 010101... + } + } + + PUTL16(dst + 0, max16); + PUTL16(dst + 2, min16); + PUTL32(dst + 4, indices); +} + +static void +get_min_max_YCoCg (const unsigned char *block, + unsigned char *mincolor, + unsigned char *maxcolor) +{ + int i; + + mincolor[2] = mincolor[1] = 255; + maxcolor[2] = maxcolor[1] = 0; + + for (i = 0; i < 16; ++i) + { + if (block[4 * i + 2] < mincolor[2]) mincolor[2] = block[4 * i + 2]; + if (block[4 * i + 1] < mincolor[1]) mincolor[1] = block[4 * i + 1]; + if (block[4 * i + 2] > maxcolor[2]) maxcolor[2] = block[4 * i + 2]; + if (block[4 * i + 1] > maxcolor[1]) maxcolor[1] = block[4 * i + 1]; + } +} + +static void +scale_YCoCg (unsigned char *block, + unsigned char *mincolor, + unsigned char *maxcolor) +{ + const int s0 = 128 / 2 - 1; + const int s1 = 128 / 4 - 1; + int m0, m1, m2, m3; + int mask0, mask1, scale; + int i; + + m0 = abs(mincolor[2] - 128); + m1 = abs(mincolor[1] - 128); + m2 = abs(maxcolor[2] - 128); + m3 = abs(maxcolor[1] - 128); + + if (m1 > m0) m0 = m1; + if (m3 > m2) m2 = m3; + if (m2 > m0) m0 = m2; + + mask0 = -(m0 <= s0); + mask1 = -(m0 <= s1); + scale = 1 + (1 & mask0) + (2 & mask1); + + mincolor[2] = (mincolor[2] - 128) * scale + 128; + mincolor[1] = (mincolor[1] - 128) * scale + 128; + mincolor[0] = (scale - 1) << 3; + + maxcolor[2] = (maxcolor[2] - 128) * scale + 128; + maxcolor[1] = (maxcolor[1] - 128) * scale + 128; + maxcolor[0] = (scale - 1) << 3; + + for (i = 0; i < 16; ++i) + { + block[i * 4 + 2] = (block[i * 4 + 2] - 128) * scale + 128; + block[i * 4 + 1] = (block[i * 4 + 1] - 128) * scale + 128; + } +} + +#define INSET_SHIFT 4 + +static void +inset_bbox_YCoCg (unsigned char *mincolor, + unsigned char *maxcolor) +{ + int inset[4], mini[4], maxi[4]; + + inset[2] = (maxcolor[2] - mincolor[2]) - ((1 << (INSET_SHIFT - 1)) - 1); + inset[1] = (maxcolor[1] - mincolor[1]) - ((1 << (INSET_SHIFT - 1)) - 1); + + mini[2] = ((mincolor[2] << INSET_SHIFT) + inset[2]) >> INSET_SHIFT; + mini[1] = ((mincolor[1] << INSET_SHIFT) + inset[1]) >> INSET_SHIFT; + + maxi[2] = ((maxcolor[2] << INSET_SHIFT) - inset[2]) >> INSET_SHIFT; + maxi[1] = ((maxcolor[1] << INSET_SHIFT) - inset[1]) >> INSET_SHIFT; + + mini[2] = (mini[2] >= 0) ? mini[2] : 0; + mini[1] = (mini[1] >= 0) ? mini[1] : 0; + + maxi[2] = (maxi[2] <= 255) ? maxi[2] : 255; + maxi[1] = (maxi[1] <= 255) ? maxi[1] : 255; + + mincolor[2] = (mini[2] & 0xf8) | (mini[2] >> 5); + mincolor[1] = (mini[1] & 0xfc) | (mini[1] >> 6); + + maxcolor[2] = (maxi[2] & 0xf8) | (maxi[2] >> 5); + maxcolor[1] = (maxi[1] & 0xfc) | (maxi[1] >> 6); +} + +static void +select_diagonal_YCoCg (const unsigned char *block, + unsigned char *mincolor, + unsigned char *maxcolor) +{ + unsigned char mid0, mid1, side, mask, b0, b1, c0, c1; + int i; + + mid0 = ((int)mincolor[2] + maxcolor[2] + 1) >> 1; + mid1 = ((int)mincolor[1] + maxcolor[1] + 1) >> 1; + + side = 0; + for (i = 0; i < 16; ++i) + { + b0 = block[i * 4 + 2] >= mid0; + b1 = block[i * 4 + 1] >= mid1; + side += (b0 ^ b1); + } + + mask = -(side > 8); + mask &= -(mincolor[2] != maxcolor[2]); + + c0 = mincolor[1]; + c1 = maxcolor[1]; + + c0 ^= c1; + c1 ^= c0 & mask; + c0 ^= c1; + + mincolor[1] = c0; + maxcolor[1] = c1; +} + +static void +encode_YCoCg_block (unsigned char *dst, + unsigned char *block) +{ + unsigned char colors[4][3], *maxcolor, *mincolor; + unsigned int mask; + int c0, c1, d0, d1, d2, d3; + int b0, b1, b2, b3, b4; + int x0, x1, x2; + int i, idx; + + maxcolor = &colors[0][0]; + mincolor = &colors[1][0]; + + get_min_max_YCoCg(block, mincolor, maxcolor); + scale_YCoCg(block, mincolor, maxcolor); + inset_bbox_YCoCg(mincolor, maxcolor); + select_diagonal_YCoCg(block, mincolor, maxcolor); + + lerp_rgb13(&colors[2][0], maxcolor, mincolor); + lerp_rgb13(&colors[3][0], mincolor, maxcolor); + + mask = 0; + + for (i = 0; i < 16; ++i) + { + c0 = block[4 * i + 2]; + c1 = block[4 * i + 1]; + + d0 = abs(colors[0][2] - c0) + abs(colors[0][1] - c1); + d1 = abs(colors[1][2] - c0) + abs(colors[1][1] - c1); + d2 = abs(colors[2][2] - c0) + abs(colors[2][1] - c1); + d3 = abs(colors[3][2] - c0) + abs(colors[3][1] - c1); + + b0 = d0 > d3; + b1 = d1 > d2; + b2 = d0 > d2; + b3 = d1 > d3; + b4 = d2 > d3; + + x0 = b1 & b2; + x1 = b0 & b3; + x2 = b0 & b4; + + idx = (x2 | ((x0 | x1) << 1)); + + mask |= idx << (2 * i); + } + + PUTL16(dst + 0, pack_rgb565(maxcolor)); + PUTL16(dst + 2, pack_rgb565(mincolor)); + PUTL32(dst + 4, mask); +} + +/* write DXT3 alpha block */ +static void +encode_alpha_block_BC2 (unsigned char *dst, + const unsigned char *block) +{ + int i, a1, a2; + + block += 3; + + for (i = 0; i < 8; ++i) + { + a1 = mul8bit(block[8 * i + 0], 0x0f); + a2 = mul8bit(block[8 * i + 4], 0x0f); + *dst++ = (a2 << 4) | a1; + } +} + +/* Write DXT5 alpha block */ +static void +encode_alpha_block_BC3 (unsigned char *dst, + const unsigned char *block, + const int offset) +{ + int i, v, mn, mx; + int dist, bias, dist2, dist4, bits, mask; + int a, idx, t; + + block += offset; + block += 3; + + /* find min/max alpha pair */ + mn = mx = block[0]; + for (i = 0; i < 16; ++i) + { + v = block[4 * i]; + if(v > mx) mx = v; + if(v < mn) mn = v; + } + + /* encode them */ + *dst++ = mx; + *dst++ = mn; + + /* + * determine bias and emit indices + * given the choice of mx/mn, these indices are optimal: + * http://fgiesen.wordpress.com/2009/12/15/dxt5-alpha-block-index-determination/ + */ + dist = mx - mn; + dist4 = dist * 4; + dist2 = dist * 2; + bias = (dist < 8) ? (dist - 1) : (dist / 2 + 2); + bias -= mn * 7; + bits = 0; + mask = 0; + + for (i = 0; i < 16; ++i) + { + a = block[4 * i] * 7 + bias; + + /* select index. this is a "linear scale" lerp factor between 0 + (val=min) and 7 (val=max). */ + t = (a >= dist4) ? -1 : 0; idx = t & 4; a -= dist4 & t; + t = (a >= dist2) ? -1 : 0; idx += t & 2; a -= dist2 & t; + idx += (a >= dist); + + /* turn linear scale into DXT index (0/1 are extremal pts) */ + idx = -idx & 7; + idx ^= (2 > idx); + + /* write index */ + mask |= idx << bits; + if ((bits += 3) >= 8) + { + *dst++ = mask; + mask >>= 8; + bits -= 8; + } + } +} + +#define BLOCK_COUNT(w, h) ((((h) + 3) >> 2) * (((w) + 3) >> 2)) +#define BLOCK_OFFSET(x, y, w, bs) (((y) >> 2) * ((bs) * (((w) + 3) >> 2)) + ((bs) * ((x) >> 2))) + +static void +compress_BC1 (unsigned char *dst, + const unsigned char *src, + int w, + int h, + int flags) +{ + const unsigned int block_count = BLOCK_COUNT(w, h); + unsigned int i; + unsigned char block[64], *p; + int x, y; + +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y) +#endif + for (i = 0; i < block_count; ++i) + { + x = (i % ((w + 3) >> 2)) << 2; + y = (i / ((w + 3) >> 2)) << 2; + p = dst + BLOCK_OFFSET(x, y, w, 8); + extract_block(src, x, y, w, h, block); + encode_color_block(p, block, DXT_BC1 | flags); + } +} + +static void +compress_BC2 (unsigned char *dst, + const unsigned char *src, + int w, + int h, + int flags) +{ + const unsigned int block_count = BLOCK_COUNT(w, h); + unsigned int i; + unsigned char block[64], *p; + int x, y; + +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y) +#endif + for (i = 0; i < block_count; ++i) + { + x = (i % ((w + 3) >> 2)) << 2; + y = (i / ((w + 3) >> 2)) << 2; + p = dst + BLOCK_OFFSET(x, y, w, 16); + extract_block(src, x, y, w, h, block); + encode_alpha_block_BC2(p, block); + encode_color_block(p + 8, block, DXT_BC2 | flags); + } +} + +static void +compress_BC3 (unsigned char *dst, + const unsigned char *src, + int w, + int h, + int flags) +{ + const unsigned int block_count = BLOCK_COUNT(w, h); + unsigned int i; + unsigned char block[64], *p; + int x, y; + +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y) +#endif + for (i = 0; i < block_count; ++i) + { + x = (i % ((w + 3) >> 2)) << 2; + y = (i / ((w + 3) >> 2)) << 2; + p = dst + BLOCK_OFFSET(x, y, w, 16); + extract_block(src, x, y, w, h, block); + encode_alpha_block_BC3(p, block, 0); + encode_color_block(p + 8, block, DXT_BC3 | flags); + } +} + +static void +compress_BC4 (unsigned char *dst, + const unsigned char *src, + int w, + int h) +{ + const unsigned int block_count = BLOCK_COUNT(w, h); + unsigned int i; + unsigned char block[64], *p; + int x, y; + +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y) +#endif + for (i = 0; i < block_count; ++i) + { + x = (i % ((w + 3) >> 2)) << 2; + y = (i / ((w + 3) >> 2)) << 2; + p = dst + BLOCK_OFFSET(x, y, w, 8); + extract_block(src, x, y, w, h, block); + encode_alpha_block_BC3(p, block, -1); + } +} + +static void +compress_BC5 (unsigned char *dst, + const unsigned char *src, + int w, + int h) +{ + const unsigned int block_count = BLOCK_COUNT(w, h); + unsigned int i; + unsigned char block[64], *p; + int x, y; + +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y) +#endif + for (i = 0; i < block_count; ++i) + { + x = (i % ((w + 3) >> 2)) << 2; + y = (i / ((w + 3) >> 2)) << 2; + p = dst + BLOCK_OFFSET(x, y, w, 16); + extract_block(src, x, y, w, h, block); + /* Pixels are ordered as BGRA (see write_layer) + * First we encode red -1+3: channel 2; + * then we encode green -2+3: channel 1. + */ + encode_alpha_block_BC3(p, block, -1); + encode_alpha_block_BC3(p + 8, block, -2); + } +} + +static void +compress_YCoCg (unsigned char *dst, + const unsigned char *src, + int w, + int h) +{ + const unsigned int block_count = BLOCK_COUNT(w, h); + unsigned int i; + unsigned char block[64], *p; + int x, y; + +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y) +#endif + for (i = 0; i < block_count; ++i) + { + x = (i % ((w + 3) >> 2)) << 2; + y = (i / ((w + 3) >> 2)) << 2; + p = dst + BLOCK_OFFSET(x, y, w, 16); + extract_block(src, x, y, w, h, block); + encode_alpha_block_BC3(p, block, 0); + encode_YCoCg_block(p + 8, block); + } +} + +int +dxt_compress (unsigned char *dst, + unsigned char *src, + int format, + unsigned int width, + unsigned int height, + int bpp, + int mipmaps, + int flags) +{ + int i, size, w, h; + unsigned int offset; + unsigned char *tmp = NULL; + int j; + unsigned char *s; + + if (bpp == 1) + { + /* grayscale promoted to BGRA */ + + size = get_mipmapped_size(width, height, 4, 0, mipmaps, + DDS_COMPRESS_NONE); + tmp = g_malloc(size); + + for (i = j = 0; j < size; ++i, j += 4) + { + tmp[j + 0] = src[i]; + tmp[j + 1] = src[i]; + tmp[j + 2] = src[i]; + tmp[j + 3] = 255; + } + + bpp = 4; + } + else if (bpp == 2) + { + /* gray-alpha promoted to BGRA */ + + size = get_mipmapped_size(width, height, 4, 0, mipmaps, + DDS_COMPRESS_NONE); + tmp = g_malloc(size); + + for (i = j = 0; j < size; i += 2, j += 4) + { + tmp[j + 0] = src[i]; + tmp[j + 1] = src[i]; + tmp[j + 2] = src[i]; + tmp[j + 3] = src[i + 1]; + } + + bpp = 4; + } + else if (bpp == 3) + { + size = get_mipmapped_size(width, height, 4, 0, mipmaps, + DDS_COMPRESS_NONE); + tmp = g_malloc(size); + + for (i = j = 0; j < size; i += 3, j += 4) + { + tmp[j + 0] = src[i + 0]; + tmp[j + 1] = src[i + 1]; + tmp[j + 2] = src[i + 2]; + tmp[j + 3] = 255; + } + + bpp = 4; + } + + offset = 0; + w = width; + h = height; + s = tmp ? tmp : src; + + for (i = 0; i < mipmaps; ++i) + { + switch (format) + { + case DDS_COMPRESS_BC1: + compress_BC1(dst + offset, s, w, h, flags); + break; + case DDS_COMPRESS_BC2: + compress_BC2(dst + offset, s, w, h, flags); + break; + case DDS_COMPRESS_BC3: + case DDS_COMPRESS_BC3N: + case DDS_COMPRESS_RXGB: + case DDS_COMPRESS_AEXP: + case DDS_COMPRESS_YCOCG: + compress_BC3(dst + offset, s, w, h, flags); + break; + case DDS_COMPRESS_BC4: + compress_BC4(dst + offset, s, w, h); + break; + case DDS_COMPRESS_BC5: + compress_BC5(dst + offset, s, w, h); + break; + case DDS_COMPRESS_YCOCGS: + compress_YCoCg(dst + offset, s, w, h); + break; + default: + compress_BC3(dst + offset, s, w, h, flags); + break; + } + s += (w * h * bpp); + offset += get_mipmapped_size(w, h, 0, 0, 1, format); + w = MAX(1, w >> 1); + h = MAX(1, h >> 1); + } + + if (tmp) + g_free(tmp); + + return 1; +} + +static void +decode_color_block (unsigned char *block, + unsigned char *src, + int format) +{ + int i, x, y; + unsigned char *d = block; + unsigned int indices, idx; + unsigned char colors[4][3]; + unsigned short c0, c1; + + c0 = GETL16(&src[0]); + c1 = GETL16(&src[2]); + + unpack_rgb565(colors[0], c0); + unpack_rgb565(colors[1], c1); + + if ((c0 > c1) || (format == DDS_COMPRESS_BC3)) + { + lerp_rgb13(colors[2], colors[0], colors[1]); + lerp_rgb13(colors[3], colors[1], colors[0]); + } + else + { + for (i = 0; i < 3; ++i) + { + colors[2][i] = (colors[0][i] + colors[1][i] + 1) >> 1; + colors[3][i] = 255; + } + } + + src += 4; + for (y = 0; y < 4; ++y) + { + indices = src[y]; + for (x = 0; x < 4; ++x) + { + idx = indices & 0x03; + d[0] = colors[idx][2]; + d[1] = colors[idx][1]; + d[2] = colors[idx][0]; + if (format == DDS_COMPRESS_BC1) + d[3] = ((c0 <= c1) && idx == 3) ? 0 : 255; + indices >>= 2; + d += 4; + } + } +} + +static void +decode_alpha_block_BC2 (unsigned char *block, + unsigned char *src) +{ + int x, y; + unsigned char *d = block; + unsigned int bits; + + for (y = 0; y < 4; ++y) + { + bits = GETL16(&src[2 * y]); + for (x = 0; x < 4; ++x) + { + d[0] = (bits & 0x0f) * 17; + bits >>= 4; + d += 4; + } + } +} + +static void +decode_alpha_block_BC3 (unsigned char *block, + unsigned char *src, + int w) +{ + int x, y, code; + unsigned char *d = block; + unsigned char a0 = src[0]; + unsigned char a1 = src[1]; + unsigned long long bits = GETL64(src) >> 16; + + for (y = 0; y < 4; ++y) + { + for (x = 0; x < 4; ++x) + { + code = ((unsigned int)bits) & 0x07; + if (code == 0) + d[0] = a0; + else if (code == 1) + d[0] = a1; + else if (a0 > a1) + d[0] = ((8 - code) * a0 + (code - 1) * a1) / 7; + else if (code >= 6) + d[0] = (code == 6) ? 0 : 255; + else + d[0] = ((6 - code) * a0 + (code - 1) * a1) / 5; + bits >>= 3; + d += 4; + } + + if (w < 4) + bits >>= (3 * (4 - w)); + } +} + +static void +make_normal (unsigned char *dst, + unsigned char x, + unsigned char y) +{ + float nx = 2.0f * ((float)x / 255.0f) - 1.0f; + float ny = 2.0f * ((float)y / 255.0f) - 1.0f; + float nz = 0.0f; + float d = 1.0f - nx * nx + ny * ny; + int z; + + if (d > 0) + nz = sqrtf(d); + + z = (int)(255.0f * (nz + 1) / 2.0f); + z = MAX(0, MIN(255, z)); + + dst[0] = x; + dst[1] = y; + dst[2] = z; +} + +static void +normalize_block (unsigned char *block, + int format) +{ + int x, y, tmp; + + for (y = 0; y < 4; ++y) + { + for (x = 0; x < 4; ++x) + { + if (format == DDS_COMPRESS_BC3) + { + tmp = block[y * 16 + (x * 4)]; + make_normal(&block[y * 16 + (x * 4)], + block[y * 16 + (x * 4) + 3], + block[y * 16 + (x * 4) + 1]); + block[y * 16 + (x * 4) + 3] = tmp; + } + else if (format == DDS_COMPRESS_BC5) + { + make_normal(&block[y * 16 + (x * 4)], + block[y * 16 + (x * 4)], + block[y * 16 + (x * 4) + 1]); + } + } + } +} + +static void +put_block (unsigned char *dst, + unsigned char *block, + unsigned int bx, + unsigned int by, + unsigned int width, + unsigned height, + int bpp) +{ + int x, y, i; + unsigned char *d; + + for (y = 0; y < 4 && ((by + y) < height); ++y) + { + d = dst + ((y + by) * width + bx) * bpp; + for (x = 0; x < 4 && ((bx + x) < width); ++x) + { + for (i = 0; i < bpp; ++ i) + *d++ = block[y * 16 + (x * 4) + i]; + } + } +} + +int +dxt_decompress (unsigned char *dst, + unsigned char *src, + int format, + unsigned int size, + unsigned int width, + unsigned int height, + int bpp, + int normals) +{ + unsigned char *s; + unsigned int x, y; + unsigned char block[16 * 4]; + + s = src; + + for (y = 0; y < height; y += 4) + { + for (x = 0; x < width; x += 4) + { + memset(block, 0, 16 * 4); + + if (format == DDS_COMPRESS_BC1) + { + decode_color_block(block, s, format); + s += 8; + } + else if (format == DDS_COMPRESS_BC2) + { + decode_alpha_block_BC2(block + 3, s); + decode_color_block(block, s + 8, format); + s += 16; + } + else if (format == DDS_COMPRESS_BC3) + { + decode_alpha_block_BC3(block + 3, s, width); + decode_color_block(block, s + 8, format); + s += 16; + } + else if (format == DDS_COMPRESS_BC4) + { + decode_alpha_block_BC3(block, s, width); + s += 8; + } + else if (format == DDS_COMPRESS_BC5) + { + decode_alpha_block_BC3(block, s, width); + decode_alpha_block_BC3(block + 1, s + 8, width); + s += 16; + } + + if (normals) + normalize_block(block, format); + + put_block(dst, block, x, y, width, height, bpp); + } + } + + return 1; +} diff --git a/plug-ins/file-dds/dxt.h b/plug-ins/file-dds/dxt.h new file mode 100644 index 0000000..5364bbe --- /dev/null +++ b/plug-ins/file-dds/dxt.h @@ -0,0 +1,49 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef __DXT_H__ +#define __DXT_H__ + +typedef enum dxt_flags_e +{ + DXT_BC1 = 1 << 0, + DXT_BC2 = 1 << 1, + DXT_BC3 = 1 << 2, + DXT_PERCEPTUAL = 1 << 3, +} dxt_flags_t; + +int dxt_compress (unsigned char *dst, + unsigned char *src, + int format, + unsigned int width, + unsigned int height, + int bpp, + int mipmaps, + int flags); +int dxt_decompress (unsigned char *dst, + unsigned char *src, + int format, + unsigned int size, + unsigned int width, + unsigned int height, + int bpp, + int normals); + +#endif /* __DXT_H__ */ diff --git a/plug-ins/file-dds/dxt_tables.h b/plug-ins/file-dds/dxt_tables.h new file mode 100644 index 0000000..e30bb6a --- /dev/null +++ b/plug-ins/file-dds/dxt_tables.h @@ -0,0 +1,216 @@ +#ifndef __DXT_TABLES_H__ +#define __DXT_TABLES_H__ + +static const unsigned char quantRB[256 + 16] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x52, + 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x5a, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x6b, + 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, + 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, + 0xce, 0xce, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd6, 0xd6, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xef, 0xef, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char quantG[256 + 16] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x08, + 0x08, 0x08, 0x08, 0x0c, 0x0c, 0x0c, 0x0c, 0x10, + 0x10, 0x10, 0x10, 0x14, 0x14, 0x14, 0x14, 0x18, + 0x18, 0x18, 0x18, 0x1c, 0x1c, 0x1c, 0x1c, 0x20, + 0x20, 0x20, 0x20, 0x24, 0x24, 0x24, 0x24, 0x28, + 0x28, 0x28, 0x28, 0x2c, 0x2c, 0x2c, 0x2c, 0x30, + 0x30, 0x30, 0x30, 0x34, 0x34, 0x34, 0x34, 0x38, + 0x38, 0x38, 0x38, 0x3c, 0x3c, 0x3c, 0x3c, 0x41, + 0x41, 0x41, 0x41, 0x45, 0x45, 0x45, 0x45, 0x49, + 0x49, 0x49, 0x49, 0x4d, 0x4d, 0x4d, 0x4d, 0x51, + 0x51, 0x51, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x59, 0x59, 0x59, 0x59, 0x5d, 0x5d, 0x5d, 0x5d, + 0x61, 0x61, 0x61, 0x61, 0x65, 0x65, 0x65, 0x65, + 0x69, 0x69, 0x69, 0x69, 0x6d, 0x6d, 0x6d, 0x6d, + 0x71, 0x71, 0x71, 0x71, 0x75, 0x75, 0x75, 0x75, + 0x79, 0x79, 0x79, 0x79, 0x7d, 0x7d, 0x7d, 0x7d, + 0x82, 0x82, 0x82, 0x82, 0x86, 0x86, 0x86, 0x86, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8e, 0x8e, 0x8e, 0x8e, + 0x92, 0x92, 0x92, 0x92, 0x96, 0x96, 0x96, 0x96, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9e, 0x9e, 0x9e, 0x9e, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa6, 0xa6, 0xa6, 0xa6, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xae, 0xae, 0xae, + 0xae, 0xb2, 0xb2, 0xb2, 0xb2, 0xb6, 0xb6, 0xb6, + 0xb6, 0xba, 0xba, 0xba, 0xba, 0xbe, 0xbe, 0xbe, + 0xbe, 0xc3, 0xc3, 0xc3, 0xc3, 0xc7, 0xc7, 0xc7, + 0xc7, 0xcb, 0xcb, 0xcb, 0xcb, 0xcf, 0xcf, 0xcf, + 0xcf, 0xd3, 0xd3, 0xd3, 0xd3, 0xd7, 0xd7, 0xd7, + 0xd7, 0xdb, 0xdb, 0xdb, 0xdb, 0xdf, 0xdf, 0xdf, + 0xdf, 0xe3, 0xe3, 0xe3, 0xe3, 0xe7, 0xe7, 0xe7, + 0xe7, 0xeb, 0xeb, 0xeb, 0xeb, 0xef, 0xef, 0xef, + 0xef, 0xf3, 0xf3, 0xf3, 0xf3, 0xf7, 0xf7, 0xf7, + 0xf7, 0xfb, 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char omatch5[256][2] = +{ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x01}, {0x00, 0x01}, + {0x01, 0x00}, {0x01, 0x00}, {0x01, 0x00}, {0x01, 0x01}, + {0x01, 0x01}, {0x01, 0x01}, {0x01, 0x02}, {0x00, 0x04}, + {0x02, 0x01}, {0x02, 0x01}, {0x02, 0x01}, {0x02, 0x02}, + {0x02, 0x02}, {0x02, 0x02}, {0x02, 0x03}, {0x01, 0x05}, + {0x03, 0x02}, {0x03, 0x02}, {0x04, 0x00}, {0x03, 0x03}, + {0x03, 0x03}, {0x03, 0x03}, {0x03, 0x04}, {0x03, 0x04}, + {0x03, 0x04}, {0x03, 0x05}, {0x04, 0x03}, {0x04, 0x03}, + {0x05, 0x02}, {0x04, 0x04}, {0x04, 0x04}, {0x04, 0x05}, + {0x04, 0x05}, {0x05, 0x04}, {0x05, 0x04}, {0x05, 0x04}, + {0x06, 0x03}, {0x05, 0x05}, {0x05, 0x05}, {0x05, 0x06}, + {0x04, 0x08}, {0x06, 0x05}, {0x06, 0x05}, {0x06, 0x05}, + {0x06, 0x06}, {0x06, 0x06}, {0x06, 0x06}, {0x06, 0x07}, + {0x05, 0x09}, {0x07, 0x06}, {0x07, 0x06}, {0x08, 0x04}, + {0x07, 0x07}, {0x07, 0x07}, {0x07, 0x07}, {0x07, 0x08}, + {0x07, 0x08}, {0x07, 0x08}, {0x07, 0x09}, {0x08, 0x07}, + {0x08, 0x07}, {0x09, 0x06}, {0x08, 0x08}, {0x08, 0x08}, + {0x08, 0x09}, {0x08, 0x09}, {0x09, 0x08}, {0x09, 0x08}, + {0x09, 0x08}, {0x0a, 0x07}, {0x09, 0x09}, {0x09, 0x09}, + {0x09, 0x0a}, {0x08, 0x0c}, {0x0a, 0x09}, {0x0a, 0x09}, + {0x0a, 0x09}, {0x0a, 0x0a}, {0x0a, 0x0a}, {0x0a, 0x0a}, + {0x0a, 0x0b}, {0x09, 0x0d}, {0x0b, 0x0a}, {0x0b, 0x0a}, + {0x0c, 0x08}, {0x0b, 0x0b}, {0x0b, 0x0b}, {0x0b, 0x0b}, + {0x0b, 0x0c}, {0x0b, 0x0c}, {0x0b, 0x0c}, {0x0b, 0x0d}, + {0x0c, 0x0b}, {0x0c, 0x0b}, {0x0d, 0x0a}, {0x0c, 0x0c}, + {0x0c, 0x0c}, {0x0c, 0x0d}, {0x0c, 0x0d}, {0x0d, 0x0c}, + {0x0d, 0x0c}, {0x0d, 0x0c}, {0x0e, 0x0b}, {0x0d, 0x0d}, + {0x0d, 0x0d}, {0x0d, 0x0e}, {0x0c, 0x10}, {0x0e, 0x0d}, + {0x0e, 0x0d}, {0x0e, 0x0d}, {0x0e, 0x0e}, {0x0e, 0x0e}, + {0x0e, 0x0e}, {0x0e, 0x0f}, {0x0d, 0x11}, {0x0f, 0x0e}, + {0x0f, 0x0e}, {0x10, 0x0c}, {0x0f, 0x0f}, {0x0f, 0x0f}, + {0x0f, 0x0f}, {0x0f, 0x10}, {0x0f, 0x10}, {0x0f, 0x10}, + {0x0f, 0x11}, {0x10, 0x0f}, {0x10, 0x0f}, {0x11, 0x0e}, + {0x10, 0x10}, {0x10, 0x10}, {0x10, 0x11}, {0x10, 0x11}, + {0x11, 0x10}, {0x11, 0x10}, {0x11, 0x10}, {0x12, 0x0f}, + {0x11, 0x11}, {0x11, 0x11}, {0x11, 0x12}, {0x10, 0x14}, + {0x12, 0x11}, {0x12, 0x11}, {0x12, 0x11}, {0x12, 0x12}, + {0x12, 0x12}, {0x12, 0x12}, {0x12, 0x13}, {0x11, 0x15}, + {0x13, 0x12}, {0x13, 0x12}, {0x14, 0x10}, {0x13, 0x13}, + {0x13, 0x13}, {0x13, 0x13}, {0x13, 0x14}, {0x13, 0x14}, + {0x13, 0x14}, {0x13, 0x15}, {0x14, 0x13}, {0x14, 0x13}, + {0x15, 0x12}, {0x14, 0x14}, {0x14, 0x14}, {0x14, 0x15}, + {0x14, 0x15}, {0x15, 0x14}, {0x15, 0x14}, {0x15, 0x14}, + {0x16, 0x13}, {0x15, 0x15}, {0x15, 0x15}, {0x15, 0x16}, + {0x14, 0x18}, {0x16, 0x15}, {0x16, 0x15}, {0x16, 0x15}, + {0x16, 0x16}, {0x16, 0x16}, {0x16, 0x16}, {0x16, 0x17}, + {0x15, 0x19}, {0x17, 0x16}, {0x17, 0x16}, {0x18, 0x14}, + {0x17, 0x17}, {0x17, 0x17}, {0x17, 0x17}, {0x17, 0x18}, + {0x17, 0x18}, {0x17, 0x18}, {0x17, 0x19}, {0x18, 0x17}, + {0x18, 0x17}, {0x19, 0x16}, {0x18, 0x18}, {0x18, 0x18}, + {0x18, 0x19}, {0x18, 0x19}, {0x19, 0x18}, {0x19, 0x18}, + {0x19, 0x18}, {0x1a, 0x17}, {0x19, 0x19}, {0x19, 0x19}, + {0x19, 0x1a}, {0x18, 0x1c}, {0x1a, 0x19}, {0x1a, 0x19}, + {0x1a, 0x19}, {0x1a, 0x1a}, {0x1a, 0x1a}, {0x1a, 0x1a}, + {0x1a, 0x1b}, {0x19, 0x1d}, {0x1b, 0x1a}, {0x1b, 0x1a}, + {0x1c, 0x18}, {0x1b, 0x1b}, {0x1b, 0x1b}, {0x1b, 0x1b}, + {0x1b, 0x1c}, {0x1b, 0x1c}, {0x1b, 0x1c}, {0x1b, 0x1d}, + {0x1c, 0x1b}, {0x1c, 0x1b}, {0x1d, 0x1a}, {0x1c, 0x1c}, + {0x1c, 0x1c}, {0x1c, 0x1d}, {0x1c, 0x1d}, {0x1d, 0x1c}, + {0x1d, 0x1c}, {0x1d, 0x1c}, {0x1e, 0x1b}, {0x1d, 0x1d}, + {0x1d, 0x1d}, {0x1d, 0x1e}, {0x1d, 0x1e}, {0x1e, 0x1d}, + {0x1e, 0x1d}, {0x1e, 0x1d}, {0x1e, 0x1e}, {0x1e, 0x1e}, + {0x1e, 0x1e}, {0x1e, 0x1f}, {0x1e, 0x1f}, {0x1f, 0x1e}, + {0x1f, 0x1e}, {0x1f, 0x1e}, {0x1f, 0x1f}, {0x1f, 0x1f}, +}; + +static const unsigned char omatch6[256][2] = +{ + {0x00, 0x00}, {0x00, 0x01}, {0x01, 0x00}, {0x01, 0x01}, + {0x01, 0x01}, {0x01, 0x02}, {0x02, 0x01}, {0x02, 0x02}, + {0x02, 0x02}, {0x02, 0x03}, {0x03, 0x02}, {0x03, 0x03}, + {0x03, 0x03}, {0x03, 0x04}, {0x04, 0x03}, {0x04, 0x04}, + {0x04, 0x04}, {0x04, 0x05}, {0x05, 0x04}, {0x05, 0x05}, + {0x05, 0x05}, {0x05, 0x06}, {0x06, 0x05}, {0x00, 0x11}, + {0x06, 0x06}, {0x06, 0x07}, {0x07, 0x06}, {0x02, 0x10}, + {0x07, 0x07}, {0x07, 0x08}, {0x08, 0x07}, {0x03, 0x11}, + {0x08, 0x08}, {0x08, 0x09}, {0x09, 0x08}, {0x05, 0x10}, + {0x09, 0x09}, {0x09, 0x0a}, {0x0a, 0x09}, {0x06, 0x11}, + {0x0a, 0x0a}, {0x0a, 0x0b}, {0x0b, 0x0a}, {0x08, 0x10}, + {0x0b, 0x0b}, {0x0b, 0x0c}, {0x0c, 0x0b}, {0x09, 0x11}, + {0x0c, 0x0c}, {0x0c, 0x0d}, {0x0d, 0x0c}, {0x0b, 0x10}, + {0x0d, 0x0d}, {0x0d, 0x0e}, {0x0e, 0x0d}, {0x0c, 0x11}, + {0x0e, 0x0e}, {0x0e, 0x0f}, {0x0f, 0x0e}, {0x0e, 0x10}, + {0x0f, 0x0f}, {0x0f, 0x10}, {0x10, 0x0e}, {0x10, 0x0f}, + {0x11, 0x0e}, {0x10, 0x10}, {0x10, 0x11}, {0x11, 0x10}, + {0x12, 0x0f}, {0x11, 0x11}, {0x11, 0x12}, {0x12, 0x11}, + {0x14, 0x0e}, {0x12, 0x12}, {0x12, 0x13}, {0x13, 0x12}, + {0x15, 0x0f}, {0x13, 0x13}, {0x13, 0x14}, {0x14, 0x13}, + {0x17, 0x0e}, {0x14, 0x14}, {0x14, 0x15}, {0x15, 0x14}, + {0x18, 0x0f}, {0x15, 0x15}, {0x15, 0x16}, {0x16, 0x15}, + {0x1a, 0x0e}, {0x16, 0x16}, {0x16, 0x17}, {0x17, 0x16}, + {0x1b, 0x0f}, {0x17, 0x17}, {0x17, 0x18}, {0x18, 0x17}, + {0x13, 0x21}, {0x18, 0x18}, {0x18, 0x19}, {0x19, 0x18}, + {0x15, 0x20}, {0x19, 0x19}, {0x19, 0x1a}, {0x1a, 0x19}, + {0x16, 0x21}, {0x1a, 0x1a}, {0x1a, 0x1b}, {0x1b, 0x1a}, + {0x18, 0x20}, {0x1b, 0x1b}, {0x1b, 0x1c}, {0x1c, 0x1b}, + {0x19, 0x21}, {0x1c, 0x1c}, {0x1c, 0x1d}, {0x1d, 0x1c}, + {0x1b, 0x20}, {0x1d, 0x1d}, {0x1d, 0x1e}, {0x1e, 0x1d}, + {0x1c, 0x21}, {0x1e, 0x1e}, {0x1e, 0x1f}, {0x1f, 0x1e}, + {0x1e, 0x20}, {0x1f, 0x1f}, {0x1f, 0x20}, {0x20, 0x1e}, + {0x20, 0x1f}, {0x21, 0x1e}, {0x20, 0x20}, {0x20, 0x21}, + {0x21, 0x20}, {0x22, 0x1f}, {0x21, 0x21}, {0x21, 0x22}, + {0x22, 0x21}, {0x24, 0x1e}, {0x22, 0x22}, {0x22, 0x23}, + {0x23, 0x22}, {0x25, 0x1f}, {0x23, 0x23}, {0x23, 0x24}, + {0x24, 0x23}, {0x27, 0x1e}, {0x24, 0x24}, {0x24, 0x25}, + {0x25, 0x24}, {0x28, 0x1f}, {0x25, 0x25}, {0x25, 0x26}, + {0x26, 0x25}, {0x2a, 0x1e}, {0x26, 0x26}, {0x26, 0x27}, + {0x27, 0x26}, {0x2b, 0x1f}, {0x27, 0x27}, {0x27, 0x28}, + {0x28, 0x27}, {0x23, 0x31}, {0x28, 0x28}, {0x28, 0x29}, + {0x29, 0x28}, {0x25, 0x30}, {0x29, 0x29}, {0x29, 0x2a}, + {0x2a, 0x29}, {0x26, 0x31}, {0x2a, 0x2a}, {0x2a, 0x2b}, + {0x2b, 0x2a}, {0x28, 0x30}, {0x2b, 0x2b}, {0x2b, 0x2c}, + {0x2c, 0x2b}, {0x29, 0x31}, {0x2c, 0x2c}, {0x2c, 0x2d}, + {0x2d, 0x2c}, {0x2b, 0x30}, {0x2d, 0x2d}, {0x2d, 0x2e}, + {0x2e, 0x2d}, {0x2c, 0x31}, {0x2e, 0x2e}, {0x2e, 0x2f}, + {0x2f, 0x2e}, {0x2e, 0x30}, {0x2f, 0x2f}, {0x2f, 0x30}, + {0x30, 0x2e}, {0x30, 0x2f}, {0x31, 0x2e}, {0x30, 0x30}, + {0x30, 0x31}, {0x31, 0x30}, {0x32, 0x2f}, {0x31, 0x31}, + {0x31, 0x32}, {0x32, 0x31}, {0x34, 0x2e}, {0x32, 0x32}, + {0x32, 0x33}, {0x33, 0x32}, {0x35, 0x2f}, {0x33, 0x33}, + {0x33, 0x34}, {0x34, 0x33}, {0x37, 0x2e}, {0x34, 0x34}, + {0x34, 0x35}, {0x35, 0x34}, {0x38, 0x2f}, {0x35, 0x35}, + {0x35, 0x36}, {0x36, 0x35}, {0x3a, 0x2e}, {0x36, 0x36}, + {0x36, 0x37}, {0x37, 0x36}, {0x3b, 0x2f}, {0x37, 0x37}, + {0x37, 0x38}, {0x38, 0x37}, {0x3d, 0x2e}, {0x38, 0x38}, + {0x38, 0x39}, {0x39, 0x38}, {0x3e, 0x2f}, {0x39, 0x39}, + {0x39, 0x3a}, {0x3a, 0x39}, {0x3a, 0x3a}, {0x3a, 0x3a}, + {0x3a, 0x3b}, {0x3b, 0x3a}, {0x3b, 0x3b}, {0x3b, 0x3b}, + {0x3b, 0x3c}, {0x3c, 0x3b}, {0x3c, 0x3c}, {0x3c, 0x3c}, + {0x3c, 0x3d}, {0x3d, 0x3c}, {0x3d, 0x3d}, {0x3d, 0x3d}, + {0x3d, 0x3e}, {0x3e, 0x3d}, {0x3e, 0x3e}, {0x3e, 0x3e}, + {0x3e, 0x3f}, {0x3f, 0x3e}, {0x3f, 0x3f}, {0x3f, 0x3f}, +}; + +#endif /* __DXT_TABLES_H__ */ diff --git a/plug-ins/file-dds/endian_rw.h b/plug-ins/file-dds/endian_rw.h new file mode 100644 index 0000000..1d0b5fc --- /dev/null +++ b/plug-ins/file-dds/endian_rw.h @@ -0,0 +1,69 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef __ENDIAN_RW_H__ +#define __ENDIAN_RW_H__ + +#define GETL64(buf) \ + (((unsigned long long)(buf)[0] ) | \ + ((unsigned long long)(buf)[1] << 8) | \ + ((unsigned long long)(buf)[2] << 16) | \ + ((unsigned long long)(buf)[3] << 24) | \ + ((unsigned long long)(buf)[4] << 32) | \ + ((unsigned long long)(buf)[5] << 40) | \ + ((unsigned long long)(buf)[6] << 48) | \ + ((unsigned long long)(buf)[7] << 56)) + +#define GETL32(buf) \ + (((unsigned int)(buf)[0] ) | \ + ((unsigned int)(buf)[1] << 8) | \ + ((unsigned int)(buf)[2] << 16) | \ + ((unsigned int)(buf)[3] << 24)) + +#define GETL24(buf) \ + (((unsigned int)(buf)[0] ) | \ + ((unsigned int)(buf)[1] << 8) | \ + ((unsigned int)(buf)[2] << 16)) + +#define GETL16(buf) \ + (((unsigned short)(buf)[0] ) | \ + ((unsigned short)(buf)[1] << 8)) + +#define PUTL16(buf, s) \ + (buf)[0] = ((s) ) & 0xff; \ + (buf)[1] = ((s) >> 8) & 0xff; + +#define PUTL32(buf, l) \ + (buf)[0] = ((l) ) & 0xff; \ + (buf)[1] = ((l) >> 8) & 0xff; \ + (buf)[2] = ((l) >> 16) & 0xff; \ + (buf)[3] = ((l) >> 24) & 0xff; + +#define PUTL64(buf, ll) \ + (buf)[0] = ((ll) ) & 0xff; \ + (buf)[1] = ((ll) >> 8) & 0xff; \ + (buf)[2] = ((ll) >> 16) & 0xff; \ + (buf)[3] = ((ll) >> 24) & 0xff; \ + (buf)[4] = ((ll) >> 32) & 0xff; \ + (buf)[5] = ((ll) >> 40) & 0xff; \ + (buf)[6] = ((ll) >> 48) & 0xff; \ + (buf)[7] = ((ll) >> 56) & 0xff; + +#endif /* __ENDIAN_RW_H__ */ diff --git a/plug-ins/file-dds/imath.h b/plug-ins/file-dds/imath.h new file mode 100644 index 0000000..785c16d --- /dev/null +++ b/plug-ins/file-dds/imath.h @@ -0,0 +1,77 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef __IMATH_H__ +#define __IMATH_H__ + +#ifndef MIN +# ifdef __GNUC__ +# define MIN(a, b) ({typeof(a) _a=(a); typeof(b) _b=(b); _a < _b ? _a : _b;}) +# else +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +# endif +#endif + +#ifndef MAX +# ifdef __GNUC__ +# define MAX(a, b) ({typeof(a) _a=(a); typeof(b) _b=(b); _a > _b ? _a : _b;}) +# else +# define MAX(a, b) ((a) > (b) ? (a) : (b)) +# endif +#endif + +#define IS_POW2(x) (!((x) & ((x) - 1))) +#define IS_MUL4(x) (((x) & 3) == 0) + +/* round integer x up to next multiple of 4 */ +#define RND_MUL4(x) ((x) + (4 - ((x) & 3))) + +static inline int +mul8bit (int a, + int b) +{ + int t = a * b + 128; + + return (t + (t >> 8)) >> 8; +} + +static inline int +blerp (int a, + int b, + int x) +{ + return a + mul8bit(b - a, x); +} + +static inline int +icerp (int a, + int b, + int c, + int d, + int x) +{ + int p = (d - c) - (a - b); + int q = (a - b) - p; + int r = c - a; + + return (x * (x * (x * p + (q << 7)) + (r << 14)) + (b << 21)) >> 21; +} + +#endif /* __IMATH_H__ */ diff --git a/plug-ins/file-dds/mipmap.c b/plug-ins/file-dds/mipmap.c new file mode 100644 index 0000000..a1ad0c1 --- /dev/null +++ b/plug-ins/file-dds/mipmap.c @@ -0,0 +1,1132 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <float.h> + +#include <gtk/gtk.h> + +#ifdef _OPENMP +#include <omp.h> +#endif + +#include "dds.h" +#include "mipmap.h" +#include "imath.h" +#include "color.h" + +typedef float (*filterfunc_t)(float); +typedef int (*wrapfunc_t)(int, int); +typedef void (*mipmapfunc_t)(unsigned char *, int, int, unsigned char *, int, int, int, filterfunc_t, float, wrapfunc_t, int, float); +typedef void (*volmipmapfunc_t)(unsigned char *, int, int, int, unsigned char *, int, int, int, int, filterfunc_t, float, wrapfunc_t, int, float); + +/****************************************************************************** + * size functions * + ******************************************************************************/ + +int +get_num_mipmaps (int width, + int height) +{ + int w = width << 1; + int h = height << 1; + int n = 0; + + while (w != 1 || h != 1) + { + if (w > 1) w >>= 1; + if (h > 1) h >>= 1; + ++n; + } + + return n; +} + +unsigned int +get_mipmapped_size (int width, + int height, + int bpp, + int level, + int num, + int format) +{ + int w, h, n = 0; + unsigned int size = 0; + + w = width >> level; + h = height >> level; + w = MAX(1, w); + h = MAX(1, h); + w <<= 1; + h <<= 1; + + while (n < num && (w != 1 || h != 1)) + { + if (w > 1) w >>= 1; + if (h > 1) h >>= 1; + if (format == DDS_COMPRESS_NONE) + size += (w * h); + else + size += ((w + 3) >> 2) * ((h + 3) >> 2); + ++n; + } + + if (format == DDS_COMPRESS_NONE) + { + size *= bpp; + } + else + { + if (format == DDS_COMPRESS_BC1 || format == DDS_COMPRESS_BC4) + size *= 8; + else + size *= 16; + } + + return size; +} + +unsigned int +get_volume_mipmapped_size (int width, + int height, + int depth, + int bpp, + int level, + int num, + int format) +{ + int w, h, d, n = 0; + unsigned int size = 0; + + w = width >> level; + h = height >> level; + d = depth >> level; + w = MAX(1, w); + h = MAX(1, h); + d = MAX(1, d); + w <<= 1; + h <<= 1; + d <<= 1; + + while (n < num && (w != 1 || h != 1)) + { + if (w > 1) w >>= 1; + if (h > 1) h >>= 1; + if (d > 1) d >>= 1; + if (format == DDS_COMPRESS_NONE) + size += (w * h * d); + else + size += (((w + 3) >> 2) * ((h + 3) >> 2) * d); + ++n; + } + + if (format == DDS_COMPRESS_NONE) + { + size *= bpp; + } + else + { + if (format == DDS_COMPRESS_BC1 || format == DDS_COMPRESS_BC4) + size *= 8; + else + size *= 16; + } + + return size; +} + +int +get_next_mipmap_dimensions (int *next_w, + int *next_h, + int curr_w, + int curr_h) +{ + if (curr_w == 1 || curr_h == 1) + return 0; + + if (next_w) *next_w = curr_w >> 1; + if (next_h) *next_h = curr_h >> 1; + + return 1; +} + +/****************************************************************************** + * wrap modes * + ******************************************************************************/ + +static int +wrap_mirror (int x, + int max) +{ + if (max == 1) x = 0; + x = abs(x); + while (x >= max) + x = abs(max + max - x - 2); + + return x; +} + +static int +wrap_repeat (int x, + int max) +{ + if (x >= 0) + return x % max; + + return (x + 1) % max + max - 1; +} + +static int +wrap_clamp (int x, + int max) +{ + return MAX(0, MIN(max - 1, x)); +} + +/****************************************************************************** + * gamma-correction * + ******************************************************************************/ + +static int +linear_to_gamma (int gc, + int v, + float gamma) +{ + if (gc == 1) + { + v = (int)(powf((float)v / 255.0f, gamma) * 255); + if (v > 255) v = 255; + } + else if (gc == 2) + { + v = linear_to_sRGB(v); + } + + return v; +} + +static int +gamma_to_linear (int gc, + int v, + float gamma) +{ + if (gc == 1) + { + v = (int)(powf((float)v / 255.0f, 1.0f / gamma) * 255); + if(v > 255) v = 255; + } + else if (gc == 2) + { + v = sRGB_to_linear(v); + } + + return v; +} + +/****************************************************************************** + * filters * + ******************************************************************************/ + +static float +box_filter (float t) +{ + if ((t >= -0.5f) && (t < 0.5f)) + return 1.0f; + + return 0.0f; +} + +static float +triangle_filter (float t) +{ + if (t < 0.0f) t = -t; + if (t < 1.0f) return 1.0f - t; + + return 0.0f; +} + +static float +quadratic_filter (float t) +{ + if (t < 0.0f) t = -t; + if (t < 0.5f) return 0.75f - t * t; + if (t < 1.5f) + { + t -= 1.5f; + return 0.5f * t * t; + } + + return 0.0f; +} + +static float +bspline_filter (float t) +{ + float tt; + + if (t < 0.0f) + t = -t; + + if (t < 1.0f) + { + tt = t * t; + return ((0.5f * tt * t) - tt + (2.0f / 3.0f)); + } + else if (t < 2.0f) + { + t = 2.0f - t; + return (1.0f / 6.0f) * (t * t * t); + } + + return 0.0f; +} + +static float +mitchell (float t, + const float B, + const float C) +{ + float tt; + + tt = t * t; + if (t < 0.0f) + t = -t; + + if (t < 1.0f) + { + t = (((12.0f - 9.0f * B - 6.0f * C) * (t * tt)) + + ((-18.0f + 12.0f * B + 6.0f * C) * tt) + + (6.0f - 2.0f * B)); + + return t / 6.0f; + } + else if (t < 2.0f) + { + t = (((-1.0f * B - 6.0f * C) * (t * tt)) + + ((6.0f * B + 30.0f * C) * tt) + + ((-12.0f * B - 48.0f * C) * t) + + (8.0f * B + 24.0f * C)); + + return t / 6.0f; + } + + return 0.0f; +} + +static float +mitchell_filter (float t) +{ + return mitchell(t, 1.0f / 3.0f, 1.0f / 3.0f); +} + +static float +sinc (float x) +{ + x = (x * M_PI); + if (fabsf(x) < 1e-04f) + return 1.0f + x * x * (-1.0f / 6.0f + x * x * 1.0f / 120.0f); + + return sinf(x) / x; +} + +static float +lanczos_filter (float t) +{ + if (t < 0.0f) t = -t; + if (t < 3.0f) return sinc(t) * sinc(t / 3.0f); + + return 0.0f; +} + +static float +bessel0 (float x) +{ + const float EPSILON = 1e-6f; + float xh, sum, pow, ds; + int k; + + xh = 0.5f * x; + sum = 1.0f; + pow = 1.0f; + k = 0; + ds = 1.0f; + + while (ds > sum * EPSILON) + { + ++k; + pow = pow * (xh / k); + ds = pow * pow; + sum += ds; + } + + return sum; +} + +static float +kaiser_filter (float t) +{ + if (t < 0.0f) t = -t; + + if (t < 3.0f) + { + const float alpha = 4.0f; + const float rb04 = 0.0884805322f; // 1.0f / bessel0(4.0f); + const float ratio = t / 3.0f; + if ((1.0f - ratio * ratio) >= 0) + return sinc(t) * bessel0(alpha * sqrtf(1.0f - ratio * ratio)) * rb04; + } + + return 0.0f; +} + +/****************************************************************************** + * 2D image scaling * + ******************************************************************************/ + +static void +scale_image_nearest (unsigned char *dst, + int dw, + int dh, + unsigned char *src, + int sw, + int sh, + int bpp, + filterfunc_t filter, + float support, + wrapfunc_t wrap, + int gc, + float gamma) +{ + int n, x, y; + int ix, iy; + int srowbytes = sw * bpp; + int drowbytes = dw * bpp; + + for (y = 0; y < dh; ++y) + { + iy = (y * sh + sh / 2) / dh; + for (x = 0; x < dw; ++x) + { + ix = (x * sw + sw / 2) / dw; + for (n = 0; n < bpp; ++n) + { + dst[y * drowbytes + (x * bpp) + n] = + src[iy * srowbytes + (ix * bpp) + n]; + } + } + } +} + +static void +scale_image (unsigned char *dst, + int dw, + int dh, + unsigned char *src, + int sw, + int sh, + int bpp, + filterfunc_t filter, + float support, + wrapfunc_t wrap, + int gc, + float gamma) +{ + const float blur = 1.0f; + const float xfactor = (float)dw / (float)sw; + const float yfactor = (float)dh / (float)sh; + + int x, y, start, stop, nmax, n, i; + int sstride = sw * bpp; + float center, contrib, density, s, r, t; + + unsigned char *d, *row, *col; + + float xscale = MIN(xfactor, 1.0f) / blur; + float yscale = MIN(yfactor, 1.0f) / blur; + float xsupport = support / xscale; + float ysupport = support / yscale; + unsigned char *tmp; + + if (xsupport <= 0.5f) + { + xsupport = 0.5f + 1e-10f; + xscale = 1.0f; + } + + if (ysupport <= 0.5f) + { + ysupport = 0.5f + 1e-10f; + yscale = 1.0f; + } + +#ifdef _OPENMP + tmp = g_malloc(sw * bpp * omp_get_max_threads()); +#else + tmp = g_malloc(sw * bpp); +#endif + +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic) \ + private(x, y, d, row, col, center, start, stop, nmax, s, i, n, density, r, t, contrib) +#endif + for (y = 0; y < dh; ++y) + { + /* resample in Y direction to temp buffer */ + d = tmp; +#ifdef _OPENMP + d += (sw * bpp * omp_get_thread_num()); +#endif + + center = ((float)y + 0.5f) / yfactor; + start = (int)(center - ysupport + 0.5f); + stop = (int)(center + ysupport + 0.5f); + nmax = stop - start; + s = (float)start - center + 0.5f; + + for (x = 0; x < sw; ++x) + { + col = src + (x * bpp); + + for (i = 0; i < bpp; ++i) + { + density = 0.0f; + r = 0.0f; + + for (n = 0; n < nmax; ++n) + { + contrib = filter((s + n) * yscale); + density += contrib; + if (i == 3) + t = col[(wrap(start + n, sh) * sstride) + i]; + else + t = linear_to_gamma(gc, col[(wrap(start + n, sh) * sstride) + i], gamma); + r += t * contrib; + } + + if (density != 0.0f && density != 1.0f) + r /= density; + + r = MIN(255, MAX(0, r)); + + if (i != 3) + r = gamma_to_linear(gc, r, gamma); + + d[(x * bpp) + i] = (unsigned char)r; + } + } + + /* resample in X direction using temp buffer */ + row = d; + d = dst; + + for (x = 0; x < dw; ++x) + { + center = ((float)x + 0.5f) / xfactor; + start = (int)(center - xsupport + 0.5f); + stop = (int)(center + xsupport + 0.5f); + nmax = stop - start; + s = (float)start - center + 0.5f; + + for (i = 0; i < bpp; ++i) + { + density = 0.0f; + r = 0.0f; + + for (n = 0; n < nmax; ++n) + { + contrib = filter((s + n) * xscale); + density += contrib; + if (i == 3) + t = row[(wrap(start + n, sw) * bpp) + i]; + else + t = linear_to_gamma(gc, row[(wrap(start + n, sw) * bpp) + i], gamma); + r += t * contrib; + } + + if (density != 0.0f && density != 1.0f) + r /= density; + + r = MIN(255, MAX(0, r)); + + if (i != 3) + r = gamma_to_linear(gc, r, gamma); + + d[(y * (dw * bpp)) + (x * bpp) + i] = (unsigned char)r; + } + } + } + + g_free (tmp); +} + +/****************************************************************************** + * 3D image scaling * + ******************************************************************************/ + +static void +scale_volume_image_nearest (unsigned char *dst, + int dw, + int dh, + int dd, + unsigned char *src, + int sw, + int sh, + int sd, + int bpp, + filterfunc_t filter, + float support, + wrapfunc_t wrap, + int gc, + float gamma) +{ + int n, x, y, z; + int ix, iy, iz; + + for (z = 0; z < dd; ++z) + { + iz = (z * sd + sd / 2) / dd; + for (y = 0; y < dh; ++y) + { + iy = (y * sh + sh / 2) / dh; + for (x = 0; x < dw; ++x) + { + ix = (x * sw + sw / 2) / dw; + for (n = 0; n < bpp; ++n) + { + dst[(z * (dw * dh)) + (y * dw) + (x * bpp) + n] = + src[(iz * (sw * sh)) + (iy * sw) + (ix * bpp) + n]; + } + } + } + } +} + +static void +scale_volume_image (unsigned char *dst, + int dw, + int dh, + int dd, + unsigned char *src, + int sw, + int sh, + int sd, + int bpp, + filterfunc_t filter, + float support, + wrapfunc_t wrap, + int gc, + float gamma) +{ + const float blur = 1.0f; + const float xfactor = (float)dw / (float)sw; + const float yfactor = (float)dh / (float)sh; + const float zfactor = (float)dd / (float)sd; + + int x, y, z, start, stop, nmax, n, i; + int sstride = sw * bpp; + int zstride = sh * sw * bpp; + float center, contrib, density, s, r, t; + + unsigned char *d, *row, *col, *slice; + + float xscale = MIN(xfactor, 1.0f) / blur; + float yscale = MIN(yfactor, 1.0f) / blur; + float zscale = MIN(zfactor, 1.0f) / blur; + float xsupport = support / xscale; + float ysupport = support / yscale; + float zsupport = support / zscale; + unsigned char *tmp1, *tmp2; + + /* down to a 2D image, use the faster 2D image resampler */ + if (dd == 1 && sd == 1) + { + scale_image(dst, dw, dh, src, sw, sh, bpp, filter, support, wrap, gc, gamma); + return; + } + + if (xsupport <= 0.5f) + { + xsupport = 0.5f + 1e-10f; + xscale = 1.0f; + } + + if (ysupport <= 0.5f) + { + ysupport = 0.5f + 1e-10f; + yscale = 1.0f; + } + + if (zsupport <= 0.5f) + { + zsupport = 0.5f + 1e-10f; + zscale = 1.0f; + } + + tmp1 = g_malloc(sh * sw * bpp); + tmp2 = g_malloc(dh * sw * bpp); + + for (z = 0; z < dd; ++z) + { + /* resample in Z direction */ + d = tmp1; + + center = ((float)z + 0.5f) / zfactor; + start = (int)(center - zsupport + 0.5f); + stop = (int)(center + zsupport + 0.5f); + nmax = stop - start; + s = (float)start - center + 0.5f; + +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic) \ + private(x, y, slice, i, n, density, r, t, contrib) +#endif + for (y = 0; y < sh; ++y) + { + for (x = 0; x < sw; ++x) + { + slice = src + (y * (sw * bpp)) + (x * bpp); + + for (i = 0; i < bpp; ++i) + { + density = 0.0f; + r = 0.0f; + + for (n = 0; n < nmax; ++n) + { + contrib = filter((s + n) * zscale); + density += contrib; + if (i == 3) + t = slice[(wrap(start + n, sd) * zstride) + i]; + else + t = linear_to_gamma(gc, slice[(wrap(start + n, sd) * zstride) + i], gamma); + r += t * contrib; + } + + if (density != 0.0f && density != 1.0f) + r /= density; + + r = MIN(255, MAX(0, r)); + + if (i != 3) + r = gamma_to_linear(gc, r, gamma); + + d[((y * sw) + x) * bpp + i] = (unsigned char)r; + } + } + } + + /* resample in Y direction */ + d = tmp2; +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic) \ + private(x, y, col, center, start, stop, nmax, s, i, n, density, r, t, contrib) +#endif + for (y = 0; y < dh; ++y) + { + center = ((float)y + 0.5f) / yfactor; + start = (int)(center - ysupport + 0.5f); + stop = (int)(center + ysupport + 0.5f); + nmax = stop - start; + s = (float)start - center + 0.5f; + + for (x = 0; x < sw; ++x) + { + col = tmp1 + (x * bpp); + + for (i = 0; i < bpp; ++i) + { + density = 0.0f; + r = 0.0f; + + for (n = 0; n < nmax; ++n) + { + contrib = filter((s + n) * yscale); + density += contrib; + if (i == 3) + t = col[(wrap(start + n, sh) * sstride) + i]; + else + t = linear_to_gamma(gc, col[(wrap(start + n, sh) * sstride) + i], gamma); + r += t * contrib; + } + + if (density != 0.0f && density != 1.0f) + r /= density; + + r = MIN(255, MAX(0, r)); + + if (i != 3) + r = gamma_to_linear(gc, r, gamma); + + d[((y * sw) + x) * bpp + i] = (unsigned char)r; + } + } + } + + /* resample in X direction */ + d = dst; +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic) \ + private(x, y, row, center, start, stop, nmax, s, i, n, density, r, t, contrib) +#endif + for (y = 0; y < dh; ++y) + { + row = tmp2 + (y * sstride); + + for (x = 0; x < dw; ++x) + { + center = ((float)x + 0.5f) / xfactor; + start = (int)(center - xsupport + 0.5f); + stop = (int)(center + xsupport + 0.5f); + nmax = stop - start; + s = (float)start - center + 0.5f; + + for (i = 0; i < bpp; ++i) + { + density = 0.0f; + r = 0.0f; + + for (n = 0; n < nmax; ++n) + { + contrib = filter((s + n) * xscale); + density += contrib; + if (i == 3) + t = row[(wrap(start + n, sw) * bpp) + i]; + else + t = linear_to_gamma(gc, row[(wrap(start + n, sw) * bpp) + i], gamma); + r += t * contrib; + } + + if (density != 0.0f && density != 1.0f) + r /= density; + + r = MIN(255, MAX(0, r)); + + if (i != 3) + r = gamma_to_linear(gc, r, gamma); + + d[((z * dh * dw) + (y * dw) + x) * bpp + i] = (unsigned char)r; + } + } + } + } + + g_free (tmp1); + g_free (tmp2); +} + +/****************************************************************************** + * filter lookup table * + ******************************************************************************/ + +static struct +{ + int filter; + filterfunc_t func; + float support; +} filters[] = +{ + { DDS_MIPMAP_FILTER_BOX, box_filter, 0.5f }, + { DDS_MIPMAP_FILTER_TRIANGLE, triangle_filter, 1.0f }, + { DDS_MIPMAP_FILTER_QUADRATIC, quadratic_filter, 1.5f }, + { DDS_MIPMAP_FILTER_BSPLINE, bspline_filter, 2.0f }, + { DDS_MIPMAP_FILTER_MITCHELL, mitchell_filter, 2.0f }, + { DDS_MIPMAP_FILTER_LANCZOS, lanczos_filter, 3.0f }, + { DDS_MIPMAP_FILTER_KAISER, kaiser_filter, 3.0f }, + { DDS_MIPMAP_FILTER_MAX, NULL, 0.0f } +}; + +/* + * Alpha test coverage - portion of visible texels after alpha test: + * if (texel_alpha < alpha_test_threshold) + * discard; + */ +static float +calc_alpha_test_coverage (unsigned char *src, + unsigned int width, + unsigned int height, + int bpp, + float alpha_test_threshold, + float alpha_scale) +{ + unsigned int x, y; + int rowbytes = width * bpp; + int coverage = 0; + const int alpha_channel_idx = 3; + + if (bpp <= alpha_channel_idx) + { + /* No alpha channel */ + return 1.f; + } + + for (y = 0; y < height; ++y) + { + for (x = 0; x < width; ++x) + { + const float alpha = src[y * rowbytes + (x * bpp) + alpha_channel_idx]; + if ((alpha * alpha_scale) >= (alpha_test_threshold * 255)) + { + ++coverage; + } + } + } + + return (float)coverage / (width * height); +} + +static void +scale_alpha_to_coverage (unsigned char *img, + unsigned int width, + unsigned int height, + int bpp, + float desired_coverage, + float alpha_test_threshold) +{ + int i; + unsigned int x, y; + const int rowbytes = width * bpp; + const int alpha_channel_idx = 3; + float min_alpha_scale = 0.0f; + float max_alpha_scale = 4.0f; + float alpha_scale = 1.0f; + + if (bpp <= alpha_channel_idx) + { + /* No alpha channel */ + return; + } + + /* Binary search */ + for (i = 0; i < 10; i++) + { + float cur_coverage = calc_alpha_test_coverage(img, width, height, bpp, alpha_test_threshold, alpha_scale); + + if (cur_coverage < desired_coverage) + { + min_alpha_scale = alpha_scale; + } + else if (cur_coverage > desired_coverage) + { + max_alpha_scale = alpha_scale; + } + else + { + break; + } + + alpha_scale = (min_alpha_scale + max_alpha_scale) / 2; + } + + /* Scale alpha channel */ + for (y = 0; y < height; ++y) + { + for (x = 0; x < width; ++x) + { + float new_alpha = img[y * rowbytes + (x * bpp) + alpha_channel_idx] * alpha_scale; + if (new_alpha > 255.0f) + { + new_alpha = 255.0f; + } + + img[y * rowbytes + (x * bpp) + alpha_channel_idx] = (unsigned char)new_alpha; + } + } +} + +/****************************************************************************** + * mipmap generation * + ******************************************************************************/ + +int +generate_mipmaps (unsigned char *dst, + unsigned char *src, + unsigned int width, + unsigned int height, + int bpp, + int indexed, + int mipmaps, + int filter, + int wrap, + int gc, + float gamma, + int preserve_alpha_coverage, + float alpha_test_threshold) +{ + int i; + unsigned int sw, sh, dw, dh; + unsigned char *s, *d; + mipmapfunc_t mipmap_func = NULL; + filterfunc_t filter_func = NULL; + wrapfunc_t wrap_func = NULL; + float support = 0.0f; + const int has_alpha = (bpp >= 3); + float alpha_test_coverage = 1; + + if (indexed || filter == DDS_MIPMAP_FILTER_NEAREST) + { + mipmap_func = scale_image_nearest; + } + else + { + if ((filter <= DDS_MIPMAP_FILTER_DEFAULT) || + (filter >= DDS_MIPMAP_FILTER_MAX)) + filter = DDS_MIPMAP_FILTER_BOX; + + mipmap_func = scale_image; + + for (i = 0; filters[i].filter != DDS_MIPMAP_FILTER_MAX; ++i) + { + if (filter == filters[i].filter) + { + filter_func = filters[i].func; + support = filters[i].support; + break; + } + } + } + + switch (wrap) + { + case DDS_MIPMAP_WRAP_MIRROR: wrap_func = wrap_mirror; break; + case DDS_MIPMAP_WRAP_REPEAT: wrap_func = wrap_repeat; break; + case DDS_MIPMAP_WRAP_CLAMP: wrap_func = wrap_clamp; break; + default: wrap_func = wrap_clamp; break; + } + + if (has_alpha && preserve_alpha_coverage) + { + alpha_test_coverage = calc_alpha_test_coverage(src, width, height, bpp, + alpha_test_threshold, + 1.0f); + } + + memcpy (dst, src, width * height * bpp); + + s = dst; + d = dst + (width * height * bpp); + + sw = width; + sh = height; + + for (i = 1; i < mipmaps; ++i) + { + dw = MAX(1, sw >> 1); + dh = MAX(1, sh >> 1); + + mipmap_func(d, dw, dh, s, sw, sh, bpp, filter_func, support, wrap_func, gc, gamma); + + if (has_alpha && preserve_alpha_coverage) + { + scale_alpha_to_coverage(d, dw, dh, bpp, alpha_test_coverage, alpha_test_threshold); + } + + s = d; + sw = dw; + sh = dh; + d += (dw * dh * bpp); + } + + return 1; +} + +int +generate_volume_mipmaps (unsigned char *dst, + unsigned char *src, + unsigned int width, + unsigned int height, + unsigned int depth, + int bpp, + int indexed, + int mipmaps, + int filter, + int wrap, + int gc, + float gamma) +{ + int i; + unsigned int sw, sh, sd; + unsigned int dw, dh, dd; + unsigned char *s, *d; + volmipmapfunc_t mipmap_func = NULL; + filterfunc_t filter_func = NULL; + wrapfunc_t wrap_func = NULL; + float support = 0.0f; + + if (indexed || filter == DDS_MIPMAP_FILTER_NEAREST) + { + mipmap_func = scale_volume_image_nearest; + } + else + { + if ((filter <= DDS_MIPMAP_FILTER_DEFAULT) || + (filter >= DDS_MIPMAP_FILTER_MAX)) + filter = DDS_MIPMAP_FILTER_BOX; + + mipmap_func = scale_volume_image; + + for (i = 0; filters[i].filter != DDS_MIPMAP_FILTER_MAX; ++i) + { + if (filter == filters[i].filter) + { + filter_func = filters[i].func; + support = filters[i].support; + break; + } + } + } + + switch (wrap) + { + case DDS_MIPMAP_WRAP_MIRROR: wrap_func = wrap_mirror; break; + case DDS_MIPMAP_WRAP_REPEAT: wrap_func = wrap_repeat; break; + case DDS_MIPMAP_WRAP_CLAMP: wrap_func = wrap_clamp; break; + default: wrap_func = wrap_clamp; break; + } + + memcpy (dst, src, width * height * depth * bpp); + + s = dst; + d = dst + (width * height * depth * bpp); + + sw = width; + sh = height; + sd = depth; + + for (i = 1; i < mipmaps; ++i) + { + dw = MAX(1, sw >> 1); + dh = MAX(1, sh >> 1); + dd = MAX(1, sd >> 1); + + mipmap_func (d, dw, dh, dd, s, sw, sh, sd, bpp, filter_func, support, wrap_func, gc, gamma); + + s = d; + sw = dw; + sh = dh; + sd = dd; + d += (dw * dh * dd * bpp); + } + + return 1; +} diff --git a/plug-ins/file-dds/mipmap.h b/plug-ins/file-dds/mipmap.h new file mode 100644 index 0000000..166f326 --- /dev/null +++ b/plug-ins/file-dds/mipmap.h @@ -0,0 +1,75 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef __MIPMAP_H__ +#define __MIPMAP_H__ + +int get_num_mipmaps (int width, + int height); +unsigned int get_mipmapped_size (int width, + int height, + int bpp, + int level, + int num, + int format); +unsigned int get_volume_mipmapped_size (int width, + int height, + int depth, + int bpp, + int level, + int num, + int format); +int get_next_mipmap_dimensions (int *next_w, + int *next_h, + int curr_w, + int curr_h); + +float cubic_interpolate (float a, + float b, + float c, + float d, + float x); +int generate_mipmaps (unsigned char *dst, + unsigned char *src, + unsigned int width, + unsigned int height, + int bpp, + int indexed, + int mipmaps, + int filter, + int wrap, + int gamma_correct, + float gamma, + int preserve_alpha_test_coverage, + float alpha_test_threshold); +int generate_volume_mipmaps (unsigned char *dst, + unsigned char *src, + unsigned int width, + unsigned int height, + unsigned int depth, + int bpp, + int indexed, + int mipmaps, + int filter, + int wrap, + int gamma_correct, + float gamma); + +#endif /* __MIPMAP_H__ */ diff --git a/plug-ins/file-dds/misc.c b/plug-ins/file-dds/misc.c new file mode 100644 index 0000000..c8c3593 --- /dev/null +++ b/plug-ins/file-dds/misc.c @@ -0,0 +1,261 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. + */ + +#include <libgimp/gimp.h> +#include "misc.h" + +static inline float +saturate (float a) +{ + if(a < 0) a = 0; + if(a > 1) a = 1; + + return a; +} + +void +decode_ycocg_image (gint32 drawableID, + gboolean shadow) +{ + GeglBuffer *buffer, *sbuffer; + const Babl *format; + unsigned char *data; + unsigned int i, w, h, num_pixels; + + const float offset = 0.5f * 256.0f / 255.0f; + float Y, Co, Cg, R, G, B; + + buffer = gimp_drawable_get_buffer (drawableID); + + if (shadow) + { + sbuffer = gimp_drawable_get_shadow_buffer (drawableID); + gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, sbuffer, NULL); + g_object_unref (buffer); + buffer = sbuffer; + } + + format = babl_format ("R'G'B'A u8"); + + w = gegl_buffer_get_width (buffer); + h = gegl_buffer_get_height (buffer); + num_pixels = w * h; + + data = g_malloc (num_pixels * 4); + + gegl_buffer_get (buffer, GEGL_RECTANGLE(0, 0, w, h), 1.0, format, data, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + gimp_progress_init ("Decoding YCoCg pixels..."); + + for (i = 0; i < num_pixels; ++i) + { + Y = (float)data[4 * i + 3] / 255.0f; + Co = (float)data[4 * i + 0] / 255.0f; + Cg = (float)data[4 * i + 1] / 255.0f; + + /* convert YCoCg to RGB */ + Co -= offset; + Cg -= offset; + + R = saturate(Y + Co - Cg); + G = saturate(Y + Cg); + B = saturate(Y - Co - Cg); + + /* copy new alpha from blue */ + data[4 * i + 3] = data[4 * i + 2]; + + data[4 * i + 0] = (unsigned char)(R * 255.0f); + data[4 * i + 1] = (unsigned char)(G * 255.0f); + data[4 * i + 2] = (unsigned char)(B * 255.0f); + + if ((i & 0x7fff) == 0) + gimp_progress_update ((float)i / (float)num_pixels); + } + + gegl_buffer_set (buffer, GEGL_RECTANGLE(0, 0, w, h), 0, format, data, + GEGL_AUTO_ROWSTRIDE); + + gimp_progress_update (1.0); + + gegl_buffer_flush (buffer); + + if (shadow) + gimp_drawable_merge_shadow (drawableID, TRUE); + + gimp_drawable_update (drawableID, 0, 0, w, h); + + g_free (data); + + g_object_unref (buffer); +} + +void +decode_ycocg_scaled_image (gint32 drawableID, + gboolean shadow) +{ + GeglBuffer *buffer, *sbuffer; + const Babl *format; + unsigned char *data; + unsigned int i, w, h, num_pixels; + + const float offset = 0.5f * 256.0f / 255.0f; + float Y, Co, Cg, R, G, B, s; + + buffer = gimp_drawable_get_buffer (drawableID); + + if (shadow) + { + sbuffer = gimp_drawable_get_shadow_buffer(drawableID); + gegl_buffer_copy(buffer, NULL, GEGL_ABYSS_NONE, sbuffer, NULL); + g_object_unref(buffer); + buffer = sbuffer; + } + + format = babl_format ("R'G'B'A u8"); + + w = gegl_buffer_get_width (buffer); + h = gegl_buffer_get_height (buffer); + num_pixels = w * h; + + data = g_malloc (num_pixels * 4); + + gegl_buffer_get (buffer, GEGL_RECTANGLE(0, 0, w, h), 1.0, format, data, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + gimp_progress_init ("Decoding YCoCg (scaled) pixels..."); + + for (i = 0; i < num_pixels; ++i) + { + Y = (float)data[4 * i + 3] / 255.0f; + Co = (float)data[4 * i + 0] / 255.0f; + Cg = (float)data[4 * i + 1] / 255.0f; + s = (float)data[4 * i + 2] / 255.0f; + + /* convert YCoCg to RGB */ + s = 1.0f / ((255.0f / 8.0f) * s + 1.0f); + + Co = (Co - offset) * s; + Cg = (Cg - offset) * s; + + R = saturate(Y + Co - Cg); + G = saturate(Y + Cg); + B = saturate(Y - Co - Cg); + + data[4 * i + 0] = (unsigned char)(R * 255.0f); + data[4 * i + 1] = (unsigned char)(G * 255.0f); + data[4 * i + 2] = (unsigned char)(B * 255.0f); + + /* set alpha to 1 */ + data[4 * i + 3] = 255; + + if ((i & 0x7fff) == 0) + gimp_progress_update ((float)i / (float)num_pixels); + } + + gegl_buffer_set (buffer, GEGL_RECTANGLE(0, 0, w, h), 0, format, data, + GEGL_AUTO_ROWSTRIDE); + + gimp_progress_update (1.0); + + gegl_buffer_flush (buffer); + + if (shadow) + gimp_drawable_merge_shadow (drawableID, TRUE); + + gimp_drawable_update (drawableID, 0, 0, w, h); + + g_free (data); + + g_object_unref (buffer); +} + +void +decode_alpha_exp_image (gint32 drawableID, + gboolean shadow) +{ + GeglBuffer *buffer, *sbuffer; + const Babl *format; + unsigned char *data; + unsigned int i, w, h, num_pixels; + int R, G, B, A; + + buffer = gimp_drawable_get_buffer (drawableID); + + if (shadow) + { + sbuffer = gimp_drawable_get_shadow_buffer (drawableID); + gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, sbuffer, NULL); + g_object_unref (buffer); + buffer = sbuffer; + } + + format = babl_format ("R'G'B'A u8"); + + w = gegl_buffer_get_width (buffer); + h = gegl_buffer_get_height (buffer); + num_pixels = w * h; + + data = g_malloc (num_pixels * 4); + + gegl_buffer_get (buffer, GEGL_RECTANGLE(0, 0, w, h), 1.0, format, data, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + gimp_progress_init ("Decoding Alpha-exponent pixels..."); + + for (i = 0; i < num_pixels; ++i) + { + R = data[4 * i + 0]; + G = data[4 * i + 1]; + B = data[4 * i + 2]; + A = data[4 * i + 3]; + + R = (R * A + 1) >> 8; + G = (G * A + 1) >> 8; + B = (B * A + 1) >> 8; + A = 255; + + data[4 * i + 0] = R; + data[4 * i + 1] = G; + data[4 * i + 2] = B; + data[4 * i + 3] = A; + + if ((i & 0x7fff) == 0) + gimp_progress_update ((float)i / (float)num_pixels); + } + + gegl_buffer_set (buffer, GEGL_RECTANGLE(0, 0, w, h), 0, format, data, + GEGL_AUTO_ROWSTRIDE); + + gimp_progress_update (1.0); + + gegl_buffer_flush (buffer); + + if (shadow) + gimp_drawable_merge_shadow (drawableID, TRUE); + + gimp_drawable_update (drawableID, 0, 0, w, h); + + g_free (data); + + g_object_unref (buffer); +} diff --git a/plug-ins/file-dds/misc.h b/plug-ins/file-dds/misc.h new file mode 100644 index 0000000..73656ee --- /dev/null +++ b/plug-ins/file-dds/misc.h @@ -0,0 +1,31 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef __MISC_H__ +#define __MISC_H__ + +void decode_ycocg_image (gint32 drawableID, + gboolean shadow); +void decode_ycocg_scaled_image (gint32 drawableID, + gboolean shadow); +void decode_alpha_exp_image (gint32 drawableID, + gboolean shadow); + +#endif /* __MISC_H__ */ diff --git a/plug-ins/file-dds/mktables.c b/plug-ins/file-dds/mktables.c new file mode 100644 index 0000000..99eef6d --- /dev/null +++ b/plug-ins/file-dds/mktables.c @@ -0,0 +1,130 @@ +#include <stdlib.h> +#include <stdio.h> + +static int mul8bit(int a, int b) +{ + int t = a * b + 128; + return((t + (t >> 8)) >> 8); +} + +static int lerp13(int a, int b) +{ +#if 0 + return(a + mul8bit(b - a, 0x55)); +#else + return((2 * a + b) / 3); +#endif +} + +static void prepare_opt_table(unsigned char *tab, + const unsigned char *expand, int size) +{ + int i, mn, mx, bestE, minE, maxE, e; + + for(i = 0; i < 256; ++i) + { + bestE = 256 * 100; + + for(mn = 0; mn < size; ++mn) + { + for(mx = 0; mx < size; ++mx) + { + minE = expand[mn]; + maxE = expand[mx]; + e = abs(lerp13(maxE, minE) - i) * 100; + + e += abs(mx - mn) * 3; + + if(e < bestE) + { + tab[i * 2 + 0] = mx; + tab[i * 2 + 1] = mn; + bestE = e; + } + } + } + } +} + +#if 0 +int main(void) +{ + FILE *fp; + int i, v; + unsigned char expand5[32]; + unsigned char expand6[64]; + unsigned char quantRB[256 + 16]; + unsigned char quantG[256 + 16]; + unsigned char omatch5[256][2]; + unsigned char omatch6[256][2]; + + fp = fopen("dxt_tables.h", "w"); + fprintf(fp, + "#ifndef DXT_TABLES_H\n" + "#define DXT_TABLES_H\n\n"); + + for(i = 0; i < 32; ++i) + expand5[i] = (i << 3) | (i >> 2); + + for(i = 0; i < 64; ++i) + expand6[i] = (i << 2) | (i >> 4); + + for(i = 0; i < 256 + 16; ++i) + { + v = i - 8; + if(v < 0) v = 0; + if(v > 255) v = 255; + quantRB[i] = expand5[mul8bit(v, 31)]; + quantG[i] = expand6[mul8bit(v, 63)]; + } + + fprintf(fp, + "static const unsigned char quantRB[256 + 16] =\n" + "{"); + for(i = 0; i < 256 + 16; ++i) + { + if(i % 8 == 0) fprintf(fp, "\n "); + fprintf(fp, "0x%02x, ", quantRB[i]); + } + fprintf(fp, "\n};\n\n"); + + fprintf(fp, + "static const unsigned char quantG[256 + 16] =\n" + "{"); + for(i = 0; i < 256 + 16; ++i) + { + if(i % 8 == 0) fprintf(fp, "\n "); + fprintf(fp, "0x%02x, ", quantG[i]); + } + fprintf(fp, "\n};\n\n"); + + prepare_opt_table(&omatch5[0][0], expand5, 32); + prepare_opt_table(&omatch6[0][0], expand6, 64); + + fprintf(fp, + "static const unsigned char omatch5[256][2] =\n" + "{"); + for(i = 0; i < 256; ++i) + { + if(i % 4 == 0) fprintf(fp, "\n "); + fprintf(fp, "{0x%02x, 0x%02x}, ", omatch5[i][0], omatch5[i][1]); + } + fprintf(fp, "\n};\n\n"); + + fprintf(fp, + "static const unsigned char omatch6[256][2] =\n" + "{"); + for(i = 0; i < 256; ++i) + { + if(i % 4 == 0) fprintf(fp, "\n "); + fprintf(fp, "{0x%02x, 0x%02x}, ", omatch6[i][0], omatch6[i][1]); + } + fprintf(fp, "\n};\n\n"); + + fprintf(fp, "#endif\n"); + + fclose(fp); + + return(0); +} +#endif diff --git a/plug-ins/file-dds/vec.h b/plug-ins/file-dds/vec.h new file mode 100644 index 0000000..cc3c344 --- /dev/null +++ b/plug-ins/file-dds/vec.h @@ -0,0 +1,245 @@ +/* + * DDS GIMP plugin + * + * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>, + * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef __VEC_H__ +#define __VEC_H__ + +#include <math.h> + +#ifdef __SSE__ +#define USE_SSE 1 +#endif + +#ifdef USE_SSE +#include <immintrin.h> +#endif + +#include "imath.h" + +typedef float vec4_t __attribute__((vector_size(16))); +typedef float sym3x3_t[6]; + +#define VEC4_CONST4(x, y, z, w) {x, y, z, w} +#define VEC4_CONST3(x, y, z) {x, y, z, 0.0f} +#define VEC4_CONST1(x) {x, x, x, x} + +static inline vec4_t +vec4_set (float x, + float y, + float z, + float w) +{ +#ifdef USE_SSE + return _mm_setr_ps(x, y, z, w); +#else + vec4_t v = { x, y, z, w }; + return v; +#endif +} + +static inline vec4_t +vec4_set1 (float f) +{ +#ifdef USE_SSE + return _mm_set1_ps(f); +#else + vec4_t v = { f, f, f, f }; + return v; +#endif +} + +static inline vec4_t +vec4_zero (void) +{ +#ifdef USE_SSE + return _mm_setzero_ps(); +#else + vec4_t v = { 0, 0, 0, 0 }; + return v; +#endif +} + +static inline void +vec4_store (float *f, + const vec4_t v) +{ +#ifdef USE_SSE + _mm_store_ps (f, v); +#else + f[0] = v[0]; f[1] = v[1]; f[2] = v[2]; f[3] = v[3]; +#endif +} + +static inline vec4_t +vec4_splatx (const vec4_t v) +{ +#ifdef USE_SSE + return _mm_shuffle_ps(v, v, 0x00); +#else + vec4_t r = { v[0], v[0], v[0], v[0] }; + return r; +#endif +} + +static inline vec4_t +vec4_splaty (const vec4_t v) +{ +#ifdef USE_SSE + return _mm_shuffle_ps(v, v, 0x55); +#else + vec4_t r = { v[1], v[1], v[1], v[1] }; + return r; +#endif +} + +static inline vec4_t +vec4_splatz (const vec4_t v) +{ +#ifdef USE_SSE + return _mm_shuffle_ps(v, v, 0xaa); +#else + vec4_t r = { v[2], v[2], v[2], v[2] }; + return r; +#endif +} + +static inline vec4_t +vec4_splatw (const vec4_t v) +{ +#ifdef USE_SSE + return _mm_shuffle_ps(v, v, 0xff); +#else + vec4_t r = { v[3], v[3], v[3], v[3] }; + return r; +#endif +} + +static inline vec4_t +vec4_rcp (const vec4_t v) +{ +#ifdef USE_SSE + __m128 est = _mm_rcp_ps (v); + __m128 diff = _mm_sub_ps (_mm_set1_ps(1.0f), _mm_mul_ps(est, v)); + return _mm_add_ps(_mm_mul_ps(diff, est), est); +#else + vec4_t one = { 1.0f, 1.0f, 1.0f, 1.0f }; + return one / v; +#endif +} + +static inline vec4_t +vec4_min (const vec4_t a, + const vec4_t b) +{ +#ifdef USE_SSE + return _mm_min_ps(a, b); +#else + return vec4_set (MIN(a[0], b[0]), MIN(a[1], b[1]), MIN(a[2], b[2]), MIN(a[3], b[3])); +#endif +} + +static inline vec4_t +vec4_max (const vec4_t a, + const vec4_t b) +{ +#ifdef USE_SSE + return _mm_max_ps (a, b); +#else + return vec4_set (MAX(a[0], b[0]), MAX(a[1], b[1]), MAX(a[2], b[2]), MAX(a[3], b[3])); +#endif +} + +static inline vec4_t +vec4_trunc (const vec4_t v) +{ +#ifdef USE_SSE +# ifdef __SSE4_1__ + return _mm_round_ps(v, _MM_FROUND_TRUNC); +# elif defined(__SSE2__) + return _mm_cvtepi32_ps(_mm_cvttps_epi32(v)); +# else + // convert to ints + __m128 in = v; + __m64 lo = _mm_cvttps_pi32(in); + __m64 hi = _mm_cvttps_pi32(_mm_movehl_ps(in, in)); + // convert to floats + __m128 part = _mm_movelh_ps(in, _mm_cvtpi32_ps(in, hi)); + __m128 trunc = _mm_cvtpi32_ps(part, lo); + // clear mmx state + _mm_empty (); + return trunc; +# endif +#else + vec4_t r = { v[0] > 0.0f ? floorf(v[0]) : ceil(v[0]), + v[1] > 0.0f ? floorf(v[1]) : ceil(v[1]), + v[2] > 0.0f ? floorf(v[2]) : ceil(v[2]), + v[3] > 0.0f ? floorf(v[3]) : ceil(v[3]), }; + return r; +#endif +} + +static inline float +vec4_accum (const vec4_t v) +{ +#ifdef USE_SSE + float rv; + __m128 t; +# ifdef __SSE3__ + t = _mm_hadd_ps(v, v); + t = _mm_hadd_ps(t, t); +# else + t = _mm_add_ps(v, _mm_movehl_ps(v, v)); + t = _mm_add_ss(t, _mm_shuffle_ps(t, t, 0x01)); +# endif + _mm_store_ss(&rv, t); + return rv; +#else + return v[0] + v[1] + v[2] + v[3]; +#endif +} + +static inline float +vec4_dot (const vec4_t a, + const vec4_t b) +{ +#if defined(USE_SSE) && defined(__SSE4_1__) + float rv; + __m128 t = _mm_dp_ps(a, b, 0xff); + _mm_store_ss(&rv, t); + return rv; +#else + return vec4_accum(a * b); +#endif +} + +static inline int +vec4_cmplt (const vec4_t a, + const vec4_t b) +{ +#ifdef USE_SSE + __m128 bits = _mm_cmplt_ps(a, b); + int val = _mm_movemask_ps(bits); + return val != 0; +#else + return (a[0] < b[0]) || (a[1] < b[1]) || (a[2] < b[2]) || (a[3] < b[3]); +#endif +} + +#endif /* __VEC_H__ */ |