1#include "image_manipulation.h"
2#include <base/math.h>
3#include <base/system.h>
4
5#define TW_DILATE_ALPHA_THRESHOLD 10
6
7static void Dilate(int w, int h, const uint8_t *pSrc, uint8_t *pDest, uint8_t AlphaThreshold = TW_DILATE_ALPHA_THRESHOLD)
8{
9 const int BPP = 4; // RGBA assumed
10 int ix, iy;
11 const int aDirX[] = {0, -1, 1, 0};
12 const int aDirY[] = {-1, 0, 0, 1};
13
14 int AlphaCompIndex = BPP - 1;
15
16 int m = 0;
17 for(int y = 0; y < h; y++)
18 {
19 for(int x = 0; x < w; x++, m += BPP)
20 {
21 for(int i = 0; i < BPP; ++i)
22 pDest[m + i] = pSrc[m + i];
23 if(pSrc[m + AlphaCompIndex] > AlphaThreshold)
24 continue;
25
26 int aSumOfOpaque[] = {0, 0, 0};
27 int Counter = 0;
28 for(int c = 0; c < 4; c++)
29 {
30 ix = clamp(val: x + aDirX[c], lo: 0, hi: w - 1);
31 iy = clamp(val: y + aDirY[c], lo: 0, hi: h - 1);
32 int k = iy * w * BPP + ix * BPP;
33 if(pSrc[k + AlphaCompIndex] > AlphaThreshold)
34 {
35 for(int p = 0; p < BPP - 1; ++p)
36 aSumOfOpaque[p] += pSrc[k + p];
37 ++Counter;
38 break;
39 }
40 }
41
42 if(Counter > 0)
43 {
44 for(int i = 0; i < BPP - 1; ++i)
45 {
46 aSumOfOpaque[i] /= Counter;
47 pDest[m + i] = (uint8_t)aSumOfOpaque[i];
48 }
49
50 pDest[m + AlphaCompIndex] = 255;
51 }
52 }
53 }
54}
55
56static void CopyColorValues(int w, int h, int BPP, const uint8_t *pSrc, uint8_t *pDest)
57{
58 int m = 0;
59 for(int y = 0; y < h; y++)
60 {
61 for(int x = 0; x < w; x++, m += BPP)
62 {
63 for(int i = 0; i < BPP - 1; ++i)
64 {
65 if(pDest[m + 3] == 0)
66 pDest[m + i] = pSrc[m + i];
67 }
68 }
69 }
70}
71
72void DilateImage(uint8_t *pImageBuff, int w, int h)
73{
74 DilateImageSub(pImageBuff, w, h, x: 0, y: 0, sw: w, sh: h);
75}
76
77void DilateImageSub(uint8_t *pImageBuff, int w, int h, int x, int y, int sw, int sh)
78{
79 const int BPP = 4; // RGBA assumed
80 uint8_t *apBuffer[2] = {NULL, NULL};
81
82 apBuffer[0] = (uint8_t *)malloc(size: (size_t)sw * sh * sizeof(uint8_t) * BPP);
83 apBuffer[1] = (uint8_t *)malloc(size: (size_t)sw * sh * sizeof(uint8_t) * BPP);
84 uint8_t *pBufferOriginal = (uint8_t *)malloc(size: (size_t)sw * sh * sizeof(uint8_t) * BPP);
85
86 for(int Y = 0; Y < sh; ++Y)
87 {
88 int SrcImgOffset = ((y + Y) * w * BPP) + (x * BPP);
89 int DstImgOffset = (Y * sw * BPP);
90 int CopySize = sw * BPP;
91 mem_copy(dest: &pBufferOriginal[DstImgOffset], source: &pImageBuff[SrcImgOffset], size: CopySize);
92 }
93
94 Dilate(w: sw, h: sh, pSrc: pBufferOriginal, pDest: apBuffer[0]);
95
96 for(int i = 0; i < 5; i++)
97 {
98 Dilate(w: sw, h: sh, pSrc: apBuffer[0], pDest: apBuffer[1]);
99 Dilate(w: sw, h: sh, pSrc: apBuffer[1], pDest: apBuffer[0]);
100 }
101
102 CopyColorValues(w: sw, h: sh, BPP, pSrc: apBuffer[0], pDest: pBufferOriginal);
103
104 free(ptr: apBuffer[0]);
105 free(ptr: apBuffer[1]);
106
107 for(int Y = 0; Y < sh; ++Y)
108 {
109 int SrcImgOffset = ((y + Y) * w * BPP) + (x * BPP);
110 int DstImgOffset = (Y * sw * BPP);
111 int CopySize = sw * BPP;
112 mem_copy(dest: &pImageBuff[SrcImgOffset], source: &pBufferOriginal[DstImgOffset], size: CopySize);
113 }
114
115 free(ptr: pBufferOriginal);
116}
117
118static float CubicHermite(float A, float B, float C, float D, float t)
119{
120 float a = -A / 2.0f + (3.0f * B) / 2.0f - (3.0f * C) / 2.0f + D / 2.0f;
121 float b = A - (5.0f * B) / 2.0f + 2.0f * C - D / 2.0f;
122 float c = -A / 2.0f + C / 2.0f;
123 float d = B;
124
125 return (a * t * t * t) + (b * t * t) + (c * t) + d;
126}
127
128static void GetPixelClamped(const uint8_t *pSourceImage, int x, int y, uint32_t W, uint32_t H, size_t BPP, uint8_t aTmp[])
129{
130 x = clamp<int>(val: x, lo: 0, hi: (int)W - 1);
131 y = clamp<int>(val: y, lo: 0, hi: (int)H - 1);
132
133 for(size_t i = 0; i < BPP; i++)
134 {
135 aTmp[i] = pSourceImage[x * BPP + (W * BPP * y) + i];
136 }
137}
138
139static void SampleBicubic(const uint8_t *pSourceImage, float u, float v, uint32_t W, uint32_t H, size_t BPP, uint8_t aSample[])
140{
141 float X = (u * W) - 0.5f;
142 int xInt = (int)X;
143 float xFract = X - std::floor(x: X);
144
145 float Y = (v * H) - 0.5f;
146 int yInt = (int)Y;
147 float yFract = Y - std::floor(x: Y);
148
149 uint8_t aPX00[4];
150 uint8_t aPX10[4];
151 uint8_t aPX20[4];
152 uint8_t aPX30[4];
153
154 uint8_t aPX01[4];
155 uint8_t aPX11[4];
156 uint8_t aPX21[4];
157 uint8_t aPX31[4];
158
159 uint8_t aPX02[4];
160 uint8_t aPX12[4];
161 uint8_t aPX22[4];
162 uint8_t aPX32[4];
163
164 uint8_t aPX03[4];
165 uint8_t aPX13[4];
166 uint8_t aPX23[4];
167 uint8_t aPX33[4];
168
169 GetPixelClamped(pSourceImage, x: xInt - 1, y: yInt - 1, W, H, BPP, aTmp: aPX00);
170 GetPixelClamped(pSourceImage, x: xInt + 0, y: yInt - 1, W, H, BPP, aTmp: aPX10);
171 GetPixelClamped(pSourceImage, x: xInt + 1, y: yInt - 1, W, H, BPP, aTmp: aPX20);
172 GetPixelClamped(pSourceImage, x: xInt + 2, y: yInt - 1, W, H, BPP, aTmp: aPX30);
173
174 GetPixelClamped(pSourceImage, x: xInt - 1, y: yInt + 0, W, H, BPP, aTmp: aPX01);
175 GetPixelClamped(pSourceImage, x: xInt + 0, y: yInt + 0, W, H, BPP, aTmp: aPX11);
176 GetPixelClamped(pSourceImage, x: xInt + 1, y: yInt + 0, W, H, BPP, aTmp: aPX21);
177 GetPixelClamped(pSourceImage, x: xInt + 2, y: yInt + 0, W, H, BPP, aTmp: aPX31);
178
179 GetPixelClamped(pSourceImage, x: xInt - 1, y: yInt + 1, W, H, BPP, aTmp: aPX02);
180 GetPixelClamped(pSourceImage, x: xInt + 0, y: yInt + 1, W, H, BPP, aTmp: aPX12);
181 GetPixelClamped(pSourceImage, x: xInt + 1, y: yInt + 1, W, H, BPP, aTmp: aPX22);
182 GetPixelClamped(pSourceImage, x: xInt + 2, y: yInt + 1, W, H, BPP, aTmp: aPX32);
183
184 GetPixelClamped(pSourceImage, x: xInt - 1, y: yInt + 2, W, H, BPP, aTmp: aPX03);
185 GetPixelClamped(pSourceImage, x: xInt + 0, y: yInt + 2, W, H, BPP, aTmp: aPX13);
186 GetPixelClamped(pSourceImage, x: xInt + 1, y: yInt + 2, W, H, BPP, aTmp: aPX23);
187 GetPixelClamped(pSourceImage, x: xInt + 2, y: yInt + 2, W, H, BPP, aTmp: aPX33);
188
189 for(size_t i = 0; i < BPP; i++)
190 {
191 float Clmn0 = CubicHermite(A: aPX00[i], B: aPX10[i], C: aPX20[i], D: aPX30[i], t: xFract);
192 float Clmn1 = CubicHermite(A: aPX01[i], B: aPX11[i], C: aPX21[i], D: aPX31[i], t: xFract);
193 float Clmn2 = CubicHermite(A: aPX02[i], B: aPX12[i], C: aPX22[i], D: aPX32[i], t: xFract);
194 float Clmn3 = CubicHermite(A: aPX03[i], B: aPX13[i], C: aPX23[i], D: aPX33[i], t: xFract);
195
196 float Valuef = CubicHermite(A: Clmn0, B: Clmn1, C: Clmn2, D: Clmn3, t: yFract);
197
198 Valuef = clamp<float>(val: Valuef, lo: 0.0f, hi: 255.0f);
199
200 aSample[i] = (uint8_t)Valuef;
201 }
202}
203
204static void ResizeImage(const uint8_t *pSourceImage, uint32_t SW, uint32_t SH, uint8_t *pDestinationImage, uint32_t W, uint32_t H, size_t BPP)
205{
206 uint8_t aSample[4];
207 int y, x;
208
209 for(y = 0; y < (int)H; ++y)
210 {
211 float v = (float)y / (float)(H - 1);
212
213 for(x = 0; x < (int)W; ++x)
214 {
215 float u = (float)x / (float)(W - 1);
216 SampleBicubic(pSourceImage, u, v, W: SW, H: SH, BPP, aSample);
217
218 for(size_t i = 0; i < BPP; ++i)
219 {
220 pDestinationImage[x * BPP + ((W * BPP) * y) + i] = aSample[i];
221 }
222 }
223 }
224}
225
226uint8_t *ResizeImage(const uint8_t *pImageData, int Width, int Height, int NewWidth, int NewHeight, int BPP)
227{
228 uint8_t *pTmpData = (uint8_t *)malloc(size: (size_t)NewWidth * NewHeight * BPP);
229 ResizeImage(pSourceImage: pImageData, SW: Width, SH: Height, pDestinationImage: pTmpData, W: NewWidth, H: NewHeight, BPP);
230 return pTmpData;
231}
232
233int HighestBit(int OfVar)
234{
235 if(!OfVar)
236 return 0;
237
238 int RetV = 1;
239
240 while(OfVar >>= 1)
241 RetV <<= 1;
242
243 return RetV;
244}
245