1 | #include "image_manipulation.h" |
2 | #include <base/math.h> |
3 | #include <base/system.h> |
4 | |
5 | #define TW_DILATE_ALPHA_THRESHOLD 10 |
6 | |
7 | static 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 | |
56 | static 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 | |
72 | void DilateImage(uint8_t *pImageBuff, int w, int h) |
73 | { |
74 | DilateImageSub(pImageBuff, w, h, x: 0, y: 0, sw: w, sh: h); |
75 | } |
76 | |
77 | void 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 | |
118 | static 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 | |
128 | static 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 | |
139 | static 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 | |
204 | static 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 | |
226 | uint8_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 | |
233 | int 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 | |