Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/media/libyuv/libyuv/source/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 165 kB image not shown  

Quelle  planar_functions.cc   Sprache: C

 
/*
 *  Copyright 2011 The LibYuv Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS. All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */


#include "libyuv/planar_functions.h"

#include <assert.h>
#include <string.h>  // for memset()

#include "libyuv/cpu_id.h"
#include "libyuv/row.h"
#include "libyuv/scale_row.h"  // for ScaleRowDown2

#ifdef __cplusplus
namespace libyuv {
extern "C" {
#endif

// Copy a plane of data
LIBYUV_API
void CopyPlane(const uint8_t* src_y,
               int src_stride_y,
               uint8_t* dst_y,
               int dst_stride_y,
               int width,
               int height) {
  int y;
  void (*CopyRow)(const uint8_t* src, uint8_t* dst, int width) = CopyRow_C;
  if (width <= 0 || height == 0) {
    return;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_y = dst_y + (height - 1) * dst_stride_y;
    dst_stride_y = -dst_stride_y;
  }
  // Coalesce rows.
  if (src_stride_y == width && dst_stride_y == width) {
    width *= height;
    height = 1;
    src_stride_y = dst_stride_y = 0;
  }
  // Nothing to do.
  if (src_y == dst_y && src_stride_y == dst_stride_y) {
    return;
  }

#if defined(HAS_COPYROW_SSE2)
  if (TestCpuFlag(kCpuHasSSE2)) {
    CopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
  }
#endif
#if defined(HAS_COPYROW_AVX)
  if (TestCpuFlag(kCpuHasAVX)) {
    CopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
  }
#endif
#if defined(HAS_COPYROW_AVX512BW)
  if (TestCpuFlag(kCpuHasAVX512BW)) {
    CopyRow = IS_ALIGNED(width, 128) ? CopyRow_AVX512BW : CopyRow_Any_AVX512BW;
  }
#endif
#if defined(HAS_COPYROW_ERMS)
  if (TestCpuFlag(kCpuHasERMS)) {
    CopyRow = CopyRow_ERMS;
  }
#endif
#if defined(HAS_COPYROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    CopyRow = IS_ALIGNED(width, 32) ? CopyRow_NEON : CopyRow_Any_NEON;
  }
#endif
#if defined(HAS_COPYROW_SME)
  if (TestCpuFlag(kCpuHasSME)) {
    CopyRow = CopyRow_SME;
  }
#endif
#if defined(HAS_COPYROW_RVV)
  if (TestCpuFlag(kCpuHasRVV)) {
    CopyRow = CopyRow_RVV;
  }
#endif

  // Copy plane
  for (y = 0; y < height; ++y) {
    CopyRow(src_y, dst_y, width);
    src_y += src_stride_y;
    dst_y += dst_stride_y;
  }
}

LIBYUV_API
void CopyPlane_16(const uint16_t* src_y,
                  int src_stride_y,
                  uint16_t* dst_y,
                  int dst_stride_y,
                  int width,
                  int height) {
  CopyPlane((const uint8_t*)src_y, src_stride_y * 2, (uint8_t*)dst_y,
            dst_stride_y * 2, width * 2, height);
}

// Convert a plane of 16 bit data to 8 bit
LIBYUV_API
void Convert16To8Plane(const uint16_t* src_y,
                       int src_stride_y,
                       uint8_t* dst_y,
                       int dst_stride_y,
                       int scale,  // 16384 for 10 bits
                       int width,
                       int height) {
  int y;
  void (*Convert16To8Row)(const uint16_t* src_y, uint8_t* dst_y, int scale,
                          int width) = Convert16To8Row_C;

  if (width <= 0 || height == 0) {
    return;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_y = dst_y + (height - 1) * dst_stride_y;
    dst_stride_y = -dst_stride_y;
  }
  // Coalesce rows.
  if (src_stride_y == width && dst_stride_y == width) {
    width *= height;
    height = 1;
    src_stride_y = dst_stride_y = 0;
  }
#if defined(HAS_CONVERT16TO8ROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    Convert16To8Row = Convert16To8Row_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      Convert16To8Row = Convert16To8Row_NEON;
    }
  }
#endif
#if defined(HAS_CONVERT16TO8ROW_SME)
  if (TestCpuFlag(kCpuHasSME)) {
    Convert16To8Row = Convert16To8Row_SME;
  }
#endif
#if defined(HAS_CONVERT16TO8ROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3)) {
    Convert16To8Row = Convert16To8Row_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      Convert16To8Row = Convert16To8Row_SSSE3;
    }
  }
#endif
#if defined(HAS_CONVERT16TO8ROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    Convert16To8Row = Convert16To8Row_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
      Convert16To8Row = Convert16To8Row_AVX2;
    }
  }
#endif
#if defined(HAS_CONVERT16TO8ROW_AVX512BW)
  if (TestCpuFlag(kCpuHasAVX512BW)) {
    Convert16To8Row = Convert16To8Row_Any_AVX512BW;
    if (IS_ALIGNED(width, 64)) {
      Convert16To8Row = Convert16To8Row_AVX512BW;
    }
  }
#endif

  // Convert plane
  for (y = 0; y < height; ++y) {
    Convert16To8Row(src_y, dst_y, scale, width);
    src_y += src_stride_y;
    dst_y += dst_stride_y;
  }
}

// Convert a plane of 8 bit data to 16 bit
LIBYUV_API
void Convert8To16Plane(const uint8_t* src_y,
                       int src_stride_y,
                       uint16_t* dst_y,
                       int dst_stride_y,
                       int scale,  // 1024 for 10 bits
                       int width,
                       int height) {
  int y;
  void (*Convert8To16Row)(const uint8_t* src_y, uint16_t* dst_y, int scale,
                          int width) = Convert8To16Row_C;

  if (width <= 0 || height == 0) {
    return;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_y = dst_y + (height - 1) * dst_stride_y;
    dst_stride_y = -dst_stride_y;
  }
  // Coalesce rows.
  if (src_stride_y == width && dst_stride_y == width) {
    width *= height;
    height = 1;
    src_stride_y = dst_stride_y = 0;
  }
#if defined(HAS_CONVERT8TO16ROW_SSE2)
  if (TestCpuFlag(kCpuHasSSE2)) {
    Convert8To16Row = Convert8To16Row_Any_SSE2;
    if (IS_ALIGNED(width, 16)) {
      Convert8To16Row = Convert8To16Row_SSE2;
    }
  }
#endif
#if defined(HAS_CONVERT8TO16ROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    Convert8To16Row = Convert8To16Row_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
      Convert8To16Row = Convert8To16Row_AVX2;
    }
  }
#endif

  // Convert plane
  for (y = 0; y < height; ++y) {
    Convert8To16Row(src_y, dst_y, scale, width);
    src_y += src_stride_y;
    dst_y += dst_stride_y;
  }
}

// Copy I422.
LIBYUV_API
int I422Copy(const uint8_t* src_y,
             int src_stride_y,
             const uint8_t* src_u,
             int src_stride_u,
             const uint8_t* src_v,
             int src_stride_v,
             uint8_t* dst_y,
             int dst_stride_y,
             uint8_t* dst_u,
             int dst_stride_u,
             uint8_t* dst_v,
             int dst_stride_v,
             int width,
             int height) {
  int halfwidth = (width + 1) >> 1;

  if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || width <= 0 ||
      height == 0) {
    return -1;
  }

  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    src_y = src_y + (height - 1) * src_stride_y;
    src_u = src_u + (height - 1) * src_stride_u;
    src_v = src_v + (height - 1) * src_stride_v;
    src_stride_y = -src_stride_y;
    src_stride_u = -src_stride_u;
    src_stride_v = -src_stride_v;
  }

  if (dst_y) {
    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  }
  CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, height);
  CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, height);
  return 0;
}

// Copy I444.
LIBYUV_API
int I444Copy(const uint8_t* src_y,
             int src_stride_y,
             const uint8_t* src_u,
             int src_stride_u,
             const uint8_t* src_v,
             int src_stride_v,
             uint8_t* dst_y,
             int dst_stride_y,
             uint8_t* dst_u,
             int dst_stride_u,
             uint8_t* dst_v,
             int dst_stride_v,
             int width,
             int height) {
  if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || width <= 0 ||
      height == 0) {
    return -1;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    src_y = src_y + (height - 1) * src_stride_y;
    src_u = src_u + (height - 1) * src_stride_u;
    src_v = src_v + (height - 1) * src_stride_v;
    src_stride_y = -src_stride_y;
    src_stride_u = -src_stride_u;
    src_stride_v = -src_stride_v;
  }

  if (dst_y) {
    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  }
  CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
  CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
  return 0;
}

