Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++ #include "qvideoframeconversionhelper_p.h"
++ #include "qrgb.h"
++
++ #include <mutex>
++
++ QT_BEGIN_NAMESPACE
++
++ #define CLAMP(n) (n > 255 ? 255 : (n < 0 ? 0 : n))
++
++ #define EXPAND_UV(u, v) \
++ int uu = u - 128; \
++ int vv = v - 128; \
++ int rv = 409 * vv + 128; \
++ int guv = 100 * uu + 208 * vv + 128; \
++ int bu = 516 * uu + 128; \
++
++ static inline quint32 qYUVToARGB32(int y, int rv, int guv, int bu, int a = 0xff)
++ {
++ int yy = (y - 16) * 298;
++ return (a << 24)
++ | CLAMP((yy + rv) >> 8) << 16
++ | CLAMP((yy - guv) >> 8) << 8
++ | CLAMP((yy + bu) >> 8);
++ }
++
++ static inline void planarYUV420_to_ARGB32(const uchar *y, int yStride,
++ const uchar *u, int uStride,
++ const uchar *v, int vStride,
++ int uvPixelStride,
++ quint32 *rgb,
++ int width, int height)
++ {
++ height &= ~1;
++
++ for (int j = 0; j + 1 < height; j += 2) {
++ const uchar *lineY0 = y;
++ const uchar *lineY1 = y + yStride;
++ const uchar *lineU = u;
++ const uchar *lineV = v;
++
++ quint32 *rgb0 = rgb;
++ quint32 *rgb1 = rgb + width;
++ for (int i = 0; i + 1 < width; i += 2) {
++ EXPAND_UV(*lineU, *lineV);
++ lineU += uvPixelStride;
++ lineV += uvPixelStride;
++
++ rgb0[i] = qYUVToARGB32(*lineY0++, rv, guv, bu);
++ rgb0[i + 1] = qYUVToARGB32(*lineY0++, rv, guv, bu);
++ rgb1[i] = qYUVToARGB32(*lineY1++, rv, guv, bu);
++ rgb1[i + 1] = qYUVToARGB32(*lineY1++, rv, guv, bu);
++ }
++
++ y += yStride << 1; // stride * 2
++ u += uStride;
++ v += vStride;
++ rgb += width << 1; // width * 2
++ }
++ }
++
++ static inline void planarYUV422_to_ARGB32(const uchar *y, int yStride,
++ const uchar *u, int uStride,
++ const uchar *v, int vStride,
++ int uvPixelStride,
++ quint32 *rgb,
++ int width, int height)
++ {
++ for (int j = 0; j < height; ++j) {
++ const uchar *lineY0 = y;
++ const uchar *lineU = u;
++ const uchar *lineV = v;
++
++ for (int i = 0; i + 1 < width; i += 2) {
++ EXPAND_UV(*lineU, *lineV);
++ lineU += uvPixelStride;
++ lineV += uvPixelStride;
++
++ rgb[i] = qYUVToARGB32(*lineY0++, rv, guv, bu);
++ rgb[i+1] = qYUVToARGB32(*lineY0++, rv, guv, bu);
++ }
++
++ y += yStride; // stride * 2
++ u += uStride;
++ v += vStride;
++ rgb += width;
++ }
++ }
++
++ static void QT_FASTCALL qt_convert_YUV420P_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_TRIPLANAR(frame)
++ planarYUV420_to_ARGB32(plane1, plane1Stride,
++ plane2, plane2Stride,
++ plane3, plane3Stride,
++ 1,
++ reinterpret_cast<quint32*>(output),
++ width, height);
++ }
++
++ static void QT_FASTCALL qt_convert_YUV422P_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_TRIPLANAR(frame)
++ planarYUV422_to_ARGB32(plane1, plane1Stride,
++ plane2, plane2Stride,
++ plane3, plane3Stride,
++ 1,
++ reinterpret_cast<quint32*>(output),
++ width, height);
++ }
++
++
++ static void QT_FASTCALL qt_convert_YV12_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_TRIPLANAR(frame)
++ planarYUV420_to_ARGB32(plane1, plane1Stride,
++ plane3, plane3Stride,
++ plane2, plane2Stride,
++ 1,
++ reinterpret_cast<quint32*>(output),
++ width, height);
++ }
++
++ static void QT_FASTCALL qt_convert_AYUV_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_PACKED(frame)
++ MERGE_LOOPS(width, height, stride, 4)
++
++ quint32 *rgb = reinterpret_cast<quint32*>(output);
++
++ for (int i = 0; i < height; ++i) {
++ const uchar *lineSrc = src;
++
++ for (int j = 0; j < width; ++j) {
++ int a = *lineSrc++;
++ int y = *lineSrc++;
++ int u = *lineSrc++;
++ int v = *lineSrc++;
++
++ EXPAND_UV(u, v);
++
++ *rgb++ = qPremultiply(qYUVToARGB32(y, rv, guv, bu, a));
++ }
++
++ src += stride;
++ }
++ }
++
++ static void QT_FASTCALL qt_convert_AYUV_Premultiplied_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_PACKED(frame)
++ MERGE_LOOPS(width, height, stride, 4)
++
++ quint32 *rgb = reinterpret_cast<quint32*>(output);
++
++ for (int i = 0; i < height; ++i) {
++ const uchar *lineSrc = src;
++
++ for (int j = 0; j < width; ++j) {
++ int a = *lineSrc++;
++ int y = *lineSrc++;
++ int u = *lineSrc++;
++ int v = *lineSrc++;
++
++ EXPAND_UV(u, v);
++
++ *rgb++ = qYUVToARGB32(y, rv, guv, bu, a);
++ }
++
++ src += stride;
++ }
++ }
++
++ static void QT_FASTCALL qt_convert_UYVY_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_PACKED(frame)
++ MERGE_LOOPS(width, height, stride, 2)
++
++ quint32 *rgb = reinterpret_cast<quint32*>(output);
++
++ for (int i = 0; i < height; ++i) {
++ const uchar *lineSrc = src;
++ for (int j = 0; j + 1 < width; j += 2) {
++ int u = *lineSrc++;
++ int y0 = *lineSrc++;
++ int v = *lineSrc++;
++ int y1 = *lineSrc++;
++
++ EXPAND_UV(u, v);
++
++ rgb[j] = qYUVToARGB32(y0, rv, guv, bu);
++ rgb[j+1] = qYUVToARGB32(y1, rv, guv, bu);
++ }
++
++ src += stride;
++ rgb += width;
++ }
++ }
++
++ static void QT_FASTCALL qt_convert_YUYV_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_PACKED(frame)
++ MERGE_LOOPS(width, height, stride, 2)
++
++ quint32 *rgb = reinterpret_cast<quint32*>(output);
++
++ for (int i = 0; i < height; ++i) {
++ const uchar *lineSrc = src;
++ for (int j = 0; j + 1 < width; j += 2) {
++ int y0 = *lineSrc++;
++ int u = *lineSrc++;
++ int y1 = *lineSrc++;
++ int v = *lineSrc++;
++
++ EXPAND_UV(u, v);
++
++ rgb[j] = qYUVToARGB32(y0, rv, guv, bu);
++ rgb[j+1] = qYUVToARGB32(y1, rv, guv, bu);
++ }
++
++ src += stride;
++ rgb += width;
++ }
++ }
++
++ static void QT_FASTCALL qt_convert_NV12_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_BIPLANAR(frame)
++ planarYUV420_to_ARGB32(plane1, plane1Stride,
++ plane2, plane2Stride,
++ plane2 + 1, plane2Stride,
++ 2,
++ reinterpret_cast<quint32*>(output),
++ width, height);
++ }
++
++ static void QT_FASTCALL qt_convert_NV21_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_BIPLANAR(frame)
++ planarYUV420_to_ARGB32(plane1, plane1Stride,
++ plane2 + 1, plane2Stride,
++ plane2, plane2Stride,
++ 2,
++ reinterpret_cast<quint32*>(output),
++ width, height);
++ }
++
++ static void QT_FASTCALL qt_convert_IMC1_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_TRIPLANAR(frame)
++ Q_ASSERT(plane1Stride == plane2Stride);
++ Q_ASSERT(plane1Stride == plane3Stride);
++
++ planarYUV420_to_ARGB32(plane1, plane1Stride,
++ plane3, plane3Stride,
++ plane2, plane2Stride,
++ 1,
++ reinterpret_cast<quint32*>(output),
++ width, height);
++ }
++
++ static void QT_FASTCALL qt_convert_IMC2_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_BIPLANAR(frame)
++ Q_ASSERT(plane1Stride == plane2Stride);
++
++ planarYUV420_to_ARGB32(plane1, plane1Stride,
++ plane2 + (plane1Stride >> 1), plane1Stride,
++ plane2, plane1Stride,
++ 1,
++ reinterpret_cast<quint32*>(output),
++ width, height);
++ }
++
++ static void QT_FASTCALL qt_convert_IMC3_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_TRIPLANAR(frame)
++ Q_ASSERT(plane1Stride == plane2Stride);
++ Q_ASSERT(plane1Stride == plane3Stride);
++
++ planarYUV420_to_ARGB32(plane1, plane1Stride,
++ plane2, plane2Stride,
++ plane3, plane3Stride,
++ 1,
++ reinterpret_cast<quint32*>(output),
++ width, height);
++ }
++
++ static void QT_FASTCALL qt_convert_IMC4_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_BIPLANAR(frame)
++ Q_ASSERT(plane1Stride == plane2Stride);
++
++ planarYUV420_to_ARGB32(plane1, plane1Stride,
++ plane2, plane1Stride,
++ plane2 + (plane1Stride >> 1), plane1Stride,
++ 1,
++ reinterpret_cast<quint32*>(output),
++ width, height);
++ }
++
++
++ template<typename Pixel>
++ static void QT_FASTCALL qt_convert_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_PACKED(frame)
++ MERGE_LOOPS(width, height, stride, 4)
++
++ quint32 *argb = reinterpret_cast<quint32*>(output);
++
++ for (int y = 0; y < height; ++y) {
++ const Pixel *data = reinterpret_cast<const Pixel *>(src);
++
++ int x = 0;
++ for (; x < width - 3; x += 4) {
++ // Copy 4 pixels onto the stack in one go. This significantly increases performance
++ // in the case where the mapped memory is uncached (because it's a framebuffer)
++ Pixel p[4];
++ memcpy(p, data, 4*sizeof(Pixel));
++ *argb++ = qPremultiply(p[0].convert());
++ *argb++ = qPremultiply(p[1].convert());
++ *argb++ = qPremultiply(p[2].convert());
++ *argb++ = qPremultiply(p[3].convert());
++ data += 4;
++ }
++
++ // leftovers
++ for (; x < width; ++x) {
++ *argb++ = qPremultiply(data->convert());
++ ++data;
++ }
++
++ src += stride;
++ }
++ }
++
++ template<typename Pixel>
++ static void QT_FASTCALL qt_convert_premultiplied_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_PACKED(frame)
++ MERGE_LOOPS(width, height, stride, 4)
++
++ quint32 *argb = reinterpret_cast<quint32*>(output);
++
++ for (int y = 0; y < height; ++y) {
++ const Pixel *data = reinterpret_cast<const Pixel *>(src);
++
++ int x = 0;
++ for (; x < width - 3; x += 4) {
++ // Copy 4 pixels onto the stack in one go. This significantly increases performance
++ // in the case where the mapped memory is uncached (because it's a framebuffer)
++ Pixel p[4];
++ memcpy(p, data, 4*sizeof(Pixel));
++ *argb++ = p[0].convert();
++ *argb++ = p[1].convert();
++ *argb++ = p[2].convert();
++ *argb++ = p[3].convert();
++ data += 4;
++ }
++
++ // leftovers
++ for (; x < width; ++x) {
++ *argb++ = data->convert();
++ ++data;
++ }
++
++ src += stride;
++ }
++ }
++
++ static inline void planarYUV420_16bit_to_ARGB32(const uchar *y, int yStride,
++ const uchar *u, int uStride,
++ const uchar *v, int vStride,
++ int uvPixelStride,
++ quint32 *rgb,
++ int width, int height)
++ {
++ height &= ~1;
++
++ for (int j = 0; j + 1 < height; j += 2) {
++ const uchar *lineY0 = y;
++ const uchar *lineY1 = y + yStride;
++ const uchar *lineU = u;
++ const uchar *lineV = v;
++
++ quint32 *rgb0 = rgb;
++ quint32 *rgb1 = rgb + width;
++
++ for (int i = 0; i + 1 < width; i += 2) {
++ EXPAND_UV(*lineU, *lineV);
++ lineU += uvPixelStride;
++ lineV += uvPixelStride;
++
++ *rgb0++ = qYUVToARGB32(*lineY0, rv, guv, bu);
++ lineY0 += 2;
++ *rgb0++ = qYUVToARGB32(*lineY0, rv, guv, bu);
++ lineY0 += 2;
++ *rgb1++ = qYUVToARGB32(*lineY1, rv, guv, bu);
++ lineY1 += 2;
++ *rgb1++ = qYUVToARGB32(*lineY1, rv, guv, bu);
++ lineY1 += 2;
++ }
++
++ y += yStride << 1; // stride * 2
++ u += uStride;
++ v += vStride;
++ rgb += width * 2;
++ }
++ }
++
++
++ static void QT_FASTCALL qt_convert_P016_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_BIPLANAR(frame)
++ planarYUV420_16bit_to_ARGB32(plane1 + 1, plane1Stride,
++ plane2 + 1, plane2Stride,
++ plane2 + 3, plane2Stride,
++ 4,
++ reinterpret_cast<quint32*>(output),
++ width, height);
++
++ }
++
++ template <typename Y>
++ static void QT_FASTCALL qt_convert_Y_to_ARGB32(const QVideoFrame &frame, uchar *output)
++ {
++ FETCH_INFO_PACKED(frame)
++ MERGE_LOOPS(width, height, stride, (int)sizeof(Y))
++ quint32 *argb = reinterpret_cast<quint32*>(output);
++
++ using Pixel = YPixel<Y>;
++
++ for (int y = 0; y < height; ++y) {
++ const Pixel *pixel = reinterpret_cast<const Pixel *>(src);
++
++ int x = 0;
++ for (; x < width - 3; x += 4) {
++ *argb++ = pixel->convert();
++ ++pixel;
++ *argb++ = pixel->convert();
++ ++pixel;
++ *argb++ = pixel->convert();
++ ++pixel;
++ *argb++ = pixel->convert();
++ ++pixel;
++ }
++
++ // leftovers
++ for (; x < width; ++x) {
++ *argb++ = pixel->convert();
++ ++pixel;
++ }
++
++ src += stride;
++ }
++ MERGE_LOOPS(width, height, stride, 1)
++ }
++
++ template<typename Pixel>
++ static void QT_FASTCALL qt_copy_pixels_with_mask(Pixel *dst, const Pixel *src, size_t size,
++ Pixel mask)
++ {
++ for (size_t x = 0; x < size; ++x)
++ dst[x] = src[x] | mask;
++ }
++
++ static VideoFrameConvertFunc qConvertFuncs[QVideoFrameFormat::NPixelFormats] = {
++ /* Format_Invalid */ nullptr, // Not needed
++ /* Format_ARGB8888 */ qt_convert_to_ARGB32<ARGB8888>,
++ /* Format_ARGB8888_Premultiplied */ qt_convert_premultiplied_to_ARGB32<ARGB8888>,
++ /* Format_XRGB8888 */ qt_convert_premultiplied_to_ARGB32<XRGB8888>,
++ /* Format_BGRA8888 */ qt_convert_to_ARGB32<BGRA8888>,
++ /* Format_BGRA8888_Premultiplied */ qt_convert_premultiplied_to_ARGB32<BGRA8888>,
++ /* Format_BGRX8888 */ qt_convert_premultiplied_to_ARGB32<BGRX8888>,
++ /* Format_ABGR8888 */ qt_convert_to_ARGB32<ABGR8888>,
++ /* Format_XBGR8888 */ qt_convert_premultiplied_to_ARGB32<XBGR8888>,
++ /* Format_RGBA8888 */ qt_convert_to_ARGB32<RGBA8888>,
++ /* Format_RGBX8888 */ qt_convert_premultiplied_to_ARGB32<RGBX8888>,
++ /* Format_AYUV */ qt_convert_AYUV_to_ARGB32,
++ /* Format_AYUV_Premultiplied */ qt_convert_AYUV_Premultiplied_to_ARGB32,
++ /* Format_YUV420P */ qt_convert_YUV420P_to_ARGB32,
++ /* Format_YUV422P */ qt_convert_YUV422P_to_ARGB32,
++ /* Format_YV12 */ qt_convert_YV12_to_ARGB32,
++ /* Format_UYVY */ qt_convert_UYVY_to_ARGB32,
++ /* Format_YUYV */ qt_convert_YUYV_to_ARGB32,
++ /* Format_NV12 */ qt_convert_NV12_to_ARGB32,
++ /* Format_NV21 */ qt_convert_NV21_to_ARGB32,
++ /* Format_IMC1 */ qt_convert_IMC1_to_ARGB32,
++ /* Format_IMC2 */ qt_convert_IMC2_to_ARGB32,
++ /* Format_IMC3 */ qt_convert_IMC3_to_ARGB32,
++ /* Format_IMC4 */ qt_convert_IMC4_to_ARGB32,
++ /* Format_Y8 */ qt_convert_Y_to_ARGB32<uchar>,
++ /* Format_Y16 */ qt_convert_Y_to_ARGB32<ushort>,
++ /* Format_P010 */ qt_convert_P016_to_ARGB32,
++ /* Format_P016 */ qt_convert_P016_to_ARGB32,
++ /* Format_Jpeg */ nullptr, // Not needed
++ };
++
++ static PixelsCopyFunc qPixelsCopyFunc = qt_copy_pixels_with_mask<uint32_t>;
++
++ static std::once_flag InitFuncsAsmFlag;
++
++ static void qInitFuncsAsm()
++ {
++ #ifdef QT_COMPILER_SUPPORTS_SSE2
++ extern void QT_FASTCALL qt_convert_ARGB8888_to_ARGB32_sse2(const QVideoFrame &frame, uchar *output);
++ extern void QT_FASTCALL qt_convert_ABGR8888_to_ARGB32_sse2(const QVideoFrame &frame, uchar *output);
++ extern void QT_FASTCALL qt_convert_RGBA8888_to_ARGB32_sse2(const QVideoFrame &frame, uchar *output);
++ extern void QT_FASTCALL qt_convert_BGRA8888_to_ARGB32_sse2(const QVideoFrame &frame, uchar *output);
++ extern void QT_FASTCALL qt_copy_pixels_with_mask_sse2(uint32_t * dst, const uint32_t *src, size_t size, uint32_t mask);
++
++ if (qCpuHasFeature(SSE2)){
++ qConvertFuncs[QVideoFrameFormat::Format_ARGB8888] = qt_convert_ARGB8888_to_ARGB32_sse2;
++ qConvertFuncs[QVideoFrameFormat::Format_ARGB8888_Premultiplied] = qt_convert_ARGB8888_to_ARGB32_sse2;
++ qConvertFuncs[QVideoFrameFormat::Format_XRGB8888] = qt_convert_ARGB8888_to_ARGB32_sse2;
++ qConvertFuncs[QVideoFrameFormat::Format_BGRA8888] = qt_convert_BGRA8888_to_ARGB32_sse2;
++ qConvertFuncs[QVideoFrameFormat::Format_BGRA8888_Premultiplied] = qt_convert_BGRA8888_to_ARGB32_sse2;
++ qConvertFuncs[QVideoFrameFormat::Format_BGRX8888] = qt_convert_BGRA8888_to_ARGB32_sse2;
++ qConvertFuncs[QVideoFrameFormat::Format_ABGR8888] = qt_convert_ABGR8888_to_ARGB32_sse2;
++ qConvertFuncs[QVideoFrameFormat::Format_XBGR8888] = qt_convert_ABGR8888_to_ARGB32_sse2;
++ qConvertFuncs[QVideoFrameFormat::Format_RGBA8888] = qt_convert_RGBA8888_to_ARGB32_sse2;
++ qConvertFuncs[QVideoFrameFormat::Format_RGBX8888] = qt_convert_RGBA8888_to_ARGB32_sse2;
++
++ qPixelsCopyFunc = qt_copy_pixels_with_mask_sse2;
++ }
++ #endif
++ #ifdef QT_COMPILER_SUPPORTS_SSSE3
++ extern void QT_FASTCALL qt_convert_ARGB8888_to_ARGB32_ssse3(const QVideoFrame &frame, uchar *output);
++ extern void QT_FASTCALL qt_convert_ABGR8888_to_ARGB32_ssse3(const QVideoFrame &frame, uchar *output);
++ extern void QT_FASTCALL qt_convert_RGBA8888_to_ARGB32_ssse3(const QVideoFrame &frame, uchar *output);
++ extern void QT_FASTCALL qt_convert_BGRA8888_to_ARGB32_ssse3(const QVideoFrame &frame, uchar *output);
++ if (qCpuHasFeature(SSSE3)){
++ qConvertFuncs[QVideoFrameFormat::Format_ARGB8888] = qt_convert_ARGB8888_to_ARGB32_ssse3;
++ qConvertFuncs[QVideoFrameFormat::Format_ARGB8888_Premultiplied] = qt_convert_ARGB8888_to_ARGB32_ssse3;
++ qConvertFuncs[QVideoFrameFormat::Format_XRGB8888] = qt_convert_ARGB8888_to_ARGB32_ssse3;
++ qConvertFuncs[QVideoFrameFormat::Format_BGRA8888] = qt_convert_BGRA8888_to_ARGB32_ssse3;
++ qConvertFuncs[QVideoFrameFormat::Format_BGRA8888_Premultiplied] = qt_convert_BGRA8888_to_ARGB32_ssse3;
++ qConvertFuncs[QVideoFrameFormat::Format_BGRX8888] = qt_convert_BGRA8888_to_ARGB32_ssse3;
++ qConvertFuncs[QVideoFrameFormat::Format_ABGR8888] = qt_convert_ABGR8888_to_ARGB32_ssse3;
++ qConvertFuncs[QVideoFrameFormat::Format_XBGR8888] = qt_convert_ABGR8888_to_ARGB32_ssse3;
++ qConvertFuncs[QVideoFrameFormat::Format_RGBA8888] = qt_convert_RGBA8888_to_ARGB32_ssse3;
++ qConvertFuncs[QVideoFrameFormat::Format_RGBX8888] = qt_convert_RGBA8888_to_ARGB32_ssse3;
++ }
++ #endif
++ #ifdef QT_COMPILER_SUPPORTS_AVX2
++ extern void QT_FASTCALL qt_convert_ARGB8888_to_ARGB32_avx2(const QVideoFrame &frame, uchar *output);
++ extern void QT_FASTCALL qt_convert_ABGR8888_to_ARGB32_avx2(const QVideoFrame &frame, uchar *output);
++ extern void QT_FASTCALL qt_convert_RGBA8888_to_ARGB32_avx2(const QVideoFrame &frame, uchar *output);
++ extern void QT_FASTCALL qt_convert_BGRA8888_to_ARGB32_avx2(const QVideoFrame &frame, uchar *output);
++ extern void QT_FASTCALL qt_copy_pixels_with_mask_avx2(uint32_t * dst, const uint32_t *src, size_t size, uint32_t mask);
++ if (qCpuHasFeature(AVX2)){
++ qConvertFuncs[QVideoFrameFormat::Format_ARGB8888] = qt_convert_ARGB8888_to_ARGB32_avx2;
++ qConvertFuncs[QVideoFrameFormat::Format_ARGB8888_Premultiplied] = qt_convert_ARGB8888_to_ARGB32_avx2;
++ qConvertFuncs[QVideoFrameFormat::Format_XRGB8888] = qt_convert_ARGB8888_to_ARGB32_avx2;
++ qConvertFuncs[QVideoFrameFormat::Format_BGRA8888] = qt_convert_BGRA8888_to_ARGB32_avx2;
++ qConvertFuncs[QVideoFrameFormat::Format_BGRA8888_Premultiplied] = qt_convert_BGRA8888_to_ARGB32_avx2;
++ qConvertFuncs[QVideoFrameFormat::Format_BGRX8888] = qt_convert_BGRA8888_to_ARGB32_avx2;
++ qConvertFuncs[QVideoFrameFormat::Format_ABGR8888] = qt_convert_ABGR8888_to_ARGB32_avx2;
++ qConvertFuncs[QVideoFrameFormat::Format_XBGR8888] = qt_convert_ABGR8888_to_ARGB32_avx2;
++ qConvertFuncs[QVideoFrameFormat::Format_RGBA8888] = qt_convert_RGBA8888_to_ARGB32_avx2;
++ qConvertFuncs[QVideoFrameFormat::Format_RGBX8888] = qt_convert_RGBA8888_to_ARGB32_avx2;
++
++ qPixelsCopyFunc = qt_copy_pixels_with_mask_avx2;
++ }
++ #endif
++ }
++
++ VideoFrameConvertFunc qConverterForFormat(QVideoFrameFormat::PixelFormat format)
++ {
++ std::call_once(InitFuncsAsmFlag, &qInitFuncsAsm);
++
++ VideoFrameConvertFunc convert = qConvertFuncs[format];
++ return convert;
++ }
++
++ void Q_MULTIMEDIA_EXPORT qCopyPixelsWithAlphaMask(uint32_t *dst,
++ const uint32_t *src,
++ size_t pixCount,
++ QVideoFrameFormat::PixelFormat format,
++ bool srcAlphaVaries)
++ {
++ if (pixCount == 0)
++ return;
++
++ const auto mask = qAlphaMask(format);
++
++ if (srcAlphaVaries || (src[0] & mask) != mask)
++ qCopyPixelsWithMask(dst, src, pixCount, mask);
++ else
++ memcpy(dst, src, pixCount * 4);
++ }
++
++ void qCopyPixelsWithMask(uint32_t *dst, const uint32_t *src, size_t size, uint32_t mask)
++ {
++ std::call_once(InitFuncsAsmFlag, &qInitFuncsAsm);
++
++ qPixelsCopyFunc(dst, src, size, mask);
++ }
++
++ uint32_t qAlphaMask(QVideoFrameFormat::PixelFormat format)
++ {
++ switch (format) {
++ case QVideoFrameFormat::Format_ARGB8888:
++ case QVideoFrameFormat::Format_ARGB8888_Premultiplied:
++ case QVideoFrameFormat::Format_XRGB8888:
++ case QVideoFrameFormat::Format_ABGR8888:
++ case QVideoFrameFormat::Format_XBGR8888:
++ #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
++ return 0xff;
++ #else
++ return 0xff000000;
++ #endif
++ case QVideoFrameFormat::Format_BGRA8888:
++ case QVideoFrameFormat::Format_BGRA8888_Premultiplied:
++ case QVideoFrameFormat::Format_BGRX8888:
++ case QVideoFrameFormat::Format_RGBA8888:
++ case QVideoFrameFormat::Format_RGBX8888:
++ #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
++ return 0xff000000;
++ #else
++ return 0xff;
++ #endif
++ default:
++ return 0;
++ }
++ }
++
++ QT_END_NAMESPACE