/src/mozilla-central/gfx/layers/ImageDataSerializer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
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 "ImageDataSerializer.h" |
8 | | #include "gfx2DGlue.h" // for SurfaceFormatToImageFormat |
9 | | #include "mozilla/gfx/Point.h" // for IntSize |
10 | | #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc |
11 | | #include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory |
12 | | #include "mozilla/gfx/Logging.h" // for gfxDebug |
13 | | #include "mozilla/gfx/Tools.h" // for GetAlignedStride, etc |
14 | | #include "mozilla/gfx/Types.h" |
15 | | #include "mozilla/mozalloc.h" // for operator delete, etc |
16 | | #include "YCbCrUtils.h" // for YCbCr conversions |
17 | | |
18 | | namespace mozilla { |
19 | | namespace layers { |
20 | | namespace ImageDataSerializer { |
21 | | |
22 | | using namespace gfx; |
23 | | |
24 | | int32_t |
25 | | ComputeRGBStride(SurfaceFormat aFormat, int32_t aWidth) |
26 | 0 | { |
27 | 0 | return GetAlignedStride<4>(aWidth, BytesPerPixel(aFormat)); |
28 | 0 | } |
29 | | |
30 | | int32_t |
31 | | GetRGBStride(const RGBDescriptor& aDescriptor) |
32 | 0 | { |
33 | 0 | return ComputeRGBStride(aDescriptor.format(), aDescriptor.size().width); |
34 | 0 | } |
35 | | |
36 | | uint32_t |
37 | | ComputeRGBBufferSize(IntSize aSize, SurfaceFormat aFormat) |
38 | 0 | { |
39 | 0 | MOZ_ASSERT(aSize.height >= 0 && aSize.width >= 0); |
40 | 0 |
|
41 | 0 | // This takes care of checking whether there could be overflow |
42 | 0 | // with enough margin for the metadata. |
43 | 0 | if (!gfx::Factory::AllowedSurfaceSize(aSize)) { |
44 | 0 | return 0; |
45 | 0 | } |
46 | 0 | |
47 | 0 | // Note we're passing height instad of the bpp parameter, but the end |
48 | 0 | // result is the same - and the bpp was already taken care of in the |
49 | 0 | // ComputeRGBStride function. |
50 | 0 | int32_t bufsize = GetAlignedStride<16>(ComputeRGBStride(aFormat, aSize.width), |
51 | 0 | aSize.height); |
52 | 0 |
|
53 | 0 | if (bufsize < 0) { |
54 | 0 | // This should not be possible thanks to Factory::AllowedSurfaceSize |
55 | 0 | return 0; |
56 | 0 | } |
57 | 0 | |
58 | 0 | return bufsize; |
59 | 0 | } |
60 | | |
61 | | |
62 | | |
63 | | // Minimum required shmem size in bytes |
64 | | uint32_t |
65 | | ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride, |
66 | | const gfx::IntSize& aCbCrSize, int32_t aCbCrStride) |
67 | 0 | { |
68 | 0 | MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0); |
69 | 0 |
|
70 | 0 | if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || aCbCrSize.width < 0 || |
71 | 0 | !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) || |
72 | 0 | !gfx::Factory::AllowedSurfaceSize(IntSize(aCbCrStride, aCbCrSize.height))) { |
73 | 0 | return 0; |
74 | 0 | } |
75 | 0 | |
76 | 0 | // Overflow checks are performed in AllowedSurfaceSize |
77 | 0 | return GetAlignedStride<4>(aYSize.height, aYStride) + |
78 | 0 | 2 * GetAlignedStride<4>(aCbCrSize.height, aCbCrStride); |
79 | 0 | } |
80 | | |
81 | | uint32_t |
82 | | ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride, |
83 | | const gfx::IntSize& aCbCrSize, int32_t aCbCrStride, |
84 | | uint32_t aYOffset, uint32_t aCbOffset, |
85 | | uint32_t aCrOffset) |
86 | 0 | { |
87 | 0 | MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0); |
88 | 0 |
|
89 | 0 | if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || aCbCrSize.width < 0 || |
90 | 0 | !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) || |
91 | 0 | !gfx::Factory::AllowedSurfaceSize(IntSize(aCbCrStride, aCbCrSize.height))) { |
92 | 0 | return 0; |
93 | 0 | } |
94 | 0 | |
95 | 0 | uint32_t yLength = GetAlignedStride<4>(aYStride, aYSize.height); |
96 | 0 | uint32_t cbCrLength = GetAlignedStride<4>(aCbCrStride, aCbCrSize.height); |
97 | 0 | if (yLength == 0 || cbCrLength == 0) { |
98 | 0 | return 0; |
99 | 0 | } |
100 | 0 | |
101 | 0 | CheckedInt<uint32_t> yEnd = aYOffset; |
102 | 0 | yEnd += yLength; |
103 | 0 | CheckedInt<uint32_t> cbEnd = aCbOffset; |
104 | 0 | cbEnd += cbCrLength; |
105 | 0 | CheckedInt<uint32_t> crEnd = aCrOffset; |
106 | 0 | crEnd += cbCrLength; |
107 | 0 |
|
108 | 0 | if (!yEnd.isValid() || !cbEnd.isValid() || !crEnd.isValid() || |
109 | 0 | yEnd.value() > aCbOffset || cbEnd.value() > aCrOffset) { |
110 | 0 | return 0; |
111 | 0 | } |
112 | 0 | |
113 | 0 | return crEnd.value(); |
114 | 0 | } |
115 | | |
116 | | uint32_t |
117 | | ComputeYCbCrBufferSize(uint32_t aBufferSize) |
118 | 0 | { |
119 | 0 | return GetAlignedStride<4>(aBufferSize, 1); |
120 | 0 | } |
121 | | |
122 | | void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight, |
123 | | int32_t cbCrStride, int32_t cbCrHeight, |
124 | | uint32_t& outYOffset, uint32_t& outCbOffset, |
125 | | uint32_t& outCrOffset) |
126 | 0 | { |
127 | 0 | outYOffset = 0; |
128 | 0 | outCbOffset = outYOffset + GetAlignedStride<4>(yStride, yHeight); |
129 | 0 | outCrOffset = outCbOffset + GetAlignedStride<4>(cbCrStride, cbCrHeight); |
130 | 0 | } |
131 | | |
132 | | gfx::SurfaceFormat FormatFromBufferDescriptor(const BufferDescriptor& aDescriptor) |
133 | 0 | { |
134 | 0 | switch (aDescriptor.type()) { |
135 | 0 | case BufferDescriptor::TRGBDescriptor: |
136 | 0 | return aDescriptor.get_RGBDescriptor().format(); |
137 | 0 | case BufferDescriptor::TYCbCrDescriptor: |
138 | 0 | return gfx::SurfaceFormat::YUV; |
139 | 0 | default: |
140 | 0 | MOZ_CRASH("GFX: FormatFromBufferDescriptor"); |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | | gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor) |
145 | 0 | { |
146 | 0 | switch (aDescriptor.type()) { |
147 | 0 | case BufferDescriptor::TRGBDescriptor: |
148 | 0 | return aDescriptor.get_RGBDescriptor().size(); |
149 | 0 | case BufferDescriptor::TYCbCrDescriptor: |
150 | 0 | return aDescriptor.get_YCbCrDescriptor().ySize(); |
151 | 0 | default: |
152 | 0 | MOZ_CRASH("GFX: SizeFromBufferDescriptor"); |
153 | 0 | } |
154 | 0 | } |
155 | | |
156 | | Maybe<gfx::IntSize> CbCrSizeFromBufferDescriptor(const BufferDescriptor& aDescriptor) |
157 | 0 | { |
158 | 0 | switch (aDescriptor.type()) { |
159 | 0 | case BufferDescriptor::TRGBDescriptor: |
160 | 0 | return Nothing(); |
161 | 0 | case BufferDescriptor::TYCbCrDescriptor: |
162 | 0 | return Some(aDescriptor.get_YCbCrDescriptor().cbCrSize()); |
163 | 0 | default: |
164 | 0 | MOZ_CRASH("GFX: CbCrSizeFromBufferDescriptor"); |
165 | 0 | } |
166 | 0 | } |
167 | | |
168 | | Maybe<YUVColorSpace> YUVColorSpaceFromBufferDescriptor(const BufferDescriptor& aDescriptor) |
169 | 0 | { |
170 | 0 | switch (aDescriptor.type()) { |
171 | 0 | case BufferDescriptor::TRGBDescriptor: |
172 | 0 | return Nothing(); |
173 | 0 | case BufferDescriptor::TYCbCrDescriptor: |
174 | 0 | return Some(aDescriptor.get_YCbCrDescriptor().yUVColorSpace()); |
175 | 0 | default: |
176 | 0 | MOZ_CRASH("GFX: YUVColorSpaceFromBufferDescriptor"); |
177 | 0 | } |
178 | 0 | } |
179 | | |
180 | | Maybe<uint32_t> BitDepthFromBufferDescriptor(const BufferDescriptor& aDescriptor) |
181 | 0 | { |
182 | 0 | switch (aDescriptor.type()) { |
183 | 0 | case BufferDescriptor::TRGBDescriptor: |
184 | 0 | return Nothing(); |
185 | 0 | case BufferDescriptor::TYCbCrDescriptor: |
186 | 0 | return Some(aDescriptor.get_YCbCrDescriptor().bitDepth()); |
187 | 0 | default: |
188 | 0 | MOZ_CRASH("GFX: BitDepthFromBufferDescriptor"); |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | | Maybe<StereoMode> StereoModeFromBufferDescriptor(const BufferDescriptor& aDescriptor) |
193 | 0 | { |
194 | 0 | switch (aDescriptor.type()) { |
195 | 0 | case BufferDescriptor::TRGBDescriptor: |
196 | 0 | return Nothing(); |
197 | 0 | case BufferDescriptor::TYCbCrDescriptor: |
198 | 0 | return Some(aDescriptor.get_YCbCrDescriptor().stereoMode()); |
199 | 0 | default: |
200 | 0 | MOZ_CRASH("GFX: StereoModeFromBufferDescriptor"); |
201 | 0 | } |
202 | 0 | } |
203 | | |
204 | | uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) |
205 | 0 | { |
206 | 0 | return aBuffer + aDescriptor.yOffset(); |
207 | 0 | } |
208 | | |
209 | | uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) |
210 | 0 | { |
211 | 0 | return aBuffer + aDescriptor.cbOffset(); |
212 | 0 | } |
213 | | |
214 | | uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) |
215 | 0 | { |
216 | 0 | return aBuffer + aDescriptor.crOffset(); |
217 | 0 | } |
218 | | |
219 | | already_AddRefed<DataSourceSurface> |
220 | | DataSourceSurfaceFromYCbCrDescriptor(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor, gfx::DataSourceSurface* aSurface) |
221 | 0 | { |
222 | 0 | gfx::IntSize ySize = aDescriptor.ySize(); |
223 | 0 |
|
224 | 0 | RefPtr<DataSourceSurface> result; |
225 | 0 | if (aSurface) { |
226 | 0 | MOZ_ASSERT(aSurface->GetSize() == ySize); |
227 | 0 | MOZ_ASSERT(aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8); |
228 | 0 | if (aSurface->GetSize() == ySize && |
229 | 0 | aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8) { |
230 | 0 | result = aSurface; |
231 | 0 | } |
232 | 0 | } |
233 | 0 |
|
234 | 0 | if (!result) { |
235 | 0 | result = |
236 | 0 | Factory::CreateDataSourceSurface(ySize, gfx::SurfaceFormat::B8G8R8X8); |
237 | 0 | } |
238 | 0 | if (NS_WARN_IF(!result)) { |
239 | 0 | return nullptr; |
240 | 0 | } |
241 | 0 | |
242 | 0 | DataSourceSurface::MappedSurface map; |
243 | 0 | if (NS_WARN_IF(!result->Map(DataSourceSurface::MapType::WRITE, &map))) { |
244 | 0 | return nullptr; |
245 | 0 | } |
246 | 0 | |
247 | 0 | layers::PlanarYCbCrData ycbcrData; |
248 | 0 | ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor); |
249 | 0 | ycbcrData.mYStride = aDescriptor.yStride(); |
250 | 0 | ycbcrData.mYSize = ySize; |
251 | 0 | ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor); |
252 | 0 | ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor); |
253 | 0 | ycbcrData.mCbCrStride = aDescriptor.cbCrStride(); |
254 | 0 | ycbcrData.mCbCrSize = aDescriptor.cbCrSize(); |
255 | 0 | ycbcrData.mPicSize = ySize; |
256 | 0 | ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace(); |
257 | 0 | ycbcrData.mBitDepth = aDescriptor.bitDepth(); |
258 | 0 |
|
259 | 0 | gfx::ConvertYCbCrToRGB(ycbcrData, |
260 | 0 | gfx::SurfaceFormat::B8G8R8X8, |
261 | 0 | ySize, |
262 | 0 | map.mData, |
263 | 0 | map.mStride); |
264 | 0 |
|
265 | 0 | result->Unmap(); |
266 | 0 | return result.forget(); |
267 | 0 | } |
268 | | |
269 | | void |
270 | | ConvertAndScaleFromYCbCrDescriptor(uint8_t* aBuffer, |
271 | | const YCbCrDescriptor& aDescriptor, |
272 | | const gfx::SurfaceFormat& aDestFormat, |
273 | | const gfx::IntSize& aDestSize, |
274 | | unsigned char* aDestBuffer, |
275 | | int32_t aStride) |
276 | 0 | { |
277 | 0 | MOZ_ASSERT(aBuffer); |
278 | 0 |
|
279 | 0 | layers::PlanarYCbCrData ycbcrData; |
280 | 0 | ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor); |
281 | 0 | ycbcrData.mYStride = aDescriptor.yStride();; |
282 | 0 | ycbcrData.mYSize = aDescriptor.ySize(); |
283 | 0 | ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor); |
284 | 0 | ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor); |
285 | 0 | ycbcrData.mCbCrStride = aDescriptor.cbCrStride(); |
286 | 0 | ycbcrData.mCbCrSize = aDescriptor.cbCrSize(); |
287 | 0 | ycbcrData.mPicSize = aDescriptor.ySize(); |
288 | 0 | ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace(); |
289 | 0 | ycbcrData.mBitDepth = aDescriptor.bitDepth(); |
290 | 0 |
|
291 | 0 | gfx::ConvertYCbCrToRGB(ycbcrData, aDestFormat, aDestSize, aDestBuffer, aStride); |
292 | 0 | } |
293 | | |
294 | | } // namespace ImageDataSerializer |
295 | | } // namespace layers |
296 | | } // namespace mozilla |