Quake II RTX doxygen  1.0 dev
hq2x.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 2013 Andrey Nazarov
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18 
19 /*
20 HQ2x filter
21 authors: byuu and blargg
22 license: public domain
23 
24 note: this is a clean reimplementation of the original HQ2x filter, which was
25 written by Maxim Stepin (MaxSt). it is not 100% identical, but very similar.
26 */
27 
28 #include "shared/shared.h"
29 #include "common/cvar.h"
30 #include "refresh/images.h"
31 
32 static const uint8_t hqTable[256] = {
33  1, 1, 2, 4, 1, 1, 2, 4, 3, 5, 7, 8, 3, 5, 13, 15,
34  1, 1, 2, 10, 1, 1, 2, 10, 3, 5, 8, 8, 3, 5, 6, 8,
35  1, 1, 2, 4, 1, 1, 2, 4, 3, 5, 12, 14, 3, 5, 9, 16,
36  1, 1, 2, 10, 1, 1, 2, 10, 3, 5, 9, 8, 3, 5, 6, 16,
37  1, 1, 2, 4, 1, 1, 2, 4, 3, 11, 8, 8, 3, 11, 9, 8,
38  1, 1, 2, 4, 1, 1, 2, 4, 3, 5, 9, 8, 3, 5, 9, 8,
39  1, 1, 2, 4, 1, 1, 2, 4, 3, 11, 6, 8, 3, 11, 6, 16,
40  1, 1, 2, 4, 1, 1, 2, 10, 3, 5, 9, 8, 3, 11, 6, 16,
41  1, 1, 2, 4, 1, 1, 2, 4, 3, 5, 7, 8, 3, 5, 13, 15,
42  1, 1, 2, 4, 1, 1, 2, 4, 3, 5, 9, 8, 3, 5, 9, 8,
43  1, 1, 2, 4, 1, 1, 2, 4, 3, 5, 12, 14, 3, 5, 9, 16,
44  1, 1, 2, 4, 1, 1, 2, 4, 3, 5, 9, 14, 3, 5, 6, 16,
45  1, 1, 2, 4, 1, 1, 2, 4, 3, 5, 9, 8, 3, 5, 9, 15,
46  1, 1, 2, 4, 1, 1, 2, 4, 3, 5, 9, 8, 3, 5, 6, 8,
47  1, 1, 2, 4, 1, 1, 2, 4, 3, 5, 9, 8, 3, 5, 6, 16,
48  1, 1, 2, 4, 1, 1, 2, 4, 3, 5, 6, 8, 3, 5, 6, 16,
49 };
50 
51 static uint8_t rotTable[256];
52 static int32_t yccTable[8][256];
53 static int32_t maxY, maxCb, maxCr;
54 
55 static q_noinline int diff(uint32_t A_u32, uint32_t B_u32)
56 {
57  color_t A, B;
58  int32_t a, b;
59 
60  A.u32 = A_u32;
61  B.u32 = B_u32;
62 
63  if (A.u8[3] == 0 && B.u8[3] == 0)
64  return 0;
65 
66  if (A.u8[3] == 0 || B.u8[3] == 0)
67  return 1;
68 
69  a = yccTable[0][A.u8[0]] + yccTable[1][A.u8[1]] + yccTable[2][A.u8[2]];
70  b = yccTable[0][B.u8[0]] + yccTable[1][B.u8[1]] + yccTable[2][B.u8[2]];
71  if (abs(a - b) > maxY)
72  return 1;
73 
74  a = yccTable[3][A.u8[0]] + yccTable[4][A.u8[1]] + yccTable[5][A.u8[2]];
75  b = yccTable[3][B.u8[0]] + yccTable[4][B.u8[1]] + yccTable[5][B.u8[2]];
76  if (abs(a - b) > maxCb)
77  return 1;
78 
79  a = yccTable[5][A.u8[0]] + yccTable[6][A.u8[1]] + yccTable[7][A.u8[2]];
80  b = yccTable[5][B.u8[0]] + yccTable[6][B.u8[1]] + yccTable[7][B.u8[2]];
81  if (abs(a - b) > maxCr)
82  return 1;
83 
84  return 0;
85 }
86 
87 static inline int same(uint32_t A, uint32_t B)
88 {
89  return !diff(A, B);
90 }
91 
92 static inline uint64_t grow(uint64_t n)
93 {
94  n |= n << 24;
95  n &= UINT64_C(0xff00ff00ff00ff);
96  return n;
97 }
98 
99 static inline uint32_t pack(uint64_t n)
100 {
101  n &= UINT64_C(0xff00ff00ff00ff);
102  n |= n >> 24;
103  return n;
104 }
105 
106 static q_noinline uint32_t blend_1_1(uint32_t A, uint32_t B)
107 {
108  return pack((grow(A) + grow(B)) >> 1);
109 }
110 
111 static q_noinline uint32_t blend_3_1(uint32_t A, uint32_t B)
112 {
113  return pack((grow(A) * 3 + grow(B)) >> 2);
114 }
115 
116 static q_noinline uint32_t blend_7_1(uint32_t A, uint32_t B)
117 {
118  return pack((grow(A) * 7 + grow(B)) >> 3);
119 }
120 
121 static q_noinline uint32_t blend_5_3(uint32_t A, uint32_t B)
122 {
123  return pack((grow(A) * 5 + grow(B) * 3) >> 3);
124 }
125 
126 static q_noinline uint32_t blend_2_1_1(uint32_t A, uint32_t B, uint32_t C)
127 {
128  return pack((grow(A) * 2 + grow(B) + grow(C)) >> 2);
129 }
130 
131 static q_noinline uint32_t blend_5_2_1(uint32_t A, uint32_t B, uint32_t C)
132 {
133  return pack((grow(A) * 5 + grow(B) * 2 + grow(C)) >> 3);
134 }
135 
136 static q_noinline uint32_t blend_6_1_1(uint32_t A, uint32_t B, uint32_t C)
137 {
138  return pack((grow(A) * 6 + grow(B) + grow(C)) >> 3);
139 }
140 
141 static q_noinline uint32_t blend_2_3_3(uint32_t A, uint32_t B, uint32_t C)
142 {
143  return pack((grow(A) * 2 + (grow(B) + grow(C)) * 3) >> 3);
144 }
145 
146 static q_noinline uint32_t blend_14_1_1(uint32_t A, uint32_t B, uint32_t C)
147 {
148  return pack((grow(A) * 14 + grow(B) + grow(C)) >> 4);
149 }
150 
151 static q_noinline uint32_t hq2x_blend(int rule, uint32_t E, uint32_t A, uint32_t B, uint32_t D, uint32_t F, uint32_t H)
152 {
153  switch (rule) {
154  case 1:
155  return blend_2_1_1(E, D, B);
156  case 2:
157  return blend_2_1_1(E, A, D);
158  case 3:
159  return blend_2_1_1(E, A, B);
160  case 4:
161  return blend_3_1(E, D);
162  case 5:
163  return blend_3_1(E, B);
164  case 6:
165  return blend_3_1(E, A);
166  case 7:
167  return same(B, D) ? blend_2_1_1(E, D, B) : blend_3_1(E, A);
168  case 8:
169  return same(B, D) ? blend_2_1_1(E, D, B) : E;
170  case 9:
171  return same(B, D) ? blend_6_1_1(E, D, B) : blend_3_1(E, A);
172  case 10:
173  return same(B, F) ? blend_5_2_1(E, B, D) : blend_3_1(E, D);
174  case 11:
175  return same(D, H) ? blend_5_2_1(E, D, B) : blend_3_1(E, B);
176  case 12:
177  case 13:
178  return same(B, D) ? blend_2_3_3(E, D, B) : blend_3_1(E, A);
179  case 14:
180  case 15:
181  return same(B, D) ? blend_2_3_3(E, D, B) : E;
182  case 16:
183  return same(B, D) ? blend_14_1_1(E, D, B) : E;
184  default:
185  Com_Error(ERR_FATAL, "%s: bad rule %d", __func__, rule);
186  return 0;
187  }
188 }
189 
190 static q_noinline void hq4x_blend(int rule, uint32_t *p00, uint32_t *p01, uint32_t *p10, uint32_t *p11,
191  uint32_t E, uint32_t A, uint32_t B, uint32_t D, uint32_t F, uint32_t H)
192 {
193  switch (rule) {
194  case 1:
195  *p00 = blend_2_1_1(E, B, D);
196  *p01 = blend_5_2_1(E, B, D);
197  *p10 = blend_5_2_1(E, D, B);
198  *p11 = blend_6_1_1(E, D, B);
199  break;
200  case 2:
201  *p00 = blend_5_3(E, A);
202  *p01 = blend_3_1(E, A);
203  *p10 = blend_5_2_1(E, D, A);
204  *p11 = blend_7_1(E, A);
205  break;
206  case 3:
207  *p00 = blend_5_3(E, A);
208  *p01 = blend_5_2_1(E, B, A);
209  *p10 = blend_3_1(E, A);
210  *p11 = blend_7_1(E, A);
211  break;
212  case 4:
213  *p00 = blend_5_3(E, D);
214  *p01 = blend_7_1(E, D);
215  *p10 = blend_5_3(E, D);
216  *p11 = blend_7_1(E, D);
217  break;
218  case 5:
219  *p00 = blend_5_3(E, B);
220  *p01 = blend_5_3(E, B);
221  *p10 = blend_7_1(E, B);
222  *p11 = blend_7_1(E, B);
223  break;
224  case 6:
225  *p00 = blend_5_3(E, A);
226  *p01 = blend_3_1(E, A);
227  *p10 = blend_3_1(E, A);
228  *p11 = blend_7_1(E, A);
229  break;
230  case 7:
231  if (same(B, D)) {
232  *p00 = blend_1_1(B, D);
233  *p01 = blend_1_1(B, E);
234  *p10 = blend_1_1(D, E);
235  *p11 = E;
236  } else {
237  *p00 = blend_5_3(E, A);
238  *p01 = blend_3_1(E, A);
239  *p10 = blend_3_1(E, A);
240  *p11 = blend_7_1(E, A);
241  }
242  break;
243  case 8:
244  if (same(B, D)) {
245  *p00 = blend_1_1(B, D);
246  *p01 = blend_1_1(B, E);
247  *p10 = blend_1_1(D, E);
248  } else {
249  *p00 = E;
250  *p01 = E;
251  *p10 = E;
252  }
253  *p11 = E;
254  break;
255  case 9:
256  if (same(B, D)) {
257  *p00 = blend_2_1_1(E, B, D);
258  *p01 = blend_3_1(E, B);
259  *p10 = blend_3_1(E, D);
260  *p11 = E;
261  } else {
262  *p00 = blend_5_3(E, A);
263  *p01 = blend_3_1(E, A);
264  *p10 = blend_3_1(E, A);
265  *p11 = blend_7_1(E, A);
266  }
267  break;
268  case 10:
269  if (same(B, F)) {
270  *p00 = blend_3_1(E, B);
271  *p01 = blend_3_1(B, E);
272  } else {
273  *p00 = blend_5_3(E, D);
274  *p01 = blend_7_1(E, D);
275  }
276  *p10 = blend_5_3(E, D);
277  *p11 = blend_7_1(E, D);
278  break;
279  case 11:
280  if (same(D, H)) {
281  *p00 = blend_3_1(E, D);
282  *p10 = blend_3_1(D, E);
283  } else {
284  *p00 = blend_5_3(E, B);
285  *p10 = blend_7_1(E, B);
286  }
287  *p01 = blend_5_3(E, B);
288  *p11 = blend_7_1(E, B);
289  break;
290  case 12:
291  if (same(B, D)) {
292  *p00 = blend_1_1(B, D);
293  *p01 = blend_2_1_1(B, E, D);
294  *p10 = blend_5_3(D, B);
295  *p11 = blend_6_1_1(E, D, B);
296  } else {
297  *p00 = blend_5_3(E, A);
298  *p01 = blend_3_1(E, A);
299  *p10 = blend_3_1(E, A);
300  *p11 = blend_7_1(E, A);
301  }
302  break;
303  case 13:
304  if (same(B, D)) {
305  *p00 = blend_1_1(B, D);
306  *p01 = blend_5_3(B, D);
307  *p10 = blend_2_1_1(D, E, B);
308  *p11 = blend_6_1_1(E, D, B);
309  } else {
310  *p00 = blend_5_3(E, A);
311  *p01 = blend_3_1(E, A);
312  *p10 = blend_3_1(E, A);
313  *p11 = blend_7_1(E, A);
314  }
315  break;
316  case 14:
317  if (same(B, D)) {
318  *p00 = blend_1_1(B, D);
319  *p01 = blend_2_1_1(B, E, D);
320  *p10 = blend_5_3(D, B);
321  *p11 = blend_6_1_1(E, D, B);
322  } else {
323  *p00 = E;
324  *p01 = E;
325  *p10 = E;
326  *p11 = E;
327  }
328  break;
329  case 15:
330  if (same(B, D)) {
331  *p00 = blend_1_1(B, D);
332  *p01 = blend_5_3(B, D);
333  *p10 = blend_2_1_1(D, E, B);
334  *p11 = blend_6_1_1(E, D, B);
335  } else {
336  *p00 = E;
337  *p01 = E;
338  *p10 = E;
339  *p11 = E;
340  }
341  break;
342  case 16:
343  if (same(B, D))
344  *p00 = blend_2_1_1(E, B, D);
345  else
346  *p00 = E;
347  *p01 = E;
348  *p10 = E;
349  *p11 = E;
350  break;
351  default:
352  Com_Error(ERR_FATAL, "%s: bad rule %d", __func__, rule);
353  break;
354  }
355 }
356 
357 void HQ2x_Render(uint32_t *output, const uint32_t *input, int width, int height)
358 {
359  int x, y;
360 
361  for (y = 0; y < height; y++) {
362  const uint32_t *in = input + y * width;
363  uint32_t *out0 = output + (y * 2 + 0) * width * 2;
364  uint32_t *out1 = output + (y * 2 + 1) * width * 2;
365 
366  int prevline = (y == 0 ? 0 : width);
367  int nextline = (y == height - 1 ? 0 : width);
368 
369  for (x = 0; x < width; x++) {
370  int prev = (x == 0 ? 0 : 1);
371  int next = (x == width - 1 ? 0 : 1);
372 
373  uint32_t A = *(in - prevline - prev);
374  uint32_t B = *(in - prevline);
375  uint32_t C = *(in - prevline + next);
376  uint32_t D = *(in - prev);
377  uint32_t E = *(in);
378  uint32_t F = *(in + next);
379  uint32_t G = *(in + nextline - prev);
380  uint32_t H = *(in + nextline);
381  uint32_t I = *(in + nextline + next);
382 
383  int pattern;
384  pattern = diff(E, A) << 0;
385  pattern |= diff(E, B) << 1;
386  pattern |= diff(E, C) << 2;
387  pattern |= diff(E, D) << 3;
388  pattern |= diff(E, F) << 4;
389  pattern |= diff(E, G) << 5;
390  pattern |= diff(E, H) << 6;
391  pattern |= diff(E, I) << 7;
392 
393  *(out0 + 0) = hq2x_blend(hqTable[pattern], E, A, B, D, F, H); pattern = rotTable[pattern];
394  *(out0 + 1) = hq2x_blend(hqTable[pattern], E, C, F, B, H, D); pattern = rotTable[pattern];
395  *(out1 + 1) = hq2x_blend(hqTable[pattern], E, I, H, F, D, B); pattern = rotTable[pattern];
396  *(out1 + 0) = hq2x_blend(hqTable[pattern], E, G, D, H, B, F);
397 
398  in++;
399  out0 += 2;
400  out1 += 2;
401  }
402  }
403 }
404 
405 void HQ4x_Render(uint32_t *output, const uint32_t *input, int width, int height)
406 {
407  int x, y;
408 
409  for (y = 0; y < height; y++) {
410  const uint32_t *in = input + y * width;
411  uint32_t *out0 = output + (y * 4 + 0) * width * 4;
412  uint32_t *out1 = output + (y * 4 + 1) * width * 4;
413  uint32_t *out2 = output + (y * 4 + 2) * width * 4;
414  uint32_t *out3 = output + (y * 4 + 3) * width * 4;
415 
416  int prevline = (y == 0 ? 0 : width);
417  int nextline = (y == height - 1 ? 0 : width);
418 
419  for (x = 0; x < width; x++) {
420  int prev = (x == 0 ? 0 : 1);
421  int next = (x == width - 1 ? 0 : 1);
422 
423  uint32_t A = *(in - prevline - prev);
424  uint32_t B = *(in - prevline);
425  uint32_t C = *(in - prevline + next);
426  uint32_t D = *(in - prev);
427  uint32_t E = *(in);
428  uint32_t F = *(in + next);
429  uint32_t G = *(in + nextline - prev);
430  uint32_t H = *(in + nextline);
431  uint32_t I = *(in + nextline + next);
432 
433  int pattern;
434  pattern = diff(E, A) << 0;
435  pattern |= diff(E, B) << 1;
436  pattern |= diff(E, C) << 2;
437  pattern |= diff(E, D) << 3;
438  pattern |= diff(E, F) << 4;
439  pattern |= diff(E, G) << 5;
440  pattern |= diff(E, H) << 6;
441  pattern |= diff(E, I) << 7;
442 
443  hq4x_blend(hqTable[pattern], out0 + 0, out0 + 1, out1 + 0, out1 + 1, E, A, B, D, F, H); pattern = rotTable[pattern];
444  hq4x_blend(hqTable[pattern], out0 + 3, out1 + 3, out0 + 2, out1 + 2, E, C, F, B, H, D); pattern = rotTable[pattern];
445  hq4x_blend(hqTable[pattern], out3 + 3, out3 + 2, out2 + 3, out2 + 2, E, I, H, F, D, B); pattern = rotTable[pattern];
446  hq4x_blend(hqTable[pattern], out3 + 0, out2 + 0, out3 + 1, out2 + 1, E, G, D, H, B, F);
447 
448  in++;
449  out0 += 4;
450  out1 += 4;
451  out2 += 4;
452  out3 += 4;
453  }
454  }
455 }
456 
457 #define FIX(x) (int)((x) * (1 << 16))
458 
459 void HQ2x_Init(void)
460 {
461  int n;
462 
463  cvar_t *hqx_y = Cvar_Get("hqx_y", "48", CVAR_FILES);
464  cvar_t *hqx_cb = Cvar_Get("hqx_cb", "7", CVAR_FILES);
465  cvar_t *hqx_cr = Cvar_Get("hqx_cr", "6", CVAR_FILES);
466 
467  maxY = FIX(Cvar_ClampValue(hqx_y, 0, 256));
468  maxCb = FIX(Cvar_ClampValue(hqx_cb, 0, 256));
469  maxCr = FIX(Cvar_ClampValue(hqx_cr, 0, 256));
470 
471  for (n = 0; n < 256; n++) {
472  rotTable[n] = ((n >> 2) & 0x11) | ((n << 2) & 0x88)
473  | ((n & 0x01) << 5) | ((n & 0x08) << 3)
474  | ((n & 0x10) >> 3) | ((n & 0x80) >> 5);
475  }
476 
477  // libjpeg YCbCr coefficients
478  for (n = 0; n < 256; n++) {
479  yccTable[0][n] = FIX(0.29900f) * n;
480  yccTable[1][n] = FIX(0.58700f) * n;
481  yccTable[2][n] = FIX(0.11400f) * n;
482  yccTable[3][n] = -FIX(0.16874f) * n;
483  yccTable[4][n] = -FIX(0.33126f) * n;
484  yccTable[5][n] = FIX(0.50000f) * n;
485  yccTable[6][n] = -FIX(0.41869f) * n;
486  yccTable[7][n] = -FIX(0.08131f) * n;
487  }
488 }
hqTable
static const uint8_t hqTable[256]
Definition: hq2x.c:32
height
static int height
Definition: physical_sky.c:39
yccTable
static int32_t yccTable[8][256]
Definition: hq2x.c:52
HQ2x_Render
void HQ2x_Render(uint32_t *output, const uint32_t *input, int width, int height)
Definition: hq2x.c:357
input
static in_state_t input
Definition: input.c:71
hq2x_blend
static q_noinline uint32_t hq2x_blend(int rule, uint32_t E, uint32_t A, uint32_t B, uint32_t D, uint32_t F, uint32_t H)
Definition: hq2x.c:151
Cvar_Get
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags)
Definition: cvar.c:257
E
#define E(name)
Definition: g_save.c:50
same
static int same(uint32_t A, uint32_t B)
Definition: hq2x.c:87
FIX
#define FIX(x)
Definition: hq2x.c:457
F
#define F(name)
Definition: g_save.c:46
B
#define B(name)
Definition: g_save.c:40
maxCb
static int32_t maxCb
Definition: hq2x.c:53
width
static int width
Definition: physical_sky.c:38
Com_Error
void Com_Error(error_type_t type, const char *fmt,...)
Definition: g_main.c:258
Cvar_ClampValue
float Cvar_ClampValue(cvar_t *var, float min, float max)
Definition: cvar.c:571
blend_2_1_1
static q_noinline uint32_t blend_2_1_1(uint32_t A, uint32_t B, uint32_t C)
Definition: hq2x.c:126
hq4x_blend
static q_noinline void hq4x_blend(int rule, uint32_t *p00, uint32_t *p01, uint32_t *p10, uint32_t *p11, uint32_t E, uint32_t A, uint32_t B, uint32_t D, uint32_t F, uint32_t H)
Definition: hq2x.c:190
I
#define I(name)
Definition: g_save.c:44
blend_5_3
static q_noinline uint32_t blend_5_3(uint32_t A, uint32_t B)
Definition: hq2x.c:121
G
#define G(X, Y, Z)
Definition: mdfour.c:35
rotTable
static uint8_t rotTable[256]
Definition: hq2x.c:51
maxCr
static int32_t maxCr
Definition: hq2x.c:53
blend_5_2_1
static q_noinline uint32_t blend_5_2_1(uint32_t A, uint32_t B, uint32_t C)
Definition: hq2x.c:131
HQ2x_Init
void HQ2x_Init(void)
Definition: hq2x.c:459
diff
static q_noinline int diff(uint32_t A_u32, uint32_t B_u32)
Definition: hq2x.c:55
blend_1_1
static q_noinline uint32_t blend_1_1(uint32_t A, uint32_t B)
Definition: hq2x.c:106
blend_7_1
static q_noinline uint32_t blend_7_1(uint32_t A, uint32_t B)
Definition: hq2x.c:116
HQ4x_Render
void HQ4x_Render(uint32_t *output, const uint32_t *input, int width, int height)
Definition: hq2x.c:405
blend_14_1_1
static q_noinline uint32_t blend_14_1_1(uint32_t A, uint32_t B, uint32_t C)
Definition: hq2x.c:146
pack
static uint32_t pack(uint64_t n)
Definition: hq2x.c:99
blend_6_1_1
static q_noinline uint32_t blend_6_1_1(uint32_t A, uint32_t B, uint32_t C)
Definition: hq2x.c:136
grow
static uint64_t grow(uint64_t n)
Definition: hq2x.c:92
blend_2_3_3
static q_noinline uint32_t blend_2_3_3(uint32_t A, uint32_t B, uint32_t C)
Definition: hq2x.c:141
H
#define H(X, Y, Z)
Definition: mdfour.c:36
maxY
static int32_t maxY
Definition: hq2x.c:53
blend_3_1
static q_noinline uint32_t blend_3_1(uint32_t A, uint32_t B)
Definition: hq2x.c:111