Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "ImageUtils.h"
8 : #include "ImageBitmapUtils.h"
9 : #include "ImageContainer.h"
10 : #include "mozilla/AlreadyAddRefed.h"
11 : #include "mozilla/dom/ImageBitmapBinding.h"
12 : #include "mozilla/ErrorResult.h"
13 :
14 : using namespace mozilla::layers;
15 : using namespace mozilla::gfx;
16 :
17 : namespace mozilla {
18 : namespace dom {
19 :
20 : static ImageBitmapFormat
21 0 : GetImageBitmapFormatFromSurfaceFromat(SurfaceFormat aSurfaceFormat)
22 : {
23 0 : switch (aSurfaceFormat) {
24 : case SurfaceFormat::B8G8R8A8:
25 : case SurfaceFormat::B8G8R8X8:
26 0 : return ImageBitmapFormat::BGRA32;
27 : case SurfaceFormat::R8G8B8A8:
28 : case SurfaceFormat::R8G8B8X8:
29 0 : return ImageBitmapFormat::RGBA32;
30 : case SurfaceFormat::R8G8B8:
31 0 : return ImageBitmapFormat::RGB24;
32 : case SurfaceFormat::B8G8R8:
33 0 : return ImageBitmapFormat::BGR24;
34 : case SurfaceFormat::HSV:
35 0 : return ImageBitmapFormat::HSV;
36 : case SurfaceFormat::Lab:
37 0 : return ImageBitmapFormat::Lab;
38 : case SurfaceFormat::Depth:
39 0 : return ImageBitmapFormat::DEPTH;
40 : case SurfaceFormat::A8:
41 0 : return ImageBitmapFormat::GRAY8;
42 : case SurfaceFormat::R5G6B5_UINT16:
43 : case SurfaceFormat::YUV:
44 : case SurfaceFormat::NV12:
45 : case SurfaceFormat::UNKNOWN:
46 : default:
47 0 : return ImageBitmapFormat::EndGuard_;
48 : }
49 : }
50 :
51 : static ImageBitmapFormat
52 0 : GetImageBitmapFormatFromPlanarYCbCrData(layers::PlanarYCbCrData const *aData)
53 : {
54 0 : MOZ_ASSERT(aData);
55 :
56 0 : if (aData->mYSkip == 0 && aData->mCbSkip == 0 &&
57 0 : aData->mCrSkip == 0) { // Possibly three planes.
58 0 : if (aData->mCbChannel >=
59 0 : aData->mYChannel + aData->mYSize.height * aData->mYStride &&
60 0 : aData->mCrChannel >=
61 0 : aData->mCbChannel +
62 0 : aData->mCbCrSize.height * aData->mCbCrStride) { // Three planes.
63 0 : if (aData->mYSize.height == aData->mCbCrSize.height) {
64 0 : if (aData->mYSize.width == aData->mCbCrSize.width) {
65 0 : return ImageBitmapFormat::YUV444P;
66 : }
67 0 : if (((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
68 0 : return ImageBitmapFormat::YUV422P;
69 : }
70 0 : } else if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height) {
71 0 : if (((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
72 0 : return ImageBitmapFormat::YUV420P;
73 : }
74 : }
75 : }
76 0 : } else if (aData->mYSkip == 0 && aData->mCbSkip == 1 && aData->mCrSkip == 1) { // Possibly two planes.
77 0 : if (aData->mCbChannel >= aData->mYChannel + aData->mYSize.height * aData->mYStride &&
78 0 : aData->mCbChannel == aData->mCrChannel - 1) { // Two planes.
79 0 : if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height &&
80 0 : ((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
81 0 : return ImageBitmapFormat::YUV420SP_NV12; // Y-Cb-Cr
82 : }
83 0 : } else if (aData->mCrChannel >= aData->mYChannel + aData->mYSize.height * aData->mYStride &&
84 0 : aData->mCrChannel == aData->mCbChannel - 1) { // Two planes.
85 0 : if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height &&
86 0 : ((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
87 0 : return ImageBitmapFormat::YUV420SP_NV21; // Y-Cr-Cb
88 : }
89 : }
90 : }
91 :
92 0 : return ImageBitmapFormat::EndGuard_;
93 : }
94 :
95 : // ImageUtils::Impl implements the _generic_ algorithm which always readback
96 : // data in RGBA format into CPU memory.
97 : // Since layers::CairoImage is just a warpper to a DataSourceSurface, the
98 : // implementation of CairoSurfaceImpl is nothing different to the generic
99 : // version.
100 : class ImageUtils::Impl
101 : {
102 : public:
103 0 : explicit Impl(layers::Image* aImage)
104 0 : : mImage(aImage)
105 0 : , mSurface(nullptr)
106 : {
107 0 : }
108 :
109 0 : virtual ~Impl() = default;
110 :
111 : virtual ImageBitmapFormat
112 0 : GetFormat() const
113 : {
114 0 : return GetImageBitmapFormatFromSurfaceFromat(Surface()->GetFormat());
115 : }
116 :
117 : virtual uint32_t
118 0 : GetBufferLength() const
119 : {
120 0 : const uint32_t stride = Surface()->Stride();
121 0 : const IntSize size = Surface()->GetSize();
122 0 : return (uint32_t)(size.height * stride);
123 : }
124 :
125 : virtual UniquePtr<ImagePixelLayout>
126 0 : MapDataInto(uint8_t* aBuffer,
127 : uint32_t aOffset,
128 : uint32_t aBufferLength,
129 : ImageBitmapFormat aFormat,
130 : ErrorResult& aRv) const
131 : {
132 0 : DataSourceSurface::ScopedMap map(Surface(), DataSourceSurface::READ);
133 0 : if (!map.IsMapped()) {
134 0 : aRv.Throw(NS_ERROR_ILLEGAL_VALUE);
135 0 : return nullptr;
136 : }
137 :
138 : // Copy or convert data.
139 : UniquePtr<ImagePixelLayout> srcLayout =
140 0 : CreateDefaultPixelLayout(GetFormat(), Surface()->GetSize().width,
141 0 : Surface()->GetSize().height, map.GetStride());
142 :
143 : // Prepare destination buffer.
144 0 : uint8_t* dstBuffer = aBuffer + aOffset;
145 : UniquePtr<ImagePixelLayout> dstLayout =
146 0 : CopyAndConvertImageData(GetFormat(), map.GetData(), srcLayout.get(),
147 0 : aFormat, dstBuffer);
148 :
149 0 : if (!dstLayout) {
150 0 : aRv.Throw(NS_ERROR_NOT_AVAILABLE);
151 0 : return nullptr;
152 : }
153 :
154 0 : return dstLayout;
155 : }
156 :
157 : protected:
158 : Impl() {}
159 :
160 0 : DataSourceSurface* Surface() const
161 : {
162 0 : if (!mSurface) {
163 0 : RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
164 0 : MOZ_ASSERT(surface);
165 :
166 0 : mSurface = surface->GetDataSurface();
167 0 : MOZ_ASSERT(mSurface);
168 : }
169 :
170 0 : return mSurface.get();
171 : }
172 :
173 : RefPtr<layers::Image> mImage;
174 : mutable RefPtr<DataSourceSurface> mSurface;
175 : };
176 :
177 : // YUVImpl is optimized for the layers::PlanarYCbCrImage and layers::NVImage.
178 : // This implementation does not readback data in RGBA format but keep it in YUV
179 : // format family.
180 0 : class YUVImpl final : public ImageUtils::Impl
181 : {
182 : public:
183 0 : explicit YUVImpl(layers::Image* aImage)
184 0 : : Impl(aImage)
185 : {
186 0 : MOZ_ASSERT(aImage->GetFormat() == ImageFormat::PLANAR_YCBCR ||
187 : aImage->GetFormat() == ImageFormat::NV_IMAGE);
188 0 : }
189 :
190 0 : ImageBitmapFormat GetFormat() const override
191 : {
192 0 : return GetImageBitmapFormatFromPlanarYCbCrData(GetPlanarYCbCrData());
193 : }
194 :
195 0 : uint32_t GetBufferLength() const override
196 : {
197 0 : if (mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
198 0 : return mImage->AsPlanarYCbCrImage()->GetDataSize();
199 : }
200 0 : return mImage->AsNVImage()->GetBufferSize();
201 : }
202 :
203 0 : UniquePtr<ImagePixelLayout> MapDataInto(uint8_t* aBuffer,
204 : uint32_t aOffset,
205 : uint32_t aBufferLength,
206 : ImageBitmapFormat aFormat,
207 : ErrorResult& aRv) const override
208 : {
209 : // Prepare source buffer and pixel layout.
210 0 : const PlanarYCbCrData* data = GetPlanarYCbCrData();
211 :
212 : UniquePtr<ImagePixelLayout> srcLayout =
213 0 : CreatePixelLayoutFromPlanarYCbCrData(data);
214 :
215 : // Do conversion.
216 : UniquePtr<ImagePixelLayout> dstLayout =
217 0 : CopyAndConvertImageData(GetFormat(), data->mYChannel, srcLayout.get(),
218 0 : aFormat, aBuffer+aOffset);
219 :
220 0 : if (!dstLayout) {
221 0 : aRv.Throw(NS_ERROR_NOT_AVAILABLE);
222 0 : return nullptr;
223 : }
224 :
225 0 : return dstLayout;
226 : }
227 :
228 : private:
229 0 : const PlanarYCbCrData* GetPlanarYCbCrData() const
230 : {
231 0 : if (mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
232 0 : return mImage->AsPlanarYCbCrImage()->GetData();
233 : }
234 0 : return mImage->AsNVImage()->GetData();
235 : }
236 : };
237 :
238 : // TODO: optimize for other platforms.
239 : // For GONK: implement GrallocImageImpl, GrallocPlanarYCbCrImpl and GonkCameraImpl.
240 : // For Windows: implement D3D9RGB32TextureImpl and D3D11ShareHandleTextureImpl.
241 : // Others: SharedBGRImpl, MACIOSrufaceImpl, GLImageImpl, SurfaceTextureImpl
242 : // EGLImageImpl and OverlayImegImpl.
243 :
244 0 : ImageUtils::ImageUtils(layers::Image* aImage)
245 0 : : mImpl(nullptr)
246 : {
247 0 : MOZ_ASSERT(aImage, "Create ImageUtils with nullptr.");
248 0 : switch(aImage->GetFormat()) {
249 : case mozilla::ImageFormat::PLANAR_YCBCR:
250 : case mozilla::ImageFormat::NV_IMAGE:
251 0 : mImpl = new YUVImpl(aImage);
252 0 : break;
253 : case mozilla::ImageFormat::CAIRO_SURFACE:
254 : default:
255 0 : mImpl = new Impl(aImage);
256 : }
257 0 : }
258 :
259 0 : ImageUtils::~ImageUtils()
260 : {
261 0 : if (mImpl) { delete mImpl; mImpl = nullptr; }
262 0 : }
263 :
264 : ImageBitmapFormat
265 0 : ImageUtils::GetFormat() const
266 : {
267 0 : MOZ_ASSERT(mImpl);
268 0 : return mImpl->GetFormat();
269 : }
270 :
271 : uint32_t
272 0 : ImageUtils::GetBufferLength() const
273 : {
274 0 : MOZ_ASSERT(mImpl);
275 0 : return mImpl->GetBufferLength();
276 : }
277 :
278 : UniquePtr<ImagePixelLayout>
279 0 : ImageUtils::MapDataInto(uint8_t* aBuffer,
280 : uint32_t aOffset,
281 : uint32_t aBufferLength,
282 : ImageBitmapFormat aFormat,
283 : ErrorResult& aRv) const
284 : {
285 0 : MOZ_ASSERT(mImpl);
286 0 : MOZ_ASSERT(aBuffer, "Map data into a null buffer.");
287 0 : return mImpl->MapDataInto(aBuffer, aOffset, aBufferLength, aFormat, aRv);
288 : }
289 :
290 : } // namespace dom
291 : } // namespace mozilla
|