| 1 | /* |
| 2 | * Copyright (c) 2014 Clément Bœsch |
| 3 | * |
| 4 | * This file is part of FFmpeg. |
| 5 | * |
| 6 | * Permission to use, copy, modify, and/or distribute this software for any |
| 7 | * purpose with or without fee is hereby granted, provided that the above |
| 8 | * copyright notice and this permission notice appear in all copies. |
| 9 | * |
| 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 17 | */ |
| 18 | |
| 19 | /** |
| 20 | * @file |
| 21 | * hqx magnification filters (hq2x, hq3x, hq4x) |
| 22 | * |
| 23 | * Originally designed by Maxim Stephin. |
| 24 | * |
| 25 | * @see http://en.wikipedia.org/wiki/Hqx |
| 26 | * @see http://web.archive.org/web/20131114143602/http://www.hiend3d.com/hq3x.html |
| 27 | * @see http://blog.pkh.me/p/19-butchering-hqx-scaling-filters.html |
| 28 | */ |
| 29 | |
| 30 | #include "libavutil/opt.h" |
| 31 | #include "libavutil/avassert.h" |
| 32 | #include "libavutil/pixdesc.h" |
| 33 | #include "internal.h" |
| 34 | |
| 35 | typedef int (*hqxfunc_t)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); |
| 36 | |
| 37 | typedef struct { |
| 38 | const AVClass *class; |
| 39 | int n; |
| 40 | hqxfunc_t func; |
| 41 | uint32_t rgbtoyuv[1<<24]; |
| 42 | } HQXContext; |
| 43 | |
| 44 | typedef struct ThreadData { |
| 45 | AVFrame *in, *out; |
| 46 | const uint32_t *rgbtoyuv; |
| 47 | } ThreadData; |
| 48 | |
| 49 | #define OFFSET(x) offsetof(HQXContext, x) |
| 50 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM |
| 51 | static const AVOption hqx_options[] = { |
| 52 | { "n", "set scale factor", OFFSET(n), AV_OPT_TYPE_INT, {.i64 = 3}, 2, 4, .flags = FLAGS }, |
| 53 | { NULL } |
| 54 | }; |
| 55 | |
| 56 | AVFILTER_DEFINE_CLASS(hqx); |
| 57 | |
| 58 | static av_always_inline uint32_t rgb2yuv(const uint32_t *r2y, uint32_t c) |
| 59 | { |
| 60 | return r2y[c & 0xffffff]; |
| 61 | } |
| 62 | |
| 63 | static av_always_inline int yuv_diff(uint32_t yuv1, uint32_t yuv2) |
| 64 | { |
| 65 | #define YMASK 0xff0000 |
| 66 | #define UMASK 0x00ff00 |
| 67 | #define VMASK 0x0000ff |
| 68 | return abs((yuv1 & YMASK) - (yuv2 & YMASK)) > (48 << 16) || |
| 69 | abs((yuv1 & UMASK) - (yuv2 & UMASK)) > ( 7 << 8) || |
| 70 | abs((yuv1 & VMASK) - (yuv2 & VMASK)) > ( 6 << 0); |
| 71 | } |
| 72 | |
| 73 | /* (c1*w1 + c2*w2) >> s */ |
| 74 | static av_always_inline uint32_t interp_2px(uint32_t c1, int w1, uint32_t c2, int w2, int s) |
| 75 | { |
| 76 | return (((((c1 & 0xff00ff00) >> 8) * w1 + ((c2 & 0xff00ff00) >> 8) * w2) << (8 - s)) & 0xff00ff00) | |
| 77 | (((((c1 & 0x00ff00ff) ) * w1 + ((c2 & 0x00ff00ff) ) * w2) >> s ) & 0x00ff00ff); |
| 78 | } |
| 79 | |
| 80 | /* (c1*w1 + c2*w2 + c3*w3) >> s */ |
| 81 | static av_always_inline uint32_t interp_3px(uint32_t c1, int w1, uint32_t c2, int w2, uint32_t c3, int w3, int s) |
| 82 | { |
| 83 | return (((((c1 & 0xff00ff00) >> 8) * w1 + ((c2 & 0xff00ff00) >> 8) * w2 + ((c3 & 0xff00ff00) >> 8) * w3) << (8 - s)) & 0xff00ff00) | |
| 84 | (((((c1 & 0x00ff00ff) ) * w1 + ((c2 & 0x00ff00ff) ) * w2 + ((c3 & 0x00ff00ff) ) * w3) >> s ) & 0x00ff00ff); |
| 85 | } |
| 86 | |
| 87 | /* m is the mask of diff with the center pixel that matters in the pattern, and |
| 88 | * r is the expected result (bit set to 1 if there is difference with the |
| 89 | * center, 0 otherwise) */ |
| 90 | #define P(m, r) ((k_shuffled & (m)) == (r)) |
| 91 | |
| 92 | /* adjust 012345678 to 01235678: the mask doesn't contain the (null) diff |
| 93 | * between the center/current pixel and itself */ |
| 94 | #define DROP4(z) ((z) > 4 ? (z)-1 : (z)) |
| 95 | |
| 96 | /* shuffle the input mask: move bit n (4-adjusted) to position stored in p<n> */ |
| 97 | #define SHF(x, rot, n) (((x) >> ((rot) ? 7-DROP4(n) : DROP4(n)) & 1) << DROP4(p##n)) |
| 98 | |
| 99 | /* used to check if there is YUV difference between 2 pixels */ |
| 100 | #define WDIFF(c1, c2) yuv_diff(rgb2yuv(r2y, c1), rgb2yuv(r2y, c2)) |
| 101 | |
| 102 | /* bootstrap template for every interpolation code. It defines the shuffled |
| 103 | * masks and surrounding pixels. The rot flag is used to indicate if it's a |
| 104 | * rotation; its basic effect is to shuffle k using p8..p0 instead of p0..p8 */ |
| 105 | #define INTERP_BOOTSTRAP(rot) \ |
| 106 | const int k_shuffled = SHF(k,rot,0) | SHF(k,rot,1) | SHF(k,rot,2) \ |
| 107 | | SHF(k,rot,3) | 0 | SHF(k,rot,5) \ |
| 108 | | SHF(k,rot,6) | SHF(k,rot,7) | SHF(k,rot,8); \ |
| 109 | \ |
| 110 | const uint32_t w0 = w[p0], w1 = w[p1], \ |
| 111 | w3 = w[p3], w4 = w[p4], w5 = w[p5], \ |
| 112 | w7 = w[p7] |
| 113 | |
| 114 | /* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the |
| 115 | * top-left pixel in the total of the 2x2 pixels to interpolates. The function |
| 116 | * is also used for the 3 other pixels */ |
| 117 | static av_always_inline uint32_t hq2x_interp_1x1(const uint32_t *r2y, int k, |
| 118 | const uint32_t *w, |
| 119 | int p0, int p1, int p2, |
| 120 | int p3, int p4, int p5, |
| 121 | int p6, int p7, int p8) |
| 122 | { |
| 123 | INTERP_BOOTSTRAP(0); |
| 124 | |
| 125 | if ((P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5)) |
| 126 | return interp_2px(w4, 3, w3, 1, 2); |
| 127 | if ((P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3)) |
| 128 | return interp_2px(w4, 3, w1, 1, 2); |
| 129 | if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1)) |
| 130 | return w4; |
| 131 | if ((P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) || P(0xdf,0x5a) || |
| 132 | P(0x9f,0x8a) || P(0xcf,0x8a) || P(0xef,0x4e) || P(0x3f,0x0e) || |
| 133 | P(0xfb,0x5a) || P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) || |
| 134 | P(0xeb,0x8a)) && WDIFF(w3, w1)) |
| 135 | return interp_2px(w4, 3, w0, 1, 2); |
| 136 | if (P(0x0b,0x08)) |
| 137 | return interp_3px(w4, 2, w0, 1, w1, 1, 2); |
| 138 | if (P(0x0b,0x02)) |
| 139 | return interp_3px(w4, 2, w0, 1, w3, 1, 2); |
| 140 | if (P(0x2f,0x2f)) |
| 141 | return interp_3px(w4, 14, w3, 1, w1, 1, 4); |
| 142 | if (P(0xbf,0x37) || P(0xdb,0x13)) |
| 143 | return interp_3px(w4, 5, w1, 2, w3, 1, 3); |
| 144 | if (P(0xdb,0x49) || P(0xef,0x6d)) |
| 145 | return interp_3px(w4, 5, w3, 2, w1, 1, 3); |
| 146 | if (P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) || P(0x6b,0x43)) |
| 147 | return interp_2px(w4, 3, w3, 1, 2); |
| 148 | if (P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) || P(0x3b,0x19)) |
| 149 | return interp_2px(w4, 3, w1, 1, 2); |
| 150 | if (P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7e,0x0e)) |
| 151 | return interp_3px(w4, 2, w3, 3, w1, 3, 3); |
| 152 | if (P(0xfb,0x6a) || P(0x6f,0x6e) || P(0x3f,0x3e) || P(0xfb,0xfa) || |
| 153 | P(0xdf,0xde) || P(0xdf,0x1e)) |
| 154 | return interp_2px(w4, 3, w0, 1, 2); |
| 155 | if (P(0x0a,0x00) || P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) || |
| 156 | P(0xbe,0x0a) || P(0xee,0x0a) || P(0x7e,0x0a) || P(0xeb,0x4b) || |
| 157 | P(0x3b,0x1b)) |
| 158 | return interp_3px(w4, 2, w3, 1, w1, 1, 2); |
| 159 | return interp_3px(w4, 6, w3, 1, w1, 1, 3); |
| 160 | } |
| 161 | |
| 162 | /* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the |
| 163 | * top-left and top-center pixel in the total of the 3x3 pixels to |
| 164 | * interpolates. The function is also used for the 3 other couples of pixels |
| 165 | * defining the outline. The center pixel is not defined through this function, |
| 166 | * since it's just the same as the original value. */ |
| 167 | static av_always_inline void hq3x_interp_2x1(uint32_t *dst, int dst_linesize, |
| 168 | const uint32_t *r2y, int k, |
| 169 | const uint32_t *w, |
| 170 | int pos00, int pos01, |
| 171 | int p0, int p1, int p2, |
| 172 | int p3, int p4, int p5, |
| 173 | int p6, int p7, int p8, |
| 174 | int rotate) |
| 175 | { |
| 176 | INTERP_BOOTSTRAP(rotate); |
| 177 | |
| 178 | uint32_t *dst00 = &dst[dst_linesize*(pos00>>1) + (pos00&1)]; |
| 179 | uint32_t *dst01 = &dst[dst_linesize*(pos01>>1) + (pos01&1)]; |
| 180 | |
| 181 | if ((P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3)) |
| 182 | *dst00 = interp_2px(w4, 3, w1, 1, 2); |
| 183 | else if ((P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5)) |
| 184 | *dst00 = interp_2px(w4, 3, w3, 1, 2); |
| 185 | else if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1)) |
| 186 | *dst00 = w4; |
| 187 | else if ((P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) || P(0xdf,0x5a) || |
| 188 | P(0x9f,0x8a) || P(0xcf,0x8a) || P(0xef,0x4e) || P(0x3f,0x0e) || |
| 189 | P(0xfb,0x5a) || P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) || |
| 190 | P(0xeb,0x8a)) && WDIFF(w3, w1)) |
| 191 | *dst00 = interp_2px(w4, 3, w0, 1, 2); |
| 192 | else if (P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) || P(0x3b,0x19)) |
| 193 | *dst00 = interp_2px(w4, 3, w1, 1, 2); |
| 194 | else if (P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) || P(0x6b,0x43)) |
| 195 | *dst00 = interp_2px(w4, 3, w3, 1, 2); |
| 196 | else if (P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7e,0x0e)) |
| 197 | *dst00 = interp_2px(w3, 1, w1, 1, 1); |
| 198 | else if (P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) || P(0xbe,0x0a) || |
| 199 | P(0xee,0x0a) || P(0x7e,0x0a) || P(0xeb,0x4b) || P(0x3b,0x1b)) |
| 200 | *dst00 = interp_3px(w4, 2, w3, 7, w1, 7, 4); |
| 201 | else if (P(0x0b,0x08) || P(0xf9,0x68) || P(0xf3,0x62) || P(0x6d,0x6c) || |
| 202 | P(0x67,0x66) || P(0x3d,0x3c) || P(0x37,0x36) || P(0xf9,0xf8) || |
| 203 | P(0xdd,0xdc) || P(0xf3,0xf2) || P(0xd7,0xd6) || P(0xdd,0x1c) || |
| 204 | P(0xd7,0x16) || P(0x0b,0x02)) |
| 205 | *dst00 = interp_2px(w4, 3, w0, 1, 2); |
| 206 | else |
| 207 | *dst00 = interp_3px(w4, 2, w3, 1, w1, 1, 2); |
| 208 | |
| 209 | if ((P(0xfe,0xde) || P(0x9e,0x16) || P(0xda,0x12) || P(0x17,0x16) || |
| 210 | P(0x5b,0x12) || P(0xbb,0x12)) && WDIFF(w1, w5)) |
| 211 | *dst01 = w4; |
| 212 | else if ((P(0x0f,0x0b) || P(0x5e,0x0a) || P(0xfb,0x7b) || P(0x3b,0x0b) || |
| 213 | P(0xbe,0x0a) || P(0x7a,0x0a)) && WDIFF(w3, w1)) |
| 214 | *dst01 = w4; |
| 215 | else if (P(0xbf,0x8f) || P(0x7e,0x0e) || P(0xbf,0x37) || P(0xdb,0x13)) |
| 216 | *dst01 = interp_2px(w1, 3, w4, 1, 2); |
| 217 | else if (P(0x02,0x00) || P(0x7c,0x28) || P(0xed,0xa9) || P(0xf5,0xb4) || |
| 218 | P(0xd9,0x90)) |
| 219 | *dst01 = interp_2px(w4, 3, w1, 1, 2); |
| 220 | else if (P(0x4f,0x4b) || P(0xfb,0x7b) || P(0xfe,0x7e) || P(0x9f,0x1b) || |
| 221 | P(0x2f,0x0b) || P(0xbe,0x0a) || P(0x7e,0x0a) || P(0xfb,0x4b) || |
| 222 | P(0xfb,0xdb) || P(0xfe,0xde) || P(0xfe,0x56) || P(0x57,0x56) || |
| 223 | P(0x97,0x16) || P(0x3f,0x1e) || P(0xdb,0x12) || P(0xbb,0x12)) |
| 224 | *dst01 = interp_2px(w4, 7, w1, 1, 3); |
| 225 | else |
| 226 | *dst01 = w4; |
| 227 | } |
| 228 | |
| 229 | /* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the |
| 230 | * top-left block of 2x2 pixels in the total of the 4x4 pixels (or 4 blocks) to |
| 231 | * interpolates. The function is also used for the 3 other blocks of 2x2 |
| 232 | * pixels. */ |
| 233 | static av_always_inline void hq4x_interp_2x2(uint32_t *dst, int dst_linesize, |
| 234 | const uint32_t *r2y, int k, |
| 235 | const uint32_t *w, |
| 236 | int pos00, int pos01, |
| 237 | int pos10, int pos11, |
| 238 | int p0, int p1, int p2, |
| 239 | int p3, int p4, int p5, |
| 240 | int p6, int p7, int p8) |
| 241 | { |
| 242 | INTERP_BOOTSTRAP(0); |
| 243 | |
| 244 | uint32_t *dst00 = &dst[dst_linesize*(pos00>>1) + (pos00&1)]; |
| 245 | uint32_t *dst01 = &dst[dst_linesize*(pos01>>1) + (pos01&1)]; |
| 246 | uint32_t *dst10 = &dst[dst_linesize*(pos10>>1) + (pos10&1)]; |
| 247 | uint32_t *dst11 = &dst[dst_linesize*(pos11>>1) + (pos11&1)]; |
| 248 | |
| 249 | const int cond00 = (P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5); |
| 250 | const int cond01 = (P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3); |
| 251 | const int cond02 = (P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) || |
| 252 | P(0xdf,0x5a) || P(0x9f,0x8a) || P(0xcf,0x8a) || |
| 253 | P(0xef,0x4e) || P(0x3f,0x0e) || P(0xfb,0x5a) || |
| 254 | P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) || |
| 255 | P(0xeb,0x8a)) && WDIFF(w3, w1); |
| 256 | const int cond03 = P(0xdb,0x49) || P(0xef,0x6d); |
| 257 | const int cond04 = P(0xbf,0x37) || P(0xdb,0x13); |
| 258 | const int cond05 = P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) || |
| 259 | P(0x6b,0x43); |
| 260 | const int cond06 = P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) || |
| 261 | P(0x3b,0x19); |
| 262 | const int cond07 = P(0x0b,0x08) || P(0xf9,0x68) || P(0xf3,0x62) || |
| 263 | P(0x6d,0x6c) || P(0x67,0x66) || P(0x3d,0x3c) || |
| 264 | P(0x37,0x36) || P(0xf9,0xf8) || P(0xdd,0xdc) || |
| 265 | P(0xf3,0xf2) || P(0xd7,0xd6) || P(0xdd,0x1c) || |
| 266 | P(0xd7,0x16) || P(0x0b,0x02); |
| 267 | const int cond08 = (P(0x0f,0x0b) || P(0x2b,0x0b) || P(0xfe,0x4a) || |
| 268 | P(0xfe,0x1a)) && WDIFF(w3, w1); |
| 269 | const int cond09 = P(0x2f,0x2f); |
| 270 | const int cond10 = P(0x0a,0x00); |
| 271 | const int cond11 = P(0x0b,0x09); |
| 272 | const int cond12 = P(0x7e,0x2a) || P(0xef,0xab); |
| 273 | const int cond13 = P(0xbf,0x8f) || P(0x7e,0x0e); |
| 274 | const int cond14 = P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) || |
| 275 | P(0xbe,0x0a) || P(0xee,0x0a) || P(0x7e,0x0a) || |
| 276 | P(0xeb,0x4b) || P(0x3b,0x1b); |
| 277 | const int cond15 = P(0x0b,0x03); |
| 278 | |
| 279 | if (cond00) |
| 280 | *dst00 = interp_2px(w4, 5, w3, 3, 3); |
| 281 | else if (cond01) |
| 282 | *dst00 = interp_2px(w4, 5, w1, 3, 3); |
| 283 | else if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1)) |
| 284 | *dst00 = w4; |
| 285 | else if (cond02) |
| 286 | *dst00 = interp_2px(w4, 5, w0, 3, 3); |
| 287 | else if (cond03) |
| 288 | *dst00 = interp_2px(w4, 3, w3, 1, 2); |
| 289 | else if (cond04) |
| 290 | *dst00 = interp_2px(w4, 3, w1, 1, 2); |
| 291 | else if (cond05) |
| 292 | *dst00 = interp_2px(w4, 5, w3, 3, 3); |
| 293 | else if (cond06) |
| 294 | *dst00 = interp_2px(w4, 5, w1, 3, 3); |
| 295 | else if (P(0x0f,0x0b) || P(0x5e,0x0a) || P(0x2b,0x0b) || P(0xbe,0x0a) || |
| 296 | P(0x7a,0x0a) || P(0xee,0x0a)) |
| 297 | *dst00 = interp_2px(w1, 1, w3, 1, 1); |
| 298 | else if (cond07) |
| 299 | *dst00 = interp_2px(w4, 5, w0, 3, 3); |
| 300 | else |
| 301 | *dst00 = interp_3px(w4, 2, w1, 1, w3, 1, 2); |
| 302 | |
| 303 | if (cond00) |
| 304 | *dst01 = interp_2px(w4, 7, w3, 1, 3); |
| 305 | else if (cond08) |
| 306 | *dst01 = w4; |
| 307 | else if (cond02) |
| 308 | *dst01 = interp_2px(w4, 3, w0, 1, 2); |
| 309 | else if (cond09) |
| 310 | *dst01 = w4; |
| 311 | else if (cond10) |
| 312 | *dst01 = interp_3px(w4, 5, w1, 2, w3, 1, 3); |
| 313 | else if (P(0x0b,0x08)) |
| 314 | *dst01 = interp_3px(w4, 5, w1, 2, w0, 1, 3); |
| 315 | else if (cond11) |
| 316 | *dst01 = interp_2px(w4, 5, w1, 3, 3); |
| 317 | else if (cond04) |
| 318 | *dst01 = interp_2px(w1, 3, w4, 1, 2); |
| 319 | else if (cond12) |
| 320 | *dst01 = interp_3px(w1, 2, w4, 1, w3, 1, 2); |
| 321 | else if (cond13) |
| 322 | *dst01 = interp_2px(w1, 5, w3, 3, 3); |
| 323 | else if (cond05) |
| 324 | *dst01 = interp_2px(w4, 7, w3, 1, 3); |
| 325 | else if (P(0xf3,0x62) || P(0x67,0x66) || P(0x37,0x36) || P(0xf3,0xf2) || |
| 326 | P(0xd7,0xd6) || P(0xd7,0x16) || P(0x0b,0x02)) |
| 327 | *dst01 = interp_2px(w4, 3, w0, 1, 2); |
| 328 | else if (cond14) |
| 329 | *dst01 = interp_2px(w1, 1, w4, 1, 1); |
| 330 | else |
| 331 | *dst01 = interp_2px(w4, 3, w1, 1, 2); |
| 332 | |
| 333 | if (cond01) |
| 334 | *dst10 = interp_2px(w4, 7, w1, 1, 3); |
| 335 | else if (cond08) |
| 336 | *dst10 = w4; |
| 337 | else if (cond02) |
| 338 | *dst10 = interp_2px(w4, 3, w0, 1, 2); |
| 339 | else if (cond09) |
| 340 | *dst10 = w4; |
| 341 | else if (cond10) |
| 342 | *dst10 = interp_3px(w4, 5, w3, 2, w1, 1, 3); |
| 343 | else if (P(0x0b,0x02)) |
| 344 | *dst10 = interp_3px(w4, 5, w3, 2, w0, 1, 3); |
| 345 | else if (cond15) |
| 346 | *dst10 = interp_2px(w4, 5, w3, 3, 3); |
| 347 | else if (cond03) |
| 348 | *dst10 = interp_2px(w3, 3, w4, 1, 2); |
| 349 | else if (cond13) |
| 350 | *dst10 = interp_3px(w3, 2, w4, 1, w1, 1, 2); |
| 351 | else if (cond12) |
| 352 | *dst10 = interp_2px(w3, 5, w1, 3, 3); |
| 353 | else if (cond06) |
| 354 | *dst10 = interp_2px(w4, 7, w1, 1, 3); |
| 355 | else if (P(0x0b,0x08) || P(0xf9,0x68) || P(0x6d,0x6c) || P(0x3d,0x3c) || |
| 356 | P(0xf9,0xf8) || P(0xdd,0xdc) || P(0xdd,0x1c)) |
| 357 | *dst10 = interp_2px(w4, 3, w0, 1, 2); |
| 358 | else if (cond14) |
| 359 | *dst10 = interp_2px(w3, 1, w4, 1, 1); |
| 360 | else |
| 361 | *dst10 = interp_2px(w4, 3, w3, 1, 2); |
| 362 | |
| 363 | if ((P(0x7f,0x2b) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7f,0x0f)) && |
| 364 | WDIFF(w3, w1)) |
| 365 | *dst11 = w4; |
| 366 | else if (cond02) |
| 367 | *dst11 = interp_2px(w4, 7, w0, 1, 3); |
| 368 | else if (cond15) |
| 369 | *dst11 = interp_2px(w4, 7, w3, 1, 3); |
| 370 | else if (cond11) |
| 371 | *dst11 = interp_2px(w4, 7, w1, 1, 3); |
| 372 | else if (P(0x0a,0x00) || P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) || |
| 373 | P(0x7e,0x0e)) |
| 374 | *dst11 = interp_3px(w4, 6, w3, 1, w1, 1, 3); |
| 375 | else if (cond07) |
| 376 | *dst11 = interp_2px(w4, 7, w0, 1, 3); |
| 377 | else |
| 378 | *dst11 = w4; |
| 379 | } |
| 380 | |
| 381 | static av_always_inline void hqx_filter(const ThreadData *td, int jobnr, int nb_jobs, int n) |
| 382 | { |
| 383 | int x, y; |
| 384 | AVFrame *in = td->in, *out = td->out; |
| 385 | const uint32_t *r2y = td->rgbtoyuv; |
| 386 | const int height = in->height; |
| 387 | const int width = in->width; |
| 388 | const int slice_start = (height * jobnr ) / nb_jobs; |
| 389 | const int slice_end = (height * (jobnr+1)) / nb_jobs; |
| 390 | const int dst_linesize = out->linesize[0]; |
| 391 | const int src_linesize = in->linesize[0]; |
| 392 | uint8_t *dst = out->data[0] + slice_start * dst_linesize * n; |
| 393 | const uint8_t *src = in->data[0] + slice_start * src_linesize; |
| 394 | |
| 395 | const int dst32_linesize = dst_linesize >> 2; |
| 396 | const int src32_linesize = src_linesize >> 2; |
| 397 | |
| 398 | for (y = slice_start; y < slice_end; y++) { |
| 399 | const uint32_t *src32 = (const uint32_t *)src; |
| 400 | uint32_t *dst32 = (uint32_t *)dst; |
| 401 | const int prevline = y > 0 ? -src32_linesize : 0; |
| 402 | const int nextline = y < height - 1 ? src32_linesize : 0; |
| 403 | |
| 404 | for (x = 0; x < width; x++) { |
| 405 | const int prevcol = x > 0 ? -1 : 0; |
| 406 | const int nextcol = x < width -1 ? 1 : 0; |
| 407 | const uint32_t w[3*3] = { |
| 408 | src32[prevcol + prevline], src32[prevline], src32[prevline + nextcol], |
| 409 | src32[prevcol ], src32[ 0], src32[ nextcol], |
| 410 | src32[prevcol + nextline], src32[nextline], src32[nextline + nextcol] |
| 411 | }; |
| 412 | const uint32_t yuv1 = rgb2yuv(r2y, w[4]); |
| 413 | const int pattern = (w[4] != w[0] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[0]))) : 0) |
| 414 | | (w[4] != w[1] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[1]))) : 0) << 1 |
| 415 | | (w[4] != w[2] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[2]))) : 0) << 2 |
| 416 | | (w[4] != w[3] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[3]))) : 0) << 3 |
| 417 | | (w[4] != w[5] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[5]))) : 0) << 4 |
| 418 | | (w[4] != w[6] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[6]))) : 0) << 5 |
| 419 | | (w[4] != w[7] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[7]))) : 0) << 6 |
| 420 | | (w[4] != w[8] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[8]))) : 0) << 7; |
| 421 | |
| 422 | if (n == 2) { |
| 423 | dst32[dst32_linesize*0 + 0] = hq2x_interp_1x1(r2y, pattern, w, 0,1,2,3,4,5,6,7,8); // 00 |
| 424 | dst32[dst32_linesize*0 + 1] = hq2x_interp_1x1(r2y, pattern, w, 2,1,0,5,4,3,8,7,6); // 01 (vert mirrored) |
| 425 | dst32[dst32_linesize*1 + 0] = hq2x_interp_1x1(r2y, pattern, w, 6,7,8,3,4,5,0,1,2); // 10 (horiz mirrored) |
| 426 | dst32[dst32_linesize*1 + 1] = hq2x_interp_1x1(r2y, pattern, w, 8,7,6,5,4,3,2,1,0); // 11 (center mirrored) |
| 427 | } else if (n == 3) { |
| 428 | hq3x_interp_2x1(dst32, dst32_linesize, r2y, pattern, w, 0,1, 0,1,2,3,4,5,6,7,8, 0); // 00 01 |
| 429 | hq3x_interp_2x1(dst32 + 1, dst32_linesize, r2y, pattern, w, 1,3, 2,5,8,1,4,7,0,3,6, 1); // 02 12 (rotated to the right) |
| 430 | hq3x_interp_2x1(dst32 + 1*dst32_linesize, dst32_linesize, r2y, pattern, w, 2,0, 6,3,0,7,4,1,8,5,2, 1); // 20 10 (rotated to the left) |
| 431 | hq3x_interp_2x1(dst32 + 1*dst32_linesize + 1, dst32_linesize, r2y, pattern, w, 3,2, 8,7,6,5,4,3,2,1,0, 0); // 22 21 (center mirrored) |
| 432 | dst32[dst32_linesize + 1] = w[4]; // 11 |
| 433 | } else if (n == 4) { |
| 434 | hq4x_interp_2x2(dst32, dst32_linesize, r2y, pattern, w, 0,1,2,3, 0,1,2,3,4,5,6,7,8); // 00 01 10 11 |
| 435 | hq4x_interp_2x2(dst32 + 2, dst32_linesize, r2y, pattern, w, 1,0,3,2, 2,1,0,5,4,3,8,7,6); // 02 03 12 13 (vert mirrored) |
| 436 | hq4x_interp_2x2(dst32 + 2*dst32_linesize, dst32_linesize, r2y, pattern, w, 2,3,0,1, 6,7,8,3,4,5,0,1,2); // 20 21 30 31 (horiz mirrored) |
| 437 | hq4x_interp_2x2(dst32 + 2*dst32_linesize + 2, dst32_linesize, r2y, pattern, w, 3,2,1,0, 8,7,6,5,4,3,2,1,0); // 22 23 32 33 (center mirrored) |
| 438 | } else { |
| 439 | av_assert0(0); |
| 440 | } |
| 441 | |
| 442 | src32 += 1; |
| 443 | dst32 += n; |
| 444 | } |
| 445 | |
| 446 | src += src_linesize; |
| 447 | dst += dst_linesize * n; |
| 448 | } |
| 449 | } |
| 450 | |
| 451 | #define HQX_FUNC(size) \ |
| 452 | static int hq##size##x(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \ |
| 453 | { \ |
| 454 | hqx_filter(arg, jobnr, nb_jobs, size); \ |
| 455 | return 0; \ |
| 456 | } |
| 457 | |
| 458 | HQX_FUNC(2) |
| 459 | HQX_FUNC(3) |
| 460 | HQX_FUNC(4) |
| 461 | |
| 462 | static int query_formats(AVFilterContext *ctx) |
| 463 | { |
| 464 | static const enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE}; |
| 465 | ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); |
| 466 | return 0; |
| 467 | } |
| 468 | |
| 469 | static int config_output(AVFilterLink *outlink) |
| 470 | { |
| 471 | AVFilterContext *ctx = outlink->src; |
| 472 | HQXContext *hqx = ctx->priv; |
| 473 | AVFilterLink *inlink = ctx->inputs[0]; |
| 474 | |
| 475 | outlink->w = inlink->w * hqx->n; |
| 476 | outlink->h = inlink->h * hqx->n; |
| 477 | av_log(inlink->dst, AV_LOG_VERBOSE, "fmt:%s size:%dx%d -> size:%dx%d\n", |
| 478 | av_get_pix_fmt_name(inlink->format), |
| 479 | inlink->w, inlink->h, outlink->w, outlink->h); |
| 480 | return 0; |
| 481 | } |
| 482 | |
| 483 | static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
| 484 | { |
| 485 | AVFilterContext *ctx = inlink->dst; |
| 486 | AVFilterLink *outlink = ctx->outputs[0]; |
| 487 | HQXContext *hqx = ctx->priv; |
| 488 | ThreadData td; |
| 489 | AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
| 490 | if (!out) { |
| 491 | av_frame_free(&in); |
| 492 | return AVERROR(ENOMEM); |
| 493 | } |
| 494 | av_frame_copy_props(out, in); |
| 495 | out->width = outlink->w; |
| 496 | out->height = outlink->h; |
| 497 | |
| 498 | td.in = in; |
| 499 | td.out = out; |
| 500 | td.rgbtoyuv = hqx->rgbtoyuv; |
| 501 | ctx->internal->execute(ctx, hqx->func, &td, NULL, FFMIN(inlink->h, ctx->graph->nb_threads)); |
| 502 | |
| 503 | av_frame_free(&in); |
| 504 | return ff_filter_frame(outlink, out); |
| 505 | } |
| 506 | |
| 507 | static av_cold int init(AVFilterContext *ctx) |
| 508 | { |
| 509 | HQXContext *hqx = ctx->priv; |
| 510 | static const hqxfunc_t hqxfuncs[] = {hq2x, hq3x, hq4x}; |
| 511 | |
| 512 | uint32_t c; |
| 513 | int bg, rg, g; |
| 514 | |
| 515 | for (bg=-255; bg<256; bg++) { |
| 516 | for (rg=-255; rg<256; rg++) { |
| 517 | const uint32_t u = (uint32_t)((-169*rg + 500*bg)/1000) + 128; |
| 518 | const uint32_t v = (uint32_t)(( 500*rg - 81*bg)/1000) + 128; |
| 519 | int startg = FFMAX3(-bg, -rg, 0); |
| 520 | int endg = FFMIN3(255-bg, 255-rg, 255); |
| 521 | uint32_t y = (uint32_t)(( 299*rg + 1000*startg + 114*bg)/1000); |
| 522 | c = bg + (rg<<16) + 0x010101 * startg; |
| 523 | for (g = startg; g <= endg; g++) { |
| 524 | hqx->rgbtoyuv[c] = ((y++) << 16) + (u << 8) + v; |
| 525 | c+= 0x010101; |
| 526 | } |
| 527 | } |
| 528 | } |
| 529 | |
| 530 | hqx->func = hqxfuncs[hqx->n - 2]; |
| 531 | return 0; |
| 532 | } |
| 533 | |
| 534 | static const AVFilterPad hqx_inputs[] = { |
| 535 | { |
| 536 | .name = "default", |
| 537 | .type = AVMEDIA_TYPE_VIDEO, |
| 538 | .filter_frame = filter_frame, |
| 539 | }, |
| 540 | { NULL } |
| 541 | }; |
| 542 | |
| 543 | static const AVFilterPad hqx_outputs[] = { |
| 544 | { |
| 545 | .name = "default", |
| 546 | .type = AVMEDIA_TYPE_VIDEO, |
| 547 | .config_props = config_output, |
| 548 | }, |
| 549 | { NULL } |
| 550 | }; |
| 551 | |
| 552 | AVFilter ff_vf_hqx = { |
| 553 | .name = "hqx", |
| 554 | .description = NULL_IF_CONFIG_SMALL("Scale the input by 2, 3 or 4 using the hq*x magnification algorithm."), |
| 555 | .priv_size = sizeof(HQXContext), |
| 556 | .init = init, |
| 557 | .query_formats = query_formats, |
| 558 | .inputs = hqx_inputs, |
| 559 | .outputs = hqx_outputs, |
| 560 | .priv_class = &hqx_class, |
| 561 | .flags = AVFILTER_FLAG_SLICE_THREADS, |
| 562 | }; |