Coverage Report

Created: 2026-01-25 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libswscale/swscale.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2001-2011 Michael Niedermayer <michaelni@gmx.at>
3
 *
4
 * This file is part of FFmpeg.
5
 *
6
 * FFmpeg is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * FFmpeg is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with FFmpeg; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
#include <stdint.h>
22
#include <stdio.h>
23
#include <string.h>
24
25
#include "libavutil/avassert.h"
26
#include "libavutil/bswap.h"
27
#include "libavutil/common.h"
28
#include "libavutil/cpu.h"
29
#include "libavutil/emms.h"
30
#include "libavutil/intreadwrite.h"
31
#include "libavutil/mem.h"
32
#include "libavutil/mem_internal.h"
33
#include "libavutil/pixdesc.h"
34
#include "config.h"
35
#include "swscale_internal.h"
36
#include "swscale.h"
37
38
DECLARE_ALIGNED(8, const uint8_t, ff_dither_8x8_128)[9][8] = {
39
    {  36, 68,  60, 92,  34, 66,  58, 90, },
40
    { 100,  4, 124, 28,  98,  2, 122, 26, },
41
    {  52, 84,  44, 76,  50, 82,  42, 74, },
42
    { 116, 20, 108, 12, 114, 18, 106, 10, },
43
    {  32, 64,  56, 88,  38, 70,  62, 94, },
44
    {  96,  0, 120, 24, 102,  6, 126, 30, },
45
    {  48, 80,  40, 72,  54, 86,  46, 78, },
46
    { 112, 16, 104,  8, 118, 22, 110, 14, },
47
    {  36, 68,  60, 92,  34, 66,  58, 90, },
48
};
49
50
DECLARE_ALIGNED(8, static const uint8_t, sws_pb_64)[8] = {
51
    64, 64, 64, 64, 64, 64, 64, 64
52
};
53
54
static av_always_inline void fillPlane(uint8_t *plane, int stride, int width,
55
                                       int height, int y, uint8_t val)
56
0
{
57
0
    int i;
58
0
    uint8_t *ptr = plane + stride * y;
59
0
    for (i = 0; i < height; i++) {
60
0
        memset(ptr, val, width);
61
0
        ptr += stride;
62
0
    }
63
0
}
64
65
static void hScale16To19_c(SwsInternal *c, int16_t *_dst, int dstW,
66
                           const uint8_t *_src, const int16_t *filter,
67
                           const int32_t *filterPos, int filterSize)
68
0
{
69
0
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(c->opts.src_format);
70
0
    int i;
71
0
    int32_t *dst        = (int32_t *) _dst;
72
0
    const uint16_t *src = (const uint16_t *) _src;
73
0
    int bits            = desc->comp[0].depth - 1;
74
0
    int sh              = bits - 4;
75
76
0
    if ((isAnyRGB(c->opts.src_format) || c->opts.src_format==AV_PIX_FMT_PAL8) && desc->comp[0].depth<16) {
77
0
        sh = 9;
78
0
    } else if (desc->flags & AV_PIX_FMT_FLAG_FLOAT) { /* float input are process like uint 16bpc */
79
0
        sh = 16 - 1 - 4;
80
0
    }
81
82
0
    for (i = 0; i < dstW; i++) {
83
0
        int j;
84
0
        int srcPos = filterPos[i];
85
0
        int val    = 0;
86
87
0
        for (j = 0; j < filterSize; j++) {
88
0
            val += src[srcPos + j] * filter[filterSize * i + j];
89
0
        }
90
        // filter=14 bit, input=16 bit, output=30 bit, >> 11 makes 19 bit
91
0
        dst[i] = FFMIN(val >> sh, (1 << 19) - 1);
92
0
    }
93
0
}
94
95
static void hScale16To15_c(SwsInternal *c, int16_t *dst, int dstW,
96
                           const uint8_t *_src, const int16_t *filter,
97
                           const int32_t *filterPos, int filterSize)
98
0
{
99
0
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(c->opts.src_format);
100
0
    int i;
101
0
    const uint16_t *src = (const uint16_t *) _src;
102
0
    int sh              = desc->comp[0].depth - 1;
103
104
0
    if (sh<15) {
105
0
        sh = isAnyRGB(c->opts.src_format) || c->opts.src_format==AV_PIX_FMT_PAL8 ? 13 : (desc->comp[0].depth - 1);
106
0
    } else if (desc->flags & AV_PIX_FMT_FLAG_FLOAT) { /* float input are process like uint 16bpc */
107
0
        sh = 16 - 1;
108
0
    }
109
110
0
    for (i = 0; i < dstW; i++) {
111
0
        int j;
112
0
        int srcPos = filterPos[i];
113
0
        int val    = 0;
114
115
0
        for (j = 0; j < filterSize; j++) {
116
0
            val += src[srcPos + j] * filter[filterSize * i + j];
117
0
        }
118
        // filter=14 bit, input=16 bit, output=30 bit, >> 15 makes 15 bit
119
0
        dst[i] = FFMIN(val >> sh, (1 << 15) - 1);
120
0
    }
121
0
}
122
123
// bilinear / bicubic scaling
124
static void hScale8To15_c(SwsInternal *c, int16_t *dst, int dstW,
125
                          const uint8_t *src, const int16_t *filter,
126
                          const int32_t *filterPos, int filterSize)
127
0
{
128
0
    int i;
129
0
    for (i = 0; i < dstW; i++) {
130
0
        int j;
131
0
        int srcPos = filterPos[i];
132
0
        int val    = 0;
133
0
        for (j = 0; j < filterSize; j++) {
134
0
            val += ((int)src[srcPos + j]) * filter[filterSize * i + j];
135
0
        }
136
0
        dst[i] = FFMIN(val >> 7, (1 << 15) - 1); // the cubic equation does overflow ...
137
0
    }
138
0
}
139
140
static void hScale8To19_c(SwsInternal *c, int16_t *_dst, int dstW,
141
                          const uint8_t *src, const int16_t *filter,
142
                          const int32_t *filterPos, int filterSize)
143
0
{
144
0
    int i;
145
0
    int32_t *dst = (int32_t *) _dst;
146
0
    for (i = 0; i < dstW; i++) {
147
0
        int j;
148
0
        int srcPos = filterPos[i];
149
0
        int val    = 0;
150
0
        for (j = 0; j < filterSize; j++) {
151
0
            val += ((int)src[srcPos + j]) * filter[filterSize * i + j];
152
0
        }
153
0
        dst[i] = FFMIN(val >> 3, (1 << 19) - 1); // the cubic equation does overflow ...
154
0
    }
155
0
}
156
157
// FIXME all pal and rgb srcFormats could do this conversion as well
158
// FIXME all scalers more complex than bilinear could do half of this transform
159
static void chrRangeToJpeg_c(int16_t *dstU, int16_t *dstV, int width,
160
                             uint32_t _coeff, int64_t _offset)
161
0
{
162
0
    uint16_t coeff = _coeff;
163
0
    int32_t offset = _offset;
164
0
    int i;
165
0
    for (i = 0; i < width; i++) {
166
0
        int U = (dstU[i] * coeff + offset) >> 14;
167
0
        int V = (dstV[i] * coeff + offset) >> 14;
168
0
        dstU[i] = FFMIN(U, (1 << 15) - 1);
169
0
        dstV[i] = FFMIN(V, (1 << 15) - 1);
170
0
    }
171
0
}
172
173
static void chrRangeFromJpeg_c(int16_t *dstU, int16_t *dstV, int width,
174
                               uint32_t _coeff, int64_t _offset)
175
0
{
176
0
    uint16_t coeff = _coeff;
177
0
    int32_t offset = _offset;
178
0
    int i;
179
0
    for (i = 0; i < width; i++) {
180
0
        dstU[i] = (dstU[i] * coeff + offset) >> 14;
181
0
        dstV[i] = (dstV[i] * coeff + offset) >> 14;
182
0
    }
183
0
}
184
185
static void lumRangeToJpeg_c(int16_t *dst, int width,
186
                             uint32_t _coeff, int64_t _offset)
187
0
{
188
0
    uint16_t coeff = _coeff;
189
0
    int32_t offset = _offset;
190
0
    int i;
191
0
    for (i = 0; i < width; i++) {
192
0
        int Y = (dst[i] * coeff + offset) >> 14;
193
0
        dst[i] = FFMIN(Y, (1 << 15) - 1);
194
0
    }
195
0
}
196
197
static void lumRangeFromJpeg_c(int16_t *dst, int width,
198
                               uint32_t _coeff, int64_t _offset)
199
0
{
200
0
    uint16_t coeff = _coeff;
201
0
    int32_t offset = _offset;
202
0
    int i;
203
0
    for (i = 0; i < width; i++)
204
0
        dst[i] = (dst[i] * coeff + offset) >> 14;
205
0
}
206
207
static void chrRangeToJpeg16_c(int16_t *_dstU, int16_t *_dstV, int width,
208
                               uint32_t coeff, int64_t offset)
209
0
{
210
0
    int i;
211
0
    int32_t *dstU = (int32_t *) _dstU;
212
0
    int32_t *dstV = (int32_t *) _dstV;
213
0
    for (i = 0; i < width; i++) {
214
0
        int U = ((int64_t) dstU[i] * coeff + offset) >> 18;
215
0
        int V = ((int64_t) dstV[i] * coeff + offset) >> 18;
216
0
        dstU[i] = FFMIN(U, (1 << 19) - 1);
217
0
        dstV[i] = FFMIN(V, (1 << 19) - 1);
218
0
    }
219
0
}
220
221
static void chrRangeFromJpeg16_c(int16_t *_dstU, int16_t *_dstV, int width,
222
                                 uint32_t coeff, int64_t offset)
