LCOV - code coverage report
Current view: top level - engine/render - imagedata.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 15.5 % 444 69
Test Date: 2026-04-10 06:41:26 Functions: 28.6 % 42 12

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

Generated by: LCOV version 2.0-1