1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "ImageToI420.h"
8 #include "ImageContainer.h"
9 #include "libyuv/convert.h"
10 #include "mozilla/dom/ImageBitmapBinding.h"
11 #include "mozilla/dom/ImageUtils.h"
12 #include "mozilla/gfx/Point.h"
13 #include "mozilla/RefPtr.h"
14 #include "mozilla/Result.h"
15 #include "nsThreadUtils.h"
17 using mozilla::ImageFormat
;
18 using mozilla::dom::ImageBitmapFormat
;
19 using mozilla::dom::ImageUtils
;
20 using mozilla::gfx::DataSourceSurface
;
21 using mozilla::gfx::SourceSurface
;
22 using mozilla::gfx::SurfaceFormat
;
23 using mozilla::layers::Image
;
24 using mozilla::layers::PlanarYCbCrData
;
25 using mozilla::layers::PlanarYCbCrImage
;
27 static const PlanarYCbCrData
* GetPlanarYCbCrData(Image
* aImage
) {
28 switch (aImage
->GetFormat()) {
29 case ImageFormat::PLANAR_YCBCR
:
30 return aImage
->AsPlanarYCbCrImage()->GetData();
31 case ImageFormat::NV_IMAGE
:
32 return aImage
->AsNVImage()->GetData();
38 static already_AddRefed
<SourceSurface
> GetSourceSurface(Image
* aImage
) {
39 if (!aImage
->AsGLImage() || NS_IsMainThread()) {
40 return aImage
->GetAsSourceSurface();
43 // GLImage::GetAsSourceSurface() only supports main thread
44 RefPtr
<SourceSurface
> surf
;
45 NS_DispatchAndSpinEventLoopUntilComplete(
46 "ImageToI420::GLImage::GetSourceSurface"_ns
,
47 mozilla::GetMainThreadSerialEventTarget(),
48 NS_NewRunnableFunction(
49 "ImageToI420::GLImage::GetSourceSurface",
50 [&aImage
, &surf
]() { surf
= aImage
->GetAsSourceSurface(); }));
55 static nsresult
MapRv(int aRv
) {
56 // Docs for libyuv::ConvertToI420 say:
57 // Returns 0 for successful; -1 for invalid parameter. Non-zero for failure.
62 return NS_ERROR_INVALID_ARG
;
64 return NS_ERROR_FAILURE
;
70 nsresult
ConvertToI420(Image
* aImage
, uint8_t* aDestY
, int aDestStrideY
,
71 uint8_t* aDestU
, int aDestStrideU
, uint8_t* aDestV
,
73 if (!aImage
->IsValid()) {
74 return NS_ERROR_INVALID_ARG
;
77 if (const PlanarYCbCrData
* data
= GetPlanarYCbCrData(aImage
)) {
78 const ImageUtils
imageUtils(aImage
);
79 Maybe
<dom::ImageBitmapFormat
> format
= imageUtils
.GetFormat();
80 if (format
.isNothing()) {
81 MOZ_ASSERT_UNREACHABLE("YUV format conversion not implemented");
82 return NS_ERROR_NOT_IMPLEMENTED
;
84 switch (format
.value()) {
85 case ImageBitmapFormat::YUV420P
:
86 return MapRv(libyuv::I420ToI420(
87 data
->mYChannel
, data
->mYStride
, data
->mCbChannel
,
88 data
->mCbCrStride
, data
->mCrChannel
, data
->mCbCrStride
, aDestY
,
89 aDestStrideY
, aDestU
, aDestStrideU
, aDestV
, aDestStrideV
,
90 aImage
->GetSize().width
, aImage
->GetSize().height
));
91 case ImageBitmapFormat::YUV422P
:
92 return MapRv(libyuv::I422ToI420(
93 data
->mYChannel
, data
->mYStride
, data
->mCbChannel
,
94 data
->mCbCrStride
, data
->mCrChannel
, data
->mCbCrStride
, aDestY
,
95 aDestStrideY
, aDestU
, aDestStrideU
, aDestV
, aDestStrideV
,
96 aImage
->GetSize().width
, aImage
->GetSize().height
));
97 case ImageBitmapFormat::YUV444P
:
98 return MapRv(libyuv::I444ToI420(
99 data
->mYChannel
, data
->mYStride
, data
->mCbChannel
,
100 data
->mCbCrStride
, data
->mCrChannel
, data
->mCbCrStride
, aDestY
,
101 aDestStrideY
, aDestU
, aDestStrideU
, aDestV
, aDestStrideV
,
102 aImage
->GetSize().width
, aImage
->GetSize().height
));
103 case ImageBitmapFormat::YUV420SP_NV12
:
104 return MapRv(libyuv::NV12ToI420(
105 data
->mYChannel
, data
->mYStride
, data
->mCbChannel
,
106 data
->mCbCrStride
, aDestY
, aDestStrideY
, aDestU
, aDestStrideU
,
107 aDestV
, aDestStrideV
, aImage
->GetSize().width
,
108 aImage
->GetSize().height
));
109 case ImageBitmapFormat::YUV420SP_NV21
:
110 return MapRv(libyuv::NV21ToI420(
111 data
->mYChannel
, data
->mYStride
, data
->mCrChannel
,
112 data
->mCbCrStride
, aDestY
, aDestStrideY
, aDestU
, aDestStrideU
,
113 aDestV
, aDestStrideV
, aImage
->GetSize().width
,
114 aImage
->GetSize().height
));
116 MOZ_ASSERT_UNREACHABLE("YUV format conversion not implemented");
117 return NS_ERROR_NOT_IMPLEMENTED
;
121 RefPtr
<SourceSurface
> surf
= GetSourceSurface(aImage
);
123 return NS_ERROR_FAILURE
;
126 RefPtr
<DataSourceSurface
> data
= surf
->GetDataSurface();
128 return NS_ERROR_FAILURE
;
131 DataSourceSurface::ScopedMap
map(data
, DataSourceSurface::READ
);
132 if (!map
.IsMapped()) {
133 return NS_ERROR_FAILURE
;
136 switch (surf
->GetFormat()) {
137 case SurfaceFormat::B8G8R8A8
:
138 case SurfaceFormat::B8G8R8X8
:
139 return MapRv(libyuv::ARGBToI420(
140 static_cast<uint8_t*>(map
.GetData()), map
.GetStride(), aDestY
,
141 aDestStrideY
, aDestU
, aDestStrideU
, aDestV
, aDestStrideV
,
142 aImage
->GetSize().width
, aImage
->GetSize().height
));
143 case SurfaceFormat::R5G6B5_UINT16
:
144 return MapRv(libyuv::RGB565ToI420(
145 static_cast<uint8_t*>(map
.GetData()), map
.GetStride(), aDestY
,
146 aDestStrideY
, aDestU
, aDestStrideU
, aDestV
, aDestStrideV
,
147 aImage
->GetSize().width
, aImage
->GetSize().height
));
149 MOZ_ASSERT_UNREACHABLE("Surface format conversion not implemented");
150 return NS_ERROR_NOT_IMPLEMENTED
;
154 } // namespace mozilla