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