// Copy I210.
LIBYUV_API
int I210Copy(const uint16_t* src_y,
             int src_stride_y,
             const uint16_t* src_u,
             int src_stride_u,
             const uint16_t* src_v,
             int src_stride_v,
             uint16_t* dst_y,
             int dst_stride_y,
             uint16_t* dst_u,
             int dst_stride_u,
             uint16_t* dst_v,
             int dst_stride_v,
             int width,
             int height) {
  int halfwidth = (width + 1) >> 1;

  if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || width <= 0 ||
      height == 0) {
    return -1;
  }

  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    src_y = src_y + (height - 1) * src_stride_y;
    src_u = src_u + (height - 1) * src_stride_u;
    src_v = src_v + (height - 1) * src_stride_v;
    src_stride_y = -src_stride_y;
    src_stride_u = -src_stride_u;
    src_stride_v = -src_stride_v;
  }

  if (dst_y) {
    CopyPlane_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  }
  // Copy UV planes.
  CopyPlane_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, height);
  CopyPlane_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, height);
  return 0;
}

// Copy I410.
LIBYUV_API
int I410Copy(const uint16_t* src_y,
             int src_stride_y,
             const uint16_t* src_u,
             int src_stride_u,
             const uint16_t* src_v,
             int src_stride_v,
             uint16_t* dst_y,
             int dst_stride_y,
             uint16_t* dst_u,
             int dst_stride_u,
             uint16_t* dst_v,
             int dst_stride_v,
             int width,
             int height) {
  if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || width <= 0 ||
      height == 0) {
    return -1;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    src_y = src_y + (height - 1) * src_stride_y;
    src_u = src_u + (height - 1) * src_stride_u;
    src_v = src_v + (height - 1) * src_stride_v;
    src_stride_y = -src_stride_y;
    src_stride_u = -src_stride_u;
    src_stride_v = -src_stride_v;
  }

  if (dst_y) {
    CopyPlane_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  }
  CopyPlane_16(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
  CopyPlane_16(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
  return 0;
}

// Copy I400.
LIBYUV_API
int I400ToI400(const uint8_t* src_y,
               int src_stride_y,
               uint8_t* dst_y,
               int dst_stride_y,
               int width,
               int height) {
  if (!src_y || !dst_y || width <= 0 || height == 0) {
    return -1;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    src_y = src_y + (height - 1) * src_stride_y;
    src_stride_y = -src_stride_y;
  }
  CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  return 0;
}

// Convert I420 to I400.
LIBYUV_API
int I420ToI400(const uint8_t* src_y,
               int src_stride_y,
               const uint8_t* src_u,
               int src_stride_u,
               const uint8_t* src_v,
               int src_stride_v,
               uint8_t* dst_y,
               int dst_stride_y,
               int width,
               int height) {
  (void)src_u;
  (void)src_stride_u;
  (void)src_v;
  (void)src_stride_v;
  if (!src_y || !dst_y || width <= 0 || height == 0) {
    return -1;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    src_y = src_y + (height - 1) * src_stride_y;
    src_stride_y = -src_stride_y;
  }

  CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  return 0;
}

// Copy NV12. Supports inverting.
LIBYUV_API
int NV12Copy(const uint8_t* src_y,
             int src_stride_y,
             const uint8_t* src_uv,
             int src_stride_uv,
             uint8_t* dst_y,
             int dst_stride_y,
             uint8_t* dst_uv,
             int dst_stride_uv,
             int width,
             int height) {
  int halfwidth = (width + 1) >> 1;
  int halfheight = (height + 1) >> 1;

  if (!src_y || !dst_y || !src_uv || !dst_uv || width <= 0 || height == 0) {
    return -1;
  }

  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    halfheight = (height + 1) >> 1;
    src_y = src_y + (height - 1) * src_stride_y;
    src_uv = src_uv + (halfheight - 1) * src_stride_uv;
    src_stride_y = -src_stride_y;
    src_stride_uv = -src_stride_uv;
  }
  CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  CopyPlane(src_uv, src_stride_uv, dst_uv, dst_stride_uv, halfwidth * 2,
            halfheight);
  return 0;
}

// Copy NV21. Supports inverting.
LIBYUV_API
int NV21Copy(const uint8_t* src_y,
             int src_stride_y,
             const uint8_t* src_vu,
             int src_stride_vu,
             uint8_t* dst_y,
             int dst_stride_y,
             uint8_t* dst_vu,
             int dst_stride_vu,
             int width,
             int height) {
  return NV12Copy(src_y, src_stride_y, src_vu, src_stride_vu, dst_y,
                  dst_stride_y, dst_vu, dst_stride_vu, width, height);
}

// Support function for NV12 etc UV channels.
// Width and height are plane sizes (typically half pixel width).
LIBYUV_API
void SplitUVPlane(const uint8_t* src_uv,
                  int src_stride_uv,
                  uint8_t* dst_u,
                  int dst_stride_u,
                  uint8_t* dst_v,
                  int dst_stride_v,
                  int width,
                  int height) {
  int y;
  void (*SplitUVRow)(const uint8_t* src_uv, uint8_t* dst_u, uint8_t* dst_v,
                     int width) = SplitUVRow_C;
  if (width <= 0 || height == 0) {
    return;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_u = dst_u + (height - 1) * dst_stride_u;
    dst_v = dst_v + (height - 1) * dst_stride_v;
    dst_stride_u = -dst_stride_u;
    dst_stride_v = -dst_stride_v;
  }
  // Coalesce rows.
  if (src_stride_uv == width * 2 && dst_stride_u == width &&
      dst_stride_v == width) {
    width *= height;
    height = 1;
    src_stride_uv = dst_stride_u = dst_stride_v = 0;
  }
#if defined(HAS_SPLITUVROW_SSE2)
  if (TestCpuFlag(kCpuHasSSE2)) {
    SplitUVRow = SplitUVRow_Any_SSE2;
    if (IS_ALIGNED(width, 16)) {
      SplitUVRow = SplitUVRow_SSE2;
    }
  }
#endif
#if defined(HAS_SPLITUVROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    SplitUVRow = SplitUVRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
      SplitUVRow = SplitUVRow_AVX2;
    }
  }
#endif
#if defined(HAS_SPLITUVROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    SplitUVRow = SplitUVRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      SplitUVRow = SplitUVRow_NEON;
    }
  }
#endif
#if defined(HAS_SPLITUVROW_MSA)
  if (TestCpuFlag(kCpuHasMSA)) {
    SplitUVRow = SplitUVRow_Any_MSA;
    if (IS_ALIGNED(width, 32)) {
      SplitUVRow = SplitUVRow_MSA;
    }
  }
#endif
#if defined(HAS_SPLITUVROW_LSX)
  if (TestCpuFlag(kCpuHasLSX)) {
    SplitUVRow = SplitUVRow_Any_LSX;
    if (IS_ALIGNED(width, 32)) {
      SplitUVRow = SplitUVRow_LSX;
    }
  }
#endif
#if defined(HAS_SPLITUVROW_RVV)
  if (TestCpuFlag(kCpuHasRVV)) {
    SplitUVRow = SplitUVRow_RVV;
  }
#endif

  for (y = 0; y < height; ++y) {
    // Copy a row of UV.
    SplitUVRow(src_uv, dst_u, dst_v, width);
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
    src_uv += src_stride_uv;
  }
}

LIBYUV_API
void MergeUVPlane(const uint8_t* src_u,
                  int src_stride_u,
                  const uint8_t* src_v,
                  int src_stride_v,
                  uint8_t* dst_uv,
                  int dst_stride_uv,
                  int width,
                  int height) {
  int y;
  void (*MergeUVRow)(const uint8_t* src_u, const uint8_t* src_v,
                     uint8_t* dst_uv, int width) = MergeUVRow_C;
  if (width <= 0 || height == 0) {
    return;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_uv = dst_uv + (height - 1) * dst_stride_uv;
    dst_stride_uv = -dst_stride_uv;
  }
  // Coalesce rows.
  if (src_stride_u == width && src_stride_v == width &&
      dst_stride_uv == width * 2) {
    width *= height;
    height = 1;
    src_stride_u = src_stride_v = dst_stride_uv = 0;
  }
#if defined(HAS_MERGEUVROW_SSE2)
  if (TestCpuFlag(kCpuHasSSE2)) {
    MergeUVRow = MergeUVRow_Any_SSE2;
    if (IS_ALIGNED(width, 16)) {
      MergeUVRow = MergeUVRow_SSE2;
    }
  }
#endif
#if defined(HAS_MERGEUVROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    MergeUVRow = MergeUVRow_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      MergeUVRow = MergeUVRow_AVX2;
    }
  }