223
0
{
224
0
    int i;
225
0
    int32_t *dstU = (int32_t *) _dstU;
226
0
    int32_t *dstV = (int32_t *) _dstV;
227
0
    for (i = 0; i < width; i++) {
228
0
        dstU[i] = ((int64_t) dstU[i] * coeff + offset) >> 18;
229
0
        dstV[i] = ((int64_t) dstV[i] * coeff + offset) >> 18;
230
0
    }
231
0
}
232
233
static void lumRangeToJpeg16_c(int16_t *_dst, int width,
234
                               uint32_t coeff, int64_t offset)
235
0
{
236
0
    int i;
237
0
    int32_t *dst = (int32_t *) _dst;
238
0
    for (i = 0; i < width; i++) {
239
0
        int Y = ((int64_t) dst[i] * coeff + offset) >> 18;
240
0
        dst[i] = FFMIN(Y, (1 << 19) - 1);
241
0
    }
242
0
}
243
244
static void lumRangeFromJpeg16_c(int16_t *_dst, int width,
245
                                 uint32_t coeff, int64_t offset)
246
0
{
247
0
    int i;
248
0
    int32_t *dst = (int32_t *) _dst;
249
0
    for (i = 0; i < width; i++)
250
0
        dst[i] = ((int64_t) dst[i] * coeff + offset) >> 18;
251
0
}
252
253
254
0
#define DEBUG_SWSCALE_BUFFERS 0
255
#define DEBUG_BUFFERS(...)                      \
256
0
    if (DEBUG_SWSCALE_BUFFERS)                  \
257
0
        av_log(c, AV_LOG_DEBUG, __VA_ARGS__)
258
259
int ff_swscale(SwsInternal *c, const uint8_t *const src[], const int srcStride[],
260
               int srcSliceY, int srcSliceH, uint8_t *const dst[],
261
               const int dstStride[], int dstSliceY, int dstSliceH)
