Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
imageop_math.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2016-2017 Dan Torop.
4 Copyright (C) 2016 Roman Lebedev.
5 Copyright (C) 2016 Tobias Ellinghaus.
6 Copyright (C) 2019 Andreas Schneider.
7 Copyright (C) 2019, 2024 Aurélien PIERRE.
8 Copyright (C) 2019-2021 Pascal Obry.
9 Copyright (C) 2021 Hanno Schwalm.
10 Copyright (C) 2021 Ralf Brown.
11 Copyright (C) 2022 Martin Bařinka.
12
13 darktable is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 darktable is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with darktable. If not, see <http://www.gnu.org/licenses/>.
25*/
26
28#include <assert.h> // for assert
29#include <glib.h> // for MIN, MAX, CLAMP, inline
30#include <math.h> // for round, floorf, fmaxf
31#include "system/openmp.h" // for __OMP_PARALLEL_FOR__
32#include "system/simd.h" // for dt_aligned_pixel_t
33#include "pixel/interpolation.h" // for dt_interpolation_new, dt_interp...
34
35void dt_iop_flip_and_zoom_8(const uint8_t *in, int32_t iw, int32_t ih, uint8_t *out, int32_t ow, int32_t oh,
36 const dt_image_orientation_t orientation, uint32_t *width, uint32_t *height)
37{
38 // init strides:
39 const uint32_t iwd = (orientation & ORIENTATION_SWAP_XY) ? ih : iw;
40 const uint32_t iht = (orientation & ORIENTATION_SWAP_XY) ? iw : ih;
41 // DO NOT UPSCALE !!!
42 const float scale = fmaxf(1.0, fmaxf(iwd / (float)ow, iht / (float)oh));
43 const uint32_t wd = *width = MIN(ow, iwd / scale);
44 const uint32_t ht = *height = MIN(oh, iht / scale);
45 const int bpp = 4; // bytes per pixel
46 int32_t ii = 0, jj = 0;
47 int32_t si = 1, sj = iw;
48 if(orientation & ORIENTATION_FLIP_Y)
49 {
50 jj = ih - jj - 1;
51 sj = -sj;
52 }
53 if(orientation & ORIENTATION_FLIP_X)
54 {
55 ii = iw - ii - 1;
56 si = -si;
57 }
58 if(orientation & ORIENTATION_SWAP_XY)
59 {
60 int t = sj;
61 sj = si;
62 si = t;
63 }
64 const int32_t half_pixel = .5f * scale;
65 const int32_t offm = half_pixel * bpp * MIN(MIN(0, si), MIN(sj, si + sj));
66 const int32_t offM = half_pixel * bpp * MAX(MAX(0, si), MAX(sj, si + sj));
68 for(uint32_t j = 0; j < ht; j++)
69 {
70 uint8_t *out2 = out + bpp * wd * j;
71 const uint8_t *in2 = in + bpp * (iw * jj + ii + sj * (int32_t)(scale * j));
72 float stepi = 0.0f;
73 for(uint32_t i = 0; i < wd; i++)
74 {
75 const uint8_t *in3 = in2 + ((int32_t)stepi) * si * bpp;
76 // this should always be within the bounds of in[], due to the way
77 // wd/ht are constructed by always just rounding down. half_pixel should never
78 // add up to one pixel difference.
79 // we have this check with the hope the branch predictor will get rid of it:
80 if(in3 + offm >= in && in3 + offM < in + bpp * iw * ih)
81 {
82 for(int k = 0; k < 3; k++)
83 out2[k] = // in3[k];
84 CLAMP(((int32_t)in3[bpp * half_pixel * sj + k]
85 + (int32_t)in3[bpp * half_pixel * (si + sj) + k]
86 + (int32_t)in3[bpp * half_pixel * si + k]
87 + (int32_t)in3[k]) / 4,
88 0, 255);
89 }
90 out2 += bpp;
91 stepi += scale;
92 }
93 }
94}
95
96void dt_iop_clip_and_zoom_8(const uint8_t *i, int32_t ix, int32_t iy, int32_t iw, int32_t ih, int32_t ibw,
97 int32_t ibh, uint8_t *o, int32_t ox, int32_t oy, int32_t ow, int32_t oh,
98 int32_t obw, int32_t obh)
99{
100 const float scalex = iw / (float)ow;
101 const float scaley = ih / (float)oh;
102 const int32_t ix2 = MAX(ix, 0);
103 const int32_t iy2 = MAX(iy, 0);
104 const int32_t ox2 = MAX(ox, 0);
105 const int32_t oy2 = MAX(oy, 0);
106 const int32_t oh2 = MIN(MIN(oh, (ibh - iy2) / scaley), obh - oy2);
107 const int32_t ow2 = MIN(MIN(ow, (ibw - ix2) / scalex), obw - ox2);
108 assert((int)(ix2 + ow2 * scalex) <= ibw);
109 assert((int)(iy2 + oh2 * scaley) <= ibh);
110 assert(ox2 + ow2 <= obw);
111 assert(oy2 + oh2 <= obh);
112 assert(ix2 >= 0 && iy2 >= 0 && ox2 >= 0 && oy2 >= 0);
113 float x = ix2, y = iy2;
114 for(int s = 0; s < oh2; s++)
115 {
116 int idx = ox2 + obw * (oy2 + s);
117 for(int t = 0; t < ow2; t++)
118 {
119 for(int k = 0; k < 3; k++)
120 o[4 * idx + k] = // i[3*(ibw* (int)y + (int)x ) + k)];
121 CLAMP(((int32_t)i[(4 * (ibw * (int32_t)y + (int32_t)(x + .5f * scalex)) + k)]
122 + (int32_t)i[(4 * (ibw * (int32_t)(y + .5f * scaley) + (int32_t)(x + .5f * scalex)) + k)]
123 + (int32_t)i[(4 * (ibw * (int32_t)(y + .5f * scaley) + (int32_t)(x)) + k)]
124 + (int32_t)i[(4 * (ibw * (int32_t)y + (int32_t)(x)) + k)])
125 / 4,
126 0, 255);
127 x += scalex;
128 idx++;
129 }
130 y += scaley;
131 x = ix2;
132 }
133}
134
135// apply clip and zoom on parts of a supplied full image.
136// roi_in and roi_out define which part to work on.
137void dt_iop_clip_and_zoom(float *out, const float *const in, const dt_iop_roi_t *const roi_out,
138 const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride)
139{
141 dt_interpolation_resample(itor, out, roi_out, in, roi_in);
142}
143
144// apply clip and zoom on the image region supplied in the input buffer.
145// roi_in and roi_out describe which part of the full image this relates to.
146void dt_iop_clip_and_zoom_roi(float *out, const float *const in, const dt_iop_roi_t *const roi_out,
147 const dt_iop_roi_t *const roi_in, const int32_t out_stride,
148 const int32_t in_stride)
149{
151 dt_interpolation_resample_roi(itor, out, roi_out, in, roi_in);
152}
153
154#ifdef HAVE_OPENCL
155// apply clip and zoom on parts of a supplied full image.
156// roi_in and roi_out define which part to work on.
157int dt_iop_clip_and_zoom_cl(int devid, cl_mem dev_out, cl_mem dev_in, const dt_iop_roi_t *const roi_out,
158 const dt_iop_roi_t *const roi_in)
159{
161 return dt_interpolation_resample_cl(itor, devid, dev_out, roi_out, dev_in, roi_in);
162}
163
164// apply clip and zoom on the image region supplied in the input buffer.
165// roi_in and roi_out describe which part of the full image this relates to.
166int dt_iop_clip_and_zoom_roi_cl(int devid, cl_mem dev_out, cl_mem dev_in, const dt_iop_roi_t *const roi_out,
167 const dt_iop_roi_t *const roi_in)
168{
170 return dt_interpolation_resample_roi_cl(itor, devid, dev_out, roi_out, dev_in, roi_in);
171}
172
173#endif
174
175void dt_iop_clip_and_zoom_mosaic_half_size(uint16_t *const out, const uint16_t *const in,
176 const dt_iop_roi_t *const roi_out,
177 const dt_iop_roi_t *const roi_in, const int32_t out_stride,
178 const int32_t in_stride, const uint32_t filters)
179{
180 // adjust to pixel region and don't sample more than scale/2 nbs!
181 // pixel footprint on input buffer, radius:
182 const float px_footprint = 1.f / roi_out->scale;
183
184 // move to origin point 01 of a 2x2 CFA block
185 // (RGGB=0112 or CYGM=0132)
186 int trggbx = 0, trggby = 0;
187 if(FC(trggby, trggbx + 1, filters) != 1) trggbx++;
188 if(FC(trggby, trggbx, filters) != 0)
189 {
190 trggbx = (trggbx + 1) & 1;
191 trggby++;
192 }
193 const int rggbx = trggbx, rggby = trggby;
194
195 // Create a reverse lookup of FC(): for each CFA color, a list of
196 // offsets from start of a 2x2 block at which to find that
197 // color. First index is color, second is to the list of offsets,
198 // preceded by the number of offsets.
199 int clut[4][3] = {{0}};
200 for(int y = 0; y < 2; ++y)
201 for(int x = 0; x < 2; ++x)
202 {
203 const int c = FC(y + rggby, x + rggbx, filters);
204 assert(clut[c][0] < 2);
205 clut[c][++clut[c][0]] = x + y * in_stride;
206 }
208 for(int y = 0; y < roi_out->height; y++)
209 {
210 uint16_t *outc = out + out_stride * y;
211
212 const float fy = (y + roi_out->y) * px_footprint;
213 const int miny = (CLAMPS((int)floorf(fy - px_footprint), 0, roi_in->height-3) & ~1u) + rggby;
214 const int maxy = MIN(roi_in->height-1, (int)ceilf(fy + px_footprint));
215
216 float fx = roi_out->x * px_footprint;
217 for(int x = 0; x < roi_out->width; x++, fx += px_footprint, outc++)
218 {
219 const int minx = (CLAMPS((int)floorf(fx - px_footprint), 0, roi_in->width-3) & ~1u) + rggbx;
220 const int maxx = MIN(roi_in->width-1, (int)ceilf(fx + px_footprint));
221
222 const int c = FC(y, x, filters);
223 int num = 0;
224 uint32_t col = 0;
225
226 for(int yy = miny; yy < maxy; yy += 2)
227 for(int xx = minx; xx < maxx; xx += 2)
228 {
229 col += in[clut[c][1] + xx + in_stride * yy];
230 num++;
231 if (clut[c][0] == 2)
232 { // G in RGGB CFA
233 col += in[clut[c][2] + xx + in_stride * yy];
234 num++;
235 }
236 }
237 if(num) *outc = col / num;
238 }
239 }
240}
241
242void dt_iop_clip_and_zoom_mosaic_half_size_f(float *const out, const float *const in,
243 const dt_iop_roi_t *const roi_out,
244 const dt_iop_roi_t *const roi_in, const int32_t out_stride,
245 const int32_t in_stride, const uint32_t filters)
246{
247 // adjust to pixel region and don't sample more than scale/2 nbs!
248 // pixel footprint on input buffer, radius:
249 const float px_footprint = 1.f / roi_out->scale;
250 // how many 2x2 blocks can be sampled inside that area
251 const int samples = round(px_footprint / 2);
252
253 // move p to point to an rggb block:
254 int trggbx = 0, trggby = 0;
255 if(FC(trggby, trggbx + 1, filters) != 1) trggbx++;
256 if(FC(trggby, trggbx, filters) != 0)
257 {
258 trggbx = (trggbx + 1) & 1;
259 trggby++;
260 }
261 const int rggbx = trggbx, rggby = trggby;
263 for(int y = 0; y < roi_out->height; y++)
264 {
265 float *outc = out + out_stride * y;
266
267 const float fy = (y + roi_out->y) * px_footprint;
268 int py = (int)fy & ~1;
269 const float dy = (fy - py) / 2;
270 py = MIN(((roi_in->height - 6) & ~1u), py) + rggby;
271
272 int maxj = MIN(((roi_in->height - 5) & ~1u) + rggby, py + 2 * samples);
273
274 for(int x = 0; x < roi_out->width; x++)
275 {
276 dt_aligned_pixel_t col = { 0, 0, 0, 0 };
277
278 const float fx = (x + roi_out->x) * px_footprint;
279 int px = (int)fx & ~1;
280 const float dx = (fx - px) / 2;
281 px = MIN(((roi_in->width - 6) & ~1u), px) + rggbx;
282
283 const int maxi = MIN(((roi_in->width - 5) & ~1u) + rggbx, px + 2 * samples);
284
286 float num = 0;
287
288 // upper left 2x2 block of sampling region
289 p[0] = in[px + in_stride * py];
290 p[1] = in[px + 1 + in_stride * py];
291 p[2] = in[px + in_stride * (py + 1)];
292 p[3] = in[px + 1 + in_stride * (py + 1)];
293 for(int c = 0; c < 4; c++) col[c] += ((1 - dx) * (1 - dy)) * p[c];
294
295 // left 2x2 block border of sampling region
296 for(int j = py + 2; j <= maxj; j += 2)
297 {
298 p[0] = in[px + in_stride * j];
299 p[1] = in[px + 1 + in_stride * j];
300 p[2] = in[px + in_stride * (j + 1)];
301 p[3] = in[px + 1 + in_stride * (j + 1)];
302 for(int c = 0; c < 4; c++) col[c] += (1 - dx) * p[c];
303 }
304
305 // upper 2x2 block border of sampling region
306 for(int i = px + 2; i <= maxi; i += 2)
307 {
308 p[0] = in[i + in_stride * py];
309 p[1] = in[i + 1 + in_stride * py];
310 p[2] = in[i + in_stride * (py + 1)];
311 p[3] = in[i + 1 + in_stride * (py + 1)];
312 for(int c = 0; c < 4; c++) col[c] += (1 - dy) * p[c];
313 }
314
315 // 2x2 blocks in the middle of sampling region
316 for(int j = py + 2; j <= maxj; j += 2)
317 for(int i = px + 2; i <= maxi; i += 2)
318 {
319 p[0] = in[i + in_stride * j];
320 p[1] = in[i + 1 + in_stride * j];
321 p[2] = in[i + in_stride * (j + 1)];
322 p[3] = in[i + 1 + in_stride * (j + 1)];
323 for(int c = 0; c < 4; c++) col[c] += p[c];
324 }
325
326 if(maxi == px + 2 * samples && maxj == py + 2 * samples)
327 {
328 // right border
329 for(int j = py + 2; j <= maxj; j += 2)
330 {
331 p[0] = in[maxi + 2 + in_stride * j];
332 p[1] = in[maxi + 3 + in_stride * j];
333 p[2] = in[maxi + 2 + in_stride * (j + 1)];
334 p[3] = in[maxi + 3 + in_stride * (j + 1)];
335 for(int c = 0; c < 4; c++) col[c] += dx * p[c];
336 }
337
338 // upper right
339 p[0] = in[maxi + 2 + in_stride * py];
340 p[1] = in[maxi + 3 + in_stride * py];
341 p[2] = in[maxi + 2 + in_stride * (py + 1)];
342 p[3] = in[maxi + 3 + in_stride * (py + 1)];
343 for(int c = 0; c < 4; c++) col[c] += (dx * (1 - dy)) * p[c];
344
345 // lower border
346 for(int i = px + 2; i <= maxi; i += 2)
347 {
348 p[0] = in[i + in_stride * (maxj + 2)];
349 p[1] = in[i + 1 + in_stride * (maxj + 2)];
350 p[2] = in[i + in_stride * (maxj + 3)];
351 p[3] = in[i + 1 + in_stride * (maxj + 3)];
352 for(int c = 0; c < 4; c++) col[c] += dy * p[c];
353 }
354
355 // lower left 2x2 block
356 p[0] = in[px + in_stride * (maxj + 2)];
357 p[1] = in[px + 1 + in_stride * (maxj + 2)];
358 p[2] = in[px + in_stride * (maxj + 3)];
359 p[3] = in[px + 1 + in_stride * (maxj + 3)];
360 for(int c = 0; c < 4; c++) col[c] += ((1 - dx) * dy) * p[c];
361
362 // lower right 2x2 block
363 p[0] = in[maxi + 2 + in_stride * (maxj + 2)];
364 p[1] = in[maxi + 3 + in_stride * (maxj + 2)];
365 p[2] = in[maxi + 2 + in_stride * (maxj + 3)];
366 p[3] = in[maxi + 3 + in_stride * (maxj + 3)];
367 for(int c = 0; c < 4; c++) col[c] += (dx * dy) * p[c];
368
369 num = (samples + 1) * (samples + 1);
370 }
371 else if(maxi == px + 2 * samples)
372 {
373 // right border
374 for(int j = py + 2; j <= maxj; j += 2)
375 {
376 p[0] = in[maxi + 2 + in_stride * j];
377 p[1] = in[maxi + 3 + in_stride * j];
378 p[2] = in[maxi + 2 + in_stride * (j + 1)];
379 p[3] = in[maxi + 3 + in_stride * (j + 1)];
380 for(int c = 0; c < 4; c++) col[c] += dx * p[c];
381 }
382
383 // upper right
384 p[0] = in[maxi + 2 + in_stride * py];
385 p[1] = in[maxi + 3 + in_stride * py];
386 p[2] = in[maxi + 2 + in_stride * (py + 1)];
387 p[3] = in[maxi + 3 + in_stride * (py + 1)];
388 for(int c = 0; c < 4; c++) col[c] += (dx * (1 - dy)) * p[c];
389
390 num = ((maxj - py) / 2 + 1 - dy) * (samples + 1);
391 }
392 else if(maxj == py + 2 * samples)
393 {
394 // lower border
395 for(int i = px + 2; i <= maxi; i += 2)
396 {
397 p[0] = in[i + in_stride * (maxj + 2)];
398 p[1] = in[i + 1 + in_stride * (maxj + 2)];
399 p[2] = in[i + in_stride * (maxj + 3)];
400 p[3] = in[i + 1 + in_stride * (maxj + 3)];
401 for(int c = 0; c < 4; c++) col[c] += dy * p[c];
402 }
403
404 // lower left 2x2 block
405 p[0] = in[px + in_stride * (maxj + 2)];
406 p[1] = in[px + 1 + in_stride * (maxj + 2)];
407 p[2] = in[px + in_stride * (maxj + 3)];
408 p[3] = in[px + 1 + in_stride * (maxj + 3)];
409 for(int c = 0; c < 4; c++) col[c] += ((1 - dx) * dy) * p[c];
410
411 num = ((maxi - px) / 2 + 1 - dx) * (samples + 1);
412 }
413 else
414 {
415 num = ((maxi - px) / 2 + 1 - dx) * ((maxj - py) / 2 + 1 - dy);
416 }
417
418 const int c = (2 * ((y + rggby) % 2) + ((x + rggbx) % 2));
419 if(num) *outc = col[c] / num;
420 outc++;
421 }
422 }
423}
424
429void dt_iop_clip_and_zoom_mosaic_third_size_xtrans(uint16_t *const out, const uint16_t *const in,
430 const dt_iop_roi_t *const roi_out,
431 const dt_iop_roi_t *const roi_in, const int32_t out_stride,
432 const int32_t in_stride, const uint8_t (*const xtrans)[6])
433{
434 const float px_footprint = 1.f / roi_out->scale;
435 // Use box filter of width px_footprint*2+1 centered on the current
436 // sample (rounded to nearest input pixel) to anti-alias. Higher MP
437 // images need larger filters to avoid artifacts.
439 for(int y = 0; y < roi_out->height; y++)
440 {
441 uint16_t *outc = out + out_stride * y;
442
443 const float fy = (y + roi_out->y) * px_footprint;
444 const int miny = MAX(0, (int)roundf(fy - px_footprint));
445 const int maxy = MIN(roi_in->height-1, (int)roundf(fy + px_footprint));
446
447 float fx = roi_out->x * px_footprint;
448 for(int x = 0; x < roi_out->width; x++, fx += px_footprint, outc++)
449 {
450 const int minx = MAX(0, (int)roundf(fx - px_footprint));
451 const int maxx = MIN(roi_in->width-1, (int)roundf(fx + px_footprint));
452
453 const int c = FCxtrans(y, x, roi_out, xtrans);
454 int num = 0;
455 uint32_t col = 0;
456
457 for(int yy = miny; yy <= maxy; ++yy)
458 for(int xx = minx; xx <= maxx; ++xx)
459 if(FCxtrans(yy, xx, roi_in, xtrans) == c)
460 {
461 col += in[xx + in_stride * yy];
462 num++;
463 }
464 *outc = col / num;
465 }
466 }
467}
468
469void dt_iop_clip_and_zoom_mosaic_third_size_xtrans_f(float *const out, const float *const in,
470 const dt_iop_roi_t *const roi_out,
471 const dt_iop_roi_t *const roi_in, const int32_t out_stride,
472 const int32_t in_stride, const uint8_t (*const xtrans)[6])
473{
474 const float px_footprint = 1.f / roi_out->scale;
476 for(int y = 0; y < roi_out->height; y++)
477 {
478 float *outc = out + out_stride * y;
479
480 const float fy = (y + roi_out->y) * px_footprint;
481 const int miny = MAX(0, (int)roundf(fy - px_footprint));
482 const int maxy = MIN(roi_in->height-1, (int)roundf(fy + px_footprint));
483
484 float fx = roi_out->x * px_footprint;
485 for(int x = 0; x < roi_out->width; x++, fx += px_footprint, outc++)
486 {
487 const int minx = MAX(0, (int)roundf(fx - px_footprint));
488 const int maxx = MIN(roi_in->width-1, (int)roundf(fx + px_footprint));
489
490 const int c = FCxtrans(y, x, roi_out, xtrans);
491 int num = 0;
492 float col = 0.f;
493
494 for(int yy = miny; yy <= maxy; ++yy)
495 for(int xx = minx; xx <= maxx; ++xx)
496 if(FCxtrans(yy, xx, roi_in, xtrans) == c)
497 {
498 col += in[xx + in_stride * yy];
499 num++;
500 }
501 *outc = col / (float)num;
502 }
503 }
504}
505
507 const dt_iop_roi_t *const roi_out,
508 const dt_iop_roi_t *const roi_in,
509 const int32_t out_stride,
510 const int32_t in_stride)
511{
512 // adjust to pixel region and don't sample more than scale/2 nbs!
513 // pixel footprint on input buffer, radius:
514 const float px_footprint = 1.f / roi_out->scale;
515 // how many pixels can be sampled inside that area
516 const int samples = round(px_footprint);
518 for(int y = 0; y < roi_out->height; y++)
519 {
520 float *outc = out + 4 * (out_stride * y);
521
522 const float fy = (y + roi_out->y) * px_footprint;
523 int py = (int)fy;
524 const float dy = fy - py;
525 py = MIN(((roi_in->height - 3)), py);
526
527 const int maxj = MIN(((roi_in->height - 2)), py + samples);
528
529 for(int x = 0; x < roi_out->width; x++)
530 {
531 float col = 0.0f;
532
533 const float fx = (x + roi_out->x) * px_footprint;
534 int px = (int)fx;
535 const float dx = fx - px;
536 px = MIN(((roi_in->width - 3)), px);
537
538 const int maxi = MIN(((roi_in->width - 2)), px + samples);
539
540 float p;
541 float num = 0;
542
543 // upper left pixel of sampling region
544 p = in[px + in_stride * py];
545 col += ((1 - dx) * (1 - dy)) * p;
546
547 // left pixel border of sampling region
548 for(int j = py + 1; j <= maxj; j++)
549 {
550 p = in[px + in_stride * j];
551 col += (1 - dx) * p;
552 }
553
554 // upper pixel border of sampling region
555 for(int i = px + 1; i <= maxi; i++)
556 {
557 p = in[i + in_stride * py];
558 col += (1 - dy) * p;
559 }
560
561 // pixels in the middle of sampling region
562 for(int j = py + 1; j <= maxj; j++)
563 for(int i = px + 1; i <= maxi; i++)
564 {
565 p = in[i + in_stride * j];
566 col += p;
567 }
568
569 if(maxi == px + samples && maxj == py + samples)
570 {
571 // right border
572 for(int j = py + 1; j <= maxj; j++)
573 {
574 p = in[maxi + 1 + in_stride * j];
575 col += dx * p;
576 }
577
578 // upper right
579 p = in[maxi + 1 + in_stride * py];
580 col += (dx * (1 - dy)) * p;
581
582 // lower border
583 for(int i = px + 1; i <= maxi; i++)
584 {
585 p = in[i + in_stride * (maxj + 1)];
586 col += dy * p;
587 }
588
589 // lower left pixel
590 p = in[px + in_stride * (maxj + 1)];
591 col += ((1 - dx) * dy) * p;
592
593 // lower right pixel
594 p = in[maxi + 1 + in_stride * (maxj + 1)];
595 col += (dx * dy) * p;
596
597 num = (samples + 1) * (samples + 1);
598 }
599 else if(maxi == px + samples)
600 {
601 // right border
602 for(int j = py + 1; j <= maxj; j++)
603 {
604 p = in[maxi + 1 + in_stride * j];
605 col += dx * p;
606 }
607
608 // upper right
609 p = in[maxi + 1 + in_stride * py];
610 col += (dx * (1 - dy)) * p;
611
612 num = ((maxj - py) / 2 + 1 - dy) * (samples + 1);
613 }
614 else if(maxj == py + samples)
615 {
616 // lower border
617 for(int i = px + 1; i <= maxi; i++)
618 {
619 p = in[i + in_stride * (maxj + 1)];
620 col += dy * p;
621 }
622
623 // lower left pixel
624 p = in[px + in_stride * (maxj + 1)];
625 col += ((1 - dx) * dy) * p;
626
627 num = ((maxi - px) / 2 + 1 - dx) * (samples + 1);
628 }
629 else
630 {
631 num = ((maxi - px) / 2 + 1 - dx) * ((maxj - py) / 2 + 1 - dy);
632 }
633
634 const float pix = (num) ? col / num : 0.0f;
635 outc[0] = pix;
636 outc[1] = pix;
637 outc[2] = pix;
638 outc[3] = 0.0f;
639 outc += 4;
640 }
641 }
642}
643
644void dt_iop_clip_and_zoom_demosaic_half_size_f(float *out, const float *const in,
645 const dt_iop_roi_t *const roi_out,
646 const dt_iop_roi_t *const roi_in, const int32_t out_stride,
647 const int32_t in_stride, const uint32_t filters)
648{
649 // adjust to pixel region and don't sample more than scale/2 nbs!
650 // pixel footprint on input buffer, radius:
651 const float px_footprint = 1.f / roi_out->scale;
652 // how many 2x2 blocks can be sampled inside that area
653 const int samples = round(px_footprint / 2);
654
655 // move p to point to an rggb block:
656 int trggbx = 0, trggby = 0;
657 if(FC(trggby, trggbx + 1, filters) != 1) trggbx++;
658 if(FC(trggby, trggbx, filters) != 0)
659 {
660 trggbx = (trggbx + 1) & 1;
661 trggby++;
662 }
663 const int rggbx = trggbx, rggby = trggby;
665 for(int y = 0; y < roi_out->height; y++)
666 {
667 float *outc = out + 4 * (out_stride * y);
668
669 const float fy = (y + roi_out->y) * px_footprint;
670 int py = (int)fy & ~1;
671 const float dy = (fy - py) / 2;
672 py = MIN(((roi_in->height - 6) & ~1u), py) + rggby;
673
674 const int maxj = MIN(((roi_in->height - 5) & ~1u) + rggby, py + 2 * samples);
675
676 for(int x = 0; x < roi_out->width; x++)
677 {
678 dt_aligned_pixel_t col = { 0, 0, 0, 0 };
679
680 const float fx = (x + roi_out->x) * px_footprint;
681 int px = (int)fx & ~1;
682 const float dx = (fx - px) / 2;
683 px = MIN(((roi_in->width - 6) & ~1u), px) + rggbx;
684
685 const int maxi = MIN(((roi_in->width - 5) & ~1u) + rggbx, px + 2 * samples);
686
688 float num = 0;
689
690 // upper left 2x2 block of sampling region
691 p[0] = in[px + in_stride * py];
692 p[1] = in[px + 1 + in_stride * py] + in[px + in_stride * (py + 1)];
693 p[2] = in[px + 1 + in_stride * (py + 1)];
694 for(int c = 0; c < 3; c++) col[c] += ((1 - dx) * (1 - dy)) * p[c];
695
696 // left 2x2 block border of sampling region
697 for(int j = py + 2; j <= maxj; j += 2)
698 {
699 p[0] = in[px + in_stride * j];
700 p[1] = in[px + 1 + in_stride * j] + in[px + in_stride * (j + 1)];
701 p[2] = in[px + 1 + in_stride * (j + 1)];
702 for(int c = 0; c < 3; c++) col[c] += (1 - dx) * p[c];
703 }
704
705 // upper 2x2 block border of sampling region
706 for(int i = px + 2; i <= maxi; i += 2)
707 {
708 p[0] = in[i + in_stride * py];
709 p[1] = in[i + 1 + in_stride * py] + in[i + in_stride * (py + 1)];
710 p[2] = in[i + 1 + in_stride * (py + 1)];
711 for(int c = 0; c < 3; c++) col[c] += (1 - dy) * p[c];
712 }
713
714 // 2x2 blocks in the middle of sampling region
715 for(int j = py + 2; j <= maxj; j += 2)
716 for(int i = px + 2; i <= maxi; i += 2)
717 {
718 p[0] = in[i + in_stride * j];
719 p[1] = in[i + 1 + in_stride * j] + in[i + in_stride * (j + 1)];
720 p[2] = in[i + 1 + in_stride * (j + 1)];
721 for(int c = 0; c < 3; c++) col[c] += p[c];
722 }
723
724 if(maxi == px + 2 * samples && maxj == py + 2 * samples)
725 {
726 // right border
727 for(int j = py + 2; j <= maxj; j += 2)
728 {
729 p[0] = in[maxi + 2 + in_stride * j];
730 p[1] = in[maxi + 3 + in_stride * j] + in[maxi + 2 + in_stride * (j + 1)];
731 p[2] = in[maxi + 3 + in_stride * (j + 1)];
732 for(int c = 0; c < 3; c++) col[c] += dx * p[c];
733 }
734
735 // upper right
736 p[0] = in[maxi + 2 + in_stride * py];
737 p[1] = in[maxi + 3 + in_stride * py] + in[maxi + 2 + in_stride * (py + 1)];
738 p[2] = in[maxi + 3 + in_stride * (py + 1)];
739 for(int c = 0; c < 3; c++) col[c] += (dx * (1 - dy)) * p[c];
740
741 // lower border
742 for(int i = px + 2; i <= maxi; i += 2)
743 {
744 p[0] = in[i + in_stride * (maxj + 2)];
745 p[1] = in[i + 1 + in_stride * (maxj + 2)] + in[i + in_stride * (maxj + 3)];
746 p[2] = in[i + 1 + in_stride * (maxj + 3)];
747 for(int c = 0; c < 3; c++) col[c] += dy * p[c];
748 }
749
750 // lower left 2x2 block
751 p[0] = in[px + in_stride * (maxj + 2)];
752 p[1] = in[px + 1 + in_stride * (maxj + 2)] + in[px + in_stride * (maxj + 3)];
753 p[2] = in[px + 1 + in_stride * (maxj + 3)];
754 for(int c = 0; c < 3; c++) col[c] += ((1 - dx) * dy) * p[c];
755
756 // lower right 2x2 block
757 p[0] = in[maxi + 2 + in_stride * (maxj + 2)];
758 p[1] = in[maxi + 3 + in_stride * (maxj + 2)] + in[maxi + 2 + in_stride * (maxj + 3)];
759 p[2] = in[maxi + 3 + in_stride * (maxj + 3)];
760 for(int c = 0; c < 3; c++) col[c] += (dx * dy) * p[c];
761
762 num = (samples + 1) * (samples + 1);
763 }
764 else if(maxi == px + 2 * samples)
765 {
766 // right border
767 for(int j = py + 2; j <= maxj; j += 2)
768 {
769 p[0] = in[maxi + 2 + in_stride * j];
770 p[1] = in[maxi + 3 + in_stride * j] + in[maxi + 2 + in_stride * (j + 1)];
771 p[2] = in[maxi + 3 + in_stride * (j + 1)];
772 for(int c = 0; c < 3; c++) col[c] += dx * p[c];
773 }
774
775 // upper right
776 p[0] = in[maxi + 2 + in_stride * py];
777 p[1] = in[maxi + 3 + in_stride * py] + in[maxi + 2 + in_stride * (py + 1)];
778 p[2] = in[maxi + 3 + in_stride * (py + 1)];
779 for(int c = 0; c < 3; c++) col[c] += (dx * (1 - dy)) * p[c];
780
781 num = ((maxj - py) / 2 + 1 - dy) * (samples + 1);
782 }
783 else if(maxj == py + 2 * samples)
784 {
785 // lower border
786 for(int i = px + 2; i <= maxi; i += 2)
787 {
788 p[0] = in[i + in_stride * (maxj + 2)];
789 p[1] = in[i + 1 + in_stride * (maxj + 2)] + in[i + in_stride * (maxj + 3)];
790 p[2] = in[i + 1 + in_stride * (maxj + 3)];
791 for(int c = 0; c < 3; c++) col[c] += dy * p[c];
792 }
793
794 // lower left 2x2 block
795 p[0] = in[px + in_stride * (maxj + 2)];
796 p[1] = in[px + 1 + in_stride * (maxj + 2)] + in[px + in_stride * (maxj + 3)];
797 p[2] = in[px + 1 + in_stride * (maxj + 3)];
798 for(int c = 0; c < 3; c++) col[c] += ((1 - dx) * dy) * p[c];
799
800 num = ((maxi - px) / 2 + 1 - dx) * (samples + 1);
801 }
802 else
803 {
804 num = ((maxi - px) / 2 + 1 - dx) * ((maxj - py) / 2 + 1 - dy);
805 }
806
807 outc[0] = col[0] / num;
808 outc[1] = (col[1] / num) / 2.0f;
809 outc[2] = col[2] / num;
810 outc[3] = 0.0f;
811 outc += 4;
812 }
813 }
814}
815
816
818 const dt_iop_roi_t *const roi_out,
819 const dt_iop_roi_t *const roi_in,
820 const int32_t out_stride, const int32_t in_stride,
821 const uint8_t (*const xtrans)[6])
822{
823 const float px_footprint = 1.f / roi_out->scale;
824 const int samples = MAX(1, (int)floorf(px_footprint / 3));
825
826 // A slightly different algorithm than
827 // dt_iop_clip_and_zoom_demosaic_half_size_f() which aligns to 2x2
828 // Bayer grid and hence most pull additional data from all edges
829 // which don't align with CFA. Instead align to a 3x3 pattern (which
830 // is semi-regular in X-Trans CFA). This code doesn't worry about
831 // fractional pixel offset of top/left of pattern nor oversampling
832 // by non-integer number of samples.
834 for(int y = 0; y < roi_out->height; y++)
835 {
836 float *outc = out + 4 * (out_stride * y);
837 const int py = CLAMPS((int)round((y + roi_out->y - 0.5f) * px_footprint), 0, roi_in->height - 3);
838 const int ymax = MIN(roi_in->height - 3, py + 3 * samples);
839
840 for(int x = 0; x < roi_out->width; x++, outc += 4)
841 {
842 dt_aligned_pixel_t col = { 0.0f };
843 int num = 0;
844 const int px = CLAMPS((int)round((x + roi_out->x - 0.5f) * px_footprint), 0, roi_in->width - 3);
845 const int xmax = MIN(roi_in->width - 3, px + 3 * samples);
846 for(int yy = py; yy <= ymax; yy += 3)
847 for(int xx = px; xx <= xmax; xx += 3)
848 {
849 for(int j = 0; j < 3; ++j)
850 for(int i = 0; i < 3; ++i)
851 col[FCxtrans(yy + j, xx + i, roi_in, xtrans)] += in[xx + i + in_stride * (yy + j)];
852 num++;
853 }
854
855 // X-Trans RGB weighting averages to 2:5:2 for each 3x3 cell
856 outc[0] = col[0] / (num * 2);
857 outc[1] = col[1] / (num * 5);
858 outc[2] = col[2] / (num * 2);
859 }
860 }
861}
862
864{
865 yuv[0] = 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2];
866 yuv[1] = -0.147 * rgb[0] - 0.289 * rgb[1] + 0.437 * rgb[2];
867 yuv[2] = 0.615 * rgb[0] - 0.515 * rgb[1] - 0.100 * rgb[2];
868}
869
871{
872 rgb[0] = yuv[0] + 1.140 * yuv[2];
873 rgb[1] = yuv[0] - 0.394 * yuv[1] - 0.581 * yuv[2];
874 rgb[2] = yuv[0] + 2.028 * yuv[1];
875}
876
877static inline void mat4inv(const float X[][4], float R[][4])
878{
879 const float det = X[0][3] * X[1][2] * X[2][1] * X[3][0] - X[0][2] * X[1][3] * X[2][1] * X[3][0]
880 - X[0][3] * X[1][1] * X[2][2] * X[3][0] + X[0][1] * X[1][3] * X[2][2] * X[3][0]
881 + X[0][2] * X[1][1] * X[2][3] * X[3][0] - X[0][1] * X[1][2] * X[2][3] * X[3][0]
882 - X[0][3] * X[1][2] * X[2][0] * X[3][1] + X[0][2] * X[1][3] * X[2][0] * X[3][1]
883 + X[0][3] * X[1][0] * X[2][2] * X[3][1] - X[0][0] * X[1][3] * X[2][2] * X[3][1]
884 - X[0][2] * X[1][0] * X[2][3] * X[3][1] + X[0][0] * X[1][2] * X[2][3] * X[3][1]
885 + X[0][3] * X[1][1] * X[2][0] * X[3][2] - X[0][1] * X[1][3] * X[2][0] * X[3][2]
886 - X[0][3] * X[1][0] * X[2][1] * X[3][2] + X[0][0] * X[1][3] * X[2][1] * X[3][2]
887 + X[0][1] * X[1][0] * X[2][3] * X[3][2] - X[0][0] * X[1][1] * X[2][3] * X[3][2]
888 - X[0][2] * X[1][1] * X[2][0] * X[3][3] + X[0][1] * X[1][2] * X[2][0] * X[3][3]
889 + X[0][2] * X[1][0] * X[2][1] * X[3][3] - X[0][0] * X[1][2] * X[2][1] * X[3][3]
890 - X[0][1] * X[1][0] * X[2][2] * X[3][3] + X[0][0] * X[1][1] * X[2][2] * X[3][3];
891 R[0][0] = (X[1][2] * X[2][3] * X[3][1] - X[1][3] * X[2][2] * X[3][1] + X[1][3] * X[2][1] * X[3][2]
892 - X[1][1] * X[2][3] * X[3][2] - X[1][2] * X[2][1] * X[3][3] + X[1][1] * X[2][2] * X[3][3])
893 / det;
894 R[1][0] = (X[1][3] * X[2][2] * X[3][0] - X[1][2] * X[2][3] * X[3][0] - X[1][3] * X[2][0] * X[3][2]
895 + X[1][0] * X[2][3] * X[3][2] + X[1][2] * X[2][0] * X[3][3] - X[1][0] * X[2][2] * X[3][3])
896 / det;
897 R[2][0] = (X[1][1] * X[2][3] * X[3][0] - X[1][3] * X[2][1] * X[3][0] + X[1][3] * X[2][0] * X[3][1]
898 - X[1][0] * X[2][3] * X[3][1] - X[1][1] * X[2][0] * X[3][3] + X[1][0] * X[2][1] * X[3][3])
899 / det;
900 R[3][0] = (X[1][2] * X[2][1] * X[3][0] - X[1][1] * X[2][2] * X[3][0] - X[1][2] * X[2][0] * X[3][1]
901 + X[1][0] * X[2][2] * X[3][1] + X[1][1] * X[2][0] * X[3][2] - X[1][0] * X[2][1] * X[3][2])
902 / det;
903
904 R[0][1] = (X[0][3] * X[2][2] * X[3][1] - X[0][2] * X[2][3] * X[3][1] - X[0][3] * X[2][1] * X[3][2]
905 + X[0][1] * X[2][3] * X[3][2] + X[0][2] * X[2][1] * X[3][3] - X[0][1] * X[2][2] * X[3][3])
906 / det;
907 R[1][1] = (X[0][2] * X[2][3] * X[3][0] - X[0][3] * X[2][2] * X[3][0] + X[0][3] * X[2][0] * X[3][2]
908 - X[0][0] * X[2][3] * X[3][2] - X[0][2] * X[2][0] * X[3][3] + X[0][0] * X[2][2] * X[3][3])
909 / det;
910 R[2][1] = (X[0][3] * X[2][1] * X[3][0] - X[0][1] * X[2][3] * X[3][0] - X[0][3] * X[2][0] * X[3][1]
911 + X[0][0] * X[2][3] * X[3][1] + X[0][1] * X[2][0] * X[3][3] - X[0][0] * X[2][1] * X[3][3])
912 / det;
913 R[3][1] = (X[0][1] * X[2][2] * X[3][0] - X[0][2] * X[2][1] * X[3][0] + X[0][2] * X[2][0] * X[3][1]
914 - X[0][0] * X[2][2] * X[3][1] - X[0][1] * X[2][0] * X[3][2] + X[0][0] * X[2][1] * X[3][2])
915 / det;
916
917 R[0][2] = (X[0][2] * X[1][3] * X[3][1] - X[0][3] * X[1][2] * X[3][1] + X[0][3] * X[1][1] * X[3][2]
918 - X[0][1] * X[1][3] * X[3][2] - X[0][2] * X[1][1] * X[3][3] + X[0][1] * X[1][2] * X[3][3])
919 / det;
920 R[1][2] = (X[0][3] * X[1][2] * X[3][0] - X[0][2] * X[1][3] * X[3][0] - X[0][3] * X[1][0] * X[3][2]
921 + X[0][0] * X[1][3] * X[3][2] + X[0][2] * X[1][0] * X[3][3] - X[0][0] * X[1][2] * X[3][3])
922 / det;
923 R[2][2] = (X[0][1] * X[1][3] * X[3][0] - X[0][3] * X[1][1] * X[3][0] + X[0][3] * X[1][0] * X[3][1]
924 - X[0][0] * X[1][3] * X[3][1] - X[0][1] * X[1][0] * X[3][3] + X[0][0] * X[1][1] * X[3][3])
925 / det;
926 R[3][2] = (X[0][2] * X[1][1] * X[3][0] - X[0][1] * X[1][2] * X[3][0] - X[0][2] * X[1][0] * X[3][1]
927 + X[0][0] * X[1][2] * X[3][1] + X[0][1] * X[1][0] * X[3][2] - X[0][0] * X[1][1] * X[3][2])
928 / det;
929
930 R[0][3] = (X[0][3] * X[1][2] * X[2][1] - X[0][2] * X[1][3] * X[2][1] - X[0][3] * X[1][1] * X[2][2]
931 + X[0][1] * X[1][3] * X[2][2] + X[0][2] * X[1][1] * X[2][3] - X[0][1] * X[1][2] * X[2][3])
932 / det;
933 R[1][3] = (X[0][2] * X[1][3] * X[2][0] - X[0][3] * X[1][2] * X[2][0] + X[0][3] * X[1][0] * X[2][2]
934 - X[0][0] * X[1][3] * X[2][2] - X[0][2] * X[1][0] * X[2][3] + X[0][0] * X[1][2] * X[2][3])
935 / det;
936 R[2][3] = (X[0][3] * X[1][1] * X[2][0] - X[0][1] * X[1][3] * X[2][0] - X[0][3] * X[1][0] * X[2][1]
937 + X[0][0] * X[1][3] * X[2][1] + X[0][1] * X[1][0] * X[2][3] - X[0][0] * X[1][1] * X[2][3])
938 / det;
939 R[3][3] = (X[0][1] * X[1][2] * X[2][0] - X[0][2] * X[1][1] * X[2][0] + X[0][2] * X[1][0] * X[2][1]
940 - X[0][0] * X[1][2] * X[2][1] - X[0][1] * X[1][0] * X[2][2] + X[0][0] * X[1][1] * X[2][2])
941 / det;
942}
943
944static void mat4mulv(float dst[4], const float mat[4][4], const float v[4])
945{
946 for(int k = 0; k < 4; k++)
947 {
948 float x = 0.0f;
949 for(int i = 0; i < 4; i++) x += mat[k][i] * v[i];
950 dst[k] = x;
951 }
952}
953
954void dt_iop_estimate_cubic(const float x[4], const float y[4], float a[4])
955{
956 // we want to fit a spline
957 // [y] [x^3 x^2 x^1 1] [a^3]
958 // |y| = |x^3 x^2 x^1 1| |a^2|
959 // |y| |x^3 x^2 x^1 1| |a^1|
960 // [y] [x^3 x^2 x^1 1] [ 1 ]
961 // and do that by inverting the matrix X:
962
963 const float X[4][4] = { { x[0] * x[0] * x[0], x[0] * x[0], x[0], 1.0f },
964 { x[1] * x[1] * x[1], x[1] * x[1], x[1], 1.0f },
965 { x[2] * x[2] * x[2], x[2] * x[2], x[2], 1.0f },
966 { x[3] * x[3] * x[3], x[3] * x[3], x[3], 1.0f } };
967 float X_inv[4][4];
968 mat4inv(X, X_inv);
969 mat4mulv(a, X_inv, y);
970}
971
972// clang-format off
973// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
974// vim: shiftwidth=2 expandtab tabstop=2 cindent
975// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
976// clang-format on
static const float x
const int t
const float v
static dt_aligned_pixel_t rgb
const dt_colormatrix_t dt_aligned_pixel_t out
static int FCxtrans(const int row, const int col, global const unsigned char(*const xtrans)[6])
static int FC(const int row, const int col, const unsigned int filters)
dt_image_orientation_t
Definition image.h:216
@ ORIENTATION_SWAP_XY
Definition image.h:221
@ ORIENTATION_FLIP_Y
Definition image.h:219
@ ORIENTATION_FLIP_X
Definition image.h:220
int bpp
void dt_iop_clip_and_zoom_8(const uint8_t *i, int32_t ix, int32_t iy, int32_t iw, int32_t ih, int32_t ibw, int32_t ibh, uint8_t *o, int32_t ox, int32_t oy, int32_t ow, int32_t oh, int32_t obw, int32_t obh)
void dt_iop_clip_and_zoom_roi(float *out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride)
void dt_iop_clip_and_zoom_demosaic_passthrough_monochrome_f(float *out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride)
void dt_iop_clip_and_zoom_mosaic_half_size_f(float *const out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride, const uint32_t filters)
void dt_iop_clip_and_zoom(float *out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride)
int dt_iop_clip_and_zoom_cl(int devid, cl_mem dev_out, cl_mem dev_in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in)
void dt_iop_YCbCr_to_RGB(const dt_aligned_pixel_t yuv, dt_aligned_pixel_t rgb)
void dt_iop_RGB_to_YCbCr(const dt_aligned_pixel_t rgb, dt_aligned_pixel_t yuv)
void dt_iop_clip_and_zoom_demosaic_half_size_f(float *out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride, const uint32_t filters)
void dt_iop_flip_and_zoom_8(const uint8_t *in, int32_t iw, int32_t ih, uint8_t *out, int32_t ow, int32_t oh, const dt_image_orientation_t orientation, uint32_t *width, uint32_t *height)
void dt_iop_clip_and_zoom_mosaic_half_size(uint16_t *const out, const uint16_t *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride, const uint32_t filters)
void dt_iop_clip_and_zoom_demosaic_third_size_xtrans_f(float *out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride, const uint8_t(*const xtrans)[6])
void dt_iop_estimate_cubic(const float x[4], const float y[4], float a[4])
void dt_iop_clip_and_zoom_mosaic_third_size_xtrans(uint16_t *const out, const uint16_t *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride, const uint8_t(*const xtrans)[6])
int dt_iop_clip_and_zoom_roi_cl(int devid, cl_mem dev_out, cl_mem dev_in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in)
static void mat4mulv(float dst[4], const float mat[4][4], const float v[4])
static void mat4inv(const float X[][4], float R[][4])
void dt_iop_clip_and_zoom_mosaic_third_size_xtrans_f(float *const out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride, const uint8_t(*const xtrans)[6])
const struct dt_interpolation * dt_interpolation_new(enum dt_interpolation_type type)
void dt_interpolation_resample_roi(const struct dt_interpolation *itor, float *out, const dt_iop_roi_t *const roi_out, const float *const in, const dt_iop_roi_t *const roi_in)
void dt_interpolation_resample(const struct dt_interpolation *itor, float *out, const dt_iop_roi_t *const roi_out, const float *const in, const dt_iop_roi_t *const roi_in)
int dt_interpolation_resample_cl(const struct dt_interpolation *itor, const int devid, cl_mem dev_out, const dt_iop_roi_t *const roi_out, cl_mem dev_in, const dt_iop_roi_t *const roi_in)
int dt_interpolation_resample_roi_cl(const struct dt_interpolation *itor, const int devid, cl_mem dev_out, const dt_iop_roi_t *const roi_out, cl_mem dev_in, const dt_iop_roi_t *const roi_in)
@ DT_INTERPOLATION_USERPREF
float *const restrict const size_t k
#define R
#define CLAMPS(A, L, H)
Definition math.h:78
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
Region of interest passed through the pixelpipe.
Definition format.h:49
double scale
Definition format.h:51
int width
Definition format.h:50
int height
Definition format.h:50
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29