CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/Data/Format/ZIMSave.cpp
Views: 1401
1
#include <cstdio>
2
#include <cstring>
3
#include <cmath>
4
#include <zstd.h>
5
6
#include "zlib.h"
7
8
#include "Common/Log.h"
9
#include "Common/Data/Format/ZIMLoad.h"
10
#include "Common/Data/Format/ZIMSave.h"
11
12
static const char magic[5] = "ZIMG";
13
14
/*int num_levels = 1;
15
if (flags & ZIM_HAS_MIPS) {
16
num_levels = log2i(width > height ? width : height);
17
}*/
18
static unsigned int log2i(unsigned int val) {
19
unsigned int ret = -1;
20
while (val != 0) {
21
val >>= 1; ret++;
22
}
23
return ret;
24
}
25
26
27
int ezcompress(unsigned char* pDest, long* pnDestLen, const unsigned char* pSrc, long nSrcLen, int compressLevel) {
28
z_stream stream;
29
int err;
30
31
int nExtraChunks;
32
uInt destlen;
33
34
stream.next_in = (Bytef*)pSrc;
35
stream.avail_in = (uInt)nSrcLen;
36
#ifdef MAXSEG_64K
37
/* Check for source > 64K on 16-bit machine: */
38
if ((uLong)stream.avail_in != nSrcLen) return Z_BUF_ERROR;
39
#endif
40
destlen = (uInt)*pnDestLen;
41
if ((uLong)destlen != (uLong)*pnDestLen) return Z_BUF_ERROR;
42
stream.zalloc = (alloc_func)0;
43
stream.zfree = (free_func)0;
44
stream.opaque = (voidpf)0;
45
46
err = deflateInit(&stream, compressLevel);
47
if (err != Z_OK) return err;
48
nExtraChunks = 0;
49
do {
50
stream.next_out = pDest;
51
stream.avail_out = destlen;
52
err = deflate(&stream, Z_FINISH);
53
if (err == Z_STREAM_END )
54
break;
55
if (err != Z_OK) {
56
deflateEnd(&stream);
57
return err;
58
}
59
nExtraChunks += 1;
60
} while (stream.avail_out == 0);
61
62
*pnDestLen = stream.total_out;
63
64
err = deflateEnd(&stream);
65
if (err != Z_OK) return err;
66
67
return nExtraChunks ? Z_BUF_ERROR : Z_OK;
68
}
69
70
inline int clamp16(int x) { if (x < 0) return 0; if (x > 15) return 15; return x; }
71
inline int clamp32(int x) { if (x < 0) return 0; if (x > 31) return 31; return x; }
72
inline int clamp64(int x) { if (x < 0) return 0; if (x > 63) return 63; return x; }
73
74
75
bool ispowerof2 (int x) {
76
if (!x || (x&(x-1)))
77
return false;
78
else
79
return true;
80
}
81
82
void Convert(const uint8_t *image_data, int width, int height, int pitch, int flags,
83
uint8_t **data, int *data_size) {
84
// For 4444 and 565. Ordered dither matrix. looks really surprisingly good on cell phone screens at 4444.
85
int dith[16] = {
86
1, 9, 3, 11,
87
13, 5, 15, 7,
88
4, 12, 2, 10,
89
16, 8, 14, 6
90
};
91
if ((flags & ZIM_DITHER) == 0) {
92
for (int i = 0; i < 16; i++) { dith[i] = 8; }
93
}
94
switch (flags & ZIM_FORMAT_MASK) {
95
case ZIM_RGBA8888:
96
{
97
*data_size = width * height * 4;
98
*data = new uint8_t[width * height * 4];
99
for (int y = 0; y < height; y++) {
100
memcpy((*data) + y * width * 4, image_data + y * pitch, width * 4);
101
}
102
break;
103
}
104
case ZIM_RGBA4444:
105
{
106
*data_size = width * height * 2;
107
*data = new uint8_t[*data_size];
108
uint16_t *dst = (uint16_t *)(*data);
109
int i = 0;
110
for (int y = 0; y < height; y++) {
111
for (int x = 0; x < width; x++) {
112
int dithval = dith[(x&3)+((y&0x3)<<2)] - 8;
113
int r = clamp16((image_data[i * 4] + dithval) >> 4);
114
int g = clamp16((image_data[i * 4 + 1] + dithval) >> 4);
115
int b = clamp16((image_data[i * 4 + 2] + dithval) >> 4);
116
int a = clamp16((image_data[i * 4 + 3] + dithval) >> 4); // really dither alpha?
117
// Note: GL_UNSIGNED_SHORT_4_4_4_4, not GL_UNSIGNED_SHORT_4_4_4_4_REV
118
*dst++ = (r << 12) | (g << 8) | (b << 4) | (a << 0);
119
i++;
120
}
121
}
122
break;
123
}
124
case ZIM_RGB565:
125
{
126
*data_size = width * height * 2;
127
*data = new uint8_t[*data_size];
128
uint16_t *dst = (uint16_t *)(*data);
129
int i = 0;
130
for (int y = 0; y < height; y++) {
131
for (int x = 0; x < width; x++) {
132
int dithval = dith[(x&3)+((y&0x3)<<2)] - 8;
133
//dithval = 0; please check this
134
int r = clamp32((image_data[i * 4] + dithval/2) >> 3);
135
int g = clamp64((image_data[i * 4 + 1] + dithval/4) >> 2);
136
int b = clamp32((image_data[i * 4 + 2] + dithval/2) >> 3);
137
// Note: GL_UNSIGNED_SHORT_5_6_5, not GL_UNSIGNED_SHORT_5_6_5_REV
138
*dst++ = (r << 11) | (g << 5) | (b);
139
i++;
140
}
141
}
142
}
143
break;
144
145
default:
146
ERROR_LOG(Log::IO, "Unhandled ZIM format %d", flags & ZIM_FORMAT_MASK);
147
*data = 0;
148
*data_size = 0;
149
return;
150
}
151
}
152
153
// Deletes the old buffer.
154
uint8_t *DownsampleBy2(const uint8_t *image, int width, int height, int pitch) {
155
uint8_t *out = new uint8_t[(width/2) * (height/2) * 4];
156
157
int degamma[256];
158
int gamma[32768];
159
for (int i =0; i < 256; i++) {
160
degamma[i] = (int)(powf((float)i / 255.0f, 1.0f/2.2f) * 8191.0f);
161
}
162
for (int i = 0; i < 32768; i++) {
163
gamma[i] = (int)(powf((float)i / 32764.0f, 2.2f) * 255.0f);
164
}
165
166
// Really stupid mipmap downsampling - at least it does gamma though.
167
for (int y = 0; y < height; y+=2) {
168
for (int x = 0; x < width; x+=2) {
169
const uint8_t *tl = image + pitch * y + x*4;
170
const uint8_t *tr = tl + 4;
171
const uint8_t *bl = tl + pitch;
172
const uint8_t *br = bl + 4;
173
uint8_t *d = out + ((y/2) * ((width/2)) + x / 2) * 4;
174
for (int c = 0; c < 4; c++) {
175
d[c] = gamma[degamma[tl[c]] + degamma[tr[c]] + degamma[bl[c]] + degamma[br[c]]];
176
}
177
}
178
}
179
return out;
180
}
181
182
void SaveZIM(FILE *f, int width, int height, int pitch, int flags, const uint8_t *image_data, int compressLevel) {
183
fwrite(magic, 1, 4, f);
184
fwrite(&width, 1, 4, f);
185
fwrite(&height, 1, 4, f);
186
fwrite(&flags, 1, 4, f);
187
188
int num_levels = 1;
189
if (flags & ZIM_HAS_MIPS) {
190
num_levels = log2i(width > height ? height : width) + 1;
191
}
192
for (int i = 0; i < num_levels; i++) {
193
uint8_t *data = 0;
194
int data_size;
195
Convert(image_data, width, height, pitch, flags, &data, &data_size);
196
if (flags & ZIM_ZLIB_COMPRESSED) {
197
long dest_len = data_size * 2;
198
uint8_t *dest = new uint8_t[dest_len];
199
if (Z_OK == ezcompress(dest, &dest_len, data, data_size, compressLevel == 0 ? Z_DEFAULT_COMPRESSION : compressLevel)) {
200
fwrite(dest, 1, dest_len, f);
201
} else {
202
ERROR_LOG(Log::IO, "Zlib compression failed.\n");
203
}
204
delete [] dest;
205
} else if (flags & ZIM_ZSTD_COMPRESSED) {
206
size_t dest_len = ZSTD_compressBound(data_size);
207
uint8_t *dest = new uint8_t[dest_len];
208
dest_len = ZSTD_compress(dest, dest_len, data, data_size, compressLevel == 0 ? 22 : compressLevel);
209
if (!ZSTD_isError(dest_len)) {
210
fwrite(dest, 1, dest_len, f);
211
} else {
212
ERROR_LOG(Log::IO, "Zlib compression failed.\n");
213
}
214
delete [] dest;
215
} else {
216
fwrite(data, 1, data_size, f);
217
}
218
delete [] data;
219
220
if (i != num_levels - 1) {
221
uint8_t *smaller = DownsampleBy2(image_data, width, height, pitch);
222
if (i != 0) {
223
delete [] image_data;
224
}
225
image_data = smaller;
226
width /= 2;
227
height /= 2;
228
pitch = width * 4;
229
}
230
}
231
delete [] image_data;
232
}
233
234