262
0
{
263
0
    const int scale_dst = dstSliceY > 0 || dstSliceH < c->opts.dst_h;
264
265
    /* load a few things into local vars to make the code more readable?
266
     * and faster */
267
0
    const int dstW                   = c->opts.dst_w;
268
0
    int dstH                         = c->opts.dst_h;
269
270
0
    const enum AVPixelFormat dstFormat = c->opts.dst_format;
271
0
    const int flags                  = c->opts.flags;
272
0
    int32_t *vLumFilterPos           = c->vLumFilterPos;
273
0
    int32_t *vChrFilterPos           = c->vChrFilterPos;
274
275
0
    const int vLumFilterSize         = c->vLumFilterSize;
276
0
    const int vChrFilterSize         = c->vChrFilterSize;
277
278
0
    yuv2planar1_fn yuv2plane1        = c->yuv2plane1;
279
0
    yuv2planarX_fn yuv2planeX        = c->yuv2planeX;
280
0
    yuv2interleavedX_fn yuv2nv12cX   = c->yuv2nv12cX;
281
0
    yuv2packed1_fn yuv2packed1       = c->yuv2packed1;
282
0
    yuv2packed2_fn yuv2packed2       = c->yuv2packed2;
283
0
    yuv2packedX_fn yuv2packedX       = c->yuv2packedX;
284
0
    yuv2anyX_fn yuv2anyX             = c->yuv2anyX;
285
0
    const int chrSrcSliceY           =                srcSliceY >> c->chrSrcVSubSample;
286
0
    const int chrSrcSliceH           = AV_CEIL_RSHIFT(srcSliceH,   c->chrSrcVSubSample);
287
0
    int should_dither                = isNBPS(c->opts.src_format) ||
288
0
                                       is16BPS(c->opts.src_format);
289
0
    int lastDstY;
290
291
    /* vars which will change and which we need to store back in the context */
292
0
    int dstY         = c->dstY;
293
0
    int lastInLumBuf = c->lastInLumBuf;
294
0
    int lastInChrBuf = c->lastInChrBuf;
295
296
0
    int lumStart = 0;
297
0
    int lumEnd = c->descIndex[0];
298
0
    int chrStart = lumEnd;
299
0
    int chrEnd = c->descIndex[1];
300
0
    int vStart = chrEnd;
301
0
    int vEnd = c->numDesc;
302
0
    SwsSlice *src_slice = &c->slice[lumStart];
303
0
    SwsSlice *hout_slice = &c->slice[c->numSlice-2];
304
0
    SwsSlice *vout_slice = &c->slice[c->numSlice-1];
305
0
    SwsFilterDescriptor *desc = c->desc;
306
307
0
    int needAlpha = c->needAlpha;
308
309
0
    int hasLumHoles = 1;
310
0
    int hasChrHoles = 1;
311
312
0
    const uint8_t *src2[4];
313
0
    int srcStride2[4];
314
315
0
    if (isPacked(c->opts.src_format)) {
316
0
        src2[0] =
317
0
        src2[1] =
318
0
        src2[2] =
319
0
        src2[3] = src[0];
320
0
        srcStride2[0] =
321
0
        srcStride2[1] =
322
0
        srcStride2[2] =
323
0
        srcStride2[3] = srcStride[0];
324
0
    } else {
325
0
        memcpy(src2, src, sizeof(src2));
326
0
        memcpy(srcStride2, srcStride, sizeof(srcStride2));
327
0
    }
328
329
0
    srcStride2[1] *= 1 << c->vChrDrop;
330
0
    srcStride2[2] *= 1 << c->vChrDrop;
331
332
0
    DEBUG_BUFFERS("swscale() %p[%d] %p[%d] %p[%d] %p[%d] -> %p[%d] %p[%d] %p[%d] %p[%d]\n",
333
0
                  src2[0], srcStride2[0], src2[1], srcStride2[1],
334
0
                  src2[2], srcStride2[2], src2[3], srcStride2[3],
335
0
                  dst[0], dstStride[0], dst[1], dstStride[1],
336
0
                  dst[2], dstStride[2], dst[3], dstStride[3]);
337
0
    DEBUG_BUFFERS("srcSliceY: %d srcSliceH: %d dstY: %d dstH: %d\n",
338
0
                  srcSliceY, srcSliceH, dstY, dstH);
339
0
    DEBUG_BUFFERS("vLumFilterSize: %d vChrFilterSize: %d\n",
340
0
                  vLumFilterSize, vChrFilterSize);
341
342
0
    if (dstStride[0]&15 || dstStride[1]&15 ||
343
0
        dstStride[2]&15 || dstStride[3]&15) {
344
0
        SwsInternal *const ctx = c->parent ? sws_internal(c->parent) : c;
345
0
        if (flags & SWS_PRINT_INFO &&
346
0
            !atomic_exchange_explicit(&ctx->stride_unaligned_warned, 1, memory_order_relaxed)) {
347
0
            av_log(c, AV_LOG_WARNING,
348
0
                   "Warning: dstStride is not aligned!\n"
349
0
                   "         ->cannot do aligned memory accesses anymore\n");
350
0
        }
351
0
    }
352
353
0
#if ARCH_X86
354
0
    if (   (uintptr_t) dst[0]&15 || (uintptr_t) dst[1]&15 || (uintptr_t) dst[2]&15
355
0
        || (uintptr_t)src2[0]&15 || (uintptr_t)src2[1]&15 || (uintptr_t)src2[2]&15
356
0
        ||  dstStride[0]&15 ||  dstStride[1]&15 ||  dstStride[2]&15 ||  dstStride[3]&15
357
0
        || srcStride2[0]&15 || srcStride2[1]&15 || srcStride2[2]&15 || srcStride2[3]&15
358
0
    ) {
359
0
        SwsInternal *const ctx = c->parent ? sws_internal(c->parent) : c;
360
0
        int cpu_flags = av_get_cpu_flags();
361
0
        if (flags & SWS_PRINT_INFO && HAVE_MMXEXT && (cpu_flags & AV_CPU_FLAG_SSE2) &&
362
0
            !atomic_exchange_explicit(&ctx->stride_unaligned_warned,1, memory_order_relaxed)) {
363
0
            av_log(c, AV_LOG_WARNING, "Warning: data is not aligned! This can lead to a speed loss\n");
364
0
        }
365
0
    }
366
0
#endif
367
368
0
    if (scale_dst) {
369
0
        dstY         = dstSliceY;
370
0
        dstH         = dstY + dstSliceH;
371
0
        lastInLumBuf = -1;
372
0
        lastInChrBuf = -1;
373
0
    } else if (srcSliceY == 0) {
374
        /* Note the user might start scaling the picture in the middle so this
375
         * will not get executed. This is not really intended but works
376
         * currently, so people might do it. */
377
0
        dstY         = 0;
378
0
        lastInLumBuf = -1;
379
0
        lastInChrBuf = -1;
380
0
    }
381
382
0
    if (!should_dither) {
383
0
        c->chrDither8 = c->lumDither8 = sws_pb_64;
384
0
    }
385
0
    lastDstY = dstY;
386
387
0
    ff_init_vscale_pfn(c, yuv2plane1, yuv2planeX, yuv2nv12cX,
388
0
                   yuv2packed1, yuv2packed2, yuv2packedX, yuv2anyX, c->use_mmx_vfilter);
389
390
0
    ff_init_slice_from_src(src_slice, (uint8_t**)src2, srcStride2, c->opts.src_w,
391
0
            srcSliceY, srcSliceH, chrSrcSliceY, chrSrcSliceH, 1);
392
393
0
    ff_init_slice_from_src(vout_slice, (uint8_t**)dst, dstStride, c->opts.dst_w,
394
0
            dstY, dstSliceH, dstY >> c->chrDstVSubSample,
395
0
            AV_CEIL_RSHIFT(dstSliceH, c->chrDstVSubSample), scale_dst);
396
0
    if (srcSliceY == 0) {
397
0
        hout_slice->plane[0].sliceY = lastInLumBuf + 1;
398
0
        hout_slice->plane[1].sliceY = lastInChrBuf + 1;
399
0
        hout_slice->plane[2].sliceY = lastInChrBuf + 1;
400
0
        hout_slice->plane[3].sliceY = lastInLumBuf + 1;
401
402
0
        hout_slice->plane[0].sliceH =
403
0
        hout_slice->plane[1].sliceH =
404
0
        hout_slice->plane[2].sliceH =
405
0
        hout_slice->plane[3].sliceH = 0;
406
0
        hout_slice->width = dstW;
407
0
    }
408
409
0
    for (; dstY < dstH; dstY++) {
410
0
        const int chrDstY = dstY >> c->chrDstVSubSample;
411
0
        int use_mmx_vfilter= c->use_mmx_vfilter;
412
413
        // First line needed as input
414
0
        const int firstLumSrcY  = FFMAX(1 - vLumFilterSize, vLumFilterPos[dstY]);
415
0
        const int firstLumSrcY2 = FFMAX(1 - vLumFilterSize, vLumFilterPos[FFMIN(dstY | ((1 << c->chrDstVSubSample) - 1), c->opts.dst_h - 1)]);
416
        // First line needed as input
417
0
        const int firstChrSrcY  = FFMAX(1 - vChrFilterSize, vChrFilterPos[chrDstY]);
418
419
        // Last line needed as input
420
0
        int lastLumSrcY  = FFMIN(c->opts.src_h,    firstLumSrcY  + vLumFilterSize) - 1;
421
0
        int lastLumSrcY2 = FFMIN(c->opts.src_h,    firstLumSrcY2 + vLumFilterSize) - 1;
422
0
        int lastChrSrcY  = FFMIN(c->chrSrcH, firstChrSrcY  + vChrFilterSize) - 1;
423
0
        int enough_lines;
424
425
0
        int i;
426
0
        int posY, cPosY, firstPosY, lastPosY, firstCPosY, lastCPosY;
427
428
        // handle holes (FAST_BILINEAR & weird filters)
429
0
        if (firstLumSrcY > lastInLumBuf) {
430
431
0
            hasLumHoles = lastInLumBuf != firstLumSrcY - 1;
432
0
            if (hasLumHoles) {
433
0
                hout_slice->plane[0].sliceY = firstLumSrcY;
434
0
                hout_slice->plane[3].sliceY = firstLumSrcY;
435
0
                hout_slice->plane[0].sliceH =
436
0
                hout_slice->plane[3].sliceH = 0;
437
0
            }
438
439
0
            lastInLumBuf = firstLumSrcY - 1;
440
0
        }
441
0
        if (firstChrSrcY > lastInChrBuf) {
442
443
0
            hasChrHoles = lastInChrBuf != firstChrSrcY - 1;
444
0
            if (hasChrHoles) {
445
0
                hout_slice->plane[1].sliceY = firstChrSrcY;
446
0
                hout_slice->plane[2].sliceY = firstChrSrcY;
447
0
                hout_slice->plane[1].sliceH =
448
0
                hout_slice->plane[2].sliceH = 0;
449
0
            }
450
451
0
            lastInChrBuf = firstChrSrcY - 1;
452
0
        }
453
454
0
        DEBUG_BUFFERS("dstY: %d\n", dstY);
455
0
        DEBUG_BUFFERS("\tfirstLumSrcY: %d lastLumSrcY: %d lastInLumBuf: %d\n",
456
0
                      firstLumSrcY, lastLumSrcY, lastInLumBuf);
457
0
        DEBUG_BUFFERS("\tfirstChrSrcY: %d lastChrSrcY: %d lastInChrBuf: %d\n",
458
0
                      firstChrSrcY, lastChrSrcY, lastInChrBuf);
459
460
        // Do we have enough lines in this slice to output the dstY line
461
0
        enough_lines = lastLumSrcY2 < srcSliceY + srcSliceH &&
462
0
                       lastChrSrcY < AV_CEIL_RSHIFT(srcSliceY + srcSliceH, c->chrSrcVSubSample);
463
464
0
        if (!enough_lines) {
465
0
            lastLumSrcY = srcSliceY + srcSliceH - 1;
466
0
            lastChrSrcY = chrSrcSliceY + chrSrcSliceH - 1;
467
0
            DEBUG_BUFFERS("buffering slice: lastLumSrcY %d lastChrSrcY %d\n",
468
0
                          lastLumSrcY, lastChrSrcY);
469
0
        }
470
471
0
        av_assert0((lastLumSrcY - firstLumSrcY + 1) <= hout_slice->plane[0].available_lines);
472
0
        av_assert0((lastChrSrcY - firstChrSrcY + 1) <= hout_slice->plane[1].available_lines);
473
474
475
0
        posY = hout_slice->plane[0].sliceY + hout_slice->plane[0].sliceH;
476
0
        if (posY <= lastLumSrcY && !hasLumHoles) {
477
0
            firstPosY = FFMAX(firstLumSrcY, posY);
478
0
            lastPosY = FFMIN(firstLumSrcY + hout_slice->plane[0].available_lines - 1, srcSliceY + srcSliceH - 1);
479
0
        } else {
480
0
            firstPosY = posY;
481
0
            lastPosY = lastLumSrcY;
482
0
        }
483
484
0
        cPosY = hout_slice->plane[1].sliceY + hout_slice->plane[1].sliceH;
485
0
        if (cPosY <= lastChrSrcY && !hasChrHoles) {
486
0
            firstCPosY = FFMAX(firstChrSrcY, cPosY);
487
0
            lastCPosY = FFMIN(firstChrSrcY + hout_slice->plane[1].available_lines - 1, AV_CEIL_RSHIFT(srcSliceY + srcSliceH, c->chrSrcVSubSample) - 1);
488
0
        } else {
489
0
            firstCPosY = cPosY;
490
0
            lastCPosY = lastChrSrcY;
491
0
        }
492
493
0
        ff_rotate_slice(hout_slice, lastPosY, lastCPosY);
494
495
0
        if (posY < lastLumSrcY + 1) {
496
0
            for (i = lumStart; i < lumEnd; ++i)
497
0
                desc[i].process(c, &desc[i], firstPosY, lastPosY - firstPosY + 1);
498
0
        }
499
500
0
        lastInLumBuf = lastLumSrcY;
501
502
0
        if (cPosY < lastChrSrcY + 1) {
503
0
            for (i = chrStart; i < chrEnd; ++i)
504
0
                desc[i].process(c, &desc[i], firstCPosY, lastCPosY - firstCPosY + 1);
505
0
        }
506
507
0
        lastInChrBuf = lastChrSrcY;
508
509
0
        if (!enough_lines)
510
0
            break;  // we can't output a dstY line so let's try with the next slice
511
512
0
#if HAVE_MMX_INLINE
513
0
        ff_updateMMXDitherTables(c, dstY);
514
0
        c->dstW_mmx = c->opts.dst_w;
515
0
#endif
516
0
        if (should_dither) {
517
0
            c->chrDither8 = ff_dither_8x8_128[chrDstY & 7];
518
0
            c->lumDither8 = ff_dither_8x8_128[dstY    & 7];
519
0
        }
520
0
        if (dstY >= c->opts.dst_h - 2) {
521
            /* hmm looks like we can't use MMX here without overwriting
522
             * this array's tail */
523
0
            ff_sws_init_output_funcs(c, &yuv2plane1, &yuv2planeX, &yuv2nv12cX,
524
0
                                     &yuv2packed1, &yuv2packed2, &yuv2packedX, &yuv2anyX);
525
0
            use_mmx_vfilter= 0;
526
0
            ff_init_vscale_pfn(c, yuv2plane1, yuv2planeX, yuv2nv12cX,
527
0
                           yuv2packed1, yuv2packed2, yuv2packedX, yuv2anyX, use_mmx_vfilter);
528
0
        }
529
530
0
        for (i = vStart; i < vEnd; ++i)
531
0
            desc[i].process(c, &desc[i], dstY, 1);
532
0
    }
533
0
    if (isPlanar(dstFormat) && isALPHA(dstFormat) && !needAlpha) {
534
0
        int offset = lastDstY - dstSliceY;
535
0
        int length = dstW;
536
0
        int height = dstY - lastDstY;
537
538
0
        if (is16BPS(dstFormat) || isNBPS(dstFormat)) {
539
0
            const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(dstFormat);
540
0
            fillPlane16(dst[3], dstStride[3], length, height, offset,
541
0
                    1, desc->comp[3].depth,
542
0
                    isBE(dstFormat));
543
0
        } else if (is32BPS(dstFormat)) {
544
0
            const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(dstFormat);
545
0
            fillPlane32(dst[3], dstStride[3], length, height, offset,
546
0
                    1, desc->comp[3].depth,
547
0
                    isBE(dstFormat), desc->flags & AV_PIX_FMT_FLAG_FLOAT);
548
0
        } else
549
0
            fillPlane(dst[3], dstStride[3], length, height, offset, 255);
550
0
    }
551
552
0
#if HAVE_MMXEXT_INLINE
553
0
    if (av_get_cpu_flags() & AV_CPU_FLAG_MMXEXT)
554
0
        __asm__ volatile ("sfence" ::: "memory");
555
0
#endif
556
0
    emms_c();
557
558
    /* store changed local vars back in the context */
559
0
    c->dstY         = dstY;
560
0
    c->lastInLumBuf = lastInLumBuf;
561
0
    c->lastInChrBuf = lastInChrBuf;
562
563
0
    return dstY - lastDstY;
564
0
}
565
566
/*
567
 * Solve for coeff and offset:
568
 * dst = ((src << src_shift) * coeff + offset) >> (mult_shift + src_shift)
569
 *
570
 * If SwsInternal->dstBpc is > 14, coeff is uint16_t and offset is int32_t,
571
 * otherwise (SwsInternal->dstBpc is <= 14) coeff is uint32_t and offset is
572
 * int64_t.
573
 */
574
static void solve_range_convert(uint16_t src_min, uint16_t src_max,
575
                                uint16_t dst_min, uint16_t dst_max,
576
                                int src_bits, int src_shift, int mult_shift,
577
                                uint32_t *coeff, int64_t *offset)
