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 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
;
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() {
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
:
53 case WebGLTexelFormat::RA8
:
54 case WebGLTexelFormat::RA16F
:
55 case WebGLTexelFormat::RA32F
:
57 case WebGLTexelFormat::RGB8
:
58 case WebGLTexelFormat::RGB16F
:
59 case WebGLTexelFormat::RGB32F
:
61 case WebGLTexelFormat::RGBA8
:
62 case WebGLTexelFormat::BGRA8
:
63 case WebGLTexelFormat::BGRX8
:
64 case WebGLTexelFormat::RGBA16F
:
65 case WebGLTexelFormat::RGBA32F
:
68 MOZ_ASSERT(false, "Unknown texel format. Coding mistake?");
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
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
>
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
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
)
117 // Likewise, only textures uploaded from DOM elements or ImageData can possibly have to be unpremultiplied.
118 if (!CanSrcFormatComeFromDOMElementOrImageData
&&
119 PremultiplicationOp
== WebGLTexelPremultiplicationOp::Unpremultiply
)
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
)
140 // end of early return cases.
142 MOZ_ASSERT(!mAlreadyRun
, "converter should be run only once!");
145 // gather some compile-time meta-data about the formats at hand.
148 typename DataTypeForFormat
<SrcFormat
>::Type
151 typename DataTypeForFormat
<DstFormat
>::Type
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
;
159 typename DataTypeForFormat
<IntermediateSrcFormat
>::Type
162 typename DataTypeForFormat
<IntermediateDstFormat
>::Type
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
;
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
)
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) \
246 return run<SrcFormat, DstFormat>(premultiplicationOp);
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
)
268 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
271 #undef WEBGLIMAGECONVERTER_CASE_DSTFORMAT
276 void run(WebGLTexelFormat srcFormat
,
277 WebGLTexelFormat dstFormat
,
278 WebGLTexelPremultiplicationOp premultiplicationOp
)
280 #define WEBGLIMAGECONVERTER_CASE_SRCFORMAT(SrcFormat) \
282 return run<SrcFormat>(dstFormat, premultiplicationOp);
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
)
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 {
326 } // end anonymous namespace
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
,
335 if (width
<= 0 || height
<= 0)
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
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
);
369 dst_row
+= dst_delta
;
374 if (srcFormat
== WebGLTexelFormat::FormatNotSupportingAnyConversion
||
375 dstFormat
== WebGLTexelFormat::FormatNotSupportingAnyConversion
)
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");
407 } // end namespace mozilla