#endif
#if defined(HAS_MERGEUVROW_AVX512BW)
  if (TestCpuFlag(kCpuHasAVX512BW)) {
    MergeUVRow = MergeUVRow_Any_AVX512BW;
    if (IS_ALIGNED(width, 32)) {
      MergeUVRow = MergeUVRow_AVX512BW;
    }
  }
#endif
#if defined(HAS_MERGEUVROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    MergeUVRow = MergeUVRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      MergeUVRow = MergeUVRow_NEON;
    }
  }
#endif
#if defined(HAS_MERGEUVROW_SME)
  if (TestCpuFlag(kCpuHasSME)) {
    MergeUVRow = MergeUVRow_SME;
  }
#endif
#if defined(HAS_MERGEUVROW_MSA)
  if (TestCpuFlag(kCpuHasMSA)) {
    MergeUVRow = MergeUVRow_Any_MSA;
    if (IS_ALIGNED(width, 16)) {
      MergeUVRow = MergeUVRow_MSA;
    }
  }
#endif
#if defined(HAS_MERGEUVROW_LSX)
  if (TestCpuFlag(kCpuHasLSX)) {
    MergeUVRow = MergeUVRow_Any_LSX;
    if (IS_ALIGNED(width, 16)) {
      MergeUVRow = MergeUVRow_LSX;
    }
  }
#endif
#if defined(HAS_MERGEUVROW_RVV)
  if (TestCpuFlag(kCpuHasRVV)) {
    MergeUVRow = MergeUVRow_RVV;
  }
#endif

  for (y = 0; y < height; ++y) {
    // Merge a row of U and V into a row of UV.
    MergeUVRow(src_u, src_v, dst_uv, width);
    src_u += src_stride_u;
    src_v += src_stride_v;
    dst_uv += dst_stride_uv;
  }
}

// Support function for P010 etc UV channels.
// Width and height are plane sizes (typically half pixel width).
LIBYUV_API
void SplitUVPlane_16(const uint16_t* src_uv,
                     int src_stride_uv,
                     uint16_t* dst_u,
                     int dst_stride_u,
                     uint16_t* dst_v,
                     int dst_stride_v,
                     int width,
                     int height,
                     int depth) {
  int y;
  void (*SplitUVRow_16)(const uint16_t* src_uv, uint16_t* dst_u,
                        uint16_t* dst_v, int depth, int width) =
      SplitUVRow_16_C;
  if (width <= 0 || height == 0) {
    return;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_u = dst_u + (height - 1) * dst_stride_u;
    dst_v = dst_v + (height - 1) * dst_stride_v;
    dst_stride_u = -dst_stride_u;
    dst_stride_v = -dst_stride_v;
  }
  // Coalesce rows.
  if (src_stride_uv == width * 2 && dst_stride_u == width &&
      dst_stride_v == width) {
    width *= height;
    height = 1;
    src_stride_uv = dst_stride_u = dst_stride_v = 0;
  }
#if defined(HAS_SPLITUVROW_16_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    SplitUVRow_16 = SplitUVRow_16_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      SplitUVRow_16 = SplitUVRow_16_AVX2;
    }
  }
#endif
#if defined(HAS_SPLITUVROW_16_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    SplitUVRow_16 = SplitUVRow_16_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      SplitUVRow_16 = SplitUVRow_16_NEON;
    }
  }
#endif

  for (y = 0; y < height; ++y) {
    // Copy a row of UV.
    SplitUVRow_16(src_uv, dst_u, dst_v, depth, width);
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
    src_uv += src_stride_uv;
  }
}

LIBYUV_API
void MergeUVPlane_16(const uint16_t* src_u,
                     int src_stride_u,
                     const uint16_t* src_v,
                     int src_stride_v,
                     uint16_t* dst_uv,
                     int dst_stride_uv,
                     int width,
                     int height,
                     int depth) {
  int y;
  void (*MergeUVRow_16)(const uint16_t* src_u, const uint16_t* src_v,
                        uint16_t* dst_uv, int depth, int width) =
      MergeUVRow_16_C;
  assert(depth >= 8);
  assert(depth <= 16);
  if (width <= 0 || height == 0) {
    return;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_uv = dst_uv + (height - 1) * dst_stride_uv;
    dst_stride_uv = -dst_stride_uv;
  }
  // Coalesce rows.
  if (src_stride_u == width && src_stride_v == width &&
      dst_stride_uv == width * 2) {
    width *= height;
    height = 1;
    src_stride_u = src_stride_v = dst_stride_uv = 0;
  }
#if defined(HAS_MERGEUVROW_16_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    MergeUVRow_16 = MergeUVRow_16_Any_AVX2;
    if (IS_ALIGNED(width, 8)) {
      MergeUVRow_16 = MergeUVRow_16_AVX2;
    }
  }
#endif
#if defined(HAS_MERGEUVROW_16_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    MergeUVRow_16 = MergeUVRow_16_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      MergeUVRow_16 = MergeUVRow_16_NEON;
    }
  }
#endif
#if defined(HAS_MERGEUVROW_16_SME)
  if (TestCpuFlag(kCpuHasSME)) {
    MergeUVRow_16 = MergeUVRow_16_SME;
  }
#endif

  for (y = 0; y < height; ++y) {
    // Merge a row of U and V into a row of UV.
    MergeUVRow_16(src_u, src_v, dst_uv, depth, width);
    src_u += src_stride_u;
    src_v += src_stride_v;
    dst_uv += dst_stride_uv;
  }
}

// Convert plane from lsb to msb
LIBYUV_API
void ConvertToMSBPlane_16(const uint16_t* src_y,
                          int src_stride_y,
                          uint16_t* dst_y,
                          int dst_stride_y,
                          int width,
                          int height,
                          int depth) {
  int y;
  int scale = 1 << (16 - depth);
  void (*MultiplyRow_16)(const uint16_t* src_y, uint16_t* dst_y, int scale,
                         int width) = MultiplyRow_16_C;
  if (width <= 0 || height == 0) {
    return;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_y = dst_y + (height - 1) * dst_stride_y;
    dst_stride_y = -dst_stride_y;
  }
  // Coalesce rows.
  if (src_stride_y == width && dst_stride_y == width) {
    width *= height;
    height = 1;
    src_stride_y = dst_stride_y = 0;
  }

#if defined(HAS_MULTIPLYROW_16_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    MultiplyRow_16 = MultiplyRow_16_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
      MultiplyRow_16 = MultiplyRow_16_AVX2;
    }
  }
#endif
#if defined(HAS_MULTIPLYROW_16_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    MultiplyRow_16 = MultiplyRow_16_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      MultiplyRow_16 = MultiplyRow_16_NEON;
    }
  }
#endif
#if defined(HAS_MULTIPLYROW_16_SME)
  if (TestCpuFlag(kCpuHasSME)) {
    MultiplyRow_16 = MultiplyRow_16_SME;
  }
#endif

  for (y = 0; y < height; ++y) {
    MultiplyRow_16(src_y, dst_y, scale, width);
    src_y += src_stride_y;
    dst_y += dst_stride_y;
  }
}

// Convert plane from msb to lsb
LIBYUV_API
void ConvertToLSBPlane_16(const uint16_t* src_y,
                          int src_stride_y,
                          uint16_t* dst_y,
                          int dst_stride_y,
                          int width,
                          int height,
                          int depth) {
  int y;
  int scale = 1 << depth;
  void (*DivideRow)(const uint16_t* src_y, uint16_t* dst_y, int scale,
                    int width) = DivideRow_16_C;
  if (width <= 0 || height == 0) {
    return;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_y = dst_y + (height - 1) * dst_stride_y;
    dst_stride_y = -dst_stride_y;
  }
  // Coalesce rows.
  if (src_stride_y == width && dst_stride_y == width) {
    width *= height;
    height = 1;
    src_stride_y = dst_stride_y = 0;
  }

#if defined(HAS_DIVIDEROW_16_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    DivideRow = DivideRow_16_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
      DivideRow = DivideRow_16_AVX2;
    }
  }
#endif
#if defined(HAS_DIVIDEROW_16_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    DivideRow = DivideRow_16_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      DivideRow = DivideRow_16_NEON;
    }
  }
