mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
0a3adc0839
Replace love.graphics.getRawImageFormats and love.graphics.getCompressedImageFormats with love.graphics.getImageFormats. --HG-- branch : minor
570 lines
14 KiB
C++
570 lines
14 KiB
C++
/**
|
|
* Copyright (c) 2006-2016 LOVE Development Team
|
|
*
|
|
* This software is provided 'as-is', without any express or implied
|
|
* warranty. In no event will the authors be held liable for any damages
|
|
* arising from the use of this software.
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, subject to the following restrictions:
|
|
*
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software
|
|
* in a product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
**/
|
|
|
|
// LOVE
|
|
#include "PVRHandler.h"
|
|
#include "common/int.h"
|
|
|
|
// C++
|
|
#include <algorithm>
|
|
|
|
namespace love
|
|
{
|
|
namespace image
|
|
{
|
|
namespace magpie
|
|
{
|
|
|
|
namespace
|
|
{
|
|
|
|
// 'P' 'V' 'R' 3
|
|
static const uint32 PVRTEX3_IDENT = 0x03525650;
|
|
static const uint32 PVRTEX3_IDENT_REV = 0x50565203;
|
|
|
|
#pragma pack(push, 4)
|
|
struct PVRTexHeaderV3
|
|
{
|
|
uint32 version; /// Version of the file header, used to identify it.
|
|
uint32 flags; /// Various format flags.
|
|
uint64 pixelFormat; /// The pixel format, 8cc value storing the 4 channel identifiers and their respective sizes.
|
|
uint32 colorSpace; /// The Color Space of the texture, currently either linear RGB or sRGB.
|
|
uint32 channelType; /// Variable type that the channel is stored in. Supports signed/unsigned int/short/byte or float for now.
|
|
uint32 height; /// Height of the texture.
|
|
uint32 width; /// Width of the texture.
|
|
uint32 depth; /// Depth of the texture. (Z-slices)
|
|
uint32 numSurfaces; /// Number of members in a Texture Array.
|
|
uint32 numFaces; /// Number of faces in a Cube Map. Maybe be a value other than 6.
|
|
uint32 numMipmaps; /// Number of MIP Maps in the texture - NB: Includes top level.
|
|
uint32 metaDataSize; /// Size of the accompanying meta data.
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
enum PVRV3PixelFormat
|
|
{
|
|
ePVRTPF_PVRTCI_2bpp_RGB = 0,
|
|
ePVRTPF_PVRTCI_2bpp_RGBA,
|
|
ePVRTPF_PVRTCI_4bpp_RGB,
|
|
ePVRTPF_PVRTCI_4bpp_RGBA,
|
|
ePVRTPF_PVRTCII_2bpp = 4,
|
|
ePVRTPF_PVRTCII_4bpp,
|
|
ePVRTPF_ETC1 = 6,
|
|
ePVRTPF_DXT1 = 7,
|
|
ePVRTPF_DXT2,
|
|
ePVRTPF_DXT3,
|
|
ePVRTPF_DXT4,
|
|
ePVRTPF_DXT5,
|
|
ePVRTPF_BC4,
|
|
ePVRTPF_BC5,
|
|
ePVRTPF_BC6,
|
|
ePVRTPF_BC7,
|
|
ePVRTPF_ETC2_RGB = 22,
|
|
ePVRTPF_ETC2_RGBA,
|
|
ePVRTPF_ETC2_RGBA1,
|
|
ePVRTPF_EAC_R = 25,
|
|
ePVRTPF_EAC_RG,
|
|
ePVRTPF_ASTC_4x4 = 27,
|
|
ePVRTPF_ASTC_5x4,
|
|
ePVRTPF_ASTC_5x5,
|
|
ePVRTPF_ASTC_6x5,
|
|
ePVRTPF_ASTC_6x6,
|
|
ePVRTPF_ASTC_8x5,
|
|
ePVRTPF_ASTC_8x6,
|
|
ePVRTPF_ASTC_8x8,
|
|
ePVRTPF_ASTC_10x5,
|
|
ePVRTPF_ASTC_10x6,
|
|
ePVRTPF_ASTC_10x8,
|
|
ePVRTPF_ASTC_10x10,
|
|
ePVRTPF_ASTC_12x10,
|
|
ePVRTPF_ASTC_12x12,
|
|
ePVRTPF_UNKNOWN_FORMAT = 0x7F
|
|
};
|
|
|
|
enum PVRV3ChannelType
|
|
{
|
|
ePVRTCT_UNORM8 = 0,
|
|
ePVRTCT_SNORM8,
|
|
ePVRTCT_UINT8,
|
|
ePVRTCT_SINT8,
|
|
ePVRTCT_UNORM16,
|
|
ePVRTCT_SNORM16,
|
|
ePVRTCT_UINT16,
|
|
ePVRTCT_SINT16,
|
|
ePVRTCT_UNORM32,
|
|
ePVRTCT_SNORM32,
|
|
ePVRTCT_UINT32,
|
|
ePVRTCT_SINT32,
|
|
ePVRTCT_FLOAT
|
|
};
|
|
|
|
// 'P' 'V' 'R' '!'
|
|
static const uint32 PVRTEX2_IDENT = 0x21525650;
|
|
static const uint32 PVRTEX2_IDENT_REV = 0x50565221;
|
|
|
|
struct PVRTexHeaderV2
|
|
{
|
|
uint32 headerSize;
|
|
uint32 height;
|
|
uint32 width;
|
|
uint32 numMipmaps;
|
|
uint32 flags;
|
|
uint32 dataSize;
|
|
uint32 bpp;
|
|
uint32 bitmaskRed;
|
|
uint32 bitmaskGreen;
|
|
uint32 bitmaskBlue;
|
|
uint32 bitmaskAlpha;
|
|
uint32 pvrTag;
|
|
uint32 numSurfaces;
|
|
};
|
|
|
|
// The legacy V2 pixel types we support.
|
|
enum PVRPixelTypeV2
|
|
{
|
|
PixelTypePVRTC2 = 0x18,
|
|
PixelTypePVRTC4,
|
|
PixelTypePVRTCII2 = 0x1C,
|
|
PixelTypePVRTCII4,
|
|
PixelTypeDXT1 = 0x20,
|
|
PixelTypeDXT3 = 0x22,
|
|
PixelTypeDXT5 = 0x24,
|
|
PixelTypeETC1 = 0x36
|
|
};
|
|
|
|
// Convert a V2 header to V3.
|
|
void ConvertPVRHeader(PVRTexHeaderV2 header2, PVRTexHeaderV3 *header3)
|
|
{
|
|
// If the header's endianness doesn't match our own, we swap everything.
|
|
if (header2.pvrTag == PVRTEX2_IDENT_REV)
|
|
{
|
|
// All of the struct's members are uint32 values, so we can do this.
|
|
uint32 *headerArray = (uint32 *) &header2;
|
|
for (size_t i = 0; i < sizeof(PVRTexHeaderV2) / sizeof(uint32); i++)
|
|
headerArray[i] = swapuint32(headerArray[i]);
|
|
}
|
|
|
|
memset(header3, 0, sizeof(PVRTexHeaderV3));
|
|
|
|
header3->version = PVRTEX3_IDENT;
|
|
header3->height = header2.height;
|
|
header3->width = header2.width;
|
|
header3->depth = 1;
|
|
header3->numSurfaces = header2.numSurfaces;
|
|
header3->numFaces = 1;
|
|
header3->numMipmaps = header2.numMipmaps;
|
|
header3->metaDataSize = 0;
|
|
|
|
switch ((PVRPixelTypeV2) (header2.flags & 0xFF))
|
|
{
|
|
case PixelTypePVRTC2:
|
|
header3->pixelFormat = ePVRTPF_PVRTCI_2bpp_RGBA;
|
|
break;
|
|
case PixelTypePVRTC4:
|
|
header3->pixelFormat = ePVRTPF_PVRTCI_4bpp_RGBA;
|
|
break;
|
|
case PixelTypePVRTCII2:
|
|
header3->pixelFormat = ePVRTPF_PVRTCII_2bpp;
|
|
break;
|
|
case PixelTypePVRTCII4:
|
|
header3->pixelFormat = ePVRTPF_PVRTCII_4bpp;
|
|
break;
|
|
case PixelTypeDXT1:
|
|
header3->pixelFormat = ePVRTPF_DXT1;
|
|
break;
|
|
case PixelTypeDXT3:
|
|
header3->pixelFormat = ePVRTPF_DXT3;
|
|
break;
|
|
case PixelTypeDXT5:
|
|
header3->pixelFormat = ePVRTPF_DXT5;
|
|
break;
|
|
case PixelTypeETC1:
|
|
header3->pixelFormat = ePVRTPF_ETC1;
|
|
break;
|
|
default:
|
|
header3->pixelFormat = ePVRTPF_UNKNOWN_FORMAT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static PixelFormat convertFormat(PVRV3PixelFormat format, PVRV3ChannelType channeltype)
|
|
{
|
|
bool snorm = false;
|
|
|
|
switch (channeltype)
|
|
{
|
|
case ePVRTCT_SNORM8:
|
|
case ePVRTCT_SNORM16:
|
|
case ePVRTCT_SNORM32:
|
|
snorm = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (format)
|
|
{
|
|
case ePVRTPF_PVRTCI_2bpp_RGB:
|
|
return PIXELFORMAT_PVR1_RGB2;
|
|
case ePVRTPF_PVRTCI_2bpp_RGBA:
|
|
return PIXELFORMAT_PVR1_RGBA2;
|
|
case ePVRTPF_PVRTCI_4bpp_RGB:
|
|
return PIXELFORMAT_PVR1_RGB4;
|
|
case ePVRTPF_PVRTCI_4bpp_RGBA:
|
|
return PIXELFORMAT_PVR1_RGBA4;
|
|
case ePVRTPF_ETC1:
|
|
return PIXELFORMAT_ETC1;
|
|
case ePVRTPF_DXT1:
|
|
return PIXELFORMAT_DXT1;
|
|
case ePVRTPF_DXT3:
|
|
return PIXELFORMAT_DXT3;
|
|
case ePVRTPF_DXT5:
|
|
return PIXELFORMAT_DXT5;
|
|
case ePVRTPF_BC4:
|
|
return snorm ? PIXELFORMAT_BC4s : PIXELFORMAT_BC4;
|
|
case ePVRTPF_BC5:
|
|
return snorm ? PIXELFORMAT_BC5s : PIXELFORMAT_BC5;
|
|
case ePVRTPF_BC6:
|
|
return snorm ? PIXELFORMAT_BC6Hs : PIXELFORMAT_BC6H;
|
|
case ePVRTPF_BC7:
|
|
return PIXELFORMAT_BC7;
|
|
case ePVRTPF_ETC2_RGB:
|
|
return PIXELFORMAT_ETC2_RGB;
|
|
case ePVRTPF_ETC2_RGBA:
|
|
return PIXELFORMAT_ETC2_RGBA;
|
|
case ePVRTPF_ETC2_RGBA1:
|
|
return PIXELFORMAT_ETC2_RGBA1;
|
|
case ePVRTPF_EAC_R:
|
|
return snorm ? PIXELFORMAT_EAC_Rs : PIXELFORMAT_EAC_R;
|
|
case ePVRTPF_EAC_RG:
|
|
return snorm ? PIXELFORMAT_EAC_RGs : PIXELFORMAT_EAC_RG;
|
|
case ePVRTPF_ASTC_4x4:
|
|
return PIXELFORMAT_ASTC_4x4;
|
|
case ePVRTPF_ASTC_5x4:
|
|
return PIXELFORMAT_ASTC_5x4;
|
|
case ePVRTPF_ASTC_5x5:
|
|
return PIXELFORMAT_ASTC_5x5;
|
|
case ePVRTPF_ASTC_6x5:
|
|
return PIXELFORMAT_ASTC_6x5;
|
|
case ePVRTPF_ASTC_6x6:
|
|
return PIXELFORMAT_ASTC_6x6;
|
|
case ePVRTPF_ASTC_8x5:
|
|
return PIXELFORMAT_ASTC_8x5;
|
|
case ePVRTPF_ASTC_8x6:
|
|
return PIXELFORMAT_ASTC_8x6;
|
|
case ePVRTPF_ASTC_8x8:
|
|
return PIXELFORMAT_ASTC_8x8;
|
|
case ePVRTPF_ASTC_10x5:
|
|
return PIXELFORMAT_ASTC_10x5;
|
|
case ePVRTPF_ASTC_10x6:
|
|
return PIXELFORMAT_ASTC_10x6;
|
|
case ePVRTPF_ASTC_10x8:
|
|
return PIXELFORMAT_ASTC_10x8;
|
|
case ePVRTPF_ASTC_10x10:
|
|
return PIXELFORMAT_ASTC_10x10;
|
|
case ePVRTPF_ASTC_12x10:
|
|
return PIXELFORMAT_ASTC_12x10;
|
|
case ePVRTPF_ASTC_12x12:
|
|
return PIXELFORMAT_ASTC_12x12;
|
|
default:
|
|
return PIXELFORMAT_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
int getBitsPerPixel(uint64 pixelformat)
|
|
{
|
|
// Uncompressed formats have their bits per pixel stored in the high bits.
|
|
if ((pixelformat & 0xFFFFFFFF) != pixelformat)
|
|
{
|
|
const uint8 *charformat = (const uint8 *) &pixelformat;
|
|
return charformat[4] + charformat[5] + charformat[6] + charformat[7];
|
|
}
|
|
|
|
switch (pixelformat)
|
|
{
|
|
case ePVRTPF_PVRTCI_2bpp_RGB:
|
|
case ePVRTPF_PVRTCI_2bpp_RGBA:
|
|
case ePVRTPF_PVRTCII_2bpp:
|
|
return 2;
|
|
case ePVRTPF_PVRTCI_4bpp_RGB:
|
|
case ePVRTPF_PVRTCI_4bpp_RGBA:
|
|
case ePVRTPF_PVRTCII_4bpp:
|
|
case ePVRTPF_ETC1:
|
|
case ePVRTPF_DXT1:
|
|
case ePVRTPF_BC4:
|
|
case ePVRTPF_ETC2_RGB:
|
|
case ePVRTPF_ETC2_RGBA1:
|
|
case ePVRTPF_EAC_R:
|
|
return 4;
|
|
case ePVRTPF_DXT2:
|
|
case ePVRTPF_DXT3:
|
|
case ePVRTPF_DXT4:
|
|
case ePVRTPF_DXT5:
|
|
case ePVRTPF_BC5:
|
|
case ePVRTPF_BC6:
|
|
case ePVRTPF_BC7:
|
|
case ePVRTPF_ETC2_RGBA:
|
|
case ePVRTPF_EAC_RG:
|
|
return 8;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void getFormatMinDimensions(uint64 pixelformat, int &minX, int &minY, int &minZ)
|
|
{
|
|
minZ = 1;
|
|
|
|
switch (pixelformat)
|
|
{
|
|
case ePVRTPF_PVRTCI_2bpp_RGB:
|
|
case ePVRTPF_PVRTCI_2bpp_RGBA:
|
|
minX = 16;
|
|
minY = 8;
|
|
break;
|
|
case ePVRTPF_PVRTCI_4bpp_RGB:
|
|
case ePVRTPF_PVRTCI_4bpp_RGBA:
|
|
minX = minY = 8;
|
|
break;
|
|
case ePVRTPF_PVRTCII_2bpp:
|
|
minX = 8;
|
|
minY = 4;
|
|
break;
|
|
case ePVRTPF_PVRTCII_4bpp:
|
|
minX = minY = 4;
|
|
break;
|
|
case ePVRTPF_DXT1:
|
|
case ePVRTPF_DXT2:
|
|
case ePVRTPF_DXT3:
|
|
case ePVRTPF_DXT4:
|
|
case ePVRTPF_DXT5:
|
|
case ePVRTPF_BC4:
|
|
case ePVRTPF_BC5:
|
|
case ePVRTPF_BC6:
|
|
case ePVRTPF_BC7:
|
|
case ePVRTPF_ETC1:
|
|
case ePVRTPF_ETC2_RGB:
|
|
case ePVRTPF_ETC2_RGBA:
|
|
case ePVRTPF_ETC2_RGBA1:
|
|
case ePVRTPF_EAC_R:
|
|
case ePVRTPF_EAC_RG:
|
|
minX = minY = 4;
|
|
break;
|
|
case ePVRTPF_ASTC_4x4:
|
|
minX = 4;
|
|
minY = 4;
|
|
break;
|
|
case ePVRTPF_ASTC_5x4:
|
|
minX = 5;
|
|
minY = 4;
|
|
break;
|
|
case ePVRTPF_ASTC_5x5:
|
|
minX = 5;
|
|
minY = 5;
|
|
break;
|
|
case ePVRTPF_ASTC_6x5:
|
|
minX = 6;
|
|
minY = 5;
|
|
break;
|
|
case ePVRTPF_ASTC_6x6:
|
|
minX = 6;
|
|
minY = 6;
|
|
break;
|
|
case ePVRTPF_ASTC_8x5:
|
|
minX = 8;
|
|
minY = 5;
|
|
break;
|
|
case ePVRTPF_ASTC_8x6:
|
|
minX = 8;
|
|
minY = 6;
|
|
break;
|
|
case ePVRTPF_ASTC_8x8:
|
|
minX = 8;
|
|
minY = 8;
|
|
break;
|
|
case ePVRTPF_ASTC_10x5:
|
|
minX = 10;
|
|
minY = 5;
|
|
break;
|
|
case ePVRTPF_ASTC_10x6:
|
|
minX = 10;
|
|
minY = 6;
|
|
break;
|
|
case ePVRTPF_ASTC_10x8:
|
|
minX = 10;
|
|
minY = 8;
|
|
break;
|
|
case ePVRTPF_ASTC_10x10:
|
|
minX = 10;
|
|
minY = 10;
|
|
break;
|
|
case ePVRTPF_ASTC_12x10:
|
|
minX = 12;
|
|
minY = 10;
|
|
break;
|
|
case ePVRTPF_ASTC_12x12:
|
|
minX = 12;
|
|
minY = 12;
|
|
break;
|
|
default: // We don't handle all possible formats, but that's fine.
|
|
minX = minY = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
size_t getMipLevelSize(const PVRTexHeaderV3 &header, int miplevel)
|
|
{
|
|
int smallestwidth = 1;
|
|
int smallestheight = 1;
|
|
int smallestdepth = 1;
|
|
getFormatMinDimensions(header.pixelFormat, smallestwidth, smallestheight, smallestdepth);
|
|
|
|
int width = std::max((int) header.width >> miplevel, 1);
|
|
int height = std::max((int) header.height >> miplevel, 1);
|
|
int depth = std::max((int) header.depth >> miplevel, 1);
|
|
|
|
// Pad the dimensions.
|
|
width = ((width + smallestwidth - 1) / smallestwidth) * smallestwidth;
|
|
height = ((height + smallestheight - 1) / smallestheight) * smallestheight;
|
|
depth = ((depth + smallestdepth - 1) / smallestdepth) * smallestdepth;
|
|
|
|
if (header.pixelFormat >= ePVRTPF_ASTC_4x4 && header.pixelFormat <= ePVRTPF_ASTC_12x12)
|
|
return (width / smallestwidth) * (height / smallestheight) * (depth / smallestdepth) * (128 / 8);
|
|
else
|
|
return getBitsPerPixel(header.pixelFormat) * width * height * depth / 8;
|
|
}
|
|
|
|
} // Anonymous namespace.
|
|
|
|
|
|
bool PVRHandler::canParse(const filesystem::FileData *data)
|
|
{
|
|
if (data->getSize() < sizeof(PVRTexHeaderV2) || data->getSize() < sizeof(PVRTexHeaderV3))
|
|
return false;
|
|
|
|
PVRTexHeaderV3 *header3 = (PVRTexHeaderV3 *) data->getData();
|
|
|
|
// Magic number (FourCC identifier.)
|
|
if (header3->version == PVRTEX3_IDENT || header3->version == PVRTEX3_IDENT_REV)
|
|
return true;
|
|
|
|
// Maybe it has a V2 header.
|
|
PVRTexHeaderV2 *header2 = (PVRTexHeaderV2 *) data->getData();
|
|
|
|
// FourCC identifier.
|
|
if (header2->pvrTag == PVRTEX2_IDENT || header2->pvrTag == PVRTEX2_IDENT_REV)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
uint8 *PVRHandler::parse(filesystem::FileData *filedata, std::vector<CompressedImageData::SubImage> &images, size_t &dataSize, PixelFormat &format, bool &sRGB)
|
|
{
|
|
if (!canParse(filedata))
|
|
throw love::Exception("Could not decode compressed data (not a PVR file?)");
|
|
|
|
PVRTexHeaderV3 header3 = *(PVRTexHeaderV3 *) filedata->getData();
|
|
|
|
// If the header isn't the V3 format, assume it's V2 and convert.
|
|
if (header3.version != PVRTEX3_IDENT && header3.version != PVRTEX3_IDENT_REV)
|
|
ConvertPVRHeader(*(PVRTexHeaderV2 *) filedata->getData(), &header3);
|
|
|
|
// If the header's endianness doesn't match our own, then we swap everything.
|
|
if (header3.version == PVRTEX3_IDENT_REV)
|
|
{
|
|
header3.version = PVRTEX3_IDENT;
|
|
header3.flags = swapuint32(header3.flags);
|
|
header3.pixelFormat = swapuint64(header3.pixelFormat);
|
|
header3.colorSpace = swapuint32(header3.colorSpace);
|
|
header3.channelType = swapuint32(header3.channelType);
|
|
header3.height = swapuint32(header3.height);
|
|
header3.width = swapuint32(header3.width);
|
|
header3.depth = swapuint32(header3.depth);
|
|
header3.numFaces = swapuint32(header3.numFaces);
|
|
header3.numMipmaps = swapuint32(header3.numMipmaps);
|
|
header3.metaDataSize = swapuint32(header3.metaDataSize);
|
|
}
|
|
|
|
if (header3.depth > 1)
|
|
throw love::Exception("Image depths greater than 1 in PVR files are unsupported.");
|
|
|
|
PVRV3PixelFormat pixelformat = (PVRV3PixelFormat) header3.pixelFormat;
|
|
PVRV3ChannelType channeltype = (PVRV3ChannelType) header3.channelType;
|
|
|
|
PixelFormat cformat = convertFormat(pixelformat, channeltype);
|
|
|
|
if (cformat == PIXELFORMAT_UNKNOWN)
|
|
throw love::Exception("Could not parse PVR file: unsupported image format.");
|
|
|
|
size_t totalsize = 0;
|
|
uint8 *data = nullptr;
|
|
|
|
// Ignore faces and surfaces except the first ones (for now.)
|
|
for (int i = 0; i < (int) header3.numMipmaps; i++)
|
|
totalsize += getMipLevelSize(header3, i);
|
|
|
|
size_t fileoffset = sizeof(PVRTexHeaderV3) + header3.metaDataSize;
|
|
|
|
// Make sure the file actually holds this much data...
|
|
if (filedata->getSize() < fileoffset + totalsize)
|
|
throw love::Exception("Could not parse PVR file: invalid size calculation.");
|
|
|
|
try
|
|
{
|
|
data = new uint8[totalsize];
|
|
}
|
|
catch (std::bad_alloc &)
|
|
{
|
|
throw love::Exception("Out of memory.");
|
|
}
|
|
|
|
size_t curoffset = 0;
|
|
const uint8 *filebytes = (uint8 *) filedata->getData() + fileoffset;
|
|
|
|
for (int i = 0; i < (int) header3.numMipmaps; i++)
|
|
{
|
|
size_t mipsize = getMipLevelSize(header3, i);
|
|
|
|
if (curoffset + mipsize > totalsize)
|
|
break; // Just in case.
|
|
|
|
CompressedImageData::SubImage mip;
|
|
mip.width = std::max((int) header3.width >> i, 1);
|
|
mip.height = std::max((int) header3.height >> i, 1);
|
|
mip.size = mipsize;
|
|
|
|
memcpy(data + curoffset, filebytes + curoffset, mipsize);
|
|
mip.data = data + curoffset;
|
|
|
|
curoffset += mipsize;
|
|
|
|
images.push_back(mip);
|
|
}
|
|
|
|
dataSize = totalsize;
|
|
format = cformat;
|
|
sRGB = (header3.colorSpace == 1);
|
|
|
|
return data;
|
|
}
|
|
|
|
} // magpie
|
|
} // image
|
|
} // love
|