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"
10 using namespace WebGLTexelConversions
;
14 /** @class WebGLImageConverter
16 * This class is just a helper to implement WebGLContext::ConvertImage below.
20 * WebGLContext::ConvertImage has to handle hundreds of format conversion paths.
21 * It is important to minimize executable code size here. Instead of passing
22 * around 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
{
27 const size_t mWidth
, mHeight
;
28 const void* const mSrcStart
;
29 void* const mDstStart
;
30 const ptrdiff_t mSrcStride
, mDstStride
;
35 * Returns sizeof(texel)/sizeof(type). The point is that we will iterate over
36 * texels with typed pointers and this value will tell us by how much we need
37 * to increment these pointers to advance to the next texel.
39 template <WebGLTexelFormat Format
>
40 static size_t NumElementsPerTexelForFormat() {
42 case WebGLTexelFormat::A8
:
43 case WebGLTexelFormat::A16F
:
44 case WebGLTexelFormat::A32F
:
45 case WebGLTexelFormat::R8
:
46 case WebGLTexelFormat::R16F
:
47 case WebGLTexelFormat::R32F
:
48 case WebGLTexelFormat::RGB565
:
49 case WebGLTexelFormat::RGB11F11F10F
:
50 case WebGLTexelFormat::RGBA4444
:
51 case WebGLTexelFormat::RGBA5551
:
53 case WebGLTexelFormat::RA8
:
54 case WebGLTexelFormat::RA16F
:
55 case WebGLTexelFormat::RA32F
:
56 case WebGLTexelFormat::RG8
:
57 case WebGLTexelFormat::RG16F
:
58 case WebGLTexelFormat::RG32F
:
60 case WebGLTexelFormat::RGB8
:
61 case WebGLTexelFormat::RGB16F
:
62 case WebGLTexelFormat::RGB32F
:
64 case WebGLTexelFormat::RGBA8
:
65 case WebGLTexelFormat::RGBA16F
:
66 case WebGLTexelFormat::RGBA32F
:
67 case WebGLTexelFormat::BGRX8
:
68 case WebGLTexelFormat::BGRA8
:
71 MOZ_ASSERT(false, "Unknown texel format. Coding mistake?");
77 * This is the completely format-specific templatized conversion function,
78 * that will be instantiated hundreds of times for all different combinations.
79 * It is important to avoid generating useless code here. In particular, many
80 * instantiations of this function template will never be called, so we try
81 * to return immediately in these cases to allow the compiler to avoid
82 * generating useless code.
84 template <WebGLTexelFormat SrcFormat
, WebGLTexelFormat DstFormat
,
85 WebGLTexelPremultiplicationOp PremultiplicationOp
>
87 // check for never-called cases. We early-return to allow the compiler
88 // to avoid generating this code. It would be tempting to abort() instead,
89 // as returning early does leave the destination surface with uninitialized
90 // data, but that would not allow the compiler to avoid generating this
91 // code. So instead, we return early, so Success() will return false, and
92 // the caller must check that and abort in that case. See
93 // WebGLContext::ConvertImage.
95 if (SrcFormat
== DstFormat
&&
96 PremultiplicationOp
== WebGLTexelPremultiplicationOp::None
) {
97 // Should have used a fast exit path earlier, rather than entering this
98 // function. we explicitly return here to allow the compiler to avoid
99 // generating this code
103 // Only textures uploaded from DOM elements or ImageData can allow DstFormat
104 // != SrcFormat. DOM elements can only give BGRA8, BGRX8, A8, RGB565
105 // formats. See DOMElementToImageSurface. ImageData is always RGBA8. So all
106 // other SrcFormat will always satisfy DstFormat==SrcFormat, so we can avoid
107 // compiling the code for all the unreachable paths.
108 const bool CanSrcFormatComeFromDOMElementOrImageData
=
109 SrcFormat
== WebGLTexelFormat::BGRA8
||
110 SrcFormat
== WebGLTexelFormat::BGRX8
||
111 SrcFormat
== WebGLTexelFormat::A8
||
112 SrcFormat
== WebGLTexelFormat::RGB565
||
113 SrcFormat
== WebGLTexelFormat::RGBA8
;
114 if (!CanSrcFormatComeFromDOMElementOrImageData
&& SrcFormat
!= DstFormat
) {
118 // Likewise, only textures uploaded from DOM elements or ImageData can
119 // possibly have to be unpremultiplied.
120 if (!CanSrcFormatComeFromDOMElementOrImageData
&&
121 PremultiplicationOp
== WebGLTexelPremultiplicationOp::Unpremultiply
) {
125 // there is no point in premultiplication/unpremultiplication
126 // in the following cases:
127 // - the source format has no alpha
128 // - the source format has no color
129 // - the destination format has no color
130 if (!HasAlpha(SrcFormat
) || !HasColor(SrcFormat
) || !HasColor(DstFormat
)) {
131 if (PremultiplicationOp
!= WebGLTexelPremultiplicationOp::None
) {
136 // end of early return cases.
138 MOZ_ASSERT(!mAlreadyRun
, "converter should be run only once!");
141 // gather some compile-time meta-data about the formats at hand.
143 using SrcType
= typename DataTypeForFormat
<SrcFormat
>::Type
;
144 using DstType
= typename DataTypeForFormat
<DstFormat
>::Type
;
146 const WebGLTexelFormat IntermediateSrcFormat
=
147 IntermediateFormat
<SrcFormat
>::Value
;
148 const WebGLTexelFormat IntermediateDstFormat
=
149 IntermediateFormat
<DstFormat
>::Value
;
150 using IntermediateSrcType
=
151 typename DataTypeForFormat
<IntermediateSrcFormat
>::Type
;
152 using IntermediateDstType
=
153 typename DataTypeForFormat
<IntermediateDstFormat
>::Type
;
155 const size_t NumElementsPerSrcTexel
=
156 NumElementsPerTexelForFormat
<SrcFormat
>();
157 const size_t NumElementsPerDstTexel
=
158 NumElementsPerTexelForFormat
<DstFormat
>();
159 const size_t MaxElementsPerTexel
= 4;
160 MOZ_ASSERT(NumElementsPerSrcTexel
<= MaxElementsPerTexel
,
162 MOZ_ASSERT(NumElementsPerDstTexel
<= MaxElementsPerTexel
,
165 // we assume that the strides are multiples of the sizeof of respective
166 // types. this assumption will allow us to iterate over src and dst images
167 // using typed pointers, e.g. uint8_t* or uint16_t* or float*, instead of
168 // untyped pointers. So this assumption allows us to write cleaner and safer
169 // code, but it might not be true forever and if it eventually becomes
170 // wrong, we'll have to revert to always iterating using uint8_t* pointers
171 // regardless of the types at hand.
173 mSrcStride
% sizeof(SrcType
) == 0 && mDstStride
% sizeof(DstType
) == 0,
174 "Unsupported: texture stride is not a multiple of sizeof(type)");
175 const ptrdiff_t srcStrideInElements
=
176 mSrcStride
/ static_cast<ptrdiff_t>(sizeof(SrcType
));
177 const ptrdiff_t dstStrideInElements
=
178 mDstStride
/ static_cast<ptrdiff_t>(sizeof(DstType
));
179 // Pop quiz: What's `ptrdiff_t(-16) / sizeof(int32_t)`?
180 // Did you guess +4611686018427387900?
181 MOZ_ASSERT(bool(srcStrideInElements
< 0) == bool(mSrcStride
< 0));
182 MOZ_ASSERT(bool(dstStrideInElements
< 0) == bool(mDstStride
< 0));
184 const SrcType
* srcRowStart
= static_cast<const SrcType
*>(mSrcStart
);
185 DstType
* dstRowStart
= static_cast<DstType
*>(mDstStart
);
187 // the loop performing the texture format conversion
188 for (size_t i
= 0; i
< mHeight
; ++i
) {
189 const SrcType
* srcRowEnd
= srcRowStart
+ mWidth
* NumElementsPerSrcTexel
;
190 const SrcType
* srcPtr
= srcRowStart
;
191 DstType
* dstPtr
= dstRowStart
;
192 while (srcPtr
!= srcRowEnd
) {
193 // convert a single texel. We proceed in 3 steps: unpack the source
194 // texel so the corresponding interchange format (e.g. unpack RGB565 to
195 // RGBA8), convert the resulting data type to the destination type (e.g.
196 // convert from RGBA8 to RGBA32F), and finally pack the destination
197 // texel (e.g. pack RGBA32F to RGB32F).
198 IntermediateSrcType unpackedSrc
[MaxElementsPerTexel
];
199 IntermediateDstType unpackedDst
[MaxElementsPerTexel
];
201 // unpack a src texel to corresponding intermediate src format.
202 // for example, unpack RGB565 to RGBA8
203 unpack
<SrcFormat
>(srcPtr
, unpackedSrc
);
204 // convert the data type to the destination type, if needed.
205 // for example, convert RGBA8 to RGBA32F
206 convertType(unpackedSrc
, unpackedDst
);
207 // pack the destination texel.
208 // for example, pack RGBA32F to RGB32F
209 pack
<DstFormat
, PremultiplicationOp
>(unpackedDst
, dstPtr
);
211 srcPtr
+= NumElementsPerSrcTexel
;
212 dstPtr
+= NumElementsPerDstTexel
;
214 srcRowStart
+= srcStrideInElements
;
215 dstRowStart
+= dstStrideInElements
;
221 template <WebGLTexelFormat SrcFormat
, WebGLTexelFormat DstFormat
>
222 void run(WebGLTexelPremultiplicationOp premultiplicationOp
) {
223 #define WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(PremultiplicationOp) \
224 case PremultiplicationOp: \
225 return run<SrcFormat, DstFormat, PremultiplicationOp>();
227 switch (premultiplicationOp
) {
228 WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(
229 WebGLTexelPremultiplicationOp::None
)
230 WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(
231 WebGLTexelPremultiplicationOp::Premultiply
)
232 WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(
233 WebGLTexelPremultiplicationOp::Unpremultiply
)
235 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
238 #undef WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP
241 template <WebGLTexelFormat SrcFormat
>
242 void run(WebGLTexelFormat dstFormat
,
243 WebGLTexelPremultiplicationOp premultiplicationOp
) {
244 #define WEBGLIMAGECONVERTER_CASE_DSTFORMAT(DstFormat) \
246 return run<SrcFormat, DstFormat>(premultiplicationOp);
250 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A8
)
251 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A16F
)
252 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A32F
)
253 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R8
)
254 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R16F
)
255 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R32F
)
257 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA8
)
258 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA16F
)
259 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA32F
)
260 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RG8
)
261 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RG16F
)
262 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RG32F
)
264 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB565
)
265 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB8
)
266 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB11F11F10F
)
267 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB16F
)
268 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB32F
)
270 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA4444
)
271 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA5551
)
272 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA8
)
273 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA16F
)
274 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA32F
)
275 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::BGRA8
)
278 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
281 #undef WEBGLIMAGECONVERTER_CASE_DSTFORMAT
285 void run(WebGLTexelFormat srcFormat
, WebGLTexelFormat dstFormat
,
286 WebGLTexelPremultiplicationOp premultiplicationOp
) {
287 #define WEBGLIMAGECONVERTER_CASE_SRCFORMAT(SrcFormat) \
289 return run<SrcFormat>(dstFormat, premultiplicationOp);
293 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A8
)
294 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A16F
)
295 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A32F
)
296 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R8
)
297 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R16F
)
298 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R32F
)
300 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA8
)
301 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA16F
)
302 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA32F
)
304 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB565
)
305 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB8
)
306 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB16F
)
307 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB32F
)
309 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA4444
)
310 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA5551
)
311 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA8
)
312 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA16F
)
313 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA32F
)
314 // DOM element source formats
315 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRX8
)
316 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRA8
)
319 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
322 #undef WEBGLIMAGECONVERTER_CASE_SRCFORMAT
325 WebGLImageConverter(size_t width
, size_t height
, const void* srcStart
,
326 void* dstStart
, ptrdiff_t srcStride
, ptrdiff_t dstStride
)
331 mSrcStride(srcStride
),
332 mDstStride(dstStride
),
336 bool Success() const { return mSuccess
; }
339 } // end anonymous namespace
341 bool ConvertImage(size_t width
, size_t height
, const void* srcBegin
,
342 size_t srcStride
, gl::OriginPos srcOrigin
,
343 WebGLTexelFormat srcFormat
, bool srcPremultiplied
,
344 void* dstBegin
, size_t dstStride
, gl::OriginPos dstOrigin
,
345 WebGLTexelFormat dstFormat
, bool dstPremultiplied
,
346 bool* const out_wasTrivial
) {
347 *out_wasTrivial
= true;
349 if (srcFormat
== WebGLTexelFormat::FormatNotSupportingAnyConversion
||
350 dstFormat
== WebGLTexelFormat::FormatNotSupportingAnyConversion
) {
354 if (!width
|| !height
) return true;
356 const bool shouldYFlip
= (srcOrigin
!= dstOrigin
);
358 const bool canSkipPremult
=
359 (!HasAlpha(srcFormat
) || !HasColor(srcFormat
) || !HasColor(dstFormat
));
361 WebGLTexelPremultiplicationOp premultOp
;
362 if (canSkipPremult
) {
363 premultOp
= WebGLTexelPremultiplicationOp::None
;
364 } else if (!srcPremultiplied
&& dstPremultiplied
) {
365 premultOp
= WebGLTexelPremultiplicationOp::Premultiply
;
366 } else if (srcPremultiplied
&& !dstPremultiplied
) {
367 premultOp
= WebGLTexelPremultiplicationOp::Unpremultiply
;
369 premultOp
= WebGLTexelPremultiplicationOp::None
;
372 const uint8_t* srcItr
= (const uint8_t*)srcBegin
;
373 const uint8_t* const srcEnd
= srcItr
+ srcStride
* height
;
374 uint8_t* dstItr
= (uint8_t*)dstBegin
;
375 ptrdiff_t dstItrStride
= dstStride
;
377 dstItr
= dstItr
+ dstStride
* (height
- 1);
378 dstItrStride
= -dstItrStride
;
381 if (srcFormat
== dstFormat
&&
382 premultOp
== WebGLTexelPremultiplicationOp::None
) {
383 // Fast exit path: we just have to memcpy all the rows.
385 const auto bytesPerPixel
= TexelBytesForFormat(srcFormat
);
386 const size_t bytesPerRow
= bytesPerPixel
* width
;
388 while (srcItr
!= srcEnd
) {
389 memcpy(dstItr
, srcItr
, bytesPerRow
);
391 dstItr
+= dstItrStride
;
396 *out_wasTrivial
= false;
398 WebGLImageConverter
converter(width
, height
, srcItr
, dstItr
, srcStride
,
400 converter
.run(srcFormat
, dstFormat
, premultOp
);
402 if (!converter
.Success()) {
403 // the dst image may be left uninitialized, so we better not try to
404 // continue even in release builds. This should never happen anyway,
405 // and would be a bug in our code.
406 MOZ_CRASH("programming mistake in WebGL texture conversions");
412 } // end namespace mozilla