#endif
#if defined(HAS_DIVIDEROW_16_SVE2)
  if (TestCpuFlag(kCpuHasSVE2)) {
    DivideRow = DivideRow_16_SVE2;
  }
#endif

  for (y = 0; y < height; ++y) {
    DivideRow(src_y, dst_y, scale, width);
    src_y += src_stride_y;
    dst_y += dst_stride_y;
  }
}

// Swap U and V channels in interleaved UV plane.
LIBYUV_API
void SwapUVPlane(const uint8_t* src_uv,
                 int src_stride_uv,
                 uint8_t* dst_vu,
                 int dst_stride_vu,
                 int width,
                 int height) {
  int y;
  void (*SwapUVRow)(const uint8_t* src_uv, uint8_t* dst_vu, int width) =
      SwapUVRow_C;
  if (width <= 0 || height == 0) {
    return;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    src_uv = src_uv + (height - 1) * src_stride_uv;
    src_stride_uv = -src_stride_uv;
  }
  // Coalesce rows.
  if (src_stride_uv == width * 2 && dst_stride_vu == width * 2) {
    width *= height;
    height = 1;
    src_stride_uv = dst_stride_vu = 0;
  }

#if defined(HAS_SWAPUVROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3)) {
    SwapUVRow = SwapUVRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      SwapUVRow = SwapUVRow_SSSE3;
    }
  }
#endif
#if defined(HAS_SWAPUVROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    SwapUVRow = SwapUVRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
      SwapUVRow = SwapUVRow_AVX2;
    }
  }
#endif
#if defined(HAS_SWAPUVROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    SwapUVRow = SwapUVRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      SwapUVRow = SwapUVRow_NEON;
    }
  }
#endif

  for (y = 0; y < height; ++y) {
    SwapUVRow(src_uv, dst_vu, width);
    src_uv += src_stride_uv;
    dst_vu += dst_stride_vu;
  }
}

// Convert NV21 to NV12.
LIBYUV_API
int NV21ToNV12(const uint8_t* src_y,
               int src_stride_y,
               const uint8_t* src_vu,
               int src_stride_vu,
               uint8_t* dst_y,
               int dst_stride_y,
               uint8_t* dst_uv,
               int dst_stride_uv,
               int width,
               int height) {
  int halfwidth = (width + 1) >> 1;
  int halfheight = (height + 1) >> 1;

  if (!src_vu || !dst_uv || width <= 0 || height == 0) {
    return -1;
  }

  if (dst_y) {
    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  }

  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    halfheight = (height + 1) >> 1;
    src_vu = src_vu + (halfheight - 1) * src_stride_vu;
    src_stride_vu = -src_stride_vu;
  }

  SwapUVPlane(src_vu, src_stride_vu, dst_uv, dst_stride_uv, halfwidth,
              halfheight);
  return 0;
}

// Test if tile_height is a power of 2 (16 or 32)
#define IS_POWEROFTWO(x) (!((x) & ((x)-1)))

// Detile a plane of data
// tile width is 16 and assumed.
// tile_height is 16 or 32 for MM21.
// src_stride_y is bytes per row of source ignoring tiling. e.g. 640
// TODO: More detile row functions.
LIBYUV_API
int DetilePlane(const uint8_t* src_y,
                int src_stride_y,
                uint8_t* dst_y,
                int dst_stride_y,
                int width,
                int height,
                int tile_height) {
  const ptrdiff_t src_tile_stride = 16 * tile_height;
  int y;
  void (*DetileRow)(const uint8_t* src, ptrdiff_t src_tile_stride, uint8_t* dst,
                    int width) = DetileRow_C;
  if (!src_y || !dst_y || width <= 0 || height == 0 ||
      !IS_POWEROFTWO(tile_height)) {
    return -1;
  }

  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_y = dst_y + (height - 1) * dst_stride_y;
    dst_stride_y = -dst_stride_y;
  }

#if defined(HAS_DETILEROW_SSE2)
  if (TestCpuFlag(kCpuHasSSE2)) {
    DetileRow = DetileRow_Any_SSE2;
    if (IS_ALIGNED(width, 16)) {
      DetileRow = DetileRow_SSE2;
    }
  }
#endif
#if defined(HAS_DETILEROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    DetileRow = DetileRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      DetileRow = DetileRow_NEON;
    }
  }
#endif

  // Detile plane
  for (y = 0; y < height; ++y) {
    DetileRow(src_y, src_tile_stride, dst_y, width);
    dst_y += dst_stride_y;
    src_y += 16;
    // Advance to next row of tiles.
    if ((y & (tile_height - 1)) == (tile_height - 1)) {
      src_y = src_y - src_tile_stride + src_stride_y * tile_height;
    }
  }
  return 0;
}

// Convert a plane of 16 bit tiles of 16 x H to linear.
// tile width is 16 and assumed.
// tile_height is 16 or 32 for MT2T.
LIBYUV_API
int DetilePlane_16(const uint16_t* src_y,
                   int src_stride_y,
                   uint16_t* dst_y,
                   int dst_stride_y,
                   int width,
                   int height,
                   int tile_height) {
  const ptrdiff_t src_tile_stride = 16 * tile_height;
  int y;
  void (*DetileRow_16)(const uint16_t* src, ptrdiff_t src_tile_stride,
                       uint16_t* dst, int width) = DetileRow_16_C;
  if (!src_y || !dst_y || width <= 0 || height == 0 ||
      !IS_POWEROFTWO(tile_height)) {
    return -1;
  }

  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_y = dst_y + (height - 1) * dst_stride_y;
    dst_stride_y = -dst_stride_y;
  }

#if defined(HAS_DETILEROW_16_SSE2)
  if (TestCpuFlag(kCpuHasSSE2)) {
    DetileRow_16 = DetileRow_16_Any_SSE2;
    if (IS_ALIGNED(width, 16)) {
      DetileRow_16 = DetileRow_16_SSE2;
    }
  }
#endif
#if defined(HAS_DETILEROW_16_AVX)
  if (TestCpuFlag(kCpuHasAVX)) {
    DetileRow_16 = DetileRow_16_Any_AVX;
    if (IS_ALIGNED(width, 16)) {
      DetileRow_16 = DetileRow_16_AVX;
    }
  }
#endif
#if defined(HAS_DETILEROW_16_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    DetileRow_16 = DetileRow_16_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      DetileRow_16 = DetileRow_16_NEON;
    }
  }
#endif

  // Detile plane
  for (y = 0; y < height; ++y) {
    DetileRow_16(src_y, src_tile_stride, dst_y, width);
    dst_y += dst_stride_y;
    src_y += 16;
    // Advance to next row of tiles.
    if ((y & (tile_height - 1)) == (tile_height - 1)) {
      src_y = src_y - src_tile_stride + src_stride_y * tile_height;
    }
  }
  return 0;
}

LIBYUV_API
void DetileSplitUVPlane(const uint8_t* src_uv,
                        int src_stride_uv,
                        uint8_t* dst_u,
                        int dst_stride_u,
                        uint8_t* dst_v,
                        int dst_stride_v,
                        int width,
                        int height,
                        int tile_height) {
  const ptrdiff_t src_tile_stride = 16 * tile_height;
  int y;
  void (*DetileSplitUVRow)(const uint8_t* src, ptrdiff_t src_tile_stride,
                           uint8_t* dst_u, uint8_t* dst_v, int width) =
      DetileSplitUVRow_C;
  assert(src_stride_uv >= 0);
  assert(tile_height > 0);
  assert(src_stride_uv > 0);

  if (width <= 0 || height == 0) {
    return;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_u = dst_u + (height - 1) * dst_stride_u;
    dst_stride_u = -dst_stride_u;
    dst_v = dst_v + (height - 1) * dst_stride_v;
    dst_stride_v = -dst_stride_v;
  }

#if defined(HAS_DETILESPLITUVROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3)) {
    DetileSplitUVRow = DetileSplitUVRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      DetileSplitUVRow = DetileSplitUVRow_SSSE3;
    }
  }
#endif
#if defined(HAS_DETILESPLITUVROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    DetileSplitUVRow = DetileSplitUVRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      DetileSplitUVRow = DetileSplitUVRow_NEON;
    }
  }
#endif

  // Detile plane
  for (y = 0; y < height; ++y) {
    DetileSplitUVRow(src_uv, src_tile_stride, dst_u, dst_v, width);
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
    src_uv += 16;
    // Advance to next row of tiles.
    if ((y & (tile_height - 1)) == (tile_height - 1)) {
      src_uv = src_uv - src_tile_stride + src_stride_uv * tile_height;
    }
  }
}