578
0
{
579
0
    uint16_t src_range = src_max - src_min;
580
0
    uint16_t dst_range = dst_max - dst_min;
581
0
    int total_shift = mult_shift + src_shift;
582
0
    *coeff = AV_CEIL_RSHIFT(((uint64_t) dst_range << total_shift) / src_range, src_shift);
583
0
    *offset = ((int64_t) dst_max << total_shift) -
584
0
              ((int64_t) src_max << src_shift) * *coeff +
585
0
              (1U << (mult_shift - 1));
586
0
}
587
588
static void init_range_convert_constants(SwsInternal *c)
589
0
{
590
0
    const int bit_depth = c->dstBpc ? FFMIN(c->dstBpc, 16) : 8;
591
0
    const int src_bits = bit_depth <= 14 ? 15 : 19;
592
0
    const int src_shift = src_bits - bit_depth;
593
0
    const int mult_shift = bit_depth <= 14 ? 14 : 18;
594
0
    const uint16_t mpeg_min = 16U << (bit_depth - 8);
595
0
    const uint16_t mpeg_max_lum = 235U << (bit_depth - 8);
596
0
    const uint16_t mpeg_max_chr = 240U << (bit_depth - 8);
597
0
    const uint16_t jpeg_max = (1U << bit_depth) - 1;
598
0
    uint16_t src_min, src_max_lum, src_max_chr;
599
0
    uint16_t dst_min, dst_max_lum, dst_max_chr;
600
0
    if (c->opts.src_range) {
601
0
        src_min     = 0;
602
0
        src_max_lum = jpeg_max;
603
0
        src_max_chr = jpeg_max;
604
0
        dst_min     = mpeg_min;
605
0
        dst_max_lum = mpeg_max_lum;
606
0
        dst_max_chr = mpeg_max_chr;
607
0
    } else {
608
0
        src_min     = mpeg_min;
609
0
        src_max_lum = mpeg_max_lum;
610
0
        src_max_chr = mpeg_max_chr;
611
0
        dst_min     = 0;
612
0
        dst_max_lum = jpeg_max;
613
0
        dst_max_chr = jpeg_max;
614
0
    }
615
0
    solve_range_convert(src_min, src_max_lum, dst_min, dst_max_lum,
616
0
                        src_bits, src_shift, mult_shift,
617
0
                        &c->lumConvertRange_coeff, &c->lumConvertRange_offset);
618
0
    solve_range_convert(src_min, src_max_chr, dst_min, dst_max_chr,
619
0
                        src_bits, src_shift, mult_shift,
620
0
                        &c->chrConvertRange_coeff, &c->chrConvertRange_offset);
621
0
}
622
623
av_cold void ff_sws_init_range_convert(SwsInternal *c)
624
0
{
625
0
    c->lumConvertRange = NULL;
626
0
    c->chrConvertRange = NULL;
627
0
    if (c->opts.src_range != c->opts.dst_range && !isAnyRGB(c->opts.dst_format) && c->dstBpc < 32) {
628
0
        init_range_convert_constants(c);
629
0
        if (c->dstBpc <= 14) {
630
0
            if (c->opts.src_range) {
631
0
                c->lumConvertRange = lumRangeFromJpeg_c;
632
0
                c->chrConvertRange = chrRangeFromJpeg_c;
633
0
            } else {
634
0
                c->lumConvertRange = lumRangeToJpeg_c;
635
0
                c->chrConvertRange = chrRangeToJpeg_c;
636
0
            }
637
0
        } else {
638
0
            if (c->opts.src_range) {
639
0
                c->lumConvertRange = lumRangeFromJpeg16_c;
640
0
                c->chrConvertRange = chrRangeFromJpeg16_c;
641
0
            } else {
642
0
                c->lumConvertRange = lumRangeToJpeg16_c;
643
0
                c->chrConvertRange = chrRangeToJpeg16_c;
644
0
            }
645
0
        }
646
647
#if ARCH_AARCH64
648
        ff_sws_init_range_convert_aarch64(c);
649
#elif ARCH_LOONGARCH64
650
        ff_sws_init_range_convert_loongarch(c);
651
#elif ARCH_RISCV
652
        ff_sws_init_range_convert_riscv(c);
653
#elif ARCH_X86
654
        ff_sws_init_range_convert_x86(c);
655
0
#endif
656
0
    }
657
0
}
658
659
static av_cold void sws_init_swscale(SwsInternal *c)
660
0
{
661
0
    enum AVPixelFormat srcFormat = c->opts.src_format;
662
663
0
    ff_sws_init_xyzdsp(c);
664
665
0
    ff_sws_init_output_funcs(c, &c->yuv2plane1, &c->yuv2planeX,
666
0
                             &c->yuv2nv12cX, &c->yuv2packed1,
667
0
                             &c->yuv2packed2, &c->yuv2packedX, &c->yuv2anyX);
668
669
0
    ff_sws_init_input_funcs(c, &c->lumToYV12, &c->alpToYV12, &c->chrToYV12,
670
0
                            &c->readLumPlanar, &c->readAlpPlanar, &c->readChrPlanar);
671
672
0
    if (c->srcBpc == 8) {
673
0
        if (c->dstBpc <= 14) {
674
0
            c->hyScale = c->hcScale = hScale8To15_c;
675
0
            if (c->opts.flags & SWS_FAST_BILINEAR) {
676
0
                c->hyscale_fast = ff_hyscale_fast_c;
677
0
                c->hcscale_fast = ff_hcscale_fast_c;
678
0
            }
679
0
        } else {
680
0
            c->hyScale = c->hcScale = hScale8To19_c;
681
0
        }
682
0
    } else {
683
0
        c->hyScale = c->hcScale = c->dstBpc > 14 ? hScale16To19_c
684
0
                                                 : hScale16To15_c;
685
0
    }
686
687
0
    ff_sws_init_range_convert(c);
688
689
0
    if (!(isGray(srcFormat) || isGray(c->opts.dst_format) ||
690
0
          srcFormat == AV_PIX_FMT_MONOBLACK || srcFormat == AV_PIX_FMT_MONOWHITE))
691
0
        c->needs_hcscale = 1;
692
0
}
693
694
void ff_sws_init_scale(SwsInternal *c)
695
0
{
696
0
    sws_init_swscale(c);
697
698
#if ARCH_PPC
699
    ff_sws_init_swscale_ppc(c);
700
#elif ARCH_X86
701
    ff_sws_init_swscale_x86(c);
702
#elif ARCH_AARCH64
703
    ff_sws_init_swscale_aarch64(c);
704
#elif ARCH_ARM
705
    ff_sws_init_swscale_arm(c);
706
#elif ARCH_LOONGARCH64
707
    ff_sws_init_swscale_loongarch(c);
708
#elif ARCH_RISCV
709
    ff_sws_init_swscale_riscv(c);
710
#endif
711
0
}
712
713
static void reset_ptr(const uint8_t *src[], enum AVPixelFormat format)
714
0
{
715
0
    if (!isALPHA(format))
716
0
        src[3] = NULL;
717
0
    if (!isPlanar(format)) {
718
0
        src[3] = src[2] = NULL;
719
720
0
        if (!usePal(format))
721
0
            src[1] = NULL;
722
0
    }
723
0
}
724
725
static int check_image_pointers(const uint8_t * const data[4], enum AVPixelFormat pix_fmt,
726
                                const int linesizes[4])
727
0
{
728
0
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
729
0
    int i;
730
731
0
    av_assert2(desc);
732
733
0
    for (i = 0; i < 4; i++) {
734
0
        int plane = desc->comp[i].plane;
735
0
        if (!data[plane] || !linesizes[plane])
736
0
            return 0;
737
0
    }
738
739
0
    return 1;
740
0
}
741
742
static void xyz12Torgb48_c(const SwsInternal *c, uint8_t *dst, int dst_stride,
743
                           const uint8_t *src, int src_stride, int w, int h)
744
0
{
745
0
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(c->opts.src_format);
746
747
0
    for (int yp = 0; yp < h; yp++) {
748
0
        const uint16_t *src16 = (const uint16_t *) src;
749
0
        uint16_t *dst16 = (uint16_t *) dst;
750
751
0
        for (int xp = 0; xp < 3 * w; xp += 3) {
752
0
            int x, y, z, r, g, b;
753
754
0
            if (desc->flags & AV_PIX_FMT_FLAG_BE) {
755
0
                x = AV_RB16(src16 + xp + 0);
756
0
                y = AV_RB16(src16 + xp + 1);
757
0
                z = AV_RB16(src16 + xp + 2);
758
0
            } else {
759
0
                x = AV_RL16(src16 + xp + 0);
760
0
                y = AV_RL16(src16 + xp + 1);
761
0
                z = AV_RL16(src16 + xp + 2);
762
0
            }
763
764
0
            x = c->xyz2rgb.gamma.in[x >> 4];
765
0
            y = c->xyz2rgb.gamma.in[y >> 4];
766
0
            z = c->xyz2rgb.gamma.in[z >> 4];
767
768
            // convert from XYZlinear to sRGBlinear
769
0
            r = c->xyz2rgb.mat[0][0] * x +
770
0
                c->xyz2rgb.mat[0][1] * y +
771
0
                c->xyz2rgb.mat[0][2] * z >> 12;
772
0
            g = c->xyz2rgb.mat[1][0] * x +
773
0
                c->xyz2rgb.mat[1][1] * y +
774
0
                c->xyz2rgb.mat[1][2] * z >> 12;
775
0
            b = c->xyz2rgb.mat[2][0] * x +
776
0
                c->xyz2rgb.mat[2][1] * y +
777
0
                c->xyz2rgb.mat[2][2] * z >> 12;
778
779
            // limit values to 16-bit depth
780
0
            r = av_clip_uint16(r);
781
0
            g = av_clip_uint16(g);
782
0
            b = av_clip_uint16(b);
783
784
            // convert from sRGBlinear to RGB and scale from 12bit to 16bit
785
0
            if (desc->flags & AV_PIX_FMT_FLAG_BE) {
786
0
                AV_WB16(dst16 + xp + 0, c->xyz2rgb.gamma.out[r] << 4);
787
0
                AV_WB16(dst16 + xp + 1, c->xyz2rgb.gamma.out[g] << 4);
788
0
                AV_WB16(dst16 + xp + 2, c->xyz2rgb.gamma.out[b] << 4);
789
0
            } else {
790
0
                AV_WL16(dst16 + xp + 0, c->xyz2rgb.gamma.out[r] << 4);
791
0
                AV_WL16(dst16 + xp + 1, c->xyz2rgb.gamma.out[g] << 4);
792
0
                AV_WL16(dst16 + xp + 2, c->xyz2rgb.gamma.out[b] << 4);
793
0
            }
794
0
        }
795
796
0
        src += src_stride;
797
0
        dst += dst_stride;
798
0
    }
799
0
}
800
801
static void rgb48Toxyz12_c(const SwsInternal *c, uint8_t *dst, int dst_stride,
802
                           const uint8_t *src, int src_stride, int w, int h)
