PIKApp/plug-ins/file-dds/ddsread.c

2145 lines
76 KiB
C
Raw Normal View History

2023-09-26 00:35:21 +02:00
/*
* DDS PIKA plugin
*
* Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
* with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301, USA.
*/
/*
** !!! COPYRIGHT NOTICE !!!
**
** The following is based on code (C) 2003 Arne Reuter <homepage@arnereuter.de>
** URL: http://www.dr-reuter.de/arne/dds.html
**
*/
#include "config.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <glib/gstdio.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include <libpika/stdplugins-intl.h>
#include "dds.h"
2023-12-02 20:03:24 +01:00
#include "ddsread.h"
2023-09-26 00:35:21 +02:00
#include "dxt.h"
#include "endian_rw.h"
#include "imath.h"
2023-12-02 20:03:24 +01:00
#include "misc.h"
2023-09-26 00:35:21 +02:00
typedef struct
{
2023-12-02 20:03:24 +01:00
guchar rshift, gshift, bshift, ashift;
guchar rbits, gbits, bbits, abits;
guint rmask, gmask, bmask, amask;
guint bpp, pika_bpp;
guint pika_bps; /* bytes per sample */
guint dxgi_format; /* adjusted dxgi format */
gboolean is_signed; /* TRUE if data uses signed int */
gint tile_height;
guchar *palette;
2023-09-26 00:35:21 +02:00
} dds_load_info_t;
2023-12-02 20:03:24 +01:00
static gboolean read_header (dds_header_t *hdr,
FILE *fp);
static gboolean read_header_dx10 (dds_header_dx10_t *hdr,
FILE *fp);
static gboolean validate_header (dds_header_t *hdr,
GError **error);
static gboolean setup_dxgi_format (dds_header_t *hdr,
const dds_header_dx10_t *dx10hdr,
dds_load_info_t *load_info,
GError **error);
static gboolean load_layer (FILE *fp,
const dds_header_t *hdr,
const dds_header_dx10_t *dx10hdr,
dds_load_info_t *load_info,
PikaImage *image,
guint level,
gchar *prefix,
guint *layer_index,
guchar *pixels,
guchar *buf,
GError **error);
static gboolean load_mipmaps (FILE *fp,
const dds_header_t *hdr,
const dds_header_dx10_t *dx10hdr,
dds_load_info_t *load_info,
PikaImage *image,
gchar *prefix,
guint *layer_index,
guchar *pixels,
guchar *buf,
gboolean read_mipmaps,
GError **error);
static gboolean load_face (FILE *fp,
const dds_header_t *hdr,
const dds_header_dx10_t *dx10hdr,
dds_load_info_t *load_info,
PikaImage *image,
gchar *prefix,
guint *layer_index,
guchar *pixels,
guchar *buf,
gboolean read_mipmaps,
GError **error);
static guchar color_bits (guint mask);
static guchar color_shift (guint mask);
static gboolean load_dialog (PikaProcedure *procedure,
PikaProcedureConfig *config);
/* Read DDS file */
2023-09-26 00:35:21 +02:00
PikaPDBStatusType
2023-12-02 20:03:24 +01:00
read_dds (GFile *file,
PikaImage **ret_image,
gboolean interactive,
PikaProcedure *procedure,
PikaProcedureConfig *config,
GError **error)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
PikaImage *image = NULL;
guint layer_index = 0;
guchar *buf, *pixels;
2023-09-26 00:35:21 +02:00
FILE *fp;
2023-10-30 23:55:30 +01:00
gsize file_size;
2023-09-26 00:35:21 +02:00
dds_header_t hdr;
dds_header_dx10_t dx10hdr;
2023-12-02 20:03:24 +01:00
dds_load_info_t load_info;
2023-09-26 00:35:21 +02:00
GList *layers;
PikaImageBaseType type;
PikaPrecision precision;
gboolean read_mipmaps;
gint i, j;
2023-12-02 20:03:24 +01:00
guint computed_pitch_or_linsize;
gboolean flip_import;
2023-09-26 00:35:21 +02:00
if (interactive)
{
pika_ui_init ("dds");
if (! load_dialog (procedure, config))
return PIKA_PDB_CANCEL;
}
g_object_get (config,
2023-12-02 20:03:24 +01:00
"load-mipmaps", &read_mipmaps,
"flip-image", &flip_import,
2023-09-26 00:35:21 +02:00
NULL);
fp = g_fopen (g_file_peek_path (file), "rb");
if (! fp)
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Could not open '%s' for reading: %s"),
pika_file_get_utf8_name (file), g_strerror (errno));
return PIKA_PDB_EXECUTION_ERROR;
}
2023-12-02 20:03:24 +01:00
/* Get total file size to compare against header info later */
2023-10-30 23:55:30 +01:00
fseek (fp, 0L, SEEK_END);
file_size = ftell (fp);
2023-12-02 20:03:24 +01:00
fseek (fp, 0L, SEEK_SET);
2023-10-30 23:55:30 +01:00
2023-12-02 20:03:24 +01:00
pika_progress_init_printf (_("Loading: %s"), pika_file_get_utf8_name (file));
2023-09-26 00:35:21 +02:00
2023-12-02 20:03:24 +01:00
/* Read standard header */
memset (&hdr, 0, sizeof (dds_header_t));
2023-09-26 00:35:21 +02:00
read_header (&hdr, fp);
2023-12-02 20:03:24 +01:00
/* Read DX10 header if present */
2023-09-26 00:35:21 +02:00
memset (&dx10hdr, 0, sizeof (dds_header_dx10_t));
2023-12-02 20:03:24 +01:00
load_info.dxgi_format = DXGI_FORMAT_UNKNOWN;
load_info.is_signed = FALSE;
2023-09-26 00:35:21 +02:00
if (GETL32 (hdr.pixelfmt.fourcc) == FOURCC ('D','X','1','0'))
{
read_header_dx10 (&dx10hdr, fp);
2023-12-02 20:03:24 +01:00
if (! setup_dxgi_format (&hdr, &dx10hdr, &load_info, error))
2023-09-26 00:35:21 +02:00
{
fclose (fp);
return PIKA_PDB_EXECUTION_ERROR;
}
}
2023-12-02 20:03:24 +01:00
/* Check that header is actually valid */
2023-09-26 00:35:21 +02:00
if (! validate_header (&hdr, error))
{
fclose (fp);
return PIKA_PDB_EXECUTION_ERROR;
}
2023-12-02 20:03:24 +01:00
/* Normalize known non-compliant FourCC codes. */
if ((hdr.pixelfmt.flags & DDPF_FOURCC) && (hdr.pixelfmt.fourcc[1] == 0))
{
guint fourcc = GETL32 (hdr.pixelfmt.fourcc);
/* Unset the FourCC flag as D3D formats will be handled as uncompressed */
hdr.pixelfmt.flags &= ~DDPF_FOURCC;
switch (fourcc)
{
case D3DFMT_Q16W16V16U16: /* = DXGI_FORMAT_R16G16B16A16_SNORM */
case D3DFMT_A16B16G16R16: /* = DXGI_FORMAT_R16G16B16A16_UINT */
hdr.pixelfmt.flags |= DDPF_ALPHAPIXELS | DDPF_RGB;
hdr.pixelfmt.bpp = 64;
load_info.dxgi_format = DXGI_FORMAT_R16G16B16A16_UINT;
if (fourcc == D3DFMT_Q16W16V16U16)
load_info.is_signed = TRUE;
break;
case D3DFMT_A16B16G16R16F: /* = DXGI_FORMAT_R16G16B16A16_FLOAT */
hdr.pixelfmt.flags |= DDPF_ALPHAPIXELS | DDPF_RGB;
hdr.pixelfmt.bpp = 64;
load_info.dxgi_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
break;
case D3DFMT_A32B32G32R32F: /* = DXGI_FORMAT_R32G32B32A32_FLOAT */
hdr.pixelfmt.flags |= DDPF_ALPHAPIXELS | DDPF_RGB;
hdr.pixelfmt.bpp = 128;
load_info.dxgi_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
break;
case D3DFMT_D16_LOCKABLE:
case D3DFMT_D16:
case D3DFMT_L16:
case D3DFMT_INDEX16:
hdr.pixelfmt.flags |= DDPF_RGB;
hdr.pixelfmt.bpp = 16;
hdr.pixelfmt.rmask = 0x0000ffff;
hdr.pixelfmt.gmask = hdr.pixelfmt.bmask = hdr.pixelfmt.amask = 0;
load_info.dxgi_format = DXGI_FORMAT_R16_UNORM;
break;
case D3DFMT_G16R16:
hdr.pixelfmt.flags |= DDPF_RGB;
hdr.pixelfmt.bpp = 32;
hdr.pixelfmt.rmask = 0x0000ffff;
hdr.pixelfmt.gmask = 0xffff0000;
hdr.pixelfmt.bmask = hdr.pixelfmt.amask = 0;
load_info.dxgi_format = DXGI_FORMAT_R16G16_UNORM;
break;
case D3DFMT_G16R16F:
hdr.pixelfmt.flags |= DDPF_RGB;
hdr.pixelfmt.bpp = 32;
hdr.pixelfmt.rmask = 0x0000ffff;
hdr.pixelfmt.gmask = 0xffff0000;
hdr.pixelfmt.bmask = hdr.pixelfmt.amask = 0;
load_info.dxgi_format = DXGI_FORMAT_R16G16_FLOAT;
break;
case D3DFMT_R8G8B8:
hdr.pixelfmt.flags |= DDPF_RGB;
hdr.pixelfmt.bpp = 16;
hdr.pixelfmt.rmask = 0x000000ff;
hdr.pixelfmt.gmask = 0x0000ff00;
hdr.pixelfmt.bmask = hdr.pixelfmt.amask = 0;
load_info.dxgi_format = DXGI_FORMAT_R16_UNORM;
break;
default:
fclose (fp);
g_set_error (error, PIKA_PLUG_IN_ERROR, 0,
_("Unsupported uncompressed FourCC type %u"),
fourcc);
return PIKA_PDB_EXECUTION_ERROR;
}
}
/* A lot of DDS images out there don't set pitch_or_linsize, or set it to a
value we didn't expect. So we will always compute it ourselves. */
if (hdr.pixelfmt.flags & DDPF_FOURCC)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
/* Assume linear size */
computed_pitch_or_linsize = ((hdr.width + 3) >> 2) * ((hdr.height + 3) >> 2);
if (GETL32 (hdr.pixelfmt.fourcc) == FOURCC ('D','X','1','0'))
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
/* Determine scale from DXGI format */
switch (dx10hdr.dxgiFormat)
{
case DXGI_FORMAT_BC1_TYPELESS:
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
case DXGI_FORMAT_BC4_TYPELESS:
case DXGI_FORMAT_BC4_UNORM:
case DXGI_FORMAT_BC4_SNORM:
computed_pitch_or_linsize *= 8;
break;
default:
computed_pitch_or_linsize *= 16;
break;
}
}
else
{
/* Determine scale from FourCC format */
2023-09-26 00:35:21 +02:00
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'):
2023-12-02 20:03:24 +01:00
computed_pitch_or_linsize *= 8;
2023-09-26 00:35:21 +02:00
break;
default:
2023-12-02 20:03:24 +01:00
computed_pitch_or_linsize *= 16;
2023-09-26 00:35:21 +02:00
break;
}
}
2023-12-02 20:03:24 +01:00
}
else
{
/* Assume pitch */
computed_pitch_or_linsize = hdr.height * hdr.width * (hdr.pixelfmt.bpp >> 3);
}
if (computed_pitch_or_linsize != hdr.pitch_or_linsize)
{
g_printerr ("Unexpected 'pitch_or_linsize' (%u) set to %u\n",
hdr.pitch_or_linsize, computed_pitch_or_linsize);
hdr.pitch_or_linsize = computed_pitch_or_linsize;
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
/* Not sure why this is here, but it seems extremely dubious, as to my
* knowledge DDPF_ALPHAPIXELS should only be relevant to uncompressed files.
*/
2023-09-26 00:35:21 +02:00
if (hdr.pixelfmt.flags & DDPF_FOURCC)
{
/* fourcc is dXt* or rXgb */
if (hdr.pixelfmt.fourcc[1] == 'X')
hdr.pixelfmt.flags |= DDPF_ALPHAPIXELS;
}
2023-12-02 20:03:24 +01:00
load_info.rshift = color_shift (hdr.pixelfmt.rmask);
load_info.gshift = color_shift (hdr.pixelfmt.gmask);
load_info.bshift = color_shift (hdr.pixelfmt.bmask);
load_info.ashift = color_shift (hdr.pixelfmt.amask);
load_info.rbits = color_bits (hdr.pixelfmt.rmask);
load_info.gbits = color_bits (hdr.pixelfmt.gmask);
load_info.bbits = color_bits (hdr.pixelfmt.bmask);
load_info.abits = color_bits (hdr.pixelfmt.amask);
if (load_info.rbits <= 8 &&
load_info.gbits <= 8 &&
load_info.bbits <= 8 &&
load_info.abits <= 8)
{
/* 8 bits per sample */
load_info.rmask = (hdr.pixelfmt.rmask >> load_info.rshift) << (8 - load_info.rbits);
load_info.gmask = (hdr.pixelfmt.gmask >> load_info.gshift) << (8 - load_info.gbits);
load_info.bmask = (hdr.pixelfmt.bmask >> load_info.bshift) << (8 - load_info.bbits);
load_info.amask = (hdr.pixelfmt.amask >> load_info.ashift) << (8 - load_info.abits);
}
else if (load_info.rbits <= 16 &&
load_info.gbits <= 16 &&
load_info.bbits <= 16 &&
load_info.abits <= 16)
{
/* 16 bits per sample */
load_info.rmask = (hdr.pixelfmt.rmask >> load_info.rshift) << (16 - load_info.rbits);
load_info.gmask = (hdr.pixelfmt.gmask >> load_info.gshift) << (16 - load_info.gbits);
load_info.bmask = (hdr.pixelfmt.bmask >> load_info.bshift) << (16 - load_info.bbits);
load_info.amask = (hdr.pixelfmt.amask >> load_info.ashift) << (16 - load_info.abits);
}
2023-09-26 00:35:21 +02:00
else
2023-12-02 20:03:24 +01:00
{
/* 32 bits per sample in case we need this in the future */
load_info.rmask = (hdr.pixelfmt.rmask >> load_info.rshift) << (32 - load_info.rbits);
load_info.gmask = (hdr.pixelfmt.gmask >> load_info.gshift) << (32 - load_info.gbits);
load_info.bmask = (hdr.pixelfmt.bmask >> load_info.bshift) << (32 - load_info.bbits);
load_info.amask = (hdr.pixelfmt.amask >> load_info.ashift) << (32 - load_info.abits);
}
2023-09-26 00:35:21 +02:00
2023-12-02 20:03:24 +01:00
/* Determine bits-per-pixel needed */
load_info.pika_bps = 1; /* Most formats will be converted to 1 byte per sample */
if ((hdr.pixelfmt.flags & DDPF_FOURCC) && (hdr.pixelfmt.fourcc[1] != 0))
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
if (GETL32 (hdr.pixelfmt.fourcc) == FOURCC ('D','X','1','0'))
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
/* bpp from DX10 format */
switch (dx10hdr.dxgiFormat)
{
case DXGI_FORMAT_BC4_TYPELESS:
case DXGI_FORMAT_BC4_UNORM:
case DXGI_FORMAT_BC4_SNORM:
load_info.bpp = load_info.pika_bpp = 1;
type = PIKA_GRAY;
break;
case DXGI_FORMAT_BC5_TYPELESS:
case DXGI_FORMAT_BC5_UNORM:
case DXGI_FORMAT_BC5_SNORM:
load_info.bpp = load_info.pika_bpp = 3;
type = PIKA_RGB;
break;
case DXGI_FORMAT_BC6H_TYPELESS:
case DXGI_FORMAT_BC6H_UF16:
case DXGI_FORMAT_BC6H_SF16:
load_info.bpp = load_info.pika_bpp = 6;
load_info.pika_bps = 2;
type = PIKA_RGB;
break;
default:
load_info.bpp = load_info.pika_bpp = 4;
type = PIKA_RGB;
break;
}
}
else
{
/* bpp from FourCC */
switch (GETL32 (hdr.pixelfmt.fourcc))
{
case FOURCC ('A','T','I','1'):
case FOURCC ('B','C','4','U'):
case FOURCC ('B','C','4','S'):
load_info.bpp = load_info.pika_bpp = 1;
type = PIKA_GRAY;
break;
case FOURCC ('A','T','I','2'):
case FOURCC ('B','C','5','U'):
case FOURCC ('B','C','5','S'):
load_info.bpp = load_info.pika_bpp = 3;
type = PIKA_RGB;
break;
default:
load_info.bpp = load_info.pika_bpp = 4;
type = PIKA_RGB;
break;
}
2023-09-26 00:35:21 +02:00
}
}
else
{
2023-12-02 20:03:24 +01:00
load_info.bpp = hdr.pixelfmt.bpp >> 3;
2023-09-26 00:35:21 +02:00
2023-12-02 20:03:24 +01:00
if (hdr.pixelfmt.flags & DDPF_BUMPDUDV)
load_info.is_signed = TRUE;
if (load_info.bpp == 2)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
if (hdr.pixelfmt.amask == 0xf000) /* RGBA4 */
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
load_info.pika_bpp = 4;
2023-09-26 00:35:21 +02:00
type = PIKA_RGB;
}
2023-12-02 20:03:24 +01:00
else if (hdr.pixelfmt.amask == 0xff00) /* L8A8 */
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
if (hdr.pixelfmt.flags & DDPF_LUMINANCE)
{
load_info.pika_bpp = 2;
type = PIKA_GRAY;
}
else
{
load_info.pika_bpp = 4;
type = PIKA_RGB;
}
}
else if (hdr.pixelfmt.rmask == 0xff) /* R8G8 */
{
load_info.pika_bpp = 3;
type = PIKA_RGB;
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
else if (hdr.pixelfmt.bmask == 0x1f) /* R5G6B5 or RGB5A1 */
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
if (hdr.pixelfmt.amask == 0x8000) /* RGB5A1 */
load_info.pika_bpp = 4;
2023-09-26 00:35:21 +02:00
else
2023-12-02 20:03:24 +01:00
load_info.pika_bpp = 3;
2023-09-26 00:35:21 +02:00
type = PIKA_RGB;
}
2023-12-02 20:03:24 +01:00
else if (hdr.pixelfmt.rmask == 0xffff ||
2023-09-26 00:35:21 +02:00
hdr.pixelfmt.gmask == 0xffff ||
hdr.pixelfmt.bmask == 0xffff ||
2023-12-02 20:03:24 +01:00
hdr.pixelfmt.amask == 0xffff) /* L16 */
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
load_info.pika_bpp = 2;
load_info.pika_bps = 2;
2023-09-26 00:35:21 +02:00
type = PIKA_GRAY;
}
else
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
"Unsupported uncompressed dds format: "
"bpp: %d, Rmask: %x, Gmask: %x, Bmask: %x, Amask: %x",
hdr.pixelfmt.bpp,
hdr.pixelfmt.rmask, hdr.pixelfmt.gmask,
hdr.pixelfmt.bmask, hdr.pixelfmt.amask);
return PIKA_PDB_EXECUTION_ERROR;
}
}
else
{
if (hdr.pixelfmt.flags & DDPF_PALETTEINDEXED8)
{
type = PIKA_INDEXED;
2023-12-02 20:03:24 +01:00
load_info.pika_bpp = 1;
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
else if (hdr.pixelfmt.rmask == 0xe0) /* R3G3B2 */
2023-09-26 00:35:21 +02:00
{
type = PIKA_RGB;
2023-12-02 20:03:24 +01:00
load_info.pika_bpp = 3;
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
else if (load_info.bpp == 4)
2023-09-26 00:35:21 +02:00
{
type = PIKA_RGB;
2023-12-02 20:03:24 +01:00
if (load_info.dxgi_format == DXGI_FORMAT_R16G16_UINT ||
load_info.dxgi_format == DXGI_FORMAT_R16G16_FLOAT)
{
/* Output 16-bit RGB */
load_info.pika_bps = 2;
load_info.pika_bpp = 6;
}
else if (load_info.rbits > 8 ||
load_info.gbits > 8 ||
load_info.bbits > 8 ||
load_info.abits > 8)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
load_info.pika_bps = 2;
load_info.pika_bpp = 8;
2023-09-26 00:35:21 +02:00
}
else
{
2023-12-02 20:03:24 +01:00
load_info.pika_bpp = load_info.bpp;
2023-09-26 00:35:21 +02:00
}
}
2023-12-02 20:03:24 +01:00
else if (load_info.bpp == 8 ||
load_info.bpp == 16)
{
type = PIKA_RGB;
load_info.pika_bpp = load_info.bpp;
load_info.pika_bps = load_info.pika_bpp / 4;
}
2023-09-26 00:35:21 +02:00
else
{
2023-12-02 20:03:24 +01:00
/* Test alpha-only image */
if (load_info.bpp == 1 && (hdr.pixelfmt.flags & DDPF_ALPHA))
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
load_info.pika_bpp = 2;
2023-09-26 00:35:21 +02:00
type = PIKA_GRAY;
}
else
{
2023-12-02 20:03:24 +01:00
load_info.pika_bpp = load_info.bpp;
type = (load_info.bpp == 1) ? PIKA_GRAY : PIKA_RGB;
2023-09-26 00:35:21 +02:00
}
}
}
}
2023-12-02 20:03:24 +01:00
/* Set up canvas precision */
if (load_info.pika_bps == 1)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
precision = PIKA_PRECISION_U8_NON_LINEAR;
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
else if (load_info.pika_bps == 2)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
if (load_info.dxgi_format == DXGI_FORMAT_R16G16B16A16_FLOAT ||
load_info.dxgi_format == DXGI_FORMAT_R16G16_FLOAT)
precision = PIKA_PRECISION_HALF_LINEAR;
else
precision = PIKA_PRECISION_U16_NON_LINEAR;
}
else if (load_info.pika_bps == 4)
{
if (load_info.dxgi_format == DXGI_FORMAT_R32G32B32A32_FLOAT)
precision = PIKA_PRECISION_FLOAT_LINEAR;
else
precision = PIKA_PRECISION_U32_NON_LINEAR;
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
/* Verify header information is accurate to avoid allocating more memory than is actually needed */
if (load_info.bpp < 1 ||
2023-10-30 23:55:30 +01:00
(hdr.pitch_or_linsize > (file_size - sizeof (hdr))))
{
fclose (fp);
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Invalid or corrupted DDS header"));
return PIKA_PDB_EXECUTION_ERROR;
}
2023-12-02 20:03:24 +01:00
/* Generate PIKA image with set precision */
2023-09-26 00:35:21 +02:00
image = pika_image_new_with_precision (hdr.width, hdr.height, type, precision);
if (! image)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOMEM,
_("Could not allocate a new image."));
fclose (fp);
return PIKA_PDB_EXECUTION_ERROR;
}
2023-12-02 20:03:24 +01:00
/* Read palette for indexed DDS */
2023-09-26 00:35:21 +02:00
if (hdr.pixelfmt.flags & DDPF_PALETTEINDEXED8)
{
2023-12-02 20:03:24 +01:00
load_info.palette = g_malloc (256 * 4);
if (fread (load_info.palette, 1, 1024, fp) != 1024)
2023-09-26 00:35:21 +02:00
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Error reading palette."));
fclose (fp);
pika_image_delete (image);
return PIKA_PDB_EXECUTION_ERROR;
}
for (i = j = 0; i < 768; i += 3, j += 4)
{
2023-12-02 20:03:24 +01:00
load_info.palette[i + 0] = load_info.palette[j + 0];
load_info.palette[i + 1] = load_info.palette[j + 1];
load_info.palette[i + 2] = load_info.palette[j + 2];
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
pika_image_set_colormap (image, load_info.palette, 256);
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
load_info.tile_height = pika_tile_height ();
2023-09-26 00:35:21 +02:00
2023-12-02 20:03:24 +01:00
pixels = g_new (guchar, load_info.tile_height * hdr.width * load_info.pika_bpp);
2023-09-26 00:35:21 +02:00
buf = g_malloc (hdr.pitch_or_linsize);
if (! (hdr.caps.caps2 & DDSCAPS2_CUBEMAP) &&
2023-12-02 20:03:24 +01:00
! (hdr.caps.caps2 & DDSCAPS2_VOLUME) &&
dx10hdr.arraySize <= 1) /* Standard image texture with mipmaps */
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
if (! load_layer (fp, &hdr, &dx10hdr, &load_info, image, 0, "", &layer_index,
pixels, buf, error))
2023-09-26 00:35:21 +02:00
{
fclose (fp);
pika_image_delete (image);
return PIKA_PDB_EXECUTION_ERROR;
}
2023-12-02 20:03:24 +01:00
if (! load_mipmaps (fp, &hdr, &dx10hdr, &load_info, image, "", &layer_index,
pixels, buf, read_mipmaps, error))
2023-09-26 00:35:21 +02:00
{
fclose (fp);
pika_image_delete (image);
return PIKA_PDB_EXECUTION_ERROR;
}
}
2023-12-02 20:03:24 +01:00
else if (hdr.caps.caps2 & DDSCAPS2_CUBEMAP) /* Cubemap texture */
2023-09-26 00:35:21 +02:00
{
if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEX) &&
2023-12-02 20:03:24 +01:00
! load_face (fp, &hdr, &dx10hdr, &load_info, image, "(positive x)",
&layer_index, pixels, buf, read_mipmaps, error))
2023-09-26 00:35:21 +02:00
{
fclose (fp);
pika_image_delete (image);
return PIKA_PDB_EXECUTION_ERROR;
}
if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX) &&
2023-12-02 20:03:24 +01:00
! load_face (fp, &hdr, &dx10hdr, &load_info, image, "(negative x)",
&layer_index, pixels, buf, read_mipmaps, error))
2023-09-26 00:35:21 +02:00
{
fclose (fp);
pika_image_delete (image);
return PIKA_PDB_EXECUTION_ERROR;
}
if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEY) &&
2023-12-02 20:03:24 +01:00
! load_face (fp, &hdr, &dx10hdr, &load_info, image, "(positive y)",
&layer_index, pixels, buf, read_mipmaps, error))
2023-09-26 00:35:21 +02:00
{
fclose (fp);
pika_image_delete (image);
return PIKA_PDB_EXECUTION_ERROR;
}
if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY) &&
2023-12-02 20:03:24 +01:00
! load_face (fp, &hdr, &dx10hdr, &load_info, image, "(negative y)",
&layer_index, pixels, buf, read_mipmaps, error))
2023-09-26 00:35:21 +02:00
{
fclose (fp);
pika_image_delete (image);
return PIKA_PDB_EXECUTION_ERROR;
}
if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ) &&
2023-12-02 20:03:24 +01:00
! load_face (fp, &hdr, &dx10hdr, &load_info, image, "(positive z)",
&layer_index, pixels, buf, read_mipmaps, error))
2023-09-26 00:35:21 +02:00
{
fclose (fp);
pika_image_delete (image);
return PIKA_PDB_EXECUTION_ERROR;
}
if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ) &&
2023-12-02 20:03:24 +01:00
! load_face (fp, &hdr, &dx10hdr, &load_info, image, "(negative z)",
&layer_index, pixels, buf, read_mipmaps, error))
2023-09-26 00:35:21 +02:00
{
fclose (fp);
pika_image_delete (image);
return PIKA_PDB_EXECUTION_ERROR;
}
}
else if ((hdr.caps.caps2 & DDSCAPS2_VOLUME) &&
2023-12-02 20:03:24 +01:00
(hdr.flags & DDSD_DEPTH)) /* Volume texture */
2023-09-26 00:35:21 +02:00
{
guint i, level;
gchar *plane;
for (i = 0; i < hdr.depth; ++i)
{
plane = g_strdup_printf ("(z = %d)", i);
2023-12-02 20:03:24 +01:00
if (! load_layer (fp, &hdr, &dx10hdr, &load_info, image, 0, plane,
&layer_index, pixels, buf, error))
2023-09-26 00:35:21 +02:00
{
g_free (plane);
fclose (fp);
pika_image_delete (image);
return PIKA_PDB_EXECUTION_ERROR;
}
2023-12-02 20:03:24 +01:00
2023-09-26 00:35:21 +02:00
g_free (plane);
}
if ((hdr.flags & DDSD_MIPMAPCOUNT) &&
(hdr.caps.caps1 & DDSCAPS_MIPMAP) &&
read_mipmaps)
{
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);
2023-12-02 20:03:24 +01:00
if (! load_layer (fp, &hdr, &dx10hdr, &load_info, image, level, plane,
&layer_index, pixels, buf, error))
2023-09-26 00:35:21 +02:00
{
g_free (plane);
fclose (fp);
pika_image_delete (image);
return PIKA_PDB_EXECUTION_ERROR;
}
g_free (plane);
}
}
}
}
2023-12-02 20:03:24 +01:00
else if (dx10hdr.arraySize > 1) /* Array texture */
2023-09-26 00:35:21 +02:00
{
guint i;
gchar *elem;
for (i = 0; i < dx10hdr.arraySize; ++i)
{
elem = g_strdup_printf ("(array element %d)", i);
2023-12-02 20:03:24 +01:00
if (! load_layer (fp, &hdr, &dx10hdr, &load_info, image, 0, elem, &layer_index,
pixels, buf, error))
2023-09-26 00:35:21 +02:00
{
fclose (fp);
pika_image_delete (image);
return PIKA_PDB_EXECUTION_ERROR;
}
2023-12-02 20:03:24 +01:00
if (! load_mipmaps (fp, &hdr, &dx10hdr, &load_info, image, elem, &layer_index,
pixels, buf, read_mipmaps, error))
2023-09-26 00:35:21 +02:00
{
fclose (fp);
pika_image_delete (image);
return PIKA_PDB_EXECUTION_ERROR;
}
g_free (elem);
}
}
pika_progress_update (1.0);
if (hdr.pixelfmt.flags & DDPF_PALETTEINDEXED8)
2023-12-02 20:03:24 +01:00
g_free (load_info.palette);
2023-09-26 00:35:21 +02:00
g_free (buf);
g_free (pixels);
fclose (fp);
layers = pika_image_list_layers (image);
if (! layers)
{
/* XXX This error should never happen, and probably it should be a
* CRITICAL/g_return_if_fail(). Yet let's just set it to the
* GError until we better handle the debug dialog for plug-ins. A
* pop-up with this message will be easier to track. No need to
* localize it though.
*/
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
"Oops! NULL image read! Please report this!");
return PIKA_PDB_EXECUTION_ERROR;
}
pika_image_take_selected_layers (image, layers);
2023-12-02 20:03:24 +01:00
if (flip_import)
pika_image_flip (image, PIKA_ORIENTATION_VERTICAL);
2023-09-26 00:35:21 +02:00
*ret_image = image;
return PIKA_PDB_SUCCESS;
}
2023-12-02 20:03:24 +01:00
/* Read data from header */
2023-09-26 00:35:21 +02:00
static gboolean
read_header (dds_header_t *hdr,
FILE *fp)
{
guchar buf[DDS_HEADERSIZE];
if (fread (buf, 1, DDS_HEADERSIZE, fp) != DDS_HEADERSIZE)
return FALSE;
2023-12-02 20:03:24 +01:00
hdr->magic = GETL32 (buf);
2023-09-26 00:35:21 +02:00
2023-12-02 20:03:24 +01:00
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);
2023-09-26 00:35:21 +02:00
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);
2023-12-02 20:03:24 +01:00
hdr->caps.caps1 = GETL32 (buf + 108);
hdr->caps.caps2 = GETL32 (buf + 112);
2023-09-26 00:35:21 +02:00
/* PIKA-DDS special info */
if (GETL32 (buf + 32) == FOURCC ('G','I','M','P') &&
GETL32 (buf + 36) == FOURCC ('-','D','D','S'))
{
hdr->reserved.pika_dds_special.magic1 = GETL32 (buf + 32);
hdr->reserved.pika_dds_special.magic2 = GETL32 (buf + 36);
hdr->reserved.pika_dds_special.version = GETL32 (buf + 40);
hdr->reserved.pika_dds_special.extra_fourcc = GETL32 (buf + 44);
}
return TRUE;
}
2023-12-02 20:03:24 +01:00
/* Read data from DX10 header */
2023-09-26 00:35:21 +02:00
static gboolean
2023-12-02 20:03:24 +01:00
read_header_dx10 (dds_header_dx10_t *dx10hdr,
2023-09-26 00:35:21 +02:00
FILE *fp)
{
gchar buf[DDS_HEADERSIZE_DX10];
if (fread (buf, 1, DDS_HEADERSIZE_DX10, fp) != DDS_HEADERSIZE_DX10)
return FALSE;
2023-12-02 20:03:24 +01:00
dx10hdr->dxgiFormat = GETL32 (buf);
dx10hdr->resourceDimension = GETL32 (buf + 4);
dx10hdr->miscFlag = GETL32 (buf + 8);
dx10hdr->arraySize = GETL32 (buf + 12);
dx10hdr->reserved = GETL32 (buf + 16);
2023-09-26 00:35:21 +02:00
return TRUE;
}
2023-12-02 20:03:24 +01:00
/* Check data from header to determine if the DDS file is valid */
2023-09-26 00:35:21 +02:00
static gboolean
validate_header (dds_header_t *hdr,
GError **error)
{
guint fourcc;
2023-12-02 20:03:24 +01:00
/* Check ~ m a g i c ~ */
2023-09-26 00:35:21 +02:00
if (hdr->magic != FOURCC ('D','D','S',' '))
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
_("Invalid DDS format magic number."));
return FALSE;
}
2023-12-02 20:03:24 +01:00
/* Check pitch and linear size */
if ((hdr->pixelfmt.flags & DDPF_FOURCC) && (hdr->pixelfmt.fourcc[1] != 0))
2023-09-26 00:35:21 +02:00
{
/* These format errors are recoverable as we recognize other codes
* allowing us to decode the image without data loss.
* Therefore let's not bother with GUI error messaging, but still
* print out the warning to standard error. See #5357.
*/
if (hdr->flags & DDSD_PITCH)
{
g_printerr ("Warning: DDSD_PITCH is incorrectly set for DDPF_FOURCC! (recovered)\n");
hdr->flags &= DDSD_PITCH;
}
if (! (hdr->flags & DDSD_LINEARSIZE))
{
g_printerr ("Warning: DDSD_LINEARSIZE is incorrectly not set for DDPF_FOURCC! (recovered)\n");
hdr->flags |= DDSD_LINEARSIZE;
}
}
else
{
if (! (hdr->flags & DDSD_PITCH))
{
g_printerr ("Warning: DDSD_PITCH is incorrectly not set for an uncompressed texture! (recovered)\n");
hdr->flags |= DDSD_PITCH;
}
if ((hdr->flags & DDSD_LINEARSIZE))
{
g_printerr ("Warning: DDSD_LINEARSIZE is incorrectly set for an uncompressed texture! (recovered)\n");
hdr->flags &= DDSD_LINEARSIZE;
}
}
/*
if ((hdr->pixelfmt.flags & DDPF_FOURCC) ==
(hdr->pixelfmt.flags & DDPF_RGB))
{
g_message ("Invalid pixel format.\n");
return 0;
}
*/
2023-12-02 20:03:24 +01:00
fourcc = GETL32 (hdr->pixelfmt.fourcc);
2023-09-26 00:35:21 +02:00
2023-12-02 20:03:24 +01:00
/* Check all supported FOURCC codes */
2023-09-26 00:35:21 +02:00
if ((hdr->pixelfmt.flags & DDPF_FOURCC) &&
fourcc != FOURCC ('D','X','T','1') &&
fourcc != FOURCC ('D','X','T','2') &&
fourcc != FOURCC ('D','X','T','3') &&
fourcc != FOURCC ('D','X','T','4') &&
fourcc != FOURCC ('D','X','T','5') &&
fourcc != FOURCC ('R','X','G','B') &&
fourcc != FOURCC ('A','T','I','1') &&
fourcc != FOURCC ('B','C','4','U') &&
fourcc != FOURCC ('B','C','4','S') &&
fourcc != FOURCC ('A','T','I','2') &&
fourcc != FOURCC ('B','C','5','U') &&
fourcc != FOURCC ('B','C','5','S') &&
2023-12-02 20:03:24 +01:00
fourcc != D3DFMT_A16B16G16R16 &&
fourcc != D3DFMT_Q16W16V16U16 &&
fourcc != D3DFMT_A16B16G16R16F &&
fourcc != D3DFMT_G16R16F &&
fourcc != D3DFMT_A32B32G32R32F &&
2023-09-26 00:35:21 +02:00
fourcc != FOURCC ('D','X','1','0'))
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
2023-12-02 20:03:24 +01:00
"Unsupported format (FourCC: %c%c%c%c, hex: %08x).",
2023-09-26 00:35:21 +02:00
hdr->pixelfmt.fourcc[0],
2023-12-02 20:03:24 +01:00
hdr->pixelfmt.fourcc[1] != 0 ? hdr->pixelfmt.fourcc[1] : ' ',
hdr->pixelfmt.fourcc[2] != 0 ? hdr->pixelfmt.fourcc[2] : ' ',
hdr->pixelfmt.fourcc[3] != 0 ? hdr->pixelfmt.fourcc[3] : ' ',
GETL32 (hdr->pixelfmt.fourcc));
2023-09-26 00:35:21 +02:00
return FALSE;
}
2023-12-02 20:03:24 +01:00
/* Check bits-per-pixel */
2023-09-26 00:35:21 +02:00
if (hdr->pixelfmt.flags & DDPF_RGB)
{
if ((hdr->pixelfmt.bpp != 8) &&
(hdr->pixelfmt.bpp != 16) &&
(hdr->pixelfmt.bpp != 24) &&
2023-12-02 20:03:24 +01:00
(hdr->pixelfmt.bpp != 32) &&
(hdr->pixelfmt.bpp != 48) &&
(hdr->pixelfmt.bpp != 64) &&
(hdr->pixelfmt.bpp != 96) &&
(hdr->pixelfmt.bpp != 128))
2023-09-26 00:35:21 +02:00
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Invalid bpp value for RGB data: %d"),
hdr->pixelfmt.bpp);
return FALSE;
}
}
else if (hdr->pixelfmt.flags & DDPF_LUMINANCE)
{
if ((hdr->pixelfmt.bpp != 8) &&
(hdr->pixelfmt.bpp != 16))
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Invalid bpp value for luminance data: %d"),
hdr->pixelfmt.bpp);
return FALSE;
}
hdr->pixelfmt.flags |= DDPF_RGB;
}
else if (hdr->pixelfmt.flags & DDPF_PALETTEINDEXED8)
{
hdr->pixelfmt.flags |= DDPF_RGB;
}
2023-12-02 20:03:24 +01:00
/* Check format flags */
if (! (hdr->pixelfmt.flags & DDPF_RGB) &&
! (hdr->pixelfmt.flags & DDPF_ALPHA) &&
2023-09-26 00:35:21 +02:00
! (hdr->pixelfmt.flags & DDPF_FOURCC) &&
! (hdr->pixelfmt.flags & DDPF_LUMINANCE))
{
g_message ("Unknown pixel format! Taking a guess, expect trouble!");
switch (fourcc)
{
case FOURCC ('D','X','T','1'):
case FOURCC ('D','X','T','2'):
case FOURCC ('D','X','T','3'):
case FOURCC ('D','X','T','4'):
case FOURCC ('D','X','T','5'):
case FOURCC ('R','X','G','B'):
case FOURCC ('A','T','I','1'):
case FOURCC ('B','C','4','U'):
case FOURCC ('B','C','4','S'):
case FOURCC ('A','T','I','2'):
case FOURCC ('B','C','5','U'):
case FOURCC ('B','C','5','S'):
hdr->pixelfmt.flags |= DDPF_FOURCC;
break;
default:
switch (hdr->pixelfmt.bpp)
{
case 8:
if (hdr->pixelfmt.flags & DDPF_ALPHAPIXELS)
hdr->pixelfmt.flags |= DDPF_ALPHA;
else
hdr->pixelfmt.flags |= DDPF_LUMINANCE;
break;
case 16:
case 24:
case 32:
case 64:
hdr->pixelfmt.flags |= DDPF_RGB;
break;
default:
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Invalid pixel format."));
return FALSE;
}
break;
}
}
return TRUE;
}
2023-12-02 20:03:24 +01:00
/* Set necessary flags and attributes in the standard header using information found in the DX10 header.
* TODO: This is a leftover from the way DX10 support was originally bolted on, should work towards removing this entirely.
2023-09-26 00:35:21 +02:00
*/
static gboolean
2023-12-02 20:03:24 +01:00
setup_dxgi_format (dds_header_t *hdr,
const dds_header_dx10_t *dx10hdr,
dds_load_info_t *load_info,
GError **error)
2023-09-26 00:35:21 +02:00
{
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 FALSE;
2023-12-02 20:03:24 +01:00
/* Unset FourCC flag by default */
hdr->pixelfmt.flags &= ~DDPF_FOURCC;
switch (dx10hdr->dxgiFormat)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
case DXGI_FORMAT_BC1_TYPELESS:
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
case DXGI_FORMAT_BC2_TYPELESS:
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
case DXGI_FORMAT_BC3_TYPELESS:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
case DXGI_FORMAT_BC4_TYPELESS:
case DXGI_FORMAT_BC4_UNORM:
case DXGI_FORMAT_BC4_SNORM:
case DXGI_FORMAT_BC5_TYPELESS:
case DXGI_FORMAT_BC5_UNORM:
case DXGI_FORMAT_BC5_SNORM:
/* Re-set FourCC flag for compressed formats */
2023-09-26 00:35:21 +02:00
hdr->pixelfmt.flags |= DDPF_FOURCC;
2023-12-02 20:03:24 +01:00
break;
2023-09-26 00:35:21 +02:00
2023-12-02 20:03:24 +01:00
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 | DDPF_RGB;
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 | DDPF_RGB;
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 | DDPF_RGB;
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 | DDPF_RGB;
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 | DDPF_RGB;
hdr->pixelfmt.rmask = 0x00000f00;
hdr->pixelfmt.gmask = 0x000000f0;
hdr->pixelfmt.bmask = 0x0000000f;
hdr->pixelfmt.amask = 0x0000f000;
break;
case DXGI_FORMAT_R8G8_TYPELESS:
case DXGI_FORMAT_R8G8_UNORM:
case DXGI_FORMAT_R8G8_UINT:
case DXGI_FORMAT_R8G8_SNORM:
case DXGI_FORMAT_R8G8_SINT:
hdr->pixelfmt.flags |= DDPF_RGB;
hdr->pixelfmt.bpp = 16;
hdr->pixelfmt.rmask = 0x000000ff;
hdr->pixelfmt.gmask = 0x0000ff00;
hdr->pixelfmt.bmask = hdr->pixelfmt.amask = 0;
load_info->dxgi_format = DXGI_FORMAT_R8G8_UINT;
if (dx10hdr->dxgiFormat >= DXGI_FORMAT_R8G8_SNORM)
load_info->is_signed = TRUE;
break;
case DXGI_FORMAT_R16_TYPELESS:
case DXGI_FORMAT_D16_UNORM:
case DXGI_FORMAT_R16_UNORM:
case DXGI_FORMAT_R16_UINT:
case DXGI_FORMAT_R16_SNORM:
case DXGI_FORMAT_R16_SINT:
hdr->pixelfmt.flags |= DDPF_RGB;
hdr->pixelfmt.bpp = 16;
hdr->pixelfmt.rmask = 0x0000ffff;
hdr->pixelfmt.gmask = hdr->pixelfmt.bmask = hdr->pixelfmt.amask = 0;
load_info->dxgi_format = DXGI_FORMAT_R16_UNORM;
if (dx10hdr->dxgiFormat >= DXGI_FORMAT_R16_SNORM)
load_info->is_signed = TRUE;
break;
case DXGI_FORMAT_R16G16_TYPELESS:
case DXGI_FORMAT_R16G16_UNORM:
case DXGI_FORMAT_R16G16_UINT:
case DXGI_FORMAT_R16G16_SNORM:
case DXGI_FORMAT_R16G16_SINT:
hdr->pixelfmt.flags |= DDPF_RGB;
hdr->pixelfmt.bpp = 32;
hdr->pixelfmt.rmask = 0x0000ffff;
hdr->pixelfmt.gmask = 0xffff0000;
hdr->pixelfmt.bmask = hdr->pixelfmt.amask = 0;
load_info->dxgi_format = DXGI_FORMAT_R16G16_UINT;
if (dx10hdr->dxgiFormat >= DXGI_FORMAT_R16G16_SNORM)
load_info->is_signed = TRUE;
break;
case DXGI_FORMAT_R16G16_FLOAT:
hdr->pixelfmt.flags |= DDPF_RGB;
hdr->pixelfmt.bpp = 32;
hdr->pixelfmt.rmask = 0x0000ffff;
hdr->pixelfmt.gmask = 0xffff0000;
hdr->pixelfmt.bmask = hdr->pixelfmt.amask = 0;
load_info->dxgi_format = DXGI_FORMAT_R16G16_FLOAT;
break;
case DXGI_FORMAT_R16G16B16A16_TYPELESS:
case DXGI_FORMAT_R16G16B16A16_UNORM:
case DXGI_FORMAT_R16G16B16A16_UINT:
hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS | DDPF_RGB;
hdr->pixelfmt.bpp = 64;
load_info->dxgi_format = DXGI_FORMAT_R16G16B16A16_UINT;
break;
case DXGI_FORMAT_R16G16B16A16_SNORM:
case DXGI_FORMAT_R16G16B16A16_SINT:
hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS | DDPF_RGB;
hdr->pixelfmt.bpp = 64;
load_info->is_signed = TRUE;
load_info->dxgi_format = DXGI_FORMAT_R16G16B16A16_UINT;
break;
case DXGI_FORMAT_R16G16B16A16_FLOAT:
hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS | DDPF_RGB;
hdr->pixelfmt.bpp = 64;
load_info->dxgi_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
break;
2023-09-26 00:35:21 +02:00
2023-12-02 20:03:24 +01:00
case DXGI_FORMAT_R32G32B32A32_TYPELESS:
case DXGI_FORMAT_R32G32B32A32_UINT:
hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS | DDPF_RGB;
hdr->pixelfmt.bpp = 128;
load_info->dxgi_format = DXGI_FORMAT_R32G32B32A32_UINT;
break;
case DXGI_FORMAT_R32G32B32A32_SINT:
hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS | DDPF_RGB;
hdr->pixelfmt.bpp = 128;
load_info->is_signed = TRUE;
load_info->dxgi_format = DXGI_FORMAT_R32G32B32A32_UINT;
break;
case DXGI_FORMAT_R32G32B32A32_FLOAT:
hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS | DDPF_RGB;
hdr->pixelfmt.bpp = 128;
load_info->dxgi_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
break;
case DXGI_FORMAT_UNKNOWN:
g_message ("Unknown DXGI format. Expect problems...");
break;
default: /* Unsupported DXGI format */
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Unsupported DXGI format (%d)"),
dx10hdr->dxgiFormat);
return FALSE;
2023-09-26 00:35:21 +02:00
}
return TRUE;
}
static const Babl *
premultiplied_variant (const Babl* format)
{
if (format == babl_format ("R'G'B'A u8"))
return babl_format ("R'aG'aB'aA u8");
else
g_printerr ("Add format %s to premultiplied_variant () %s: %d\n",
babl_get_name (format), __FILE__, __LINE__);
return format;
}
static gboolean
2023-12-02 20:03:24 +01:00
load_layer (FILE *fp,
const dds_header_t *hdr,
const dds_header_dx10_t *dx10hdr,
dds_load_info_t *load_info,
PikaImage *image,
guint level,
gchar *prefix,
guint *layer_index,
guchar *pixels,
guchar *buf,
GError **error)
2023-09-26 00:35:21 +02:00
{
GeglBuffer *buffer;
const Babl *bablfmt = NULL;
PikaImageType type = PIKA_RGBA_IMAGE;
gchar *layer_name;
gint x, y, z, n;
PikaLayer *layer;
guint width = hdr->width >> level;
guint height = hdr->height >> level;
guint size = hdr->pitch_or_linsize >> (2 * level);
guint layerw;
gint format = DDS_COMPRESS_NONE;
2023-10-30 23:55:30 +01:00
gsize file_size;
gsize current_position;
current_position = ftell (fp);
fseek (fp, 0L, SEEK_END);
file_size = ftell (fp);
fseek (fp, current_position, SEEK_SET);
2023-09-26 00:35:21 +02:00
2023-12-02 20:03:24 +01:00
if (width < 1) width = 1;
2023-09-26 00:35:21 +02:00
if (height < 1) height = 1;
2023-12-02 20:03:24 +01:00
switch (load_info->bpp)
2023-09-26 00:35:21 +02:00
{
case 1:
if (hdr->pixelfmt.flags & DDPF_PALETTEINDEXED8)
{
type = PIKA_INDEXED_IMAGE;
}
else if (hdr->pixelfmt.rmask == 0xe0)
{
type = PIKA_RGB_IMAGE;
bablfmt = babl_format ("R'G'B' u8");
}
else if (hdr->pixelfmt.flags & DDPF_ALPHA)
{
type = PIKA_GRAYA_IMAGE;
bablfmt = babl_format ("Y'A u8");
}
else
{
type = PIKA_GRAY_IMAGE;
bablfmt = babl_format ("Y' u8");
}
break;
case 2:
if ((hdr->pixelfmt.flags & (DDPF_PALETTEINDEXED8 + DDPF_ALPHA)) ==
DDPF_PALETTEINDEXED8 + DDPF_ALPHA)
{
type = PIKA_INDEXEDA_IMAGE;
}
2023-12-02 20:03:24 +01:00
else if (hdr->pixelfmt.amask == 0xf000) /* RGBA4 */
2023-09-26 00:35:21 +02:00
{
type = PIKA_RGBA_IMAGE;
bablfmt = babl_format ("R'G'B'A u8");
}
2023-12-02 20:03:24 +01:00
else if (hdr->pixelfmt.amask == 0xff00) /* L8A8 */
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
if (hdr->pixelfmt.flags & DDPF_LUMINANCE)
{
type = PIKA_GRAYA_IMAGE;
bablfmt = babl_format ("Y'A u8");
}
else
{
type = PIKA_RGB_IMAGE;
bablfmt = babl_format ("R'G'B'A u8");
}
}
else if (hdr->pixelfmt.rmask == 0xff) /* R8G8 */
{
type = PIKA_RGB_IMAGE;
bablfmt = babl_format ("R'G'B' u8");
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
else if (hdr->pixelfmt.bmask == 0x1f) /* R5G6B5 or RGB5A1 */
2023-09-26 00:35:21 +02:00
{
type = (hdr->pixelfmt.amask == 0x8000) ? PIKA_RGBA_IMAGE : PIKA_RGB_IMAGE;
bablfmt = (hdr->pixelfmt.amask == 0x8000) ? babl_format ("R'G'B'A u8") : babl_format ("R'G'B' u8");
}
2023-12-02 20:03:24 +01:00
else if (hdr->pixelfmt.rmask == 0xffff ||
2023-09-26 00:35:21 +02:00
hdr->pixelfmt.gmask == 0xffff ||
hdr->pixelfmt.bmask == 0xffff ||
2023-12-02 20:03:24 +01:00
hdr->pixelfmt.amask == 0xffff) /* L16 */
2023-09-26 00:35:21 +02:00
{
type = PIKA_GRAY_IMAGE;
bablfmt = babl_format ("Y' u16");
}
break;
2023-12-02 20:03:24 +01:00
case 3:
type = PIKA_RGB_IMAGE;
bablfmt = babl_format ("R'G'B' u8");
break;
2023-09-26 00:35:21 +02:00
case 4:
case 8:
{
type = PIKA_RGBA_IMAGE;
2023-12-02 20:03:24 +01:00
if (load_info->pika_bps == 1)
{
bablfmt = babl_format ("R'G'B'A u8");
}
else if (load_info->pika_bps == 2)
{
if (load_info->dxgi_format == DXGI_FORMAT_R16G16B16A16_FLOAT)
bablfmt = babl_format ("R'G'B'A half");
else if (load_info->dxgi_format == DXGI_FORMAT_R16G16_FLOAT)
bablfmt = babl_format ("R'G'B' half");
else
bablfmt = babl_format ("R'G'B'A u16");
}
2023-09-26 00:35:21 +02:00
}
break;
2023-12-02 20:03:24 +01:00
case 16:
{
type = PIKA_RGBA_IMAGE;
if (load_info->dxgi_format == DXGI_FORMAT_R32G32B32A32_FLOAT)
bablfmt = babl_format ("R'G'B'A float");
else
{
bablfmt = babl_format ("R'G'B'A u32");
}
}
break;
default:
g_set_error (error, PIKA_PLUG_IN_ERROR, 0,
_("Unsupported value for bytes per pixel: %d"), load_info->bpp);
return FALSE;
break;
2023-09-26 00:35:21 +02:00
}
layer_name = (level) ? g_strdup_printf ("mipmap %d %s", level, prefix) :
g_strdup_printf ("main surface %s", prefix);
layer = pika_layer_new (image, layer_name, width, height, type, 100,
pika_image_get_default_new_layer_mode (image));
g_free (layer_name);
2023-12-02 20:03:24 +01:00
pika_image_insert_layer (image, layer, NULL, *layer_index);
2023-09-26 00:35:21 +02:00
if (type == PIKA_INDEXED_IMAGE || type == PIKA_INDEXEDA_IMAGE)
bablfmt = pika_drawable_get_format (PIKA_DRAWABLE (layer));
2023-12-02 20:03:24 +01:00
if ((*layer_index)++)
pika_item_set_visible (PIKA_ITEM (layer), FALSE);
2023-09-26 00:35:21 +02:00
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
layerw = gegl_buffer_get_width (buffer);
if (hdr->pixelfmt.flags & DDPF_FOURCC)
{
guint w = (width + 3) >> 2;
guint h = (height + 3) >> 2;
2023-12-02 20:03:24 +01:00
if (GETL32 (hdr->pixelfmt.fourcc) == FOURCC ('D','X','1','0'))
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
/* Determine compression type from DXGI format */
switch (dx10hdr->dxgiFormat)
{
case DXGI_FORMAT_BC1_TYPELESS:
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
format = DDS_COMPRESS_BC1;
break;
case DXGI_FORMAT_BC2_TYPELESS:
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
format = DDS_COMPRESS_BC2;
break;
case DXGI_FORMAT_BC3_TYPELESS:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
format = DDS_COMPRESS_BC3;
break;
case DXGI_FORMAT_BC4_TYPELESS:
case DXGI_FORMAT_BC4_UNORM:
case DXGI_FORMAT_BC4_SNORM:
format = DDS_COMPRESS_BC4;
break;
case DXGI_FORMAT_BC5_TYPELESS:
case DXGI_FORMAT_BC5_UNORM:
case DXGI_FORMAT_BC5_SNORM:
format = DDS_COMPRESS_BC5;
break;
default:
format = DDS_FORMAT_MAX;
break;
}
}
else
{
/* Determine compression type from FourCC */
switch (GETL32 (hdr->pixelfmt.fourcc))
{
case FOURCC ('D','X','T','1'): format = DDS_COMPRESS_BC1; break;
case FOURCC ('D','X','T','2'): bablfmt = premultiplied_variant (bablfmt);
case FOURCC ('D','X','T','3'): format = DDS_COMPRESS_BC2; break;
case FOURCC ('D','X','T','4'): bablfmt = premultiplied_variant (bablfmt);
case FOURCC ('D','X','T','5'): format = DDS_COMPRESS_BC3; break;
case FOURCC ('R','X','G','B'): format = DDS_COMPRESS_BC3; break;
case FOURCC ('A','T','I','1'):
case FOURCC ('B','C','4','U'):
case FOURCC ('B','C','4','S'): format = DDS_COMPRESS_BC4; break;
case FOURCC ('A','T','I','2'):
case FOURCC ('B','C','5','U'):
case FOURCC ('B','C','5','S'): format = DDS_COMPRESS_BC5; break;
}
2023-09-26 00:35:21 +02:00
}
size = w * h;
if ((format == DDS_COMPRESS_BC1) || (format == DDS_COMPRESS_BC4))
size *= 8;
else
size *= 16;
}
2023-10-30 23:55:30 +01:00
if (size > (file_size - current_position) ||
size > hdr->pitch_or_linsize)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Requested data exceeds size of file.\n"));
return FALSE;
}
2023-09-26 00:35:21 +02:00
if ((hdr->flags & DDSD_LINEARSIZE) &&
2023-12-02 20:03:24 +01:00
! fread (buf, size, 1, fp))
2023-09-26 00:35:21 +02:00
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Unexpected EOF.\n"));
return FALSE;
}
if ((hdr->pixelfmt.flags & DDPF_RGB) ||
2023-12-02 20:03:24 +01:00
(hdr->pixelfmt.flags & DDPF_ALPHA)) /* Read uncompressed pixel data */
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
guint rshiftbits, gshiftbits, bshiftbits, ashiftbits;
guint rowstride = width * load_info->bpp;
2023-09-26 00:35:21 +02:00
guint ired = 0;
guint iblue = 2;
2023-12-02 20:03:24 +01:00
/* Prior plug-in versions (3.9.91 and earlier) wrote the R and G channels reversed for RGB10A2. */
2023-09-26 00:35:21 +02:00
if (hdr->reserved.pika_dds_special.magic1 == FOURCC ('G','I','M','P') &&
hdr->reserved.pika_dds_special.version <= 199003 &&
hdr->reserved.pika_dds_special.version > 0 &&
2023-12-02 20:03:24 +01:00
load_info->bpp >= 3 && hdr->pixelfmt.amask == 0xc0000000)
2023-09-26 00:35:21 +02:00
{
g_printerr ("Switching incorrect red and green channels in RGB10A2 dds "
"written by an older version of PIKA's dds plug-in.\n");
ired = 2;
iblue = 0;
}
2023-12-02 20:03:24 +01:00
if (load_info->pika_bps == 1)
{
rshiftbits = (8 - load_info->rbits);
gshiftbits = (8 - load_info->gbits);
bshiftbits = (8 - load_info->bbits);
ashiftbits = (8 - load_info->abits);
}
else if (load_info->pika_bps == 2)
{
rshiftbits = (16 - load_info->rbits);
gshiftbits = (16 - load_info->gbits);
bshiftbits = (16 - load_info->bbits);
ashiftbits = (16 - load_info->abits);
}
else
{
/* Assuming 4 bytes per sample */
rshiftbits = (32 - load_info->rbits);
gshiftbits = (32 - load_info->gbits);
bshiftbits = (32 - load_info->bbits);
ashiftbits = (32 - load_info->abits);
}
if ((hdr->flags & DDSD_PITCH) && (rowstride > hdr->pitch_or_linsize))
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Requested data exceeds size of file.\n"));
return FALSE;
}
2023-09-26 00:35:21 +02:00
z = 0;
for (y = 0, n = 0; y < height; ++y, ++n)
{
2023-12-02 20:03:24 +01:00
if (n >= load_info->tile_height)
2023-09-26 00:35:21 +02:00
{
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0,
bablfmt, pixels, GEGL_AUTO_ROWSTRIDE);
n = 0;
2023-12-02 20:03:24 +01:00
pika_progress_update ((gdouble) y / (gdouble) hdr->height);
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
if (hdr->flags & DDSD_PITCH)
2023-10-30 23:55:30 +01:00
{
2023-12-02 20:03:24 +01:00
current_position = ftell (fp);
if (rowstride > (file_size - current_position))
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Requested data exceeds size of file.\n"));
return FALSE;
}
if (! fread (buf, rowstride, 1, fp))
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Unexpected EOF.\n"));
return FALSE;
}
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
if (! (hdr->flags & DDSD_LINEARSIZE))
z = 0;
2023-09-26 00:35:21 +02:00
for (x = 0; x < layerw; ++x)
{
guint pixel = buf[z];
2023-12-02 20:03:24 +01:00
guint pos = (n * layerw + x) * load_info->pika_bpp;
2023-09-26 00:35:21 +02:00
2023-12-02 20:03:24 +01:00
if (load_info->bpp > 1) pixel += ((guint) buf[z + 1] << 8);
if (load_info->bpp > 2) pixel += ((guint) buf[z + 2] << 16);
if (load_info->bpp > 3) pixel += ((guint) buf[z + 3] << 24);
2023-09-26 00:35:21 +02:00
2023-12-02 20:03:24 +01:00
if (load_info->bpp >= 3)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
if (hdr->pixelfmt.amask == 0xc0000000) /* RGB10A2 */
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
guint16 *pixels16 = (guint16 *) &pixels[pos];
pixels16[ired] = (guint16) (pixel >> load_info->rshift << rshiftbits);
pixels16[1] = (guint16) (pixel >> load_info->gshift << gshiftbits);
pixels16[iblue] = (guint16) (pixel >> load_info->bshift << bshiftbits);
2023-09-26 00:35:21 +02:00
if (hdr->pixelfmt.flags & DDPF_ALPHAPIXELS)
2023-12-02 20:03:24 +01:00
{
pixels16[3] = (guint16) (
(pixel >> load_info->ashift << ashiftbits & load_info->amask)
* 65535 / load_info->amask);
}
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
else if (load_info->rmask > 0 && load_info->gmask > 0 && load_info->bmask > 0)
2023-09-26 00:35:21 +02:00
{
pixels[pos] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->rshift << rshiftbits & load_info->rmask)
* 255 / load_info->rmask;
2023-09-26 00:35:21 +02:00
pixels[pos + 1] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->gshift << gshiftbits & load_info->gmask)
* 255 / load_info->gmask;
2023-09-26 00:35:21 +02:00
pixels[pos + 2] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->bshift << bshiftbits & load_info->bmask)
* 255 / load_info->bmask;
if (hdr->pixelfmt.flags & DDPF_ALPHAPIXELS && load_info->bpp == 4)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
if (load_info->amask > 0)
2023-09-26 00:35:21 +02:00
pixels[pos + 3] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->ashift << ashiftbits & load_info->amask)
* 255 / load_info->amask;
2023-09-26 00:35:21 +02:00
else
pixels[pos + 3] = 255;
}
}
2023-12-02 20:03:24 +01:00
else if (load_info->bpp == 4)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
if (load_info->pika_bps == 2)
2023-09-26 00:35:21 +02:00
{
guint16 *pixels16 = (guint16 *) &pixels[pos];
2023-12-02 20:03:24 +01:00
pixels16[0] = 0;
pixels16[1] = 0;
pixels16[2] = 0;
pixels16[3] = 0xffff;
2023-09-26 00:35:21 +02:00
2023-12-02 20:03:24 +01:00
if (load_info->rbits == 16) /* Red */
pixels16[0] = (guint16)
(pixel >> load_info->rshift & load_info->rmask);
if (load_info->gbits == 16) /* Green */
pixels16[1] = (guint16)
(pixel >> load_info->gshift & load_info->gmask);
if (load_info->bbits == 16) /* Blue */
pixels16[2] = (guint16)
(pixel >> load_info->bshift & load_info->bmask);
if (load_info->abits == 16) /* Alpha */
pixels16[3] = (guint16)
(pixel >> load_info->ashift & load_info->amask);
if (load_info->is_signed)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
gint16 *signed16 = (gint16 *) &pixels16[0];
if (load_info->rbits == 16)
pixels16[0] = (gint16) ((gint32) signed16[0] + 32768);
if (load_info->gbits == 16)
pixels16[1] = (gint16) ((gint32) signed16[1] + 32768);
if (load_info->bbits == 16)
pixels16[2] = (gint16) ((gint32) signed16[2] + 32768);
if (load_info->abits == 16)
pixels16[3] = (gint16) ((gint32) signed16[3] + 32768);
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
}
}
else if (load_info->bpp == 8 && load_info->pika_bps == 2)
{
if (load_info->dxgi_format == DXGI_FORMAT_R16G16B16A16_UINT)
{
guint16 *pixels16 = (guint16 *) &pixels[pos];
guchar *srcbuf = &buf[z];
pixels16[0] = (guint16) srcbuf[0] + ((guint16) srcbuf[1] << 8);
pixels16[1] = (guint16) srcbuf[2] + ((guint16) srcbuf[3] << 8);
pixels16[2] = (guint16) srcbuf[4] + ((guint16) srcbuf[5] << 8);
pixels16[3] = (guint16) srcbuf[6] + ((guint16) srcbuf[7] << 8);
if (load_info->is_signed)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
gint16 *signed16 = (gint16 *) &pixels16[0];
pixels16[0] = (gint16) ((gint32) signed16[0] + 32768);
pixels16[1] = (gint16) ((gint32) signed16[1] + 32768);
pixels16[2] = (gint16) ((gint32) signed16[2] + 32768);
pixels16[3] = (gint16) ((gint32) signed16[3] + 32768);
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
}
else if (load_info->dxgi_format == DXGI_FORMAT_R16G16B16A16_FLOAT)
{
guint16 *pixels16 = (guint16 *) &pixels[pos];
guint16 *srcbuf = (guint16 *) &buf[z];
pixels16[0] = srcbuf[0];
pixels16[1] = srcbuf[1];
pixels16[2] = srcbuf[2];
pixels16[3] = srcbuf[3];
}
}
else if (load_info->bpp == 16 && load_info->pika_bps == 4)
{
if (load_info->dxgi_format == DXGI_FORMAT_R32G32B32A32_UINT)
{
guint32 *pixels32 = (guint32 *) &pixels[pos];
guchar *srcbuf = &buf[z];
pixels32[0] = (guint32) srcbuf[0] + ((guint32) srcbuf[1] << 8) +
((guint32) srcbuf[2] << 16) + ((guint32) srcbuf[3] << 24);
pixels32[1] = (guint32) srcbuf[4] + ((guint32) srcbuf[5] << 8) +
((guint32) srcbuf[6] << 16) + ((guint32) srcbuf[7] << 24);
pixels32[2] = (guint32) srcbuf[8] + ((guint32) srcbuf[9] << 8) +
((guint32) srcbuf[10] << 16) + ((guint32) srcbuf[11] << 24);
pixels32[3] = (guint32) srcbuf[12] + ((guint32) srcbuf[13] << 8) +
((guint32) srcbuf[14] << 16) + ((guint32) srcbuf[15] << 24);
if (load_info->is_signed)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
gint32 *signed32 = (gint32 *) &pixels32[0];
pixels32[0] = (gint32) ((gint64) signed32[0] + 2147483648);
pixels32[1] = (gint32) ((gint64) signed32[1] + 2147483648);
pixels32[2] = (gint32) ((gint64) signed32[2] + 2147483648);
pixels32[3] = (gint32) ((gint64) signed32[3] + 2147483648);
2023-09-26 00:35:21 +02:00
}
}
2023-12-02 20:03:24 +01:00
else if (load_info->dxgi_format == DXGI_FORMAT_R32G32B32A32_FLOAT)
{
gfloat *pixelsf = (gfloat *) &pixels[pos];
gfloat *srcbuf = (gfloat *) &buf[z];
pixelsf[0] = srcbuf[0];
pixelsf[1] = srcbuf[1];
pixelsf[2] = srcbuf[2];
pixelsf[3] = srcbuf[3];
}
}
else
{
g_warning ("Unhandled format! This shouldn't happen!");
break;
2023-09-26 00:35:21 +02:00
}
}
2023-12-02 20:03:24 +01:00
else if (load_info->bpp == 2)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
if (load_info->dxgi_format == DXGI_FORMAT_R16G16_FLOAT)
{
guint16 *pixels16 = (guint16 *) &pixels[pos];
guint16 *srcbuf = (guint16 *) &buf[z];
pixels16[0] = srcbuf[0];
pixels16[1] = srcbuf[1];
pixels16[2] = 0;
}
else if (hdr->pixelfmt.amask == 0xf000) /* RGBA4 */
2023-09-26 00:35:21 +02:00
{
pixels[pos] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->rshift << rshiftbits & load_info->rmask)
* 255 / load_info->rmask;
2023-09-26 00:35:21 +02:00
pixels[pos + 1] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->gshift << gshiftbits & load_info->gmask)
* 255 / load_info->gmask;
2023-09-26 00:35:21 +02:00
pixels[pos + 2] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->bshift << bshiftbits & load_info->bmask)
* 255 / load_info->bmask;
2023-09-26 00:35:21 +02:00
pixels[pos + 3] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->ashift << ashiftbits & load_info->amask)
* 255 / load_info->amask;
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
else if (hdr->pixelfmt.amask == 0xff00) /* L8A8 */
{
if (hdr->pixelfmt.flags & DDPF_LUMINANCE)
{
pixels[pos] =
(pixel >> load_info->rshift << rshiftbits & load_info->rmask)
* 255 / load_info->rmask;
pixels[pos + 1] =
(pixel >> load_info->ashift << ashiftbits & load_info->amask)
* 255 / load_info->amask;
}
else /* R8A8 */
{
pixels[pos] =
(pixel >> load_info->rshift << rshiftbits & load_info->rmask)
* 255 / load_info->rmask;
pixels[pos + 3] =
(pixel >> load_info->ashift << ashiftbits & load_info->amask)
* 255 / load_info->amask;
pixels[pos + 1] = 0;
pixels[pos + 2] = 0;
if (load_info->is_signed)
{
pixels[pos] = (guint8) ((gint16) pixels[pos] + 128);
pixels[pos+3] = (guint8) ((gint16) pixels[pos+3] + 128);
}
}
}
else if (hdr->pixelfmt.rmask == 0xff) /* R8G8 */
2023-09-26 00:35:21 +02:00
{
pixels[pos] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->rshift << rshiftbits & load_info->rmask)
* 255 / load_info->rmask;
2023-09-26 00:35:21 +02:00
pixels[pos + 1] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->gshift << gshiftbits & load_info->gmask)
* 255 / load_info->gmask;
pixels[pos + 2] = 0;
if (load_info->is_signed)
{
pixels[pos] = (guint8) ((gint16) pixels[pos] + 128);
pixels[pos+1] = (guint8) ((gint16) pixels[pos+1] + 128);
}
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
else if (hdr->pixelfmt.bmask == 0x1f) /* R5G6B5 or RGB5A1 */
2023-09-26 00:35:21 +02:00
{
pixels[pos] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->rshift << rshiftbits & load_info->rmask)
* 255 / load_info->rmask;
2023-09-26 00:35:21 +02:00
pixels[pos + 1] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->gshift << gshiftbits & load_info->gmask)
* 255 / load_info->gmask;
2023-09-26 00:35:21 +02:00
pixels[pos + 2] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->bshift << bshiftbits & load_info->bmask)
* 255 / load_info->bmask;
2023-09-26 00:35:21 +02:00
if (hdr->pixelfmt.amask == 0x8000)
{
pixels[pos + 3] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->ashift << ashiftbits & load_info->amask)
* 255 / load_info->amask;
2023-09-26 00:35:21 +02:00
}
}
2023-12-02 20:03:24 +01:00
else if (hdr->pixelfmt.rmask == 0xffff ||
2023-09-26 00:35:21 +02:00
hdr->pixelfmt.gmask == 0xffff ||
hdr->pixelfmt.bmask == 0xffff ||
2023-12-02 20:03:24 +01:00
hdr->pixelfmt.amask == 0xffff) /* L16 */
2023-09-26 00:35:21 +02:00
{
guint16 *pixels16 = (guint16 *) &pixels[pos];
*pixels16 = (guint16) (pixel & 0xffff);
2023-12-02 20:03:24 +01:00
if (load_info->is_signed)
{
gint16 *signed16 = (gint16 *) &pixels16[0];
pixels16[0] = (gint16) ((gint32) signed16[0] + 32768);
}
2023-09-26 00:35:21 +02:00
}
}
else
{
if (hdr->pixelfmt.flags & DDPF_PALETTEINDEXED8)
{
pixels[pos] = pixel & 0xff;
}
2023-12-02 20:03:24 +01:00
else if (hdr->pixelfmt.rmask == 0xe0) /* R3G3B2 */
2023-09-26 00:35:21 +02:00
{
pixels[pos] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->rshift << rshiftbits & load_info->rmask)
* 255 / load_info->rmask;
2023-09-26 00:35:21 +02:00
pixels[pos + 1] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->gshift << gshiftbits & load_info->gmask)
* 255 / load_info->gmask;
2023-09-26 00:35:21 +02:00
pixels[pos + 2] =
2023-12-02 20:03:24 +01:00
(pixel >> load_info->bshift << bshiftbits & load_info->bmask)
* 255 / load_info->bmask;
2023-09-26 00:35:21 +02:00
}
else if (hdr->pixelfmt.flags & DDPF_ALPHA)
{
pixels[pos + 0] = 255;
pixels[pos + 1] = pixel & 0xff;
}
2023-12-02 20:03:24 +01:00
else /* LUMINANCE */
2023-09-26 00:35:21 +02:00
{
pixels[pos] = pixel & 0xff;
}
}
2023-12-02 20:03:24 +01:00
z += load_info->bpp;
2023-09-26 00:35:21 +02:00
}
}
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0,
bablfmt, pixels, GEGL_AUTO_ROWSTRIDE);
}
2023-12-02 20:03:24 +01:00
else if (hdr->pixelfmt.flags & DDPF_FOURCC) /* Read compressed pixel data */
2023-09-26 00:35:21 +02:00
{
guchar *dst;
2023-12-02 20:03:24 +01:00
dst = g_malloc (width * height * load_info->pika_bpp);
memset (dst, 0, width * height * load_info->pika_bpp);
2023-09-26 00:35:21 +02:00
2023-12-02 20:03:24 +01:00
if (load_info->pika_bpp == 4)
2023-09-26 00:35:21 +02:00
{
for (y = 0; y < height; ++y)
2023-12-02 20:03:24 +01:00
{
for (x = 0; x < width; ++x)
{
dst[y * (width * 4) + (x * 4) + 3] = 255;
}
}
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
dxt_decompress (dst, buf, format, size, width, height, load_info->pika_bpp,
2023-09-26 00:35:21 +02:00
hdr->pixelfmt.flags & DDPF_NORMAL);
2023-12-02 20:03:24 +01:00
/* Prior plug-in versions (before 3.9.90) wrote the R and G channels reversed for BC5. */
2023-09-26 00:35:21 +02:00
if (format == DDS_COMPRESS_BC5 &&
hdr->reserved.pika_dds_special.magic1 == FOURCC ('G','I','M','P') &&
hdr->reserved.pika_dds_special.version > 0 &&
hdr->reserved.pika_dds_special.version <= 199002)
{
2023-12-02 20:03:24 +01:00
g_printerr ("Switching incorrect red and green channels in BC5 DDS "
2023-09-26 00:35:21 +02:00
"written by an older version of PIKA's dds plug-in.\n");
for (y = 0; y < height; ++y)
2023-12-02 20:03:24 +01:00
{
for (x = 0; x < width; ++x)
{
guchar tmpG;
guint pix_width = width * load_info->pika_bpp;
guint x_width = x * load_info->pika_bpp;
tmpG = dst[y * pix_width + x_width];
dst[y * pix_width + x_width] = dst[y * pix_width + x_width + 1];
dst[y * pix_width + x_width + 1] = tmpG;
}
}
2023-09-26 00:35:21 +02:00
}
z = 0;
for (y = 0, n = 0; y < height; ++y, ++n)
{
2023-12-02 20:03:24 +01:00
if (n >= load_info->tile_height)
2023-09-26 00:35:21 +02:00
{
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0,
bablfmt, pixels, GEGL_AUTO_ROWSTRIDE);
n = 0;
2023-12-02 20:03:24 +01:00
pika_progress_update ((gdouble) y / (gdouble) hdr->height);
2023-09-26 00:35:21 +02:00
}
2023-12-02 20:03:24 +01:00
memcpy (pixels + n * layerw * load_info->pika_bpp,
dst + y * layerw * load_info->pika_bpp,
width * load_info->pika_bpp);
2023-09-26 00:35:21 +02:00
}
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0,
bablfmt, pixels, GEGL_AUTO_ROWSTRIDE);
g_free (dst);
}
gegl_buffer_flush (buffer);
g_object_unref (buffer);
2023-12-02 20:03:24 +01:00
/* Decode files with PIKA-specific encodings */
if (hdr->reserved.pika_dds_special.magic1 == FOURCC ('G','I','M','P') &&
2023-09-26 00:35:21 +02:00
hdr->reserved.pika_dds_special.magic2 == FOURCC ('-','D','D','S'))
{
switch (hdr->reserved.pika_dds_special.extra_fourcc)
{
case FOURCC ('A','E','X','P'):
2023-12-02 20:03:24 +01:00
decode_alpha_exponent (PIKA_DRAWABLE (layer));
2023-09-26 00:35:21 +02:00
break;
case FOURCC ('Y','C','G','1'):
2023-12-02 20:03:24 +01:00
decode_ycocg (PIKA_DRAWABLE (layer));
2023-09-26 00:35:21 +02:00
break;
case FOURCC ('Y','C','G','2'):
2023-12-02 20:03:24 +01:00
decode_ycocg_scaled (PIKA_DRAWABLE (layer));
2023-09-26 00:35:21 +02:00
break;
default:
break;
}
}
return TRUE;
}
static gboolean
2023-12-02 20:03:24 +01:00
load_mipmaps (FILE *fp,
const dds_header_t *hdr,
const dds_header_dx10_t *dx10hdr,
dds_load_info_t *load_info,
PikaImage *image,
gchar *prefix,
guint *layer_index,
guchar *pixels,
guchar *buf,
gboolean read_mipmaps,
GError **error)
2023-09-26 00:35:21 +02:00
{
guint level;
2023-12-02 20:03:24 +01:00
if ((hdr->flags & DDSD_MIPMAPCOUNT) &&
2023-09-26 00:35:21 +02:00
(hdr->caps.caps1 & DDSCAPS_MIPMAP) &&
read_mipmaps)
{
for (level = 1; level < hdr->num_mipmaps; ++level)
{
2023-12-02 20:03:24 +01:00
if (! load_layer (fp, hdr, dx10hdr, load_info, image, level, prefix, layer_index,
pixels, buf, error))
2023-09-26 00:35:21 +02:00
return FALSE;
}
}
return TRUE;
}
static gboolean
2023-12-02 20:03:24 +01:00
load_face (FILE *fp,
const dds_header_t *hdr,
const dds_header_dx10_t *dx10hdr,
dds_load_info_t *load_info,
PikaImage *image,
gchar *prefix,
guint *layer_index,
guchar *pixels,
guchar *buf,
gboolean read_mipmaps,
GError **error)
2023-09-26 00:35:21 +02:00
{
2023-12-02 20:03:24 +01:00
if (! load_layer (fp, hdr, dx10hdr, load_info, image, 0, prefix,
layer_index, pixels, buf, error))
2023-09-26 00:35:21 +02:00
return FALSE;
2023-12-02 20:03:24 +01:00
return load_mipmaps (fp, hdr, dx10hdr, load_info, image, prefix, layer_index,
pixels, buf, read_mipmaps, error);
2023-09-26 00:35:21 +02:00
}
static guchar
color_bits (guint mask)
{
guchar i = 0;
while (mask)
{
if (mask & 1) ++i;
mask >>= 1;
}
return i;
}
static guchar
color_shift (guint mask)
{
guchar i = 0;
if (! mask)
return 0;
2023-12-02 20:03:24 +01:00
while (! ((mask >> i) & 1))
2023-09-26 00:35:21 +02:00
++i;
return i;
}
static gboolean
2023-12-02 20:03:24 +01:00
load_dialog (PikaProcedure *procedure,
PikaProcedureConfig *config)
2023-09-26 00:35:21 +02:00
{
GtkWidget *dialog;
GtkWidget *vbox;
gboolean run;
dialog = pika_procedure_dialog_new (procedure,
2023-12-02 20:03:24 +01:00
config,
2023-09-26 00:35:21 +02:00
_("Open DDS"));
vbox = pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog),
"dds-read-box",
"load-mipmaps",
2023-12-02 20:03:24 +01:00
"flip-image",
2023-09-26 00:35:21 +02:00
NULL);
gtk_box_set_spacing (GTK_BOX (vbox), 8);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog),
"dds-read-box", NULL);
gtk_widget_show (dialog);
run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
return run;
}