LIBYUV_API
void DetileToYUY2(const uint8_t* src_y,
                  int src_stride_y,
                  const uint8_t* src_uv,
                  int src_stride_uv,
                  uint8_t* dst_yuy2,
                  int dst_stride_yuy2,
                  int width,
                  int height,
                  int tile_height) {
  const ptrdiff_t src_y_tile_stride = 16 * tile_height;
  const ptrdiff_t src_uv_tile_stride = src_y_tile_stride / 2;
  int y;
  void (*DetileToYUY2)(const uint8_t* src_y, ptrdiff_t src_y_tile_stride,
                       const uint8_t* src_uv, ptrdiff_t src_uv_tile_stride,
                       uint8_t* dst_yuy2, int width) = DetileToYUY2_C;
  assert(src_stride_y >= 0);
  assert(src_stride_y > 0);
  assert(src_stride_uv >= 0);
  assert(src_stride_uv > 0);
  assert(tile_height > 0);

  if (width <= 0 || height == 0 || tile_height <= 0) {
    return;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_yuy2 = dst_yuy2 + (height - 1) * dst_stride_yuy2;
    dst_stride_yuy2 = -dst_stride_yuy2;
  }

#if defined(HAS_DETILETOYUY2_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    DetileToYUY2 = DetileToYUY2_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      DetileToYUY2 = DetileToYUY2_NEON;
    }
  }
#endif

#if defined(HAS_DETILETOYUY2_SSE2)
  if (TestCpuFlag(kCpuHasSSE2)) {
    DetileToYUY2 = DetileToYUY2_Any_SSE2;
    if (IS_ALIGNED(width, 16)) {
      DetileToYUY2 = DetileToYUY2_SSE2;
    }
  }
#endif

  // Detile plane
  for (y = 0; y < height; ++y) {
    DetileToYUY2(src_y, src_y_tile_stride, src_uv, src_uv_tile_stride, dst_yuy2,
                 width);
    dst_yuy2 += dst_stride_yuy2;
    src_y += 16;

    if (y & 0x1)
      src_uv += 16;

    // Advance to next row of tiles.
    if ((y & (tile_height - 1)) == (tile_height - 1)) {
      src_y = src_y - src_y_tile_stride + src_stride_y * tile_height;
      src_uv = src_uv - src_uv_tile_stride + src_stride_uv * (tile_height / 2);
    }
  }
}

// Support function for NV12 etc RGB channels.
// Width and height are plane sizes (typically half pixel width).
LIBYUV_API
void SplitRGBPlane(const uint8_t* src_rgb,
                   int src_stride_rgb,
                   uint8_t* dst_r,
                   int dst_stride_r,
                   uint8_t* dst_g,
                   int dst_stride_g,
                   uint8_t* dst_b,
                   int dst_stride_b,
                   int width,
                   int height) {
  int y;
  void (*SplitRGBRow)(const uint8_t* src_rgb, uint8_t* dst_r, uint8_t* dst_g,
                      uint8_t* dst_b, int width) = SplitRGBRow_C;
  if (width <= 0 || height == 0) {
    return;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_r = dst_r + (height - 1) * dst_stride_r;
    dst_g = dst_g + (height - 1) * dst_stride_g;
    dst_b = dst_b + (height - 1) * dst_stride_b;
    dst_stride_r = -dst_stride_r;
    dst_stride_g = -dst_stride_g;
    dst_stride_b = -dst_stride_b;
  }
  // Coalesce rows.
  if (src_stride_rgb == width * 3 && dst_stride_r == width &&
      dst_stride_g == width && dst_stride_b == width) {
    width *= height;
    height = 1;
    src_stride_rgb = dst_stride_r = dst_stride_g = dst_stride_b = 0;
  }
#if defined(HAS_SPLITRGBROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3)) {
    SplitRGBRow = SplitRGBRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      SplitRGBRow = SplitRGBRow_SSSE3;
    }
  }
#endif
#if defined(HAS_SPLITRGBROW_SSE41)
  if (TestCpuFlag(kCpuHasSSE41)) {
    SplitRGBRow = SplitRGBRow_Any_SSE41;
    if (IS_ALIGNED(width, 16)) {
      SplitRGBRow = SplitRGBRow_SSE41;
    }
  }
#endif
#if defined(HAS_SPLITRGBROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    SplitRGBRow = SplitRGBRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
      SplitRGBRow = SplitRGBRow_AVX2;
    }
  }
#endif
#if defined(HAS_SPLITRGBROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    SplitRGBRow = SplitRGBRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      SplitRGBRow = SplitRGBRow_NEON;
    }
  }
#endif
#if defined(HAS_SPLITRGBROW_RVV)
  if (TestCpuFlag(kCpuHasRVV)) {
    SplitRGBRow = SplitRGBRow_RVV;
  }
#endif

  for (y = 0; y < height; ++y) {
    // Copy a row of RGB.
    SplitRGBRow(src_rgb, dst_r, dst_g, dst_b, width);
    dst_r += dst_stride_r;
    dst_g += dst_stride_g;
    dst_b += dst_stride_b;
    src_rgb += src_stride_rgb;
  }
}

LIBYUV_API
void MergeRGBPlane(const uint8_t* src_r,
                   int src_stride_r,
                   const uint8_t* src_g,
                   int src_stride_g,
                   const uint8_t* src_b,
                   int src_stride_b,
                   uint8_t* dst_rgb,
                   int dst_stride_rgb,
                   int width,
                   int height) {
  int y;
  void (*MergeRGBRow)(const uint8_t* src_r, const uint8_t* src_g,
                      const uint8_t* src_b, uint8_t* dst_rgb, int width) =
      MergeRGBRow_C;
  if (width <= 0 || height == 0) {
    return;
  }
  // Coalesce rows.
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_rgb = dst_rgb + (height - 1) * dst_stride_rgb;
    dst_stride_rgb = -dst_stride_rgb;
  }
  // Coalesce rows.
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
      dst_stride_rgb == width * 3) {
    width *= height;
    height = 1;
    src_stride_r = src_stride_g = src_stride_b = dst_stride_rgb = 0;
  }
#if defined(HAS_MERGERGBROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3)) {
    MergeRGBRow = MergeRGBRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      MergeRGBRow = MergeRGBRow_SSSE3;
    }
  }
#endif
#if defined(HAS_MERGERGBROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    MergeRGBRow = MergeRGBRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      MergeRGBRow = MergeRGBRow_NEON;
    }
  }
#endif
#if defined(HAS_MERGERGBROW_RVV)
  if (TestCpuFlag(kCpuHasRVV)) {
    MergeRGBRow = MergeRGBRow_RVV;
  }
#endif

  for (y = 0; y < height; ++y) {
    // Merge a row of U and V into a row of RGB.
    MergeRGBRow(src_r, src_g, src_b, dst_rgb, width);
    src_r += src_stride_r;
    src_g += src_stride_g;
    src_b += src_stride_b;
    dst_rgb += dst_stride_rgb;
  }
}

LIBYUV_NOINLINE
static void SplitARGBPlaneAlpha(const uint8_t* src_argb,
                                int src_stride_argb,
                                uint8_t* dst_r,
                                int dst_stride_r,
                                uint8_t* dst_g,
                                int dst_stride_g,
                                uint8_t* dst_b,
                                int dst_stride_b,
                                uint8_t* dst_a,
                                int dst_stride_a,
                                int width,
                                int height) {
  int y;
  void (*SplitARGBRow)(const uint8_t* src_rgb, uint8_t* dst_r, uint8_t* dst_g,
                       uint8_t* dst_b, uint8_t* dst_a, int width) =
      SplitARGBRow_C;

  assert(height > 0);

  if (width <= 0 || height == 0) {
    return;
  }
  if (src_stride_argb == width * 4 && dst_stride_r == width &&
      dst_stride_g == width && dst_stride_b == width && dst_stride_a == width) {
    width *= height;
    height = 1;
    src_stride_argb = dst_stride_r = dst_stride_g = dst_stride_b =
        dst_stride_a = 0;
  }

#if defined(HAS_SPLITARGBROW_SSE2)
  if (TestCpuFlag(kCpuHasSSE2)) {
    SplitARGBRow = SplitARGBRow_Any_SSE2;
    if (IS_ALIGNED(width, 8)) {
      SplitARGBRow = SplitARGBRow_SSE2;
    }
  }
#endif
#if defined(HAS_SPLITARGBROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3)) {
    SplitARGBRow = SplitARGBRow_Any_SSSE3;
    if (IS_ALIGNED(width, 8)) {
      SplitARGBRow = SplitARGBRow_SSSE3;
    }
  }
