/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/video/video-scaler.c
Line | Count | Source |
1 | | /* GStreamer |
2 | | * Copyright (C) <2014> Wim Taymans <wim.taymans@gmail.com> |
3 | | * |
4 | | * This library is free software; you can redistribute it and/or |
5 | | * modify it under the terms of the GNU Library General Public |
6 | | * License as published by the Free Software Foundation; either |
7 | | * version 2 of the License, or (at your option) any later version. |
8 | | * |
9 | | * This library 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 GNU |
12 | | * Library General Public License for more details. |
13 | | * |
14 | | * You should have received a copy of the GNU Library General Public |
15 | | * License along with this library; if not, write to the |
16 | | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
17 | | * Boston, MA 02110-1301, USA. |
18 | | */ |
19 | | |
20 | | #ifdef HAVE_CONFIG_H |
21 | | # include "config.h" |
22 | | #endif |
23 | | |
24 | | #include <string.h> |
25 | | #include <stdio.h> |
26 | | #include <math.h> |
27 | | |
28 | | /** |
29 | | * SECTION:gstvideoscaler |
30 | | * @title: GstVideoScaler |
31 | | * @short_description: Utility object for rescaling video frames |
32 | | * |
33 | | * #GstVideoScaler is a utility object for rescaling and resampling |
34 | | * video frames using various interpolation / sampling methods. |
35 | | * |
36 | | */ |
37 | | |
38 | | #ifndef DISABLE_ORC |
39 | | #include <orc/orcfunctions.h> |
40 | | #else |
41 | | #define orc_memcpy memcpy |
42 | | #endif |
43 | | |
44 | | #include "video-orc.h" |
45 | | #include "video-scaler.h" |
46 | | |
47 | | #ifndef GST_DISABLE_GST_DEBUG |
48 | | #define GST_CAT_DEFAULT ensure_debug_category() |
49 | | static GstDebugCategory * |
50 | | ensure_debug_category (void) |
51 | 0 | { |
52 | 0 | static gsize cat_gonce = 0; |
53 | |
|
54 | 0 | if (g_once_init_enter (&cat_gonce)) { |
55 | 0 | gsize cat_done; |
56 | |
|
57 | 0 | cat_done = (gsize) _gst_debug_category_new ("video-scaler", 0, |
58 | 0 | "video-scaler object"); |
59 | |
|
60 | 0 | g_once_init_leave (&cat_gonce, cat_done); |
61 | 0 | } |
62 | |
|
63 | 0 | return (GstDebugCategory *) cat_gonce; |
64 | 0 | } |
65 | | |
66 | | #else |
67 | | #define ensure_debug_category() /* NOOP */ |
68 | | #endif /* GST_DISABLE_GST_DEBUG */ |
69 | | |
70 | | #define SCALE_U8 12 |
71 | | #define SCALE_U8_ROUND (1 << (SCALE_U8 -1)) |
72 | 0 | #define SCALE_U8_LQ 6 |
73 | | #define SCALE_U8_LQ_ROUND (1 << (SCALE_U8_LQ -1)) |
74 | 0 | #define SCALE_U16 12 |
75 | | #define SCALE_U16_ROUND (1 << (SCALE_U16 -1)) |
76 | | |
77 | | #define LQ |
78 | | |
79 | | typedef void (*GstVideoScalerHFunc) (GstVideoScaler * scale, |
80 | | gpointer src, gpointer dest, guint dest_offset, guint width, guint n_elems); |
81 | | typedef void (*GstVideoScalerVFunc) (GstVideoScaler * scale, |
82 | | gpointer srcs[], gpointer dest, guint dest_offset, guint width, |
83 | | guint n_elems); |
84 | | |
85 | | struct _GstVideoScaler |
86 | | { |
87 | | GstVideoResamplerMethod method; |
88 | | GstVideoScalerFlags flags; |
89 | | |
90 | | GstVideoResampler resampler; |
91 | | |
92 | | gboolean merged; |
93 | | gint in_y_offset; |
94 | | gint out_y_offset; |
95 | | |
96 | | /* cached integer coefficients */ |
97 | | gint16 *taps_s16; |
98 | | gint16 *taps_s16_4; |
99 | | guint32 *offset_n; |
100 | | /* for ORC */ |
101 | | gint inc; |
102 | | |
103 | | gint tmpwidth; |
104 | | gpointer tmpline1; |
105 | | gpointer tmpline2; |
106 | | }; |
107 | | |
108 | | static void |
109 | | resampler_zip (GstVideoResampler * resampler, const GstVideoResampler * r1, |
110 | | const GstVideoResampler * r2) |
111 | 0 | { |
112 | 0 | guint i, out_size, max_taps, n_phases; |
113 | 0 | gdouble *taps; |
114 | 0 | guint32 *offset, *phase; |
115 | |
|
116 | 0 | g_return_if_fail (r1->max_taps == r2->max_taps); |
117 | | |
118 | 0 | out_size = r1->out_size + r2->out_size; |
119 | 0 | max_taps = r1->max_taps; |
120 | 0 | n_phases = out_size; |
121 | 0 | offset = g_malloc (sizeof (guint32) * out_size); |
122 | 0 | phase = g_malloc (sizeof (guint32) * n_phases); |
123 | 0 | taps = g_malloc (sizeof (gdouble) * max_taps * n_phases); |
124 | |
|
125 | 0 | resampler->in_size = r1->in_size + r2->in_size; |
126 | 0 | resampler->out_size = out_size; |
127 | 0 | resampler->max_taps = max_taps; |
128 | 0 | resampler->n_phases = n_phases; |
129 | 0 | resampler->offset = offset; |
130 | 0 | resampler->phase = phase; |
131 | 0 | resampler->n_taps = g_malloc (sizeof (guint32) * out_size); |
132 | 0 | resampler->taps = taps; |
133 | |
|
134 | 0 | for (i = 0; i < out_size; i++) { |
135 | 0 | guint idx = i / 2; |
136 | 0 | const GstVideoResampler *r; |
137 | |
|
138 | 0 | r = (i & 1) ? r2 : r1; |
139 | |
|
140 | 0 | offset[i] = r->offset[idx] * 2 + (i & 1); |
141 | 0 | phase[i] = i; |
142 | |
|
143 | 0 | memcpy (taps + i * max_taps, r->taps + r->phase[idx] * max_taps, |
144 | 0 | max_taps * sizeof (gdouble)); |
145 | 0 | } |
146 | 0 | } |
147 | | |
148 | | static void |
149 | | realloc_tmplines (GstVideoScaler * scale, gint n_elems, gint width) |
150 | 0 | { |
151 | 0 | gint n_taps = scale->resampler.max_taps; |
152 | |
|
153 | 0 | if (scale->flags & GST_VIDEO_SCALER_FLAG_INTERLACED) |
154 | 0 | n_taps *= 2; |
155 | |
|
156 | 0 | scale->tmpline1 = |
157 | 0 | g_realloc (scale->tmpline1, sizeof (gint32) * width * n_elems * n_taps); |
158 | 0 | scale->tmpline2 = |
159 | 0 | g_realloc (scale->tmpline2, sizeof (gint32) * width * n_elems); |
160 | 0 | scale->tmpwidth = width; |
161 | 0 | } |
162 | | |
163 | | static void |
164 | | scaler_dump (GstVideoScaler * scale) |
165 | 0 | { |
166 | | #if 0 |
167 | | gint i, j, in_size, out_size, max_taps; |
168 | | guint32 *offset, *phase; |
169 | | gdouble *taps; |
170 | | GstVideoResampler *r = &scale->resampler; |
171 | | |
172 | | in_size = r->in_size; |
173 | | out_size = r->out_size; |
174 | | offset = r->offset; |
175 | | phase = r->phase; |
176 | | max_taps = r->max_taps; |
177 | | taps = r->taps; |
178 | | |
179 | | g_print ("in %d, out %d, max_taps %d, n_phases %d\n", in_size, out_size, |
180 | | max_taps, r->n_phases); |
181 | | |
182 | | for (i = 0; i < out_size; i++) { |
183 | | g_print ("%d: \t%d \t%d:", i, offset[i], phase[i]); |
184 | | |
185 | | for (j = 0; j < max_taps; j++) { |
186 | | g_print ("\t%f", taps[i * max_taps + j]); |
187 | | } |
188 | | g_print ("\n"); |
189 | | } |
190 | | #endif |
191 | 0 | } |
192 | | |
193 | 0 | #define INTERLACE_SHIFT 0.5 |
194 | | |
195 | | /** |
196 | | * gst_video_scaler_new: (constructor) (skip) |
197 | | * @method: a #GstVideoResamplerMethod |
198 | | * @flags: #GstVideoScalerFlags |
199 | | * @n_taps: number of taps to use |
200 | | * @in_size: number of source elements |
201 | | * @out_size: number of destination elements |
202 | | * @options: (allow-none): extra options |
203 | | * |
204 | | * Make a new @method video scaler. @in_size source lines/pixels will |
205 | | * be scaled to @out_size destination lines/pixels. |
206 | | * |
207 | | * @n_taps specifies the amount of pixels to use from the source for one output |
208 | | * pixel. If n_taps is 0, this function chooses a good value automatically based |
209 | | * on the @method and @in_size/@out_size. |
210 | | * |
211 | | * Returns: a #GstVideoScaler |
212 | | */ |
213 | | GstVideoScaler * |
214 | | gst_video_scaler_new (GstVideoResamplerMethod method, GstVideoScalerFlags flags, |
215 | | guint n_taps, guint in_size, guint out_size, GstStructure * options) |
216 | 0 | { |
217 | 0 | GstVideoScaler *scale; |
218 | |
|
219 | 0 | g_return_val_if_fail (in_size != 0, NULL); |
220 | 0 | g_return_val_if_fail (out_size != 0, NULL); |
221 | | |
222 | 0 | scale = g_new0 (GstVideoScaler, 1); |
223 | |
|
224 | 0 | GST_DEBUG ("%d %u %u->%u", method, n_taps, in_size, out_size); |
225 | |
|
226 | 0 | scale->method = method; |
227 | 0 | scale->flags = flags; |
228 | |
|
229 | 0 | if (flags & GST_VIDEO_SCALER_FLAG_INTERLACED) { |
230 | 0 | GstVideoResampler tresamp, bresamp; |
231 | 0 | gdouble shift; |
232 | |
|
233 | 0 | shift = (INTERLACE_SHIFT * out_size) / in_size; |
234 | |
|
235 | 0 | gst_video_resampler_init (&tresamp, method, |
236 | 0 | GST_VIDEO_RESAMPLER_FLAG_HALF_TAPS, (out_size + 1) / 2, n_taps, shift, |
237 | 0 | (in_size + 1) / 2, (out_size + 1) / 2, options); |
238 | |
|
239 | 0 | n_taps = tresamp.max_taps; |
240 | |
|
241 | 0 | gst_video_resampler_init (&bresamp, method, 0, out_size - tresamp.out_size, |
242 | 0 | n_taps, -shift, in_size - tresamp.in_size, |
243 | 0 | out_size - tresamp.out_size, options); |
244 | |
|
245 | 0 | resampler_zip (&scale->resampler, &tresamp, &bresamp); |
246 | 0 | gst_video_resampler_clear (&tresamp); |
247 | 0 | gst_video_resampler_clear (&bresamp); |
248 | 0 | } else { |
249 | 0 | gst_video_resampler_init (&scale->resampler, method, |
250 | 0 | GST_VIDEO_RESAMPLER_FLAG_NONE, out_size, n_taps, 0.0, in_size, out_size, |
251 | 0 | options); |
252 | 0 | } |
253 | |
|
254 | 0 | if (out_size == 1) |
255 | 0 | scale->inc = 0; |
256 | 0 | else |
257 | 0 | scale->inc = ((in_size - 1) << 16) / (out_size - 1) - 1; |
258 | |
|
259 | 0 | scaler_dump (scale); |
260 | 0 | GST_DEBUG ("max_taps %d", scale->resampler.max_taps); |
261 | |
|
262 | 0 | return scale; |
263 | 0 | } |
264 | | |
265 | | /** |
266 | | * gst_video_scaler_free: |
267 | | * @scale: a #GstVideoScaler |
268 | | * |
269 | | * Free a previously allocated #GstVideoScaler @scale. |
270 | | */ |
271 | | void |
272 | | gst_video_scaler_free (GstVideoScaler * scale) |
273 | 0 | { |
274 | 0 | g_return_if_fail (scale != NULL); |
275 | | |
276 | 0 | gst_video_resampler_clear (&scale->resampler); |
277 | 0 | g_free (scale->taps_s16); |
278 | 0 | g_free (scale->taps_s16_4); |
279 | 0 | g_free (scale->offset_n); |
280 | 0 | g_free (scale->tmpline1); |
281 | 0 | g_free (scale->tmpline2); |
282 | 0 | g_free (scale); |
283 | 0 | } |
284 | | |
285 | | /** |
286 | | * gst_video_scaler_get_max_taps: |
287 | | * @scale: a #GstVideoScaler |
288 | | * |
289 | | * Get the maximum number of taps for @scale. |
290 | | * |
291 | | * Returns: the maximum number of taps |
292 | | */ |
293 | | guint |
294 | | gst_video_scaler_get_max_taps (GstVideoScaler * scale) |
295 | 0 | { |
296 | 0 | g_return_val_if_fail (scale != NULL, 0); |
297 | | |
298 | 0 | return scale->resampler.max_taps; |
299 | 0 | } |
300 | | |
301 | | /** |
302 | | * gst_video_scaler_get_coeff: |
303 | | * @scale: a #GstVideoScaler |
304 | | * @out_offset: an output offset |
305 | | * @in_offset: (out) (optional): result input offset |
306 | | * @n_taps: (out) (optional): result n_taps |
307 | | * |
308 | | * For a given pixel at @out_offset, get the first required input pixel at |
309 | | * @in_offset and the @n_taps filter coefficients. |
310 | | * |
311 | | * Note that for interlaced content, @in_offset needs to be incremented with |
312 | | * 2 to get the next input line. |
313 | | * |
314 | | * Returns: (array length=n_taps): an array of @n_taps gdouble values with filter coefficients. |
315 | | */ |
316 | | const gdouble * |
317 | | gst_video_scaler_get_coeff (GstVideoScaler * scale, |
318 | | guint out_offset, guint * in_offset, guint * n_taps) |
319 | 0 | { |
320 | 0 | guint offset, phase; |
321 | |
|
322 | 0 | g_return_val_if_fail (scale != NULL, NULL); |
323 | 0 | g_return_val_if_fail (out_offset < scale->resampler.out_size, NULL); |
324 | | |
325 | 0 | offset = scale->resampler.offset[out_offset]; |
326 | 0 | phase = scale->resampler.phase[out_offset]; |
327 | |
|
328 | 0 | if (in_offset) |
329 | 0 | *in_offset = offset; |
330 | 0 | if (n_taps) { |
331 | 0 | *n_taps = scale->resampler.max_taps; |
332 | 0 | if (scale->flags & GST_VIDEO_SCALER_FLAG_INTERLACED) |
333 | 0 | *n_taps *= 2; |
334 | 0 | } |
335 | 0 | return scale->resampler.taps + phase * scale->resampler.max_taps; |
336 | 0 | } |
337 | | |
338 | | static gboolean |
339 | | resampler_convert_coeff (const gdouble * src, |
340 | | gpointer dest, guint n, guint bits, guint precision) |
341 | 0 | { |
342 | 0 | gdouble multiplier; |
343 | 0 | gint i, j; |
344 | 0 | gdouble offset, l_offset, h_offset; |
345 | 0 | gboolean exact = FALSE; |
346 | |
|
347 | 0 | multiplier = (1 << precision); |
348 | | |
349 | | /* Round to integer, but with an adjustable bias that we use to |
350 | | * eliminate the DC error. */ |
351 | 0 | l_offset = 0.0; |
352 | 0 | h_offset = 1.0; |
353 | 0 | offset = 0.5; |
354 | |
|
355 | 0 | for (i = 0; i < 64; i++) { |
356 | 0 | gint sum = 0; |
357 | |
|
358 | 0 | for (j = 0; j < n; j++) { |
359 | 0 | gint16 tap = floor (offset + src[j] * multiplier); |
360 | |
|
361 | 0 | ((gint16 *) dest)[j] = tap; |
362 | |
|
363 | 0 | sum += tap; |
364 | 0 | } |
365 | 0 | if (sum == (1 << precision)) { |
366 | 0 | exact = TRUE; |
367 | 0 | break; |
368 | 0 | } |
369 | | |
370 | 0 | if (l_offset == h_offset) |
371 | 0 | break; |
372 | | |
373 | 0 | if (sum < (1 << precision)) { |
374 | 0 | if (offset > l_offset) |
375 | 0 | l_offset = offset; |
376 | 0 | offset += (h_offset - l_offset) / 2; |
377 | 0 | } else { |
378 | 0 | if (offset < h_offset) |
379 | 0 | h_offset = offset; |
380 | 0 | offset -= (h_offset - l_offset) / 2; |
381 | 0 | } |
382 | 0 | } |
383 | |
|
384 | 0 | if (!exact) |
385 | 0 | GST_DEBUG ("can't find exact taps"); |
386 | |
|
387 | 0 | return exact; |
388 | 0 | } |
389 | | |
390 | | static void |
391 | | make_s16_taps (GstVideoScaler * scale, gint n_elems, gint precision) |
392 | 0 | { |
393 | 0 | gint i, j, max_taps, n_phases, out_size, src_inc; |
394 | 0 | gint16 *taps_s16, *taps_s16_4; |
395 | 0 | gdouble *taps; |
396 | 0 | guint32 *phase, *offset, *offset_n; |
397 | |
|
398 | 0 | n_phases = scale->resampler.n_phases; |
399 | 0 | max_taps = scale->resampler.max_taps; |
400 | |
|
401 | 0 | taps = scale->resampler.taps; |
402 | 0 | taps_s16 = scale->taps_s16 = g_malloc (sizeof (gint16) * n_phases * max_taps); |
403 | |
|
404 | 0 | for (i = 0; i < n_phases; i++) { |
405 | 0 | resampler_convert_coeff (taps, taps_s16, max_taps, 16, precision); |
406 | |
|
407 | 0 | taps += max_taps; |
408 | 0 | taps_s16 += max_taps; |
409 | 0 | } |
410 | |
|
411 | 0 | out_size = scale->resampler.out_size; |
412 | |
|
413 | 0 | taps_s16 = scale->taps_s16; |
414 | 0 | phase = scale->resampler.phase; |
415 | 0 | offset = scale->resampler.offset; |
416 | |
|
417 | 0 | taps_s16_4 = scale->taps_s16_4 = |
418 | 0 | g_malloc (sizeof (gint16) * out_size * max_taps * 4); |
419 | 0 | offset_n = scale->offset_n = |
420 | 0 | g_malloc (sizeof (guint32) * out_size * max_taps); |
421 | |
|
422 | 0 | if (scale->flags & GST_VIDEO_SCALER_FLAG_INTERLACED) |
423 | 0 | src_inc = 2; |
424 | 0 | else |
425 | 0 | src_inc = 1; |
426 | |
|
427 | 0 | for (j = 0; j < max_taps; j++) { |
428 | 0 | for (i = 0; i < out_size; i++) { |
429 | 0 | gint16 tap; |
430 | |
|
431 | 0 | if (scale->merged) { |
432 | 0 | if ((i & 1) == scale->out_y_offset) |
433 | 0 | offset_n[j * out_size + i] = offset[i] + (2 * j); |
434 | 0 | else |
435 | 0 | offset_n[j * out_size + i] = offset[i] + (4 * j); |
436 | 0 | } else { |
437 | 0 | offset_n[j * out_size + i] = offset[i] + j * src_inc; |
438 | 0 | } |
439 | 0 | tap = taps_s16[phase[i] * max_taps + j]; |
440 | 0 | taps_s16_4[(j * out_size + i) * n_elems + 0] = tap; |
441 | 0 | if (n_elems > 1) |
442 | 0 | taps_s16_4[(j * out_size + i) * n_elems + 1] = tap; |
443 | 0 | if (n_elems > 2) |
444 | 0 | taps_s16_4[(j * out_size + i) * n_elems + 2] = tap; |
445 | 0 | if (n_elems > 3) |
446 | 0 | taps_s16_4[(j * out_size + i) * n_elems + 3] = tap; |
447 | 0 | } |
448 | 0 | } |
449 | 0 | } |
450 | | |
451 | | #undef ACC_SCALE |
452 | | |
453 | | static void |
454 | | video_scale_h_near_u8 (GstVideoScaler * scale, |
455 | | gpointer src, gpointer dest, guint dest_offset, guint width, guint n_elems) |
456 | 0 | { |
457 | 0 | guint8 *s, *d; |
458 | 0 | gint i; |
459 | |
|
460 | 0 | d = (guint8 *) dest + dest_offset; |
461 | 0 | s = (guint8 *) src; |
462 | |
|
463 | 0 | { |
464 | 0 | #ifndef ACC_SCALE |
465 | 0 | guint32 *offset = scale->resampler.offset + dest_offset; |
466 | |
|
467 | 0 | for (i = 0; i < width; i++) |
468 | 0 | d[i] = s[offset[i]]; |
469 | | #else |
470 | | gint acc = 0; |
471 | | |
472 | | for (i = 0; i < width; i++) { |
473 | | gint j = (acc + 0x8000) >> 16; |
474 | | d[i] = s[j]; |
475 | | acc += scale->inc; |
476 | | } |
477 | | #endif |
478 | 0 | } |
479 | 0 | } |
480 | | |
481 | | static void |
482 | | video_scale_h_near_3u8 (GstVideoScaler * scale, |
483 | | gpointer src, gpointer dest, guint dest_offset, guint width, guint n_elems) |
484 | 0 | { |
485 | 0 | guint8 *s, *d; |
486 | 0 | gint i; |
487 | |
|
488 | 0 | d = (guint8 *) dest + dest_offset; |
489 | 0 | s = (guint8 *) src; |
490 | |
|
491 | 0 | { |
492 | 0 | #ifndef ACC_SCALE |
493 | 0 | guint32 *offset = scale->resampler.offset + dest_offset; |
494 | |
|
495 | 0 | for (i = 0; i < width; i++) { |
496 | 0 | gint j = offset[i] * 3; |
497 | |
|
498 | 0 | d[i * 3 + 0] = s[j + 0]; |
499 | 0 | d[i * 3 + 1] = s[j + 1]; |
500 | 0 | d[i * 3 + 2] = s[j + 2]; |
501 | 0 | } |
502 | | #else |
503 | | gint acc = 0; |
504 | | |
505 | | for (i = 0; i < width; i++) { |
506 | | gint j = ((acc + 0x8000) >> 16) * 3; |
507 | | |
508 | | d[i * 3 + 0] = s[j + 0]; |
509 | | d[i * 3 + 1] = s[j + 1]; |
510 | | d[i * 3 + 2] = s[j + 2]; |
511 | | acc += scale->inc; |
512 | | } |
513 | | #endif |
514 | 0 | } |
515 | 0 | } |
516 | | |
517 | | static void |
518 | | video_scale_h_near_u16 (GstVideoScaler * scale, |
519 | | gpointer src, gpointer dest, guint dest_offset, guint width, guint n_elems) |
520 | 0 | { |
521 | 0 | guint16 *s, *d; |
522 | 0 | gint i; |
523 | |
|
524 | 0 | d = (guint16 *) dest + dest_offset; |
525 | 0 | s = (guint16 *) src; |
526 | |
|
527 | 0 | { |
528 | 0 | #ifndef ACC_SCALE |
529 | 0 | guint32 *offset = scale->resampler.offset + dest_offset; |
530 | |
|
531 | 0 | for (i = 0; i < width; i++) |
532 | 0 | d[i] = s[offset[i]]; |
533 | | #else |
534 | | gint acc = 0; |
535 | | |
536 | | for (i = 0; i < width; i++) { |
537 | | gint j = (acc + 0x8000) >> 16; |
538 | | d[i] = s[j]; |
539 | | acc += scale->inc; |
540 | | } |
541 | | #endif |
542 | 0 | } |
543 | 0 | } |
544 | | |
545 | | static void |
546 | | video_scale_h_near_u32 (GstVideoScaler * scale, |
547 | | gpointer src, gpointer dest, guint dest_offset, guint width, guint n_elems) |
548 | 0 | { |
549 | 0 | guint32 *s, *d; |
550 | |
|
551 | 0 | d = (guint32 *) dest + dest_offset; |
552 | 0 | s = (guint32 *) src; |
553 | |
|
554 | | #if 0 |
555 | | /* ORC is slower on this */ |
556 | | video_orc_resample_h_near_u32_lq (d, s, 0, scale->inc, width); |
557 | | #elif 0 |
558 | | video_orc_resample_h_near_u32 (d, s, offset, width); |
559 | | #else |
560 | 0 | { |
561 | 0 | gint i; |
562 | 0 | #ifndef ACC_SCALE |
563 | 0 | guint32 *offset = scale->resampler.offset + dest_offset; |
564 | |
|
565 | 0 | for (i = 0; i < width; i++) |
566 | 0 | d[i] = s[offset[i]]; |
567 | | #else |
568 | | gint acc = 0; |
569 | | |
570 | | for (i = 0; i < width; i++) { |
571 | | gint j = (acc + 0x8000) >> 16; |
572 | | d[i] = s[j]; |
573 | | acc += scale->inc; |
574 | | } |
575 | | #endif |
576 | 0 | } |
577 | 0 | #endif |
578 | 0 | } |
579 | | |
580 | | static void |
581 | | video_scale_h_near_u64 (GstVideoScaler * scale, |
582 | | gpointer src, gpointer dest, guint dest_offset, guint width, guint n_elems) |
583 | 0 | { |
584 | 0 | guint64 *s, *d; |
585 | 0 | gint i; |
586 | 0 | guint32 *offset; |
587 | |
|
588 | 0 | d = (guint64 *) dest + dest_offset; |
589 | 0 | s = (guint64 *) src; |
590 | |
|
591 | 0 | offset = scale->resampler.offset + dest_offset; |
592 | 0 | for (i = 0; i < width; i++) |
593 | 0 | d[i] = s[offset[i]]; |
594 | 0 | } |
595 | | |
596 | | static void |
597 | | video_scale_h_2tap_1u8 (GstVideoScaler * scale, |
598 | | gpointer src, gpointer dest, guint dest_offset, guint width, guint n_elems) |
599 | 0 | { |
600 | 0 | guint8 *s, *d; |
601 | |
|
602 | 0 | d = (guint8 *) dest + dest_offset; |
603 | 0 | s = (guint8 *) src; |
604 | |
|
605 | 0 | video_orc_resample_h_2tap_1u8_lq (d, s, 0, scale->inc, width); |
606 | 0 | } |
607 | | |
608 | | static void |
609 | | video_scale_h_2tap_4u8 (GstVideoScaler * scale, |
610 | | gpointer src, gpointer dest, guint dest_offset, guint width, guint n_elems) |
611 | 0 | { |
612 | 0 | guint32 *s, *d; |
613 | |
|
614 | 0 | d = (guint32 *) dest + dest_offset; |
615 | 0 | s = (guint32 *) src; |
616 | |
|
617 | 0 | video_orc_resample_h_2tap_4u8_lq (d, s, 0, scale->inc, width); |
618 | 0 | } |
619 | | |
620 | | static void |
621 | | video_scale_h_ntap_u8 (GstVideoScaler * scale, |
622 | | gpointer src, gpointer dest, guint dest_offset, guint width, guint n_elems) |
623 | 0 | { |
624 | 0 | gint16 *taps; |
625 | 0 | gint i, max_taps, count; |
626 | 0 | gpointer d; |
627 | 0 | guint32 *offset_n; |
628 | 0 | guint8 *pixels; |
629 | 0 | gint16 *temp; |
630 | |
|
631 | 0 | if (scale->taps_s16 == NULL) |
632 | 0 | #ifdef LQ |
633 | 0 | make_s16_taps (scale, n_elems, SCALE_U8_LQ); |
634 | | #else |
635 | | make_s16_taps (scale, n_elems, SCALE_U8); |
636 | | #endif |
637 | |
|
638 | 0 | max_taps = scale->resampler.max_taps; |
639 | 0 | offset_n = scale->offset_n; |
640 | |
|
641 | 0 | pixels = (guint8 *) scale->tmpline1; |
642 | | |
643 | | /* prepare the arrays */ |
644 | 0 | count = width * max_taps; |
645 | 0 | switch (n_elems) { |
646 | 0 | case 1: |
647 | 0 | { |
648 | 0 | guint8 *s = (guint8 *) src; |
649 | |
|
650 | 0 | for (i = 0; i < count; i++) |
651 | 0 | pixels[i] = s[offset_n[i]]; |
652 | |
|
653 | 0 | d = (guint8 *) dest + dest_offset; |
654 | 0 | break; |
655 | 0 | } |
656 | 0 | case 2: |
657 | 0 | { |
658 | 0 | guint16 *p16 = (guint16 *) pixels; |
659 | 0 | guint16 *s = (guint16 *) src; |
660 | |
|
661 | 0 | for (i = 0; i < count; i++) |
662 | 0 | p16[i] = s[offset_n[i]]; |
663 | |
|
664 | 0 | d = (guint16 *) dest + dest_offset; |
665 | 0 | break; |
666 | 0 | } |
667 | 0 | case 3: |
668 | 0 | { |
669 | 0 | guint8 *s = (guint8 *) src; |
670 | |
|
671 | 0 | for (i = 0; i < count; i++) { |
672 | 0 | gint j = offset_n[i] * 3; |
673 | 0 | pixels[i * 3 + 0] = s[j + 0]; |
674 | 0 | pixels[i * 3 + 1] = s[j + 1]; |
675 | 0 | pixels[i * 3 + 2] = s[j + 2]; |
676 | 0 | } |
677 | 0 | d = (guint8 *) dest + dest_offset * 3; |
678 | 0 | break; |
679 | 0 | } |
680 | 0 | case 4: |
681 | 0 | { |
682 | 0 | guint32 *p32 = (guint32 *) pixels; |
683 | 0 | guint32 *s = (guint32 *) src; |
684 | | #if 0 |
685 | | video_orc_resample_h_near_u32 (p32, s, offset_n, count); |
686 | | #else |
687 | 0 | for (i = 0; i < count; i++) |
688 | 0 | p32[i] = s[offset_n[i]]; |
689 | 0 | #endif |
690 | 0 | d = (guint32 *) dest + dest_offset; |
691 | 0 | break; |
692 | 0 | } |
693 | 0 | default: |
694 | 0 | return; |
695 | 0 | } |
696 | 0 | temp = (gint16 *) scale->tmpline2; |
697 | 0 | taps = scale->taps_s16_4; |
698 | 0 | count = width * n_elems; |
699 | |
|
700 | 0 | #ifdef LQ |
701 | 0 | if (max_taps == 2) { |
702 | 0 | video_orc_resample_h_2tap_u8_lq (d, pixels, pixels + count, taps, |
703 | 0 | taps + count, count); |
704 | 0 | } else { |
705 | | /* first pixels with first tap to temp */ |
706 | 0 | if (max_taps >= 3) { |
707 | 0 | video_orc_resample_h_multaps3_u8_lq (temp, pixels, pixels + count, |
708 | 0 | pixels + count * 2, taps, taps + count, taps + count * 2, count); |
709 | 0 | max_taps -= 3; |
710 | 0 | pixels += count * 3; |
711 | 0 | taps += count * 3; |
712 | 0 | } else { |
713 | 0 | gint first = max_taps % 3; |
714 | |
|
715 | 0 | video_orc_resample_h_multaps_u8_lq (temp, pixels, taps, count); |
716 | 0 | video_orc_resample_h_muladdtaps_u8_lq (temp, 0, pixels + count, count, |
717 | 0 | taps + count, count * 2, count, first - 1); |
718 | 0 | max_taps -= first; |
719 | 0 | pixels += count * first; |
720 | 0 | taps += count * first; |
721 | 0 | } |
722 | 0 | while (max_taps > 3) { |
723 | 0 | if (max_taps >= 6) { |
724 | 0 | video_orc_resample_h_muladdtaps3_u8_lq (temp, pixels, pixels + count, |
725 | 0 | pixels + count * 2, taps, taps + count, taps + count * 2, count); |
726 | 0 | max_taps -= 3; |
727 | 0 | pixels += count * 3; |
728 | 0 | taps += count * 3; |
729 | 0 | } else { |
730 | 0 | video_orc_resample_h_muladdtaps_u8_lq (temp, 0, pixels, count, |
731 | 0 | taps, count * 2, count, max_taps - 3); |
732 | 0 | pixels += count * (max_taps - 3); |
733 | 0 | taps += count * (max_taps - 3); |
734 | 0 | max_taps = 3; |
735 | 0 | } |
736 | 0 | } |
737 | 0 | if (max_taps == 3) { |
738 | 0 | video_orc_resample_h_muladdscaletaps3_u8_lq (d, pixels, pixels + count, |
739 | 0 | pixels + count * 2, taps, taps + count, taps + count * 2, temp, |
740 | 0 | count); |
741 | 0 | } else { |
742 | 0 | if (max_taps) { |
743 | | /* add other pixels with other taps to t4 */ |
744 | 0 | video_orc_resample_h_muladdtaps_u8_lq (temp, 0, pixels, count, |
745 | 0 | taps, count * 2, count, max_taps); |
746 | 0 | } |
747 | | /* scale and write final result */ |
748 | 0 | video_orc_resample_scaletaps_u8_lq (d, temp, count); |
749 | 0 | } |
750 | 0 | } |
751 | | #else |
752 | | /* first pixels with first tap to t4 */ |
753 | | video_orc_resample_h_multaps_u8 (temp, pixels, taps, count); |
754 | | /* add other pixels with other taps to t4 */ |
755 | | video_orc_resample_h_muladdtaps_u8 (temp, 0, pixels + count, count, |
756 | | taps + count, count * 2, count, max_taps - 1); |
757 | | /* scale and write final result */ |
758 | | video_orc_resample_scaletaps_u8 (d, temp, count); |
759 | | #endif |
760 | 0 | } |
761 | | |
762 | | static void |
763 | | video_scale_h_ntap_u16 (GstVideoScaler * scale, |
764 | | gpointer src, gpointer dest, guint dest_offset, guint width, guint n_elems) |
765 | 0 | { |
766 | 0 | gint16 *taps; |
767 | 0 | gint i, max_taps, count; |
768 | 0 | gpointer d; |
769 | 0 | guint32 *offset_n; |
770 | 0 | guint16 *pixels; |
771 | 0 | gint32 *temp; |
772 | |
|
773 | 0 | if (scale->taps_s16 == NULL) |
774 | 0 | make_s16_taps (scale, n_elems, SCALE_U16); |
775 | |
|
776 | 0 | max_taps = scale->resampler.max_taps; |
777 | 0 | offset_n = scale->offset_n; |
778 | |
|
779 | 0 | pixels = (guint16 *) scale->tmpline1; |
780 | | /* prepare the arrays FIXME, we can add this into ORC */ |
781 | 0 | count = width * max_taps; |
782 | 0 | switch (n_elems) { |
783 | 0 | case 1: |
784 | 0 | { |
785 | 0 | guint16 *s = (guint16 *) src; |
786 | |
|
787 | 0 | for (i = 0; i < count; i++) |
788 | 0 | pixels[i] = s[offset_n[i]]; |
789 | |
|
790 | 0 | d = (guint16 *) dest + dest_offset; |
791 | 0 | break; |
792 | 0 | } |
793 | 0 | case 4: |
794 | 0 | { |
795 | 0 | guint64 *p64 = (guint64 *) pixels; |
796 | 0 | guint64 *s = (guint64 *) src; |
797 | | #if 0 |
798 | | video_orc_resample_h_near_u32 (p32, s, offset_n, count); |
799 | | #else |
800 | 0 | for (i = 0; i < count; i++) |
801 | 0 | p64[i] = s[offset_n[i]]; |
802 | 0 | #endif |
803 | 0 | d = (guint64 *) dest + dest_offset; |
804 | 0 | break; |
805 | 0 | } |
806 | 0 | default: |
807 | 0 | return; |
808 | 0 | } |
809 | | |
810 | 0 | temp = (gint32 *) scale->tmpline2; |
811 | 0 | taps = scale->taps_s16_4; |
812 | 0 | count = width * n_elems; |
813 | |
|
814 | 0 | if (max_taps == 2) { |
815 | 0 | video_orc_resample_h_2tap_u16 (d, pixels, pixels + count, taps, |
816 | 0 | taps + count, count); |
817 | 0 | } else { |
818 | | /* first pixels with first tap to t4 */ |
819 | 0 | video_orc_resample_h_multaps_u16 (temp, pixels, taps, count); |
820 | | /* add other pixels with other taps to t4 */ |
821 | 0 | video_orc_resample_h_muladdtaps_u16 (temp, 0, pixels + count, count * 2, |
822 | 0 | taps + count, count * 2, count, max_taps - 1); |
823 | | /* scale and write final result */ |
824 | 0 | video_orc_resample_scaletaps_u16 (d, temp, count); |
825 | 0 | } |
826 | 0 | } |
827 | | |
828 | | static void |
829 | | video_scale_v_near_u8 (GstVideoScaler * scale, |
830 | | gpointer srcs[], gpointer dest, guint dest_offset, guint width, |
831 | | guint n_elems) |
832 | 0 | { |
833 | 0 | if (dest != srcs[0]) |
834 | 0 | memcpy (dest, srcs[0], n_elems * width); |
835 | 0 | } |
836 | | |
837 | | static void |
838 | | video_scale_v_near_u16 (GstVideoScaler * scale, |
839 | | gpointer srcs[], gpointer dest, guint dest_offset, guint width, |
840 | | guint n_elems) |
841 | 0 | { |
842 | 0 | if (dest != srcs[0]) |
843 | 0 | memcpy (dest, srcs[0], n_elems * 2 * width); |
844 | 0 | } |
845 | | |
846 | | static void |
847 | | video_scale_v_2tap_u8 (GstVideoScaler * scale, |
848 | | gpointer srcs[], gpointer dest, guint dest_offset, guint width, |
849 | | guint n_elems) |
850 | 0 | { |
851 | 0 | gint max_taps, src_inc; |
852 | 0 | guint8 *s1, *s2, *d; |
853 | 0 | gint16 p1; |
854 | |
|
855 | 0 | if (scale->taps_s16 == NULL) |
856 | 0 | #ifdef LQ |
857 | 0 | make_s16_taps (scale, n_elems, SCALE_U8_LQ + 2); |
858 | | #else |
859 | | make_s16_taps (scale, n_elems, SCALE_U8); |
860 | | #endif |
861 | |
|
862 | 0 | max_taps = scale->resampler.max_taps; |
863 | |
|
864 | 0 | if (scale->flags & GST_VIDEO_SCALER_FLAG_INTERLACED) |
865 | 0 | src_inc = 2; |
866 | 0 | else |
867 | 0 | src_inc = 1; |
868 | |
|
869 | 0 | d = (guint8 *) dest; |
870 | 0 | s1 = (guint8 *) srcs[0 * src_inc]; |
871 | 0 | s2 = (guint8 *) srcs[1 * src_inc]; |
872 | 0 | p1 = scale->taps_s16[dest_offset * max_taps + 1]; |
873 | |
|
874 | 0 | #ifdef LQ |
875 | 0 | video_orc_resample_v_2tap_u8_lq (d, s1, s2, p1, width * n_elems); |
876 | | #else |
877 | | video_orc_resample_v_2tap_u8 (d, s1, s2, p1, width * n_elems); |
878 | | #endif |
879 | 0 | } |
880 | | |
881 | | static void |
882 | | video_scale_v_2tap_u16 (GstVideoScaler * scale, |
883 | | gpointer srcs[], gpointer dest, guint dest_offset, guint width, |
884 | | guint n_elems) |
885 | 0 | { |
886 | 0 | gint max_taps, src_inc; |
887 | 0 | guint16 *s1, *s2, *d; |
888 | 0 | gint16 p1; |
889 | |
|
890 | 0 | if (scale->taps_s16 == NULL) |
891 | 0 | make_s16_taps (scale, n_elems, SCALE_U16); |
892 | |
|
893 | 0 | max_taps = scale->resampler.max_taps; |
894 | |
|
895 | 0 | if (scale->flags & GST_VIDEO_SCALER_FLAG_INTERLACED) |
896 | 0 | src_inc = 2; |
897 | 0 | else |
898 | 0 | src_inc = 1; |
899 | |
|
900 | 0 | d = (guint16 *) dest; |
901 | 0 | s1 = (guint16 *) srcs[0 * src_inc]; |
902 | 0 | s2 = (guint16 *) srcs[1 * src_inc]; |
903 | 0 | p1 = scale->taps_s16[dest_offset * max_taps + 1]; |
904 | |
|
905 | 0 | video_orc_resample_v_2tap_u16 (d, s1, s2, p1, width * n_elems); |
906 | 0 | } |
907 | | |
908 | | #if 0 |
909 | | static void |
910 | | video_scale_h_4tap_8888 (GstVideoScaler * scale, |
911 | | gpointer src, gpointer dest, guint dest_offset, guint width) |
912 | | { |
913 | | gint16 *taps; |
914 | | gint i, max_taps, count; |
915 | | guint8 *d; |
916 | | guint32 *offset_n; |
917 | | guint32 *pixels; |
918 | | |
919 | | if (scale->taps_s16 == NULL) |
920 | | make_s16_taps (scale, n_elems, S16_SCALE); |
921 | | |
922 | | max_taps = scale->resampler.max_taps; |
923 | | offset_n = scale->offset_n; |
924 | | |
925 | | d = (guint8 *) dest + 4 * dest_offset; |
926 | | |
927 | | /* prepare the arrays FIXME, we can add this into ORC */ |
928 | | count = width * max_taps; |
929 | | pixels = (guint32 *) scale->tmpline1; |
930 | | for (i = 0; i < count; i++) |
931 | | pixels[i] = ((guint32 *) src)[offset_n[i]]; |
932 | | |
933 | | taps = scale->taps_s16_4; |
934 | | count = width * 4; |
935 | | |
936 | | video_orc_resample_h_4tap_8 (d, pixels, pixels + width, pixels + 2 * width, |
937 | | pixels + 3 * width, taps, taps + count, taps + 2 * count, |
938 | | taps + 3 * count, count); |
939 | | } |
940 | | #endif |
941 | | |
942 | | static void |
943 | | video_scale_v_4tap_u8 (GstVideoScaler * scale, |
944 | | gpointer srcs[], gpointer dest, guint dest_offset, guint width, |
945 | | guint n_elems) |
946 | 0 | { |
947 | 0 | gint max_taps; |
948 | 0 | guint8 *s1, *s2, *s3, *s4, *d; |
949 | 0 | gint p1, p2, p3, p4, src_inc; |
950 | 0 | gint16 *taps; |
951 | |
|
952 | 0 | if (scale->taps_s16 == NULL) |
953 | 0 | #ifdef LQ |
954 | 0 | make_s16_taps (scale, n_elems, SCALE_U8_LQ); |
955 | | #else |
956 | | make_s16_taps (scale, n_elems, SCALE_U8); |
957 | | #endif |
958 | |
|
959 | 0 | max_taps = scale->resampler.max_taps; |
960 | 0 | taps = scale->taps_s16 + dest_offset * max_taps; |
961 | |
|
962 | 0 | if (scale->flags & GST_VIDEO_SCALER_FLAG_INTERLACED) |
963 | 0 | src_inc = 2; |
964 | 0 | else |
965 | 0 | src_inc = 1; |
966 | |
|
967 | 0 | d = (guint8 *) dest; |
968 | 0 | s1 = (guint8 *) srcs[0 * src_inc]; |
969 | 0 | s2 = (guint8 *) srcs[1 * src_inc]; |
970 | 0 | s3 = (guint8 *) srcs[2 * src_inc]; |
971 | 0 | s4 = (guint8 *) srcs[3 * src_inc]; |
972 | 0 | p1 = taps[0]; |
973 | 0 | p2 = taps[1]; |
974 | 0 | p3 = taps[2]; |
975 | 0 | p4 = taps[3]; |
976 | |
|
977 | 0 | #ifdef LQ |
978 | 0 | video_orc_resample_v_4tap_u8_lq (d, s1, s2, s3, s4, p1, p2, p3, p4, |
979 | 0 | width * n_elems); |
980 | | #else |
981 | | video_orc_resample_v_4tap_u8 (d, s1, s2, s3, s4, p1, p2, p3, p4, |
982 | | width * n_elems); |
983 | | #endif |
984 | 0 | } |
985 | | |
986 | | static void |
987 | | video_scale_v_ntap_u8 (GstVideoScaler * scale, |
988 | | gpointer srcs[], gpointer dest, guint dest_offset, guint width, |
989 | | guint n_elems) |
990 | 0 | { |
991 | 0 | gint16 *taps; |
992 | 0 | gint i, max_taps, count, src_inc; |
993 | 0 | gpointer d; |
994 | 0 | gint16 *temp; |
995 | |
|
996 | 0 | if (scale->taps_s16 == NULL) |
997 | 0 | #ifdef LQ |
998 | 0 | make_s16_taps (scale, n_elems, SCALE_U8_LQ); |
999 | | #else |
1000 | | make_s16_taps (scale, n_elems, SCALE_U8); |
1001 | | #endif |
1002 | |
|
1003 | 0 | max_taps = scale->resampler.max_taps; |
1004 | 0 | taps = scale->taps_s16 + (scale->resampler.phase[dest_offset] * max_taps); |
1005 | |
|
1006 | 0 | d = (guint32 *) dest; |
1007 | |
|
1008 | 0 | if (scale->flags & GST_VIDEO_SCALER_FLAG_INTERLACED) |
1009 | 0 | src_inc = 2; |
1010 | 0 | else |
1011 | 0 | src_inc = 1; |
1012 | |
|
1013 | 0 | temp = (gint16 *) scale->tmpline2; |
1014 | 0 | count = width * n_elems; |
1015 | |
|
1016 | 0 | #ifdef LQ |
1017 | 0 | if (max_taps >= 4) { |
1018 | 0 | video_orc_resample_v_multaps4_u8_lq (temp, srcs[0], srcs[1 * src_inc], |
1019 | 0 | srcs[2 * src_inc], srcs[3 * src_inc], taps[0], taps[1], taps[2], |
1020 | 0 | taps[3], count); |
1021 | 0 | max_taps -= 4; |
1022 | 0 | srcs += 4 * src_inc; |
1023 | 0 | taps += 4; |
1024 | 0 | } else { |
1025 | 0 | gint first = (max_taps % 4); |
1026 | |
|
1027 | 0 | video_orc_resample_v_multaps_u8_lq (temp, srcs[0], taps[0], count); |
1028 | 0 | for (i = 1; i < first; i++) { |
1029 | 0 | video_orc_resample_v_muladdtaps_u8_lq (temp, srcs[i * src_inc], taps[i], |
1030 | 0 | count); |
1031 | 0 | } |
1032 | 0 | max_taps -= first; |
1033 | 0 | srcs += first * src_inc; |
1034 | 0 | taps += first; |
1035 | 0 | } |
1036 | 0 | while (max_taps > 4) { |
1037 | 0 | if (max_taps >= 8) { |
1038 | 0 | video_orc_resample_v_muladdtaps4_u8_lq (temp, srcs[0], srcs[1 * src_inc], |
1039 | 0 | srcs[2 * src_inc], srcs[3 * src_inc], taps[0], taps[1], taps[2], |
1040 | 0 | taps[3], count); |
1041 | 0 | max_taps -= 4; |
1042 | 0 | srcs += 4 * src_inc; |
1043 | 0 | taps += 4; |
1044 | 0 | } else { |
1045 | 0 | for (i = 0; i < max_taps - 4; i++) |
1046 | 0 | video_orc_resample_v_muladdtaps_u8_lq (temp, srcs[i * src_inc], taps[i], |
1047 | 0 | count); |
1048 | 0 | srcs += (max_taps - 4) * src_inc; |
1049 | 0 | taps += (max_taps - 4); |
1050 | 0 | max_taps = 4; |
1051 | 0 | } |
1052 | 0 | } |
1053 | 0 | if (max_taps == 4) { |
1054 | 0 | video_orc_resample_v_muladdscaletaps4_u8_lq (d, srcs[0], srcs[1 * src_inc], |
1055 | 0 | srcs[2 * src_inc], srcs[3 * src_inc], temp, taps[0], taps[1], taps[2], |
1056 | 0 | taps[3], count); |
1057 | 0 | } else { |
1058 | 0 | for (i = 0; i < max_taps; i++) |
1059 | 0 | video_orc_resample_v_muladdtaps_u8_lq (temp, srcs[i * src_inc], taps[i], |
1060 | 0 | count); |
1061 | 0 | video_orc_resample_scaletaps_u8_lq (d, temp, count); |
1062 | 0 | } |
1063 | |
|
1064 | | #else |
1065 | | video_orc_resample_v_multaps_u8 (temp, srcs[0], taps[0], count); |
1066 | | for (i = 1; i < max_taps; i++) { |
1067 | | video_orc_resample_v_muladdtaps_u8 (temp, srcs[i * src_inc], taps[i], |
1068 | | count); |
1069 | | } |
1070 | | video_orc_resample_scaletaps_u8 (d, temp, count); |
1071 | | #endif |
1072 | 0 | } |
1073 | | |
1074 | | static void |
1075 | | video_scale_v_ntap_u16 (GstVideoScaler * scale, |
1076 | | gpointer srcs[], gpointer dest, guint dest_offset, guint width, |
1077 | | guint n_elems) |
1078 | 0 | { |
1079 | 0 | gint16 *taps; |
1080 | 0 | gint i, max_taps, count, src_inc; |
1081 | 0 | gpointer d; |
1082 | 0 | gint32 *temp; |
1083 | |
|
1084 | 0 | if (scale->taps_s16 == NULL) |
1085 | 0 | make_s16_taps (scale, n_elems, SCALE_U16); |
1086 | |
|
1087 | 0 | max_taps = scale->resampler.max_taps; |
1088 | 0 | taps = scale->taps_s16 + (scale->resampler.phase[dest_offset] * max_taps); |
1089 | |
|
1090 | 0 | d = (guint16 *) dest; |
1091 | |
|
1092 | 0 | if (scale->flags & GST_VIDEO_SCALER_FLAG_INTERLACED) |
1093 | 0 | src_inc = 2; |
1094 | 0 | else |
1095 | 0 | src_inc = 1; |
1096 | |
|
1097 | 0 | temp = (gint32 *) scale->tmpline2; |
1098 | 0 | count = width * n_elems; |
1099 | |
|
1100 | 0 | video_orc_resample_v_multaps_u16 (temp, srcs[0], taps[0], count); |
1101 | 0 | for (i = 1; i < max_taps; i++) { |
1102 | 0 | video_orc_resample_v_muladdtaps_u16 (temp, srcs[i * src_inc], taps[i], |
1103 | 0 | count); |
1104 | 0 | } |
1105 | 0 | video_orc_resample_scaletaps_u16 (d, temp, count); |
1106 | 0 | } |
1107 | | |
1108 | | static gint |
1109 | | get_y_offset (GstVideoFormat format) |
1110 | 0 | { |
1111 | 0 | switch (format) { |
1112 | 0 | case GST_VIDEO_FORMAT_YUY2: |
1113 | 0 | case GST_VIDEO_FORMAT_YVYU: |
1114 | 0 | return 0; |
1115 | 0 | default: |
1116 | 0 | case GST_VIDEO_FORMAT_UYVY: |
1117 | 0 | return 1; |
1118 | 0 | } |
1119 | 0 | } |
1120 | | |
1121 | | /** |
1122 | | * gst_video_scaler_combine_packed_YUV: (skip) |
1123 | | * @y_scale: a scaler for the Y component |
1124 | | * @uv_scale: a scaler for the U and V components |
1125 | | * @in_format: the input video format |
1126 | | * @out_format: the output video format |
1127 | | * |
1128 | | * Combine a scaler for Y and UV into one scaler for the packed @format. |
1129 | | * |
1130 | | * Returns: a new horizontal videoscaler for @format. |
1131 | | * |
1132 | | * Since: 1.6 |
1133 | | */ |
1134 | | GstVideoScaler * |
1135 | | gst_video_scaler_combine_packed_YUV (GstVideoScaler * y_scale, |
1136 | | GstVideoScaler * uv_scale, GstVideoFormat in_format, |
1137 | | GstVideoFormat out_format) |
1138 | 0 | { |
1139 | 0 | GstVideoScaler *scale; |
1140 | 0 | GstVideoResampler *resampler; |
1141 | 0 | guint i, out_size, max_taps, n_phases; |
1142 | 0 | gdouble *taps; |
1143 | 0 | guint32 *offset, *phase; |
1144 | |
|
1145 | 0 | g_return_val_if_fail (y_scale != NULL, NULL); |
1146 | 0 | g_return_val_if_fail (uv_scale != NULL, NULL); |
1147 | 0 | g_return_val_if_fail (uv_scale->resampler.max_taps == |
1148 | 0 | y_scale->resampler.max_taps, NULL); |
1149 | | |
1150 | 0 | scale = g_new0 (GstVideoScaler, 1); |
1151 | |
|
1152 | 0 | scale->method = y_scale->method; |
1153 | 0 | scale->flags = y_scale->flags; |
1154 | 0 | scale->merged = TRUE; |
1155 | |
|
1156 | 0 | resampler = &scale->resampler; |
1157 | |
|
1158 | 0 | out_size = GST_ROUND_UP_4 (y_scale->resampler.out_size * 2); |
1159 | 0 | max_taps = y_scale->resampler.max_taps; |
1160 | 0 | n_phases = out_size; |
1161 | 0 | offset = g_malloc (sizeof (guint32) * out_size); |
1162 | 0 | phase = g_malloc (sizeof (guint32) * n_phases); |
1163 | 0 | taps = g_malloc (sizeof (gdouble) * max_taps * n_phases); |
1164 | |
|
1165 | 0 | resampler->in_size = y_scale->resampler.in_size * 2; |
1166 | 0 | resampler->out_size = out_size; |
1167 | 0 | resampler->max_taps = max_taps; |
1168 | 0 | resampler->n_phases = n_phases; |
1169 | 0 | resampler->offset = offset; |
1170 | 0 | resampler->phase = phase; |
1171 | 0 | resampler->n_taps = g_malloc (sizeof (guint32) * out_size); |
1172 | 0 | resampler->taps = taps; |
1173 | |
|
1174 | 0 | scale->in_y_offset = get_y_offset (in_format); |
1175 | 0 | scale->out_y_offset = get_y_offset (out_format); |
1176 | 0 | scale->inc = y_scale->inc; |
1177 | |
|
1178 | 0 | for (i = 0; i < out_size; i++) { |
1179 | 0 | gint ic; |
1180 | |
|
1181 | 0 | if ((i & 1) == scale->out_y_offset) { |
1182 | 0 | ic = MIN (i / 2, y_scale->resampler.out_size - 1); |
1183 | 0 | offset[i] = y_scale->resampler.offset[ic] * 2 + scale->in_y_offset; |
1184 | 0 | memcpy (taps + i * max_taps, y_scale->resampler.taps + |
1185 | 0 | y_scale->resampler.phase[ic] * max_taps, max_taps * sizeof (gdouble)); |
1186 | 0 | } else { |
1187 | 0 | ic = MIN (i / 4, uv_scale->resampler.out_size - 1); |
1188 | 0 | offset[i] = uv_scale->resampler.offset[ic] * 4 + (i & 3); |
1189 | 0 | memcpy (taps + i * max_taps, uv_scale->resampler.taps + |
1190 | 0 | uv_scale->resampler.phase[ic] * max_taps, |
1191 | 0 | max_taps * sizeof (gdouble)); |
1192 | 0 | } |
1193 | 0 | phase[i] = i; |
1194 | 0 | } |
1195 | |
|
1196 | 0 | scaler_dump (scale); |
1197 | |
|
1198 | 0 | return scale; |
1199 | 0 | } |
1200 | | |
1201 | | static gboolean |
1202 | | get_functions (GstVideoScaler * hscale, GstVideoScaler * vscale, |
1203 | | GstVideoFormat format, |
1204 | | GstVideoScalerHFunc * hfunc, GstVideoScalerVFunc * vfunc, |
1205 | | gint * n_elems, guint * width, gint * bits) |
1206 | 0 | { |
1207 | 0 | gboolean mono = FALSE; |
1208 | |
|
1209 | 0 | switch (format) { |
1210 | 0 | case GST_VIDEO_FORMAT_GRAY8: |
1211 | 0 | *bits = 8; |
1212 | 0 | *n_elems = 1; |
1213 | 0 | mono = TRUE; |
1214 | 0 | break; |
1215 | 0 | case GST_VIDEO_FORMAT_YUY2: |
1216 | 0 | case GST_VIDEO_FORMAT_YVYU: |
1217 | 0 | case GST_VIDEO_FORMAT_UYVY: |
1218 | 0 | *bits = 8; |
1219 | 0 | *n_elems = 1; |
1220 | 0 | *width = GST_ROUND_UP_4 (*width * 2); |
1221 | 0 | break; |
1222 | 0 | case GST_VIDEO_FORMAT_RGB: |
1223 | 0 | case GST_VIDEO_FORMAT_BGR: |
1224 | 0 | case GST_VIDEO_FORMAT_v308: |
1225 | 0 | case GST_VIDEO_FORMAT_IYU2: |
1226 | 0 | *bits = 8; |
1227 | 0 | *n_elems = 3; |
1228 | 0 | break; |
1229 | 0 | case GST_VIDEO_FORMAT_AYUV: |
1230 | 0 | case GST_VIDEO_FORMAT_RGBx: |
1231 | 0 | case GST_VIDEO_FORMAT_BGRx: |
1232 | 0 | case GST_VIDEO_FORMAT_xRGB: |
1233 | 0 | case GST_VIDEO_FORMAT_xBGR: |
1234 | 0 | case GST_VIDEO_FORMAT_RGBA: |
1235 | 0 | case GST_VIDEO_FORMAT_BGRA: |
1236 | 0 | case GST_VIDEO_FORMAT_ARGB: |
1237 | 0 | case GST_VIDEO_FORMAT_ABGR: |
1238 | 0 | *bits = 8; |
1239 | 0 | *n_elems = 4; |
1240 | 0 | break; |
1241 | 0 | case GST_VIDEO_FORMAT_ARGB64: |
1242 | 0 | case GST_VIDEO_FORMAT_ARGB64_LE: |
1243 | 0 | case GST_VIDEO_FORMAT_ARGB64_BE: |
1244 | 0 | case GST_VIDEO_FORMAT_RGBA64_BE: |
1245 | 0 | case GST_VIDEO_FORMAT_RGBA64_LE: |
1246 | 0 | case GST_VIDEO_FORMAT_BGRA64_BE: |
1247 | 0 | case GST_VIDEO_FORMAT_BGRA64_LE: |
1248 | 0 | case GST_VIDEO_FORMAT_ABGR64_BE: |
1249 | 0 | case GST_VIDEO_FORMAT_ABGR64_LE: |
1250 | 0 | case GST_VIDEO_FORMAT_AYUV64: |
1251 | 0 | *bits = 16; |
1252 | 0 | *n_elems = 4; |
1253 | 0 | break; |
1254 | 0 | case GST_VIDEO_FORMAT_GRAY16_LE: |
1255 | 0 | case GST_VIDEO_FORMAT_GRAY16_BE: |
1256 | 0 | *bits = 16; |
1257 | 0 | *n_elems = 1; |
1258 | 0 | mono = TRUE; |
1259 | 0 | break; |
1260 | 0 | case GST_VIDEO_FORMAT_NV12: |
1261 | 0 | case GST_VIDEO_FORMAT_NV16: |
1262 | 0 | case GST_VIDEO_FORMAT_NV21: |
1263 | 0 | case GST_VIDEO_FORMAT_NV24: |
1264 | 0 | case GST_VIDEO_FORMAT_NV61: |
1265 | 0 | *bits = 8; |
1266 | 0 | *n_elems = 2; |
1267 | 0 | break; |
1268 | 0 | default: |
1269 | 0 | return FALSE; |
1270 | 0 | } |
1271 | 0 | if (*bits == 8) { |
1272 | 0 | switch (hscale ? hscale->resampler.max_taps : 0) { |
1273 | 0 | case 0: |
1274 | 0 | break; |
1275 | 0 | case 1: |
1276 | 0 | if (*n_elems == 1) |
1277 | 0 | *hfunc = video_scale_h_near_u8; |
1278 | 0 | else if (*n_elems == 2) |
1279 | 0 | *hfunc = video_scale_h_near_u16; |
1280 | 0 | else if (*n_elems == 3) |
1281 | 0 | *hfunc = video_scale_h_near_3u8; |
1282 | 0 | else if (*n_elems == 4) |
1283 | 0 | *hfunc = video_scale_h_near_u32; |
1284 | 0 | break; |
1285 | 0 | case 2: |
1286 | 0 | if (*n_elems == 1 && mono) |
1287 | 0 | *hfunc = video_scale_h_2tap_1u8; |
1288 | 0 | else if (*n_elems == 4) |
1289 | 0 | *hfunc = video_scale_h_2tap_4u8; |
1290 | 0 | else |
1291 | 0 | *hfunc = video_scale_h_ntap_u8; |
1292 | 0 | break; |
1293 | 0 | default: |
1294 | 0 | *hfunc = video_scale_h_ntap_u8; |
1295 | 0 | break; |
1296 | 0 | } |
1297 | 0 | switch (vscale ? vscale->resampler.max_taps : 0) { |
1298 | 0 | case 0: |
1299 | 0 | break; |
1300 | 0 | case 1: |
1301 | 0 | *vfunc = video_scale_v_near_u8; |
1302 | 0 | break; |
1303 | 0 | case 2: |
1304 | 0 | *vfunc = video_scale_v_2tap_u8; |
1305 | 0 | break; |
1306 | 0 | case 4: |
1307 | 0 | *vfunc = video_scale_v_4tap_u8; |
1308 | 0 | break; |
1309 | 0 | default: |
1310 | 0 | *vfunc = video_scale_v_ntap_u8; |
1311 | 0 | break; |
1312 | 0 | } |
1313 | 0 | } else if (*bits == 16) { |
1314 | 0 | switch (hscale ? hscale->resampler.max_taps : 0) { |
1315 | 0 | case 0: |
1316 | 0 | break; |
1317 | 0 | case 1: |
1318 | 0 | if (*n_elems == 1) |
1319 | 0 | *hfunc = video_scale_h_near_u16; |
1320 | 0 | else |
1321 | 0 | *hfunc = video_scale_h_near_u64; |
1322 | 0 | break; |
1323 | 0 | default: |
1324 | 0 | *hfunc = video_scale_h_ntap_u16; |
1325 | 0 | break; |
1326 | 0 | } |
1327 | 0 | switch (vscale ? vscale->resampler.max_taps : 0) { |
1328 | 0 | case 0: |
1329 | 0 | break; |
1330 | 0 | case 1: |
1331 | 0 | *vfunc = video_scale_v_near_u16; |
1332 | 0 | break; |
1333 | 0 | case 2: |
1334 | 0 | *vfunc = video_scale_v_2tap_u16; |
1335 | 0 | break; |
1336 | 0 | default: |
1337 | 0 | *vfunc = video_scale_v_ntap_u16; |
1338 | 0 | break; |
1339 | 0 | } |
1340 | 0 | } |
1341 | 0 | return TRUE; |
1342 | 0 | } |
1343 | | |
1344 | | /** |
1345 | | * gst_video_scaler_horizontal: |
1346 | | * @scale: a #GstVideoScaler |
1347 | | * @format: a #GstVideoFormat for @src and @dest |
1348 | | * @src: source pixels |
1349 | | * @dest: destination pixels |
1350 | | * @dest_offset: the horizontal destination offset |
1351 | | * @width: the number of pixels to scale |
1352 | | * |
1353 | | * Horizontally scale the pixels in @src to @dest, starting from @dest_offset |
1354 | | * for @width samples. |
1355 | | */ |
1356 | | void |
1357 | | gst_video_scaler_horizontal (GstVideoScaler * scale, GstVideoFormat format, |
1358 | | gpointer src, gpointer dest, guint dest_offset, guint width) |
1359 | 0 | { |
1360 | 0 | gint n_elems, bits; |
1361 | 0 | GstVideoScalerHFunc func = NULL; |
1362 | |
|
1363 | 0 | g_return_if_fail (scale != NULL); |
1364 | 0 | g_return_if_fail (src != NULL); |
1365 | 0 | g_return_if_fail (dest != NULL); |
1366 | 0 | g_return_if_fail (dest_offset + width <= scale->resampler.out_size); |
1367 | | |
1368 | 0 | if (!get_functions (scale, NULL, format, &func, NULL, &n_elems, &width, &bits) |
1369 | 0 | || func == NULL) |
1370 | 0 | goto no_func; |
1371 | | |
1372 | 0 | if (scale->tmpwidth < width) |
1373 | 0 | realloc_tmplines (scale, n_elems, width); |
1374 | |
|
1375 | 0 | func (scale, src, dest, dest_offset, width, n_elems); |
1376 | 0 | return; |
1377 | | |
1378 | 0 | no_func: |
1379 | 0 | { |
1380 | 0 | GST_WARNING ("no scaler function for format"); |
1381 | 0 | } |
1382 | 0 | } |
1383 | | |
1384 | | /** |
1385 | | * gst_video_scaler_vertical: |
1386 | | * @scale: a #GstVideoScaler |
1387 | | * @format: a #GstVideoFormat for @srcs and @dest |
1388 | | * @src_lines: source pixels lines |
1389 | | * @dest: destination pixels |
1390 | | * @dest_offset: the vertical destination offset |
1391 | | * @width: the number of pixels to scale |
1392 | | * |
1393 | | * Vertically combine @width pixels in the lines in @src_lines to @dest. |
1394 | | * @dest is the location of the target line at @dest_offset and |
1395 | | * @srcs are the input lines for @dest_offset. |
1396 | | */ |
1397 | | void |
1398 | | gst_video_scaler_vertical (GstVideoScaler * scale, GstVideoFormat format, |
1399 | | gpointer src_lines[], gpointer dest, guint dest_offset, guint width) |
1400 | 0 | { |
1401 | 0 | gint n_elems, bits; |
1402 | 0 | GstVideoScalerVFunc func = NULL; |
1403 | |
|
1404 | 0 | g_return_if_fail (scale != NULL); |
1405 | 0 | g_return_if_fail (src_lines != NULL); |
1406 | 0 | g_return_if_fail (dest != NULL); |
1407 | 0 | g_return_if_fail (dest_offset < scale->resampler.out_size); |
1408 | | |
1409 | 0 | if (!get_functions (NULL, scale, format, NULL, &func, &n_elems, &width, &bits) |
1410 | 0 | || func == NULL) |
1411 | 0 | goto no_func; |
1412 | | |
1413 | 0 | if (scale->tmpwidth < width) |
1414 | 0 | realloc_tmplines (scale, n_elems, width); |
1415 | |
|
1416 | 0 | func (scale, src_lines, dest, dest_offset, width, n_elems); |
1417 | |
|
1418 | 0 | return; |
1419 | | |
1420 | 0 | no_func: |
1421 | 0 | { |
1422 | 0 | GST_WARNING ("no scaler function for format"); |
1423 | 0 | } |
1424 | 0 | } |
1425 | | |
1426 | | |
1427 | | /** |
1428 | | * gst_video_scaler_2d: |
1429 | | * @hscale: a horizontal #GstVideoScaler |
1430 | | * @vscale: a vertical #GstVideoScaler |
1431 | | * @format: a #GstVideoFormat for @srcs and @dest |
1432 | | * @src: source pixels |
1433 | | * @src_stride: source pixels stride |
1434 | | * @dest: destination pixels |
1435 | | * @dest_stride: destination pixels stride |
1436 | | * @x: the horizontal destination offset |
1437 | | * @y: the vertical destination offset |
1438 | | * @width: the number of output pixels to scale |
1439 | | * @height: the number of output lines to scale |
1440 | | * |
1441 | | * Scale a rectangle of pixels in @src with @src_stride to @dest with |
1442 | | * @dest_stride using the horizontal scaler @hscaler and the vertical |
1443 | | * scaler @vscale. |
1444 | | * |
1445 | | * One or both of @hscale and @vscale can be NULL to only perform scaling in |
1446 | | * one dimension or do a copy without scaling. |
1447 | | * |
1448 | | * @x and @y are the coordinates in the destination image to process. |
1449 | | */ |
1450 | | void |
1451 | | gst_video_scaler_2d (GstVideoScaler * hscale, GstVideoScaler * vscale, |
1452 | | GstVideoFormat format, gpointer src, gint src_stride, |
1453 | | gpointer dest, gint dest_stride, guint x, guint y, |
1454 | | guint width, guint height) |
1455 | 0 | { |
1456 | 0 | gint n_elems, bits; |
1457 | 0 | GstVideoScalerHFunc hfunc = NULL; |
1458 | 0 | GstVideoScalerVFunc vfunc = NULL; |
1459 | 0 | gint i; |
1460 | 0 | gboolean interlaced; |
1461 | |
|
1462 | 0 | g_return_if_fail (src != NULL); |
1463 | 0 | g_return_if_fail (dest != NULL); |
1464 | | |
1465 | 0 | if (!get_functions (hscale, vscale, format, &hfunc, &vfunc, &n_elems, &width, |
1466 | 0 | &bits)) |
1467 | 0 | goto no_func; |
1468 | | |
1469 | 0 | interlaced = vscale && !!(vscale->flags & GST_VIDEO_SCALER_FLAG_INTERLACED); |
1470 | |
|
1471 | 0 | #define LINE(s,ss,i) ((guint8 *)(s) + ((i) * (ss))) |
1472 | 0 | #define TMP_LINE(s,i) ((guint8 *)((s)->tmpline1) + (i) * (sizeof (gint32) * width * n_elems)) |
1473 | |
|
1474 | 0 | if (vscale == NULL) { |
1475 | 0 | if (hscale == NULL) { |
1476 | 0 | guint xo, xw; |
1477 | 0 | guint8 *s, *d; |
1478 | |
|
1479 | 0 | xo = x * n_elems; |
1480 | 0 | xw = width * n_elems * (bits / 8); |
1481 | |
|
1482 | 0 | s = LINE (src, src_stride, y) + xo; |
1483 | 0 | d = LINE (dest, dest_stride, y) + xo; |
1484 | | |
1485 | | /* no scaling, do memcpy */ |
1486 | 0 | for (i = y; i < height; i++) { |
1487 | 0 | memcpy (d, s, xw); |
1488 | 0 | d += dest_stride; |
1489 | 0 | s += src_stride; |
1490 | 0 | } |
1491 | 0 | } else { |
1492 | 0 | if (hscale->tmpwidth < width) |
1493 | 0 | realloc_tmplines (hscale, n_elems, width); |
1494 | | |
1495 | | /* only horizontal scaling */ |
1496 | 0 | for (i = y; i < height; i++) { |
1497 | 0 | hfunc (hscale, LINE (src, src_stride, i), LINE (dest, dest_stride, i), |
1498 | 0 | x, width, n_elems); |
1499 | 0 | } |
1500 | 0 | } |
1501 | 0 | } else { |
1502 | 0 | guint v_taps; |
1503 | 0 | gpointer *lines; |
1504 | |
|
1505 | 0 | if (vscale->tmpwidth < width) |
1506 | 0 | realloc_tmplines (vscale, n_elems, width); |
1507 | |
|
1508 | 0 | v_taps = vscale->resampler.max_taps; |
1509 | |
|
1510 | 0 | lines = g_alloca ((interlaced ? 2 : 1) * v_taps * sizeof (gpointer)); |
1511 | 0 | memset (lines, 0, (interlaced ? 2 : 1) * v_taps * sizeof (gpointer)); |
1512 | |
|
1513 | 0 | if (hscale == NULL) { |
1514 | 0 | guint src_inc = interlaced ? 2 : 1; |
1515 | | |
1516 | | /* only vertical scaling */ |
1517 | 0 | for (i = y; i < height; i++) { |
1518 | 0 | guint in, j; |
1519 | |
|
1520 | 0 | in = vscale->resampler.offset[i]; |
1521 | 0 | for (j = 0; j < v_taps; j++) { |
1522 | 0 | guint l = in + j * src_inc; |
1523 | |
|
1524 | 0 | g_assert (l < vscale->resampler.in_size); |
1525 | 0 | lines[j * src_inc] = LINE (src, src_stride, l); |
1526 | 0 | } |
1527 | | |
1528 | 0 | vfunc (vscale, lines, LINE (dest, dest_stride, i), i, width, n_elems); |
1529 | 0 | } |
1530 | 0 | } else { |
1531 | 0 | gint s1, s2; |
1532 | 0 | guint *tmpline_lines; |
1533 | |
|
1534 | 0 | tmpline_lines = g_newa (guint, (interlaced ? 2 : 1) * v_taps); |
1535 | | /* initialize with -1 */ |
1536 | 0 | memset (tmpline_lines, 0xff, |
1537 | 0 | (interlaced ? 2 : 1) * v_taps * sizeof (guint)); |
1538 | |
|
1539 | 0 | if (hscale->tmpwidth < width) |
1540 | 0 | realloc_tmplines (hscale, n_elems, width); |
1541 | |
|
1542 | 0 | s1 = width * vscale->resampler.offset[height - 1]; |
1543 | 0 | s2 = width * height; |
1544 | |
|
1545 | 0 | if (s1 <= s2) { |
1546 | 0 | for (i = y; i < height; i++) { |
1547 | 0 | guint in, j; |
1548 | 0 | guint src_inc = interlaced ? 2 : 1; |
1549 | 0 | guint f2_offset = (interlaced && (i % 2 == 1)) * v_taps; |
1550 | |
|
1551 | 0 | in = vscale->resampler.offset[i]; |
1552 | 0 | for (j = 0; j < v_taps; j++) { |
1553 | 0 | guint k; |
1554 | 0 | guint l = in + j * src_inc; |
1555 | |
|
1556 | 0 | g_assert (l < vscale->resampler.in_size); |
1557 | | |
1558 | | /* First check if we already have this line in tmplines */ |
1559 | 0 | for (k = f2_offset; k < v_taps + f2_offset; k++) { |
1560 | 0 | if (tmpline_lines[k] == l) { |
1561 | 0 | lines[j * src_inc] = TMP_LINE (vscale, k); |
1562 | 0 | break; |
1563 | 0 | } |
1564 | 0 | } |
1565 | | /* Found */ |
1566 | 0 | if (k < v_taps + f2_offset) |
1567 | 0 | continue; |
1568 | | |
1569 | | /* Otherwise find an empty line we can clear */ |
1570 | 0 | for (k = f2_offset; k < v_taps + f2_offset; k++) { |
1571 | 0 | if (tmpline_lines[k] < in || tmpline_lines[k] == -1) |
1572 | 0 | break; |
1573 | 0 | } |
1574 | | |
1575 | | /* Must not happen, that would mean we don't have enough space to |
1576 | | * begin with */ |
1577 | 0 | g_assert (k < v_taps + f2_offset); |
1578 | | |
1579 | 0 | hfunc (hscale, LINE (src, src_stride, l), TMP_LINE (vscale, k), x, |
1580 | 0 | width, n_elems); |
1581 | 0 | tmpline_lines[k] = l; |
1582 | 0 | lines[j * src_inc] = TMP_LINE (vscale, k); |
1583 | 0 | } |
1584 | | |
1585 | 0 | vfunc (vscale, lines, LINE (dest, dest_stride, i), i, width, n_elems); |
1586 | 0 | } |
1587 | 0 | } else { |
1588 | 0 | guint vx, vw, w1, ws; |
1589 | 0 | guint h_taps; |
1590 | |
|
1591 | 0 | h_taps = hscale->resampler.max_taps; |
1592 | 0 | w1 = x + width - 1; |
1593 | 0 | ws = hscale->resampler.offset[w1]; |
1594 | | |
1595 | | /* we need to estimate the area that we first need to scale in the |
1596 | | * vertical direction. Scale x and width to find the lower bound and |
1597 | | * overshoot the width to find the upper bound */ |
1598 | 0 | vx = (hscale->inc * x) >> 16; |
1599 | 0 | vx = MIN (vx, hscale->resampler.offset[x]); |
1600 | 0 | vw = (hscale->inc * (x + width)) >> 16; |
1601 | 0 | if (hscale->merged) { |
1602 | 0 | if ((w1 & 1) == hscale->out_y_offset) |
1603 | 0 | vw = MAX (vw, ws + (2 * h_taps)); |
1604 | 0 | else |
1605 | 0 | vw = MAX (vw, ws + (4 * h_taps)); |
1606 | 0 | } else { |
1607 | 0 | vw = MAX (vw, ws + h_taps); |
1608 | 0 | } |
1609 | 0 | vw += 1; |
1610 | | /* but clamp to max size */ |
1611 | 0 | vw = MIN (vw, hscale->resampler.in_size); |
1612 | |
|
1613 | 0 | if (vscale->tmpwidth < vw) |
1614 | 0 | realloc_tmplines (vscale, n_elems, vw); |
1615 | |
|
1616 | 0 | for (i = y; i < height; i++) { |
1617 | 0 | guint in, j; |
1618 | 0 | guint src_inc = interlaced ? 2 : 1; |
1619 | |
|
1620 | 0 | in = vscale->resampler.offset[i]; |
1621 | 0 | for (j = 0; j < v_taps; j++) { |
1622 | 0 | guint l = in + j * src_inc; |
1623 | |
|
1624 | 0 | g_assert (l < vscale->resampler.in_size); |
1625 | 0 | lines[j * src_inc] = LINE (src, src_stride, l) + vx * n_elems; |
1626 | 0 | } |
1627 | | |
1628 | 0 | vfunc (vscale, lines, TMP_LINE (vscale, 0) + vx * n_elems, i, |
1629 | 0 | vw - vx, n_elems); |
1630 | |
|
1631 | 0 | hfunc (hscale, TMP_LINE (vscale, 0), LINE (dest, dest_stride, |
1632 | 0 | i), x, width, n_elems); |
1633 | 0 | } |
1634 | 0 | } |
1635 | 0 | } |
1636 | 0 | } |
1637 | 0 | return; |
1638 | | |
1639 | 0 | no_func: |
1640 | 0 | { |
1641 | 0 | GST_WARNING ("no scaler function for format"); |
1642 | 0 | } |
1643 | 0 | } |
1644 | | |
1645 | | #undef LINE |
1646 | | #undef TMP_LINE |