LCOV - code coverage report
Current view: top level - engine/render - imagedata.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 15.3 % 452 69
Test Date: 2026-08-20 06:51:03 Functions: 27.9 % 43 12

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

Generated by: LCOV version 2.0-1