LCOV - code coverage report
Current view: top level - engine/render - imagedata.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 14.4 % 436 63
Test Date: 2025-04-16 12:09:02 Functions: 23.1 % 39 9

            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            0 : bool ImageData::matchstring(std::string_view s, size_t len, std::string_view d)
     712              : {
     713            0 :     return len == d.size() && !std::memcmp(s.data(), d.data(), d.size());
     714              : }
     715              : 
     716           10 : bool ImageData::texturedata(const char *tname, bool msg, int * const compress, int * const wrap, const char *tdir, int ttype)
     717              : {
     718            0 :     auto parsetexcommands = [] (const char *&cmds, const char *&cmd, size_t &len, std::array<const char *, 4> &arg)
     719              :     {
     720            0 :         const char *end = nullptr;
     721            0 :         cmd = &cmds[1];
     722            0 :         end = std::strchr(cmd, '>');
     723            0 :         if(!end)
     724              :         {
     725            0 :             return true;
     726              :         }
     727            0 :         cmds = std::strchr(cmd, '<');
     728            0 :         len = std::strcspn(cmd, ":,><");
     729            0 :         for(int i = 0; i < 4; ++i)
     730              :         {
     731            0 :             arg[i] = std::strchr(i ? arg[i-1] : cmd, i ? ',' : ':');
     732            0 :             if(!arg[i] || arg[i] >= end)
     733              :             {
     734            0 :                 arg[i] = "";
     735              :             }
     736              :             else
     737              :             {
     738            0 :                 arg[i]++;
     739              :             }
     740              :         }
     741            0 :         return false;
     742              :     };
     743              : 
     744           10 :     const char *cmds = nullptr,
     745           10 :                *file = tname;
     746           10 :     if(tname[0]=='<')
     747              :     {
     748            0 :         cmds = tname;
     749            0 :         file = std::strrchr(tname, '>');
     750            0 :         if(!file)
     751              :         {
     752            0 :             if(msg)
     753              :             {
     754            0 :                 conoutf(Console_Error, "could not load <> modified texture %s", tname);
     755              :             }
     756            0 :             return false;
     757              :         }
     758            0 :         file++;
     759              :     }
     760              :     string pname;
     761           10 :     if(tdir)
     762              :     {
     763            0 :         formatstring(pname, "%s/%s", tdir, file);
     764            0 :         file = path(pname);
     765              :     }
     766           10 :     for(const char *pcmds = cmds; pcmds;)
     767              :     {
     768            0 :         const char *cmd = nullptr;
     769            0 :         size_t len = 0;
     770            0 :         std::array<const char *, 4> arg = { nullptr, nullptr, nullptr, nullptr };
     771              : 
     772            0 :         if(parsetexcommands(pcmds, cmd, len, arg))
     773              :         {
     774            0 :             break;
     775              :         }
     776              : 
     777            0 :         if(matchstring(cmd, len, "stub"))
     778              :         {
     779            0 :             return canloadsurface(file);
     780              :         }
     781              :     }
     782           10 :     if(msg)
     783              :     {
     784            5 :         renderprogress(loadprogress, file);
     785              :     }
     786           10 :     if(!data)
     787              :     {
     788           10 :         SDL_Surface *s = loadsurface(file);
     789           10 :         if(!s)
     790              :         {
     791            9 :             if(msg)
     792              :             {
     793            5 :                 conoutf(Console_Error, "could not load texture %s", file);
     794              :             }
     795            9 :             return false;
     796              :         }
     797            1 :         int bpp = s->format->BitsPerPixel;
     798            1 :         if(bpp%8 || !texformat(bpp/8))
     799              :         {
     800            0 :             SDL_FreeSurface(s); conoutf(Console_Error, "texture must be 8, 16, 24, or 32 bpp: %s", file);
     801            0 :             return false;
     802              :         }
     803            1 :         if(std::max(s->w, s->h) > (1<<12))
     804              :         {
     805            0 :             SDL_FreeSurface(s); conoutf(Console_Error, "texture size exceeded %dx%d pixels: %s", 1<<12, 1<<12, file);
     806            0 :             return false;
     807              :         }
     808            1 :         wraptex(s);
     809              :     }
     810              : 
     811            1 :     while(cmds)
     812              :     {
     813            0 :         const char *cmd = nullptr;
     814            0 :         size_t len = 0;
     815            0 :         std::array<const char *, 4> arg = { nullptr, nullptr, nullptr, nullptr };
     816              : 
     817            0 :         if(parsetexcommands(cmds, cmd, len, arg))
     818              :         {
     819            0 :             break;
     820              :         }
     821              : 
     822            0 :         if(compressed)
     823              :         {
     824            0 :             goto compressed; //see `compressed` nested between else & if (yes it's ugly)
     825              :         }
     826            0 :         if(matchstring(cmd, len, "mad"))
     827              :         {
     828            0 :             texmad(parsevec(arg[0]), parsevec(arg[1]));
     829              :         }
     830            0 :         else if(matchstring(cmd, len, "colorify"))
     831              :         {
     832            0 :             texcolorify(parsevec(arg[0]), parsevec(arg[1]));
     833              :         }
     834            0 :         else if(matchstring(cmd, len, "colormask"))
     835              :         {
     836            0 :             texcolormask(parsevec(arg[0]), *arg[1] ? parsevec(arg[1]) : vec(1, 1, 1));
     837              :         }
     838            0 :         else if(matchstring(cmd, len, "normal"))
     839              :         {
     840            0 :             int emphasis = std::atoi(arg[0]);
     841            0 :             texnormal(emphasis > 0 ? emphasis : 3);
     842              :         }
     843            0 :         else if(matchstring(cmd, len, "dup"))
     844              :         {
     845            0 :             texdup(std::atoi(arg[0]), std::atoi(arg[1]));
     846              :         }
     847            0 :         else if(matchstring(cmd, len, "offset"))
     848              :         {
     849            0 :             texoffset(std::atoi(arg[0]), std::atoi(arg[1]));
     850              :         }
     851            0 :         else if(matchstring(cmd, len, "rotate"))
     852              :         {
     853            0 :             texrotate(std::atoi(arg[0]), ttype);
     854              :         }
     855            0 :         else if(matchstring(cmd, len, "reorient"))
     856              :         {
     857            0 :             texreorient(std::atoi(arg[0])>0, std::atoi(arg[1])>0, std::atoi(arg[2])>0, ttype);
     858              :         }
     859            0 :         else if(matchstring(cmd, len, "crop"))
     860              :         {
     861            0 :             texcrop(std::atoi(arg[0]), std::atoi(arg[1]), *arg[2] ? std::atoi(arg[2]) : -1, *arg[3] ? std::atoi(arg[3]) : -1);
     862              :         }
     863            0 :         else if(matchstring(cmd, len, "mix"))
     864              :         {
     865            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);
     866              :         }
     867            0 :         else if(matchstring(cmd, len, "grey"))
     868              :         {
     869            0 :             texgrey();
     870              :         }
     871            0 :         else if(matchstring(cmd, len, "premul"))
     872              :         {
     873            0 :             texpremul();
     874              :         }
     875            0 :         else if(matchstring(cmd, len, "agrad"))
     876              :         {
     877            0 :             texagrad(std::atof(arg[0]), std::atof(arg[1]), std::atof(arg[2]), std::atof(arg[3]));
     878              :         }
     879            0 :         else if(matchstring(cmd, len, "blend"))
     880              :         {
     881            0 :             ImageData src, mask;
     882              :             string srcname, maskname;
     883            0 :             copystring(srcname, stringslice(arg[0], std::strcspn(arg[0], ":,><")));
     884            0 :             copystring(maskname, stringslice(arg[1], std::strcspn(arg[1], ":,><")));
     885            0 :             if(srcname[0] && src.texturedata(srcname, false, nullptr, nullptr, tdir, ttype) && (!maskname[0] || mask.texturedata(maskname, false, nullptr, nullptr, tdir, ttype)))
     886              :             {
     887            0 :                 texblend(src, maskname[0] ? mask : src);
     888              :             }
     889            0 :         }
     890            0 :         else if(matchstring(cmd, len, "thumbnail"))
     891              :         {
     892            0 :             int xdim = std::atoi(arg[0]),
     893            0 :                 ydim = std::atoi(arg[1]);
     894            0 :             if(xdim <= 0 || xdim > (1<<12))
     895              :             {
     896            0 :                 xdim = 64;
     897              :             }
     898            0 :             if(ydim <= 0 || ydim > (1<<12))
     899              :             {
     900            0 :                 ydim = xdim; //default 64
     901              :             }
     902            0 :             if(w > xdim || h > ydim)
     903              :             {
     904            0 :                 scaleimage(xdim, ydim);
     905              :             }
     906              :         }
     907            0 :         else if(matchstring(cmd, len, "compress"))
     908              :         {
     909            0 :             int scale = std::atoi(arg[0]);
     910            0 :             if(compress)
     911              :             {
     912            0 :                 *compress = scale;
     913              :             }
     914              :         }
     915            0 :         else if(matchstring(cmd, len, "nocompress"))
     916              :         {
     917            0 :             if(compress)
     918              :             {
     919            0 :                 *compress = -1;
     920              :             }
     921              :         }
     922              :         // note that the else/if in else-if is separated by a goto breakpoint
     923              :         else
     924            0 :     compressed:
     925            0 :         if(matchstring(cmd, len, "mirror"))
     926              :         {
     927            0 :             if(wrap)
     928              :             {
     929            0 :                 *wrap |= 0x300;
     930              :             }
     931              :         }
     932            0 :         else if(matchstring(cmd, len, "noswizzle"))
     933              :         {
     934            0 :             if(wrap)
     935              :             {
     936            0 :                 *wrap |= 0x10000;
     937              :             }
     938              :         }
     939              :     }
     940            1 :     return true;
     941              : }
     942              : 
     943            0 : bool ImageData::texturedata(const Slot &slot, const Slot::Tex &tex, bool msg, int *compress, int *wrap)
     944              : {
     945            0 :     return texturedata(tex.name, msg, compress, wrap, slot.texturedir(), tex.type);
     946              : }
     947              : 
     948            0 : void ImageData::reorientnormals(uchar * RESTRICT src, int sw, int sh, int bpp, int stride, uchar * RESTRICT dst, bool flipx, bool flipy, bool swapxy)
     949              : {
     950            0 :     int stridex = bpp,
     951            0 :         stridey = bpp;
     952            0 :     if(swapxy)
     953              :     {
     954            0 :         stridex *= sh;
     955              :     }
     956              :     else
     957              :     {
     958            0 :         stridey *= sw;
     959              :     }
     960            0 :     if(flipx)
     961              :     {
     962            0 :         dst += (sw-1)*stridex;
     963            0 :         stridex = -stridex;
     964              :     }
     965            0 :     if(flipy)
     966              :     {
     967            0 :         dst += (sh-1)*stridey;
     968            0 :         stridey = -stridey;
     969              :     }
     970            0 :     uchar *srcrow = src;
     971            0 :     for(int i = 0; i < sh; ++i)
     972              :     {
     973            0 :         for(uchar *curdst = dst, *src = srcrow, *end = &srcrow[sw*bpp]; src < end;)
     974              :         {
     975            0 :             uchar nx = *src++, ny = *src++;
     976            0 :             if(flipx)
     977              :             {
     978            0 :                 nx = 255-nx;
     979              :             }
     980            0 :             if(flipy)
     981              :             {
     982            0 :                 ny = 255-ny;
     983              :             }
     984            0 :             if(swapxy)
     985              :             {
     986            0 :                 std::swap(nx, ny);
     987              :             }
     988            0 :             curdst[0] = nx;
     989            0 :             curdst[1] = ny;
     990            0 :             curdst[2] = *src++;
     991            0 :             if(bpp > 3)
     992              :             {
     993            0 :                 curdst[3] = *src++;
     994              :             }
     995            0 :             curdst += stridex;
     996              :         }
     997            0 :         srcrow += stride;
     998            0 :         dst += stridey;
     999              :     }
    1000            0 : }
        

Generated by: LCOV version 2.0-1