imgutils.c 21.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
 * @file
 * misc image utilities
 */

24
#include "avassert.h"
25
#include "common.h"
26
#include "imgutils.h"
27
#include "imgutils_internal.h"
28
#include "internal.h"
29
#include "intreadwrite.h"
30
#include "log.h"
31
#include "mathematics.h"
32
#include "pixdesc.h"
33
#include "rational.h"
34

35
void av_image_fill_max_pixsteps(int max_pixsteps[4], int max_pixstep_comps[4],
36 37 38 39 40 41 42 43 44
                                const AVPixFmtDescriptor *pixdesc)
{
    int i;
    memset(max_pixsteps, 0, 4*sizeof(max_pixsteps[0]));
    if (max_pixstep_comps)
        memset(max_pixstep_comps, 0, 4*sizeof(max_pixstep_comps[0]));

    for (i = 0; i < 4; i++) {
        const AVComponentDescriptor *comp = &(pixdesc->comp[i]);
45 46
        if (comp->step > max_pixsteps[comp->plane]) {
            max_pixsteps[comp->plane] = comp->step;
47 48 49 50 51 52
            if (max_pixstep_comps)
                max_pixstep_comps[comp->plane] = i;
        }
    }
}

53 54 55 56 57 58 59
static inline
int image_get_linesize(int width, int plane,
                       int max_step, int max_step_comp,
                       const AVPixFmtDescriptor *desc)
{
    int s, shifted_w, linesize;

60 61 62
    if (!desc)
        return AVERROR(EINVAL);

63 64 65 66 67 68 69
    if (width < 0)
        return AVERROR(EINVAL);
    s = (max_step_comp == 1 || max_step_comp == 2) ? desc->log2_chroma_w : 0;
    shifted_w = ((width + (1 << s) - 1)) >> s;
    if (shifted_w && max_step > INT_MAX / shifted_w)
        return AVERROR(EINVAL);
    linesize = max_step * shifted_w;
70

71
    if (desc->flags & AV_PIX_FMT_FLAG_BITSTREAM)
72 73 74 75
        linesize = (linesize + 7) >> 3;
    return linesize;
}

