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 "ImageContainer.h"
9 #include "mozilla/AlreadyAddRefed.h"
10 #include "mozilla/dom/ImageBitmapBinding.h"
11 #include "mozilla/ErrorResult.h"
12 
13 using namespace mozilla::layers;
14 using namespace mozilla::gfx;
15 
16 namespace mozilla::dom {
17 
GetImageBitmapFormatFromSurfaceFromat(SurfaceFormat aSurfaceFormat)18 static ImageBitmapFormat GetImageBitmapFormatFromSurfaceFromat(
19     SurfaceFormat aSurfaceFormat) {
20   switch (aSurfaceFormat) {
21     case SurfaceFormat::B8G8R8A8:
22     case SurfaceFormat::B8G8R8X8:
23       return ImageBitmapFormat::BGRA32;
24     case SurfaceFormat::R8G8B8A8:
25     case SurfaceFormat::R8G8B8X8:
26       return ImageBitmapFormat::RGBA32;
27     case SurfaceFormat::R8G8B8:
28       return ImageBitmapFormat::RGB24;
29     case SurfaceFormat::B8G8R8:
30       return ImageBitmapFormat::BGR24;
31     case SurfaceFormat::HSV:
32       return ImageBitmapFormat::HSV;
33     case SurfaceFormat::Lab:
34       return ImageBitmapFormat::Lab;
35     case SurfaceFormat::Depth:
36       return ImageBitmapFormat::DEPTH;
37     case SurfaceFormat::A8:
38       return ImageBitmapFormat::GRAY8;
39     case SurfaceFormat::R5G6B5_UINT16:
40     case SurfaceFormat::YUV:
41     case SurfaceFormat::NV12:
42     case SurfaceFormat::P010:
43     case SurfaceFormat::P016:
44     case SurfaceFormat::UNKNOWN:
45     default:
46       return ImageBitmapFormat::EndGuard_;
47   }
48 }
49 
GetImageBitmapFormatFromPlanarYCbCrData(layers::PlanarYCbCrData const * aData)50 static ImageBitmapFormat GetImageBitmapFormatFromPlanarYCbCrData(
51     layers::PlanarYCbCrData const* aData) {
52   MOZ_ASSERT(aData);
53 
54   if (aData->mYSkip == 0 && aData->mCbSkip == 0 &&
55       aData->mCrSkip == 0) {  // Possibly three planes.
56     if (aData->mCbChannel >=
57             aData->mYChannel + aData->mYSize.height * aData->mYStride &&
58         aData->mCrChannel >=
59             aData->mCbChannel + aData->mCbCrSize.height *
60                                     aData->mCbCrStride) {  // Three planes.
61       if (aData->mYSize.height == aData->mCbCrSize.height) {
62         if (aData->mYSize.width == aData->mCbCrSize.width) {
63           return ImageBitmapFormat::YUV444P;
64         }
65         if (((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
66           return ImageBitmapFormat::YUV422P;
67         }
68       } else if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height) {
69         if (((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
70           return ImageBitmapFormat::YUV420P;
71         }
72       }
73     }
74   } else if (aData->mYSkip == 0 && aData->mCbSkip == 1 &&
75              aData->mCrSkip == 1) {  // Possibly two planes.
76     if (aData->mCbChannel >=
77             aData->mYChannel + aData->mYSize.height * aData->mYStride &&
78         aData->mCbChannel == aData->mCrChannel - 1) {  // Two planes.
79       if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height &&
80           ((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
81         return ImageBitmapFormat::YUV420SP_NV12;  // Y-Cb-Cr
82       }
83     } else if (aData->mCrChannel >=
84                    aData->mYChannel + aData->mYSize.height * aData->mYStride &&
85                aData->mCrChannel == aData->mCbChannel - 1) {  // Two planes.
86       if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height &&
87           ((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
88         return ImageBitmapFormat::YUV420SP_NV21;  // Y-Cr-Cb
89       }
90     }
91   }
92 
93   return ImageBitmapFormat::EndGuard_;
94 }
95 
96 // ImageUtils::Impl implements the _generic_ algorithm which always readback
97 // data in RGBA format into CPU memory.
98 // Since layers::CairoImage is just a warpper to a DataSourceSurface, the
99 // implementation of CairoSurfaceImpl is nothing different to the generic
100 // version.
101 class ImageUtils::Impl {
102  public:
Impl(layers::Image * aImage)103   explicit Impl(layers::Image* aImage) : mImage(aImage), mSurface(nullptr) {}
104 
105   virtual ~Impl() = default;
106 
GetFormat() const107   virtual ImageBitmapFormat GetFormat() const {
108     return GetImageBitmapFormatFromSurfaceFromat(Surface()->GetFormat());
109   }
110 
GetBufferLength() const111   virtual uint32_t GetBufferLength() const {
112     DataSourceSurface::ScopedMap map(Surface(), DataSourceSurface::READ);
113     const uint32_t stride = map.GetStride();
114     const IntSize size = Surface()->GetSize();
115     return (uint32_t)(size.height * stride);
116   }
117 
118  protected:
119   Impl() = default;
120 
Surface() const121   DataSourceSurface* Surface() const {
122     if (!mSurface) {
123       RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
124       MOZ_ASSERT(surface);
125 
126       mSurface = surface->GetDataSurface();
127       MOZ_ASSERT(mSurface);
128     }
129 
130     return mSurface.get();
131   }
132 
133   RefPtr<layers::Image> mImage;
134   mutable RefPtr<DataSourceSurface> mSurface;
135 };
136 
137 // YUVImpl is optimized for the layers::PlanarYCbCrImage and layers::NVImage.
138 // This implementation does not readback data in RGBA format but keep it in YUV
139 // format family.
140 class YUVImpl final : public ImageUtils::Impl {
141  public:
YUVImpl(layers::Image * aImage)142   explicit YUVImpl(layers::Image* aImage) : Impl(aImage) {
143     MOZ_ASSERT(aImage->GetFormat() == ImageFormat::PLANAR_YCBCR ||
144                aImage->GetFormat() == ImageFormat::NV_IMAGE);
145   }
146 
GetFormat() const147   ImageBitmapFormat GetFormat() const override {
148     return GetImageBitmapFormatFromPlanarYCbCrData(GetPlanarYCbCrData());
149   }
150 
GetBufferLength() const151   uint32_t GetBufferLength() const override {
152     if (mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
153       return mImage->AsPlanarYCbCrImage()->GetDataSize();
154     }
155     return mImage->AsNVImage()->GetBufferSize();
156   }
157 
158  private:
GetPlanarYCbCrData() const159   const PlanarYCbCrData* GetPlanarYCbCrData() const {
160     if (mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
161       return mImage->AsPlanarYCbCrImage()->GetData();
162     }
163     return mImage->AsNVImage()->GetData();
164   }
165 };
166 
167 // TODO: optimize for other platforms.
168 // For Windows: implement D3D9RGB32TextureImpl and D3D11ShareHandleTextureImpl.
169 // Others: SharedBGRImpl, MACIOSrufaceImpl, GLImageImpl, SurfaceTextureImpl
170 //         EGLImageImpl and OverlayImegImpl.
171 
ImageUtils(layers::Image * aImage)172 ImageUtils::ImageUtils(layers::Image* aImage) : mImpl(nullptr) {
173   MOZ_ASSERT(aImage, "Create ImageUtils with nullptr.");
174   switch (aImage->GetFormat()) {
175     case mozilla::ImageFormat::PLANAR_YCBCR:
176     case mozilla::ImageFormat::NV_IMAGE:
177       mImpl = new YUVImpl(aImage);
178       break;
179     case mozilla::ImageFormat::CAIRO_SURFACE:
180     default:
181       mImpl = new Impl(aImage);
182   }
183 }
184 
~ImageUtils()185 ImageUtils::~ImageUtils() {
186   if (mImpl) {
187     delete mImpl;
188     mImpl = nullptr;
189   }
190 }
191 
GetFormat() const192 ImageBitmapFormat ImageUtils::GetFormat() const {
193   MOZ_ASSERT(mImpl);
194   return mImpl->GetFormat();
195 }
196 
GetBufferLength() const197 uint32_t ImageUtils::GetBufferLength() const {
198   MOZ_ASSERT(mImpl);
199   return mImpl->GetBufferLength();
200 }
201 
202 }  // namespace mozilla::dom
203