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