Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/ycbcr/./../../../gfx/ycbcr/yuv_convert.cpp
Warning:line 541, column 5
Value stored to 'source_dy' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_gfx_ycbcr0.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/ycbcr -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/ycbcr -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_YCBCR_ROW_SSE -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/gfx/ycbcr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/ycbcr -I /root/firefox-clang/media/libyuv/libyuv/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ Unified_cpp_gfx_ycbcr0.cpp
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// This webpage shows layout of YV12 and other YUV formats
6// http://www.fourcc.org/yuv.php
7// The actual conversion is best described here
8// http://en.wikipedia.org/wiki/YUV
9// An article on optimizing YUV conversion using tables instead of multiplies
10// http://lestourtereaux.free.fr/papers/data/yuvrgb.pdf
11//
12// YV12 is a full plane of Y and a half height, half width chroma planes
13// YV16 is a full plane of Y and a full height, half width chroma planes
14// YV24 is a full plane of Y and a full height, full width chroma planes
15// Y8 is a full plane of Y and no chroma planes (i.e., monochrome)
16//
17// ARGB pixel format is output, which on little endian is stored as BGRA.
18// The alpha is set to 255, allowing the application to use RGBA or RGB32.
19
20#include "yuv_convert.h"
21
22#include "libyuv.h"
23#include "mozilla/IntegerRange.h"
24#include "mozilla/SSE.h"
25#include "mozilla/StaticPrefs_gfx.h"
26#include "scale_yuv_argb.h"
27// Header for low level row functions.
28#include "yuv_row.h"
29
30namespace mozilla {
31
32namespace gfx {
33
34// 16.16 fixed point arithmetic
35const int kFractionBits = 16;
36const int kFractionMax = 1 << kFractionBits;
37const int kFractionMask = ((1 << kFractionBits) - 1);
38
39// clang-format off
40
41nsresult ToNSResult(int aLibyuvResult) {
42 // Docs for libyuv::ConvertToI420 say:
43 // Returns 0 for successful; -1 for invalid parameter. Non-zero for failure.
44 switch (aLibyuvResult) {
45 case 0:
46 return NS_OK;
47 case -1:
48 return NS_ERROR_INVALID_ARG;
49 default:
50 return NS_ERROR_FAILURE;
51 }
52}
53
54libyuv::FourCC FourCCFromYUVType(YUVType aYUVType) {
55 switch (aYUVType) {
56 case YV24: return libyuv::FOURCC_I444;
57 case YV16: return libyuv::FOURCC_I422;
58 case YV12: return libyuv::FOURCC_I420;
59 case Y8: return libyuv::FOURCC_I400;
60 default: return libyuv::FOURCC_ANY;
61 }
62}
63
64int GBRPlanarToARGB(const uint8_t* src_y, int y_pitch,
65 const uint8_t* src_u, int u_pitch,
66 const uint8_t* src_v, int v_pitch,
67 uint8_t* rgb_buf, int rgb_pitch,
68 int pic_width, int pic_height) {
69 // libyuv has no native conversion function for this
70 // fixme: replace with something less awful
71 for (const auto row : IntegerRange(pic_height)) {
72 for (const auto col : IntegerRange(pic_width)) {
73 rgb_buf[rgb_pitch * row + col * 4 + 0] = src_u[u_pitch * row + col];
74 rgb_buf[rgb_pitch * row + col * 4 + 1] = src_y[y_pitch * row + col];
75 rgb_buf[rgb_pitch * row + col * 4 + 2] = src_v[v_pitch * row + col];
76 rgb_buf[rgb_pitch * row + col * 4 + 3] = 255;
77 }
78 }
79 return 0;
80}
81
82// Convert a frame of YUV to 32 bit ARGB or ABGR.
83nsresult
84ConvertYCbCrToRGB32(const uint8_t* y_buf,
85 const uint8_t* u_buf,
86 const uint8_t* v_buf,
87 uint8_t* rgb_buf,
88 int pic_x,
89 int pic_y,
90 int pic_width,
91 int pic_height,
92 int y_pitch,
93 int uv_pitch,
94 int rgb_pitch,
95 YUVType yuv_type,
96 YUVColorSpace yuv_color_space,
97 ColorRange color_range,
98 RGB32Type rgb32_type) {
99 if (pic_x < 0 || pic_y < 0 || y_pitch < 0 || uv_pitch < 0 || rgb_pitch < 0) {
100 NS_WARNING("Negative origin or pitch is unsupported")NS_DebugBreak(NS_DEBUG_WARNING, "Negative origin or pitch is unsupported"
, nullptr, "./../../../gfx/ycbcr/yuv_convert.cpp", 100)
;
101 return NS_ERROR_NOT_IMPLEMENTED;
102 }
103
104 // Deprecated function's conversion is accurate.
105 // libyuv converion is a bit inaccurate to get performance. It dynamically
106 // calculates RGB from YUV to use simd. In it, signed byte is used for
107 // conversion's coefficient, but it requests 129. libyuv cut 129 to 127. And
108 // only 6 bits are used for a decimal part during the dynamic calculation.
109 //
110 // The function is still fast on some old intel chips.
111 // See Bug 1256475.
112 bool use_deprecated = StaticPrefs::gfx_ycbcr_accurate_conversion() ||
113 (supports_mmx() && supports_sse() && !supports_sse3() &&
114 yuv_color_space == YUVColorSpace::BT601 &&
115 color_range == ColorRange::LIMITED);
116 // The deprecated function only support BT601.
117 // See Bug 1210357.
118 if (yuv_color_space != YUVColorSpace::BT601) {
119 use_deprecated = false;
120 }
121 if (use_deprecated) {
122 return ConvertYCbCrToRGB32_deprecated(
123 y_buf, u_buf, v_buf, rgb_buf, pic_x, pic_y, pic_width, pic_height,
124 y_pitch, uv_pitch, rgb_pitch, yuv_type, rgb32_type);
125 }
126
127 decltype(libyuv::I420ToARGBMatrix)* fConvertYUVToARGB = nullptr;
128 const uint8_t* src_y = nullptr;
129 const uint8_t* src_u = nullptr;
130 const uint8_t* src_v = nullptr;
131 const libyuv::YuvConstants* yuv_constant = nullptr;
132 bool swap_uv = rgb32_type == RGB32Type::ABGR;
133
134 switch (yuv_color_space) {
135 case YUVColorSpace::BT2020:
136 yuv_constant = color_range == ColorRange::LIMITED
137 ? swap_uv? &libyuv::kYvu2020Constants : &libyuv::kYuv2020Constants
138 : swap_uv? &libyuv::kYvuV2020Constants : &libyuv::kYuvV2020Constants;
139 break;
140 case YUVColorSpace::BT709:
141 yuv_constant = color_range == ColorRange::LIMITED
142 ? swap_uv? &libyuv::kYvuH709Constants : &libyuv::kYuvH709Constants
143 : swap_uv? &libyuv::kYvuF709Constants : &libyuv::kYuvF709Constants;
144 break;
145 case YUVColorSpace::Identity:
146 if (yuv_type == YV24) {
147 break;
148 }
149 NS_WARNING("Identity (aka RGB) with chroma subsampling is unsupported")NS_DebugBreak(NS_DEBUG_WARNING, "Identity (aka RGB) with chroma subsampling is unsupported"
, nullptr, "./../../../gfx/ycbcr/yuv_convert.cpp", 149)
;
150 return NS_ERROR_NOT_IMPLEMENTED;
151 // TODO: Consider using BT601 for unsupported input?
152 default:
153 MOZ_FALLTHROUGH_ASSERT("Unsupported YUVColorSpace")do { do { } while (false); MOZ_ReportCrash("" "MOZ_FALLTHROUGH_ASSERT: "
"Unsupported YUVColorSpace", "./../../../gfx/ycbcr/yuv_convert.cpp"
, 153); AnnotateMozCrashReason("MOZ_CRASH(" "MOZ_FALLTHROUGH_ASSERT: "
"Unsupported YUVColorSpace" ")"); do { MOZ_CrashSequence(__null
, 153); __attribute__((nomerge)) ::abort(); } while (false); }
while (false)
;
154 case YUVColorSpace::BT601:
155 yuv_constant = color_range == ColorRange::LIMITED
156 ? swap_uv? &libyuv::kYvuI601Constants : &libyuv::kYuvI601Constants
157 : swap_uv? &libyuv::kYvuJPEGConstants : &libyuv::kYuvJPEGConstants;
158 break;
159 }
160
161 switch (yuv_type) {
162 case YV24: {
163 src_y = y_buf + y_pitch * pic_y + pic_x;
164 src_u = u_buf + uv_pitch * pic_y + pic_x;
165 src_v = v_buf + uv_pitch * pic_y + pic_x;
166
167 if (yuv_color_space == YUVColorSpace::Identity) {
168 const uint8_t* u_channel = swap_uv? src_v : src_u;
169 const uint8_t* v_channel = swap_uv? src_u : src_v;
170 // Special case for RGB image
171 return ToNSResult(GBRPlanarToARGB(src_y, y_pitch, u_channel, uv_pitch, v_channel,
172 uv_pitch, rgb_buf, rgb_pitch, pic_width, pic_height));
173 }
174
175 fConvertYUVToARGB = libyuv::I444ToARGBMatrix;
176 break;
177 }
178 case YV16: {
179 src_y = y_buf + y_pitch * pic_y + pic_x;
180 // Only used when pic_x is even; odd pic_x is handled below.
181 src_u = u_buf + uv_pitch * pic_y + pic_x / 2;
182 src_v = v_buf + uv_pitch * pic_y + pic_x / 2;
183
184 fConvertYUVToARGB = libyuv::I422ToARGBMatrix;
185 break;
186 }
187 case YV12: {
188 src_y = y_buf + y_pitch * pic_y + pic_x;
189 // Only used when pic_x and pic_y are both even; odd values are handled
190 // below.
191 src_u = u_buf + uv_pitch * (pic_y / 2) + pic_x / 2;
192 src_v = v_buf + uv_pitch * (pic_y / 2) + pic_x / 2;
193
194 fConvertYUVToARGB = libyuv::I420ToARGBMatrix;
195 break;
196 }
197 case Y8: {
198 src_y = y_buf + y_pitch * pic_y + pic_x;
199 MOZ_ASSERT(u_buf == nullptr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(u_buf == nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(u_buf == nullptr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("u_buf == nullptr"
, "./../../../gfx/ycbcr/yuv_convert.cpp", 199); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "u_buf == nullptr" ")"); do { MOZ_CrashSequence
(__null, 199); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
200 MOZ_ASSERT(v_buf == nullptr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(v_buf == nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(v_buf == nullptr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("v_buf == nullptr"
, "./../../../gfx/ycbcr/yuv_convert.cpp", 200); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "v_buf == nullptr" ")"); do { MOZ_CrashSequence
(__null, 200); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
201
202 if (color_range == ColorRange::LIMITED) {
203 return ToNSResult(libyuv::I400ToARGB(src_y, y_pitch, rgb_buf, rgb_pitch,
204 pic_width, pic_height));
205 }
206 return ToNSResult(libyuv::J400ToARGB(src_y, y_pitch, rgb_buf, rgb_pitch,
207 pic_width, pic_height));
208 }
209 default:
210 MOZ_ASSERT_UNREACHABLE("Unsupported YUV type")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Unsupported YUV type" ")", "./../../../gfx/ycbcr/yuv_convert.cpp"
, 210); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Unsupported YUV type" ")"); do {
MOZ_CrashSequence(__null, 210); __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
211 return NS_ERROR_NOT_IMPLEMENTED;
212 }
213
214 const uint8_t* u_channel = swap_uv? src_v : src_u;
215 const uint8_t* v_channel = swap_uv? src_u : src_v;
216
217 // libyuv handles odd crop widths and heights correctly via its _Any_ row
218 // variants, but cannot handle an odd pic_x or pic_y for subsampled formats.
219 // For YV12/YV16 an odd pic_x means the Y plane starts at a position that
220 // doesn't align with a chroma column boundary; for YV12, odd pic_y has the
221 // same problem vertically. libyuv would then pair each Y sample with the
222 // wrong chroma, affecting every pixel in the image.
223 //
224 // Fix: split the output into up to 4 non-overlapping regions, each with
225 // even-aligned source coordinates. Integer division of an odd sx or sy by 2
226 // floors to the correct chroma index (both luma positions in a 2x2 block
227 // share one chroma sample), so all regions are correct.
228 //
229 // With dx = pic_x & 1 and dy = pic_y & 1 (each 0 or 1):
230 //
231 // +------------+--------------------+
232 // | dx x dy | (W-dx) x dy | <- only when dy > 0
233 // +------------+--------------------+
234 // | dx x (H-dy)| (W-dx) x (H-dy) | <- left col only when dx > 0
235 // +------------+--------------------+
236
237 // dx/dy: 1 if pic_x/pic_y is odd and the format has chroma subsampling in
238 // that axis, 0 otherwise.
239 int dx = (yuv_type == YV12 || yuv_type == YV16) ? (pic_x & 1) : 0;
240 int dy = (yuv_type == YV12) ? (pic_y & 1) : 0;
241 if (dx | dy) {
242 // Converts source sub-region (sx, sy, w, h) to the area at dst in rgb_buf.
243 auto convert = [&](int sx, int sy, int w, int h, uint8_t* dst) -> nsresult {
244 if (w <= 0 || h <= 0) {
245 return NS_OK;
246 }
247 const uint8_t* py = y_buf + sy * y_pitch + sx;
248 const uint8_t* pu;
249 const uint8_t* pv;
250 if (yuv_type == YV12) {
251 pu = u_buf + (sy / 2) * uv_pitch + sx / 2;
252 pv = v_buf + (sy / 2) * uv_pitch + sx / 2;
253 } else { // YV16: full-height chroma planes, only horizontally subsampled.
254 pu = u_buf + sy * uv_pitch + sx / 2;
255 pv = v_buf + sy * uv_pitch + sx / 2;
256 }
257 const uint8_t* uc = swap_uv ? pv : pu;
258 const uint8_t* vc = swap_uv ? pu : pv;
259 return ToNSResult(fConvertYUVToARGB(py, y_pitch, uc, uv_pitch, vc, uv_pitch,
260 dst, rgb_pitch, yuv_constant, w, h));
261 };
262 if (dy) {
263 // First output row (source row pic_y is odd).
264 if (dx) {
265 // Corner pixel: source (pic_x, pic_y) → output top-left.
266 nsresult rv = convert(pic_x, pic_y, 1, 1, rgb_buf);
267 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
268 return rv;
269 }
270 }
271 // Remaining columns of first row; source x pic_x+dx is even.
272 nsresult rv = convert(pic_x + dx, pic_y, pic_width - dx, 1, rgb_buf + dx * 4);
273 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
274 return rv;
275 }
276 }
277 int sy = pic_y + dy; // even-aligned source y for remaining output rows
278 int h = pic_height - dy;
279 if (dx) {
280 // Left column of remaining rows; width=1 handles the odd source x.
281 nsresult rv = convert(pic_x, sy, 1, h, rgb_buf + dy * rgb_pitch);
282 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
283 return rv;
284 }
285 }
286 // Main body: source (pic_x+dx, sy), both even-aligned.
287 return convert(pic_x + dx, sy, pic_width - dx, h,
288 rgb_buf + dy * rgb_pitch + dx * 4);
289 }
290
291 return ToNSResult(fConvertYUVToARGB(src_y, y_pitch, u_channel, uv_pitch,
292 v_channel, uv_pitch, rgb_buf, rgb_pitch,
293 yuv_constant, pic_width, pic_height));
294}
295
296// Convert a frame of YUV to 32 bit ARGB or ABGR.
297nsresult
298ConvertYCbCrToRGB32_deprecated(const uint8_t* y_buf,
299 const uint8_t* u_buf,
300 const uint8_t* v_buf,
301 uint8_t* rgb_buf,
302 int pic_x,
303 int pic_y,
304 int pic_width,
305 int pic_height,
306 int y_pitch,
307 int uv_pitch,
308 int rgb_pitch,
309 YUVType yuv_type,
310 RGB32Type rgb32_type) {
311 unsigned int y_shift = yuv_type == YV12 ? 1 : 0;
312 unsigned int x_shift = yuv_type == YV24 ? 0 : 1;
313 // Test for SSE because the optimized code uses movntq, which is not part of MMX.
314 bool has_sse = supports_mmx() && supports_sse();
315 // There is no optimized YV24 SSE routine so we check for this and
316 // fall back to the C code.
317 has_sse &= yuv_type != YV24;
318 bool odd_pic_x = yuv_type != YV24 && pic_x % 2 != 0;
319 int x_width = odd_pic_x ? pic_width - 1 : pic_width;
320 bool swap_uv = rgb32_type == RGB32Type::ABGR;
321 const uint8_t* u_channel = swap_uv? v_buf : u_buf;
322 const uint8_t* v_channel = swap_uv? u_buf : v_buf;
323
324 for (int y = pic_y; y < pic_height + pic_y; ++y) {
325 uint8_t* rgb_row = rgb_buf + (y - pic_y) * rgb_pitch;
326 const uint8_t* y_ptr = y_buf + y * y_pitch + pic_x;
327 const uint8_t* u_ptr = u_channel + (y >> y_shift) * uv_pitch + (pic_x >> x_shift);
328 const uint8_t* v_ptr = v_channel + (y >> y_shift) * uv_pitch + (pic_x >> x_shift);
329
330 if (odd_pic_x) {
331 // Handle the single odd pixel manually and use the
332 // fast routines for the remaining.
333 FastConvertYUVToRGB32Row_C(y_ptr++,
334 u_ptr++,
335 v_ptr++,
336 rgb_row,
337 1,
338 x_shift);
339 rgb_row += 4;
340 }
341
342 if (has_sse) {
343 FastConvertYUVToRGB32Row(y_ptr,
344 u_ptr,
345 v_ptr,
346 rgb_row,
347 x_width);
348 }
349 else {
350 FastConvertYUVToRGB32Row_C(y_ptr,
351 u_ptr,
352 v_ptr,
353 rgb_row,
354 x_width,
355 x_shift);
356 }
357 }
358
359 // MMX used for FastConvertYUVToRGB32Row requires emms instruction.
360 if (has_sse)
361 EMMS()((void)0);
362
363 return NS_OK;
364}
365
366// C version does 8 at a time to mimic MMX code
367static void FilterRows_C(uint8_t* ybuf, const uint8_t* y0_ptr, const uint8_t* y1_ptr,
368 int source_width, int source_y_fraction) {
369 int y1_fraction = source_y_fraction;
370 int y0_fraction = 256 - y1_fraction;
371 uint8_t* end = ybuf + source_width;
372 do {
373 ybuf[0] = (y0_ptr[0] * y0_fraction + y1_ptr[0] * y1_fraction) >> 8;
374 ybuf[1] = (y0_ptr[1] * y0_fraction + y1_ptr[1] * y1_fraction) >> 8;
375 ybuf[2] = (y0_ptr[2] * y0_fraction + y1_ptr[2] * y1_fraction) >> 8;
376 ybuf[3] = (y0_ptr[3] * y0_fraction + y1_ptr[3] * y1_fraction) >> 8;
377 ybuf[4] = (y0_ptr[4] * y0_fraction + y1_ptr[4] * y1_fraction) >> 8;
378 ybuf[5] = (y0_ptr[5] * y0_fraction + y1_ptr[5] * y1_fraction) >> 8;
379 ybuf[6] = (y0_ptr[6] * y0_fraction + y1_ptr[6] * y1_fraction) >> 8;
380 ybuf[7] = (y0_ptr[7] * y0_fraction + y1_ptr[7] * y1_fraction) >> 8;
381 y0_ptr += 8;
382 y1_ptr += 8;
383 ybuf += 8;
384 } while (ybuf < end);
385}
386
387#ifdef MOZILLA_MAY_SUPPORT_MMX1
388void FilterRows_MMX(uint8_t* ybuf, const uint8_t* y0_ptr, const uint8_t* y1_ptr,
389 int source_width, int source_y_fraction);
390#endif
391
392#ifdef MOZILLA_MAY_SUPPORT_SSE21
393void FilterRows_SSE2(uint8_t* ybuf, const uint8_t* y0_ptr, const uint8_t* y1_ptr,
394 int source_width, int source_y_fraction);
395#endif
396
397static inline void FilterRows(uint8_t* ybuf, const uint8_t* y0_ptr,
398 const uint8_t* y1_ptr, int source_width,
399 int source_y_fraction) {
400#ifdef MOZILLA_MAY_SUPPORT_SSE21
401 if (mozilla::supports_sse2()) {
402 FilterRows_SSE2(ybuf, y0_ptr, y1_ptr, source_width, source_y_fraction);
403 return;
404 }
405#endif
406
407#ifdef MOZILLA_MAY_SUPPORT_MMX1
408 if (mozilla::supports_mmx()) {
409 FilterRows_MMX(ybuf, y0_ptr, y1_ptr, source_width, source_y_fraction);
410 return;
411 }
412#endif
413
414 FilterRows_C(ybuf, y0_ptr, y1_ptr, source_width, source_y_fraction);
415}
416
417
418// Scale a frame of YUV to 32 bit ARGB.
419nsresult
420ScaleYCbCrToRGB32(const uint8_t* y_buf,
421 const uint8_t* u_buf,
422 const uint8_t* v_buf,
423 uint8_t* rgb_buf,
424 int source_width,
425 int source_height,
426 int width,
427 int height,
428 int y_pitch,
429 int uv_pitch,
430 int rgb_pitch,
431 YUVType yuv_type,
432 YUVColorSpace yuv_color_space,
433 ScaleFilter filter) {
434 bool use_deprecated =
435 StaticPrefs::gfx_ycbcr_accurate_conversion() ||
436#if defined(XP_WIN) && defined(_M_X64) && !defined(__clang__1)
437 // libyuv does not support SIMD scaling on MSVC 64bit. See Bug 1295927.
438 supports_sse3() ||
439#endif
440#if defined(MOZ_YCBCR_ROW_SSE1)
441 // Only worth taking the deprecated path where the SSE row functions
442 // exist; otherwise it just falls back to LinearScaleYUVToRGB32Row_C and
443 // friends, which are no faster than libyuv.
444 (supports_mmx() && supports_sse() && !supports_sse3());
445#else
446 false;
447#endif
448 // The deprecated function only support BT601.
449 // See Bug 1210357.
450 if (yuv_color_space != YUVColorSpace::BT601) {
451 use_deprecated = false;
452 }
453 if (use_deprecated) {
454 return ScaleYCbCrToRGB32_deprecated(
455 y_buf, u_buf, v_buf, rgb_buf, source_width, source_height, width,
456 height, y_pitch, uv_pitch, rgb_pitch, yuv_type, ROTATE_0, filter);
457 }
458
459 return ToNSResult(YUVToARGBScale(
460 y_buf, y_pitch, u_buf, uv_pitch, v_buf, uv_pitch,
461 FourCCFromYUVType(yuv_type), yuv_color_space, source_width, source_height,
462 rgb_buf, rgb_pitch, width, height, libyuv::kFilterBilinear));
463}
464
465// Scale a frame of YUV to 32 bit ARGB.
466nsresult
467ScaleYCbCrToRGB32_deprecated(const uint8_t* y_buf,
468 const uint8_t* u_buf,
469 const uint8_t* v_buf,
470 uint8_t* rgb_buf,
471 int source_width,
472 int source_height,
473 int width,
474 int height,
475 int y_pitch,
476 int uv_pitch,
477 int rgb_pitch,
478 YUVType yuv_type,
479 Rotate view_rotate,
480 ScaleFilter filter) {
481 bool has_mmx = supports_mmx();
482
483 // 4096 allows 3 buffers to fit in 12k.
484 // Helps performance on CPU with 16K L1 cache.
485 // Large enough for 3830x2160 and 30" displays which are 2560x1600.
486 const int kFilterBufferSize = 4096;
487 // Disable filtering if the screen is too big (to avoid buffer overflows).
488 // This should never happen to regular users: they don't have monitors
489 // wider than 4096 pixels.
490 // TODO(fbarchard): Allow rotated videos to filter.
491 if (source_width > kFilterBufferSize || view_rotate)
492 filter = FILTER_NONE;
493
494 unsigned int y_shift = yuv_type == YV12 ? 1 : 0;
495 // Diagram showing origin and direction of source sampling.
496 // ->0 4<-
497 // 7 3
498 //
499 // 6 5
500 // ->1 2<-
501 // Rotations that start at right side of image.
502 if ((view_rotate == ROTATE_180) ||
503 (view_rotate == ROTATE_270) ||
504 (view_rotate == MIRROR_ROTATE_0) ||
505 (view_rotate == MIRROR_ROTATE_90)) {
506 y_buf += source_width - 1;
507 u_buf += source_width / 2 - 1;
508 v_buf += source_width / 2 - 1;
509 source_width = -source_width;
510 }
511 // Rotations that start at bottom of image.
512 if ((view_rotate == ROTATE_90) ||
513 (view_rotate == ROTATE_180) ||
514 (view_rotate == MIRROR_ROTATE_90) ||
515 (view_rotate == MIRROR_ROTATE_180)) {
516 y_buf += (source_height - 1) * y_pitch;
517 u_buf += ((source_height >> y_shift) - 1) * uv_pitch;
518 v_buf += ((source_height >> y_shift) - 1) * uv_pitch;
519 source_height = -source_height;
520 }
521
522 // Handle zero sized destination.
523 if (width == 0 || height == 0)
524 return NS_ERROR_INVALID_ARG;
525 int source_dx = source_width * kFractionMax / width;
526 int source_dy = source_height * kFractionMax / height;
527 int source_dx_uv = source_dx;
528
529 if ((view_rotate == ROTATE_90) ||
530 (view_rotate == ROTATE_270)) {
531 int tmp = height;
532 height = width;
533 width = tmp;
534 tmp = source_height;
535 source_height = source_width;
536 source_width = tmp;
537 int original_dx = source_dx;
538 int original_dy = source_dy;
539 source_dx = ((original_dy >> kFractionBits) * y_pitch) << kFractionBits;
540 source_dx_uv = ((original_dy >> kFractionBits) * uv_pitch) << kFractionBits;
541 source_dy = original_dx;
Value stored to 'source_dy' is never read
542 if (view_rotate == ROTATE_90) {
543 y_pitch = -1;
544 uv_pitch = -1;
545 source_height = -source_height;
546 } else {
547 y_pitch = 1;
548 uv_pitch = 1;
549 }
550 }
551
552 // Need padding because FilterRows() will write 1 to 16 extra pixels
553 // after the end for SSE2 version.
554 uint8_t yuvbuf[16 + kFilterBufferSize * 3 + 16];
555 uint8_t* ybuf =
556 reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(yuvbuf + 15) & ~15);
557 uint8_t* ubuf = ybuf + kFilterBufferSize;
558 uint8_t* vbuf = ubuf + kFilterBufferSize;
559 // TODO(fbarchard): Fixed point math is off by 1 on negatives.
560 int yscale_fixed = (source_height << kFractionBits) / height;
561
562 // TODO(fbarchard): Split this into separate function for better efficiency.
563 for (int y = 0; y < height; ++y) {
564 uint8_t* dest_pixel = rgb_buf + y * rgb_pitch;
565 int source_y_subpixel = (y * yscale_fixed);
566 if (yscale_fixed >= (kFractionMax * 2)) {
567 source_y_subpixel += kFractionMax / 2; // For 1/2 or less, center filter.
568 }
569 int source_y = source_y_subpixel >> kFractionBits;
570
571 const uint8_t* y0_ptr = y_buf + source_y * y_pitch;
572 const uint8_t* y1_ptr = y0_ptr + y_pitch;
573
574 const uint8_t* u0_ptr = u_buf + (source_y >> y_shift) * uv_pitch;
575 const uint8_t* u1_ptr = u0_ptr + uv_pitch;
576 const uint8_t* v0_ptr = v_buf + (source_y >> y_shift) * uv_pitch;
577 const uint8_t* v1_ptr = v0_ptr + uv_pitch;
578
579 // vertical scaler uses 16.8 fixed point
580 int source_y_fraction = (source_y_subpixel & kFractionMask) >> 8;
581 int source_uv_fraction =
582 ((source_y_subpixel >> y_shift) & kFractionMask) >> 8;
583
584 const uint8_t* y_ptr = y0_ptr;
585 const uint8_t* u_ptr = u0_ptr;
586 const uint8_t* v_ptr = v0_ptr;
587 // Apply vertical filtering if necessary.
588 // TODO(fbarchard): Remove memcpy when not necessary.
589 if (filter & mozilla::gfx::FILTER_BILINEAR_V) {
590 if (yscale_fixed != kFractionMax &&
591 source_y_fraction && ((source_y + 1) < source_height)) {
592 FilterRows(ybuf, y0_ptr, y1_ptr, source_width, source_y_fraction);
593 } else {
594 memcpy(ybuf, y0_ptr, source_width);
595 }
596 y_ptr = ybuf;
597 ybuf[source_width] = ybuf[source_width-1];
598 int uv_source_width = (source_width + 1) / 2;
599 if (yscale_fixed != kFractionMax &&
600 source_uv_fraction &&
601 (((source_y >> y_shift) + 1) < (source_height >> y_shift))) {
602 FilterRows(ubuf, u0_ptr, u1_ptr, uv_source_width, source_uv_fraction);
603 FilterRows(vbuf, v0_ptr, v1_ptr, uv_source_width, source_uv_fraction);
604 } else {
605 memcpy(ubuf, u0_ptr, uv_source_width);
606 memcpy(vbuf, v0_ptr, uv_source_width);
607 }
608 u_ptr = ubuf;
609 v_ptr = vbuf;
610 ubuf[uv_source_width] = ubuf[uv_source_width - 1];
611 vbuf[uv_source_width] = vbuf[uv_source_width - 1];
612 }
613 if (source_dx == kFractionMax) { // Not scaled
614 FastConvertYUVToRGB32Row(y_ptr, u_ptr, v_ptr,
615 dest_pixel, width);
616 } else if (filter & FILTER_BILINEAR_H) {
617 LinearScaleYUVToRGB32Row(y_ptr, u_ptr, v_ptr,
618 dest_pixel, width, source_dx);
619 } else {
620// Specialized scalers and rotation.
621#if defined(MOZILLA_MAY_SUPPORT_SSE1) && defined(_MSC_VER) && defined(_M_IX86) && !defined(__clang__1)
622 if(mozilla::supports_sse()) {
623 if (width == (source_width * 2)) {
624 DoubleYUVToRGB32Row_SSE(y_ptr, u_ptr, v_ptr,
625 dest_pixel, width);
626 } else if ((source_dx & kFractionMask) == 0) {
627 // Scaling by integer scale factor. ie half.
628 ConvertYUVToRGB32Row_SSE(y_ptr, u_ptr, v_ptr,
629 dest_pixel, width,
630 source_dx >> kFractionBits);
631 } else if (source_dx_uv == source_dx) { // Not rotated.
632 ScaleYUVToRGB32Row(y_ptr, u_ptr, v_ptr,
633 dest_pixel, width, source_dx);
634 } else {
635 RotateConvertYUVToRGB32Row_SSE(y_ptr, u_ptr, v_ptr,
636 dest_pixel, width,
637 source_dx >> kFractionBits,
638 source_dx_uv >> kFractionBits);
639 }
640 }
641 else {
642 ScaleYUVToRGB32Row_C(y_ptr, u_ptr, v_ptr,
643 dest_pixel, width, source_dx);
644 }
645#else
646 (void)source_dx_uv;
647 ScaleYUVToRGB32Row(y_ptr, u_ptr, v_ptr,
648 dest_pixel, width, source_dx);
649#endif
650 }
651 }
652 // MMX used for FastConvertYUVToRGB32Row and FilterRows requires emms.
653 if (has_mmx)
654 EMMS()((void)0);
655
656 return NS_OK;
657}
658
659nsresult
660ConvertI420AlphaToARGB32(const uint8_t* y_buf,
661 const uint8_t* u_buf,
662 const uint8_t* v_buf,
663 const uint8_t* a_buf,
664 uint8_t* argb_buf,
665 int pic_width,
666 int pic_height,
667 int ya_pitch,
668 int uv_pitch,
669 int argb_pitch) {
670
671 // The downstream graphics stack expects an attenuated input, hence why the
672 // attenuation parameter is set.
673 return ToNSResult(libyuv::I420AlphaToARGB(
674 y_buf, ya_pitch, u_buf, uv_pitch, v_buf, uv_pitch, a_buf, ya_pitch,
675 argb_buf, argb_pitch, pic_width, pic_height, 1));
676}
677
678} // namespace gfx
679} // namespace mozilla