1 | // [Blend2D] |
2 | // 2D Vector Graphics Powered by a JIT Compiler. |
3 | // |
4 | // [License] |
5 | // Zlib - See LICENSE.md file in the package. |
6 | |
7 | #include "./blapi-build_p.h" |
8 | #include "./blimagescale_p.h" |
9 | #include "./blmath_p.h" |
10 | #include "./blformat_p.h" |
11 | #include "./blgeometry_p.h" |
12 | #include "./blrgba_p.h" |
13 | #include "./blruntime_p.h" |
14 | #include "./blsupport_p.h" |
15 | |
16 | // ============================================================================ |
17 | // [BLImageScale - Global Variables] |
18 | // ============================================================================ |
19 | |
20 | static constexpr const BLImageScaleOptions blImageScaleOptionsNone = { |
21 | nullptr, // UserFunc. |
22 | nullptr, // UserData. |
23 | 2.0, // Radius. |
24 | {{ |
25 | 1.0 / 3.0, // Mitchell B. |
26 | 1.0 / 3.0, // Mitchell C. |
27 | 0.0 // Reserved. |
28 | }} |
29 | }; |
30 | |
31 | // ============================================================================ |
32 | // [BLImageScale - Ops] |
33 | // ============================================================================ |
34 | |
35 | struct BLImageScaleOps { |
36 | BLResult (BL_CDECL* weights)(BLImageScaleContext::Data* d, uint32_t dir, BLImageScaleUserFunc func, const void* data) BL_NOEXCEPT; |
37 | void (BL_CDECL* horz[BL_FORMAT_COUNT])(const BLImageScaleContext::Data* d, uint8_t* dstLine, intptr_t dstStride, const uint8_t* srcLine, intptr_t srcStride) BL_NOEXCEPT; |
38 | void (BL_CDECL* vert[BL_FORMAT_COUNT])(const BLImageScaleContext::Data* d, uint8_t* dstLine, intptr_t dstStride, const uint8_t* srcLine, intptr_t srcStride) BL_NOEXCEPT; |
39 | }; |
40 | static BLImageScaleOps blImageScaleOps; |
41 | |
42 | // ============================================================================ |
43 | // [BLImageScale - BuiltInParams] |
44 | // ============================================================================ |
45 | |
46 | // Data needed by some functions that take additional parameters. |
47 | struct BLImageScaleBuiltInParams { |
48 | double radius; |
49 | |
50 | struct Mitchell { |
51 | double p0, p2, p3; |
52 | double q0, q1, q2, q3; |
53 | } mitchell; |
54 | |
55 | BL_INLINE void initMitchell(double b, double c) noexcept { |
56 | constexpr double k1Div3 = 1.0 / 3.0; |
57 | constexpr double k1Div6 = 1.0 / 6.0; |
58 | constexpr double k4Div3 = 4.0 / 3.0; |
59 | |
60 | mitchell.p0 = 1.0 - k1Div3 * b; |
61 | mitchell.p2 = -3.0 + 2.0 * b + c; |
62 | mitchell.p3 = 2.0 - 1.5 * b - c; |
63 | |
64 | mitchell.q0 = k4Div3 * b + c * 4.0; |
65 | mitchell.q1 = -2.0 * b - c * 8.0; |
66 | mitchell.q2 = b + c * 5.0; |
67 | mitchell.q3 = -k1Div6 * b - c; |
68 | } |
69 | }; |
70 | |
71 | // ============================================================================ |
72 | // [BLImageScale - Utilities] |
73 | // ============================================================================ |
74 | |
75 | // Calculates a Bessel function of first kind of order `n`. |
76 | // |
77 | // Adapted for use in AGG library by Andy Wilk <castor.vulgaris@gmail.com> |
78 | static BL_INLINE double blBessel(double x, int n) noexcept { |
79 | double d = 1e-6; |
80 | double b0 = 0.0; |
81 | double b1 = 0.0; |
82 | |
83 | if (blAbs(x) <= d) |
84 | return n != 0 ? 0.0 : 1.0; |
85 | |
86 | // Set up a starting order for recurrence. |
87 | int m1 = blAbs(x) > 5.0 ? int(blAbs(1.4 * x + 60.0 / x)) : int(blAbs(x) + 6); |
88 | int m2 = blMax(int(blAbs(x)) / 4 + 2 + n, m1); |
89 | |
90 | for (;;) { |
91 | double c2 = blEpsilon<double>(); |
92 | double c3 = 0.0; |
93 | double c4 = 0.0; |
94 | |
95 | int m8 = m2 & 1; |
96 | for (int i = 1, iEnd = m2 - 1; i < iEnd; i++) { |
97 | double c6 = 2 * (m2 - i) * c2 / x - c3; |
98 | c3 = c2; |
99 | c2 = c6; |
100 | |
101 | if (m2 - i - 1 == n) |
102 | b1 = c6; |
103 | |
104 | m8 ^= 1; |
105 | if (m8) |
106 | c4 += c6 * 2.0; |
107 | } |
108 | |
109 | double c6 = 2.0 * c2 / x - c3; |
110 | if (n == 0) |
111 | b1 = c6; |
112 | |
113 | c4 += c6; |
114 | b1 /= c4; |
115 | |
116 | if (blAbs(b1 - b0) < d) |
117 | return b1; |
118 | |
119 | b0 = b1; |
120 | m2 += 3; |
121 | } |
122 | } |
123 | |
124 | static BL_INLINE double blSinXDivX(double x) noexcept { |
125 | return blSin(x) / x; |
126 | } |
127 | |
128 | static BL_INLINE double blLanczos(double x, double y) noexcept { |
129 | return blSinXDivX(x) * blSinXDivX(y); |
130 | } |
131 | |
132 | static BL_INLINE double blBlackman(double x, double y) noexcept { |
133 | return blSinXDivX(x) * (0.42 + 0.5 * blCos(y) + 0.08 * blCos(y * 2.0)); |
134 | } |
135 | |
136 | // ============================================================================ |
137 | // [BLImageScale - Functions] |
138 | // ============================================================================ |
139 | |
140 | static BLResult BL_CDECL blImageScaleNearestFunc(double* dst, const double* tArray, size_t n, const void* data) noexcept { |
141 | BL_UNUSED(data); |
142 | |
143 | for (size_t i = 0; i < n; i++) { |
144 | double t = tArray[i]; |
145 | dst[i] = t <= 0.5 ? 1.0 : 0.0; |
146 | } |
147 | |
148 | return BL_SUCCESS; |
149 | } |
150 | |
151 | static BLResult BL_CDECL blImageScaleBilinearFunc(double* dst, const double* tArray, size_t n, const void* data) noexcept { |
152 | BL_UNUSED(data); |
153 | |
154 | for (size_t i = 0; i < n; i++) { |
155 | double t = tArray[i]; |
156 | dst[i] = t < 1.0 ? 1.0 - t : 0.0; |
157 | } |
158 | |
159 | return BL_SUCCESS; |
160 | } |
161 | |
162 | static BLResult BL_CDECL blImageScaleBicubicFunc(double* dst, const double* tArray, size_t n, const void* data) noexcept { |
163 | BL_UNUSED(data); |
164 | constexpr double k2Div3 = 2.0 / 3.0; |
165 | |
166 | // 0.5t^3 - t^2 + 2/3 == (0.5t - 1.0) t^2 + 2/3 |
167 | for (size_t i = 0; i < n; i++) { |
168 | double t = tArray[i]; |
169 | dst[i] = t < 1.0 ? (t * 0.5 - 1.0) * blSquare(t) + k2Div3 : |
170 | t < 2.0 ? blPow3(2.0 - t) / 6.0 : 0.0; |
171 | } |
172 | |
173 | return BL_SUCCESS; |
174 | } |
175 | |
176 | static BLResult BL_CDECL blImageScaleBellFunc(double* dst, const double* tArray, size_t n, const void* data) noexcept { |
177 | BL_UNUSED(data); |
178 | |
179 | for (size_t i = 0; i < n; i++) { |
180 | double t = tArray[i]; |
181 | dst[i] = t < 0.5 ? 0.75 - blSquare(t) : |
182 | t < 1.5 ? 0.50 * blSquare(t - 1.5) : 0.0; |
183 | } |
184 | |
185 | return BL_SUCCESS; |
186 | } |
187 | |
188 | static BLResult BL_CDECL blImageScaleGaussFunc(double* dst, const double* tArray, size_t n, const void* data) noexcept { |
189 | BL_UNUSED(data); |
190 | constexpr double x = 0.7978845608; // sqrt(2 / PI); |
191 | |
192 | for (size_t i = 0; i < n; i++) { |
193 | double t = tArray[i]; |
194 | dst[i] = t <= 2.0 ? exp(blSquare(t) * -2.0) * x : 0.0; |
195 | } |
196 | |
197 | return BL_SUCCESS; |
198 | } |
199 | |
200 | static BLResult BL_CDECL blImageScaleHermiteFunc(double* dst, const double* tArray, size_t n, const void* data) noexcept { |
201 | BL_UNUSED(data); |
202 | |
203 | for (size_t i = 0; i < n; i++) { |
204 | double t = tArray[i]; |
205 | dst[i] = t < 1.0 ? (2.0 * t - 3.0) * blSquare(t) + 1.0 : 0.0; |
206 | } |
207 | |
208 | return BL_SUCCESS; |
209 | } |
210 | |
211 | static BLResult BL_CDECL blImageScaleHanningFunc(double* dst, const double* tArray, size_t n, const void* data) noexcept { |
212 | BL_UNUSED(data); |
213 | |
214 | for (size_t i = 0; i < n; i++) { |
215 | double t = tArray[i]; |
216 | dst[i] = t <= 1.0 ? 0.5 + 0.5 * blCos(t * BL_MATH_PI) : 0.0; |
217 | } |
218 | |
219 | return BL_SUCCESS; |
220 | } |
221 | |
222 | static BLResult BL_CDECL blImageScaleCatromFunc(double* dst, const double* tArray, size_t n, const void* data) noexcept { |
223 | BL_UNUSED(data); |
224 | |
225 | for (size_t i = 0; i < n; i++) { |
226 | double t = tArray[i]; |
227 | dst[i] = t < 1.0 ? 0.5 * (2.0 + t * t * (t * 3.0 - 5.0)) : |
228 | t < 2.0 ? 0.5 * (4.0 + t * (t * (5.0 - t) - 8.0)) : 0.0; |
229 | } |
230 | |
231 | return BL_SUCCESS; |
232 | } |
233 | |
234 | static BLResult BL_CDECL blImageScaleBesselFunc(double* dst, const double* tArray, size_t n, const void* data) noexcept { |
235 | BL_UNUSED(data); |
236 | constexpr double x = BL_MATH_PI * 0.25; |
237 | |
238 | for (size_t i = 0; i < n; i++) { |
239 | double t = tArray[i]; |
240 | dst[i] = t == 0.0 ? x : t <= 3.2383 ? blBessel(t * BL_MATH_PI, 1) / (2.0 * t) : 0.0; |
241 | } |
242 | |
243 | return BL_SUCCESS; |
244 | } |
245 | |
246 | static BLResult BL_CDECL blImageScaleSincFunc(double* dst, const double* tArray, size_t n, const void* data) noexcept { |
247 | const double r = static_cast<const BLImageScaleBuiltInParams*>(data)->radius; |
248 | |
249 | for (size_t i = 0; i < n; i++) { |
250 | double t = tArray[i]; |
251 | dst[i] = t == 0.0 ? 1.0 : t <= r ? blSinXDivX(t * BL_MATH_PI) : 0.0; |
252 | } |
253 | |
254 | return BL_SUCCESS; |
255 | } |
256 | |
257 | static BLResult BL_CDECL blImageScaleLanczosFunc(double* dst, const double* tArray, size_t n, const void* data) noexcept { |
258 | const double r = static_cast<const BLImageScaleBuiltInParams*>(data)->radius; |
259 | const double x = BL_MATH_PI; |
260 | const double y = BL_MATH_PI / r; |
261 | |
262 | for (size_t i = 0; i < n; i++) { |
263 | double t = tArray[i]; |
264 | dst[i] = t == 0.0 ? 1.0 : t <= r ? blLanczos(t * x, t * y) : 0.0; |
265 | } |
266 | |
267 | return BL_SUCCESS; |
268 | } |
269 | |
270 | static BLResult BL_CDECL blImageScaleBlackmanFunc(double* dst, const double* tArray, size_t n, const void* data) noexcept { |
271 | const double r = static_cast<const BLImageScaleBuiltInParams*>(data)->radius; |
272 | const double x = BL_MATH_PI; |
273 | const double y = BL_MATH_PI / r; |
274 | |
275 | for (size_t i = 0; i < n; i++) { |
276 | double t = tArray[i]; |
277 | dst[i] = t == 0.0 ? 1.0 : t <= r ? blBlackman(t * x, t * y) : 0.0; |
278 | } |
279 | |
280 | return BL_SUCCESS; |
281 | } |
282 | |
283 | static BLResult BL_CDECL blImageScaleMitchellFunc(double* dst, const double* tArray, size_t n, const void* data) noexcept { |
284 | const BLImageScaleBuiltInParams::Mitchell& p = static_cast<const BLImageScaleBuiltInParams*>(data)->mitchell; |
285 | |
286 | for (size_t i = 0; i < n; i++) { |
287 | double t = tArray[i]; |
288 | dst[i] = t < 1.0 ? p.p0 + blSquare(t) * (p.p2 + t * p.p3) : |
289 | t < 2.0 ? p.q0 + t * (p.q1 + t * (p.q2 + t * p.q3)) : 0.0; |
290 | } |
291 | |
292 | return BL_SUCCESS; |
293 | } |
294 | |
295 | // ============================================================================ |
296 | // [BLImageScale - Weights] |
297 | // ============================================================================ |
298 | |
299 | static BLResult BL_CDECL blImageScaleWeights(BLImageScaleContext::Data* d, uint32_t dir, BLImageScaleUserFunc userFunc, const void* userData) noexcept { |
300 | int32_t* weightList = d->weightList[dir]; |
301 | BLImageScaleContext::Record* recordList = d->recordList[dir]; |
302 | |
303 | int dstSize = d->dstSize[dir]; |
304 | int srcSize = d->srcSize[dir]; |
305 | int kernelSize = d->kernelSize[dir]; |
306 | |
307 | double radius = d->radius[dir]; |
308 | double factor = d->factor[dir]; |
309 | double scale = d->scale[dir]; |
310 | int32_t isUnbound = 0; |
311 | |
312 | BLMemBufferTmp<512> wMem; |
313 | double* wData = static_cast<double*>(wMem.alloc(unsigned(kernelSize) * sizeof(double))); |
314 | |
315 | if (BL_UNLIKELY(!wData)) |
316 | return blTraceError(BL_ERROR_OUT_OF_MEMORY); |
317 | |
318 | for (int i = 0; i < dstSize; i++) { |
319 | double wPos = (double(i) + 0.5) / scale - 0.5; |
320 | double wSum = 0.0; |
321 | |
322 | int left = int(wPos - radius); |
323 | int right = left + kernelSize; |
324 | int wIndex; |
325 | |
326 | // Calculate all weights for the destination pixel. |
327 | wPos -= left; |
328 | for (wIndex = 0; wIndex < kernelSize; wIndex++, wPos -= 1.0) { |
329 | wData[wIndex] = blAbs(wPos * factor); |
330 | } |
331 | |
332 | // User function can fail. |
333 | BL_PROPAGATE(userFunc(wData, wData, unsigned(kernelSize), userData)); |
334 | |
335 | // Remove padded pixels from left and right. |
336 | wIndex = 0; |
337 | while (left < 0) { |
338 | double w = wData[wIndex]; |
339 | wData[++wIndex] += w; |
340 | left++; |
341 | } |
342 | |
343 | int wCount = kernelSize; |
344 | while (right > srcSize) { |
345 | BL_ASSERT(wCount > 0); |
346 | double w = wData[--wCount]; |
347 | wData[wCount - 1] += w; |
348 | right--; |
349 | } |
350 | |
351 | recordList[i].pos = 0; |
352 | recordList[i].count = 0; |
353 | |
354 | if (wIndex < wCount) { |
355 | // Sum all weights. |
356 | int j; |
357 | |
358 | for (j = wIndex; j < wCount; j++) { |
359 | double w = wData[j]; |
360 | wSum += w; |
361 | } |
362 | |
363 | int iStrongest = 0; |
364 | int32_t iSum = 0; |
365 | int32_t iMax = 0; |
366 | |
367 | double wScale = 65535 / wSum; |
368 | for (j = wIndex; j < wCount; j++) { |
369 | int32_t w = int32_t(wData[j] * wScale) >> 8; |
370 | |
371 | // Remove zero weight from the beginning of the list. |
372 | if (w == 0 && wIndex == j) { |
373 | wIndex++; |
374 | left++; |
375 | continue; |
376 | } |
377 | |
378 | weightList[j - wIndex] = w; |
379 | iSum += w; |
380 | isUnbound |= w; |
381 | |
382 | if (iMax < w) { |
383 | iMax = w; |
384 | iStrongest = j - wIndex; |
385 | } |
386 | } |
387 | |
388 | // Normalize the strongest weight so the sum matches `0x100`. |
389 | if (iSum != 0x100) |
390 | weightList[iStrongest] += int32_t(0x100) - iSum; |
391 | |
392 | // `wCount` is now absolute size of weights in `weightList`. |
393 | wCount -= wIndex; |
394 | |
395 | // Remove all zero weights from the end of the weight array. |
396 | while (wCount > 0 && weightList[wCount - 1] == 0) |
397 | wCount--; |
398 | |
399 | if (wCount) { |
400 | BL_ASSERT(left >= 0); |
401 | recordList[i].pos = uint32_t(left); |
402 | recordList[i].count = uint32_t(wCount); |
403 | } |
404 | } |
405 | |
406 | weightList += kernelSize; |
407 | } |
408 | |
409 | d->isUnbound[dir] = isUnbound < 0; |
410 | return BL_SUCCESS; |
411 | } |
412 | |
413 | // ============================================================================ |
414 | // [BLImageScale - Horz] |
415 | // ============================================================================ |
416 | |
417 | static void BL_CDECL blImageScaleHorzPrgb32(const BLImageScaleContext::Data* d, uint8_t* dstLine, intptr_t dstStride, const uint8_t* srcLine, intptr_t srcStride) noexcept { |
418 | uint32_t dw = uint32_t(d->dstSize[0]); |
419 | uint32_t sh = uint32_t(d->srcSize[1]); |
420 | uint32_t kernelSize = uint32_t(d->kernelSize[0]); |
421 | |
422 | if (!d->isUnbound[BLImageScaleContext::kDirHorz]) { |
423 | for (uint32_t y = 0; y < sh; y++) { |
424 | const BLImageScaleContext::Record* recordList = d->recordList[BLImageScaleContext::kDirHorz]; |
425 | const int32_t* weightList = d->weightList[BLImageScaleContext::kDirHorz]; |
426 | |
427 | uint8_t* dp = dstLine; |
428 | |
429 | for (uint32_t x = 0; x < dw; x++) { |
430 | const uint8_t* sp = srcLine + recordList->pos * 4; |
431 | const int32_t* wp = weightList; |
432 | |
433 | uint32_t cr_cb = 0x00800080u; |
434 | uint32_t ca_cg = 0x00800080u; |
435 | |
436 | for (uint32_t i = recordList->count; i; i--) { |
437 | uint32_t p0 = blMemReadU32a(sp); |
438 | uint32_t w0 = unsigned(wp[0]); |
439 | |
440 | ca_cg += ((p0 >> 8) & 0x00FF00FFu) * w0; |
441 | cr_cb += ((p0 ) & 0x00FF00FFu) * w0; |
442 | |
443 | sp += 4; |
444 | wp += 1; |
445 | } |
446 | |
447 | blMemWriteU32a(dp, (ca_cg & 0xFF00FF00u) + ((cr_cb & 0xFF00FF00u) >> 8)); |
448 | dp += 4; |
449 | |
450 | recordList += 1; |
451 | weightList += kernelSize; |
452 | } |
453 | |
454 | dstLine += dstStride; |
455 | srcLine += srcStride; |
456 | } |
457 | } |
458 | else { |
459 | for (uint32_t y = 0; y < sh; y++) { |
460 | const BLImageScaleContext::Record* recordList = d->recordList[BLImageScaleContext::kDirHorz]; |
461 | const int32_t* weightList = d->weightList[BLImageScaleContext::kDirHorz]; |
462 | |
463 | uint8_t* dp = dstLine; |
464 | |
465 | for (uint32_t x = 0; x < dw; x++) { |
466 | const uint8_t* sp = srcLine + recordList->pos * 4; |
467 | const int32_t* wp = weightList; |
468 | |
469 | int32_t ca = 0x80; |
470 | int32_t cr = 0x80; |
471 | int32_t cg = 0x80; |
472 | int32_t cb = 0x80; |
473 | |
474 | for (uint32_t i = recordList->count; i; i--) { |
475 | uint32_t p0 = blMemReadU32a(sp); |
476 | int32_t w0 = wp[0]; |
477 | |
478 | ca += int32_t((p0 >> 24) ) * w0; |
479 | cr += int32_t((p0 >> 16) & 0xFFu) * w0; |
480 | cg += int32_t((p0 >> 8) & 0xFFu) * w0; |
481 | cb += int32_t((p0 ) & 0xFFu) * w0; |
482 | |
483 | sp += 4; |
484 | wp += 1; |
485 | } |
486 | |
487 | ca = blClamp<int32_t>(ca >> 8, 0, 255); |
488 | cr = blClamp<int32_t>(cr >> 8, 0, ca); |
489 | cg = blClamp<int32_t>(cg >> 8, 0, ca); |
490 | cb = blClamp<int32_t>(cb >> 8, 0, ca); |
491 | |
492 | blMemWriteU32a(dp, blRgba32Pack(uint32_t(cr), uint32_t(cg), uint32_t(cb), uint32_t(ca))); |
493 | dp += 4; |
494 | |
495 | recordList += 1; |
496 | weightList += kernelSize; |
497 | } |
498 | |
499 | dstLine += dstStride; |
500 | srcLine += srcStride; |
501 | } |
502 | } |
503 | } |
504 | |
505 | static void BL_CDECL blImageScaleHorzXrgb32(const BLImageScaleContext::Data* d, uint8_t* dstLine, intptr_t dstStride, const uint8_t* srcLine, intptr_t srcStride) noexcept { |
506 | uint32_t dw = uint32_t(d->dstSize[0]); |
507 | uint32_t sh = uint32_t(d->srcSize[1]); |
508 | uint32_t kernelSize = uint32_t(d->kernelSize[0]); |
509 | |
510 | if (!d->isUnbound[BLImageScaleContext::kDirHorz]) { |
511 | for (uint32_t y = 0; y < sh; y++) { |
512 | const BLImageScaleContext::Record* recordList = d->recordList[BLImageScaleContext::kDirHorz]; |
513 | const int32_t* weightList = d->weightList[BLImageScaleContext::kDirHorz]; |
514 | |
515 | uint8_t* dp = dstLine; |
516 | |
517 | for (uint32_t x = 0; x < dw; x++) { |
518 | const uint8_t* sp = srcLine + recordList->pos * 4; |
519 | const int32_t* wp = weightList; |
520 | |
521 | uint32_t cx_cg = 0x00008000u; |
522 | uint32_t cr_cb = 0x00800080u; |
523 | |
524 | for (uint32_t i = recordList->count; i; i--) { |
525 | uint32_t p0 = blMemReadU32a(sp); |
526 | uint32_t w0 = unsigned(wp[0]); |
527 | |
528 | cx_cg += (p0 & 0x0000FF00u) * w0; |
529 | cr_cb += (p0 & 0x00FF00FFu) * w0; |
530 | |
531 | sp += 4; |
532 | wp += 1; |
533 | } |
534 | |
535 | blMemWriteU32a(dp, 0xFF000000u + (((cx_cg & 0x00FF0000u) | (cr_cb & 0xFF00FF00u)) >> 8)); |
536 | dp += 4; |
537 | |
538 | recordList += 1; |
539 | weightList += kernelSize; |
540 | } |
541 | |
542 | dstLine += dstStride; |
543 | srcLine += srcStride; |
544 | } |
545 | } |
546 | else { |
547 | for (uint32_t y = 0; y < sh; y++) { |
548 | const BLImageScaleContext::Record* recordList = d->recordList[BLImageScaleContext::kDirHorz]; |
549 | const int32_t* weightList = d->weightList[BLImageScaleContext::kDirHorz]; |
550 | |
551 | uint8_t* dp = dstLine; |
552 | |
553 | for (uint32_t x = 0; x < dw; x++) { |
554 | const uint8_t* sp = srcLine + recordList->pos * 4; |
555 | const int32_t* wp = weightList; |
556 | |
557 | int32_t cr = 0x80; |
558 | int32_t cg = 0x80; |
559 | int32_t cb = 0x80; |
560 | |
561 | for (uint32_t i = recordList->count; i; i--) { |
562 | uint32_t p0 = blMemReadU32a(sp); |
563 | int32_t w0 = wp[0]; |
564 | |
565 | cr += int32_t((p0 >> 16) & 0xFF) * w0; |
566 | cg += int32_t((p0 >> 8) & 0xFF) * w0; |
567 | cb += int32_t((p0 ) & 0xFF) * w0; |
568 | |
569 | sp += 4; |
570 | wp += 1; |
571 | } |
572 | |
573 | cr = blClamp<int32_t>(cr >> 8, 0, 255); |
574 | cg = blClamp<int32_t>(cg >> 8, 0, 255); |
575 | cb = blClamp<int32_t>(cb >> 8, 0, 255); |
576 | |
577 | blMemWriteU32a(dp, blRgba32Pack(uint32_t(cr), uint32_t(cg), uint32_t(cb), 0xFFu)); |
578 | dp += 4; |
579 | |
580 | recordList += 1; |
581 | weightList += kernelSize; |
582 | } |
583 | |
584 | dstLine += dstStride; |
585 | srcLine += srcStride; |
586 | } |
587 | } |
588 | } |
589 | |
590 | static void BL_CDECL blImageScaleHorzA8(const BLImageScaleContext::Data* d, uint8_t* dstLine, intptr_t dstStride, const uint8_t* srcLine, intptr_t srcStride) noexcept { |
591 | uint32_t dw = uint32_t(d->dstSize[0]); |
592 | uint32_t sh = uint32_t(d->srcSize[1]); |
593 | uint32_t kernelSize = uint32_t(d->kernelSize[0]); |
594 | |
595 | if (!d->isUnbound[BLImageScaleContext::kDirHorz]) { |
596 | for (uint32_t y = 0; y < sh; y++) { |
597 | const BLImageScaleContext::Record* recordList = d->recordList[BLImageScaleContext::kDirHorz]; |
598 | const int32_t* weightList = d->weightList[BLImageScaleContext::kDirHorz]; |
599 | |
600 | uint8_t* dp = dstLine; |
601 | |
602 | for (uint32_t x = 0; x < dw; x++) { |
603 | const uint8_t* sp = srcLine + recordList->pos * 1; |
604 | const int32_t* wp = weightList; |
605 | |
606 | uint32_t ca = 0x80; |
607 | |
608 | for (uint32_t i = recordList->count; i; i--) { |
609 | uint32_t p0 = sp[0]; |
610 | uint32_t w0 = unsigned(wp[0]); |
611 | |
612 | ca += p0 * w0; |
613 | |
614 | sp += 1; |
615 | wp += 1; |
616 | } |
617 | |
618 | dp[0] = uint8_t(ca >> 8); |
619 | |
620 | recordList += 1; |
621 | weightList += kernelSize; |
622 | |
623 | dp += 1; |
624 | } |
625 | |
626 | dstLine += dstStride; |
627 | srcLine += srcStride; |
628 | } |
629 | } |
630 | else { |
631 | for (uint32_t y = 0; y < sh; y++) { |
632 | const BLImageScaleContext::Record* recordList = d->recordList[BLImageScaleContext::kDirHorz]; |
633 | const int32_t* weightList = d->weightList[BLImageScaleContext::kDirHorz]; |
634 | |
635 | uint8_t* dp = dstLine; |
636 | |
637 | for (uint32_t x = 0; x < dw; x++) { |
638 | const uint8_t* sp = srcLine + recordList->pos * 1; |
639 | const int32_t* wp = weightList; |
640 | |
641 | int32_t ca = 0x80; |
642 | |
643 | for (uint32_t i = recordList->count; i; i--) { |
644 | uint32_t p0 = sp[0]; |
645 | int32_t w0 = wp[0]; |
646 | |
647 | ca += (int32_t)p0 * w0; |
648 | |
649 | sp += 1; |
650 | wp += 1; |
651 | } |
652 | |
653 | dp[0] = blClampToByte(ca >> 8); |
654 | |
655 | recordList += 1; |
656 | weightList += kernelSize; |
657 | |
658 | dp += 1; |
659 | } |
660 | |
661 | dstLine += dstStride; |
662 | srcLine += srcStride; |
663 | } |
664 | } |
665 | } |
666 | |
667 | // ============================================================================ |
668 | // [BLImageScale - Vert] |
669 | // ============================================================================ |
670 | |
671 | static void BL_CDECL blImageScaleVertPrgb32(const BLImageScaleContext::Data* d, uint8_t* dstLine, intptr_t dstStride, const uint8_t* srcLine, intptr_t srcStride) noexcept { |
672 | uint32_t dw = uint32_t(d->dstSize[0]); |
673 | uint32_t dh = uint32_t(d->dstSize[1]); |
674 | uint32_t kernelSize = uint32_t(d->kernelSize[BLImageScaleContext::kDirVert]); |
675 | |
676 | const BLImageScaleContext::Record* recordList = d->recordList[BLImageScaleContext::kDirVert]; |
677 | const int32_t* weightList = d->weightList[BLImageScaleContext::kDirVert]; |
678 | |
679 | if (!d->isUnbound[BLImageScaleContext::kDirVert]) { |
680 | for (uint32_t y = 0; y < dh; y++) { |
681 | const uint8_t* srcData = srcLine + intptr_t(recordList->pos) * srcStride; |
682 | uint8_t* dp = dstLine; |
683 | |
684 | uint32_t count = recordList->count; |
685 | for (uint32_t x = 0; x < dw; x++) { |
686 | const uint8_t* sp = srcData; |
687 | const int32_t* wp = weightList; |
688 | |
689 | uint32_t cr_cb = 0x00800080; |
690 | uint32_t ca_cg = 0x00800080; |
691 | |
692 | for (uint32_t i = count; i; i--) { |
693 | uint32_t p0 = blMemReadU32a(sp); |
694 | uint32_t w0 = unsigned(wp[0]); |
695 | |
696 | ca_cg += ((p0 >> 8) & 0x00FF00FF) * w0; |
697 | cr_cb += ((p0 ) & 0x00FF00FF) * w0; |
698 | |
699 | sp += srcStride; |
700 | wp += 1; |
701 | } |
702 | |
703 | blMemWriteU32a(dp, (ca_cg & 0xFF00FF00) + ((cr_cb & 0xFF00FF00) >> 8)); |
704 | dp += 4; |
705 | srcData += 4; |
706 | } |
707 | |
708 | recordList += 1; |
709 | weightList += kernelSize; |
710 | |
711 | dstLine += dstStride; |
712 | } |
713 | } |
714 | else { |
715 | for (uint32_t y = 0; y < dh; y++) { |
716 | const uint8_t* srcData = srcLine + intptr_t(recordList->pos) * srcStride; |
717 | uint8_t* dp = dstLine; |
718 | |
719 | uint32_t count = recordList->count; |
720 | for (uint32_t x = 0; x < dw; x++) { |
721 | const uint8_t* sp = srcData; |
722 | const int32_t* wp = weightList; |
723 | |
724 | int32_t ca = 0x80; |
725 | int32_t cr = 0x80; |
726 | int32_t cg = 0x80; |
727 | int32_t cb = 0x80; |
728 | |
729 | for (uint32_t i = count; i; i--) { |
730 | uint32_t p0 = blMemReadU32a(sp); |
731 | int32_t w0 = wp[0]; |
732 | |
733 | ca += int32_t((p0 >> 24) ) * w0; |
734 | cr += int32_t((p0 >> 16) & 0xFFu) * w0; |
735 | cg += int32_t((p0 >> 8) & 0xFFu) * w0; |
736 | cb += int32_t((p0 ) & 0xFFu) * w0; |
737 | |
738 | sp += srcStride; |
739 | wp += 1; |
740 | } |
741 | |
742 | ca = blClamp<int32_t>(ca >> 8, 0, 255); |
743 | cr = blClamp<int32_t>(cr >> 8, 0, ca); |
744 | cg = blClamp<int32_t>(cg >> 8, 0, ca); |
745 | cb = blClamp<int32_t>(cb >> 8, 0, ca); |
746 | |
747 | blMemWriteU32a(dp, blRgba32Pack(uint32_t(cr), uint32_t(cg), uint32_t(cb), uint32_t(ca))); |
748 | dp += 4; |
749 | srcData += 4; |
750 | } |
751 | |
752 | recordList += 1; |
753 | weightList += kernelSize; |
754 | |
755 | dstLine += dstStride; |
756 | } |
757 | } |
758 | } |
759 | |
760 | static void BL_CDECL blImageScaleVertXrgb32(const BLImageScaleContext::Data* d, uint8_t* dstLine, intptr_t dstStride, const uint8_t* srcLine, intptr_t srcStride) noexcept { |
761 | uint32_t dw = uint32_t(d->dstSize[0]); |
762 | uint32_t dh = uint32_t(d->dstSize[1]); |
763 | uint32_t kernelSize = uint32_t(d->kernelSize[BLImageScaleContext::kDirVert]); |
764 | |
765 | const BLImageScaleContext::Record* recordList = d->recordList[BLImageScaleContext::kDirVert]; |
766 | const int32_t* weightList = d->weightList[BLImageScaleContext::kDirVert]; |
767 | |
768 | if (!d->isUnbound[BLImageScaleContext::kDirVert]) { |
769 | for (uint32_t y = 0; y < dh; y++) { |
770 | const uint8_t* srcData = srcLine + intptr_t(recordList->pos) * srcStride; |
771 | uint8_t* dp = dstLine; |
772 | |
773 | uint32_t count = recordList->count; |
774 | for (uint32_t x = 0; x < dw; x++) { |
775 | const uint8_t* sp = srcData; |
776 | const int32_t* wp = weightList; |
777 | |
778 | uint32_t cx_cg = 0x00008000u; |
779 | uint32_t cr_cb = 0x00800080u; |
780 | |
781 | for (uint32_t i = count; i; i--) { |
782 | uint32_t p0 = blMemReadU32a(sp); |
783 | uint32_t w0 = unsigned(wp[0]); |
784 | |
785 | cx_cg += (p0 & 0x0000FF00u) * w0; |
786 | cr_cb += (p0 & 0x00FF00FFu) * w0; |
787 | |
788 | sp += srcStride; |
789 | wp += 1; |
790 | } |
791 | |
792 | blMemWriteU32a(dp, 0xFF000000u + (((cx_cg & 0x00FF0000u) | (cr_cb & 0xFF00FF00u)) >> 8)); |
793 | dp += 4; |
794 | srcData += 4; |
795 | } |
796 | |
797 | recordList += 1; |
798 | weightList += kernelSize; |
799 | |
800 | dstLine += dstStride; |
801 | } |
802 | } |
803 | else { |
804 | for (uint32_t y = 0; y < dh; y++) { |
805 | const uint8_t* srcData = srcLine + intptr_t(recordList->pos) * srcStride; |
806 | uint8_t* dp = dstLine; |
807 | |
808 | uint32_t count = recordList->count; |
809 | for (uint32_t x = 0; x < dw; x++) { |
810 | const uint8_t* sp = srcData; |
811 | const int32_t* wp = weightList; |
812 | |
813 | int32_t cr = 0x80; |
814 | int32_t cg = 0x80; |
815 | int32_t cb = 0x80; |
816 | |
817 | for (uint32_t i = count; i; i--) { |
818 | uint32_t p0 = blMemReadU32a(sp); |
819 | int32_t w0 = wp[0]; |
820 | |
821 | cr += int32_t((p0 >> 16) & 0xFFu) * w0; |
822 | cg += int32_t((p0 >> 8) & 0xFFu) * w0; |
823 | cb += int32_t((p0 ) & 0xFFu) * w0; |
824 | |
825 | sp += srcStride; |
826 | wp += 1; |
827 | } |
828 | |
829 | cr = blClamp<int32_t>(cr >> 8, 0, 255); |
830 | cg = blClamp<int32_t>(cg >> 8, 0, 255); |
831 | cb = blClamp<int32_t>(cb >> 8, 0, 255); |
832 | |
833 | blMemWriteU32a(dp, blRgba32Pack(uint32_t(cr), uint32_t(cg), uint32_t(cb), 0xFFu)); |
834 | dp += 4; |
835 | srcData += 4; |
836 | } |
837 | |
838 | recordList += 1; |
839 | weightList += kernelSize; |
840 | |
841 | dstLine += dstStride; |
842 | } |
843 | } |
844 | } |
845 | |
846 | static void BL_CDECL blImageScaleVertBytes(const BLImageScaleContext::Data* d, uint8_t* dstLine, intptr_t dstStride, const uint8_t* srcLine, intptr_t srcStride, uint32_t wScale) noexcept { |
847 | uint32_t dw = uint32_t(d->dstSize[0]) * wScale; |
848 | uint32_t dh = uint32_t(d->dstSize[1]); |
849 | uint32_t kernelSize = uint32_t(d->kernelSize[BLImageScaleContext::kDirVert]); |
850 | |
851 | const BLImageScaleContext::Record* recordList = d->recordList[BLImageScaleContext::kDirVert]; |
852 | const int32_t* weightList = d->weightList[BLImageScaleContext::kDirVert]; |
853 | |
854 | if (!d->isUnbound[BLImageScaleContext::kDirVert]) { |
855 | for (uint32_t y = 0; y < dh; y++) { |
856 | const uint8_t* srcData = srcLine + intptr_t(recordList->pos) * srcStride; |
857 | uint8_t* dp = dstLine; |
858 | |
859 | uint32_t x = dw; |
860 | uint32_t i = 0; |
861 | uint32_t count = recordList->count; |
862 | |
863 | if (((intptr_t)dp & 0x7) == 0) |
864 | goto BoundLarge; |
865 | i = 8u - uint32_t((uintptr_t)dp & 0x7u); |
866 | |
867 | BoundSmall: |
868 | x -= i; |
869 | do { |
870 | const uint8_t* sp = srcData; |
871 | const int32_t* wp = weightList; |
872 | |
873 | uint32_t c0 = 0x80; |
874 | |
875 | for (uint32_t j = count; j; j--) { |
876 | uint32_t p0 = sp[0]; |
877 | uint32_t w0 = unsigned(wp[0]); |
878 | |
879 | c0 += p0 * w0; |
880 | |
881 | sp += srcStride; |
882 | wp += 1; |
883 | } |
884 | |
885 | dp[0] = (uint8_t)(c0 >> 8); |
886 | dp += 1; |
887 | srcData += 1; |
888 | } while (--i); |
889 | |
890 | BoundLarge: |
891 | while (x >= 8) { |
892 | const uint8_t* sp = srcData; |
893 | const int32_t* wp = weightList; |
894 | |
895 | uint32_t c0 = 0x00800080u; |
896 | uint32_t c1 = 0x00800080u; |
897 | uint32_t c2 = 0x00800080u; |
898 | uint32_t c3 = 0x00800080u; |
899 | |
900 | for (uint32_t j = count; j; j--) { |
901 | uint32_t p0 = blMemReadU32a(sp + 0u); |
902 | uint32_t p1 = blMemReadU32a(sp + 4u); |
903 | uint32_t w0 = unsigned(wp[0]); |
904 | |
905 | c0 += ((p0 ) & 0x00FF00FFu) * w0; |
906 | c1 += ((p0 >> 8) & 0x00FF00FFu) * w0; |
907 | c2 += ((p1 ) & 0x00FF00FFu) * w0; |
908 | c3 += ((p1 >> 8) & 0x00FF00FFu) * w0; |
909 | |
910 | sp += srcStride; |
911 | wp += 1; |
912 | } |
913 | |
914 | blMemWriteU32a(dp + 0u, ((c0 & 0xFF00FF00u) >> 8) + (c1 & 0xFF00FF00u)); |
915 | blMemWriteU32a(dp + 4u, ((c2 & 0xFF00FF00u) >> 8) + (c3 & 0xFF00FF00u)); |
916 | |
917 | dp += 8; |
918 | srcData += 8; |
919 | x -= 8; |
920 | } |
921 | |
922 | i = x; |
923 | if (i != 0) |
924 | goto BoundSmall; |
925 | |
926 | recordList += 1; |
927 | weightList += kernelSize; |
928 | |
929 | dstLine += dstStride; |
930 | } |
931 | } |
932 | else { |
933 | for (uint32_t y = 0; y < dh; y++) { |
934 | const uint8_t* srcData = srcLine + intptr_t(recordList->pos) * srcStride; |
935 | uint8_t* dp = dstLine; |
936 | |
937 | uint32_t x = dw; |
938 | uint32_t i = 0; |
939 | uint32_t count = recordList->count; |
940 | |
941 | if (((size_t)dp & 0x3) == 0) |
942 | goto UnboundLarge; |
943 | i = 4u - uint32_t((uintptr_t)dp & 0x3u); |
944 | |
945 | UnboundSmall: |
946 | x -= i; |
947 | do { |
948 | const uint8_t* sp = srcData; |
949 | const int32_t* wp = weightList; |
950 | |
951 | int32_t c0 = 0x80; |
952 | |
953 | for (uint32_t j = count; j; j--) { |
954 | uint32_t p0 = sp[0]; |
955 | int32_t w0 = wp[0]; |
956 | |
957 | c0 += int32_t(p0) * w0; |
958 | |
959 | sp += srcStride; |
960 | wp += 1; |
961 | } |
962 | |
963 | dp[0] = (uint8_t)(uint32_t)blClamp<int32_t>(c0 >> 8, 0, 255); |
964 | dp += 1; |
965 | srcData += 1; |
966 | } while (--i); |
967 | |
968 | UnboundLarge: |
969 | while (x >= 4) { |
970 | const uint8_t* sp = srcData; |
971 | const int32_t* wp = weightList; |
972 | |
973 | int32_t c0 = 0x80; |
974 | int32_t c1 = 0x80; |
975 | int32_t c2 = 0x80; |
976 | int32_t c3 = 0x80; |
977 | |
978 | for (uint32_t j = count; j; j--) { |
979 | uint32_t p0 = blMemReadU32a(sp); |
980 | uint32_t w0 = unsigned(wp[0]); |
981 | |
982 | c0 += ((p0 ) & 0xFF) * w0; |
983 | c1 += ((p0 >> 8) & 0xFF) * w0; |
984 | c2 += ((p0 >> 16) & 0xFF) * w0; |
985 | c3 += ((p0 >> 24) ) * w0; |
986 | |
987 | sp += srcStride; |
988 | wp += 1; |
989 | } |
990 | |
991 | blMemWriteU32a(dp, uint32_t(blClamp<int32_t>(c0 >> 8, 0, 255) ) | |
992 | uint32_t(blClamp<int32_t>(c1 >> 8, 0, 255) << 8) | |
993 | uint32_t(blClamp<int32_t>(c2 >> 8, 0, 255) << 16) | |
994 | uint32_t(blClamp<int32_t>(c3 >> 8, 0, 255) << 24)); |
995 | dp += 4; |
996 | srcData += 4; |
997 | x -= 4; |
998 | } |
999 | |
1000 | i = x; |
1001 | if (i != 0) |
1002 | goto UnboundSmall; |
1003 | |
1004 | recordList += 1; |
1005 | weightList += kernelSize; |
1006 | |
1007 | dstLine += dstStride; |
1008 | } |
1009 | } |
1010 | } |
1011 | |
1012 | static void BL_CDECL blImageScaleVertA8(const BLImageScaleContext::Data* d, uint8_t* dstLine, intptr_t dstStride, const uint8_t* srcLine, intptr_t srcStride) noexcept { |
1013 | blImageScaleVertBytes(d, dstLine, dstStride, srcLine, srcStride, 1); |
1014 | } |
1015 | |
1016 | // ============================================================================ |
1017 | // [BLImageScale - Reset] |
1018 | // ============================================================================ |
1019 | |
1020 | BLResult BLImageScaleContext::reset() noexcept { |
1021 | if (data != nullptr) |
1022 | free(data); |
1023 | |
1024 | data = nullptr; |
1025 | return BL_SUCCESS; |
1026 | } |
1027 | |
1028 | // ============================================================================ |
1029 | // [BLImageScale - Interface] |
1030 | // ============================================================================ |
1031 | |
1032 | BLResult BLImageScaleContext::create(const BLSizeI& to, const BLSizeI& from, uint32_t filter, const BLImageScaleOptions* options) noexcept { |
1033 | if (!options) |
1034 | options = &blImageScaleOptionsNone; |
1035 | |
1036 | BLImageScaleBuiltInParams p; |
1037 | BLImageScaleUserFunc userFunc; |
1038 | const void* userData = &p; |
1039 | |
1040 | // -------------------------------------------------------------------------- |
1041 | // [Setup Parameters] |
1042 | // -------------------------------------------------------------------------- |
1043 | |
1044 | if (!blIsValid(to) || !blIsValid(from)) |
1045 | return blTraceError(BL_ERROR_INVALID_VALUE); |
1046 | |
1047 | switch (filter) { |
1048 | case BL_IMAGE_SCALE_FILTER_NEAREST : userFunc = blImageScaleNearestFunc ; p.radius = 1.0; break; |
1049 | case BL_IMAGE_SCALE_FILTER_BILINEAR: userFunc = blImageScaleBilinearFunc; p.radius = 1.0; break; |
1050 | case BL_IMAGE_SCALE_FILTER_BICUBIC : userFunc = blImageScaleBicubicFunc ; p.radius = 2.0; break; |
1051 | case BL_IMAGE_SCALE_FILTER_BELL : userFunc = blImageScaleBellFunc ; p.radius = 1.5; break; |
1052 | case BL_IMAGE_SCALE_FILTER_GAUSS : userFunc = blImageScaleGaussFunc ; p.radius = 2.0; break; |
1053 | case BL_IMAGE_SCALE_FILTER_HERMITE : userFunc = blImageScaleHermiteFunc ; p.radius = 1.0; break; |
1054 | case BL_IMAGE_SCALE_FILTER_HANNING : userFunc = blImageScaleHanningFunc ; p.radius = 1.0; break; |
1055 | case BL_IMAGE_SCALE_FILTER_CATROM : userFunc = blImageScaleCatromFunc ; p.radius = 2.0; break; |
1056 | case BL_IMAGE_SCALE_FILTER_BESSEL : userFunc = blImageScaleBesselFunc ; p.radius = 3.2383; break; |
1057 | |
1058 | case BL_IMAGE_SCALE_FILTER_SINC : userFunc = blImageScaleSincFunc ; p.radius = options->radius; break; |
1059 | case BL_IMAGE_SCALE_FILTER_LANCZOS : userFunc = blImageScaleLanczosFunc ; p.radius = options->radius; break; |
1060 | case BL_IMAGE_SCALE_FILTER_BLACKMAN: userFunc = blImageScaleBlackmanFunc; p.radius = options->radius; break; |
1061 | |
1062 | case BL_IMAGE_SCALE_FILTER_MITCHELL: { |
1063 | p.radius = 2.0; |
1064 | if (!blIsFinite(options->mitchell.b) || !blIsFinite(options->mitchell.c)) |
1065 | return blTraceError(BL_ERROR_INVALID_VALUE); |
1066 | |
1067 | p.initMitchell(options->mitchell.b, options->mitchell.c); |
1068 | userFunc = blImageScaleMitchellFunc; |
1069 | break; |
1070 | } |
1071 | |
1072 | case BL_IMAGE_SCALE_FILTER_USER: { |
1073 | userFunc = options->userFunc; |
1074 | userData = options->userData; |
1075 | |
1076 | if (!userFunc) |
1077 | return blTraceError(BL_ERROR_INVALID_VALUE); |
1078 | break; |
1079 | } |
1080 | |
1081 | default: |
1082 | return blTraceError(BL_ERROR_INVALID_VALUE); |
1083 | } |
1084 | |
1085 | if (!(p.radius >= 1.0 && p.radius <= 16.0)) |
1086 | return blTraceError(BL_ERROR_INVALID_VALUE); |
1087 | |
1088 | // -------------------------------------------------------------------------- |
1089 | // [Setup Weights] |
1090 | // -------------------------------------------------------------------------- |
1091 | |
1092 | double scale[2]; |
1093 | double factor[2]; |
1094 | double radius[2]; |
1095 | int kernelSize[2]; |
1096 | int isUnbound[2]; |
1097 | |
1098 | scale[0] = double(to.w) / double(from.w); |
1099 | scale[1] = double(to.h) / double(from.h); |
1100 | |
1101 | factor[0] = 1.0; |
1102 | factor[1] = 1.0; |
1103 | |
1104 | radius[0] = p.radius; |
1105 | radius[1] = p.radius; |
1106 | |
1107 | if (scale[0] < 1.0) { factor[0] = scale[0]; radius[0] = p.radius / scale[0]; } |
1108 | if (scale[1] < 1.0) { factor[1] = scale[1]; radius[1] = p.radius / scale[1]; } |
1109 | |
1110 | kernelSize[0] = blCeilToInt(1.0 + 2.0 * radius[0]); |
1111 | kernelSize[1] = blCeilToInt(1.0 + 2.0 * radius[1]); |
1112 | |
1113 | isUnbound[0] = false; |
1114 | isUnbound[1] = false; |
1115 | |
1116 | size_t wWeightDataSize = size_t(to.w) * unsigned(kernelSize[0]) * sizeof(int32_t); |
1117 | size_t hWeightDataSize = size_t(to.h) * unsigned(kernelSize[1]) * sizeof(int32_t); |
1118 | size_t wRecordDataSize = size_t(to.w) * sizeof(Record); |
1119 | size_t hRecordDataSize = size_t(to.h) * sizeof(Record); |
1120 | size_t dataSize = sizeof(Data) + wWeightDataSize + hWeightDataSize + wRecordDataSize + hRecordDataSize; |
1121 | |
1122 | if (this->data) |
1123 | free(this->data); |
1124 | |
1125 | this->data = static_cast<Data*>(malloc(dataSize)); |
1126 | if (BL_UNLIKELY(!this->data)) |
1127 | return blTraceError(BL_ERROR_OUT_OF_MEMORY); |
1128 | |
1129 | // Init data. |
1130 | Data* d = this->data; |
1131 | d->dstSize[0] = to.w; |
1132 | d->dstSize[1] = to.h; |
1133 | d->srcSize[0] = from.w; |
1134 | d->srcSize[1] = from.h; |
1135 | d->kernelSize[0] = kernelSize[0]; |
1136 | d->kernelSize[1] = kernelSize[1]; |
1137 | d->isUnbound[0] = isUnbound[0]; |
1138 | d->isUnbound[1] = isUnbound[1]; |
1139 | |
1140 | d->scale[0] = scale[0]; |
1141 | d->scale[1] = scale[1]; |
1142 | d->factor[0] = factor[0]; |
1143 | d->factor[1] = factor[1]; |
1144 | d->radius[0] = radius[0]; |
1145 | d->radius[1] = radius[1]; |
1146 | |
1147 | // Distribute the memory buffer. |
1148 | uint8_t* dataPtr = blOffsetPtr<uint8_t>(d, sizeof(Data)); |
1149 | |
1150 | d->weightList[kDirHorz] = reinterpret_cast<int32_t*>(dataPtr); dataPtr += wWeightDataSize; |
1151 | d->weightList[kDirVert] = reinterpret_cast<int32_t*>(dataPtr); dataPtr += hWeightDataSize; |
1152 | d->recordList[kDirHorz] = reinterpret_cast<Record*>(dataPtr); dataPtr += wRecordDataSize; |
1153 | d->recordList[kDirVert] = reinterpret_cast<Record*>(dataPtr); |
1154 | |
1155 | // Built-in filters will probably never fail, however, custom filters can and |
1156 | // it wouldn't be safe to just continue. |
1157 | BL_PROPAGATE(blImageScaleOps.weights(d, kDirHorz, userFunc, userData)); |
1158 | BL_PROPAGATE(blImageScaleOps.weights(d, kDirVert, userFunc, userData)); |
1159 | |
1160 | return BL_SUCCESS; |
1161 | } |
1162 | |
1163 | BLResult BLImageScaleContext::processHorzData(uint8_t* dstLine, intptr_t dstStride, const uint8_t* srcLine, intptr_t srcStride, uint32_t format) const noexcept { |
1164 | BL_ASSERT(isInitialized()); |
1165 | blImageScaleOps.horz[format](this->data, dstLine, dstStride, srcLine, srcStride); |
1166 | return BL_SUCCESS; |
1167 | } |
1168 | |
1169 | BLResult BLImageScaleContext::processVertData(uint8_t* dstLine, intptr_t dstStride, const uint8_t* srcLine, intptr_t srcStride, uint32_t format) const noexcept { |
1170 | BL_ASSERT(isInitialized()); |
1171 | blImageScaleOps.vert[format](this->data, dstLine, dstStride, srcLine, srcStride); |
1172 | return BL_SUCCESS; |
1173 | } |
1174 | |
1175 | // ============================================================================ |
1176 | // [BLImageScale - Runtime Init] |
1177 | // ============================================================================ |
1178 | |
1179 | void blImageScalerRtInit(BLRuntimeContext* rt) noexcept { |
1180 | BL_UNUSED(rt); |
1181 | |
1182 | blImageScaleOps.weights = blImageScaleWeights; |
1183 | |
1184 | blImageScaleOps.horz[BL_FORMAT_PRGB32] = blImageScaleHorzPrgb32; |
1185 | blImageScaleOps.horz[BL_FORMAT_XRGB32] = blImageScaleHorzXrgb32; |
1186 | blImageScaleOps.horz[BL_FORMAT_A8 ] = blImageScaleHorzA8; |
1187 | |
1188 | blImageScaleOps.vert[BL_FORMAT_PRGB32] = blImageScaleVertPrgb32; |
1189 | blImageScaleOps.vert[BL_FORMAT_XRGB32] = blImageScaleVertXrgb32; |
1190 | blImageScaleOps.vert[BL_FORMAT_A8 ] = blImageScaleVertA8; |
1191 | } |
1192 | |