803
0
{
804
0
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(c->opts.dst_format);
805
806
0
    for (int yp = 0; yp < h; yp++) {
807
0
        uint16_t *src16 = (uint16_t *) src;
808
0
        uint16_t *dst16 = (uint16_t *) dst;
809
810
0
        for (int xp = 0; xp < 3 * w; xp += 3) {
811
0
            int x, y, z, r, g, b;
812
813
0
            if (desc->flags & AV_PIX_FMT_FLAG_BE) {
814
0
                r = AV_RB16(src16 + xp + 0);
815
0
                g = AV_RB16(src16 + xp + 1);
816
0
                b = AV_RB16(src16 + xp + 2);
817
0
            } else {
818
0
                r = AV_RL16(src16 + xp + 0);
819
0
                g = AV_RL16(src16 + xp + 1);
820
0
                b = AV_RL16(src16 + xp + 2);
821
0
            }
822
823
0
            r = c->rgb2xyz.gamma.in[r >> 4];
824
0
            g = c->rgb2xyz.gamma.in[g >> 4];
825
0
            b = c->rgb2xyz.gamma.in[b >> 4];
826
827
            // convert from sRGBlinear to XYZlinear
828
0
            x = c->rgb2xyz.mat[0][0] * r +
829
0
                c->rgb2xyz.mat[0][1] * g +
830
0
                c->rgb2xyz.mat[0][2] * b >> 12;
831
0
            y = c->rgb2xyz.mat[1][0] * r +
832
0
                c->rgb2xyz.mat[1][1] * g +
833
0
                c->rgb2xyz.mat[1][2] * b >> 12;
834
0
            z = c->rgb2xyz.mat[2][0] * r +
835
0
                c->rgb2xyz.mat[2][1] * g +
836
0
                c->rgb2xyz.mat[2][2] * b >> 12;
837
838
            // limit values to 16-bit depth
839
0
            x = av_clip_uint16(x);
840
0
            y = av_clip_uint16(y);
841
0
            z = av_clip_uint16(z);
842
843
            // convert from XYZlinear to X'Y'Z' and scale from 12bit to 16bit
844
0
            if (desc->flags & AV_PIX_FMT_FLAG_BE) {
845
0
                AV_WB16(dst16 + xp + 0, c->rgb2xyz.gamma.out[x] << 4);
846
0
                AV_WB16(dst16 + xp + 1, c->rgb2xyz.gamma.out[y] << 4);
847
0
                AV_WB16(dst16 + xp + 2, c->rgb2xyz.gamma.out[z] << 4);
848
0
            } else {
849
0
                AV_WL16(dst16 + xp + 0, c->rgb2xyz.gamma.out[x] << 4);
850
0
                AV_WL16(dst16 + xp + 1, c->rgb2xyz.gamma.out[y] << 4);
851
0
                AV_WL16(dst16 + xp + 2, c->rgb2xyz.gamma.out[z] << 4);
852
0
            }
853
0
        }
854
855
0
        src += src_stride;
856
0
        dst += dst_stride;
857
0
    }
858
0
}
859
860
av_cold void ff_sws_init_xyzdsp(SwsInternal *c)
861
0
{
862
0
    c->xyz12Torgb48 = xyz12Torgb48_c;
863
0
    c->rgb48Toxyz12 = rgb48Toxyz12_c;
864
865
#if ARCH_AARCH64
866
    ff_sws_init_xyzdsp_aarch64(c);
867
#endif
868
0
}
869
870
void ff_update_palette(SwsInternal *c, const uint32_t *pal)
871
0
{
872
0
    uint32_t *rgb2yuv = c->input_rgb2yuv_table;
873
874
0
    int32_t ry = rgb2yuv[RY_IDX], gy = rgb2yuv[GY_IDX], by = rgb2yuv[BY_IDX];
875
0
    int32_t ru = rgb2yuv[RU_IDX], gu = rgb2yuv[GU_IDX], bu = rgb2yuv[BU_IDX];
876
0
    int32_t rv = rgb2yuv[RV_IDX], gv = rgb2yuv[GV_IDX], bv = rgb2yuv[BV_IDX];
877
878
0
    for (int i = 0; i < 256; i++) {
879
0
        int r, g, b, y, u, v, a = 0xff;
880
0
        if (c->opts.src_format == AV_PIX_FMT_PAL8) {
881
0
            uint32_t p = pal[i];
882
0
            a = (p >> 24) & 0xFF;
883
0
            r = (p >> 16) & 0xFF;
884
0
            g = (p >>  8) & 0xFF;
885
0
            b =  p        & 0xFF;
886
0
        } else if (c->opts.src_format == AV_PIX_FMT_RGB8) {
887
0
            r = ( i >> 5     ) * 36;
888
0
            g = ((i >> 2) & 7) * 36;
889
0
            b = ( i       & 3) * 85;
890
0
        } else if (c->opts.src_format == AV_PIX_FMT_BGR8) {
891
0
            b = ( i >> 6     ) * 85;
892
0
            g = ((i >> 3) & 7) * 36;
893
0
            r = ( i       & 7) * 36;
894
0
        } else if (c->opts.src_format == AV_PIX_FMT_RGB4_BYTE) {
895
0
            r = ( i >> 3     ) * 255;
896
0
            g = ((i >> 1) & 3) * 85;
897
0
            b = ( i       & 1) * 255;
898
0
        } else if (c->opts.src_format == AV_PIX_FMT_GRAY8 || c->opts.src_format == AV_PIX_FMT_GRAY8A) {
899
0
            r = g = b = i;
900
0
        } else {
901
0
            av_assert1(c->opts.src_format == AV_PIX_FMT_BGR4_BYTE);
902
0
            b = ( i >> 3     ) * 255;
903
0
            g = ((i >> 1) & 3) * 85;
904
0
            r = ( i       & 1) * 255;
905
0
        }
906
907
0
        y = av_clip_uint8((ry * r + gy * g + by * b + ( 33 << (RGB2YUV_SHIFT - 1))) >> RGB2YUV_SHIFT);
908
0
        u = av_clip_uint8((ru * r + gu * g + bu * b + (257 << (RGB2YUV_SHIFT - 1))) >> RGB2YUV_SHIFT);
909
0
        v = av_clip_uint8((rv * r + gv * g + bv * b + (257 << (RGB2YUV_SHIFT - 1))) >> RGB2YUV_SHIFT);
910
911
0
        c->pal_yuv[i]= y + (u<<8) + (v<<16) + ((unsigned)a<<24);
912
913
0
        switch (c->opts.dst_format) {
914
0
        case AV_PIX_FMT_BGR32:
915
0
#if !HAVE_BIGENDIAN
916
0
        case AV_PIX_FMT_RGB24:
917
0
#endif
918
0
            c->pal_rgb[i]=  r + (g<<8) + (b<<16) + ((unsigned)a<<24);
919
0
            break;
920
0
        case AV_PIX_FMT_BGR32_1:
921
#if HAVE_BIGENDIAN
922
        case AV_PIX_FMT_BGR24:
923
#endif
924
0
            c->pal_rgb[i]= a + (r<<8) + (g<<16) + ((unsigned)b<<24);
925
0
            break;
926
0
        case AV_PIX_FMT_RGB32_1:
927
#if HAVE_BIGENDIAN
928
        case AV_PIX_FMT_RGB24:
929
#endif
930
0
            c->pal_rgb[i]= a + (b<<8) + (g<<16) + ((unsigned)r<<24);
931
0
            break;
932
0
        case AV_PIX_FMT_GBRP:
933
0
        case AV_PIX_FMT_GBRAP:
934
#if HAVE_BIGENDIAN
935
            c->pal_rgb[i]= a + (r<<8) + (b<<16) + ((unsigned)g<<24);
936
#else
937
0
            c->pal_rgb[i]= g + (b<<8) + (r<<16) + ((unsigned)a<<24);
938
0
#endif
939
0
            break;
940
0
        case AV_PIX_FMT_RGB32:
941
0
#if !HAVE_BIGENDIAN
942
0
        case AV_PIX_FMT_BGR24:
943
0
#endif
944
0
        default:
945
0
            c->pal_rgb[i]=  b + (g<<8) + (r<<16) + ((unsigned)a<<24);
946
0
        }
947
0
    }
948
0
}
949
950
static int scale_internal(SwsContext *sws,
951
                          const uint8_t * const srcSlice[], const int srcStride[],
952
                          int srcSliceY, int srcSliceH,
953
                          uint8_t *const dstSlice[], const int dstStride[],
954
                          int dstSliceY, int dstSliceH);
955
956
static int scale_gamma(SwsInternal *c,
957
                       const uint8_t * const srcSlice[], const int srcStride[],
958
                       int srcSliceY, int srcSliceH,
959
                       uint8_t * const dstSlice[], const int dstStride[],
960
                       int dstSliceY, int dstSliceH)