76
int av_image_get_linesize(enum AVPixelFormat pix_fmt, int width, int plane)
77
{
78
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
79 80 81
    int max_step     [4];       /* max pixel step for each plane */
    int max_step_comp[4];       /* the component for each plane which has the max pixel step */

82
    if (!desc || desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
83 84
        return AVERROR(EINVAL);

85
    av_image_fill_max_pixsteps(max_step, max_step_comp, desc);
86
    return image_get_linesize(width, plane, max_step[plane], max_step_comp[plane], desc);
87 88
}

89
int av_image_fill_linesizes(int linesizes[4], enum AVPixelFormat pix_fmt, int width)
90
{
91
    int i, ret;
92
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
93 94
    int max_step     [4];       /* max pixel step for each plane */
    int max_step_comp[4];       /* the component for each plane which has the max pixel step */
95

96
    memset(linesizes, 0, 4*sizeof(linesizes[0]));
97

98
    if (!desc || desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
99
        return AVERROR(EINVAL);
100

101
    av_image_fill_max_pixsteps(max_step, max_step_comp, desc);
102
    for (i = 0; i < 4; i++) {
103 104 105
        if ((ret = image_get_linesize(width, i, max_step[i], max_step_comp[i], desc)) < 0)
            return ret;
        linesizes[i] = ret;
106 107 108 109 110
    }

    return 0;
}

111
int av_image_fill_pointers(uint8_t *data[4], enum AVPixelFormat pix_fmt, int height,
112
                           uint8_t *ptr, const int linesizes[4])
113
{
114
    int i, total_size, size[4] = { 0 }, has_plane[4] = { 0 };
115

116
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
117 118
    memset(data     , 0, sizeof(data[0])*4);

119
    if (!desc || desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
120 121 122
        return AVERROR(EINVAL);

    data[0] = ptr;
123 124
    if (linesizes[0] > (INT_MAX - 1024) / height)
        return AVERROR(EINVAL);
125
    size[0] = linesizes[0] * height;
126

127
    if (desc->flags & AV_PIX_FMT_FLAG_PAL ||
128
        desc->flags & FF_PSEUDOPAL) {
129 130
        data[1] = ptr + size[0]; /* palette is stored here as 256 32 bits words */
        return size[0] + 256 * 4;
131
    }
132 133 134 135 136

    for (i = 0; i < 4; i++)
        has_plane[desc->comp[i].plane] = 1;

    total_size = size[0];
137
    for (i = 1; i < 4 && has_plane[i]; i++) {
138 139 140
        int h, s = (i == 1 || i == 2) ? desc->log2_chroma_h : 0;
        data[i] = data[i-1] + size[i-1];
        h = (height + (1 << s) - 1) >> s;
141 142
        if (linesizes[i] > INT_MAX / h)
            return AVERROR(EINVAL);
143
        size[i] = h * linesizes[i];
144 145
        if (total_size > INT_MAX - size[i])
            return AVERROR(EINVAL);
146 147 148 149
        total_size += size[i];
    }

    return total_size;
150
}
151

152
int avpriv_set_systematic_pal2(uint32_t pal[256], enum AVPixelFormat pix_fmt)
153 154 155 156 157 158 159
{
    int i;

    for (i = 0; i < 256; i++) {
        int r, g, b;

        switch (pix_fmt) {
160
        case AV_PIX_FMT_RGB8:
161 162 163 164
            r = (i>>5    )*36;
            g = ((i>>2)&7)*36;
            b = (i&3     )*85;
            break;
165
        case AV_PIX_FMT_BGR8:
166 167 168 169
            b = (i>>6    )*85;
            g = ((i>>3)&7)*36;
            r = (i&7     )*36;
            break;
170
        case AV_PIX_FMT_RGB4_BYTE:
171 172 173 174
            r = (i>>3    )*255;
            g = ((i>>1)&3)*85;
            b = (i&1     )*255;
            break;
175
        case AV_PIX_FMT_BGR4_BYTE:
176 177 178 179
            b = (i>>3    )*255;
            g = ((i>>1)&3)*85;
            r = (i&1     )*255;
            break;
180
        case AV_PIX_FMT_GRAY8:
181 182 183 184 185
            r = b = g = i;
            break;
        default:
            return AVERROR(EINVAL);
        }
186
        pal[i] = b + (g << 8) + (r << 16) + (0xFFU << 24);
187 188 189 190 191
    }

    return 0;
}

192
int av_image_alloc(uint8_t *pointers[4], int linesizes[4],
193
                   int w, int h, enum AVPixelFormat pix_fmt, int align)
194
{
195
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
196 197 198
    int i, ret;
    uint8_t *buf;

199 200 201
    if (!desc)
        return AVERROR(EINVAL);

202 203
    if ((ret = av_image_check_size(w, h, 0, NULL)) < 0)
        return ret;
204
    if ((ret = av_image_fill_linesizes(linesizes, pix_fmt, align>7 ? FFALIGN(w, 8) : w)) < 0)
205 206 207 208 209 210 211 212 213 214 215 216 217 218
        return ret;

    for (i = 0; i < 4; i++)
        linesizes[i] = FFALIGN(linesizes[i], align);

    if ((ret = av_image_fill_pointers(pointers, pix_fmt, h, NULL, linesizes)) < 0)
        return ret;
    buf = av_malloc(ret + align);
    if (!buf)
        return AVERROR(ENOMEM);
    if ((ret = av_image_fill_pointers(pointers, pix_fmt, h, buf, linesizes)) < 0) {
        av_free(buf);
        return ret;
    }
219
    if (desc->flags & AV_PIX_FMT_FLAG_PAL || (desc->flags & FF_PSEUDOPAL && pointers[1])) {
220
        avpriv_set_systematic_pal2((uint32_t*)pointers[1], pix_fmt);
221 222 223 224 225
        if (align < 4) {
            av_log(NULL, AV_LOG_ERROR, "Formats with a palette require a minimum alignment of 4\n");
            return AVERROR(EINVAL);
        }
    }
226

227
    if ((desc->flags & AV_PIX_FMT_FLAG_PAL ||
228
         desc->flags & FF_PSEUDOPAL) && pointers[1] &&
229 230 231 232 233 234
        pointers[1] - pointers[0] > linesizes[0] * h) {
        /* zero-initialize the padding before the palette */
        memset(pointers[0] + linesizes[0] * h, 0,
               pointers[1] - pointers[0] - linesizes[0] * h);
    }

235 236 237
    return ret;
}

238 239 240 241 242 243
typedef struct ImgUtils {
    const AVClass *class;
    int   log_offset;
    void *log_ctx;
} ImgUtils;

244
static const AVClass imgutils_class = {
245 246 247 248
    .class_name                = "IMGUTILS",
    .item_name                 = av_default_item_name,
    .option                    = NULL,
    .version                   = LIBAVUTIL_VERSION_INT,
249 250 251
    .log_level_offset_offset   = offsetof(ImgUtils, log_offset),
    .parent_log_context_offset = offsetof(ImgUtils, log_ctx),
};
252

253
int av_image_check_size2(unsigned int w, unsigned int h, int64_t max_pixels, enum AVPixelFormat pix_fmt, int log_offset, void *log_ctx)
254
{
255 256 257 258 259
    ImgUtils imgutils = {
        .class      = &imgutils_class,
        .log_offset = log_offset,
        .log_ctx    = log_ctx,
    };
260 261 262 263
    int64_t stride = av_image_get_linesize(pix_fmt, w, 0);
    if (stride <= 0)
        stride = 8LL*w;
    stride += 128*8;
264

265 266 267 268
    if ((int)w<=0 || (int)h<=0 || stride >= INT_MAX || stride*(uint64_t)(h+128) >= INT_MAX) {
        av_log(&imgutils, AV_LOG_ERROR, "Picture size %ux%u is invalid\n", w, h);
        return AVERROR(EINVAL);
    }
269

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
    if (max_pixels < INT64_MAX) {
        if (w*(int64_t)h > max_pixels) {
            av_log(&imgutils, AV_LOG_ERROR,
                    "Picture size %ux%u exceeds specified max pixel count %"PRId64", see the documentation if you wish to increase it\n",
                    w, h, max_pixels);
            return AVERROR(EINVAL);
        }
    }

    return 0;
}

int av_image_check_size(unsigned int w, unsigned int h, int log_offset, void *log_ctx)
{
    return av_image_check_size2(w, h, INT64_MAX, AV_PIX_FMT_NONE, log_offset, log_ctx);
285
}
286

287 288 289 290
int av_image_check_sar(unsigned int w, unsigned int h, AVRational sar)
{
    int64_t scaled_dim;

291
    if (sar.den <= 0 || sar.num < 0)
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
        return AVERROR(EINVAL);

    if (!sar.num || sar.num == sar.den)
        return 0;

    if (sar.num < sar.den)
        scaled_dim = av_rescale_rnd(w, sar.num, sar.den, AV_ROUND_ZERO);
    else
        scaled_dim = av_rescale_rnd(h, sar.den, sar.num, AV_ROUND_ZERO);

    if (scaled_dim > 0)
        return 0;

    return AVERROR(EINVAL);
}

308 309 310
static void image_copy_plane(uint8_t       *dst, ptrdiff_t dst_linesize,
                             const uint8_t *src, ptrdiff_t src_linesize,
                             ptrdiff_t bytewidth, int height)
311 312 313
{
    if (!dst || !src)
        return;
314 315
    av_assert0(abs(src_linesize) >= bytewidth);
    av_assert0(abs(dst_linesize) >= bytewidth);
316 317 318 319 320 321 322
    for (;height > 0; height--) {
        memcpy(dst, src, bytewidth);
        dst += dst_linesize;
        src += src_linesize;
    }
}

323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
static void image_copy_plane_uc_from(uint8_t       *dst, ptrdiff_t dst_linesize,
                                     const uint8_t *src, ptrdiff_t src_linesize,
                                     ptrdiff_t bytewidth, int height)
{
    int ret = -1;

#if ARCH_X86
    ret = ff_image_copy_plane_uc_from_x86(dst, dst_linesize, src, src_linesize,
                                          bytewidth, height);
#endif

    if (ret < 0)
        image_copy_plane(dst, dst_linesize, src, src_linesize, bytewidth, height);
}

void av_image_copy_plane(uint8_t       *dst, int dst_linesize,
                         const uint8_t *src, int src_linesize,
                         int bytewidth, int height)
{
    image_copy_plane(dst, dst_linesize, src, src_linesize, bytewidth, height);
}

static void image_copy(uint8_t *dst_data[4], const ptrdiff_t dst_linesizes[4],
                       const uint8_t *src_data[4], const ptrdiff_t src_linesizes[4],
                       enum AVPixelFormat pix_fmt, int width, int height,
                       void (*copy_plane)(uint8_t *, ptrdiff_t, const uint8_t *,
                                          ptrdiff_t, ptrdiff_t, int))
350
{
351
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
352

353
    if (!desc || desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
354 355
        return;

356
    if (desc->flags & AV_PIX_FMT_FLAG_PAL ||
357
        desc->flags & FF_PSEUDOPAL) {
358 359 360
        copy_plane(dst_data[0], dst_linesizes[0],
                   src_data[0], src_linesizes[0],
                   width, height);
361
        /* copy the palette */
362 363
        if ((desc->flags & AV_PIX_FMT_FLAG_PAL) || (dst_data[1] && src_data[1]))
            memcpy(dst_data[1], src_data[1], 4*256);
364 365 366 367 368 369 370 371
    } else {
        int i, planes_nb = 0;

        for (i = 0; i < desc->nb_components; i++)
            planes_nb = FFMAX(planes_nb, desc->comp[i].plane + 1);

        for (i = 0; i < planes_nb; i++) {
            int h = height;
372
            ptrdiff_t bwidth = av_image_get_linesize(pix_fmt, width, i);
373
            if (bwidth < 0) {
374
                av_log(NULL, AV_LOG_ERROR, "av_image_get_linesize failed\n");
375 376
                return;
            }
377
            if (i == 1 || i == 2) {
378
                h = AV_CEIL_RSHIFT(height, desc->log2_chroma_h);
379
            }
380 381 382
            copy_plane(dst_data[i], dst_linesizes[i],
                       src_data[i], src_linesizes[i],
                       bwidth, h);
383 384 385
        }
    }
}
386

387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
void av_image_copy(uint8_t *dst_data[4], int dst_linesizes[4],
                   const uint8_t *src_data[4], const int src_linesizes[4],
                   enum AVPixelFormat pix_fmt, int width, int height)
{
    ptrdiff_t dst_linesizes1[4], src_linesizes1[4];
    int i;

    for (i = 0; i < 4; i++) {
        dst_linesizes1[i] = dst_linesizes[i];
        src_linesizes1[i] = src_linesizes[i];
    }

    image_copy(dst_data, dst_linesizes1, src_data, src_linesizes1, pix_fmt,
               width, height, image_copy_plane);
}

void av_image_copy_uc_from(uint8_t *dst_data[4], const ptrdiff_t dst_linesizes[4],
                           const uint8_t *src_data[4], const ptrdiff_t src_linesizes[4],
                           enum AVPixelFormat pix_fmt, int width, int height)
{
    image_copy(dst_data, dst_linesizes, src_data, src_linesizes, pix_fmt,
               width, height, image_copy_plane_uc_from);
}

411
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4],
412 413
                         const uint8_t *src, enum AVPixelFormat pix_fmt,
                         int width, int height, int align)
414 415 416
{
    int ret, i;

417 418
    ret = av_image_check_size(width, height, 0, NULL);
    if (ret < 0)
419 420
        return ret;

421 422
    ret = av_image_fill_linesizes(dst_linesize, pix_fmt, width);
    if (ret < 0)
423 424 425 426 427
        return ret;

    for (i = 0; i < 4; i++)
        dst_linesize[i] = FFALIGN(dst_linesize[i], align);

428
    return av_image_fill_pointers(dst_data, pix_fmt, height, (uint8_t *)src, dst_linesize);
429 430
}

431 432
int av_image_get_buffer_size(enum AVPixelFormat pix_fmt,
                             int width, int height, int align)
433 434 435
{
    uint8_t *data[4];
    int linesize[4];
436 437
    int ret;
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
438 439
    if (!desc)
        return AVERROR(EINVAL);
440 441 442 443 444 445

    ret = av_image_check_size(width, height, 0, NULL);
    if (ret < 0)
        return ret;

    // do not include palette for these pseudo-paletted formats
446
    if (desc->flags & FF_PSEUDOPAL)
447
        return FFALIGN(width, align) * height;
448 449 450

    return av_image_fill_arrays(data, linesize, NULL, pix_fmt,
                                width, height, align);
451 452 453
}

int av_image_copy_to_buffer(uint8_t *dst, int dst_size,
454 455 456 457
                            const uint8_t * const src_data[4],
                            const int src_linesize[4],
                            enum AVPixelFormat pix_fmt,
                            int width, int height, int align)
458 459 460
{
    int i, j, nb_planes = 0, linesize[4];
    int size = av_image_get_buffer_size(pix_fmt, width, height, align);
461
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
462
    int ret;
463

464
    if (size > dst_size || size < 0 || !desc)
465 466 467 468
        return AVERROR(EINVAL);

    for (i = 0; i < desc->nb_components; i++)
        nb_planes = FFMAX(desc->comp[i].plane, nb_planes);
469

470 471
    nb_planes++;

472 473 474
    ret = av_image_fill_linesizes(linesize, pix_fmt, width);
    av_assert0(ret >= 0); // was checked previously

475 476 477 478 479 480 481 482 483 484 485 486
    for (i = 0; i < nb_planes; i++) {
        int h, shift = (i == 1 || i == 2) ? desc->log2_chroma_h : 0;
        const uint8_t *src = src_data[i];
        h = (height + (1 << shift) - 1) >> shift;

        for (j = 0; j < h; j++) {
            memcpy(dst, src, linesize[i]);
            dst += FFALIGN(linesize[i], align);
            src += src_linesize[i];
        }
    }

487
    if (desc->flags & AV_PIX_FMT_FLAG_PAL) {
488
        uint32_t *d32 = (uint32_t *)dst;
489

490 491 492 493 494 495
        for (i = 0; i<256; i++)
            AV_WL32(d32 + i, AV_RN32(src_data[1] + 4*i));
    }

    return size;
}
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559

// Fill dst[0..dst_size] with the bytes in clear[0..clear_size]. The clear
// bytes are repeated until dst_size is reached. If dst_size is unaligned (i.e.
// dst_size%clear_size!=0), the remaining data will be filled with the beginning
// of the clear data only.
static void memset_bytes(uint8_t *dst, size_t dst_size, uint8_t *clear,
                         size_t clear_size)
{
    size_t pos = 0;
    int same = 1;
    int i;

    if (!clear_size)
        return;

    // Reduce to memset() if possible.
    for (i = 0; i < clear_size; i++) {
        if (clear[i] != clear[0]) {
            same = 0;
            break;
        }
    }
    if (same)
        clear_size = 1;

    if (clear_size == 1) {
        memset(dst, clear[0], dst_size);
        dst_size = 0;
    } else if (clear_size == 2) {
        uint16_t val = AV_RN16(clear);
        for (; dst_size >= 2; dst_size -= 2) {
            AV_WN16(dst, val);
            dst += 2;
        }
    } else if (clear_size == 4) {
        uint32_t val = AV_RN32(clear);
        for (; dst_size >= 4; dst_size -= 4) {
            AV_WN32(dst, val);
            dst += 4;
        }
    } else if (clear_size == 8) {
        uint32_t val = AV_RN64(clear);
        for (; dst_size >= 8; dst_size -= 8) {
            AV_WN64(dst, val);
            dst += 8;
        }
    }

    for (; dst_size; dst_size--)
        *dst++ = clear[pos++ % clear_size];
}

// Maximum size in bytes of a plane element (usually a pixel, or multiple pixels
// if it's a subsampled packed format).
#define MAX_BLOCK_SIZE 32

int av_image_fill_black(uint8_t *dst_data[4], const ptrdiff_t dst_linesize[4],
                        enum AVPixelFormat pix_fmt, enum AVColorRange range,
                        int width, int height)
{
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
    int nb_planes = av_pix_fmt_count_planes(pix_fmt);
    // A pixel or a group of pixels on each plane, with a value that represents black.
    // Consider e.g. AV_PIX_FMT_UYVY422 for non-trivial cases.
560
    uint8_t clear_block[4][MAX_BLOCK_SIZE] = {{0}}; // clear padding with 0
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
    int clear_block_size[4] = {0};
    ptrdiff_t plane_line_bytes[4] = {0};
    int rgb, limited;
    int plane, c;

    if (!desc || nb_planes < 1 || nb_planes > 4 || desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
        return AVERROR(EINVAL);

    rgb = !!(desc->flags & AV_PIX_FMT_FLAG_RGB);
    limited = !rgb && range != AVCOL_RANGE_JPEG;

    if (desc->flags & AV_PIX_FMT_FLAG_BITSTREAM) {
        ptrdiff_t bytewidth = av_image_get_linesize(pix_fmt, width, 0);
        uint8_t *data;
        int mono = pix_fmt == AV_PIX_FMT_MONOWHITE || pix_fmt == AV_PIX_FMT_MONOBLACK;
        int fill = pix_fmt == AV_PIX_FMT_MONOWHITE ? 0xFF : 0;
        if (nb_planes != 1 || !(rgb || mono) || bytewidth < 1)
            return AVERROR(EINVAL);

        if (!dst_data)
            return 0;

        data = dst_data[0];

        // (Bitstream + alpha will be handled incorrectly - it'll remain transparent.)
        for (;height > 0; height--) {
            memset(data, fill, bytewidth);
            data += dst_linesize[0];
        }
        return 0;
    }

    for (c = 0; c < desc->nb_components; c++) {
        const AVComponentDescriptor comp = desc->comp[c];

        // We try to operate on entire non-subsampled pixel groups (for
        // AV_PIX_FMT_UYVY422 this would mean two consecutive pixels).
        clear_block_size[comp.plane] = FFMAX(clear_block_size[comp.plane], comp.step);

        if (clear_block_size[comp.plane] > MAX_BLOCK_SIZE)
            return AVERROR(EINVAL);
    }

    // Create a byte array for clearing 1 pixel (sometimes several pixels).
    for (c = 0; c < desc->nb_components; c++) {
        const AVComponentDescriptor comp = desc->comp[c];
        // (Multiple pixels happen e.g. with AV_PIX_FMT_UYVY422.)
        int w = clear_block_size[comp.plane] / comp.step;
        uint8_t *c_data[4];
        const int c_linesize[4] = {0};
        uint16_t src_array[MAX_BLOCK_SIZE];
        uint16_t src = 0;
        int x;

        if (comp.depth > 16)
            return AVERROR(EINVAL);
        if (!rgb && comp.depth < 8)
            return AVERROR(EINVAL);
        if (w < 1)
            return AVERROR(EINVAL);

        if (c == 0 && limited) {
            src = 16 << (comp.depth - 8);
        } else if ((c == 1 || c == 2) && !rgb) {
            src = 128 << (comp.depth - 8);
        } else if (c == 3) {
            // (Assume even limited YUV uses full range alpha.)
            src = (1 << comp.depth) - 1;
        }

        for (x = 0; x < w; x++)
            src_array[x] = src;

        for (x = 0; x < 4; x++)
            c_data[x] = &clear_block[x][0];

        av_write_image_line(src_array, c_data, c_linesize, desc, 0, 0, c, w);
    }

    for (plane = 0; plane < nb_planes; plane++) {
        plane_line_bytes[plane] = av_image_get_linesize(pix_fmt, width, plane);
        if (plane_line_bytes[plane] < 0)
            return AVERROR(EINVAL);
    }

    if (!dst_data)
        return 0;

    for (plane = 0; plane < nb_planes; plane++) {
        size_t bytewidth = plane_line_bytes[plane];
        uint8_t *data = dst_data[plane];
        int chroma_div = plane == 1 || plane == 2 ? desc->log2_chroma_h : 0;
        int plane_h = ((height + ( 1 << chroma_div) - 1)) >> chroma_div;

        for (; plane_h > 0; plane_h--) {
            memset_bytes(data, bytewidth, &clear_block[plane][0], clear_block_size[plane]);
            data += dst_linesize[plane];
        }
    }

    return 0;
}