[gimp/gimp-2-10] file-dds: add original source code of the DDS plug-in
- From: Jehan <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/gimp-2-10] file-dds: add original source code of the DDS plug-in
- Date: Wed, 2 Jan 2019 18:10:22 +0000 (UTC)
commit 369884cf9c55761124038f7b4076a6df8ca23fa1
Author: Alexandre Prokoudine <alexandre prokoudine gmail com>
Date: Tue Nov 13 02:35:07 2018 +0300
file-dds: add original source code of the DDS plug-in
This is the code from the 'gimp-2.9' branch of the plug-in,
integrated into GIMP's build system.
(cherry picked from commit 79bc2dc1effa820396e15e79f8ed56ffb0a85933)
configure.ac | 1 +
plug-ins/Makefile.am | 1 +
plug-ins/file-dds/COPYING | 339 +++++++
plug-ins/file-dds/LICENSE | 21 +
plug-ins/file-dds/LICENSE.nvtt | 23 +
plug-ins/file-dds/Makefile.am | 65 ++
plug-ins/file-dds/README | 29 +
plug-ins/file-dds/README.dxt | 175 ++++
plug-ins/file-dds/TODO | 7 +
plug-ins/file-dds/color.c | 55 ++
plug-ins/file-dds/color.h | 91 ++
plug-ins/file-dds/dds.c | 373 +++++++
plug-ins/file-dds/dds.h | 327 +++++++
plug-ins/file-dds/ddsplugin.h | 78 ++
plug-ins/file-dds/ddsread.c | 1235 +++++++++++++++++++++++
plug-ins/file-dds/ddswrite.c | 2122 ++++++++++++++++++++++++++++++++++++++++
plug-ins/file-dds/dxt.c | 1412 ++++++++++++++++++++++++++
plug-ins/file-dds/dxt.h | 41 +
plug-ins/file-dds/dxt_tables.h | 216 ++++
plug-ins/file-dds/endian.h | 71 ++
plug-ins/file-dds/imath.h | 67 ++
plug-ins/file-dds/mipmap.c | 1013 +++++++++++++++++++
plug-ins/file-dds/mipmap.h | 47 +
plug-ins/file-dds/misc.c | 252 +++++
plug-ins/file-dds/misc.h | 30 +
plug-ins/file-dds/mktables.c | 128 +++
plug-ins/file-dds/vec.h | 226 +++++
27 files changed, 8445 insertions(+)
---
diff --git a/configure.ac b/configure.ac
index ed07556119..c61b633bfd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2856,6 +2856,7 @@ build/windows/installer/Makefile
build/windows/installer/lang/Makefile
plug-ins/Makefile
plug-ins/file-bmp/Makefile
+plug-ins/file-dds/Makefile
plug-ins/file-exr/Makefile
plug-ins/file-faxg3/Makefile
plug-ins/file-fits/Makefile
diff --git a/plug-ins/Makefile.am b/plug-ins/Makefile.am
index 76b92b13cb..5fe438479f 100644
--- a/plug-ins/Makefile.am
+++ b/plug-ins/Makefile.am
@@ -29,6 +29,7 @@ SUBDIRS = \
$(pygimp) \
file-bmp \
$(file_darktable) \
+ file-dds \
$(file_exr) \
file-faxg3 \
file-fits \
diff --git a/plug-ins/file-dds/COPYING b/plug-ins/file-dds/COPYING
new file mode 100644
index 0000000000..6f17fd7034
--- /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/LICENSE b/plug-ins/file-dds/LICENSE
new file mode 100644
index 0000000000..74522cc711
--- /dev/null
+++ b/plug-ins/file-dds/LICENSE
@@ -0,0 +1,21 @@
+/*
+ DDS GIMP plugin
+
+ Copyright (C) 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.
+*/
diff --git a/plug-ins/file-dds/LICENSE.nvtt b/plug-ins/file-dds/LICENSE.nvtt
new file mode 100644
index 0000000000..a42204cc9e
--- /dev/null
+++ b/plug-ins/file-dds/LICENSE.nvtt
@@ -0,0 +1,23 @@
+// Copyright (c) 2009-2011 Ignacio Castano <castano gmail com>
+// Copyright (c) 2007-2009 NVIDIA Corporation -- Ignacio Castano <icastano nvidia com>
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
diff --git a/plug-ins/file-dds/Makefile.am b/plug-ins/file-dds/Makefile.am
new file mode 100644
index 0000000000..9c4aa8fb3a
--- /dev/null
+++ b/plug-ins/file-dds/Makefile.am
@@ -0,0 +1,65 @@
+## 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
+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.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) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_dds_RC)
diff --git a/plug-ins/file-dds/README b/plug-ins/file-dds/README
new file mode 100644
index 0000000000..40903432b8
--- /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/README.dxt b/plug-ins/file-dds/README.dxt
new file mode 100644
index 0000000000..9de2adf7b9
--- /dev/null
+++ b/plug-ins/file-dds/README.dxt
@@ -0,0 +1,175 @@
+The following notes are from an email I received from Fabian 'ryg' Giesen. They
+should help answer some questions regarding the code found in dxt.c
+
+---
+
+mul8bit: This formula is equivalent to (a*b)/255 for a fairly large set of
+values (didn't bother finding out the exact bounds). It's a fairly well-known
+trick also used in other parts of the GIMP codebase (among others) and was
+first documented by Jim Blinn, I think. A good reference is his book "Dirty
+Pixels".
+
+---
+
+lerp_rgb: The expression computed is exactly equivalent to mul8bit(a[i],255-f)
++ mul8bit(b[i],f) - I just verified that by brute force for -255 <= b[i]-a[i]
+<= 255 because I couldn't be bothered to find a derivation for this :) . You
+customarily use a factor between 0 and 256 incluse for LERPing if you can, but
+normal DXT blocks have colors placed at 1/3 and 2/3 between the two
+interpolated colors. 255 is divisible by 3, so lerp_rgb can later be used in
+eval_colors to determine the result of
+
+ a*(1/3) + b*(2/3) and a*(2/3) + b*(1/3)
+
+exactly, which is nice :)
+
+---
+
+dither_block: This is just Floyd-Steinberg dithering. Distributing the error
+terms to the adjacent pixels for each source pixel is the customary variant to
+write this, but since blocks are so small, it's nearly all boundary
+cases; "gathering" the error terms per source pixel turned out to be simpler.
+
+---
+
+match_colors_block: This includes a few tricks. We want to map each source
+color to its nearest representable color (index), using the euclidean distance
+as a metric.
+
+The obvious, brute-force way is to just compare squared distances to the 4
+representable colors for each source pixel (using, for example,
+color_distance); this requires a lot of arithmetic operations.
+
+Instead, the code uses the fact that the 4 colors lie on a line in RGB space
+(only approximately in truth, since we have discrete steps). It's a well-known
+fact in geometry that if P is the closest point to the line L in 3D space, and
+Q is the point closest to P on L, then (P-Q) is orthogonal to the direction of
+L. So (in R3 at least), we can simply determine Q by projecting P onto the
+direction vector of L, which is done by the 16 dot products in the first for
+loop. Since the RGB values have discrete steps in reality, this is just an
+approximation, but a quite good one.
+
+The loop after that determines where the 4 actually representable colors lie
+along the line. After that, you simply need to determine which of those 4
+values your current pixel's dot product is closest to. Instead of doing a
+bunch of comparisions per pixel, the code computes the points along the line
+at which the decision would change (that's c0pt, halfpt and c3pt). This would
+still require 3 comparisions; by testing the middle point - which is halfpt -
+first, one point is always excluded from consideration, which reduces the
+number of compares to two in all cases. No big deal, but hey, why not :)
+
+Similarly, instead of dithering full RGB values, I just dither the dot product
+values. Again, by my experiments this works just as well and reduces the
+amount of work significantly.
+
+---
+
+optimize_colors_block: This first determines min/max/mean for r,g,b and the
+covariance matrix for the color distribution. The latter is used to determine
+the principal component (=eigenvector with largest eigenvalue) of that color
+distribution, or the direction along which the colors in the block vary most
+(in layman's terms) - the eigenvector is determined using simple power
+iteration (a standard technique). That iteration needs a seed vector; I just
+use (max_r-min_r,max_g-min_g,max_b-min_b), which works well in practice. If
+the iteration converges to a vector with very small magnitude (or zero), which
+can happen sometimes, it just defaults to an approximation of the YCbCr Y
+vector (scaled appropriately to make sure no precision is lost with the dot
+products).
+
+This is then used as an initial approximation for the direction of the line
+through RGB color space that is used to select colors for that block. It
+simply uses the two most extreme points along that axis as the two colors
+stored in the block.
+
+---
+
+refine_block: This takes a block and a chosen set of color indices, and tries
+to determine the optimal endpoints for these indices (i.e. the full process
+is: take block, use color distribution to get rough estimate of optimal
+direction, assign color indices accordingly, use these to get better
+endpoints, assign indices again). The computation just solves a least-squares
+system to minimize the square error between the actual pixels and the
+interpolated colors (solving for the two extremal colors). The least-squares
+computation turns out to boil down to solving a 2x2 system of linear equations
+for each of the RGB color channels; the actual solution is computed using
+Cramer's rule.
+
+The code is somewhat weird (especially the "prods"/"akku" thing), but that's
+just to reduce the amount of computation done (this piece of code is a hot
+spot, so it's worth it).
+
+The (!yy || !xx || xx * yy == xy*xy) part checks whether the system of linear
+equations is degenerate. After pondering about this some months ago, I found
+out that the only case in which this can ever happen is when all pixels of the
+source block get mapped to the same color value. But that case can be handled
+better in any case, by just using the single-color lookup tables. I've
+attached the new version of refine_block using that observation - it both
+increases performance (a bit) and image quality, so it's pretty neat.
+
+---
+
+encode_alpha_block_DXT5: The only thing that shouldn't be obvious is the index
+computation. This just uses some two's complement arithmetic and bit shuffling
+to avoid computing
+
+ 7 * (in_alpha - min_alpha) / (max_alpha - min_alpha)
+
+which would be more expensive. (The extra calc with idx is just because of the
+weird DXT color numbering).
+
+---
+
+Some more notes on the general flow:
+
+The computation without dithering is just as I explained in the part about
+refine_block:
+1. Calc initial endpoints directly from block colors
+2. Determine color indices for these endpoints
+3. Optimize endpoints given color indices
+4. Determine new color indices given optimized endpoints
+
+With dithering, there's a twist: The first two steps are done using a version
+of the block dithered to colors that are representable using 16-bit 565 RGB
+values. I've found that this significantly improves visual quality with
+dithering; if you don't do this, the colors inside a block typically vary too
+little for dithering to be useful. This process decreases objective quality
+but typically looks notably better.
+
+The single-color match (omatch5/omatch6) trick: If the block only contains a
+single color (or, for the improved version of refine_block, if the color
+values are sufficiently close to all map to the same index), an optimal
+solution can be used instead.
+
+You normally want solid-color blocks to map to solid-color block (because
+dithering patterns are very obvious otherwise). This means that all color
+indices for the block are going to be identical, i.e. all 0, 1, 2 or 3. All-0
+is symmetrical to all-1 (with the endpoints flipped), and all-2 is symmetrical
+to all-3 (again with the endpoints flipped). So you only need to consider
+all-0 or all-2 indices for the block. Furthermore, all-0 means that the first
+endpoint specified in the block gets used for all pixels; you can get the same
+result by setting both endpoints to the same value and using index 2 for
+everything.
+
+In short, you can always set all indices to 2 without sacrificing any quality
+whatsoever. For any of the color components R,G,B, you then want to determine
+5-bit or 6-bit values a and b such that
+
+ expand[a]*(2/3) + expand[b]*(1/3) is as close as possible to R/G/B
+
+and that's exactly what's in omatch5 (for 5-bit values) and omatch6 (for 6-bit
+values).
+
+If you use the 3-color+transparency mode of DXT1, you need separate versions
+for omatch5/omatch6 for this case, since the interpolated value is exactly
+halfway between the two endpoints instead of 1/3 of the way along. But I
+recommend against that mode, because even if your top-level mipmap has 1-bit
+transparency, mipmaps will have more than 2 distinct values, and the DXT mode
+is selected per texture. That's why my original code doesn't support the
+3-color mode of DXT1 at all: I don't think it's useful in practice.
+
+Not sure if all of this is useful to you or not, but I guess it might make
+sense to at least put this in a seperate text file or whatever, because
+otherwise it's really quite hard to see what's going on in some places.
+
+Cheers,
+-Fabian "ryg" Giesen
diff --git a/plug-ins/file-dds/TODO b/plug-ins/file-dds/TODO
new file mode 100644
index 0000000000..94b0411105
--- /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 0000000000..edc8f6c93e
--- /dev/null
+++ b/plug-ins/file-dds/color.c
@@ -0,0 +1,55 @@
+/*
+ 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>
+
+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 0000000000..8cb1f544d9
--- /dev/null
+++ b/plug-ins/file-dds/color.h
@@ -0,0 +1,91 @@
+/*
+ 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.
+*/
+
+#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 coefficents, 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
diff --git a/plug-ins/file-dds/dds.c b/plug-ins/file-dds/dds.c
new file mode 100644
index 0000000000..2e1f2dfe12
--- /dev/null
+++ b/plug-ins/file-dds/dds.c
@@ -0,0 +1,373 @@
+/*
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "ddsplugin.h"
+#include "dds.h"
+#include "misc.h"
+
+FILE *errFile;
+gchar *prog_name = "dds";
+gchar *filename;
+gint interactive_dds;
+
+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, 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"}
+};
+
+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"}
+};
+
+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",
+ "<Load>/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",
+ "<Save>/DDS image",
+ "INDEXED, GRAY, RGB",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS(save_args), 0,
+ save_args, 0);
+
+ gimp_register_file_handler_mime(SAVE_PROC, "image/dds");
+ gimp_register_save_handler(SAVE_PROC,
+ "dds",
+ "");
+
+ gimp_install_procedure(DECODE_YCOCG_PROC,
+ "Converts YCoCg encoded pixels to RGB",
+ "Converts YCoCg encoded pixels to RGB",
+ "Shawn Kirst",
+ "Shawn Kirst",
+ "2008",
+ "<Image>/Filters/Colors/Decode YCoCg",
+ "RGBA",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS(decode_args), 0,
+ decode_args, 0);
+
+ 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",
+ "<Image>/Filters/Colors/Decode YCoCg (scaled)",
+ "RGBA",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS(decode_args), 0,
+ decode_args, 0);
+
+ 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",
+ "<Image>/Filters/Colors/Decode Alpha exponent",
+ "RGBA",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS(decode_args), 0,
+ decode_args, 0);
+
+}
+
+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);
+ interactive_dds = 1;
+ gimp_get_data(LOAD_PROC, &dds_read_vals);
+ break;
+ case GIMP_RUN_NONINTERACTIVE:
+ interactive_dds = 0;
+ dds_read_vals.show_dialog = 0;
+ 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);
+ if(status == GIMP_PDB_SUCCESS && imageID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = imageID;
+ if(interactive_dds)
+ 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);
+ interactive_dds = 1;
+ break;
+ case GIMP_RUN_NONINTERACTIVE:
+ interactive_dds = 0;
+ 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);
+ interactive_dds = 0;
+ break;
+ default:
+ break;
+ }
+
+ if(dds_write_vals.gamma < 1e-04f)
+ dds_write_vals.gamma = gimp_gamma();
+
+ if(status == GIMP_PDB_SUCCESS)
+ {
+ status = write_dds(param[3].data.d_string, imageID, drawableID);
+ 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);
+ }
+ 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();
+ }
+ 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 0000000000..6affc41a5c
--- /dev/null
+++ b/plug-ins/file-dds/dds.h
@@ -0,0 +1,327 @@
+/*
+ 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.
+*/
+
+#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
diff --git a/plug-ins/file-dds/ddsplugin.h b/plug-ins/file-dds/ddsplugin.h
new file mode 100644
index 0000000000..809ea34b0a
--- /dev/null
+++ b/plug-ins/file-dds/ddsplugin.h
@@ -0,0 +1,78 @@
+/*
+ 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.
+*/
+
+#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 show_dialog;
+ int mipmaps;
+ int decode_images;
+} DDSReadVals;
+
+extern DDSWriteVals dds_write_vals;
+extern DDSReadVals dds_read_vals;
+
+extern GimpPDBStatusType read_dds(gchar *filename, gint32 *imageID);
+extern GimpPDBStatusType write_dds(gchar *, gint32, gint32);
+
+extern gint interactive_dds;
+extern gchar *prog_name;
+extern gchar *filename;
+extern FILE *errorFile;
+
+#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
diff --git a/plug-ins/file-dds/ddsread.c b/plug-ins/file-dds/ddsread.c
new file mode 100644
index 0000000000..c2af3d50b2
--- /dev/null
+++ b/plug-ins/file-dds/ddsread.c
@@ -0,0 +1,1235 @@
+/*
+ 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 <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 "ddsplugin.h"
+#include "dds.h"
+#include "dxt.h"
+#include "endian.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 int runme = 0;
+
+GimpPDBStatusType read_dds(gchar *filename, gint32 *imageID)
+{
+ 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 && dds_read_vals.show_dialog)
+ {
+ 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]);
+
+ *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->flags & DDSD_PITCH) == (hdr->flags & DDSD_LINEARSIZE))
+ {
+ //g_message("Warning: DDSD_PITCH or DDSD_LINEARSIZE is not set.\n");
+ if(hdr->pixelfmt.flags & DDPF_FOURCC)
+ hdr->flags |= DDSD_LINEARSIZE;
+ else
+ 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','3') &&
+ 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','3'):
+ 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 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_NORMAL_MODE);
+ 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','3'): format = DDS_COMPRESS_BC2; break;
+ 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), 1.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), 1.0,
+ bablfmt, pixels, GEGL_AUTO_ROWSTRIDE);
+ }
+ else if(hdr->pixelfmt.flags & DDPF_FOURCC)
+ {
+ unsigned char *dst;
+
+ if(!(hdr->flags & DDSD_LINEARSIZE))
+ {
+ g_message("Image marked as compressed, but DDSD_LINEARSIZE is not set.\n");
+ return(0);
+ }
+
+ 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), 1.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), 1.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)
+{
+ unsigned char 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 = 1;
+ 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,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_signal_connect(GTK_OBJECT(dlg), "response",
+ GTK_SIGNAL_FUNC(load_dialog_response),
+ 0);
+ gtk_signal_connect(GTK_OBJECT(dlg), "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ 0);
+
+ vbox = gtk_vbox_new(0, 8);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->vbox), vbox, 1, 1, 0);
+ gtk_widget_show(vbox);
+
+ check = gtk_check_button_new_with_label("Load mipmaps");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), dds_read_vals.mipmaps);
+ gtk_signal_connect(GTK_OBJECT(check), "clicked",
+ GTK_SIGNAL_FUNC(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_label("Automatically decode YCoCg/AExp images when detected");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), dds_read_vals.decode_images);
+ gtk_signal_connect(GTK_OBJECT(check), "clicked",
+ GTK_SIGNAL_FUNC(toggle_clicked), &dds_read_vals.decode_images);
+ gtk_box_pack_start(GTK_BOX(vbox), check, 1, 1, 0);
+ gtk_widget_show(check);
+
+ check = gtk_check_button_new_with_label("Show this dialog");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), dds_read_vals.show_dialog);
+ gtk_signal_connect(GTK_OBJECT(check), "clicked",
+ GTK_SIGNAL_FUNC(toggle_clicked), &dds_read_vals.show_dialog);
+ gtk_box_pack_start(GTK_BOX(vbox), check, 1, 1, 0);
+ gtk_widget_show(check);
+
+ gtk_widget_show(dlg);
+
+ runme = 0;
+
+ 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 0000000000..b81770f00f
--- /dev/null
+++ b/plug-ins/file-dds/ddswrite.c
@@ -0,0 +1,2122 @@
+/*
+ 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 <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 "ddsplugin.h"
+#include "dds.h"
+#include "dxt.h"
+#include "mipmap.h"
+#include "endian.h"
+#include "imath.h"
+#include "color.h"
+
+static gint save_dialog(gint32 image_id, gint32 drawable);
+static void save_dialog_response(GtkWidget *widget, gint response_id, gpointer data);
+static int write_image(FILE *fp, gint32 image_id, gint32 drawable_id);
+
+static int runme = 0;
+
+enum
+{
+ COMBO_VALUE, COMBO_STRING, COMBO_SENSITIVE
+};
+
+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 gint is_cubemap = 0;
+static gint is_volume = 0;
+static gint is_array = 0;
+static gint is_mipmap_chain_valid = 0;
+
+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
+{
+ int value;
+ char *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 int check_mipmaps(gint32 image_id, int savetype)
+{
+ gint *layers, num_layers;
+ int i, j, w, h, mipw, miph, num_mipmaps, num_surfaces = 0;
+ int min_surfaces = 1, max_surfaces = 1;
+ int valid = 1;
+ 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 = 0;
+ 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 = 0;
+ break;
+ }
+ }
+ }
+
+ return(valid);
+}
+
+static int check_cubemap(gint32 image_id)
+{
+ gint *layers, num_layers;
+ int cubemap = 1, i, j, k, w, h;
+ char *layer_name;
+ GimpImageType type;
+
+ layers = gimp_image_get_layers(image_id, &num_layers);
+
+ if(num_layers < 6) return(0);
+
+ /* 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(0);
+
+ /* 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 = 0;
+ 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 = 0;
+ }
+ }
+ }
+
+ 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 = 0;
+ 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 = 0;
+ }
+ }
+
+ /* 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 = 0;
+ }
+ }
+ }
+
+ return(cubemap);
+}
+
+static int check_volume(gint32 image_id)
+{
+ gint *layers, num_layers;
+ int volume = 0, i, w, h;
+ GimpImageType type;
+
+ layers = gimp_image_get_layers(image_id, &num_layers);
+
+ if(num_layers > 1)
+ {
+ volume = 1;
+
+ /* 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 = 0;
+ }
+
+ 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 = 0;
+ }
+ }
+ }
+
+ return(volume);
+}
+
+static int check_array(gint32 image_id)
+{
+ gint *layers, num_layers;
+ int array = 0, 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 = 1;
+
+ /* 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 = 0;
+ }
+
+ 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 = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ return(array);
+}
+
+static int get_array_size(gint32 image_id)
+{
+ gint *layers, num_layers;
+ int i, w, h, 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)
+{
+ 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 int write_image(FILE *fp, gint32 image_id, gint32 drawable_id)
+{
+ GimpImageType drawable_type;
+ GimpImageBaseType basetype;
+ int i, w, h, bpp = 0, fmtbpp = 0, has_alpha = 0;
+ int num_mipmaps;
+ unsigned char hdr[DDS_HEADERSIZE], hdr10[DDS_HEADERSIZE_DX10];
+ unsigned int flags = 0, pflags = 0, caps = 0, caps2 = 0, size = 0;
+ unsigned int rmask = 0, gmask = 0, bmask = 0, amask = 0;
+ unsigned int fourcc = 0;
+ DXGI_FORMAT dxgi_format = DXGI_FORMAT_UNKNOWN;
+ gint32 num_layers, *layers;
+ guchar *cmap;
+ gint colors;
+ unsigned char zero[4] = {0, 0, 0, 0};
+ int is_dx10 = 0, 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(1);
+}
+
+static GtkWidget *string_value_combo_new(string_value_t *strings,
+ int active_value)
+{
+ GtkWidget *opt;
+ GtkCellRenderer *renderer;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ int i, 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)
+{
+ int 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,
+ int value, int sensitive)
+{
+ 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_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,
+ int 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 = 1;
+ 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, 1);
+ 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, 0);
+ 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)
+{
+ 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.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)
+{
+ int *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, 1);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), 0);
+ }
+ else
+ {
+ gtk_widget_set_sensitive(spin, 0);
+ 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 adv_opt_expanded(GtkWidget *widget, gpointer data)
+{
+ dds_write_vals.show_adv_opt = !gtk_expander_get_expanded(GTK_EXPANDER(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, *vbox2, *hbox;
+ GtkWidget *table;
+ GtkWidget *label;
+ GtkWidget *opt;
+ GtkWidget *check;
+ GtkWidget *spin;
+ GtkWidget *expander;
+ 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("Save as DDS", "dds", NULL, GTK_WIN_POS_MOUSE,
+ gimp_standard_help_func, SAVE_PROC,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_signal_connect(GTK_OBJECT(dlg), "response",
+ GTK_SIGNAL_FUNC(save_dialog_response),
+ 0);
+ gtk_signal_connect(GTK_OBJECT(dlg), "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ 0);
+
+ gtk_window_set_resizable(GTK_WINDOW(dlg), 0);
+
+ vbox = gtk_vbox_new(0, 4);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->vbox), vbox, 1, 1, 0);
+ gtk_widget_show(vbox);
+
+ table = gtk_table_new(4, 2, 0);
+ gtk_widget_show(table);
+ gtk_box_pack_start(GTK_BOX(vbox), table, 1, 1, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 4);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 8);
+
+ label = gtk_label_new("Compression:");
+ gtk_widget_show(label);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
+ (GtkAttachOptions)(GTK_FILL),
+ (GtkAttachOptions)(0), 0, 0);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+
+ opt = string_value_combo_new(compression_strings,
+ dds_write_vals.compression);
+ gtk_widget_show(opt);
+ gtk_table_attach(GTK_TABLE(table), opt, 1, 2, 0, 1,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions)(GTK_EXPAND), 0, 0);
+
+ gtk_signal_connect(GTK_OBJECT(opt), "changed",
+ GTK_SIGNAL_FUNC(compression_selected), 0);
+
+ compress_opt = opt;
+
+ label = gtk_label_new("Format:");
+ gtk_widget_show(label);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
+ (GtkAttachOptions)(GTK_FILL),
+ (GtkAttachOptions)(0), 0, 0);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+
+ opt = string_value_combo_new(format_strings, dds_write_vals.format);
+ gtk_widget_show(opt);
+ gtk_table_attach(GTK_TABLE(table), opt, 1, 2, 1, 2,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions)(GTK_EXPAND), 0, 0);
+
+ gtk_signal_connect(GTK_OBJECT(opt), "changed",
+ GTK_SIGNAL_FUNC(string_value_combo_selected),
+ &dds_write_vals.format);
+
+ gtk_widget_set_sensitive(opt, dds_write_vals.compression == DDS_COMPRESS_NONE);
+
+ format_opt = opt;
+
+ label = gtk_label_new("Save:");
+ gtk_widget_show(label);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3,
+ (GtkAttachOptions)(GTK_FILL),
+ (GtkAttachOptions)(0), 0, 0);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+
+ opt = string_value_combo_new(save_type_strings, dds_write_vals.savetype);
+ gtk_widget_show(opt);
+ gtk_table_attach(GTK_TABLE(table), opt, 1, 2, 2, 3,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions)(GTK_EXPAND), 0, 0);
+
+ gtk_signal_connect(GTK_OBJECT(opt), "changed",
+ GTK_SIGNAL_FUNC(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);
+
+ label = gtk_label_new("Mipmaps:");
+ gtk_widget_show(label);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4,
+ (GtkAttachOptions)(GTK_FILL),
+ (GtkAttachOptions)(0), 0, 0);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+
+ opt = string_value_combo_new(mipmap_strings,
+ dds_write_vals.mipmaps);
+ gtk_widget_show(opt);
+ gtk_table_attach(GTK_TABLE(table), opt, 1, 2, 3, 4,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions)(GTK_EXPAND), 0, 0);
+
+ gtk_signal_connect(GTK_OBJECT(opt), "changed",
+ GTK_SIGNAL_FUNC(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_hbox_new(0, 8);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, 1, 1, 0);
+ gtk_widget_show(hbox);
+
+ check = gtk_check_button_new_with_label("Transparent index:");
+ gtk_box_pack_start(GTK_BOX(hbox), check, 0, 0, 0);
+ gtk_signal_connect(GTK_OBJECT(check), "clicked",
+ GTK_SIGNAL_FUNC(transindex_clicked), 0);
+ gtk_widget_show(check);
+
+ spin = gtk_spin_button_new(GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 255, 1, 1, 0)), 1, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), spin, 1, 1, 0);
+ gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin),
+ GTK_UPDATE_IF_VALID);
+ gtk_signal_connect(GTK_OBJECT(spin), "value_changed",
+ GTK_SIGNAL_FUNC(transindex_changed), 0);
+ gtk_widget_show(spin);
+
+ g_object_set_data(G_OBJECT(check), "spin", spin);
+
+ if(basetype != GIMP_INDEXED)
+ {
+ gtk_widget_set_sensitive(check, 0);
+ gtk_widget_set_sensitive(spin, 0);
+ }
+ else if(dds_write_vals.transindex < 0)
+ {
+ gtk_widget_set_sensitive(spin, 0);
+ }
+ else if(dds_write_vals.transindex >= 0)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), 1);
+ 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, 0);
+ }
+
+ expander = gtk_expander_new("<b>Advanced Options</b>");
+ gtk_expander_set_use_markup(GTK_EXPANDER(expander), 1);
+ gtk_expander_set_expanded(GTK_EXPANDER(expander), dds_write_vals.show_adv_opt);
+ gtk_expander_set_spacing(GTK_EXPANDER(expander), 8);
+ gtk_signal_connect(GTK_OBJECT(expander), "activate",
+ GTK_SIGNAL_FUNC(adv_opt_expanded), 0);
+ gtk_box_pack_start(GTK_BOX(vbox), expander, 1, 1, 0);
+ gtk_widget_show(expander);
+
+
+ vbox2 = gtk_vbox_new(0, 4);
+ gtk_container_add(GTK_CONTAINER(expander), vbox2);
+ gtk_widget_show(vbox2);
+
+ frame = gtk_frame_new("Compression");
+ gtk_box_pack_start(GTK_BOX(vbox2), frame, 1, 1, 0);
+ gtk_widget_show(frame);
+
+ table = gtk_table_new(1, 2, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 4);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 8);
+ gtk_container_set_border_width(GTK_CONTAINER(table), 8);
+ gtk_container_add(GTK_CONTAINER(frame), table);
+ gtk_widget_show(table);
+
+ check = gtk_check_button_new_with_label("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, 0, 2, 0, 1,
+ (GtkAttachOptions)(GTK_FILL),
+ (GtkAttachOptions)(0), 0, 0);
+ gtk_signal_connect(GTK_OBJECT(check), "clicked",
+ GTK_SIGNAL_FUNC(toggle_clicked), &dds_write_vals.perceptual_metric);
+ gtk_widget_show(check);
+
+ pm_chk = check;
+
+ frame = gtk_frame_new("Mipmaps");
+ gtk_box_pack_start(GTK_BOX(vbox2), frame, 1, 1, 0);
+ gtk_widget_show(frame);
+
+ table = gtk_table_new(7, 2, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 4);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 8);
+ gtk_container_set_border_width(GTK_CONTAINER(table), 8);
+ gtk_container_add(GTK_CONTAINER(frame), table);
+ gtk_widget_show(table);
+
+ label = gtk_label_new("Filter:");
+ gtk_widget_show(label);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
+ (GtkAttachOptions)(GTK_FILL),
+ (GtkAttachOptions)(0), 0, 0);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+
+ opt = string_value_combo_new(mipmap_filter_strings,
+ dds_write_vals.mipmap_filter);
+ gtk_widget_show(opt);
+ gtk_table_attach(GTK_TABLE(table), opt, 1, 2, 0, 1,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions)(GTK_EXPAND), 0, 0);
+
+ gtk_signal_connect(GTK_OBJECT(opt), "changed",
+ GTK_SIGNAL_FUNC(string_value_combo_selected),
+ &dds_write_vals.mipmap_filter);
+
+ mipmap_filter_opt = opt;
+
+ label = gtk_label_new("Wrap mode:");
+ gtk_widget_show(label);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
+ (GtkAttachOptions)(GTK_FILL),
+ (GtkAttachOptions)(0), 0, 0);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+
+ opt = string_value_combo_new(mipmap_wrap_strings,
+ dds_write_vals.mipmap_wrap);
+ gtk_widget_show(opt);
+ gtk_table_attach(GTK_TABLE(table), opt, 1, 2, 1, 2,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions)(GTK_EXPAND), 0, 0);
+
+ gtk_signal_connect(GTK_OBJECT(opt), "changed",
+ GTK_SIGNAL_FUNC(string_value_combo_selected),
+ &dds_write_vals.mipmap_wrap);
+
+ mipmap_wrap_opt = opt;
+
+ check = gtk_check_button_new_with_label("Apply 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,
+ (GtkAttachOptions)(GTK_FILL),
+ (GtkAttachOptions)(0), 0, 0);
+ gtk_signal_connect(GTK_OBJECT(check), "clicked",
+ GTK_SIGNAL_FUNC(gamma_correct_clicked), NULL);
+ gtk_widget_show(check);
+
+ gamma_chk = check;
+
+ check = gtk_check_button_new_with_label("Use sRGB 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,
+ (GtkAttachOptions)(GTK_FILL),
+ (GtkAttachOptions)(0), 0, 0);
+ gtk_signal_connect(GTK_OBJECT(check), "clicked",
+ GTK_SIGNAL_FUNC(srgb_clicked), NULL);
+ gtk_widget_show(check);
+
+ srgb_chk = check;
+
+ label = gtk_label_new("Gamma:");
+ gtk_widget_show(label);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5,
+ (GtkAttachOptions)(GTK_FILL),
+ (GtkAttachOptions)(0), 0, 0);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+
+ spin = gtk_spin_button_new(GTK_ADJUSTMENT(gtk_adjustment_new(dds_write_vals.gamma, 1e-05, 100, 0.1, 0.5,
0)), 1, 1);
+ gtk_table_attach(GTK_TABLE(table), spin, 1, 2, 4, 5,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions)(GTK_EXPAND), 0, 0);
+ gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_IF_VALID);
+ gtk_signal_connect(GTK_OBJECT(spin), "value_changed",
+ GTK_SIGNAL_FUNC(gamma_changed), 0);
+ gtk_widget_show(spin);
+
+ gamma_spin = spin;
+
+ check = gtk_check_button_new_with_label("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,
+ (GtkAttachOptions)(GTK_FILL),
+ (GtkAttachOptions)(0), 0, 0);
+ gtk_signal_connect(GTK_OBJECT(check), "clicked",
+ GTK_SIGNAL_FUNC(alpha_coverage_clicked), NULL);
+ gtk_widget_show(check);
+
+ alpha_coverage_chk = check;
+
+ label = gtk_label_new("Alpha test threshold:");
+ gtk_widget_show(label);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 6, 7,
+ (GtkAttachOptions)(GTK_FILL),
+ (GtkAttachOptions)(0), 0, 0);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+
+ spin = gtk_spin_button_new(GTK_ADJUSTMENT(gtk_adjustment_new(dds_write_vals.alpha_test_threshold, 0, 1,
0.01, 0.1, 0)), 1, 2);
+ gtk_table_attach(GTK_TABLE(table), spin, 1, 2, 6, 7,
+ (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions)(GTK_EXPAND), 0, 0);
+ gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_IF_VALID);
+ gtk_signal_connect(GTK_OBJECT(spin), "value_changed",
+ GTK_SIGNAL_FUNC(alpha_test_threshold_changed), 0);
+ gtk_widget_show(spin);
+
+ 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 = 0;
+
+ 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 0000000000..8ebbe98123
--- /dev/null
+++ b/plug-ins/file-dds/dxt.c
@@ -0,0 +1,1412 @@
+/*
+ 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.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 coefficents */
+ 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 0000000000..a49c85a803
--- /dev/null
+++ b/plug-ins/file-dds/dxt.h
@@ -0,0 +1,41 @@
+/*
+ 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.
+*/
+
+#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
diff --git a/plug-ins/file-dds/dxt_tables.h b/plug-ins/file-dds/dxt_tables.h
new file mode 100644
index 0000000000..14aee2b52a
--- /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
diff --git a/plug-ins/file-dds/endian.h b/plug-ins/file-dds/endian.h
new file mode 100644
index 0000000000..53d32f2dc5
--- /dev/null
+++ b/plug-ins/file-dds/endian.h
@@ -0,0 +1,71 @@
+/*
+ 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.
+*/
+
+#ifndef ENDIAN_H
+#define ENDIAN_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
diff --git a/plug-ins/file-dds/imath.h b/plug-ins/file-dds/imath.h
new file mode 100644
index 0000000000..6f4dbc759f
--- /dev/null
+++ b/plug-ins/file-dds/imath.h
@@ -0,0 +1,67 @@
+/*
+ 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.
+*/
+
+#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
diff --git a/plug-ins/file-dds/mipmap.c b/plug-ins/file-dds/mipmap.c
new file mode 100644
index 0000000000..71b58d84e5
--- /dev/null
+++ b/plug-ins/file-dds/mipmap.c
@@ -0,0 +1,1013 @@
+/*
+ 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;
+
+ if(xsupport <= 0.5f)
+ {
+ xsupport = 0.5f + 1e-10f;
+ xscale = 1.0f;
+ }
+ if(ysupport <= 0.5f)
+ {
+ ysupport = 0.5f + 1e-10f;
+ yscale = 1.0f;
+ }
+
+ unsigned char *tmp;
+
+#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)
+{
+ /* 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;
+ }
+
+ 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;
+
+ 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;
+ }
+
+ unsigned char *tmp1, *tmp2;
+
+ 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;
+ */
+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);
+}
+
+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 0000000000..ff77eb3c46
--- /dev/null
+++ b/plug-ins/file-dds/mipmap.h
@@ -0,0 +1,47 @@
+/*
+ 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.
+*/
+
+#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
diff --git a/plug-ins/file-dds/misc.c b/plug-ins/file-dds/misc.c
new file mode 100644
index 0000000000..4183c27191
--- /dev/null
+++ b/plug-ins/file-dds/misc.c
@@ -0,0 +1,252 @@
+/*
+ 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>
+
+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, 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), 1.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, 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), 1.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, 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), 1.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 0000000000..d54482a9af
--- /dev/null
+++ b/plug-ins/file-dds/misc.h
@@ -0,0 +1,30 @@
+/*
+ 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.
+*/
+
+#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
diff --git a/plug-ins/file-dds/mktables.c b/plug-ins/file-dds/mktables.c
new file mode 100644
index 0000000000..80f5818a04
--- /dev/null
+++ b/plug-ins/file-dds/mktables.c
@@ -0,0 +1,128 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+int mul8bit(int a, int b)
+{
+ int t = a * b + 128;
+ return((t + (t >> 8)) >> 8);
+}
+
+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;
+ }
+ }
+ }
+ }
+}
+
+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);
+}
diff --git a/plug-ins/file-dds/vec.h b/plug-ins/file-dds/vec.h
new file mode 100644
index 0000000000..fd94de6651
--- /dev/null
+++ b/plug-ins/file-dds/vec.h
@@ -0,0 +1,226 @@
+/*
+ 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.
+*/
+
+#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()
+{
+#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
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]