Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libsnes/bsnes/nall/image.hpp
2 views
1
#ifndef NALL_IMAGE_HPP
2
#define NALL_IMAGE_HPP
3
4
#include <nall/bmp.hpp>
5
#include <nall/filemap.hpp>
6
#include <nall/interpolation.hpp>
7
#include <nall/png.hpp>
8
#include <nall/stdint.hpp>
9
#include <algorithm>
10
11
namespace nall {
12
13
struct image {
14
uint8_t *data;
15
unsigned width;
16
unsigned height;
17
unsigned pitch;
18
19
bool endian; //0 = little, 1 = big
20
unsigned depth;
21
unsigned stride;
22
23
struct Channel {
24
uint64_t mask;
25
unsigned depth;
26
unsigned shift;
27
} alpha, red, green, blue;
28
29
typedef double (*interpolation)(double, double, double, double, double);
30
static inline unsigned bitDepth(uint64_t color);
31
static inline unsigned bitShift(uint64_t color);
32
static inline uint64_t normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth);
33
34
inline image& operator=(const image &source);
35
inline image& operator=(image &&source);
36
inline image(const image &source);
37
inline image(image &&source);
38
inline image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
39
inline image();
40
inline ~image();
41
42
inline uint64_t read(const uint8_t *data) const;
43
inline void write(uint8_t *data, uint64_t value) const;
44
45
inline void free();
46
inline void allocate(unsigned width, unsigned height);
47
inline void clear(uint64_t color);
48
inline bool load(const string &filename);
49
//inline bool loadBMP(const uint8_t *data, unsigned size);
50
inline bool loadPNG(const uint8_t *data, unsigned size);
51
inline void scale(unsigned width, unsigned height, interpolation op);
52
inline void transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
53
inline void alphaBlend(uint64_t alphaColor);
54
55
protected:
56
inline uint64_t interpolate(double mu, const uint64_t *s, interpolation op);
57
inline void scaleX(unsigned width, interpolation op);
58
inline void scaleY(unsigned height, interpolation op);
59
inline bool loadBMP(const string &filename);
60
inline bool loadPNG(const string &filename);
61
};
62
63
//static
64
65
unsigned image::bitDepth(uint64_t color) {
66
unsigned depth = 0;
67
if(color) while((color & 1) == 0) color >>= 1;
68
while((color & 1) == 1) { color >>= 1; depth++; }
69
return depth;
70
}
71
72
unsigned image::bitShift(uint64_t color) {
73
unsigned shift = 0;
74
if(color) while((color & 1) == 0) { color >>= 1; shift++; }
75
return shift;
76
}
77
78
uint64_t image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) {
79
while(sourceDepth < targetDepth) {
80
color = (color << sourceDepth) | color;
81
sourceDepth += sourceDepth;
82
}
83
if(targetDepth < sourceDepth) color >>= (sourceDepth - targetDepth);
84
return color;
85
}
86
87
//public
88
89
image& image::operator=(const image &source) {
90
free();
91
92
width = source.width;
93
height = source.height;
94
pitch = source.pitch;
95
96
endian = source.endian;
97
stride = source.stride;
98
99
alpha = source.alpha;
100
red = source.red;
101
green = source.green;
102
blue = source.blue;
103
104
data = new uint8_t[width * height * stride];
105
memcpy(data, source.data, width * height * stride);
106
return *this;
107
}
108
109
image& image::operator=(image &&source) {
110
width = source.width;
111
height = source.height;
112
pitch = source.pitch;
113
114
endian = source.endian;
115
stride = source.stride;
116
117
alpha = source.alpha;
118
red = source.red;
119
green = source.green;
120
blue = source.blue;
121
122
data = source.data;
123
source.data = nullptr;
124
return *this;
125
}
126
127
image::image(const image &source) : data(nullptr) {
128
operator=(source);
129
}
130
131
image::image(image &&source) : data(nullptr) {
132
operator=(std::forward<image>(source));
133
}
134
135
image::image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) : data(nullptr) {
136
width = 0, height = 0, pitch = 0;
137
138
this->endian = endian;
139
this->depth = depth;
140
this->stride = (depth / 8) + ((depth & 7) > 0);
141
142
alpha.mask = alphaMask, red.mask = redMask, green.mask = greenMask, blue.mask = blueMask;
143
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
144
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
145
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
146
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
147
}
148
149
image::image() : data(nullptr) {
150
width = 0, height = 0, pitch = 0;
151
152
this->endian = 0;
153
this->depth = 32;
154
this->stride = 4;
155
156
alpha.mask = 255u << 24, red.mask = 255u << 16, green.mask = 255u << 8, blue.mask = 255u << 0;
157
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
158
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
159
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
160
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
161
}
162
163
image::~image() {
164
free();
165
}
166
167
uint64_t image::read(const uint8_t *data) const {
168
uint64_t result = 0;
169
if(endian == 0) {
170
for(signed n = stride - 1; n >= 0; n--) result = (result << 8) | data[n];
171
} else {
172
for(signed n = 0; n < stride; n++) result = (result << 8) | data[n];
173
}
174
return result;
175
}
176
177
void image::write(uint8_t *data, uint64_t value) const {
178
if(endian == 0) {
179
for(signed n = 0; n < stride; n++) { data[n] = value; value >>= 8; }
180
} else {
181
for(signed n = stride - 1; n >= 0; n--) { data[n] = value; value >>= 8; }
182
}
183
}
184
185
void image::free() {
186
if(data) delete[] data;
187
data = nullptr;
188
}
189
190
void image::allocate(unsigned width, unsigned height) {
191
if(data != nullptr && this->width == width && this->height == height) return;
192
free();
193
data = new uint8_t[width * height * stride]();
194
pitch = width * stride;
195
this->width = width;
196
this->height = height;
197
}
198
199
void image::clear(uint64_t color) {
200
uint8_t *dp = data;
201
for(unsigned n = 0; n < width * height; n++) {
202
write(dp, color);
203
dp += stride;
204
}
205
}
206
207
bool image::load(const string &filename) {
208
if(loadBMP(filename) == true) return true;
209
if(loadPNG(filename) == true) return true;
210
return false;
211
}
212
213
void image::scale(unsigned outputWidth, unsigned outputHeight, interpolation op) {
214
if(width != outputWidth) scaleX(outputWidth, op);
215
if(height != outputHeight) scaleY(outputHeight, op);
216
}
217
218
void image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAlphaMask, uint64_t outputRedMask, uint64_t outputGreenMask, uint64_t outputBlueMask) {
219
image output(outputEndian, outputDepth, outputAlphaMask, outputRedMask, outputGreenMask, outputBlueMask);
220
output.allocate(width, height);
221
222
#pragma omp parallel for
223
for(unsigned y = 0; y < height; y++) {
224
uint8_t *dp = output.data + output.pitch * y;
225
uint8_t *sp = data + pitch * y;
226
for(unsigned x = 0; x < width; x++) {
227
uint64_t color = read(sp);
228
sp += stride;
229
230
uint64_t a = (color & alpha.mask) >> alpha.shift;
231
uint64_t r = (color & red.mask) >> red.shift;
232
uint64_t g = (color & green.mask) >> green.shift;
233
uint64_t b = (color & blue.mask) >> blue.shift;
234
235
a = normalize(a, alpha.depth, output.alpha.depth);
236
r = normalize(r, red.depth, output.red.depth);
237
g = normalize(g, green.depth, output.green.depth);
238
b = normalize(b, blue.depth, output.blue.depth);
239
240
output.write(dp, (a << output.alpha.shift) | (r << output.red.shift) | (g << output.green.shift) | (b << output.blue.shift));
241
dp += output.stride;
242
}
243
}
244
245
operator=(std::move(output));
246
}
247
248
void image::alphaBlend(uint64_t alphaColor) {
249
uint64_t alphaR = (alphaColor & red.mask) >> red.shift;
250
uint64_t alphaG = (alphaColor & green.mask) >> green.shift;
251
uint64_t alphaB = (alphaColor & blue.mask) >> blue.shift;
252
253
#pragma omp parallel for
254
for(unsigned y = 0; y < height; y++) {
255
uint8_t *dp = data + pitch * y;
256
for(unsigned x = 0; x < width; x++) {
257
uint64_t color = read(dp);
258
259
uint64_t colorA = (color & alpha.mask) >> alpha.shift;
260
uint64_t colorR = (color & red.mask) >> red.shift;
261
uint64_t colorG = (color & green.mask) >> green.shift;
262
uint64_t colorB = (color & blue.mask) >> blue.shift;
263
double alphaScale = (double)colorA / (double)((1 << alpha.depth) - 1);
264
265
colorA = (1 << alpha.depth) - 1;
266
colorR = (colorR * alphaScale) + (alphaR * (1.0 - alphaScale));
267
colorG = (colorG * alphaScale) + (alphaG * (1.0 - alphaScale));
268
colorB = (colorB * alphaScale) + (alphaB * (1.0 - alphaScale));
269
270
write(dp, (colorA << alpha.shift) | (colorR << red.shift) | (colorG << green.shift) | (colorB << blue.shift));
271
dp += stride;
272
}
273
}
274
}
275
276
//protected
277
278
uint64_t image::interpolate(double mu, const uint64_t *s, double (*op)(double, double, double, double, double)) {
279
uint64_t aa = (s[0] & alpha.mask) >> alpha.shift, ar = (s[0] & red.mask) >> red.shift,
280
ag = (s[0] & green.mask) >> green.shift, ab = (s[0] & blue.mask) >> blue.shift;
281
uint64_t ba = (s[1] & alpha.mask) >> alpha.shift, br = (s[1] & red.mask) >> red.shift,
282
bg = (s[1] & green.mask) >> green.shift, bb = (s[1] & blue.mask) >> blue.shift;
283
uint64_t ca = (s[2] & alpha.mask) >> alpha.shift, cr = (s[2] & red.mask) >> red.shift,
284
cg = (s[2] & green.mask) >> green.shift, cb = (s[2] & blue.mask) >> blue.shift;
285
uint64_t da = (s[3] & alpha.mask) >> alpha.shift, dr = (s[3] & red.mask) >> red.shift,
286
dg = (s[3] & green.mask) >> green.shift, db = (s[3] & blue.mask) >> blue.shift;
287
288
int64_t A = op(mu, aa, ba, ca, da);
289
int64_t R = op(mu, ar, br, cr, dr);
290
int64_t G = op(mu, ag, bg, cg, dg);
291
int64_t B = op(mu, ab, bb, cb, db);
292
293
A = max(0, min(A, (1 << alpha.depth) - 1));
294
R = max(0, min(R, (1 << red.depth) - 1));
295
G = max(0, min(G, (1 << green.depth) - 1));
296
B = max(0, min(B, (1 << blue.depth) - 1));
297
298
return (A << alpha.shift) | (R << red.shift) | (G << green.shift) | (B << blue.shift);
299
}
300
301
void image::scaleX(unsigned outputWidth, interpolation op) {
302
uint8_t *outputData = new uint8_t[outputWidth * height * stride];
303
unsigned outputPitch = outputWidth * stride;
304
double step = (double)width / (double)outputWidth;
305
const uint8_t *terminal = data + pitch * height;
306
307
#pragma omp parallel for
308
for(unsigned y = 0; y < height; y++) {
309
uint8_t *dp = outputData + outputPitch * y;
310
uint8_t *sp = data + pitch * y;
311
312
double fraction = 0.0;
313
uint64_t s[4] = { sp < terminal ? read(sp) : 0 }; //B,C (0,1) = center of kernel { 0, 0, 1, 2 }
314
s[1] = s[0];
315
s[2] = sp + stride < terminal ? read(sp += stride) : s[1];
316
s[3] = sp + stride < terminal ? read(sp += stride) : s[2];
317
318
for(unsigned x = 0; x < width; x++) {
319
while(fraction <= 1.0) {
320
if(dp >= outputData + outputPitch * height) break;
321
write(dp, interpolate(fraction, (const uint64_t*)&s, op));
322
dp += stride;
323
fraction += step;
324
}
325
326
s[0] = s[1]; s[1] = s[2]; s[2] = s[3];
327
if(sp + stride < terminal) s[3] = read(sp += stride);
328
fraction -= 1.0;
329
}
330
}
331
332
free();
333
data = outputData;
334
width = outputWidth;
335
pitch = width * stride;
336
}
337
338
void image::scaleY(unsigned outputHeight, interpolation op) {
339
uint8_t *outputData = new uint8_t[width * outputHeight * stride];
340
double step = (double)height / (double)outputHeight;
341
const uint8_t *terminal = data + pitch * height;
342
343
#pragma omp parallel for
344
for(unsigned x = 0; x < width; x++) {
345
uint8_t *dp = outputData + stride * x;
346
uint8_t *sp = data + stride * x;
347
348
double fraction = 0.0;
349
uint64_t s[4] = { sp < terminal ? read(sp) : 0 };
350
s[1] = s[0];
351
s[2] = sp + pitch < terminal ? read(sp += pitch) : s[1];
352
s[3] = sp + pitch < terminal ? read(sp += pitch) : s[2];
353
354
for(unsigned y = 0; y < height; y++) {
355
while(fraction <= 1.0) {
356
if(dp >= outputData + pitch * outputHeight) break;
357
write(dp, interpolate(fraction, (const uint64_t*)&s, op));
358
dp += pitch;
359
fraction += step;
360
}
361
362
s[0] = s[1]; s[1] = s[2]; s[2] = s[3];
363
if(sp + pitch < terminal) s[3] = read(sp += pitch);
364
fraction -= 1.0;
365
}
366
}
367
368
free();
369
data = outputData;
370
height = outputHeight;
371
}
372
373
bool image::loadBMP(const string &filename) {
374
uint32_t *outputData;
375
unsigned outputWidth, outputHeight;
376
if(bmp::read(filename, outputData, outputWidth, outputHeight) == false) return false;
377
378
allocate(outputWidth, outputHeight);
379
const uint32_t *sp = outputData;
380
uint8_t *dp = data;
381
382
for(unsigned y = 0; y < outputHeight; y++) {
383
for(unsigned x = 0; x < outputWidth; x++) {
384
uint32_t color = *sp++;
385
uint64_t a = normalize((uint8_t)(color >> 24), 8, alpha.depth);
386
uint64_t r = normalize((uint8_t)(color >> 16), 8, red.depth);
387
uint64_t g = normalize((uint8_t)(color >> 8), 8, green.depth);
388
uint64_t b = normalize((uint8_t)(color >> 0), 8, blue.depth);
389
write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift));
390
dp += stride;
391
}
392
}
393
394
delete[] outputData;
395
return true;
396
}
397
398
bool image::loadPNG(const uint8_t *pngData, unsigned pngSize) {
399
png source;
400
if(source.decode(pngData, pngSize) == false) return false;
401
402
allocate(source.info.width, source.info.height);
403
const uint8_t *sp = source.data;
404
uint8_t *dp = data;
405
406
auto decode = [&]() -> uint64_t {
407
uint64_t p, r, g, b, a;
408
409
switch(source.info.colorType) {
410
case 0: //L
411
r = g = b = source.readbits(sp);
412
a = (1 << source.info.bitDepth) - 1;
413
break;
414
case 2: //R,G,B
415
r = source.readbits(sp);
416
g = source.readbits(sp);
417
b = source.readbits(sp);
418
a = (1 << source.info.bitDepth) - 1;
419
break;
420
case 3: //P
421
p = source.readbits(sp);
422
r = source.info.palette[p][0];
423
g = source.info.palette[p][1];
424
b = source.info.palette[p][2];
425
a = (1 << source.info.bitDepth) - 1;
426
break;
427
case 4: //L,A
428
r = g = b = source.readbits(sp);
429
a = source.readbits(sp);
430
break;
431
case 6: //R,G,B,A
432
r = source.readbits(sp);
433
g = source.readbits(sp);
434
b = source.readbits(sp);
435
a = source.readbits(sp);
436
break;
437
}
438
439
a = normalize(a, source.info.bitDepth, alpha.depth);
440
r = normalize(r, source.info.bitDepth, red.depth);
441
g = normalize(g, source.info.bitDepth, green.depth);
442
b = normalize(b, source.info.bitDepth, blue.depth);
443
444
return (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift);
445
};
446
447
for(unsigned y = 0; y < height; y++) {
448
for(unsigned x = 0; x < width; x++) {
449
write(dp, decode());
450
dp += stride;
451
}
452
}
453
454
return true;
455
}
456
457
bool image::loadPNG(const string &filename) {
458
filemap map;
459
if(map.open(filename, filemap::mode::read) == false) return false;
460
return loadPNG(map.data(), map.size());
461
}
462
463
}
464
465
#endif
466
467