vf_hqx.c 24 KB

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