Line data Source code
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/. */
4 :
5 : #include "WebGLContext.h"
6 : #include "WebGLTexelConversions.h"
7 :
8 : namespace mozilla {
9 :
10 : using namespace WebGLTexelConversions;
11 :
12 : namespace {
13 :
14 : /** @class WebGLImageConverter
15 : *
16 : * This class is just a helper to implement WebGLContext::ConvertImage below.
17 : *
18 : * Design comments:
19 : *
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.
25 : */
26 : class WebGLImageConverter
27 : {
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;
34 :
35 : /*
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.
39 : */
40 : template<WebGLTexelFormat Format>
41 0 : static size_t NumElementsPerTexelForFormat() {
42 : switch (Format) {
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:
53 0 : return 1;
54 : case WebGLTexelFormat::RA8:
55 : case WebGLTexelFormat::RA16F:
56 : case WebGLTexelFormat::RA32F:
57 : case WebGLTexelFormat::RG8:
58 : case WebGLTexelFormat::RG16F:
59 : case WebGLTexelFormat::RG32F:
60 0 : return 2;
61 : case WebGLTexelFormat::RGB8:
62 : case WebGLTexelFormat::RGB16F:
63 : case WebGLTexelFormat::RGB32F:
64 0 : return 3;
65 : case WebGLTexelFormat::RGBA8:
66 : case WebGLTexelFormat::RGBA16F:
67 : case WebGLTexelFormat::RGBA32F:
68 : case WebGLTexelFormat::BGRX8:
69 : case WebGLTexelFormat::BGRA8:
70 0 : return 4;
71 : default:
72 : MOZ_ASSERT(false, "Unknown texel format. Coding mistake?");
73 : return 0;
74 : }
75 : }
76 :
77 : /*
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 generating
83 : * useless code.
84 : */
85 : template<WebGLTexelFormat SrcFormat,
86 : WebGLTexelFormat DstFormat,
87 : WebGLTexelPremultiplicationOp PremultiplicationOp>
88 0 : void run()
89 : {
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 code.
94 : // So instead, we return early, so Success() will return false, and the caller
95 : // must check that and abort in that case. See WebGLContext::ConvertImage.
96 :
97 : if (SrcFormat == DstFormat &&
98 : PremultiplicationOp == WebGLTexelPremultiplicationOp::None)
99 : {
100 : // Should have used a fast exit path earlier, rather than entering this function.
101 : // we explicitly return here to allow the compiler to avoid generating this code
102 0 : return;
103 : }
104 :
105 : // Only textures uploaded from DOM elements or ImageData can allow DstFormat != SrcFormat.
106 : // DOM elements can only give BGRA8, BGRX8, A8, RGB565 formats. See DOMElementToImageSurface.
107 : // ImageData is always RGBA8. So all other SrcFormat will always satisfy DstFormat==SrcFormat,
108 : // so we can avoid compiling the code for all the unreachable paths.
109 : const bool CanSrcFormatComeFromDOMElementOrImageData
110 : = SrcFormat == WebGLTexelFormat::BGRA8 ||
111 : SrcFormat == WebGLTexelFormat::BGRX8 ||
112 : SrcFormat == WebGLTexelFormat::A8 ||
113 : SrcFormat == WebGLTexelFormat::RGB565 ||
114 0 : SrcFormat == WebGLTexelFormat::RGBA8;
115 : if (!CanSrcFormatComeFromDOMElementOrImageData &&
116 : SrcFormat != DstFormat)
117 : {
118 0 : return;
119 : }
120 :
121 : // Likewise, only textures uploaded from DOM elements or ImageData can possibly have to be unpremultiplied.
122 : if (!CanSrcFormatComeFromDOMElementOrImageData &&
123 : PremultiplicationOp == WebGLTexelPremultiplicationOp::Unpremultiply)
124 : {
125 0 : return;
126 : }
127 :
128 : // there is no point in premultiplication/unpremultiplication
129 : // in the following cases:
130 : // - the source format has no alpha
131 : // - the source format has no color
132 : // - the destination format has no color
133 0 : if (!HasAlpha(SrcFormat) ||
134 0 : !HasColor(SrcFormat) ||
135 0 : !HasColor(DstFormat))
136 : {
137 :
138 : if (PremultiplicationOp != WebGLTexelPremultiplicationOp::None)
139 : {
140 0 : return;
141 : }
142 : }
143 :
144 : // end of early return cases.
145 :
146 0 : MOZ_ASSERT(!mAlreadyRun, "converter should be run only once!");
147 0 : mAlreadyRun = true;
148 :
149 : // gather some compile-time meta-data about the formats at hand.
150 :
151 : typedef
152 : typename DataTypeForFormat<SrcFormat>::Type
153 : SrcType;
154 : typedef
155 : typename DataTypeForFormat<DstFormat>::Type
156 : DstType;
157 :
158 : const WebGLTexelFormat IntermediateSrcFormat
159 0 : = IntermediateFormat<SrcFormat>::Value;
160 : const WebGLTexelFormat IntermediateDstFormat
161 0 : = IntermediateFormat<DstFormat>::Value;
162 : typedef
163 : typename DataTypeForFormat<IntermediateSrcFormat>::Type
164 : IntermediateSrcType;
165 : typedef
166 : typename DataTypeForFormat<IntermediateDstFormat>::Type
167 : IntermediateDstType;
168 :
169 0 : const size_t NumElementsPerSrcTexel = NumElementsPerTexelForFormat<SrcFormat>();
170 0 : const size_t NumElementsPerDstTexel = NumElementsPerTexelForFormat<DstFormat>();
171 0 : const size_t MaxElementsPerTexel = 4;
172 0 : MOZ_ASSERT(NumElementsPerSrcTexel <= MaxElementsPerTexel, "unhandled format");
173 0 : MOZ_ASSERT(NumElementsPerDstTexel <= MaxElementsPerTexel, "unhandled format");
174 :
175 : // we assume that the strides are multiples of the sizeof of respective types.
176 : // this assumption will allow us to iterate over src and dst images using typed
177 : // pointers, e.g. uint8_t* or uint16_t* or float*, instead of untyped pointers.
178 : // So this assumption allows us to write cleaner and safer code, but it might
179 : // not be true forever and if it eventually becomes wrong, we'll have to revert
180 : // to always iterating using uint8_t* pointers regardless of the types at hand.
181 0 : MOZ_ASSERT(mSrcStride % sizeof(SrcType) == 0 &&
182 : mDstStride % sizeof(DstType) == 0,
183 : "Unsupported: texture stride is not a multiple of sizeof(type)");
184 0 : const ptrdiff_t srcStrideInElements = mSrcStride / sizeof(SrcType);
185 0 : const ptrdiff_t dstStrideInElements = mDstStride / sizeof(DstType);
186 :
187 0 : const SrcType* srcRowStart = static_cast<const SrcType*>(mSrcStart);
188 0 : DstType* dstRowStart = static_cast<DstType*>(mDstStart);
189 :
190 : // the loop performing the texture format conversion
191 0 : for (size_t i = 0; i < mHeight; ++i) {
192 0 : const SrcType* srcRowEnd = srcRowStart + mWidth * NumElementsPerSrcTexel;
193 0 : const SrcType* srcPtr = srcRowStart;
194 0 : DstType* dstPtr = dstRowStart;
195 0 : while (srcPtr != srcRowEnd) {
196 : // convert a single texel. We proceed in 3 steps: unpack the source texel
197 : // so the corresponding interchange format (e.g. unpack RGB565 to RGBA8),
198 : // convert the resulting data type to the destination type (e.g. convert
199 : // from RGBA8 to RGBA32F), and finally pack the destination texel
200 : // (e.g. pack RGBA32F to RGB32F).
201 : IntermediateSrcType unpackedSrc[MaxElementsPerTexel];
202 : IntermediateDstType unpackedDst[MaxElementsPerTexel];
203 :
204 : // unpack a src texel to corresponding intermediate src format.
205 : // for example, unpack RGB565 to RGBA8
206 0 : unpack<SrcFormat>(srcPtr, unpackedSrc);
207 : // convert the data type to the destination type, if needed.
208 : // for example, convert RGBA8 to RGBA32F
209 0 : convertType(unpackedSrc, unpackedDst);
210 : // pack the destination texel.
211 : // for example, pack RGBA32F to RGB32F
212 0 : pack<DstFormat, PremultiplicationOp>(unpackedDst, dstPtr);
213 :
214 0 : srcPtr += NumElementsPerSrcTexel;
215 0 : dstPtr += NumElementsPerDstTexel;
216 : }
217 0 : srcRowStart += srcStrideInElements;
218 0 : dstRowStart += dstStrideInElements;
219 : }
220 :
221 0 : mSuccess = true;
222 0 : return;
223 : }
224 :
225 : template<WebGLTexelFormat SrcFormat,
226 : WebGLTexelFormat DstFormat>
227 0 : void run(WebGLTexelPremultiplicationOp premultiplicationOp)
228 : {
229 : #define WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(PremultiplicationOp) \
230 : case PremultiplicationOp: \
231 : return run<SrcFormat, DstFormat, PremultiplicationOp>();
232 :
233 0 : switch (premultiplicationOp) {
234 0 : WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(WebGLTexelPremultiplicationOp::None)
235 0 : WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(WebGLTexelPremultiplicationOp::Premultiply)
236 0 : WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(WebGLTexelPremultiplicationOp::Unpremultiply)
237 : default:
238 0 : MOZ_ASSERT(false, "unhandled case. Coding mistake?");
239 : }
240 :
241 : #undef WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP
242 : }
243 :
244 : template<WebGLTexelFormat SrcFormat>
245 0 : void run(WebGLTexelFormat dstFormat,
246 : WebGLTexelPremultiplicationOp premultiplicationOp)
247 : {
248 : #define WEBGLIMAGECONVERTER_CASE_DSTFORMAT(DstFormat) \
249 : case DstFormat: \
250 : return run<SrcFormat, DstFormat>(premultiplicationOp);
251 :
252 0 : switch (dstFormat) {
253 : // 1-channel formats
254 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A8)
255 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A16F)
256 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A32F)
257 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R8)
258 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R16F)
259 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R32F)
260 : // 2-channel formats
261 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA8)
262 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA16F)
263 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA32F)
264 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RG8)
265 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RG16F)
266 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RG32F)
267 : // 3-channel formats
268 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB565)
269 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB8)
270 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB11F11F10F)
271 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB16F)
272 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB32F)
273 : // 4-channel formats
274 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA4444)
275 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA5551)
276 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA8)
277 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA16F)
278 0 : WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA32F)
279 :
280 : default:
281 0 : MOZ_ASSERT(false, "unhandled case. Coding mistake?");
282 : }
283 :
284 : #undef WEBGLIMAGECONVERTER_CASE_DSTFORMAT
285 : }
286 :
287 : public:
288 :
289 0 : void run(WebGLTexelFormat srcFormat,
290 : WebGLTexelFormat dstFormat,
291 : WebGLTexelPremultiplicationOp premultiplicationOp)
292 : {
293 : #define WEBGLIMAGECONVERTER_CASE_SRCFORMAT(SrcFormat) \
294 : case SrcFormat: \
295 : return run<SrcFormat>(dstFormat, premultiplicationOp);
296 :
297 0 : switch (srcFormat) {
298 : // 1-channel formats
299 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A8)
300 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A16F)
301 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A32F)
302 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R8)
303 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R16F)
304 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R32F)
305 : // 2-channel formats
306 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA8)
307 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA16F)
308 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA32F)
309 : // 3-channel formats
310 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB565)
311 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB8)
312 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB16F)
313 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB32F)
314 : // 4-channel formats
315 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA4444)
316 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA5551)
317 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA8)
318 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA16F)
319 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA32F)
320 : // DOM element source formats
321 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRX8)
322 0 : WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRA8)
323 :
324 : default:
325 0 : MOZ_ASSERT(false, "unhandled case. Coding mistake?");
326 : }
327 :
328 : #undef WEBGLIMAGECONVERTER_CASE_SRCFORMAT
329 : }
330 :
331 0 : WebGLImageConverter(size_t width, size_t height,
332 : const void* srcStart, void* dstStart,
333 : ptrdiff_t srcStride, ptrdiff_t dstStride)
334 0 : : mWidth(width), mHeight(height),
335 : mSrcStart(srcStart), mDstStart(dstStart),
336 : mSrcStride(srcStride), mDstStride(dstStride),
337 0 : mAlreadyRun(false), mSuccess(false)
338 0 : {}
339 :
340 0 : bool Success() const {
341 0 : return mSuccess;
342 : }
343 : };
344 :
345 : } // end anonymous namespace
346 :
347 : bool
348 0 : ConvertImage(size_t width, size_t height,
349 : const void* srcBegin, size_t srcStride, gl::OriginPos srcOrigin,
350 : WebGLTexelFormat srcFormat, bool srcPremultiplied,
351 : void* dstBegin, size_t dstStride, gl::OriginPos dstOrigin,
352 : WebGLTexelFormat dstFormat, bool dstPremultiplied,
353 : bool* const out_wasTrivial)
354 : {
355 0 : *out_wasTrivial = true;
356 :
357 0 : if (srcFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion ||
358 : dstFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion)
359 : {
360 0 : return false;
361 : }
362 :
363 0 : if (!width || !height)
364 0 : return true;
365 :
366 0 : const bool shouldYFlip = (srcOrigin != dstOrigin);
367 :
368 0 : const bool canSkipPremult = (!HasAlpha(srcFormat) ||
369 0 : !HasColor(srcFormat) ||
370 0 : !HasColor(dstFormat));
371 :
372 : WebGLTexelPremultiplicationOp premultOp;
373 0 : if (canSkipPremult) {
374 0 : premultOp = WebGLTexelPremultiplicationOp::None;
375 0 : } else if (!srcPremultiplied && dstPremultiplied) {
376 0 : premultOp = WebGLTexelPremultiplicationOp::Premultiply;
377 0 : } else if (srcPremultiplied && !dstPremultiplied) {
378 0 : premultOp = WebGLTexelPremultiplicationOp::Unpremultiply;
379 : } else {
380 0 : premultOp = WebGLTexelPremultiplicationOp::None;
381 : }
382 :
383 0 : const uint8_t* srcItr = (const uint8_t*)srcBegin;
384 0 : const uint8_t* const srcEnd = srcItr + srcStride * height;
385 0 : uint8_t* dstItr = (uint8_t*)dstBegin;
386 0 : ptrdiff_t dstItrStride = dstStride;
387 0 : if (shouldYFlip) {
388 0 : dstItr = dstItr + dstStride * (height - 1);
389 0 : dstItrStride = -dstItrStride;
390 : }
391 :
392 0 : if (srcFormat == dstFormat && premultOp == WebGLTexelPremultiplicationOp::None) {
393 : // Fast exit path: we just have to memcpy all the rows.
394 : //
395 : // The case where absolutely nothing needs to be done is supposed to have
396 : // been handled earlier (in TexImage2D_base, etc).
397 : //
398 : // So the case we're handling here is when even though no format conversion is
399 : // needed, we still might have to flip vertically and/or to adjust to a different
400 : // stride.
401 :
402 : // We ignore canSkipPremult for this perf trap, since it's an avoidable perf cliff
403 : // under the WebGL API user's control.
404 0 : MOZ_ASSERT((srcPremultiplied != dstPremultiplied ||
405 : shouldYFlip ||
406 : srcStride != dstStride),
407 : "Performance trap -- should handle this case earlier to avoid memcpy");
408 :
409 0 : const auto bytesPerPixel = TexelBytesForFormat(srcFormat);
410 0 : const size_t bytesPerRow = bytesPerPixel * width;
411 :
412 0 : while (srcItr != srcEnd) {
413 0 : memcpy(dstItr, srcItr, bytesPerRow);
414 0 : srcItr += srcStride;
415 0 : dstItr += dstItrStride;
416 : }
417 0 : return true;
418 : }
419 :
420 0 : *out_wasTrivial = false;
421 :
422 0 : WebGLImageConverter converter(width, height, srcItr, dstItr, srcStride, dstItrStride);
423 0 : converter.run(srcFormat, dstFormat, premultOp);
424 :
425 0 : if (!converter.Success()) {
426 : // the dst image may be left uninitialized, so we better not try to
427 : // continue even in release builds. This should never happen anyway,
428 : // and would be a bug in our code.
429 0 : MOZ_CRASH("programming mistake in WebGL texture conversions");
430 : }
431 :
432 0 : return true;
433 : }
434 :
435 : } // end namespace mozilla
|