#endif
#if defined(HAS_SPLITARGBROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    SplitARGBRow = SplitARGBRow_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      SplitARGBRow = SplitARGBRow_AVX2;
    }
  }
#endif
#if defined(HAS_SPLITARGBROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    SplitARGBRow = SplitARGBRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      SplitARGBRow = SplitARGBRow_NEON;
    }
  }
#endif
#if defined(HAS_SPLITARGBROW_RVV)
  if (TestCpuFlag(kCpuHasRVV)) {
    SplitARGBRow = SplitARGBRow_RVV;
  }
#endif

  for (y = 0; y < height; ++y) {
    SplitARGBRow(src_argb, dst_r, dst_g, dst_b, dst_a, width);
    dst_r += dst_stride_r;
    dst_g += dst_stride_g;
    dst_b += dst_stride_b;
    dst_a += dst_stride_a;
    src_argb += src_stride_argb;
  }
}

LIBYUV_NOINLINE
static void SplitARGBPlaneOpaque(const uint8_t* src_argb,
                                 int src_stride_argb,
                                 uint8_t* dst_r,
                                 int dst_stride_r,
                                 uint8_t* dst_g,
                                 int dst_stride_g,
                                 uint8_t* dst_b,
                                 int dst_stride_b,
                                 int width,
                                 int height) {
  int y;
  void (*SplitXRGBRow)(const uint8_t* src_rgb, uint8_t* dst_r, uint8_t* dst_g,
                       uint8_t* dst_b, int width) = SplitXRGBRow_C;
  assert(height > 0);

  if (width <= 0 || height == 0) {
    return;
  }
  if (src_stride_argb == width * 4 && dst_stride_r == width &&
      dst_stride_g == width && dst_stride_b == width) {
    width *= height;
    height = 1;
    src_stride_argb = dst_stride_r = dst_stride_g = dst_stride_b = 0;
  }

#if defined(HAS_SPLITXRGBROW_SSE2)
  if (TestCpuFlag(kCpuHasSSE2)) {
    SplitXRGBRow = SplitXRGBRow_Any_SSE2;
    if (IS_ALIGNED(width, 8)) {
      SplitXRGBRow = SplitXRGBRow_SSE2;
    }
  }
#endif
#if defined(HAS_SPLITXRGBROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3)) {
    SplitXRGBRow = SplitXRGBRow_Any_SSSE3;
    if (IS_ALIGNED(width, 8)) {
      SplitXRGBRow = SplitXRGBRow_SSSE3;
    }
  }
#endif
#if defined(HAS_SPLITXRGBROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    SplitXRGBRow = SplitXRGBRow_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      SplitXRGBRow = SplitXRGBRow_AVX2;
    }
  }
#endif
#if defined(HAS_SPLITXRGBROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    SplitXRGBRow = SplitXRGBRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      SplitXRGBRow = SplitXRGBRow_NEON;
    }
  }
#endif
#if defined(HAS_SPLITXRGBROW_RVV)
  if (TestCpuFlag(kCpuHasRVV)) {
    SplitXRGBRow = SplitXRGBRow_RVV;
  }
#endif

  for (y = 0; y < height; ++y) {
    SplitXRGBRow(src_argb, dst_r, dst_g, dst_b, width);
    dst_r += dst_stride_r;
    dst_g += dst_stride_g;
    dst_b += dst_stride_b;
    src_argb += src_stride_argb;
  }
}

LIBYUV_API
void SplitARGBPlane(const uint8_t* src_argb,
                    int src_stride_argb,
                    uint8_t* dst_r,
                    int dst_stride_r,
                    uint8_t* dst_g,
                    int dst_stride_g,
                    uint8_t* dst_b,
                    int dst_stride_b,
                    uint8_t* dst_a,
                    int dst_stride_a,
                    int width,
                    int height) {
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_r = dst_r + (height - 1) * dst_stride_r;
    dst_g = dst_g + (height - 1) * dst_stride_g;
    dst_b = dst_b + (height - 1) * dst_stride_b;
    dst_a = dst_a + (height - 1) * dst_stride_a;
    dst_stride_r = -dst_stride_r;
    dst_stride_g = -dst_stride_g;
    dst_stride_b = -dst_stride_b;
    dst_stride_a = -dst_stride_a;
  }

  if (dst_a == NULL) {
    SplitARGBPlaneOpaque(src_argb, src_stride_argb, dst_r, dst_stride_r, dst_g,
                         dst_stride_g, dst_b, dst_stride_b, width, height);
  } else {
    SplitARGBPlaneAlpha(src_argb, src_stride_argb, dst_r, dst_stride_r, dst_g,
                        dst_stride_g, dst_b, dst_stride_b, dst_a, dst_stride_a,
                        width, height);
  }
}

LIBYUV_NOINLINE
static void MergeARGBPlaneAlpha(const uint8_t* src_r,
                                int src_stride_r,
                                const uint8_t* src_g,
                                int src_stride_g,
                                const uint8_t* src_b,
                                int src_stride_b,
                                const uint8_t* src_a,
                                int src_stride_a,
                                uint8_t* dst_argb,
                                int dst_stride_argb,
                                int width,
                                int height) {
  int y;
  void (*MergeARGBRow)(const uint8_t* src_r, const uint8_t* src_g,
                       const uint8_t* src_b, const uint8_t* src_a,
                       uint8_t* dst_argb, int width) = MergeARGBRow_C;

  assert(height > 0);

  if (width <= 0 || height == 0) {
    return;
  }
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
      src_stride_a == width && dst_stride_argb == width * 4) {
    width *= height;
    height = 1;
    src_stride_r = src_stride_g = src_stride_b = src_stride_a =
        dst_stride_argb = 0;
  }
#if defined(HAS_MERGEARGBROW_SSE2)
  if (TestCpuFlag(kCpuHasSSE2)) {
    MergeARGBRow = MergeARGBRow_Any_SSE2;
    if (IS_ALIGNED(width, 8)) {
      MergeARGBRow = MergeARGBRow_SSE2;
    }
  }
#endif
#if defined(HAS_MERGEARGBROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    MergeARGBRow = MergeARGBRow_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      MergeARGBRow = MergeARGBRow_AVX2;
    }
  }
#endif
#if defined(HAS_MERGEARGBROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    MergeARGBRow = MergeARGBRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      MergeARGBRow = MergeARGBRow_NEON;
    }
  }
#endif
#if defined(HAS_MERGEARGBROW_RVV)
  if (TestCpuFlag(kCpuHasRVV)) {
    MergeARGBRow = MergeARGBRow_RVV;
  }
#endif

  for (y = 0; y < height; ++y) {
    MergeARGBRow(src_r, src_g, src_b, src_a, dst_argb, width);
    src_r += src_stride_r;
    src_g += src_stride_g;
    src_b += src_stride_b;
    src_a += src_stride_a;
    dst_argb += dst_stride_argb;
  }
}

LIBYUV_NOINLINE
static void MergeARGBPlaneOpaque(const uint8_t* src_r,
                                 int src_stride_r,
                                 const uint8_t* src_g,
                                 int src_stride_g,
                                 const uint8_t* src_b,
                                 int src_stride_b,
                                 uint8_t* dst_argb,
                                 int dst_stride_argb,
                                 int width,
                                 int height) {
  int y;
  void (*MergeXRGBRow)(const uint8_t* src_r, const uint8_t* src_g,
                       const uint8_t* src_b, uint8_t* dst_argb, int width) =
      MergeXRGBRow_C;

  assert(height > 0);

  if (width <= 0 || height == 0) {
    return;
  }
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
      dst_stride_argb == width * 4) {
    width *= height;
    height = 1;
    src_stride_r = src_stride_g = src_stride_b = dst_stride_argb = 0;
  }
#if defined(HAS_MERGEXRGBROW_SSE2)
  if (TestCpuFlag(kCpuHasSSE2)) {
    MergeXRGBRow = MergeXRGBRow_Any_SSE2;
    if (IS_ALIGNED(width, 8)) {
      MergeXRGBRow = MergeXRGBRow_SSE2;
    }
  }
#endif
#if defined(HAS_MERGEXRGBROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    MergeXRGBRow = MergeXRGBRow_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      MergeXRGBRow = MergeXRGBRow_AVX2;
    }
  }
