| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2013 Clément Bœsch | ||
| 3 | * Copyright (c) 2018 Paul B Mahol | ||
| 4 | * | ||
| 5 | * This file is part of FFmpeg. | ||
| 6 | * | ||
| 7 | * FFmpeg is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with FFmpeg; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | /** | ||
| 23 | * @file | ||
| 24 | * 3D Lookup table filter | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <float.h> | ||
| 28 | |||
| 29 | #include "config_components.h" | ||
| 30 | |||
| 31 | #include "libavutil/mem.h" | ||
| 32 | #include "libavutil/opt.h" | ||
| 33 | #include "libavutil/file_open.h" | ||
| 34 | #include "libavutil/intfloat.h" | ||
| 35 | #include "libavutil/avassert.h" | ||
| 36 | #include "libavutil/avstring.h" | ||
| 37 | #include "drawutils.h" | ||
| 38 | #include "filters.h" | ||
| 39 | #include "video.h" | ||
| 40 | #include "lut3d.h" | ||
| 41 | |||
| 42 | #define R 0 | ||
| 43 | #define G 1 | ||
| 44 | #define B 2 | ||
| 45 | #define A 3 | ||
| 46 | |||
| 47 | #define OFFSET(x) offsetof(LUT3DContext, x) | ||
| 48 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM | ||
| 49 | #define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM | ||
| 50 | #define COMMON_OPTIONS \ | ||
| 51 | { "interp", "select interpolation mode", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERPOLATE_TETRAHEDRAL}, 0, NB_INTERP_MODE-1, TFLAGS, .unit = "interp_mode" }, \ | ||
| 52 | { "nearest", "use values from the nearest defined points", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_NEAREST}, 0, 0, TFLAGS, .unit = "interp_mode" }, \ | ||
| 53 | { "trilinear", "interpolate values using the 8 points defining a cube", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TRILINEAR}, 0, 0, TFLAGS, .unit = "interp_mode" }, \ | ||
| 54 | { "tetrahedral", "interpolate values using a tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TETRAHEDRAL}, 0, 0, TFLAGS, .unit = "interp_mode" }, \ | ||
| 55 | { "pyramid", "interpolate values using a pyramid", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_PYRAMID}, 0, 0, TFLAGS, .unit = "interp_mode" }, \ | ||
| 56 | { "prism", "interpolate values using a prism", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_PRISM}, 0, 0, TFLAGS, .unit = "interp_mode" }, \ | ||
| 57 | { NULL } | ||
| 58 | |||
| 59 | #define EXPONENT_MASK 0x7F800000 | ||
| 60 | #define MANTISSA_MASK 0x007FFFFF | ||
| 61 | #define SIGN_MASK 0x80000000 | ||
| 62 | |||
| 63 | ✗ | static inline float sanitizef(float f) | |
| 64 | { | ||
| 65 | union av_intfloat32 t; | ||
| 66 | ✗ | t.f = f; | |
| 67 | |||
| 68 | ✗ | if ((t.i & EXPONENT_MASK) == EXPONENT_MASK) { | |
| 69 | ✗ | if ((t.i & MANTISSA_MASK) != 0) { | |
| 70 | // NAN | ||
| 71 | ✗ | return 0.0f; | |
| 72 | ✗ | } else if (t.i & SIGN_MASK) { | |
| 73 | // -INF | ||
| 74 | ✗ | return -FLT_MAX; | |
| 75 | } else { | ||
| 76 | // +INF | ||
| 77 | ✗ | return FLT_MAX; | |
| 78 | } | ||
| 79 | } | ||
| 80 | ✗ | return f; | |
| 81 | } | ||
| 82 | |||
| 83 | ✗ | static inline float lerpf(float v0, float v1, float f) | |
| 84 | { | ||
| 85 | ✗ | return v0 + (v1 - v0) * f; | |
| 86 | } | ||
| 87 | |||
| 88 | ✗ | static inline struct rgbvec lerp(const struct rgbvec *v0, const struct rgbvec *v1, float f) | |
| 89 | { | ||
| 90 | ✗ | struct rgbvec v = { | |
| 91 | ✗ | lerpf(v0->r, v1->r, f), lerpf(v0->g, v1->g, f), lerpf(v0->b, v1->b, f) | |
| 92 | }; | ||
| 93 | ✗ | return v; | |
| 94 | } | ||
| 95 | |||
| 96 | #define NEAR(x) ((int)((x) + .5)) | ||
| 97 | #define PREV(x) ((int)(x)) | ||
| 98 | #define NEXT(x) (FFMIN((int)(x) + 1, lut3d->lutsize - 1)) | ||
| 99 | |||
| 100 | /** | ||
| 101 | * Get the nearest defined point | ||
| 102 | */ | ||
| 103 | ✗ | static inline struct rgbvec interp_nearest(const LUT3DContext *lut3d, | |
| 104 | const struct rgbvec *s) | ||
| 105 | { | ||
| 106 | ✗ | return lut3d->lut[NEAR(s->r) * lut3d->lutsize2 + NEAR(s->g) * lut3d->lutsize + NEAR(s->b)]; | |
| 107 | } | ||
| 108 | |||
| 109 | /** | ||
| 110 | * Interpolate using the 8 vertices of a cube | ||
| 111 | * @see https://en.wikipedia.org/wiki/Trilinear_interpolation | ||
| 112 | */ | ||
| 113 | ✗ | static inline struct rgbvec interp_trilinear(const LUT3DContext *lut3d, | |
| 114 | const struct rgbvec *s) | ||
| 115 | { | ||
| 116 | ✗ | const int lutsize2 = lut3d->lutsize2; | |
| 117 | ✗ | const int lutsize = lut3d->lutsize; | |
| 118 | ✗ | const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)}; | |
| 119 | ✗ | const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)}; | |
| 120 | ✗ | const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]}; | |
| 121 | ✗ | const struct rgbvec c000 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + prev[2]]; | |
| 122 | ✗ | const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]]; | |
| 123 | ✗ | const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]]; | |
| 124 | ✗ | const struct rgbvec c011 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + next[2]]; | |
| 125 | ✗ | const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]]; | |
| 126 | ✗ | const struct rgbvec c101 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + next[2]]; | |
| 127 | ✗ | const struct rgbvec c110 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + prev[2]]; | |
| 128 | ✗ | const struct rgbvec c111 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + next[2]]; | |
| 129 | ✗ | const struct rgbvec c00 = lerp(&c000, &c100, d.r); | |
| 130 | ✗ | const struct rgbvec c10 = lerp(&c010, &c110, d.r); | |
| 131 | ✗ | const struct rgbvec c01 = lerp(&c001, &c101, d.r); | |
| 132 | ✗ | const struct rgbvec c11 = lerp(&c011, &c111, d.r); | |
| 133 | ✗ | const struct rgbvec c0 = lerp(&c00, &c10, d.g); | |
| 134 | ✗ | const struct rgbvec c1 = lerp(&c01, &c11, d.g); | |
| 135 | ✗ | const struct rgbvec c = lerp(&c0, &c1, d.b); | |
| 136 | ✗ | return c; | |
| 137 | } | ||
| 138 | |||
| 139 | ✗ | static inline struct rgbvec interp_pyramid(const LUT3DContext *lut3d, | |
| 140 | const struct rgbvec *s) | ||
| 141 | { | ||
| 142 | ✗ | const int lutsize2 = lut3d->lutsize2; | |
| 143 | ✗ | const int lutsize = lut3d->lutsize; | |
| 144 | ✗ | const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)}; | |
| 145 | ✗ | const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)}; | |
| 146 | ✗ | const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]}; | |
| 147 | ✗ | const struct rgbvec c000 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + prev[2]]; | |
| 148 | ✗ | const struct rgbvec c111 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + next[2]]; | |
| 149 | struct rgbvec c; | ||
| 150 | |||
| 151 | ✗ | if (d.g > d.r && d.b > d.r) { | |
| 152 | ✗ | const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]]; | |
| 153 | ✗ | const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]]; | |
| 154 | ✗ | const struct rgbvec c011 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + next[2]]; | |
| 155 | |||
| 156 | ✗ | c.r = c000.r + (c111.r - c011.r) * d.r + (c010.r - c000.r) * d.g + (c001.r - c000.r) * d.b + | |
| 157 | ✗ | (c011.r - c001.r - c010.r + c000.r) * d.g * d.b; | |
| 158 | ✗ | c.g = c000.g + (c111.g - c011.g) * d.r + (c010.g - c000.g) * d.g + (c001.g - c000.g) * d.b + | |
| 159 | ✗ | (c011.g - c001.g - c010.g + c000.g) * d.g * d.b; | |
| 160 | ✗ | c.b = c000.b + (c111.b - c011.b) * d.r + (c010.b - c000.b) * d.g + (c001.b - c000.b) * d.b + | |
| 161 | ✗ | (c011.b - c001.b - c010.b + c000.b) * d.g * d.b; | |
| 162 | ✗ | } else if (d.r > d.g && d.b > d.g) { | |
| 163 | ✗ | const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]]; | |
| 164 | ✗ | const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]]; | |
| 165 | ✗ | const struct rgbvec c101 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + next[2]]; | |
| 166 | |||
| 167 | ✗ | c.r = c000.r + (c100.r - c000.r) * d.r + (c111.r - c101.r) * d.g + (c001.r - c000.r) * d.b + | |
| 168 | ✗ | (c101.r - c001.r - c100.r + c000.r) * d.r * d.b; | |
| 169 | ✗ | c.g = c000.g + (c100.g - c000.g) * d.r + (c111.g - c101.g) * d.g + (c001.g - c000.g) * d.b + | |
| 170 | ✗ | (c101.g - c001.g - c100.g + c000.g) * d.r * d.b; | |
| 171 | ✗ | c.b = c000.b + (c100.b - c000.b) * d.r + (c111.b - c101.b) * d.g + (c001.b - c000.b) * d.b + | |
| 172 | ✗ | (c101.b - c001.b - c100.b + c000.b) * d.r * d.b; | |
| 173 | } else { | ||
| 174 | ✗ | const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]]; | |
| 175 | ✗ | const struct rgbvec c110 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + prev[2]]; | |
| 176 | ✗ | const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]]; | |
| 177 | |||
| 178 | ✗ | c.r = c000.r + (c100.r - c000.r) * d.r + (c010.r - c000.r) * d.g + (c111.r - c110.r) * d.b + | |
| 179 | ✗ | (c110.r - c100.r - c010.r + c000.r) * d.r * d.g; | |
| 180 | ✗ | c.g = c000.g + (c100.g - c000.g) * d.r + (c010.g - c000.g) * d.g + (c111.g - c110.g) * d.b + | |
| 181 | ✗ | (c110.g - c100.g - c010.g + c000.g) * d.r * d.g; | |
| 182 | ✗ | c.b = c000.b + (c100.b - c000.b) * d.r + (c010.b - c000.b) * d.g + (c111.b - c110.b) * d.b + | |
| 183 | ✗ | (c110.b - c100.b - c010.b + c000.b) * d.r * d.g; | |
| 184 | } | ||
| 185 | |||
| 186 | ✗ | return c; | |
| 187 | } | ||
| 188 | |||
| 189 | ✗ | static inline struct rgbvec interp_prism(const LUT3DContext *lut3d, | |
| 190 | const struct rgbvec *s) | ||
| 191 | { | ||
| 192 | ✗ | const int lutsize2 = lut3d->lutsize2; | |
| 193 | ✗ | const int lutsize = lut3d->lutsize; | |
| 194 | ✗ | const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)}; | |
| 195 | ✗ | const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)}; | |
| 196 | ✗ | const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]}; | |
| 197 | ✗ | const struct rgbvec c000 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + prev[2]]; | |
| 198 | ✗ | const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]]; | |
| 199 | ✗ | const struct rgbvec c101 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + next[2]]; | |
| 200 | ✗ | const struct rgbvec c111 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + next[2]]; | |
| 201 | struct rgbvec c; | ||
| 202 | |||
| 203 | ✗ | if (d.b > d.r) { | |
| 204 | ✗ | const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]]; | |
| 205 | ✗ | const struct rgbvec c011 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + next[2]]; | |
| 206 | |||
| 207 | ✗ | c.r = c000.r + (c001.r - c000.r) * d.b + (c101.r - c001.r) * d.r + (c010.r - c000.r) * d.g + | |
| 208 | ✗ | (c000.r - c010.r - c001.r + c011.r) * d.b * d.g + | |
| 209 | ✗ | (c001.r - c011.r - c101.r + c111.r) * d.r * d.g; | |
| 210 | ✗ | c.g = c000.g + (c001.g - c000.g) * d.b + (c101.g - c001.g) * d.r + (c010.g - c000.g) * d.g + | |
| 211 | ✗ | (c000.g - c010.g - c001.g + c011.g) * d.b * d.g + | |
| 212 | ✗ | (c001.g - c011.g - c101.g + c111.g) * d.r * d.g; | |
| 213 | ✗ | c.b = c000.b + (c001.b - c000.b) * d.b + (c101.b - c001.b) * d.r + (c010.b - c000.b) * d.g + | |
| 214 | ✗ | (c000.b - c010.b - c001.b + c011.b) * d.b * d.g + | |
| 215 | ✗ | (c001.b - c011.b - c101.b + c111.b) * d.r * d.g; | |
| 216 | } else { | ||
| 217 | ✗ | const struct rgbvec c110 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + prev[2]]; | |
| 218 | ✗ | const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]]; | |
| 219 | |||
| 220 | ✗ | c.r = c000.r + (c101.r - c100.r) * d.b + (c100.r - c000.r) * d.r + (c010.r - c000.r) * d.g + | |
| 221 | ✗ | (c100.r - c110.r - c101.r + c111.r) * d.b * d.g + | |
| 222 | ✗ | (c000.r - c010.r - c100.r + c110.r) * d.r * d.g; | |
| 223 | ✗ | c.g = c000.g + (c101.g - c100.g) * d.b + (c100.g - c000.g) * d.r + (c010.g - c000.g) * d.g + | |
| 224 | ✗ | (c100.g - c110.g - c101.g + c111.g) * d.b * d.g + | |
| 225 | ✗ | (c000.g - c010.g - c100.g + c110.g) * d.r * d.g; | |
| 226 | ✗ | c.b = c000.b + (c101.b - c100.b) * d.b + (c100.b - c000.b) * d.r + (c010.b - c000.b) * d.g + | |
| 227 | ✗ | (c100.b - c110.b - c101.b + c111.b) * d.b * d.g + | |
| 228 | ✗ | (c000.b - c010.b - c100.b + c110.b) * d.r * d.g; | |
| 229 | } | ||
| 230 | |||
| 231 | ✗ | return c; | |
| 232 | } | ||
| 233 | |||
| 234 | /** | ||
| 235 | * Tetrahedral interpolation. Based on code found in Truelight Software Library paper. | ||
| 236 | * @see http://www.filmlight.ltd.uk/pdf/whitepapers/FL-TL-TN-0057-SoftwareLib.pdf | ||
| 237 | */ | ||
| 238 | ✗ | static inline struct rgbvec interp_tetrahedral(const LUT3DContext *lut3d, | |
| 239 | const struct rgbvec *s) | ||
| 240 | { | ||
| 241 | ✗ | const int lutsize2 = lut3d->lutsize2; | |
| 242 | ✗ | const int lutsize = lut3d->lutsize; | |
| 243 | ✗ | const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)}; | |
| 244 | ✗ | const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)}; | |
| 245 | ✗ | const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]}; | |
| 246 | ✗ | const struct rgbvec c000 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + prev[2]]; | |
| 247 | ✗ | const struct rgbvec c111 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + next[2]]; | |
| 248 | struct rgbvec c; | ||
| 249 | ✗ | if (d.r > d.g) { | |
| 250 | ✗ | if (d.g > d.b) { | |
| 251 | ✗ | const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]]; | |
| 252 | ✗ | const struct rgbvec c110 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + prev[2]]; | |
| 253 | ✗ | c.r = (1-d.r) * c000.r + (d.r-d.g) * c100.r + (d.g-d.b) * c110.r + (d.b) * c111.r; | |
| 254 | ✗ | c.g = (1-d.r) * c000.g + (d.r-d.g) * c100.g + (d.g-d.b) * c110.g + (d.b) * c111.g; | |
| 255 | ✗ | c.b = (1-d.r) * c000.b + (d.r-d.g) * c100.b + (d.g-d.b) * c110.b + (d.b) * c111.b; | |
| 256 | ✗ | } else if (d.r > d.b) { | |
| 257 | ✗ | const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]]; | |
| 258 | ✗ | const struct rgbvec c101 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + next[2]]; | |
| 259 | ✗ | c.r = (1-d.r) * c000.r + (d.r-d.b) * c100.r + (d.b-d.g) * c101.r + (d.g) * c111.r; | |
| 260 | ✗ | c.g = (1-d.r) * c000.g + (d.r-d.b) * c100.g + (d.b-d.g) * c101.g + (d.g) * c111.g; | |
| 261 | ✗ | c.b = (1-d.r) * c000.b + (d.r-d.b) * c100.b + (d.b-d.g) * c101.b + (d.g) * c111.b; | |
| 262 | } else { | ||
| 263 | ✗ | const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]]; | |
| 264 | ✗ | const struct rgbvec c101 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + next[2]]; | |
| 265 | ✗ | c.r = (1-d.b) * c000.r + (d.b-d.r) * c001.r + (d.r-d.g) * c101.r + (d.g) * c111.r; | |
| 266 | ✗ | c.g = (1-d.b) * c000.g + (d.b-d.r) * c001.g + (d.r-d.g) * c101.g + (d.g) * c111.g; | |
| 267 | ✗ | c.b = (1-d.b) * c000.b + (d.b-d.r) * c001.b + (d.r-d.g) * c101.b + (d.g) * c111.b; | |
| 268 | } | ||
| 269 | } else { | ||
| 270 | ✗ | if (d.b > d.g) { | |
| 271 | ✗ | const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]]; | |
| 272 | ✗ | const struct rgbvec c011 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + next[2]]; | |
| 273 | ✗ | c.r = (1-d.b) * c000.r + (d.b-d.g) * c001.r + (d.g-d.r) * c011.r + (d.r) * c111.r; | |
| 274 | ✗ | c.g = (1-d.b) * c000.g + (d.b-d.g) * c001.g + (d.g-d.r) * c011.g + (d.r) * c111.g; | |
| 275 | ✗ | c.b = (1-d.b) * c000.b + (d.b-d.g) * c001.b + (d.g-d.r) * c011.b + (d.r) * c111.b; | |
| 276 | ✗ | } else if (d.b > d.r) { | |
| 277 | ✗ | const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]]; | |
| 278 | ✗ | const struct rgbvec c011 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + next[2]]; | |
| 279 | ✗ | c.r = (1-d.g) * c000.r + (d.g-d.b) * c010.r + (d.b-d.r) * c011.r + (d.r) * c111.r; | |
| 280 | ✗ | c.g = (1-d.g) * c000.g + (d.g-d.b) * c010.g + (d.b-d.r) * c011.g + (d.r) * c111.g; | |
| 281 | ✗ | c.b = (1-d.g) * c000.b + (d.g-d.b) * c010.b + (d.b-d.r) * c011.b + (d.r) * c111.b; | |
| 282 | } else { | ||
| 283 | ✗ | const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]]; | |
| 284 | ✗ | const struct rgbvec c110 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + prev[2]]; | |
| 285 | ✗ | c.r = (1-d.g) * c000.r + (d.g-d.r) * c010.r + (d.r-d.b) * c110.r + (d.b) * c111.r; | |
| 286 | ✗ | c.g = (1-d.g) * c000.g + (d.g-d.r) * c010.g + (d.r-d.b) * c110.g + (d.b) * c111.g; | |
| 287 | ✗ | c.b = (1-d.g) * c000.b + (d.g-d.r) * c010.b + (d.r-d.b) * c110.b + (d.b) * c111.b; | |
| 288 | } | ||
| 289 | } | ||
| 290 | ✗ | return c; | |
| 291 | } | ||
| 292 | |||
| 293 | ✗ | static inline float prelut_interp_1d_linear(const Lut3DPreLut *prelut, | |
| 294 | int idx, const float s) | ||
| 295 | { | ||
| 296 | ✗ | const int lut_max = prelut->size - 1; | |
| 297 | ✗ | const float scaled = (s - prelut->min[idx]) * prelut->scale[idx]; | |
| 298 | ✗ | const float x = av_clipf(scaled, 0.0f, lut_max); | |
| 299 | ✗ | const int prev = PREV(x); | |
| 300 | ✗ | const int next = FFMIN((int)(x) + 1, lut_max); | |
| 301 | ✗ | const float p = prelut->lut[idx][prev]; | |
| 302 | ✗ | const float n = prelut->lut[idx][next]; | |
| 303 | ✗ | const float d = x - (float)prev; | |
| 304 | ✗ | return lerpf(p, n, d); | |
| 305 | } | ||
| 306 | |||
| 307 | ✗ | static inline struct rgbvec apply_prelut(const Lut3DPreLut *prelut, | |
| 308 | const struct rgbvec *s) | ||
| 309 | { | ||
| 310 | struct rgbvec c; | ||
| 311 | |||
| 312 | ✗ | if (prelut->size <= 0) | |
| 313 | ✗ | return *s; | |
| 314 | |||
| 315 | ✗ | c.r = prelut_interp_1d_linear(prelut, 0, s->r); | |
| 316 | ✗ | c.g = prelut_interp_1d_linear(prelut, 1, s->g); | |
| 317 | ✗ | c.b = prelut_interp_1d_linear(prelut, 2, s->b); | |
| 318 | ✗ | return c; | |
| 319 | } | ||
| 320 | |||
| 321 | #define DEFINE_INTERP_FUNC_PLANAR(name, nbits, depth) \ | ||
| 322 | static int interp_##nbits##_##name##_p##depth(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \ | ||
| 323 | { \ | ||
| 324 | int x, y; \ | ||
| 325 | const LUT3DContext *lut3d = ctx->priv; \ | ||
| 326 | const Lut3DPreLut *prelut = &lut3d->prelut; \ | ||
| 327 | const ThreadData *td = arg; \ | ||
| 328 | const AVFrame *in = td->in; \ | ||
| 329 | const AVFrame *out = td->out; \ | ||
| 330 | const int direct = out == in; \ | ||
| 331 | const int slice_start = (in->height * jobnr ) / nb_jobs; \ | ||
| 332 | const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \ | ||
| 333 | uint8_t *grow = out->data[0] + slice_start * out->linesize[0]; \ | ||
| 334 | uint8_t *brow = out->data[1] + slice_start * out->linesize[1]; \ | ||
| 335 | uint8_t *rrow = out->data[2] + slice_start * out->linesize[2]; \ | ||
| 336 | uint8_t *arow = out->data[3] + slice_start * out->linesize[3]; \ | ||
| 337 | const uint8_t *srcgrow = in->data[0] + slice_start * in->linesize[0]; \ | ||
| 338 | const uint8_t *srcbrow = in->data[1] + slice_start * in->linesize[1]; \ | ||
| 339 | const uint8_t *srcrrow = in->data[2] + slice_start * in->linesize[2]; \ | ||
| 340 | const uint8_t *srcarow = in->data[3] + slice_start * in->linesize[3]; \ | ||
| 341 | const float lut_max = lut3d->lutsize - 1; \ | ||
| 342 | const float scale_f = 1.0f / ((1<<depth) - 1); \ | ||
| 343 | const float scale_r = lut3d->scale.r * lut_max; \ | ||
| 344 | const float scale_g = lut3d->scale.g * lut_max; \ | ||
| 345 | const float scale_b = lut3d->scale.b * lut_max; \ | ||
| 346 | \ | ||
| 347 | for (y = slice_start; y < slice_end; y++) { \ | ||
| 348 | uint##nbits##_t *dstg = (uint##nbits##_t *)grow; \ | ||
| 349 | uint##nbits##_t *dstb = (uint##nbits##_t *)brow; \ | ||
| 350 | uint##nbits##_t *dstr = (uint##nbits##_t *)rrow; \ | ||
| 351 | uint##nbits##_t *dsta = (uint##nbits##_t *)arow; \ | ||
| 352 | const uint##nbits##_t *srcg = (const uint##nbits##_t *)srcgrow; \ | ||
| 353 | const uint##nbits##_t *srcb = (const uint##nbits##_t *)srcbrow; \ | ||
| 354 | const uint##nbits##_t *srcr = (const uint##nbits##_t *)srcrrow; \ | ||
| 355 | const uint##nbits##_t *srca = (const uint##nbits##_t *)srcarow; \ | ||
| 356 | for (x = 0; x < in->width; x++) { \ | ||
| 357 | const struct rgbvec rgb = {srcr[x] * scale_f, \ | ||
| 358 | srcg[x] * scale_f, \ | ||
| 359 | srcb[x] * scale_f}; \ | ||
| 360 | const struct rgbvec prelut_rgb = apply_prelut(prelut, &rgb); \ | ||
| 361 | const struct rgbvec scaled_rgb = {av_clipf(prelut_rgb.r * scale_r, 0, lut_max), \ | ||
| 362 | av_clipf(prelut_rgb.g * scale_g, 0, lut_max), \ | ||
| 363 | av_clipf(prelut_rgb.b * scale_b, 0, lut_max)}; \ | ||
| 364 | struct rgbvec vec = interp_##name(lut3d, &scaled_rgb); \ | ||
| 365 | dstr[x] = av_clip_uintp2(vec.r * (float)((1<<depth) - 1), depth); \ | ||
| 366 | dstg[x] = av_clip_uintp2(vec.g * (float)((1<<depth) - 1), depth); \ | ||
| 367 | dstb[x] = av_clip_uintp2(vec.b * (float)((1<<depth) - 1), depth); \ | ||
| 368 | if (!direct && in->linesize[3]) \ | ||
| 369 | dsta[x] = srca[x]; \ | ||
| 370 | } \ | ||
| 371 | grow += out->linesize[0]; \ | ||
| 372 | brow += out->linesize[1]; \ | ||
| 373 | rrow += out->linesize[2]; \ | ||
| 374 | arow += out->linesize[3]; \ | ||
| 375 | srcgrow += in->linesize[0]; \ | ||
| 376 | srcbrow += in->linesize[1]; \ | ||
| 377 | srcrrow += in->linesize[2]; \ | ||
| 378 | srcarow += in->linesize[3]; \ | ||
| 379 | } \ | ||
| 380 | return 0; \ | ||
| 381 | } | ||
| 382 | |||
| 383 | ✗ | DEFINE_INTERP_FUNC_PLANAR(nearest, 8, 8) | |
| 384 | ✗ | DEFINE_INTERP_FUNC_PLANAR(trilinear, 8, 8) | |
| 385 | ✗ | DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 8, 8) | |
| 386 | ✗ | DEFINE_INTERP_FUNC_PLANAR(pyramid, 8, 8) | |
| 387 | ✗ | DEFINE_INTERP_FUNC_PLANAR(prism, 8, 8) | |
| 388 | |||
| 389 | ✗ | DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 9) | |
| 390 | ✗ | DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 9) | |
| 391 | ✗ | DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 9) | |
| 392 | ✗ | DEFINE_INTERP_FUNC_PLANAR(pyramid, 16, 9) | |
| 393 | ✗ | DEFINE_INTERP_FUNC_PLANAR(prism, 16, 9) | |
| 394 | |||
| 395 | ✗ | DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 10) | |
| 396 | ✗ | DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 10) | |
| 397 | ✗ | DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 10) | |
| 398 | ✗ | DEFINE_INTERP_FUNC_PLANAR(pyramid, 16, 10) | |
| 399 | ✗ | DEFINE_INTERP_FUNC_PLANAR(prism, 16, 10) | |
| 400 | |||
| 401 | ✗ | DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 12) | |
| 402 | ✗ | DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 12) | |
| 403 | ✗ | DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 12) | |
| 404 | ✗ | DEFINE_INTERP_FUNC_PLANAR(pyramid, 16, 12) | |
| 405 | ✗ | DEFINE_INTERP_FUNC_PLANAR(prism, 16, 12) | |
| 406 | |||
| 407 | ✗ | DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 14) | |
| 408 | ✗ | DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 14) | |
| 409 | ✗ | DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 14) | |
| 410 | ✗ | DEFINE_INTERP_FUNC_PLANAR(pyramid, 16, 14) | |
| 411 | ✗ | DEFINE_INTERP_FUNC_PLANAR(prism, 16, 14) | |
| 412 | |||
| 413 | ✗ | DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 16) | |
| 414 | ✗ | DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 16) | |
| 415 | ✗ | DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 16) | |
| 416 | ✗ | DEFINE_INTERP_FUNC_PLANAR(pyramid, 16, 16) | |
| 417 | ✗ | DEFINE_INTERP_FUNC_PLANAR(prism, 16, 16) | |
| 418 | |||
| 419 | #define DEFINE_INTERP_FUNC_PLANAR_FLOAT(name, depth) \ | ||
| 420 | static int interp_##name##_pf##depth(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \ | ||
| 421 | { \ | ||
| 422 | int x, y; \ | ||
| 423 | const LUT3DContext *lut3d = ctx->priv; \ | ||
| 424 | const Lut3DPreLut *prelut = &lut3d->prelut; \ | ||
| 425 | const ThreadData *td = arg; \ | ||
| 426 | const AVFrame *in = td->in; \ | ||
| 427 | const AVFrame *out = td->out; \ | ||
| 428 | const int direct = out == in; \ | ||
| 429 | const int slice_start = (in->height * jobnr ) / nb_jobs; \ | ||
| 430 | const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \ | ||
| 431 | uint8_t *grow = out->data[0] + slice_start * out->linesize[0]; \ | ||
| 432 | uint8_t *brow = out->data[1] + slice_start * out->linesize[1]; \ | ||
| 433 | uint8_t *rrow = out->data[2] + slice_start * out->linesize[2]; \ | ||
| 434 | uint8_t *arow = out->data[3] + slice_start * out->linesize[3]; \ | ||
| 435 | const uint8_t *srcgrow = in->data[0] + slice_start * in->linesize[0]; \ | ||
| 436 | const uint8_t *srcbrow = in->data[1] + slice_start * in->linesize[1]; \ | ||
| 437 | const uint8_t *srcrrow = in->data[2] + slice_start * in->linesize[2]; \ | ||
| 438 | const uint8_t *srcarow = in->data[3] + slice_start * in->linesize[3]; \ | ||
| 439 | const float lut_max = lut3d->lutsize - 1; \ | ||
| 440 | const float scale_r = lut3d->scale.r * lut_max; \ | ||
| 441 | const float scale_g = lut3d->scale.g * lut_max; \ | ||
| 442 | const float scale_b = lut3d->scale.b * lut_max; \ | ||
| 443 | \ | ||
| 444 | for (y = slice_start; y < slice_end; y++) { \ | ||
| 445 | float *dstg = (float *)grow; \ | ||
| 446 | float *dstb = (float *)brow; \ | ||
| 447 | float *dstr = (float *)rrow; \ | ||
| 448 | float *dsta = (float *)arow; \ | ||
| 449 | const float *srcg = (const float *)srcgrow; \ | ||
| 450 | const float *srcb = (const float *)srcbrow; \ | ||
| 451 | const float *srcr = (const float *)srcrrow; \ | ||
| 452 | const float *srca = (const float *)srcarow; \ | ||
| 453 | for (x = 0; x < in->width; x++) { \ | ||
| 454 | const struct rgbvec rgb = {sanitizef(srcr[x]), \ | ||
| 455 | sanitizef(srcg[x]), \ | ||
| 456 | sanitizef(srcb[x])}; \ | ||
| 457 | const struct rgbvec prelut_rgb = apply_prelut(prelut, &rgb); \ | ||
| 458 | const struct rgbvec scaled_rgb = {av_clipf(prelut_rgb.r * scale_r, 0, lut_max), \ | ||
| 459 | av_clipf(prelut_rgb.g * scale_g, 0, lut_max), \ | ||
| 460 | av_clipf(prelut_rgb.b * scale_b, 0, lut_max)}; \ | ||
| 461 | struct rgbvec vec = interp_##name(lut3d, &scaled_rgb); \ | ||
| 462 | dstr[x] = vec.r; \ | ||
| 463 | dstg[x] = vec.g; \ | ||
| 464 | dstb[x] = vec.b; \ | ||
| 465 | if (!direct && in->linesize[3]) \ | ||
| 466 | dsta[x] = srca[x]; \ | ||
| 467 | } \ | ||
| 468 | grow += out->linesize[0]; \ | ||
| 469 | brow += out->linesize[1]; \ | ||
| 470 | rrow += out->linesize[2]; \ | ||
| 471 | arow += out->linesize[3]; \ | ||
| 472 | srcgrow += in->linesize[0]; \ | ||
| 473 | srcbrow += in->linesize[1]; \ | ||
| 474 | srcrrow += in->linesize[2]; \ | ||
| 475 | srcarow += in->linesize[3]; \ | ||
| 476 | } \ | ||
| 477 | return 0; \ | ||
| 478 | } | ||
| 479 | |||
| 480 | ✗ | DEFINE_INTERP_FUNC_PLANAR_FLOAT(nearest, 32) | |
| 481 | ✗ | DEFINE_INTERP_FUNC_PLANAR_FLOAT(trilinear, 32) | |
| 482 | ✗ | DEFINE_INTERP_FUNC_PLANAR_FLOAT(tetrahedral, 32) | |
| 483 | ✗ | DEFINE_INTERP_FUNC_PLANAR_FLOAT(pyramid, 32) | |
| 484 | ✗ | DEFINE_INTERP_FUNC_PLANAR_FLOAT(prism, 32) | |
| 485 | |||
| 486 | #define DEFINE_INTERP_FUNC(name, nbits) \ | ||
| 487 | static int interp_##nbits##_##name(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \ | ||
| 488 | { \ | ||
| 489 | int x, y; \ | ||
| 490 | const LUT3DContext *lut3d = ctx->priv; \ | ||
| 491 | const Lut3DPreLut *prelut = &lut3d->prelut; \ | ||
| 492 | const ThreadData *td = arg; \ | ||
| 493 | const AVFrame *in = td->in; \ | ||
| 494 | const AVFrame *out = td->out; \ | ||
| 495 | const int direct = out == in; \ | ||
| 496 | const int step = lut3d->step; \ | ||
| 497 | const uint8_t r = lut3d->rgba_map[R]; \ | ||
| 498 | const uint8_t g = lut3d->rgba_map[G]; \ | ||
| 499 | const uint8_t b = lut3d->rgba_map[B]; \ | ||
| 500 | const uint8_t a = lut3d->rgba_map[A]; \ | ||
| 501 | const int slice_start = (in->height * jobnr ) / nb_jobs; \ | ||
| 502 | const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \ | ||
| 503 | uint8_t *dstrow = out->data[0] + slice_start * out->linesize[0]; \ | ||
| 504 | const uint8_t *srcrow = in ->data[0] + slice_start * in ->linesize[0]; \ | ||
| 505 | const float lut_max = lut3d->lutsize - 1; \ | ||
| 506 | const float scale_f = 1.0f / ((1<<nbits) - 1); \ | ||
| 507 | const float scale_r = lut3d->scale.r * lut_max; \ | ||
| 508 | const float scale_g = lut3d->scale.g * lut_max; \ | ||
| 509 | const float scale_b = lut3d->scale.b * lut_max; \ | ||
| 510 | \ | ||
| 511 | for (y = slice_start; y < slice_end; y++) { \ | ||
| 512 | uint##nbits##_t *dst = (uint##nbits##_t *)dstrow; \ | ||
| 513 | const uint##nbits##_t *src = (const uint##nbits##_t *)srcrow; \ | ||
| 514 | for (x = 0; x < in->width * step; x += step) { \ | ||
| 515 | const struct rgbvec rgb = {src[x + r] * scale_f, \ | ||
| 516 | src[x + g] * scale_f, \ | ||
| 517 | src[x + b] * scale_f}; \ | ||
| 518 | const struct rgbvec prelut_rgb = apply_prelut(prelut, &rgb); \ | ||
| 519 | const struct rgbvec scaled_rgb = {av_clipf(prelut_rgb.r * scale_r, 0, lut_max), \ | ||
| 520 | av_clipf(prelut_rgb.g * scale_g, 0, lut_max), \ | ||
| 521 | av_clipf(prelut_rgb.b * scale_b, 0, lut_max)}; \ | ||
| 522 | struct rgbvec vec = interp_##name(lut3d, &scaled_rgb); \ | ||
| 523 | dst[x + r] = av_clip_uint##nbits(vec.r * (float)((1<<nbits) - 1)); \ | ||
| 524 | dst[x + g] = av_clip_uint##nbits(vec.g * (float)((1<<nbits) - 1)); \ | ||
| 525 | dst[x + b] = av_clip_uint##nbits(vec.b * (float)((1<<nbits) - 1)); \ | ||
| 526 | if (!direct && step == 4) \ | ||
| 527 | dst[x + a] = src[x + a]; \ | ||
| 528 | } \ | ||
| 529 | dstrow += out->linesize[0]; \ | ||
| 530 | srcrow += in ->linesize[0]; \ | ||
| 531 | } \ | ||
| 532 | return 0; \ | ||
| 533 | } | ||
| 534 | |||
| 535 | ✗ | DEFINE_INTERP_FUNC(nearest, 8) | |
| 536 | ✗ | DEFINE_INTERP_FUNC(trilinear, 8) | |
| 537 | ✗ | DEFINE_INTERP_FUNC(tetrahedral, 8) | |
| 538 | ✗ | DEFINE_INTERP_FUNC(pyramid, 8) | |
| 539 | ✗ | DEFINE_INTERP_FUNC(prism, 8) | |
| 540 | |||
| 541 | ✗ | DEFINE_INTERP_FUNC(nearest, 16) | |
| 542 | ✗ | DEFINE_INTERP_FUNC(trilinear, 16) | |
| 543 | ✗ | DEFINE_INTERP_FUNC(tetrahedral, 16) | |
| 544 | ✗ | DEFINE_INTERP_FUNC(pyramid, 16) | |
| 545 | ✗ | DEFINE_INTERP_FUNC(prism, 16) | |
| 546 | |||
| 547 | #define MAX_LINE_SIZE 512 | ||
| 548 | |||
| 549 | ✗ | static int skip_line(const char *p) | |
| 550 | { | ||
| 551 | ✗ | while (*p && av_isspace(*p)) | |
| 552 | ✗ | p++; | |
| 553 | ✗ | return !*p || *p == '#'; | |
| 554 | } | ||
| 555 | |||
| 556 | ✗ | static char* fget_next_word(char* dst, int max, FILE* f) | |
| 557 | { | ||
| 558 | int c; | ||
| 559 | ✗ | char *p = dst; | |
| 560 | |||
| 561 | /* for null */ | ||
| 562 | ✗ | max--; | |
| 563 | /* skip until next non whitespace char */ | ||
| 564 | ✗ | while ((c = fgetc(f)) != EOF) { | |
| 565 | ✗ | if (av_isspace(c)) | |
| 566 | ✗ | continue; | |
| 567 | |||
| 568 | ✗ | *p++ = c; | |
| 569 | ✗ | max--; | |
| 570 | ✗ | break; | |
| 571 | } | ||
| 572 | |||
| 573 | /* get max bytes or up until next whitespace char */ | ||
| 574 | ✗ | for (; max > 0; max--) { | |
| 575 | ✗ | if ((c = fgetc(f)) == EOF) | |
| 576 | ✗ | break; | |
| 577 | |||
| 578 | ✗ | if (av_isspace(c)) | |
| 579 | ✗ | break; | |
| 580 | |||
| 581 | ✗ | *p++ = c; | |
| 582 | } | ||
| 583 | |||
| 584 | ✗ | *p = 0; | |
| 585 | ✗ | if (p == dst) | |
| 586 | ✗ | return NULL; | |
| 587 | ✗ | return p; | |
| 588 | } | ||
| 589 | |||
| 590 | #define NEXT_LINE(loop_cond) do { \ | ||
| 591 | if (!fgets(line, sizeof(line), f)) { \ | ||
| 592 | av_log(ctx, AV_LOG_ERROR, "Unexpected EOF\n"); \ | ||
| 593 | return AVERROR_INVALIDDATA; \ | ||
| 594 | } \ | ||
| 595 | } while (loop_cond) | ||
| 596 | |||
| 597 | #define NEXT_LINE_OR_GOTO(loop_cond, label) do { \ | ||
| 598 | if (!fgets(line, sizeof(line), f)) { \ | ||
| 599 | av_log(ctx, AV_LOG_ERROR, "Unexpected EOF\n"); \ | ||
| 600 | ret = AVERROR_INVALIDDATA; \ | ||
| 601 | goto label; \ | ||
| 602 | } \ | ||
| 603 | } while (loop_cond) | ||
| 604 | |||
| 605 | ✗ | static int allocate_3dlut(AVFilterContext *ctx, int lutsize, int prelut) | |
| 606 | { | ||
| 607 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 608 | int i; | ||
| 609 | ✗ | if (lutsize < 2 || lutsize > MAX_LEVEL) { | |
| 610 | ✗ | av_log(ctx, AV_LOG_ERROR, "Too large or invalid 3D LUT size\n"); | |
| 611 | ✗ | return AVERROR(EINVAL); | |
| 612 | } | ||
| 613 | |||
| 614 | ✗ | av_freep(&lut3d->lut); | |
| 615 | ✗ | lut3d->lut = av_malloc_array(lutsize * lutsize * lutsize, sizeof(*lut3d->lut)); | |
| 616 | ✗ | if (!lut3d->lut) | |
| 617 | ✗ | return AVERROR(ENOMEM); | |
| 618 | |||
| 619 | ✗ | if (prelut) { | |
| 620 | ✗ | lut3d->prelut.size = PRELUT_SIZE; | |
| 621 | ✗ | for (i = 0; i < 3; i++) { | |
| 622 | ✗ | av_freep(&lut3d->prelut.lut[i]); | |
| 623 | ✗ | lut3d->prelut.lut[i] = av_malloc_array(PRELUT_SIZE, sizeof(*lut3d->prelut.lut[0])); | |
| 624 | ✗ | if (!lut3d->prelut.lut[i]) | |
| 625 | ✗ | return AVERROR(ENOMEM); | |
| 626 | } | ||
| 627 | } else { | ||
| 628 | ✗ | lut3d->prelut.size = 0; | |
| 629 | ✗ | for (i = 0; i < 3; i++) { | |
| 630 | ✗ | av_freep(&lut3d->prelut.lut[i]); | |
| 631 | } | ||
| 632 | } | ||
| 633 | ✗ | lut3d->lutsize = lutsize; | |
| 634 | ✗ | lut3d->lutsize2 = lutsize * lutsize; | |
| 635 | ✗ | return 0; | |
| 636 | } | ||
| 637 | |||
| 638 | /* Basically r g and b float values on each line, with a facultative 3DLUTSIZE | ||
| 639 | * directive; seems to be generated by Davinci */ | ||
| 640 | ✗ | static int parse_dat(AVFilterContext *ctx, FILE *f) | |
| 641 | { | ||
| 642 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 643 | char line[MAX_LINE_SIZE]; | ||
| 644 | int ret, i, j, k, size, size2; | ||
| 645 | |||
| 646 | ✗ | lut3d->lutsize = size = 33; | |
| 647 | ✗ | size2 = size * size; | |
| 648 | |||
| 649 | ✗ | NEXT_LINE(skip_line(line)); | |
| 650 | ✗ | if (!strncmp(line, "3DLUTSIZE ", 10)) { | |
| 651 | ✗ | size = strtol(line + 10, NULL, 0); | |
| 652 | |||
| 653 | ✗ | NEXT_LINE(skip_line(line)); | |
| 654 | } | ||
| 655 | |||
| 656 | ✗ | ret = allocate_3dlut(ctx, size, 0); | |
| 657 | ✗ | if (ret < 0) | |
| 658 | ✗ | return ret; | |
| 659 | |||
| 660 | ✗ | for (k = 0; k < size; k++) { | |
| 661 | ✗ | for (j = 0; j < size; j++) { | |
| 662 | ✗ | for (i = 0; i < size; i++) { | |
| 663 | ✗ | struct rgbvec *vec = &lut3d->lut[k * size2 + j * size + i]; | |
| 664 | ✗ | if (k != 0 || j != 0 || i != 0) | |
| 665 | ✗ | NEXT_LINE(skip_line(line)); | |
| 666 | ✗ | if (av_sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != 3) | |
| 667 | ✗ | return AVERROR_INVALIDDATA; | |
| 668 | } | ||
| 669 | } | ||
| 670 | } | ||
| 671 | ✗ | return 0; | |
| 672 | } | ||
| 673 | |||
| 674 | /* Iridas format */ | ||
| 675 | ✗ | static int parse_cube(AVFilterContext *ctx, FILE *f) | |
| 676 | { | ||
| 677 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 678 | char line[MAX_LINE_SIZE]; | ||
| 679 | ✗ | float min[3] = {0.0, 0.0, 0.0}; | |
| 680 | ✗ | float max[3] = {1.0, 1.0, 1.0}; | |
| 681 | |||
| 682 | ✗ | while (fgets(line, sizeof(line), f)) { | |
| 683 | ✗ | if (!strncmp(line, "LUT_3D_SIZE", 11)) { | |
| 684 | int ret, i, j, k; | ||
| 685 | ✗ | const int size = strtol(line + 12, NULL, 0); | |
| 686 | ✗ | const int size2 = size * size; | |
| 687 | |||
| 688 | ✗ | ret = allocate_3dlut(ctx, size, 0); | |
| 689 | ✗ | if (ret < 0) | |
| 690 | ✗ | return ret; | |
| 691 | |||
| 692 | ✗ | for (k = 0; k < size; k++) { | |
| 693 | ✗ | for (j = 0; j < size; j++) { | |
| 694 | ✗ | for (i = 0; i < size; i++) { | |
| 695 | ✗ | struct rgbvec *vec = &lut3d->lut[i * size2 + j * size + k]; | |
| 696 | |||
| 697 | do { | ||
| 698 | ✗ | try_again: | |
| 699 | ✗ | NEXT_LINE(0); | |
| 700 | ✗ | if (!strncmp(line, "DOMAIN_", 7)) { | |
| 701 | ✗ | float *vals = NULL; | |
| 702 | ✗ | if (!strncmp(line + 7, "MIN ", 4)) vals = min; | |
| 703 | ✗ | else if (!strncmp(line + 7, "MAX ", 4)) vals = max; | |
| 704 | ✗ | if (!vals) | |
| 705 | ✗ | return AVERROR_INVALIDDATA; | |
| 706 | ✗ | if (av_sscanf(line + 11, "%f %f %f", vals, vals + 1, vals + 2) != 3) | |
| 707 | ✗ | return AVERROR_INVALIDDATA; | |
| 708 | ✗ | av_log(ctx, AV_LOG_DEBUG, "min: %f %f %f | max: %f %f %f\n", | |
| 709 | ✗ | min[0], min[1], min[2], max[0], max[1], max[2]); | |
| 710 | ✗ | goto try_again; | |
| 711 | ✗ | } else if (!strncmp(line, "TITLE", 5)) { | |
| 712 | ✗ | goto try_again; | |
| 713 | } | ||
| 714 | ✗ | } while (skip_line(line)); | |
| 715 | ✗ | if (av_sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != 3) | |
| 716 | ✗ | return AVERROR_INVALIDDATA; | |
| 717 | } | ||
| 718 | } | ||
| 719 | } | ||
| 720 | ✗ | break; | |
| 721 | } | ||
| 722 | } | ||
| 723 | |||
| 724 | ✗ | lut3d->scale.r = av_clipf(1. / (max[0] - min[0]), 0.f, 1.f); | |
| 725 | ✗ | lut3d->scale.g = av_clipf(1. / (max[1] - min[1]), 0.f, 1.f); | |
| 726 | ✗ | lut3d->scale.b = av_clipf(1. / (max[2] - min[2]), 0.f, 1.f); | |
| 727 | |||
| 728 | ✗ | return 0; | |
| 729 | } | ||
| 730 | |||
| 731 | /* Assume 17x17x17 LUT with a 16-bit depth | ||
| 732 | * FIXME: it seems there are various 3dl formats */ | ||
| 733 | ✗ | static int parse_3dl(AVFilterContext *ctx, FILE *f) | |
| 734 | { | ||
| 735 | char line[MAX_LINE_SIZE]; | ||
| 736 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 737 | int ret, i, j, k; | ||
| 738 | ✗ | const int size = 17; | |
| 739 | ✗ | const int size2 = 17 * 17; | |
| 740 | ✗ | const float scale = 16*16*16; | |
| 741 | |||
| 742 | ✗ | lut3d->lutsize = size; | |
| 743 | |||
| 744 | ✗ | ret = allocate_3dlut(ctx, size, 0); | |
| 745 | ✗ | if (ret < 0) | |
| 746 | ✗ | return ret; | |
| 747 | |||
| 748 | ✗ | NEXT_LINE(skip_line(line)); | |
| 749 | ✗ | for (k = 0; k < size; k++) { | |
| 750 | ✗ | for (j = 0; j < size; j++) { | |
| 751 | ✗ | for (i = 0; i < size; i++) { | |
| 752 | int r, g, b; | ||
| 753 | ✗ | struct rgbvec *vec = &lut3d->lut[k * size2 + j * size + i]; | |
| 754 | |||
| 755 | ✗ | NEXT_LINE(skip_line(line)); | |
| 756 | ✗ | if (av_sscanf(line, "%d %d %d", &r, &g, &b) != 3) | |
| 757 | ✗ | return AVERROR_INVALIDDATA; | |
| 758 | ✗ | vec->r = r / scale; | |
| 759 | ✗ | vec->g = g / scale; | |
| 760 | ✗ | vec->b = b / scale; | |
| 761 | } | ||
| 762 | } | ||
| 763 | } | ||
| 764 | ✗ | return 0; | |
| 765 | } | ||
| 766 | |||
| 767 | /* Pandora format */ | ||
| 768 | ✗ | static int parse_m3d(AVFilterContext *ctx, FILE *f) | |
| 769 | { | ||
| 770 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 771 | float scale; | ||
| 772 | ✗ | int ret, i, j, k, size, size2, in = -1, out = -1; | |
| 773 | char line[MAX_LINE_SIZE]; | ||
| 774 | ✗ | uint8_t rgb_map[3] = {0, 1, 2}; | |
| 775 | |||
| 776 | ✗ | while (fgets(line, sizeof(line), f)) { | |
| 777 | ✗ | if (!strncmp(line, "in", 2)) in = strtol(line + 2, NULL, 0); | |
| 778 | ✗ | else if (!strncmp(line, "out", 3)) out = strtol(line + 3, NULL, 0); | |
| 779 | ✗ | else if (!strncmp(line, "values", 6)) { | |
| 780 | ✗ | const char *p = line + 6; | |
| 781 | #define SET_COLOR(id) do { \ | ||
| 782 | while (av_isspace(*p)) \ | ||
| 783 | p++; \ | ||
| 784 | switch (*p) { \ | ||
| 785 | case 'r': rgb_map[id] = 0; break; \ | ||
| 786 | case 'g': rgb_map[id] = 1; break; \ | ||
| 787 | case 'b': rgb_map[id] = 2; break; \ | ||
| 788 | } \ | ||
| 789 | while (*p && !av_isspace(*p)) \ | ||
| 790 | p++; \ | ||
| 791 | } while (0) | ||
| 792 | ✗ | SET_COLOR(0); | |
| 793 | ✗ | SET_COLOR(1); | |
| 794 | ✗ | SET_COLOR(2); | |
| 795 | ✗ | break; | |
| 796 | } | ||
| 797 | } | ||
| 798 | |||
| 799 | ✗ | if (in == -1 || out == -1) { | |
| 800 | ✗ | av_log(ctx, AV_LOG_ERROR, "in and out must be defined\n"); | |
| 801 | ✗ | return AVERROR_INVALIDDATA; | |
| 802 | } | ||
| 803 | ✗ | if (in < 2 || out < 2 || | |
| 804 | ✗ | in > MAX_LEVEL*MAX_LEVEL*MAX_LEVEL || | |
| 805 | out > MAX_LEVEL*MAX_LEVEL*MAX_LEVEL) { | ||
| 806 | ✗ | av_log(ctx, AV_LOG_ERROR, "invalid in (%d) or out (%d)\n", in, out); | |
| 807 | ✗ | return AVERROR_INVALIDDATA; | |
| 808 | } | ||
| 809 | ✗ | for (size = 1; size*size*size < in; size++); | |
| 810 | ✗ | lut3d->lutsize = size; | |
| 811 | ✗ | size2 = size * size; | |
| 812 | |||
| 813 | ✗ | ret = allocate_3dlut(ctx, size, 0); | |
| 814 | ✗ | if (ret < 0) | |
| 815 | ✗ | return ret; | |
| 816 | |||
| 817 | ✗ | scale = 1. / (out - 1); | |
| 818 | |||
| 819 | ✗ | for (k = 0; k < size; k++) { | |
| 820 | ✗ | for (j = 0; j < size; j++) { | |
| 821 | ✗ | for (i = 0; i < size; i++) { | |
| 822 | ✗ | struct rgbvec *vec = &lut3d->lut[k * size2 + j * size + i]; | |
| 823 | float val[3]; | ||
| 824 | |||
| 825 | ✗ | NEXT_LINE(0); | |
| 826 | ✗ | if (av_sscanf(line, "%f %f %f", val, val + 1, val + 2) != 3) | |
| 827 | ✗ | return AVERROR_INVALIDDATA; | |
| 828 | ✗ | vec->r = val[rgb_map[0]] * scale; | |
| 829 | ✗ | vec->g = val[rgb_map[1]] * scale; | |
| 830 | ✗ | vec->b = val[rgb_map[2]] * scale; | |
| 831 | } | ||
| 832 | } | ||
| 833 | } | ||
| 834 | ✗ | return 0; | |
| 835 | } | ||
| 836 | |||
| 837 | ✗ | static int nearest_sample_index(float *data, float x, int low, int hi) | |
| 838 | { | ||
| 839 | int mid; | ||
| 840 | ✗ | if (x < data[low]) | |
| 841 | ✗ | return low; | |
| 842 | |||
| 843 | ✗ | if (x > data[hi]) | |
| 844 | ✗ | return hi; | |
| 845 | |||
| 846 | for (;;) { | ||
| 847 | ✗ | av_assert0(x >= data[low]); | |
| 848 | ✗ | av_assert0(x <= data[hi]); | |
| 849 | ✗ | av_assert0((hi-low) > 0); | |
| 850 | |||
| 851 | ✗ | if (hi - low == 1) | |
| 852 | ✗ | return low; | |
| 853 | |||
| 854 | ✗ | mid = (low + hi) / 2; | |
| 855 | |||
| 856 | ✗ | if (x < data[mid]) | |
| 857 | ✗ | hi = mid; | |
| 858 | else | ||
| 859 | ✗ | low = mid; | |
| 860 | } | ||
| 861 | |||
| 862 | return 0; | ||
| 863 | } | ||
| 864 | |||
| 865 | #define NEXT_FLOAT_OR_GOTO(value, label) \ | ||
| 866 | if (!fget_next_word(line, sizeof(line) ,f)) { \ | ||
| 867 | ret = AVERROR_INVALIDDATA; \ | ||
| 868 | goto label; \ | ||
| 869 | } \ | ||
| 870 | if (av_sscanf(line, "%f", &value) != 1) { \ | ||
| 871 | ret = AVERROR_INVALIDDATA; \ | ||
| 872 | goto label; \ | ||
| 873 | } | ||
| 874 | |||
| 875 | ✗ | static int parse_cinespace(AVFilterContext *ctx, FILE *f) | |
| 876 | { | ||
| 877 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 878 | char line[MAX_LINE_SIZE]; | ||
| 879 | ✗ | float in_min[3] = {0.0, 0.0, 0.0}; | |
| 880 | ✗ | float in_max[3] = {1.0, 1.0, 1.0}; | |
| 881 | ✗ | float out_min[3] = {0.0, 0.0, 0.0}; | |
| 882 | ✗ | float out_max[3] = {1.0, 1.0, 1.0}; | |
| 883 | ✗ | int inside_metadata = 0, size, size2; | |
| 884 | ✗ | int prelut = 0; | |
| 885 | ✗ | int ret = 0; | |
| 886 | |||
| 887 | ✗ | int prelut_sizes[3] = {0, 0, 0}; | |
| 888 | ✗ | float *in_prelut[3] = {NULL, NULL, NULL}; | |
| 889 | ✗ | float *out_prelut[3] = {NULL, NULL, NULL}; | |
| 890 | |||
| 891 | ✗ | NEXT_LINE_OR_GOTO(skip_line(line), end); | |
| 892 | ✗ | if (strncmp(line, "CSPLUTV100", 10)) { | |
| 893 | ✗ | av_log(ctx, AV_LOG_ERROR, "Not cineSpace LUT format\n"); | |
| 894 | ✗ | ret = AVERROR(EINVAL); | |
| 895 | ✗ | goto end; | |
| 896 | } | ||
| 897 | |||
| 898 | ✗ | NEXT_LINE_OR_GOTO(skip_line(line), end); | |
| 899 | ✗ | if (strncmp(line, "3D", 2)) { | |
| 900 | ✗ | av_log(ctx, AV_LOG_ERROR, "Not 3D LUT format\n"); | |
| 901 | ✗ | ret = AVERROR(EINVAL); | |
| 902 | ✗ | goto end; | |
| 903 | } | ||
| 904 | |||
| 905 | ✗ | while (1) { | |
| 906 | ✗ | NEXT_LINE_OR_GOTO(skip_line(line), end); | |
| 907 | |||
| 908 | ✗ | if (!strncmp(line, "BEGIN METADATA", 14)) { | |
| 909 | ✗ | inside_metadata = 1; | |
| 910 | ✗ | continue; | |
| 911 | } | ||
| 912 | ✗ | if (!strncmp(line, "END METADATA", 12)) { | |
| 913 | ✗ | inside_metadata = 0; | |
| 914 | ✗ | continue; | |
| 915 | } | ||
| 916 | ✗ | if (inside_metadata == 0) { | |
| 917 | int size_r, size_g, size_b; | ||
| 918 | |||
| 919 | ✗ | for (int i = 0; i < 3; i++) { | |
| 920 | ✗ | int npoints = strtol(line, NULL, 0); | |
| 921 | |||
| 922 | ✗ | if (npoints > 2) { | |
| 923 | float v,last; | ||
| 924 | |||
| 925 | ✗ | if (npoints > PRELUT_SIZE) { | |
| 926 | ✗ | av_log(ctx, AV_LOG_ERROR, "Prelut size too large.\n"); | |
| 927 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 928 | ✗ | goto end; | |
| 929 | } | ||
| 930 | |||
| 931 | ✗ | if (in_prelut[i] || out_prelut[i]) { | |
| 932 | ✗ | av_log(ctx, AV_LOG_ERROR, "Invalid file has multiple preluts.\n"); | |
| 933 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 934 | ✗ | goto end; | |
| 935 | } | ||
| 936 | |||
| 937 | ✗ | in_prelut[i] = (float*)av_malloc(npoints * sizeof(float)); | |
| 938 | ✗ | out_prelut[i] = (float*)av_malloc(npoints * sizeof(float)); | |
| 939 | ✗ | if (!in_prelut[i] || !out_prelut[i]) { | |
| 940 | ✗ | ret = AVERROR(ENOMEM); | |
| 941 | ✗ | goto end; | |
| 942 | } | ||
| 943 | |||
| 944 | ✗ | prelut_sizes[i] = npoints; | |
| 945 | ✗ | in_min[i] = FLT_MAX; | |
| 946 | ✗ | in_max[i] = -FLT_MAX; | |
| 947 | ✗ | out_min[i] = FLT_MAX; | |
| 948 | ✗ | out_max[i] = -FLT_MAX; | |
| 949 | |||
| 950 | ✗ | for (int j = 0; j < npoints; j++) { | |
| 951 | ✗ | NEXT_FLOAT_OR_GOTO(v, end) | |
| 952 | ✗ | in_min[i] = FFMIN(in_min[i], v); | |
| 953 | ✗ | in_max[i] = FFMAX(in_max[i], v); | |
| 954 | ✗ | in_prelut[i][j] = v; | |
| 955 | ✗ | if (j > 0 && v < last) { | |
| 956 | ✗ | av_log(ctx, AV_LOG_ERROR, "Invalid file, non increasing prelut.\n"); | |
| 957 | ✗ | ret = AVERROR(ENOMEM); | |
| 958 | ✗ | goto end; | |
| 959 | } | ||
| 960 | ✗ | last = v; | |
| 961 | } | ||
| 962 | |||
| 963 | ✗ | for (int j = 0; j < npoints; j++) { | |
| 964 | ✗ | NEXT_FLOAT_OR_GOTO(v, end) | |
| 965 | ✗ | out_min[i] = FFMIN(out_min[i], v); | |
| 966 | ✗ | out_max[i] = FFMAX(out_max[i], v); | |
| 967 | ✗ | out_prelut[i][j] = v; | |
| 968 | } | ||
| 969 | |||
| 970 | ✗ | } else if (npoints == 2) { | |
| 971 | ✗ | NEXT_LINE_OR_GOTO(skip_line(line), end); | |
| 972 | ✗ | if (av_sscanf(line, "%f %f", &in_min[i], &in_max[i]) != 2) { | |
| 973 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 974 | ✗ | goto end; | |
| 975 | } | ||
| 976 | ✗ | NEXT_LINE_OR_GOTO(skip_line(line), end); | |
| 977 | ✗ | if (av_sscanf(line, "%f %f", &out_min[i], &out_max[i]) != 2) { | |
| 978 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 979 | ✗ | goto end; | |
| 980 | } | ||
| 981 | |||
| 982 | } else { | ||
| 983 | ✗ | av_log(ctx, AV_LOG_ERROR, "Unsupported number of pre-lut points.\n"); | |
| 984 | ✗ | ret = AVERROR_PATCHWELCOME; | |
| 985 | ✗ | goto end; | |
| 986 | } | ||
| 987 | |||
| 988 | ✗ | NEXT_LINE_OR_GOTO(skip_line(line), end); | |
| 989 | } | ||
| 990 | |||
| 991 | ✗ | if (av_sscanf(line, "%d %d %d", &size_r, &size_g, &size_b) != 3) { | |
| 992 | ✗ | ret = AVERROR(EINVAL); | |
| 993 | ✗ | goto end; | |
| 994 | } | ||
| 995 | ✗ | if (size_r != size_g || size_r != size_b) { | |
| 996 | ✗ | av_log(ctx, AV_LOG_ERROR, "Unsupported size combination: %dx%dx%d.\n", size_r, size_g, size_b); | |
| 997 | ✗ | ret = AVERROR_PATCHWELCOME; | |
| 998 | ✗ | goto end; | |
| 999 | } | ||
| 1000 | |||
| 1001 | ✗ | size = size_r; | |
| 1002 | ✗ | size2 = size * size; | |
| 1003 | |||
| 1004 | ✗ | if (prelut_sizes[0] && prelut_sizes[1] && prelut_sizes[2]) | |
| 1005 | ✗ | prelut = 1; | |
| 1006 | |||
| 1007 | ✗ | ret = allocate_3dlut(ctx, size, prelut); | |
| 1008 | ✗ | if (ret < 0) | |
| 1009 | ✗ | goto end; | |
| 1010 | |||
| 1011 | ✗ | for (int k = 0; k < size; k++) { | |
| 1012 | ✗ | for (int j = 0; j < size; j++) { | |
| 1013 | ✗ | for (int i = 0; i < size; i++) { | |
| 1014 | ✗ | struct rgbvec *vec = &lut3d->lut[i * size2 + j * size + k]; | |
| 1015 | |||
| 1016 | ✗ | NEXT_LINE_OR_GOTO(skip_line(line), end); | |
| 1017 | ✗ | if (av_sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != 3) { | |
| 1018 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 1019 | ✗ | goto end; | |
| 1020 | } | ||
| 1021 | |||
| 1022 | ✗ | vec->r *= out_max[0] - out_min[0]; | |
| 1023 | ✗ | vec->g *= out_max[1] - out_min[1]; | |
| 1024 | ✗ | vec->b *= out_max[2] - out_min[2]; | |
| 1025 | } | ||
| 1026 | } | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | ✗ | break; | |
| 1030 | } | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | ✗ | if (prelut) { | |
| 1034 | ✗ | for (int c = 0; c < 3; c++) { | |
| 1035 | |||
| 1036 | ✗ | lut3d->prelut.min[c] = in_min[c]; | |
| 1037 | ✗ | lut3d->prelut.max[c] = in_max[c]; | |
| 1038 | ✗ | lut3d->prelut.scale[c] = (1.0f / (float)(in_max[c] - in_min[c])) * (lut3d->prelut.size - 1); | |
| 1039 | |||
| 1040 | ✗ | for (int i = 0; i < lut3d->prelut.size; ++i) { | |
| 1041 | ✗ | float mix = (float) i / (float)(lut3d->prelut.size - 1); | |
| 1042 | ✗ | float x = lerpf(in_min[c], in_max[c], mix), a, b; | |
| 1043 | |||
| 1044 | ✗ | int idx = nearest_sample_index(in_prelut[c], x, 0, prelut_sizes[c]-1); | |
| 1045 | ✗ | av_assert0(idx + 1 < prelut_sizes[c]); | |
| 1046 | |||
| 1047 | ✗ | a = out_prelut[c][idx + 0]; | |
| 1048 | ✗ | b = out_prelut[c][idx + 1]; | |
| 1049 | ✗ | mix = x - in_prelut[c][idx]; | |
| 1050 | |||
| 1051 | ✗ | lut3d->prelut.lut[c][i] = sanitizef(lerpf(a, b, mix)); | |
| 1052 | } | ||
| 1053 | } | ||
| 1054 | ✗ | lut3d->scale.r = 1.00f; | |
| 1055 | ✗ | lut3d->scale.g = 1.00f; | |
| 1056 | ✗ | lut3d->scale.b = 1.00f; | |
| 1057 | |||
| 1058 | } else { | ||
| 1059 | ✗ | lut3d->scale.r = av_clipf(1. / (in_max[0] - in_min[0]), 0.f, 1.f); | |
| 1060 | ✗ | lut3d->scale.g = av_clipf(1. / (in_max[1] - in_min[1]), 0.f, 1.f); | |
| 1061 | ✗ | lut3d->scale.b = av_clipf(1. / (in_max[2] - in_min[2]), 0.f, 1.f); | |
| 1062 | } | ||
| 1063 | |||
| 1064 | ✗ | end: | |
| 1065 | ✗ | for (int c = 0; c < 3; c++) { | |
| 1066 | ✗ | av_freep(&in_prelut[c]); | |
| 1067 | ✗ | av_freep(&out_prelut[c]); | |
| 1068 | } | ||
| 1069 | ✗ | return ret; | |
| 1070 | } | ||
| 1071 | |||
| 1072 | ✗ | static int set_identity_matrix(AVFilterContext *ctx, int size) | |
| 1073 | { | ||
| 1074 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 1075 | int ret, i, j, k; | ||
| 1076 | ✗ | const int size2 = size * size; | |
| 1077 | ✗ | const float c = 1. / (size - 1); | |
| 1078 | |||
| 1079 | ✗ | ret = allocate_3dlut(ctx, size, 0); | |
| 1080 | ✗ | if (ret < 0) | |
| 1081 | ✗ | return ret; | |
| 1082 | |||
| 1083 | ✗ | for (k = 0; k < size; k++) { | |
| 1084 | ✗ | for (j = 0; j < size; j++) { | |
| 1085 | ✗ | for (i = 0; i < size; i++) { | |
| 1086 | ✗ | struct rgbvec *vec = &lut3d->lut[k * size2 + j * size + i]; | |
| 1087 | ✗ | vec->r = k * c; | |
| 1088 | ✗ | vec->g = j * c; | |
| 1089 | ✗ | vec->b = i * c; | |
| 1090 | } | ||
| 1091 | } | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | ✗ | return 0; | |
| 1095 | } | ||
| 1096 | |||
| 1097 | static const enum AVPixelFormat pix_fmts[] = { | ||
| 1098 | AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, | ||
| 1099 | AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, | ||
| 1100 | AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, | ||
| 1101 | AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR, | ||
| 1102 | AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0, | ||
| 1103 | AV_PIX_FMT_RGB48, AV_PIX_FMT_BGR48, | ||
| 1104 | AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64, | ||
| 1105 | AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, | ||
| 1106 | AV_PIX_FMT_GBRP9, | ||
| 1107 | AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, | ||
| 1108 | AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, | ||
| 1109 | AV_PIX_FMT_GBRP14, | ||
| 1110 | AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, | ||
| 1111 | AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32, | ||
| 1112 | AV_PIX_FMT_NONE | ||
| 1113 | }; | ||
| 1114 | |||
| 1115 | #if CONFIG_LUT3D_FILTER || CONFIG_HALDCLUT_FILTER | ||
| 1116 | |||
| 1117 | ✗ | static int config_input(AVFilterLink *inlink) | |
| 1118 | { | ||
| 1119 | int depth, is16bit, isfloat, planar; | ||
| 1120 | ✗ | LUT3DContext *lut3d = inlink->dst->priv; | |
| 1121 | ✗ | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); | |
| 1122 | |||
| 1123 | ✗ | depth = desc->comp[0].depth; | |
| 1124 | ✗ | is16bit = desc->comp[0].depth > 8; | |
| 1125 | ✗ | planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR; | |
| 1126 | ✗ | isfloat = desc->flags & AV_PIX_FMT_FLAG_FLOAT; | |
| 1127 | ✗ | ff_fill_rgba_map(lut3d->rgba_map, inlink->format); | |
| 1128 | ✗ | lut3d->step = av_get_padded_bits_per_pixel(desc) >> (3 + is16bit); | |
| 1129 | |||
| 1130 | #define SET_FUNC(name) do { \ | ||
| 1131 | if (planar && !isfloat) { \ | ||
| 1132 | switch (depth) { \ | ||
| 1133 | case 8: lut3d->interp = interp_8_##name##_p8; break; \ | ||
| 1134 | case 9: lut3d->interp = interp_16_##name##_p9; break; \ | ||
| 1135 | case 10: lut3d->interp = interp_16_##name##_p10; break; \ | ||
| 1136 | case 12: lut3d->interp = interp_16_##name##_p12; break; \ | ||
| 1137 | case 14: lut3d->interp = interp_16_##name##_p14; break; \ | ||
| 1138 | case 16: lut3d->interp = interp_16_##name##_p16; break; \ | ||
| 1139 | } \ | ||
| 1140 | } else if (isfloat) { lut3d->interp = interp_##name##_pf32; \ | ||
| 1141 | } else if (is16bit) { lut3d->interp = interp_16_##name; \ | ||
| 1142 | } else { lut3d->interp = interp_8_##name; } \ | ||
| 1143 | } while (0) | ||
| 1144 | |||
| 1145 | ✗ | switch (lut3d->interpolation) { | |
| 1146 | ✗ | case INTERPOLATE_NEAREST: SET_FUNC(nearest); break; | |
| 1147 | ✗ | case INTERPOLATE_TRILINEAR: SET_FUNC(trilinear); break; | |
| 1148 | ✗ | case INTERPOLATE_TETRAHEDRAL: SET_FUNC(tetrahedral); break; | |
| 1149 | ✗ | case INTERPOLATE_PYRAMID: SET_FUNC(pyramid); break; | |
| 1150 | ✗ | case INTERPOLATE_PRISM: SET_FUNC(prism); break; | |
| 1151 | ✗ | default: | |
| 1152 | ✗ | av_assert0(0); | |
| 1153 | } | ||
| 1154 | |||
| 1155 | #if ARCH_X86 && HAVE_X86ASM | ||
| 1156 | ✗ | ff_lut3d_init_x86(lut3d, desc); | |
| 1157 | #endif | ||
| 1158 | |||
| 1159 | ✗ | return 0; | |
| 1160 | } | ||
| 1161 | |||
| 1162 | ✗ | static AVFrame *apply_lut(AVFilterLink *inlink, AVFrame *in) | |
| 1163 | { | ||
| 1164 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 1165 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 1166 | ✗ | AVFilterLink *outlink = inlink->dst->outputs[0]; | |
| 1167 | AVFrame *out; | ||
| 1168 | ThreadData td; | ||
| 1169 | |||
| 1170 | ✗ | if (av_frame_is_writable(in)) { | |
| 1171 | ✗ | out = in; | |
| 1172 | } else { | ||
| 1173 | ✗ | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
| 1174 | ✗ | if (!out) { | |
| 1175 | ✗ | av_frame_free(&in); | |
| 1176 | ✗ | return NULL; | |
| 1177 | } | ||
| 1178 | ✗ | av_frame_copy_props(out, in); | |
| 1179 | } | ||
| 1180 | |||
| 1181 | ✗ | av_frame_side_data_remove_by_props(&out->side_data, &out->nb_side_data, | |
| 1182 | AV_SIDE_DATA_PROP_COLOR_DEPENDENT); | ||
| 1183 | |||
| 1184 | ✗ | td.in = in; | |
| 1185 | ✗ | td.out = out; | |
| 1186 | ✗ | ff_filter_execute(ctx, lut3d->interp, &td, NULL, | |
| 1187 | ✗ | FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); | |
| 1188 | |||
| 1189 | ✗ | if (out != in) | |
| 1190 | ✗ | av_frame_free(&in); | |
| 1191 | |||
| 1192 | ✗ | return out; | |
| 1193 | } | ||
| 1194 | |||
| 1195 | ✗ | static int filter_frame(AVFilterLink *inlink, AVFrame *in) | |
| 1196 | { | ||
| 1197 | ✗ | AVFilterLink *outlink = inlink->dst->outputs[0]; | |
| 1198 | ✗ | AVFrame *out = apply_lut(inlink, in); | |
| 1199 | ✗ | if (!out) | |
| 1200 | ✗ | return AVERROR(ENOMEM); | |
| 1201 | ✗ | return ff_filter_frame(outlink, out); | |
| 1202 | } | ||
| 1203 | |||
| 1204 | ✗ | static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, | |
| 1205 | char *res, int res_len, int flags) | ||
| 1206 | { | ||
| 1207 | int ret; | ||
| 1208 | |||
| 1209 | ✗ | ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); | |
| 1210 | ✗ | if (ret < 0) | |
| 1211 | ✗ | return ret; | |
| 1212 | |||
| 1213 | ✗ | return config_input(ctx->inputs[0]); | |
| 1214 | } | ||
| 1215 | |||
| 1216 | /* These options are shared between several filters; | ||
| 1217 | * &lut3d_haldclut_options[COMMON_OPTIONS_OFFSET] must always | ||
| 1218 | * point to the first of the COMMON_OPTIONS. */ | ||
| 1219 | #define COMMON_OPTIONS_OFFSET CONFIG_LUT3D_FILTER | ||
| 1220 | static const AVOption lut3d_haldclut_options[] = { | ||
| 1221 | #if CONFIG_LUT3D_FILTER | ||
| 1222 | { "file", "set 3D LUT file name", OFFSET(file), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, | ||
| 1223 | #endif | ||
| 1224 | #if CONFIG_HALDCLUT_FILTER | ||
| 1225 | { "clut", "when to process CLUT", OFFSET(clut), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, .flags = TFLAGS, .unit = "clut" }, | ||
| 1226 | { "first", "process only first CLUT, ignore rest", 0, AV_OPT_TYPE_CONST, {.i64=0}, .flags = TFLAGS, .unit = "clut" }, | ||
| 1227 | { "all", "process all CLUTs", 0, AV_OPT_TYPE_CONST, {.i64=1}, .flags = TFLAGS, .unit = "clut" }, | ||
| 1228 | #endif | ||
| 1229 | COMMON_OPTIONS | ||
| 1230 | }; | ||
| 1231 | |||
| 1232 | #if CONFIG_LUT3D_FILTER | ||
| 1233 | |||
| 1234 | AVFILTER_DEFINE_CLASS_EXT(lut3d, "lut3d", lut3d_haldclut_options); | ||
| 1235 | |||
| 1236 | ✗ | static av_cold int lut3d_init(AVFilterContext *ctx) | |
| 1237 | { | ||
| 1238 | int ret; | ||
| 1239 | FILE *f; | ||
| 1240 | const char *ext; | ||
| 1241 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 1242 | |||
| 1243 | ✗ | lut3d->scale.r = lut3d->scale.g = lut3d->scale.b = 1.f; | |
| 1244 | |||
| 1245 | ✗ | if (!lut3d->file) { | |
| 1246 | ✗ | return set_identity_matrix(ctx, 32); | |
| 1247 | } | ||
| 1248 | |||
| 1249 | ✗ | f = avpriv_fopen_utf8(lut3d->file, "r"); | |
| 1250 | ✗ | if (!f) { | |
| 1251 | ✗ | ret = AVERROR(errno); | |
| 1252 | ✗ | av_log(ctx, AV_LOG_ERROR, "%s: %s\n", lut3d->file, av_err2str(ret)); | |
| 1253 | ✗ | return ret; | |
| 1254 | } | ||
| 1255 | |||
| 1256 | ✗ | ext = strrchr(lut3d->file, '.'); | |
| 1257 | ✗ | if (!ext) { | |
| 1258 | ✗ | av_log(ctx, AV_LOG_ERROR, "Unable to guess the format from the extension\n"); | |
| 1259 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 1260 | ✗ | goto end; | |
| 1261 | } | ||
| 1262 | ✗ | ext++; | |
| 1263 | |||
| 1264 | ✗ | if (!av_strcasecmp(ext, "dat")) { | |
| 1265 | ✗ | ret = parse_dat(ctx, f); | |
| 1266 | ✗ | } else if (!av_strcasecmp(ext, "3dl")) { | |
| 1267 | ✗ | ret = parse_3dl(ctx, f); | |
| 1268 | ✗ | } else if (!av_strcasecmp(ext, "cube")) { | |
| 1269 | ✗ | ret = parse_cube(ctx, f); | |
| 1270 | ✗ | } else if (!av_strcasecmp(ext, "m3d")) { | |
| 1271 | ✗ | ret = parse_m3d(ctx, f); | |
| 1272 | ✗ | } else if (!av_strcasecmp(ext, "csp")) { | |
| 1273 | ✗ | ret = parse_cinespace(ctx, f); | |
| 1274 | } else { | ||
| 1275 | ✗ | av_log(ctx, AV_LOG_ERROR, "Unrecognized '.%s' file type\n", ext); | |
| 1276 | ✗ | ret = AVERROR(EINVAL); | |
| 1277 | } | ||
| 1278 | |||
| 1279 | ✗ | if (!ret && !lut3d->lutsize) { | |
| 1280 | ✗ | av_log(ctx, AV_LOG_ERROR, "3D LUT is empty\n"); | |
| 1281 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 1282 | } | ||
| 1283 | |||
| 1284 | ✗ | end: | |
| 1285 | ✗ | fclose(f); | |
| 1286 | ✗ | return ret; | |
| 1287 | } | ||
| 1288 | |||
| 1289 | ✗ | static av_cold void lut3d_uninit(AVFilterContext *ctx) | |
| 1290 | { | ||
| 1291 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 1292 | int i; | ||
| 1293 | ✗ | av_freep(&lut3d->lut); | |
| 1294 | |||
| 1295 | ✗ | for (i = 0; i < 3; i++) { | |
| 1296 | ✗ | av_freep(&lut3d->prelut.lut[i]); | |
| 1297 | } | ||
| 1298 | ✗ | } | |
| 1299 | |||
| 1300 | static const AVFilterPad lut3d_inputs[] = { | ||
| 1301 | { | ||
| 1302 | .name = "default", | ||
| 1303 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 1304 | .filter_frame = filter_frame, | ||
| 1305 | .config_props = config_input, | ||
| 1306 | }, | ||
| 1307 | }; | ||
| 1308 | |||
| 1309 | const FFFilter ff_vf_lut3d = { | ||
| 1310 | .p.name = "lut3d", | ||
| 1311 | .p.description = NULL_IF_CONFIG_SMALL("Adjust colors using a 3D LUT."), | ||
| 1312 | .p.priv_class = &lut3d_class, | ||
| 1313 | .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | | ||
| 1314 | AVFILTER_FLAG_SLICE_THREADS, | ||
| 1315 | .priv_size = sizeof(LUT3DContext), | ||
| 1316 | .init = lut3d_init, | ||
| 1317 | .uninit = lut3d_uninit, | ||
| 1318 | FILTER_INPUTS(lut3d_inputs), | ||
| 1319 | FILTER_OUTPUTS(ff_video_default_filterpad), | ||
| 1320 | FILTER_PIXFMTS_ARRAY(pix_fmts), | ||
| 1321 | .process_command = process_command, | ||
| 1322 | }; | ||
| 1323 | #endif | ||
| 1324 | |||
| 1325 | #if CONFIG_HALDCLUT_FILTER | ||
| 1326 | |||
| 1327 | ✗ | static void update_clut_packed(LUT3DContext *lut3d, const AVFrame *frame) | |
| 1328 | { | ||
| 1329 | ✗ | const uint8_t *data = frame->data[0]; | |
| 1330 | ✗ | const ptrdiff_t linesize = frame->linesize[0]; | |
| 1331 | ✗ | const int w = lut3d->clut_width; | |
| 1332 | ✗ | const int step = lut3d->clut_step; | |
| 1333 | ✗ | const uint8_t *rgba_map = lut3d->clut_rgba_map; | |
| 1334 | ✗ | const int level = lut3d->lutsize; | |
| 1335 | ✗ | const int level2 = lut3d->lutsize2; | |
| 1336 | |||
| 1337 | #define LOAD_CLUT(nbits) do { \ | ||
| 1338 | int i, j, k, x = 0, y = 0; \ | ||
| 1339 | \ | ||
| 1340 | for (k = 0; k < level; k++) { \ | ||
| 1341 | for (j = 0; j < level; j++) { \ | ||
| 1342 | for (i = 0; i < level; i++) { \ | ||
| 1343 | const uint##nbits##_t *src = (const uint##nbits##_t *) \ | ||
| 1344 | (data + y*linesize + x*step); \ | ||
| 1345 | struct rgbvec *vec = &lut3d->lut[i * level2 + j * level + k]; \ | ||
| 1346 | vec->r = src[rgba_map[0]] / (float)((1<<(nbits)) - 1); \ | ||
| 1347 | vec->g = src[rgba_map[1]] / (float)((1<<(nbits)) - 1); \ | ||
| 1348 | vec->b = src[rgba_map[2]] / (float)((1<<(nbits)) - 1); \ | ||
| 1349 | if (++x == w) { \ | ||
| 1350 | x = 0; \ | ||
| 1351 | y++; \ | ||
| 1352 | } \ | ||
| 1353 | } \ | ||
| 1354 | } \ | ||
| 1355 | } \ | ||
| 1356 | } while (0) | ||
| 1357 | |||
| 1358 | ✗ | switch (lut3d->clut_bits) { | |
| 1359 | ✗ | case 8: LOAD_CLUT(8); break; | |
| 1360 | ✗ | case 16: LOAD_CLUT(16); break; | |
| 1361 | } | ||
| 1362 | ✗ | } | |
| 1363 | |||
| 1364 | ✗ | static void update_clut_planar(LUT3DContext *lut3d, const AVFrame *frame) | |
| 1365 | { | ||
| 1366 | ✗ | const uint8_t *datag = frame->data[0]; | |
| 1367 | ✗ | const uint8_t *datab = frame->data[1]; | |
| 1368 | ✗ | const uint8_t *datar = frame->data[2]; | |
| 1369 | ✗ | const ptrdiff_t glinesize = frame->linesize[0]; | |
| 1370 | ✗ | const ptrdiff_t blinesize = frame->linesize[1]; | |
| 1371 | ✗ | const ptrdiff_t rlinesize = frame->linesize[2]; | |
| 1372 | ✗ | const int w = lut3d->clut_width; | |
| 1373 | ✗ | const int level = lut3d->lutsize; | |
| 1374 | ✗ | const int level2 = lut3d->lutsize2; | |
| 1375 | |||
| 1376 | #define LOAD_CLUT_PLANAR(nbits, depth) do { \ | ||
| 1377 | int i, j, k, x = 0, y = 0; \ | ||
| 1378 | \ | ||
| 1379 | for (k = 0; k < level; k++) { \ | ||
| 1380 | for (j = 0; j < level; j++) { \ | ||
| 1381 | for (i = 0; i < level; i++) { \ | ||
| 1382 | const uint##nbits##_t *gsrc = (const uint##nbits##_t *) \ | ||
| 1383 | (datag + y*glinesize); \ | ||
| 1384 | const uint##nbits##_t *bsrc = (const uint##nbits##_t *) \ | ||
| 1385 | (datab + y*blinesize); \ | ||
| 1386 | const uint##nbits##_t *rsrc = (const uint##nbits##_t *) \ | ||
| 1387 | (datar + y*rlinesize); \ | ||
| 1388 | struct rgbvec *vec = &lut3d->lut[i * level2 + j * level + k]; \ | ||
| 1389 | vec->r = gsrc[x] / (float)((1<<(depth)) - 1); \ | ||
| 1390 | vec->g = bsrc[x] / (float)((1<<(depth)) - 1); \ | ||
| 1391 | vec->b = rsrc[x] / (float)((1<<(depth)) - 1); \ | ||
| 1392 | if (++x == w) { \ | ||
| 1393 | x = 0; \ | ||
| 1394 | y++; \ | ||
| 1395 | } \ | ||
| 1396 | } \ | ||
| 1397 | } \ | ||
| 1398 | } \ | ||
| 1399 | } while (0) | ||
| 1400 | |||
| 1401 | ✗ | switch (lut3d->clut_bits) { | |
| 1402 | ✗ | case 8: LOAD_CLUT_PLANAR(8, 8); break; | |
| 1403 | ✗ | case 9: LOAD_CLUT_PLANAR(16, 9); break; | |
| 1404 | ✗ | case 10: LOAD_CLUT_PLANAR(16, 10); break; | |
| 1405 | ✗ | case 12: LOAD_CLUT_PLANAR(16, 12); break; | |
| 1406 | ✗ | case 14: LOAD_CLUT_PLANAR(16, 14); break; | |
| 1407 | ✗ | case 16: LOAD_CLUT_PLANAR(16, 16); break; | |
| 1408 | } | ||
| 1409 | ✗ | } | |
| 1410 | |||
| 1411 | ✗ | static void update_clut_float(LUT3DContext *lut3d, const AVFrame *frame) | |
| 1412 | { | ||
| 1413 | ✗ | const uint8_t *datag = frame->data[0]; | |
| 1414 | ✗ | const uint8_t *datab = frame->data[1]; | |
| 1415 | ✗ | const uint8_t *datar = frame->data[2]; | |
| 1416 | ✗ | const ptrdiff_t glinesize = frame->linesize[0]; | |
| 1417 | ✗ | const ptrdiff_t blinesize = frame->linesize[1]; | |
| 1418 | ✗ | const ptrdiff_t rlinesize = frame->linesize[2]; | |
| 1419 | ✗ | const int w = lut3d->clut_width; | |
| 1420 | ✗ | const int level = lut3d->lutsize; | |
| 1421 | ✗ | const int level2 = lut3d->lutsize2; | |
| 1422 | |||
| 1423 | ✗ | int i, j, k, x = 0, y = 0; | |
| 1424 | |||
| 1425 | ✗ | for (k = 0; k < level; k++) { | |
| 1426 | ✗ | for (j = 0; j < level; j++) { | |
| 1427 | ✗ | for (i = 0; i < level; i++) { | |
| 1428 | ✗ | const float *gsrc = (const float *)(datag + y*glinesize); | |
| 1429 | ✗ | const float *bsrc = (const float *)(datab + y*blinesize); | |
| 1430 | ✗ | const float *rsrc = (const float *)(datar + y*rlinesize); | |
| 1431 | ✗ | struct rgbvec *vec = &lut3d->lut[i * level2 + j * level + k]; | |
| 1432 | ✗ | vec->r = rsrc[x]; | |
| 1433 | ✗ | vec->g = gsrc[x]; | |
| 1434 | ✗ | vec->b = bsrc[x]; | |
| 1435 | ✗ | if (++x == w) { | |
| 1436 | ✗ | x = 0; | |
| 1437 | ✗ | y++; | |
| 1438 | } | ||
| 1439 | } | ||
| 1440 | } | ||
| 1441 | } | ||
| 1442 | ✗ | } | |
| 1443 | |||
| 1444 | ✗ | static int config_output(AVFilterLink *outlink) | |
| 1445 | { | ||
| 1446 | ✗ | AVFilterContext *ctx = outlink->src; | |
| 1447 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 1448 | int ret; | ||
| 1449 | |||
| 1450 | ✗ | ret = ff_framesync_init_dualinput(&lut3d->fs, ctx); | |
| 1451 | ✗ | if (ret < 0) | |
| 1452 | ✗ | return ret; | |
| 1453 | ✗ | outlink->w = ctx->inputs[0]->w; | |
| 1454 | ✗ | outlink->h = ctx->inputs[0]->h; | |
| 1455 | ✗ | outlink->time_base = ctx->inputs[0]->time_base; | |
| 1456 | ✗ | if ((ret = ff_framesync_configure(&lut3d->fs)) < 0) | |
| 1457 | ✗ | return ret; | |
| 1458 | ✗ | return 0; | |
| 1459 | } | ||
| 1460 | |||
| 1461 | ✗ | static int activate(AVFilterContext *ctx) | |
| 1462 | { | ||
| 1463 | ✗ | LUT3DContext *s = ctx->priv; | |
| 1464 | ✗ | return ff_framesync_activate(&s->fs); | |
| 1465 | } | ||
| 1466 | |||
| 1467 | ✗ | static int config_clut(AVFilterLink *inlink) | |
| 1468 | { | ||
| 1469 | int size, level, w, h; | ||
| 1470 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 1471 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 1472 | ✗ | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); | |
| 1473 | |||
| 1474 | ✗ | av_assert0(desc); | |
| 1475 | |||
| 1476 | ✗ | lut3d->clut_bits = desc->comp[0].depth; | |
| 1477 | ✗ | lut3d->clut_planar = av_pix_fmt_count_planes(inlink->format) > 1; | |
| 1478 | ✗ | lut3d->clut_float = desc->flags & AV_PIX_FMT_FLAG_FLOAT; | |
| 1479 | |||
| 1480 | ✗ | lut3d->clut_step = av_get_padded_bits_per_pixel(desc) >> 3; | |
| 1481 | ✗ | ff_fill_rgba_map(lut3d->clut_rgba_map, inlink->format); | |
| 1482 | |||
| 1483 | ✗ | if (inlink->w > inlink->h) | |
| 1484 | ✗ | av_log(ctx, AV_LOG_INFO, "Padding on the right (%dpx) of the " | |
| 1485 | ✗ | "Hald CLUT will be ignored\n", inlink->w - inlink->h); | |
| 1486 | ✗ | else if (inlink->w < inlink->h) | |
| 1487 | ✗ | av_log(ctx, AV_LOG_INFO, "Padding at the bottom (%dpx) of the " | |
| 1488 | ✗ | "Hald CLUT will be ignored\n", inlink->h - inlink->w); | |
| 1489 | ✗ | lut3d->clut_width = w = h = FFMIN(inlink->w, inlink->h); | |
| 1490 | |||
| 1491 | ✗ | for (level = 1; level*level*level < w; level++); | |
| 1492 | ✗ | size = level*level*level; | |
| 1493 | ✗ | if (size != w) { | |
| 1494 | ✗ | av_log(ctx, AV_LOG_WARNING, "The Hald CLUT width does not match the level\n"); | |
| 1495 | ✗ | return AVERROR_INVALIDDATA; | |
| 1496 | } | ||
| 1497 | ✗ | av_assert0(w == h && w == size); | |
| 1498 | ✗ | level *= level; | |
| 1499 | ✗ | if (level > MAX_LEVEL) { | |
| 1500 | ✗ | const int max_clut_level = sqrt(MAX_LEVEL); | |
| 1501 | ✗ | const int max_clut_size = max_clut_level*max_clut_level*max_clut_level; | |
| 1502 | ✗ | av_log(ctx, AV_LOG_ERROR, "Too large Hald CLUT " | |
| 1503 | "(maximum level is %d, or %dx%d CLUT)\n", | ||
| 1504 | max_clut_level, max_clut_size, max_clut_size); | ||
| 1505 | ✗ | return AVERROR(EINVAL); | |
| 1506 | } | ||
| 1507 | |||
| 1508 | ✗ | return allocate_3dlut(ctx, level, 0); | |
| 1509 | } | ||
| 1510 | |||
| 1511 | ✗ | static int update_apply_clut(FFFrameSync *fs) | |
| 1512 | { | ||
| 1513 | ✗ | AVFilterContext *ctx = fs->parent; | |
| 1514 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 1515 | ✗ | AVFilterLink *inlink = ctx->inputs[0]; | |
| 1516 | AVFrame *master, *second, *out; | ||
| 1517 | int ret; | ||
| 1518 | |||
| 1519 | ✗ | ret = ff_framesync_dualinput_get(fs, &master, &second); | |
| 1520 | ✗ | if (ret < 0) | |
| 1521 | ✗ | return ret; | |
| 1522 | ✗ | if (!second) | |
| 1523 | ✗ | return ff_filter_frame(ctx->outputs[0], master); | |
| 1524 | ✗ | if (lut3d->clut || !lut3d->got_clut) { | |
| 1525 | ✗ | if (lut3d->clut_float) | |
| 1526 | ✗ | update_clut_float(ctx->priv, second); | |
| 1527 | ✗ | else if (lut3d->clut_planar) | |
| 1528 | ✗ | update_clut_planar(ctx->priv, second); | |
| 1529 | else | ||
| 1530 | ✗ | update_clut_packed(ctx->priv, second); | |
| 1531 | ✗ | lut3d->got_clut = 1; | |
| 1532 | } | ||
| 1533 | ✗ | out = apply_lut(inlink, master); | |
| 1534 | ✗ | return ff_filter_frame(ctx->outputs[0], out); | |
| 1535 | } | ||
| 1536 | |||
| 1537 | ✗ | static av_cold int haldclut_init(AVFilterContext *ctx) | |
| 1538 | { | ||
| 1539 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 1540 | ✗ | lut3d->scale.r = lut3d->scale.g = lut3d->scale.b = 1.f; | |
| 1541 | ✗ | lut3d->fs.on_event = update_apply_clut; | |
| 1542 | ✗ | return 0; | |
| 1543 | } | ||
| 1544 | |||
| 1545 | ✗ | static av_cold void haldclut_uninit(AVFilterContext *ctx) | |
| 1546 | { | ||
| 1547 | ✗ | LUT3DContext *lut3d = ctx->priv; | |
| 1548 | ✗ | ff_framesync_uninit(&lut3d->fs); | |
| 1549 | ✗ | av_freep(&lut3d->lut); | |
| 1550 | ✗ | } | |
| 1551 | |||
| 1552 | ✗ | FRAMESYNC_DEFINE_CLASS_EXT(haldclut, LUT3DContext, fs, | |
| 1553 | &lut3d_haldclut_options[COMMON_OPTIONS_OFFSET]); | ||
| 1554 | |||
| 1555 | static const AVFilterPad haldclut_inputs[] = { | ||
| 1556 | { | ||
| 1557 | .name = "main", | ||
| 1558 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 1559 | .config_props = config_input, | ||
| 1560 | },{ | ||
| 1561 | .name = "clut", | ||
| 1562 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 1563 | .config_props = config_clut, | ||
| 1564 | }, | ||
| 1565 | }; | ||
| 1566 | |||
| 1567 | static const AVFilterPad haldclut_outputs[] = { | ||
| 1568 | { | ||
| 1569 | .name = "default", | ||
| 1570 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 1571 | .config_props = config_output, | ||
| 1572 | }, | ||
| 1573 | }; | ||
| 1574 | |||
| 1575 | const FFFilter ff_vf_haldclut = { | ||
| 1576 | .p.name = "haldclut", | ||
| 1577 | .p.description = NULL_IF_CONFIG_SMALL("Adjust colors using a Hald CLUT."), | ||
| 1578 | .p.priv_class = &haldclut_class, | ||
| 1579 | .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | | ||
| 1580 | AVFILTER_FLAG_SLICE_THREADS, | ||
| 1581 | .priv_size = sizeof(LUT3DContext), | ||
| 1582 | .preinit = haldclut_framesync_preinit, | ||
| 1583 | .init = haldclut_init, | ||
| 1584 | .uninit = haldclut_uninit, | ||
| 1585 | .activate = activate, | ||
| 1586 | FILTER_INPUTS(haldclut_inputs), | ||
| 1587 | FILTER_OUTPUTS(haldclut_outputs), | ||
| 1588 | FILTER_PIXFMTS_ARRAY(pix_fmts), | ||
| 1589 | .process_command = process_command, | ||
| 1590 | }; | ||
| 1591 | #endif | ||
| 1592 | |||
| 1593 | #endif /* CONFIG_LUT3D_FILTER || CONFIG_HALDCLUT_FILTER */ | ||
| 1594 | |||
| 1595 | #if CONFIG_LUT1D_FILTER | ||
| 1596 | |||
| 1597 | enum interp_1d_mode { | ||
| 1598 | INTERPOLATE_1D_NEAREST, | ||
| 1599 | INTERPOLATE_1D_LINEAR, | ||
| 1600 | INTERPOLATE_1D_CUBIC, | ||
| 1601 | INTERPOLATE_1D_COSINE, | ||
| 1602 | INTERPOLATE_1D_SPLINE, | ||
| 1603 | NB_INTERP_1D_MODE | ||
| 1604 | }; | ||
| 1605 | |||
| 1606 | #define MAX_1D_LEVEL 65536 | ||
| 1607 | |||
| 1608 | typedef struct LUT1DContext { | ||
| 1609 | const AVClass *class; | ||
| 1610 | char *file; | ||
| 1611 | int interpolation; ///<interp_1d_mode | ||
| 1612 | struct rgbvec scale; | ||
| 1613 | uint8_t rgba_map[4]; | ||
| 1614 | int step; | ||
| 1615 | float lut[3][MAX_1D_LEVEL]; | ||
| 1616 | int lutsize; | ||
| 1617 | avfilter_action_func *interp; | ||
| 1618 | } LUT1DContext; | ||
| 1619 | |||
| 1620 | #undef OFFSET | ||
| 1621 | #define OFFSET(x) offsetof(LUT1DContext, x) | ||
| 1622 | |||
| 1623 | ✗ | static void set_identity_matrix_1d(LUT1DContext *lut1d, int size) | |
| 1624 | { | ||
| 1625 | ✗ | const float c = 1. / (size - 1); | |
| 1626 | int i; | ||
| 1627 | |||
| 1628 | ✗ | lut1d->lutsize = size; | |
| 1629 | ✗ | for (i = 0; i < size; i++) { | |
| 1630 | ✗ | lut1d->lut[0][i] = i * c; | |
| 1631 | ✗ | lut1d->lut[1][i] = i * c; | |
| 1632 | ✗ | lut1d->lut[2][i] = i * c; | |
| 1633 | } | ||
| 1634 | ✗ | } | |
| 1635 | |||
| 1636 | ✗ | static int parse_cinespace_1d(AVFilterContext *ctx, FILE *f) | |
| 1637 | { | ||
| 1638 | ✗ | LUT1DContext *lut1d = ctx->priv; | |
| 1639 | char line[MAX_LINE_SIZE]; | ||
| 1640 | ✗ | float in_min[3] = {0.0, 0.0, 0.0}; | |
| 1641 | ✗ | float in_max[3] = {1.0, 1.0, 1.0}; | |
| 1642 | ✗ | float out_min[3] = {0.0, 0.0, 0.0}; | |
| 1643 | ✗ | float out_max[3] = {1.0, 1.0, 1.0}; | |
| 1644 | ✗ | int inside_metadata = 0, size; | |
| 1645 | |||
| 1646 | ✗ | NEXT_LINE(skip_line(line)); | |
| 1647 | ✗ | if (strncmp(line, "CSPLUTV100", 10)) { | |
| 1648 | ✗ | av_log(ctx, AV_LOG_ERROR, "Not cineSpace LUT format\n"); | |
| 1649 | ✗ | return AVERROR(EINVAL); | |
| 1650 | } | ||
| 1651 | |||
| 1652 | ✗ | NEXT_LINE(skip_line(line)); | |
| 1653 | ✗ | if (strncmp(line, "1D", 2)) { | |
| 1654 | ✗ | av_log(ctx, AV_LOG_ERROR, "Not 1D LUT format\n"); | |
| 1655 | ✗ | return AVERROR(EINVAL); | |
| 1656 | } | ||
| 1657 | |||
| 1658 | ✗ | while (1) { | |
| 1659 | ✗ | NEXT_LINE(skip_line(line)); | |
| 1660 | |||
| 1661 | ✗ | if (!strncmp(line, "BEGIN METADATA", 14)) { | |
| 1662 | ✗ | inside_metadata = 1; | |
| 1663 | ✗ | continue; | |
| 1664 | } | ||
| 1665 | ✗ | if (!strncmp(line, "END METADATA", 12)) { | |
| 1666 | ✗ | inside_metadata = 0; | |
| 1667 | ✗ | continue; | |
| 1668 | } | ||
| 1669 | ✗ | if (inside_metadata == 0) { | |
| 1670 | ✗ | for (int i = 0; i < 3; i++) { | |
| 1671 | ✗ | int npoints = strtol(line, NULL, 0); | |
| 1672 | |||
| 1673 | ✗ | if (npoints != 2) { | |
| 1674 | ✗ | av_log(ctx, AV_LOG_ERROR, "Unsupported number of pre-lut points.\n"); | |
| 1675 | ✗ | return AVERROR_PATCHWELCOME; | |
| 1676 | } | ||
| 1677 | |||
| 1678 | ✗ | NEXT_LINE(skip_line(line)); | |
| 1679 | ✗ | if (av_sscanf(line, "%f %f", &in_min[i], &in_max[i]) != 2) | |
| 1680 | ✗ | return AVERROR_INVALIDDATA; | |
| 1681 | ✗ | NEXT_LINE(skip_line(line)); | |
| 1682 | ✗ | if (av_sscanf(line, "%f %f", &out_min[i], &out_max[i]) != 2) | |
| 1683 | ✗ | return AVERROR_INVALIDDATA; | |
| 1684 | ✗ | NEXT_LINE(skip_line(line)); | |
| 1685 | } | ||
| 1686 | |||
| 1687 | ✗ | size = strtol(line, NULL, 0); | |
| 1688 | |||
| 1689 | ✗ | if (size < 2 || size > MAX_1D_LEVEL) { | |
| 1690 | ✗ | av_log(ctx, AV_LOG_ERROR, "Too large or invalid 1D LUT size\n"); | |
| 1691 | ✗ | return AVERROR(EINVAL); | |
| 1692 | } | ||
| 1693 | |||
| 1694 | ✗ | lut1d->lutsize = size; | |
| 1695 | |||
| 1696 | ✗ | for (int i = 0; i < size; i++) { | |
| 1697 | ✗ | NEXT_LINE(skip_line(line)); | |
| 1698 | ✗ | if (av_sscanf(line, "%f %f %f", &lut1d->lut[0][i], &lut1d->lut[1][i], &lut1d->lut[2][i]) != 3) | |
| 1699 | ✗ | return AVERROR_INVALIDDATA; | |
| 1700 | ✗ | lut1d->lut[0][i] *= out_max[0] - out_min[0]; | |
| 1701 | ✗ | lut1d->lut[1][i] *= out_max[1] - out_min[1]; | |
| 1702 | ✗ | lut1d->lut[2][i] *= out_max[2] - out_min[2]; | |
| 1703 | } | ||
| 1704 | |||
| 1705 | ✗ | break; | |
| 1706 | } | ||
| 1707 | } | ||
| 1708 | |||
| 1709 | ✗ | lut1d->scale.r = av_clipf(1. / (in_max[0] - in_min[0]), 0.f, 1.f); | |
| 1710 | ✗ | lut1d->scale.g = av_clipf(1. / (in_max[1] - in_min[1]), 0.f, 1.f); | |
| 1711 | ✗ | lut1d->scale.b = av_clipf(1. / (in_max[2] - in_min[2]), 0.f, 1.f); | |
| 1712 | |||
| 1713 | ✗ | return 0; | |
| 1714 | } | ||
| 1715 | |||
| 1716 | ✗ | static int parse_cube_1d(AVFilterContext *ctx, FILE *f) | |
| 1717 | { | ||
| 1718 | ✗ | LUT1DContext *lut1d = ctx->priv; | |
| 1719 | char line[MAX_LINE_SIZE]; | ||
| 1720 | ✗ | float min[3] = {0.0, 0.0, 0.0}; | |
| 1721 | ✗ | float max[3] = {1.0, 1.0, 1.0}; | |
| 1722 | |||
| 1723 | ✗ | while (fgets(line, sizeof(line), f)) { | |
| 1724 | ✗ | if (!strncmp(line, "LUT_1D_SIZE", 11)) { | |
| 1725 | ✗ | const int size = strtol(line + 12, NULL, 0); | |
| 1726 | int i; | ||
| 1727 | |||
| 1728 | ✗ | if (size < 2 || size > MAX_1D_LEVEL) { | |
| 1729 | ✗ | av_log(ctx, AV_LOG_ERROR, "Too large or invalid 1D LUT size\n"); | |
| 1730 | ✗ | return AVERROR(EINVAL); | |
| 1731 | } | ||
| 1732 | ✗ | lut1d->lutsize = size; | |
| 1733 | ✗ | for (i = 0; i < size; i++) { | |
| 1734 | do { | ||
| 1735 | ✗ | try_again: | |
| 1736 | ✗ | NEXT_LINE(0); | |
| 1737 | ✗ | if (!strncmp(line, "DOMAIN_", 7)) { | |
| 1738 | ✗ | float *vals = NULL; | |
| 1739 | ✗ | if (!strncmp(line + 7, "MIN ", 4)) vals = min; | |
| 1740 | ✗ | else if (!strncmp(line + 7, "MAX ", 4)) vals = max; | |
| 1741 | ✗ | if (!vals) | |
| 1742 | ✗ | return AVERROR_INVALIDDATA; | |
| 1743 | ✗ | if (av_sscanf(line + 11, "%f %f %f", vals, vals + 1, vals + 2) != 3) | |
| 1744 | ✗ | return AVERROR_INVALIDDATA; | |
| 1745 | ✗ | av_log(ctx, AV_LOG_DEBUG, "min: %f %f %f | max: %f %f %f\n", | |
| 1746 | ✗ | min[0], min[1], min[2], max[0], max[1], max[2]); | |
| 1747 | ✗ | goto try_again; | |
| 1748 | ✗ | } else if (!strncmp(line, "LUT_1D_INPUT_RANGE ", 19)) { | |
| 1749 | ✗ | if (av_sscanf(line + 19, "%f %f", min, max) != 2) | |
| 1750 | ✗ | return AVERROR_INVALIDDATA; | |
| 1751 | ✗ | min[1] = min[2] = min[0]; | |
| 1752 | ✗ | max[1] = max[2] = max[0]; | |
| 1753 | ✗ | goto try_again; | |
| 1754 | ✗ | } else if (!strncmp(line, "TITLE", 5)) { | |
| 1755 | ✗ | goto try_again; | |
| 1756 | } | ||
| 1757 | ✗ | } while (skip_line(line)); | |
| 1758 | ✗ | if (av_sscanf(line, "%f %f %f", &lut1d->lut[0][i], &lut1d->lut[1][i], &lut1d->lut[2][i]) != 3) | |
| 1759 | ✗ | return AVERROR_INVALIDDATA; | |
| 1760 | } | ||
| 1761 | ✗ | break; | |
| 1762 | } | ||
| 1763 | } | ||
| 1764 | |||
| 1765 | ✗ | lut1d->scale.r = av_clipf(1. / (max[0] - min[0]), 0.f, 1.f); | |
| 1766 | ✗ | lut1d->scale.g = av_clipf(1. / (max[1] - min[1]), 0.f, 1.f); | |
| 1767 | ✗ | lut1d->scale.b = av_clipf(1. / (max[2] - min[2]), 0.f, 1.f); | |
| 1768 | |||
| 1769 | ✗ | return 0; | |
| 1770 | } | ||
| 1771 | |||
| 1772 | static const AVOption lut1d_options[] = { | ||
| 1773 | { "file", "set 1D LUT file name", OFFSET(file), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = TFLAGS }, | ||
| 1774 | { "interp", "select interpolation mode", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERPOLATE_1D_LINEAR}, 0, NB_INTERP_1D_MODE-1, TFLAGS, .unit = "interp_mode" }, | ||
| 1775 | { "nearest", "use values from the nearest defined points", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_NEAREST}, 0, 0, TFLAGS, .unit = "interp_mode" }, | ||
| 1776 | { "linear", "use values from the linear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_LINEAR}, 0, 0, TFLAGS, .unit = "interp_mode" }, | ||
| 1777 | { "cosine", "use values from the cosine interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_COSINE}, 0, 0, TFLAGS, .unit = "interp_mode" }, | ||
| 1778 | { "cubic", "use values from the cubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_CUBIC}, 0, 0, TFLAGS, .unit = "interp_mode" }, | ||
| 1779 | { "spline", "use values from the spline interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_SPLINE}, 0, 0, TFLAGS, .unit = "interp_mode" }, | ||
| 1780 | { NULL } | ||
| 1781 | }; | ||
| 1782 | |||
| 1783 | AVFILTER_DEFINE_CLASS(lut1d); | ||
| 1784 | |||
| 1785 | ✗ | static inline float interp_1d_nearest(const LUT1DContext *lut1d, | |
| 1786 | int idx, const float s) | ||
| 1787 | { | ||
| 1788 | ✗ | return lut1d->lut[idx][NEAR(s)]; | |
| 1789 | } | ||
| 1790 | |||
| 1791 | #define NEXT1D(x) (FFMIN((int)(x) + 1, lut1d->lutsize - 1)) | ||
| 1792 | |||
| 1793 | ✗ | static inline float interp_1d_linear(const LUT1DContext *lut1d, | |
| 1794 | int idx, const float s) | ||
| 1795 | { | ||
| 1796 | ✗ | const int prev = PREV(s); | |
| 1797 | ✗ | const int next = NEXT1D(s); | |
| 1798 | ✗ | const float d = s - prev; | |
| 1799 | ✗ | const float p = lut1d->lut[idx][prev]; | |
| 1800 | ✗ | const float n = lut1d->lut[idx][next]; | |
| 1801 | |||
| 1802 | ✗ | return lerpf(p, n, d); | |
| 1803 | } | ||
| 1804 | |||
| 1805 | ✗ | static inline float interp_1d_cosine(const LUT1DContext *lut1d, | |
| 1806 | int idx, const float s) | ||
| 1807 | { | ||
| 1808 | ✗ | const int prev = PREV(s); | |
| 1809 | ✗ | const int next = NEXT1D(s); | |
| 1810 | ✗ | const float d = s - prev; | |
| 1811 | ✗ | const float p = lut1d->lut[idx][prev]; | |
| 1812 | ✗ | const float n = lut1d->lut[idx][next]; | |
| 1813 | ✗ | const float m = (1.f - cosf(d * M_PI)) * .5f; | |
| 1814 | |||
| 1815 | ✗ | return lerpf(p, n, m); | |
| 1816 | } | ||
| 1817 | |||
| 1818 | ✗ | static inline float interp_1d_cubic(const LUT1DContext *lut1d, | |
| 1819 | int idx, const float s) | ||
| 1820 | { | ||
| 1821 | ✗ | const int prev = PREV(s); | |
| 1822 | ✗ | const int next = NEXT1D(s); | |
| 1823 | ✗ | const float mu = s - prev; | |
| 1824 | float a0, a1, a2, a3, mu2; | ||
| 1825 | |||
| 1826 | ✗ | float y0 = lut1d->lut[idx][FFMAX(prev - 1, 0)]; | |
| 1827 | ✗ | float y1 = lut1d->lut[idx][prev]; | |
| 1828 | ✗ | float y2 = lut1d->lut[idx][next]; | |
| 1829 | ✗ | float y3 = lut1d->lut[idx][FFMIN(next + 1, lut1d->lutsize - 1)]; | |
| 1830 | |||
| 1831 | |||
| 1832 | ✗ | mu2 = mu * mu; | |
| 1833 | ✗ | a0 = y3 - y2 - y0 + y1; | |
| 1834 | ✗ | a1 = y0 - y1 - a0; | |
| 1835 | ✗ | a2 = y2 - y0; | |
| 1836 | ✗ | a3 = y1; | |
| 1837 | |||
| 1838 | ✗ | return a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3; | |
| 1839 | } | ||
| 1840 | |||
| 1841 | ✗ | static inline float interp_1d_spline(const LUT1DContext *lut1d, | |
| 1842 | int idx, const float s) | ||
| 1843 | { | ||
| 1844 | ✗ | const int prev = PREV(s); | |
| 1845 | ✗ | const int next = NEXT1D(s); | |
| 1846 | ✗ | const float x = s - prev; | |
| 1847 | float c0, c1, c2, c3; | ||
| 1848 | |||
| 1849 | ✗ | float y0 = lut1d->lut[idx][FFMAX(prev - 1, 0)]; | |
| 1850 | ✗ | float y1 = lut1d->lut[idx][prev]; | |
| 1851 | ✗ | float y2 = lut1d->lut[idx][next]; | |
| 1852 | ✗ | float y3 = lut1d->lut[idx][FFMIN(next + 1, lut1d->lutsize - 1)]; | |
| 1853 | |||
| 1854 | ✗ | c0 = y1; | |
| 1855 | ✗ | c1 = .5f * (y2 - y0); | |
| 1856 | ✗ | c2 = y0 - 2.5f * y1 + 2.f * y2 - .5f * y3; | |
| 1857 | ✗ | c3 = .5f * (y3 - y0) + 1.5f * (y1 - y2); | |
| 1858 | |||
| 1859 | ✗ | return ((c3 * x + c2) * x + c1) * x + c0; | |
| 1860 | } | ||
| 1861 | |||
| 1862 | #define DEFINE_INTERP_FUNC_PLANAR_1D(name, nbits, depth) \ | ||
| 1863 | static int interp_1d_##nbits##_##name##_p##depth(AVFilterContext *ctx, \ | ||
| 1864 | void *arg, int jobnr, \ | ||
| 1865 | int nb_jobs) \ | ||
| 1866 | { \ | ||
| 1867 | int x, y; \ | ||
| 1868 | const LUT1DContext *lut1d = ctx->priv; \ | ||
| 1869 | const ThreadData *td = arg; \ | ||
| 1870 | const AVFrame *in = td->in; \ | ||
| 1871 | const AVFrame *out = td->out; \ | ||
| 1872 | const int direct = out == in; \ | ||
| 1873 | const int slice_start = (in->height * jobnr ) / nb_jobs; \ | ||
| 1874 | const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \ | ||
| 1875 | uint8_t *grow = out->data[0] + slice_start * out->linesize[0]; \ | ||
| 1876 | uint8_t *brow = out->data[1] + slice_start * out->linesize[1]; \ | ||
| 1877 | uint8_t *rrow = out->data[2] + slice_start * out->linesize[2]; \ | ||
| 1878 | uint8_t *arow = out->data[3] + slice_start * out->linesize[3]; \ | ||
| 1879 | const uint8_t *srcgrow = in->data[0] + slice_start * in->linesize[0]; \ | ||
| 1880 | const uint8_t *srcbrow = in->data[1] + slice_start * in->linesize[1]; \ | ||
| 1881 | const uint8_t *srcrrow = in->data[2] + slice_start * in->linesize[2]; \ | ||
| 1882 | const uint8_t *srcarow = in->data[3] + slice_start * in->linesize[3]; \ | ||
| 1883 | const float factor = (1 << depth) - 1; \ | ||
| 1884 | const float scale_r = (lut1d->scale.r / factor) * (lut1d->lutsize - 1); \ | ||
| 1885 | const float scale_g = (lut1d->scale.g / factor) * (lut1d->lutsize - 1); \ | ||
| 1886 | const float scale_b = (lut1d->scale.b / factor) * (lut1d->lutsize - 1); \ | ||
| 1887 | \ | ||
| 1888 | for (y = slice_start; y < slice_end; y++) { \ | ||
| 1889 | uint##nbits##_t *dstg = (uint##nbits##_t *)grow; \ | ||
| 1890 | uint##nbits##_t *dstb = (uint##nbits##_t *)brow; \ | ||
| 1891 | uint##nbits##_t *dstr = (uint##nbits##_t *)rrow; \ | ||
| 1892 | uint##nbits##_t *dsta = (uint##nbits##_t *)arow; \ | ||
| 1893 | const uint##nbits##_t *srcg = (const uint##nbits##_t *)srcgrow; \ | ||
| 1894 | const uint##nbits##_t *srcb = (const uint##nbits##_t *)srcbrow; \ | ||
| 1895 | const uint##nbits##_t *srcr = (const uint##nbits##_t *)srcrrow; \ | ||
| 1896 | const uint##nbits##_t *srca = (const uint##nbits##_t *)srcarow; \ | ||
| 1897 | for (x = 0; x < in->width; x++) { \ | ||
| 1898 | float r = srcr[x] * scale_r; \ | ||
| 1899 | float g = srcg[x] * scale_g; \ | ||
| 1900 | float b = srcb[x] * scale_b; \ | ||
| 1901 | r = interp_1d_##name(lut1d, 0, r); \ | ||
| 1902 | g = interp_1d_##name(lut1d, 1, g); \ | ||
| 1903 | b = interp_1d_##name(lut1d, 2, b); \ | ||
| 1904 | dstr[x] = av_clip_uintp2(r * factor, depth); \ | ||
| 1905 | dstg[x] = av_clip_uintp2(g * factor, depth); \ | ||
| 1906 | dstb[x] = av_clip_uintp2(b * factor, depth); \ | ||
| 1907 | if (!direct && in->linesize[3]) \ | ||
| 1908 | dsta[x] = srca[x]; \ | ||
| 1909 | } \ | ||
| 1910 | grow += out->linesize[0]; \ | ||
| 1911 | brow += out->linesize[1]; \ | ||
| 1912 | rrow += out->linesize[2]; \ | ||
| 1913 | arow += out->linesize[3]; \ | ||
| 1914 | srcgrow += in->linesize[0]; \ | ||
| 1915 | srcbrow += in->linesize[1]; \ | ||
| 1916 | srcrrow += in->linesize[2]; \ | ||
| 1917 | srcarow += in->linesize[3]; \ | ||
| 1918 | } \ | ||
| 1919 | return 0; \ | ||
| 1920 | } | ||
| 1921 | |||
| 1922 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(nearest, 8, 8) | |
| 1923 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(linear, 8, 8) | |
| 1924 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(cosine, 8, 8) | |
| 1925 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(cubic, 8, 8) | |
| 1926 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(spline, 8, 8) | |
| 1927 | |||
| 1928 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(nearest, 16, 9) | |
| 1929 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(linear, 16, 9) | |
| 1930 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(cosine, 16, 9) | |
| 1931 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(cubic, 16, 9) | |
| 1932 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(spline, 16, 9) | |
| 1933 | |||
| 1934 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(nearest, 16, 10) | |
| 1935 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(linear, 16, 10) | |
| 1936 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(cosine, 16, 10) | |
| 1937 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(cubic, 16, 10) | |
| 1938 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(spline, 16, 10) | |
| 1939 | |||
| 1940 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(nearest, 16, 12) | |
| 1941 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(linear, 16, 12) | |
| 1942 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(cosine, 16, 12) | |
| 1943 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(cubic, 16, 12) | |
| 1944 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(spline, 16, 12) | |
| 1945 | |||
| 1946 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(nearest, 16, 14) | |
| 1947 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(linear, 16, 14) | |
| 1948 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(cosine, 16, 14) | |
| 1949 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(cubic, 16, 14) | |
| 1950 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(spline, 16, 14) | |
| 1951 | |||
| 1952 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(nearest, 16, 16) | |
| 1953 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(linear, 16, 16) | |
| 1954 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(cosine, 16, 16) | |
| 1955 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(cubic, 16, 16) | |
| 1956 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D(spline, 16, 16) | |
| 1957 | |||
| 1958 | #define DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(name, depth) \ | ||
| 1959 | static int interp_1d_##name##_pf##depth(AVFilterContext *ctx, \ | ||
| 1960 | void *arg, int jobnr, \ | ||
| 1961 | int nb_jobs) \ | ||
| 1962 | { \ | ||
| 1963 | int x, y; \ | ||
| 1964 | const LUT1DContext *lut1d = ctx->priv; \ | ||
| 1965 | const ThreadData *td = arg; \ | ||
| 1966 | const AVFrame *in = td->in; \ | ||
| 1967 | const AVFrame *out = td->out; \ | ||
| 1968 | const int direct = out == in; \ | ||
| 1969 | const int slice_start = (in->height * jobnr ) / nb_jobs; \ | ||
| 1970 | const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \ | ||
| 1971 | uint8_t *grow = out->data[0] + slice_start * out->linesize[0]; \ | ||
| 1972 | uint8_t *brow = out->data[1] + slice_start * out->linesize[1]; \ | ||
| 1973 | uint8_t *rrow = out->data[2] + slice_start * out->linesize[2]; \ | ||
| 1974 | uint8_t *arow = out->data[3] + slice_start * out->linesize[3]; \ | ||
| 1975 | const uint8_t *srcgrow = in->data[0] + slice_start * in->linesize[0]; \ | ||
| 1976 | const uint8_t *srcbrow = in->data[1] + slice_start * in->linesize[1]; \ | ||
| 1977 | const uint8_t *srcrrow = in->data[2] + slice_start * in->linesize[2]; \ | ||
| 1978 | const uint8_t *srcarow = in->data[3] + slice_start * in->linesize[3]; \ | ||
| 1979 | const float lutsize = lut1d->lutsize - 1; \ | ||
| 1980 | const float scale_r = lut1d->scale.r * lutsize; \ | ||
| 1981 | const float scale_g = lut1d->scale.g * lutsize; \ | ||
| 1982 | const float scale_b = lut1d->scale.b * lutsize; \ | ||
| 1983 | \ | ||
| 1984 | for (y = slice_start; y < slice_end; y++) { \ | ||
| 1985 | float *dstg = (float *)grow; \ | ||
| 1986 | float *dstb = (float *)brow; \ | ||
| 1987 | float *dstr = (float *)rrow; \ | ||
| 1988 | float *dsta = (float *)arow; \ | ||
| 1989 | const float *srcg = (const float *)srcgrow; \ | ||
| 1990 | const float *srcb = (const float *)srcbrow; \ | ||
| 1991 | const float *srcr = (const float *)srcrrow; \ | ||
| 1992 | const float *srca = (const float *)srcarow; \ | ||
| 1993 | for (x = 0; x < in->width; x++) { \ | ||
| 1994 | float r = av_clipf(sanitizef(srcr[x]) * scale_r, 0.0f, lutsize); \ | ||
| 1995 | float g = av_clipf(sanitizef(srcg[x]) * scale_g, 0.0f, lutsize); \ | ||
| 1996 | float b = av_clipf(sanitizef(srcb[x]) * scale_b, 0.0f, lutsize); \ | ||
| 1997 | r = interp_1d_##name(lut1d, 0, r); \ | ||
| 1998 | g = interp_1d_##name(lut1d, 1, g); \ | ||
| 1999 | b = interp_1d_##name(lut1d, 2, b); \ | ||
| 2000 | dstr[x] = r; \ | ||
| 2001 | dstg[x] = g; \ | ||
| 2002 | dstb[x] = b; \ | ||
| 2003 | if (!direct && in->linesize[3]) \ | ||
| 2004 | dsta[x] = srca[x]; \ | ||
| 2005 | } \ | ||
| 2006 | grow += out->linesize[0]; \ | ||
| 2007 | brow += out->linesize[1]; \ | ||
| 2008 | rrow += out->linesize[2]; \ | ||
| 2009 | arow += out->linesize[3]; \ | ||
| 2010 | srcgrow += in->linesize[0]; \ | ||
| 2011 | srcbrow += in->linesize[1]; \ | ||
| 2012 | srcrrow += in->linesize[2]; \ | ||
| 2013 | srcarow += in->linesize[3]; \ | ||
| 2014 | } \ | ||
| 2015 | return 0; \ | ||
| 2016 | } | ||
| 2017 | |||
| 2018 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(nearest, 32) | |
| 2019 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(linear, 32) | |
| 2020 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(cosine, 32) | |
| 2021 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(cubic, 32) | |
| 2022 | ✗ | DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(spline, 32) | |
| 2023 | |||
| 2024 | #define DEFINE_INTERP_FUNC_1D(name, nbits) \ | ||
| 2025 | static int interp_1d_##nbits##_##name(AVFilterContext *ctx, void *arg, \ | ||
| 2026 | int jobnr, int nb_jobs) \ | ||
| 2027 | { \ | ||
| 2028 | int x, y; \ | ||
| 2029 | const LUT1DContext *lut1d = ctx->priv; \ | ||
| 2030 | const ThreadData *td = arg; \ | ||
| 2031 | const AVFrame *in = td->in; \ | ||
| 2032 | const AVFrame *out = td->out; \ | ||
| 2033 | const int direct = out == in; \ | ||
| 2034 | const int step = lut1d->step; \ | ||
| 2035 | const uint8_t r = lut1d->rgba_map[R]; \ | ||
| 2036 | const uint8_t g = lut1d->rgba_map[G]; \ | ||
| 2037 | const uint8_t b = lut1d->rgba_map[B]; \ | ||
| 2038 | const uint8_t a = lut1d->rgba_map[A]; \ | ||
| 2039 | const int slice_start = (in->height * jobnr ) / nb_jobs; \ | ||
| 2040 | const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \ | ||
| 2041 | uint8_t *dstrow = out->data[0] + slice_start * out->linesize[0]; \ | ||
| 2042 | const uint8_t *srcrow = in ->data[0] + slice_start * in ->linesize[0]; \ | ||
| 2043 | const float factor = (1 << nbits) - 1; \ | ||
| 2044 | const float scale_r = (lut1d->scale.r / factor) * (lut1d->lutsize - 1); \ | ||
| 2045 | const float scale_g = (lut1d->scale.g / factor) * (lut1d->lutsize - 1); \ | ||
| 2046 | const float scale_b = (lut1d->scale.b / factor) * (lut1d->lutsize - 1); \ | ||
| 2047 | \ | ||
| 2048 | for (y = slice_start; y < slice_end; y++) { \ | ||
| 2049 | uint##nbits##_t *dst = (uint##nbits##_t *)dstrow; \ | ||
| 2050 | const uint##nbits##_t *src = (const uint##nbits##_t *)srcrow; \ | ||
| 2051 | for (x = 0; x < in->width * step; x += step) { \ | ||
| 2052 | float rr = src[x + r] * scale_r; \ | ||
| 2053 | float gg = src[x + g] * scale_g; \ | ||
| 2054 | float bb = src[x + b] * scale_b; \ | ||
| 2055 | rr = interp_1d_##name(lut1d, 0, rr); \ | ||
| 2056 | gg = interp_1d_##name(lut1d, 1, gg); \ | ||
| 2057 | bb = interp_1d_##name(lut1d, 2, bb); \ | ||
| 2058 | dst[x + r] = av_clip_uint##nbits(rr * factor); \ | ||
| 2059 | dst[x + g] = av_clip_uint##nbits(gg * factor); \ | ||
| 2060 | dst[x + b] = av_clip_uint##nbits(bb * factor); \ | ||
| 2061 | if (!direct && step == 4) \ | ||
| 2062 | dst[x + a] = src[x + a]; \ | ||
| 2063 | } \ | ||
| 2064 | dstrow += out->linesize[0]; \ | ||
| 2065 | srcrow += in ->linesize[0]; \ | ||
| 2066 | } \ | ||
| 2067 | return 0; \ | ||
| 2068 | } | ||
| 2069 | |||
| 2070 | ✗ | DEFINE_INTERP_FUNC_1D(nearest, 8) | |
| 2071 | ✗ | DEFINE_INTERP_FUNC_1D(linear, 8) | |
| 2072 | ✗ | DEFINE_INTERP_FUNC_1D(cosine, 8) | |
| 2073 | ✗ | DEFINE_INTERP_FUNC_1D(cubic, 8) | |
| 2074 | ✗ | DEFINE_INTERP_FUNC_1D(spline, 8) | |
| 2075 | |||
| 2076 | ✗ | DEFINE_INTERP_FUNC_1D(nearest, 16) | |
| 2077 | ✗ | DEFINE_INTERP_FUNC_1D(linear, 16) | |
| 2078 | ✗ | DEFINE_INTERP_FUNC_1D(cosine, 16) | |
| 2079 | ✗ | DEFINE_INTERP_FUNC_1D(cubic, 16) | |
| 2080 | ✗ | DEFINE_INTERP_FUNC_1D(spline, 16) | |
| 2081 | |||
| 2082 | ✗ | static int config_input_1d(AVFilterLink *inlink) | |
| 2083 | { | ||
| 2084 | int depth, is16bit, isfloat, planar; | ||
| 2085 | ✗ | LUT1DContext *lut1d = inlink->dst->priv; | |
| 2086 | ✗ | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); | |
| 2087 | |||
| 2088 | ✗ | depth = desc->comp[0].depth; | |
| 2089 | ✗ | is16bit = desc->comp[0].depth > 8; | |
| 2090 | ✗ | planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR; | |
| 2091 | ✗ | isfloat = desc->flags & AV_PIX_FMT_FLAG_FLOAT; | |
| 2092 | ✗ | ff_fill_rgba_map(lut1d->rgba_map, inlink->format); | |
| 2093 | ✗ | lut1d->step = av_get_padded_bits_per_pixel(desc) >> (3 + is16bit); | |
| 2094 | |||
| 2095 | #define SET_FUNC_1D(name) do { \ | ||
| 2096 | if (planar && !isfloat) { \ | ||
| 2097 | switch (depth) { \ | ||
| 2098 | case 8: lut1d->interp = interp_1d_8_##name##_p8; break; \ | ||
| 2099 | case 9: lut1d->interp = interp_1d_16_##name##_p9; break; \ | ||
| 2100 | case 10: lut1d->interp = interp_1d_16_##name##_p10; break; \ | ||
| 2101 | case 12: lut1d->interp = interp_1d_16_##name##_p12; break; \ | ||
| 2102 | case 14: lut1d->interp = interp_1d_16_##name##_p14; break; \ | ||
| 2103 | case 16: lut1d->interp = interp_1d_16_##name##_p16; break; \ | ||
| 2104 | } \ | ||
| 2105 | } else if (isfloat) { lut1d->interp = interp_1d_##name##_pf32; \ | ||
| 2106 | } else if (is16bit) { lut1d->interp = interp_1d_16_##name; \ | ||
| 2107 | } else { lut1d->interp = interp_1d_8_##name; } \ | ||
| 2108 | } while (0) | ||
| 2109 | |||
| 2110 | ✗ | switch (lut1d->interpolation) { | |
| 2111 | ✗ | case INTERPOLATE_1D_NEAREST: SET_FUNC_1D(nearest); break; | |
| 2112 | ✗ | case INTERPOLATE_1D_LINEAR: SET_FUNC_1D(linear); break; | |
| 2113 | ✗ | case INTERPOLATE_1D_COSINE: SET_FUNC_1D(cosine); break; | |
| 2114 | ✗ | case INTERPOLATE_1D_CUBIC: SET_FUNC_1D(cubic); break; | |
| 2115 | ✗ | case INTERPOLATE_1D_SPLINE: SET_FUNC_1D(spline); break; | |
| 2116 | ✗ | default: | |
| 2117 | ✗ | av_assert0(0); | |
| 2118 | } | ||
| 2119 | |||
| 2120 | ✗ | return 0; | |
| 2121 | } | ||
| 2122 | |||
| 2123 | ✗ | static av_cold int lut1d_init(AVFilterContext *ctx) | |
| 2124 | { | ||
| 2125 | int ret; | ||
| 2126 | FILE *f; | ||
| 2127 | const char *ext; | ||
| 2128 | ✗ | LUT1DContext *lut1d = ctx->priv; | |
| 2129 | |||
| 2130 | ✗ | lut1d->scale.r = lut1d->scale.g = lut1d->scale.b = 1.f; | |
| 2131 | |||
| 2132 | ✗ | if (!lut1d->file) { | |
| 2133 | ✗ | set_identity_matrix_1d(lut1d, 32); | |
| 2134 | ✗ | return 0; | |
| 2135 | } | ||
| 2136 | |||
| 2137 | ✗ | f = avpriv_fopen_utf8(lut1d->file, "r"); | |
| 2138 | ✗ | if (!f) { | |
| 2139 | ✗ | ret = AVERROR(errno); | |
| 2140 | ✗ | av_log(ctx, AV_LOG_ERROR, "%s: %s\n", lut1d->file, av_err2str(ret)); | |
| 2141 | ✗ | return ret; | |
| 2142 | } | ||
| 2143 | |||
| 2144 | ✗ | ext = strrchr(lut1d->file, '.'); | |
| 2145 | ✗ | if (!ext) { | |
| 2146 | ✗ | av_log(ctx, AV_LOG_ERROR, "Unable to guess the format from the extension\n"); | |
| 2147 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 2148 | ✗ | goto end; | |
| 2149 | } | ||
| 2150 | ✗ | ext++; | |
| 2151 | |||
| 2152 | ✗ | if (!av_strcasecmp(ext, "cube") || !av_strcasecmp(ext, "1dlut")) { | |
| 2153 | ✗ | ret = parse_cube_1d(ctx, f); | |
| 2154 | ✗ | } else if (!av_strcasecmp(ext, "csp")) { | |
| 2155 | ✗ | ret = parse_cinespace_1d(ctx, f); | |
| 2156 | } else { | ||
| 2157 | ✗ | av_log(ctx, AV_LOG_ERROR, "Unrecognized '.%s' file type\n", ext); | |
| 2158 | ✗ | ret = AVERROR(EINVAL); | |
| 2159 | } | ||
| 2160 | |||
| 2161 | ✗ | if (!ret && !lut1d->lutsize) { | |
| 2162 | ✗ | av_log(ctx, AV_LOG_ERROR, "1D LUT is empty\n"); | |
| 2163 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 2164 | } | ||
| 2165 | |||
| 2166 | ✗ | end: | |
| 2167 | ✗ | fclose(f); | |
| 2168 | ✗ | return ret; | |
| 2169 | } | ||
| 2170 | |||
| 2171 | ✗ | static AVFrame *apply_1d_lut(AVFilterLink *inlink, AVFrame *in) | |
| 2172 | { | ||
| 2173 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 2174 | ✗ | LUT1DContext *lut1d = ctx->priv; | |
| 2175 | ✗ | AVFilterLink *outlink = inlink->dst->outputs[0]; | |
| 2176 | AVFrame *out; | ||
| 2177 | ThreadData td; | ||
| 2178 | |||
| 2179 | ✗ | if (av_frame_is_writable(in)) { | |
| 2180 | ✗ | out = in; | |
| 2181 | } else { | ||
| 2182 | ✗ | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
| 2183 | ✗ | if (!out) { | |
| 2184 | ✗ | av_frame_free(&in); | |
| 2185 | ✗ | return NULL; | |
| 2186 | } | ||
| 2187 | ✗ | av_frame_copy_props(out, in); | |
| 2188 | } | ||
| 2189 | |||
| 2190 | ✗ | td.in = in; | |
| 2191 | ✗ | td.out = out; | |
| 2192 | ✗ | ff_filter_execute(ctx, lut1d->interp, &td, NULL, | |
| 2193 | ✗ | FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); | |
| 2194 | |||
| 2195 | ✗ | if (out != in) | |
| 2196 | ✗ | av_frame_free(&in); | |
| 2197 | |||
| 2198 | ✗ | return out; | |
| 2199 | } | ||
| 2200 | |||
| 2201 | ✗ | static int filter_frame_1d(AVFilterLink *inlink, AVFrame *in) | |
| 2202 | { | ||
| 2203 | ✗ | AVFilterLink *outlink = inlink->dst->outputs[0]; | |
| 2204 | ✗ | AVFrame *out = apply_1d_lut(inlink, in); | |
| 2205 | ✗ | if (!out) | |
| 2206 | ✗ | return AVERROR(ENOMEM); | |
| 2207 | ✗ | return ff_filter_frame(outlink, out); | |
| 2208 | } | ||
| 2209 | |||
| 2210 | ✗ | static int lut1d_process_command(AVFilterContext *ctx, const char *cmd, const char *args, | |
| 2211 | char *res, int res_len, int flags) | ||
| 2212 | { | ||
| 2213 | ✗ | LUT1DContext *lut1d = ctx->priv; | |
| 2214 | int ret; | ||
| 2215 | |||
| 2216 | ✗ | ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); | |
| 2217 | ✗ | if (ret < 0) | |
| 2218 | ✗ | return ret; | |
| 2219 | |||
| 2220 | ✗ | ret = lut1d_init(ctx); | |
| 2221 | ✗ | if (ret < 0) { | |
| 2222 | ✗ | set_identity_matrix_1d(lut1d, 32); | |
| 2223 | ✗ | return ret; | |
| 2224 | } | ||
| 2225 | ✗ | return config_input_1d(ctx->inputs[0]); | |
| 2226 | } | ||
| 2227 | |||
| 2228 | static const AVFilterPad lut1d_inputs[] = { | ||
| 2229 | { | ||
| 2230 | .name = "default", | ||
| 2231 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 2232 | .filter_frame = filter_frame_1d, | ||
| 2233 | .config_props = config_input_1d, | ||
| 2234 | }, | ||
| 2235 | }; | ||
| 2236 | |||
| 2237 | const FFFilter ff_vf_lut1d = { | ||
| 2238 | .p.name = "lut1d", | ||
| 2239 | .p.description = NULL_IF_CONFIG_SMALL("Adjust colors using a 1D LUT."), | ||
| 2240 | .p.priv_class = &lut1d_class, | ||
| 2241 | .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | | ||
| 2242 | AVFILTER_FLAG_SLICE_THREADS, | ||
| 2243 | .priv_size = sizeof(LUT1DContext), | ||
| 2244 | .init = lut1d_init, | ||
| 2245 | FILTER_INPUTS(lut1d_inputs), | ||
| 2246 | FILTER_OUTPUTS(ff_video_default_filterpad), | ||
| 2247 | FILTER_PIXFMTS_ARRAY(pix_fmts), | ||
| 2248 | .process_command = lut1d_process_command, | ||
| 2249 | }; | ||
| 2250 | #endif | ||
| 2251 |