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"
7 #include "GLBlitHelper.h"
11 using namespace WebGLTexelConversions
;
15 /** @class WebGLImageConverter
17 * This class is just a helper to implement WebGLContext::ConvertImage below.
21 * WebGLContext::ConvertImage has to handle hundreds of format conversion paths.
22 * It is important to minimize executable code size here. Instead of passing
23 * around a large number of function parameters hundreds of times, we create a
24 * WebGLImageConverter object once, storing these parameters, and then we call
25 * the run() method on it.
27 class WebGLImageConverter
{
28 const size_t mWidth
, mHeight
;
29 const void* const mSrcStart
;
30 void* const mDstStart
;
31 const ptrdiff_t mSrcStride
, mDstStride
;
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 <WebGLTexelFormat Format
>
41 static size_t NumElementsPerTexelForFormat() {
43 case WebGLTexelFormat::A8
:
44 case WebGLTexelFormat::A16F
:
45 case WebGLTexelFormat::A32F
:
46 case WebGLTexelFormat::R8
:
47 case WebGLTexelFormat::R16F
:
48 case WebGLTexelFormat::R32F
:
49 case WebGLTexelFormat::RGB565
:
50 case WebGLTexelFormat::RGB11F11F10F
:
51 case WebGLTexelFormat::RGBA4444
:
52 case WebGLTexelFormat::RGBA5551
:
54 case WebGLTexelFormat::RA8
:
55 case WebGLTexelFormat::RA16F
:
56 case WebGLTexelFormat::RA32F
:
57 case WebGLTexelFormat::RG8
:
58 case WebGLTexelFormat::RG16F
:
59 case WebGLTexelFormat::RG32F
:
61 case WebGLTexelFormat::RGB8
:
62 case WebGLTexelFormat::RGB16F
:
63 case WebGLTexelFormat::RGB32F
:
65 case WebGLTexelFormat::RGBA8
:
66 case WebGLTexelFormat::RGBA16F
:
67 case WebGLTexelFormat::RGBA32F
:
68 case WebGLTexelFormat::BGRX8
:
69 case WebGLTexelFormat::BGRA8
:
72 MOZ_ASSERT(false, "Unknown texel format. Coding mistake?");
78 * This is the completely format-specific templatized conversion function,
79 * that will be instantiated hundreds of times for all different combinations.
80 * It is important to avoid generating useless code here. In particular, many
81 * instantiations of this function template will never be called, so we try
82 * to return immediately in these cases to allow the compiler to avoid
83 * generating useless code.
85 template <WebGLTexelFormat SrcFormat
, WebGLTexelFormat DstFormat
,
86 WebGLTexelPremultiplicationOp PremultiplicationOp
,
87 dom::PredefinedColorSpace SrcColorSpace
,
88 dom::PredefinedColorSpace DstColorSpace
>
90 // check for never-called cases. We early-return to allow the compiler
91 // to avoid generating this code. It would be tempting to abort() instead,
92 // as returning early does leave the destination surface with uninitialized
93 // data, but that would not allow the compiler to avoid generating this
94 // code. So instead, we return early, so Success() will return false, and
95 // the caller must check that and abort in that case. See
96 // WebGLContext::ConvertImage.
98 bool sameColorSpace
= (SrcColorSpace
== DstColorSpace
);
100 if (SrcFormat
== DstFormat
&&
101 PremultiplicationOp
== WebGLTexelPremultiplicationOp::None
&&
103 // Should have used a fast exit path earlier, rather than entering this
104 // function. we explicitly return here to allow the compiler to avoid
105 // generating this code
109 // Only textures uploaded from DOM elements or ImageData can allow DstFormat
110 // != SrcFormat. DOM elements can only give BGRA8, BGRX8, A8, RGB565
111 // formats. See DOMElementToImageSurface. ImageData is always RGBA8. So all
112 // other SrcFormat will always satisfy DstFormat==SrcFormat, so we can avoid
113 // compiling the code for all the unreachable paths.
114 const bool CanSrcFormatComeFromDOMElementOrImageData
=
115 SrcFormat
== WebGLTexelFormat::BGRA8
||
116 SrcFormat
== WebGLTexelFormat::BGRX8
||
117 SrcFormat
== WebGLTexelFormat::A8
||
118 SrcFormat
== WebGLTexelFormat::RGB565
||
119 SrcFormat
== WebGLTexelFormat::RGBA8
;
120 if (!CanSrcFormatComeFromDOMElementOrImageData
&& SrcFormat
!= DstFormat
) {
124 // Likewise, only textures uploaded from DOM elements or ImageData can
125 // possibly have to be unpremultiplied.
126 if (!CanSrcFormatComeFromDOMElementOrImageData
&&
127 PremultiplicationOp
== WebGLTexelPremultiplicationOp::Unpremultiply
) {
131 // there is no point in premultiplication/unpremultiplication
132 // in the following cases:
133 // - the source format has no alpha
134 // - the source format has no color
135 // - the destination format has no color
136 if (!HasAlpha(SrcFormat
) || !HasColor(SrcFormat
) || !HasColor(DstFormat
)) {
137 if (PremultiplicationOp
!= WebGLTexelPremultiplicationOp::None
) {
142 // end of early return cases.
144 MOZ_ASSERT(!mAlreadyRun
, "converter should be run only once!");
147 // gather some compile-time meta-data about the formats at hand.
149 using SrcType
= typename DataTypeForFormat
<SrcFormat
>::Type
;
150 using DstType
= typename DataTypeForFormat
<DstFormat
>::Type
;
152 const WebGLTexelFormat IntermediateSrcFormat
=
153 IntermediateFormat
<SrcFormat
>::Value
;
154 const WebGLTexelFormat IntermediateDstFormat
=
155 IntermediateFormat
<DstFormat
>::Value
;
156 using IntermediateSrcType
=
157 typename DataTypeForFormat
<IntermediateSrcFormat
>::Type
;
158 using IntermediateDstType
=
159 typename DataTypeForFormat
<IntermediateDstFormat
>::Type
;
161 const size_t NumElementsPerSrcTexel
=
162 NumElementsPerTexelForFormat
<SrcFormat
>();
163 const size_t NumElementsPerDstTexel
=
164 NumElementsPerTexelForFormat
<DstFormat
>();
165 const size_t MaxElementsPerTexel
= 4;
166 MOZ_ASSERT(NumElementsPerSrcTexel
<= MaxElementsPerTexel
,
168 MOZ_ASSERT(NumElementsPerDstTexel
<= MaxElementsPerTexel
,
171 // we assume that the strides are multiples of the sizeof of respective
172 // types. this assumption will allow us to iterate over src and dst images
173 // using typed pointers, e.g. uint8_t* or uint16_t* or float*, instead of
174 // untyped pointers. So this assumption allows us to write cleaner and safer
175 // code, but it might not be true forever and if it eventually becomes
176 // wrong, we'll have to revert to always iterating using uint8_t* pointers
177 // regardless of the types at hand.
179 mSrcStride
% sizeof(SrcType
) == 0 && mDstStride
% sizeof(DstType
) == 0,
180 "Unsupported: texture stride is not a multiple of sizeof(type)");
181 const ptrdiff_t srcStrideInElements
=
182 mSrcStride
/ static_cast<ptrdiff_t>(sizeof(SrcType
));
183 const ptrdiff_t dstStrideInElements
=
184 mDstStride
/ static_cast<ptrdiff_t>(sizeof(DstType
));
185 // Pop quiz: What's `ptrdiff_t(-16) / sizeof(int32_t)`?
186 // Did you guess +4611686018427387900?
187 MOZ_ASSERT(bool(srcStrideInElements
< 0) == bool(mSrcStride
< 0));
188 MOZ_ASSERT(bool(dstStrideInElements
< 0) == bool(mDstStride
< 0));
190 const SrcType
* srcRowStart
= static_cast<const SrcType
*>(mSrcStart
);
191 DstType
* dstRowStart
= static_cast<DstType
*>(mDstStart
);
193 static auto inColorSpace2
= gfx::ToColorSpace2(SrcColorSpace
);
194 static auto outColorSpace2
= gfx::ToColorSpace2(DstColorSpace
);
196 auto inColorProfile
= gl::GLBlitHelper::ToColorProfileDesc(inColorSpace2
);
197 auto outColorProfile
= gl::GLBlitHelper::ToColorProfileDesc(outColorSpace2
);
199 const auto conversion
= color::ColorProfileConversionDesc::From({
200 .src
= *inColorProfile
,
201 .dst
= *outColorProfile
,
204 // the loop performing the texture format conversion
205 for (size_t i
= 0; i
< mHeight
; ++i
) {
206 const SrcType
* srcRowEnd
= srcRowStart
+ mWidth
* NumElementsPerSrcTexel
;
207 const SrcType
* srcPtr
= srcRowStart
;
208 DstType
* dstPtr
= dstRowStart
;
209 while (srcPtr
!= srcRowEnd
) {
210 // convert a single texel. We proceed in 4 steps: unpack the source
211 // texel so the corresponding interchange format (e.g. unpack RGB565 to
212 // RGBA8), do colorSpace conversion if necessary, convert the resulting
213 // data type to the destination type (e.g. convert from RGBA8 to
214 // RGBA32F), and finally pack the destination texel (e.g. pack RGBA32F
216 IntermediateSrcType unpackedSrc
[MaxElementsPerTexel
];
217 IntermediateDstType unpackedDst
[MaxElementsPerTexel
];
219 // unpack a src texel to corresponding intermediate src format.
220 // for example, unpack RGB565 to RGBA8
221 unpack
<SrcFormat
>(srcPtr
, unpackedSrc
);
223 if (!sameColorSpace
) {
224 // do colorSpace conversion, which leaves alpha untouched
225 float srcAsFloat
[MaxElementsPerTexel
];
226 convertType(unpackedSrc
, srcAsFloat
);
228 color::vec3({srcAsFloat
[0], srcAsFloat
[1], srcAsFloat
[2]});
229 auto outTexelVec
= conversion
.DstFromSrc(inTexelVec
);
230 srcAsFloat
[0] = outTexelVec
[0];
231 srcAsFloat
[1] = outTexelVec
[1];
232 srcAsFloat
[2] = outTexelVec
[2];
233 convertType(srcAsFloat
, unpackedSrc
);
236 // convert the data type to the destination type, if needed.
237 // for example, convert RGBA8 to RGBA32F
238 convertType(unpackedSrc
, unpackedDst
);
239 // pack the destination texel.
240 // for example, pack RGBA32F to RGB32F
241 pack
<DstFormat
, PremultiplicationOp
>(unpackedDst
, dstPtr
);
243 srcPtr
+= NumElementsPerSrcTexel
;
244 dstPtr
+= NumElementsPerDstTexel
;
246 srcRowStart
+= srcStrideInElements
;
247 dstRowStart
+= dstStrideInElements
;
253 template <WebGLTexelFormat SrcFormat
, WebGLTexelFormat DstFormat
,
254 WebGLTexelPremultiplicationOp PremultiplicationOp
,
255 dom::PredefinedColorSpace SrcColorSpace
>
256 void run(dom::PredefinedColorSpace dstColorSpace
) {
257 #define WEBGLIMAGECONVERTER_CASE_DSTCOLORSPACE(DstColorSpace) \
258 case DstColorSpace: \
259 return run<SrcFormat, DstFormat, PremultiplicationOp, SrcColorSpace, \
262 switch (dstColorSpace
) {
263 WEBGLIMAGECONVERTER_CASE_DSTCOLORSPACE(dom::PredefinedColorSpace::Srgb
)
264 WEBGLIMAGECONVERTER_CASE_DSTCOLORSPACE(
265 dom::PredefinedColorSpace::Display_p3
)
267 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
270 #undef WEBGLIMAGECONVERTER_CASE_DSTCOLORSPACE
273 template <WebGLTexelFormat SrcFormat
, WebGLTexelFormat DstFormat
,
274 WebGLTexelPremultiplicationOp PremultiplicationOp
>
275 void run(dom::PredefinedColorSpace srcColorSpace
,
276 dom::PredefinedColorSpace dstColorSpace
) {
277 #define WEBGLIMAGECONVERTER_CASE_SRCCOLORSPACE(SrcColorSpace) \
278 case SrcColorSpace: \
279 return run<SrcFormat, DstFormat, PremultiplicationOp, SrcColorSpace>( \
282 switch (srcColorSpace
) {
283 WEBGLIMAGECONVERTER_CASE_SRCCOLORSPACE(dom::PredefinedColorSpace::Srgb
)
284 WEBGLIMAGECONVERTER_CASE_SRCCOLORSPACE(
285 dom::PredefinedColorSpace::Display_p3
)
287 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
290 #undef WEBGLIMAGECONVERTER_CASE_SRCCOLORSPACE
293 template <WebGLTexelFormat SrcFormat
, WebGLTexelFormat DstFormat
>
294 void run(WebGLTexelPremultiplicationOp premultiplicationOp
,
295 dom::PredefinedColorSpace srcColorSpace
,
296 dom::PredefinedColorSpace dstColorSpace
) {
297 #define WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(PremultiplicationOp) \
298 case PremultiplicationOp: \
299 return run<SrcFormat, DstFormat, PremultiplicationOp>(srcColorSpace, \
302 switch (premultiplicationOp
) {
303 WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(
304 WebGLTexelPremultiplicationOp::None
)
305 WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(
306 WebGLTexelPremultiplicationOp::Premultiply
)
307 WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(
308 WebGLTexelPremultiplicationOp::Unpremultiply
)
310 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
313 #undef WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP
316 template <WebGLTexelFormat SrcFormat
>
317 void run(WebGLTexelFormat dstFormat
,
318 WebGLTexelPremultiplicationOp premultiplicationOp
,
319 dom::PredefinedColorSpace srcColorSpace
,
320 dom::PredefinedColorSpace dstColorSpace
) {
321 #define WEBGLIMAGECONVERTER_CASE_DSTFORMAT(DstFormat) \
323 return run<SrcFormat, DstFormat>(premultiplicationOp, srcColorSpace, \
328 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A8
)
329 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A16F
)
330 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A32F
)
331 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R8
)
332 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R16F
)
333 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R32F
)
335 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA8
)
336 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA16F
)
337 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA32F
)
338 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RG8
)
339 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RG16F
)
340 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RG32F
)
342 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB565
)
343 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB8
)
344 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB11F11F10F
)
345 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB16F
)
346 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB32F
)
348 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA4444
)
349 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA5551
)
350 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA8
)
351 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA16F
)
352 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA32F
)
353 WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::BGRA8
)
356 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
359 #undef WEBGLIMAGECONVERTER_CASE_DSTFORMAT
363 void run(WebGLTexelFormat srcFormat
, WebGLTexelFormat dstFormat
,
364 WebGLTexelPremultiplicationOp premultiplicationOp
,
365 dom::PredefinedColorSpace srcColorSpace
,
366 dom::PredefinedColorSpace dstColorSpace
) {
367 #define WEBGLIMAGECONVERTER_CASE_SRCFORMAT(SrcFormat) \
369 return run<SrcFormat>(dstFormat, premultiplicationOp, srcColorSpace, \
374 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A8
)
375 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A16F
)
376 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A32F
)
377 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R8
)
378 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R16F
)
379 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R32F
)
381 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA8
)
382 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA16F
)
383 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA32F
)
385 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB565
)
386 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB8
)
387 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB16F
)
388 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB32F
)
390 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA4444
)
391 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA5551
)
392 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA8
)
393 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA16F
)
394 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA32F
)
395 // DOM element source formats
396 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRX8
)
397 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRA8
)
400 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
403 #undef WEBGLIMAGECONVERTER_CASE_SRCFORMAT
406 WebGLImageConverter(size_t width
, size_t height
, const void* srcStart
,
407 void* dstStart
, ptrdiff_t srcStride
, ptrdiff_t dstStride
)
412 mSrcStride(srcStride
),
413 mDstStride(dstStride
),
417 bool Success() const { return mSuccess
; }
420 } // end anonymous namespace
422 bool ConvertImage(size_t width
, size_t height
, const void* srcBegin
,
423 size_t srcStride
, gl::OriginPos srcOrigin
,
424 WebGLTexelFormat srcFormat
, bool srcPremultiplied
,
425 void* dstBegin
, size_t dstStride
, gl::OriginPos dstOrigin
,
426 WebGLTexelFormat dstFormat
, bool dstPremultiplied
,
427 dom::PredefinedColorSpace srcColorSpace
,
428 dom::PredefinedColorSpace dstColorSpace
,
429 bool* const out_wasTrivial
) {
430 *out_wasTrivial
= true;
432 if (srcFormat
== WebGLTexelFormat::FormatNotSupportingAnyConversion
||
433 dstFormat
== WebGLTexelFormat::FormatNotSupportingAnyConversion
) {
437 if (!width
|| !height
) return true;
439 const bool shouldYFlip
= (srcOrigin
!= dstOrigin
);
441 const bool canSkipPremult
=
442 (!HasAlpha(srcFormat
) || !HasColor(srcFormat
) || !HasColor(dstFormat
));
444 WebGLTexelPremultiplicationOp premultOp
;
445 if (canSkipPremult
) {
446 premultOp
= WebGLTexelPremultiplicationOp::None
;
447 } else if (!srcPremultiplied
&& dstPremultiplied
) {
448 premultOp
= WebGLTexelPremultiplicationOp::Premultiply
;
449 } else if (srcPremultiplied
&& !dstPremultiplied
) {
450 premultOp
= WebGLTexelPremultiplicationOp::Unpremultiply
;
452 premultOp
= WebGLTexelPremultiplicationOp::None
;
455 const uint8_t* srcItr
= (const uint8_t*)srcBegin
;
456 const uint8_t* const srcEnd
= srcItr
+ srcStride
* height
;
457 uint8_t* dstItr
= (uint8_t*)dstBegin
;
458 ptrdiff_t dstItrStride
= dstStride
;
460 dstItr
= dstItr
+ dstStride
* (height
- 1);
461 dstItrStride
= -dstItrStride
;
464 bool sameColorSpace
= (srcColorSpace
== dstColorSpace
);
466 if (srcFormat
== dstFormat
&&
467 premultOp
== WebGLTexelPremultiplicationOp::None
&& sameColorSpace
) {
468 // Fast exit path: we just have to memcpy all the rows.
470 const auto bytesPerPixel
= TexelBytesForFormat(srcFormat
);
471 const size_t bytesPerRow
= bytesPerPixel
* width
;
473 while (srcItr
!= srcEnd
) {
474 memcpy(dstItr
, srcItr
, bytesPerRow
);
476 dstItr
+= dstItrStride
;
481 *out_wasTrivial
= false;
483 WebGLImageConverter
converter(width
, height
, srcItr
, dstItr
, srcStride
,
485 converter
.run(srcFormat
, dstFormat
, premultOp
, srcColorSpace
, dstColorSpace
);
486 if (!converter
.Success()) {
487 // the dst image may be left uninitialized, so we better not try to
488 // continue even in release builds. This should never happen anyway,
489 // and would be a bug in our code.
490 MOZ_CRASH("programming mistake in WebGL texture conversions");
496 } // end namespace mozilla