Bug 1700051: part 26) Correct typo in comment of `mozInlineSpellWordUtil::BuildSoftTe...
[gecko.git] / dom / canvas / WebGLTexelConversions.cpp
blob40765dec0da82e3193505acf1732dc2341fd6ae2
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
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;
31 bool mAlreadyRun;
32 bool mSuccess;
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() {
41 switch (Format) {
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:
52 return 1;
53 case WebGLTexelFormat::RA8:
54 case WebGLTexelFormat::RA16F:
55 case WebGLTexelFormat::RA32F:
56 case WebGLTexelFormat::RG8:
57 case WebGLTexelFormat::RG16F:
58 case WebGLTexelFormat::RG32F:
59 return 2;
60 case WebGLTexelFormat::RGB8:
61 case WebGLTexelFormat::RGB16F:
62 case WebGLTexelFormat::RGB32F:
63 return 3;
64 case WebGLTexelFormat::RGBA8:
65 case WebGLTexelFormat::RGBA16F:
66 case WebGLTexelFormat::RGBA32F:
67 case WebGLTexelFormat::BGRX8:
68 case WebGLTexelFormat::BGRA8:
69 return 4;
70 default:
71 MOZ_ASSERT(false, "Unknown texel format. Coding mistake?");
72 return 0;
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>
86 void run() {
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
100 return;
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) {
115 return;
118 // Likewise, only textures uploaded from DOM elements or ImageData can
119 // possibly have to be unpremultiplied.
120 if (!CanSrcFormatComeFromDOMElementOrImageData &&
121 PremultiplicationOp == WebGLTexelPremultiplicationOp::Unpremultiply) {
122 return;
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) {
132 return;
136 // end of early return cases.
138 MOZ_ASSERT(!mAlreadyRun, "converter should be run only once!");
139 mAlreadyRun = true;
141 // gather some compile-time meta-data about the formats at hand.
143 typedef typename DataTypeForFormat<SrcFormat>::Type SrcType;
144 typedef typename DataTypeForFormat<DstFormat>::Type DstType;
146 const WebGLTexelFormat IntermediateSrcFormat =
147 IntermediateFormat<SrcFormat>::Value;
148 const WebGLTexelFormat IntermediateDstFormat =
149 IntermediateFormat<DstFormat>::Value;
150 typedef typename DataTypeForFormat<IntermediateSrcFormat>::Type
151 IntermediateSrcType;
152 typedef typename DataTypeForFormat<IntermediateDstFormat>::Type
153 IntermediateDstType;
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,
161 "unhandled format");
162 MOZ_ASSERT(NumElementsPerDstTexel <= MaxElementsPerTexel,
163 "unhandled format");
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.
172 MOZ_ASSERT(
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;
218 mSuccess = true;
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)
234 default:
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) \
245 case DstFormat: \
246 return run<SrcFormat, DstFormat>(premultiplicationOp);
248 switch (dstFormat) {
249 // 1-channel formats
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)
256 // 2-channel formats
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)
263 // 3-channel formats
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)
269 // 4-channel formats
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)
276 default:
277 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
280 #undef WEBGLIMAGECONVERTER_CASE_DSTFORMAT
283 public:
284 void run(WebGLTexelFormat srcFormat, WebGLTexelFormat dstFormat,
285 WebGLTexelPremultiplicationOp premultiplicationOp) {
286 #define WEBGLIMAGECONVERTER_CASE_SRCFORMAT(SrcFormat) \
287 case SrcFormat: \
288 return run<SrcFormat>(dstFormat, premultiplicationOp);
290 switch (srcFormat) {
291 // 1-channel formats
292 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A8)
293 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A16F)
294 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A32F)
295 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R8)
296 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R16F)
297 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R32F)
298 // 2-channel formats
299 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA8)
300 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA16F)
301 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA32F)
302 // 3-channel formats
303 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB565)
304 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB8)
305 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB16F)
306 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB32F)
307 // 4-channel formats
308 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA4444)
309 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA5551)
310 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA8)
311 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA16F)
312 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA32F)
313 // DOM element source formats
314 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRX8)
315 WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRA8)
317 default:
318 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
321 #undef WEBGLIMAGECONVERTER_CASE_SRCFORMAT
324 WebGLImageConverter(size_t width, size_t height, const void* srcStart,
325 void* dstStart, ptrdiff_t srcStride, ptrdiff_t dstStride)
326 : mWidth(width),
327 mHeight(height),
328 mSrcStart(srcStart),
329 mDstStart(dstStart),
330 mSrcStride(srcStride),
331 mDstStride(dstStride),
332 mAlreadyRun(false),
333 mSuccess(false) {}
335 bool Success() const { return mSuccess; }
338 } // end anonymous namespace
340 bool ConvertImage(size_t width, size_t height, const void* srcBegin,
341 size_t srcStride, gl::OriginPos srcOrigin,
342 WebGLTexelFormat srcFormat, bool srcPremultiplied,
343 void* dstBegin, size_t dstStride, gl::OriginPos dstOrigin,
344 WebGLTexelFormat dstFormat, bool dstPremultiplied,
345 bool* const out_wasTrivial) {
346 *out_wasTrivial = true;
348 if (srcFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion ||
349 dstFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion) {
350 return false;
353 if (!width || !height) return true;
355 const bool shouldYFlip = (srcOrigin != dstOrigin);
357 const bool canSkipPremult =
358 (!HasAlpha(srcFormat) || !HasColor(srcFormat) || !HasColor(dstFormat));
360 WebGLTexelPremultiplicationOp premultOp;
361 if (canSkipPremult) {
362 premultOp = WebGLTexelPremultiplicationOp::None;
363 } else if (!srcPremultiplied && dstPremultiplied) {
364 premultOp = WebGLTexelPremultiplicationOp::Premultiply;
365 } else if (srcPremultiplied && !dstPremultiplied) {
366 premultOp = WebGLTexelPremultiplicationOp::Unpremultiply;
367 } else {
368 premultOp = WebGLTexelPremultiplicationOp::None;
371 const uint8_t* srcItr = (const uint8_t*)srcBegin;
372 const uint8_t* const srcEnd = srcItr + srcStride * height;
373 uint8_t* dstItr = (uint8_t*)dstBegin;
374 ptrdiff_t dstItrStride = dstStride;
375 if (shouldYFlip) {
376 dstItr = dstItr + dstStride * (height - 1);
377 dstItrStride = -dstItrStride;
380 if (srcFormat == dstFormat &&
381 premultOp == WebGLTexelPremultiplicationOp::None) {
382 // Fast exit path: we just have to memcpy all the rows.
384 // The case where absolutely nothing needs to be done is supposed to have
385 // been handled earlier (in TexImage2D_base, etc).
387 // So the case we're handling here is when even though no format conversion
388 // is needed, we still might have to flip vertically and/or to adjust to a
389 // different stride.
391 // We ignore canSkipPremult for this perf trap, since it's an avoidable perf
392 // cliff under the WebGL API user's control.
393 MOZ_ASSERT(
394 (srcPremultiplied != dstPremultiplied || shouldYFlip ||
395 srcStride != dstStride),
396 "Performance trap -- should handle this case earlier to avoid memcpy");
398 const auto bytesPerPixel = TexelBytesForFormat(srcFormat);
399 const size_t bytesPerRow = bytesPerPixel * width;
401 while (srcItr != srcEnd) {
402 memcpy(dstItr, srcItr, bytesPerRow);
403 srcItr += srcStride;
404 dstItr += dstItrStride;
406 return true;
409 *out_wasTrivial = false;
411 WebGLImageConverter converter(width, height, srcItr, dstItr, srcStride,
412 dstItrStride);
413 converter.run(srcFormat, dstFormat, premultOp);
415 if (!converter.Success()) {
416 // the dst image may be left uninitialized, so we better not try to
417 // continue even in release builds. This should never happen anyway,
418 // and would be a bug in our code.
419 MOZ_CRASH("programming mistake in WebGL texture conversions");
422 return true;
425 } // end namespace mozilla