Bumping manifests a=b2g-bump
[gecko.git] / dom / canvas / WebGLTexelConversions.cpp
blob99b177e6bfb83785710ce5dc55dc2bb1af6093a4
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "WebGLContext.h"
6 #include "WebGLTexelConversions.h"
8 namespace mozilla {
10 using namespace WebGLTexelConversions;
12 namespace {
14 /** @class WebGLImageConverter
16 * This class is just a helper to implement WebGLContext::ConvertImage below.
18 * Design comments:
20 * WebGLContext::ConvertImage has to handle hundreds of format conversion paths.
21 * It is important to minimize executable code size here. Instead of passing around
22 * a large number of function parameters hundreds of times, we create a
23 * WebGLImageConverter object once, storing these parameters, and then we call
24 * the run() method on it.
26 class WebGLImageConverter
28 const size_t mWidth, mHeight;
29 const void* const mSrcStart;
30 void* const mDstStart;
31 const ptrdiff_t mSrcStride, mDstStride;
32 bool mAlreadyRun;
33 bool mSuccess;
36 * Returns sizeof(texel)/sizeof(type). The point is that we will iterate over
37 * texels with typed pointers and this value will tell us by how much we need
38 * to increment these pointers to advance to the next texel.
40 template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
41 static size_t NumElementsPerTexelForFormat() {
42 switch (Format) {
43 case WebGLTexelFormat::R8:
44 case WebGLTexelFormat::A8:
45 case WebGLTexelFormat::R16F:
46 case WebGLTexelFormat::A16F:
47 case WebGLTexelFormat::R32F:
48 case WebGLTexelFormat::A32F:
49 case WebGLTexelFormat::RGBA5551:
50 case WebGLTexelFormat::RGBA4444:
51 case WebGLTexelFormat::RGB565:
52 return 1;
53 case WebGLTexelFormat::RA8:
54 case WebGLTexelFormat::RA16F:
55 case WebGLTexelFormat::RA32F:
56 return 2;
57 case WebGLTexelFormat::RGB8:
58 case WebGLTexelFormat::RGB16F:
59 case WebGLTexelFormat::RGB32F:
60 return 3;
61 case WebGLTexelFormat::RGBA8:
62 case WebGLTexelFormat::BGRA8:
63 case WebGLTexelFormat::BGRX8:
64 case WebGLTexelFormat::RGBA16F:
65 case WebGLTexelFormat::RGBA32F:
66 return 4;
67 default:
68 MOZ_ASSERT(false, "Unknown texel format. Coding mistake?");
69 return 0;
74 * This is the completely format-specific templatized conversion function,
75 * that will be instantiated hundreds of times for all different combinations.
76 * It is important to avoid generating useless code here. In particular, many
77 * instantiations of this function template will never be called, so we try
78 * to return immediately in these cases to allow the compiler to avoid generating
79 * useless code.
81 template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) SrcFormat,
82 MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) DstFormat,
83 MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelPremultiplicationOp) PremultiplicationOp>
84 void run()
86 // check for never-called cases. We early-return to allow the compiler
87 // to avoid generating this code. It would be tempting to abort() instead,
88 // as returning early does leave the destination surface with uninitialized
89 // data, but that would not allow the compiler to avoid generating this code.
90 // So instead, we return early, so Success() will return false, and the caller
91 // must check that and abort in that case. See WebGLContext::ConvertImage.
93 if (SrcFormat == DstFormat &&
94 PremultiplicationOp == WebGLTexelPremultiplicationOp::None)
96 // Should have used a fast exit path earlier, rather than entering this function.
97 // we explicitly return here to allow the compiler to avoid generating this code
98 return;
101 // Only textures uploaded from DOM elements or ImageData can allow DstFormat != SrcFormat.
102 // DOM elements can only give BGRA8, BGRX8, A8, RGB565 formats. See DOMElementToImageSurface.
103 // ImageData is always RGBA8. So all other SrcFormat will always satisfy DstFormat==SrcFormat,
104 // so we can avoid compiling the code for all the unreachable paths.
105 const bool CanSrcFormatComeFromDOMElementOrImageData
106 = SrcFormat == WebGLTexelFormat::BGRA8 ||
107 SrcFormat == WebGLTexelFormat::BGRX8 ||
108 SrcFormat == WebGLTexelFormat::A8 ||
109 SrcFormat == WebGLTexelFormat::RGB565 ||
110 SrcFormat == WebGLTexelFormat::RGBA8;
111 if (!CanSrcFormatComeFromDOMElementOrImageData &&
112 SrcFormat != DstFormat)
114 return;
117 // Likewise, only textures uploaded from DOM elements or ImageData can possibly have to be unpremultiplied.
118 if (!CanSrcFormatComeFromDOMElementOrImageData &&
119 PremultiplicationOp == WebGLTexelPremultiplicationOp::Unpremultiply)
121 return;
124 // there is no point in premultiplication/unpremultiplication
125 // in the following cases:
126 // - the source format has no alpha
127 // - the source format has no color
128 // - the destination format has no color
129 if (!HasAlpha(SrcFormat) ||
130 !HasColor(SrcFormat) ||
131 !HasColor(DstFormat))
134 if (PremultiplicationOp != WebGLTexelPremultiplicationOp::None)
136 return;
140 // end of early return cases.
142 MOZ_ASSERT(!mAlreadyRun, "converter should be run only once!");
143 mAlreadyRun = true;
145 // gather some compile-time meta-data about the formats at hand.
147 typedef
148 typename DataTypeForFormat<SrcFormat>::Type
149 SrcType;
150 typedef
151 typename DataTypeForFormat<DstFormat>::Type
152 DstType;
154 const MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) IntermediateSrcFormat
155 = IntermediateFormat<SrcFormat>::Value;
156 const MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) IntermediateDstFormat
157 = IntermediateFormat<DstFormat>::Value;
158 typedef
159 typename DataTypeForFormat<IntermediateSrcFormat>::Type
160 IntermediateSrcType;
161 typedef
162 typename DataTypeForFormat<IntermediateDstFormat>::Type
163 IntermediateDstType;
165 const size_t NumElementsPerSrcTexel = NumElementsPerTexelForFormat<SrcFormat>();
166 const size_t NumElementsPerDstTexel = NumElementsPerTexelForFormat<DstFormat>();
167 const size_t MaxElementsPerTexel = 4;
168 MOZ_ASSERT(NumElementsPerSrcTexel <= MaxElementsPerTexel, "unhandled format");
169 MOZ_ASSERT(NumElementsPerDstTexel <= MaxElementsPerTexel, "unhandled format");
171 // we assume that the strides are multiples of the sizeof of respective types.
172 // this assumption will allow us to iterate over src and dst images using typed
173 // pointers, e.g. uint8_t* or uint16_t* or float*, instead of untyped pointers.
174 // So this assumption allows us to write cleaner and safer code, but it might
175 // not be true forever and if it eventually becomes wrong, we'll have to revert
176 // to always iterating using uint8_t* pointers regardless of the types at hand.
177 MOZ_ASSERT(mSrcStride % sizeof(SrcType) == 0 &&
178 mDstStride % sizeof(DstType) == 0,
179 "Unsupported: texture stride is not a multiple of sizeof(type)");
180 const ptrdiff_t srcStrideInElements = mSrcStride / sizeof(SrcType);
181 const ptrdiff_t dstStrideInElements = mDstStride / sizeof(DstType);
183 const SrcType* srcRowStart = static_cast<const SrcType*>(mSrcStart);
184 DstType* dstRowStart = static_cast<DstType*>(mDstStart);
186 // the loop performing the texture format conversion
187 for (size_t i = 0; i < mHeight; ++i) {
188 const SrcType* srcRowEnd = srcRowStart + mWidth * NumElementsPerSrcTexel;
189 const SrcType* srcPtr = srcRowStart;
190 DstType* dstPtr = dstRowStart;
191 while (srcPtr != srcRowEnd) {
192 // convert a single texel. We proceed in 3 steps: unpack the source texel
193 // so the corresponding interchange format (e.g. unpack RGB565 to RGBA8),
194 // convert the resulting data type to the destination type (e.g. convert
195 // from RGBA8 to RGBA32F), and finally pack the destination texel
196 // (e.g. pack RGBA32F to RGB32F).
197 IntermediateSrcType unpackedSrc[MaxElementsPerTexel];
198 IntermediateDstType unpackedDst[MaxElementsPerTexel];
200 // unpack a src texel to corresponding intermediate src format.
201 // for example, unpack RGB565 to RGBA8
202 unpack<SrcFormat>(srcPtr, unpackedSrc);
203 // convert the data type to the destination type, if needed.
204 // for example, convert RGBA8 to RGBA32F
205 convertType(unpackedSrc, unpackedDst);
206 // pack the destination texel.
207 // for example, pack RGBA32F to RGB32F
208 pack<DstFormat, PremultiplicationOp>(unpackedDst, dstPtr);
210 srcPtr += NumElementsPerSrcTexel;
211 dstPtr += NumElementsPerDstTexel;
213 srcRowStart += srcStrideInElements;
214 dstRowStart += dstStrideInElements;
217 mSuccess = true;
218 return;
221 template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) SrcFormat,
222 MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) DstFormat>
223 void run(WebGLTexelPremultiplicationOp premultiplicationOp)
225 #define WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(PremultiplicationOp) \
226 case PremultiplicationOp: \
227 return run<SrcFormat, DstFormat, PremultiplicationOp>();
229 switch (premultiplicationOp) {
230 WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(WebGLTexelPremultiplicationOp::None)
231 WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(WebGLTexelPremultiplicationOp::Premultiply)
232 WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(WebGLTexelPremultiplicationOp::Unpremultiply)
233 default:
234 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
237 #undef WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP
240 template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) SrcFormat>
241 void run(WebGLTexelFormat dstFormat,
242 WebGLTexelPremultiplicationOp premultiplicationOp)
244 #define WEBGLIMAGECONVERTER_CASE_DSTFORMAT(DstFormat) \
245 case DstFormat: \
246 return run<SrcFormat, DstFormat>(premultiplicationOp);
248 switch (dstFormat) {
249 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R8)
250 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A8)
251 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R16F)
252 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A16F)
253 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R32F)
254 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A32F)
255 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA8)
256 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA16F)
257 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA32F)
258 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB8)
259 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB565)
260 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB16F)
261 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB32F)
262 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA8)
263 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA5551)
264 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA4444)
265 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA16F)
266 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA32F)
267 default:
268 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
271 #undef WEBGLIMAGECONVERTER_CASE_DSTFORMAT
274 public:
276 void run(WebGLTexelFormat srcFormat,
277 WebGLTexelFormat dstFormat,
278 WebGLTexelPremultiplicationOp premultiplicationOp)
280 #define WEBGLIMAGECONVERTER_CASE_SRCFORMAT(SrcFormat) \
281 case SrcFormat: \
282 return run<SrcFormat>(dstFormat, premultiplicationOp);
284 switch (srcFormat) {
285 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R8)
286 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A8)
287 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R16F)
288 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A16F)
289 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R32F)
290 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A32F)
291 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA8)
292 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA16F)
293 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA32F)
294 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB8)
295 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRX8) // source format only
296 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB565)
297 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB16F)
298 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB32F)
299 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA8)
300 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRA8)
301 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA5551)
302 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA4444)
303 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA16F)
304 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA32F)
305 default:
306 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
309 #undef WEBGLIMAGECONVERTER_CASE_SRCFORMAT
312 WebGLImageConverter(size_t width, size_t height,
313 const void* srcStart, void* dstStart,
314 ptrdiff_t srcStride, ptrdiff_t dstStride)
315 : mWidth(width), mHeight(height),
316 mSrcStart(srcStart), mDstStart(dstStart),
317 mSrcStride(srcStride), mDstStride(dstStride),
318 mAlreadyRun(false), mSuccess(false)
321 bool Success() const {
322 return mSuccess;
326 } // end anonymous namespace
328 bool
329 WebGLContext::ConvertImage(size_t width, size_t height, size_t srcStride, size_t dstStride,
330 const uint8_t* src, uint8_t* dst,
331 WebGLTexelFormat srcFormat, bool srcPremultiplied,
332 WebGLTexelFormat dstFormat, bool dstPremultiplied,
333 size_t dstTexelSize)
335 if (width <= 0 || height <= 0)
336 return true;
338 const bool FormatsRequireNoPremultiplicationOp =
339 !HasAlpha(srcFormat) ||
340 !HasColor(srcFormat) ||
341 !HasColor(dstFormat);
343 if (srcFormat == dstFormat &&
344 (FormatsRequireNoPremultiplicationOp || srcPremultiplied == dstPremultiplied))
346 // fast exit path: we just have to memcpy all the rows.
348 // The case where absolutely nothing needs to be done is supposed to have
349 // been handled earlier (in TexImage2D_base, etc).
351 // So the case we're handling here is when even though no format conversion is needed,
352 // we still might have to flip vertically and/or to adjust to a different stride.
354 MOZ_ASSERT(mPixelStoreFlipY || srcStride != dstStride, "Performance trap -- should handle this case earlier, to avoid memcpy");
356 size_t row_size = width * dstTexelSize; // doesn't matter, src and dst formats agree
357 const uint8_t* ptr = src;
358 const uint8_t* src_end = src + height * srcStride;
360 uint8_t* dst_row = mPixelStoreFlipY
361 ? dst + (height-1) * dstStride
362 : dst;
363 ptrdiff_t dstStrideSigned(dstStride);
364 ptrdiff_t dst_delta = mPixelStoreFlipY ? -dstStrideSigned : dstStrideSigned;
366 while(ptr != src_end) {
367 memcpy(dst_row, ptr, row_size);
368 ptr += srcStride;
369 dst_row += dst_delta;
371 return true;
374 if (srcFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion ||
375 dstFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion)
377 return false;
380 uint8_t* dstStart = dst;
381 ptrdiff_t signedDstStride = dstStride;
382 if (mPixelStoreFlipY) {
383 dstStart = dst + (height - 1) * dstStride;
384 signedDstStride = -signedDstStride;
387 WebGLImageConverter converter(width, height, src, dstStart, srcStride, signedDstStride);
389 const WebGLTexelPremultiplicationOp premultiplicationOp
390 = FormatsRequireNoPremultiplicationOp ? WebGLTexelPremultiplicationOp::None
391 : (!srcPremultiplied && dstPremultiplied) ? WebGLTexelPremultiplicationOp::Premultiply
392 : (srcPremultiplied && !dstPremultiplied) ? WebGLTexelPremultiplicationOp::Unpremultiply
393 : WebGLTexelPremultiplicationOp::None;
395 converter.run(srcFormat, dstFormat, premultiplicationOp);
397 if (!converter.Success()) {
398 // the dst image may be left uninitialized, so we better not try to
399 // continue even in release builds. This should never happen anyway,
400 // and would be a bug in our code.
401 NS_RUNTIMEABORT("programming mistake in WebGL texture conversions");
404 return true;
407 } // end namespace mozilla