content/canvas/src/WebGLTexelConversions.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     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
    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;
    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<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
    41     static size_t NumElementsPerTexelForFormat() {
    42         switch (Format) {
    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:
    52                 return 1;
    53             case WebGLTexelFormat::RA8:
    54             case WebGLTexelFormat::RA16F:
    55             case WebGLTexelFormat::RA32F:
    56                 return 2;
    57             case WebGLTexelFormat::RGB8:
    58             case WebGLTexelFormat::RGB16F:
    59             case WebGLTexelFormat::RGB32F:
    60                 return 3;
    61             case WebGLTexelFormat::RGBA8:
    62             case WebGLTexelFormat::BGRA8:
    63             case WebGLTexelFormat::BGRX8:
    64             case WebGLTexelFormat::RGBA16F:
    65             case WebGLTexelFormat::RGBA32F:
    66                 return 4;
    67             default:
    68                 MOZ_ASSERT(false, "Unknown texel format. Coding mistake?");
    69                 return 0;
    70         }
    71     }
    73     /*
    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
    79      * useless code.
    80      */
    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>
    84     void run()
    85     {
    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)
    95         {
    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
    98             return;
    99         }
   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)
   113         {
   114             return;
   115         }
   117         // Likewise, only textures uploaded from DOM elements or ImageData can possibly have to be unpremultiplied.
   118         if (!CanSrcFormatComeFromDOMElementOrImageData &&
   119             PremultiplicationOp == WebGLTexelPremultiplicationOp::Unpremultiply)
   120         {
   121             return;
   122         }
   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))
   132         {
   134             if (PremultiplicationOp != WebGLTexelPremultiplicationOp::None)
   135             {
   136                 return;
   137             }
   138         }
   140         // end of early return cases.
   142         MOZ_ASSERT(!mAlreadyRun, "converter should be run only once!");
   143         mAlreadyRun = true;
   145         // gather some compile-time meta-data about the formats at hand.
   147         typedef
   148             typename DataTypeForFormat<SrcFormat>::Type
   149             SrcType;
   150         typedef
   151             typename DataTypeForFormat<DstFormat>::Type
   152             DstType;
   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;
   158         typedef
   159             typename DataTypeForFormat<IntermediateSrcFormat>::Type
   160             IntermediateSrcType;
   161         typedef
   162             typename DataTypeForFormat<IntermediateDstFormat>::Type
   163             IntermediateDstType;
   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;
   212             }
   213             srcRowStart += srcStrideInElements;
   214             dstRowStart += dstStrideInElements;
   215         }
   217         mSuccess = true;
   218         return;
   219     }
   221     template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) SrcFormat,
   222              MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) DstFormat>
   223     void run(WebGLTexelPremultiplicationOp premultiplicationOp)
   224     {
   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)
   233             default:
   234                 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
   235         }
   237         #undef WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP
   238     }
   240     template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) SrcFormat>
   241     void run(WebGLTexelFormat dstFormat,
   242              WebGLTexelPremultiplicationOp premultiplicationOp)
   243     {
   244         #define WEBGLIMAGECONVERTER_CASE_DSTFORMAT(DstFormat) \
   245             case DstFormat: \
   246                 return run<SrcFormat, DstFormat>(premultiplicationOp);
   248         switch (dstFormat) {
   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)
   267             default:
   268                 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
   269         }
   271         #undef WEBGLIMAGECONVERTER_CASE_DSTFORMAT
   272     }
   274 public:
   276     void run(WebGLTexelFormat srcFormat,
   277              WebGLTexelFormat dstFormat,
   278              WebGLTexelPremultiplicationOp premultiplicationOp)
   279     {
   280         #define WEBGLIMAGECONVERTER_CASE_SRCFORMAT(SrcFormat) \
   281             case SrcFormat: \
   282                 return run<SrcFormat>(dstFormat, premultiplicationOp);
   284         switch (srcFormat) {
   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)
   305             default:
   306                 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
   307         }
   309         #undef WEBGLIMAGECONVERTER_CASE_SRCFORMAT
   310     }
   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)
   319     {}
   321     bool Success() const {
   322         return mSuccess;
   323     }
   324 };
   326 } // end anonymous namespace
   328 void
   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,
   333                            size_t dstTexelSize)
   334 {
   335     if (width <= 0 || height <= 0)
   336         return;
   338     const bool FormatsRequireNoPremultiplicationOp =
   339         !HasAlpha(srcFormat) ||
   340         !HasColor(srcFormat) ||
   341         !HasColor(dstFormat);
   343     if (srcFormat == dstFormat &&
   344         (FormatsRequireNoPremultiplicationOp || srcPremultiplied == dstPremultiplied))
   345     {
   346         // fast exit path: we just have to memcpy all the rows.
   347         //
   348         // The case where absolutely nothing needs to be done is supposed to have
   349         // been handled earlier (in TexImage2D_base, etc).
   350         //
   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
   362                            : dst;
   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);
   368             ptr += srcStride;
   369             dst_row += dst_delta;
   370         }
   371         return;
   372     }
   374     uint8_t* dstStart = dst;
   375     ptrdiff_t signedDstStride = dstStride;
   376     if (mPixelStoreFlipY) {
   377         dstStart = dst + (height - 1) * dstStride;
   378         signedDstStride = -signedDstStride;
   379     }
   381     WebGLImageConverter converter(width, height, src, dstStart, srcStride, signedDstStride);
   383     const WebGLTexelPremultiplicationOp premultiplicationOp
   384         = FormatsRequireNoPremultiplicationOp     ? WebGLTexelPremultiplicationOp::None
   385         : (!srcPremultiplied && dstPremultiplied) ? WebGLTexelPremultiplicationOp::Premultiply
   386         : (srcPremultiplied && !dstPremultiplied) ? WebGLTexelPremultiplicationOp::Unpremultiply
   387                                                   : WebGLTexelPremultiplicationOp::None;
   389     converter.run(srcFormat, dstFormat, premultiplicationOp);
   391     if (!converter.Success()) {
   392         // the dst image may be left uninitialized, so we better not try to
   393         // continue even in release builds. This should never happen anyway,
   394         // and would be a bug in our code.
   395         NS_RUNTIMEABORT("programming mistake in WebGL texture conversions");
   396     }
   397 }
   399 } // end namespace mozilla

mercurial