Line data Source code
1 : /**
2 : * @brief texture information class definitions
3 : *
4 : * This file implements a class containing the associated date with a texture image.
5 : * It is only used by texture.cpp and shaderparam.cpp.
6 : */
7 : #include "../libprimis-headers/cube.h"
8 : #include "../../shared/geomexts.h"
9 : #include "../../shared/glexts.h"
10 :
11 : #include "imagedata.h"
12 : #include "renderwindow.h"
13 : #include "shaderparam.h"
14 : #include "texture.h"
15 :
16 : #include "interface/control.h"
17 : #include "interface/console.h"
18 : #include "interface/cs.h" //for stringslice
19 :
20 : namespace
21 : {
22 : //helper function for texturedata
23 0 : vec parsevec(const char *arg)
24 : {
25 0 : vec v(0, 0, 0);
26 0 : uint i = 0;
27 0 : for(; arg[0] && (!i || arg[0]=='/') && i<3; arg += std::strcspn(arg, "/,><"), i++)
28 : {
29 0 : if(i)
30 : {
31 0 : arg++;
32 : }
33 0 : v[i] = std::atof(arg);
34 : }
35 0 : if(i==1)
36 : {
37 0 : v.y = v.z = v.x;
38 : }
39 0 : return v;
40 : }
41 :
42 : //helper function for texturedata
43 0 : bool canloadsurface(const char *name)
44 : {
45 0 : stream *f = openfile(name, "rb");
46 0 : if(!f)
47 : {
48 0 : return false;
49 : }
50 0 : delete f;
51 0 : return true;
52 : }
53 : }
54 :
55 10 : ImageData::ImageData()
56 10 : : data(nullptr), owner(nullptr), freefunc(nullptr)
57 10 : {}
58 :
59 :
60 1 : ImageData::ImageData(int nw, int nh, int nbpp, int nlevels, int nalign, GLenum ncompressed)
61 : {
62 1 : setdata(nullptr, nw, nh, nbpp, nlevels, nalign, ncompressed);
63 1 : }
64 :
65 11 : ImageData::~ImageData()
66 : {
67 11 : cleanup();
68 11 : }
69 :
70 1 : int ImageData::width() const
71 : {
72 1 : return w;
73 : }
74 :
75 2 : int ImageData::height() const
76 : {
77 2 : return h;
78 : }
79 :
80 2 : int ImageData::depth() const
81 : {
82 2 : return bpp;
83 : }
84 :
85 2 : void ImageData::setdata(uchar *ndata, int nw, int nh, int nbpp, int nlevels, int nalign, GLenum ncompressed)
86 : {
87 2 : w = nw;
88 2 : h = nh;
89 2 : bpp = nbpp;
90 2 : levels = nlevels;
91 2 : align = nalign;
92 2 : pitch = align ? 0 : w*bpp;
93 2 : compressed = ncompressed;
94 2 : data = ndata ? ndata : new uchar[calcsize()];
95 2 : if(!ndata)
96 : {
97 1 : owner = this;
98 1 : freefunc = nullptr;
99 : }
100 2 : }
101 :
102 0 : int ImageData::calclevelsize(int level) const
103 : {
104 0 : return ((std::max(w>>level, 1)+align-1)/align)*((std::max(h>>level, 1)+align-1)/align)*bpp;
105 : }
106 :
107 1 : int ImageData::calcsize() const
108 : {
109 1 : if(!align)
110 : {
111 1 : return w*h*bpp;
112 : }
113 0 : int lw = w,
114 0 : lh = h,
115 0 : size = 0;
116 0 : for(int i = 0; i < levels; ++i)
117 : {
118 0 : if(lw<=0)
119 : {
120 0 : lw = 1;
121 : }
122 0 : if(lh<=0)
123 : {
124 0 : lh = 1;
125 : }
126 0 : size += ((lw+align-1)/align)*((lh+align-1)/align)*bpp;
127 0 : if(lw*lh==1)
128 : {
129 0 : break;
130 : }
131 0 : lw >>= 1;
132 0 : lh >>= 1;
133 : }
134 0 : return size;
135 : }
136 :
137 11 : void ImageData::disown()
138 : {
139 11 : data = nullptr;
140 11 : owner = nullptr;
141 11 : freefunc = nullptr;
142 11 : }
143 :
144 11 : void ImageData::cleanup()
145 : {
146 11 : if(owner==this)
147 : {
148 1 : delete[] data;
149 : }
150 10 : else if(freefunc)
151 : {
152 1 : (*freefunc)(owner);
153 : }
154 11 : disown();
155 11 : }
156 :
157 : // Deletes the data associated with the current ImageData object
158 : //and makes the object point to the one passed by parameter
159 0 : void ImageData::replace(ImageData &d)
160 : {
161 0 : cleanup();
162 0 : *this = d;
163 0 : if(owner == &d)
164 : {
165 0 : owner = this;
166 : }
167 0 : d.disown();
168 0 : }
169 :
170 1 : void ImageData::wraptex(SDL_Surface *s)
171 : {
172 1 : setdata(static_cast<uchar *>(s->pixels), s->w, s->h, s->format->BytesPerPixel);
173 1 : pitch = s->pitch;
174 1 : owner = s;
175 1 : freefunc = reinterpret_cast<void (*)(void *)>(SDL_FreeSurface);
176 1 : }
177 :
178 : #define WRITE_TEX(t, body) do \
179 : { \
180 : uchar *dstrow = t.data; \
181 : for(int y = 0; y < t.h; ++y) \
182 : { \
183 : for(uchar *dst = dstrow, *end = &dstrow[t.w*t.bpp]; dst < end; dst += t.bpp) \
184 : { \
185 : body; \
186 : } \
187 : dstrow += t.pitch; \
188 : } \
189 : } while(0)
190 :
191 : /**
192 : * @brief Manpulates one texture using another texture's data.
193 : *
194 : * For each pixel in the source, executes an arbitrary effect, passed as the body
195 : * parameter.
196 : *
197 : * @param t imagedata object, destination of texture write
198 : * @param s imagedata object, source of texture write
199 : * @param body code to execute
200 : */
201 : #define READ_WRITE_TEX(t, s, body) do \
202 : { \
203 : uchar *dstrow = t.data, *srcrow = s.data; \
204 : for(int y = 0; y < t.h; ++y) \
205 : { \
206 : for(uchar *dst = dstrow, *src = srcrow, *end = &srcrow[s.w*s.bpp]; src < end; dst += t.bpp, src += s.bpp) \
207 : { \
208 : body; \
209 : } \
210 : dstrow += t.pitch; \
211 : srcrow += s.pitch; \
212 : } \
213 : } while(0)
214 :
215 : #define READ_2_WRITE_TEX(t, s1, src1, s2, src2, body) do \
216 : { \
217 : uchar *dstrow = t.data, *src1row = s1.data, *src2row = s2.data; \
218 : for(int y = 0; y < t.h; ++y) \
219 : { \
220 : for(uchar *dst = dstrow, *end = &dstrow[t.w*t.bpp], *src1 = src1row, *src2 = src2row; dst < end; dst += t.bpp, src1 += s1.bpp, src2 += s2.bpp) \
221 : { \
222 : body; \
223 : } \
224 : dstrow += t.pitch; \
225 : src1row += s1.pitch; \
226 : src2row += s2.pitch; \
227 : } \
228 : } while(0)
229 :
230 : #define READ_WRITE_RGB_TEX(t, s, body) \
231 : { \
232 : if(t.bpp >= 3) \
233 : { \
234 : READ_WRITE_TEX(t, s, body); \
235 : } \
236 : else \
237 : { \
238 : ImageData rgb(t.w, t.h, 3); \
239 : READ_2_WRITE_TEX(rgb, t, orig, s, src, { dst[0] = dst[1] = dst[2] = orig[0]; body; }); \
240 : t.replace(rgb); \
241 : } \
242 : }
243 :
244 0 : void ImageData::forcergbimage()
245 : {
246 0 : if(bpp >= 3)
247 : {
248 0 : return;
249 : }
250 0 : ImageData d(w, h, 3);
251 0 : READ_WRITE_TEX(d, ((*this)), { dst[0] = dst[1] = dst[2] = src[0]; });
252 0 : replace(d);
253 0 : }
254 :
255 : #define READ_WRITE_RGBA_TEX(t, s, body) \
256 : { \
257 : if(t.bpp >= 4) \
258 : { \
259 : READ_WRITE_TEX(t, s, body); \
260 : } \
261 : else \
262 : { \
263 : ImageData rgba(t.w, t.h, 4); \
264 : if(t.bpp==3) \
265 : { \
266 : READ_2_WRITE_TEX(rgba, t, orig, s, src, { dst[0] = orig[0]; dst[1] = orig[1]; dst[2] = orig[2]; body; }); \
267 : } \
268 : else \
269 : { \
270 : READ_2_WRITE_TEX(rgba, t, orig, s, src, { dst[0] = dst[1] = dst[2] = orig[0]; body; }); \
271 : } \
272 : t.replace(rgba); \
273 : } \
274 : }
275 :
276 0 : void ImageData::swizzleimage()
277 : {
278 0 : if(bpp==2)
279 : {
280 0 : ImageData d(w, h, 4);
281 0 : READ_WRITE_TEX(d, (*this), { dst[0] = dst[1] = dst[2] = src[0]; dst[3] = src[1]; });
282 0 : replace(d);
283 0 : }
284 0 : else if(bpp==1)
285 : {
286 0 : ImageData d(w, h, 3);
287 0 : READ_WRITE_TEX(d, (*this), { dst[0] = dst[1] = dst[2] = src[0]; });
288 0 : replace(d);
289 0 : }
290 0 : }
291 :
292 0 : void ImageData::scaleimage(int w, int h)
293 : {
294 0 : ImageData d(w, h, bpp);
295 0 : scaletexture(data, w, h, bpp, pitch, d.data, w, h);
296 0 : replace(d);
297 0 : }
298 :
299 0 : void ImageData::texreorient(bool flipx, bool flipy, bool swapxy, int type)
300 : {
301 0 : ImageData d(swapxy ? h : w, swapxy ? w : h, bpp, levels, align, compressed);
302 0 : switch(compressed)
303 : {
304 : default:
305 0 : if(type == Tex_Normal && bpp >= 3)
306 : {
307 0 : reorientnormals(data, w, h, bpp, pitch, d.data, flipx, flipy, swapxy);
308 : }
309 : else
310 : {
311 0 : reorienttexture(data, w, h, bpp, pitch, d.data, flipx, flipy, swapxy);
312 : }
313 0 : break;
314 : }
315 0 : replace(d);
316 0 : }
317 :
318 0 : void ImageData::texrotate(int numrots, int type)
319 : {
320 0 : if(numrots>=1 && numrots<=7)
321 : {
322 0 : const texrotation &r = texrotations[numrots];
323 0 : texreorient(r.flipx, r.flipy, r.swapxy, type);
324 : }
325 0 : }
326 :
327 0 : void ImageData::texoffset(int xoffset, int yoffset)
328 : {
329 0 : xoffset = std::max(xoffset, 0);
330 0 : xoffset %= w;
331 0 : yoffset = std::max(yoffset, 0);
332 0 : yoffset %= h;
333 0 : if(!xoffset && !yoffset)
334 : {
335 0 : return;
336 : }
337 0 : ImageData d(w, h, bpp);
338 0 : uchar *src = data;
339 0 : for(int y = 0; y < h; ++y)
340 : {
341 0 : uchar *dst = static_cast<uchar *>(d.data)+((y+yoffset)%d.h)*d.pitch;
342 0 : std::memcpy(dst+xoffset*bpp, src, (w-xoffset)*bpp);
343 0 : std::memcpy(dst, src+(w-xoffset)*bpp, xoffset*bpp);
344 0 : src += pitch;
345 : }
346 0 : replace(d);
347 0 : }
348 :
349 0 : void ImageData::texcrop(int x, int y, int w, int h)
350 : {
351 0 : x = std::clamp(x, 0, w);
352 0 : y = std::clamp(y, 0, h);
353 0 : w = std::min(w < 0 ? w : w, w - x);
354 0 : h = std::min(h < 0 ? h : h, h - y);
355 0 : if(!w || !h)
356 : {
357 0 : return;
358 : }
359 0 : ImageData d(w, h, bpp);
360 0 : uchar *src = data + y*pitch + x*bpp,
361 0 : *dst = d.data;
362 0 : for(int y = 0; y < h; ++y)
363 : {
364 0 : std::memcpy(dst, src, w*bpp);
365 0 : src += pitch;
366 0 : dst += d.pitch;
367 : }
368 0 : replace(d);
369 0 : }
370 :
371 0 : void ImageData::texmad(const vec &mul, const vec &add)
372 : {
373 0 : if(bpp < 3 && (mul.x != mul.y || mul.y != mul.z || add.x != add.y || add.y != add.z))
374 : {
375 0 : swizzleimage();
376 : }
377 0 : int maxk = std::min(static_cast<int>(bpp), 3);
378 0 : WRITE_TEX((*this),
379 : for(int k = 0; k < maxk; ++k)
380 : {
381 : dst[k] = static_cast<uchar>(std::clamp(dst[k]*mul[k] + 255*add[k], 0.0f, 255.0f));
382 : }
383 : );
384 0 : }
385 :
386 0 : void ImageData::texcolorify(const vec &color, vec weights)
387 : {
388 0 : if(bpp < 3)
389 : {
390 0 : return;
391 : }
392 0 : if(weights.iszero())
393 : {
394 0 : weights = vec(0.21f, 0.72f, 0.07f);
395 : }
396 0 : WRITE_TEX((*this),
397 : float lum = dst[0]*weights.x + dst[1]*weights.y + dst[2]*weights.z;
398 : for(int k = 0; k < 3; ++k)
399 : {
400 : dst[k] = static_cast<uchar>(std::clamp(lum*color[k], 0.0f, 255.0f));
401 : }
402 : );
403 : }
404 :
405 0 : void ImageData::texcolormask(const vec &color1, const vec &color2)
406 : {
407 0 : if(bpp < 4)
408 : {
409 0 : return;
410 : }
411 0 : ImageData d(w, h, 3);
412 0 : READ_WRITE_TEX(d, (*this),
413 : vec color;
414 : color.lerp(color2, color1, src[3]/255.0f);
415 : for(int k = 0; k < 3; ++k)
416 : {
417 : dst[k] = static_cast<uchar>(std::clamp(color[k]*src[k], 0.0f, 255.0f));
418 : }
419 : );
420 0 : replace(d);
421 0 : }
422 :
423 0 : void ImageData::texdup(int srcchan, int dstchan)
424 : {
425 0 : if(srcchan==dstchan || std::max(srcchan, dstchan) >= bpp)
426 : {
427 0 : return;
428 : }
429 0 : WRITE_TEX((*this), dst[dstchan] = dst[srcchan]);
430 : }
431 :
432 0 : void ImageData::texmix(int c1, int c2, int c3, int c4)
433 : {
434 0 : int numchans = c1 < 0 ? 0 : (c2 < 0 ? 1 : (c3 < 0 ? 2 : (c4 < 0 ? 3 : 4)));
435 0 : if(numchans <= 0)
436 : {
437 0 : return;
438 : }
439 0 : ImageData d(w, h, numchans);
440 0 : READ_WRITE_TEX(d, (*this),
441 : switch(numchans)
442 : {
443 : case 4:
444 : {
445 : dst[3] = src[c4];
446 : break;
447 : }
448 : case 3:
449 : {
450 : dst[2] = src[c3];
451 : break;
452 : }
453 : case 2:
454 : {
455 : dst[1] = src[c2];
456 : break;
457 : }
458 : case 1:
459 : {
460 : dst[0] = src[c1];
461 : break;
462 : }
463 : }
464 : );
465 0 : replace(d);
466 0 : }
467 :
468 0 : void ImageData::texgrey()
469 : {
470 0 : if(bpp <= 2)
471 : {
472 0 : return;
473 : }
474 0 : ImageData d(w, h, bpp >= 4 ? 2 : 1);
475 0 : if(bpp >= 4)
476 : {
477 0 : READ_WRITE_TEX(d, (*this),
478 : dst[0] = src[0];
479 : dst[1] = src[3];
480 : );
481 : }
482 : else
483 : {
484 0 : READ_WRITE_TEX(d, (*this), dst[0] = src[0]);
485 : }
486 0 : replace(d);
487 0 : }
488 :
489 0 : void ImageData::texpremul()
490 : {
491 0 : switch(bpp)
492 : {
493 0 : case 2:
494 0 : WRITE_TEX((*this),
495 : dst[0] = static_cast<uchar>((static_cast<uint>(dst[0])*static_cast<uint>(dst[1]))/255);
496 : );
497 0 : break;
498 0 : case 4:
499 0 : WRITE_TEX((*this),
500 : uint alpha = dst[3];
501 : dst[0] = static_cast<uchar>((static_cast<uint>(dst[0])*alpha)/255);
502 : dst[1] = static_cast<uchar>((static_cast<uint>(dst[1])*alpha)/255);
503 : dst[2] = static_cast<uchar>((static_cast<uint>(dst[2])*alpha)/255);
504 : );
505 0 : break;
506 : }
507 0 : }
508 :
509 0 : void ImageData::texagrad(float x2, float y2, float x1, float y1)
510 : {
511 0 : if(bpp != 2 && bpp != 4)
512 : {
513 0 : return;
514 : }
515 0 : y1 = 1 - y1;
516 0 : y2 = 1 - y2;
517 0 : float minx = 1,
518 0 : miny = 1,
519 0 : maxx = 1,
520 0 : maxy = 1;
521 0 : if(x1 != x2)
522 : {
523 0 : minx = (0 - x1) / (x2 - x1);
524 0 : maxx = (1 - x1) / (x2 - x1);
525 : }
526 0 : if(y1 != y2)
527 : {
528 0 : miny = (0 - y1) / (y2 - y1);
529 0 : maxy = (1 - y1) / (y2 - y1);
530 : }
531 0 : float dx = (maxx - minx)/std::max(w-1, 1),
532 0 : dy = (maxy - miny)/std::max(h-1, 1),
533 0 : cury = miny;
534 0 : for(uchar *dstrow = data + bpp - 1, *endrow = dstrow + h*pitch; dstrow < endrow; dstrow += pitch)
535 : {
536 0 : float curx = minx;
537 0 : for(uchar *dst = dstrow, *end = &dstrow[w*bpp]; dst < end; dst += bpp)
538 : {
539 0 : dst[0] = static_cast<uchar>(dst[0]*std::clamp(curx, 0.0f, 1.0f)*std::clamp(cury, 0.0f, 1.0f));
540 0 : curx += dx;
541 : }
542 0 : cury += dy;
543 : }
544 : }
545 :
546 0 : void ImageData::texblend(const ImageData &s0, const ImageData &m0)
547 : {
548 0 : ImageData s(s0),
549 0 : m(m0);
550 0 : if(s.w != w || s.h != h)
551 : {
552 0 : s.scaleimage(w, h);
553 : }
554 0 : if(m.w != w || m.h != h)
555 : {
556 0 : m.scaleimage(w, h);
557 : }
558 : if(&s == &m)
559 : {
560 : if(s.bpp == 2)
561 : {
562 : if(bpp >= 3)
563 : {
564 : s.swizzleimage();
565 : }
566 : }
567 : else if(s.bpp == 4)
568 : {
569 : if(bpp < 3)
570 : {
571 : swizzleimage();
572 : }
573 : }
574 : else
575 : {
576 : return;
577 : }
578 : //need to declare int for each var because it's inside a macro body
579 : if(bpp < 3)
580 : {
581 : READ_WRITE_TEX((*this), s,
582 : int srcblend = src[1];
583 : int dstblend = 255 - srcblend;
584 : dst[0] = static_cast<uchar>((dst[0]*dstblend + src[0]*srcblend)/255);
585 : );
586 : }
587 : else
588 : {
589 : READ_WRITE_TEX((*this), s,
590 : int srcblend = src[3];
591 : int dstblend = 255 - srcblend;
592 : dst[0] = static_cast<uchar>((dst[0]*dstblend + src[0]*srcblend)/255);
593 : dst[1] = static_cast<uchar>((dst[1]*dstblend + src[1]*srcblend)/255);
594 : dst[2] = static_cast<uchar>((dst[2]*dstblend + src[2]*srcblend)/255);
595 : );
596 : }
597 : }
598 : else
599 : {
600 0 : if(s.bpp < 3)
601 : {
602 0 : if(bpp >= 3)
603 : {
604 0 : s.swizzleimage();
605 : }
606 : }
607 0 : else if(bpp < 3)
608 : {
609 0 : swizzleimage();
610 : }
611 0 : if(bpp < 3)
612 : {
613 0 : READ_2_WRITE_TEX((*this), s, src, m, mask,
614 : int srcblend = mask[0];
615 : int dstblend = 255 - srcblend;
616 : dst[0] = static_cast<uchar>((dst[0]*dstblend + src[0]*srcblend)/255);
617 : );
618 : }
619 : else
620 : {
621 0 : READ_2_WRITE_TEX((*this), s, src, m, mask,
622 : int srcblend = mask[0];
623 : int dstblend = 255 - srcblend;
624 : dst[0] = static_cast<uchar>((dst[0]*dstblend + src[0]*srcblend)/255);
625 : dst[1] = static_cast<uchar>((dst[1]*dstblend + src[1]*srcblend)/255);
626 : dst[2] = static_cast<uchar>((dst[2]*dstblend + src[2]*srcblend)/255);
627 : );
628 : }
629 : }
630 0 : }
631 :
632 0 : void ImageData::addglow(const ImageData &g, const vec &glowcolor)
633 : {
634 0 : if(g.bpp < 3)
635 : {
636 0 : READ_WRITE_RGB_TEX((*this), g,
637 : for(int k = 0; k < 3; ++k)
638 : {
639 : dst[k] = std::clamp(static_cast<int>(dst[k]) + static_cast<int>(src[0]*glowcolor[k]), 0, 255);
640 : }
641 : );
642 : }
643 : else
644 : {
645 0 : READ_WRITE_RGB_TEX((*this), g,
646 : for(int k = 0; k < 3; ++k)
647 : {
648 : dst[k] = std::clamp(static_cast<int>(dst[k]) + static_cast<int>(src[k]*glowcolor[k]), 0, 255);
649 : }
650 : );
651 : }
652 0 : }
653 :
654 0 : void ImageData::mergespec(const ImageData &s)
655 : {
656 0 : if(s.bpp < 3)
657 : {
658 0 : READ_WRITE_RGBA_TEX((*this), s,
659 : dst[3] = src[0];
660 : );
661 : }
662 : else
663 : {
664 0 : READ_WRITE_RGBA_TEX((*this), s,
665 : dst[3] = (static_cast<int>(src[0]) + static_cast<int>(src[1]) + static_cast<int>(src[2]))/3;
666 : );
667 : }
668 0 : }
669 :
670 0 : void ImageData::mergedepth(const ImageData &z)
671 : {
672 0 : READ_WRITE_RGBA_TEX((*this), z,
673 : dst[3] = src[0];
674 : );
675 0 : }
676 :
677 0 : void ImageData::mergealpha(const ImageData &s)
678 : {
679 0 : if(s.bpp < 3)
680 : {
681 0 : READ_WRITE_RGBA_TEX((*this), s,
682 : dst[3] = src[0];
683 : );
684 : }
685 : else
686 : {
687 0 : READ_WRITE_RGBA_TEX((*this), s,
688 : dst[3] = src[3];
689 : );
690 : }
691 0 : }
692 :
693 0 : void ImageData::collapsespec()
694 : {
695 0 : ImageData d(w, h, 1);
696 0 : if(bpp >= 3)
697 : {
698 0 : READ_WRITE_TEX(d, (*this),
699 : {
700 : dst[0] = (static_cast<int>(src[0]) + static_cast<int>(src[1]) + static_cast<int>(src[2]))/3;
701 : });
702 : }
703 : else
704 : {
705 0 : READ_WRITE_TEX(d, (*this), { dst[0] = src[0]; });
706 : }
707 0 : replace(d);
708 0 : }
709 :
710 0 : void ImageData::texnormal(int emphasis)
711 : {
712 0 : ImageData d(w, h, 3);
713 0 : const uchar *src = data;
714 0 : uchar *dst = d.data;
715 0 : for(int y = 0; y < h; ++y)
716 : {
717 0 : for(int x = 0; x < w; ++x)
718 : {
719 0 : vec normal(0.0f, 0.0f, 255.0f/emphasis);
720 0 : normal.x += src[y*pitch + ((x+w-1)%w)*bpp];
721 0 : normal.x -= src[y*pitch + ((x+1)%w)*bpp];
722 0 : normal.y += src[((y+h-1)%h)*pitch + x*bpp];
723 0 : normal.y -= src[((y+1)%h)*pitch + x*bpp];
724 0 : normal.normalize();
725 0 : *(dst++) = static_cast<uchar>(127.5f + normal.x*127.5f);
726 0 : *(dst++) = static_cast<uchar>(127.5f + normal.y*127.5f);
727 0 : *(dst++) = static_cast<uchar>(127.5f + normal.z*127.5f);
728 : }
729 : }
730 0 : replace(d);
731 0 : }
732 :
733 0 : bool ImageData::matchstring(std::string_view s, size_t len, std::string_view d)
734 : {
735 0 : return len == d.size() && !std::memcmp(s.data(), d.data(), d.size());
736 : }
737 :
738 10 : bool ImageData::texturedata(const char *tname, bool msg, int * const compress, int * const wrap, const char *tdir, int ttype)
739 : {
740 0 : auto parsetexcommands = [] (const char *&cmds, const char *&cmd, size_t &len, std::array<const char *, 4> &arg)
741 : {
742 0 : const char *end = nullptr;
743 0 : cmd = &cmds[1];
744 0 : end = std::strchr(cmd, '>');
745 0 : if(!end)
746 : {
747 0 : return true;
748 : }
749 0 : cmds = std::strchr(cmd, '<');
750 0 : len = std::strcspn(cmd, ":,><");
751 0 : for(int i = 0; i < 4; ++i)
752 : {
753 0 : arg[i] = std::strchr(i ? arg[i-1] : cmd, i ? ',' : ':');
754 0 : if(!arg[i] || arg[i] >= end)
755 : {
756 0 : arg[i] = "";
757 : }
758 : else
759 : {
760 0 : arg[i]++;
761 : }
762 : }
763 0 : return false;
764 : };
765 :
766 10 : const char *cmds = nullptr,
767 10 : *file = tname;
768 10 : if(tname[0]=='<')
769 : {
770 0 : cmds = tname;
771 0 : file = std::strrchr(tname, '>');
772 0 : if(!file)
773 : {
774 0 : if(msg)
775 : {
776 0 : conoutf(Console_Error, "could not load <> modified texture %s", tname);
777 : }
778 0 : return false;
779 : }
780 0 : file++;
781 : }
782 : string pname;
783 10 : if(tdir)
784 : {
785 0 : formatstring(pname, "%s/%s", tdir, file);
786 0 : file = path(pname);
787 : }
788 10 : for(const char *pcmds = cmds; pcmds;)
789 : {
790 0 : const char *cmd = nullptr;
791 0 : size_t len = 0;
792 0 : std::array<const char *, 4> arg = { nullptr, nullptr, nullptr, nullptr };
793 :
794 0 : if(parsetexcommands(pcmds, cmd, len, arg))
795 : {
796 0 : break;
797 : }
798 :
799 0 : if(matchstring(cmd, len, "stub"))
800 : {
801 0 : return canloadsurface(file);
802 : }
803 : }
804 10 : if(msg)
805 : {
806 5 : renderprogress(loadprogress, file);
807 : }
808 10 : if(!data)
809 : {
810 10 : SDL_Surface *s = loadsurface(file);
811 10 : if(!s)
812 : {
813 9 : if(msg)
814 : {
815 5 : conoutf(Console_Error, "could not load texture %s", file);
816 : }
817 9 : return false;
818 : }
819 1 : int bpp = s->format->BitsPerPixel;
820 1 : if(bpp%8 || !texformat(bpp/8))
821 : {
822 0 : SDL_FreeSurface(s); conoutf(Console_Error, "texture must be 8, 16, 24, or 32 bpp: %s", file);
823 0 : return false;
824 : }
825 1 : if(std::max(s->w, s->h) > (1<<12))
826 : {
827 0 : SDL_FreeSurface(s); conoutf(Console_Error, "texture size exceeded %dx%d pixels: %s", 1<<12, 1<<12, file);
828 0 : return false;
829 : }
830 1 : wraptex(s);
831 : }
832 :
833 1 : while(cmds)
834 : {
835 0 : const char *cmd = nullptr;
836 0 : size_t len = 0;
837 0 : std::array<const char *, 4> arg = { nullptr, nullptr, nullptr, nullptr };
838 :
839 0 : if(parsetexcommands(cmds, cmd, len, arg))
840 : {
841 0 : break;
842 : }
843 :
844 0 : if(compressed)
845 : {
846 0 : goto compressed; //see `compressed` nested between else & if (yes it's ugly)
847 : }
848 0 : if(matchstring(cmd, len, "mad"))
849 : {
850 0 : texmad(parsevec(arg[0]), parsevec(arg[1]));
851 : }
852 0 : else if(matchstring(cmd, len, "colorify"))
853 : {
854 0 : texcolorify(parsevec(arg[0]), parsevec(arg[1]));
855 : }
856 0 : else if(matchstring(cmd, len, "colormask"))
857 : {
858 0 : texcolormask(parsevec(arg[0]), *arg[1] ? parsevec(arg[1]) : vec(1, 1, 1));
859 : }
860 0 : else if(matchstring(cmd, len, "normal"))
861 : {
862 0 : int emphasis = std::atoi(arg[0]);
863 0 : texnormal(emphasis > 0 ? emphasis : 3);
864 : }
865 0 : else if(matchstring(cmd, len, "dup"))
866 : {
867 0 : texdup(std::atoi(arg[0]), std::atoi(arg[1]));
868 : }
869 0 : else if(matchstring(cmd, len, "offset"))
870 : {
871 0 : texoffset(std::atoi(arg[0]), std::atoi(arg[1]));
872 : }
873 0 : else if(matchstring(cmd, len, "rotate"))
874 : {
875 0 : texrotate(std::atoi(arg[0]), ttype);
876 : }
877 0 : else if(matchstring(cmd, len, "reorient"))
878 : {
879 0 : texreorient(std::atoi(arg[0])>0, std::atoi(arg[1])>0, std::atoi(arg[2])>0, ttype);
880 : }
881 0 : else if(matchstring(cmd, len, "crop"))
882 : {
883 0 : texcrop(std::atoi(arg[0]), std::atoi(arg[1]), *arg[2] ? std::atoi(arg[2]) : -1, *arg[3] ? std::atoi(arg[3]) : -1);
884 : }
885 0 : else if(matchstring(cmd, len, "mix"))
886 : {
887 0 : texmix(*arg[0] ? std::atoi(arg[0]) : -1, *arg[1] ? std::atoi(arg[1]) : -1, *arg[2] ? std::atoi(arg[2]) : -1, *arg[3] ? std::atoi(arg[3]) : -1);
888 : }
889 0 : else if(matchstring(cmd, len, "grey"))
890 : {
891 0 : texgrey();
892 : }
893 0 : else if(matchstring(cmd, len, "premul"))
894 : {
895 0 : texpremul();
896 : }
897 0 : else if(matchstring(cmd, len, "agrad"))
898 : {
899 0 : texagrad(std::atof(arg[0]), std::atof(arg[1]), std::atof(arg[2]), std::atof(arg[3]));
900 : }
901 0 : else if(matchstring(cmd, len, "blend"))
902 : {
903 0 : ImageData src, mask;
904 : string srcname, maskname;
905 0 : copystring(srcname, stringslice(arg[0], std::strcspn(arg[0], ":,><")));
906 0 : copystring(maskname, stringslice(arg[1], std::strcspn(arg[1], ":,><")));
907 0 : if(srcname[0] && src.texturedata(srcname, false, nullptr, nullptr, tdir, ttype) && (!maskname[0] || mask.texturedata(maskname, false, nullptr, nullptr, tdir, ttype)))
908 : {
909 0 : texblend(src, maskname[0] ? mask : src);
910 : }
911 0 : }
912 0 : else if(matchstring(cmd, len, "thumbnail"))
913 : {
914 0 : int xdim = std::atoi(arg[0]),
915 0 : ydim = std::atoi(arg[1]);
916 0 : if(xdim <= 0 || xdim > (1<<12))
917 : {
918 0 : xdim = 64;
919 : }
920 0 : if(ydim <= 0 || ydim > (1<<12))
921 : {
922 0 : ydim = xdim; //default 64
923 : }
924 0 : if(w > xdim || h > ydim)
925 : {
926 0 : scaleimage(xdim, ydim);
927 : }
928 : }
929 0 : else if(matchstring(cmd, len, "compress"))
930 : {
931 0 : int scale = std::atoi(arg[0]);
932 0 : if(compress)
933 : {
934 0 : *compress = scale;
935 : }
936 : }
937 0 : else if(matchstring(cmd, len, "nocompress"))
938 : {
939 0 : if(compress)
940 : {
941 0 : *compress = -1;
942 : }
943 : }
944 : // note that the else/if in else-if is separated by a goto breakpoint
945 : else
946 0 : compressed:
947 0 : if(matchstring(cmd, len, "mirror"))
948 : {
949 0 : if(wrap)
950 : {
951 0 : *wrap |= 0x300;
952 : }
953 : }
954 0 : else if(matchstring(cmd, len, "noswizzle"))
955 : {
956 0 : if(wrap)
957 : {
958 0 : *wrap |= 0x10000;
959 : }
960 : }
961 : }
962 1 : return true;
963 : }
964 :
965 0 : bool ImageData::texturedata(const Slot &slot, const Slot::Tex &tex, bool msg, int *compress, int *wrap)
966 : {
967 0 : return texturedata(tex.name, msg, compress, wrap, slot.texturedir(), tex.type);
968 : }
969 :
970 0 : void ImageData::reorientnormals(uchar * RESTRICT src, int sw, int sh, int bpp, int stride, uchar * RESTRICT dst, bool flipx, bool flipy, bool swapxy)
971 : {
972 0 : int stridex = bpp,
973 0 : stridey = bpp;
974 0 : if(swapxy)
975 : {
976 0 : stridex *= sh;
977 : }
978 : else
979 : {
980 0 : stridey *= sw;
981 : }
982 0 : if(flipx)
983 : {
984 0 : dst += (sw-1)*stridex;
985 0 : stridex = -stridex;
986 : }
987 0 : if(flipy)
988 : {
989 0 : dst += (sh-1)*stridey;
990 0 : stridey = -stridey;
991 : }
992 0 : uchar *srcrow = src;
993 0 : for(int i = 0; i < sh; ++i)
994 : {
995 0 : for(uchar *curdst = dst, *src = srcrow, *end = &srcrow[sw*bpp]; src < end;)
996 : {
997 0 : uchar nx = *src++, ny = *src++;
998 0 : if(flipx)
999 : {
1000 0 : nx = 255-nx;
1001 : }
1002 0 : if(flipy)
1003 : {
1004 0 : ny = 255-ny;
1005 : }
1006 0 : if(swapxy)
1007 : {
1008 0 : std::swap(nx, ny);
1009 : }
1010 0 : curdst[0] = nx;
1011 0 : curdst[1] = ny;
1012 0 : curdst[2] = *src++;
1013 0 : if(bpp > 3)
1014 : {
1015 0 : curdst[3] = *src++;
1016 : }
1017 0 : curdst += stridex;
1018 : }
1019 0 : srcrow += stride;
1020 0 : dst += stridey;
1021 : }
1022 0 : }
|