summaryrefslogtreecommitdiffstats
path: root/plug-ins/file-dds
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/file-dds')
-rw-r--r--plug-ins/file-dds/COPYING339
-rw-r--r--plug-ins/file-dds/Makefile.am68
-rw-r--r--plug-ins/file-dds/Makefile.in1045
-rw-r--r--plug-ins/file-dds/README29
-rw-r--r--plug-ins/file-dds/TODO7
-rw-r--r--plug-ins/file-dds/color.c58
-rw-r--r--plug-ins/file-dds/color.h96
-rw-r--r--plug-ins/file-dds/dds.c434
-rw-r--r--plug-ins/file-dds/dds.h325
-rw-r--r--plug-ins/file-dds/ddsplugin.h76
-rw-r--r--plug-ins/file-dds/ddsread.c1322
-rw-r--r--plug-ins/file-dds/ddswrite.c2240
-rw-r--r--plug-ins/file-dds/dxt.c1522
-rw-r--r--plug-ins/file-dds/dxt.h49
-rw-r--r--plug-ins/file-dds/dxt_tables.h216
-rw-r--r--plug-ins/file-dds/endian_rw.h69
-rw-r--r--plug-ins/file-dds/imath.h77
-rw-r--r--plug-ins/file-dds/mipmap.c1132
-rw-r--r--plug-ins/file-dds/mipmap.h75
-rw-r--r--plug-ins/file-dds/misc.c261
-rw-r--r--plug-ins/file-dds/misc.h31
-rw-r--r--plug-ins/file-dds/mktables.c130
-rw-r--r--plug-ins/file-dds/vec.h245
23 files changed, 9846 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..11a4b22
--- /dev/null
+++ b/plug-ins/file-dds/Makefile.in
@@ -0,0 +1,1045 @@
+# Makefile.in generated by automake 1.16.2 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)/m4macros/gtk-doc.m4 \
+ $(top_srcdir)/m4macros/intltool.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(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_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_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@
+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@
+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..6994677
--- /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)((r << 2) & 0x3ff) << 20) |
+ ((unsigned int)((g << 2) & 0x3ff) << 10) |
+ ((unsigned int)((b << 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..8e79073
--- /dev/null
+++ b/plug-ins/file-dds/dds.c
@@ -0,0 +1,434 @@
+/*
+ * 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"},
+ { 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"}
+};
+
+#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",
+ "");
+
+#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;
+
+ 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))
+ {
+ 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 ((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);
+ 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..8442c53
--- /dev/null
+++ b/plug-ins/file-dds/dds.h
@@ -0,0 +1,325 @@
+/*
+ * 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_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..6747894
--- /dev/null
+++ b/plug-ins/file-dds/ddsplugin.h
@@ -0,0 +1,76 @@
+/*
+ * 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 90
+
+#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;
+} 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);
+
+
+#define LOAD_PROC "file-dds-load"
+#define SAVE_PROC "file-dds-save"
+
+#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..4f962df
--- /dev/null
+++ b/plug-ins/file-dds/ddsread.c
@@ -0,0 +1,1322 @@
+/*
+ * 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;
+ 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;
+ dds_header_t hdr;
+ dds_header_dx10_t dx10hdr;
+ dds_load_info_t d;
+ gint *layers, layer_count;
+ GimpImageBaseType type;
+ 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;
+ }
+
+ 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;
+ }
+
+ 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 //L16
+ {
+ d.gimp_bpp = 1;
+ type = GIMP_GRAY;
+ }
+ }
+ 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;
+ }
+ }
+ }
+ }
+
+ image = gimp_image_new (hdr.width, hdr.height, type);
+
+ 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_message ("Warning: DDSD_PITCH is not set!");
+ hdr->flags |= DDSD_PITCH;
+ }
+ }
+
+ /*
+ 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;
+
+ 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;
+ bablfmt = babl_format ("R'G'B' u8");
+ }
+ 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.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 /* L16 */
+ {
+ type = GIMP_GRAY_IMAGE;
+ bablfmt = babl_format ("Y' u8");
+ }
+ 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 ((*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 ((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))
+ {
+ 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);
+ }
+
+ 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 + 0] = (pixel >> d->bshift) >> 2;
+ pixels[pos + 1] = (pixel >> d->gshift) >> 2;
+ pixels[pos + 2] = (pixel >> d->rshift) >> 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 //L16
+ pixels[pos] = (unsigned char)(255 * ((float)(pixel & 0xffff) / 65535.0f));
+ }
+ 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);
+
+ 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..cf6a341
--- /dev/null
+++ b/plug-ins/file-dds/ddswrite.c
@@ -0,0 +1,2240 @@
+/*
+ * 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, "Image / Selected layer" },
+ { DDS_SAVE_CUBEMAP, "As cube map" },
+ { DDS_SAVE_VOLUMEMAP, "As volume map" },
+ { DDS_SAVE_ARRAY, "As texture array" },
+ { -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)
+{
+ 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);
+
+ 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 (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;
+
+ 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) ? 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
+ {
+ 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_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);
+
+ opt = string_value_combo_new (mipmap_strings, dds_write_vals.mipmaps);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
+ _("_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..440875b
--- /dev/null
+++ b/plug-ins/file-dds/dxt.c
@@ -0,0 +1,1522 @@
+/*
+ * 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);
+ encode_alpha_block_BC3(p, block, -2);
+ encode_alpha_block_BC3(p + 8, block, -1);
+ }
+}
+
+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, 255, 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 + 8, width);
+ decode_alpha_block_BC3(block + 1, s, 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__ */