Coverage Report

Created: 2026-05-28 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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