1/**
2 * Copyright (c) 2006-2023 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21// LOVE
22#include "PKMHandler.h"
23#include "common/int.h"
24#include "common/Exception.h"
25
26namespace love
27{
28namespace image
29{
30namespace magpie
31{
32
33namespace
34{
35
36// Big endian to host (and vice versa.)
37inline uint16 swap16big(uint16 x)
38{
39#ifdef LOVE_BIG_ENDIAN
40 return x;
41#else
42 return swapuint16(x);
43#endif
44}
45
46static const uint8 pkmIdentifier[] = {'P','K','M',' '};
47
48struct PKMHeader
49{
50 uint8 identifier[4];
51 uint8 version[2];
52 uint16 textureFormatBig;
53 uint16 extendedWidthBig;
54 uint16 extendedHeightBig;
55 uint16 widthBig;
56 uint16 heightBig;
57};
58
59enum PKMTextureFormat
60{
61 ETC1_RGB_NO_MIPMAPS = 0,
62 ETC2PACKAGE_RGB_NO_MIPMAPS,
63 ETC2PACKAGE_RGBA_NO_MIPMAPS_OLD,
64 ETC2PACKAGE_RGBA_NO_MIPMAPS,
65 ETC2PACKAGE_RGBA1_NO_MIPMAPS,
66 ETC2PACKAGE_R_NO_MIPMAPS,
67 ETC2PACKAGE_RG_NO_MIPMAPS,
68 ETC2PACKAGE_R_SIGNED_NO_MIPMAPS,
69 ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS
70};
71
72static PixelFormat convertFormat(uint16 texformat)
73{
74 switch (texformat)
75 {
76 case ETC1_RGB_NO_MIPMAPS:
77 return PIXELFORMAT_ETC1;
78 case ETC2PACKAGE_RGB_NO_MIPMAPS:
79 return PIXELFORMAT_ETC2_RGB;
80 case ETC2PACKAGE_RGBA_NO_MIPMAPS_OLD:
81 case ETC2PACKAGE_RGBA_NO_MIPMAPS:
82 return PIXELFORMAT_ETC2_RGBA;
83 case ETC2PACKAGE_RGBA1_NO_MIPMAPS:
84 return PIXELFORMAT_ETC2_RGBA1;
85 case ETC2PACKAGE_R_NO_MIPMAPS:
86 return PIXELFORMAT_EAC_R;
87 case ETC2PACKAGE_RG_NO_MIPMAPS:
88 return PIXELFORMAT_EAC_RG;
89 case ETC2PACKAGE_R_SIGNED_NO_MIPMAPS:
90 return PIXELFORMAT_EAC_Rs;
91 case ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS:
92 return PIXELFORMAT_EAC_RGs;
93 default:
94 return PIXELFORMAT_UNKNOWN;
95 }
96}
97
98} // Anonymous namespace.
99
100bool PKMHandler::canParseCompressed(Data *data)
101{
102 if (data->getSize() <= sizeof(PKMHeader))
103 return false;
104
105 const PKMHeader *header = (const PKMHeader *) data->getData();
106
107 if (memcmp(header->identifier, pkmIdentifier, 4) != 0)
108 return false;
109
110 // At the time of this writing, only v1.0 and v2.0 exist.
111 if ((header->version[0] != '2' && header->version[0] != '1') || header->version[1] != '0')
112 return false;
113
114 return true;
115}
116
117StrongRef<CompressedMemory> PKMHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB)
118{
119 if (!canParseCompressed(filedata))
120 throw love::Exception("Could not decode compressed data (not a PKM file?)");
121
122 PKMHeader header = *(const PKMHeader *) filedata->getData();
123
124 header.textureFormatBig = swap16big(header.textureFormatBig);
125 header.extendedWidthBig = swap16big(header.extendedWidthBig);
126 header.extendedHeightBig = swap16big(header.extendedHeightBig);
127 header.widthBig = swap16big(header.widthBig);
128 header.heightBig = swap16big(header.heightBig);
129
130 PixelFormat cformat = convertFormat(header.textureFormatBig);
131
132 if (cformat == PIXELFORMAT_UNKNOWN)
133 throw love::Exception("Could not parse PKM file: unsupported texture format.");
134
135 // The rest of the file after the header is all texture data.
136 size_t totalsize = filedata->getSize() - sizeof(PKMHeader);
137
138 StrongRef<CompressedMemory> memory;
139 memory.set(new CompressedMemory(totalsize), Acquire::NORETAIN);
140
141 // PKM files only store a single mipmap level.
142 memcpy(memory->data, (uint8 *) filedata->getData() + sizeof(PKMHeader), totalsize);
143
144 // TODO: verify whether glCompressedTexImage works properly with the unpadded
145 // width and height values (extended == padded.)
146 int width = header.widthBig;
147 int height = header.heightBig;
148
149 images.emplace_back(new CompressedSlice(cformat, width, height, memory, 0, totalsize), Acquire::NORETAIN);
150
151 format = cformat;
152 sRGB = false;
153
154 return memory;
155}
156
157} // magpie
158} // image
159} // love
160
161