961
0
{
962
0
    int ret = scale_internal(c->cascaded_context[0],
963
0
                             srcSlice, srcStride, srcSliceY, srcSliceH,
964
0
                             c->cascaded_tmp[0], c->cascaded_tmpStride[0], 0, c->opts.src_h);
965
966
0
    if (ret < 0)
967
0
        return ret;
968
969
0
    if (c->cascaded_context[2])
970
0
        ret = scale_internal(c->cascaded_context[1], (const uint8_t * const *)c->cascaded_tmp[0],
971
0
                             c->cascaded_tmpStride[0], srcSliceY, srcSliceH,
972
0
                             c->cascaded_tmp[1], c->cascaded_tmpStride[1], 0, c->opts.dst_h);
973
0
    else
974
0
        ret = scale_internal(c->cascaded_context[1], (const uint8_t * const *)c->cascaded_tmp[0],
975
0
                             c->cascaded_tmpStride[0], srcSliceY, srcSliceH,
976
0
                             dstSlice, dstStride, dstSliceY, dstSliceH);
977
978
0
    if (ret < 0)
979
0
        return ret;
980
981
0
    if (c->cascaded_context[2]) {
982
0
        const int dstY1 = sws_internal(c->cascaded_context[1])->dstY;
983
0
        ret = scale_internal(c->cascaded_context[2], (const uint8_t * const *)c->cascaded_tmp[1],
984
0
                             c->cascaded_tmpStride[1], dstY1 - ret, dstY1,
985
0
                             dstSlice, dstStride, dstSliceY, dstSliceH);
986
0
    }
987
0
    return ret;
988
0
}
989
990
static int scale_cascaded(SwsInternal *c,
991
                          const uint8_t * const srcSlice[], const int srcStride[],
992
                          int srcSliceY, int srcSliceH,
993
                          uint8_t * const dstSlice[], const int dstStride[],
994
                          int dstSliceY, int dstSliceH)
995
0
{
996
0
    const int dstH0 = c->cascaded_context[0]->dst_h;
997
0
    int ret = scale_internal(c->cascaded_context[0],
998
0
                             srcSlice, srcStride, srcSliceY, srcSliceH,
999
0
                             c->cascaded_tmp[0], c->cascaded_tmpStride[0],
1000
0
                             0, dstH0);
1001
0
    if (ret < 0)
1002
0
        return ret;
1003
0
    ret = scale_internal(c->cascaded_context[1],
1004
0
                         (const uint8_t * const * )c->cascaded_tmp[0], c->cascaded_tmpStride[0],
1005
0
                         0, dstH0, dstSlice, dstStride, dstSliceY, dstSliceH);
1006
0
    return ret;
1007
0
}
1008
1009
static int scale_internal(SwsContext *sws,
1010
                          const uint8_t * const srcSlice[], const int srcStride[],
1011
                          int srcSliceY, int srcSliceH,
1012
                          uint8_t *const dstSlice[], const int dstStride[],
1013
                          int dstSliceY, int dstSliceH)
1014
0
{
1015
0
    SwsInternal *c = sws_internal(sws);
1016
0
    const int scale_dst = dstSliceY > 0 || dstSliceH < sws->dst_h;
1017
0
    const int frame_start = scale_dst || !c->sliceDir;
1018
0
    int i, ret;
1019
0
    const uint8_t *src2[4];
1020
0
    uint8_t *dst2[4];
1021
0
    int macro_height_src = isBayer(sws->src_format) ? 2 : (1 << c->chrSrcVSubSample);
1022
0
    int macro_height_dst = isBayer(sws->dst_format) ? 2 : (1 << c->chrDstVSubSample);
1023
    // copy strides, so they can safely be modified
1024
0
    int srcStride2[4];
1025
0
    int dstStride2[4];
1026
0
    int srcSliceY_internal = srcSliceY;
1027
1028
0
    if (!srcStride || !dstStride || !dstSlice || !srcSlice) {
1029
0
        av_log(c, AV_LOG_ERROR, "One of the input parameters to sws_scale() is NULL, please check the calling code\n");
1030
0
        return AVERROR(EINVAL);
1031
0
    }
1032
1033
0
    if ((srcSliceY  & (macro_height_src - 1)) ||
1034
0
        ((srcSliceH & (macro_height_src - 1)) && srcSliceY + srcSliceH != sws->src_h) ||
1035
0
        srcSliceY + srcSliceH > sws->src_h ||
1036
0
        (isBayer(sws->src_format) && srcSliceH <= 1)) {
1037
0
        av_log(c, AV_LOG_ERROR, "Slice parameters %d, %d are invalid\n", srcSliceY, srcSliceH);
1038
0
        return AVERROR(EINVAL);
1039
0
    }
1040
1041
0
    if ((dstSliceY  & (macro_height_dst - 1)) ||
1042
0
        ((dstSliceH & (macro_height_dst - 1)) && dstSliceY + dstSliceH != sws->dst_h) ||
1043
0
        dstSliceY + dstSliceH > sws->dst_h) {
1044
0
        av_log(c, AV_LOG_ERROR, "Slice parameters %d, %d are invalid\n", dstSliceY, dstSliceH);
1045
0
        return AVERROR(EINVAL);
1046
0
    }
1047
1048
0
    if (!check_image_pointers(srcSlice, sws->src_format, srcStride)) {
1049
0
        av_log(c, AV_LOG_ERROR, "bad src image pointers\n");
1050
0
        return AVERROR(EINVAL);
1051
0
    }
1052
0
    if (!check_image_pointers((const uint8_t* const*)dstSlice, sws->dst_format, dstStride)) {
1053
0
        av_log(c, AV_LOG_ERROR, "bad dst image pointers\n");
1054
0
        return AVERROR(EINVAL);
1055
0
    }
1056
1057
    // do not mess up sliceDir if we have a "trailing" 0-size slice
1058
0
    if (srcSliceH == 0)
1059
0
        return 0;
1060
1061
0
    if (sws->gamma_flag && c->cascaded_context[0])
1062
0
        return scale_gamma(c, srcSlice, srcStride, srcSliceY, srcSliceH,
1063
0
                           dstSlice, dstStride, dstSliceY, dstSliceH);
1064
1065
0
    if (c->cascaded_context[0] && srcSliceY == 0 && srcSliceH == c->cascaded_context[0]->src_h)
1066
0
        return scale_cascaded(c, srcSlice, srcStride, srcSliceY, srcSliceH,
1067
0
                              dstSlice, dstStride, dstSliceY, dstSliceH);
1068
1069
0
    if (!srcSliceY && (sws->flags & SWS_BITEXACT) && sws->dither == SWS_DITHER_ED && c->dither_error[0])
1070
0
        for (i = 0; i < 4; i++)
1071
0
            memset(c->dither_error[i], 0, sizeof(c->dither_error[0][0]) * (sws->dst_w+2));
1072
1073
0
    if (usePal(sws->src_format))
1074
0
        ff_update_palette(c, (const uint32_t *)srcSlice[1]);
1075
1076
0
    memcpy(src2,       srcSlice,  sizeof(src2));
1077
0
    memcpy(dst2,       dstSlice,  sizeof(dst2));
1078
0
    memcpy(srcStride2, srcStride, sizeof(srcStride2));
1079
0
    memcpy(dstStride2, dstStride, sizeof(dstStride2));
1080
1081
0
    if (frame_start && !scale_dst) {
1082
0
        if (srcSliceY != 0 && srcSliceY + srcSliceH != sws->src_h) {
1083
0
            av_log(c, AV_LOG_ERROR, "Slices start in the middle!\n");
1084
0
            return AVERROR(EINVAL);
1085
0
        }
1086
1087
0
        c->sliceDir = (srcSliceY == 0) ? 1 : -1;
1088
0
    } else if (scale_dst)
1089
0
        c->sliceDir = 1;
1090
1091
0
    if (c->src0Alpha && !c->dst0Alpha && isALPHA(sws->dst_format)) {
1092
0
        uint8_t *base;
1093
0
        int x,y;
1094
1095
0
        av_fast_malloc(&c->rgb0_scratch, &c->rgb0_scratch_allocated,
1096
0
                       FFABS(srcStride[0]) * srcSliceH + 32);
1097
0
        if (!c->rgb0_scratch)
1098
0
            return AVERROR(ENOMEM);
1099
1100
0
        base = srcStride[0] < 0 ? c->rgb0_scratch - srcStride[0] * (srcSliceH-1) :
1101
0
                                  c->rgb0_scratch;
1102
0
        for (y=0; y<srcSliceH; y++){
1103
0
            memcpy(base + srcStride[0]*y, src2[0] + srcStride[0]*y, 4*sws->src_w);
1104
0
            for (x=c->src0Alpha-1; x<4*sws->src_w; x+=4) {
1105
0
                base[ srcStride[0]*y + x] = 0xFF;
1106
0
            }
1107
0
        }
1108
0
        src2[0] = base;
1109
0
    }
1110
1111
0
    if (c->srcXYZ && !(c->dstXYZ && sws->src_w==sws->dst_w && sws->src_h==sws->dst_h)) {
1112
0
        uint8_t *base;
1113
1114
0
        av_fast_malloc(&c->xyz_scratch, &c->xyz_scratch_allocated,
1115
0
                       FFABS(srcStride[0]) * srcSliceH + 32);
1116
0
        if (!c->xyz_scratch)
1117
0
            return AVERROR(ENOMEM);
1118
1119
0
        base = srcStride[0] < 0 ? c->xyz_scratch - srcStride[0] * (srcSliceH-1) :
1120
0
                                  c->xyz_scratch;
1121
1122
0
        c->xyz12Torgb48(c, base, srcStride[0], src2[0], srcStride[0], sws->src_w, srcSliceH);
1123
0
        src2[0] = base;
1124
0
    }
1125
1126
0
    if (c->sliceDir != 1) {
1127
        // slices go from bottom to top => we flip the image internally
1128
0
        for (i=0; i<4; i++) {
1129
0
            srcStride2[i] *= -1;
1130
0
            dstStride2[i] *= -1;
1131
0
        }
1132
1133
0
        src2[0] += (srcSliceH - 1) * srcStride[0];
1134
0
        if (!usePal(sws->src_format))
1135
0
            src2[1] += ((srcSliceH >> c->chrSrcVSubSample) - 1) * srcStride[1];
1136
0
        src2[2] += ((srcSliceH >> c->chrSrcVSubSample) - 1) * srcStride[2];
1137
0
        src2[3] += (srcSliceH - 1) * srcStride[3];
1138
0
        dst2[0] += ( sws->dst_h                         - 1) * dstStride[0];
1139
0
        dst2[1] += ((sws->dst_h >> c->chrDstVSubSample) - 1) * dstStride[1];
1140
0
        dst2[2] += ((sws->dst_h >> c->chrDstVSubSample) - 1) * dstStride[2];
1141
0
        dst2[3] += ( sws->dst_h                         - 1) * dstStride[3];
1142
1143
0
        srcSliceY_internal = sws->src_h-srcSliceY-srcSliceH;
1144
0
    }
1145
0
    reset_ptr(src2, sws->src_format);
1146
0
    reset_ptr((void*)dst2, sws->dst_format);
1147
1148
0
    if (c->convert_unscaled) {
1149
0
        int offset  = srcSliceY_internal;
1150
0
        int slice_h = srcSliceH;
1151
1152
        // for dst slice scaling, offset the pointers to match the unscaled API
1153
0
        if (scale_dst) {
1154
0
            av_assert0(offset == 0);
1155
0
            for (i = 0; i < 4 && src2[i]; i++) {
1156
0
                if (!src2[i] || (i > 0 && usePal(sws->src_format)))
1157
0
                    break;
1158
0
                src2[i] += (dstSliceY >> ((i == 1 || i == 2) ? c->chrSrcVSubSample : 0)) * srcStride2[i];
1159
0
            }
1160
1161
0
            for (i = 0; i < 4 && dst2[i]; i++) {
1162
0
                if (!dst2[i] || (i > 0 && usePal(sws->dst_format)))
1163
0
                    break;
1164
0
                dst2[i] -= (dstSliceY >> ((i == 1 || i == 2) ? c->chrDstVSubSample : 0)) * dstStride2[i];
1165
0
            }
1166
0
            offset  = dstSliceY;
1167
0
            slice_h = dstSliceH;
1168
0
        }
1169
1170
0
        ret = c->convert_unscaled(c, src2, srcStride2, offset, slice_h,
1171
0
                                  dst2, dstStride2);
1172
0
        if (scale_dst)
1173
0
            dst2[0] += dstSliceY * dstStride2[0];
1174
0
    } else {
1175
0
        ret = ff_swscale(c, src2, srcStride2, srcSliceY_internal, srcSliceH,
1176
0
                         dst2, dstStride2, dstSliceY, dstSliceH);
1177
0
    }
1178
1179
0
    if (c->dstXYZ && !(c->srcXYZ && sws->src_w==sws->dst_w && sws->src_h==sws->dst_h)) {
1180
0
        uint8_t *dst;
1181
1182
0
        if (scale_dst) {
1183
0
            dst = dst2[0];
1184
0
        } else {
1185
0
            int dstY = c->dstY ? c->dstY : srcSliceY + srcSliceH;
1186
1187
0
            av_assert0(dstY >= ret);
1188
0
            av_assert0(ret >= 0);
1189
0
            av_assert0(sws->dst_h >= dstY);
1190
0
            dst = dst2[0] + (dstY - ret) * dstStride2[0];
1191
0
        }
1192
1193
        /* replace on the same data */
1194
0
        c->rgb48Toxyz12(c, dst, dstStride2[0], dst, dstStride2[0], sws->dst_w, ret);
1195
0
    }
1196
1197
    /* reset slice direction at end of frame */
1198
0
    if ((srcSliceY_internal + srcSliceH == sws->src_h) || scale_dst)
1199
0
        c->sliceDir = 0;
1200
1201
0
    return ret;
1202
0
}
1203
1204
void sws_frame_end(SwsContext *sws)
1205
0
{
1206
0
    SwsInternal *c = sws_internal(sws);
1207
0
    av_frame_unref(c->frame_src);
1208
0
    av_frame_unref(c->frame_dst);
1209
0
    c->src_ranges.nb_ranges = 0;
1210
0
}
1211
1212
int sws_frame_start(SwsContext *sws, AVFrame *dst, const AVFrame *src)
1213
0
{
1214
0
    SwsInternal *c = sws_internal(sws);
1215
0
    int ret, allocated = 0;
1216
1217
0
    ret = av_frame_ref(c->frame_src, src);
1218
0
    if (ret < 0)
1219
0
        return ret;
1220
1221
0
    if (!dst->buf[0]) {
1222
0
        dst->width  = sws->dst_w;
1223
0
        dst->height = sws->dst_h;
1224
0
        dst->format = sws->dst_format;
1225
1226
0
        ret = av_frame_get_buffer(dst, 0);
1227
0
        if (ret < 0)
1228
0
            return ret;
1229
0
        allocated = 1;
1230
0
    }
1231
1232
0
    ret = av_frame_ref(c->frame_dst, dst);
1233
0
    if (ret < 0) {
1234
0
        if (allocated)
1235
0
            av_frame_unref(dst);
1236
1237
0
        return ret;
1238
0
    }
1239
1240
0
    return 0;
1241
0
}
1242
1243
int sws_send_slice(SwsContext *sws, unsigned int slice_start,
1244
                   unsigned int slice_height)
