LCOV - code coverage report
Current view: top level - engine/render - imagedata.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 63 434 14.5 %
Date: 2024-11-22 05:07:59 Functions: 9 38 23.7 %

          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 : }

Generated by: LCOV version 1.14