#endif
#if defined(HAS_MERGEXRGBROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    MergeXRGBRow = MergeXRGBRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      MergeXRGBRow = MergeXRGBRow_NEON;
    }
  }
#endif
#if defined(HAS_MERGEXRGBROW_RVV)
  if (TestCpuFlag(kCpuHasRVV)) {
    MergeXRGBRow = MergeXRGBRow_RVV;
  }
#endif

  for (y = 0; y < height; ++y) {
    MergeXRGBRow(src_r, src_g, src_b, dst_argb, width);
    src_r += src_stride_r;
    src_g += src_stride_g;
    src_b += src_stride_b;
    dst_argb += dst_stride_argb;
  }
}

LIBYUV_API
void MergeARGBPlane(const uint8_t* src_r,
                    int src_stride_r,
                    const uint8_t* src_g,
                    int src_stride_g,
                    const uint8_t* src_b,
                    int src_stride_b,
                    const uint8_t* src_a,
                    int src_stride_a,
                    uint8_t* dst_argb,
                    int dst_stride_argb,
                    int width,
                    int height) {
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
    dst_stride_argb = -dst_stride_argb;
  }

  if (src_a == NULL) {
    MergeARGBPlaneOpaque(src_r, src_stride_r, src_g, src_stride_g, src_b,
                         src_stride_b, dst_argb, dst_stride_argb, width,
                         height);
  } else {
    MergeARGBPlaneAlpha(src_r, src_stride_r, src_g, src_stride_g, src_b,
                        src_stride_b, src_a, src_stride_a, dst_argb,
                        dst_stride_argb, width, height);
  }
}

// TODO(yuan): Support 2 bit alpha channel.
LIBYUV_API
void MergeXR30Plane(const uint16_t* src_r,
                    int src_stride_r,
                    const uint16_t* src_g,
                    int src_stride_g,
                    const uint16_t* src_b,
                    int src_stride_b,
                    uint8_t* dst_ar30,
                    int dst_stride_ar30,
                    int width,
                    int height,
                    int depth) {
  int y;
  void (*MergeXR30Row)(const uint16_t* src_r, const uint16_t* src_g,
                       const uint16_t* src_b, uint8_t* dst_ar30, int depth,
                       int width) = MergeXR30Row_C;

  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_ar30 = dst_ar30 + (height - 1) * dst_stride_ar30;
    dst_stride_ar30 = -dst_stride_ar30;
  }
  // Coalesce rows.
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
      dst_stride_ar30 == width * 4) {
    width *= height;
    height = 1;
    src_stride_r = src_stride_g = src_stride_b = dst_stride_ar30 = 0;
  }
#if defined(HAS_MERGEXR30ROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    MergeXR30Row = MergeXR30Row_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      MergeXR30Row = MergeXR30Row_AVX2;
    }
  }
#endif
#if defined(HAS_MERGEXR30ROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    if (depth == 10) {
      MergeXR30Row = MergeXR30Row_10_Any_NEON;
      if (IS_ALIGNED(width, 8)) {
        MergeXR30Row = MergeXR30Row_10_NEON;
      }
    } else {
      MergeXR30Row = MergeXR30Row_Any_NEON;
      if (IS_ALIGNED(width, 8)) {
        MergeXR30Row = MergeXR30Row_NEON;
      }
    }
  }
#endif

  for (y = 0; y < height; ++y) {
    MergeXR30Row(src_r, src_g, src_b, dst_ar30, depth, width);
    src_r += src_stride_r;
    src_g += src_stride_g;
    src_b += src_stride_b;
    dst_ar30 += dst_stride_ar30;
  }
}

LIBYUV_NOINLINE
static void MergeAR64PlaneAlpha(const uint16_t* src_r,
                                int src_stride_r,
                                const uint16_t* src_g,
                                int src_stride_g,
                                const uint16_t* src_b,
                                int src_stride_b,
                                const uint16_t* src_a,
                                int src_stride_a,
                                uint16_t* dst_ar64,
                                int dst_stride_ar64,
                                int width,
                                int height,
                                int depth) {
  int y;
  void (*MergeAR64Row)(const uint16_t* src_r, const uint16_t* src_g,
                       const uint16_t* src_b, const uint16_t* src_a,
                       uint16_t* dst_argb, int depth, int width) =
      MergeAR64Row_C;

  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
      src_stride_a == width && dst_stride_ar64 == width * 4) {
    width *= height;
    height = 1;
    src_stride_r = src_stride_g = src_stride_b = src_stride_a =
        dst_stride_ar64 = 0;
  }
#if defined(HAS_MERGEAR64ROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    MergeAR64Row = MergeAR64Row_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      MergeAR64Row = MergeAR64Row_AVX2;
    }
  }
#endif
#if defined(HAS_MERGEAR64ROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    MergeAR64Row = MergeAR64Row_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      MergeAR64Row = MergeAR64Row_NEON;
    }
  }
#endif

  for (y = 0; y < height; ++y) {
    MergeAR64Row(src_r, src_g, src_b, src_a, dst_ar64, depth, width);
    src_r += src_stride_r;
    src_g += src_stride_g;
    src_b += src_stride_b;
    src_a += src_stride_a;
    dst_ar64 += dst_stride_ar64;
  }
}

LIBYUV_NOINLINE
static void MergeAR64PlaneOpaque(const uint16_t* src_r,
                                 int src_stride_r,
                                 const uint16_t* src_g,
                                 int src_stride_g,
                                 const uint16_t* src_b,
                                 int src_stride_b,
                                 uint16_t* dst_ar64,
                                 int dst_stride_ar64,
                                 int width,
                                 int height,
                                 int depth) {
  int y;
  void (*MergeXR64Row)(const uint16_t* src_r, const uint16_t* src_g,
                       const uint16_t* src_b, uint16_t* dst_argb, int depth,
                       int width) = MergeXR64Row_C;

  // Coalesce rows.
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
      dst_stride_ar64 == width * 4) {
    width *= height;
    height = 1;
    src_stride_r = src_stride_g = src_stride_b = dst_stride_ar64 = 0;
  }
#if defined(HAS_MERGEXR64ROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    MergeXR64Row = MergeXR64Row_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      MergeXR64Row = MergeXR64Row_AVX2;
    }
  }
#endif
#if defined(HAS_MERGEXR64ROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    MergeXR64Row = MergeXR64Row_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      MergeXR64Row = MergeXR64Row_NEON;
    }
  }
#endif

  for (y = 0; y < height; ++y) {
    MergeXR64Row(src_r, src_g, src_b, dst_ar64, depth, width);
    src_r += src_stride_r;
    src_g += src_stride_g;
    src_b += src_stride_b;
    dst_ar64 += dst_stride_ar64;
  }
}

LIBYUV_API
void MergeAR64Plane(const uint16_t* src_r,
                    int src_stride_r,
                    const uint16_t* src_g,
                    int src_stride_g,
                    const uint16_t* src_b,
                    int src_stride_b,
                    const uint16_t* src_a,
                    int src_stride_a,
                    uint16_t* dst_ar64,
                    int dst_stride_ar64,
                    int width,
                    int height,
                    int depth) {
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_ar64 = dst_ar64 + (height - 1) * dst_stride_ar64;
    dst_stride_ar64 = -dst_stride_ar64;
  }

  if (src_a == NULL) {
    MergeAR64PlaneOpaque(src_r, src_stride_r, src_g, src_stride_g, src_b,
                         src_stride_b, dst_ar64, dst_stride_ar64, width, height,
                         depth);
  } else {
    MergeAR64PlaneAlpha(src_r, src_stride_r, src_g, src_stride_g, src_b,
                        src_stride_b, src_a, src_stride_a, dst_ar64,
                        dst_stride_ar64, width, height, depth);
  }
}

LIBYUV_NOINLINE
static void MergeARGB16To8PlaneAlpha(const uint16_t* src_r,
                                     int src_stride_r,
                                     const uint16_t* src_g,
                                     int src_stride_g,
                                     const uint16_t* src_b,
                                     int src_stride_b,
                                     const uint16_t* src_a,
                                     int src_stride_a,
                                     uint8_t* dst_argb,
                                     int dst_stride_argb,
                                     int width,
                                     int height,
                                     int depth) {
  int y;
  void (*MergeARGB16To8Row)(const uint16_t* src_r, const uint16_t* src_g,
                            const uint16_t* src_b, const uint16_t* src_a,
                            uint8_t* dst_argb, int depth, int width) =
      MergeARGB16To8Row_C;

  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
      src_stride_a == width && dst_stride_argb == width * 4) {
    width *= height;
    height = 1;
    src_stride_r = src_stride_g = src_stride_b = src_stride_a =
        dst_stride_argb = 0;
  }