1245
0
{
1246
0
    SwsInternal *c = sws_internal(sws);
1247
0
    int ret;
1248
1249
0
    ret = ff_range_add(&c->src_ranges, slice_start, slice_height);
1250
0
    if (ret < 0)
1251
0
        return ret;
1252
1253
0
    return 0;
1254
0
}
1255
1256
unsigned int sws_receive_slice_alignment(const SwsContext *sws)
1257
0
{
1258
0
    SwsInternal *c = sws_internal(sws);
1259
0
    if (c->slice_ctx)
1260
0
        return sws_internal(c->slice_ctx[0])->dst_slice_align;
1261
1262
0
    return c->dst_slice_align;
1263
0
}
1264
1265
int sws_receive_slice(SwsContext *sws, unsigned int slice_start,
1266
                      unsigned int slice_height)
1267
0
{
1268
0
    SwsInternal *c = sws_internal(sws);
1269
0
    unsigned int align = sws_receive_slice_alignment(sws);
1270
0
    uint8_t *dst[4];
1271
1272
    /* wait until complete input has been received */
1273
0
    if (!(c->src_ranges.nb_ranges == 1        &&
1274
0
          c->src_ranges.ranges[0].start == 0 &&
1275
0
          c->src_ranges.ranges[0].len == sws->src_h))
1276
0
        return AVERROR(EAGAIN);
1277
1278
0
    if ((slice_start > 0 || slice_height < sws->dst_h) &&
1279
0
        (slice_start % align || slice_height % align)) {
1280
0
        av_log(c, AV_LOG_ERROR,
1281
0
               "Incorrectly aligned output: %u/%u not multiples of %u\n",
1282
0
               slice_start, slice_height, align);
1283
0
        return AVERROR(EINVAL);
1284
0
    }
1285
1286
0
    if (c->slicethread) {
1287
0
        int nb_jobs = c->nb_slice_ctx;
1288
0
        int ret = 0;
1289
1290
0
        if (c->slice_ctx[0]->dither == SWS_DITHER_ED)
1291
0
            nb_jobs = 1;
1292
1293
0
        c->dst_slice_start  = slice_start;
1294
0
        c->dst_slice_height = slice_height;
1295
1296
0
        avpriv_slicethread_execute(c->slicethread, nb_jobs, 0);
1297
1298
0
        for (int i = 0; i < c->nb_slice_ctx; i++) {
1299
0
            if (c->slice_err[i] < 0) {
1300
0
                ret = c->slice_err[i];
1301
0
                break;
1302
0
            }
1303
0
        }
1304
1305
0
        memset(c->slice_err, 0, c->nb_slice_ctx * sizeof(*c->slice_err));
1306
1307
0
        return ret;
1308
0
    }
1309
1310
0
    for (int i = 0; i < FF_ARRAY_ELEMS(dst); i++) {
1311
0
        ptrdiff_t offset = c->frame_dst->linesize[i] * (ptrdiff_t)(slice_start >> c->chrDstVSubSample);
1312
0
        dst[i] = FF_PTR_ADD(c->frame_dst->data[i], offset);
1313
0
    }
1314
1315
0
    return scale_internal(sws, (const uint8_t * const *)c->frame_src->data,
1316
0
                          c->frame_src->linesize, 0, sws->src_h,
1317
0
                          dst, c->frame_dst->linesize, slice_start, slice_height);
1318
0
}
1319
1320
static void get_frame_pointers(const AVFrame *frame, uint8_t *data[4],
1321
                               int linesize[4], int field)
1322
0
{
1323
0
    for (int i = 0; i < 4; i++) {
1324
0
        data[i]     = frame->data[i];
1325
0
        linesize[i] = frame->linesize[i];
1326
0
    }
1327
1328
0
    if (!(frame->flags & AV_FRAME_FLAG_INTERLACED)) {
1329
0
        av_assert1(!field);
1330
0
        return;
1331
0
    }
1332
1333
0
    if (field == FIELD_BOTTOM) {
1334
        /* Odd rows, offset by one line */
1335
0
        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
1336
0
        for (int i = 0; i < 4; i++) {
1337
0
            data[i] += linesize[i];
1338
0
            if (desc->flags & AV_PIX_FMT_FLAG_PAL)
1339
0
                break;
1340
0
        }
1341
0
    }
1342
1343
    /* Take only every second line */
1344
0
    for (int i = 0; i < 4; i++)
1345
0
        linesize[i] <<= 1;
1346
0
}
1347
1348
/* Subset of av_frame_ref() that only references (video) data buffers */
1349
static int frame_ref(AVFrame *dst, const AVFrame *src)
1350
0
{
1351
    /* ref the buffers */
1352
0
    for (int i = 0; i < FF_ARRAY_ELEMS(src->buf); i++) {
1353
0
        if (!src->buf[i])
1354
0
            continue;
1355
0
        dst->buf[i] = av_buffer_ref(src->buf[i]);
1356
0
        if (!dst->buf[i])
1357
0
            return AVERROR(ENOMEM);
1358
0
    }
1359
1360
0
    memcpy(dst->data,     src->data,     sizeof(src->data));
1361
0
    memcpy(dst->linesize, src->linesize, sizeof(src->linesize));
1362
0
    return 0;
1363
0
}
1364
1365
int sws_scale_frame(SwsContext *sws, AVFrame *dst, const AVFrame *src)
1366
0
{
1367
0
    int ret;
1368
0
    SwsInternal *c = sws_internal(sws);
1369
0
    if (!src || !dst)
1370
0
        return AVERROR(EINVAL);
1371
1372
0
    if (c->frame_src) {
1373
        /* Context has been initialized with explicit values, fall back to
1374
         * legacy API */
1375
0
        ret = sws_frame_start(sws, dst, src);
1376
0
        if (ret < 0)
1377
0
            return ret;
1378
1379
0
        ret = sws_send_slice(sws, 0, src->height);
1380
0
        if (ret >= 0)
1381
0
            ret = sws_receive_slice(sws, 0, dst->height);
1382
1383
0
        sws_frame_end(sws);
1384
1385
0
        return ret;
1386
0
    }
1387
1388
0
    ret = sws_frame_setup(sws, dst, src);
1389
0
    if (ret < 0)
1390
0
        return ret;
1391
1392
0
    if (!src->data[0])
1393
0
        return 0;
1394
1395
0
    if (c->graph[FIELD_TOP]->noop &&
1396
0
        (!c->graph[FIELD_BOTTOM] || c->graph[FIELD_BOTTOM]->noop) &&
1397
0
        src->buf[0] && !dst->buf[0] && !dst->data[0])
1398
0
    {
1399
        /* Lightweight refcopy */
1400
0
        ret = frame_ref(dst, src);
1401
0
        if (ret < 0)
1402
0
            return ret;
1403
0
    } else {
1404
0
        if (!dst->data[0]) {
1405
0
            ret = av_frame_get_buffer(dst, 0);
1406
0
            if (ret < 0)
1407
0
                return ret;
1408
0
        }
1409
1410
0
        for (int field = 0; field < 2; field++) {
1411
0
            SwsGraph *graph = c->graph[field];
1412
0
            uint8_t *dst_data[4], *src_data[4];
1413
0
            int dst_linesize[4], src_linesize[4];
1414
0
            get_frame_pointers(dst, dst_data, dst_linesize, field);
1415
0
            get_frame_pointers(src, src_data, src_linesize, field);
1416
0
            ff_sws_graph_run(graph, dst_data, dst_linesize,
1417
0
                          (const uint8_t **) src_data, src_linesize);
1418
0
            if (!graph->dst.interlaced)
1419
0
                break;
1420
0
        }
1421
0
    }
1422
1423
0
    return 0;
1424
0
}
1425
1426
static int validate_params(SwsContext *ctx)
1427
0
{
1428
0
#define VALIDATE(field, min, max) \
1429
0
    if (ctx->field < min || ctx->field > max) { \
1430
0
        av_log(ctx, AV_LOG_ERROR, "'%s' (%d) out of range [%d, %d]\n", \
1431
0
               #field, (int) ctx->field, min, max); \
1432
0
        return AVERROR(EINVAL); \
1433
0
    }
1434
1435
0
    VALIDATE(threads,       0, SWS_MAX_THREADS);
1436
0
    VALIDATE(dither,        0, SWS_DITHER_NB - 1)
1437
0
    VALIDATE(alpha_blend,   0, SWS_ALPHA_BLEND_NB - 1)
1438
0
    return 0;
1439
0
}
1440
1441
int sws_frame_setup(SwsContext *ctx, const AVFrame *dst, const AVFrame *src)
1442
0
{
1443
0
    SwsInternal *s = sws_internal(ctx);
1444
0
    const char *err_msg;
1445
0
    int ret;
1446
1447
0
    if (!src || !dst)
1448
0
        return AVERROR(EINVAL);
1449
0
    if ((ret = validate_params(ctx)) < 0)
1450
0
        return ret;
1451
1452
0
    for (int field = 0; field < 2; field++) {
1453
0
        SwsFormat src_fmt = ff_fmt_from_frame(src, field);
1454
0
        SwsFormat dst_fmt = ff_fmt_from_frame(dst, field);
1455
0
        int src_ok, dst_ok;
1456
1457
0
        if ((src->flags ^ dst->flags) & AV_FRAME_FLAG_INTERLACED) {
1458
0
            err_msg = "Cannot convert interlaced to progressive frames or vice versa.\n";
1459
0
            ret = AVERROR(EINVAL);
1460
0
            goto fail;
1461
0
        }
1462
1463
0
        src_ok = ff_test_fmt(&src_fmt, 0);
1464
0
        dst_ok = ff_test_fmt(&dst_fmt, 1);
1465
0
        if ((!src_ok || !dst_ok) && !ff_props_equal(&src_fmt, &dst_fmt)) {
1466
0
            err_msg = src_ok ? "Unsupported output" : "Unsupported input";
1467
0
            ret = AVERROR(ENOTSUP);
1468
0
            goto fail;
1469
0
        }
1470
1471
0
        ret = ff_sws_graph_reinit(ctx, &dst_fmt, &src_fmt, field, &s->graph[field]);
1472
0
        if (ret < 0) {
1473
0
            err_msg = "Failed initializing scaling graph";
1474
0
            goto fail;
1475
0
        }
1476
1477
0
        if (s->graph[field]->incomplete && ctx->flags & SWS_STRICT) {
1478
0
            err_msg = "Incomplete scaling graph";
1479
0
            ret = AVERROR(EINVAL);
1480
0
            goto fail;
1481
0
        }
1482
1483
0
        if (!src_fmt.interlaced) {
1484
0
            ff_sws_graph_free(&s->graph[FIELD_BOTTOM]);
1485
0
            break;
1486
0
        }
1487
1488
0
        continue;
1489
1490
0
    fail:
1491
0
        av_log(ctx, AV_LOG_ERROR, "%s (%s): fmt:%s csp:%s prim:%s trc:%s ->"
1492
0
                                          " fmt:%s csp:%s prim:%s trc:%s\n",
1493
0
               err_msg, av_err2str(ret),
1494
0
               av_get_pix_fmt_name(src_fmt.format), av_color_space_name(src_fmt.csp),
1495
0
               av_color_primaries_name(src_fmt.color.prim), av_color_transfer_name(src_fmt.color.trc),
1496
0
               av_get_pix_fmt_name(dst_fmt.format), av_color_space_name(dst_fmt.csp),
1497
0
               av_color_primaries_name(dst_fmt.color.prim), av_color_transfer_name(dst_fmt.color.trc));
1498
1499
0
        for (int i = 0; i < FF_ARRAY_ELEMS(s->graph); i++)
1500
0
            ff_sws_graph_free(&s->graph[i]);
1501
1502
0
        return ret;
1503
0
    }
1504
1505
0
    return 0;
1506
0
}
1507
1508
/**
1509
 * swscale wrapper, so we don't need to export the SwsContext.
1510
 * Assumes planar YUV to be in YUV order instead of YVU.
1511
 */
1512
int attribute_align_arg sws_scale(SwsContext *sws,
1513
                                  const uint8_t * const srcSlice[],
1514
                                  const int srcStride[], int srcSliceY,
1515
                                  int srcSliceH, uint8_t *const dst[],
1516
                                  const int dstStride[])
1517
0
{
1518
0
    SwsInternal *c = sws_internal(sws);
1519
0
    if (c->nb_slice_ctx) {
1520
0
        sws = c->slice_ctx[0];
1521
0
        c = sws_internal(sws);
1522
0
    }
1523
1524
0
    return scale_internal(sws, srcSlice, srcStride, srcSliceY, srcSliceH,
1525
0
                          dst, dstStride, 0, sws->dst_h);
1526
0
}
1527
1528
void ff_sws_slice_worker(void *priv, int jobnr, int threadnr,
1529
                         int nb_jobs, int nb_threads)
1530
0
{
1531
0
    SwsInternal *parent = priv;
1532
0
    SwsContext     *sws = parent->slice_ctx[threadnr];
1533
0
    SwsInternal      *c = sws_internal(sws);
1534
1535
0
    const int slice_height = FFALIGN(FFMAX((parent->dst_slice_height + nb_jobs - 1) / nb_jobs, 1),
1536
0
                                     c->dst_slice_align);
1537
0
    const int slice_start  = jobnr * slice_height;
1538
0
    const int slice_end    = FFMIN((jobnr + 1) * slice_height, parent->dst_slice_height);
1539
0
    int err = 0;
1540
1541
0
    if (slice_end > slice_start) {
1542
0
        uint8_t *dst[4] = { NULL };
1543
1544
0
        for (int i = 0; i < FF_ARRAY_ELEMS(dst) && parent->frame_dst->data[i]; i++) {
1545
0
            const int vshift = (i == 1 || i == 2) ? c->chrDstVSubSample : 0;
1546
0
            const ptrdiff_t offset = parent->frame_dst->linesize[i] *
1547
0
                (ptrdiff_t)((slice_start + parent->dst_slice_start) >> vshift);
1548
1549
0
            dst[i] = parent->frame_dst->data[i] + offset;
1550
0
        }
1551
1552
0
        err = scale_internal(sws, (const uint8_t * const *)parent->frame_src->data,
1553
0
                             parent->frame_src->linesize, 0, sws->src_h,
1554
0
                             dst, parent->frame_dst->linesize,
1555
0
                             parent->dst_slice_start + slice_start, slice_end - slice_start);
1556
0
    }
1557
1558
0
    parent->slice_err[threadnr] = err;
1559
0
}