#if defined(HAS_MERGEARGB16TO8ROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    MergeARGB16To8Row = MergeARGB16To8Row_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      MergeARGB16To8Row = MergeARGB16To8Row_AVX2;
    }
  }
#endif
#if defined(HAS_MERGEARGB16TO8ROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    MergeARGB16To8Row = MergeARGB16To8Row_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      MergeARGB16To8Row = MergeARGB16To8Row_NEON;
    }
  }
#endif

  for (y = 0; y < height; ++y) {
    MergeARGB16To8Row(src_r, src_g, src_b, src_a, dst_argb, depth, width);
    src_r += src_stride_r;
    src_g += src_stride_g;
    src_b += src_stride_b;
    src_a += src_stride_a;
    dst_argb += dst_stride_argb;
  }
}

LIBYUV_NOINLINE
static void MergeARGB16To8PlaneOpaque(const uint16_t* src_r,
                                      int src_stride_r,
                                      const uint16_t* src_g,
                                      int src_stride_g,
                                      const uint16_t* src_b,
                                      int src_stride_b,
                                      uint8_t* dst_argb,
                                      int dst_stride_argb,
                                      int width,
                                      int height,
                                      int depth) {
  int y;
  void (*MergeXRGB16To8Row)(const uint16_t* src_r, const uint16_t* src_g,
                            const uint16_t* src_b, uint8_t* dst_argb, int depth,
                            int width) = MergeXRGB16To8Row_C;

  // Coalesce rows.
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
      dst_stride_argb == width * 4) {
    width *= height;
    height = 1;
    src_stride_r = src_stride_g = src_stride_b = dst_stride_argb = 0;
  }
#if defined(HAS_MERGEXRGB16TO8ROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    MergeXRGB16To8Row = MergeXRGB16To8Row_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      MergeXRGB16To8Row = MergeXRGB16To8Row_AVX2;
    }
  }
#endif
#if defined(HAS_MERGEXRGB16TO8ROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    MergeXRGB16To8Row = MergeXRGB16To8Row_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      MergeXRGB16To8Row = MergeXRGB16To8Row_NEON;
    }
  }
#endif

  for (y = 0; y < height; ++y) {
    MergeXRGB16To8Row(src_r, src_g, src_b, dst_argb, depth, width);
    src_r += src_stride_r;
    src_g += src_stride_g;
    src_b += src_stride_b;
    dst_argb += dst_stride_argb;
  }
}

LIBYUV_API
void MergeARGB16To8Plane(const uint16_t* src_r,
                         int src_stride_r,
                         const uint16_t* src_g,
                         int src_stride_g,
                         const uint16_t* src_b,
                         int src_stride_b,
                         const uint16_t* src_a,
                         int src_stride_a,
                         uint8_t* dst_argb,
                         int dst_stride_argb,
                         int width,
                         int height,
                         int depth) {
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
    dst_stride_argb = -dst_stride_argb;
  }

  if (src_a == NULL) {
    MergeARGB16To8PlaneOpaque(src_r, src_stride_r, src_g, src_stride_g, src_b,
                              src_stride_b, dst_argb, dst_stride_argb, width,
                              height, depth);
  } else {
    MergeARGB16To8PlaneAlpha(src_r, src_stride_r, src_g, src_stride_g, src_b,
                             src_stride_b, src_a, src_stride_a, dst_argb,
                             dst_stride_argb, width, height, depth);
  }
}

// Convert YUY2 to I422.
LIBYUV_API
int YUY2ToI422(const uint8_t* src_yuy2,
               int src_stride_yuy2,
               uint8_t* dst_y,
               int dst_stride_y,
               uint8_t* dst_u,
               int dst_stride_u,
               uint8_t* dst_v,
               int dst_stride_v,
               int width,
               int height) {
  int y;
  void (*YUY2ToUV422Row)(const uint8_t* src_yuy2, uint8_t* dst_u,
                         uint8_t* dst_v, int width) = YUY2ToUV422Row_C;
  void (*YUY2ToYRow)(const uint8_t* src_yuy2, uint8_t* dst_y, int width) =
      YUY2ToYRow_C;
  if (!src_yuy2 || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0) {
    return -1;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2;
    src_stride_yuy2 = -src_stride_yuy2;
  }
  // Coalesce rows.
  if (src_stride_yuy2 == width * 2 && dst_stride_y == width &&
      dst_stride_u * 2 == width && dst_stride_v * 2 == width &&
      width * height <= 32768) {
    width *= height;
    height = 1;
    src_stride_yuy2 = dst_stride_y = dst_stride_u = dst_stride_v = 0;
  }
#if defined(HAS_YUY2TOYROW_SSE2)
  if (TestCpuFlag(kCpuHasSSE2)) {
    YUY2ToUV422Row = YUY2ToUV422Row_Any_SSE2;
    YUY2ToYRow = YUY2ToYRow_Any_SSE2;
    if (IS_ALIGNED(width, 16)) {
      YUY2ToUV422Row = YUY2ToUV422Row_SSE2;
      YUY2ToYRow = YUY2ToYRow_SSE2;
    }
  }
#endif
#if defined(HAS_YUY2TOYROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    YUY2ToUV422Row = YUY2ToUV422Row_Any_AVX2;
    YUY2ToYRow = YUY2ToYRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
      YUY2ToUV422Row = YUY2ToUV422Row_AVX2;
      YUY2ToYRow = YUY2ToYRow_AVX2;
    }
  }
#endif
#if defined(HAS_YUY2TOYROW_NEON)
  if (TestCpuFlag(kCpuHasNEON)) {
    YUY2ToYRow = YUY2ToYRow_Any_NEON;
    YUY2ToUV422Row = YUY2ToUV422Row_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      YUY2ToYRow = YUY2ToYRow_NEON;
      YUY2ToUV422Row = YUY2ToUV422Row_NEON;
    }
  }
#endif
#if defined(HAS_YUY2TOYROW_MSA) && defined(HAS_YUY2TOUV422ROW_MSA)
  if (TestCpuFlag(kCpuHasMSA)) {
    YUY2ToYRow = YUY2ToYRow_Any_MSA;
    YUY2ToUV422Row = YUY2ToUV422Row_Any_MSA;
    if (IS_ALIGNED(width, 32)) {
      YUY2ToYRow = YUY2ToYRow_MSA;
      YUY2ToUV422Row = YUY2ToUV422Row_MSA;
    }
  }
#endif
#if defined(HAS_YUY2TOYROW_LSX) && defined(HAS_YUY2TOUV422ROW_LSX)
  if (TestCpuFlag(kCpuHasLSX)) {
    YUY2ToYRow = YUY2ToYRow_Any_LSX;
    YUY2ToUV422Row = YUY2ToUV422Row_Any_LSX;
    if (IS_ALIGNED(width, 16)) {
      YUY2ToYRow = YUY2ToYRow_LSX;
      YUY2ToUV422Row = YUY2ToUV422Row_LSX;
    }
  }
#endif
#if defined(HAS_YUY2TOYROW_LASX) && defined(HAS_YUY2TOUV422ROW_LASX)
  if (TestCpuFlag(kCpuHasLASX)) {
    YUY2ToYRow = YUY2ToYRow_Any_LASX;
    YUY2ToUV422Row = YUY2ToUV422Row_Any_LASX;
    if (IS_ALIGNED(width, 32)) {
      YUY2ToYRow = YUY2ToYRow_LASX;
      YUY2ToUV422Row = YUY2ToUV422Row_LASX;
    }
  }
#endif

  for (y = 0; y < height; ++y) {
    YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width);
    YUY2ToYRow(src_yuy2, dst_y, width);
    src_yuy2 += src_stride_yuy2;
    dst_y += dst_stride_y;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  return 0;
}

// Convert UYVY to I422.
LIBYUV_API
int UYVYToI422(const uint8_t* src_uyvy,
               int src_stride_uyvy,
               uint8_t* dst_y,
               int dst_stride_y,
               uint8_t* dst_u,
               int dst_stride_u,
               uint8_t* dst_v,
               int dst_stride_v,
               int width,
               int height) {
  int y;
  void (*UYVYToUV422Row)(const uint8_t* src_uyvy, uint8_t* dst_u,
                         uint8_t* dst_v, int width) = UYVYToUV422Row_C;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=92 G=92

¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.